Event loops: CONNECT of the HTTP proxy and SOCKS, pop3p, imapp, smtpp

give up their fiber while relayed

The children for which mapping is their last thing set their tail too:

- socks, for CONNECT: its log line; the request's buffer is freed before
  mapping, nothing needing it after;
- proxy, for CONNECT: the status page its end sends for the result
  where one is, now proxystatus() for both, and the log line of its
  request, a copy of which is the tail's. Not in a request httpsrv hands
  over, whose end is httpsrv's;
- pop3p, imapp (with a copy of its tag), smtpp (its command freed before
  mapping): the log line and the error reply their ends send.

A tail may wait now, sending to the client: once mapping is over the end
of a client whose fiber gave up its stack runs on a fiber taken again
for it, which, after a reload, has sockmap() go on with the mapping
first. Only with no stack to be had is it ended there, logged with no
tail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vladimir Dubrovin 2026-10-26 00:04:39 +03:00
parent 973fc89d58
commit 90d798eaaf
6 changed files with 150 additions and 65 deletions

View File

@ -25,6 +25,24 @@
#define NOSTARTTLS param->srv->nostarttls #define NOSTARTTLS param->srv->nostarttls
#endif #endif
/* as the end below, once mapping is over: data is the tag */
static void imapptail(struct clientparam *param, void *data){
unsigned char buf[320];
char *tag = (char *)data;
if(*param->hostname&&*param->extusername) {
sprintf((char *)buf, "%.128s@%.128s%c%hu", param->extusername, param->hostname, (*SAPORT(&param->sinsr)==143)?0:':', ntohs(*SAPORT(&param->sinsr)));
dolog(param, buf);
}
else dolog(param, NULL);
if(param->clisock != INVALID_SOCKET) {
if ((param->res > 0 && param->res < 100) || (param->res > 691 && param->res <700)) {
sprintf((char *)buf, "%.60s NO proxy error\r\n", (tag && *tag)? tag : "*");
socksend(param, param->clisock, buf, (int)strlen((char *)buf),param->srv->timeouts[STRING_S]);
}
}
}
void * imappchild(struct clientparam* param) { void * imappchild(struct clientparam* param) {
int i=0, res, method=CL_LOGINCMD, caps=0; int i=0, res, method=CL_LOGINCMD, caps=0;
unsigned char buf[2048]; unsigned char buf[2048];
@ -224,6 +242,7 @@ void * imappchild(struct clientparam* param) {
} }
param->statscli64 += (uint64_t)(strlen((char *)tag) + strlen((char *)param->extusername) + strlen((char *)pb) + 11); param->statscli64 += (uint64_t)(strlen((char *)tag) + strlen((char *)param->extusername) + strlen((char *)pb) + 11);
param->nwrites++; param->nwrites++;
if((param->maptaildata = strdup((char *)tag))) param->maptail = imapptail;
RETURN (mapsocket(param, 180)); RETURN (mapsocket(param, 180));
CLEANRET: CLEANRET:

View File

@ -848,51 +848,51 @@ static void cbmapidle(struct loop *l, struct looptimer *t){
cbmapdone(m); cbmapdone(m);
} }
/* What the child of the client does once mapping is over, as its end and /* The end of a client whose fiber gave up its stack while it was relayed,
that of its fiber would: the result, its tail, the connection released on a fiber again, which the child's end (maptail) may wait on: after a
and freed. */ reload sockmap() goes on with the mapping first. Then what the child and
static void cbmapfinish(struct clientparam *param, int res){ its fiber would do: the result, the tail, the connection released and
freed. */
static void cbmapfinish(void *arg){
struct cbmap *m = (struct cbmap *)arg;
struct clientparam *param = m->param;
int res = m->res, timeo = m->timeosec, usesplice = m->usesplice0;
free(m);
if(res == -2) res = sockmap(param, timeo, usesplice);
param->res = res; param->res = res;
if(param->maptail) param->maptail(param, param->maptaildata); if(param->maptail) param->maptail(param, param->maptaildata);
releaseparam(param); releaseparam(param);
freeparam(param); freeparam(param);
} }
/* a fiber for the client again: sockmap() goes on with the mapping after /* The mapping is over: its fiber goes on, which frees m, or, with none, a
a reload, then the child's end */ fiber is taken to end the client as the child would. */
static void cbmapresume(void *arg){
struct cbmap *m = (struct cbmap *)arg;
struct clientparam *param = m->param;
int timeo = m->timeosec, usesplice = m->usesplice0;
free(m);
cbmapfinish(param, sockmap(param, timeo, usesplice));
}
/* The mapping is over: its fiber goes on, or, with none, the loop ends it
as the child would, or gives it a fiber again for sockmap() to take it
over after a reload. m is freed. */
static void cbmapdone(struct cbmap *m){ static void cbmapdone(struct cbmap *m){
struct clientparam *param = m->param;
struct fiberslot *fs; struct fiberslot *fs;
int res;
if(!m->abandoned){ if(!m->abandoned){
/* the fiber frees m */
fiberrun(m->lp, m->f); fiberrun(m->lp, m->f);
return; return;
} }
if(m->res == -2){ if((fs = fibertake(m->lp, m->param->stacksize))){
if((fs = fibertake(m->lp, param->stacksize))){ fiberstart(&fs->f, cbmapfinish, m);
fiberstart(&fs->f, cbmapresume, m);
fiberrun(m->lp, &fs->f); fiberrun(m->lp, &fs->f);
return; return;
} }
m->res = 21; /* no stack for it: as it can be, here, with no waits left to its
} tail, a reload taken for its end */
res = m->res; if(m->res == -2) m->res = 21;
{
struct clientparam *param = m->param;
param->res = m->res;
free(m); free(m);
cbmapfinish(param, res); param->maptail = NULL;
dolog(param, NULL);
releaseparam(param);
freeparam(param);
}
} }
/* sockmapcbfunc: sockmap() of a fiber, as callbacks, its result; -2 for /* sockmapcbfunc: sockmap() of a fiber, as callbacks, its result; -2 for

View File

@ -18,6 +18,20 @@
#define NOSTARTTLS param->srv->nostarttls #define NOSTARTTLS param->srv->nostarttls
#endif #endif
/* as the end below, once mapping is over */
static void pop3ptail(struct clientparam *param, void *data){
unsigned char buf[320];
if(*param->hostname&&*param->extusername) {
sprintf((char *)buf, "%.128s@%.128s%c%hu", param->extusername, param->hostname, (*SAPORT(&param->sinsr)==110)?0:':', ntohs(*SAPORT(&param->sinsr)));
dolog(param, buf);
}
else dolog(param, NULL);
if(param->clisock != INVALID_SOCKET) {
if ((param->res > 0 && param->res < 100) || (param->res > 611 && param->res <700)) socksend(param, param->clisock, (unsigned char *)"-ERR\r\n", 6,param->srv->timeouts[STRING_S]);
}
}
void * pop3pchild(struct clientparam* param) { void * pop3pchild(struct clientparam* param) {
int i=0, res; int i=0, res;
unsigned char buf[320]; unsigned char buf[320];
@ -65,6 +79,7 @@ void * pop3pchild(struct clientparam* param) {
{RETURN(623);} {RETURN(623);}
param->statscli64 += (uint64_t)(strlen((char *)param->extusername) + 7); param->statscli64 += (uint64_t)(strlen((char *)param->extusername) + 7);
param->nwrites++; param->nwrites++;
param->maptail = pop3ptail;
RETURN (mapsocket(param, 180)); RETURN (mapsocket(param, 180));
CLEANRET: CLEANRET:

View File

@ -493,6 +493,50 @@ void file2url(unsigned char *sb, unsigned char *buf, unsigned bufsize, int * inb
} }
/* The status page the client is sent for the result in param->res, where
one is: the rest of a request whose headers were not all read is read
first. */
static void proxystatus(struct clientparam *param, int hdrsdone, char *ftpbuf, int inftpbuf){
int stidx = -1, i;
int r = param->res;
if(r == 555 || !r || param->clisock == INVALID_SOCKET || !(r < 90 || r >=800 || r == 100 ||(r > 500 && r< 800)))
return;
if(!hdrsdone && ((r >= 509 && r < 517) || r > 900)) {
unsigned char rest[256];
int atstart = 1;
while((i = sockgetlinebuf(param, CLIENT, rest, sizeof(rest) - 1, '\n', param->srv->timeouts[STRING_S])) > 0){
if(atstart && i <= 2 && rest[i-1] == '\n') break;
atstart = (rest[i-1] == '\n');
}
}
if(r == 10) stidx = 2;
else if(r == 700 || r == 701) stidx = 16;
else if(r == 100 || (r > 10 && r < 20) || (r > 701 && r <= 705)) stidx = 1;
else if(r >= 20 && r < 30) stidx = 6;
else if(r >= 30 && r < 80) stidx = 5;
else if(r == 1 || (!param->srv->needuser && r < 10)) stidx = 11;
else if(r < 10) stidx = 7;
else if(r == 999) stidx = 4;
else if(r == 519) stidx = 3;
else if(r == 517) stidx = 15;
else if(r == 780) stidx = 10;
else if(r >= 511 && r <= 516) stidx = 0;
if(stidx >= 0) {
send_st(param, stidx);
if((r == 700 || r == 701) && ftpbuf) socksend(param, param->clisock, (unsigned char *)ftpbuf, inftpbuf, param->srv->timeouts[STRING_S]);
}
}
/* the end of a CONNECT, as proxychild()'s, once mapping is over: data is
its request line */
static void proxytail(struct clientparam *param, void *data){
proxystatus(param, 1, NULL, 0);
logurl(param, (char *)data, 0);
}
void * proxychild(struct clientparam* param) { void * proxychild(struct clientparam* param) {
int res=0, i=0; int res=0, i=0;
unsigned char* buf = NULL, *newbuf; unsigned char* buf = NULL, *newbuf;
@ -1250,6 +1294,10 @@ for(;;){
return (void *)param->redirectfunc; return (void *)param->redirectfunc;
} }
hdrfree(&buf, &bufown); hdrfree(&buf, &bufown);
/* mapping its last thing, but in a request httpsrv hands over, whose
end is httpsrv's: the rest of the end can be a loop's */
if(!param->onerequest && (param->maptaildata = req? (void *)strdup((char *)req) : NULL))
param->maptail = proxytail;
param->res = mapsocket(param, param->srv->timeouts[CONNECTION_L]); param->res = mapsocket(param, param->srv->timeouts[CONNECTION_L]);
RETURN(param->res); RETURN(param->res);
} }
@ -1617,38 +1665,7 @@ REQUESTEND:
CLEANRET: CLEANRET:
if(param->res != 555 && param->res && param->clisock != INVALID_SOCKET && (param->res < 90 || param->res >=800 || param->res == 100 ||(param->res > 500 && param->res< 800))) { proxystatus(param, hdrsdone, ftpbuf, inftpbuf);
int stidx = -1;
int r = param->res;
/* the rest of a request whose headers were not all read */
if(!hdrsdone && ((r >= 509 && r < 517) || r > 900)) {
unsigned char rest[256];
int atstart = 1;
while((i = sockgetlinebuf(param, CLIENT, rest, sizeof(rest) - 1, '\n', param->srv->timeouts[STRING_S])) > 0){
if(atstart && i <= 2 && rest[i-1] == '\n') break;
atstart = (rest[i-1] == '\n');
}
}
if(r == 10) stidx = 2;
else if(r == 700 || r == 701) stidx = 16;
else if(r == 100 || (r > 10 && r < 20) || (r > 701 && r <= 705)) stidx = 1;
else if(r >= 20 && r < 30) stidx = 6;
else if(r >= 30 && r < 80) stidx = 5;
else if(r == 1 || (!param->srv->needuser && r < 10)) stidx = 11;
else if(r < 10) stidx = 7;
else if(r == 999) stidx = 4;
else if(r == 519) stidx = 3;
else if(r == 517) stidx = 15;
else if(r == 780) stidx = 10;
else if(r >= 511 && r <= 516) stidx = 0;
if(stidx >= 0) {
send_st(param, stidx);
if(r == 700 || r == 701) socksend(param, param->clisock, (unsigned char *)ftpbuf, inftpbuf, param->srv->timeouts[STRING_S]);
}
}
logurl(param, (char *)req, ftp); logurl(param, (char *)req, ftp);
freeptr(&req); hdrfree(&buf, &bufown); freeptr(&ftpbase); freeptr(&req); hdrfree(&buf, &bufown); freeptr(&ftpbase);
return (NULL); return (NULL);

View File

@ -114,6 +114,25 @@ int readdata (struct clientparam* param) {
} }
/* as the end below, once mapping is over */
static void smtpptail(struct clientparam *param, void *data){
unsigned char buf[320];
if(*param->hostname&&*param->extusername) {
sprintf((char *)buf, "%.128s@%.128s%c%hu", param->extusername, param->hostname, *SAPORT(&param->sinsr)==587?0:':',ntohs(*SAPORT(&param->sinsr)));
dolog(param, buf);
}
else dolog(param, NULL);
if(param->clisock != INVALID_SOCKET) {
if ((param->res > 0 && param->res < 100) || (param->res > 661 && param->res <700)) socksend(param, param->clisock, (unsigned char *)"571 \r\n", 6,param->srv->timeouts[STRING_S]);
}
}
/* mapping the last thing: what is not needed any more freed, the end left
to the tail */
#define SMTPLASTMAP { if(command){ free(command); command = NULL; } param->maptail = smtpptail; \
RETURN (mapsocket(param, 180)); }
void * smtppchild(struct clientparam* param) { void * smtppchild(struct clientparam* param) {
int i=0, res; int i=0, res;
unsigned char buf[320]; unsigned char buf[320];
@ -309,7 +328,7 @@ void * smtppchild(struct clientparam* param) {
if(param->nhdrfilterscli || param->nhdrfilterssrv || param->ndatfilterscli || param->ndatfilterssrv){ if(param->nhdrfilterscli || param->nhdrfilterssrv || param->ndatfilterscli || param->ndatfilterssrv){
do { do {
if(res == 22) RETURN (mapsocket(param, 180)); if(res == 22) SMTPLASTMAP
if(res != 2 && (res = readreply(param)) <= 0) break; if(res != 2 && (res = readreply(param)) <= 0) break;
if(res == 221) RETURN(0); if(res == 221) RETURN(0);
if(res == 354) res = readdata(param); if(res == 354) res = readdata(param);
@ -323,7 +342,7 @@ void * smtppchild(struct clientparam* param) {
#endif #endif
RETURN (mapsocket(param, 180)); SMTPLASTMAP
CLEANRET: CLEANRET:

View File

@ -30,6 +30,14 @@ static void printcommand(unsigned char * buf, int command, struct clientparam *p
} }
/* the end of a CONNECT, as below, once mapping is over */
static void sockstail(struct clientparam *param, void *data){
unsigned char logline[340];
printcommand(logline, 1, param);
dolog(param, logline);
}
void * sockschild(struct clientparam* param) { void * sockschild(struct clientparam* param) {
int res; int res;
unsigned i=0; unsigned i=0;
@ -347,6 +355,13 @@ fflush(stderr);
if(buf != param->tmpbuf) free(buf); if(buf != param->tmpbuf) free(buf);
return (void *)param->redirectfunc; return (void *)param->redirectfunc;
} }
/* the request is not needed any more: its end is
the log line, which mapping may leave to a loop */
if(buf != param->tmpbuf){
free(buf);
buf = param->tmpbuf;
}
param->maptail = sockstail;
param->res = mapsocket(param, param->srv->timeouts[CONNECTION_L]); param->res = mapsocket(param, param->srv->timeouts[CONNECTION_L]);
break; break;
case 2: case 2: