@@ -878,22 +878,29 @@ async def _open_tcp_connection(self, target: str, port: int,
878878 errors : list [str ] = []
879879 loop = asyncio .get_running_loop ()
880880
881+ # Strip IPv6 brackets (CONNECT may deliver "[::1]" as the hostname).
882+ # ipaddress.ip_address() rejects the bracketed form, which would
883+ # otherwise force a DNS lookup for an IP literal and fail.
884+ lookup_target = target .strip ()
885+ if lookup_target .startswith ("[" ) and lookup_target .endswith ("]" ):
886+ lookup_target = lookup_target [1 :- 1 ]
887+
881888 try :
882- ipaddress .ip_address (target )
883- candidates = [(0 , target )]
889+ ipaddress .ip_address (lookup_target )
890+ candidates = [(0 , lookup_target )]
884891 except ValueError :
885892 try :
886893 infos = await asyncio .wait_for (
887894 loop .getaddrinfo (
888- target ,
895+ lookup_target ,
889896 port ,
890897 family = socket .AF_UNSPEC ,
891898 type = socket .SOCK_STREAM ,
892899 ),
893900 timeout = timeout ,
894901 )
895902 except Exception as exc :
896- raise OSError (f"dns lookup failed for { target } : { exc !r} " ) from exc
903+ raise OSError (f"dns lookup failed for { lookup_target } : { exc !r} " ) from exc
897904
898905 candidates = []
899906 seen = set ()
@@ -1122,14 +1129,36 @@ async def _relay_http_stream(self, host: str, port: int, reader, writer):
11221129 break
11231130
11241131 header_block = first_line
1132+ oversized_headers = False
11251133 while True :
11261134 line = await asyncio .wait_for (reader .readline (), timeout = 10 )
11271135 header_block += line
11281136 if len (header_block ) > MAX_HEADER_BYTES :
1137+ oversized_headers = True
11291138 break
11301139 if line in (b"\r \n " , b"\n " , b"" ):
11311140 break
11321141
1142+ # Reject truncated / oversized header blocks cleanly rather
1143+ # than forwarding a half-parsed request to the relay — doing
1144+ # so would send malformed JSON payloads to Apps Script and
1145+ # leave the client hanging until its own timeout fires.
1146+ if oversized_headers :
1147+ log .warning (
1148+ "MITM header block exceeds %d bytes — closing (%s)" ,
1149+ MAX_HEADER_BYTES , host ,
1150+ )
1151+ try :
1152+ writer .write (
1153+ b"HTTP/1.1 431 Request Header Fields Too Large\r \n "
1154+ b"Connection: close\r \n "
1155+ b"Content-Length: 0\r \n \r \n "
1156+ )
1157+ await writer .drain ()
1158+ except Exception :
1159+ pass
1160+ break
1161+
11331162 # Read body
11341163 body = b""
11351164 if _has_unsupported_transfer_encoding (header_block ):
@@ -1162,11 +1191,11 @@ async def _relay_http_stream(self, host: str, port: int, reader, writer):
11621191 if b":" in raw_line :
11631192 k , v = raw_line .decode (errors = "replace" ).split (":" , 1 )
11641193 headers [k .strip ()] = v .strip ()
1165-
1194+
11661195 # Shortening the length of X API URLs to prevent relay errors.
11671196 if host == "x.com" and re .match (r"/i/api/graphql/[^/]+/[^?]+\?variables=" , path ):
11681197 path = path .split ("&" )[0 ]
1169-
1198+
11701199 # MITM traffic arrives as origin-form paths; SOCKS/plain HTTP can
11711200 # also send absolute-form requests. Normalize both to full URLs.
11721201 if path .startswith ("http://" ) or path .startswith ("https://" ):
0 commit comments