diff --git a/shadowsocks/asyncdns.py b/shadowsocks/asyncdns.py index c5fc99d..e64895c 100644 --- a/shadowsocks/asyncdns.py +++ b/shadowsocks/asyncdns.py @@ -24,6 +24,13 @@ import struct import re import logging +if __name__ == '__main__': + import sys + import inspect + file_path = os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe()))) + os.chdir(file_path) + sys.path.insert(0, os.path.join(file_path, '../')) + from shadowsocks import common, lru_cache, eventloop, shell @@ -71,6 +78,17 @@ QTYPE_CNAME = 5 QTYPE_NS = 2 QCLASS_IN = 1 +def detect_ipv6_supprot(): + if 'has_ipv6' in dir(socket): + s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + try: + s.connect(('ipv6.google.com', 0)) + return True + except: + pass + return False + +IPV6_CONNECTION_SUPPORT = detect_ipv6_supprot() def build_address(address): address = address.strip(b'.') @@ -338,17 +356,17 @@ class DNSResolver(object): answer[2] == QCLASS_IN: ip = answer[0] break - if not ip and self._hostname_status.get(hostname, STATUS_IPV6) \ - == STATUS_IPV4: - self._hostname_status[hostname] = STATUS_IPV6 - self._send_req(hostname, QTYPE_AAAA) + if not ip and self._hostname_status.get(hostname, STATUS_IPV4) \ + == STATUS_IPV6: + self._hostname_status[hostname] = STATUS_IPV4 + self._send_req(hostname, QTYPE_A) else: if ip: self._cache[hostname] = ip self._call_callback(hostname, ip) - elif self._hostname_status.get(hostname, None) == STATUS_IPV6: + elif self._hostname_status.get(hostname, None) == STATUS_IPV4: for question in response.questions: - if question[1] == QTYPE_AAAA: + if question[1] == QTYPE_A: self._call_callback(hostname, None) break @@ -414,14 +432,21 @@ class DNSResolver(object): return arr = self._hostname_to_cb.get(hostname, None) if not arr: - self._hostname_status[hostname] = STATUS_IPV4 - self._send_req(hostname, QTYPE_A) + if IPV6_CONNECTION_SUPPORT: + self._hostname_status[hostname] = STATUS_IPV6 + self._send_req(hostname, QTYPE_AAAA) + else: + self._hostname_status[hostname] = STATUS_IPV4 + self._send_req(hostname, QTYPE_A) self._hostname_to_cb[hostname] = [callback] self._cb_to_hostname[callback] = hostname else: arr.append(callback) # TODO send again only if waited too long - self._send_req(hostname, QTYPE_A) + if IPV6_CONNECTION_SUPPORT: + self._send_req(hostname, QTYPE_AAAA) + else: + self._send_req(hostname, QTYPE_A) def close(self): if self._sock: @@ -479,3 +504,4 @@ def test(): if __name__ == '__main__': test() + diff --git a/shadowsocks/common.py b/shadowsocks/common.py index 2b11b9f..7f306ea 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -54,6 +54,16 @@ def to_str(s): return s.decode('utf-8') return s +def int32(x): + if x > 0xFFFFFFFF or x < 0: + x &= 0xFFFFFFFF + if x > 0x7FFFFFFF: + x = int(0x100000000 - x) + if x < 0x80000000: + return -x + else: + return -2147483648 + return x def inet_ntop(family, ipstr): if family == socket.AF_INET: diff --git a/shadowsocks/encrypt.py b/shadowsocks/encrypt.py index 834b18c..d3b2752 100644 --- a/shadowsocks/encrypt.py +++ b/shadowsocks/encrypt.py @@ -77,6 +77,7 @@ class Encryptor(object): self.iv = None self.iv_sent = False self.cipher_iv = b'' + self.iv_buf = b'' self.decipher = None method = method.lower() self._method_info = self.get_method_info(method) @@ -122,16 +123,21 @@ class Encryptor(object): def decrypt(self, buf): if len(buf) == 0: return buf - if self.decipher is None: - decipher_iv_len = self._method_info[1] - decipher_iv = buf[:decipher_iv_len] + if self.decipher is not None: #optimize + return self.decipher.update(buf) + + decipher_iv_len = self._method_info[1] + if len(self.iv_buf) <= decipher_iv_len: + self.iv_buf += buf + if len(self.iv_buf) > decipher_iv_len: + decipher_iv = self.iv_buf[:decipher_iv_len] self.decipher = self.get_cipher(self.key, self.method, 0, iv=decipher_iv) - buf = buf[decipher_iv_len:] - if len(buf) == 0: - return buf - return self.decipher.update(buf) - + buf = self.iv_buf[decipher_iv_len:] + del self.iv_buf + return self.decipher.update(buf) + else: + return b'' def encrypt_all(password, method, op, data): result = [] diff --git a/shadowsocks/local.py b/shadowsocks/local.py index 4255a2e..c6df20c 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -23,7 +23,12 @@ import os import logging import signal -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) +if __name__ == '__main__': + import inspect + file_path = os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe()))) + os.chdir(file_path) + sys.path.insert(0, os.path.join(file_path, '../')) + from shadowsocks import shell, daemon, eventloop, tcprelay, udprelay, asyncdns diff --git a/shadowsocks/manager.py b/shadowsocks/manager.py index c069d4d..bfabd7f 100644 --- a/shadowsocks/manager.py +++ b/shadowsocks/manager.py @@ -168,7 +168,7 @@ class Manager(object): send_data(r) r.clear() i = 0 - if len(r) > 0: + if len(r) > 0 : send_data(r) self._statistics.clear() diff --git a/shadowsocks/obfsplugin/http_simple.py b/shadowsocks/obfsplugin/http_simple.py index 75e9294..efd7f2a 100644 --- a/shadowsocks/obfsplugin/http_simple.py +++ b/shadowsocks/obfsplugin/http_simple.py @@ -22,8 +22,10 @@ import sys import hashlib import logging import binascii +import struct import base64 import datetime +import random from shadowsocks import common from shadowsocks.obfsplugin import plain @@ -66,14 +68,53 @@ class http_simple(plain.plain): self.host = None self.port = 0 self.recv_buffer = b'' + self.user_agent = [b"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0", + b"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/44.0", + b"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36", + b"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/27.0.1453.93 Chrome/27.0.1453.93 Safari/537.36", + b"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0", + b"Mozilla/5.0 (compatible; WOW64; MSIE 10.0; Windows NT 6.2)", + b"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", + b"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)", + b"Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko", + b"Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/BuildID) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36", + b"Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3", + b"Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3"] + + def encode_head(self, buf): + ret = b'' + for ch in buf: + ret += '%' + binascii.hexlify(ch) + return ret def client_encode(self, buf): - # TODO - return buf + if self.has_sent_header: + return buf + if len(buf) > 64: + headlen = random.randint(1, 64) + else: + headlen = len(buf) + headdata = buf[:headlen] + buf = buf[headlen:] + port = b'' + if self.server_info.port != 80: + port = b':' + common.to_bytes(str(self.server_info.port)) + http_head = b"GET /" + self.encode_head(headdata) + b" HTTP/1.1\r\n" + http_head += b"Host: " + (self.server_info.param or self.server_info.host) + port + b"\r\n" + http_head += b"User-Agent: " + random.choice(self.user_agent) + b"\r\n" + http_head += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nDNT: 1\r\nConnection: keep-alive\r\n\r\n" + self.has_sent_header = True + return http_head + buf def client_decode(self, buf): - # TODO - return (buf, False) + if self.has_recv_header: + return (buf, False) + pos = buf.find(b'\r\n\r\n') + if pos >= 0: + self.has_recv_header = True + return (buf[pos + 4:], False) + else: + return (b'', False) def server_encode(self, buf): if self.has_sent_header: @@ -110,17 +151,19 @@ class http_simple(plain.plain): if self.has_recv_header: return (buf, True, False) - buf = self.recv_buffer + buf + self.recv_buffer += buf + buf = self.recv_buffer if len(buf) > 10: if match_begin(buf, b'GET /') or match_begin(buf, b'POST /'): if len(buf) > 65536: self.recv_buffer = None + logging.warn('http_simple: over size') return self.not_match_return(buf) else: #not http header, run on original protocol self.recv_buffer = None + logging.debug('http_simple: not match begin') return self.not_match_return(buf) else: - self.recv_buffer = buf return (b'', True, False) datas = buf.split(b'\r\n\r\n', 1) @@ -130,29 +173,47 @@ class http_simple(plain.plain): if len(ret_buf) >= 15: self.has_recv_header = True return (ret_buf, True, False) - self.recv_buffer = buf return (b'', True, False) else: - self.recv_buffer = buf return (b'', True, False) - return self.not_match_return(buf) class http2_simple(plain.plain): def __init__(self, method): self.method = method self.has_sent_header = False self.has_recv_header = False + self.raw_trans_sent = False self.host = None self.port = 0 self.recv_buffer = b'' def client_encode(self, buf): - # TODO - return buf + if self.raw_trans_sent: + return buf + self.send_buffer += buf + if not self.has_sent_header: + self.has_sent_header = True + http_head = b"GET / HTTP/1.1\r\n" + http_head += b"Host: " + (self.server_info.param or self.server_info.host) + port + b"\r\n" + http_head += b"Connection: Upgrade, HTTP2-Settings\r\nUpgrade: h2c\r\n" + http_head += b"HTTP2-Settings: " + base64.urlsafe_b64encode(buf) + b"\r\n" + return http_head + b"\r\n" + if self.has_recv_header: + ret = self.send_buffer + self.send_buffer = b'' + self.raw_trans_sent = True + return ret + return b'' def client_decode(self, buf): - # TODO - return (buf, False) + if self.has_recv_header: + return (buf, False) + pos = buf.find(b'\r\n\r\n') + if pos >= 0: + self.has_recv_header = True + return (buf[pos + 4:], False) + else: + return (b'', False) def server_encode(self, buf): if self.has_sent_header: @@ -173,7 +234,8 @@ class http2_simple(plain.plain): if self.has_recv_header: return (buf, True, False) - buf = self.recv_buffer + buf + self.recv_buffer += buf + buf = self.recv_buffer if len(buf) > 10: if match_begin(buf, b'GET /'): pass @@ -181,7 +243,6 @@ class http2_simple(plain.plain): self.recv_buffer = None return self.not_match_return(buf) else: - self.recv_buffer = buf return (b'', True, False) datas = buf.split(b'\r\n\r\n', 1) @@ -193,10 +254,8 @@ class http2_simple(plain.plain): ret_buf += datas[1] self.has_recv_header = True return (ret_buf, True, False) - self.recv_buffer = buf return (b'', True, False) else: - self.recv_buffer = buf return (b'', True, False) return self.not_match_return(buf) @@ -205,13 +264,30 @@ class tls_simple(plain.plain): self.method = method self.has_sent_header = False self.has_recv_header = False + self.raw_trans_sent = False def client_encode(self, buf): - return buf + if self.raw_trans_sent: + return buf + self.send_buffer += buf + if not self.has_sent_header: + self.has_sent_header = True + data = b"\x03\x03" + os.urandom(32) + binascii.unhexlify(b"000016c02bc02fc00ac009c013c01400330039002f0035000a0100006fff01000100000a00080006001700180019000b0002010000230000337400000010002900270568322d31360568322d31350568322d313402683208737064792f332e3108687474702f312e31000500050100000000000d001600140401050106010201040305030603020304020202") + data = b"\x01\x00" + struct.pack('>H', len(data)) + data + data = b"\x16\x03\x01" + struct.pack('>H', len(data)) + data + return data + if self.has_recv_header: + ret = self.send_buffer + self.send_buffer = b'' + self.raw_trans_sent = True + return ret + return b'' def client_decode(self, buf): - # (buffer_to_recv, is_need_to_encode_and_send_back) - return (buf, False) + if self.has_recv_header: + return (buf, False) + self.has_recv_header = True + return (b'', True) def server_encode(self, buf): if self.has_sent_header: @@ -239,13 +315,31 @@ class random_head(plain.plain): self.method = method self.has_sent_header = False self.has_recv_header = False + self.raw_trans_sent = False + self.raw_trans_recv = False + self.send_buffer = b'' def client_encode(self, buf): - return buf + if self.raw_trans_sent: + return buf + self.send_buffer += buf + if not self.has_sent_header: + self.has_sent_header = True + data = os.urandom(common.ord(os.urandom(1)[0]) % 96 + 4) + crc = (0xffffffff - binascii.crc32(data)) & 0xffffffff + return data + struct.pack(' self.front + 0x4000: + logging.warn('auth_simple: wrong id') + return False + if connection_id in self.alloc: + logging.warn('auth_simple: duplicate id 2') + return False + if self.back <= connection_id: + self.back = connection_id + 1 + self.alloc[connection_id] = 1 + while (self.front in self.alloc) or self.front + 0x1000 < self.back: + if self.front in self.alloc: + del self.alloc[self.front] + self.front += 1 + return True + +class obfs_auth_data(object): + def __init__(self): + self.sub_obfs = None + self.client_id = {} + self.startup_time = int(time.time() - 30) & 0xFFFFFFFF + self.local_client_id = b'' + self.connection_id = 0 + + def update(self, client_id, connection_id): + if client_id in self.client_id: + self.client_id[client_id].update() + + def insert(self, client_id, connection_id): + max_client = 16 + if client_id not in self.client_id or not self.client_id[client_id].enable: + active = 0 + for c_id in self.client_id: + if self.client_id[c_id].is_active(): + active += 1 + if active >= max_client: + logging.warn('auth_simple: max active clients exceeded') + return False + + if len(self.client_id) < max_client: + if client_id not in self.client_id: + self.client_id[client_id] = client_queue(connection_id) + else: + self.client_id[client_id].re_enable(connection_id) + return self.client_id[client_id].insert(connection_id) + keys = self.client_id.keys() + random.shuffle(keys) + for c_id in keys: + if not self.client_id[c_id].is_active() and self.client_id[c_id].enable: + if len(self.client_id) >= 256: + del self.client_id[c_id] + else: + self.client_id[c_id].enable = False + if client_id not in self.client_id: + self.client_id[client_id] = client_queue(connection_id) + else: + self.client_id[client_id].re_enable(connection_id) + return self.client_id[client_id].insert(connection_id) + logging.warn('auth_simple: no inactive client [assert]') + return False + else: + return self.client_id[client_id].insert(connection_id) + +class auth_simple(verify_base): + def __init__(self, method): + super(auth_simple, self).__init__(method) + self.recv_buf = b'' + self.unit_len = 8100 + self.decrypt_packet_num = 0 + self.raw_trans = False + self.has_sent_header = False + self.has_recv_header = False + self.client_id = 0 + self.connection_id = 0 + + def init_data(self): + return obfs_auth_data() + + def pack_data(self, buf): + if len(buf) == 0: + return b'' + rnd_data = os.urandom(common.ord(os.urandom(1)[0]) % 16) + data = common.chr(len(rnd_data) + 1) + rnd_data + buf + data = struct.pack('>H', len(data) + 6) + data + crc = (0xffffffff - binascii.crc32(data)) & 0xffffffff + data += struct.pack(' 0xFF000000: + self.server_info.data.local_client_id = b'' + if not self.server_info.data.local_client_id: + self.server_info.data.local_client_id = os.urandom(4) + logging.debug("local_client_id %s" % (binascii.hexlify(self.server_info.data.local_client_id),)) + self.server_info.data.connection_id = struct.unpack(' self.unit_len: + ret += self.pack_data(buf[:self.unit_len]) + buf = buf[self.unit_len:] + ret += self.pack_data(buf) + return ret + + def client_post_decrypt(self, buf): + if self.raw_trans: + return buf + self.recv_buf += buf + out_buf = b'' + while len(self.recv_buf) > 2: + length = struct.unpack('>H', self.recv_buf[:2])[0] + if length >= 8192: + self.raw_trans = True + self.recv_buf = b'' + if self.decrypt_packet_num == 0: + return None + else: + raise Exception('server_post_decrype data error') + if length > len(self.recv_buf): + break + + if (binascii.crc32(self.recv_buf[:length]) & 0xffffffff) != 0xffffffff: + self.raw_trans = True + self.recv_buf = b'' + if self.decrypt_packet_num == 0: + return None + else: + raise Exception('server_post_decrype data uncorrect CRC32') + + pos = common.ord(self.recv_buf[2]) + 2 + out_buf += self.recv_buf[pos:length - 4] + self.recv_buf = self.recv_buf[length:] + + if out_buf: + self.decrypt_packet_num += 1 + return out_buf + + def server_pre_encrypt(self, buf): + ret = b'' + while len(buf) > self.unit_len: + ret += self.pack_data(buf[:self.unit_len]) + buf = buf[self.unit_len:] + ret += self.pack_data(buf) + return ret + + def server_post_decrypt(self, buf): + if self.raw_trans: + return buf + self.recv_buf += buf + out_buf = b'' + while len(self.recv_buf) > 2: + length = struct.unpack('>H', self.recv_buf[:2])[0] + if length >= 8192: + self.raw_trans = True + self.recv_buf = b'' + if self.decrypt_packet_num == 0: + logging.info('auth_simple: over size') + return b'E' + else: + raise Exception('server_post_decrype data error') + if length > len(self.recv_buf): + break + + if (binascii.crc32(self.recv_buf[:length]) & 0xffffffff) != 0xffffffff: + logging.info('auth_simple: crc32 error, data %s' % (binascii.hexlify(self.recv_buf[:length]),)) + self.raw_trans = True + self.recv_buf = b'' + if self.decrypt_packet_num == 0: + return b'E' + else: + raise Exception('server_post_decrype data uncorrect CRC32') + + pos = common.ord(self.recv_buf[2]) + 2 + out_buf += self.recv_buf[pos:length - 4] + if not self.has_recv_header: + if len(out_buf) < 12: + self.raw_trans = True + self.recv_buf = b'' + logging.info('auth_simple: too short') + return b'E' + utc_time = struct.unpack(' 60 * 3 or common.int32(utc_time - self.server_info.data.startup_time) < 0: + self.raw_trans = True + self.recv_buf = b'' + logging.info('auth_simple: wrong timestamp, time_dif %d, data %s' % (time_dif, binascii.hexlify(out_buf),)) + return b'E' + elif self.server_info.data.insert(client_id, connection_id): + self.has_recv_header = True + out_buf = out_buf[12:] + self.client_id = client_id + self.connection_id = connection_id + else: + self.raw_trans = True + self.recv_buf = b'' + logging.info('auth_simple: auth fail, data %s' % (binascii.hexlify(out_buf),)) + return b'E' + self.recv_buf = self.recv_buf[length:] + + if out_buf: + self.server_info.data.update(self.client_id, self.connection_id) + self.decrypt_packet_num += 1 + return out_buf + diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 006a372..68d187f 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -23,7 +23,12 @@ import os import logging import signal -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) +if __name__ == '__main__': + import inspect + file_path = os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe()))) + os.chdir(file_path) + sys.path.insert(0, os.path.join(file_path, '../')) + from shadowsocks import shell, daemon, eventloop, tcprelay, udprelay, \ asyncdns, manager @@ -68,7 +73,8 @@ def main(): obfs = config["obfs"] a_config = config.copy() ipv6_ok = False - logging.info("server start with password [%s] obfs [%s] method [%s]" % (password, obfs, a_config['method'])) + logging.info("server start with password [%s] method [%s] obfs [%s] obfs_param [%s]" % + (password, a_config['method'], obfs, a_config['obfs_param'])) if 'server_ipv6' in a_config: try: if len(a_config['server_ipv6']) > 2 and a_config['server_ipv6'][0] == "[" and a_config['server_ipv6'][-1] == "]": @@ -77,7 +83,7 @@ def main(): a_config['password'] = password a_config['obfs'] = obfs a_config['server'] = a_config['server_ipv6'] - logging.info("starting server at %s:%d" % + logging.info("starting server at [%s]:%d" % (a_config['server'], int(port))) tcp_servers.append(tcprelay.TCPRelay(a_config, dns_resolver, False)) udp_servers.append(udprelay.UDPRelay(a_config, dns_resolver, False)) diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 2bb973a..cce6df5 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -30,11 +30,8 @@ import random from shadowsocks import encrypt, obfs, eventloop, shell, common from shadowsocks.common import pre_parse_header, parse_header -# set it 'False' to use both new protocol and the original shadowsocks protocal -# set it 'True' to use new protocol ONLY, to avoid GFW detecting -FORCE_NEW_PROTOCOL = False # set it 'True' if run as a local client and connect to a server which support new protocol -CLIENT_NEW_PROTOCOL = False +CLIENT_NEW_PROTOCOL = False #deprecated # we clear at most TIMEOUTS_CLEAN_SIZE timeouts each time TIMEOUTS_CLEAN_SIZE = 512 @@ -118,8 +115,6 @@ class TCPRelayHandler(object): config['method']) self._encrypt_correct = True self._obfs = obfs.obfs(config['obfs']) - if server.obfs_data is None: - server.obfs_data = self._obfs.init_data() server_info = obfs.server_info(server.obfs_data) server_info.host = config['server'] server_info.port = server._listen_port @@ -268,8 +263,8 @@ class TCPRelayHandler(object): if sock == self._local_sock and self._encrypt_correct: obfs_encode = self._obfs.server_encode(data) data = obfs_encode - l = len(data) - if l > 0: + if data: + l = len(data) s = sock.send(data) if s < l: data = data[s:] @@ -310,7 +305,7 @@ class TCPRelayHandler(object): def _get_redirect_host(self, client_address, ogn_data): # test - host_list = [(b"www.bing.com", 80), (b"www.microsoft.com", 80), (b"www.baidu.com", 443), (b"www.qq.com", 80), (b"www.csdn.net", 80), (b"1.2.3.4", 1000)] + host_list = [(b"www.bing.com", 80), (b"www.microsoft.com", 80), (b"cloudfront.com", 80), (b"cloudflare.com", 80), (b"1.2.3.4", 1000), (b"0.0.0.0", 0)] hash_code = binascii.crc32(ogn_data) addrs = socket.getaddrinfo(client_address[0], client_address[1], 0, socket.SOCK_STREAM, socket.SOL_TCP) af, socktype, proto, canonname, sa = addrs[0] @@ -338,7 +333,8 @@ class TCPRelayHandler(object): data = self._obfs.client_pre_encrypt(data) data = self._encryptor.encrypt(data) data = self._obfs.client_encode(data) - self._data_to_write_to_remote.append(data) + if data: + self._data_to_write_to_remote.append(data) if self._is_local and not self._fastopen_connected and \ self._config['fast_open']: # for sslocal and fastopen, we basically wait for data and use @@ -404,8 +400,6 @@ class TCPRelayHandler(object): if self._is_local: header_result = parse_header(data) else: - if data is None or FORCE_NEW_PROTOCOL and common.ord(data[0]) != 0x88 and (~common.ord(data[0]) & 0xff) != 0x88: - data = self._handel_protocol_error(self._client_address, ogn_data) data = pre_parse_header(data) if data is None: data = self._handel_protocol_error(self._client_address, ogn_data) @@ -436,7 +430,9 @@ class TCPRelayHandler(object): data += struct.pack('