From 8c2ad56665c874d20269440ff24d7226a0c24787 Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Sat, 8 Aug 2026 20:46:52 +0300 Subject: [PATCH] Use memmove for overlapping region --- man/3proxy.cfg.5 | 7 +++++++ src/proxymain.c | 6 ++++++ src/sockgetchar.c | 5 +++-- src/sockmap.c | 23 +++++++++++++++-------- src/structures.h | 1 + 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/man/3proxy.cfg.5 b/man/3proxy.cfg.5 index ff46ba3..b3572e4 100644 --- a/man/3proxy.cfg.5 +++ b/man/3proxy.cfg.5 @@ -199,6 +199,13 @@ is disabled even when built, because current Linux does not implement SPLICE_F_MOVE, so no real zero-copy takes place and the read/write path is faster for most traffic. Rebuild with -DWITHSPLICE to make -s available, -s0 disables it explicitly. +.br +.B -C + (for TCP services) keep the session until both sides close the connection +(TCP half-close). By default a connection closed by any of the sides terminates +the session, buffered data is delivered before the sockets are closed. Half-close +is required for the protocols where one of the sides closes its sending side and +expects the answer, it may cause the sockets to be kept in CLOSE_WAIT state. .br (for dnspr) simple, do not use resolver and 3proxy cache, always use external DNS server. .br diff --git a/src/proxymain.c b/src/proxymain.c index cdd0680..00f841d 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -329,6 +329,8 @@ int MODULEMAINFUNC (int argc, char** argv){ #endif #ifdef WITHSPLICE " -s Use splice() - no filtering for data, off by default\n" + " -C keep the session until both sides close the connection (TCP half-close),\n" + " by default connection closed by any of the sides terminates the session\n" #endif "-g(GRACE_TRAFF,GRACE_NUM,GRACE_DELAY) - delay GRACE_DELAY milliseconds before polling if average polling size below GRACE_TRAFF bytes and GRACE_NUM read operations in single directions are detected within 1 second to minimize polling\n" " -fFORMAT logging format (see documentation)\n" @@ -596,6 +598,9 @@ int MODULEMAINFUNC (int argc, char** argv){ case 'g': sscanf(argv[i]+2, "%d,%d,%d", &srv.gracetraf, &srv.gracenum, &srv.gracedelay); break; + case 'C': + srv.halfclose = *(argv[i]+2)? atoi(argv[i]+2) : 1; + break; case 's': #ifdef WITHSPLICE if(isudp || srv.service == S_ADMIN) @@ -1247,6 +1252,7 @@ void srvinit(struct srvparam * srv, struct clientparam *param){ #ifdef WITHSPLICE srv->usesplice = 0; #endif + srv->halfclose = 0; memset(param, 0, sizeof(struct clientparam)); param->srv = srv; param->version = srv->version; diff --git a/src/sockgetchar.c b/src/sockgetchar.c index a7c2d85..cd76351 100644 --- a/src/sockgetchar.c +++ b/src/sockgetchar.c @@ -171,12 +171,13 @@ int sockgetlinebuf(struct clientparam * param, DIRECTION which, unsigned char * if(delim != EOF){ unsigned char *d = (unsigned char *)memchr(base + *offp, delim, n); if(d) n = (int)(d - (base + *offp)) + 1; - memcpy(buf + i, base + *offp, n); + /* caller may use param->srvbuf / param->clibuf as buf */ + memmove(buf + i, base + *offp, n); i += n; *offp += n; if(d || i >= bufsize) return i; } else { - memcpy(buf + i, base + *offp, n); + memmove(buf + i, base + *offp, n); i += n; *offp += n; if(i >= bufsize) return i; } diff --git a/src/sockmap.c b/src/sockmap.c index 02e8b96..df7ad16 100644 --- a/src/sockmap.c +++ b/src/sockmap.c @@ -44,7 +44,13 @@ ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t le #define MIN(a,b) ((a>b)?b:a) #define RETURN(xxx) { res = xxx; goto CLEANRET; } +/* Unless half-close is requested for the service, a connection closed by any + of the sides terminates the session, remaining buffered data is delivered + before the sockets are closed. */ +#define SESSIONEND (!halfclose && (CLIENTTERMREAD || SERVERTERMREAD)) + int sockmap(struct clientparam * param, int timeo, int usesplice){ + int halfclose = param->srv->halfclose; uint64_t fromclient=0x7fffffffffffffff, fromserver =0x7fffffffffffffff; uint64_t inclientbuf = 0, inserverbuf = 0; int FROMCLIENT = 1, TOCLIENTBUF = 1, TOSERVER = 1, @@ -129,13 +135,13 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){ #ifdef WITHSPLICE || inserverpipe #endif - || (!SERVERTERMREAD ))) + || (!SERVERTERMREAD && !SESSIONEND))) || ((!SERVERTERMWRITE) && fromclient && (inclientbuf #ifdef WITHSPLICE || inclientpipe #endif - || (!CLIENTTERMREAD ))) + || (!CLIENTTERMREAD && !SESSIONEND))) ){ @@ -354,7 +360,7 @@ log(logbuf); } } } - if(fromclient>inclientpipe && FROMCLIENT && TOCLIENTPIPE){ + if(fromclient>inclientpipe && FROMCLIENT && TOCLIENTPIPE && !SESSIONEND){ int error; socklen_t len=sizeof(error); #ifdef WITHLOG @@ -396,7 +402,7 @@ log("done read from client to pipe"); continue; } } - if(fromserver > inserverpipe && FROMSERVER && TOSERVERPIPE){ + if(fromserver > inserverpipe && FROMSERVER && TOSERVERPIPE && !SESSIONEND){ int error; socklen_t len=sizeof(error); errno = 0; @@ -449,7 +455,7 @@ log("done read from server to pipe\n"); else #endif { - if(fromclient > inclientbuf && FROMCLIENT && TOCLIENTBUF){ + if(fromclient > inclientbuf && FROMCLIENT && TOCLIENTBUF && !SESSIONEND){ #ifdef WITHLOG log("read from client to buf"); #endif @@ -482,7 +488,7 @@ log("done read from client to buf"); } } - if(fromserver > inserverbuf && FROMSERVER && TOSERVERBUF){ + if(fromserver > inserverbuf && FROMSERVER && TOSERVERBUF && !SESSIONEND){ #ifdef WITHLOG log("read from server to buf"); #endif @@ -547,7 +553,7 @@ log("done read from server to buf"); // if(!CLIENTTERMREAD || !CLIENTTERMWRITE){ if(!after){ fds[fdsc].fd = param->clisock; - if(fromclient && !CLIENTTERMREAD && !FROMCLIENT && (( + if(fromclient && !CLIENTTERMREAD && !FROMCLIENT && !SESSIONEND && (( #ifdef WITHSPLICE !usesplice && #endif @@ -607,7 +613,7 @@ log("ready to write to client"); // if(!SERVERTERMREAD || !SERVERTERMWRITE){ if(!after){ fds[fdsc].fd = param->remsock; - if(fromserver && !SERVERTERMREAD && !FROMSERVER && (( + if(fromserver && !SERVERTERMREAD && !FROMSERVER && !SESSIONEND && (( #ifdef WITHSPLICE !usesplice && #endif @@ -827,3 +833,4 @@ CLEANRET: return res; } +#undef SESSIONEND diff --git a/src/structures.h b/src/structures.h index 3f471ab..8d7eeb2 100644 --- a/src/structures.h +++ b/src/structures.h @@ -551,6 +551,7 @@ struct srvparam { #ifdef WITHSPLICE int usesplice; #endif + int halfclose; unsigned bufsize; unsigned authcachetype, authcachetime; unsigned logdumpsrv, logdumpcli;