int checkACL(struct clientparam * param){ struct ace* acentry; if(!param->srv->acl) { return alwaysauth(param); } for(acentry = param->srv->acl; acentry; acentry = acentry->next) { if(ACLmatches(acentry, param)) { param->nolog = acentry->nolog; param->weight = acentry->weight; if(acentry->action == 2) { struct ace dup; if(param->operation < 256 && !(param->operation & CONNECT)){ continue; } if(param->redirected && acentry->chains && SAISNULL(&acentry->chains->addr) && !*SAPORT(&acentry->chains->addr)) { continue; } dup = *acentry; return handleredirect(param, &dup); } return acentry->action; } } return 3; }
int handleredirect(struct clientparam * param, struct ace * acentry){ int connected = 0; int weight = 1000; int res; int done = 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(cur->next)continue; return 0; } else if(SAISNULL(&cur->addr) && !*SAPORT(&cur->addr)){ 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; } switch(cur->type){ case R_POP3: param->redirectfunc = pop3pchild; break; case R_FTP: param->redirectfunc = ftpprchild; break; case R_ADMIN: param->redirectfunc = adminchild; break; case R_ICQ: param->redirectfunc = icqprchild; break; case R_SMTP: param->redirectfunc = smtppchild; break; default: param->redirectfunc = proxychild; } if(cur->next)continue; return 0; } else if(!*SAPORT(&cur->addr) && !SAISNULL(&cur->addr)) { unsigned short 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; } } else { res = (redir)?clientnegotiate(redir, param, (struct sockaddr *)&cur->addr):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)); } } return 0; } connected = 1; } if(!connected) return 9; return (redir)?clientnegotiate(redir, param, (struct sockaddr *)¶m->req):0; }
int ACLmatches(struct ace* acentry, struct clientparam * param){ struct userlist * userentry; struct iplist *ipentry; struct portlist *portentry; struct period *periodentry; unsigned char * username; struct hostname * hstentry=NULL; int i; int match = 0; username = param->username?param->username:(unsigned char *)"-"; if(acentry->src) { for(ipentry = acentry->src; ipentry; ipentry = ipentry->next) if(IPInentry((struct sockaddr *)¶m->sincr, ipentry)) { break; } if(!ipentry) return 0; } if((acentry->dst && !SAISNULL(¶m->req)) || (acentry->dstnames && param->hostname)) { for(ipentry = acentry->dst; ipentry; ipentry = ipentry->next) if(IPInentry((struct sockaddr *)¶m->req, ipentry)) { break; } if(!ipentry) { if(acentry->dstnames && param->hostname){ for(i=0; param->hostname[i]; i++){ param->hostname[i] = tolower(param->hostname[i]); } while(i > 5 && param->hostname[i-1] == '.') param->hostname[i-1] = 0; for(hstentry = acentry->dstnames; hstentry; hstentry = hstentry->next){ switch(hstentry->matchtype){ case 0: if(strstr((char *)param->hostname, (char *)hstentry->name)) match = 1; break; case 1: if(strstr((char *)param->hostname, (char *)hstentry->name) == (char *)param->hostname) match = 1; break; case 2: if(strstr((char *)param->hostname, (char *)hstentry->name) == (char *)(param->hostname + i - (strlen((char *)hstentry->name)))) match = 1; break; default: if(!strcmp((char *)param->hostname, (char *)hstentry->name)) match = 1; break; } if(match) break; } } } if(!ipentry && !hstentry) return 0; } if(acentry->ports && *SAPORT(¶m->req)) { for (portentry = acentry->ports; portentry; portentry = portentry->next) if(ntohs(*SAPORT(¶m->req)) >= portentry->startport && ntohs(*SAPORT(¶m->req)) <= portentry->endport) { break; } if(!portentry) return 0; } if(acentry->wdays){ if(!(acentry -> wdays & wday)) return 0; } if(acentry->periods){ int start_time = (int)(param->time_start - basetime); for(periodentry = acentry->periods; periodentry; periodentry = periodentry -> next) if(start_time >= periodentry->fromtime && start_time < periodentry->totime){ break; } if(!periodentry) return 0; } if(acentry->users){ for(userentry = acentry->users; userentry; userentry = userentry->next) if(!strcmp((char *)username, (char *)userentry->user)){ break; } if(!userentry) return 0; } if(acentry->operation) { if((acentry->operation & param->operation) != param->operation){ return 0; } } if(acentry->weight && (acentry->weight < param->weight)) return 0; return 1; }
void * sockschild(struct clientparam* param) { int res; unsigned i=0; SOCKET s; unsigned size; SASIZETYPE sasize; unsigned short port = 0; unsigned char * buf=NULL; unsigned char c; unsigned char command=0; struct pollfd fds[3]; int ver=0; int havepass = 0; #ifndef NOIPV6 struct sockaddr_in6 sin = {AF_INET6}; #else struct sockaddr_in sin = {AF_INET}; #endif int len; param->service = S_SOCKS; if(!(buf = myalloc(BUFSIZE))) {RETURN(21);} memset(buf, 0, BUFSIZE); if ((ver = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_L], 0)) != 5 && ver != 4) { RETURN(401); } /* version */ param->service = ver; if(ver == 5){ if ((i = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} /* nmethods */ for (; i; i--) { if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} if (res == 2 && param->srv->needuser) { havepass = res; } } buf[0] = 5; buf[1] = (param->srv->needuser > 1 && !havepass)? 255 : havepass; if(socksend(param->clisock, buf, 2, conf.timeouts[STRING_S])!=2){RETURN(401);} if (param->srv->needuser > 1 && !havepass) RETURN(4); if (havepass) { if (((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_L], 0))) != 1) { RETURN(412); } if ((i = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(451);} if (i && (unsigned)(res = sockgetlinebuf(param, CLIENT, buf, i, 0, conf.timeouts[STRING_S])) != i){RETURN(441);}; buf[i] = 0; if(!param->username)param->username = (unsigned char *)mystrdup((char *)buf); if ((i = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(445);} if (i && (unsigned)(res = sockgetlinebuf(param, CLIENT, buf, i, 0, conf.timeouts[STRING_S])) != i){RETURN(441);}; buf[i] = 0; if(!param->password)param->password = (unsigned char *)mystrdup((char *)buf); buf[0] = 1; buf[1] = 0; if(socksend(param->clisock, buf, 2, conf.timeouts[STRING_S])!=2){RETURN(481);} } if ((c = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_L], 0)) != 5) { RETURN(421); } /* version */ } if( (command = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) < 1 || command > 3){command = 0; RETURN(407);} /* command */ if(ver == 5){ if (sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0) == EOF) {RETURN(447);} /* reserved */ c = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0); /* atype */ } else { if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} buf[0] = (unsigned char) res; if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} buf[1] = (unsigned char) res; port = *(unsigned short*)buf; c = 1; } size = 4; *SAFAMILY(¶m->sinsr) = *SAFAMILY(¶m->req) = AF_INET; switch(c) { #ifndef NOIPV6 case 4: if(param->srv->family == 4) RETURN(997); size = 16; *SAFAMILY(¶m->sinsr) = *SAFAMILY(¶m->req) = AF_INET6; #endif case 1: for (i = 0; i<size; i++){ if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} buf[i] = (unsigned char)res; } #ifndef NOIPV6 if (c == 1 && param->srv->family==6){ char prefix[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255}; *SAFAMILY(¶m->sinsr) = *SAFAMILY(¶m->req) = AF_INET6; memcpy(SAADDR(¶m->sinsr), prefix, 12); memcpy(12 + (char *)SAADDR(¶m->sinsr), buf, 4); memcpy(SAADDR(¶m->req), prefix, 12); memcpy(12 + (char *)SAADDR(¶m->req), buf, 4); } else { #endif memcpy(SAADDR(¶m->sinsr), buf, size); memcpy(SAADDR(¶m->req), buf, size); #ifndef NOIPV6 } #endif if(SAISNULL(¶m->req)) { RETURN(421); } myinet_ntop(*SAFAMILY(¶m->sinsr), SAADDR(¶m->sinsr), (char *)buf, 64); break; case 3: if ((size = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(451);} /* nmethods */ for (i=0; i<size; i++){ /* size < 256 */ if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(451);} buf[i] = (unsigned char)res; } buf[i] = 0; if(!getip46(param->srv->family, buf, (struct sockaddr *) ¶m->req)) RETURN(100); memcpy(¶m->sinsr, ¶m->req, sizeof(param->req)); break; default: RETURN(997); } if(param->hostname)myfree(param->hostname); param->hostname = (unsigned char *)mystrdup((char *)buf); if (ver == 5) { if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} buf[0] = (unsigned char) res; if ((res = sockgetcharcli(param, conf.timeouts[SINGLEBYTE_S], 0)) == EOF) {RETURN(441);} buf[1] = (unsigned char) res; port = *(unsigned short*)buf; } else { sockgetlinebuf(param, CLIENT, buf, BUFSIZE - 1, 0, conf.timeouts[STRING_S]); buf[127] = 0; if(param->srv->needuser && *buf && !param->username)param->username = (unsigned char *)mystrdup((char *)buf); if(!memcmp(SAADDR(¶m->req), "\0\0\0", 3)){ param->service = S_SOCKS45; sockgetlinebuf(param, CLIENT, buf, BUFSIZE - 1, 0, conf.timeouts[STRING_S]); buf[127] = 0; if(param->hostname)myfree(param->hostname); param->hostname = (unsigned char *)mystrdup((char *)buf); if(!getip46(param->srv->family, buf, (struct sockaddr *) ¶m->req)) RETURN(100); memcpy(¶m->sinsr, ¶m->req, sizeof(¶m->req)); } } *SAPORT(¶m->sinsr) = *SAPORT(¶m->req) = port; if(command == 1 && !*SAPORT(¶m->sinsr)) {RETURN(421);} switch(command) { case 1: param->operation = CONNECT; break; case 2: case 3: #ifndef NOIPV6 memcpy(¶m->sinsl, *SAFAMILY(¶m->req)==AF_INET6? (struct sockaddr *)¶m->srv->extsa6:(struct sockaddr *)¶m->srv->extsa, SASIZE(¶m->req)); #else memcpy(¶m->sinsl, ¶m->srv->extsa, SASIZE(¶m->req)); #endif if ((param->remsock=so._socket(SASOCK(¶m->req), command == 2? SOCK_STREAM:SOCK_DGRAM, command == 2?IPPROTO_TCP:IPPROTO_UDP)) == INVALID_SOCKET) {RETURN (11);} param->operation = command == 2?BIND:UDPASSOC; break; default: RETURN(997); } if((res = (*param->srv->authfunc)(param))) { RETURN(res); } if(command > 1) { if(so._bind(param->remsock,(struct sockaddr *)¶m->sinsl,SASIZE(¶m->sinsl))) { *SAPORT(¶m->sinsl) = 0; if(so._bind(param->remsock,(struct sockaddr *)¶m->sinsl,SASIZE(¶m->sinsl)))RETURN (12); #if SOCKSTRACE > 0 fprintf(stderr, "%hu binded to communicate with server\n", *SAPORT(¶m->sins)); fflush(stderr); #endif } sasize = SASIZE(¶m->sinsl); so._getsockname(param->remsock, (struct sockaddr *)¶m->sinsl, &sasize); if(command == 3) { param->ctrlsock = param->clisock; param->clisock = so._socket(SASOCK(¶m->sincr), SOCK_DGRAM, IPPROTO_UDP); if(param->clisock == INVALID_SOCKET) {RETURN(11);} memcpy(&sin, ¶m->sincl, sizeof(&sin)); *SAPORT(&sin) = 0; if(so._bind(param->clisock,(struct sockaddr *)&sin,sizeof(sin))) {RETURN (12);} #if SOCKSTRACE > 0 fprintf(stderr, "%hu binded to communicate with client\n", ntohs(*SAPORT(&sin)) ); fflush(stderr); #endif } } param->res = 0; CLEANRET: if(param->clisock != INVALID_SOCKET){ int repcode; sasize = sizeof(sin); if(command != 3) so._getsockname(param->remsock, (struct sockaddr *)&sin, &sasize); else so._getsockname(param->clisock, (struct sockaddr *)&sin, &sasize); #if SOCKSTRACE > 0 fprintf(stderr, "Sending confirmation to client with code %d for %s with %s:%hu\n", param->res, commands[command], inet_ntoa(sin.sin_addr), ntohs(sin.sin_port) ); fflush(stderr); #endif if(!param->res) repcode = 0; else if(param->res <= 10) repcode = 2; else if (param->res < 20) repcode = 5; else if (param->res < 30) repcode = 1; else if (param->res < 100) repcode = 4; else repcode = param->res%10; if(ver == 5){ buf[0] = 5; buf[1] = repcode; buf[2] = 0; buf[3] = (*SAFAMILY(&sin) == AF_INET)?1:4; memcpy(buf+4, SAADDR(&sin), SAADDRLEN(&sin)); memcpy(buf+4+SAADDRLEN(&sin), SAPORT(&sin), 2); socksend((command == 3)?param->ctrlsock:param->clisock, buf, 6+SAADDRLEN(&sin), conf.timeouts[STRING_S]); } else{ buf[0] = 0; buf[1] = 90 + !!(repcode); memcpy(buf+2, SAPORT(&sin), 2); memcpy(buf+4, SAADDR(&sin), 4); socksend(param->clisock, buf, 8, conf.timeouts[STRING_S]); } if (param->res == 0) { switch(command) { case 1: if(param->redirectfunc){ if(buf)myfree(buf); return (*param->redirectfunc)(param); } param->res = sockmap(param, conf.timeouts[CONNECTION_L]); break; case 2: so._listen (param->remsock, 1); fds[0].fd = param->remsock; fds[1].fd = param->clisock; fds[0].events = fds[1].events = POLLIN; res = so._poll(fds, 2, conf.timeouts[CONNECTION_L] * 1000); if (res < 1 || fds[1].revents) { res = 460; break; } sasize = sizeof(param->sinsr); s = so._accept(param->remsock, (struct sockaddr *)¶m->sinsr, &sasize); so._closesocket(param->remsock); param->remsock = s; if(s == INVALID_SOCKET) { param->res = 462; break; } if(SAISNULL(¶m->req) && memcmp(SAADDR(¶m->req),SAADDR(¶m->sinsr),SAADDRLEN(¶m->req))) { param->res = 470; break; } #if SOCKSTRACE > 0 fprintf(stderr, "Sending incoming connection to client with code %d for %s with %hu\n", param->res, commands[command], *SAPORT(param->sins); ); fflush(stderr); #endif if(ver == 5){ buf[3] = (*SAFAMILY(¶m->sinsr) == AF_INET)?1:4; memcpy(buf+4, SAADDR(¶m->sinsr), SAADDRLEN(¶m->sinsr)); memcpy(buf+4+SAADDRLEN(¶m->sinsr), SAPORT(¶m->sinsr), 2); socksend(param->clisock, buf, 6+SAADDRLEN(¶m->sinsr), conf.timeouts[STRING_S]); } else { memcpy (buf+2, SAPORT(¶m->sinsr), 2); memcpy (buf+4, SAADDR(¶m->sinsr), 4); socksend(param->clisock, buf, 8, conf.timeouts[STRING_S]); } param->res = sockmap(param, conf.timeouts[CONNECTION_S]); break; case 3: memcpy(¶m->sinsr, ¶m->req, sizeof(param->sinsr)); myfree(buf); if(!(buf = myalloc(LARGEBUFSIZE))) {RETURN(21);} 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 = so._poll(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 = so._recvfrom(param->clisock, 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; memcpy(SAADDR(¶m->sinsr), buf+4, size); *SAFAMILY(¶m->sinsr) = (size == 4)?AF_INET:AF_INET6; 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, (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){ if(socksendto(param->remsock, (struct sockaddr *)¶m->sinsr, buf+i, len - i, conf.timeouts[SINGLEBYTE_L]*1000) <= 0){ param->res = 467; break; } param->statscli64+=(len - i); param->nwrites++; #if SOCKSTRACE > 1 fprintf(stderr, "UDP packet relayed from client to %s:%hu size %d, header %d\n", inet_ntoa(param->sins.sin_addr), ntohs(param->sins.sin_port), (len - i), i ); fprintf(stderr, "client address is assumed to be %s:%hu\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port) ); 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 = so._recvfrom(param->remsock, buf+6+SAADDRLEN(¶m->sinsl), 65535 - 10, 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); if(socksendto(param->clisock, (struct sockaddr *)&sin, buf, len + 6 + SAADDRLEN(¶m->sinsr), conf.timeouts[SINGLEBYTE_L]*1000) <=0){ param->res = 469; break; } #if SOCKSTRACE > 1 fprintf(stderr, "UDP packet relayed to client from %hu size %d\n", ntohs(*SAPORT(¶m->sinsr)), len ); fflush(stderr); #endif } } break; default: param->res = 417; break; } }