diff --git a/src/mainclient.c b/src/mainclient.c index 512bd8f..17e40d1 100644 --- a/src/mainclient.c +++ b/src/mainclient.c @@ -161,6 +161,7 @@ static void serveparam(struct clientparam *param){ static void freeworker(struct clientparam *param){ releaseparam(param); _3proxy_sem_destroy(param->wake); + buffree(param); free(param); } @@ -286,8 +287,16 @@ static int srvspawn(struct srvstart *s, SOCKET client_sock){ } _3proxy_mutex_unlock(&tpool.mutex); reused = newparam != NULL; - if(!reused && (!(newparam = malloc (sizeof(*defparam))) || (!job && _3proxy_sem_init_timed(newparam->wake, 0, 1)))){ - if(newparam) free(newparam); + if(!reused && (newparam = malloc (sizeof(*defparam)))){ + newparam->clibufdata = newparam->srvbufdata = NULL; + newparam->clibuf = newparam->srvbuf = NULL; + if(bufalloc(newparam) || (!job && _3proxy_sem_init_timed(newparam->wake, 0, 1))){ + buffree(newparam); + free(newparam); + newparam = NULL; + } + } + if(!newparam){ if(job) free(job); srv->so._closesocket(srv->so.state, client_sock); defparam->res = 21; @@ -480,6 +489,7 @@ void releaseparam(struct clientparam * param) { void freeparam(struct clientparam * param) { releaseparam(param); + buffree(param); free(param); } diff --git a/src/mainloop.c b/src/mainloop.c index 99c4fd4..f39104a 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -478,6 +478,19 @@ static void cbbuf(struct clientparam *param, int side, unsigned char **buf, unsi } } +/* The side's own buffer given back while it is empty, once nothing but the + relay can use it: the client's fiber gave up its stack. The next read + makes it again. */ +static void cbbufdrop(struct cbmap *m, int side){ + struct clientparam *param = m->param; + unsigned char **own = side? ¶m->srvbufdata : ¶m->clibufdata; + unsigned char **buf = side? ¶m->srvbuf : ¶m->clibuf; + + if(!m->abandoned || !*own || *buf != *own || (side? param->srvinbuf : param->cliinbuf)) return; + free(*own); + *own = *buf = NULL; +} + /* what a way has buffered and in its pipe: none once it is not moving */ static uint64_t cbheld(struct cbmap *m, struct cbway *w){ unsigned char *buf; @@ -620,6 +633,15 @@ static int cbway(struct cbmap *m, struct cbway *w){ #endif if(w->left > inb && !m->term_r[w->src] && !end && room > 0 && ((rs->ready & (POLLIN | POLLHUP | POLLERR)) || cbpending(m, w->src))){ + /* given back while empty: made again */ + if(!buf){ + if(bufget(param, w->src? SERVER : CLIENT)){ + m->stop = 1; + m->stopres = 21; + return 1; + } + cbbuf(param, w->src, &buf, &inbuf, &offset, &size); + } n = sf->_recvfrom(param->sostate, from, (char *)buf + *inbuf, (int)CBMIN(room, w->left - inb), 0, NULL, NULL); if(n > 0){ *inbuf += n; @@ -684,6 +706,7 @@ static int cbway(struct cbmap *m, struct cbway *w){ m->term_w[w->dst] = 1; sf->_shutdown(param->sostate, to, SHUT_WR); } + cbbufdrop(m, w->src); return progress; } @@ -859,6 +882,16 @@ static void cbmapfinish(void *arg){ int res = m->res, timeo = m->timeosec, usesplice = m->usesplice0; free(m); + /* the buffers given back while relayed are the child's again: without + them only the end is logged */ + if(bufalloc(param)){ + param->res = (res == -2)? 21 : res; + param->maptail = NULL; + dolog(param, NULL); + releaseparam(param); + freeparam(param); + return; + } if(res == -2) res = sockmap(param, timeo, usesplice); param->res = res; if(param->maptail) param->maptail(param, param->maptaildata); @@ -985,6 +1018,8 @@ static int fibermap(struct clientparam *param, int timeo, int usesplice){ m->abandoned = 1; ((struct fiberslot *)m->f)->abandoned = 1; m->f = NULL; + cbbufdrop(m, 0); + cbbufdrop(m, 1); } fiberyield(); /* resumed only if not abandoned */ diff --git a/src/mainudp.c b/src/mainudp.c index fe21368..5e65b63 100644 --- a/src/mainudp.c +++ b/src/mainudp.c @@ -145,6 +145,13 @@ static struct udpsession *udpnew(struct udpstate *st){ return NULL; } memset(param, 0, sizeof(*param)); + if(bufalloc(param)){ + buffree(param); + free(param); + free(ss); + _3proxy_sem_unlock(srv->childslots); + return NULL; + } memcpy(param, st->u.defparam, offsetof(struct clientparam, filters)); param->clibuf = param->clibufdata; param->srvbuf = param->srvbufdata; diff --git a/src/proxy.h b/src/proxy.h index c2967bd..6a2948b 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -359,6 +359,9 @@ int paramstr(unsigned char *field, size_t size, const unsigned char *s); /* sets one of a connection's names or credentials; 1 if it is too long */ #define PARAMSTR(p, f, s) paramstr((p)->f, sizeof((p)->f), (const unsigned char *)(s)) void bufrelease(struct clientparam * param, DIRECTION which); +int bufget(struct clientparam * param, DIRECTION which); +int bufalloc(struct clientparam * param); +void buffree(struct clientparam * param); int sockgetlinebuf(struct clientparam * param, DIRECTION which, unsigned char * buf, int bufsize, int delim, int to); int getmultiline(struct clientparam * param, DIRECTION which, unsigned char * buf, int bufsize, const char *cap, int *found); diff --git a/src/proxymain.c b/src/proxymain.c index 4cdeb83..6688a5b 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -260,11 +260,19 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int if(! (newparam = malloc (sizeof(s.defparam)))){ return 2; }; + newparam->clibufdata = newparam->srvbufdata = NULL; + newparam->clibuf = newparam->srvbuf = NULL; + if(bufalloc(newparam)){ + buffree(newparam); + free(newparam); + return 2; + } memcpy(newparam, &s.defparam, offsetof(struct clientparam, filters)); newparam->clibuf = newparam->clibufdata; newparam->srvbuf = newparam->srvbufdata; newparam->clibufsize = newparam->srvbufsize = SRVBUFSIZE(&s.srv); childfunc(newparam); + buffree(newparam); free(newparam); return 0; diff --git a/src/sockgetchar.c b/src/sockgetchar.c index c803da6..2dd5744 100644 --- a/src/sockgetchar.c +++ b/src/sockgetchar.c @@ -77,6 +77,31 @@ int sockrecvfrom(struct clientparam *param, SOCKET sock, struct sockaddr * sin, } } +/* The side's own buffer, made if it is not there, and the one used if + none is. 1 if it cannot be had. */ +int bufget(struct clientparam * param, DIRECTION which){ + unsigned char **own = which? ¶m->srvbufdata : ¶m->clibufdata; + unsigned char **buf = which? ¶m->srvbuf : ¶m->clibuf; + + if(!*own && !(*own = malloc(CLIENTBUFSIZE))) return 1; + if(!*buf) *buf = *own; + return 0; +} + +/* Both sides' buffers, as bufget(). */ +int bufalloc(struct clientparam * param){ + return bufget(param, CLIENT) || bufget(param, SERVER); +} + +/* The own buffers freed, with the structure. */ +void buffree(struct clientparam * param){ + if(param->clibuf == param->clibufdata) param->clibuf = NULL; + if(param->srvbuf == param->srvbufdata) param->srvbuf = NULL; + free(param->clibufdata); + free(param->srvbufdata); + param->clibufdata = param->srvbufdata = NULL; +} + /* Starts an empty buffer over from its beginning, and goes back to the connection's own buffer if a filter needed a bigger one. Does nothing while anything is left unread. */ diff --git a/src/structures.h b/src/structures.h index 4003064..8e9b39e 100644 --- a/src/structures.h +++ b/src/structures.h @@ -963,10 +963,12 @@ struct clientparam { something done at once, never for what has to outlive a call into anything that could log. */ unsigned char tmpbuf[CLIENTTMPBUFSIZE]; - /* Each side's data buffer, which clibuf and srvbuf point to unless a - filter needed a bigger one. */ - unsigned char clibufdata[CLIENTBUFSIZE], - srvbufdata[CLIENTBUFSIZE]; + /* Each side's data buffer, CLIENTBUFSIZE of malloc's, which clibuf and + srvbuf point to unless a filter needed a bigger one. Made with the + connection (bufalloc()); a loop's relay gives an empty one back while + nothing but the relay can use it, and makes it again to read. */ + unsigned char *clibufdata, + *srvbufdata; }; /* Threads whose connections have ended wait here for the next one, with