Browse Source

auto adjust TCP_MAXSEG

dev
破娃酱 8 years ago
parent
commit
d82c931f8e
  1. 6
      shadowsocks/obfs.py
  2. 20
      shadowsocks/obfsplugin/auth.py
  3. 5
      shadowsocks/obfsplugin/obfs_tls.py
  4. 3
      shadowsocks/obfsplugin/plain.py
  5. 63
      shadowsocks/tcprelay.py
  6. 5
      shadowsocks/udprelay.py

6
shadowsocks/obfs.py

@ -53,6 +53,9 @@ class obfs(object):
def set_server_info(self, server_info): def set_server_info(self, server_info):
return self.obfs.set_server_info(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): def get_method_info(self, method):
method = method.lower() method = method.lower()
m = method_supported.get(method) m = method_supported.get(method)
@ -62,6 +65,9 @@ class obfs(object):
m = self._method_info m = self._method_info
return m[0](method) return m[0](method)
def get_overhead(self, direction):
return self.obfs.get_overhead(direction)
def client_pre_encrypt(self, buf): def client_pre_encrypt(self, buf):
return self.obfs.client_pre_encrypt(buf) return self.obfs.client_pre_encrypt(buf)

20
shadowsocks/obfsplugin/auth.py

@ -82,10 +82,14 @@ class auth_base(plain.plain):
super(auth_base, self).__init__(method) super(auth_base, self).__init__(method)
self.method = method self.method = method
self.no_compatible_method = '' self.no_compatible_method = ''
self.overhead = 7
def init_data(self): def init_data(self):
return '' 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): def set_server_info(self, server_info):
self.server_info = server_info self.server_info = server_info
@ -103,6 +107,7 @@ class auth_base(plain.plain):
def not_match_return(self, buf): def not_match_return(self, buf):
self.raw_trans = True self.raw_trans = True
self.overhead = 0
if self.method == self.no_compatible_method: if self.method == self.no_compatible_method:
return (b'E'*2048, False) return (b'E'*2048, False)
return (buf, False) return (buf, False)
@ -871,6 +876,9 @@ class auth_aes128(auth_base):
def init_data(self): def init_data(self):
return obfs_auth_v2_data() 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): def set_server_info(self, server_info):
self.server_info = server_info self.server_info = server_info
try: try:
@ -1174,10 +1182,14 @@ class auth_aes128_sha1(auth_base):
self.user_id = None self.user_id = None
self.user_key = None self.user_key = None
self.last_rnd_len = 0 self.last_rnd_len = 0
self.overhead = 9
def init_data(self): def init_data(self):
return obfs_auth_mu_data() 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): def set_server_info(self, server_info):
self.server_info = server_info self.server_info = server_info
try: try:
@ -1198,9 +1210,15 @@ class auth_aes128_sha1(auth_base):
return int(v * max_val) return int(v * max_val)
def rnd_data_len(self, buf_size, full_buf_size): 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 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 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: if buf_size > 900:
return struct.unpack('>H', os.urandom(2))[0] % rev_len return struct.unpack('>H', os.urandom(2))[0] % rev_len
return self.trapezoid_random_int(rev_len, -0.3) return self.trapezoid_random_int(rev_len, -0.3)

5
shadowsocks/obfsplugin/obfs_tls.py

@ -65,10 +65,14 @@ class tls_ticket_auth(plain.plain):
self.client_id = b'' self.client_id = b''
self.max_time_dif = 60 * 60 * 24 # time dif (second) setting self.max_time_dif = 60 * 60 * 24 # time dif (second) setting
self.tls_version = b'\x03\x03' self.tls_version = b'\x03\x03'
self.overhead = 5
def init_data(self): def init_data(self):
return obfs_auth_data() 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): def sni(self, url):
url = common.to_bytes(url) url = common.to_bytes(url)
data = b"\x00" + struct.pack('>H', len(url)) + 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): def decode_error_return(self, buf):
self.handshake_status = -1 self.handshake_status = -1
self.overhead = 0
if self.method == 'tls1.2_ticket_auth': if self.method == 'tls1.2_ticket_auth':
return (b'E'*2048, False, False) return (b'E'*2048, False, False)
return (buf, True, False) return (buf, True, False)

3
shadowsocks/obfsplugin/plain.py

@ -40,6 +40,9 @@ class plain(object):
def init_data(self): def init_data(self):
return b'' return b''
def get_overhead(self, direction): # direction: true for c->s false for s->c
return 0
def get_server_info(self): def get_server_info(self):
return self.server_info return self.server_info

63
shadowsocks/tcprelay.py

@ -91,6 +91,8 @@ WAIT_STATUS_READING = 1
WAIT_STATUS_WRITING = 2 WAIT_STATUS_WRITING = 2
WAIT_STATUS_READWRITING = WAIT_STATUS_READING | WAIT_STATUS_WRITING WAIT_STATUS_READWRITING = WAIT_STATUS_READING | WAIT_STATUS_WRITING
NETWORK_MTU = 1492
TCP_MSS = NETWORK_MTU - 40
BUF_SIZE = 32 * 1024 BUF_SIZE = 32 * 1024
UDP_MAX_BUF_SIZE = 65536 UDP_MAX_BUF_SIZE = 65536
@ -137,6 +139,7 @@ class TCPRelayHandler(object):
self._accept_address = local_sock.getsockname()[:2] self._accept_address = local_sock.getsockname()[:2]
self._user = None self._user = None
self._user_id = server._listen_port self._user_id = server._listen_port
self._tcp_mss = TCP_MSS
# TCP Relay works as either sslocal or ssserver # TCP Relay works as either sslocal or ssserver
# if is_local, this is sslocal # if is_local, this is sslocal
@ -151,6 +154,16 @@ class TCPRelayHandler(object):
return return
self._encrypt_correct = True self._encrypt_correct = True
self._obfs = obfs.obfs(config['obfs']) 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 = obfs.server_info(server.obfs_data)
server_info.host = config['server'] server_info.host = config['server']
server_info.port = server._listen_port 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_str = common.to_bytes(config['password'])
server_info.key = self._encryptor.cipher_key server_info.key = self._encryptor.cipher_key
server_info.head_len = 30 server_info.head_len = 30
server_info.tcp_mss = 1448 server_info.tcp_mss = self._tcp_mss
server_info.buffer_size = BUF_SIZE server_info.buffer_size = self._recv_buffer_size
self._obfs.set_server_info(server_info) self._obfs.set_server_info(server_info)
self._protocol = obfs.obfs(config['protocol'])
server_info = obfs.server_info(server.protocol_data) server_info = obfs.server_info(server.protocol_data)
server_info.host = config['server'] server_info.host = config['server']
server_info.port = server._listen_port 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_str = common.to_bytes(config['password'])
server_info.key = self._encryptor.cipher_key server_info.key = self._encryptor.cipher_key
server_info.head_len = 30 server_info.head_len = 30
server_info.tcp_mss = 1448 server_info.tcp_mss = self._tcp_mss
server_info.buffer_size = BUF_SIZE server_info.buffer_size = self._recv_buffer_size
self._protocol.set_server_info(server_info) self._protocol.set_server_info(server_info)
self._redir_list = config.get('redirect', ["*#0.0.0.0:0"]) 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_sum = common.to_str(host).rsplit('#', 1)
items_match = common.to_str(items_sum[0]).rsplit(':', 1) items_match = common.to_str(items_sum[0]).rsplit(':', 1)
items = common.to_str(items_sum[1]).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 len(items_match) > 1:
if items_match[1] != "*": if items_match[1] != "*":
try: try:
match_port = int(items_match[1]) if self._server._listen_port != int(items_match[1]) and int(items_match[1]) != 0:
continue
except: except:
pass 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 continue
if len(items) > 1: if len(items) > 1:
try: try:
@ -573,6 +583,12 @@ class TCPRelayHandler(object):
if header_result is None: if header_result is None:
data = self._handel_protocol_error(self._client_address, ogn_data) data = self._handel_protocol_error(self._client_address, ogn_data)
header_result = parse_header(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 connecttype, remote_addr, remote_port, header_length = header_result
common.connect_log('%s connecting %s:%d via port %d by UID %d' % common.connect_log('%s connecting %s:%d via port %d by UID %d' %
((connecttype == 0) and 'TCP' or 'UDP', ((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])) logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
self.destroy() 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): def _on_local_read(self):
# handle all local read events and dispatch them to methods for # handle all local read events and dispatch them to methods for
# each stage # each stage
if not self._local_sock: if not self._local_sock:
return return
is_local = self._is_local 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 data = None
try: try:
data = self._local_sock.recv(BUF_SIZE) data = self._local_sock.recv(recv_buffer_size)
except (OSError, IOError) as e: except (OSError, IOError) as e:
if eventloop.errno_from_exception(e) in \ if eventloop.errno_from_exception(e) in \
(errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK): (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK):
@ -848,7 +879,11 @@ class TCPRelayHandler(object):
data = struct.pack('>H', size) + data 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])) #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: 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: except (OSError, IOError) as e:
if eventloop.errno_from_exception(e) in \ if eventloop.errno_from_exception(e) in \
(errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK, 10035): #errno.WSAEWOULDBLOCK (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK, 10035): #errno.WSAEWOULDBLOCK

5
shadowsocks/udprelay.py

@ -75,9 +75,6 @@ import threading
from shadowsocks import encrypt, obfs, eventloop, lru_cache, common, shell from shadowsocks import encrypt, obfs, eventloop, lru_cache, common, shell
from shadowsocks.common import pre_parse_header, parse_header, pack_addr 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: # for each handler, we have 2 stream directions:
# upstream: from client to server direction # upstream: from client to server direction
# read local and write to remote # 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_str = common.to_bytes(config['password'])
server_info.key = encrypt.encrypt_key(self._password, self._method) server_info.key = encrypt.encrypt_key(self._password, self._method)
server_info.head_len = 30 server_info.head_len = 30
server_info.tcp_mss = 1440 server_info.tcp_mss = 1452
server_info.buffer_size = BUF_SIZE server_info.buffer_size = BUF_SIZE
self._protocol.set_server_info(server_info) self._protocol.set_server_info(server_info)

Loading…
Cancel
Save