1- import asyncio , socket , urllib .parse , time , re , base64 , hmac , struct , hashlib
1+ import asyncio , socket , urllib .parse , time , re , base64 , hmac , struct , hashlib , fcntl
22
33HTTP_LINE = re .compile ('([^ ]+) +(.+?) +(HTTP/[^ ]+)$' )
44packstr = lambda s , n = 1 : len (s ).to_bytes (n , 'big' ) + s
@@ -232,25 +232,53 @@ async def http_channel(self, reader, writer, stat_bytes, _):
232232 finally :
233233 writer .close ()
234234
235- SO_ORIGINAL_DST = 80
236- class Redirect (BaseProtocol ):
237- name = 'redir'
235+ class Transparent (BaseProtocol ):
238236 def correct_header (self , header , auth , sock , ** kw ):
239- try :
240- buf = sock .getsockopt (socket .SOL_IP , SO_ORIGINAL_DST , 16 )
241- assert len (buf ) == 16
242- remote = (socket .inet_ntoa (buf [4 :8 ]), int .from_bytes (buf [2 :4 ], 'big' ))
243- assert sock .getsockname () != remote
244- except Exception :
237+ remote = self .query_remote (sock )
238+ if remote is None or sock .getsockname () == remote :
245239 return False
246240 return auth and header == auth [:1 ] or not auth
247241 async def parse (self , reader , auth , authtable , sock , ** kw ):
248242 if auth :
249243 if (await reader .read_n (len (auth )- 1 )) != auth [1 :]:
250- raise Exception ('Unauthorized Redir ' )
244+ raise Exception (f 'Unauthorized { self . name } ' )
251245 authtable .set_authed ()
252- buf = sock .getsockopt (socket .SOL_IP , SO_ORIGINAL_DST , 16 )
253- return socket .inet_ntoa (buf [4 :8 ]), int .from_bytes (buf [2 :4 ], 'big' ), b''
246+ remote = self .query_remote (sock )
247+ return remote [0 ], remote [1 ], b''
248+
249+ SO_ORIGINAL_DST = 80
250+ SOL_IPV6 = 41
251+ class Redirect (Transparent ):
252+ name = 'redir'
253+ def query_remote (self , sock ):
254+ try :
255+ #if sock.family == socket.AF_INET:
256+ if "." in sock .getsockname ()[0 ]:
257+ buf = sock .getsockopt (socket .SOL_IP , SO_ORIGINAL_DST , 16 )
258+ assert len (buf ) == 16
259+ return socket .inet_ntoa (buf [4 :8 ]), int .from_bytes (buf [2 :4 ], 'big' )
260+ else :
261+ buf = sock .getsockopt (SOL_IPV6 , SO_ORIGINAL_DST , 28 )
262+ assert len (buf ) == 28
263+ return socket .inet_ntop (socket .AF_INET6 , buf [8 :24 ]), int .from_bytes (buf [2 :4 ], 'big' )
264+ except Exception :
265+ pass
266+
267+ class Pf (Transparent ):
268+ name = 'pf'
269+ def query_remote (self , sock ):
270+ try :
271+ src = sock .getpeername ()
272+ dst = sock .getsockname ()
273+ src_ip = socket .inet_pton (sock .family , src [0 ])
274+ dst_ip = socket .inet_pton (sock .family , dst [0 ])
275+ pnl = bytearray (struct .pack ('!16s16s32xHxxHxx8xBBxB' , src_ip , dst_ip , src [1 ], dst [1 ], sock .family , socket .IPPROTO_TCP , 2 ))
276+ if not hasattr (self , 'pf' ):
277+ self .pf = open ('/dev/pf' , 'a+b' )
278+ fcntl .ioctl (self .pf .fileno (), 0xc0544417 , pnl )
279+ return socket .inet_ntop (sock .family , pnl [48 :48 + len (src_ip )]), int .from_bytes (pnl [76 :78 ], 'big' )
280+ except Exception :
281+ pass
254282
255283async def parse (protos , reader , ** kw ):
256284 proto = next (filter (lambda p : p .correct_header (None , ** kw ), protos ), None )
@@ -267,7 +295,7 @@ async def parse(protos, reader, **kw):
267295 return (proto ,) + ret
268296 raise Exception (f'Unsupported protocol { header } ' )
269297
270- MAPPINGS = dict (direct = Direct (), http = HTTP (), socks5 = Socks5 (), socks4 = Socks4 (), ss = Shadowsocks (), ssr = ShadowsocksR (), redir = Redirect (), ssl = '' , secure = '' )
298+ MAPPINGS = dict (direct = Direct (), http = HTTP (), socks5 = Socks5 (), socks4 = Socks4 (), ss = Shadowsocks (), ssr = ShadowsocksR (), redir = Redirect (), pf = Pf (), ssl = '' , secure = '' )
271299MAPPINGS ['socks' ] = MAPPINGS ['socks5' ]
272300
273301def get_protos (rawprotos ):
0 commit comments