From d2159c3fda50eb7c0974a1c49e98e49505645988 Mon Sep 17 00:00:00 2001 From: micheal Date: Sun, 20 Nov 2022 13:47:14 -1100 Subject: [PATCH] 4.13.5 fix crash issue. --- code/default/launcher/download_modules.py | 2 +- code/default/launcher/start.py | 2 +- .../lib/noarch/front_base/boringssl_wrap.py | 158 +++++++++++------- code/default/lib/tests/stress_boringssl.py | 10 +- code/default/lib/tests/stress_boringssl2.py | 143 ++++++++++++++++ code/default/version.txt | 2 +- .../x_tunnel/local/tls_relay_front/config.py | 5 +- 7 files changed, 251 insertions(+), 71 deletions(-) create mode 100644 code/default/lib/tests/stress_boringssl2.py diff --git a/code/default/launcher/download_modules.py b/code/default/launcher/download_modules.py index 226a8a6488..65e3281020 100644 --- a/code/default/launcher/download_modules.py +++ b/code/default/launcher/download_modules.py @@ -128,6 +128,7 @@ def download_worker(): if not os.path.isdir(switchyomega_path): return + time.sleep(150) sha256_fn = os.path.join(switchyomega_path, "Sha256.txt") download_file("https://raw.githubusercontent.com/XX-net/XX-Net/master/SwitchyOmega/Sha256.txt", sha256_fn) sha256_dict = get_sha256(sha256_fn) @@ -138,7 +139,6 @@ def download_worker(): def start_download(): - time.sleep(150) th = threading.Thread(target=download_worker) th.start() return True diff --git a/code/default/launcher/start.py b/code/default/launcher/start.py index 348383afc3..bdc62968f8 100644 --- a/code/default/launcher/start.py +++ b/code/default/launcher/start.py @@ -93,7 +93,7 @@ def exit_handler(): atexit.register(exit_handler) -has_desktop = True +has_desktop = sys_platform.has_desktop def main(): diff --git a/code/default/lib/noarch/front_base/boringssl_wrap.py b/code/default/lib/noarch/front_base/boringssl_wrap.py index 0bd2698bcd..f5a3fe77f0 100644 --- a/code/default/lib/noarch/front_base/boringssl_wrap.py +++ b/code/default/lib/noarch/front_base/boringssl_wrap.py @@ -4,6 +4,7 @@ import socket +import threading import utils @@ -14,6 +15,7 @@ class SSLConnection(object): BIO_CLOSE = 1 def __init__(self, context, sock, ip_str=None, sni=None, on_close=None): + self._lock = threading.Lock() self._context = context self._sock = sock self.ip_str = utils.to_bytes(ip_str) @@ -66,6 +68,9 @@ def wrap(self): raise socket.error("SSL_connect fail: %s" % error) def do_handshake(self): + if not self._connection: + raise socket.error("do_handshake fail: not connected") + ret = bssl.SSL_do_handshake(self._connection) if ret == 1: return @@ -74,6 +79,9 @@ def do_handshake(self): raise socket.error("do_handshake fail: %s" % error) def is_support_h2(self): + if not self._connection: + return False + out_data_pp = ffi.new("uint8_t**", ffi.NULL) out_len_p = ffi.new("unsigned*") bssl.SSL_get0_alpn_selected(self._connection, out_data_pp, out_len_p) @@ -90,21 +98,13 @@ def setblocking(self, block): self._sock.setblocking(block) def __getattr__(self, attr): - if attr == "socket_closed": - # work around in case close before finished init. - return True - - elif attr in ('is_support_h2', "_on_close", '_context', '_sock', '_connection', '_makefile_refs', + if attr in ('is_support_h2', "_on_close", '_context', '_sock', '_connection', '_makefile_refs', 'sni', 'wrap', 'socket_closed'): return getattr(self, attr) elif hasattr(self._connection, attr): return getattr(self._connection, attr) - def __del__(self): - if not self.socket_closed and self._connection: - self.close() - def get_cert(self): if self.peer_cert: return self.peer_cert @@ -113,25 +113,27 @@ def x509_name_to_string(xname): line = bssl.X509_NAME_oneline(xname, ffi.NULL, 0) return ffi.string(line) - try: - cert = bssl.SSL_get_peer_certificate(self._connection) - if cert == ffi.NULL: - raise Exception("get cert failed") + with self._lock: + if self._connection: + try: + cert = bssl.SSL_get_peer_certificate(self._connection) + if cert == ffi.NULL: + raise Exception("get cert failed") - alt_names_p = bssl.get_alt_names(cert) - if alt_names_p == ffi.NULL: - raise Exception("get alt_names failed") + alt_names_p = bssl.get_alt_names(cert) + if alt_names_p == ffi.NULL: + raise Exception("get alt_names failed") - alt_names = utils.to_str(ffi.string(alt_names_p)) - bssl.free(alt_names_p) + alt_names = utils.to_str(ffi.string(alt_names_p)) + bssl.free(alt_names_p) - subject = x509_name_to_string(bssl.X509_get_subject_name(cert)) - issuer = x509_name_to_string(bssl.X509_get_issuer_name(cert)) - altName = alt_names.split(";") - except Exception as e: - subject = "" - issuer = "" - altName = [] + subject = x509_name_to_string(bssl.X509_get_subject_name(cert)) + issuer = x509_name_to_string(bssl.X509_get_issuer_name(cert)) + altName = alt_names.split(";") + except Exception as e: + subject = "" + issuer = "" + altName = [] self.peer_cert = { "cert": subject, @@ -143,40 +145,66 @@ def x509_name_to_string(xname): return self.peer_cert def send(self, data, flags=0): - try: - ret = bssl.SSL_write(self._connection, data, len(data)) - return ret - except Exception as e: - self._context.logger.exception("ssl send:%r", e) - raise e + with self._lock: + if not self._connection: + e = socket.error(5) + e.errno = 5 + raise e + + try: + ret = bssl.SSL_write(self._connection, data, len(data)) + if ret <= 0: + errno = bssl.SSL_get_error(self._connection, ret) + self._context.logger.warn("send n:%d errno: %d ip:%s", ret, errno, self.ip_str) + e = socket.error(2) + e.errno = errno + raise e + + return ret + except Exception as e: + self._context.logger.exception("ssl send:%r", e) + raise e def recv(self, bufsiz, flags=0): - buf = bytes(bufsiz) - n = bssl.SSL_read(self._connection, buf, bufsiz) - if n <= 0: - errno = bssl.SSL_get_error(self._connection, n) - self._context.logger.warn("recv errno: %d ip:%s", errno, self.ip_str) - e = socket.error(2) - e.errno = errno - raise e - - dat = buf[:n] - return dat + with self._lock: + if not self._connection: + e = socket.error(2) + e.errno = 5 + raise e + + buf = bytes(bufsiz) + n = bssl.SSL_read(self._connection, buf, bufsiz) + if n <= 0: + errno = bssl.SSL_get_error(self._connection, n) + self._context.logger.warn("recv n:%d errno: %d ip:%s", n, errno, self.ip_str) + e = socket.error(2) + e.errno = errno + raise e + + dat = buf[:n] + self._context.logger.debug("recv %d", n) + return dat def recv_into(self, buf, nbytes=None): - if not nbytes: - nbytes = len(buf) - - b = ffi.from_buffer(buf) - n = bssl.SSL_read(self._connection, b, nbytes) - if n <= 0: - errno = bssl.SSL_get_error(self._connection, n) - self._context.logger.warn("recv_into errno: %d ip:%s", errno, self.ip_str) - e = socket.error(2) - e.errno = errno - raise e - - return n + with self._lock: + if not self._connection: + e = socket.error(2) + e.errno = 5 + raise e + + if not nbytes: + nbytes = len(buf) + buf_new = bytes(nbytes) + + n = bssl.SSL_read(self._connection, buf_new, nbytes) + if n <= 0: + errno = bssl.SSL_get_error(self._connection, n) + e = socket.error(2) + e.errno = errno + raise e + + buf[:n] = buf_new[:n] + return n def read(self, bufsiz, flags=0): return self.recv(bufsiz, flags) @@ -185,27 +213,29 @@ def write(self, buf, flags=0): return self.send(buf, flags) def close(self): - if self._makefile_refs < 1: + with self._lock: self.running = False if not self.socket_closed: + if self._connection: + bssl.SSL_shutdown(self._connection) - bssl.SSL_shutdown(self._connection) - bssl.SSL_free(self._connection) - self._connection = None - - self._sock = None self.socket_closed = True if self._on_close: self._on_close(self.ip_str) - else: - self._makefile_refs -= 1 + + def __del__(self): + self.close() + if self._connection: + bssl.SSL_free(self._connection) + self._connection = None + self._sock = None def settimeout(self, t): if not self.running: return if self.timeout != t: - # self._sock.settimeout(t) + self._sock.settimeout(t) self.timeout = t def makefile(self, mode='r', bufsize=-1): diff --git a/code/default/lib/tests/stress_boringssl.py b/code/default/lib/tests/stress_boringssl.py index bdd88c2e79..9440c7557c 100644 --- a/code/default/lib/tests/stress_boringssl.py +++ b/code/default/lib/tests/stress_boringssl.py @@ -31,7 +31,6 @@ logger = xlog.getLogger("stress") from front_base.openssl_wrap import SSLContext -from front_base.host_manager import HostManagerBase from front_base.connect_creator import ConnectCreator from front_base.check_ip import CheckIp @@ -61,4 +60,11 @@ def round(): front.stop() -round() +def loop(): + while True: + round() + # time.sleep(1) + + +loop() + diff --git a/code/default/lib/tests/stress_boringssl2.py b/code/default/lib/tests/stress_boringssl2.py new file mode 100644 index 0000000000..3fb9464254 --- /dev/null +++ b/code/default/lib/tests/stress_boringssl2.py @@ -0,0 +1,143 @@ + +import socket + +from boringssl import lib as bssl, ffi + + +class SSLConnection(object): + BIO_CLOSE = 1 + + def __init__(self, context, sock, ip_str=None, sni=None, on_close=None): + self._context = context + self._sock = sock + # self.ip_str = utils.to_bytes(ip_str) + self.sni = sni + self._makefile_refs = 0 + self._on_close = on_close + self.peer_cert = None + self.socket_closed = False + self.timeout = self._sock.gettimeout() or 0.1 + self.running = True + self._connection = None + self.wrap() + + def wrap(self): + fn = self._sock.fileno() + bio = bssl.BIO_new_socket(fn, self.BIO_CLOSE) + + self._connection = bssl.SSL_new(self._context.ctx) + + bssl.SSL_set_tlsext_host_name(self._connection, self.sni) + + bssl.SSL_set_bio(self._connection, bio, bio) + + if self._context.enable_h2: + proto = b"h2" + setting = b"h2" + ret = bssl.SSL_add_application_settings(self._connection, + proto, len(proto), + setting, len(setting)) + # print(ret) + + ret = bssl.SSL_connect(self._connection) + + def send(self, data): + bssl.SSL_write(self._connection, data, len(data)) + + def recv(self, size): + buf = bytes(size) + n = bssl.SSL_read(self._connection, buf, size) + if n <= 0: + return None + + dat = buf[:n] + return dat + + def close(self): + print("close") + bssl.SSL_shutdown(self._connection) + bssl.SSL_free(self._connection) + self._connection = None + + def __del__(self): + self.close() + + +class SSLContext(object): + def __init__(self, enable_h2=True): + method = bssl.TLS_method() + self.ctx = bssl.SSL_CTX_new(method) + self.enable_h2 = enable_h2 + bssl.SSL_CTX_set_grease_enabled(self.ctx, 1) + + cmd = b"ALL:!aPSK:!ECDSA+SHA1:!3DES" + bssl.SSL_CTX_set_cipher_list(self.ctx, cmd) + + if enable_h2: + alpn = b"" + for proto in [b"h2", b"http/1.1"]: + proto_len = len(proto) + alpn += proto_len.to_bytes(1, 'big') + proto + bssl.SSL_CTX_set_alpn_protos(self.ctx, alpn, len(alpn)) + bssl.SSL_CTX_enable_ocsp_stapling(self.ctx) + bssl.SSL_CTX_enable_signed_cert_timestamps(self.ctx) + + #SSL_SIGN_ECDSA_SECP256R1_SHA256, SSL_SIGN_RSA_PSS_RSAE_SHA256, + #SSL_SIGN_RSA_PKCS1_SHA256, SSL_SIGN_ECDSA_SECP384R1_SHA384, + #SSL_SIGN_RSA_PSS_RSAE_SHA384, SSL_SIGN_RSA_PKCS1_SHA384, + #SSL_SIGN_RSA_PSS_RSAE_SHA512, SSL_SIGN_RSA_PKCS1_SHA512, + algs = [0x0403, 0x0804, 0x0401, 0x0503, 0x0805, 0x0501, 0x0806, 0x0601] + algs_buf = ffi.new("uint16_t[%s]" % (len(algs))) + i = 0 + for alg in algs: + algs_buf[i] = alg + i += 1 + cdata_ptr = ffi.cast("uint16_t *", algs_buf) + bssl.SSL_CTX_set_verify_algorithm_prefs(self.ctx, cdata_ptr, len(algs)) + + bssl.SSL_CTX_set_min_proto_version(self.ctx, 0x0303) + + bssl.SetCompression(self.ctx) + + +def round(): + ip = "127.0.0.1" + host = "agentnobody.pics" + sock = socket.socket(socket.AF_INET if ':' not in ip else socket.AF_INET6) + sock.settimeout(3) + try: + sock.connect((ip, 443)) + except Exception as e: + print("connnect fail") + return + + context = SSLContext(enable_h2=False) + connection = SSLConnection(context, sock, sni=host.encode("utf-8")) + sock.setblocking(True) + # connection.settimeout(10) + print("connected") + + connection.send(b"GET / HTTP/1.0\r\n") + connection.send(b"Host: %s\r\n" % host.encode("utf-8")) + connection.send(b"User-Agent: curl\r\n") + connection.send(b"Accept: */*\r\n") + connection.send(b"\r\n") + + while True: + try: + r = connection.recv(10240) + if not r: + break + print(r.decode("utf-8")) + except socket.timeout: + break + except Exception as e: + break + + +def loop(): + while True: + round() + + +loop() diff --git a/code/default/version.txt b/code/default/version.txt index f54e21527a..99be846e62 100644 --- a/code/default/version.txt +++ b/code/default/version.txt @@ -1 +1 @@ -4.13.4 \ No newline at end of file +4.13.5 \ No newline at end of file diff --git a/code/default/x_tunnel/local/tls_relay_front/config.py b/code/default/x_tunnel/local/tls_relay_front/config.py index 97a182adea..6aca6cdf3c 100644 --- a/code/default/x_tunnel/local/tls_relay_front/config.py +++ b/code/default/x_tunnel/local/tls_relay_front/config.py @@ -11,13 +11,14 @@ def __init__(self, fn): self.set_var("front_continue_fail_block", 10) # https_dispatcher - self.set_var("dispather_min_idle_workers", 0) + self.set_var("dispather_min_idle_workers", 1) self.set_var("dispather_work_min_idle_time", 0) self.set_var("dispather_work_max_score", 20000) + self.set_var("dispather_min_workers", 1) self.set_var("dispather_max_workers", 60) # connect_manager - self.set_var("https_connection_pool_min", 0) + self.set_var("https_connection_pool_min", 1) self.set_var("max_links_per_ip", 3) # connect_creator