From 6696b35d74c016031e425305dba7ae060c1dab8a Mon Sep 17 00:00:00 2001 From: z3apa3a <3proxy@3proxy.ru> Date: Mon, 19 Dec 2016 02:56:23 +0300 Subject: [PATCH] Added -s option support for proxying with splice() for Liux (without copying network data to userspace). Currently only for tcppm. --- Makefile.Linux | 2 +- src/proxy.h | 8 ++ src/proxymain.c | 8 +- src/sockmap.c | 215 +++++++++++++++++++++++++++++++++++++++++++++++ src/structures.h | 3 + src/tcppm.c | 4 +- 6 files changed, 236 insertions(+), 4 deletions(-) diff --git a/Makefile.Linux b/Makefile.Linux index 83c5dc7..7f7b42a 100644 --- a/Makefile.Linux +++ b/Makefile.Linux @@ -10,7 +10,7 @@ BUILDDIR = CC = gcc -CFLAGS = -g -O2 -c -pthread -DGETHOSTBYNAME_R -D_THREAD_SAFE -D_REENTRANT -DNOODBC -DWITH_STD_MALLOC -DFD_SETSIZE=4096 -DWITH_POLL +CFLAGS = -g -O2 -c -pthread -DWITHSPLICE -DGETHOSTBYNAME_R -D_THREAD_SAFE -D_REENTRANT -DNOODBC -DWITH_STD_MALLOC -DFD_SETSIZE=4096 -DWITH_POLL COUT = -o LN = gcc DCFLAGS = -fpic diff --git a/src/proxy.h b/src/proxy.h index 9c1cdf7..8c334e6 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -150,6 +150,7 @@ extern int timetoexit; extern struct extparam conf; int sockmap(struct clientparam * param, int timeo); +int splicemap(struct clientparam * param, int timeo); int socksend(SOCKET sock, unsigned char * buf, int bufsize, int to); int socksendto(SOCKET sock, struct sockaddr * sin, unsigned char * buf, int bufsize, int to); int sockrecvfrom(SOCKET sock, struct sockaddr * sin, unsigned char * buf, int bufsize, int to); @@ -318,6 +319,13 @@ extern struct datatype datatypes[64]; extern struct commands commandhandlers[]; +#ifdef WITHSPLICE +#define mapsocket(a,b) (a->srv->usesplice?splicemap(a,b):sockmap(a,b)) +#else +#define mapsocket(a,b) sockmap(a,b) +#endif + + #ifdef _WINCE char * CEToUnicode (const char *str); int cesystem(const char *str); diff --git a/src/proxymain.c b/src/proxymain.c index d6c1a37..69d0581 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -121,6 +121,9 @@ int MODULEMAINFUNC (int argc, char** argv){ #else " -u never ask for username\n" " -u2 always ask for username\n" +#endif +#ifdef WITHSLICE + " -s Use slice() - faster proxing, but no filtering for data\n" #endif " -fFORMAT logging format (see documentation)\n" " -l log to stderr\n" @@ -303,7 +306,10 @@ int MODULEMAINFUNC (int argc, char** argv){ break; case 's': case 'a': - srv.singlepacket = 1 + atoi(argv[i]+2); + if(isudp) + srv.singlepacket = 1 + atoi(argv[i]+2); + else + srv.usesplice = 1 + atoi(argv[i]+2); break; default: error = 1; diff --git a/src/sockmap.c b/src/sockmap.c index a2cd9b2..a7d36c5 100644 --- a/src/sockmap.c +++ b/src/sockmap.c @@ -10,6 +10,221 @@ #define BUFSIZE (param->srv->bufsize?param->srv->bufsize:((param->service == S_UDPPM)?UDPBUFSIZE:TCPBUFSIZE)) +#ifdef WITHSPLICE + +#include +ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags); +#ifndef SPLICE_F_MOVE +#define SPLICE_F_MOVE 0x01 +#endif +#ifndef SPLICE_F_NONBLOCK +#define SPLICE_F_NONBLOCK 0x02 +#endif +#ifndef SPLICE_F_MORE +#define SPLICE_F_MORE 0x04 +#endif +#ifndef SPLICE_F_GIFT +#define SPLICE_F_GIFT 0x08 +#endif + +#define RETURN(xxx) { param->res = xxx; goto CLEANRET; } +#define MIN(a,b) ((a>b)?b:a) + +#define MAXSPLICE 65536 + +int splicemap(struct clientparam * param, int timeo){ + struct pollfd fds[2]; + int pipesrv[2] = {-1,-1}; + int pipecli[2] = {-1,-1}; + uint64_t sent=0, received=0; + int res = 0, stop = 0; + int srvstate = 0, clistate = 0; + int insrvpipe = 0, inclipipe = 0; + int rfromserver = 0, rfromclient = 0; + int sleeptime = 0; + + + param->res = 0; + if(pipe(pipecli) < 0) RETURN(21); + if(pipe(pipesrv) < 0) RETURN(21); + + fds[0].fd = param->clisock; + fds[1].fd = param->remsock; + + while(!stop && !conf.timetoexit){ + + fds[0].events = fds[1].events = 0; + + if(srvstate && !param->waitclient64){ +#if DEBUGLEVEL > 2 +(*param->srv->logfunc)(param, "splice: will send to client"); +#endif + fds[0].events |= POLLOUT; + } + rfromserver = MAXSPLICE; + if(param->waitserver64) rfromserver = MIN(MAXSPLICE, param->waitserver64 - (received + insrvpipe)); + if(srvstate < 2 && rfromserver > 0) { +#if DEBUGLEVEL > 2 +(*param->srv->logfunc)(param, "splice: will recv from server"); +#endif + fds[1].events |= POLLIN; + } + if(clistate && !param->waitserver64){ +#if DEBUGLEVEL > 2 +(*param->srv->logfunc)(param, "splice: will send to server"); +#endif + fds[1].events |= POLLOUT; + } + rfromclient = MAXSPLICE; + if(param->waitclient64) rfromclient = MIN(MAXSPLICE, param->waitclient64 - (sent + inclipipe)); + if(clistate < 2 && rfromclient > 0) { +#if DEBUGLEVEL > 2 +(*param->srv->logfunc)(param, "splice :will recv from client"); +#endif + fds[0].events |= POLLIN; + } + if(!fds[0].events && !fds[1].events) RETURN (666); + res = so._poll(fds, 2, timeo*1000); + if(res < 0){ + if(errno != EAGAIN && errno != EINTR) RETURN(91); + if(errno == EINTR) usleep(SLEEPTIME); + continue; + } + if(res < 1){ + RETURN(92); + } + if( (fds[0].revents & (POLLERR|POLLHUP|POLLNVAL)) && !(fds[0].revents & POLLIN)) { + fds[0].revents = 0; + stop = 1; + param->res = 90; + } + if( (fds[1].revents & (POLLERR|POLLHUP|POLLNVAL)) && !(fds[1].revents & POLLIN)){ + fds[1].revents = 0; + stop = 1; + param->res = 90; + } + if((fds[0].revents & POLLOUT)){ +#if DEBUGLEVEL > 2 +(*param->srv->logfunc)(param, "splice: send to client"); +#endif + res = splice(pipesrv[0], NULL, param->clisock, NULL, MIN(MAXSPLICE, insrvpipe), SPLICE_F_NONBLOCK | SPLICE_F_MORE | SPLICE_F_MOVE); + if(res < 0) { + if(errno != EAGAIN && errno != EINTR) RETURN(96); + if(errno == EINTR) usleep(SLEEPTIME); + continue; + } + if(res){ + insrvpipe -= res; + received += res; + + if(param->bandlimfunc) { + sleeptime = (*param->bandlimfunc)(param, res, 0); + } + srvstate = 0; + } + else srvstate = 2; + if(param->waitserver64 && param->waitserver64 <= received){ + RETURN (98); + } + } + if((fds[1].revents & POLLOUT)){ +#if DEBUGLEVEL > 2 +(*param->srv->logfunc)(param, "splice: send to server"); +#endif + res = splice(pipecli[0], NULL, param->remsock, NULL, MIN(MAXSPLICE, inclipipe), SPLICE_F_NONBLOCK | SPLICE_F_MORE | SPLICE_F_MOVE); + if(res < 0) { + if(errno != EAGAIN && errno != EINTR) RETURN(97); + if(errno == EINTR) usleep(SLEEPTIME); + continue; + } + if(res){ + inclipipe -= res; + sent += res; + param->nwrites++; + param->statscli64 += res; + + if(param->bandlimfunc) { + int sl1; + sl1 = (*param->bandlimfunc)(param, 0, res); + if(sl1 > sleeptime) sleeptime = sl1; + } + clistate = 0; + } + else clistate = 2; + if(param->waitclient64 && param->waitclient64 <= sent){ + RETURN (99); + } + } + if ((fds[0].revents & POLLIN)) { +#if DEBUGLEVEL > 2 +(*param->srv->logfunc)(param, "splice: recv from client"); +#endif + res = splice(param->clisock, NULL, pipecli[1], NULL, rfromclient, SPLICE_F_NONBLOCK | SPLICE_F_MORE | SPLICE_F_MOVE); + if (res < 0){ + if(errno != EAGAIN && errno != EINTR) RETURN(94); + if(errno == EINTR) usleep(SLEEPTIME); + continue; + } + if (res==0) { + so._shutdown(param->clisock, SHUT_RDWR); + so._closesocket(param->clisock); + fds[0].fd = param->clisock = INVALID_SOCKET; + stop = 1; + } + else { + inclipipe += res; + clistate = 1; + if(insrvpipe >= MAXSPLICE) clistate = 2; + } + } + if ((fds[1].revents & POLLIN)) { +#if DEBUGLEVEL > 2 +(*param->srv->logfunc)(param, "splice: recv from server"); +#endif + res = splice(param->remsock, NULL, pipesrv[1], NULL, rfromserver, SPLICE_F_NONBLOCK | SPLICE_F_MORE | SPLICE_F_MOVE); + if (res < 0){ + if(errno != EAGAIN && errno != EINTR) RETURN(93); + if(errno == EINTR) usleep(SLEEPTIME); + continue; + } + if (res==0) { + so._shutdown(param->remsock, SHUT_RDWR); + so._closesocket(param->remsock); + fds[1].fd = param->remsock = INVALID_SOCKET; + stop = 2; + } + else { + insrvpipe += res; + param->statssrv64 += res; + param->nreads++; + srvstate = 1; + if(insrvpipe >= MAXSPLICE) srvstate = 2; + } + } + if(sleeptime > 0) { + if(sleeptime > (timeo * 1000)){RETURN (95);} + usleep(sleeptime * SLEEPTIME); + sleeptime = 0; + } + } + +#if DEBUGLEVEL > 2 +(*param->srv->logfunc)(param, "splice: finished with mapping"); +#endif + +CLEANRET: + + if(pipecli[0] >= 0) close(pipecli[0]); + if(pipecli[1] >= 0) close(pipecli[1]); + if(pipesrv[0] >= 0) close(pipesrv[0]); + if(pipesrv[1] >= 0) close(pipesrv[1]); + + return param->res; +} + +#endif + + int sockmap(struct clientparam * param, int timeo){ int res=0; uint64_t sent=0, received=0; diff --git a/src/structures.h b/src/structures.h index 6b96d60..558269c 100644 --- a/src/structures.h +++ b/src/structures.h @@ -388,6 +388,9 @@ struct srvparam { int family; int stacksize; int noforce; +#ifdef WITHSPLICE + int usesplice; +#endif unsigned bufsize; unsigned logdumpsrv, logdumpcli; #ifndef NOIPV6 diff --git a/src/tcppm.c b/src/tcppm.c index 62cdf56..e7faaca 100644 --- a/src/tcppm.c +++ b/src/tcppm.c @@ -20,7 +20,7 @@ void * tcppmchild(struct clientparam* param) { param->operation = CONNECT; res = (*param->srv->authfunc)(param); if(res) {RETURN(res);} - RETURN (sockmap(param, conf.timeouts[CONNECTION_L])); + RETURN (mapsocket(param, conf.timeouts[CONNECTION_L])); CLEANRET: (*param->srv->logfunc)(param, NULL); @@ -34,7 +34,7 @@ struct proxydef childdef = { 0, 0, S_TCPPM, - "" + " -s use splice() (Fast proxying but no filtering)\n" }; #include "proxymain.c" #endif