diff --git a/shadowsocks/obfs.py b/shadowsocks/obfs.py index 68a92f1..d89e534 100644 --- a/shadowsocks/obfs.py +++ b/shadowsocks/obfs.py @@ -53,6 +53,9 @@ class obfs(object): def set_server_info(self, server_info): return self.obfs.set_server_info(server_info) + def get_server_info(self): + return self.obfs.get_server_info() + def get_method_info(self, method): method = method.lower() m = method_supported.get(method) @@ -62,6 +65,9 @@ class obfs(object): m = self._method_info return m[0](method) + def get_overhead(self, direction): + return self.obfs.get_overhead(direction) + def client_pre_encrypt(self, buf): return self.obfs.client_pre_encrypt(buf) diff --git a/shadowsocks/obfsplugin/auth.py b/shadowsocks/obfsplugin/auth.py index 98a958d..7d8fe65 100755 --- a/shadowsocks/obfsplugin/auth.py +++ b/shadowsocks/obfsplugin/auth.py @@ -82,10 +82,14 @@ class auth_base(plain.plain): super(auth_base, self).__init__(method) self.method = method self.no_compatible_method = '' + self.overhead = 7 def init_data(self): return '' + def get_overhead(self, direction): # direction: true for c->s false for s->c + return self.overhead + def set_server_info(self, server_info): self.server_info = server_info @@ -103,6 +107,7 @@ class auth_base(plain.plain): def not_match_return(self, buf): self.raw_trans = True + self.overhead = 0 if self.method == self.no_compatible_method: return (b'E'*2048, False) return (buf, False) @@ -871,6 +876,9 @@ class auth_aes128(auth_base): def init_data(self): return obfs_auth_v2_data() + def get_overhead(self, direction): # direction: true for c->s false for s->c + return 9 + def set_server_info(self, server_info): self.server_info = server_info try: @@ -1174,10 +1182,14 @@ class auth_aes128_sha1(auth_base): self.user_id = None self.user_key = None self.last_rnd_len = 0 + self.overhead = 9 def init_data(self): return obfs_auth_mu_data() + def get_overhead(self, direction): # direction: true for c->s false for s->c + return self.overhead + def set_server_info(self, server_info): self.server_info = server_info try: @@ -1198,9 +1210,15 @@ class auth_aes128_sha1(auth_base): return int(v * max_val) def rnd_data_len(self, buf_size, full_buf_size): + if full_buf_size >= self.server_info.buffer_size: + return 0 rev_len = self.server_info.tcp_mss - buf_size - 9 - if rev_len <= 0 or self.last_rnd_len >= self.server_info.buffer_size or full_buf_size >= self.server_info.buffer_size: + if rev_len == 0: return 0 + if rev_len < 0: + if rev_len > -self.server_info.tcp_mss: + return self.trapezoid_random_int(rev_len + self.server_info.tcp_mss, -0.3) + return common.ord(os.urandom(1)[0]) % 32 if buf_size > 900: return struct.unpack('>H', os.urandom(2))[0] % rev_len return self.trapezoid_random_int(rev_len, -0.3) diff --git a/shadowsocks/obfsplugin/obfs_tls.py b/shadowsocks/obfsplugin/obfs_tls.py index 7d35f7c..fee3c81 100644 --- a/shadowsocks/obfsplugin/obfs_tls.py +++ b/shadowsocks/obfsplugin/obfs_tls.py @@ -65,10 +65,14 @@ class tls_ticket_auth(plain.plain): self.client_id = b'' self.max_time_dif = 60 * 60 * 24 # time dif (second) setting self.tls_version = b'\x03\x03' + self.overhead = 5 def init_data(self): return obfs_auth_data() + def get_overhead(self, direction): # direction: true for c->s false for s->c + return self.overhead + def sni(self, url): url = common.to_bytes(url) data = b"\x00" + struct.pack('>H', len(url)) + url @@ -184,6 +188,7 @@ class tls_ticket_auth(plain.plain): def decode_error_return(self, buf): self.handshake_status = -1 + self.overhead = 0 if self.method == 'tls1.2_ticket_auth': return (b'E'*2048, False, False) return (buf, True, False) diff --git a/shadowsocks/obfsplugin/plain.py b/shadowsocks/obfsplugin/plain.py index 1d4fbab..2b943ac 100644 --- a/shadowsocks/obfsplugin/plain.py +++ b/shadowsocks/obfsplugin/plain.py @@ -40,6 +40,9 @@ class plain(object): def init_data(self): return b'' + def get_overhead(self, direction): # direction: true for c->s false for s->c + return 0 + def get_server_info(self): return self.server_info diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 2d792ba..ce9db08 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -91,6 +91,8 @@ WAIT_STATUS_READING = 1 WAIT_STATUS_WRITING = 2 WAIT_STATUS_READWRITING = WAIT_STATUS_READING | WAIT_STATUS_WRITING +NETWORK_MTU = 1492 +TCP_MSS = NETWORK_MTU - 40 BUF_SIZE = 32 * 1024 UDP_MAX_BUF_SIZE = 65536 @@ -137,6 +139,7 @@ class TCPRelayHandler(object): self._accept_address = local_sock.getsockname()[:2] self._user = None self._user_id = server._listen_port + self._tcp_mss = TCP_MSS # TCP Relay works as either sslocal or ssserver # if is_local, this is sslocal @@ -151,6 +154,16 @@ class TCPRelayHandler(object): return self._encrypt_correct = True self._obfs = obfs.obfs(config['obfs']) + self._protocol = obfs.obfs(config['protocol']) + self._overhead = self._obfs.get_overhead(self._is_local) + self._protocol.get_overhead(self._is_local) + self._recv_buffer_size = BUF_SIZE - self._overhead + + try: + self._tcp_mss = local_sock.getsockopt(socket.SOL_TCP, socket.TCP_MAXSEG) + logging.debug("TCP MSS = %d" % (self._tcp_mss,)) + except: + pass + server_info = obfs.server_info(server.obfs_data) server_info.host = config['server'] server_info.port = server._listen_port @@ -165,11 +178,10 @@ class TCPRelayHandler(object): server_info.key_str = common.to_bytes(config['password']) server_info.key = self._encryptor.cipher_key server_info.head_len = 30 - server_info.tcp_mss = 1448 - server_info.buffer_size = BUF_SIZE + server_info.tcp_mss = self._tcp_mss + server_info.buffer_size = self._recv_buffer_size self._obfs.set_server_info(server_info) - self._protocol = obfs.obfs(config['protocol']) server_info = obfs.server_info(server.protocol_data) server_info.host = config['server'] server_info.port = server._listen_port @@ -184,8 +196,8 @@ class TCPRelayHandler(object): server_info.key_str = common.to_bytes(config['password']) server_info.key = self._encryptor.cipher_key server_info.head_len = 30 - server_info.tcp_mss = 1448 - server_info.buffer_size = BUF_SIZE + server_info.tcp_mss = self._tcp_mss + server_info.buffer_size = self._recv_buffer_size self._protocol.set_server_info(server_info) self._redir_list = config.get('redirect', ["*#0.0.0.0:0"]) @@ -439,18 +451,16 @@ class TCPRelayHandler(object): items_sum = common.to_str(host).rsplit('#', 1) items_match = common.to_str(items_sum[0]).rsplit(':', 1) items = common.to_str(items_sum[1]).rsplit(':', 1) - if len(items_match) > 1: - if self._server._listen_port != int(items_match[1]): - continue - match_port = 0 if len(items_match) > 1: if items_match[1] != "*": try: - match_port = int(items_match[1]) + if self._server._listen_port != int(items_match[1]) and int(items_match[1]) != 0: + continue except: pass - if items_match[0] != "*" and common.match_regex(items_match[0], ogn_data) == False and \ - not (match_port == self._server._listen_port or match_port == 0): + + if items_match[0] != "*" and common.match_regex( + items_match[0], ogn_data) == False: continue if len(items) > 1: try: @@ -573,6 +583,12 @@ class TCPRelayHandler(object): if header_result is None: data = self._handel_protocol_error(self._client_address, ogn_data) header_result = parse_header(data) + self._overhead = self._obfs.get_overhead(self._is_local) + self._protocol.get_overhead(self._is_local) + self._recv_buffer_size = BUF_SIZE - self._overhead + server_info = self._obfs.get_server_info() + server_info.buffer_size = self._recv_buffer_size + server_info = self._protocol.get_server_info() + server_info.buffer_size = self._recv_buffer_size connecttype, remote_addr, remote_port, header_length = header_result common.connect_log('%s connecting %s:%d via port %d by UID %d' % ((connecttype == 0) and 'TCP' or 'UDP', @@ -743,15 +759,30 @@ class TCPRelayHandler(object): logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1])) self.destroy() + def _get_read_size(self, sock, recv_buffer_size): + if self._overhead == 0: + return buffer_size + buffer_size = len(sock.recv(recv_buffer_size, socket.MSG_PEEK)) + if buffer_size == recv_buffer_size: + return buffer_size + s = buffer_size % self._tcp_mss + self._overhead + if s > self._tcp_mss: + return buffer_size + s - self._tcp_mss + return buffer_size + def _on_local_read(self): # handle all local read events and dispatch them to methods for # each stage if not self._local_sock: return is_local = self._is_local + if is_local: + recv_buffer_size = self._get_read_size(self._local_sock, self._recv_buffer_size) + else: + recv_buffer_size = BUF_SIZE data = None try: - data = self._local_sock.recv(BUF_SIZE) + data = self._local_sock.recv(recv_buffer_size) except (OSError, IOError) as e: if eventloop.errno_from_exception(e) in \ (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK): @@ -848,7 +879,11 @@ class TCPRelayHandler(object): data = struct.pack('>H', size) + data #logging.info('UDP over TCP recvfrom %s:%d %d bytes to %s:%d' % (addr[0], addr[1], len(data), self._client_address[0], self._client_address[1])) else: - data = self._remote_sock.recv(BUF_SIZE) + if self._is_local: + recv_buffer_size = BUF_SIZE + else: + recv_buffer_size = self._get_read_size(self._remote_sock, self._recv_buffer_size) + data = self._remote_sock.recv(recv_buffer_size) except (OSError, IOError) as e: if eventloop.errno_from_exception(e) in \ (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK, 10035): #errno.WSAEWOULDBLOCK diff --git a/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index 19f4728..1b4aae9 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -75,9 +75,6 @@ import threading from shadowsocks import encrypt, obfs, eventloop, lru_cache, common, shell from shadowsocks.common import pre_parse_header, parse_header, pack_addr -# we clear at most TIMEOUTS_CLEAN_SIZE timeouts each time -TIMEOUTS_CLEAN_SIZE = 512 - # for each handler, we have 2 stream directions: # upstream: from client to server direction # read local and write to remote @@ -927,7 +924,7 @@ class UDPRelay(object): server_info.key_str = common.to_bytes(config['password']) server_info.key = encrypt.encrypt_key(self._password, self._method) server_info.head_len = 30 - server_info.tcp_mss = 1440 + server_info.tcp_mss = 1452 server_info.buffer_size = BUF_SIZE self._protocol.set_server_info(server_info)