3proxy/src/sockmap.c
Vladimir Dubrovin baad3f3543
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
Leave a hung-up socket out of poll() rather than pass an invalid one
A socket which hung up and of which nothing is asked was given to poll()
as INVALID_SOCKET, so that poll() would not return at once for the
hang-up every time. poll() ignores a negative descriptor, but SOCKET is
unsigned on Windows, and WSAPoll fails the whole call for it: sockmap()
ended the session as soon as a client half-closed. Such a socket is now
not in the array at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 98fd0f329f)
2026-10-13 13:09:41 +03:00

886 lines
23 KiB
C

/*
3APA3A simplest proxy server
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
please read License Agreement
*/
#include "proxy.h"
#define MAXFAILATTEMPT 10
#ifdef WITHLOG
#if WITHLOG > 1
char logbuf[1024];
#endif
#define log(X) dolog(param,(const unsigned char *)(X))
#else
#define log(X)
#endif
#ifdef WITHSPLICE
#include <fcntl.h>
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 MAXSPLICE 65536
#endif
#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))
/* A hang-up on a socket there is data to write to. With poll() it comes
once both sides are shut down, and the write tried then fails, which ends
it. WSAPoll reports it as soon as the peer has half-closed, a peer still
reading, and keeps reporting it whether the socket has room or not: room
is waited for with select() then, up to timeo seconds. Returns whether to
try the write. */
static int hupwrite(SOCKET s, short revents, int timeo){
#ifdef _WIN32
if(!(revents & POLLOUT)){
fd_set wfd;
struct timeval tv;
FD_ZERO(&wfd);
FD_SET(s, &wfd);
tv.tv_sec = timeo;
tv.tv_usec = 0;
if(select(0, NULL, &wfd, NULL, &tv) < 1) return 0;
}
#endif
return 1;
}
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,
FROMSERVER = 1, TOSERVERBUF = 1, TOCLIENT = 1;
#if WITHLOG > 1
int FROMCLIENTBUF = 1, FROMSERVERBUF = 1;
#endif
int HASERROR=0;
int CLIENTTERMREAD = 0, CLIENTTERMWRITE = 0, SERVERTERMREAD = 0, SERVERTERMWRITE = 0;
/* the side has hung up: closed by the peer after its own was shut down */
int CLIENTHUP = 0, SERVERHUP = 0;
/* whether the side's socket is in this round's poll() */
int clipolled = 1, srvpolled = 1;
int after = 0;
struct pollfd fds[8];
int fdsc = 0;
int sleeptime = 0;
FILTER_ACTION action;
int res = 0;
SASIZETYPE sasize;
int needaction = 0;
int graceclinum=0, gracesrvnum=0, graceclitraf=0, gracesrvtraf=0;
time_t gracetime = 0;
int cli_events = 0;
int srv_events = 0;
#ifdef WITHSPLICE
uint64_t inclientpipe = 0, inserverpipe = 0;
int TOCLIENTPIPE = 0, FROMCLIENTPIPE = 0, TOSERVERPIPE = 0, FROMSERVERPIPE = 0;
int pipesrv[2] = {-1,-1};
int pipecli[2] = {-1,-1};
if((!param->nolongdatfilter && (param->ndatfilterscli > 0 || param->ndatfilterssrv))) usesplice = 0;
if(usesplice){
TOCLIENTPIPE = FROMCLIENTPIPE = TOSERVERPIPE = FROMSERVERPIPE = 1;
TOCLIENTBUF = TOSERVERBUF = 0;
if(pipe2(pipecli, O_NONBLOCK) < 0) RETURN (21);
if(pipe2(pipesrv, O_NONBLOCK) < 0) RETURN (21);
}
#endif
inserverbuf = param->srvinbuf - param->srvoffset;
inclientbuf = param->cliinbuf - param->clioffset;
if(param->waitclient64) {
fromclient = param->waitclient64;
fromserver = 0;
inserverbuf = 0;
TOCLIENT = 0;
FROMSERVER = 0;
}
if(param->waitserver64) {
fromserver = param->waitserver64;
fromclient = 0;
inclientbuf = 0;
TOSERVER = 0;
FROMCLIENT = 0;
}
if(inserverbuf >= fromserver) FROMSERVER = 0;
if(inclientbuf >= fromclient) FROMCLIENT = 0;
#ifdef WITHSPLICE
if(!usesplice)
#endif
{
if(fromserver && !param->srvbuf && (!(param->srvbuf=malloc(SRVBUFSIZE)) || !(param->srvbufsize = SRVBUFSIZE))){
RETURN (21);
}
if(fromclient && !param->clibuf && (!(param->clibuf=malloc(SRVBUFSIZE)) || !(param->clibufsize = SRVBUFSIZE))){
RETURN (21);
}
}
if(param->srvinbuf == param->srvoffset) param->srvinbuf =param->srvoffset = 0;
if(param->cliinbuf == param->clioffset) param->cliinbuf =param->clioffset = 0;
if(param->clibufsize == param->cliinbuf) TOCLIENTBUF = 0;
if(param->srvbufsize == param->srvinbuf) TOSERVERBUF = 0;
action = handlepredatflt(param);
if(action == HANDLED){
RETURN(0);
}
if(action != PASS) RETURN(19);
while(
((!CLIENTTERMWRITE) && fromserver && (inserverbuf
#ifdef WITHSPLICE
|| inserverpipe
#endif
|| (!SERVERTERMREAD && !SESSIONEND)))
||
((!SERVERTERMWRITE) && fromclient && (inclientbuf
#ifdef WITHSPLICE
|| inclientpipe
#endif
|| (!CLIENTTERMREAD && !SESSIONEND)))
){
#if WITHLOG > 1
sprintf(logbuf, "int FROMCLIENT = %d, TOCLIENTBUF = %d, FROMCLIENTBUF = %d, TOSERVER = %d, "
"FROMSERVER = %d, TOSERVERBUF = %d, FROMSERVERBUF = %d, TOCLIENT = %d; inclientbuf=%d; "
"inserverbuf=%d, CLIENTTERMREAD=%d CLIENTTERMWRITE=%d SERVERTERMREAD=%d SERVERTERMWRITE=%d fromserver=%u, fromclient=%u"
#ifdef WITHSPLICE
", inserverpipe=%d, inclentpipe=%d "
"TOCLIENTPIPE=%d FROMCLIENTPIPE==%d TOSERVERPIPE==%d FROMSERVERPIPE=%d"
#endif
,
FROMCLIENT, TOCLIENTBUF, FROMCLIENTBUF, TOSERVER,
FROMSERVER, TOSERVERBUF, FROMSERVERBUF, TOCLIENT,
(int)inclientbuf, (int)inserverbuf, CLIENTTERMREAD, CLIENTTERMWRITE, SERVERTERMREAD, SERVERTERMWRITE,
(unsigned)fromserver, (unsigned)fromclient
#ifdef WITHSPLICE
,(int)inserverpipe, (int)inclientpipe,
TOCLIENTPIPE, FROMCLIENTPIPE, TOSERVERPIPE, FROMSERVERPIPE
#endif
);
log(logbuf);
#endif
if(needaction && param->srv->gracedelay && !sleeptime){
if(gracetime != conf.time){
gracetime = conf.time;
graceclinum=gracesrvnum=graceclitraf=gracesrvtraf=0;
}
else {
if( (graceclinum && graceclitraf && graceclinum>=param->srv->gracenum && (!param->srv->gracetraf || graceclitraf/graceclinum <= param->srv->gracetraf)) ||
(gracesrvnum && gracesrvtraf && gracesrvnum>=param->srv->gracenum && (!param->srv->gracetraf || gracesrvtraf/gracesrvnum <= param->srv->gracetraf))) {
sleeptime = param->srv->gracedelay;
}
}
}
if(needaction > 2 && !sleeptime){
if(needaction > (MAXFAILATTEMPT+1)){RETURN (93);}
sleeptime = (1<<(needaction-2));
}
if(sleeptime > 0) {
if(sleeptime > (timeo * 1000)){RETURN (93);}
memset(fds, 0, sizeof(fds));
fds[0].fd = param->clisock;
fds[1].fd = param->remsock;
param->srv->so._poll(param->sostate, fds, 2, sleeptime);
sleeptime = 0;
}
if((param->srv->logdumpsrv && (param->statssrv64 > param->srv->logdumpsrv)) ||
(param->srv->logdumpcli && (param->statscli64 > param->srv->logdumpcli)))
dolog(param, NULL);
if(param->version < conf.version){
if(!param->srv->noforce && (res = (*param->srv->authfunc)(param)) && res != 2) {RETURN(res);}
param->paused = conf.paused;
param->version = conf.version;
}
if((param->maxtrafin64 && param->statssrv64 >= param->maxtrafin64) || (param->maxtrafout64 && param->statscli64 >= param->maxtrafout64)){
RETURN (10);
}
if(inclientbuf && TOSERVER){
#ifdef WITHLOG
log("send to server from buf");
#endif
if(!param->nolongdatfilter){
action = handledatfltcli(param, &param->clibuf, (int *)&param->clibufsize, param->clioffset, (int *)&param->cliinbuf);
if(action == HANDLED){
RETURN(0);
}
if(action != PASS) RETURN(19);
inclientbuf=param->cliinbuf - param->clioffset;
}
if(!inclientbuf){
param->clioffset = param->cliinbuf = 0;
if(fromclient) TOCLIENTBUF = 1;
}
sasize = SASIZE(&param->sinsr);
res = param->srv->so._sendto(param->sostate, param->remsock, (char *)param->clibuf + param->clioffset, (int)MIN(inclientbuf, fromclient), 0, (struct sockaddr*)&param->sinsr, sasize);
if(res <= 0) {
TOSERVER = 0;
if(errno && errno != EAGAIN && errno != EINTR){
SERVERTERMREAD = 1;
SERVERTERMWRITE = 1;
HASERROR |= 2;
}
}
else {
#ifdef WITHLOG
log("done send to server from buf");
#endif
param->nwrites++;
param->statscli64 += res;
inclientbuf -= res;
fromclient -= res;
param->clioffset += res;
if(param->clioffset == param->cliinbuf)param->clioffset = param->cliinbuf = 0;
if(param->cliinbuf < param->clibufsize) TOCLIENTBUF = 1;
if(param->bandlimfunc) {
int sl1;
sl1 = (*param->bandlimfunc)(param, 0, res);
if(sl1 > sleeptime) sleeptime = sl1;
}
needaction = 0;
continue;
}
}
if(inserverbuf && TOCLIENT){
#ifdef WITHLOG
log("send to client from buf");
#endif
if(!param->nolongdatfilter){
action = handledatfltsrv(param, &param->srvbuf, (int *)&param->srvbufsize, param->srvoffset, (int *)&param->srvinbuf);
if(action == HANDLED){
RETURN(0);
}
if(action != PASS) RETURN(19);
inserverbuf = param->srvinbuf - param->srvoffset;
}
if(!inserverbuf){
param->srvinbuf = param->srvoffset = 0;
if(fromserver) TOSERVERBUF = 1;
continue;
}
sasize = SASIZE(&param->sincr);
res = param->srv->so._sendto(param->sostate, param->clisock, (char *)param->srvbuf + param->srvoffset, (int)MIN(inserverbuf,fromserver), 0, (struct sockaddr*)&param->sincr, sasize);
if(res <= 0) {
TOCLIENT = 0;
if(errno && errno != EAGAIN && errno != EINTR){
CLIENTTERMREAD = 1;
CLIENTTERMWRITE = 1;
HASERROR |= 1;
}
}
else {
#ifdef WITHLOG
log("done send to client from buf");
#endif
inserverbuf -= res;
fromserver -= res;
param->srvoffset += res;
if(param->srvoffset == param->srvinbuf)param->srvoffset = param->srvinbuf =0;
if(param->srvinbuf < param->srvbufsize) TOSERVERBUF = 1;
needaction = 0;
continue;
}
}
#ifdef WITHSPLICE
if(usesplice){
if(inclientpipe && !inclientbuf && FROMCLIENTPIPE && TOSERVER){
#ifdef WITHLOG
log("send to server from pipe");
#endif
res = splice(pipecli[0], NULL, param->remsock, NULL, MIN(MAXSPLICE, inclientpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
#ifdef WITHLOG
log("server from pipe splice finished\n");
#if WITHLOG > 1
#ifdef WITHSPLICE
sprintf(logbuf, "res: %d, errno: %d", (int)res, (int)errno);
log(logbuf);
#endif
#endif
#endif
if(res >0) {
param->nwrites++;
param->statscli64 += res;
inclientpipe -= res;
fromclient -= res;
if(fromclient) TOCLIENTPIPE = 1;
if(param->bandlimfunc) {
int sl1;
sl1 = (*param->bandlimfunc)(param, 0, res);
if(sl1 > sleeptime) sleeptime = sl1;
}
needaction = 0;
continue;
}
else {
FROMCLIENTPIPE = TOSERVER = 0;
if(errno && errno != EINTR && errno != EAGAIN) {
SERVERTERMREAD = 1;
SERVERTERMWRITE = 1;
HASERROR |= 2;
}
}
}
if(inserverpipe && !inserverbuf && FROMSERVERPIPE && TOCLIENT){
#ifdef WITHLOG
log("send to client from pipe");
#endif
res = splice(pipesrv[0], NULL, param->clisock, NULL, MIN(MAXSPLICE, inserverpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
#ifdef WITHLOG
log("client from pipe splice finished\n");
#if WITHLOG > 1
#ifdef WITHSPLICE
sprintf(logbuf, "res: %d, errno: %d", (int)res, (int)errno);
log(logbuf);
#endif
#endif
#endif
if(res > 0) {
inserverpipe -= res;
fromserver -= res;
if(fromserver)TOSERVERPIPE = 1;
needaction = 0;
continue;
}
else {
FROMSERVERPIPE = TOCLIENT = 0;
if(errno && errno != EINTR && errno != EAGAIN) {
CLIENTTERMREAD = 1;
CLIENTTERMWRITE = 1;
HASERROR |= 1;
}
}
}
if(fromclient>inclientpipe && FROMCLIENT && TOCLIENTPIPE && !SESSIONEND){
int error;
socklen_t len=sizeof(error);
#ifdef WITHLOG
log("read from client to pipe");
#endif
errno = 0;
res = splice(param->clisock, NULL, pipecli[1], NULL, (int)MIN((uint64_t)MAXSPLICE - inclientpipe, (uint64_t)fromclient-inclientpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
#ifdef WITHLOG
log("client to pipe splice finished\n");
#if WITHLOG > 1
#ifdef WITHSPLICE
sprintf(logbuf, "res: %d, errno: %d", (int)res, (int)errno);
log(logbuf);
#endif
#endif
#endif
if(res <= 0) {
FROMCLIENT = TOCLIENTPIPE = 0;
if(res == 0 && !errno) {
CLIENTTERMREAD = 1;
continue;
}
if(errno && errno != EINTR && errno != EAGAIN) {
CLIENTTERMREAD = 1;
CLIENTTERMWRITE = 1;
continue;
}
}
else {
#ifdef WITHLOG
log("done read from client to pipe");
#endif
graceclinum++;
graceclitraf += res;
gracesrvnum = gracesrvtraf = 0;
inclientpipe += res;
if(inclientpipe >= MAXSPLICE) TOCLIENTPIPE = 0;
needaction = 0;
continue;
}
}
if(fromserver > inserverpipe && FROMSERVER && TOSERVERPIPE && !SESSIONEND){
int error;
socklen_t len=sizeof(error);
errno = 0;
#ifdef WITHLOG
log("read from server to pipe\n");
#endif
res = splice(param->remsock, NULL, pipesrv[1], NULL, MIN(MAXSPLICE - inserverpipe, fromserver - inserverpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
#ifdef WITHLOG
log("server to pipe splice finished\n");
#if WITHLOG > 1
#ifdef WITHSPLICE
sprintf(logbuf, "res: %d, errno: %d", (int)res, (int)errno);
log(logbuf);
#endif
#endif
#endif
if(res <= 0) {
FROMSERVER = TOSERVERPIPE = 0;
if(res == 0 && !errno) {
SERVERTERMREAD = 1;
continue;
}
if(errno && errno != EINTR && errno != EAGAIN) {
SERVERTERMREAD = 1;
SERVERTERMWRITE = 1;
continue;
}
}
else {
#ifdef WITHLOG
log("done read from server to pipe\n");
#endif
gracesrvnum++;
gracesrvtraf += res;
graceclinum = graceclitraf = 0;
param->nreads++;
param->statssrv64 += res;
inserverpipe += res;
if(inserverpipe >= MAXSPLICE) TOSERVERPIPE = 0;
if(param->bandlimfunc) {
int sl1;
sl1 = (*param->bandlimfunc)(param, res, 0);
if(sl1 > sleeptime) sleeptime = sl1;
}
needaction = 0;
continue;
}
}
}
else
#endif
{
if(fromclient > inclientbuf && FROMCLIENT && TOCLIENTBUF && !SESSIONEND){
#ifdef WITHLOG
log("read from client to buf");
#endif
sasize = sizeof(param->sincr);
res = param->srv->so._recvfrom(param->sostate, param->clisock, (char *)param->clibuf + param->cliinbuf, (int)MIN((uint64_t)param->clibufsize - param->cliinbuf, fromclient-inclientbuf), 0, (struct sockaddr *)&param->sincr, &sasize);
if(res <= 0) {
FROMCLIENT = 0;
if(res == 0) {
CLIENTTERMREAD = 1;
continue;
}
if(errno && errno != EINTR && errno !=EAGAIN){
CLIENTTERMREAD = 1;
CLIENTTERMWRITE = 1;
continue;
}
}
else {
#ifdef WITHLOG
log("done read from client to buf");
#endif
graceclinum++;
graceclitraf += res;
gracesrvnum = gracesrvtraf = 0;
inclientbuf += res;
param->cliinbuf += res;
if(param->clibufsize == param->cliinbuf) TOCLIENTBUF = 0;
needaction = 0;
continue;
}
}
if(fromserver > inserverbuf && FROMSERVER && TOSERVERBUF && !SESSIONEND){
#ifdef WITHLOG
log("read from server to buf");
#endif
sasize = sizeof(param->sinsr);
res = param->srv->so._recvfrom(param->sostate, param->remsock, (char *)param->srvbuf + param->srvinbuf, (int)MIN((uint64_t)param->srvbufsize - param->srvinbuf, fromserver-inserverbuf), 0, (struct sockaddr *)&param->sinsr, &sasize);
if(res <= 0) {
FROMSERVER = 0;
if(res == 0) {
SERVERTERMREAD = 1;
continue;
}
if(errno && errno != EINTR && errno !=EAGAIN) {
SERVERTERMREAD = 1;
SERVERTERMWRITE = 1;
continue;
}
}
else {
#ifdef WITHLOG
log("done read from server to buf");
#endif
gracesrvnum++;
gracesrvtraf += res;
graceclinum = graceclitraf = 0;
param->nreads++;
param->statssrv64 += res;
inserverbuf += res;
param->srvinbuf += res;
if(param->bandlimfunc) {
int sl1;
sl1 = (*param->bandlimfunc)(param, res, 0);
if(sl1 > sleeptime) sleeptime = sl1;
}
if(param->srvbufsize == param->srvinbuf) TOSERVERBUF = 0;
needaction = 0;
continue;
}
}
}
if (CLIENTTERMREAD && !inclientbuf
#ifdef WITHSPLICE
&& !inclientpipe
#endif
&& !SERVERTERMWRITE) {
SERVERTERMWRITE = 1;
param->srv->so._shutdown(param->sostate, param->remsock, SHUT_WR);
}
if (SERVERTERMREAD && !inserverbuf
#ifdef WITHSPLICE
&& !inserverpipe
#endif
&& !CLIENTTERMWRITE) {
CLIENTTERMWRITE = 1;
param->srv->so._shutdown(param->sostate, param->clisock, SHUT_WR);
}
for(after = 0, cli_events=0, srv_events=0; after < 2; after ++){
fdsc = 0;
if(!after){
memset(fds, 0, sizeof(fds));
}
// if(!CLIENTTERMREAD || !CLIENTTERMWRITE){
if(!after){
fds[fdsc].fd = param->clisock;
if(fromclient && !CLIENTTERMREAD && !FROMCLIENT && !SESSIONEND && ((
#ifdef WITHSPLICE
!usesplice &&
#endif
TOCLIENTBUF)
#ifdef WITHSPLICE
|| (usesplice)
#endif
))
cli_events |= POLLIN;
if(!TOCLIENT && !CLIENTTERMWRITE && (inserverbuf
#ifdef WITHSPLICE
|| inserverpipe
#endif
))
cli_events |= POLLOUT;
/* poll() reports a hang-up whatever is asked, and would
return at once every time: the socket is left out then,
rather than given as an invalid one, which WSAPoll takes
for an error */
clipolled = cli_events || !CLIENTHUP;
if(cli_events){
fds[fdsc].fd = param->clisock;
fds[fdsc].events = cli_events;
#ifdef WITHLOG
if(cli_events & POLLIN)
log("wait reading from client");
if(cli_events & POLLOUT)
log("wait writing to client");
#endif
}
}
else if(!clipolled){
}
else if(cli_events){
if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {
CLIENTTERMREAD = 1;
CLIENTTERMWRITE = 1;
HASERROR |= 1;
}
else {
if(fds[fdsc].revents & POLLIN) {
#ifdef WITHLOG
log("ready to read from client");
#endif
FROMCLIENT = 1;
}
if(fds[fdsc].revents & POLLOUT) {
#ifdef WITHLOG
log("ready to write to client");
#endif
TOCLIENT = 1;
}
if(fds[fdsc].revents & (POLLHUP)) {
if(fds[fdsc].events & POLLIN) FROMCLIENT = 1;
if(fds[fdsc].events & POLLOUT){
if(hupwrite(param->clisock, fds[fdsc].revents, timeo)) TOCLIENT = 1;
else CLIENTTERMWRITE = 1;
}
}
}
} else if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {
CLIENTTERMREAD = 1;
CLIENTTERMWRITE = 1;
} else if(fds[fdsc].revents & POLLHUP) {
/* what the client sent before it closed is still to be
read, once the buffer has room for it */
CLIENTHUP = 1;
}
if(clipolled) fdsc++;
// if(!SERVERTERMREAD || !SERVERTERMWRITE){
if(!after){
fds[fdsc].fd = param->remsock;
if(fromserver && !SERVERTERMREAD && !FROMSERVER && !SESSIONEND && ((
#ifdef WITHSPLICE
!usesplice &&
#endif
TOSERVERBUF)
#ifdef WITHSPLICE
|| (usesplice)
#endif
))
srv_events |= POLLIN;
if(!TOSERVER && !SERVERTERMWRITE && (inclientbuf
#ifdef WITHSPLICE
|| inclientpipe
#endif
))
srv_events |= POLLOUT;
srvpolled = srv_events || !SERVERHUP;
if(srv_events){
fds[fdsc].fd = param->remsock;
fds[fdsc].events = srv_events;
#ifdef WITHLOG
if(srv_events & POLLIN)
log("wait reading from server");
if(srv_events & POLLOUT)
log("wait writing from server");
#endif
}
}
else if(!srvpolled){
}
else if(srv_events){
if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {
#ifdef WITHLOG
log("poll from server failed");
#endif
SERVERTERMREAD = 1;
SERVERTERMWRITE = 1;
HASERROR |=2;
}
else {
if(fds[fdsc].revents & POLLIN) {
#ifdef WITHLOG
log("ready to read from server");
#endif
FROMSERVER = 1;
}
if(fds[fdsc].revents & POLLOUT) {
#ifdef WITHLOG
log("ready to write to server");
#endif
TOSERVER = 1;
}
if(fds[fdsc].revents & (POLLHUP)) {
#ifdef WITHLOG
log("server terminated connection");
#endif
if(fds[fdsc].events & POLLIN) FROMSERVER = 1;
if(fds[fdsc].events & POLLOUT){
if(hupwrite(param->remsock, fds[fdsc].revents, timeo)) TOSERVER = 1;
else SERVERTERMWRITE = 1;
}
}
}
} else if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {
SERVERTERMREAD = 1;
SERVERTERMWRITE = 1;
} else if(fds[fdsc].revents & POLLHUP) {
/* the rest of the answer may still be waiting to be read */
SERVERHUP = 1;
}
if(srvpolled) fdsc++;
// }
#ifdef WITHSPLICE
if(usesplice){
if(fromclient>inclientpipe && !TOCLIENTPIPE && inclientpipe < MAXSPLICE){
if(!after){
#ifdef WITHLOG
log("wait writing to client pipe");
#endif
fds[fdsc].fd = pipecli[1];
fds[fdsc].events |= POLLOUT;
}
else {
if(fds[fdsc].revents & (POLLHUP|POLLERR|POLLNVAL)){
RETURN(90);
}
if(fds[fdsc].revents & POLLOUT) {
#ifdef WITHLOG
log("ready to write to client pipe");
#endif
TOCLIENTPIPE = 1;
}
}
fdsc++;
}
if(inclientpipe && !FROMCLIENTPIPE){
if(!after){
#ifdef WITHLOG
log("wait reading from client pipe");
#endif
fds[fdsc].fd = pipecli[0];
fds[fdsc].events |= (POLLIN);
}
else {
if(fds[fdsc].revents & (POLLHUP|POLLERR|POLLNVAL)){
RETURN(90);
}
#ifdef WITHLOG
log("ready reading from client pipe");
#endif
if(fds[fdsc].revents & (POLLIN)) FROMCLIENTPIPE = 1;
}
fdsc++;
}
if(fromserver>inserverpipe && !TOSERVERPIPE && inserverpipe < MAXSPLICE){
if(!after){
#ifdef WITHLOG
log("wait writing to server pipe");
#endif
fds[fdsc].fd = pipesrv[1];
fds[fdsc].events |= POLLOUT;
}
else {
if(fds[fdsc].revents & (POLLHUP|POLLERR|POLLNVAL)){
RETURN(90);
}
#ifdef WITHLOG
log("ready writing to server pipe");
#endif
if(fds[fdsc].revents & POLLOUT) TOSERVERPIPE = 1;
}
fdsc++;
}
if(inserverpipe && !FROMSERVERPIPE){
if(!after){
#ifdef WITHLOG
log("wait reading from server pipe");
#endif
fds[fdsc].fd = pipesrv[0];
fds[fdsc].events |= (POLLIN);
}
else {
if(fds[fdsc].revents & (POLLHUP|POLLERR|POLLNVAL)){
RETURN(90);
}
#ifdef WITHLOG
log("ready reading from server pipe");
#endif
if(fds[fdsc].revents & (POLLIN)) FROMSERVERPIPE = 1;
}
fdsc++;
}
}
#endif
if(param->ctrlsock != INVALID_SOCKET) {
if(!after) {
fds[fdsc].fd = param->ctrlsock;
fds[fdsc].events = POLLIN;
} else if(fds[fdsc].revents) {
CLIENTTERMREAD = CLIENTTERMWRITE = SERVERTERMREAD = SERVERTERMWRITE = 1;
}
fdsc++;
}
if(param->ctrlsocksrv != INVALID_SOCKET) {
if(!after) {
fds[fdsc].fd = param->ctrlsocksrv;
fds[fdsc].events = POLLIN;
} else if(fds[fdsc].revents) {
CLIENTTERMREAD = CLIENTTERMWRITE = SERVERTERMREAD = SERVERTERMWRITE = 1;
}
fdsc++;
}
if(!after){
if(!fdsc) RETURN(90);
#ifdef WITHLOG
log("entering poll");
#endif
res = param->srv->so._poll(param->sostate, fds, fdsc,
((CLIENTTERMREAD || SERVERTERMREAD)?
MIN(timeo, conf.timeouts[LINGER_TO]) : timeo) * 1000);
#ifdef WITHLOG
log("leaving poll");
#endif
if(res < 0){
#ifdef WITHLOG
log("poll error");
#endif
if(errno != EINTR) RETURN(91);
break;
}
if(res < 1){
#ifdef WITHLOG
log("timeout");
#endif
RETURN (92);
}
}
}
needaction++;
}
res = 0;
if(!fromserver && param->waitserver64) res = 98;
else if(!fromclient && param->waitclient64) res = 99;
else if(HASERROR) res = 94+HASERROR;
else if((inclientbuf || inserverbuf)) res = 94;
#ifdef WITHSPLICE
else if(inclientpipe || inserverpipe) res = 94;
#endif
if((param->nwrites > 0 && !SERVERTERMWRITE) || (param->nreads > 0 && !CLIENTTERMWRITE))
usleep(SLEEPTIME * 10);
CLEANRET:
#ifdef WITHSPLICE
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]);
#endif
return res;
}
#undef SESSIONEND