Skip to content

Commit c2aaf37

Browse files
committed
fix: improve IP address handling and reject oversized header blocks in ProxyServer
1 parent d14b299 commit c2aaf37

1 file changed

Lines changed: 35 additions & 6 deletions

File tree

src/proxy_server.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)