mirror of
https://github.com/3proxy/3proxy.git
synced 2026-08-26 09:55:48 +08:00
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
wolfSSL reserves about 48K of static thread-local storage. glibc counts that against the thread stack, so pthread_create() was refused with EINVAL and no service thread was ever created: the process started, listened on nothing, logged nothing and waited forever on the semaphore the thread was supposed to release. Every wolfSSL build on glibc was affected. Add that reserve to the thread stack when built against wolfSSL. musl puts the block beside the stack rather than inside it and needs nothing extra, and OpenSSL has no static TLS at all, so neither pays for it. Check the pthread_create() return value as well, so a thread that cannot be created reports itself and suggests stacksize instead of hanging.
970 lines
25 KiB
C
970 lines
25 KiB
C
/*
|
|
3APA3A simplest proxy server
|
|
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
|
|
|
please read License Agreement
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "proxy.h"
|
|
/* returns 0 on success, 1 if atyp is not supported for family, 2 if the
|
|
address is null. The address is set in both the 0 and the 2 case. */
|
|
int socks5_setaddr(int family, int atyp, const unsigned char *addr, PROXYSOCKADDRTYPE *sa){
|
|
int i, len = 4;
|
|
|
|
memset(sa, 0, sizeof(*sa));
|
|
if(atyp == 4){
|
|
#ifdef NOIPV6
|
|
return 1;
|
|
#else
|
|
if(family == 4) return 1;
|
|
len = 16;
|
|
*SAFAMILY(sa) = AF_INET6;
|
|
memcpy(SAADDR(sa), addr, 16);
|
|
#endif
|
|
}
|
|
#ifndef NOIPV6
|
|
else if(family == 6){
|
|
unsigned char prefix[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255};
|
|
|
|
*SAFAMILY(sa) = AF_INET6;
|
|
memcpy(SAADDR(sa), prefix, 12);
|
|
memcpy(12 + (unsigned char *)SAADDR(sa), addr, 4);
|
|
}
|
|
#endif
|
|
else {
|
|
*SAFAMILY(sa) = AF_INET;
|
|
memcpy(SAADDR(sa), addr, 4);
|
|
}
|
|
for(i = 0; i < len && !addr[i]; i++);
|
|
return (i == len)? 2 : 0;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
#include <sched.h>
|
|
|
|
int switch_ns(struct srvparam *srv, int target_fd) {
|
|
if(target_fd < 0) return 0;
|
|
if(srv->saved_nsfd >= 0 && setns(srv->saved_nsfd, CLONE_NEWNET)) return -1;
|
|
return setns(target_fd, CLONE_NEWNET);
|
|
}
|
|
#endif
|
|
|
|
|
|
char * copyright = COPYRIGHT;
|
|
|
|
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
|
|
# define MYRAND_ARC4RANDOM 1
|
|
#elif defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 36)))
|
|
# define MYRAND_ARC4RANDOM 1
|
|
#elif defined(__GLIBC__) && ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 25)))
|
|
# define MYRAND_FALLBACK 1
|
|
#elif defined(__linux__)
|
|
# define MYRAND_GETRANDOM 1
|
|
# include <sys/random.h>
|
|
#elif defined(_WIN32)
|
|
# if defined(__WATCOMC__)
|
|
# define MYRAND_CRYPTGENRANDOM 1
|
|
# include <wincrypt.h>
|
|
# else
|
|
# define MYRAND_BCRYPTGENRANDOM 1
|
|
# include <bcrypt.h>
|
|
# pragma comment(lib, "bcrypt.lib")
|
|
# endif
|
|
#else
|
|
# define MYRAND_FALLBACK 1
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
pthread_attr_t pa;
|
|
|
|
|
|
void daemonize(void){
|
|
pid_t pid = fork();
|
|
if(pid > 0) {
|
|
usleep(SLEEPTIME);
|
|
_exit(0);
|
|
}
|
|
if(pid < 0) {
|
|
perror("fork()");
|
|
return;
|
|
}
|
|
setsid();
|
|
}
|
|
|
|
#endif
|
|
|
|
unsigned char **stringtable = NULL;
|
|
|
|
#ifdef WITH_UN
|
|
void make_un(const unsigned char *path, struct sockaddr_un * sun){
|
|
memset(sun, 0, sizeof(*sun));
|
|
sun->sun_family = AF_UNIX;
|
|
strncpy(sun->sun_path, (char *)path, sizeof(sun->sun_path) - 1);
|
|
if(*path == '@')*sun->sun_path = 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
int myinet_ntop(int af, void *src, char *dst, socklen_t size){
|
|
#ifdef WITH_UN
|
|
if(af == AF_UNIX){
|
|
struct sockaddr_un *sun = (struct sockaddr_un *)src;
|
|
int ephemeral = 0;
|
|
char *path = sun->sun_path;
|
|
char *basename;
|
|
if(!path[0] && path[1]){
|
|
ephemeral = 1;
|
|
*dst++ = '@';
|
|
path++;
|
|
}
|
|
basename = strrchr(path, '/');
|
|
if(basename) basename++;
|
|
else basename = path;
|
|
if(size > 0){
|
|
strncpy(dst, basename, (size > 40) ? 40 : size - (ephemeral + 1));
|
|
dst[((size > 40) ? 40 : size - (ephemeral + 1))] = 0;
|
|
}
|
|
return (int)strlen(dst);
|
|
}
|
|
#endif
|
|
#ifndef NOIPV6
|
|
if(af != AF_INET6){
|
|
#endif
|
|
unsigned u = ntohl(((struct in_addr *)src)->s_addr);
|
|
return sprintf(dst, "%u.%u.%u.%u",
|
|
((u&0xFF000000)>>24),
|
|
((u&0x00FF0000)>>16),
|
|
((u&0x0000FF00)>>8),
|
|
((u&0x000000FF)));
|
|
#ifndef NOIPV6
|
|
}
|
|
*dst = 0;
|
|
inet_ntop(af, src, dst, size);
|
|
return (int)strlen(dst);
|
|
#endif
|
|
}
|
|
|
|
char *rotations[] = {
|
|
"",
|
|
"/min",
|
|
"/hour",
|
|
"/day",
|
|
"/week",
|
|
"/month",
|
|
"/year",
|
|
"",
|
|
};
|
|
|
|
|
|
int timeouts[12] = {
|
|
1, /* SINGLEBYTE_S */
|
|
5, /* SINGLEBYTE_L */
|
|
30, /* STRING_S */
|
|
60, /* STRING_L */
|
|
180, /* CONNECTION_S */
|
|
1800, /* CONNECTION_L */
|
|
15, /* DNS_TO */
|
|
60, /* CHAIN_TO */
|
|
15, /* CONNECT_TO */
|
|
5, /* CONNBACK_TO */
|
|
5, /* LINGER_TO */
|
|
0
|
|
};
|
|
|
|
#ifndef _WIN32
|
|
/* PTHREAD_STACK_MIN is 128K on glibc/aarch64 and glibc/powerpc and may be
|
|
a sysconf() call with _GNU_SOURCE. pthread_attr_setstacksize() fails with
|
|
EINVAL below it and the thread silently gets the 8M system default stack.
|
|
*/
|
|
size_t threadstacksize(int extra){
|
|
long size = BASESTACKSIZE + TLSSTACKSIZE + 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 = {
|
|
.timeouts = timeouts,
|
|
.acl = NULL,
|
|
.conffile = NULL,
|
|
.bandlimiter = NULL,
|
|
.bandlimiterout = NULL,
|
|
.connlimiter = NULL,
|
|
.trafcounter = NULL,
|
|
.services = NULL,
|
|
.stacksize = 0,
|
|
.counterd = -1,
|
|
.haveerror = 0,
|
|
.rotate = 0,
|
|
.paused = 0,
|
|
.archiverc = 0,
|
|
.demon = 0,
|
|
.maxchild = DEFAULT_MAXCHILD,
|
|
.backlog = 0,
|
|
.needreload = 0,
|
|
.timetoexit = 0,
|
|
.version = 0,
|
|
.noforce = 0,
|
|
.bandlimver = 0,
|
|
.parentretries = 2,
|
|
.authcachetype = 6,
|
|
.authcachetime = 600,
|
|
.filtermaxsize = 1048576,
|
|
.gracetraf = 0,
|
|
.gracenum = 0,
|
|
.gracedelay = 0,
|
|
.maxseg = 0,
|
|
.logname = NULL,
|
|
.archiver = NULL,
|
|
.logtype = NONE,
|
|
.countertype = NONE,
|
|
.counterfile = NULL,
|
|
#ifndef NOIPV6
|
|
.intsa = {AF_INET},
|
|
.extsa6 = {AF_INET6},
|
|
.extsa = {AF_INET},
|
|
#else
|
|
.intsa = {AF_INET},
|
|
.extsa = {AF_INET},
|
|
#endif
|
|
.pwl = NULL,
|
|
.authenticate = NULL,
|
|
.authfunc = doconnect,
|
|
.logfunc = lognone,
|
|
.bandlimfunc = NULL,
|
|
.trafcountfunc = NULL,
|
|
.logtarget = NULL,
|
|
.logformat = NULL,
|
|
.fmon = NULL,
|
|
.filters = NULL,
|
|
.authfuncs = NULL,
|
|
.stdlog = NULL,
|
|
.demanddialprog = NULL,
|
|
.stringtable = NULL,
|
|
.logtime = (time_t)0,
|
|
.time = (time_t)0,
|
|
.logdumpsrv = 0,
|
|
.logdumpcli = 0,
|
|
.delimchar = '@',
|
|
};
|
|
|
|
int numservers=0;
|
|
|
|
char* NULLADDR="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
|
|
|
uint32_t myrand(void){
|
|
#if defined(MYRAND_ARC4RANDOM)
|
|
return arc4random();
|
|
#elif defined(MYRAND_GETRANDOM)
|
|
uint32_t r = 0;
|
|
ssize_t n;
|
|
do {
|
|
n = getrandom(&r, sizeof(r), 0);
|
|
} while(n < 0 && errno == EINTR);
|
|
return r;
|
|
#elif defined(MYRAND_BCRYPTGENRANDOM)
|
|
uint32_t r = 0;
|
|
BCryptGenRandom(NULL, (PUCHAR)&r, sizeof(r), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
|
return r;
|
|
#elif defined(MYRAND_CRYPTGENRANDOM)
|
|
uint32_t r = 0;
|
|
HCRYPTPROV prov;
|
|
if(CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT|CRYPT_SILENT)){
|
|
CryptGenRandom(prov, sizeof(r), (BYTE *)&r);
|
|
CryptReleaseContext(prov, 0);
|
|
}
|
|
return r;
|
|
#else
|
|
return ((uint32_t)rand() << 16) ^ (uint32_t)rand();
|
|
#endif
|
|
}
|
|
|
|
#ifndef WITH_POLL
|
|
#ifndef WITH_WSAPOLL
|
|
int
|
|
#ifdef _WIN32
|
|
WINAPI
|
|
#endif
|
|
|
|
mypoll(struct mypollfd *fds, unsigned int nfds, int timeout){
|
|
fd_set readfd;
|
|
fd_set writefd;
|
|
fd_set oobfd;
|
|
struct timeval tv;
|
|
unsigned i;
|
|
int num;
|
|
SOCKET maxfd = 0;
|
|
|
|
tv.tv_sec = timeout/1000;
|
|
tv.tv_usec = (timeout%1000)*1000;
|
|
FD_ZERO(&readfd);
|
|
FD_ZERO(&writefd);
|
|
FD_ZERO(&oobfd);
|
|
for(i=0; i<nfds; i++){
|
|
#ifndef _WIN32
|
|
if(fds[i].fd >= FD_SETSIZE) continue;
|
|
#endif
|
|
if((fds[i].events&POLLIN))FD_SET(fds[i].fd, &readfd);
|
|
if((fds[i].events&POLLOUT))FD_SET(fds[i].fd, &writefd);
|
|
if((fds[i].events&POLLPRI))FD_SET(fds[i].fd, &oobfd);
|
|
fds[i].revents = 0;
|
|
if(fds[i].fd > maxfd) maxfd = fds[i].fd;
|
|
}
|
|
if((num = select(((int)(maxfd))+1, &readfd, &writefd, &oobfd, &tv)) < 1) return num;
|
|
for(i=0; i<nfds; i++){
|
|
#ifndef _WIN32
|
|
if(fds[i].fd >= FD_SETSIZE) continue;
|
|
#endif
|
|
if(FD_ISSET(fds[i].fd, &readfd)) fds[i].revents |= POLLIN;
|
|
if(FD_ISSET(fds[i].fd, &writefd)) fds[i].revents |= POLLOUT;
|
|
if(FD_ISSET(fds[i].fd, &oobfd)) fds[i].revents |= POLLPRI;
|
|
}
|
|
return num;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#ifdef _WIN32
|
|
SOCKET WINAPI def_socket(void* state, int domain, int type, int protocol){
|
|
return socket(domain, type, protocol);
|
|
}
|
|
SOCKET WINAPI def_accept(void* state, SOCKET s, struct sockaddr * addr, int * addrlen){
|
|
return accept(s, addr, addrlen);
|
|
}
|
|
int WINAPI def_bind(void* state, SOCKET s, const struct sockaddr *addr, int addrlen){
|
|
return bind(s, addr, addrlen);
|
|
}
|
|
int WINAPI def_listen(void* state, SOCKET s, int backlog){
|
|
return listen(s, backlog);
|
|
}
|
|
int WINAPI def_connect(void* state, SOCKET s, const struct sockaddr *name, int namelen){
|
|
return connect(s, name, namelen);
|
|
}
|
|
int WINAPI def_getpeername(void* state, SOCKET s, struct sockaddr * name, int * namelen){
|
|
return getpeername(s, name, namelen);
|
|
}
|
|
int WINAPI def_getsockname(void* state, SOCKET s, struct sockaddr * name, int * namelen){
|
|
return getsockname(s, name, namelen);
|
|
}
|
|
int WINAPI def_getsockopt(void* state, SOCKET s, int level, int optname, char * optval, int * optlen){
|
|
return getsockopt(s, level, optname, optval, optlen);
|
|
}
|
|
int WINAPI def_setsockopt(void* state, SOCKET s, int level, int optname, const char *optval, int optlen){
|
|
return setsockopt(s, level, optname, optval, optlen);
|
|
}
|
|
int WINAPI def_poll(void* state, struct pollfd *fds, unsigned int nfds, int timeout){
|
|
#ifndef WITH_POLL
|
|
#ifndef WITH_WSAPOLL
|
|
return mypoll(fds, nfds, timeout);
|
|
#else
|
|
return WSAPoll(fds, nfds, timeout);
|
|
#endif
|
|
#else
|
|
return poll(fds, nfds, timeout);
|
|
#endif
|
|
}
|
|
int WINAPI def_send(void* state, SOCKET s, const char *msg, int len, int flags){
|
|
return send(s, msg, len, flags);
|
|
}
|
|
int WINAPI def_sendto(void* state, SOCKET s, const char *msg, int len, int flags, const struct sockaddr *to, int tolen){
|
|
return sendto(s, msg, len, flags, to, tolen);
|
|
}
|
|
|
|
int WINAPI def_recv(void* state, SOCKET s, char *buf, int len, int flags){
|
|
return recv(s, buf, len, flags);
|
|
}
|
|
int WINAPI def_recvfrom(void* state, SOCKET s, char * buf, int len, int flags, struct sockaddr * from, int * fromlen){
|
|
return recvfrom(s, buf, len, flags, from, fromlen);
|
|
}
|
|
int WINAPI def_shutdown(void* state, SOCKET s, int how){
|
|
return shutdown(s, how);
|
|
}
|
|
int WINAPI def_closesocket(void* state, SOCKET s){
|
|
usleep(SLEEPTIME);
|
|
return closesocket(s);
|
|
}
|
|
#else
|
|
SOCKET def_socket(void* state, int domain, int type, int protocol){
|
|
return socket(domain, type, protocol);
|
|
}
|
|
SOCKET def_accept(void* state, SOCKET s, struct sockaddr * addr, socklen_t* addrlen){
|
|
return accept(s, addr, addrlen);
|
|
}
|
|
int def_bind(void* state, SOCKET s, const struct sockaddr *addr, socklen_t addrlen){
|
|
return bind(s, addr, addrlen);
|
|
}
|
|
int def_getpeername(void* state, SOCKET s, struct sockaddr * name, socklen_t* namelen){
|
|
return getpeername(s, name, namelen);
|
|
}
|
|
int def_getsockname(void* state, SOCKET s, struct sockaddr * name, socklen_t* namelen){
|
|
return getsockname(s, name, namelen);
|
|
}
|
|
int def_listen(void* state, SOCKET s, int backlog){
|
|
return listen(s, backlog);
|
|
}
|
|
int def_connect(void* state, SOCKET s, const struct sockaddr *name, socklen_t namelen){
|
|
return connect(s, name, namelen);
|
|
}
|
|
int def_getsockopt(void* state, SOCKET s, int level, int optname, void * optval, socklen_t * optlen){
|
|
return getsockopt(s, level, optname, optval, optlen);
|
|
}
|
|
int def_setsockopt(void* state, int s, int level, int optname, const void *optval, socklen_t optlen){
|
|
return setsockopt(s, level, optname, optval, optlen);
|
|
}
|
|
|
|
int def_poll(void* state, struct pollfd *fds, nfds_t nfds, int timeout){
|
|
#ifndef WITH_POLL
|
|
return mypoll(fds, nfds, timeout);
|
|
#else
|
|
return poll(fds, nfds, timeout);
|
|
#endif
|
|
}
|
|
|
|
ssize_t def_send(void* state, SOCKET s, const void *msg, size_t len, int flags){
|
|
return send(s, msg, len, flags);
|
|
}
|
|
ssize_t def_sendto(void* state, SOCKET s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen){
|
|
return sendto(s, msg, len, flags, to, tolen);
|
|
}
|
|
ssize_t def_recv(void* state, SOCKET s, void *buf, size_t len, int flags){
|
|
return recv(s, buf, len, flags);
|
|
}
|
|
ssize_t def_recvfrom(void* state, SOCKET s, void * buf, size_t len, int flags, struct sockaddr * from, socklen_t* fromlen){
|
|
return recvfrom(s, buf, len, flags, from, fromlen);
|
|
}
|
|
int def_shutdown(void* state, SOCKET s, int how){
|
|
return shutdown(s, how);
|
|
}
|
|
int def_closesocket(void* state, SOCKET s){
|
|
return close(s);
|
|
}
|
|
#endif
|
|
|
|
struct sockfuncs so = {
|
|
NULL,
|
|
NULL,
|
|
def_socket,
|
|
def_accept,
|
|
def_bind,
|
|
def_listen,
|
|
def_connect,
|
|
def_getpeername,
|
|
def_getsockname,
|
|
def_getsockopt,
|
|
def_setsockopt,
|
|
def_poll,
|
|
def_send,
|
|
def_sendto,
|
|
def_recv,
|
|
def_recvfrom,
|
|
def_shutdown,
|
|
def_closesocket
|
|
};
|
|
|
|
#ifdef _WINCE
|
|
|
|
static char cebuf[1024];
|
|
static char ceargbuf[256];
|
|
char * ceargv[32];
|
|
|
|
char * CEToUnicode (const char *str){
|
|
int i;
|
|
|
|
for(i=0; i<510 && str[i]; i++){
|
|
cebuf[(i*2)] = str[i];
|
|
cebuf[(i*2)+1] = 0;
|
|
}
|
|
cebuf[(i*2)] = 0;
|
|
cebuf[(i*2)+1] = 0;
|
|
return cebuf;
|
|
};
|
|
|
|
int cesystem(const char *str){
|
|
STARTUPINFO startupInfo = {0};
|
|
startupInfo.cb = sizeof(startupInfo);
|
|
|
|
PROCESS_INFORMATION processInformation;
|
|
|
|
return CreateProcessW((LPWSTR)CEToUnicode(str), NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startupInfo, &processInformation);
|
|
}
|
|
|
|
int ceparseargs(const char *str){
|
|
int argc = 0, i;
|
|
int space = 1;
|
|
|
|
for(i=0; i<250 && argc<30 && str[2*i]; i++){
|
|
ceargbuf[i] = str[2*i];
|
|
if(space && ceargbuf[i]!=' '&& ceargbuf[i]!='\t'&& ceargbuf[i]!='\r'&& ceargbuf[i]!='\n'){
|
|
ceargv[argc++] = ceargbuf + i;
|
|
space = 0;
|
|
}
|
|
else if(!space && (ceargbuf[i]==' ' || ceargbuf[i]=='\t' || ceargbuf[i]=='\r' || ceargbuf[i]=='\n')){
|
|
ceargbuf[i] = 0;
|
|
space = 1;
|
|
}
|
|
}
|
|
return argc;
|
|
}
|
|
|
|
#endif
|
|
|
|
int parsehost(int family, unsigned char *host, struct sockaddr *sa){
|
|
char *sp=NULL,*se=NULL;
|
|
uint16_t port=0;
|
|
int ret = 0;
|
|
|
|
if(!host) return 2;
|
|
if(*host == '[') se=strchr((char *)host, ']');
|
|
if ( (sp = strchr(se?se:(char *)host, ':')) && !strchr(sp+1, ':')) *sp = 0;
|
|
if(se){
|
|
*se = 0;
|
|
}
|
|
if(sp){
|
|
port = atoi(sp+1);
|
|
}
|
|
ret = !getip46(family, host + (se!=0), (struct sockaddr *)sa);
|
|
if(se) *se = ']';
|
|
if(sp) *sp = ':';
|
|
if(port)*SAPORT(sa) = htons(port);
|
|
return ret;
|
|
}
|
|
|
|
int parsehostname(char *hostname, struct clientparam *param, uint16_t port){
|
|
char *sp=NULL,*se=NULL;
|
|
int ret = 0;
|
|
|
|
if(!hostname || !*hostname)return 2;
|
|
if(*hostname == '[') se=strchr(hostname, ']');
|
|
if ((sp = strchr(se?se:hostname, ':'))) {
|
|
if(strchr(sp+1, ':'))sp = NULL;
|
|
else *sp = 0;
|
|
}
|
|
if(se){
|
|
*se = 0;
|
|
}
|
|
if(!*hostname){
|
|
if(sp){
|
|
port = atoi(sp+1);
|
|
*sp = ':';
|
|
}
|
|
*SAPORT(¶m->req) = htons(port);
|
|
return 0;
|
|
}
|
|
if(strlen(hostname + (se!=0)) > 253){
|
|
if(se) *se = ']';
|
|
if(sp) *sp = ':';
|
|
return 1;
|
|
}
|
|
if(hostname != (char *)param->hostname){
|
|
if(param->hostname) free(param->hostname);
|
|
param->hostname = (unsigned char *)strdup(hostname + (se!=0));
|
|
}
|
|
if(sp){
|
|
port = atoi(sp+1);
|
|
}
|
|
ret = !getip46(param->srv->family, param->hostname, (struct sockaddr *)¶m->req);
|
|
if(se) *se = ']';
|
|
if(sp) *sp = ':';
|
|
*SAPORT(¶m->req) = htons(port);
|
|
memset(¶m->sinsr, 0, sizeof(param->sinsr));
|
|
return ret;
|
|
}
|
|
|
|
int parseusername(char *username, struct clientparam *param, int extpasswd){
|
|
char *sb = NULL, *se = NULL, *sp = NULL;
|
|
|
|
if(!username || !*username) return 1;
|
|
if(param->srv->needuser && (sb = strchr(username, ':')) && (se = strchr(sb + 1, ':')) && (!extpasswd || (sp = strchr(se + 1, ':')))){
|
|
*sb = 0;
|
|
*se = 0;
|
|
if(sp) *sp = 0;
|
|
if(*(sb+1)) {
|
|
if(param->password) free(param->password);
|
|
param->password = (unsigned char *)strdup(sb+1);
|
|
}
|
|
if(*username) {
|
|
if(param->username) free(param->username);
|
|
param->username = (unsigned char *)strdup(username);
|
|
}
|
|
username = se+1;
|
|
}
|
|
if(extpasswd){
|
|
if(!sp) sp = strchr(username, ':');
|
|
if(sp){
|
|
*sp = 0;
|
|
if(param->extpassword) free(param->extpassword);
|
|
param->extpassword = (unsigned char *) strdup(sp+1);
|
|
}
|
|
}
|
|
if(param->extusername) free(param->extusername);
|
|
param->extusername = (unsigned char *)strdup(username);
|
|
if(sb) *sb = ':';
|
|
if(se) *se = ':';
|
|
if(sp) *sp = ':';
|
|
return 0;
|
|
}
|
|
|
|
int parseconnusername(char *username, struct clientparam *param, int extpasswd, uint16_t port){
|
|
char *sb, *se;
|
|
if(!username || !*username) return 1;
|
|
if ((sb=strchr(username, conf.delimchar)) == NULL){
|
|
if(!param->hostname && param->remsock == INVALID_SOCKET) return 2;
|
|
if(param->hostname)parsehostname((char *)param->hostname, param, *SAPORT(¶m->req)? ntohs(*SAPORT(¶m->req)) : port);
|
|
return parseusername(username, param, extpasswd);
|
|
}
|
|
while ((se=strchr(sb+1, conf.delimchar)))sb=se;
|
|
*(sb) = 0;
|
|
if(parseusername(username, param, extpasswd)) return 3;
|
|
*(sb) = conf.delimchar;
|
|
if(parsehostname(sb+1, param, *SAPORT(¶m->req)? ntohs(*SAPORT(¶m->req)) : port)) return 4;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to){
|
|
struct pollfd fds[1];
|
|
#ifdef _WIN32
|
|
unsigned long ul = 1;
|
|
ioctlsocket(sock, FIONBIO, &ul);
|
|
#else
|
|
fcntl(sock,F_SETFL, O_NONBLOCK | fcntl(sock,F_GETFL));
|
|
#endif
|
|
if(param?param->srv->so._connect(param->sostate, sock,sa,size) : so._connect(so.state, sock,sa,size)) {
|
|
if(errno != EAGAIN && errno != EINPROGRESS) return 13;
|
|
}
|
|
if(!errno) return 0;
|
|
memset(fds, 0, sizeof(fds));
|
|
fds[0].fd = sock;
|
|
fds[0].events = POLLOUT;
|
|
if((param?param->srv->so._poll(param->sostate, fds, 1, to*1000):so._poll(so.state, fds, 1, to*1000)) <= 0 || !(fds[0].revents & POLLOUT) || (fds[0].revents & (POLLERR|POLLHUP))) {
|
|
return 13;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int doconnect(struct clientparam * param){
|
|
SASIZETYPE size;
|
|
|
|
|
|
if (*SAFAMILY(¶m->sincl) == *SAFAMILY(¶m->req) && !memcmp(SAADDR(¶m->sincl), SAADDR(¶m->req), SAADDRLEN(¶m->req)) &&
|
|
*SAPORT(¶m->sincl) == *SAPORT(¶m->req)) return 519;
|
|
|
|
if (param->operation == ADMIN || (( param->operation == DNSRESOLVE || param->operation == BIND || param->operation == UDPASSOC) && !param->redirected))
|
|
return 0;
|
|
if (param->operation == UDPASSOC && param->ctrlsocksrv != INVALID_SOCKET) return 0;
|
|
if (param->remsock != INVALID_SOCKET){
|
|
if(param->operation == UDPASSOC) return 0;
|
|
size = sizeof(param->sinsr);
|
|
if(param->srv->so._getpeername(param->sostate, param->remsock, (struct sockaddr *)¶m->sinsr, &size)==-1) {return (14);}
|
|
}
|
|
else {
|
|
struct linger lg = {1,conf.timeouts[LINGER_TO]};
|
|
|
|
if(SAISNULL(¶m->sinsr)){
|
|
if(SAISNULL(¶m->req)) {
|
|
return 100;
|
|
}
|
|
*SAFAMILY(¶m->sinsr) = *SAFAMILY(¶m->req);
|
|
memcpy(SAADDR(¶m->sinsr), SAADDR(¶m->req), SAADDRLEN(¶m->req));
|
|
}
|
|
if(!*SAPORT(¶m->sinsr))*SAPORT(¶m->sinsr) = *SAPORT(¶m->req);
|
|
if ((param->remsock=param->srv->so._socket(param->sostate, SASOCK(¶m->sinsr), SOCK_STREAM,
|
|
#ifdef WITH_UN
|
|
*SAFAMILY(¶m->sinsr) == AF_UNIX? 0 :
|
|
#endif
|
|
IPPROTO_TCP
|
|
)) == INVALID_SOCKET) {return (11);}
|
|
if(SAISNULL(¶m->sinsl)){
|
|
#ifdef WITH_UN
|
|
if(*SAFAMILY(¶m->sinsr) == AF_UNIX) param->sinsl = param->sinsr;
|
|
else
|
|
#endif
|
|
#ifndef NOIPV6
|
|
if(*SAFAMILY(¶m->sinsr) == AF_INET6) param->sinsl = param->srv->extsa6;
|
|
else
|
|
#endif
|
|
param->sinsl = param->srv->extsa;
|
|
}
|
|
*SAPORT(¶m->sinsl) = 0;
|
|
setopts(param->remsock, param->srv->srvsockopts);
|
|
|
|
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
|
|
#if defined SO_BINDTODEVICE
|
|
if(param->srv->obindtodevice) {
|
|
if(param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_BINDTODEVICE, param->srv->obindtodevice, strlen(param->srv->obindtodevice) + 1))
|
|
return 12;
|
|
}
|
|
#elif defined IP_BOUND_IF
|
|
if(param->srv->obindtodevice) {
|
|
int idx;
|
|
idx = if_nametoindex(param->srv->obindtodevice);
|
|
if(!idx || (*SAFAMILY(¶m->sinsl) == AF_INET && param->srv->so._setsockopt(param->sostate, param->remsock, IPPROTO_IP, IP_BOUND_IF, &idx, sizeof(idx))))
|
|
return 12;
|
|
#ifndef NOIPV6
|
|
#ifndef IPV6_BOUND_IF
|
|
#define IPV6_BOUND_IF 125
|
|
#endif
|
|
if(*SAFAMILY(¶m->sinsl) == AF_INET6 && param->srv->so._setsockopt(param->sostate, param->remsock, IPPROTO_IPV6, IPV6_BOUND_IF, &idx, sizeof(idx))) return 12;
|
|
#endif
|
|
}
|
|
#endif
|
|
#ifdef WITH_UN
|
|
if(*SAFAMILY(¶m->sinsl) != AF_UNIX)
|
|
#endif
|
|
if(param->srv->so._bind(param->sostate, param->remsock, (struct sockaddr*)¶m->sinsl, SASIZE(¶m->sinsl))==-1) {
|
|
return 12;
|
|
}
|
|
|
|
if(param->operation >= 256 || (param->operation & CONNECT) || param->redirected){
|
|
if(connectwithpoll(param, param->remsock,(struct sockaddr *)¶m->sinsr,SASIZE(¶m->sinsr),conf.timeouts[CONNECT_TO])) {
|
|
return 13;
|
|
}
|
|
}
|
|
size = sizeof(param->sinsl);
|
|
if(param->srv->so._getsockname(param->sostate, param->remsock, (struct sockaddr *)¶m->sinsl, &size)==-1) {return (15);}
|
|
}
|
|
if (param->nconnectfilters){
|
|
FILTER_ACTION action;
|
|
|
|
action = handleconnectflt(param);
|
|
if(action != PASS) return 19;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int scanaddr(const unsigned char *s, uint32_t * ip, uint32_t * mask) {
|
|
unsigned d1, d2, d3, d4, m;
|
|
int res;
|
|
if ((res = sscanf((char *)s, "%u.%u.%u.%u/%u", &d1, &d2, &d3, &d4, &m)) < 4) return 0;
|
|
if(mask && res == 4) *mask = 0xFFFFFFFF;
|
|
else if (mask) *mask = htonl(0xFFFFFFFF << (32 - m));
|
|
*ip = htonl ((d1<<24) ^ (d2<<16) ^ (d3<<8) ^ d4);
|
|
return res;
|
|
}
|
|
|
|
RESOLVFUNC resolvfunc = NULL;
|
|
#ifndef _WIN32
|
|
_3proxy_mutex_t gethostbyname_mutex;
|
|
int ghbn_init = 0;
|
|
#endif
|
|
|
|
#ifndef NOSTDRESOLVE
|
|
#ifdef GETHOSTBYNAME_R
|
|
struct hostent * my_gethostbyname(char *name, char *buf, struct hostent *hp){
|
|
struct hostent *result;
|
|
int gherrno;
|
|
|
|
#ifdef _SOLARIS
|
|
return gethostbyname_r(name, hp, buf, 1024, &gherrno);
|
|
#else
|
|
if(gethostbyname_r(name, hp, buf, 1024, &result, &gherrno) != 0)
|
|
return NULL;
|
|
return result;
|
|
#endif
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef NOIPV6
|
|
uint32_t getip(unsigned char *name){
|
|
uint32_t retval;
|
|
int i;
|
|
int ndots = 0;
|
|
struct hostent *hp=NULL;
|
|
RESOLVFUNC tmpresolv;
|
|
|
|
#ifdef GETHOSTBYNAME_R
|
|
struct hostent he;
|
|
char ghbuf[1024];
|
|
#define gethostbyname(NAME) my_gethostbyname(NAME, ghbuf, &he)
|
|
#endif
|
|
|
|
if(strlen((char *)name)>255)name[255] = 0;
|
|
for(i=0; name[i]; i++){
|
|
if(name[i] == '.'){
|
|
if(++ndots > 3) break;
|
|
continue;
|
|
}
|
|
if(name[i] <'0' || name[i] >'9') break;
|
|
}
|
|
if(!name[i] && ndots == 3){
|
|
if(scanaddr(name, &retval, NULL) == 4){
|
|
return retval;
|
|
}
|
|
}
|
|
if((tmpresolv=resolvfunc)){
|
|
if((*tmpresolv)(AF_INET, name, (unsigned char *)&retval)) return retval;
|
|
if(conf.demanddialprog) system(conf.demanddialprog);
|
|
return (*tmpresolv)(AF_INET, name, (unsigned char *)&retval)?retval:0;
|
|
}
|
|
#ifndef NOSTDRESOLVE
|
|
#if !defined(_WIN32) && !defined(GETHOSTBYNAME_R)
|
|
if(!ghbn_init){
|
|
_3proxy_mutex_init(&gethostbyname_mutex);
|
|
ghbn_init++;
|
|
}
|
|
_3proxy_mutex_lock(&gethostbyname_mutex);
|
|
#endif
|
|
hp=gethostbyname((char *)name);
|
|
if (!hp && conf.demanddialprog) {
|
|
system(conf.demanddialprog);
|
|
hp=gethostbyname((char *)name);
|
|
}
|
|
retval = hp?*(uint32_t *)hp->h_addr:0;
|
|
#if !defined(_WIN32) && !defined(GETHOSTBYNAME_R)
|
|
_3proxy_mutex_unlock(&gethostbyname_mutex);
|
|
#endif
|
|
#ifdef GETHOSTBYNAME_R
|
|
#undef gethostbyname
|
|
#endif
|
|
#else
|
|
retval=0;
|
|
#endif
|
|
return retval;
|
|
}
|
|
#endif
|
|
|
|
int afdetect(unsigned char *name){
|
|
int ndots=0, ncols=0, nhex=0;
|
|
int i;
|
|
|
|
for(i=0; name[i]; i++){
|
|
if(name[i] == '.'){
|
|
if(++ndots > 3) {
|
|
return -1;
|
|
}
|
|
}
|
|
else if(name[i] == ':'){
|
|
if(++ncols > 7) {
|
|
return -1;
|
|
}
|
|
}
|
|
else if(name[i] == '%' || (name[i] >= 'a' && name[i] <= 'f') || (name[i] >= 'A' && name[i] <= 'F')){
|
|
nhex++;
|
|
}
|
|
else if(name[i] <'0' || name[i] >'9') {
|
|
return -1;
|
|
}
|
|
}
|
|
if(ndots == 3 && ncols == 0 && nhex == 0){
|
|
return AF_INET;
|
|
}
|
|
if(ncols >= 2) {
|
|
return AF_INET6;
|
|
}
|
|
return -1;
|
|
|
|
}
|
|
|
|
uint32_t getip46(int family, unsigned char *name, struct sockaddr *sa){
|
|
#ifndef NOIPV6
|
|
int detect;
|
|
#ifndef NOSTDRESOLVE
|
|
struct addrinfo *ai, hint;
|
|
#endif
|
|
RESOLVFUNC tmpresolv;
|
|
|
|
if(!sa) return 0;
|
|
if(!family) {
|
|
family = 4;
|
|
#else
|
|
((struct sockaddr_in *)sa)->sin_family = AF_INET;
|
|
return (((struct sockaddr_in *)sa)->sin_addr.s_addr = getip(name))? AF_INET:0;
|
|
#endif
|
|
#ifndef NOIPV6
|
|
}
|
|
|
|
detect = afdetect(name);
|
|
if(detect != -1){
|
|
if(family == 4 && detect != AF_INET) return 0;
|
|
*SAFAMILY(sa) = (family == 6)? AF_INET6 : detect;
|
|
return inet_pton(*SAFAMILY(sa), (char *)name, SAADDR(sa))>0? *SAFAMILY(sa) : 0;
|
|
}
|
|
|
|
if((tmpresolv = resolvfunc)){
|
|
int f = (family == 6 || family == 64)?AF_INET6:AF_INET;
|
|
*SAFAMILY(sa) = f;
|
|
if(tmpresolv(f, name, SAADDR(sa))) return f;
|
|
if(family == 4 || family == 6) return 0;
|
|
f = (family == 46)? AF_INET6 : AF_INET;
|
|
*SAFAMILY(sa) = f;
|
|
if(tmpresolv(f, name, SAADDR(sa))) return f;
|
|
return 0;
|
|
}
|
|
#ifndef NOSTDRESOLVE
|
|
memset(&hint, 0, sizeof(hint));
|
|
hint.ai_family = (family == 6 || family == 64)?AF_INET6:AF_INET;
|
|
if (getaddrinfo((char *)name, NULL, &hint, &ai)) {
|
|
if(family == 64 || family == 46){
|
|
hint.ai_family = (family == 64)?AF_INET:AF_INET6;
|
|
if (getaddrinfo((char *)name, NULL, &hint, &ai)) return 0;
|
|
}
|
|
else return 0;
|
|
}
|
|
if(ai){
|
|
if(ai->ai_addr->sa_family == AF_INET || ai->ai_addr->sa_family == AF_INET6){
|
|
*SAFAMILY(sa)=ai->ai_addr->sa_family;
|
|
memcpy(SAADDR(sa), SAADDR(ai->ai_addr), SAADDRLEN(ai->ai_addr));
|
|
freeaddrinfo(ai);
|
|
return *SAFAMILY(sa);
|
|
}
|
|
freeaddrinfo(ai);
|
|
}
|
|
#endif
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int hascap(const unsigned char *line, const char *cap){
|
|
int len = (int)strlen(cap);
|
|
for(; *line; line++) if(!strncasecmp((char *)line, cap, len)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int getmultiline(struct clientparam *param, DIRECTION which, unsigned char *buf, int bufsize, const char *cap, int *found){
|
|
int i;
|
|
do {
|
|
i = sockgetlinebuf(param, which, buf, bufsize, '\n', conf.timeouts[STRING_L]);
|
|
if(i > 0 && cap && found && !*found){
|
|
buf[i] = 0;
|
|
if(hascap(buf, cap)) *found = 1;
|
|
}
|
|
} while (i > 3 && buf[3] == '-');
|
|
return i;
|
|
}
|