/* 3APA3A simpliest proxy server (c) 2002-2016 by Vladimir Dubrovin <3proxy@3proxy.ru> please read License Agreement */ #include "proxy.h" 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); } buf = param->srvbuf; username = buf + 2048; if(user) { if (*user == '*') { if(!param->username) return 4; user = param->username; pass = param->password; } } 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->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->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->remsock, buf, 3, conf.timeouts[CHAIN_TO]) != 3){ return 51; } param->statssrv64+=len; 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->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->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, 256, 0, conf.timeouts[CHAIN_TO]) > 1) break; return 59; 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; 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))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)){ 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_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, 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)); } } return 0; } connected = 1; } if(!connected || !redir) return 0; return clientnegotiate(redir, param, (struct sockaddr *)¶m->req, param->hostname); } int IPInentry(struct sockaddr *sa, struct iplist *ipentry){ int addrlen; unsigned char *ip, *ipf, *ipt; if(!sa || ! ipentry || *SAFAMILY(sa) != ipentry->family) return 0; ip = (unsigned char *)SAADDR(sa); ipf = (unsigned char *)&ipentry->ip_from; ipt = (unsigned char *)&ipentry->ip_to; addrlen = SAADDRLEN(sa); if(memcmp(ip,ipf,addrlen) < 0 || memcmp(ip,ipt,addrlen) > 0) return 0; return 1; } 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){ int lname, lhost; switch(hstentry->matchtype){ case 0: #ifndef _WIN32 if(strcasestr((char *)param->hostname, (char *)hstentry->name)) match = 1; #else if(strstr((char *)param->hostname, (char *)hstentry->name)) match = 1; #endif break; case 1: if(!strncasecmp((char *)param->hostname, (char *)hstentry->name, strlen((char *)hstentry->name))) match = 1; break; case 2: lname = strlen((char *)hstentry->name); lhost = strlen((char *)param->hostname); if(lhost > lname){ if(!strncasecmp((char *)param->hostname + (lhost - lname), (char *)hstentry->name, lname)) match = 1; } break; default: if(!strcasecmp((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; } int startconnlims (struct clientparam *param){ struct connlim * ce; time_t delta; uint64_t rating; int ret = 0; pthread_mutex_lock(&connlim_mutex); for(ce = conf.connlimiter; ce; ce = ce->next) { if(ACLmatches(ce->ace, param)){ if(ce->ace->action == NOCONNLIM)break; if(!ce->period){ if(ce->rate <= ce->rating) { ret = 1; break; } ce->rating++; continue; } delta = conf.time - ce->basetime; if(ce->period <= delta || ce->basetime > conf.time){ ce->basetime = conf.time; ce->rating = 0x100000; continue; } rating = delta? ((ce->rating * (ce->period - delta)) / ce->period) + 0x100000 : ce->rating + 0x100000; if (rating > (ce->rate<<20)) { ret = 2; break; } ce->rating = rating; ce->basetime = conf.time; } } pthread_mutex_unlock(&connlim_mutex); return ret; } void stopconnlims (struct clientparam *param){ struct connlim * ce; pthread_mutex_lock(&connlim_mutex); for(ce = conf.connlimiter; ce; ce = ce->next) { if(ACLmatches(ce->ace, param)){ if(ce->ace->action == NOCONNLIM)break; if(!ce->period && ce->rating){ ce->rating--; continue; } } } pthread_mutex_unlock(&connlim_mutex); } static void initbandlims (struct clientparam *param){ struct bandlim * be; int i; param->bandlimfunc = NULL; param->bandlims[0] = NULL; param->bandlimsout[0] = NULL; if(!conf.bandlimfunc || (!conf.bandlimiter && !conf.bandlimiterout)) return; for(i=0, be = conf.bandlimiter; be && inext) { if(ACLmatches(be->ace, param)){ if(be->ace->action == NOBANDLIM) { break; } param->bandlims[i++] = be; param->bandlimfunc = conf.bandlimfunc; } } if(ibandlims[i] = NULL; for(i=0, be = conf.bandlimiterout; be && inext) { if(ACLmatches(be->ace, param)){ if(be->ace->action == NOBANDLIM) { break; } param->bandlimsout[i++] = be; param->bandlimfunc = conf.bandlimfunc; } } if(ibandlimsout[i] = NULL; param->bandlimver = conf.bandlimver; } unsigned bandlimitfunc(struct clientparam *param, unsigned nbytesin, unsigned nbytesout){ unsigned sleeptime = 0, nsleeptime; time_t sec; unsigned msec; unsigned now; int i; #ifdef _WIN32 struct timeb tb; ftime(&tb); sec = (unsigned)tb.time; msec = (unsigned)tb.millitm*1000; #else struct timeval tv; gettimeofday(&tv, NULL); sec = tv.tv_sec; msec = tv.tv_usec; #endif if(!nbytesin && !nbytesout) return 0; pthread_mutex_lock(&bandlim_mutex); if(param->bandlimver != conf.bandlimver){ initbandlims(param); param->bandlimver = conf.bandlimver; } for(i=0; nbytesin&& ibandlims[i]; i++){ if( !param->bandlims[i]->basetime || param->bandlims[i]->basetime > sec || param->bandlims[i]->basetime < (sec - 120) ) { param->bandlims[i]->basetime = sec; param->bandlims[i]->nexttime = 0; continue; } now = (unsigned)((sec - param->bandlims[i]->basetime) * 1000000) + msec; nsleeptime = (param->bandlims[i]->nexttime > now)? param->bandlims[i]->nexttime - now : 0; sleeptime = (nsleeptime > sleeptime)? nsleeptime : sleeptime; param->bandlims[i]->basetime = sec; param->bandlims[i]->nexttime = msec + nsleeptime + ((nbytesin > 512)? ((nbytesin+32)/64)*(((64*8*1000000)/param->bandlims[i]->rate)) : ((nbytesin+1) * (8*1000000))/param->bandlims[i]->rate); } for(i=0; nbytesout && ibandlimsout[i]; i++){ if( !param->bandlimsout[i]->basetime || param->bandlimsout[i]->basetime > sec || param->bandlimsout[i]->basetime < (sec - 120) ) { param->bandlimsout[i]->basetime = sec; param->bandlimsout[i]->nexttime = 0; continue; } now = (unsigned)((sec - param->bandlimsout[i]->basetime) * 1000000) + msec; nsleeptime = (param->bandlimsout[i]->nexttime > now)? param->bandlimsout[i]->nexttime - now : 0; sleeptime = (nsleeptime > sleeptime)? nsleeptime : sleeptime; param->bandlimsout[i]->basetime = sec; param->bandlimsout[i]->nexttime = msec + nsleeptime + ((nbytesout > 512)? ((nbytesout+32)/64)*((64*8*1000000)/param->bandlimsout[i]->rate) : ((nbytesout+1)* (8*1000000))/param->bandlimsout[i]->rate); } pthread_mutex_unlock(&bandlim_mutex); return sleeptime/1000; } void trafcountfunc(struct clientparam *param){ struct trafcount * tc; int countout = 0; pthread_mutex_lock(&tc_mutex); for(tc = conf.trafcounter; tc; tc = tc->next) { if(ACLmatches(tc->ace, param)){ if(tc->ace->action == NOCOUNTIN) { countout = 1; break; } if(tc->ace->action == NOCOUNTALL) break; if(tc->ace->action != COUNTIN && tc->ace->action != COUNTALL) { countout = 1; continue; } tc->traf64 += param->statssrv64; tc->updated = conf.time; } } if(countout) for(tc = conf.trafcounter; tc; tc = tc->next) { if(ACLmatches(tc->ace, param)){ if(tc->ace->action == NOCOUNTOUT || tc->ace->action == NOCOUNTALL) break; if(tc->ace->action != COUNTOUT && tc->ace->action != COUNTALL ) { continue; } tc->traf64 += param->statscli64; tc->updated = conf.time; } } pthread_mutex_unlock(&tc_mutex); } int alwaysauth(struct clientparam * param){ int res; struct trafcount * tc; int countout = 0; if(conf.connlimiter && param->remsock == INVALID_SOCKET && startconnlims(param)) return 95; res = doconnect(param); if(!res){ if(conf.bandlimfunc && (conf.bandlimiter||conf.bandlimiterout)){ pthread_mutex_lock(&bandlim_mutex); initbandlims(param); pthread_mutex_unlock(&bandlim_mutex); } if(conf.trafcountfunc && conf.trafcounter) { pthread_mutex_lock(&tc_mutex); for(tc = conf.trafcounter; tc; tc = tc->next) { if(tc->disabled) continue; if(ACLmatches(tc->ace, param)){ if(tc->ace->action == NOCOUNTIN) { countout = 1; break; } if(tc->ace->action == NOCOUNTALL) break; if(tc->ace->action != COUNTIN) { countout = 1; if(tc->ace->action != COUNTALL) continue; } if(tc->traflim64 <= tc->traf64) return 10; param->trafcountfunc = conf.trafcountfunc; param->maxtrafin64 = tc->traflim64 - tc->traf64; } } if(countout)for(tc = conf.trafcounter; tc; tc = tc->next) { if(tc->disabled) continue; if(ACLmatches(tc->ace, param)){ if(tc->ace->action == NOCOUNTOUT || tc->ace->action == NOCOUNTALL) break; if(tc->ace->action != COUNTOUT && tc->ace->action != COUNTALL) { continue; } if(tc->traflim64 <= tc->traf64) return 10; param->trafcountfunc = conf.trafcountfunc; param->maxtrafout64 = tc->traflim64 - tc->traf64; } } pthread_mutex_unlock(&tc_mutex); } } return res; } int checkACL(struct clientparam * param){ struct ace* acentry; if(!param->srv->acl) { return 0; } 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; int res=60,i=0; if(param->operation < 256 && !(param->operation & CONNECT)){ continue; } if(param->redirected && acentry->chains && SAISNULL(&acentry->chains->addr) && !*SAPORT(&acentry->chains->addr)) { continue; } if(param->remsock != INVALID_SOCKET) { return 0; } for(; i < conf.parentretries; i++){ dup = *acentry; res = handleredirect(param, &dup); if(!res) break; if(param->remsock != INVALID_SOCKET) so._closesocket(param->remsock); param->remsock = INVALID_SOCKET; } return res; } return acentry->action; } } return 3; } struct authcache { char * username; char * password; time_t expires; #ifndef NOIPV6 struct sockaddr_in6 sa, sinsl; #else struct sockaddr_in sa, sinsl; #endif struct ace *acl; struct authcache *next; } *authc = NULL; int cacheauth(struct clientparam * param){ struct authcache *ac, *last=NULL; pthread_mutex_lock(&hash_mutex); for(ac = authc; ac; ){ if(ac->expires <= conf.time){ if(ac->username)myfree(ac->username); if(ac->password)myfree(ac->password); if(!last){ authc = ac->next; myfree(ac); ac = authc; } else { last->next = ac->next; myfree(ac); ac = last->next; } continue; } if( (!(conf.authcachetype&2) || (param->username && ac->username && !strcmp(ac->username, (char *)param->username))) && (!(conf.authcachetype&4) || (ac->password && param->password && !strcmp(ac->password, (char *)param->password))) && (!(conf.authcachetype&16) || (ac->acl == param->srv->acl)) ) { if(!(conf.authcachetype&1) || ((*SAFAMILY(&ac->sa) == *SAFAMILY(¶m->sincr) && !memcmp(SAADDR(&ac->sa), SAADDR(¶m->sincr), SAADDRLEN(&ac->sa))))){ if(conf.authcachetype&32) { param->sinsl = ac->sinsl; } if(param->username){ myfree(param->username); } param->username = (unsigned char *)mystrdup(ac->username); pthread_mutex_unlock(&hash_mutex); return 0; } else if ((conf.authcachetype&1) && (conf.authcachetype&8)) { pthread_mutex_unlock(&hash_mutex); return 10; } } last = ac; ac = ac->next; } pthread_mutex_unlock(&hash_mutex); return 4; } int doauth(struct clientparam * param){ int res = 0; struct auth *authfuncs; struct authcache *ac; char * tmp; int ret = 0; for(authfuncs=param->srv->authfuncs; authfuncs; authfuncs=authfuncs->next){ res = authfuncs->authenticate?(*authfuncs->authenticate)(param):0; if(!res) { if(authfuncs->authorize && (res = (*authfuncs->authorize)(param))) return res; if(conf.authcachetype && authfuncs->authenticate && authfuncs->authenticate != cacheauth && param->username && (!(conf.authcachetype&4) || (!param->pwtype && param->password))){ pthread_mutex_lock(&hash_mutex); for(ac = authc; ac; ac = ac->next){ if( (!(conf.authcachetype&2) || !strcmp(ac->username, (char *)param->username)) && (!(conf.authcachetype&1) || (*SAFAMILY(&ac->sa) == *SAFAMILY(¶m->sincr) && !memcmp(SAADDR(&ac->sa), SAADDR(¶m->sincr), SAADDRLEN(&ac->sa)))) && (!(conf.authcachetype&4) || (ac->password && !strcmp(ac->password, (char *)param->password))) && (!(conf.authcachetype&16) || (ac->acl == param->srv->acl)) ) { ac->expires = conf.time + conf.authcachetime; if(strcmp(ac->username, (char *)param->username)){ tmp = ac->username; ac->username = mystrdup((char *)param->username); myfree(tmp); } if((conf.authcachetype&4)){ tmp = ac->password; ac->password = mystrdup((char *)param->password); myfree(tmp); } ac->sa = param->sincr; if(conf.authcachetype&32) { ac->sinsl = param-> sinsl; *SAPORT(&ac->sinsl) = 0; } break; } } if(!ac){ ac = myalloc(sizeof(struct authcache)); if(ac){ ac->expires = conf.time + conf.authcachetime; ac->username = param->username?mystrdup((char *)param->username):NULL; ac->sa = param->sincr; ac->password = NULL; if((conf.authcachetype&4) && param->password) ac->password = mystrdup((char *)param->password); if(conf.authcachetype&32) { ac->sinsl = param->sinsl; *SAPORT(&ac->sinsl) = 0; } } ac->next = authc; authc = ac; } pthread_mutex_unlock(&hash_mutex); } break; } if(res > ret) ret = res; if(ret > 9) return ret; } if(!res){ return alwaysauth(param); } return ret; } int ipauth(struct clientparam * param){ int res; unsigned char *username; username = param->username; param->username = NULL; res = checkACL(param); param->username = username; return res; } int userauth(struct clientparam * param){ return (param->username)? 0:4; } int dnsauth(struct clientparam * param){ char buf[128]; char addr[16]; char dig[]="0123456789abcdef"; unsigned u; int i; if(*SAFAMILY(¶m->sincr)!=AF_INET){ char *s = buf; for(i=15; i>=0; i--){ unsigned char c=((unsigned char *)SAADDR(¶m->sincr))[i]; *s++ = dig[(c&0xf)]; *s++ = '.'; *s++ = dig[(c>>4)]; *s++ = '.'; } sprintf(s, "ip6.arpa"); } else { u = ntohl(*(unsigned long *)SAADDR(¶m->sincr)); sprintf(buf, "%u.%u.%u.%u.in-addr.arpa", ((u&0x000000FF)), ((u&0x0000FF00)>>8), ((u&0x00FF0000)>>16), ((u&0xFF000000)>>24)); } if(!udpresolve(*SAFAMILY(¶m->sincr), (unsigned char *)buf, (unsigned char *)addr, NULL, param, 1)) { return 3; } if(memcmp(SAADDR(¶m->sincr), addr, SAADDRLEN(¶m->sincr))) { return 3; } return param->username? 0:3; } int strongauth(struct clientparam * param){ struct passwords * pwl; unsigned char buf[256]; if(!param->username) return 4; pthread_mutex_lock(&pwl_mutex); for(pwl = conf.pwl; pwl; pwl=pwl->next){ if(!strcmp((char *)pwl->user, (char *)param->username)) switch(pwl->pwtype) { case CL: if(!pwl->password || !*pwl->password){ break; } else if (!param->pwtype && param->password && !strcmp((char *)param->password, (char *)pwl->password)){ break; } #ifndef NOCRYPT else if (param->pwtype == 2 && param->password) { ntpwdhash(buf, pwl->password, 0); mschap(buf, param->password, buf + 16); if(!memcmp(buf+16, param->password+8, 24)) { break; } } #endif pthread_mutex_unlock(&pwl_mutex); return 6; #ifndef NOCRYPT case CR: if(param->password && !param->pwtype && !strcmp((char *)pwl->password, (char *)mycrypt(param->password, pwl->password,buf))) { break; } pthread_mutex_unlock(&pwl_mutex); return 7; case NT: if(param->password && !param->pwtype && !memcmp(pwl->password, ntpwdhash(buf,param->password, 1), 32)) { break; } else if (param->pwtype == 2){ fromhex(pwl->password, buf, 16); mschap(buf, param->password, buf + 16); if(!memcmp(buf + 16, param->password+8, 24)) { break; } } pthread_mutex_unlock(&pwl_mutex); return 8; #endif default: pthread_mutex_unlock(&pwl_mutex); return 999; } else continue; pthread_mutex_unlock(&pwl_mutex); return 0; } pthread_mutex_unlock(&pwl_mutex); return 5; } int radauth(struct clientparam * param); struct auth authfuncs[] = { {authfuncs+1, NULL, NULL, ""}, {authfuncs+2, ipauth, NULL, "iponly"}, {authfuncs+3, userauth, checkACL, "useronly"}, {authfuncs+4, dnsauth, checkACL, "dnsname"}, {authfuncs+5, strongauth, checkACL, "strong"}, {authfuncs+6, cacheauth, checkACL, "cache"}, #ifndef NORADIUS #define AUTHOFFSET 1 {authfuncs+7, radauth, checkACL, "radius"}, #else #define AUTHOFFSET 0 #endif {authfuncs+7+AUTHOFFSET, NULL, NULL, "none"}, {NULL, NULL, NULL, ""} }; struct hashtable dns_table = {0, 4, {0,0,0,0}, NULL, NULL, NULL}; struct hashtable dns6_table = {0, 16, {0,0,0,0}, NULL, NULL, NULL}; void nametohash(const unsigned char * name, unsigned char *hash, unsigned char *rnd){ unsigned i, j, k; memcpy(hash, rnd, sizeof(unsigned)*4); for(i=0, j=0, k=0; name[j]; j++){ hash[i] += (toupper(name[j]) - 32)+rnd[((toupper(name[j]))*29277+rnd[(k+j+i)%16]+k+j+i)%16]; if(++i == sizeof(unsigned)*4) { i = 0; k++; } } } unsigned hashindex(struct hashtable *ht, const unsigned char* hash){ unsigned t1, t2, t3, t4; t1 = *(unsigned *)hash; t2 = *(unsigned *)(hash + sizeof(unsigned)); t3 = *(unsigned *)(hash + (2*sizeof(unsigned))); t4 = *(unsigned *)(hash + (3*sizeof(unsigned))); return (t1 + (t2 * 7) + (t3 * 17) + (t4 * 29) ) % (ht->hashsize >> 2); } void destroyhashtable(struct hashtable *ht){ pthread_mutex_lock(&hash_mutex); if(ht->hashtable){ myfree(ht->hashtable); ht->hashtable = NULL; } if(ht->hashvalues){ myfree(ht->hashvalues); ht->hashvalues = NULL; } ht->hashsize = 0; pthread_mutex_unlock(&hash_mutex); } #define hvalue(I) ((struct hashentry *)((char *)ht->hashvalues + (I)*(sizeof(struct hashentry) + ht->recsize - 4))) int inithashtable(struct hashtable *ht, unsigned nhashsize){ unsigned i; clock_t c; #ifdef _WIN32 struct timeb tb; ftime(&tb); #else struct timeval tb; struct timezone tz; gettimeofday(&tb, &tz); #endif c = clock(); if(nhashsize<4) return 1; pthread_mutex_lock(&hash_mutex); if(ht->hashtable){ myfree(ht->hashtable); ht->hashtable = NULL; } if(ht->hashvalues){ myfree(ht->hashvalues); ht->hashvalues = NULL; } ht->hashsize = 0; if(!(ht->hashtable = myalloc((nhashsize>>2) * sizeof(struct hashentry *)))){ pthread_mutex_unlock(&hash_mutex); return 2; } if(!(ht->hashvalues = myalloc(nhashsize * (sizeof(struct hashentry) + (ht->recsize-4))))){ myfree(ht->hashtable); ht->hashtable = NULL; pthread_mutex_unlock(&hash_mutex); return 3; } ht->hashsize = nhashsize; ht->rnd[0] = myrand(&tb, sizeof(tb)); ht->rnd[1] = myrand(ht->hashtable, sizeof(ht->hashtable)); ht->rnd[2] = myrand(&c, sizeof(c)); ht->rnd[3] = myrand(ht->hashvalues,sizeof(ht->hashvalues)); memset(ht->hashtable, 0, (ht->hashsize>>2) * sizeof(struct hashentry *)); memset(ht->hashvalues, 0, ht->hashsize * (sizeof(struct hashentry) + ht->recsize -4)); for(i = 0; i< (ht->hashsize - 1); i++) { hvalue(i)->next = hvalue(i+1); } ht->hashempty = ht->hashvalues; pthread_mutex_unlock(&hash_mutex); return 0; } void hashadd(struct hashtable *ht, const unsigned char* name, unsigned char* value, time_t expires){ struct hashentry * hen, *he; struct hashentry ** hep; unsigned index; pthread_mutex_lock(&hash_mutex); if(!ht||!value||!name||!ht->hashtable||!ht->hashempty) { pthread_mutex_unlock(&hash_mutex); return; } hen = ht->hashempty; ht->hashempty = ht->hashempty->next; nametohash(name, hen->hash, (unsigned char *)ht->rnd); memcpy(hen->value, value, ht->recsize); hen->expires = expires; hen->next = NULL; index = hashindex(ht, hen->hash); for(hep = ht->hashtable + index; (he = *hep)!=NULL; ){ if(he->expires < conf.time || !memcmp(hen->hash, he->hash, sizeof(he->hash))) { (*hep) = he->next; he->expires = 0; he->next = ht->hashempty; ht->hashempty = he; } else hep=&(he->next); } hen->next = ht->hashtable[index]; ht->hashtable[index] = hen; pthread_mutex_unlock(&hash_mutex); } unsigned long hashresolv(struct hashtable *ht, const unsigned char* name, unsigned char* value, unsigned *ttl){ unsigned char hash[sizeof(unsigned)*4]; struct hashentry ** hep; struct hashentry *he; unsigned index; pthread_mutex_lock(&hash_mutex); if(!ht || !ht->hashtable || !name) { pthread_mutex_unlock(&hash_mutex); return 0; } nametohash(name, hash, (unsigned char *)ht->rnd); index = hashindex(ht, hash); for(hep = ht->hashtable + index; (he = *hep)!=NULL; ){ if(he->expires < conf.time) { (*hep) = he->next; he->expires = 0; he->next = ht->hashempty; ht->hashempty = he; } else if(!memcmp(hash, he->hash, sizeof(unsigned)*4)){ if(ttl) *ttl = (unsigned)(he->expires - conf.time); memcpy(value, he->value, ht->recsize); pthread_mutex_unlock(&hash_mutex); return 1; } else hep=&(he->next); } pthread_mutex_unlock(&hash_mutex); return 0; } struct nserver nservers[MAXNSERVERS] = {{{0},0}, {{0},0}, {{0},0}, {{0},0}, {{0},0}}; struct nserver authnserver; unsigned long udpresolve(int af, unsigned char * name, unsigned char * value, unsigned *retttl, struct clientparam* param, int makeauth){ int i,n; unsigned long retval; if((af == AF_INET) && (retval = hashresolv(&dns_table, name, value, retttl))) { return retval; } if((af == AF_INET6) && (retval = hashresolv(&dns6_table, name, value, retttl))) { return retval; } n = (makeauth && !SAISNULL(&authnserver.addr))? 1 : numservers; for(i=0; isinsl : &addr; sinsr = (param && !makeauth)? ¶m->sinsr : &addr; memset(sinsl, 0, sizeof(addr)); memset(sinsr, 0, sizeof(addr)); if(makeauth && !SAISNULL(&authnserver.addr)){ usetcp = authnserver.usetcp; *SAFAMILY(sinsl) = *SAFAMILY(&authnserver.addr); } else { usetcp = nservers[i].usetcp; *SAFAMILY(sinsl) = *SAFAMILY(&nservers[i].addr); } if((sock=so._socket(SASOCK(sinsl), usetcp?SOCK_STREAM:SOCK_DGRAM, usetcp?IPPROTO_TCP:IPPROTO_UDP)) == INVALID_SOCKET) break; if(so._bind(sock,(struct sockaddr *)sinsl,SASIZE(sinsl))){ so._shutdown(sock, SHUT_RDWR); so._closesocket(sock); break; } if(makeauth && !SAISNULL(&authnserver.addr)){ *sinsr = authnserver.addr; } else { *sinsr = nservers[i].addr; } if(usetcp){ if(connectwithpoll(sock,(struct sockaddr *)sinsr,SASIZE(sinsr),CONNECT_TO)) { so._shutdown(sock, SHUT_RDWR); so._closesocket(sock); break; } #ifdef TCP_NODELAY { int opt = 1; setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, sizeof(opt)); } #endif } len = (int)strlen((char *)name); serial = myrand(name,len); *(unsigned short*)buf = serial; /* query id */ buf[2] = 1; /* recursive */ buf[3] = 0; buf[4] = 0; buf[5] = 1; /* 1 request */ buf[6] = buf[7] = 0; /* no replies */ buf[8] = buf[9] = 0; /* no ns count */ buf[10] = buf[11] = 0; /* no additional */ if(len > 255) { len = 255; } memcpy(buf + 13, name, len); len += 13; buf[len] = 0; for(s2 = buf + 12; (s1 = (unsigned char *)strchr((char *)s2 + 1, '.')); s2 = s1)*s2 = (unsigned char)((s1 - s2) - 1); *s2 = (len - (int)(s2 - buf)) - 1; len++; buf[len++] = 0; buf[len++] = (makeauth == 1)? 0x0c : (af==AF_INET6? 0x1c:0x01); /* PTR:host address */ buf[len++] = 0; buf[len++] = 1; /* INET */ if(usetcp){ buf-=2; *(unsigned short*)buf = htons(len); len+=2; } if(socksendto(sock, (struct sockaddr *)sinsr, buf, len, conf.timeouts[SINGLEBYTE_L]*1000) != len){ so._shutdown(sock, SHUT_RDWR); so._closesocket(sock); continue; } if(param) param->statscli64 += len; len = sockrecvfrom(sock, (struct sockaddr *)sinsr, buf, 4096, conf.timeouts[DNS_TO]*1000); so._shutdown(sock, SHUT_RDWR); so._closesocket(sock); if(len <= 13) { continue; } if(param) param->statssrv64 += len; if(usetcp){ unsigned short us; us = ntohs(*(unsigned short*)buf); len-=2; buf+=2; if(us > 4096 || us < len || (us > len && sockrecvfrom(sock, (struct sockaddr *)sinsr, buf+len, us-len, conf.timeouts[DNS_TO]*1000) != us-len)) { continue; } } if(*(unsigned short *)buf != serial)continue; if((na = buf[7] + (((unsigned short)buf[6])<<8)) < 1) { return 0; } nq = buf[5] + (((unsigned short)buf[4])<<8); if (nq != 1) { continue; /* we did only 1 request */ } for(k = 13; k= len) { continue; } k += 4; if(na > 255) na = 255; /* somebody is very evil */ for (j = 0; j < na; j++) { /* now there should be answers */ while(buf[k] < 192 && buf[k] !=0 && (k+buf[k]+14) < len) k+= (buf[k] + 1); if(!buf[k]) k--; if((k+(af == AF_INET6?28:16)) > len) { break; } flen = buf[k+11] + (((unsigned short)buf[k+10])<<8); if((k+12+flen) > len) { break; } if(makeauth != 1){ if(buf[k+2] != 0 || buf[k+3] != (af == AF_INET6?0x1c:0x1) || flen != (af == AF_INET6?16:4)) { k+= (12 + flen); continue; /* we need A IPv4 */ } ttl = ntohl(*(unsigned long *)(buf + k + 6)); memcpy(value, buf + k + 12, af == AF_INET6? 16:4); if(ttl < 60 || ttl > (3600*12)) ttl = 300; hashadd(af == AF_INET6?&dns6_table:&dns_table, name, value, conf.time+ttl); if(retttl) *retttl = ttl; return 1; } else { if(buf[k+2] != 0 || buf[k+3] != 0x0c) { k+= (12 + flen); continue; /* we need A PTR */ } for (s2 = buf + k + 12; s2 < (buf + k + 12 + len) && *s2; ){ s1 = s2 + ((unsigned)*s2) + 1; *s2 = '.'; s2 = s1; } *s2 = 0; if(param->username)myfree(param->username); param->username = (unsigned char *)mystrdup ((char *)buf + k + 13); return udpresolve(af,param->username, value, NULL, NULL, 2); } } } return 0; } unsigned long myresolver(int af, unsigned char * name, unsigned char * value){ return udpresolve(af, name, value, NULL, NULL, 0); } unsigned long fakeresolver (int af, unsigned char *name, unsigned char * value){ memset(value, 0, af == AF_INET6? 16 : 4); if(af == AF_INET6){ memset(value, 0, 16); value[15] = 2; } else { value[0] = 127; value[1] = 0; value[2] = 0; value[3] = 2; } return 1; } #ifndef NOODBC SQLHENV henv = NULL; SQLHSTMT hstmt = NULL; SQLHDBC hdbc = NULL; char * sqlstring = NULL; void close_sql(){ if(hstmt) { SQLFreeHandle(SQL_HANDLE_STMT, hstmt); hstmt = NULL; } if(hdbc){ SQLDisconnect(hdbc); SQLFreeHandle(SQL_HANDLE_DBC, hdbc); hdbc = NULL; } if(henv) { SQLFreeHandle(SQL_HANDLE_ENV, henv); henv = NULL; } } int attempt = 0; time_t attempt_time = 0; int init_sql(char * s){ SQLRETURN retcode; char * datasource; char * username; char * password; char * string; if(!s) return 0; if(!sqlstring || strcmp(sqlstring, s)){ string = sqlstring; sqlstring=mystrdup(s); if(string)myfree(string); } if(hstmt || hdbc || henv) close_sql(); attempt++; attempt_time = time(0); if(!henv){ retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); if (!henv || (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)){ henv = NULL; return 0; } retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) { return 0; } } if(!hdbc){ retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); if (!hdbc || (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)) { hdbc = NULL; SQLFreeHandle(SQL_HANDLE_ENV, henv); henv = NULL; return 0; } SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (void*)15, 0); } string = mystrdup(sqlstring); if(!string) return 0; datasource = strtok(string, ","); username = strtok(NULL, ","); password = strtok(NULL, ","); /* Connect to data source */ retcode = SQLConnect(hdbc, (SQLCHAR*) datasource, (SQLSMALLINT)strlen(datasource), (SQLCHAR*) username, (SQLSMALLINT)((username)?strlen(username):0), (SQLCHAR*) password, (SQLSMALLINT)((password)?strlen(password):0)); myfree(string); if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO){ SQLFreeHandle(SQL_HANDLE_DBC, hdbc); hdbc = NULL; SQLFreeHandle(SQL_HANDLE_ENV, henv); henv = NULL; return 0; } retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO){ close_sql(); return 0; } return 1; } void sqlerr (char *buf){ if(conf.stdlog){ fprintf(conf.stdlog, "%s\n", buf); fflush(conf.stdlog); } pthread_mutex_unlock(&log_mutex); } unsigned char statbuf[8192]; void logsql(struct clientparam * param, const unsigned char *s) { SQLRETURN ret; int len; if(param->nolog) return; pthread_mutex_lock(&log_mutex); len = dobuf(param, statbuf, s, (unsigned char *)"\'"); if(attempt > 5){ time_t t; t = time(0); if (t - attempt_time < 180){ sqlerr((char *)statbuf); return; } } if(!hstmt){ if(!init_sql(sqlstring)) { sqlerr((char *)statbuf); return; } } if(hstmt){ ret = SQLExecDirect(hstmt, (SQLCHAR *)statbuf, (SQLINTEGER)len); if(ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO){ close_sql(); if(!init_sql(sqlstring)){ sqlerr((char *)statbuf); return; } if(hstmt) { ret = SQLExecDirect(hstmt, (SQLCHAR *)statbuf, (SQLINTEGER)len); if(ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO){ sqlerr((char *)statbuf); return; } attempt = 0; } } attempt = 0; } pthread_mutex_unlock(&log_mutex); } #endif