1.客户机连接到服务器,发送一个版本标识/方法选择报文
VER 1
NMETHODS 1
METHODS 1
x’05’
n(1-255)
methons
mothons(选择n中) : X’00’ 无验证需求 X’01’ 通用安全服务应用程序接口(GSSAPI) X’02’ 用户名/密码(USERNAME/PASSWORD) X’03’ 至 X’7F’ IANA 分配(IANA ASSIGNED) X’80’ 至 X’FE’ 私人方法保留(RESERVED FOR PRIVATE METHODS) X’FF’ 无可接受方法(NO ACCEPTABLE METHODS)
2.服务器返回方法选择报文
ver1
methons1
x’05’
number
3.客户端发送请求的详细的报文
VER1
CMD1
RSV1
ATYP1
DST.ADDR n
DST.PORT2
05
CONNECT X=‘01’, BIND= X’02’ , UDP ASSOCIATE =X’03’
保留
IP V4 address: X’01’,DOMAINNAME: X’03’,IP V6 address: X’04’
4.服务器返回报文
VER 1
REP1
RSV1
ATYP1
BND.ADDRn
BND.PORT2
05
o X’00’ succeeded o X’01’ general SOCKS server failure o X’02’ connection not allowed by ruleset o X’03’ Network unreachable o X’04’ Host unreachable o X’05’ Connection refused o X’06’ TTL expired o X’07’ Command not supported o X’08’ Address type not supported o X’09’ to X’FF’ unassigned
X’00’
o IP V4 address: X’01’ o DOMAINNAME: X’03’ o IP V6 address: X’04’
BND.PORT和BND.ADDR域包含了欲连接主机的地址和端口号。
5.开始数据的传输
RSV
FRAG
ATYP
DST.ADDR
DST.PORT
DATA
X’0000’
Current fragment number
o IP V4 address: X’01’ o DOMAINNAME: X’03’ o IP V6 address: X’04’
附加知识
大小端
端:即数据的存储的顺序,好比int = 123
大端的存储的方式:0x00 = 1 , 0x01 = 2 , 0x03 = 3
小端的储存方式:0x00 = 3 , 0x01 = 2 , 0x03 = 1
#! usr/ bin/ evn python
#! - * - coding: utf8 - * -
import socket
import threading
import struct
import select
from datetime import datetime
import base64
Server_addr = "127.0.0.1"
Server_port = 1080
Server_listen = 5
Auth = False
def make_server ( ) :
"" "
开启服务器,开始监听
: return : 服务器
"" "
server = socket. socket ( socket. AF_INET, socket. SOCK_STREAM)
# server.setblocking(False)
server. setsockopt ( socket. SOL_SOCKET, socket. SO_REUSEADDR, 1 ) # 开启端口服用
server. bind ( ( Server_addr, Server_port) ) #
server. listen ( Server_listen)
return server
def base64_encrypt ( content) :
return base64. encodestring ( content)
def base64_decrypt ( content) :
return base64. decodestring ( content)
def recv_argue ( conn, size) :
"" "
接收数据,最后判断数据是否接收完成
: param conn: socket
: param size: datas len
: return : 接收到的数据datas
"" "
remain = size
datas = [ ]
while remain > 0 :
data = conn. recv ( remain)
if len ( data) == 0 :
raise Exception ( "Connection end." )
remain - = len ( data)
datas. append ( data)
datas = "" . join ( datas)
if len ( datas) != size:
raise Exception ( "Protocol error" )
return datas
def authenticate ( conn, auth) :
"" "
认证数据的正确性,用户名和密码,以及发送ver和mothod给client,让client知道相应的认证的方式
: param conn:
: param auth:
: return :
"" "
req = recv_argue ( conn, 3 ) # 这里接受到的数据req装这客户端的所有可接受的认证方式,但是服务器看不看是他的事。。。。。
if auth:
# nedd auth
conn. send ( "\x05\x02" )
checkUserPassword ( conn)
else :
# don't need auth
conn. send ( "\x05\x00" )
return conn
def checkUserPassword ( conn) :
pass
def handleRemote ( conn) :
"" "
此处接受client的报文(ver, cmd, rsv, atyp, dst. addr, dst. port),
处理报文,req[ 4 ] : 0x01 == ipv4, 0x03 == domainname, 0x04 == ipv6
: param conn:
: return :
"" "
req = recv_argue ( conn, 5 )
if len ( req) != 5 :
raise Exception ( "1.request error" )
if len ( req) == 0 :
raise Exception ( "Connection end." )
if ord ( req[ 3 ] ) == 1 :
addr_ip = recv_argue ( conn, 4 )
addr = socket. inet_ntoa ( addr_ip)
elif ord ( req[ 3 ] ) == 3 :
addr_len = ord ( req[ 4 ] )
addr = recv_argue ( conn, int ( addr_len) )
elif ord ( req[ 3 ] ) == 4 :
addr_ip = recv_argue ( conn, 16 )
addr = socket. inet_ntop ( socket. AF_INET6, addr_ip)
else :
raise Exception ( "addr type not support!" )
port = struct . unpack ( ">H" , recv_argue ( conn, 2 ) ) [ 0 ] # port is 2 byte, 因为python存储int 为byte时有大小端的概念,所以. . .
print datetime. now ( ) , addr, port
remote = socket. create_connection ( ( addr, port) )
return remote
def handleRequest ( conn, remote) :
"" "
服务器返回报文( ver, rep, rsv, atyp, bnd. addr, bnd. port) , rep== 0x00 is succeed , else fail
: param conn:
: param remote:
: return :
"" "
if remote:
#success
reply = "\x05\x00\x00\x01"
else :
#faile
reply = "\x05\x01\x00\x01"
reply + = socket. inet_aton ( Server_addr) + struct . pack ( ">H" , Server_port)
conn. send ( reply)
return conn
def send_all ( sock, data) :
bytes_send = 0
while True:
res = sock. send ( data[ bytes_send: ] )
if res < 0 :
return res
bytes_send + = res
if bytes_send == len ( data) :
return bytes_send
def sofineConnRemote ( conn, remote) :
try:
sockset = [ conn, remote]
while True:
r, w, e = select. select ( sockset, [ ] , [ ] )
if conn in r:
data = conn. recv ( 4096 )
if len ( data) <= 0 :
break
res = send_all ( remote, data)
if res < len ( data) :
raise Exception ( "faile to send all data to remote" )
if remote in r:
data = remote. recv ( 4096 )
if len ( data) <= 0 :
break
res = send_all ( conn, data)
if res < len ( data) :
raise Exception ( "failed to send all data to conn" )
finally:
conn. close ( )
remote. close ( )
def sockets5_server ( conn, tth) :
conn = authenticate ( conn, Auth) # 版本以及方法的选择,0x05 是版本,Auth= 0x00 是无须认证,= 0x02 用户名以及密码
remote = handleRemote ( conn)
conn = handleRequest ( conn, remote)
sofineConnRemote ( conn, remote)
if __name__ == '__main__' :
server = make_server ( )
tth = 0
while True:
conn, addr = server. accept ( ) # conn相当于一个建立好连接的socket
tth + = 1
threading. Thread ( target= sockets5_server, args= ( conn, tth) ) . start ( )
整体的运行的过程
1.启动代理服务器proxy_server,一旦有client接入,启动ProxyThread线程函数
void StartProxy ( u_short LisPort) {
WSADATA WSAData;
if ( WSAStartup ( MAKEWORD ( 2 , 2 ) , & WSAData) ) return ;
SOCKET sProxy = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP) ;
if ( sProxy == SOCKET_ERROR) return ;
struct sockaddr_in Server = {
0 } ;
Server. sin_family = AF_INET;
Server. sin_addr. S_un. S_addr = INADDR_ANY;
Server. sin_port = htons ( LisPort) ;
if ( bind ( sProxy, ( LPSOCKADDR) & Server, sizeof ( Server) ) == SOCKET_ERROR) return ;
if ( listen ( sProxy, SOMAXCONN) == SOCKET_ERROR) return ;
while ( 1 ) {
SOCKET sClient = accept ( sProxy, NULL , NULL ) ;
HANDLE hThread = CreateThread ( NULL , 0 , ( LPTHREAD_START_ROUTINE) ProxyThread, ( PVOID) sClient, 0 , NULL ) ;
if ( hThread)
CloseHandle ( hThread) ;
}
closesocket ( sProxy) ;
WSACleanup ( ) ;
}
2. client_socket这个链接发送数据(ver,cmd,rsv,atyp,ip_len,ip_info),proxy_server与其进行协商
DWORD WINAPI ProxyThread ( PVOID sClient) {
SOCKET CSsocket[ 2 ] ;
CSsocket[ 0 ] = ( SOCKET) sClient;
CSsocket[ 1 ] = NULL ;
char buf[ 1024 ] ;
memset ( buf, 0 , sizeof ( buf) ) ;
int DataLen = recv ( CSsocket[ 0 ] , buf, sizeof ( buf) , 0 ) ;
if ( DataLen < 3 )
goto exit;
char ProxyType = buf[ 0 ] ;
if ( ProxyType == 5 )
{
if ( ! DoSocks5 ( CSsocket, buf) )
goto exit;
}
else if ( ProxyType == 4 )
{
Socks4Req * Socks4Request = ( Socks4Req * ) buf;
IP_PORT IPP;
IPP. Port = Socks4Request-> wPort;
if ( buf[ 4 ] != 0x00 )
IPP. IP = Socks4Request-> dwIP;
else
{
HOSTENT * hostent = gethostbyname ( ( char * ) & Socks4Request-> other + 1 ) ;
if ( hostent == NULL )
goto exit;
IPP. IP = * * ( PULONG* ) hostent-> h_addr_list;
}
memset ( Socks4Request, 0 , 9 ) ;
CSsocket[ 1 ] = ConnectToRemoteIP ( & IPP) ;
if ( CSsocket[ 1 ] )
Socks4Request-> REP = 0x5A ;
else
Socks4Request-> REP = 0x5B ;
if ( send ( CSsocket[ 0 ] , ( char * ) Socks4Request, 8 , 0 ) == SOCKET_ERROR)
goto exit;
if ( Socks4Request-> REP == 0x5B )
goto exit;
}
else
{
if ( ! HttpProxy ( CSsocket, buf, DataLen) )
goto exit;
}
if ( CSsocket[ 0 ] && CSsocket[ 1 ] )
TCPTransfer ( CSsocket) ;
exit:
if ( CSsocket[ 1 ] )
closesocket ( CSsocket[ 1 ] ) ;
if ( CSsocket[ 0 ] )
closesocket ( CSsocket[ 0 ] ) ;
return 0 ;
}
BOOL DoSocks5 ( SOCKET * CSsocket, char * ReceiveBuf) {
if ( ! Authentication ( CSsocket[ 0 ] , ReceiveBuf) )
goto exit;
Socks5Reply SAC;
SAC. Ver = 0x05 ;
SAC. REP = 0x01 ;
SAC. RSV = 0x00 ;
SAC. ATYP = 0x01 ;
IP_PORT IP_Port;
int CMD = Get_IP_Port ( CSsocket[ 0 ] , ReceiveBuf, & IP_Port) ;
if ( ! CMD) goto exit;
else if ( CMD == 1 ) {
CSsocket[ 1 ]