diff --git a/CMakeLists.txt b/CMakeLists.txt index f7d969c..09c205a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -415,11 +415,12 @@ set(3PROXY_CORE_SOURCES # Object libraries for common sources (shared between executables) # ============================================================================ -# Common object library (sockmap, sockgetchar, common, log) +# Common object library (sockmap, sockgetchar, common, sem, log) add_library(common_obj OBJECT src/sockmap.c src/sockgetchar.c src/common.c + src/sem.c src/log.c ) target_include_directories(common_obj PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) diff --git a/src/Makefile.inc b/src/Makefile.inc index 1cb5a40..fba8ae8 100644 --- a/src/Makefile.inc +++ b/src/Makefile.inc @@ -13,6 +13,9 @@ udpsockmap$(OBJSUFFICS): udpsockmap.c proxy.h structures.h common$(OBJSUFFICS): common.c proxy.h structures.h $(CC) $(CFLAGS) common.c +sem$(OBJSUFFICS): sem.c proxy.h structures.h + $(CC) $(CFLAGS) sem.c + plugins$(OBJSUFFICS): plugins.c proxy.h structures.h $(CC) $(CFLAGS) plugins.c @@ -56,32 +59,32 @@ socks$(OBJSUFFICS): socks.c proxy.h structures.h proxymain.c 3proxy$(OBJSUFFICS): 3proxy.c proxy.h structures.h $(CC) $(CFLAGS) 3proxy.c -$(BUILDDIR)$(PREFIX)proxy$(EXESUFFICS): sockmap$(OBJSUFFICS) proxy$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS) - $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)proxy$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) proxy$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) +$(BUILDDIR)$(PREFIX)proxy$(EXESUFFICS): sockmap$(OBJSUFFICS) proxy$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS) + $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)proxy$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) proxy$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) -$(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS): sockmap$(OBJSUFFICS) pop3p$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) $(COMPATLIBS) - $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) pop3p$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) +$(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS): sockmap$(OBJSUFFICS) pop3p$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) $(COMPATLIBS) + $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) pop3p$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) -$(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS): sockmap$(OBJSUFFICS) imapp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) base64$(OBJSUFFICS) $(COMPATLIBS) - $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) imapp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) base64$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) +$(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS): sockmap$(OBJSUFFICS) imapp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) base64$(OBJSUFFICS) $(COMPATLIBS) + $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) imapp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) base64$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) -$(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS): sockmap$(OBJSUFFICS) smtpp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) base64$(OBJSUFFICS) $(COMPATLIBS) - $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) smtpp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) base64$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) +$(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS): sockmap$(OBJSUFFICS) smtpp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) base64$(OBJSUFFICS) $(COMPATLIBS) + $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) smtpp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) base64$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) -$(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS): sockmap$(OBJSUFFICS) ftppr$(OBJSUFFICS) ftp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) $(COMPATLIBS) - $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) ftppr$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) +$(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS): sockmap$(OBJSUFFICS) ftppr$(OBJSUFFICS) ftp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) $(COMPATLIBS) + $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) ftppr$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) -$(BUILDDIR)$(PREFIX)socks$(EXESUFFICS): sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) socks$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) - $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)socks$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) socks$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) $(LIBS) +$(BUILDDIR)$(PREFIX)socks$(EXESUFFICS): sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) socks$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) + $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)socks$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) socks$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) $(LIBS) -$(BUILDDIR)$(PREFIX)tcppm$(EXESUFFICS): sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tcppm$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) - $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)tcppm$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tcppm$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) $(LIBS) +$(BUILDDIR)$(PREFIX)tcppm$(EXESUFFICS): sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tcppm$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) + $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)tcppm$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tcppm$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) $(LIBS) -$(BUILDDIR)$(PREFIX)udppm$(EXESUFFICS): sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) udppm$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) hash$(OBJSUFFICS) - $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)udppm$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) udppm$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) hash$(OBJSUFFICS) $(LIBS) +$(BUILDDIR)$(PREFIX)udppm$(EXESUFFICS): sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) udppm$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) hash$(OBJSUFFICS) + $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)udppm$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) udppm$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) hash$(OBJSUFFICS) $(LIBS) -$(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS): sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tlspr$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) - $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tlspr$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) $(LIBS) +$(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) mainfunc$(OBJSUFFICS): proxy.h structures.h proxymain.c $(CC) $(COUT)mainfunc$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)MODULEMAINFUNC=mainfunc proxymain.c @@ -194,6 +197,6 @@ ssl$(OBJSUFFICS): ssl.c structures.h proxy.h ssl.h pcre$(OBJSUFFICS): pcre.c structures.h $(CC) $(COUT)pcre$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)WITH_PCRE pcre.c -$(BUILDDIR)3proxy$(EXESUFFICS): 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) log$(OBJSUFFICS) datatypes$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(TRANSPARENT_OBJS) $(COMPATLIBS) $(VERSIONDEP) - $(LN) $(LNOUT)$(BUILDDIR)3proxy$(EXESUFFICS) $(LDFLAGS) $(VERFILE) 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) datatypes$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(TRANSPARENT_OBJS) $(COMPATLIBS) $(LIBS) $(PCRE_LIBS) +$(BUILDDIR)3proxy$(EXESUFFICS): 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) log$(OBJSUFFICS) datatypes$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(TRANSPARENT_OBJS) $(COMPATLIBS) $(VERSIONDEP) + $(LN) $(LNOUT)$(BUILDDIR)3proxy$(EXESUFFICS) $(LDFLAGS) $(VERFILE) 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) datatypes$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(TRANSPARENT_OBJS) $(COMPATLIBS) $(LIBS) $(PCRE_LIBS) diff --git a/src/common.c b/src/common.c index 92386b8..6dacf65 100644 --- a/src/common.c +++ b/src/common.c @@ -187,33 +187,6 @@ size_t threadstacksize(int extra){ if(size < (long)PTHREAD_STACK_MIN) size = (long)PTHREAD_STACK_MIN; return (size_t)size; } - -int _3proxy_sem_init_f(_3proxy_sem_t *sem, unsigned count, unsigned maxcount){ - sem->count = count; - sem->maxcount = maxcount; - if(pthread_mutex_init(&sem->mutex, NULL)) return 1; - if(pthread_cond_init(&sem->cond, NULL)){ - pthread_mutex_destroy(&sem->mutex); - return 1; - } - return 0; -} - -void _3proxy_sem_lock_f(_3proxy_sem_t *sem){ - pthread_mutex_lock(&sem->mutex); - while(!sem->count) pthread_cond_wait(&sem->cond, &sem->mutex); - sem->count--; - pthread_mutex_unlock(&sem->mutex); -} - -void _3proxy_sem_unlock_f(_3proxy_sem_t *sem){ - pthread_mutex_lock(&sem->mutex); - if(sem->count < sem->maxcount){ - sem->count++; - pthread_cond_signal(&sem->cond); - } - pthread_mutex_unlock(&sem->mutex); -} #endif struct extparam conf = { diff --git a/src/proxy.h b/src/proxy.h index c347ebe..cf1e5e6 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -322,13 +322,23 @@ extern _3proxy_sem_t udpinit; #define _3proxy_sem_init(x, count, maxcount) (((x) = CreateSemaphore(NULL, (count), (maxcount), NULL))? 0 : 1) #define _3proxy_sem_lock(x) WaitForSingleObject(x, INFINITE) #define _3proxy_sem_unlock(x) ReleaseSemaphore(x, 1, NULL) +#define _3proxy_sem_init_timed(x, count, maxcount) _3proxy_sem_init(x, (count), (maxcount)) +#define _3proxy_sem_timedlock(x, ms) (WaitForSingleObject(x, ms) != WAIT_OBJECT_0) +#define _3proxy_sem_destroy(x) CloseHandle(x) #else -int _3proxy_sem_init_f(_3proxy_sem_t *sem, unsigned count, unsigned maxcount); +int _3proxy_sem_init_f(_3proxy_sem_t *sem, unsigned count, unsigned maxcount, int timed); void _3proxy_sem_lock_f(_3proxy_sem_t *sem); +int _3proxy_sem_timedlock_f(_3proxy_sem_t *sem, unsigned ms); void _3proxy_sem_unlock_f(_3proxy_sem_t *sem); -#define _3proxy_sem_init(x, count, maxcount) _3proxy_sem_init_f(&x, (count), (maxcount)) +void _3proxy_sem_destroy_f(_3proxy_sem_t *sem); +#define _3proxy_sem_init(x, count, maxcount) _3proxy_sem_init_f(&x, (count), (maxcount), 0) +/* for a semaphore waited on with a timeout: the wait is not stretched or cut + short by the system time being changed, where the platform allows it */ +#define _3proxy_sem_init_timed(x, count, maxcount) _3proxy_sem_init_f(&x, (count), (maxcount), 1) #define _3proxy_sem_lock(x) _3proxy_sem_lock_f(&x) +#define _3proxy_sem_timedlock(x, ms) _3proxy_sem_timedlock_f(&x, (ms)) #define _3proxy_sem_unlock(x) _3proxy_sem_unlock_f(&x) +#define _3proxy_sem_destroy(x) _3proxy_sem_destroy_f(&x) #endif diff --git a/src/proxymain.c b/src/proxymain.c index 7d0e4bd..be3d6dd 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -308,6 +308,7 @@ int MODULEMAINFUNC (int argc, char** argv){ FILE *fp = NULL; struct linger lg; int nlog = 5000; + int haveslot = 0, nfull = 0; #ifdef __linux__ int saved_nsfd = -1; #endif @@ -990,14 +991,19 @@ int MODULEMAINFUNC (int argc, char** argv){ for (;;) { for(;;){ - while((conf.paused == srv.paused && srv.childcount >= srv.maxchild)){ - nlog++; - if(!srv.silent && nlog > 5000) { + /* 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 && conf.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); - nlog = 0; } - usleep(SLEEPTIME); } if (iscbc) break; if (conf.paused != srv.paused) break; @@ -1213,6 +1219,7 @@ int MODULEMAINFUNC (int argc, char** argv){ } else { srv.childcount++; + haveslot = 0; newparam->threadid = (uint64_t)thread; } _3proxy_mutex_unlock(&srv.counter_mutex); @@ -1230,6 +1237,8 @@ int MODULEMAINFUNC (int argc, char** argv){ #endif if(!srv.silent) srv.logfunc(&defparam, (unsigned char *)"Exiting thread"); + /* srvfree() waits for every slot to come back, this one included */ + if(haveslot) _3proxy_sem_unlock(srv.childslots); srvfree(&srv); #ifndef _WIN32 @@ -1284,6 +1293,7 @@ void srvinit(struct srvparam * srv, struct clientparam *param){ param->remsock = param->clisock = param->ctrlsock = param->ctrlsocksrv = INVALID_SOCKET; *SAFAMILY(¶m->req) = *SAFAMILY(¶m->sinsl) = *SAFAMILY(¶m->sinsr) = *SAFAMILY(¶m->sincr) = *SAFAMILY(¶m->sincl) = AF_INET; _3proxy_mutex_init(&srv->counter_mutex); + (void)_3proxy_sem_init_timed(srv->childslots, srv->maxchild, srv->maxchild); #ifndef NOUDPMAIN if(!udpinited){ (void)_3proxy_sem_init(udpinit, 1, 1); @@ -1329,6 +1339,8 @@ void srvinit2(struct srvparam * srv, struct clientparam *param){ } void srvfree(struct srvparam * srv){ + int i; + if(srv->srvsock != INVALID_SOCKET) { so._closesocket(srv->so.state, srv->srvsock); #ifdef WITH_UN @@ -1339,16 +1351,13 @@ void srvfree(struct srvparam * srv){ if(srv->cbsock != INVALID_SOCKET) so._closesocket(srv->so.state, srv->cbsock); srv->cbsock = INVALID_SOCKET; srv->service = S_ZOMBIE; - /* under the mutex, so the last child has let go of it before it is destroyed */ - for(;;){ - struct clientparam *child; - - _3proxy_mutex_lock(&srv->counter_mutex); - child = srv->child; - _3proxy_mutex_unlock(&srv->counter_mutex); - if(!child) break; - usleep(SLEEPTIME * 100); - } + /* Every child returns its slot as the last thing it does with the service, + so once all of them are back no child is left. A child posts its slot + holding the mutex: taking the mutex once more waits for the last one to + have let go of both before they are destroyed. */ + for(i = srv->maxchild; i; i--) _3proxy_sem_lock(srv->childslots); + _3proxy_mutex_lock(&srv->counter_mutex); + _3proxy_mutex_unlock(&srv->counter_mutex); #ifndef STDMAIN if(srv->filter){ while(srv->nfilters){ @@ -1367,6 +1376,7 @@ void srvfree(struct srvparam * srv){ if(srv->authfuncs)freeauth(srv->authfuncs); #endif _3proxy_mutex_destroy(&srv->counter_mutex); + _3proxy_sem_destroy(srv->childslots); if(srv->target) free(srv->target); if(srv->logtarget) free(srv->logtarget); if(srv->logformat) free(srv->logformat); @@ -1454,6 +1464,9 @@ void freeparam(struct clientparam * param) { param->next->prev = param->prev; } (param->srv->childcount)--; + /* 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); } diff --git a/src/sem.c b/src/sem.c new file mode 100644 index 0000000..8f00acc --- /dev/null +++ b/src/sem.c @@ -0,0 +1,110 @@ +/* + 3APA3A simplest proxy server + (c) 2002-2026 by Vladimir Dubrovin + + please read License Agreement + +*/ + +/* Counting semaphores for POSIX systems, built on a mutex and a condition + variable: unnamed POSIX semaphores are not available everywhere (macOS) + and have no timed wait there. Windows uses its own semaphores, see proxy.h. */ + +#include "proxy.h" + +#ifndef _WIN32 +/* macOS has CLOCK_MONOTONIC but no pthread_condattr_setclock(); a relative + wait is used there instead, which the system time does not affect either */ +#if defined(CLOCK_MONOTONIC) && !defined(__APPLE__) +#define SEM_MONOTONIC +#endif + +int _3proxy_sem_init_f(_3proxy_sem_t *sem, unsigned count, unsigned maxcount, int timed){ + int res; + + sem->count = count; + sem->maxcount = maxcount; + sem->monotonic = 0; + if(pthread_mutex_init(&sem->mutex, NULL)) return 1; +#ifdef SEM_MONOTONIC + if(timed){ + pthread_condattr_t attr; + + if(!pthread_condattr_init(&attr)){ + if(!pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) sem->monotonic = 1; + res = pthread_cond_init(&sem->cond, &attr); + pthread_condattr_destroy(&attr); + } + else res = pthread_cond_init(&sem->cond, NULL); + } + else +#endif + res = pthread_cond_init(&sem->cond, NULL); + if(res){ + pthread_mutex_destroy(&sem->mutex); + return 1; + } + return 0; +} + +void _3proxy_sem_lock_f(_3proxy_sem_t *sem){ + pthread_mutex_lock(&sem->mutex); + while(!sem->count) pthread_cond_wait(&sem->cond, &sem->mutex); + sem->count--; + pthread_mutex_unlock(&sem->mutex); +} + +/* Returns 0 with the semaphore taken, nonzero if ms passed first. */ +int _3proxy_sem_timedlock_f(_3proxy_sem_t *sem, unsigned ms){ + struct timespec ts; + int res = 0; + +#ifdef __APPLE__ + ts.tv_sec = ms / 1000; + ts.tv_nsec = (long)(ms % 1000) * 1000000; + pthread_mutex_lock(&sem->mutex); + while(!sem->count && !res) res = pthread_cond_timedwait_relative_np(&sem->cond, &sem->mutex, &ts); +#else + /* the deadline is on the clock the condition variable was set up with */ +#ifdef SEM_MONOTONIC + if(sem->monotonic) clock_gettime(CLOCK_MONOTONIC, &ts); + else +#endif + { + struct timeval tv; + + gettimeofday(&tv, NULL); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + } + ts.tv_sec += ms / 1000; + ts.tv_nsec += (long)(ms % 1000) * 1000000; + if(ts.tv_nsec >= 1000000000){ + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + pthread_mutex_lock(&sem->mutex); + while(!sem->count && !res) res = pthread_cond_timedwait(&sem->cond, &sem->mutex, &ts); +#endif + if(sem->count){ + sem->count--; + res = 0; + } + pthread_mutex_unlock(&sem->mutex); + return res; +} + +void _3proxy_sem_unlock_f(_3proxy_sem_t *sem){ + pthread_mutex_lock(&sem->mutex); + if(sem->count < sem->maxcount){ + sem->count++; + pthread_cond_signal(&sem->cond); + } + pthread_mutex_unlock(&sem->mutex); +} + +void _3proxy_sem_destroy_f(_3proxy_sem_t *sem){ + pthread_cond_destroy(&sem->cond); + pthread_mutex_destroy(&sem->mutex); +} +#endif diff --git a/src/structures.h b/src/structures.h index 1802564..42e6640 100644 --- a/src/structures.h +++ b/src/structures.h @@ -47,6 +47,7 @@ typedef struct _3proxy_sem_s { pthread_cond_t cond; unsigned count; unsigned maxcount; + int monotonic; /* timed waits measured on CLOCK_MONOTONIC */ } _3proxy_sem_t; #else #include @@ -635,6 +636,7 @@ struct srvparam { #endif PROXYSOCKADDRTYPE extsa; _3proxy_mutex_t counter_mutex; + _3proxy_sem_t childslots; /* maxchild of them, one held by each child */ struct pollfd fds; FILE *stdlog; unsigned char * target; diff --git a/tests/README.md b/tests/README.md index 02356a1..03efb58 100644 --- a/tests/README.md +++ b/tests/README.md @@ -69,7 +69,7 @@ means to stand on its own should start with one - otherwise an earlier ## What is not covered yet -41 of the 112 configuration commands appear in a test, and the count says +42 of the 112 configuration commands appear in a test, and the count says nothing about service options: the IPv6 case, for instance, exercises -4, -6, -46, -64 and -i without adding a command to it. What follows is roughly the order worth working through: how much of the product a gap @@ -78,12 +78,13 @@ covers, and how much of a fixture it needs. ### Traffic limits and accounting `bandlimin` `bandlimout` `nobandlimin` `nobandlimout` `connlim` `noconnlim` -`countin` `countout` `countall` and the `no*` forms, `maxconn`. +`countin` `countout` `countall` and the `no*` forms. Cheap and worth doing first: `data?size=` and a stopwatch measure a bandwidth limit, and the admin counters page already shows what a counter holds. `countin` appears in a configuration today but nothing checks that it -counts. `connlim` and `maxconn` need concurrent connections. +counts. `connlim` needs concurrent connections, held open the way the +`maxconn` case holds them. ### The mail proxies diff --git a/tests/cases/maxconn.py b/tests/cases/maxconn.py new file mode 100644 index 0000000..8c7ca13 --- /dev/null +++ b/tests/cases/maxconn.py @@ -0,0 +1,107 @@ +"""maxconn: a service serves at most that many connections at once, and a +connection over the limit waits for one of them to finish.""" + +import os +import signal +import socket +import time + + +def run(t): + # The origin runs in a process of its own, so the limit only applies to + # the mapper in front of it. + origin = t.free_port() + t.start("maxconn_origin", f""" + auth iponly + allow * + http echo * /echo** + httpsrv -p{origin} + """, ports=[origin]) + + mapped = t.free_port() + server = t.start("maxconn", f""" + log + maxconn 2 + auth iponly + allow * + tcppm {mapped} 127.0.0.1 {origin} + """, ports=[mapped]) + + def connect(): + sock = socket.create_connection(("127.0.0.1", mapped), 5) + opened.append(sock) + return sock + + def served(sock, timeout): + """Ask over a kept alive connection; True once the whole reply is in, + so the connection is ready for the next request.""" + sock.sendall(b"GET /echo HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n") + sock.settimeout(timeout) + data = b"" + try: + while b"\r\n\r\n" not in data: + chunk = sock.recv(4096) + if not chunk: + return False + data += chunk + head, body = data.split(b"\r\n\r\n", 1) + length = 0 + for line in head.split(b"\r\n")[1:]: + name, _, value = line.partition(b":") + if name.strip().lower() == b"content-length": + length = int(value) + while len(body) < length: + chunk = sock.recv(4096) + if not chunk: + return False + body += chunk + return head.startswith(b"HTTP/1.1 200") + except OSError: + return False + + opened = [] + try: + # the probe that saw the port open holds a slot until its session + # ends, so wait for both slots to be free before taking them + deadline = time.time() + 5 + while True: + first, second = connect(), connect() + both = served(first, 1) and served(second, 1) + if both or time.time() > deadline: + break + first.close() + second.close() + t.eq(True, both, "two connections are served under maxconn 2") + + third = connect() + t.eq(False, served(third, 1.5), + "a third connection waits while both slots are held") + t.contains(t.wait_output(server, "too many connected clients"), + "too many connected clients", + "a service at its limit says so in the log") + + first.close() + t.eq(True, served(third, 5), + "the waiting connection is served once a slot is freed") + t.eq(True, served(second, 5), + "a connection holding a slot keeps being served") + + if os.name == "nt": + t.skip("reload while at the limit (no SIGUSR1 on Windows)") + else: + waiting = connect() + t.eq(False, served(waiting, 1), + "the limit holds again with two connections open") + server.proc.send_signal(signal.SIGUSR1) + # A connection made the instant the signal arrives can land on + # the listener being replaced and be reset; give the new service + # the moment it needs. + time.sleep(1.5) + fresh = connect() + t.eq(True, served(fresh, 5), + "a reload while the limit is reached starts a service with " + "its slots free") + t.eq(None, server.proc.poll(), "the process survives the reload") + finally: + for sock in opened: + sock.close()