3proxy/src/proxymain.c
Vladimir Dubrovin f9b56fc15d SIGPIPE ignored before any service starts, and in standalone services
3proxy ignored SIGPIPE only after readconfig(), which starts the
services: a client gone while written to in the meantime killed the
process (as auto's case saw on macOS). Standalone services never ignored
it. Sends pass no MSG_NOSIGNAL, so this is the only guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 13:56:38 +03:00

262 lines
7.9 KiB
C

/*
3APA3A simplest proxy server
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
please read License Agreement
*/
#include "proxy.h"
#ifdef __linux__
#include <sched.h>
#endif
#ifndef MODULEMAINFUNC
#define STDMAIN
#endif
/* A service between its preparation and its run. The preparation, done
by the thread reading the configuration, parses the service's options and
sets it up, so whatever is wrong with them is reported there; listening,
which may wait for its address for long, and the service itself are left
to the service's own thread. */
struct srvstart {
struct srvparam srv;
struct clientparam defparam;
unsigned char *cbc_string, *cbl_string;
int isudp, iscbc, iscbl;
const struct udpservice *udp;
#ifndef _WIN32
pthread_attr_t pa;
#endif
#ifdef STDMAIN
#ifndef _WIN32
int inetd;
#endif
#endif
};
struct listener;
struct udpstate;
/* proxymain.c is one unit, built for 3proxy (MODULEMAINFUNC) and, included
by a service's own file (WITHMAIN), for a standalone service; its parts:
mainconf.c services from their configuration lines
mainlisten.c listening sockets, shared and taken over on a reload
mainclient.c threads for the clients of TCP services, and filters
maintcp.c the main loop of a TCP service
mainudp.c the main loop of a UDP service, in its one thread
Their functions of file scope are declared here, so that they can be in
any order. */
static void srvdiscard(struct srvstart *s);
static void srvunlink(struct srvparam *srv);
static int srvprepare(struct srvstart *s, int argc, char **argv);
static int samestr(const char *a, const char *b);
static int sameaddress(struct srvstart *s);
static void * itcopy (void * from, size_t size);
static void listenercount(struct srvparam *srv, int delta);
static void unbound(PROXYSOCKADDRTYPE *sa, uint64_t *dev, uint64_t *ino);
static void ununlink(PROXYSOCKADDRTYPE *sa, uint64_t dev, uint64_t ino);
static int srvlisten(struct srvstart *s);
static int srvopen(struct srvstart *s);
static int bindtimeopt(const char *name);
static int sameopts(struct listener *l, struct srvparam *srv);
static void takeopts(struct listener *l, struct srvparam *srv);
static int samelistener(struct listener *l, struct srvparam *srv, int isudp);
static int listenerfor(struct listener *l, struct srvstart *s);
static int listenerhas(struct listener *l, struct extparam *config);
static void listenerstop(struct srvparam *srv);
static void listenerrelease(struct srvparam *srv);
static void slotrelease(struct srvparam *srv);
static void serveparam(struct clientparam *param);
static void freeworker(struct clientparam *param);
static unsigned poollimit(void);
static int poolpark(struct clientparam *param);
static int srvspawn(struct srvstart *s, SOCKET client_sock);
static void filterswap(struct clientparam *param, unsigned char **cur, unsigned char *now);
static int cblisten(struct srvstart *s);
static int tcpnext(struct srvstart *s, SOCKET *client, int *nlog);
static void udpkey2hash(const struct hashtable *ht, void *index, uint8_t *hash);
static void udpjoin(struct srvparam *srv, struct clientparam *param, int new);
static struct udpsession *udpnew(struct udpstate *st);
static void udpend(struct udpsession *ss);
static void udpendall(struct udpsession *list, int res);
static void udpadd(struct udpstate *st, struct udpsession *ss);
static void udpremove(struct udpstate *st, struct udpsession *ss);
static void udprefresh(struct udpsession *ss, uint64_t now);
static void udpheapset(struct udpstate *st, int i, struct udpsession *ss);
static void udpheapup(struct udpstate *st, int i);
static void udpheapdown(struct udpstate *st, int i);
static int udpheapadd(struct udpstate *st, struct udpsession *ss);
static void udpheapdel(struct udpstate *st, struct udpsession *ss);
static void udpafter(struct udpstate *st, struct udpsession *ss, int end);
static void udpexpire(struct udpstate *st);
static void udpreap(struct udpstate *st);
static void udphandover(struct udpstate *st);
static void udpadopt(struct udpstate *st);
static int udpolder(struct srvparam *srv);
static void udpclients(struct udpstate *st);
static int udploop(struct srvstart *s);
#ifndef NOTCPMAIN
static int tcploop(struct srvstart *s, int *haveslot);
#endif
static int srvrun(struct srvstart *s);
/* NOUDPMAIN and NOTCPMAIN leave out what only UDP or only TCP services
need, for a standalone service of the other kind. */
#include "mainconf.c"
#include "mainlisten.c"
#include "mainclient.c"
#include "maintcp.c"
#include "mainudp.c"
static int srvrun(struct srvstart *s){
struct srvparam *srv = &s->srv;
struct clientparam *defparam = &s->defparam;
int res = 0, haveslot = 0;
/* -r: a TCP service connects out for each client instead of listening */
int connectback = !s->isudp && s->iscbc;
#ifndef _WIN32
pthread_attr_init(&s->pa);
pthread_attr_setstacksize(&s->pa,threadstacksize(srv->stacksize));
pthread_attr_setdetachstate(&s->pa,PTHREAD_CREATE_DETACHED);
#ifndef STDMAIN
{
sigset_t mask;
sigfillset(&mask);
pthread_sigmask(SIG_SETMASK, &mask, NULL);
}
#endif
#endif
if(!connectback && (res = srvlisten(s))) goto EXIT;
#ifndef NOTCPMAIN
if(!s->isudp && s->iscbl && (res = cblisten(s))) goto EXIT;
#endif
/* counted for the automatic cap of the thread pool while it runs */
_3proxy_mutex_lock(&tpool.mutex);
tpool.services++;
tpool.maxchild += srv->maxchild;
_3proxy_mutex_unlock(&tpool.mutex);
#ifndef NOUDPMAIN
if(s->isudp) res = udploop(s);
else
#endif
#ifndef NOTCPMAIN
res = tcploop(s, &haveslot);
#endif
_3proxy_mutex_lock(&tpool.mutex);
tpool.services--;
tpool.maxchild -= srv->maxchild;
_3proxy_mutex_unlock(&tpool.mutex);
EXIT:
#ifndef STDMAIN
_3proxy_mutex_lock(&config_mutex);
srvunlink(srv);
_3proxy_mutex_unlock(&config_mutex);
#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);
srvdiscard(s);
#ifndef _WIN32
pthread_attr_destroy(&s->pa);
#endif
return res;
}
#ifdef STDMAIN
#ifndef _WINCE
int main (int argc, char** argv){
#else
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow){
int argc;
char ** argv;
WNDCLASS wc;
HWND hwnd = 0;
#endif
struct srvstart s;
int res;
#ifndef _WIN32
int opt = 1;
struct linger lg;
struct clientparam *newparam;
#endif
#ifdef _WINCE
argc = ceparseargs((char *)lpCmdLine);
argv = ceargv;
if(FindWindow(lpCmdLine, lpCmdLine)) return 0;
ZeroMemory(&wc,sizeof(wc));
wc.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
wc.hInstance=hInstance;
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.lpfnWndProc=DefWindowProc;
wc.style=CS_HREDRAW|CS_VREDRAW;
wc.lpszClassName=lpCmdLine;
RegisterClass(&wc);
hwnd = CreateWindowEx(WS_EX_TOOLWINDOW,lpCmdLine,lpCmdLine,WS_VISIBLE|WS_POPUP,0,0,0,0,0,0,hInstance,0);
#endif
#ifdef _WIN32
WSADATA wd;
WSAStartup(MAKEWORD( 1, 1 ), &wd);
#endif
_3proxy_mutex_init(&tpool.mutex);
loginit();
#ifndef _WIN32
/* a client gone while it is written to is an error, not the end */
signal(SIGPIPE, SIG_IGN);
#endif
if((res = srvprepare(&s, argc, argv))) return res;
/* errors go to the service's log once there is no standard error */
logerrset(s.srv.logger);
#ifndef _WIN32
if(s.inetd) {
fcntl(0,F_SETFL,O_NONBLOCK | fcntl(0,F_GETFL));
if(!s.isudp){
lg.l_onoff = 1;
lg.l_linger = s.srv.timeouts[STRING_L];
s.srv.so._setsockopt(s.srv.so.state, 0, SOL_SOCKET, SO_LINGER, (unsigned char *)&lg, sizeof(lg));
s.srv.so._setsockopt(s.srv.so.state, 0, SOL_SOCKET, SO_OOBINLINE, (unsigned char *)&opt, sizeof(int));
}
s.defparam.clisock = 0;
if(! (newparam = malloc (sizeof(s.defparam)))){
return 2;
};
memcpy(newparam, &s.defparam, offsetof(struct clientparam, filters));
newparam->clibuf = newparam->clibufdata;
newparam->srvbuf = newparam->srvbufdata;
newparam->clibufsize = newparam->srvbufsize = SRVBUFSIZE(&s.srv);
childfunc(newparam);
free(newparam);
return 0;
}
#endif
res = srvrun(&s);
pooldrain();
return res;
}
#endif