From c6a2ddb2261a6d8f54efb07f0ba6db0e88000550 Mon Sep 17 00:00:00 2001 From: micheal Date: Wed, 6 Mar 2024 20:46:01 +1300 Subject: [PATCH] release 5.9.10 + update blacklist. + add direct mode + query DNS from local first for unknown domain. --- README.md | 4 +- code/default/launcher/web_control.py | 2 +- code/default/lib/noarch/front_base/http1.py | 19 +-- .../lib/noarch/front_base/http_common.py | 2 +- code/default/smart_router/local/dns_query.py | 6 +- .../smart_router/local/gfw_white_list.txt | 11 ++ code/default/smart_router/local/ip_region.py | 2 +- code/default/smart_router/local/pac_server.py | 8 +- code/default/smart_router/local/pipe_socks.py | 43 +++++- .../default/smart_router/local/smart_route.py | 4 + .../scripts/update_domain_list.py | 63 +++++++++ .../smart_router/tests/test_black_list.py | 40 ++++++ .../smart_router/tests/test_set_policy.py | 46 +++++++ .../smart_router/web_ui/config_general.html | 1 + code/default/version.txt | 2 +- code/default/x_tunnel/local/proxy_handler.py | 6 +- .../x_tunnel/local/seley_front/rc4_wrap.py | 22 ++- code/default/x_tunnel/local/web_control.py | 1 + code/default/x_tunnel/tests/test_proxy.py | 129 ++++++++++++++++++ 19 files changed, 381 insertions(+), 30 deletions(-) create mode 100644 code/default/smart_router/scripts/update_domain_list.py create mode 100644 code/default/smart_router/tests/test_black_list.py create mode 100644 code/default/smart_router/tests/test_set_policy.py create mode 100644 code/default/x_tunnel/tests/test_proxy.py diff --git a/README.md b/README.md index 54f494b8f3..98a9fd513d 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ ### 最新公告: - 2024-02-25 -* 最新版5.9.9, 修复5.9.7智能策略更新的bug。 + 2024-03-06 +* 最新版5.9.10, 更新黑名单列表。 * 5.9.0 升级GAE服务端到python3 * 5.8.8 改进iOS下连接性能 * 5.7.0 为X-Tunnel增加新通道 diff --git a/code/default/launcher/web_control.py b/code/default/launcher/web_control.py index 71c549f730..bed1ee7bff 100644 --- a/code/default/launcher/web_control.py +++ b/code/default/launcher/web_control.py @@ -923,7 +923,7 @@ def req_log_files(self): sys.path.append(x_tunnel_local) from upload_logs import pack_logs - data = pack_logs(10 * 1024 * 1024) + data = pack_logs(200 * 1024 * 1024) self.send_response("application/zip", data) def req_mem_info_handler(self): diff --git a/code/default/lib/noarch/front_base/http1.py b/code/default/lib/noarch/front_base/http1.py index b8e7f529b3..d4f207c1d2 100644 --- a/code/default/lib/noarch/front_base/http1.py +++ b/code/default/lib/noarch/front_base/http1.py @@ -68,8 +68,9 @@ def request(self, task): def keep_alive_thread(self): while time.time() - self.ssl_sock.create_time < self.config.http1_first_ping_wait: if not self.keep_running: - self.close("exit ") + self.close("exit") return + time.sleep(3) if self.config.http1_first_ping_wait and self.processed_tasks == 0: @@ -87,7 +88,7 @@ def keep_alive_thread(self): elif self.config.http1_idle_time: while self.keep_running: - time_to_sleep = max(self.config.http1_idle_time - (time.time() - self.last_recv_time), 0.2) + time_to_sleep = max(self.config.http1_idle_time - (time.time() - self.last_recv_time), 3) time.sleep(time_to_sleep) if not self.request_onway and time.time() - self.last_recv_time > self.config.http1_idle_time: @@ -113,7 +114,6 @@ def work_loop(self): self.close("keep alive") return - self.last_recv_time = time.time() continue # self.logger.debug("http1 get task") @@ -127,7 +127,6 @@ def work_loop(self): self.request_task(task) self.request_onway = False self.last_send_time = time_now - self.last_recv_time = time_now life_end_reason = self.is_life_end() if life_end_reason: @@ -138,7 +137,6 @@ def request_task(self, task): timeout = task.timeout self.request_onway = True start_time = time.time() - self.last_recv_time = start_time self.record_active("request") task.set_state("h1_req") @@ -160,10 +158,11 @@ def request_task(self, task): sended = self.ssl_sock.send(task.body[start:start + send_size]) start += sended - task.set_state("h1_req_sended") + task.set_state("h1_req_sent") except Exception as e: - self.logger.warn("%s h1_request send:%r inactive_time:%d task.timeout:%d", - self.ip_str, e, time.time() - self.last_recv_time, task.timeout) + self.logger.warn("%s %s h1_request send:%r inactive_time:%d task.timeout:%d", + self.ip_str, self.ssl_sock.getsockname(), + e, time.time() - self.last_recv_time, task.timeout) self.logger.warn('%s trace:%s', self.ip_str, self.get_trace()) self.retry_task_cb(task) @@ -175,6 +174,7 @@ def request_task(self, task): response = simple_http_client.Response(self.ssl_sock) response.begin(timeout=timeout) task.set_state("response_begin") + self.last_recv_time = time.time() except Exception as e: self.logger.warn("%s h1_request recv:%r inactive_time:%d task.timeout:%d", self.ip_str, e, time.time() - self.last_recv_time, task.timeout) @@ -201,7 +201,7 @@ def request_task(self, task): task.queue.put(response) else: if self.config.http2_show_debug: - self.logger.debug("got pong for %s status:%d", self.ip_str, response.status) + self.logger.debug("got res for %s status:%d", self.ip_str, response.status) try: read_target = int(response.content_length) @@ -290,6 +290,7 @@ def head_request(self): self.record_active("head end") rtt = (time.time() - start_time) * 1000 self.update_rtt(rtt) + self.last_recv_time = time.time() return True except Exception as e: self.logger.warn("h1 %s HEAD keep alive request fail:%r", self.ssl_sock.ip_str, e) diff --git a/code/default/lib/noarch/front_base/http_common.py b/code/default/lib/noarch/front_base/http_common.py index 5ebd465365..bb946183d0 100644 --- a/code/default/lib/noarch/front_base/http_common.py +++ b/code/default/lib/noarch/front_base/http_common.py @@ -217,7 +217,7 @@ def __init__(self, logger, ip_manager, config, ssl_sock, close_cb, retry_task_cb self.last_send_time = self.ssl_sock.create_time self.life_end_time = self.ssl_sock.create_time + \ random.randint(self.config.connection_max_life, int(self.config.connection_max_life * 1.5)) - # self.logger.debug("worker.init %s", self.ip_str) + # self.logger.debug("worker.init %s %s", self.ip_str, self.ssl_sock.getsockname()) def __str__(self): o = "" diff --git a/code/default/smart_router/local/dns_query.py b/code/default/smart_router/local/dns_query.py index c2645f722d..f8c4d1055c 100644 --- a/code/default/smart_router/local/dns_query.py +++ b/code/default/smart_router/local/dns_query.py @@ -635,6 +635,10 @@ def query_blocked_domain(self, domain, dns_type): ]) def query_unknown_domain(self, domain, dns_type): + res = self.local_dns_resolve.query(domain, dns_type) + if res: + return res + return self.parallel_query.query(domain, dns_type, [ self.https_query.query, self.tls_query.query, @@ -662,7 +666,7 @@ def query(self, domain, dns_type=1, history=[]): xlog.debug("DNS query:%s in black", domain) return ips - elif b"." not in domain or g.gfwlist.in_white_list(domain) or rule in ["direct"]: + elif b"." not in domain or g.gfwlist.in_white_list(domain) or rule in ["direct"] or g.config.pac_policy == "all_Direct": ips = self.local_dns_resolve.query(domain, timeout=1) g.domain_cache.set_ips(domain, ips, dns_type) return ips diff --git a/code/default/smart_router/local/gfw_white_list.txt b/code/default/smart_router/local/gfw_white_list.txt index a478c62ad1..b4cbfa5083 100644 --- a/code/default/smart_router/local/gfw_white_list.txt +++ b/code/default/smart_router/local/gfw_white_list.txt @@ -6,6 +6,7 @@ 178.com 2345.com 360safe.com +51.la 56.com 5eplay.com 5ewin.com @@ -26,6 +27,7 @@ alipay.com alipayobjects.com alivecdn.com aliyuncs.com +aliyunddos1030.com allrace.com amap.com amemv.com @@ -53,6 +55,10 @@ bokecs.net bootcss.com btigroup.io bytedance.com +bytedns1.com +bytefcdn.com +byteimg.com +bytegecko.co cccpan.com cdncl.net chiphell.com @@ -76,6 +82,8 @@ douyin.com douyincdn.com douyinliving.com douyinpic.com +douyinstatic.com.w.cdngslb.com +douyinstatic.com.queniuuf.com douyinvod.com douyu.com duoduocdn.com @@ -125,6 +133,7 @@ kmf.com ksyungslb.com kugou.com kuiniuca.com +kunluncan.com le.com lecloud.com leisu.com @@ -174,6 +183,7 @@ qq-zuidazy.com qq.com qqmail.com qtlglb.com +queniusy.com remuxhdr.com ruioushang.com sandai.net @@ -207,6 +217,7 @@ ubuntu.com uniqueway.com umeng.com videocc.net +volcfcdndvs.com vzan.cc vzuu.com wangyuan.com diff --git a/code/default/smart_router/local/ip_region.py b/code/default/smart_router/local/ip_region.py index e0bafa32e7..5d947f0034 100644 --- a/code/default/smart_router/local/ip_region.py +++ b/code/default/smart_router/local/ip_region.py @@ -119,7 +119,7 @@ def generate_db(self): '198.51.100.0/24', # TEST-NET-2 '203.0.113.0/24', # TEST-NET-3 # 连续地址直到 IP 结束,特殊处理 - # '224.0.0.0/4', #组播地址(D类) + '224.0.0.0/4', #组播地址(D类) # '240.0.0.0/4', #保留地址(E类) ) keeplist = [] diff --git a/code/default/smart_router/local/pac_server.py b/code/default/smart_router/local/pac_server.py index dc3509c03b..d0be0ce839 100644 --- a/code/default/smart_router/local/pac_server.py +++ b/code/default/smart_router/local/pac_server.py @@ -27,7 +27,7 @@ gae_ca_file = os.path.join(env_info.data_path, "gae_proxy", "CA.crt") -allow_policy = ["black_GAE", "black_X-Tunnel", "smart-router", "all_X-Tunnel"] +allow_policy = ["black_GAE", "black_X-Tunnel", "smart-router", "all_X-Tunnel", "all_Direct"] def get_serving_pacfile(): @@ -52,6 +52,10 @@ def policy_all_to_proxy(self, host, port): content = content.replace(self.PROXY_LISTEN, proxy) return content + def policy_all_to_direct(self): + content = """function FindProxyForURL(url, host) { return 'DIRECT';}""" + return content + def policy_blacklist_to_proxy(self, host, port): content = get_serving_pacfile() @@ -89,6 +93,8 @@ def do_GET(self): content = self.policy_blacklist_to_proxy(host, "%s" % g.x_tunnel_socks_port) elif g.config.pac_policy == "all_X-Tunnel": content = self.policy_all_to_proxy(host, "%s" % g.x_tunnel_socks_port) + elif g.config.pac_policy == "all_Direct": + content = self.policy_all_to_direct() else: content = self.policy_all_to_proxy(host, g.config.proxy_port) diff --git a/code/default/smart_router/local/pipe_socks.py b/code/default/smart_router/local/pipe_socks.py index c6b3f35d9c..56905e535e 100644 --- a/code/default/smart_router/local/pipe_socks.py +++ b/code/default/smart_router/local/pipe_socks.py @@ -1,6 +1,8 @@ import threading import time import sys +import socket +import errno from xx_six import BlockingIOError import utils @@ -214,18 +216,21 @@ def flush_send_s(s2, d1): try: d = s1.recv(65535) + except BlockingIOError as e: + xlog.debug("%s recv BlockingIOError:%r", s1, e) + continue except Exception as e: - # xlog.debug("%s recv e:%r", s1, e) + xlog.debug("%s recv e:%r", s1, e) self.close(s1, "r") continue if not d: # socket closed by peer. - # xlog.debug("%s recv empty, close", s1) + xlog.debug("%s recv empty, close", s1) self.close(s1, "r") continue - # xlog.debug("direct received %d bytes from:%s", len(d), s1) + xlog.debug("direct received %d bytes from:%s", len(d), s1) s1.recved_data += len(d) s1.recved_times += 1 @@ -253,6 +258,10 @@ def flush_send_s(s2, d1): flush_send_s(s2, d1) s2.sent_data += len(d1) s2.sent_times += 1 + except BlockingIOError as e: + xlog.warn("Except %s flush_send_s BlockingIOError %r", s2, e) + self.close(s2, "w") + continue except Exception as e: xlog.warn("send split SNI:%s fail:%r", s2.host, e) self.close(s2, "w") @@ -268,12 +277,24 @@ def flush_send_s(s2, d1): s2.sent_data += sent s2.sent_times += 1 # xlog.debug("direct send %d to %s from:%s total:%d", sent, s2, s1, len(d)) + except BlockingIOError as e: + xlog.warn("Except %s send BlockingIOError %r", s2, e) + sent = 0 + except socket.error as e: + if e.errno == errno.EAGAIN: + # if str(e) == "[Errno 35] Resource temporarily unavailable": + xlog.warn("%s send errno.EAGAIN %r", s2, e) + time.sleep(0.1) + sent = 0 + else: + self.close(s2, "w") + continue except Exception as e: # xlog.debug("%s send e:%r", s2, e) if sys.version_info[0] == 3 and isinstance(e, BlockingIOError): # This error happened on upload large file or speed test # Just ignore this error and will be fine - # xlog.warn("%s send BlockingIOError %r", s2, e) + xlog.warn("%s send BlockingIOError %r", s2, e) sent = 0 else: # xlog.warn("%s send except:%r", s2, e) @@ -320,11 +341,23 @@ def flush_send_s(s2, d1): s1.sent_data += sent s1.sent_times += 1 # xlog.debug("send buffered %d bytes to %s", sent, s1) + except BlockingIOError as e: + xlog.warn("Except %s send BlockingIOError %r", s1, e) + sent = 0 + except socket.error as e: + if e.errno == errno.EAGAIN: + # if str(e) == "[Errno 35] Resource temporarily unavailable": + xlog.warn("%s send errno.EAGAIN %r", s1, e) + time.sleep(0.1) + sent = 0 + else: + self.close(s1, "w") + continue except Exception as e: if sys.version_info[0] == 3 and isinstance(e, BlockingIOError): # This error happened on upload large file or speed test # Just ignore this error and will be fine - # xlog.debug("%s sent BlockingIOError %r", s1, e) + xlog.debug("%s sent BlockingIOError %r", s1, e) sent = 0 else: # xlog.debug("%s sent e:%r", s1, e) diff --git a/code/default/smart_router/local/smart_route.py b/code/default/smart_router/local/smart_route.py index 75224bead7..a143fbbaad 100644 --- a/code/default/smart_router/local/smart_route.py +++ b/code/default/smart_router/local/smart_route.py @@ -497,6 +497,8 @@ def handle_ip_proxy(sock, ip, port, client_address): rule = "direct" elif g.config.pac_policy == "all_X-Tunnel": rule = "socks" + elif g.config.pac_policy == "all_Direct": + rule = "direct" if rule: return try_loop("ip user", [rule], sock, ip, port, client_address) @@ -556,6 +558,8 @@ def handle_domain_proxy(sock, host, port, client_address, left_buf=""): rule = "gae" elif utils.check_ip_valid(host) and utils.is_private_ip(host): rule = "direct" + elif g.config.pac_policy == "all_Direct": + rule = "direct" if not rule and (g.config.bypass_speedtest and g.gfwlist.in_speedtest_whitelist(host)): xlog.debug("speedtest %s", host) diff --git a/code/default/smart_router/scripts/update_domain_list.py b/code/default/smart_router/scripts/update_domain_list.py new file mode 100644 index 0000000000..9fc4b000b0 --- /dev/null +++ b/code/default/smart_router/scripts/update_domain_list.py @@ -0,0 +1,63 @@ +import os +import sys +from os.path import join + +current_path = os.path.dirname(os.path.abspath(__file__)) +local_path = os.path.abspath( os.path.join(current_path, os.pardir, "local")) +root_path = os.path.abspath(os.path.join(current_path, os.pardir, os.pardir)) +noarch_path = join(root_path, "lib", "noarch") +sys.path.append(noarch_path) + + +import simple_http_client +import utils + + +def download_list(url): + res = simple_http_client.request("GET", url) + content = res.text + return utils.to_str(content) + + +def parse_list(content): + black_suffix = [] + black_keyword = [] + black_ipmask = [] + for line in content.split(): + if not line or line.startswith("#"): + continue + + if line.startswith("DOMAIN-SUFFIX,") and line.endswith(",Proxy"): + _, suffix, _ = line.split(",")[0:3] + black_suffix.append(suffix) + + if line.startswith("DOMAIN-KEYWORD,") and line.endswith(",Proxy"): + _, keyword, _ = line.split(",")[0:3] + black_keyword.append(keyword) + + if line.startswith("IP-CIDR,") and line.endswith(",Proxy"): + _, ipmask, _ = line.split(",")[0:3] + black_ipmask.append(ipmask) + + black_suffix.sort() + black_keyword.sort() + black_ipmask.sort() + + return black_suffix, black_keyword, black_ipmask + + +def update_blacklist(): + # url = "https://github.com/Johnshall/Shadowrocket-ADBlock-Rules-Forever/raw/release/sr_top500_banlist.conf" + url = "https://raw.githubusercontent.com/Johnshall/Shadowrocket-ADBlock-Rules-Forever/release/sr_top500_banlist.conf" + content = download_list(url) + black_suffix, black_keyword, black_ipmask = parse_list(content) + + with open(join(local_path, "gfw_black_list.txt"), "w") as fd: + fd.write("\r\n".join(black_suffix)) + + with open(join(local_path, "gfw_black_keywords.txt"), "w") as fd: + fd.write("\r\n".join(black_keyword)) + + +if __name__ == "__main__": + update_blacklist() diff --git a/code/default/smart_router/tests/test_black_list.py b/code/default/smart_router/tests/test_black_list.py new file mode 100644 index 0000000000..9c2cce2d77 --- /dev/null +++ b/code/default/smart_router/tests/test_black_list.py @@ -0,0 +1,40 @@ +import os +from os.path import join +import sys +import unittest + + +current_path = os.path.dirname(os.path.abspath(__file__)) +smart_route_path = os.path.abspath(os.path.join(current_path, os.path.pardir)) +local_path = os.path.join(smart_route_path, "local") +sys.path.append(local_path) + +default_path = os.path.abspath(join(smart_route_path, os.path.pardir)) +noarch_path = join(default_path, "lib", "noarch") +sys.path.append(noarch_path) + +import gfwlist + + +class TestGFW(unittest.TestCase): + def test_Ip_Mask(self): + ip = "1.2.3.4" + ip_mask = "1.2.3.0/24" + ip_masks = [ip_mask] + subnets = gfwlist.IpMask(ip_masks) + c = subnets.check_ip(ip) + print(c) + + print(subnets.check_ip("1.2.3.5")) + print(subnets.check_ip("1.2.4.5")) + print(subnets.check_ip("1.3.4.5")) + + def test_domain(self): + gfw = gfwlist.GfwList() + print(gfw.ip_in_black_list("91.108.56.1")) + print(gfw.ip_in_black_list("1.1.1.1")) + + def test_keyword(self): + gfw = gfwlist.GfwList() + print(gfw.in_block_list(b"www.amazon.com")) + print(gfw.in_block_list(b"www.apple.com")) diff --git a/code/default/smart_router/tests/test_set_policy.py b/code/default/smart_router/tests/test_set_policy.py new file mode 100644 index 0000000000..fba166814a --- /dev/null +++ b/code/default/smart_router/tests/test_set_policy.py @@ -0,0 +1,46 @@ +import requests +import re +from unittest import TestCase + + +class TestSetPolicy(TestCase): + def test_set_global(self): + url = "http://localhost:8085/module/smart_router/control/config" + headers = { + "Content-Type": "application/json" + } + data = { + "cmd": "set", + "pac_policy": "all_X-Tunnel" + } + r = requests.post(url, json=data, headers=headers) + print(r.text) + + def test_set_smart(self): + url = "http://localhost:8085/module/smart_router/control/config" + headers = { + "Content-Type": "application/json" + } + data = { + "cmd": "set", + "pac_policy": "smart-router" + } + r = requests.post(url, json=data, headers=headers) + print(r.text) + + def test_match(self): + r = re.compile("google|apple") + s1 = "www.google.com" + s2 = "www.apple.com" + s3 = "www.ms.com" + g1 = r.search(s1) + print(g1) + g2 = r.search(s2) + print(g2) + g3 = r.search(s3) + print(g3) + + def test_re(self): + strs = 'Test result 1: Not Ok -31.08' + g = re.search(r'\bNot Ok\b', strs).group(0) + print(g) \ No newline at end of file diff --git a/code/default/smart_router/web_ui/config_general.html b/code/default/smart_router/web_ui/config_general.html index 1c3a25a24a..8eb0374d68 100644 --- a/code/default/smart_router/web_ui/config_general.html +++ b/code/default/smart_router/web_ui/config_general.html @@ -23,6 +23,7 @@ + diff --git a/code/default/version.txt b/code/default/version.txt index 76d1514dde..c55621169c 100644 --- a/code/default/version.txt +++ b/code/default/version.txt @@ -1 +1 @@ -5.9.9 \ No newline at end of file +5.9.10 \ No newline at end of file diff --git a/code/default/x_tunnel/local/proxy_handler.py b/code/default/x_tunnel/local/proxy_handler.py index 1bdca22898..95dfba1ea9 100644 --- a/code/default/x_tunnel/local/proxy_handler.py +++ b/code/default/x_tunnel/local/proxy_handler.py @@ -268,7 +268,7 @@ def socks5_handler(self): conn_id = proxy_session.create_conn(sock, addr, port) if not conn_id: - xlog.warn("create conn fail") + xlog.warn("socks5 create conn to %s:%d fail", addr, port) reply = b"\x05\x01\x00" + addrtype_pack + addr_pack + struct.pack(">H", port) sock.send(reply) return @@ -314,7 +314,7 @@ def https_handler(self): sock = self.connection conn_id = proxy_session.create_conn(sock, host, port) if not conn_id: - xlog.warn("create conn fail") + xlog.warn("https create conn to %s:%d fail", host, port) sock.send(b'HTTP/1.1 500 Fail\r\n\r\n') return @@ -378,7 +378,7 @@ def http_handler(self, first_char): sock = self.connection conn_id = proxy_session.create_conn(sock, host, port) if not conn_id: - xlog.warn("create conn fail") + xlog.warn("http create conn to %s:%d fail", host, port) sock.send(b'HTTP/1.1 500 Fail\r\n\r\n') return diff --git a/code/default/x_tunnel/local/seley_front/rc4_wrap.py b/code/default/x_tunnel/local/seley_front/rc4_wrap.py index 72b536aee0..344f4dfa62 100644 --- a/code/default/x_tunnel/local/seley_front/rc4_wrap.py +++ b/code/default/x_tunnel/local/seley_front/rc4_wrap.py @@ -3,6 +3,7 @@ import struct import json import os +import errno import env_info import xlog @@ -110,6 +111,9 @@ def is_support_h2(self): def setblocking(self, block): self._sock.setblocking(block) + def getsockname(self): + self._sock.getsockname() + def __getattr__(self, attr): if attr == "socket_closed": # work around in case close before finished init. @@ -144,11 +148,19 @@ def connect(self, *args, **kwargs): def send(self, data, flags=0): data = self.encode(data) - try: - return self._sock.send(data) - except Exception as e: - #self.logger.exception("ssl send:%r", e) - raise e + while True: + try: + bytes_sent = self._sock.send(data) + return bytes_sent + except BlockingIOError as e: + time.sleep(0.1) + continue + except socket.error as e: + if e.errno == errno.EAGAIN: + # if str(e) == "[Errno 35] Resource temporarily unavailable": + time.sleep(0.1) + else: + raise e def recv(self, bufsiz, flags=0): data = self._sock.recv(bufsiz) diff --git a/code/default/x_tunnel/local/web_control.py b/code/default/x_tunnel/local/web_control.py index e96a17dfe6..cf561a0594 100644 --- a/code/default/x_tunnel/local/web_control.py +++ b/code/default/x_tunnel/local/web_control.py @@ -275,6 +275,7 @@ def req_token_login_handler(self): "reason": "Password format fail" }) + g.config.api_server = g.config.default_config["api_server"] if g.config.update_cloudflare_domains and cloudflare_domains: g.http_client.save_cloudflare_domain(cloudflare_domains) if g.tls_relay_front and tls_relay.get("ips"): diff --git a/code/default/x_tunnel/tests/test_proxy.py b/code/default/x_tunnel/tests/test_proxy.py new file mode 100644 index 0000000000..6bfb049d54 --- /dev/null +++ b/code/default/x_tunnel/tests/test_proxy.py @@ -0,0 +1,129 @@ +from unittest import TestCase +import json +import os +import time +import sys + +current_path = os.path.dirname(os.path.abspath(__file__)) +default_path = os.path.abspath(os.path.join(current_path, os.path.pardir, os.path.pardir)) +root_path = os.path.abspath(os.path.join(default_path, os.path.pardir, os.path.pardir)) + +noarch_lib = os.path.abspath(os.path.join(default_path, 'lib', 'noarch')) +sys.path.append(noarch_lib) + +import utils +import simple_http_server +from dnslib.dns import DNSRecord, DNSHeader, DNSQuestion +import socket + +import simple_http_client +from xlog import getLogger +xlog = getLogger("test") + + +class ProxyTest(TestCase): + def __init__(self): + super().__init__() + + def test_xtunnel_logout(self): + xlog.info("Start testing XTunnel logout") + res = simple_http_client.request("POST", "http://127.0.0.1:8085/module/x_tunnel/control/logout", timeout=10) + self.assertEqual(res.status, 200) + self.xtunnel_login_status = False + xlog.info("Finished testing XTunnel logout") + + def smart_route_proxy_http(self): + xlog.info("Start testing SmartRouter HTTP proxy protocol") + proxy = "http://localhost:8086" + res = simple_http_client.request("GET", "https://github.com/", proxy=proxy, timeout=20) + self.assertEqual(res.status, 200) + xlog.info("Finished testing SmartRouter HTTP proxy protocol") + + def smart_route_proxy_socks4(self): + xlog.info("Start testing SmartRouter SOCKS4 proxy protocol") + proxy = "socks4://localhost:8086" + res = simple_http_client.request("GET", "https://github.com/", proxy=proxy, timeout=15) + self.assertEqual(res.status, 200) + xlog.info("Finished testing SmartRouter SOCKS4 proxy protocol") + + def smart_route_proxy_socks5(self): + xlog.info("Start testing SmartRouter SOCKS5 proxy protocol") + proxy = "socks5://localhost:8086" + res = simple_http_client.request("GET", "https://github.com/", proxy=proxy, timeout=15) + self.assertEqual(res.status, 200) + xlog.info("Finished testing SmartRouter SOCKS5 proxy protocol") + + def smart_route_dns_query(self): + xlog.info("Start testing SmartRouter DNS Query") + domain = "appsec.hicloud.com" + d = DNSRecord(DNSHeader(123)) + d.add_question(DNSQuestion(domain, 1)) + req4_pack = d.pack() + + for port in [8053, 53]: + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.sendto(req4_pack, ("127.0.0.1", port)) + sock.settimeout(5) + + try: + response, server = sock.recvfrom(8192) + except Exception as e: + xlog.warn("recv fail for port:%s e:%r", port, e) + continue + + p = DNSRecord.parse(response) + for r in p.rr: + ip = utils.to_bytes(str(r.rdata)) + xlog.info("IP:%s" % ip) + self.assertEqual(utils.check_ip_valid(ip), True) + + xlog.info("Finished testing SmartRouter DNS Query") + return + + def xtunnel_token_login(self): + xlog.info("Start testing XTunnel login") + headers = { + "Content-Type": "application/json" + } + data = { + "login_token": os.getenv("XTUNNEL_TOKEN"), + } + data = json.dumps(data) + res = simple_http_client.request("POST", "http://127.0.0.1:8085/module/x_tunnel/control/token_login", + headers=headers, body=data, timeout=60) + self.assertEqual(res.status, 200) + self.xtunnel_login_status = True + xlog.info("Finished testing XTunnel login") + + def test_xtunnel_proxy_http(self): + xlog.info("Start testing XTunnel HTTP proxy protocol") + # if not self.xtunnel_login_status: + # self.xtunnel_token_login() + proxy = "http://localhost:1080" + for _ in range(3): + res = simple_http_client.request("GET", "https://github.com/", proxy=proxy, timeout=30) + if not res: + time.sleep(5) + continue + self.assertEqual(res.status, 200) + xlog.info("Finished testing XTunnel HTTP proxy protocol") + + self.assertEqual(res.status, 200) + + def xtunnel_proxy_socks4(self): + xlog.info("Start testing XTunnel Socks4 proxy protocol") + if not self.xtunnel_login_status: + self.xtunnel_token_login() + proxy = "socks4://localhost:1080" + res = simple_http_client.request("GET", "https://github.com/", proxy=proxy, timeout=15) + self.assertEqual(res.status, 200) + xlog.info("Finished testing XTunnel Socks4 proxy protocol") + + def xtunnel_proxy_socks5(self): + xlog.info("Start testing XTunnel Socks5 proxy protocol") + if not self.xtunnel_login_status: + self.xtunnel_token_login() + proxy = "socks5://localhost:1080" + res = simple_http_client.request("GET", "https://github.com/", proxy=proxy, timeout=15) + self.assertEqual(res.status, 200) + xlog.info("Finished testing XTunnel Socks5 proxy protocol")