22
33CLIENT_TIMEOUT = 10
44CONNECTION_TIMEOUT = 90
5- AUTH_TIME = 86400 * 7
5+ AUTH_TIME = 86400 * 30
66HTTP_LINE = re .compile ('([^ ]+) +(.+?) +([^ ]+)' )
77
88def all_stat (stats ):
@@ -75,7 +75,14 @@ async def http_channel(reader, writer, stat_bytes):
7575 finally :
7676 writer .close ()
7777
78- async def proxy_handler (reader , writer , types , auth , rserver , rtype , rauth , match , block , verbose , stats , auth_tables , ** kwargs ):
78+ async def apply_cipher (reader , writer , cipher , read ):
79+ writer_cipher = cipher [0 ].new (key = cipher [1 ])
80+ writer .write (writer_cipher .nonce )
81+ writer .write = lambda s , o = writer .write , p = writer_cipher .encrypt : o (p (s ))
82+ reader_cipher = cipher [0 ].new (key = cipher [1 ], nonce = await read (8 ))
83+ reader .feed_data = lambda s , o = reader .feed_data , p = reader_cipher .decrypt : o (p (s ))
84+
85+ async def proxy_handler (reader , writer , types , auth , rserver , rtype , rauth , match , block , verbose , stats , auth_tables , cipher , rcipher , ** kwargs ):
7986 try :
8087 initbuf = b''
8188 pack2 = lambda s : struct .pack ('>H' , s )
@@ -84,6 +91,8 @@ async def proxy_handler(reader, writer, types, auth, rserver, rtype, rauth, matc
8491 read = lambda n : asyncio .wait_for (reader .readexactly (n ), timeout = CLIENT_TIMEOUT )
8592 writer .get_extra_info ('socket' ).setsockopt (socket .IPPROTO_TCP , socket .TCP_NODELAY , 1 )
8693 remote_ip = writer .get_extra_info ('peername' )[0 ]
94+ if cipher :
95+ await apply_cipher (reader , writer , cipher , read )
8796 header = await read (1 )
8897 if 'socks' in types and header == b'\x05 ' :
8998 methods = await read ((await read (1 ))[0 ])
@@ -151,7 +160,7 @@ async def proxy_handler(reader, writer, types, auth, rserver, rtype, rauth, matc
151160 raise Exception ('Unsupported protocol' )
152161 if block and block (host_name ):
153162 raise Exception ('BLOCKED ' + host_name )
154- host_name_2 = '.' .join (host_name .split ('.' )[- 2 :]) if host_name .split ('.' )[- 1 ].isalpha () else host_name
163+ host_name_2 = '.' .join (host_name .split ('.' )[- 3 if host_name . endswith ( '.com.cn' ) else - 2 :]) if host_name .split ('.' )[- 1 ].isalpha () else host_name
155164 tostat = (stats [0 ], stats .setdefault (remote_ip , {}).setdefault (host_name_2 , [0 ]* 6 ))
156165 modstat = lambda i : lambda s : [st .__setitem__ (i , st [i ] + s ) for st in tostat ]
157166 viaproxy = bool (rserver and (not match or match (host_name )))
@@ -175,10 +184,12 @@ async def proxy_handler(reader, writer, types, auth, rserver, rtype, rauth, matc
175184 raise Exception (f'Connection timeout { rserver } ' )
176185 try :
177186 writer_remote .get_extra_info ('socket' ).setsockopt (socket .IPPROTO_TCP , socket .TCP_NODELAY , 1 )
187+ readr = lambda n : asyncio .wait_for (reader_remote .readexactly (n ), timeout = CLIENT_TIMEOUT )
188+ if viaproxy and rcipher :
189+ await apply_cipher (reader_remote , writer_remote , rcipher , readr )
178190 writer_remote .write (initbuf )
179191 if viaproxy and rtype == 'socks' :
180192 await asyncio .wait_for (reader_remote .readuntil (b'\x00 \x05 \x00 \x00 ' ), timeout = CLIENT_TIMEOUT )
181- readr = lambda n : asyncio .wait_for (reader_remote .readexactly (n ), timeout = CLIENT_TIMEOUT )
182193 await readr (6 if (await readr (1 ))[0 ] == 1 else (await readr (1 ))[0 ] + 2 )
183194 elif viaproxy and rtype == 'http' :
184195 await asyncio .wait_for (reader_remote .readuntil (b'\r \n \r \n ' ), timeout = CLIENT_TIMEOUT )
@@ -200,13 +211,18 @@ def main():
200211 def addr_compile (s ):
201212 ip , _ , port = s .partition (':' )
202213 return (ip , int (port ) if port else 18436 )
214+ def cipher_compile (key ): #pip install pycryptodome
215+ from Crypto .Cipher import ChaCha20
216+ return (ChaCha20 , key .encode ().rjust (32 , b'\x55 ' ))
203217 parser = argparse .ArgumentParser (description = 'Proxy server that can tunnel by http,socks,psocks protocol.' )
204218 parser .add_argument ('-p' , dest = 'port' , type = int , default = 8080 , help = 'listen port server bound to (default: 8080)' )
205219 parser .add_argument ('-t' , dest = 'types' , type = lambda s : s .split (',' ), default = ['socks' ,'http' ], help = 'proxy server protocols (default: socks,http)' )
206220 parser .add_argument ('-a' , dest = 'auth' , type = lambda s : s .encode (), help = 'authentication requirement' )
221+ parser .add_argument ('-c' , dest = 'cipher' , type = cipher_compile , help = 'cipher key (default: no cipher)' )
207222 parser .add_argument ('-rs' , dest = 'rserver' , type = addr_compile , help = 'remote server address (default: direct)' )
208223 parser .add_argument ('-rt' , dest = 'rtype' , default = 'psocks' , help = 'remote server type (default: psocks)' )
209224 parser .add_argument ('-ra' , dest = 'rauth' , default = b'' , type = lambda s : s .encode (), help = 'remote authorization code' )
225+ parser .add_argument ('-rc' , dest = 'rcipher' , type = cipher_compile , help = 'remote cipher key (default: no cipher)' )
210226 parser .add_argument ('-m' , dest = 'match' , type = pattern_compile , help = 'match pattern file' )
211227 parser .add_argument ('-b' , dest = 'block' , type = pattern_compile , help = 'block pattern file' )
212228 parser .add_argument ('-v' , dest = 'v' , action = 'store_true' , help = 'print verbose output' )
0 commit comments