/* 3APA3A simplest proxy server (c) 2002-2021 by Vladimir Dubrovin <3proxy@3proxy.org> please read License Agreement */ #include "proxy.h" static FILTER_ACTION (*ext_ssl_parent)(struct clientparam * param) = NULL; static FILTER_ACTION ssl_parent(struct clientparam * param){ if(ext_ssl_parent) return ext_ssl_parent(param); ext_ssl_parent = pluginlink.findbyname("ssl_parent"); if(ext_ssl_parent) return ext_ssl_parent(param); return REJECT; } int clientnegotiate(struct chain * redir, struct clientparam * param, struct sockaddr * addr, unsigned char * hostname){ unsigned char *buf; unsigned char *username; int res; int len=0; unsigned char * user, *pass; user = redir->extuser; pass = redir->extpass; if (!param->srvbufsize){ param->srvbufsize = SRVBUFSIZE; param->srvbuf = myalloc(param->srvbufsize); if(!param->srvbuf) return 21; } buf = param->srvbuf; username = buf + 2048; if(user) { if (*user == '*') { if(!param->username) return 4; user = param->username; pass = param->password; } } if(redir->secure){ res = ssl_parent(param); if(res != PASS) return res; } switch(redir->type){ case R_TCP: case R_HTTP: return 0; case R_CONNECT: case R_CONNECTP: { len = sprintf((char *)buf, "CONNECT "); if(redir->type == R_CONNECTP && hostname) { char * needreplace; needreplace = strchr((char *)hostname, ':'); if(needreplace) buf[len++] = '['; len += sprintf((char *)buf + len, "%.256s", (char *)hostname); if(needreplace) buf[len++] = ']'; } else { if(*SAFAMILY(addr) == AF_INET6) buf[len++] = '['; len += myinet_ntop(*SAFAMILY(addr), SAADDR(addr), (char *)buf+len, 256); if(*SAFAMILY(addr) == AF_INET6) buf[len++] = ']'; } len += sprintf((char *)buf + len, ":%hu HTTP/1.0\r\nConnection: keep-alive\r\n", ntohs(*SAPORT(addr))); if(user){ len += sprintf((char *)buf + len, "Proxy-Authorization: Basic "); sprintf((char *)username, "%.128s:%.128s", user, pass?pass:(unsigned char *)""); en64(username, buf+len, (int)strlen((char *)username)); len = (int)strlen((char *)buf); len += sprintf((char *)buf + len, "\r\n"); } len += sprintf((char *)buf + len, "\r\n"); if(socksend(param, param->remsock, buf, len, conf.timeouts[CHAIN_TO]) != (int)strlen((char *)buf)) return 31; param->statssrv64+=len; param->nwrites++; if((res = sockgetlinebuf(param, SERVER,buf,13,'\n',conf.timeouts[CHAIN_TO])) < 13) return 32; if(buf[9] != '2') return 33; while((res = sockgetlinebuf(param, SERVER,buf,1023,'\n', conf.timeouts[CHAIN_TO])) > 2); if(res <= 0) return 34; return 0; } case R_SOCKS4: case R_SOCKS4P: case R_SOCKS4B: { if(*SAFAMILY(addr) != AF_INET) return 44; buf[0] = 4; buf[1] = 1; memcpy(buf+2, SAPORT(addr), 2); if(redir->type == R_SOCKS4P && hostname) { buf[4] = buf[5] = buf[6] = 0; buf[7] = 3; } else memcpy(buf+4, SAADDR(addr), 4); if(!user)user = (unsigned char *)"anonymous"; len = (int)strlen((char *)user) + 1; memcpy(buf+8, user, len); len += 8; if(redir->type == R_SOCKS4P && hostname) { int hostnamelen; hostnamelen = (int)strlen((char *)hostname) + 1; if(hostnamelen > 255) hostnamelen = 255; memcpy(buf+len, hostname, hostnamelen); len += hostnamelen; } if(socksend(param, param->remsock, buf, len, conf.timeouts[CHAIN_TO]) < len){ return 41; } param->statssrv64+=len; param->nwrites++; if((len = sockgetlinebuf(param, SERVER, buf, (redir->type == R_SOCKS4B)? 3:8, EOF, conf.timeouts[CHAIN_TO])) != ((redir->type == R_SOCKS4B)? 3:8)){ return 42; } if(buf[1] != 90) { return 43; } } return 0; case R_SOCKS5: case R_SOCKS5P: case R_SOCKS5B: { int inbuf = 0; buf[0] = 5; buf[1] = 1; buf[2] = user? 2 : 0; if(socksend(param, param->remsock, buf, 3, conf.timeouts[CHAIN_TO]) != 3){ return 51; } param->statssrv64+=3; param->nwrites++; if(sockgetlinebuf(param, SERVER, buf, 2, EOF, conf.timeouts[CHAIN_TO]) != 2){ return 52; } if(buf[0] != 5) { return 53; } if(buf[1] != 0 && !(buf[1] == 2 && user)){ return 54; } if(buf[1] == 2){ buf[inbuf++] = 1; buf[inbuf] = (unsigned char)strlen((char *)user); memcpy(buf+inbuf+1, user, buf[inbuf]); inbuf += buf[inbuf] + 1; buf[inbuf] = pass?(unsigned char)strlen((char *)pass):0; if(pass)memcpy(buf+inbuf+1, pass, buf[inbuf]); inbuf += buf[inbuf] + 1; if(socksend(param, param->remsock, buf, inbuf, conf.timeouts[CHAIN_TO]) != inbuf){ return 51; } param->statssrv64+=inbuf; param->nwrites++; if(sockgetlinebuf(param, SERVER, buf, 2, EOF, 60) != 2){ return 55; } if(buf[0] != 1 || buf[1] != 0) { return 56; } } buf[0] = 5; buf[1] = 1; buf[2] = 0; if(redir->type == R_SOCKS5P && hostname) { buf[3] = 3; len = (int)strlen((char *)hostname); if(len > 255) len = 255; buf[4] = len; memcpy(buf + 5, hostname, len); len += 5; } else { len = 3; buf[len++] = (*SAFAMILY(addr) == AF_INET)? 1 : 4; memcpy(buf+len, SAADDR(addr), SAADDRLEN(addr)); len += SAADDRLEN(addr); } memcpy(buf+len, SAPORT(addr), 2); len += 2; if(socksend(param, param->remsock, buf, len, conf.timeouts[CHAIN_TO]) != len){ return 51; } param->statssrv64+=len; param->nwrites++; if(sockgetlinebuf(param, SERVER, buf, 4, EOF, conf.timeouts[CHAIN_TO]) != 4){ return 57; } if(buf[0] != 5) { return 53; } if(buf[1] != 0) { return 60 + (buf[1] % 10); } switch (buf[3]) { case 1: if (redir->type == R_SOCKS5B || sockgetlinebuf(param, SERVER, buf, 6, EOF, conf.timeouts[CHAIN_TO]) == 6) break; return 59; case 3: if (sockgetlinebuf(param, SERVER, buf, 1, EOF, conf.timeouts[CHAIN_TO]) != 1) return 59; len = (unsigned char)buf[0]; if (sockgetlinebuf(param, SERVER, buf, len + 2, EOF, conf.timeouts[CHAIN_TO]) != len + 2) return 59; break; case 4: if (sockgetlinebuf(param, SERVER, buf, 18, EOF, conf.timeouts[CHAIN_TO]) == 18) break; return 59; default: return 58; } return 0; } default: return 30; } } int handleredirect(struct clientparam * param, struct ace * acentry){ int connected = 0; int weight = 1000; int res; int done = 0; int ha = 0; struct chain * cur; struct chain * redir = NULL; int r2; if(param->remsock != INVALID_SOCKET) { return 0; } if(SAISNULL(¶m->req) || !*SAPORT(¶m->req)) { return 100; } r2 = (myrand(param, sizeof(struct clientparam))%1000); for(cur = acentry->chains; cur; cur=cur->next){ if(((weight = weight - cur->weight) > r2)|| done) { if(weight <= 0) { weight += 1000; done = 0; r2 = (myrand(param, sizeof(struct clientparam))%1000); } continue; } param->redirected++; done = 1; if(weight <= 0) { weight += 1000; done = 0; r2 = (myrand(param, sizeof(struct clientparam))%1000); } if(!connected){ if(cur->type == R_EXTIP){ param->sinsl = cur->addr; if(SAISNULL(¶m->sinsl) && (*SAFAMILY(¶m->sincr) == AF_INET || *SAFAMILY(¶m->sincr) == AF_INET6))param->sinsl = param->sincr; #ifndef NOIPV6 else if(cur->cidr && *SAFAMILY(¶m->sinsl) == AF_INET6){ uint16_t c; int i; for(i = 0; i < 8; i++){ if(i==4)myrand(¶m->sincr, sizeof(param->sincr)); else if(i==6) myrand(¶m->req, sizeof(param->req)); if(i*16 >= cur->cidr) ((uint16_t *)SAADDR(¶m->sinsl))[i] |= rand(); else if ((i+1)*16 > cur->cidr){ c = rand(); c >>= (cur->cidr - (i*16)); c |= ntohs(((uint16_t *)SAADDR(¶m->sinsl))[i]); ((uint16_t *)SAADDR(¶m->sinsl))[i] = htons(c); } } } #endif if(cur->next)continue; return 0; } else if(SAISNULL(&cur->addr) && !*SAPORT(&cur->addr)){ int i; if(cur->extuser){ if(param->extusername) myfree(param->extusername); param->extusername = (unsigned char *)mystrdup((char *)((*cur->extuser == '*' && param->username)? param->username : cur->extuser)); if(cur->extpass){ if(param->extpassword) myfree(param->extpassword); param->extpassword = (unsigned char *)mystrdup((char *)((*cur->extuser == '*' && param->password)?param->password : cur->extpass)); } if(*cur->extuser == '*' && !param->username) return 4; } for(i=0; redirs[i].name; i++){ if(cur->type == redirs[i].redir) { param->redirectfunc = redirs[i].func; break; } } if(cur->type == R_HA){ ha = 1; } if(cur->next)continue; if(!ha) return 0; } else if(!*SAPORT(&cur->addr) && !SAISNULL(&cur->addr)) { uint16_t port = *SAPORT(¶m->sinsr); param->sinsr = cur->addr; *SAPORT(¶m->sinsr) = port; } else if(SAISNULL(&cur->addr) && *SAPORT(&cur->addr)) *SAPORT(¶m->sinsr) = *SAPORT(&cur->addr); else { param->sinsr = cur->addr; } if((res = alwaysauth(param))){ return (res >= 10)? res : 60+res; } if(ha) { char buf[128]; int len; len = sprintf(buf, "PROXY %s ", *SAFAMILY(¶m->sincr) == AF_INET6 ? "TCP6" : "TCP4"); len += myinet_ntop(*SAFAMILY(¶m->sincr), SAADDR(¶m->sincr), buf+len, sizeof(buf) - len); buf[len++] = ' '; len += myinet_ntop(*SAFAMILY(¶m->sincl), SAADDR(¶m->sincl), buf+len, sizeof(buf) - len); len += sprintf(buf + len, " %hu %hu\r\n", ntohs(*SAPORT(¶m->sincr)), ntohs(*SAPORT(¶m->sincl)) ); if(socksend(param, param->remsock, (unsigned char *)buf, len, conf.timeouts[CHAIN_TO])!=len) return 39; return 0; } } else { res = (redir)?clientnegotiate(redir, param, (struct sockaddr *)&cur->addr, cur->exthost):0; if(res) return res; } redir = cur; param->redirtype = redir->type; if(redir->type == R_TCP || redir->type ==R_HTTP) { if(cur->extuser){ if(*cur -> extuser == '*' && !param->username) return 4; if(param->extusername) myfree(param->extusername); param->extusername = (unsigned char *)mystrdup((char *)((*cur->extuser == '*' && param->username)? param->username : cur->extuser)); if(cur->extpass){ if(param->extpassword) myfree(param->extpassword); param->extpassword = (unsigned char *)mystrdup((char *)((*cur->extuser == '*' && param->password)?param->password : cur->extpass)); } } if(redir->secure) return ssl_parent(param); return 0; } connected = 1; } if(!connected || !redir) return 0; return clientnegotiate(redir, param, (struct sockaddr *)¶m->req, param->hostname); }