From 0d0a29a46516e1a1d5ed2f37fae71e33194722b9 Mon Sep 17 00:00:00 2001 From: cheney Date: Tue, 26 May 2026 21:53:34 +0800 Subject: [PATCH] =?UTF-8?q?30x=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proxy/proxy_client.py | 195 +++++++++++++++++++++++++++++++++--------- 1 file changed, 156 insertions(+), 39 deletions(-) diff --git a/proxy/proxy_client.py b/proxy/proxy_client.py index 420beea..dbe5665 100644 --- a/proxy/proxy_client.py +++ b/proxy/proxy_client.py @@ -110,38 +110,23 @@ def modify_request_headers(request_data): return modified_data, method, path -def handle_request(request_data): - """Handle a single request from device B - completely independent thread""" - if isinstance(request_data, bytes): - request_data_str = request_data.decode('utf-8', errors='replace') - else: - request_data_str = request_data - - request_id = extract_request_id(request_data_str) - - modified_data, method, path = modify_request_headers(request_data_str) - - body_size = 0 - if "\r\n\r\n" in modified_data: - body_size = len(modified_data) - modified_data.find("\r\n\r\n") - 4 - - log_info("System", "Request: %s %s://%s:%d%s - Body: %d bytes - ID: %s" % (method, TARGET_PROTOCOL, TARGET_HOST, TARGET_PORT, path, body_size, request_id)) - +def send_request_to_target(host, port, protocol, request_data, request_id): + """Send request to target server and return response""" target_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) target_sock.settimeout(30) try: - target_sock.connect((TARGET_HOST, TARGET_PORT)) - log_info("System", "Connected to target: %s:%d" % (TARGET_HOST, TARGET_PORT)) + target_sock.connect((host, port)) + log_info("System", "Connected to target: %s:%d" % (host, port)) - if TARGET_PROTOCOL == 'https': + if protocol == 'https': context = ssl.create_default_context() - target_sock = context.wrap_socket(target_sock, server_hostname=TARGET_HOST) + target_sock = context.wrap_socket(target_sock, server_hostname=host) log_info("System", "SSL connection established with target") - if isinstance(modified_data, str): - modified_data = modified_data.encode('utf-8') - target_sock.sendall(modified_data) + if isinstance(request_data, str): + request_data = request_data.encode('utf-8') + target_sock.sendall(request_data) response = b"" content_length = 0 @@ -179,23 +164,157 @@ def handle_request(request_data): body = response[headers_end:] response = headers + ("\r\nX-Proxy-Request-ID: " + request_id).encode('utf-8') + body - status_code = "500" - if b"\r\n\r\n" in response: - headers_end = response.find(b"\r\n\r\n") - headers = response[:headers_end].decode('utf-8', errors='replace') - for line in headers.split("\r\n"): - if line.startswith("HTTP/"): - status_code = line.split()[1] - break - - log_info("System", "Response: %s %s - Status: %s - ID: %s" % (method, path, status_code, request_id)) - return response + finally: + target_sock.close() + +def parse_location(location): + """Parse Location header to get protocol, host, port, and path""" + protocol = TARGET_PROTOCOL + host = TARGET_HOST + port = TARGET_PORT + path = "/" + + if location.startswith("http://") or location.startswith("https://"): + # Full URL + protocol_end = location.find("://") + protocol = location[:protocol_end] + remaining = location[protocol_end + 3:] + path_start = remaining.find("/") + if path_start == -1: + host_part = remaining + path = "/" + else: + host_part = remaining[:path_start] + path = remaining[path_start:] + + port_pos = host_part.find(":") + if port_pos != -1: + host = host_part[:port_pos] + port = int(host_part[port_pos + 1:]) + else: + host = host_part + port = 443 if protocol == 'https' else 80 + elif location.startswith("/"): + # Absolute path + path = location + else: + # Relative path + path = "/" + location + + return protocol, host, port, path + +def modify_redirect_request(original_request, new_protocol, new_host, new_port, new_path): + """Modify request for redirect""" + lines = original_request.split("\r\n") + new_lines = [] + + first_line = lines[0] + parts = first_line.split() + if len(parts) >= 3: + method = parts[0] + http_version = parts[2] + new_first_line = "%s %s %s" % (method, new_path, http_version) + new_lines.append(new_first_line) + + for line in lines[1:]: + if not line: + continue + if line.lower().startswith("host:"): + new_lines.append("Host: %s:%d" % (new_host, new_port)) + elif line.lower().startswith("origin:"): + new_lines.append("Origin: %s://%s:%d" % (new_protocol, new_host, new_port)) + elif line.lower().startswith("referer:"): + new_lines.append("Referer: %s://%s:%d/" % (new_protocol, new_host, new_port)) + elif line.lower().startswith("connection:"): + continue + elif line.lower().startswith("keep-alive:"): + continue + elif line.lower().startswith("proxy-connection:"): + continue + elif line.lower().startswith("x-forwarded-for:"): + continue + elif line.lower().startswith("x-proxy-request-id:"): + continue + else: + new_lines.append(line) + + new_lines.append("") + new_lines.append("") + + if "\r\n\r\n" in original_request: + body_start = original_request.find("\r\n\r\n") + 4 + body = original_request[body_start:] + modified_data = "\r\n".join(new_lines[:-2]) + "\r\n\r\n" + body + else: + modified_data = "\r\n".join(new_lines[:-2]) + "\r\n\r\n" + + return modified_data + +def handle_request(request_data): + """Handle a single request from device B - completely independent thread""" + if isinstance(request_data, bytes): + request_data_str = request_data.decode('utf-8', errors='replace') + else: + request_data_str = request_data + + request_id = extract_request_id(request_data_str) + + modified_data, method, path = modify_request_headers(request_data_str) + + body_size = 0 + if "\r\n\r\n" in modified_data: + body_size = len(modified_data) - modified_data.find("\r\n\r\n") - 4 + + log_info("System", "Request: %s %s://%s:%d%s - Body: %d bytes - ID: %s" % (method, TARGET_PROTOCOL, TARGET_HOST, TARGET_PORT, path, body_size, request_id)) + + current_protocol = TARGET_PROTOCOL + current_host = TARGET_HOST + current_port = TARGET_PORT + current_request = modified_data + redirect_count = 0 + max_redirects = 5 + + try: + while redirect_count <= max_redirects: + response = send_request_to_target(current_host, current_port, current_protocol, current_request, request_id) + + status_code = "500" + location = None + + if b"\r\n\r\n" in response: + headers_end = response.find(b"\r\n\r\n") + headers = response[:headers_end].decode('utf-8', errors='replace') + for line in headers.split("\r\n"): + if line.startswith("HTTP/"): + status_code = line.split()[1] + elif line.lower().startswith("location:"): + location = line.split(":", 1)[1].strip() + + log_info("System", "Response: %s %s - Status: %s - ID: %s" % (method, path, status_code, request_id)) + + if status_code not in ("301", "302") or not location: + return response + + redirect_count += 1 + log_info("System", "Redirect %d: %s" % (redirect_count, location)) + + new_protocol, new_host, new_port, new_path = parse_location(location) + current_protocol = new_protocol + current_host = new_host + current_port = new_port + current_request = modify_redirect_request(request_data_str, new_protocol, new_host, new_port, new_path) + path = new_path + + log_error("System", "Too many redirects (%d)" % max_redirects) + error_response = ("HTTP/1.1 508 Loop Detected\r\nX-Proxy-Request-ID: " + (request_id if request_id else "") + "\r\nContent-Type: text/plain\r\n\r\nToo many redirects\r\n").encode('utf-8') + return error_response + except socket.error as e: if e.errno == 111 or e.errno == 10061: - log_error("System", "Cannot connect to target: %s:%d" % (TARGET_HOST, TARGET_PORT)) - error_response = ("HTTP/1.1 502 Bad Gateway\r\nX-Proxy-Request-ID: " + (request_id if request_id else "") + "\r\nContent-Type: text/plain\r\n\r\nCannot connect to target: %s:%d\r\n" % (TARGET_HOST, TARGET_PORT)).encode('utf-8') + log_error("System", "Cannot connect to target: %s:%d" % (current_host, current_port)) + error_response = ("HTTP/1.1 502 Bad Gateway\r\nX-Proxy-Request-ID: " + (request_id if request_id else "") + "\r\nContent-Type: text/plain\r\n\r\nCannot connect to target: %s:%d\r\n" % (current_host, current_port)).encode('utf-8') return error_response else: log_error("System", "Socket error: %s" % str(e)) @@ -209,8 +328,6 @@ def handle_request(request_data): log_error("System", "Error handling request: %s" % str(e)) error_response = ("HTTP/1.1 500 Internal Server Error\r\nX-Proxy-Request-ID: " + (request_id if request_id else "") + "\r\nContent-Type: text/plain\r\n\r\nInternal error\r\n").encode('utf-8') return error_response - finally: - target_sock.close() def process_request(b_socket, request_data): """Process a request in separate thread and send response back"""