mirror of
https://github.com/3proxy/3proxy.git
synced 2026-10-28 22:05:48 +08:00
Split proxymain.c into its parts
proxymain.c stays the one unit built for 3proxy and included by each standalone service, and now includes its parts: mainconf.c services from their configuration lines: options, srvprepare(), mainprepare(), srvinit(), srvfree(), and copying and freeing access lists and authentication mainlisten.c listening sockets, shared and taken over on a reload, and reattach() mainclient.c threads for the clients of TCP services, their pool, srvspawn(), releaseparam(), and the filters' hooks maintcp.c the main loop of a TCP service, tcploop(), taken out of srvrun(), with tcpnext() and cblisten() mainudp.c the main loop of a UDP service proxymain.c keeps srvstart, srvrun() and main(), and declares the functions of file scope of the parts so that they can be in any order. The code is moved, not changed, but for tcploop(). The parts are dependencies of the objects that include proxymain.c in Makefile.inc. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
58424770aa
commit
5cbc72d289
@ -1,3 +1,6 @@
|
|||||||
|
# proxymain.c and the parts it includes
|
||||||
|
PROXYMAIN = proxymain.c mainconf.c mainlisten.c mainclient.c maintcp.c mainudp.c
|
||||||
|
|
||||||
#
|
#
|
||||||
# 3 proxy common Makefile
|
# 3 proxy common Makefile
|
||||||
#
|
#
|
||||||
@ -28,32 +31,32 @@ ftp$(OBJSUFFICS): ftp.c proxy.h structures.h
|
|||||||
sockgetchar$(OBJSUFFICS): sockgetchar.c proxy.h structures.h
|
sockgetchar$(OBJSUFFICS): sockgetchar.c proxy.h structures.h
|
||||||
$(CC) $(CFLAGS) sockgetchar.c
|
$(CC) $(CFLAGS) sockgetchar.c
|
||||||
|
|
||||||
proxy$(OBJSUFFICS): proxy.c proxy.h structures.h proxymain.c
|
proxy$(OBJSUFFICS): proxy.c proxy.h structures.h $(PROXYMAIN)
|
||||||
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)ANONYMOUS $(DEFINEOPTION)NOUDPMAIN proxy.c
|
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)ANONYMOUS $(DEFINEOPTION)NOUDPMAIN proxy.c
|
||||||
|
|
||||||
pop3p$(OBJSUFFICS): pop3p.c proxy.h structures.h proxymain.c
|
pop3p$(OBJSUFFICS): pop3p.c proxy.h structures.h $(PROXYMAIN)
|
||||||
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)NOUDPMAIN pop3p.c
|
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)NOUDPMAIN pop3p.c
|
||||||
|
|
||||||
imapp$(OBJSUFFICS): imapp.c proxy.h structures.h proxymain.c
|
imapp$(OBJSUFFICS): imapp.c proxy.h structures.h $(PROXYMAIN)
|
||||||
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)NOUDPMAIN imapp.c
|
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)NOUDPMAIN imapp.c
|
||||||
|
|
||||||
smtpp$(OBJSUFFICS): smtpp.c proxy.h structures.h proxymain.c
|
smtpp$(OBJSUFFICS): smtpp.c proxy.h structures.h $(PROXYMAIN)
|
||||||
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)NOUDPMAIN smtpp.c
|
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)NOUDPMAIN smtpp.c
|
||||||
|
|
||||||
ftppr$(OBJSUFFICS): ftppr.c proxy.h structures.h proxymain.c
|
ftppr$(OBJSUFFICS): ftppr.c proxy.h structures.h $(PROXYMAIN)
|
||||||
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)NOUDPMAIN ftppr.c
|
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)NOUDPMAIN ftppr.c
|
||||||
|
|
||||||
tcppm$(OBJSUFFICS): tcppm.c proxy.h structures.h proxymain.c
|
tcppm$(OBJSUFFICS): tcppm.c proxy.h structures.h $(PROXYMAIN)
|
||||||
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOUDPMAIN tcppm.c
|
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOUDPMAIN tcppm.c
|
||||||
|
|
||||||
udppm$(OBJSUFFICS): udppm.c proxy.h structures.h proxymain.c
|
udppm$(OBJSUFFICS): udppm.c proxy.h structures.h $(PROXYMAIN)
|
||||||
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOTCPMAIN udppm.c
|
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOTCPMAIN udppm.c
|
||||||
|
|
||||||
tlspr$(OBJSUFFICS): tlspr.c proxy.h structures.h proxymain.c
|
tlspr$(OBJSUFFICS): tlspr.c proxy.h structures.h $(PROXYMAIN)
|
||||||
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOUDPMAIN tlspr.c
|
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOUDPMAIN tlspr.c
|
||||||
|
|
||||||
|
|
||||||
socks$(OBJSUFFICS): socks.c proxy.h structures.h proxymain.c
|
socks$(OBJSUFFICS): socks.c proxy.h structures.h $(PROXYMAIN)
|
||||||
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)NOUDPMAIN socks.c
|
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)NOUDPMAIN socks.c
|
||||||
|
|
||||||
3proxy$(OBJSUFFICS): 3proxy.c proxy.h structures.h
|
3proxy$(OBJSUFFICS): 3proxy.c proxy.h structures.h
|
||||||
@ -86,7 +89,7 @@ $(BUILDDIR)$(PREFIX)udppm$(EXESUFFICS): sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFF
|
|||||||
$(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS): sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tlspr$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS)
|
$(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS): sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tlspr$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS)
|
||||||
$(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tlspr$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) $(LIBS)
|
$(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tlspr$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) $(LIBS)
|
||||||
|
|
||||||
mainfunc$(OBJSUFFICS): proxy.h structures.h proxymain.c
|
mainfunc$(OBJSUFFICS): proxy.h structures.h $(PROXYMAIN)
|
||||||
$(CC) $(COUT)mainfunc$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)MODULEMAINFUNC=mainfunc proxymain.c
|
$(CC) $(COUT)mainfunc$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)MODULEMAINFUNC=mainfunc proxymain.c
|
||||||
|
|
||||||
srvproxy$(OBJSUFFICS): proxy.c proxy.h structures.h
|
srvproxy$(OBJSUFFICS): proxy.c proxy.h structures.h
|
||||||
|
|||||||
602
src/mainclient.c
Normal file
602
src/mainclient.c
Normal file
@ -0,0 +1,602 @@
|
|||||||
|
/*
|
||||||
|
3APA3A simplest proxy server
|
||||||
|
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
||||||
|
|
||||||
|
please read License Agreement
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Clients of TCP services: a thread for each, from a pool, from the filters
|
||||||
|
to the end of the child serving it, and the filters' hooks.
|
||||||
|
Part of proxymain.c, which includes it. */
|
||||||
|
|
||||||
|
/* Child functions do not call each other, a child requesting redirection to
|
||||||
|
another child returns it instead of calling it, to keep the stack flat.
|
||||||
|
The child which completed the request returns NULL. param is logged by
|
||||||
|
the child and released here, but not freed: its thread may keep it.
|
||||||
|
*/
|
||||||
|
void * childfunc(struct clientparam * param){
|
||||||
|
PROXYFUNC pf = param->srv->pf;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; pf && i < MAXCHILDREDIRECTS; i++) pf = (PROXYFUNC)(*pf)(param);
|
||||||
|
if(pf){
|
||||||
|
param->res = 101;
|
||||||
|
dolog(param, (unsigned char *)"Redirection loop");
|
||||||
|
}
|
||||||
|
releaseparam(param);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* One connection, from the filters to the end of the child which answers
|
||||||
|
it. The structure is released, not freed, when it returns. */
|
||||||
|
static void serveparam(struct clientparam *param){
|
||||||
|
int i = -1;
|
||||||
|
#ifdef MODULEMAINFUNC
|
||||||
|
if(makefilters(param->srv, param) > CONTINUE){
|
||||||
|
releaseparam(param);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if(param->srv->cbsock != INVALID_SOCKET){
|
||||||
|
SASIZETYPE size = sizeof(param->sinsr);
|
||||||
|
struct pollfd fds;
|
||||||
|
fds.fd = param->srv->cbsock;
|
||||||
|
fds.events = POLLIN;
|
||||||
|
fds.revents = 0;
|
||||||
|
for(i=5+(param->srv->maxchild>>10); i; i--){
|
||||||
|
if(param->srv->so._poll(param->sostate, &fds, 1, 1000*CONNBACK_TO)!=1){
|
||||||
|
dolog(param, (unsigned char *)"Connect back not received, check connback client");
|
||||||
|
i = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
param->remsock = param->srv->so._accept(param->sostate, param->srv->cbsock, (struct sockaddr*)¶m->sinsr, &size);
|
||||||
|
if(param->remsock == INVALID_SOCKET) {
|
||||||
|
dolog(param, (unsigned char *)"Connect back accept() failed");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
unsigned long ul=1;
|
||||||
|
ioctlsocket(param->remsock, FIONBIO, &ul);
|
||||||
|
#else
|
||||||
|
fcntl(param->remsock,F_SETFL,O_NONBLOCK | fcntl(param->remsock,F_GETFL));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifndef WITHMAIN
|
||||||
|
param->req = param->sinsr;
|
||||||
|
if(param->srv->acl) param->res = checkACL(param);
|
||||||
|
if(param->res){
|
||||||
|
dolog(param, (unsigned char *)"Connect back ACL failed");
|
||||||
|
param->srv->so._closesocket(param->sostate, param->remsock);
|
||||||
|
param->remsock = INVALID_SOCKET;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if(socksend(param, param->remsock, (unsigned char *)"C", 1, CONNBACK_TO) != 1){
|
||||||
|
dolog(param, (unsigned char *)"Connect back sending command failed");
|
||||||
|
param->srv->so._closesocket(param->sostate, param->remsock);
|
||||||
|
param->remsock = INVALID_SOCKET;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!i){
|
||||||
|
param->res = 13;
|
||||||
|
releaseparam(param);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
#ifndef WITHMAIN
|
||||||
|
#ifndef _WIN32
|
||||||
|
sigset_t mask;
|
||||||
|
sigfillset(&mask);
|
||||||
|
pthread_sigmask(SIG_SETMASK, &mask, NULL);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(param->srv->haproxy){
|
||||||
|
char buf[128];
|
||||||
|
int i;
|
||||||
|
i = sockgetlinebuf(param, CLIENT, (unsigned char *)buf, sizeof(buf)-1, '\n', param->srv->timeouts[STRING_S]);
|
||||||
|
if(i > 12 && !strncasecmp(buf, "PROXY TCP", 9)){
|
||||||
|
char *token, *token2=NULL;
|
||||||
|
unsigned short u1=0, u2=0;
|
||||||
|
buf[i] = 0;
|
||||||
|
token = strchr(buf, ' ');
|
||||||
|
if(token) token = strchr(token+1, ' ');
|
||||||
|
if(token) token++;
|
||||||
|
if(token) token2 = strchr(token+1, ' ');
|
||||||
|
if(token2) {
|
||||||
|
*token2 = 0;
|
||||||
|
getip46(46, (unsigned char*) token, (struct sockaddr *)¶m->sincr);
|
||||||
|
token = token2+1;
|
||||||
|
token2 = strchr(token, ' ');
|
||||||
|
}
|
||||||
|
if(token2) {
|
||||||
|
*token2 = 0;
|
||||||
|
getip46(46, (unsigned char *) token, (struct sockaddr *)¶m->sincl);
|
||||||
|
token = token2+1;
|
||||||
|
token2 = strchr(token, ' ');
|
||||||
|
}
|
||||||
|
if(token){
|
||||||
|
sscanf(token,"%hu%hu", &u1, &u2);
|
||||||
|
if(u1) *SAPORT(¶m->sincr) = htons(u1);
|
||||||
|
if(u2) *SAPORT(¶m->sincl) = htons(u2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
childfunc(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Frees a worker's structure, once its thread is done with it. */
|
||||||
|
static void freeworker(struct clientparam *param){
|
||||||
|
releaseparam(param);
|
||||||
|
_3proxy_sem_destroy(param->wake);
|
||||||
|
free(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The automatic cap: 32 idle workers for each running service, but never
|
||||||
|
more than all of them could have connections. */
|
||||||
|
static unsigned poollimit(void){
|
||||||
|
unsigned limit;
|
||||||
|
|
||||||
|
if(runconf->poolmax >= 0) limit = (unsigned)runconf->poolmax;
|
||||||
|
else {
|
||||||
|
limit = 32 * tpool.services;
|
||||||
|
if(limit > tpool.maxchild) limit = tpool.maxchild;
|
||||||
|
}
|
||||||
|
tpool.limit = limit;
|
||||||
|
return limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parks a worker whose connection has ended and waits for the next one.
|
||||||
|
Returns 0 when the thread is to end instead: on exit, when the pool is
|
||||||
|
full, when the thread was left in another network namespace, or when it
|
||||||
|
waited longer than the idle timeout. */
|
||||||
|
static int poolpark(struct clientparam *param){
|
||||||
|
int timeout;
|
||||||
|
|
||||||
|
_3proxy_mutex_lock(&tpool.mutex);
|
||||||
|
if(timetoexit || nsbroken() || tpool.nidle >= poollimit()){
|
||||||
|
_3proxy_mutex_unlock(&tpool.mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
param->poolnext = tpool.idle;
|
||||||
|
tpool.idle = param;
|
||||||
|
tpool.nidle++;
|
||||||
|
param->pooled = 1;
|
||||||
|
timeout = runconf->pooltimeout;
|
||||||
|
_3proxy_mutex_unlock(&tpool.mutex);
|
||||||
|
|
||||||
|
if(timeout <= 0) _3proxy_sem_lock(param->wake);
|
||||||
|
else if(_3proxy_sem_timedlock(param->wake, (unsigned)timeout * 1000)){
|
||||||
|
struct clientparam **pp;
|
||||||
|
|
||||||
|
_3proxy_mutex_lock(&tpool.mutex);
|
||||||
|
if(param->pooled){
|
||||||
|
for(pp = &tpool.idle; *pp != param; pp = &(*pp)->poolnext);
|
||||||
|
*pp = param->poolnext;
|
||||||
|
tpool.nidle--;
|
||||||
|
param->pooled = 0;
|
||||||
|
_3proxy_mutex_unlock(&tpool.mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_3proxy_mutex_unlock(&tpool.mutex);
|
||||||
|
/* taken from the pool as the wait ran out: the connection follows */
|
||||||
|
_3proxy_sem_lock(param->wake);
|
||||||
|
}
|
||||||
|
return !param->poolexit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wakes every idle worker to end, when the process exits. */
|
||||||
|
void pooldrain(void){
|
||||||
|
struct clientparam *param;
|
||||||
|
|
||||||
|
_3proxy_mutex_lock(&tpool.mutex);
|
||||||
|
while((param = tpool.idle)){
|
||||||
|
tpool.idle = param->poolnext;
|
||||||
|
tpool.nidle--;
|
||||||
|
param->pooled = 0;
|
||||||
|
param->poolexit = 1;
|
||||||
|
_3proxy_sem_unlock(param->wake);
|
||||||
|
}
|
||||||
|
_3proxy_mutex_unlock(&tpool.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD WINAPI threadfunc(LPVOID p) {
|
||||||
|
#else
|
||||||
|
void * threadfunc (void *p) {
|
||||||
|
#endif
|
||||||
|
struct clientparam *param = (struct clientparam *)p;
|
||||||
|
|
||||||
|
do serveparam(param);
|
||||||
|
while(poolpark(param));
|
||||||
|
freeworker(param);
|
||||||
|
#ifdef _WIN32
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A thread for the client, client_sock for TCP. 0 once it runs, the slot
|
||||||
|
in hand going with it. */
|
||||||
|
static int srvspawn(struct srvstart *s, SOCKET client_sock){
|
||||||
|
struct srvparam *srv = &s->srv;
|
||||||
|
struct clientparam *defparam = &s->defparam;
|
||||||
|
struct clientparam * newparam = NULL, **pp;
|
||||||
|
pthread_t thread;
|
||||||
|
unsigned char buf[256];
|
||||||
|
int error = 0, reused;
|
||||||
|
size_t need;
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE h;
|
||||||
|
|
||||||
|
need = 16384 + srv->stacksize;
|
||||||
|
#else
|
||||||
|
need = threadstacksize(srv->stacksize);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* an idle worker if one has the stack this service needs */
|
||||||
|
_3proxy_mutex_lock(&tpool.mutex);
|
||||||
|
for(pp = &tpool.idle; *pp; pp = &(*pp)->poolnext){
|
||||||
|
if((*pp)->stacksize >= need){
|
||||||
|
newparam = *pp;
|
||||||
|
*pp = newparam->poolnext;
|
||||||
|
newparam->pooled = 0;
|
||||||
|
tpool.nidle--;
|
||||||
|
tpool.reused++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_3proxy_mutex_unlock(&tpool.mutex);
|
||||||
|
reused = newparam != NULL;
|
||||||
|
if(!reused && (!(newparam = malloc (sizeof(*defparam))) || _3proxy_sem_init_timed(newparam->wake, 0, 1))){
|
||||||
|
if(newparam) free(newparam);
|
||||||
|
srv->so._closesocket(srv->so.state, client_sock);
|
||||||
|
defparam->res = 21;
|
||||||
|
if(!srv->silent)dolog(defparam, (unsigned char *)"Memory Allocation Failed");
|
||||||
|
usleep(SLEEPTIME);
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
if(!reused){
|
||||||
|
newparam->stacksize = need;
|
||||||
|
newparam->pooled = newparam->poolexit = 0;
|
||||||
|
newparam->poolnext = NULL;
|
||||||
|
}
|
||||||
|
memcpy(newparam, defparam, offsetof(struct clientparam, filters));
|
||||||
|
newparam->clibuf = newparam->clibufdata;
|
||||||
|
newparam->srvbuf = newparam->srvbufdata;
|
||||||
|
newparam->clibufsize = newparam->srvbufsize = SRVBUFSIZE(srv);
|
||||||
|
clearstat(newparam);
|
||||||
|
if(!s->isudp) newparam->clisock = client_sock;
|
||||||
|
newparam->prev = newparam->next = NULL;
|
||||||
|
_3proxy_mutex_lock(&srv->counter_mutex);
|
||||||
|
if(!srv->child){
|
||||||
|
srv->child = newparam;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newparam->next = srv->child;
|
||||||
|
srv->child = srv->child->prev = newparam;
|
||||||
|
}
|
||||||
|
/* a worker from the pool is woken below, once it is one of the children */
|
||||||
|
if(!reused){
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef _WINCE
|
||||||
|
h = (HANDLE)_beginthreadex((LPSECURITY_ATTRIBUTES )NULL, (unsigned)(16384 + srv->stacksize), (void *)threadfunc, (void *) newparam, 0, &thread);
|
||||||
|
#else
|
||||||
|
h = (HANDLE)CreateThread((LPSECURITY_ATTRIBUTES )NULL, (unsigned)(16384 + srv->stacksize), (void *)threadfunc, (void *) newparam, 0, &thread);
|
||||||
|
#endif
|
||||||
|
if (h) {
|
||||||
|
CloseHandle(h);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprintf((char *)buf, "_beginthreadex(): %s", _strerror(NULL));
|
||||||
|
error = 1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
if ((error = pthread_create(&thread, &s->pa, threadfunc, (void *)newparam))){
|
||||||
|
sprintf((char *)buf, "pthread_create(): %s", strerror(error));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if(error){
|
||||||
|
if(!srv->silent)dolog(defparam, buf);
|
||||||
|
if(newparam->prev) newparam->prev->next = newparam->next;
|
||||||
|
else srv->child = newparam->next;
|
||||||
|
if(newparam->next) newparam->next->prev = newparam->prev;
|
||||||
|
if(newparam->clisock != INVALID_SOCKET){
|
||||||
|
srv->so._shutdown(srv->so.state, newparam->clisock, SHUT_RDWR);
|
||||||
|
srv->so._closesocket(srv->so.state, newparam->clisock);
|
||||||
|
newparam->clisock = INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
newparam->srv = NULL;
|
||||||
|
freeworker(newparam);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
srv->childcount++;
|
||||||
|
#ifndef STDMAIN
|
||||||
|
listenercount(srv, 1);
|
||||||
|
#endif
|
||||||
|
if(!reused){
|
||||||
|
newparam->workerid = (uint64_t)thread;
|
||||||
|
_3proxy_mutex_lock(&tpool.mutex);
|
||||||
|
tpool.created++;
|
||||||
|
_3proxy_mutex_unlock(&tpool.mutex);
|
||||||
|
}
|
||||||
|
newparam->threadid = newparam->workerid;
|
||||||
|
}
|
||||||
|
_3proxy_mutex_unlock(&srv->counter_mutex);
|
||||||
|
if(reused) _3proxy_sem_unlock(newparam->wake);
|
||||||
|
memset(&defparam->sincl, 0, sizeof(defparam->sincl));
|
||||||
|
memset(&defparam->sincr, 0, sizeof(defparam->sincr));
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Ends a connection: everything it holds is released and it leaves its
|
||||||
|
service, but the structure itself stays, with nothing in it pointing to
|
||||||
|
what was freed. After this the service may be gone at any moment, so
|
||||||
|
param->srv is cleared and nothing here touches it again. */
|
||||||
|
void releaseparam(struct clientparam * param) {
|
||||||
|
if(param->srv){
|
||||||
|
if(param->srv->so.freefunc) param->srv->so.freefunc(param->sostate);
|
||||||
|
}
|
||||||
|
if(param->clibuf != param->clibufdata) free(param->clibuf);
|
||||||
|
if(param->srvbuf != param->srvbufdata) free(param->srvbuf);
|
||||||
|
param->clibuf = param->clibufdata;
|
||||||
|
param->srvbuf = param->srvbufdata;
|
||||||
|
#ifdef WITHSPLICE
|
||||||
|
if(param->pipecli[0] >= 0) close(param->pipecli[0]);
|
||||||
|
if(param->pipecli[1] >= 0) close(param->pipecli[1]);
|
||||||
|
if(param->pipesrv[0] >= 0) close(param->pipesrv[0]);
|
||||||
|
if(param->pipesrv[1] >= 0) close(param->pipesrv[1]);
|
||||||
|
param->pipecli[0] = param->pipecli[1] = param->pipesrv[0] = param->pipesrv[1] = -1;
|
||||||
|
#endif
|
||||||
|
/* Not shut down on Windows: there shutting down receiving resets the
|
||||||
|
connection as soon as the peer sends anything more, and close()
|
||||||
|
alone sends whatever is queued before its FIN. Elsewhere it stays:
|
||||||
|
on the BSDs it discards what is left unread, so the close that
|
||||||
|
follows is not turned into a reset. */
|
||||||
|
if(param->srv) {
|
||||||
|
if(param->ctrlsocksrv != INVALID_SOCKET && param->ctrlsocksrv != param->remsock) {
|
||||||
|
#ifndef _WIN32
|
||||||
|
param->srv->so._shutdown(param->sostate, param->ctrlsocksrv, SHUT_RDWR);
|
||||||
|
#endif
|
||||||
|
param->srv->so._closesocket(param->sostate, param->ctrlsocksrv);
|
||||||
|
}
|
||||||
|
if(param->ctrlsock != INVALID_SOCKET && param->ctrlsock != param->clisock) {
|
||||||
|
#ifndef _WIN32
|
||||||
|
param->srv->so._shutdown(param->sostate, param->ctrlsock, SHUT_RDWR);
|
||||||
|
#endif
|
||||||
|
param->srv->so._closesocket(param->sostate, param->ctrlsock);
|
||||||
|
}
|
||||||
|
if(param->remsock != INVALID_SOCKET) {
|
||||||
|
#ifndef _WIN32
|
||||||
|
param->srv->so._shutdown(param->sostate, param->remsock, SHUT_RDWR);
|
||||||
|
#endif
|
||||||
|
param->srv->so._closesocket(param->sostate, param->remsock);
|
||||||
|
}
|
||||||
|
if(param->clisock != INVALID_SOCKET) {
|
||||||
|
#ifndef _WIN32
|
||||||
|
param->srv->so._shutdown(param->sostate, param->clisock, SHUT_RDWR);
|
||||||
|
#endif
|
||||||
|
param->srv->so._closesocket(param->sostate, param->clisock);
|
||||||
|
}
|
||||||
|
param->ctrlsocksrv = param->ctrlsock = param->remsock = param->clisock = INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
#ifndef STDMAIN
|
||||||
|
while(param->nfilters > 0){
|
||||||
|
param->nfilters--;
|
||||||
|
if(param->filters[param->nfilters].filter->filter_clear)
|
||||||
|
(*param->filters[param->nfilters].filter->filter_clear)(param->filters[param->nfilters].data);
|
||||||
|
}
|
||||||
|
if(param->connlim) stopconnlims(param);
|
||||||
|
/* a slot held in the newest service on the listener after a reload */
|
||||||
|
if(param->newsrv){
|
||||||
|
slotrelease(param->newsrv);
|
||||||
|
param->newsrv = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* the sockets were closed and the filters cleared with it */
|
||||||
|
param->sostate = NULL;
|
||||||
|
/* Leaving the service comes last. Once its last child is gone, srvfree()
|
||||||
|
frees the service along with its socket functions, filters and mutex,
|
||||||
|
and a close above may have lingered for seconds before getting here. */
|
||||||
|
if(param->srv){
|
||||||
|
_3proxy_mutex_lock(¶m->srv->counter_mutex);
|
||||||
|
if(param->prev || param->next || param->srv->child == param){
|
||||||
|
if(param->prev){
|
||||||
|
param->prev->next = param->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
param->srv->child = param->next;
|
||||||
|
if(param->next){
|
||||||
|
param->next->prev = param->prev;
|
||||||
|
}
|
||||||
|
(param->srv->childcount)--;
|
||||||
|
#ifndef STDMAIN
|
||||||
|
listenercount(param->srv, -1);
|
||||||
|
#endif
|
||||||
|
/* under the mutex too: srvfree() may destroy the semaphore as
|
||||||
|
soon as the mutex shows the last child gone */
|
||||||
|
_3proxy_sem_unlock(param->srv->childslots);
|
||||||
|
}
|
||||||
|
_3proxy_mutex_unlock(¶m->srv->counter_mutex);
|
||||||
|
param->prev = param->next = NULL;
|
||||||
|
param->srv = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeparam(struct clientparam * param) {
|
||||||
|
releaseparam(param);
|
||||||
|
free(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILTER_ACTION handleconnectflt(struct clientparam *cparam){
|
||||||
|
#ifndef STDMAIN
|
||||||
|
FILTER_ACTION action;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<cparam->nconnectfilters ;i++){
|
||||||
|
action = (*cparam->connectfilters[i]->filter->filter_connect)(cparam->connectfilters[i]->data, cparam);
|
||||||
|
if(action!=CONTINUE) return action;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* A buffer filter given a buffer too small for what it makes of it hands
|
||||||
|
back one of its own, allocated with malloc, and leaves the one it was given
|
||||||
|
alone: nothing refers to that one any more, so it is freed here, unless it
|
||||||
|
is one of the connection's own buffers. The caller owns whatever *buf_p
|
||||||
|
points to on return. */
|
||||||
|
static void filterswap(struct clientparam *param, unsigned char **cur, unsigned char *now){
|
||||||
|
if(now != *cur){
|
||||||
|
if(*cur != param->clibufdata && *cur != param->srvbufdata && *cur != param->tmpbuf) free(*cur);
|
||||||
|
*cur = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef STDMAIN
|
||||||
|
FILTER_ACTION makefilters (struct srvparam *srv, struct clientparam *param){
|
||||||
|
FILTER_ACTION res=PASS;
|
||||||
|
FILTER_ACTION action;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(!srv->nfilters) return PASS;
|
||||||
|
|
||||||
|
for(i=0; i<srv->nfilters; i++){
|
||||||
|
if(!srv->filter[i].filter_client)continue;
|
||||||
|
/* the slot may hold what the connection before left in it */
|
||||||
|
param->filters[param->nfilters].data = NULL;
|
||||||
|
action = (*srv->filter[i].filter_client)(srv->filter[i].data, param, ¶m->filters[param->nfilters].data);
|
||||||
|
if(action == PASS) continue;
|
||||||
|
if(action > CONTINUE) {
|
||||||
|
/* a filter refusing the connection is not kept, but it may
|
||||||
|
have made a state for it */
|
||||||
|
if(srv->filter[i].filter_clear && param->filters[param->nfilters].data)
|
||||||
|
(*srv->filter[i].filter_clear)(param->filters[param->nfilters].data);
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
param->filters[param->nfilters].filter = srv->filter + i;
|
||||||
|
if(srv->filter[i].filter_request)param->reqfilters[param->nreqfilters++] = param->filters + param->nfilters;
|
||||||
|
if(srv->filter[i].filter_connect)param->connectfilters[param->nconnectfilters++] = param->filters + param->nfilters;
|
||||||
|
if(srv->filter[i].filter_afterauth)param->afterauthfilters[param->nafterauthfilters++] = param->filters + param->nfilters;
|
||||||
|
if(srv->filter[i].filter_header_cli)param->hdrfilterscli[param->nhdrfilterscli++] = param->filters + param->nfilters;
|
||||||
|
if(srv->filter[i].filter_header_srv)param->hdrfilterssrv[param->nhdrfilterssrv++] = param->filters + param->nfilters;
|
||||||
|
if(srv->filter[i].filter_predata)param->predatfilters[param->npredatfilters++] = param->filters + param->nfilters;
|
||||||
|
if(srv->filter[i].filter_data_cli)param->datfilterscli[param->ndatfilterscli++] = param->filters + param->nfilters;
|
||||||
|
if(srv->filter[i].filter_data_srv)param->datfilterssrv[param->ndatfilterssrv++] = param->filters + param->nfilters;
|
||||||
|
param->nfilters++;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STDMAIN
|
||||||
|
FILTER_ACTION handleafterauthflt(struct clientparam *cparam){
|
||||||
|
#ifndef STDMAIN
|
||||||
|
FILTER_ACTION action;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<cparam->nafterauthfilters ;i++){
|
||||||
|
action = (*cparam->afterauthfilters[i]->filter->filter_afterauth)(cparam->afterauthfilters[i]->data, cparam);
|
||||||
|
if(action!=CONTINUE) return action;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FILTER_ACTION handlereqfilters(struct clientparam *param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p){
|
||||||
|
FILTER_ACTION action;
|
||||||
|
int i;
|
||||||
|
unsigned char *cur = *buf_p;
|
||||||
|
|
||||||
|
for(i=0; i<param->nreqfilters; i++){
|
||||||
|
action = (*param->reqfilters[i]->filter->filter_request)(param->reqfilters[i]->data, param, buf_p, bufsize_p, offset, length_p);
|
||||||
|
filterswap(param, &cur, *buf_p);
|
||||||
|
if(action!=CONTINUE) return action;
|
||||||
|
}
|
||||||
|
return PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILTER_ACTION handlehdrfilterssrv(struct clientparam *param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p){
|
||||||
|
FILTER_ACTION action;
|
||||||
|
int i;
|
||||||
|
unsigned char *cur = *buf_p;
|
||||||
|
|
||||||
|
for(i=0; i<param->nhdrfilterssrv; i++){
|
||||||
|
action = (*param->hdrfilterssrv[i]->filter->filter_header_srv)(param->hdrfilterssrv[i]->data, param, buf_p, bufsize_p, offset, length_p);
|
||||||
|
filterswap(param, &cur, *buf_p);
|
||||||
|
if(action!=CONTINUE) return action;
|
||||||
|
}
|
||||||
|
return PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILTER_ACTION handlehdrfilterscli(struct clientparam *param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p){
|
||||||
|
FILTER_ACTION action;
|
||||||
|
int i;
|
||||||
|
unsigned char *cur = *buf_p;
|
||||||
|
|
||||||
|
for(i = 0; i < param->nhdrfilterscli; i++){
|
||||||
|
action = (*param->hdrfilterscli[i]->filter->filter_header_cli)(param->hdrfilterscli[i]->data, param, buf_p, bufsize_p, offset, length_p);
|
||||||
|
filterswap(param, &cur, *buf_p);
|
||||||
|
if(action!=CONTINUE) return action;
|
||||||
|
}
|
||||||
|
return PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FILTER_ACTION handlepredatflt(struct clientparam *cparam){
|
||||||
|
#ifndef STDMAIN
|
||||||
|
FILTER_ACTION action;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(cparam->predatdone) return PASS;
|
||||||
|
cparam->predatdone = 1;
|
||||||
|
for(i=0; i<cparam->npredatfilters ;i++){
|
||||||
|
action = (*cparam->predatfilters[i]->filter->filter_predata)(cparam->predatfilters[i]->data, cparam);
|
||||||
|
if(action!=CONTINUE) return action;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILTER_ACTION handledatfltcli(struct clientparam *cparam, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p){
|
||||||
|
#ifndef STDMAIN
|
||||||
|
FILTER_ACTION action;
|
||||||
|
int i;
|
||||||
|
unsigned char *cur = *buf_p;
|
||||||
|
|
||||||
|
for(i=0; i<cparam->ndatfilterscli ;i++){
|
||||||
|
action = (*cparam->datfilterscli[i]->filter->filter_data_cli)(cparam->datfilterscli[i]->data, cparam, buf_p, bufsize_p, offset, length_p);
|
||||||
|
filterswap(cparam, &cur, *buf_p);
|
||||||
|
if(action!=CONTINUE) return action;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILTER_ACTION handledatfltsrv(struct clientparam *cparam, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p){
|
||||||
|
FILTER_ACTION action;
|
||||||
|
int i;
|
||||||
|
unsigned char *cur = *buf_p;
|
||||||
|
|
||||||
|
for(i=0; i<cparam->ndatfilterssrv; i++){
|
||||||
|
action = (*cparam->datfilterssrv[i]->filter->filter_data_srv)(cparam->datfilterssrv[i]->data, cparam, buf_p, bufsize_p, offset, length_p);
|
||||||
|
filterswap(cparam, &cur, *buf_p);
|
||||||
|
if(action!=CONTINUE) return action;
|
||||||
|
}
|
||||||
|
return PASS;
|
||||||
|
}
|
||||||
1125
src/mainconf.c
Normal file
1125
src/mainconf.c
Normal file
File diff suppressed because it is too large
Load Diff
552
src/mainlisten.c
Normal file
552
src/mainlisten.c
Normal file
@ -0,0 +1,552 @@
|
|||||||
|
/*
|
||||||
|
3APA3A simplest proxy server
|
||||||
|
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
||||||
|
|
||||||
|
please read License Agreement
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Listening sockets, shared by the services of a configuration on one
|
||||||
|
address and taken over by those of the next one on a reload, and the
|
||||||
|
clients of the old services going by the new rules.
|
||||||
|
Part of proxymain.c, which includes it. */
|
||||||
|
|
||||||
|
/* The listening socket. A UDP one also receives the datagrams of new
|
||||||
|
clients, so it is their client socket too. 0, or the code to exit with. */
|
||||||
|
#ifndef STDMAIN
|
||||||
|
/* A listening socket, shared by the services of a configuration on the
|
||||||
|
same address, port, network namespace and device, all accepting on it.
|
||||||
|
It outlives the services that opened it when, on a reload, services of
|
||||||
|
the new configuration on the same address with the same bind-time
|
||||||
|
options take it over. The old services stop accepting once their
|
||||||
|
configuration is no longer in force; whoever lets go of the socket last
|
||||||
|
closes it. A UDP service's socket is the way its sessions answer too:
|
||||||
|
the old sessions go on answering through it, the new services reading
|
||||||
|
what comes, the sessions found by client address whichever service made
|
||||||
|
them. */
|
||||||
|
struct listener {
|
||||||
|
struct listener *next;
|
||||||
|
SOCKET sock;
|
||||||
|
int isudp;
|
||||||
|
/* a service connecting back (-r) has no socket to share, but its
|
||||||
|
clients find the newest service through a listener all the same:
|
||||||
|
one without a socket, for the place it connects to */
|
||||||
|
char *cbdest;
|
||||||
|
PROXYSOCKADDRTYPE sa;
|
||||||
|
char *netns, *device;
|
||||||
|
struct sockoptset opts;
|
||||||
|
int maxseg;
|
||||||
|
uint64_t undev, unino; /* its file, for a Unix socket on a path */
|
||||||
|
int optversion; /* the configuration whose options are set */
|
||||||
|
/* the services accepting on it, linked by lisnext */
|
||||||
|
struct srvparam *owners;
|
||||||
|
int users; /* the services holding it: those accepting,
|
||||||
|
and those which did until their last client
|
||||||
|
is gone, whose clients look up the newest
|
||||||
|
service through it after a reload */
|
||||||
|
/* held while the first of its services opens the socket, binding
|
||||||
|
which may take long: the others then use what it opened */
|
||||||
|
_3proxy_mutex_t open_mutex;
|
||||||
|
/* its clients, whichever of its services, old or new, they came to:
|
||||||
|
those connected now, and those done with */
|
||||||
|
uint64_t clients, processed;
|
||||||
|
/* the traffic of its clients as logged: from the server (%I) and
|
||||||
|
from the client (%O) */
|
||||||
|
uint64_t trafin, trafout;
|
||||||
|
_3proxy_mutex_t count_mutex;
|
||||||
|
/* a UDP service's sessions on a reload, for the newest service on the
|
||||||
|
listener to adopt */
|
||||||
|
struct udpsession *udphand;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A client of srv comes (1) or goes (-1). */
|
||||||
|
static void listenercount(struct srvparam *srv, int delta){
|
||||||
|
struct listener *l = srv->listener;
|
||||||
|
|
||||||
|
if(!l) return;
|
||||||
|
_3proxy_mutex_lock(&l->count_mutex);
|
||||||
|
if(delta > 0) l->clients++;
|
||||||
|
else {
|
||||||
|
l->clients--;
|
||||||
|
l->processed++;
|
||||||
|
}
|
||||||
|
_3proxy_mutex_unlock(&l->count_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The traffic of a client, about to be cleared once logged, counted for
|
||||||
|
its listener. */
|
||||||
|
void listenertraffic(struct clientparam *param){
|
||||||
|
struct listener *l = param->srv->listener;
|
||||||
|
|
||||||
|
if(!l) return;
|
||||||
|
_3proxy_mutex_lock(&l->count_mutex);
|
||||||
|
l->trafin += param->statssrv64;
|
||||||
|
l->trafout += param->statscli64;
|
||||||
|
_3proxy_mutex_unlock(&l->count_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t nocount = 0;
|
||||||
|
|
||||||
|
/* The counters of a running service's listener, for the admin pages; the
|
||||||
|
listener lasts as long as the service. */
|
||||||
|
uint64_t *listenerclients(struct srvparam *srv){
|
||||||
|
return srv->listener? &srv->listener->clients : &nocount;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t *listenerprocessed(struct srvparam *srv){
|
||||||
|
return srv->listener? &srv->listener->processed : &nocount;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t *listenertrafin(struct srvparam *srv){
|
||||||
|
return srv->listener? &srv->listener->trafin : &nocount;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t *listenertrafout(struct srvparam *srv){
|
||||||
|
return srv->listener? &srv->listener->trafout : &nocount;
|
||||||
|
}
|
||||||
|
static struct listener *listeners = NULL;
|
||||||
|
_3proxy_mutex_t listener_mutex;
|
||||||
|
|
||||||
|
static void listenerstop(struct srvparam *srv);
|
||||||
|
#ifndef NOUDPMAIN
|
||||||
|
static void udpendall(struct udpsession *list, int res);
|
||||||
|
#endif
|
||||||
|
static void takeopts(struct listener *l, struct srvparam *srv);
|
||||||
|
static void listenerrelease(struct srvparam *srv);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_UN
|
||||||
|
/* A Unix socket bound to a path: its file is noted, as a service of a
|
||||||
|
newer configuration may bind the same path anew before this socket is
|
||||||
|
closed, when the file is the other's, which closing must not remove. */
|
||||||
|
static void unbound(PROXYSOCKADDRTYPE *sa, uint64_t *dev, uint64_t *ino){
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if(*SAFAMILY(sa) == AF_UNIX && *SAADDR(sa) && !stat((char *)SAADDR(sa), &st)){
|
||||||
|
*dev = (uint64_t)st.st_dev;
|
||||||
|
*ino = (uint64_t)st.st_ino;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ununlink(PROXYSOCKADDRTYPE *sa, uint64_t dev, uint64_t ino){
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if(*SAFAMILY(sa) == AF_UNIX && *SAADDR(sa) && !stat((char *)SAADDR(sa), &st) &&
|
||||||
|
(uint64_t)st.st_dev == dev && (uint64_t)st.st_ino == ino)
|
||||||
|
unlink((char *)SAADDR(sa));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The listening socket of the service: its listener's, opened by the first
|
||||||
|
of the listener's services to get here. */
|
||||||
|
static int srvlisten(struct srvstart *s){
|
||||||
|
#ifndef STDMAIN
|
||||||
|
struct srvparam *srv = &s->srv;
|
||||||
|
struct clientparam *defparam = &s->defparam;
|
||||||
|
struct listener *l = srv->listener;
|
||||||
|
SOCKET sock;
|
||||||
|
unsigned char buf[256];
|
||||||
|
int res, taken;
|
||||||
|
|
||||||
|
if(l){
|
||||||
|
_3proxy_mutex_lock(&l->open_mutex);
|
||||||
|
_3proxy_mutex_lock(&listener_mutex);
|
||||||
|
sock = l->sock;
|
||||||
|
_3proxy_mutex_unlock(&listener_mutex);
|
||||||
|
if(sock == INVALID_SOCKET){
|
||||||
|
if(!(res = srvopen(s))){
|
||||||
|
_3proxy_mutex_lock(&listener_mutex);
|
||||||
|
l->sock = srv->srvsock;
|
||||||
|
l->optversion = srv->version;
|
||||||
|
l->undev = srv->undev;
|
||||||
|
l->unino = srv->unino;
|
||||||
|
_3proxy_mutex_unlock(&listener_mutex);
|
||||||
|
}
|
||||||
|
else if(srv->srvsock != INVALID_SOCKET){
|
||||||
|
so._closesocket(so.state, srv->srvsock);
|
||||||
|
srv->srvsock = INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
_3proxy_mutex_unlock(&l->open_mutex);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/* listening already: taken over from a service of the configuration
|
||||||
|
before, its options to be set, or opened by another service of
|
||||||
|
this one; only the backlog may be new */
|
||||||
|
taken = l->optversion != srv->version;
|
||||||
|
if(taken) takeopts(l, srv);
|
||||||
|
_3proxy_mutex_unlock(&l->open_mutex);
|
||||||
|
srv->srvsock = sock;
|
||||||
|
#ifndef NOUDPMAIN
|
||||||
|
if(s->isudp) defparam->clisock = sock;
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
if(srv->so._listen (srv->so.state, sock, srv->backlog?srv->backlog : 1+(srv->maxchild>>3))==-1) {
|
||||||
|
srv->srvsock = INVALID_SOCKET;
|
||||||
|
sprintf((char *)buf, "listen(): %s", strerror(errno));
|
||||||
|
if(!srv->silent)dolog(defparam, buf);
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
if(!srv->silent){
|
||||||
|
sprintf((char *)buf, "Accepting connections, %s [%"PRIu64"/%"PRIu64"]", taken? "taken over" : "shared",
|
||||||
|
(uint64_t)getpid(), (uint64_t)pthread_self());
|
||||||
|
dolog(defparam, buf);
|
||||||
|
}
|
||||||
|
srv->fds.fd = sock;
|
||||||
|
srv->fds.events = POLLIN;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return srvopen(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Opens, binds and listens on the service's socket. */
|
||||||
|
static int srvopen(struct srvstart *s){
|
||||||
|
struct srvparam *srv = &s->srv;
|
||||||
|
struct clientparam *defparam = &s->defparam;
|
||||||
|
SOCKET sock = INVALID_SOCKET;
|
||||||
|
unsigned sleeptime;
|
||||||
|
unsigned char buf[256];
|
||||||
|
#ifdef _WIN32
|
||||||
|
unsigned long ul = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NOTCPMAIN
|
||||||
|
if(!s->isudp){
|
||||||
|
sock=nssocket(srv, &srv->so, srv->so.state, NS_IN, SASOCK(&srv->intsa), SOCK_STREAM,
|
||||||
|
#ifdef WITH_UN
|
||||||
|
*SAFAMILY(&srv->intsa) == AF_UNIX? 0 :
|
||||||
|
#endif
|
||||||
|
IPPROTO_TCP
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifndef NOUDPMAIN
|
||||||
|
if(s->isudp){
|
||||||
|
sock=nssocket(srv, &srv->so, srv->so.state, NS_IN, SASOCK(&srv->intsa), SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if( sock == INVALID_SOCKET) {
|
||||||
|
perror("socket()");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
setopts(sock, &srv->lissockopts, srv->maxseg);
|
||||||
|
#ifdef _WIN32
|
||||||
|
ioctlsocket(sock, FIONBIO, &ul);
|
||||||
|
#else
|
||||||
|
fcntl(sock,F_SETFL,O_NONBLOCK | fcntl(sock,F_GETFL));
|
||||||
|
#endif
|
||||||
|
srv->srvsock = sock;
|
||||||
|
#if defined SO_BINDTODEVICE || defined IP_BOUND_IF
|
||||||
|
if(bindtodevice(&srv->so, srv->so.state, sock, srv->ibindtodevice, *SAFAMILY(&srv->intsa))) {
|
||||||
|
dolog(defparam, (unsigned char *)"failed to bind device");
|
||||||
|
return -12;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_UN
|
||||||
|
if(*SAFAMILY(&srv->intsa) == AF_UNIX){
|
||||||
|
struct sockaddr_un *sun = (struct sockaddr_un *)&srv->intsa;
|
||||||
|
if(*sun->sun_path)unlink(sun->sun_path);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for(sleeptime = SLEEPTIME * 100; srv->so._bind(srv->so.state, sock, (struct sockaddr*)&srv->intsa, SASIZE(&srv->intsa))==-1; usleep(sleeptime)) {
|
||||||
|
sprintf((char *)buf, "bind(): %s", strerror(errno));
|
||||||
|
if(!srv->silent)dolog(defparam, buf);
|
||||||
|
sleeptime = (sleeptime<<1);
|
||||||
|
if(!sleeptime) return -3;
|
||||||
|
}
|
||||||
|
#ifdef WITH_UN
|
||||||
|
unbound(&srv->intsa, &srv->undev, &srv->unino);
|
||||||
|
#endif
|
||||||
|
#ifndef NOTCPMAIN
|
||||||
|
if(!s->isudp){
|
||||||
|
if(srv->so._listen (srv->so.state, sock, srv->backlog?srv->backlog : 1+(srv->maxchild>>3))==-1) {
|
||||||
|
sprintf((char *)buf, "listen(): %s", strerror(errno));
|
||||||
|
if(!srv->silent)dolog(defparam, buf);
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifndef NOUDPMAIN
|
||||||
|
if(s->isudp) defparam->clisock = sock;
|
||||||
|
#endif
|
||||||
|
srv->fds.fd = sock;
|
||||||
|
srv->fds.events = POLLIN;
|
||||||
|
|
||||||
|
if(!srv->silent){
|
||||||
|
sprintf((char *)buf, "Accepting connections [%"PRIu64"/%"PRIu64"]", (uint64_t)getpid(), (uint64_t)pthread_self());
|
||||||
|
dolog(defparam, buf);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef STDMAIN
|
||||||
|
/* -ol options that act only when set before bind(): a listening socket
|
||||||
|
differing in them is another socket. The rest are set on a socket taken
|
||||||
|
over, those no longer asked for turned off. */
|
||||||
|
static int bindtimeopt(const char *name){
|
||||||
|
return !strncmp(name, "SO_REUSE", 8) || !strcmp(name, "SO_EXCLUSIVEADDRUSE") ||
|
||||||
|
!strcmp(name, "SO_PORT_SCALABILITY") || !strcmp(name, "IP_TRANSPARENT") ||
|
||||||
|
!strcmp(name, "TCP_FASTOPEN");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Whether a socket with the options of l can be made one with those of srv:
|
||||||
|
the same bind-time ones, with the same values; TCP_MAXSEG, whose default
|
||||||
|
is not 0, cannot be turned off again. */
|
||||||
|
static int sameopts(struct listener *l, struct srvparam *srv){
|
||||||
|
const struct sockoptset *a = &l->opts, *b = &srv->lissockopts;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; sockopts[i].optname && i < MAXSOCKOPTS; i++){
|
||||||
|
unsigned bit = 1u << i;
|
||||||
|
|
||||||
|
if(bindtimeopt(sockopts[i].optname)){
|
||||||
|
if((a->set & bit) != (b->set & bit)) return 0;
|
||||||
|
if((a->set & bit) && a->value[i] != b->value[i]) return 0;
|
||||||
|
}
|
||||||
|
#ifdef TCP_MAXSEG
|
||||||
|
else if(sockopts[i].opt == TCP_MAXSEG && *sockopts[i].optname == 'T' &&
|
||||||
|
(a->set & bit) && !(b->set & bit)) return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets the options of srv on the socket of l taken over: those given, and
|
||||||
|
off those which l had and srv has not. */
|
||||||
|
static void takeopts(struct listener *l, struct srvparam *srv){
|
||||||
|
struct sockoptset off;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(&off, 0, sizeof(off));
|
||||||
|
for(i = 0; sockopts[i].optname && i < MAXSOCKOPTS; i++){
|
||||||
|
unsigned bit = 1u << i;
|
||||||
|
|
||||||
|
if((l->opts.set & bit) && !(srv->lissockopts.set & bit) && !bindtimeopt(sockopts[i].optname))
|
||||||
|
off.set |= bit;
|
||||||
|
}
|
||||||
|
if(off.set) setopts(l->sock, &off, 0);
|
||||||
|
setopts(l->sock, &srv->lissockopts, srv->maxseg);
|
||||||
|
_3proxy_mutex_lock(&listener_mutex);
|
||||||
|
l->opts = srv->lissockopts;
|
||||||
|
l->maxseg = srv->maxseg;
|
||||||
|
_3proxy_mutex_unlock(&listener_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int samelistener(struct listener *l, struct srvparam *srv, int isudp){
|
||||||
|
if(l->isudp != isudp || l->cbdest) return 0;
|
||||||
|
#ifdef __linux__
|
||||||
|
if(!samestr(l->netns, srv->inetns)) return 0;
|
||||||
|
#endif
|
||||||
|
#if defined SO_BINDTODEVICE || defined IP_BOUND_IF
|
||||||
|
if(!samestr(l->device, srv->ibindtodevice)) return 0;
|
||||||
|
#endif
|
||||||
|
return SASIZE(&l->sa) == SASIZE(&srv->intsa) &&
|
||||||
|
!memcmp(&l->sa, &srv->intsa, SASIZE(&srv->intsa)) &&
|
||||||
|
sameopts(l, srv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Whether s goes on listener l: one connecting back (-r) on the
|
||||||
|
pseudo-listener of the place it connects to, any other on the socket for
|
||||||
|
its address. */
|
||||||
|
static int listenerfor(struct listener *l, struct srvstart *s){
|
||||||
|
if(!s->isudp && s->iscbc) return l->cbdest && s->cbc_string && !strcmp(l->cbdest, (char *)s->cbc_string);
|
||||||
|
return !l->cbdest && samelistener(l, &s->srv, s->isudp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int listenerhas(struct listener *l, struct extparam *config){
|
||||||
|
struct srvparam *o;
|
||||||
|
|
||||||
|
for(o = l->owners; o; o = o->lisnext) if(o->config == config) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A prepared service, before it starts and before, on a reload, the old
|
||||||
|
services are told to stop: it joins the listener another service of its
|
||||||
|
configuration has for the same address, or takes over one an older
|
||||||
|
configuration's services accept on, one of a service of its type first.
|
||||||
|
Otherwise it gets a listener of its own, its socket opened when it
|
||||||
|
starts. A service connecting back has a pseudo-listener without a socket
|
||||||
|
for the place it connects to, which several of its services may share
|
||||||
|
too. Without memory for a listener the service keeps its socket to
|
||||||
|
itself. */
|
||||||
|
void mainclaim(void *service){
|
||||||
|
struct srvstart *s = (struct srvstart *)service;
|
||||||
|
struct srvparam *srv = &s->srv;
|
||||||
|
struct listener *l, *found = NULL, *newl;
|
||||||
|
|
||||||
|
/* made beforehand, as the mutex is held from finding a listener to
|
||||||
|
joining it, lest its last service let go of it meanwhile */
|
||||||
|
if((newl = malloc(sizeof(*newl)))){
|
||||||
|
memset(newl, 0, sizeof(*newl));
|
||||||
|
newl->sock = INVALID_SOCKET;
|
||||||
|
newl->isudp = s->isudp;
|
||||||
|
newl->maxseg = srv->maxseg;
|
||||||
|
newl->sa = srv->intsa;
|
||||||
|
newl->opts = srv->lissockopts;
|
||||||
|
newl->optversion = srv->version;
|
||||||
|
if((!s->isudp && s->iscbc && s->cbc_string && !(newl->cbdest = strdup((char *)s->cbc_string)))
|
||||||
|
#ifdef __linux__
|
||||||
|
|| (srv->inetns && !(newl->netns = strdup(srv->inetns)))
|
||||||
|
#endif
|
||||||
|
#if defined SO_BINDTODEVICE || defined IP_BOUND_IF
|
||||||
|
|| (srv->ibindtodevice && !(newl->device = strdup(srv->ibindtodevice)))
|
||||||
|
#endif
|
||||||
|
){
|
||||||
|
free(newl->cbdest);
|
||||||
|
free(newl->netns);
|
||||||
|
free(newl->device);
|
||||||
|
free(newl);
|
||||||
|
newl = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_3proxy_mutex_init(&newl->open_mutex);
|
||||||
|
_3proxy_mutex_init(&newl->count_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_3proxy_mutex_lock(&listener_mutex);
|
||||||
|
for(l = listeners; l; l = l->next){
|
||||||
|
if(!l->owners || !listenerfor(l, s)) continue;
|
||||||
|
if(listenerhas(l, srv->config)){
|
||||||
|
found = l;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!found || (l->owners->service == srv->service && found->owners->service != srv->service))
|
||||||
|
found = l;
|
||||||
|
}
|
||||||
|
if(!found && (found = newl)){
|
||||||
|
newl = NULL;
|
||||||
|
found->next = listeners;
|
||||||
|
listeners = found;
|
||||||
|
}
|
||||||
|
if(found){
|
||||||
|
srv->lisnext = found->owners;
|
||||||
|
found->owners = srv;
|
||||||
|
found->users++;
|
||||||
|
srv->listener = found;
|
||||||
|
}
|
||||||
|
_3proxy_mutex_unlock(&listener_mutex);
|
||||||
|
if(newl){
|
||||||
|
_3proxy_mutex_destroy(&newl->open_mutex);
|
||||||
|
_3proxy_mutex_destroy(&newl->count_mutex);
|
||||||
|
free(newl->cbdest);
|
||||||
|
free(newl->netns);
|
||||||
|
free(newl->device);
|
||||||
|
free(newl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A service stops accepting: once the last of the listener's services has,
|
||||||
|
the socket is closed, so that connections do not queue up on it. */
|
||||||
|
static void listenerstop(struct srvparam *srv){
|
||||||
|
struct listener *l = srv->listener;
|
||||||
|
struct srvparam **op;
|
||||||
|
SOCKET sock = INVALID_SOCKET;
|
||||||
|
struct udpsession *hand = NULL;
|
||||||
|
|
||||||
|
_3proxy_mutex_lock(&listener_mutex);
|
||||||
|
for(op = &l->owners; *op; op = &(*op)->lisnext) if(*op == srv){
|
||||||
|
*op = srv->lisnext;
|
||||||
|
srv->lisnext = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!l->owners){
|
||||||
|
sock = l->sock;
|
||||||
|
l->sock = INVALID_SOCKET;
|
||||||
|
/* sessions handed over with no service left to adopt them */
|
||||||
|
hand = l->udphand;
|
||||||
|
l->udphand = NULL;
|
||||||
|
}
|
||||||
|
_3proxy_mutex_unlock(&listener_mutex);
|
||||||
|
#ifndef NOUDPMAIN
|
||||||
|
udpendall(hand, 26);
|
||||||
|
#endif
|
||||||
|
if(sock == INVALID_SOCKET) return;
|
||||||
|
so._closesocket(so.state, sock);
|
||||||
|
#ifdef WITH_UN
|
||||||
|
ununlink(&l->sa, l->undev, l->unino);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A service lets go of its listener once its last client is gone, having
|
||||||
|
stopped accepting before; the last to let go frees it. */
|
||||||
|
static void listenerrelease(struct srvparam *srv){
|
||||||
|
struct listener *l = srv->listener, **lp;
|
||||||
|
int last;
|
||||||
|
|
||||||
|
srv->listener = NULL;
|
||||||
|
_3proxy_mutex_lock(&listener_mutex);
|
||||||
|
last = !--l->users;
|
||||||
|
if(last) for(lp = &listeners; *lp; lp = &(*lp)->next) if(*lp == l) {
|
||||||
|
*lp = l->next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_3proxy_mutex_unlock(&listener_mutex);
|
||||||
|
if(!last) return;
|
||||||
|
_3proxy_mutex_destroy(&l->open_mutex);
|
||||||
|
_3proxy_mutex_destroy(&l->count_mutex);
|
||||||
|
if(l->netns) free(l->netns);
|
||||||
|
if(l->device) free(l->device);
|
||||||
|
if(l->cbdest) free(l->cbdest);
|
||||||
|
free(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gives back a client's slot in a service it was not a child of. */
|
||||||
|
static void slotrelease(struct srvparam *srv){
|
||||||
|
_3proxy_mutex_lock(&srv->counter_mutex);
|
||||||
|
_3proxy_sem_unlock(srv->childslots);
|
||||||
|
_3proxy_mutex_unlock(&srv->counter_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* After a reload, unless noforce: a client goes by the rules of a newest
|
||||||
|
service of its type on its listener, now accepting there, re-authenticated
|
||||||
|
as it if auth is set. It takes one of that service's maxconn slots,
|
||||||
|
which counts it there and keeps the service, and its configuration,
|
||||||
|
until the client is done; a slot it held in a service before is given
|
||||||
|
back. 0 to go on, or the code to end the connection with: 26 if no
|
||||||
|
service of its type is there now, 27 if none of those has a slot free,
|
||||||
|
or what the authentication says. */
|
||||||
|
int reattach(struct clientparam *param, int auth){
|
||||||
|
struct srvparam *cur = PSRV(param), *newest, *orig, *o;
|
||||||
|
int version = runconf->version, res, newv = 0;
|
||||||
|
|
||||||
|
/* without a listener (connecting back) nothing replaces it */
|
||||||
|
if(!cur->listener){
|
||||||
|
param->version = version;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_3proxy_mutex_lock(&listener_mutex);
|
||||||
|
for(o = cur->listener->owners; o; o = o->lisnext) if(o->version > newv) newv = o->version;
|
||||||
|
if(newv <= cur->version){
|
||||||
|
/* the old services have not let go of the socket yet */
|
||||||
|
_3proxy_mutex_unlock(&listener_mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* one of the newest services of the client's type with a slot free;
|
||||||
|
param->service: the old service is a zombie by now. While it accepts
|
||||||
|
such a service is not freed, nor after the client has its slot. */
|
||||||
|
res = 26;
|
||||||
|
newest = NULL;
|
||||||
|
for(o = cur->listener->owners; o; o = o->lisnext){
|
||||||
|
if(o->version != newv || o->service != param->service) continue;
|
||||||
|
if(!_3proxy_sem_timedlock(o->childslots, 0)){
|
||||||
|
newest = o;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
res = 27;
|
||||||
|
}
|
||||||
|
_3proxy_mutex_unlock(&listener_mutex);
|
||||||
|
if(!newest) return res;
|
||||||
|
if(param->newsrv) slotrelease(param->newsrv);
|
||||||
|
param->newsrv = newest;
|
||||||
|
param->version = version;
|
||||||
|
if(!auth) return 0;
|
||||||
|
/* authenticated as the newest service's client: its access lists, users,
|
||||||
|
limits and counters */
|
||||||
|
orig = param->srv;
|
||||||
|
param->srv = newest;
|
||||||
|
res = (*newest->authfunc)(param);
|
||||||
|
param->srv = orig;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
217
src/maintcp.c
Normal file
217
src/maintcp.c
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
3APA3A simplest proxy server
|
||||||
|
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
||||||
|
|
||||||
|
please read License Agreement
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* The main loop of a TCP service: a slot of maxchild, then a connection
|
||||||
|
accepted, or made back for -r, given a thread.
|
||||||
|
Part of proxymain.c, which includes it. */
|
||||||
|
|
||||||
|
#ifndef NOTCPMAIN
|
||||||
|
/* -R: the socket the far side of a TCP service connects back to. */
|
||||||
|
static int cblisten(struct srvstart *s){
|
||||||
|
struct srvparam *srv = &s->srv;
|
||||||
|
struct clientparam *defparam = &s->defparam;
|
||||||
|
PROXYSOCKADDRTYPE cbsa;
|
||||||
|
|
||||||
|
parsehost(srv->family, s->cbl_string, (struct sockaddr *)&cbsa);
|
||||||
|
if((srv->cbsock=nssocket(srv, &srv->so, srv->so.state, NS_OUT, SASOCK(&cbsa), SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET) {
|
||||||
|
dolog(defparam, (unsigned char *)"Failed to allocate connect back socket");
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
setopts(srv->cbsock, &srv->cbssockopts, srv->maxseg);
|
||||||
|
|
||||||
|
if(srv->so._bind(srv->so.state, srv->cbsock, (struct sockaddr*)&cbsa, SASIZE(&cbsa))==-1) {
|
||||||
|
dolog(defparam, (unsigned char *)"Failed to bind connect back socket");
|
||||||
|
return -7;
|
||||||
|
}
|
||||||
|
if(srv->so._listen(srv->so.state, srv->cbsock, 1 + (srv->maxchild>>4))==-1) {
|
||||||
|
dolog(defparam, (unsigned char *)"Failed to listen connect back socket");
|
||||||
|
return -8;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* What getting the next client came to. */
|
||||||
|
enum { NEXT_CLIENT, NEXT_RETRY, NEXT_STOP };
|
||||||
|
|
||||||
|
#ifndef NOTCPMAIN
|
||||||
|
/* The next TCP client: accepted, or with -r connected to and asked to
|
||||||
|
go ahead. */
|
||||||
|
static int tcpnext(struct srvstart *s, SOCKET *client, int *nlog){
|
||||||
|
struct srvparam *srv = &s->srv;
|
||||||
|
struct clientparam *defparam = &s->defparam;
|
||||||
|
SOCKET new_sock;
|
||||||
|
SASIZETYPE size;
|
||||||
|
unsigned char buf[256];
|
||||||
|
int error = 0, opt = 1;
|
||||||
|
#ifndef _WIN32
|
||||||
|
struct linger lg;
|
||||||
|
#else
|
||||||
|
unsigned long ul = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size = sizeof(defparam->sincr);
|
||||||
|
if(s->iscbc){
|
||||||
|
new_sock=nssocket(&s->srv, &so, so.state, NS_OUT, SASOCK(&defparam->sincr), SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if(new_sock == INVALID_SOCKET){
|
||||||
|
usleep(SLEEPTIME);
|
||||||
|
return NEXT_RETRY;
|
||||||
|
}
|
||||||
|
setopts(new_sock, &srv->cbcsockopts, srv->maxseg);
|
||||||
|
|
||||||
|
parsehost(srv->family, s->cbc_string, (struct sockaddr *)&defparam->sincr);
|
||||||
|
#if defined SO_BINDTODEVICE || defined IP_BOUND_IF
|
||||||
|
/* as for a listening socket, an interface that cannot be
|
||||||
|
bound to is not worth retrying */
|
||||||
|
if(bindtodevice(&so, so.state, new_sock, srv->ibindtodevice, *SAFAMILY(&defparam->sincr))) {
|
||||||
|
dolog(defparam, (unsigned char *)"failed to bind device");
|
||||||
|
so._closesocket(so.state, new_sock);
|
||||||
|
return NEXT_STOP;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if(connectwithpoll(NULL, new_sock,(struct sockaddr *)&defparam->sincr,SASIZE(&defparam->sincr),CONNBACK_TO)) {
|
||||||
|
so._closesocket(so.state, new_sock);
|
||||||
|
usleep(SLEEPTIME);
|
||||||
|
return NEXT_RETRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sockrecvfrom(NULL, new_sock, NULL, buf, 1, 60*1000) != 1 || *buf!='C') {
|
||||||
|
so._closesocket(so.state, new_sock);
|
||||||
|
usleep(SLEEPTIME);
|
||||||
|
return NEXT_RETRY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
new_sock = srv->so._accept(srv->so.state, srv->srvsock, (struct sockaddr*)&defparam->sincr, &size);
|
||||||
|
if(new_sock == INVALID_SOCKET){
|
||||||
|
#ifdef _WIN32
|
||||||
|
switch(WSAGetLastError()){
|
||||||
|
case WSAEMFILE:
|
||||||
|
case WSAENOBUFS:
|
||||||
|
case WSAENETDOWN:
|
||||||
|
usleep(SLEEPTIME * 10);
|
||||||
|
break;
|
||||||
|
case WSAEINTR:
|
||||||
|
error = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
switch (errno){
|
||||||
|
#ifdef EMFILE
|
||||||
|
case EMFILE:
|
||||||
|
#endif
|
||||||
|
#ifdef ENFILE
|
||||||
|
case ENFILE:
|
||||||
|
#endif
|
||||||
|
#ifdef ENOBUFS
|
||||||
|
case ENOBUFS:
|
||||||
|
#endif
|
||||||
|
#ifdef ENOMEM
|
||||||
|
case ENOMEM:
|
||||||
|
#endif
|
||||||
|
usleep(SLEEPTIME * 10);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
(*nlog)++;
|
||||||
|
if(!srv->silent && (error || *nlog > 5000)) {
|
||||||
|
sprintf((char *)buf, "accept(): %s", strerror(errno));
|
||||||
|
dolog(defparam, buf);
|
||||||
|
*nlog = 0;
|
||||||
|
}
|
||||||
|
return NEXT_RETRY;
|
||||||
|
}
|
||||||
|
setopts(new_sock, &srv->clisockopts, srv->maxseg);
|
||||||
|
}
|
||||||
|
size = sizeof(defparam->sincl);
|
||||||
|
if(srv->so._getsockname(srv->so.state, new_sock, (struct sockaddr *)&defparam->sincl, &size)){
|
||||||
|
sprintf((char *)buf, "getsockname(): %s", strerror(errno));
|
||||||
|
if(!srv->silent)dolog(defparam, buf);
|
||||||
|
srv->so._closesocket(srv->so.state, new_sock);
|
||||||
|
return NEXT_RETRY;
|
||||||
|
}
|
||||||
|
#ifdef WITH_UN
|
||||||
|
if(*SAFAMILY(&defparam->sincl) == AF_UNIX) defparam->sincr = defparam->sincl;
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN32
|
||||||
|
ioctlsocket(new_sock, FIONBIO, &ul);
|
||||||
|
#else
|
||||||
|
fcntl(new_sock,F_SETFL,O_NONBLOCK | fcntl(new_sock,F_GETFL));
|
||||||
|
#endif
|
||||||
|
#ifndef _WIN32
|
||||||
|
/* Not on Windows: there closesocket() on a non-blocking socket with a linger
|
||||||
|
timeout fails with WSAEWOULDBLOCK while data is queued, leaving the socket
|
||||||
|
open. Without it the close completes in the background, data first. */
|
||||||
|
lg.l_onoff = 1;
|
||||||
|
lg.l_linger = srv->timeouts[STRING_L];
|
||||||
|
srv->so._setsockopt(srv->so.state, new_sock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
|
||||||
|
#endif
|
||||||
|
srv->so._setsockopt(srv->so.state, new_sock, SOL_SOCKET, SO_OOBINLINE, (char *)&opt, sizeof(int));
|
||||||
|
*client = new_sock;
|
||||||
|
return NEXT_CLIENT;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NOTCPMAIN
|
||||||
|
/* Clients until the service is paused: a connection is only taken with a
|
||||||
|
slot of maxchild in hand, which goes to its child; a slot not handed on is
|
||||||
|
kept for the next one, in *haveslot, and given back by the caller. */
|
||||||
|
static int tcploop(struct srvstart *s, int *haveslot){
|
||||||
|
struct srvparam *srv = &s->srv;
|
||||||
|
struct clientparam *defparam = &s->defparam;
|
||||||
|
SOCKET new_sock = INVALID_SOCKET;
|
||||||
|
int error = 0, next, nfull = 0;
|
||||||
|
int nlog = 5000;
|
||||||
|
unsigned char buf[256];
|
||||||
|
/* -r: a TCP service connects out for each client instead of listening */
|
||||||
|
int connectback = s->iscbc;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
for(;;){
|
||||||
|
/* A connection is only taken with a slot of maxchild in hand, which
|
||||||
|
goes to its child; a slot not handed on is kept for the next one.
|
||||||
|
The wait is timed to notice a reload. */
|
||||||
|
while(!(*haveslot) && srv->config->paused == srv->paused){
|
||||||
|
if(!_3proxy_sem_timedlock(srv->childslots, 1000)){
|
||||||
|
(*haveslot) = 1;
|
||||||
|
nfull = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!srv->silent && !(nfull++ % 5)) {
|
||||||
|
sprintf((char *)buf, "Warning: too many connected clients (%d/%d)", srv->childcount, srv->maxchild);
|
||||||
|
dolog(defparam, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (connectback) break;
|
||||||
|
if (srv->config->paused != srv->paused) break;
|
||||||
|
error = srv->so._poll(srv->so.state, &srv->fds, 1, 1000);
|
||||||
|
if (error >= 1) break;
|
||||||
|
if (error == 0) continue;
|
||||||
|
if (errno != EAGAIN && errno != EINTR) {
|
||||||
|
sprintf((char *)buf, "poll(): %s/%d", strerror(errno), errno);
|
||||||
|
if(!srv->silent)dolog(defparam, buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if((srv->config->paused != srv->paused) || (error < 0)) break;
|
||||||
|
error = 0;
|
||||||
|
next = tcpnext(s, &new_sock, &nlog);
|
||||||
|
if(next == NEXT_STOP) break;
|
||||||
|
if(next == NEXT_RETRY) continue;
|
||||||
|
if(!srvspawn(s, new_sock)) (*haveslot) = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
391
src/mainudp.c
Normal file
391
src/mainudp.c
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
/*
|
||||||
|
3APA3A simplest proxy server
|
||||||
|
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
||||||
|
|
||||||
|
please read License Agreement
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* The main loop of a UDP service: no thread for a client.
|
||||||
|
Part of proxymain.c, which includes it. */
|
||||||
|
|
||||||
|
#ifndef NOUDPMAIN
|
||||||
|
/* A UDP service's loop, in the service's thread: the datagrams of its
|
||||||
|
clients come on the service's socket, those of their destinations on the
|
||||||
|
sockets of their sessions, and every answer to a client leaves from the
|
||||||
|
service's socket. No thread for a client. */
|
||||||
|
struct udpstate {
|
||||||
|
struct udploop u;
|
||||||
|
const struct udpservice *svc;
|
||||||
|
struct udpsession *head;
|
||||||
|
int count;
|
||||||
|
struct hashtable keys; /* a session by its client's address */
|
||||||
|
struct pollfd *fds;
|
||||||
|
struct udpsession **of; /* the session of each of fds, NULL: the service */
|
||||||
|
int nalloc;
|
||||||
|
int nfull;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void udpkey2hash(const struct hashtable *ht, void *index, uint8_t *hash){
|
||||||
|
PROXYSOCKADDRTYPE *sa = (PROXYSOCKADDRTYPE *)index;
|
||||||
|
uint32_t m1, m2;
|
||||||
|
|
||||||
|
m1 = murmurhash3(SAADDR(sa), SAADDRLEN(sa), 0x3a3a3a3a);
|
||||||
|
m2 = murmurhash3(SAPORT(sa), 2, m1);
|
||||||
|
memcpy(hash, &m1, 4);
|
||||||
|
memcpy(hash+4, &m2, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
int udpanswer(struct udploop *u, struct clientparam *param, unsigned char *buf, int len){
|
||||||
|
return param->srv->so._sendto(param->sostate, param->srv->srvsock, (char *)buf, len, 0,
|
||||||
|
(struct sockaddr *)¶m->sincr, SASIZE(¶m->sincr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A session among its service's children, counted there and, if it is new,
|
||||||
|
on the listener. */
|
||||||
|
static void udpjoin(struct srvparam *srv, struct clientparam *param, int new){
|
||||||
|
param->prev = param->next = NULL;
|
||||||
|
_3proxy_mutex_lock(&srv->counter_mutex);
|
||||||
|
if(srv->child){
|
||||||
|
param->next = srv->child;
|
||||||
|
srv->child->prev = param;
|
||||||
|
}
|
||||||
|
srv->child = param;
|
||||||
|
srv->childcount++;
|
||||||
|
#ifndef STDMAIN
|
||||||
|
if(new) listenercount(srv, 1);
|
||||||
|
#endif
|
||||||
|
_3proxy_mutex_unlock(&srv->counter_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A new client: a slot of maxchild and a param, as a thread for it would
|
||||||
|
have; NULL without a slot or memory. */
|
||||||
|
static struct udpsession *udpnew(struct udpstate *st){
|
||||||
|
struct srvparam *srv = st->u.srv;
|
||||||
|
struct udpsession *ss;
|
||||||
|
struct clientparam *param = NULL;
|
||||||
|
|
||||||
|
if(_3proxy_sem_timedlock(srv->childslots, 0)){
|
||||||
|
if(!srv->silent && !(st->nfull++ % 100)){
|
||||||
|
char buf[128];
|
||||||
|
|
||||||
|
sprintf(buf, "Warning: too many connected clients (%d/%d), datagram dropped", srv->childcount, srv->maxchild);
|
||||||
|
dolog(st->u.defparam, (unsigned char *)buf);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
st->nfull = 0;
|
||||||
|
if(!(ss = calloc(1, sizeof(*ss))) || !(param = malloc(sizeof(*param)))){
|
||||||
|
free(ss);
|
||||||
|
_3proxy_sem_unlock(srv->childslots);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(param, 0, sizeof(*param));
|
||||||
|
memcpy(param, st->u.defparam, offsetof(struct clientparam, filters));
|
||||||
|
param->clibuf = param->clibufdata;
|
||||||
|
param->srvbuf = param->srvbufdata;
|
||||||
|
param->clibufsize = param->srvbufsize = SRVBUFSIZE(srv);
|
||||||
|
clearstat(param);
|
||||||
|
param->clisock = param->remsock = param->ctrlsock = param->ctrlsocksrv = INVALID_SOCKET;
|
||||||
|
udpjoin(srv, param, 1);
|
||||||
|
ss->param = param;
|
||||||
|
ss->svc = st->svc;
|
||||||
|
#ifdef MODULEMAINFUNC
|
||||||
|
if(makefilters(srv, param) > CONTINUE){
|
||||||
|
freeparam(param);
|
||||||
|
free(ss);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return ss;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A session ends: logged by its service, its sockets closed, its slot
|
||||||
|
given back. Its own service may be gone after. */
|
||||||
|
static void udpend(struct udpsession *ss){
|
||||||
|
struct clientparam *param = ss->param;
|
||||||
|
|
||||||
|
if(ss->svc->end) (*ss->svc->end)(NULL, ss);
|
||||||
|
param->clisock = INVALID_SOCKET;
|
||||||
|
freeparam(param);
|
||||||
|
free(ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void udpendall(struct udpsession *list, int res){
|
||||||
|
struct udpsession *ss;
|
||||||
|
|
||||||
|
while((ss = list)){
|
||||||
|
list = ss->next;
|
||||||
|
if(!ss->param->res) ss->param->res = res;
|
||||||
|
udpend(ss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void udpadd(struct udpstate *st, struct udpsession *ss){
|
||||||
|
ss->prev = NULL;
|
||||||
|
ss->next = st->head;
|
||||||
|
if(st->head) st->head->prev = ss;
|
||||||
|
st->head = ss;
|
||||||
|
st->count++;
|
||||||
|
ss->keyed = st->svc->keyed;
|
||||||
|
if(ss->keyed) hashadd(&st->keys, &ss->param->sincr, &ss, MAX_COUNTER_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void udpremove(struct udpstate *st, struct udpsession *ss){
|
||||||
|
if(ss->prev) ss->prev->next = ss->next;
|
||||||
|
else st->head = ss->next;
|
||||||
|
if(ss->next) ss->next->prev = ss->prev;
|
||||||
|
ss->next = ss->prev = NULL;
|
||||||
|
st->count--;
|
||||||
|
if(ss->keyed) hashdelete(&st->keys, &ss->param->sincr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void udprefresh(struct udpsession *ss, time_t now){
|
||||||
|
if(ss->idle) ss->deadline = now + ss->idle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef STDMAIN
|
||||||
|
/* On a reload: the sessions go to the listener, for the newest service on
|
||||||
|
it to adopt; if it has none, the last service to stop on it ends them. */
|
||||||
|
static void udphandover(struct udpstate *st){
|
||||||
|
struct listener *l = st->u.srv->listener;
|
||||||
|
struct udpsession *ss, *last = NULL;
|
||||||
|
|
||||||
|
for(ss = st->head; ss; ss = ss->next){
|
||||||
|
if(ss->keyed) hashdelete(&st->keys, &ss->param->sincr);
|
||||||
|
ss->keyed = 0;
|
||||||
|
last = ss;
|
||||||
|
}
|
||||||
|
if(!last) return;
|
||||||
|
if(!l){
|
||||||
|
udpendall(st->head, 26);
|
||||||
|
st->head = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_3proxy_mutex_lock(&listener_mutex);
|
||||||
|
last->next = l->udphand;
|
||||||
|
if(l->udphand) l->udphand->prev = last;
|
||||||
|
l->udphand = st->head;
|
||||||
|
_3proxy_mutex_unlock(&listener_mutex);
|
||||||
|
st->head = NULL;
|
||||||
|
st->count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sessions of an older service on the listener become this one's: a slot
|
||||||
|
here, the old one given back, its rules and limits, re-authenticated; 26
|
||||||
|
if it is another kind of service, 27 without a slot. */
|
||||||
|
static void udpadopt(struct udpstate *st){
|
||||||
|
struct srvparam *srv = st->u.srv, *old;
|
||||||
|
struct listener *l = srv->listener;
|
||||||
|
struct udpsession *list, *ss;
|
||||||
|
struct clientparam *param;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if(!l || !l->udphand) return;
|
||||||
|
_3proxy_mutex_lock(&listener_mutex);
|
||||||
|
list = l->udphand;
|
||||||
|
l->udphand = NULL;
|
||||||
|
_3proxy_mutex_unlock(&listener_mutex);
|
||||||
|
while((ss = list)){
|
||||||
|
list = ss->next;
|
||||||
|
ss->next = ss->prev = NULL;
|
||||||
|
param = ss->param;
|
||||||
|
old = param->srv;
|
||||||
|
if(old == srv) { udpadd(st, ss); continue; }
|
||||||
|
if(param->service != srv->service){
|
||||||
|
param->res = 26;
|
||||||
|
udpend(ss);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(_3proxy_sem_timedlock(srv->childslots, 0)){
|
||||||
|
param->res = 27;
|
||||||
|
udpend(ss);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* what belongs to the old service and configuration let go */
|
||||||
|
while(param->nfilters > 0){
|
||||||
|
param->nfilters--;
|
||||||
|
if(param->filters[param->nfilters].filter->filter_clear)
|
||||||
|
(*param->filters[param->nfilters].filter->filter_clear)(param->filters[param->nfilters].data);
|
||||||
|
}
|
||||||
|
if(param->connlim) stopconnlims(param);
|
||||||
|
param->bandlimfunc = NULL;
|
||||||
|
param->bandlims[0] = param->bandlimsout[0] = NULL;
|
||||||
|
param->trafcountfunc = NULL;
|
||||||
|
param->maxtrafin64 = param->maxtrafout64 = 0;
|
||||||
|
_3proxy_mutex_lock(&old->counter_mutex);
|
||||||
|
if(param->prev) param->prev->next = param->next;
|
||||||
|
else old->child = param->next;
|
||||||
|
if(param->next) param->next->prev = param->prev;
|
||||||
|
old->childcount--;
|
||||||
|
_3proxy_sem_unlock(old->childslots);
|
||||||
|
_3proxy_mutex_unlock(&old->counter_mutex);
|
||||||
|
param->srv = srv;
|
||||||
|
param->sostate = srv->so.state;
|
||||||
|
param->version = srv->version;
|
||||||
|
udpjoin(srv, param, 0);
|
||||||
|
ss->svc = st->svc;
|
||||||
|
#ifdef MODULEMAINFUNC
|
||||||
|
if(makefilters(srv, param) > CONTINUE){
|
||||||
|
param->res = 26;
|
||||||
|
udpend(ss);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if((res = (*srv->authfunc)(param))){
|
||||||
|
param->res = res;
|
||||||
|
udpend(ss);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
udpadd(st, ss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STDMAIN
|
||||||
|
/* After a reload, while an older service of this kind still accepts on the
|
||||||
|
listener, it goes on serving the service's socket alone, until it hands
|
||||||
|
its sessions over: a client is not given a session in both. */
|
||||||
|
static int udpolder(struct srvparam *srv){
|
||||||
|
struct srvparam *o;
|
||||||
|
int older = 0;
|
||||||
|
|
||||||
|
if(!srv->listener) return 0;
|
||||||
|
_3proxy_mutex_lock(&listener_mutex);
|
||||||
|
for(o = srv->listener->owners; o; o = o->lisnext)
|
||||||
|
if(o != srv && o->version < srv->version && o->service == srv->service) older = 1;
|
||||||
|
_3proxy_mutex_unlock(&listener_mutex);
|
||||||
|
return older;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The datagrams waiting on the service's socket: to a session, or a new
|
||||||
|
one. Some at a time, so that the sessions get their turn. */
|
||||||
|
static void udpclients(struct udpstate *st, time_t now){
|
||||||
|
struct srvparam *srv = st->u.srv;
|
||||||
|
struct clientparam *defparam = st->u.defparam;
|
||||||
|
struct udpsession *ss;
|
||||||
|
SASIZETYPE size;
|
||||||
|
int len, n;
|
||||||
|
|
||||||
|
for(n = 0; n < 64; n++){
|
||||||
|
size = sizeof(defparam->sincr);
|
||||||
|
len = srv->so._recvfrom(srv->so.state, srv->srvsock, (char *)srv->udpbuf, UDPBUFSIZE, 0,
|
||||||
|
(struct sockaddr *)&defparam->sincr, &size);
|
||||||
|
if(len < 0){
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* an answer to a client was refused (ICMP): reported here */
|
||||||
|
if(WSAGetLastError() == WSAECONNRESET) continue;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(st->svc->keyed && hashresolv(&st->keys, &defparam->sincr, &ss, NULL)){
|
||||||
|
if(ss->done) continue;
|
||||||
|
udprefresh(ss, now);
|
||||||
|
if(st->svc->client && (*st->svc->client)(&st->u, ss, srv->udpbuf, len)) ss->done = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!(ss = udpnew(st))) continue;
|
||||||
|
ss->param->sincr = defparam->sincr;
|
||||||
|
udpadd(st, ss);
|
||||||
|
if((*st->svc->first)(&st->u, ss, srv->udpbuf, len)) ss->done = 1;
|
||||||
|
else if(!ss->deadline || ss->idle) udprefresh(ss, now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udploop(struct srvstart *s){
|
||||||
|
struct udpstate st;
|
||||||
|
struct srvparam *srv = &s->srv;
|
||||||
|
struct udpsession *ss, *next;
|
||||||
|
time_t now;
|
||||||
|
int i, n, nfds, timeo, res = 0, wait = 0;
|
||||||
|
|
||||||
|
memset(&st, 0, sizeof(st));
|
||||||
|
st.u.srv = srv;
|
||||||
|
st.u.defparam = &s->defparam;
|
||||||
|
st.u.buf = srv->udpbuf2;
|
||||||
|
st.svc = s->udp;
|
||||||
|
st.keys.index2hash_add = udpkey2hash;
|
||||||
|
st.keys.index2hash_search = udpkey2hash;
|
||||||
|
st.keys.recsize = sizeof(struct udpsession *);
|
||||||
|
st.keys.hash_size = 8;
|
||||||
|
if(st.svc->keyed && inithashtable(&st.keys, 64, 256, 1048576)) return 21;
|
||||||
|
for(;;){
|
||||||
|
if(srv->config->paused != srv->paused) break;
|
||||||
|
#ifndef STDMAIN
|
||||||
|
udpadopt(&st);
|
||||||
|
#endif
|
||||||
|
if(st.nalloc < 2 * st.count + 1){
|
||||||
|
int want = 2 * st.count + 64;
|
||||||
|
struct pollfd *f = realloc(st.fds, want * sizeof(*f));
|
||||||
|
|
||||||
|
if(f) st.fds = f;
|
||||||
|
if(f && (st.of = realloc(st.of, want * sizeof(*st.of)))) st.nalloc = want;
|
||||||
|
else { res = 21; break; }
|
||||||
|
}
|
||||||
|
now = time(NULL);
|
||||||
|
timeo = 1000;
|
||||||
|
#ifndef STDMAIN
|
||||||
|
/* waiting for an older service's sessions: soon looked for again */
|
||||||
|
if((wait = udpolder(srv))) timeo = 20;
|
||||||
|
#endif
|
||||||
|
nfds = 0;
|
||||||
|
st.fds[nfds].fd = srv->srvsock;
|
||||||
|
st.fds[nfds].events = wait? 0 : POLLIN;
|
||||||
|
st.fds[nfds].revents = 0;
|
||||||
|
st.of[nfds++] = NULL;
|
||||||
|
for(ss = st.head; ss; ss = ss->next){
|
||||||
|
if(ss->deadline && (ss->deadline - now) * 1000 < timeo)
|
||||||
|
timeo = ss->deadline > now? (int)(ss->deadline - now) * 1000 : 0;
|
||||||
|
if(ss->param->remsock != INVALID_SOCKET){
|
||||||
|
st.fds[nfds].fd = ss->param->remsock;
|
||||||
|
st.fds[nfds].events = POLLIN;
|
||||||
|
st.fds[nfds].revents = 0;
|
||||||
|
st.of[nfds++] = ss;
|
||||||
|
}
|
||||||
|
/* a parent's control connection: its end ends the session */
|
||||||
|
if(ss->param->ctrlsocksrv != INVALID_SOCKET && ss->param->ctrlsocksrv != ss->param->remsock){
|
||||||
|
st.fds[nfds].fd = ss->param->ctrlsocksrv;
|
||||||
|
st.fds[nfds].events = POLLIN;
|
||||||
|
st.fds[nfds].revents = 0;
|
||||||
|
st.of[nfds++] = ss;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n = srv->so._poll(srv->so.state, st.fds, nfds, timeo);
|
||||||
|
if(n < 0 && errno != EINTR && errno != EAGAIN){
|
||||||
|
res = 91;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
now = time(NULL);
|
||||||
|
for(i = 1; n > 0 && i < nfds; i++){
|
||||||
|
ss = st.of[i];
|
||||||
|
if(!st.fds[i].revents || ss->done) continue;
|
||||||
|
if(st.fds[i].fd == ss->param->remsock){
|
||||||
|
udprefresh(ss, now);
|
||||||
|
if((*st.svc->server)(&st.u, ss)) ss->done = 1;
|
||||||
|
}
|
||||||
|
else ss->done = 1;
|
||||||
|
}
|
||||||
|
if(n > 0 && !wait && st.fds[0].revents) udpclients(&st, now);
|
||||||
|
for(ss = st.head; ss; ss = next){
|
||||||
|
next = ss->next;
|
||||||
|
if(!ss->done && ss->deadline && ss->deadline <= now){
|
||||||
|
ss->done = 1;
|
||||||
|
ss->param->res = st.svc->timeoutres;
|
||||||
|
}
|
||||||
|
if(ss->done){
|
||||||
|
udpremove(&st, ss);
|
||||||
|
udpend(ss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifndef STDMAIN
|
||||||
|
if(srv->config->paused != srv->paused) udphandover(&st);
|
||||||
|
#endif
|
||||||
|
udpendall(st.head, 26);
|
||||||
|
if(st.svc->keyed) destroyhashtable(&st.keys);
|
||||||
|
free(st.fds);
|
||||||
|
free(st.of);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
2864
src/proxymain.c
2864
src/proxymain.c
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user