mirror of
https://github.com/3proxy/3proxy.git
synced 2026-04-28 15:10:12 +08:00
375 lines
10 KiB
C
375 lines
10 KiB
C
/*
|
|
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);
|
|
}
|