From 89a7508f265a44a45ca3c6973d4d8659f1e2d3af Mon Sep 17 00:00:00 2001 From: clowwindy Date: Fri, 2 May 2014 12:28:53 +0800 Subject: [PATCH] lint code --- CHANGES | 4 ++ README.md | 6 ++ config.json | 4 +- shadowsocks/local.py | 134 ++++++++++++++++++++++--------------------- shadowsocks/utils.py | 6 +- 5 files changed, 87 insertions(+), 67 deletions(-) diff --git a/CHANGES b/CHANGES index 15fbdbd..055f918 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +1.4.0 2014-05-02 +- Adds UDP relay +- TCP fast open supports on Linux 3.7+ + 1.3.7 2014-04-10 - Fix a typo in help diff --git a/README.md b/README.md index bd33fa7..6cdb3bc 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Explanation of the fields: password a password used to encrypt transfer timeout in seconds method encryption method, "bf-cfb", "aes-256-cfb", "des-cfb", "rc4", etc. Default is table, which is not secure. "aes-256-cfb" is recommended + fast_open use TCP_FASTOPEN, true/false `cd` into the directory of `config.json`. Run `ssserver` on your server. To run it in the background, run `nohup ssserver > log &`. @@ -112,6 +113,11 @@ Or: $ sudo apt-get install libevent-dev python-pip $ sudo pip install gevent +If both of your server and client are deployed on Linux 3.7+, you can turn on +fast_open for lower latency. + + echo 3 > /proc/sys/net/ipv4/tcp_fastopen + License ------- MIT diff --git a/config.json b/config.json index 7adf48d..25879b1 100644 --- a/config.json +++ b/config.json @@ -4,5 +4,7 @@ "local_port":1080, "password":"barfoo!", "timeout":600, - "method":"table" + "method":"table", + "local_address":"127.0.0.1", + "fast_open":false } diff --git a/shadowsocks/local.py b/shadowsocks/local.py index 7f8fd40..4cdb807 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -69,43 +69,51 @@ class ThreadingTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): class Socks5Server(SocketServer.StreamRequestHandler): - def getServer(self): - aPort = REMOTE_PORT - aServer = SERVER - if isinstance(REMOTE_PORT, list): + @staticmethod + def get_server(): + a_port = config_server_port + a_server = config_server + if isinstance(config_server_port, list): # support config like "server_port": [8081, 8082] - aPort = random.choice(REMOTE_PORT) - if isinstance(SERVER, list): + a_port = random.choice(config_server_port) + if isinstance(config_server, list): # support config like "server": ["123.123.123.1", "123.123.123.2"] - aServer = random.choice(SERVER) + a_server = random.choice(config_server) - r = re.match(r'^(.*)\:(\d+)$', aServer) + r = re.match(r'^(.*):(\d+)$', a_server) if r: # support config like "server": "123.123.123.1:8381" # or "server": ["123.123.123.1:8381", "123.123.123.2:8381"] - aServer = r.group(1) - aPort = int(r.group(2)) - return (aServer, aPort) + a_server = r.group(1) + a_port = int(r.group(2)) + return a_server, a_port - def handle_tcp(self, sock, remote, pending_data=None, server=None, port=None): + @staticmethod + def handle_tcp(sock, remote, encryptor, pending_data=None, + server=None, port=None): connected = False try: - if FAST_OPEN: + if config_fast_open: fdset = [sock] else: fdset = [sock, remote] while True: r, w, e = select.select(fdset, [], []) if sock in r: - if not connected and FAST_OPEN: + if not connected and config_fast_open: data = sock.recv(4096) - data = self.encrypt(pending_data + data) + data = encryptor.encrypt(pending_data + data) + pending_data = None + logging.info('fast open %s:%d' % (server, port)) remote.sendto(data, MSG_FASTOPEN, (server, port)) connected = True fdset = [sock, remote] - logging.info('fast open %s:%d' % (server, port)) else: - data = self.encrypt(sock.recv(4096)) + data = sock.recv(4096) + if pending_data: + data = pending_data + data + pending_data = None + data = encryptor.encrypt(data) if len(data) <= 0: break result = send_all(remote, data) @@ -113,7 +121,7 @@ class Socks5Server(SocketServer.StreamRequestHandler): raise Exception('failed to send all data') if remote in r: - data = self.decrypt(remote.recv(4096)) + data = encryptor.decrypt(remote.recv(4096)) if len(data) <= 0: break result = send_all(sock, data) @@ -123,18 +131,9 @@ class Socks5Server(SocketServer.StreamRequestHandler): sock.close() remote.close() - def encrypt(self, data): - return self.encryptor.encrypt(data) - - def decrypt(self, data): - return self.encryptor.decrypt(data) - - def send_encrypt(self, sock, data): - sock.send(self.encrypt(data)) - def handle(self): try: - self.encryptor = encrypt.Encryptor(KEY, METHOD) + encryptor = encrypt.Encryptor(config_password, config_method) sock = self.connection sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) data = sock.recv(262) @@ -216,23 +215,23 @@ class Socks5Server(SocketServer.StreamRequestHandler): reply += socket.inet_aton('0.0.0.0') + struct.pack(">H", 2222) self.wfile.write(reply) # reply immediately - aServer, aPort = self.getServer() - addrs = socket.getaddrinfo(aServer, aPort) + a_server, a_port = Socks5Server.get_server() + addrs = socket.getaddrinfo(a_server, a_port) if addrs: af, socktype, proto, canonname, sa = addrs[0] - if FAST_OPEN: + if config_fast_open: remote = socket.socket(af, socktype, proto) - # remote.setsockopt(socket.IPPROTO_TCP, - # socket.TCP_NODELAY, 1) - self.handle_tcp(sock, remote, addr_to_send, aServer, - aPort) - else: - remote = socket.create_connection((aServer, aPort)) remote.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - self.send_encrypt(remote, addr_to_send) + Socks5Server.handle_tcp(sock, remote, encryptor, + addr_to_send, a_server, a_port) + else: logging.info('connecting %s:%d' % (addr, port[0])) - self.handle_tcp(sock, remote) + remote = socket.create_connection((a_server, a_port)) + remote.setsockopt(socket.IPPROTO_TCP, + socket.TCP_NODELAY, 1) + Socks5Server.handle_tcp(sock, remote, encryptor, + addr_to_send) finally: pass # except socket.error, e: @@ -247,7 +246,8 @@ class Socks5Server(SocketServer.StreamRequestHandler): def main(): - global SERVER, REMOTE_PORT, KEY, METHOD, FAST_OPEN + global config_server, config_server_port, config_password, config_method,\ + config_fast_open logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)-8s %(message)s', @@ -266,14 +266,13 @@ def main(): pass print 'shadowsocks %s' % version - KEY = None - METHOD = None - LOCAL = '' - IPv6 = False + config_password = None + config_method = None config_path = utils.find_config() try: - optlist, args = getopt.getopt(sys.argv[1:], 's:b:p:k:l:m:c:6') + optlist, args = getopt.getopt(sys.argv[1:], 's:b:p:k:l:m:c:', + ['fast-open']) for key, value in optlist: if key == '-c': config_path = value @@ -290,7 +289,8 @@ def main(): else: config = {} - optlist, args = getopt.getopt(sys.argv[1:], 's:b:p:k:l:m:c:6') + optlist, args = getopt.getopt(sys.argv[1:], 's:b:p:k:l:m:c:', + ['fast-open']) for key, value in optlist: if key == '-p': config['server_port'] = int(value) @@ -304,35 +304,41 @@ def main(): config['method'] = value elif key == '-b': config['local_address'] = value - elif key == '-6': - IPv6 = True - except getopt.GetoptError: + elif key == '--fast-open': + config['fast_open'] = True + except getopt.GetoptError as e: + logging.error(e) utils.print_local_help() sys.exit(2) - SERVER = config['server'] - REMOTE_PORT = config['server_port'] - PORT = config['local_port'] - KEY = config['password'] - METHOD = config.get('method', None) - LOCAL = config.get('local_address', '127.0.0.1') - TIMEOUT = config.get('timeout', 600) - FAST_OPEN = config.get('fast_open', False) + config_server = config['server'] + config_server_port = config['server_port'] + config_local_port = config['local_port'] + config_password = config['password'] + config_method = config.get('method', None) + config_local_address = config.get('local_address', '127.0.0.1') + config_timeout = config.get('timeout', 600) + config_fast_open = config.get('fast_open', False) - if not KEY and not config_path: + if not config_password and not config_path: sys.exit('config not specified, please read ' 'https://github.com/clowwindy/shadowsocks') utils.check_config(config) - encrypt.init_table(KEY, METHOD) + encrypt.init_table(config_password, config_method) - if IPv6: - ThreadingTCPServer.address_family = socket.AF_INET6 + addrs = socket.getaddrinfo(config_local_address, config_local_port) + if not addrs: + logging.error('cant resolve local address') + sys.exit(1) + ThreadingTCPServer.address_family = addrs[0][0] try: - udprelay.UDPRelay(LOCAL, int(PORT), SERVER, REMOTE_PORT, KEY, METHOD, - int(TIMEOUT), True).start() - server = ThreadingTCPServer((LOCAL, PORT), Socks5Server) + udprelay.UDPRelay(config_local_address, int(config_local_port), + config_server, config_server_port, config_password, + config_method, int(config_timeout), True).start() + server = ThreadingTCPServer((config_local_address, config_local_port), + Socks5Server) logging.info("starting local at %s:%d" % tuple(server.server_address[:2])) server.serve_forever() diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 1f6c1cc..9e2fe14 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -48,7 +48,7 @@ def check_config(config): def print_local_help(): print '''usage: sslocal [-h] -s SERVER_ADDR -p SERVER_PORT [-b LOCAL_ADDR] - -l LOCAL_PORT -k PASSWORD -m METHOD [-c config] + -l LOCAL_PORT -k PASSWORD -m METHOD [-c config] [--fast-open] optional arguments: -h, --help show this help message and exit @@ -59,12 +59,13 @@ optional arguments: -k PASSWORD password -m METHOD encryption method, for example, aes-256-cfb -c CONFIG path to config file + --fast-open use TCP_FASTOPEN, requires Linux 3.7+ ''' def print_server_help(): print '''usage: ssserver [-h] -s SERVER_ADDR -p SERVER_PORT -k PASSWORD - -m METHOD [-c config] + -m METHOD [-c config] [--fast-open] optional arguments: -h, --help show this help message and exit @@ -73,4 +74,5 @@ optional arguments: -k PASSWORD password -m METHOD encryption method, for example, aes-256-cfb -c CONFIG path to config file + --fast-open use TCP_FASTOPEN, requires Linux 3.7+ ''' \ No newline at end of file