3proxy/src/proxy.h
Vladimir Dubrovin 6f02ec2747 Take another local port when the one bound cannot reach the destination
extport binds a local port out of its range and connects from it. A port
which is free to bind may still be half of a pair which is closing from an
earlier connection to the same destination, and Windows fails the connect
with WSAEADDRINUSE where other systems fail the bind and fall back. The
request then failed with error 13 for a reason another port would have
avoided, which is what the range is searched for in the first place.

Retry the connect with another port from the range. Only for that error, so
a destination which is simply unreachable still costs one attempt, and out of
the same ten tries the bind already spends, so the attempts parentretries
makes are not multiplied. EADDRINUSE joins the errno values proxy.h maps to
their Winsock numbers, without which the test would never match on Windows.

parent_ports gave its three services one window between them, which is how
the case met the collision on the Windows runner. Each has its own now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:03:15 +03:00

503 lines
17 KiB
C

/*
3APA3A simplest proxy server
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
please read License Agreement
*/
#define COPYRIGHT "(c)3APA3A, Vladimir Dubrovin & 3proxy.org\n"\
"Documentation and sources: https://3proxy.org/\n"\
"Please read license agreement in \'copying\' file.\n"\
"You may not use this program without accepting license agreement"
#ifndef _3PROXY_H_
#define _3PROXY_H_
#include "version.h"
#ifndef WITH_SSL
#ifndef NORADIUS
#define NORADIUS
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#define MAXUSERNAME 128
#define _PASSWORD_LEN 256
#define MAXNSERVERS 5
#define DEFAULT_MAXCHILD 500
#define TCPBUFSIZE 65536
#define SRVBUFSIZE (param->srv->bufsize?param->srv->bufsize:((param->service == S_UDPPM)?UDPBUFSIZE:TCPBUFSIZE))
#ifdef _WIN32
#include <winsock2.h>
#include <sys/timeb.h>
#ifndef _WINCE
#include <io.h>
#else
#include <sys/unistd.h>
#endif
#include <process.h>
#define SASIZETYPE int
#define SHUT_RDWR SD_BOTH
#define SHUT_WR SD_SEND
#define SHUT_RD SD_RECEIVE
#else
#ifndef FD_SETSIZE
#define FD_SETSIZE 4096
#endif
#include <signal.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#include <syslog.h>
#include <errno.h>
#endif
#ifdef __CYGWIN__
#include <windows.h>
#define daemonize() FreeConsole()
#define SLEEPTIME 1000
#undef _WIN32
#elif _WIN32
#ifdef errno
#undef errno
#endif
#define errno WSAGetLastError()
#ifdef EAGAIN
#undef EAGAIN
#endif
#define EAGAIN WSAEWOULDBLOCK
#ifdef EINTR
#undef EINTR
#endif
#ifndef EINPROGRESS
#define EINPROGRESS WSAEWOULDBLOCK
#endif
#ifdef EADDRINUSE
#undef EADDRINUSE
#endif
#define EADDRINUSE WSAEADDRINUSE
#define EINTR WSAEWOULDBLOCK
#define SLEEPTIME 1
#define usleep Sleep
#define pthread_self GetCurrentThreadId
#define getpid GetCurrentProcessId
#define pthread_t unsigned
#ifndef _WINCE
#define daemonize() FreeConsole()
#else
#define daemonize()
#endif
#define socket(x, y, z) WSASocket(x, y, z, NULL, 0, 0)
#define accept(x, y, z) WSAAccept(x, y, z, NULL, 0)
#define ftruncate chsize
#else
#include <pthread.h>
#ifndef PTHREAD_STACK_MIN
#define PTHREAD_STACK_MIN 32768
#endif
void daemonize(void);
#define SLEEPTIME 1000
#ifndef O_BINARY
#define O_BINARY 0
#endif
#endif
/* Keeps a callee with a large frame out of the caller's frame, e.g. the
8K log buffer of logstdout() out of dolog(), which calls it in a branch
taken only when there is no service.
*/
#if defined(__GNUC__)
#define NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
#define NOINLINE __declspec(noinline)
#else
#define NOINLINE
#endif
/* Thread stack size, stacksize command value is added to it. BSD libc uses
significantly more stack, e.g. in vfprintf() called by syslog().
*/
#ifndef BASESTACKSIZE
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
#define BASESTACKSIZE 65536
#else
#define BASESTACKSIZE 49152
#endif
#endif
/* wolfSSL reserves around 48K of static thread-local storage. glibc counts
that against the thread stack, so pthread_create() fails with EINVAL and
no thread starts at all. musl places the block next to the stack instead
of inside it and needs nothing extra, and OpenSSL has no static TLS.
musl identifies itself by no macro of its own, but it does not define
__GLIBC__, which any libc header pulled in above would have set.
*/
#ifndef TLSSTACKSIZE
#if defined(__linux__) && !defined(__GLIBC__)
#define TLSSTACKSIZE 0
#elif defined(WITH_WOLFSSL)
#define TLSSTACKSIZE 49152
#else
#define TLSSTACKSIZE 0
#endif
#endif
/* A UDP service answers from the address the client sent to, so the reply has
to leave the listening socket. Sharing that socket with the request handler
is the simple way, and what every Unix build does.
Older Windows cannot have two operations in flight on one socket, so a
build for it binds a second socket to the same address instead, which needs
SO_REUSEADDR on the listening socket as well. A build for those versions
asks for that with NO_SHARE_UDP_SOCKET, as Makefile.watcom does.
*/
#ifndef SHARE_UDP_SOCKET
#ifndef NO_SHARE_UDP_SOCKET
#define SHARE_UDP_SOCKET
#endif
#endif
#ifndef _WIN32
size_t threadstacksize(int extra);
#endif
#ifdef WITH_ODBC
#ifndef _WIN32
#include <sqltypes.h>
#endif
#include <sql.h>
#include <sqlext.h>
#endif
#ifdef _WIN32
#ifndef strcasecmp
#define strcasecmp stricmp
#endif
#ifndef strncasecmp
#define strncasecmp strnicmp
#endif
#define seterrno3(x) _set_errno(x)
#else
#define seterrno3(x) (errno = x)
#endif
#ifndef SOCKET_ERROR
#define SOCKET_ERROR -1
#endif
#ifndef isnumber
#define isnumber(n) (n >= '0' && n <= '9')
#endif
#ifndef ishex
#define ishex(n) ((n >= '0' && n <= '9') || (n >= 'a' && n<='f') || (n >= 'A' && n <= 'F'))
#endif
#define isallowed(n) ((n >= '0' && n <= '9') || (n >= 'a' && n <= 'z') || (n >= 'A' && n <= 'Z') || (n >= '*' && n <= '/') || n == '_')
#include "structures.h"
#define MAXRADIUS 5
#define DEFLOGFORMAT "G%y%m%d%H%M%S.%. %p %E %U %C:%c %R:%r %O %I %h %T"
#ifdef _TIME64_T_DEFINED
#ifdef _MAX__TIME64_T
#define MAX_COUNTER_TIME (_MAX__TIME64_T)
#elif defined (MAX__TIME64_T)
#define MAX_COUNTER_TIME (MAX__TIME64_T)
#else
#define MAX_COUNTER_TIME (0x793406fff)
#endif
#else
#define MAX_COUNTER_TIME ((sizeof(time_t)>4)?(time_t)0x793406fff:(time_t)0x7fffffff)
#endif
extern RESOLVFUNC resolvfunc;
extern int wday;
extern time_t basetime;
extern int timetoexit;
extern struct extparam conf;
extern int timeouts[12];
int sockmap(struct clientparam * param, int timeo, int usesplice);
int udpsockmap(struct clientparam * param, int timeo);
int udpbind(struct clientparam * param);
int socks5_setaddr(int family, int atyp, const unsigned char *addr, PROXYSOCKADDRTYPE *sa);
#ifdef __linux__
int switch_ns(struct srvparam *srv, int target_fd);
#endif
int socksend(struct clientparam *param, SOCKET sock, unsigned char * buf, int bufsize, int to);
int socksendto(struct clientparam *param, SOCKET sock, struct sockaddr * sin, unsigned char * buf, int bufsize, int to);
int sockrecvfrom(struct clientparam *param, SOCKET sock, struct sockaddr * sin, unsigned char * buf, int bufsize, int to);
int sockgetcharcli(struct clientparam * param, int timeosec, int timeousec);
int sockgetcharsrv(struct clientparam * param, int timeosec, int timeousec);
unsigned long sockfillbuffcli(struct clientparam * param, unsigned long size, int timeosec);
unsigned long sockfillbuffsrv(struct clientparam * param, unsigned long size, int timeosec);
int sockgetlinebuf(struct clientparam * param, DIRECTION which, unsigned char * buf, int bufsize, int delim, int to);
int getmultiline(struct clientparam * param, DIRECTION which, unsigned char * buf, int bufsize, const char *cap, int *found);
int hascap(const unsigned char *line, const char *cap);
void dolog(struct clientparam * param, const unsigned char *s);
int dobuf(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec);
int dobuf2(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec, struct tm* tm, char * format);
extern FILE * stdlog;
NOINLINE void logstdout(struct clientparam * param, const unsigned char *s);
void logsyslog(struct clientparam * param, const unsigned char *s);
void lognone(struct clientparam * param, const unsigned char *s);
void logradius(struct clientparam * param, const unsigned char *s);
#ifndef NOSQL
void logsql(struct clientparam * param, const unsigned char *s);
int init_sql(char * s);
void close_sql();
#endif
int doconnect(struct clientparam * param);
int alwaysauth(struct clientparam * param);
int ipauth(struct clientparam * param);
int doauth(struct clientparam * param);
int strongauth(struct clientparam * param);
void trafcountfunc(struct clientparam *param);
unsigned bandlimitfunc(struct clientparam *param, unsigned nbytesin, unsigned nbytesout);
int handleredirect(struct clientparam * param, struct ace * acentry);
int scanaddr(const unsigned char *s, uint32_t * ip, uint32_t * mask);
int myinet_ntop(int af, void *src, char *dst, socklen_t size);
extern struct nserver nservers[MAXNSERVERS];
extern struct nserver authnserver;
uint32_t getip(unsigned char *name);
uint32_t getip46(int family, unsigned char *name, struct sockaddr *sa);
int afdetect(unsigned char *name);
uint32_t myresolver(int, unsigned char *, unsigned char *);
uint32_t fakeresolver (int, unsigned char *, unsigned char*);
void freeparam(struct clientparam * param);
void clearstat(struct clientparam * param);
void dumpcounters(struct trafcount *tl, int counterd);
int startconnlims (struct clientparam *param);
void stopconnlims (struct clientparam *param);
int socks5_udp_build_hdr(unsigned char *buf, PROXYSOCKADDRTYPE *addr);
extern struct auth authfuncs[];
int reload (void);
extern int paused;
extern int demon;
unsigned char * mycrypt(const unsigned char *key, const unsigned char *salt, unsigned char *buf);
unsigned char * ntpwdhash (unsigned char *szHash, const unsigned char *szPassword, int tohex);
int de64 (const unsigned char *in, unsigned char *out, int maxlen);
unsigned char* en64 (const unsigned char *in, unsigned char *out, int inlen, int outsize);
void tohex(unsigned char *in, unsigned char *out, int len);
void fromhex(unsigned char *in, unsigned char *out, int len);
extern _3proxy_sem_t udpinit;
#ifdef _WIN32
#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)
#else
int _3proxy_sem_init_f(_3proxy_sem_t *sem, unsigned count, unsigned maxcount);
void _3proxy_sem_lock_f(_3proxy_sem_t *sem);
void _3proxy_sem_unlock_f(_3proxy_sem_t *sem);
#define _3proxy_sem_init(x, count, maxcount) _3proxy_sem_init_f(&x, (count), (maxcount))
#define _3proxy_sem_lock(x) _3proxy_sem_lock_f(&x)
#define _3proxy_sem_unlock(x) _3proxy_sem_unlock_f(&x)
#endif
int ftplogin(struct clientparam *param, char *buf, int *inbuf);
int ftpcd(struct clientparam *param, unsigned char* path, char *buf, int *inbuf);
int ftpsyst(struct clientparam *param, unsigned char *buf, unsigned len);
int ftppwd(struct clientparam *param, unsigned char *buf, unsigned len);
int ftptype(struct clientparam *param, unsigned char* f_type);
int ftpres(struct clientparam *param, unsigned char * buf, int len);
SOCKET ftpcommand(struct clientparam *param, unsigned char * command, unsigned char *arg);
int text2unicode(const char * text, char * buf, int buflen);
void unicode2text(const char *unicode, char * buf, int len);
void genchallenge(struct clientparam *param, char * challenge, char *buf);
void mschap(const unsigned char *win_password,
const unsigned char *challenge, unsigned char *response);
void destroyhashtable(struct hashtable *ht);
int inithashtable(struct hashtable *ht, unsigned tablesize, unsigned poolsize, unsigned growlimit);
void hashadd(struct hashtable *ht, void* name, void* value, time_t expires);
void hashdelete(struct hashtable *ht, void* name);
int hashresolv(struct hashtable *ht, void* name, void* value, uint32_t *ttl);
int parsehost(int family, unsigned char *host, struct sockaddr *sa);
int parsehostname(char *hostname, struct clientparam *param, uint16_t port);
int parseusername(char *username, struct clientparam *param, int extpasswd);
int parseconnusername(char *username, struct clientparam *param, int extpasswd, uint16_t port);
int ACLmatches(struct ace* acentry, struct clientparam * param);
int checkACL(struct clientparam * param);
char * aceaction (int action);
extern int havelog;
uint32_t udpresolve(int af, unsigned char * name, unsigned char * value, uint32_t *retttl, struct clientparam* param, int makeauth);
struct ace * copyacl (struct ace *ac);
struct auth * copyauth (struct auth *);
void * itfree(void *data, void * retval);
void freeacl(struct ace *ac);
void freeauth(struct auth *);
void freefilter(struct filter *filter);
void freeconf(struct extparam *confp);
struct passwords * copypwl (struct passwords *pwl);
void freepwl(struct passwords *pw);
void copyfilter(struct filter *, struct srvparam *srv);
FILTER_ACTION makefilters (struct srvparam *srv, struct clientparam *param);
FILTER_ACTION handlereqfilters(struct clientparam *param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p);
FILTER_ACTION handleconnectflt(struct clientparam *param);
FILTER_ACTION handleafterauthflt(struct clientparam *param);
FILTER_ACTION handlehdrfilterscli(struct clientparam *param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p);
FILTER_ACTION handlehdrfilterssrv(struct clientparam *param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p);
FILTER_ACTION handlepredatflt(struct clientparam *param);
FILTER_ACTION handledatfltcli(struct clientparam *param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p);
FILTER_ACTION handledatfltsrv(struct clientparam *param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p);
void srvinit(struct srvparam * srv, struct clientparam *param);
void srvinit2(struct srvparam * srv, struct clientparam *param);
void srvfree(struct srvparam * srv);
unsigned char * dologname (unsigned char *buf, unsigned char *name, const unsigned char *ext, ROTATION lt, time_t t);
int readconfig(FILE * fp);
void initcommands(void);
int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to);
int bindwithrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range);
#ifdef WITH_PCRE
/* One regular expression implementation for the whole program: the pcre
commands and every pattern that carries a pcre: prefix. */
void * pcre_pattern_compile(const unsigned char *pattern, char *errbuf, int errlen);
void pcre_pattern_free(void *re);
int pcre_pattern_match(void *re, const unsigned char *subject, struct capture *caps, int maxcaps);
#endif
int pushbackcli(struct clientparam * param, const unsigned char * data, int len);
int parsepattern(struct hostname *h, unsigned char *arg);
int parsepathpattern(struct hostname *h, unsigned char *arg);
int patternmatchcaps(const struct hostname *h, const unsigned char *str,
struct capture *caps, int *ncaps);
int patternmatch(const struct hostname *h, const unsigned char *str);
int patternmatchpos(const struct hostname *h, const unsigned char *str, int *start, int *len);
void applyportranges(struct clientparam * param, struct ace * acentry);
uint32_t myrand(void);
uint32_t murmurhash3(const void *key, int len, uint32_t seed);
extern char *copyright;
#define SERVICES 5
void * dnsprchild(struct clientparam * param);
void * pop3pchild(struct clientparam * param);
void * imappchild(struct clientparam * param);
void * smtppchild(struct clientparam * param);
void * proxychild(struct clientparam * param);
void * sockschild(struct clientparam * param);
void * tcppmchild(struct clientparam * param);
void * autochild(struct clientparam * param);
void * udppmchild(struct clientparam * param);
#ifdef WITH_HTTPSRV
int op_admin(struct httpreq *r, const unsigned char *params);
int op_admin_counters(struct httpreq *r, const unsigned char *params);
int op_admin_reload(struct httpreq *r, const unsigned char *params);
int op_admin_services(struct httpreq *r, const unsigned char *params);
#endif
#ifdef WITH_HTTPSRV
void * httpsrvchild(struct clientparam * param);
int httpopbyname(const unsigned char *name);
int httpchunk(struct clientparam *param, const char *buf, int len);
void freehttprules(struct httprule *rule);
#endif
void * ftpprchild(struct clientparam * param);
void * tlsprchild(struct clientparam * param);
/* Child functions return the child to redirect the request to, or NULL if
the request is complete. childfunc() calls them and releases param.
Recursive redirection, e.g. a socks service redirected to socks5, used to
be limited by the stack size only, MAXCHILDREDIRECTS limits it now.
*/
#define MAXCHILDREDIRECTS 16
void * childfunc(struct clientparam * param);
struct datatype;
struct dictionary;
struct node;
struct property;
extern _3proxy_mutex_t config_mutex;
extern _3proxy_mutex_t bandlim_mutex;
extern _3proxy_mutex_t connlim_mutex;
extern _3proxy_mutex_t tc_mutex;
extern _3proxy_mutex_t log_mutex;
extern _3proxy_mutex_t rad_mutex;
extern struct datatype datatypes[64];
extern struct commands commandhandlers[];
#ifdef WITHSPLICE
#define mapsocket(a,b) ((a->srv->usesplice && !a->ndatfilterssrv && !a->ndatfilterscli)?sockmap(a,b,1):sockmap(a,b,0))
#else
#define mapsocket(a,b) sockmap(a,b, 0)
#endif
#ifdef WITH_UN
void make_un(const unsigned char *path, struct sockaddr_un * sun);
#endif
extern struct radserver {
PROXYSOCKADDRTYPE authaddr, logaddr, localaddr;
} radiuslist[MAXRADIUS];
extern char radiussecret[64];
extern int nradservers;
extern struct socketoptions {
int opt;
char * optname;
} sockopts[];
void setopts(SOCKET s, int opts);
char * printopts(char *sep);
#ifdef _WINCE
char * CEToUnicode (const char *str);
int cesystem(const char *str);
int ceparseargs(const char *str);
extern char * ceargv[32];
#define system(S) cesystem(S)
#endif
#define WEBBANNERS 35
#endif