/* Socks Server 5 * Copyright (C) 2002 - 2006 by Matteo Ricchetti - * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include"SS5Main.h" #include"SS5Defs.h" #include"SS5Mod_socks5.h" #include"SS5OpenLdap.h" #include"SS5Mod_log.h" S5RetCode InitModule( struct _module *m ) { m->MethodParsing=MethodParsing; m->RequestParsing=RequestParsing; m->UpstreamServing=UpstreamServing; m->ConnectServing=ConnectServing; m->BindServing=BindServing; m->UdpAssociateServing=UdpAssociateServing; m->UdpAssociateResponse=UdpAssociateResponse; m->AddMethod=AddMethod; m->FreeMethod=FreeMethod; m->GetMethod=GetMethod; m->AddRoute=AddRoute; m->FreeRoute=FreeRoute; m->GetRoute=GetRoute; m->AddProxy=AddProxy; m->FreeProxy=FreeProxy; m->GetProxy=GetProxy; return OK; } S5RetCode MethodParsing(struct _SS5MethodInfo *mi, struct _SS5ClientInfo *ci, struct _SS5Socks5Data *sd) { register unsigned int i; char logString[128]; pid_t pid; /* * Get child/thread pid */ if( NOTTHREADED() ) pid=getpid(); else pid=(unsigned int)pthread_self(); /* * Receive socks V5 method or V4 request */ if( (sd->MethodBytesReceived=recv(ci->Socket,sd->MethodRequest,sizeof(sd->MethodRequest),0)) <= 0 ) { ERRNO(pid) return ERR; } if( (unsigned char)sd->MethodRequest[0] == SOCKS4_VERSION ) { mi->Ver=SOCKS4_VERSION; mi->NMeth=0; mi->NoAuth=ERR; mi->BasicAuth=ERR; memcpy(sd->TcpRequest,sd->MethodRequest, sd->MethodBytesReceived); sd->TcpRBytesReceived=sd->MethodBytesReceived; return OK; } else if( (unsigned char)sd->MethodRequest[0] == SOCKS5_VERSION ) { mi->Ver=SOCKS5_VERSION; mi->NMeth=(unsigned char)sd->MethodRequest[1]; /* TODO: * Bisogna valorrizzare i campi noauth e basicauth a NO oppure SI * in modo da evidenziare nessuna valorizzazione e quindi nessun * metodo supportato */ for(i=0;i<(mi->NMeth);i++) { if( (unsigned char)sd->MethodRequest[2+i] == 0 ) mi->NoAuth=OK; else if ( (unsigned char)sd->MethodRequest[2+i] == 2 ) mi->BasicAuth=OK; } } else { return ERR; } /* * Send response V5 to client containing supported methods */ sd->MethodResponse[0]=5; switch( GetMethod(inet_network(ci->SrcAddr), ci->SrcPort) ) { case NOAUTH: sd->MethodResponse[1] = NOAUTH; mi->Method = (unsigned char)sd->MethodResponse[1]; break; case USRPWD: sd->MethodResponse[1] = USRPWD; mi->Method = (unsigned char)sd->MethodResponse[1]; break; case FAKEPWD: sd->MethodResponse[1] = USRPWD; mi->Method = FAKEPWD; break; default: sd->MethodResponse[1] = (unsigned char)NOMETHOD; mi->Method = (unsigned char)sd->MethodResponse[1]; if( (sd->MethodBytesSent = send(ci->Socket,sd->MethodResponse,sizeof(sd->MethodResponse),SS5_SEND_OPT)) == -1) { ERRNO(pid) return ERR; } return ERR; break; } if( (sd->MethodBytesSent = send(ci->Socket,sd->MethodResponse,sizeof(sd->MethodResponse),SS5_SEND_OPT)) == -1) { ERRNO(pid); return ERR; } return OK; } S5RetCode RequestParsing(struct _SS5ClientInfo *ci, struct _SS5Socks5Data *sd, struct _SS5RequestInfo *ri) { unsigned int i, c, len; char logString[128]; pid_t pid; /* * Get child/thread pid */ if( NOTTHREADED() ) pid=getpid(); else pid=(unsigned int)pthread_self(); memset((char *)sd->TcpRequest, 0, sizeof(sd->TcpRequest)); if( (sd->TcpRBytesReceived=recv(ci->Socket,sd->TcpRequest,sizeof(sd->TcpRequest),0)) <= 0 ) { ERRNO(pid) return ERR; } ri->Ver=(unsigned char)sd->TcpRequest[0]; ri->Cmd=(unsigned char)sd->TcpRequest[1]; switch( sd->TcpRequest[3] ) { case IPV4: /* * Destination address is dot notation */ ri->ATyp=IPV4; ri->DstPort=0; ri->DstPort +=(unsigned char)sd->TcpRequest[8]; ri->DstPort <<=8; ri->DstPort +=(unsigned char)sd->TcpRequest[9]; snprintf(ri->DstAddr,sizeof(ri->DstAddr),"%hu.%hu.%hu.%hu",(unsigned char)sd->TcpRequest[4], (unsigned char)sd->TcpRequest[5], (unsigned char)sd->TcpRequest[6], (unsigned char)sd->TcpRequest[7]); break; case IPV6: /* Not supported */ return (-1 * S5REQUEST_ADDNOTSUPPORT); break; case DOMAIN: /* * Destination address is FQDN */ len=(unsigned char)sd->TcpRequest[4] + 5; ri->DstPort=0; ri->DstPort +=(unsigned char)sd->TcpRequest[len]; ri->DstPort <<=8; ri->DstPort +=(unsigned char)sd->TcpRequest[len+1]; for(c=0,i=5;iDstAddr[c]=(unsigned char)sd->TcpRequest[i]; ri->DstAddr[c]='\0'; ri->ATyp=DOMAIN; break; } return OK; } S5RetCode UpstreamServing(struct _SS5UpstreamInfo *ui, struct _SS5ClientInfo *ci, struct _SS5RequestInfo *ri, int *applicationSocket, struct _SS5Socks5Data *sd, struct _SS5AuthInfo *ai) { struct sockaddr_in applicationSsin, bindInterfaceSsin; char logString[128]; pid_t pid; struct in_addr in; unsigned short ip_a, ip_b, ip_c, ip_d; /* * Get child/thread pid */ if( NOTTHREADED() ) pid=getpid(); else pid=(unsigned int)pthread_self(); if ( (*applicationSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { ERRNO(pid) return( -1 * S5REQUEST_ISERROR ); } /* * SS5: set bind interface if configured */ if( ROUTE() ) { if( (in.s_addr=(unsigned long int)GetRoute(inet_network(ci->SrcAddr), ai->Username)) ) { memset((char *)&bindInterfaceSsin, 0, sizeof(struct sockaddr_in)); if( (bindInterfaceSsin.sin_addr.s_addr=in.s_addr) ) { bindInterfaceSsin.sin_family = AF_INET; bindInterfaceSsin.sin_port = htons(0); if ( bind(*applicationSocket, (struct sockaddr *)&bindInterfaceSsin, sizeof(struct sockaddr_in)) == -1 ) { ERRNO(pid) return( -1 * S5REQUEST_ISERROR ); } } } } memset((char *)&applicationSsin, 0, sizeof(struct sockaddr_in)); applicationSsin.sin_family = AF_INET; applicationSsin.sin_port = htons(ui->DstPort); applicationSsin.sin_addr.s_addr = (unsigned long int)ui->DstAddr; if( connect(*applicationSocket,(struct sockaddr *)&applicationSsin,sizeof(struct sockaddr_in)) != -1 ) { /* * Proxy client method towards upstream socks server */ if( send(*applicationSocket,sd->MethodRequest,sd->MethodBytesReceived,SS5_SEND_OPT) == -1) { ERRNO(pid) return( -1 * S5REQUEST_ISERROR ); } if( recv(*applicationSocket,sd->MethodResponse,sizeof(sd->MethodResponse),0) <= 0 ) { ERRNO(pid) return( -1 * S5REQUEST_ISERROR ); } /* * Proxy client connect request towards upstream socks server */ if( send(*applicationSocket,sd->TcpRequest,sd->TcpRBytesReceived,SS5_SEND_OPT) == -1) { ERRNO(pid) return( -1 * S5REQUEST_ISERROR ); } if( ri->Cmd == BIND ) { /* * Proxy client bind request towards upstream socks server */ if( (sd->TcpRBytesReceived=recv(*applicationSocket,sd->Response,sizeof(sd->Response),0)) <= 0 ) { ERRNO(pid) return( -1 * S5REQUEST_ISERROR ); } /* * Intercept bind ip address: if equal 0.0.0.0, replace it */ if( sd->Response[4] == 0 && sd->Response[5] == 0 && sd->Response[6] == 0 && sd->Response[7] == 0 ) { sscanf((const char *)ui->DstAddr,"%hu.%hu.%hu.%hu",&ip_a,&ip_b,&ip_c,&ip_d); sd->Response[4]=ip_a; sd->Response[5]=ip_b; sd->Response[6]=ip_c; sd->Response[7]=ip_d; } if( send(ci->Socket,sd->Response,sd->TcpRBytesReceived,SS5_SEND_OPT) == -1) { ERRNO(pid) return( -1 * S5REQUEST_ISERROR ); } } return OK; } else { return( -1 * S5REQUEST_HOSTUNREACH ); } return OK; } S5RetCode ConnectServing(struct _SS5ClientInfo *ci, struct _SS5RequestInfo *ri, struct _SS5AuthInfo *ai, int *applicationSocket, struct _SS5Socks5Data *sd) { register unsigned int index; char logString[128]; S5RetCode err=S5REQUEST_SUCCEDED; S5Limit resolvedHostNumber=0; struct _S5HostList resolvedHostList[MAXDNS_RESOLV]; struct sockaddr_in applicationSsin, bindInterfaceSsin; struct in_addr in; unsigned short ipA, ipB, ipC, ipD; pid_t pid; /* * Get child/thread pid */ if( NOTTHREADED() ) pid=getpid(); else pid=(unsigned int)pthread_self(); /* * SS5: Resolve hostname before connecting */ if( ri->ATyp == DOMAIN ) { if( S5ResolvHostName(ri, (struct _S5HostList *)resolvedHostList, &resolvedHostNumber) == ERR ) err=S5REQUEST_ISERROR; } if( err == S5REQUEST_SUCCEDED ) { if ( (*applicationSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { ERRNO(pid) err=S5REQUEST_ISERROR; } else { /* * SS5: set bind interface if configured */ if( ROUTE() ) { if( (in.s_addr=(unsigned long int)GetRoute(inet_network(ci->SrcAddr), ai->Username)) ) { memset((char *)&bindInterfaceSsin, 0, sizeof(struct sockaddr_in)); if( (bindInterfaceSsin.sin_addr.s_addr=in.s_addr) ) { bindInterfaceSsin.sin_family = AF_INET; bindInterfaceSsin.sin_port = htons(0); if ( bind(*applicationSocket, (struct sockaddr *)&bindInterfaceSsin, sizeof(struct sockaddr_in)) == -1 ) { ERRNO(pid) err=S5REQUEST_ISERROR; } } } } if( err == S5REQUEST_SUCCEDED ) { bzero((char *)&applicationSsin, sizeof(struct sockaddr_in)); applicationSsin.sin_family = AF_INET; applicationSsin.sin_port = htons(ri->DstPort); applicationSsin.sin_addr.s_addr = inet_addr(ri->DstAddr); if( connect(*applicationSocket,(struct sockaddr *)&applicationSsin,sizeof(struct sockaddr_in)) == -1 ) { ERRNO(pid) err=S5REQUEST_CONNREFUSED; /* * Try connecting to other destinations in case of multiple dns answers */ for(index=1;indexDstAddr,resolvedHostList[index].NextHost,sizeof(ri->DstAddr)); applicationSsin.sin_addr.s_addr = inet_addr(ri->DstAddr); if( connect(*applicationSocket,(struct sockaddr *)&applicationSsin,sizeof(struct sockaddr_in)) != -1 ) { err=S5REQUEST_SUCCEDED; break; } } } } } } /* * Prepare and send socks V5 response */ memcpy(sd->Response,sd->TcpRequest,32); sscanf((const char *)ri->DstAddr,"%hu.%hu.%hu.%hu",&ipA,&ipB,&ipC,&ipD); sd->Response[0]=5; sd->Response[1]=err; sd->Response[2]=0; sd->Response[3]=IPV4; sd->Response[4]=ipA; sd->Response[5]=ipB; sd->Response[6]=ipC; sd->Response[7]=ipD; sd->Response[8]=ri->DstPort >> 8; sd->Response[9]=(ri->DstPort << 8) >> 8; switch( ri->ATyp ) { /* * Socks V5 Header is 10 bytes */ case IPV4: case DOMAIN: if( send(ci->Socket,sd->Response,10,SS5_SEND_OPT) == -1) { ERRNO(pid) err=S5REQUEST_ISERROR; } break; /* * Socks V5 Header is 22 bytes but IPV6 is not supported */ case IPV6: err=S5REQUEST_ADDNOTSUPPORT; break; } if( err != S5REQUEST_SUCCEDED ) return (-1 * err); else return OK; } S5RetCode BindServing(struct _SS5ClientInfo *ci, struct _SS5RequestInfo *ri, struct _SS5AuthInfo *ai, int *applicationSocket, struct _SS5Socks5Data *sd) { unsigned int len, fd; int cb = 0; unsigned short ipA, ipB, ipC, ipD; char addr[16]; struct timeval tv; fd_set fdset; S5Limit resolvedHostNumber=1; struct _S5HostList resolvedHostList[MAXDNS_RESOLV]; struct in_addr in; struct sockaddr_in applicationSsin, clientBindSsin; char logString[128]; S5RetCode err=S5REQUEST_SUCCEDED; pid_t pid; /* * Get child/thread pid */ if( NOTTHREADED() ) pid=getpid(); else pid=(unsigned int)pthread_self(); /* * SS5: Resolve hostname before binding */ if( ri->ATyp == DOMAIN ) { if( S5ResolvHostName(ri, (struct _S5HostList *)resolvedHostList, &resolvedHostNumber) == ERR ) { err = S5REQUEST_ISERROR; } } if( err == S5REQUEST_SUCCEDED ) { /* * Create application socket */ if ((*applicationSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { ERRNO(pid) err = S5REQUEST_ISERROR; } else { /* * Create client socket for bind */ if ((cb = socket(AF_INET, SOCK_STREAM, 0)) == -1) { ERRNO(pid) err = S5REQUEST_ISERROR; } else { memset((char *)&clientBindSsin, 0, sizeof(struct sockaddr_in)); clientBindSsin.sin_family = AF_INET; clientBindSsin.sin_port = htons(0); /* * Look for the right interface for binding */ if( S5GetBindIf(ri->DstAddr,addr) == ERR ) { /* Match with destination address in socks request */ clientBindSsin.sin_addr.s_addr = htonl(INADDR_ANY); } else clientBindSsin.sin_addr.s_addr = inet_addr(addr); /* * SS5: set bind interface if configured */ if( ROUTE() ) { if( (in.s_addr=(unsigned long int)GetRoute(inet_network(ci->SrcAddr), ai->Username)) ) clientBindSsin.sin_addr.s_addr = in.s_addr; } if (bind(cb, (struct sockaddr *)&clientBindSsin, sizeof(struct sockaddr_in)) == -1) { ERRNO(pid) err = S5REQUEST_ISERROR; } else { /* * Get clientbind info */ len=sizeof(struct sockaddr); getsockname(cb,(struct sockaddr *)&clientBindSsin,&len); /* * SS5: listen for a queue length equal to one */ if (listen(cb, 1) == -1) { ERRNO(pid) err = S5REQUEST_ISERROR; } } } } } in.s_addr=clientBindSsin.sin_addr.s_addr; strncpy(addr,(char *)inet_ntoa(in),sizeof(addr)); sscanf((const char *)addr,"%hu.%hu.%hu.%hu",&ipA,&ipB,&ipC,&ipD); /* * Send socks response */ memcpy(sd->Response,sd->TcpRequest,32); sd->Response[0]=5; sd->Response[1]=err; sd->Response[2]=0; sd->Response[3]=IPV4; sd->Response[4]=ipA; sd->Response[5]=ipB; sd->Response[6]=ipC; sd->Response[7]=ipD; sd->Response[8]=(ntohs(clientBindSsin.sin_port) >> 8); sd->Response[9]=((ntohs(clientBindSsin.sin_port) << 8) >> 8); switch( ri->ATyp ) { /* * Socks V5 Header is 10 bytes */ case IPV4: case DOMAIN: if( send(ci->Socket,sd->Response,10,SS5_SEND_OPT) == -1) { ERRNO(pid) return(-1 * S5REQUEST_ISERROR); } break; /* * Socks V5 Header is 22 bytes but IPV6 is not supported */ case IPV6: return (-1 * S5REQUEST_ADDNOTSUPPORT); break; } if( err == S5REQUEST_SUCCEDED ) { /* * Wait for BIND_TIMEOUT before closing listen port */ bzero((char *)&applicationSsin, sizeof(struct sockaddr_in)); len = sizeof (struct sockaddr_in); FD_ZERO(&fdset); FD_SET(cb,&fdset); tv.tv_sec=BIND_TIMEOUT; tv.tv_usec=0; if( (fd=select(cb+1,&fdset,NULL,NULL,&tv)) ) { if( FD_ISSET(cb,&fdset) ) { if ((*applicationSocket = accept(cb, (struct sockaddr *)&applicationSsin, &len)) == -1) { ERRNO(pid) close(cb); return (-1 * S5REQUEST_TTLEXPIRED); } } } else { /* * Timeout expired accepting connection from remote application */ close(cb); return (-1 * S5REQUEST_TTLEXPIRED); } /* * Socks response packet */ sd->Response[1]=S5REQUEST_SUCCEDED; switch( ri->ATyp ) { /* * Socks V5 Header is 10 bytes */ case IPV4: case DOMAIN: if( send(ci->Socket,sd->Response,10,SS5_SEND_OPT) == -1) { ERRNO(pid) return(-1 * S5REQUEST_ISERROR); } break; /* * Socks V5 Header is 22 bytes but IPV6 is not supported */ case IPV6: return (-1 * S5REQUEST_ADDNOTSUPPORT); break; } } if( err != S5REQUEST_SUCCEDED ) return (-1 * err); else return OK; } S5RetCode UdpAssociateServing(struct _SS5ClientInfo *ci, struct _SS5RequestInfo *ri, struct _SS5UdpRequestInfo *uri, struct _SS5UdpClientInfo *uci, int *applicationSocket, struct _SS5Socks5Data *sd, struct _SS5ProxyData *pd) { register unsigned int count,index; unsigned int len, fd; unsigned short ipA, ipB, ipC, ipD; char addr[16]; char logString[128]; pid_t pid; fd_set fdset; struct timeval tv; S5Limit resolvedHostNumber=1; struct _S5HostList resolvedHostList[MAXDNS_RESOLV]; struct in_addr in; struct sockaddr_in serverbind_ssin, clientBindSsin; S5RetCode err=S5REQUEST_SUCCEDED; /* * Get child/thread pid */ if( NOTTHREADED() ) pid=getpid(); else pid=(unsigned int)pthread_self(); /* * SS5: Resolve hostname before binding */ if( ri->ATyp == DOMAIN ) { if( S5ResolvHostName(ri, (struct _S5HostList *)resolvedHostList, &resolvedHostNumber) == ERR ) { err=S5REQUEST_ISERROR; } } if( err == S5REQUEST_SUCCEDED ) { /* * Create server socket vs client */ if ((uci->Socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { ERRNO(pid) err=S5REQUEST_ISERROR; } else { memset((char *)&serverbind_ssin, 0, sizeof(struct sockaddr_in)); serverbind_ssin.sin_family = AF_INET; serverbind_ssin.sin_port = htons(0); /* * Look for the right interface to bind before * receiving socks request from client (UDP) */ if( S5GetBindIf(ri->DstAddr,addr) ) { /* Match with destination address in socks request */ serverbind_ssin.sin_addr.s_addr = inet_addr(addr); } else if( S5GetBindIf(ci->SrcAddr,addr) ) { /* Match with client source address*/ serverbind_ssin.sin_addr.s_addr = inet_addr(addr); } else /* Bind ANY (if option set) */ serverbind_ssin.sin_addr.s_addr = htonl(INADDR_ANY); /* * SS5: bind socket to manage request vs client */ if( bind(uci->Socket,(struct sockaddr *)&serverbind_ssin,sizeof(struct sockaddr_in)) ) { ERRNO(pid) err=S5REQUEST_ISERROR; } else { /* * Get information about ip and port after bind operation * to send to client */ len = sizeof (struct sockaddr_in); getsockname(uci->Socket,(struct sockaddr *)&serverbind_ssin,&len); } } } in.s_addr=serverbind_ssin.sin_addr.s_addr; strncpy(addr,(char *)inet_ntoa(in),sizeof(addr)); sscanf((const char *)addr,"%hu.%hu.%hu.%hu",&ipA,&ipB,&ipC,&ipD); /* * SS5: create response to send to client */ memset(sd->Response,0,sizeof(sd->Response)); sd->Response[0]=5; sd->Response[1]=err; sd->Response[2]=0; sd->Response[4]=ipA; sd->Response[5]=ipB; sd->Response[6]=ipC; sd->Response[7]=ipD; sd->Response[8]=(ntohs(serverbind_ssin.sin_port) >> 8); sd->Response[9]=((ntohs(serverbind_ssin.sin_port) << 8) >> 8); switch( ri->ATyp ) { /* Socks V5 Header 10 Bytes */ case IPV4: sd->Response[3]=IPV4; if( send(ci->Socket,sd->Response,10,SS5_SEND_OPT) == -1) { ERRNO(pid) return( -1 * S5REQUEST_ISERROR); } break; case DOMAIN: /* Socks V5 Header 10 Bytes */ sd->Response[3]=DOMAIN; if( send(ci->Socket,sd->Response,10,SS5_SEND_OPT) == -1) { ERRNO(pid) return( -1 * S5REQUEST_ISERROR); } break; /* * Socks V5 Header is 22 Bytesi but is not supported */ case IPV6: return (-1 * S5REQUEST_ADDNOTSUPPORT); break; } if( err == S5REQUEST_SUCCEDED ) { /* * Wait for receiving socks V5 request from client * until UDP_TIMEOUT value */ FD_ZERO(&fdset); FD_SET(uci->Socket,&fdset); tv.tv_sec=UDP_TIMEOUT; tv.tv_usec=0; len=sizeof(struct sockaddr_in); memset(sd->UdpRequest,0,sizeof(sd->UdpRequest)); if( (fd=select(uci->Socket+1,&fdset,NULL,NULL,&tv)) ) { if( FD_ISSET(uci->Socket,&fdset) ) { if( (sd->UdpRBytesReceived=recvfrom(uci->Socket,sd->UdpRequest,sizeof(sd->UdpRequest),0,(struct sockaddr *)&clientBindSsin,(socklen_t *)&len)) == -1 ) { ERRNO(pid) return( -1 * S5REQUEST_ISERROR); } } } else { /* UDP timeout expired */ return( -1 * S5REQUEST_TTLEXPIRED); } /* * Set udp request info */ uri->ATyp=(unsigned char)sd->UdpRequest[3]; uri->Frag=(unsigned char)sd->UdpRequest[2]; /* * Check for fragmentation bit set */ if( uri->Frag ) { return( -1 * S5REQUEST_ISERROR); } /* * Get remote peer address and port * to set udp client info */ len=sizeof(struct sockaddr); getpeername(uci->Socket,(struct sockaddr *)&clientBindSsin,&len); in.s_addr=clientBindSsin.sin_addr.s_addr; strncpy(addr,(char *)inet_ntoa(in),sizeof(addr)); sscanf((const char *)addr,"%hu.%hu.%hu.%hu",&ipA,&ipB,&ipC,&ipD); strncpy(uci->SrcAddr,addr,sizeof(uci->SrcAddr)); uci->SrcPort=ntohs(clientBindSsin.sin_port); /* * Set udp request info */ switch( uri->ATyp ) { case IPV4: uri->DstPort=0; uri->DstPort +=(unsigned char)sd->UdpRequest[8]; uri->DstPort <<=8; uri->DstPort +=(unsigned char)sd->UdpRequest[9]; snprintf(uri->DstAddr,sizeof(ri->DstAddr),"%hu.%hu.%hu.%hu",(unsigned char)sd->UdpRequest[4], (unsigned char)sd->UdpRequest[5], (unsigned char)sd->UdpRequest[6], (unsigned char)sd->UdpRequest[7]); /* * Move data after socks header to proxy buffer */ for(count=0;count<((unsigned int)sd->UdpRBytesReceived-10);count++) pd->UdpSend[count]=sd->UdpRequest[count+10]; pd->UdpSBufLen=sd->UdpRBytesReceived-10; break; case IPV6: /* IPV6 is not supported */ return (-1 * S5REQUEST_ADDNOTSUPPORT); break; case DOMAIN: len=(unsigned char)sd->UdpRequest[4] + 5; uri->DstPort=0; uri->DstPort +=(unsigned char)sd->UdpRequest[len]; uri->DstPort <<=8; uri->DstPort +=(unsigned char)sd->UdpRequest[len+1]; for(count=0,index=5;indexDstAddr[count]=sd->UdpRequest[index+2]; uri->DstAddr[count]='\0'; /* * Move data after socks header to proxy buffer */ len=7+sd->UdpRequest[4]; for(count=0;count<(sd->UdpRBytesReceived-len);count++) pd->UdpSend[count]=sd->UdpRequest[count+len]; pd->UdpSBufLen=sd->UdpRBytesReceived-len; break; } /* * Verify client source address and port * are the same as specified in socks request */ if( S5VerifyBind(uci,ri) == ERR ) { return( -1 * S5REQUEST_ACLDENY); } /* * SS5: Resolve hostname of udp destination address */ if( ri->ATyp == DOMAIN ) if( S5ResolvHostName((struct _SS5RequestInfo *)uri, (struct _S5HostList *)resolvedHostList, &resolvedHostNumber) == ERR ) return( -1 * S5REQUEST_ISERROR); /* * SS5: create socket for application */ if ((*applicationSocket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { ERRNO(pid) return( -1 * S5REQUEST_ISERROR); } } if( err != S5REQUEST_SUCCEDED ) return (-1 * err); else return OK; } S5RetCode UdpAssociateResponse(struct _SS5UdpRequestInfo *uri, struct _SS5UdpClientInfo *uci, struct _SS5Socks5Data *sd, struct _SS5ProxyData *pd) { register int count; unsigned int len; pid_t pid; char logString[128]; unsigned short ipA, ipB, ipC, ipD; struct sockaddr_in clientBindSsin; /* * Get child/thread pid */ if( NOTTHREADED() ) pid=getpid(); else pid=(unsigned int)pthread_self(); memset((char *)&clientBindSsin, 0, sizeof(struct sockaddr_in)); clientBindSsin.sin_family = AF_INET; clientBindSsin.sin_port = htons(uci->SrcPort); clientBindSsin.sin_addr.s_addr = inet_addr(uci->SrcAddr); sscanf(uci->SrcAddr,"%hu.%hu.%hu.%hu",&ipA,&ipB,&ipC,&ipD); memset(sd->UdpRequest,0,sizeof(sd->UdpRequest)); switch( uri->ATyp ) { case IPV4: case DOMAIN: sd->UdpRequest[0]=0; sd->UdpRequest[1]=0; sd->UdpRequest[2]=uri->Frag; sd->UdpRequest[3]=uri->ATyp; sd->UdpRequest[4]=ipA; sd->UdpRequest[5]=ipB; sd->UdpRequest[6]=ipC; sd->UdpRequest[7]=ipD; sd->UdpRequest[8] = ((uci->SrcPort) >> 8); sd->UdpRequest[9] = ((uci->SrcPort<< 8) >> 8); break; /* * Socks V5 Header is 22 bytes but IPV6 is not supported */ case IPV6: return (-1 * S5REQUEST_ADDNOTSUPPORT); break; } /* * Send response to client */ for(count=0;count<(pd->UdpRBufLen);count++) sd->UdpRequest[count+10]=pd->UdpRecv[count]; /* * Relay application response to client */ len=sizeof(struct sockaddr_in); if( (sd->UdpRBytesSent=sendto(uci->Socket,sd->UdpRequest,pd->UdpRBufLen+10,0,(struct sockaddr *)&clientBindSsin,(socklen_t)len)) == -1 ) { ERRNO(pid) return (-1 * S5REQUEST_ISERROR); } return OK; } S5RetCode S5GetBindIf( char *applicationIp, char *clientBind ) { int index; if( (index=S5IfMatch(applicationIp)) != -1 ) { strncpy(clientBind,S5Interface[index]->IP,sizeof(S5Interface[index]->IP) - 1); return OK; } return ERR; } S5RetCode S5ResolvHostName( struct _SS5RequestInfo *ri, struct _S5HostList *resolvedHostList, S5Limit *resolvedHostNumber) { register unsigned int index,count; struct hostent *dhost; char logString[128]; pid_t pid; /* * Get child/thread pid */ if( NOTTHREADED() ) pid=getpid(); else pid=(unsigned int)pthread_self(); if( (dhost=(struct hostent *)gethostbyname(ri->DstAddr)) == NULL ) { /* TODO: handle h_error */ return ERR; } /* * In case of multiple answers, save all */ *resolvedHostNumber=0; for (index=0; dhost->h_addr_list[index] && index < MAXDNS_RESOLV; index++) { strncpy(resolvedHostList[index].NextHost,(char *)inet_ntoa(*(struct in_addr *)dhost->h_addr_list[index]),sizeof(resolvedHostList[index].NextHost)); *resolvedHostNumber=*resolvedHostNumber + 1; } if( index == MAXDNS_RESOLV ) { if( VERBOSE() ) { snprintf(logString,128,"[%u] [VERB] Max number of multiple dns records reached while resolving destination: %d.",pid, MAXDNS_RESOLV); LOGUPDATE() } } /* * If request, order dns answers */ if( SS5SocksOpt.DnsOrder ) { S5OrderIP(resolvedHostList, resolvedHostNumber); if( VERBOSE() ) { snprintf(logString,128,"[%u] [VERB] Ordering multiple records dns.",pid); LOGUPDATE() for(count=0;count<*resolvedHostNumber; count++) { snprintf(logString,128,"[%u] [VERB] Resolved %s to %s.",pid,ri->DstAddr,resolvedHostList[count].NextHost); LOGUPDATE() } } } strncpy(ri->DstAddr,resolvedHostList[0].NextHost,sizeof(ri->DstAddr)); return OK; } S5RetCode S5OrderIP( struct _S5HostList *resolvedHostList, S5Limit *resolvedHostNumber ) { register unsigned int index; unsigned int swap; char ip_tmp[16]; do { swap=0; for(index=0;index<(*resolvedHostNumber)-1;index++) if( S5CompIP(resolvedHostList[index].NextHost,resolvedHostList[index+1].NextHost) ) { strncpy(ip_tmp,resolvedHostList[index+1].NextHost,sizeof(resolvedHostList[index+1].NextHost) - 1); strncpy(resolvedHostList[index+1].NextHost,resolvedHostList[index].NextHost,sizeof(resolvedHostList[index].NextHost) - 1); strncpy(resolvedHostList[index].NextHost,ip_tmp,sizeof(ip_tmp) - 1); swap=1; } } while(swap); return OK; } S5RetCode S5CompIP(char src[16],char dst[16] ) { unsigned short ips_a, ips_b, ips_c, ips_d, ipd_a, ipd_b, ipd_c, ipd_d; sscanf(src,"%hu.%hu.%hu.%hu",&ips_a,&ips_b,&ips_c,&ips_d); sscanf(dst,"%hu.%hu.%hu.%hu",&ipd_a,&ipd_b,&ipd_c,&ipd_d); if( (ips_a > ipd_a) ) return OK; else if( (ipd_a >ips_a) ) return ERR; else if( (ips_b >ipd_b) ) return OK; else if( (ipd_b >ips_b) ) return ERR; else if( (ips_c >ipd_c) ) return OK; else if( (ipd_c >ips_c) ) return ERR; else if( (ips_d >ipd_d) ) return OK; else if( (ipd_d >ips_d) ) return ERR; return ERR; } S5RetCode S5VerifyBind(struct _SS5UdpClientInfo *uci, struct _SS5RequestInfo *ri ) { if( STREQ(uci->SrcAddr,ri->DstAddr,sizeof(uci->SrcAddr) - 1) && (uci->SrcPort == ri->DstPort) ) return OK; else if ( STREQ(ri->DstAddr,"0.0.0.0",sizeof("0.0.0.0") - 1) && (uci->SrcPort == ri->DstPort) ) return OK; else return ERR; } S5RetCode S5IfMatch(char ip[16]) { unsigned int count; unsigned short ipb_a, ipb_b, ipb_c, ipb_d; unsigned short ips_a, ips_b, ips_c, ips_d; unsigned short msk_a, msk_b, msk_c, msk_d; sscanf((const char *)ip,"%hu.%hu.%hu.%hu",&ipb_a,&ipb_b,&ipb_c,&ipb_d); for(count=0;countIP,"%hu.%hu.%hu.%hu",&ips_a,&ips_b,&ips_c,&ips_d); sscanf(S5Interface[count]->NetMask,"%hu.%hu.%hu.%hu",&msk_a,&msk_b,&msk_c,&msk_d); if( !((ips_a & msk_a) - (ipb_a & msk_a) + (ips_b & msk_b) - (ipb_b & msk_b) + (ips_c & msk_c) - \ (ipb_c & msk_c) + (ips_d & msk_d) - (ipb_d & msk_d)) ) return count; } return -1; } inline S5RetCode FileCheck( char *group, char *user ) { FILE *groupFile; pid_t pid; char groupFileName[192]; char userName[64]; char logString[128]; /* * Get child/thread pid */ if( NOTTHREADED() ) pid=getpid(); else pid=(unsigned int)pthread_self(); if( SS5SocksOpt.Profiling == FILE_PROFILING ) { strncpy(groupFileName,S5ProfilePath,sizeof(groupFileName)); strncat(groupFileName,"/",strlen("/")); strncat(groupFileName,group,strlen(group)); strncat(groupFileName,"\0",strlen("\0")); if( (groupFile = fopen(groupFileName,"r")) == NULL ) { ERRNO(pid) return ERR; } /* * Check for username into configuration file for access profile */ while( fscanf(groupFile,"%64s",userName) != EOF ) { if( userName[0] != '#' ) if( STRCASEEQ(userName,user,64) ) { fclose(groupFile); return OK; } } fclose(groupFile); return ERR; } return ERR; } /* ***************************** HASH for ROUTING TABLE **************************** */ inline S5Limit S5RouteHash( unsigned long int sa ) { return ( sa % MAXROUTELIST ); } unsigned long int GetRoute(unsigned long int sa, char *uname) { S5Limit index,nm; S5RetCode err = ERR; struct _S5RouteNode *node; unsigned long int n_sa; for(nm=0;nm<=32;nm +=1) { if( nm < 32) n_sa=((sa >> nm) << nm); else n_sa=0; index=S5RouteHash( n_sa ); if( S5RouteList[index] != NULL ) { node=S5RouteList[index]; do { if( (node->SrcAddr == n_sa) && (node->Mask == (nm)) ) { if( node->Group[0] != '-' ) { /* * Look for username into group (file or directory) defined in permit line */ if( SS5SocksOpt.Profiling == FILE_PROFILING ) err=FileCheck(node->Group,uname); else if( SS5SocksOpt.Profiling == LDAP_PROFILING ) err=DirectoryCheck(node->Group,uname); if( err ) { return node->SrcIf; } } else return node->SrcIf; } node=node->next; } while(node != NULL ); } } return ERR; } S5RetCode AddRoute(unsigned long int sa, unsigned long int si, char *group, unsigned int mask ) { int index; struct _S5RouteNode *node; index=S5RouteHash( sa ); if( _tmp_S5RouteList[index]== NULL ) { _tmp_S5RouteList[index]=(struct _S5RouteNode *)calloc(1,sizeof(struct _S5RouteNode)); _tmp_S5RouteList[index]->Mask=mask; _tmp_S5RouteList[index]->SrcAddr=sa; _tmp_S5RouteList[index]->SrcIf=si; strncpy(_tmp_S5RouteList[index]->Group,group,sizeof(_tmp_S5RouteList[index]->Group)); _tmp_S5RouteList[index]->next=NULL; } else { node=_tmp_S5RouteList[index]; while( node->next != NULL ){ node=node->next; } node->next=(struct _S5RouteNode *)calloc(1,sizeof(struct _S5RouteNode)); node->next->Mask=mask; node->next->SrcAddr=sa; node->next->SrcIf=si; strncpy(node->next->Group,group,sizeof(node->next->Group)); node->next->next=NULL; } return OK; } S5RetCode FreeRoute( struct _S5RouteNode **node ) { struct _S5RouteNode *lnode; struct _S5RouteNode *lnode_prev=NULL; lnode=*node; if( lnode != NULL ) { do { while( lnode->next != NULL ) { lnode_prev=lnode; lnode=lnode->next; } free(lnode); if( lnode_prev != NULL ) { lnode_prev->next=NULL; lnode=lnode_prev; lnode_prev=NULL; } else lnode=NULL; } while( (lnode) != NULL ); } *node=NULL; return OK; } S5RetCode S5BrowseRouteList( struct _S5RouteNode *node ) { struct _S5RouteNode *lnode; int found=0; lnode=node; do { if(lnode != NULL ) { printf("Browse: %lu %u %lu\f", lnode->SrcAddr,lnode->Mask,lnode->SrcIf); found++; lnode=lnode->next; } } while( lnode != NULL ); return found; } /* ***************************** HASH for UPSTREAM PROXY **************************** */ inline S5Limit S5ProxyHash( unsigned long int da, unsigned int dp ) { register unsigned int i; register long int hashVal = 0; register unsigned int len; char s[24]; snprintf(s,sizeof(s),"%lu%u",da,dp); len=strlen(s); for(i=0; i> nm) << nm); else n_da=0; index=S5ProxyHash( n_da, dp ); if( S5ProxyList[index]!= NULL ) { node=S5ProxyList[index]; do { if( (node->DstAddr == n_da) && (node->Mask == nm) && (node->DstPort == dp) ) { ui->DstAddr=node->ProxyAddr; ui->DstPort=node->ProxyPort; if( node->Type == PROXY ) { return OK; } return ERR_NOPROXY; } node=node->next; } while(node != NULL ); } } for(nm=0;nm<=32;nm++) { if( nm < 32) n_da=((da >> nm) << nm); else n_da=0; index=S5ProxyHash( n_da, 0 ); if( S5ProxyList[index]!= NULL ) { node=S5ProxyList[index]; do { if( (node->DstAddr == n_da ) && (node->Mask == nm) && (dp >= node->DstRangeMin) && (dp <= node->DstRangeMax) ) { ui->DstAddr=node->ProxyAddr; ui->DstPort=node->ProxyPort; if( node->Type == PROXY ) { return OK; } return ERR_NOPROXY; } node=node->next; } while(node != NULL ); } } return ERR; } S5RetCode AddProxy(unsigned int type, unsigned long int da, unsigned long int dp, unsigned long int pa, unsigned int pp, unsigned int mask ) { int index; struct _S5ProxyNode *node; if( dp > 65535 ) index=S5ProxyHash( da, 0 ); else index=S5ProxyHash( da, dp ); if( _tmp_S5ProxyList[index]== NULL ) { _tmp_S5ProxyList[index]=(struct _S5ProxyNode *)calloc(1,sizeof(struct _S5ProxyNode)); _tmp_S5ProxyList[index]->Mask=mask; _tmp_S5ProxyList[index]->DstAddr=da; _tmp_S5ProxyList[index]->Type=type; if( dp > 65535 ) { _tmp_S5ProxyList[index]->DstPort=0; _tmp_S5ProxyList[index]->DstRangeMax=dp; _tmp_S5ProxyList[index]->DstRangeMax >>= 16; _tmp_S5ProxyList[index]->DstRangeMax <<= 16; _tmp_S5ProxyList[index]->DstRangeMin = dp - _tmp_S5ProxyList[index]->DstRangeMax; _tmp_S5ProxyList[index]->DstRangeMax >>= 16; } else _tmp_S5ProxyList[index]->DstPort=dp; _tmp_S5ProxyList[index]->ProxyAddr=pa; _tmp_S5ProxyList[index]->ProxyPort=pp; _tmp_S5ProxyList[index]->next=NULL; } else { node=_tmp_S5ProxyList[index]; while( node->next != NULL ){ node=node->next; } node->next=(struct _S5ProxyNode *)calloc(1,sizeof(struct _S5ProxyNode)); node->next->Mask=mask; node->next->DstAddr=da; node->next->Type=type; if( dp > 65535 ) { node->next->DstPort=0; node->next->DstRangeMax=dp; node->next->DstRangeMax >>= 16; node->next->DstRangeMax <<= 16; node->next->DstRangeMin = dp - node->next->DstRangeMax; node->next->DstRangeMax >>= 16; } else node->next->DstPort=dp; node->next->ProxyAddr=pa; node->next->ProxyPort=pp; node->next->next=NULL; } return OK; } S5RetCode FreeProxy( struct _S5ProxyNode **node ) { struct _S5ProxyNode *lnode; struct _S5ProxyNode *lnode_prev=NULL; lnode=*node; if( lnode != NULL ) { do { while( lnode->next != NULL ) { lnode_prev=lnode; lnode=lnode->next; } free(lnode); if( lnode_prev != NULL ) { lnode_prev->next=NULL; lnode=lnode_prev; lnode_prev=NULL; } else lnode=NULL; } while( (lnode) != NULL ); } *node=NULL; return OK; } S5RetCode S5BrowseProxyList( struct _S5ProxyNode *node ) { struct _S5ProxyNode *lnode; int found=0; lnode=node; do { if(lnode != NULL ) { printf("%lu %u %lu %lu %u\f", lnode->DstAddr,lnode->Mask,lnode->DstPort,lnode->ProxyAddr,lnode->ProxyPort); lnode=lnode->next; found++; } } while( lnode != NULL ); return found; } /* ***************************** HASH for METHOD **************************** */ inline S5Limit S5MethodHash( unsigned long int sa, unsigned int sp ) { register unsigned int i,len; register int hashVal = 0; char s[24]; snprintf(s,sizeof(s),"%lu%u",sa,sp); len=strlen(s); for(i=0; i> nm) << nm); else n_sa=0; index=S5MethodHash( n_sa, 0 ); if( S5MethodList[index]!= NULL ) { node=S5MethodList[index]; do { if( (node->SrcAddr == n_sa) && (node->Mask == nm) && (sp >= node->SrcRangeMin) && (sp <= node->SrcRangeMax) ) { return node->Method; } node=node->next; } while(node != NULL ); } } for(nm=0;nm<=32;nm++) { if( nm < 32) n_sa=((sa >> nm) << nm); else n_sa=0; index=S5MethodHash( n_sa, sp ); if( S5MethodList[index]!= NULL ) { node=S5MethodList[index]; do { if( (node->SrcAddr == n_sa) && (node->Mask == nm) && (node->SrcPort == sp) ) { return node->Method; } node=node->next; } while(node != NULL ); } } return ERR; } S5RetCode AddMethod(unsigned long int sa, unsigned long int sp, unsigned int me, unsigned int mask) { int index; struct _S5MethodNode *node; if( sp > 65535 ) index=S5MethodHash( sa, 0 ); else index=S5MethodHash( sa, sp ); if( _tmp_S5MethodList[index]== NULL ) { _tmp_S5MethodList[index]=(struct _S5MethodNode *)calloc(1,sizeof(struct _S5MethodNode)); _tmp_S5MethodList[index]->Mask=mask; _tmp_S5MethodList[index]->SrcAddr=sa; if( sp > 65535 ) { _tmp_S5MethodList[index]->SrcPort=0; _tmp_S5MethodList[index]->SrcRangeMax=sp; _tmp_S5MethodList[index]->SrcRangeMax >>= 16; _tmp_S5MethodList[index]->SrcRangeMax <<= 16; _tmp_S5MethodList[index]->SrcRangeMin = sp - _tmp_S5MethodList[index]->SrcRangeMax; _tmp_S5MethodList[index]->SrcRangeMax >>= 16; } else _tmp_S5MethodList[index]->SrcPort=sp; _tmp_S5MethodList[index]->Method=me; _tmp_S5MethodList[index]->next=NULL; } else { node=_tmp_S5MethodList[index]; while( node->next != NULL ){ node=node->next; } node->next=(struct _S5MethodNode *)calloc(1,sizeof(struct _S5MethodNode)); node->next->Mask=mask; node->next->SrcAddr=sa; if( sp > 65535 ) { node->next->SrcPort=0; node->next->SrcRangeMax=sp; node->next->SrcRangeMax >>= 16; node->next->SrcRangeMax <<= 16; node->next->SrcRangeMin = sp - node->next->SrcRangeMax; node->next->SrcRangeMax >>= 16; } else node->next->SrcPort=sp; node->next->Method=me; node->next->next=NULL; } return OK; } S5RetCode FreeMethod( struct _S5MethodNode **node ) { struct _S5MethodNode *lnode; struct _S5MethodNode *lnode_prev=NULL; lnode=*node; if( lnode != NULL ) { do { while( lnode->next != NULL ) { lnode_prev=lnode; lnode=lnode->next; } free(lnode); if( lnode_prev != NULL ) { lnode_prev->next=NULL; lnode=lnode_prev; lnode_prev=NULL; } else lnode=NULL; } while( (lnode) != NULL ); } *node=NULL; return OK; } S5RetCode S5BrowseMethodList( struct _S5MethodNode *node ) { struct _S5MethodNode *lnode; int found=0; lnode=node; do { if(lnode != NULL ) { printf("Browse - P: %p - %lu %u %lu %u\f",(void *)lnode,lnode->SrcAddr,lnode->Mask,lnode->SrcPort,lnode->Method); lnode=lnode->next; found++; } } while( lnode != NULL ); return found; }