diff --git a/src/proxymain.c b/src/proxymain.c index 6e7a32b..edae276 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -930,7 +930,7 @@ int MODULEMAINFUNC (int argc, char** argv){ if(udplen <= 0) continue; _3proxy_sem_lock(udpinit); if(hashresolv(&udp_table, &defparam, &toparam, NULL)) { - socksendto(toparam, toparam->remsock, (struct sockaddr *)&toparam->sinsr, udpbuf, udplen, 0); + srv.so._sendto(toparam->sostate, toparam->remsock, (char *)udpbuf, udplen, 0, (struct sockaddr *)&toparam->sinsr, SASIZE(&toparam->sinsr)); _3proxy_sem_unlock(udpinit); toparam->statscli64 += udplen; toparam->nwrites++; diff --git a/src/redirect.c b/src/redirect.c index d7a4cd2..68c7096 100644 --- a/src/redirect.c +++ b/src/redirect.c @@ -132,6 +132,8 @@ int clientnegotiate(struct chain * redir, struct clientparam * param, struct soc case R_SOCKS5B: { int inbuf = 0; + int atyp; + int skip_port = 0; buf[0] = 5; buf[1] = 1; buf[2] = user? 2 : 0; @@ -170,9 +172,14 @@ int clientnegotiate(struct chain * redir, struct clientparam * param, struct soc } } buf[0] = 5; - buf[1] = 1; + buf[1] = (param->operation == UDPASSOC && redir->type != R_SOCKS5B) ? 3 : 1; buf[2] = 0; - if(redir->type == R_SOCKS5P && hostname) { + if (param->operation == UDPASSOC && redir->type != R_SOCKS5B) { + buf[3] = 1; + memset(buf + 4, 0, 6); + len = 10; + skip_port = 1; + } else if(redir->type == R_SOCKS5P && hostname) { buf[3] = 3; len = (int)strlen((char *)hostname); if(len > 255) len = 255; @@ -186,8 +193,10 @@ int clientnegotiate(struct chain * redir, struct clientparam * param, struct soc memcpy(buf+len, SAADDR(addr), SAADDRLEN(addr)); len += SAADDRLEN(addr); } - memcpy(buf+len, SAPORT(addr), 2); - len += 2; + if (!skip_port) { + memcpy(buf+len, SAPORT(addr), 2); + len += 2; + } if(socksend(param, param->remsock, buf, len, conf.timeouts[CHAIN_TO]) != len){ return 51; } @@ -202,6 +211,7 @@ int clientnegotiate(struct chain * redir, struct clientparam * param, struct soc if(buf[1] != 0) { return 60 + (buf[1] % 10); } + atyp = buf[3]; switch (buf[3]) { case 1: if (redir->type == R_SOCKS5B || sockgetlinebuf(param, SERVER, buf, 6, EOF, conf.timeouts[CHAIN_TO]) == 6) @@ -219,6 +229,21 @@ int clientnegotiate(struct chain * redir, struct clientparam * param, struct soc default: return 58; } + if (param->operation == UDPASSOC && (redir->type == R_SOCKS5 || redir->type == R_SOCKS5P) && param->udp_nhops < 3) { + PROXYSOCKADDRTYPE *relay = ¶m->udp_relay[param->udp_nhops]; + memset(relay, 0, sizeof(*relay)); + if (atyp == 1) { + ((struct sockaddr_in *)relay)->sin_family = AF_INET; + memcpy(&((struct sockaddr_in *)relay)->sin_addr, buf, 4); + memcpy(&((struct sockaddr_in *)relay)->sin_port, buf + 4, 2); + param->udp_nhops++; + } else if (atyp == 4) { + ((struct sockaddr_in6 *)relay)->sin6_family = AF_INET6; + memcpy(&((struct sockaddr_in6 *)relay)->sin6_addr, buf, 16); + memcpy(&((struct sockaddr_in6 *)relay)->sin6_port, buf + 16, 2); + param->udp_nhops++; + } + } return 0; } diff --git a/src/sockmap.c b/src/sockmap.c index 4ff0c1d..342b1fd 100644 --- a/src/sockmap.c +++ b/src/sockmap.c @@ -52,7 +52,7 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){ int HASERROR=0; int CLIENTTERMREAD = 0, CLIENTTERMWRITE = 0, SERVERTERMREAD = 0, SERVERTERMWRITE = 0; int after = 0; - struct pollfd fds[6]; + struct pollfd fds[8]; struct pollfd *fdsp = fds; int fdsc = 0; int sleeptime = 0; @@ -212,7 +212,7 @@ log("send to server from buf"); param->clioffset = param->cliinbuf = 0; if(fromclient) TOCLIENTBUF = 1; } - sasize = sizeof(param->sinsr); + sasize = SASIZE(¶m->sinsr); res = param->srv->so._sendto(param->sostate, param->remsock, (char *)param->clibuf + param->clioffset, (int)MIN(inclientbuf, fromclient), 0, (struct sockaddr*)¶m->sinsr, sasize); if(res <= 0) { TOSERVER = 0; @@ -258,7 +258,7 @@ log("send to client from buf"); param->srvinbuf = param->srvoffset = 0; continue; } - sasize = sizeof(param->sincr); + sasize = SASIZE(¶m->sincr); res = param->srv->so._sendto(param->sostate, param->clisock, (char *)param->srvbuf + param->srvoffset, (int)MIN(inserverbuf,fromserver), 0, (struct sockaddr*)¶m->sincr, sasize); if(res <= 0) { TOCLIENT = 0; @@ -706,6 +706,24 @@ log("ready reading from server pipe"); } } #endif + if(param->ctrlsock != INVALID_SOCKET) { + if(!after) { + fds[fdsc].fd = param->ctrlsock; + fds[fdsc].events = POLLIN; + } else if(fds[fdsc].revents) { + CLIENTTERMREAD = CLIENTTERMWRITE = SERVERTERMREAD = SERVERTERMWRITE = 1; + } + fdsc++; + } + if(param->ctrlsocksrv != INVALID_SOCKET) { + if(!after) { + fds[fdsc].fd = param->ctrlsocksrv; + fds[fdsc].events = POLLIN; + } else if(fds[fdsc].revents) { + CLIENTTERMREAD = CLIENTTERMWRITE = SERVERTERMREAD = SERVERTERMWRITE = 1; + } + fdsc++; + } if(!after){ if(!fdsc) RETURN(90); diff --git a/src/socks.c b/src/socks.c index bd4b32a..c39e65c 100644 --- a/src/socks.c +++ b/src/socks.c @@ -13,7 +13,6 @@ unsigned char * commands[] = {(unsigned char *)"UNKNOWN", (unsigned char *)"CONNECT", (unsigned char *)"BIND", (unsigned char *)"UDPMAP"}; #define BUFSIZE 1024 -#define LARGEBUFSIZE 67000 #if SOCKSTRACE > 0 char tracebuf[256]; @@ -407,111 +406,7 @@ fflush(stderr); break; case 3: param->sinsr = param->req; - myfree(buf); - if(!(buf = myalloc(LARGEBUFSIZE))) {RETURN(21);} - sin = param->sincr; - - for(;;){ - fds[0].fd = param->remsock; - fds[1].fd = param->clisock; - fds[2].fd = param->ctrlsock; - fds[2].events = fds[1].events = fds[0].events = POLLIN; - - res = param->srv->so._poll(param->sostate, fds, 3, conf.timeouts[CONNECTION_L]*1000); - if(res <= 0) { - param->res = 463; - break; - } - if (fds[2].revents) { - param->res = 0; - break; - } - if (fds[1].revents) { - sasize = sizeof(sin); - if((len = param->srv->so._recvfrom(param->sostate, param->clisock, (char *)buf, 65535, 0, (struct sockaddr *)&sin, &sasize)) <= 10) { - param->res = 464; - break; - } - if(SAADDRLEN(&sin) != SAADDRLEN(¶m->sincr) || memcmp(SAADDR(&sin), SAADDR(¶m->sincr), SAADDRLEN(&sin))){ - param->res = 465; - break; - } - if(buf[0] || buf[1] || buf[2]) { - param->res = 466; - break; - } - size = 4; - switch(buf[3]) { - case 4: - size = 16; - case 1: - i = 4+size; - *SAFAMILY(¶m->sinsr) = (size == 4)?AF_INET:AF_INET6; - memcpy(SAADDR(¶m->sinsr), buf+4, size); - break; - case 3: - size = buf[4]; - for (i=4; size; i++, size--){ - buf[i] = buf[i+1]; - } - buf[i++] = 0; - if(!getip46(param->srv->family, buf+4, (struct sockaddr *) ¶m->sinsr)) RETURN(100); - break; - default: - RETURN(997); - } - - memcpy(SAPORT(¶m->sinsr), buf+i, 2); - i+=2; - - sasize = sizeof(param->sinsr); - if(len > (int)i){ - socksendto(param, param->remsock, (struct sockaddr *)¶m->sinsr, buf+i, len - i, conf.timeouts[SINGLEBYTE_L]*1000); - param->statscli64+=(len - i); - param->nwrites++; -#if SOCKSTRACE > 1 -myinet_ntop(*SAFAMILY(¶m->sinsr), ¶m->sinsr, tracebuf, SASIZE(¶m->sinsr)); - -fprintf(stderr, "UDP packet relayed from client to %s:%hu size %d, header %d\n", - tracebuf, - ntohs(*SAPORT(¶m->sinsr)), - (len - i), - i - ); -myinet_ntop(*SAFAMILY(&sin), &sin, tracebuf, SASIZE(&sin)); -fprintf(stderr, "client address is assumed to be %s:%hu\n", - tracebuf, - ntohs(*SAPORT(&sin)) - ); -fflush(stderr); -#endif - } - - } - if (fds[0].revents) { - sasize = sizeof(param->sinsr); - buf[0]=buf[1]=buf[2]=0; - buf[3]=(*SAFAMILY(¶m->sinsl) == AF_INET)?1:4; - if((len = param->srv->so._recvfrom(param->sostate, param->remsock, (char *)buf+6+SAADDRLEN(¶m->sinsl), 65535 - (6+SAADDRLEN(¶m->sinsl)), 0, (struct sockaddr *)¶m->sinsr, &sasize)) <= 0) { - param->res = 468; - break; - } - param->statssrv64+=len; - param->nreads++; - memcpy(buf+4, SAADDR(¶m->sinsr), SAADDRLEN(¶m->sinsr)); - memcpy(buf+4+SAADDRLEN(¶m->sinsr), SAPORT(¶m->sinsr), 2); - sasize = sizeof(sin); - socksendto(param, param->clisock, (struct sockaddr *)&sin, buf, len + 6 + SAADDRLEN(¶m->sinsr), conf.timeouts[SINGLEBYTE_L]*1000); -#if SOCKSTRACE > 1 -fprintf(stderr, "UDP packet relayed to client from %hu size %d\n", - ntohs(*SAPORT(¶m->sinsr)), - len - ); -fflush(stderr); -#endif - - } - } + param->res = udpsockmap(param, conf.timeouts[CONNECTION_L]); break; default: param->res = 417; diff --git a/src/udppm.c b/src/udppm.c index 080c038..ee9971b 100644 --- a/src/udppm.c +++ b/src/udppm.c @@ -53,14 +53,15 @@ void * udppmchild(struct clientparam* param) { authres = (*param->srv->authfunc)(param); if(authres) { RETURN(authres); } if(!param->srv->singlepacket)hashadd(&udp_table, param, ¶m, MAX_COUNTER_TIME); - socksendto(param, param->remsock, (struct sockaddr *)¶m->sinsr, param->srvbuf, param->srvinbuf, 0); + param->srv->so._sendto(param->sostate, param->remsock, (char *)param->srvbuf, param->srvinbuf, 0, (struct sockaddr *)¶m->sinsr, SASIZE(¶m->sinsr)); _3proxy_sem_unlock(udpinit); param->statscli64 += param->srvinbuf; param->srvinbuf = 0; param->nwrites++; param->clisock = param->srv->srvsock; + param->udp_nhops = 1; param->waitserver64 = 0x7fffffffffffffff; - param->res = mapsocket(param, conf.timeouts[STRING_L]); + param->res = udpsockmap(param, conf.timeouts[STRING_L]); _3proxy_sem_lock(udpinit); if(!param->srv->singlepacket)hashdelete(&udp_table, param); diff --git a/src/udpsockmap.c b/src/udpsockmap.c new file mode 100644 index 0000000..2bd2314 --- /dev/null +++ b/src/udpsockmap.c @@ -0,0 +1,215 @@ +/* + 3APA3A simplest proxy server + (c) 2002-2021 by Vladimir Dubrovin <3proxy@3proxy.org> + + please read License Agreement + +*/ + +#include "proxy.h" + +static int socks5_udp_build_hdr(unsigned char *buf, PROXYSOCKADDRTYPE *addr) +{ + buf[0] = buf[1] = buf[2] = 0; + buf[3] = (*SAFAMILY(addr) == AF_INET) ? 1 : 4; + memcpy(buf + 4, SAADDR(addr), SAADDRLEN(addr)); + memcpy(buf + 4 + SAADDRLEN(addr), SAPORT(addr), 2); + return 4 + SAADDRLEN(addr) + 2; +} + +static int socks5_udp_skip_hdr(unsigned char *buf, int len) +{ + int addr_len; + if (len < 4) return -1; + switch (buf[3]) { + case 1: addr_len = 4; break; + case 4: addr_len = 16; break; + case 3: + if (len < 5) return -1; + addr_len = 1 + (unsigned char)buf[4]; + break; + default: return -1; + } + int off = 4 + addr_len + 2; + return (off <= len) ? off : -1; +} + +/* + * udpsockmap: bidirectional UDP relay. + * + * param->udp_nhops selects the relay mode: + * 0 direct SOCKS5 relay (strip/add headers) + * 1 one parent SOCKS5 proxy (pass datagrams unchanged) + * 2 two parent proxies (prepend 1 header / strip 1 header) + * 3 three parent proxies (prepend 2 headers / strip 2 headers) + * + * param->waitserver64 non-zero: skip client socket polling (server→client only) + * param->srv->singlepacket non-zero: return after first datagram sent to client + * param->ctrlsock TCP control socket from the client; INVALID_SOCKET if none. + */ +int udpsockmap(struct clientparam *param, int timeo) +{ + PROXYSOCKADDRTYPE sin; + PROXYSOCKADDRTYPE from; + struct pollfd fds[4]; + SASIZETYPE sasize; + int len, res, nfds; + int nhops = param->udp_nhops; + int clisock_idx = -1, ctrlsock_idx = -1, ctrlsocksrv_idx = -1; + int firstpacket = 1; + + if (param->srvbufsize < UDPBUFSIZE) { + unsigned char *newbuf = myrealloc(param->srvbuf, UDPBUFSIZE); + if (!newbuf) return 21; + param->srvbuf = newbuf; + param->srvbufsize = UDPBUFSIZE; + } + sin = param->sincr; + + /* Build poll array once — sockets don't change across iterations */ + nfds = 0; + fds[nfds].fd = param->remsock; /* always index 0 */ + fds[nfds].events = POLLIN; + nfds++; + + if (!param->waitserver64) { + fds[nfds].fd = param->clisock; + fds[nfds].events = POLLIN; + clisock_idx = nfds++; + } + + if (param->ctrlsock != INVALID_SOCKET) { + fds[nfds].fd = param->ctrlsock; + fds[nfds].events = POLLIN; + ctrlsock_idx = nfds++; + } + + if (param->ctrlsocksrv != INVALID_SOCKET) { + fds[nfds].fd = param->ctrlsocksrv; + fds[nfds].events = POLLIN; + ctrlsocksrv_idx = nfds++; + } + + for (;;) { + res = param->srv->so._poll(param->sostate, fds, nfds, timeo * 1000); + if (res < 0) return 463; + if (res == 0) return 92; + + /* datagram from client */ + if (clisock_idx >= 0 && fds[clisock_idx].revents) { + int recvoff = 0, k; + sasize = sizeof(sin); + for (k = 1; k < nhops; k++) + recvoff += 4 + (int)SAADDRLEN(¶m->udp_relay[k]) + 2; + len = param->srv->so._recvfrom(param->sostate, param->clisock, + (char *)param->srvbuf + recvoff, UDPBUFSIZE - recvoff, + 0, (struct sockaddr *)&sin, &sasize); + if (len <= 0) return 464; + + if (SAADDRLEN(&sin) != SAADDRLEN(¶m->sincr) || + memcmp(SAADDR(&sin), SAADDR(¶m->sincr), SAADDRLEN(&sin))) + continue; + if (firstpacket) { + param->sincr = sin; + firstpacket = 0; + } else if (memcmp(SAPORT(&sin), SAPORT(¶m->sincr), 2)) { + continue; + } + + if (nhops == 0) { + int i; + if (len < 10 || param->srvbuf[0] || param->srvbuf[1] || param->srvbuf[2]) + return 466; + switch (param->srvbuf[3]) { + case 1: + *SAFAMILY(¶m->sinsr) = AF_INET; + memcpy(SAADDR(¶m->sinsr), param->srvbuf + 4, 4); + i = 8; + break; + case 4: + if (len < 22) return 466; + *SAFAMILY(¶m->sinsr) = AF_INET6; + memcpy(SAADDR(¶m->sinsr), param->srvbuf + 4, 16); + i = 20; + break; + case 3: { + int sz = param->srvbuf[4], j; + if (len < 7 + sz) return 466; + for (j = 4; j < 4 + sz; j++) param->srvbuf[j] = param->srvbuf[j + 1]; + param->srvbuf[4 + sz] = 0; + i = 5 + sz; + if (!getip46(param->srv->family, param->srvbuf + 4, + (struct sockaddr *)¶m->sinsr)) + return 100; + break; + } + default: return 997; + } + memcpy(SAPORT(¶m->sinsr), param->srvbuf + i, 2); + i += 2; + if (len > i) { + param->srv->so._sendto(param->sostate, param->remsock, + (char *)param->srvbuf + i, len - i, 0, + (struct sockaddr *)¶m->sinsr, SASIZE(¶m->sinsr)); + param->statscli64 += (len - i); + param->nwrites++; + } + } else { + int off = 0; + for (k = 1; k < nhops; k++) + off += socks5_udp_build_hdr(param->srvbuf + off, ¶m->udp_relay[k]); + param->srv->so._sendto(param->sostate, param->remsock, + (char *)param->srvbuf, off + len, 0, + (struct sockaddr *)¶m->udp_relay[0], SASIZE(¶m->udp_relay[0])); + param->statscli64 += len; + param->nwrites++; + } + } + + /* datagram from server / parent relay */ + if (fds[0].revents) { + int hdrsize = (nhops == 0) ? 4 + (int)SAADDRLEN(¶m->sinsr) + 2 : 0; + int sendoff = 0, sendlen; + sasize = sizeof(from); + if (hdrsize > UDPBUFSIZE) return 468; + len = param->srv->so._recvfrom(param->sostate, param->remsock, + (char *)param->srvbuf + hdrsize, UDPBUFSIZE - hdrsize, 0, + (struct sockaddr *)&from, &sasize); + if (len <= 0) return 468; + if (!SAISNULL(¶m->sinsr) && *SAPORT(¶m->sinsr)) { + if (SAADDRLEN(&from) != SAADDRLEN(¶m->sinsr) || + memcmp(SAADDR(&from), SAADDR(¶m->sinsr), SAADDRLEN(&from)) || + memcmp(SAPORT(&from), SAPORT(¶m->sinsr), 2)) + continue; + } + param->statssrv64 += len; + param->nreads++; + sendlen = len; + if (nhops == 0) { + param->srvbuf[0] = param->srvbuf[1] = param->srvbuf[2] = 0; + param->srvbuf[3] = (*SAFAMILY(¶m->sinsr) == AF_INET) ? 1 : 4; + memcpy(param->srvbuf + 4, SAADDR(¶m->sinsr), SAADDRLEN(¶m->sinsr)); + memcpy(param->srvbuf + 4 + SAADDRLEN(¶m->sinsr), SAPORT(¶m->sinsr), 2); + sendlen = len + hdrsize; + } else if (nhops >= 2) { + int off = 0, k; + for (k = 1; k < nhops; k++) { + int next = socks5_udp_skip_hdr(param->srvbuf + off, len - off); + if (next < 0) break; + off += next; + } + sendoff = off; + sendlen = len - off; + } + if (sendlen > 0) + param->srv->so._sendto(param->sostate, param->clisock, + (char *)param->srvbuf + sendoff, sendlen, 0, + (struct sockaddr *)&sin, SASIZE(&sin)); + if (param->srv->singlepacket) return 0; + } + + if ((ctrlsock_idx >= 0 && fds[ctrlsock_idx].revents) || + (ctrlsocksrv_idx >= 0 && fds[ctrlsocksrv_idx].revents)) return 0; + } + return 0; +}