From 5316b3bf115ce915bfa182afdf926045625304fc Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 28 Jan 2015 17:17:05 +0800 Subject: [PATCH 01/39] update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fb96264..6c1b61e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,7 @@ pip-log.txt # Unit test / coverage reports htmlcov -.coverage +.coverage* .tox #Translations From ada97ab6d9ae3b9ad1e6d91ab92cc6f8c37259c2 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 31 Jan 2015 02:58:40 +0800 Subject: [PATCH 02/39] improve comments --- shadowsocks/tcprelay.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 3b48901..d68e424 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -43,30 +43,22 @@ TIMEOUT_PRECISION = 4 MSG_FASTOPEN = 0x20000000 -# SOCKS CMD defination +# SOCKS command definition CMD_CONNECT = 1 CMD_BIND = 2 CMD_UDP_ASSOCIATE = 3 -# TCP Relay can be either sslocal or ssserver -# for sslocal it is called is_local=True - # for each opening port, we have a TCP Relay + # for each connection, we have a TCP Relay Handler to handle the connection # for each handler, we have 2 sockets: # local: connected to the client # remote: connected to remote server -# for each handler, we have 2 streams: -# upstream: from client to server direction -# read local and write to remote -# downstream: from server to client direction -# read remote and write to local - # for each handler, it could be at one of several stages: -# sslocal: +# as sslocal: # stage 0 SOCKS hello received from local, send hello to local # stage 1 addr received from local, query DNS for remote # stage 2 UDP assoc @@ -74,7 +66,7 @@ CMD_UDP_ASSOCIATE = 3 # stage 4 still connecting, more data from local received # stage 5 remote connected, piping local and remote -# ssserver: +# as ssserver: # stage 0 just jump to stage 1 # stage 1 addr received from local, query DNS for remote # stage 3 DNS resolved, connect to remote @@ -89,11 +81,16 @@ STAGE_CONNECTING = 4 STAGE_STREAM = 5 STAGE_DESTROYED = -1 -# stream direction +# for each handler, we have 2 stream directions: +# upstream: from client to server direction +# read local and write to remote +# downstream: from server to client direction +# read remote and write to local + STREAM_UP = 0 STREAM_DOWN = 1 -# stream wait status, indicating it's waiting for reading, etc +# for each stream, it's waiting for reading, or writing, or both WAIT_STATUS_INIT = 0 WAIT_STATUS_READING = 1 WAIT_STATUS_WRITING = 2 @@ -112,6 +109,9 @@ class TCPRelayHandler(object): self._remote_sock = None self._config = config self._dns_resolver = dns_resolver + + # TCP Relay works as either sslocal or ssserver + # if is_local, this is sslocal self._is_local = is_local self._stage = STAGE_INIT self._encryptor = encrypt.Encryptor(config['password'], From 8783e0e9aea644f3839bfbcf9763090a1c39d0f1 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sat, 31 Jan 2015 11:56:17 +0800 Subject: [PATCH 03/39] Move is_ip from asyncdns to common Since implement CIDR forbidden need this function, move it to common file seems to be a better choice. --- shadowsocks/asyncdns.py | 18 +++--------------- shadowsocks/common.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/shadowsocks/asyncdns.py b/shadowsocks/asyncdns.py index 6f60dc9..6fee6b9 100644 --- a/shadowsocks/asyncdns.py +++ b/shadowsocks/asyncdns.py @@ -233,18 +233,6 @@ def parse_response(data): return None -def is_ip(address): - for family in (socket.AF_INET, socket.AF_INET6): - try: - if type(address) != str: - address = address.decode('utf8') - socket.inet_pton(family, address) - return family - except (TypeError, ValueError, OSError, IOError): - pass - return False - - def is_valid_hostname(hostname): if len(hostname) > 255: return False @@ -296,7 +284,7 @@ class DNSResolver(object): parts = line.split() if len(parts) >= 2: server = parts[1] - if is_ip(server) == socket.AF_INET: + if common.is_ip(server) == socket.AF_INET: if type(server) != str: server = server.decode('utf8') self._servers.append(server) @@ -316,7 +304,7 @@ class DNSResolver(object): parts = line.split() if len(parts) >= 2: ip = parts[0] - if is_ip(ip): + if common.is_ip(ip): for i in range(1, len(parts)): hostname = parts[i] if hostname: @@ -423,7 +411,7 @@ class DNSResolver(object): hostname = hostname.encode('utf8') if not hostname: callback(None, Exception('empty hostname')) - elif is_ip(hostname): + elif common.is_ip(hostname): callback((hostname, hostname), None) elif hostname in self._hosts: logging.debug('hit hosts: %s', hostname) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index e4f698c..2be17e5 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -101,6 +101,18 @@ def inet_pton(family, addr): raise RuntimeError("What family?") +def is_ip(address): + for family in (socket.AF_INET, socket.AF_INET6): + try: + if type(address) != str: + address = address.decode('utf8') + inet_pton(family, address) + return family + except (TypeError, ValueError, OSError, IOError): + pass + return False + + def patch_socket(): if not hasattr(socket, 'inet_pton'): socket.inet_pton = inet_pton From 100ebcf064f4a8e5840012bdccef106794208569 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sat, 31 Jan 2015 19:50:10 +0800 Subject: [PATCH 04/39] Add IPNetwork class to support CIDR calculation Usage: Use IPNetwork(str|list) to create an IPNetwork object. Use operator 'in' to determine whether the specified IP address is in the IP network or not, like: >>> '192.168.1.1' in IPNetwork('192.168.1.0/24') True Both IPv4 and IPv6 address are supported. Note: When using string to initialize the IPNetwork, a comma seperated IP network list should be provided. Currently, IPNetwork just support standard CIDR like: x.x.x.x/y eg. 192.168.1.0/24 ::x/y eg. ::1/10 If pure IP address was provided, it will be treated as implicit IP network, like 192.168.0.0 will be treated as 192.168.0.0/16 and 192.168.1.1 will be treated as 192.168.1.1/32 This implicit translate may cause some unexpected behavior, like user provide 192.168.2.0 and expect it will be treated as 192.168.2.0/24 but actually it will be translated to 192.168.2.0/23 because there are 9 continuous 0 from right. In order to avoid confusion, a warning message will be displayed when pure IP address was provided. Other variants of CIDR are not supported yet. --- shadowsocks/common.py | 67 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index 2be17e5..d582923 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -184,6 +184,57 @@ def parse_header(data): return addrtype, to_bytes(dest_addr), dest_port, header_length +class IPNetwork(object): + ADDRLENGTH = {socket.AF_INET: 32, socket.AF_INET6: 128} + + def __init__(self, addrs): + self._network_list_v4 = [] + self._network_list_v6 = [] + if type(addrs) == str: + addrs = addrs.split(',') + map(self.add_network, addrs) + + def add_network(self, addr): + block = addr.split('/') + addr_family = is_ip(block[0]) + if addr_family is socket.AF_INET: + ip, = struct.unpack("!I", socket.inet_aton(block[0])) + elif addr_family is socket.AF_INET6: + hi, lo = struct.unpack("!QQ", inet_pton(addr_family, block[0])) + ip = (hi << 64) | lo + else: + raise SyntaxError("Not a valid CIDR notation: %s" % addr) + if len(block) is 1: + prefix_size = 0 + while ((ip & 1) == 0): + ip >>= 1 + prefix_size += 1 + logging.warn("You did't specify CIDR routing prefix size for %s, " + "implicit treated as %s/%d" % (addr, addr, + IPNetwork.ADDRLENGTH[addr_family] - prefix_size)) + elif block[1].isdigit() and int(block[1]) <= IPNetwork.ADDRLENGTH[addr_family]: + prefix_size = IPNetwork.ADDRLENGTH[addr_family] - int(block[1]) + ip >>= prefix_size + else: + raise SyntaxError("Not a valid CIDR notation: %s" % addr) + if addr_family is socket.AF_INET: + self._network_list_v4.append((ip, prefix_size)) + else: + self._network_list_v6.append((ip, prefix_size)) + + def __contains__(self, addr): + addr_family = is_ip(addr) + if addr_family is socket.AF_INET: + ip, = struct.unpack("!I", socket.inet_aton(addr)) + return any(map(lambda (naddr, ps): naddr == ip >> ps, self._network_list_v4)) + elif addr_family is socket.AF_INET6: + hi, lo = struct.unpack("!QQ", inet_pton(addr_family, addr)) + ip = (hi << 64) | lo + return any(map(lambda (naddr, ps): naddr == ip >> ps, self._network_list_v6)) + else: + return False + + def test_inet_conv(): ipv4 = b'8.8.4.4' b = inet_pton(socket.AF_INET, ipv4) @@ -210,7 +261,23 @@ def test_pack_header(): assert pack_addr(b'www.google.com') == b'\x03\x0ewww.google.com' +def test_ip_network(): + ip_network = IPNetwork('127.0.0.0/24,::ff:1/112,::1,192.168.1.1,192.168.2.0') + assert '127.0.0.1' in ip_network + assert '127.0.1.1' not in ip_network + assert ':ff:ffff' in ip_network + assert '::ffff:1' not in ip_network + assert '::1' in ip_network + assert '::2' not in ip_network + assert '192.168.1.1' in ip_network + assert '192.168.1.2' not in ip_network + assert '192.168.2.1' in ip_network + assert '192.168.3.1' in ip_network # 192.168.2.0 is treated as 192.168.2.0/23 + assert 'www.google.com' not in ip_network + + if __name__ == '__main__': test_inet_conv() test_parse_header() test_pack_header() + test_ip_network() From 8af359ae05d6fa60b9f60e6f732c6e02bb4b6b93 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sat, 31 Jan 2015 20:32:09 +0800 Subject: [PATCH 05/39] Use IPNetwork supporting forbidden ip feature in config utils This commit also make "forbidden_ip" field available in config file. If no forbidden ip specified in command line and config file, default to "127.0.0.0/8,::1". --- shadowsocks/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index a51c965..0c77125 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -29,7 +29,7 @@ import json import sys import getopt import logging -from shadowsocks.common import to_bytes, to_str +from shadowsocks.common import to_bytes, to_str, IPNetwork VERBOSE_LEVEL = 5 @@ -186,6 +186,7 @@ def get_config(is_local): config['verbose'] = config.get('verbose', False) config['local_address'] = config.get('local_address', '127.0.0.1') config['local_port'] = config.get('local_port', 1080) + config['forbidden_ip'] = IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1')) if is_local: if config.get('server', None) is None: logging.error('server addr not specified') From aa28796524eb7ff9ccb9a73afa4353079d2a9f71 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 1 Feb 2015 00:17:03 +0800 Subject: [PATCH 06/39] Make common fit PEP8 --- shadowsocks/common.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index d582923..0f94e07 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -185,7 +185,7 @@ def parse_header(data): class IPNetwork(object): - ADDRLENGTH = {socket.AF_INET: 32, socket.AF_INET6: 128} + ADDRLENGTH = {socket.AF_INET: 32, socket.AF_INET6: 128, False: 0} def __init__(self, addrs): self._network_list_v4 = [] @@ -197,6 +197,7 @@ class IPNetwork(object): def add_network(self, addr): block = addr.split('/') addr_family = is_ip(block[0]) + addr_len = IPNetwork.ADDRLENGTH[addr_family] if addr_family is socket.AF_INET: ip, = struct.unpack("!I", socket.inet_aton(block[0])) elif addr_family is socket.AF_INET6: @@ -210,10 +211,9 @@ class IPNetwork(object): ip >>= 1 prefix_size += 1 logging.warn("You did't specify CIDR routing prefix size for %s, " - "implicit treated as %s/%d" % (addr, addr, - IPNetwork.ADDRLENGTH[addr_family] - prefix_size)) - elif block[1].isdigit() and int(block[1]) <= IPNetwork.ADDRLENGTH[addr_family]: - prefix_size = IPNetwork.ADDRLENGTH[addr_family] - int(block[1]) + "implicit treated as %s/%d" % (addr, addr, addr_len)) + elif block[1].isdigit() and int(block[1]) <= addr_len: + prefix_size = addr_len - int(block[1]) ip >>= prefix_size else: raise SyntaxError("Not a valid CIDR notation: %s" % addr) @@ -226,11 +226,13 @@ class IPNetwork(object): addr_family = is_ip(addr) if addr_family is socket.AF_INET: ip, = struct.unpack("!I", socket.inet_aton(addr)) - return any(map(lambda (naddr, ps): naddr == ip >> ps, self._network_list_v4)) + return any(map(lambda (n, ps): n == ip >> ps, + self._network_list_v4)) elif addr_family is socket.AF_INET6: hi, lo = struct.unpack("!QQ", inet_pton(addr_family, addr)) ip = (hi << 64) | lo - return any(map(lambda (naddr, ps): naddr == ip >> ps, self._network_list_v6)) + return any(map(lambda (n, ps): n == ip >> ps, + self._network_list_v6)) else: return False @@ -262,7 +264,7 @@ def test_pack_header(): def test_ip_network(): - ip_network = IPNetwork('127.0.0.0/24,::ff:1/112,::1,192.168.1.1,192.168.2.0') + ip_network = IPNetwork('127.0.0.0/24,::ff:1/112,::1,192.168.1.1,192.0.2.0') assert '127.0.0.1' in ip_network assert '127.0.1.1' not in ip_network assert ':ff:ffff' in ip_network @@ -271,8 +273,8 @@ def test_ip_network(): assert '::2' not in ip_network assert '192.168.1.1' in ip_network assert '192.168.1.2' not in ip_network - assert '192.168.2.1' in ip_network - assert '192.168.3.1' in ip_network # 192.168.2.0 is treated as 192.168.2.0/23 + assert '192.0.2.1' in ip_network + assert '192.0.3.1' in ip_network # 192.0.2.0 is treated as 192.0.2.0/23 assert 'www.google.com' not in ip_network From b11d8489862765a2aa13d1594c855c4e652f8ccf Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 1 Feb 2015 00:42:12 +0800 Subject: [PATCH 07/39] Fix for Python3 lambda behavior change In Python3, lambda no longer support use tuple as syntax. So, ugly changes is inevitable. --- shadowsocks/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index 0f94e07..68ca025 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -226,12 +226,12 @@ class IPNetwork(object): addr_family = is_ip(addr) if addr_family is socket.AF_INET: ip, = struct.unpack("!I", socket.inet_aton(addr)) - return any(map(lambda (n, ps): n == ip >> ps, + return any(map(lambda n_ps: n_ps[0] == ip >> n_ps[1], self._network_list_v4)) elif addr_family is socket.AF_INET6: hi, lo = struct.unpack("!QQ", inet_pton(addr_family, addr)) ip = (hi << 64) | lo - return any(map(lambda (n, ps): n == ip >> ps, + return any(map(lambda n_ps: n_ps[0] == ip >> n_ps[1], self._network_list_v6)) else: return False From 070108f78bb81f4dc6391fd575d5268f51481cea Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 1 Feb 2015 00:48:15 +0800 Subject: [PATCH 08/39] Disable forbidden ip feature for local shadowsocks Since forbidden ip is server-side only, disable it for local-side. This commit also supress warning about IPv6 loopback because I can confirm ::1/128 is the only loopback address, not like IPv4. --- shadowsocks/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 0c77125..6ea3daa 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -186,7 +186,6 @@ def get_config(is_local): config['verbose'] = config.get('verbose', False) config['local_address'] = config.get('local_address', '127.0.0.1') config['local_port'] = config.get('local_port', 1080) - config['forbidden_ip'] = IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1')) if is_local: if config.get('server', None) is None: logging.error('server addr not specified') @@ -194,6 +193,8 @@ def get_config(is_local): sys.exit(2) else: config['server'] = config.get('server', '0.0.0.0') + config['forbidden_ip'] = \ + IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1/128')) config['server_port'] = config.get('server_port', 8388) if is_local and not config.get('password', None): From 79b9b53dbea59627d43ec7ee4eb2dd54777460be Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 1 Feb 2015 01:57:17 +0800 Subject: [PATCH 09/39] Never process empty string and prevent infinite loop If user provide an empty string as network range, inet_pton will treate it as an IPv6 unspecified address, it seems a bug but I can't confirm. Then empty string will be converted to 0, 0 & 1 always be zero, so it caused dead loop. --- shadowsocks/common.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index 68ca025..d4a9a67 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -195,6 +195,8 @@ class IPNetwork(object): map(self.add_network, addrs) def add_network(self, addr): + if addr is "": + return block = addr.split('/') addr_family = is_ip(block[0]) addr_len = IPNetwork.ADDRLENGTH[addr_family] @@ -207,7 +209,7 @@ class IPNetwork(object): raise SyntaxError("Not a valid CIDR notation: %s" % addr) if len(block) is 1: prefix_size = 0 - while ((ip & 1) == 0): + while (ip & 1) == 0 and ip is not 0: ip >>= 1 prefix_size += 1 logging.warn("You did't specify CIDR routing prefix size for %s, " From a0aa9173a8001b5c20e270d7b5dcec28172cfa3f Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 1 Feb 2015 02:37:30 +0800 Subject: [PATCH 10/39] Fix for Python3 map changed behavior In Python3, map returns an iterator instead of list in Python2, which cause map "lazier" than before, wrap with list() force it running. --- shadowsocks/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index d4a9a67..0c4e278 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -192,7 +192,7 @@ class IPNetwork(object): self._network_list_v6 = [] if type(addrs) == str: addrs = addrs.split(',') - map(self.add_network, addrs) + list(map(self.add_network, addrs)) def add_network(self, addr): if addr is "": From 17624d0b99731a5947a2df9090039ad531883cc7 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 1 Feb 2015 02:40:48 +0800 Subject: [PATCH 11/39] Fix large file test since forbidden ip list is default to localhost Use an empty forbidden ip list to override default list should work. --- tests/test_large_file.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_large_file.sh b/tests/test_large_file.sh index e8acd79..33bcb59 100755 --- a/tests/test_large_file.sh +++ b/tests/test_large_file.sh @@ -8,7 +8,7 @@ mkdir -p tmp $PYTHON shadowsocks/local.py -c tests/aes.json & LOCAL=$! -$PYTHON shadowsocks/server.py -c tests/aes.json & +$PYTHON shadowsocks/server.py -c tests/aes.json --forbidden-ip "" & SERVER=$! sleep 3 From 453a9c61a6135087b72b247d325157cc67a15921 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 09:09:35 +0800 Subject: [PATCH 12/39] show CIDR error more friendly --- shadowsocks/common.py | 4 ++-- shadowsocks/utils.py | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index 0c4e278..1c10671 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -206,7 +206,7 @@ class IPNetwork(object): hi, lo = struct.unpack("!QQ", inet_pton(addr_family, block[0])) ip = (hi << 64) | lo else: - raise SyntaxError("Not a valid CIDR notation: %s" % addr) + raise Exception("Not a valid CIDR notation: %s" % addr) if len(block) is 1: prefix_size = 0 while (ip & 1) == 0 and ip is not 0: @@ -218,7 +218,7 @@ class IPNetwork(object): prefix_size = addr_len - int(block[1]) ip >>= prefix_size else: - raise SyntaxError("Not a valid CIDR notation: %s" % addr) + raise Exception("Not a valid CIDR notation: %s" % addr) if addr_family is socket.AF_INET: self._network_list_v4.append((ip, prefix_size)) else: diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 6ea3daa..f791a2a 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -193,8 +193,12 @@ def get_config(is_local): sys.exit(2) else: config['server'] = config.get('server', '0.0.0.0') - config['forbidden_ip'] = \ - IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1/128')) + try: + config['forbidden_ip'] = \ + IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1/128')) + except Exception as e: + logging.error(e) + sys.exit(2) config['server_port'] = config.get('server_port', 8388) if is_local and not config.get('password', None): From c39bbbe237f230df62c17c751731f64743d92838 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 09:13:29 +0800 Subject: [PATCH 13/39] test invalid CIDR --- tests/test_command.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_command.sh b/tests/test_command.sh index eba4c8c..0995299 100755 --- a/tests/test_command.sh +++ b/tests/test_command.sh @@ -36,5 +36,7 @@ $LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d sto assert "$SERVER 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": password or port_password not specified" $LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop +assert "$SERVER 2>&1 --forbidden-ip 127.0.0.1/4a -m rc4-md5 -k 12345 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": Not a valid CIDR notation: 127.0.0.1/4a" +$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop assert_end command From b77f419336ee2f2dd5b3823bdde92124c95d7a94 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 09:23:45 +0800 Subject: [PATCH 14/39] test if localhost is in the default forbidden list --- .jenkins.sh | 6 ++++++ tests/test.py | 3 +++ 2 files changed, 9 insertions(+) diff --git a/.jenkins.sh b/.jenkins.sh index f14969f..c67fcdb 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -48,6 +48,12 @@ run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0 run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --workers 1" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -qq -b 127.0.0.1" run_test python tests/test.py --with-coverage --should-fail --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=127.0.0.1,::1,8.8.8.8" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" +# test localhost is in the forbidden list by default +run_test python tests/test.py --with-coverage --should-fail --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" + +# test localhost available when forbidden list is empty +run_test python tests/test.py --with-coverage --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=''" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" + if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then if [ 3 -eq `cat /proc/sys/net/ipv4/tcp_fastopen` ] ; then # we have to run it twice: diff --git a/tests/test.py b/tests/test.py index 933027b..5ad94c9 100755 --- a/tests/test.py +++ b/tests/test.py @@ -41,6 +41,7 @@ parser.add_argument('-a', '--client-args', type=str, default=None) parser.add_argument('-b', '--server-args', type=str, default=None) parser.add_argument('--with-coverage', action='store_true', default=None) parser.add_argument('--should-fail', action='store_true', default=None) +parser.add_argument('--tcp-only', action='store_true', default=None) parser.add_argument('--url', type=str, default='http://www.example.com/') parser.add_argument('--dns', type=str, default='8.8.8.8') @@ -127,6 +128,8 @@ try: else: if r != 0: sys.exit(1) + if config.tcp_only: + break p4 = Popen(['socksify', 'dig', '@%s' % config.dns, 'www.google.com'], stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) From 4aba904d6ec205f30c187b331a69b4cb8661d5f5 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 09:24:49 +0800 Subject: [PATCH 15/39] fix typo --- .jenkins.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.jenkins.sh b/.jenkins.sh index c67fcdb..510732a 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -51,7 +51,7 @@ run_test python tests/test.py --with-coverage --should-fail --url="http://localh # test localhost is in the forbidden list by default run_test python tests/test.py --with-coverage --should-fail --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" -# test localhost available when forbidden list is empty +# test localhost is available when forbidden list is empty run_test python tests/test.py --with-coverage --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=''" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then From 5a7225c54b6cd2f0cbef2566de07f76dc2f64c70 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 09:27:18 +0800 Subject: [PATCH 16/39] fix a problem in test arguments --- .jenkins.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.jenkins.sh b/.jenkins.sh index 510732a..8d06b1d 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -52,7 +52,7 @@ run_test python tests/test.py --with-coverage --should-fail --url="http://localh run_test python tests/test.py --with-coverage --should-fail --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" # test localhost is available when forbidden list is empty -run_test python tests/test.py --with-coverage --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=''" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" +run_test python tests/test.py --with-coverage --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then if [ 3 -eq `cat /proc/sys/net/ipv4/tcp_fastopen` ] ; then From 3b7755bd5ebe451c8070f68696e5477b116d99ec Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 09:38:32 +0800 Subject: [PATCH 17/39] fix a travis issue --- .jenkins.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.jenkins.sh b/.jenkins.sh index 8d06b1d..73c4abc 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -46,13 +46,13 @@ run_test python tests/test.py --with-coverage -c tests/workers.json run_test python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-client-side.json run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -q" -a "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -vv" run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --workers 1" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -qq -b 127.0.0.1" -run_test python tests/test.py --with-coverage --should-fail --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=127.0.0.1,::1,8.8.8.8" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" +run_test python tests/test.py --with-coverage --should-fail --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=127.0.0.1,::1,8.8.8.8" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" # test localhost is in the forbidden list by default -run_test python tests/test.py --with-coverage --should-fail --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" +run_test python tests/test.py --with-coverage --should-fail --tcp-only --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" # test localhost is available when forbidden list is empty -run_test python tests/test.py --with-coverage --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" +run_test python tests/test.py --with-coverage --tcp-only --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then if [ 3 -eq `cat /proc/sys/net/ipv4/tcp_fastopen` ] ; then From da65d0a2eee60ee98f3dd9d58971c354695f5816 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 17:15:10 +0800 Subject: [PATCH 18/39] update coverage_server --- tests/coverage_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/coverage_server.py b/tests/coverage_server.py index 2df55e3..d752cff 100644 --- a/tests/coverage_server.py +++ b/tests/coverage_server.py @@ -11,7 +11,7 @@ if __name__ == '__main__': with open('/tmp/%s-coverage' % project, 'rb') as f: coverage = f.read().strip() n = int(coverage.strip('%')) - if n > 80: + if n >= 80: color = 'brightgreen' else: color = 'yellow' From ae99698b4e2df939b95dbb58b5e748ce85cfa1f0 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 15:38:55 +0800 Subject: [PATCH 19/39] implement --user fix @278 --- shadowsocks/daemon.py | 32 ++++++++++++++++++++++++++++++++ shadowsocks/local.py | 1 + shadowsocks/server.py | 2 ++ shadowsocks/utils.py | 22 +++++++++++++--------- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index d206ccf..3d2b448 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -183,3 +183,35 @@ def daemon_stop(pid_file): sys.exit(1) print('stopped') os.unlink(pid_file) + + +def set_user(username): + if username is None: + return + + import pwd + import grp + + try: + pwrec = pwd.getpwnam(username) + except KeyError: + logging.error('user not found: %s' % username) + raise + user = pwrec[0] + uid = pwrec[2] + gid = pwrec[3] + + cur_uid = os.getuid() + if uid == cur_uid: + return + if cur_uid != 0: + logging.error('can not set user as nonroot user') + # will raise later + + # inspired by supervisor + if hasattr(os, 'setgroups'): + groups = [grprec[2] for grprec in grp.getgrall() if user in grprec[3]] + groups.insert(0, gid) + os.setgroups(groups) + os.setgid(gid) + os.setuid(uid) diff --git a/shadowsocks/local.py b/shadowsocks/local.py index 994b6d8..87c5215 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -73,6 +73,7 @@ def main(): sys.exit(1) signal.signal(signal.SIGINT, int_handler) + daemon.set_user(config.get('user', None)) loop.run() except (KeyboardInterrupt, IOError, OSError) as e: logging.error(e) diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 8eed4ad..345ec0b 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -86,6 +86,8 @@ def main(): loop = eventloop.EventLoop() dns_resolver.add_to_loop(loop) list(map(lambda s: s.add_to_loop(loop), tcp_servers + udp_servers)) + + daemon.set_user(config.get('user', None)) loop.run() except (KeyboardInterrupt, IOError, OSError) as e: logging.error(e) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index f791a2a..b12f54d 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -89,7 +89,11 @@ def check_config(config): if config.get('password') in [b'mypassword']: logging.error('DON\'T USE DEFAULT PASSWORD! Please change it in your ' 'config.json!') - exit(1) + sys.exit(1) + if config.get('user', None) is not None: + if os.name != 'posix': + logging.error('user can be used only on Unix') + sys.exit(1) def get_config(is_local): @@ -97,11 +101,11 @@ def get_config(is_local): format='%(levelname)-s: %(message)s') if is_local: shortopts = 'hd:s:b:p:k:l:m:c:t:vq' - longopts = ['help', 'fast-open', 'pid-file=', 'log-file='] + longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'user='] else: shortopts = 'hd:s:p:k:m:c:t:vq' longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers=', - 'forbidden-ip='] + 'forbidden-ip=', 'user='] try: config_path = find_config() optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts) @@ -147,6 +151,8 @@ def get_config(is_local): config['fast_open'] = True elif key == '--workers': config['workers'] = int(value) + elif key == '--user': + config['user'] = to_str(value) elif key == '--forbidden-ip': config['forbidden_ip'] = to_str(value).split(',') elif key in ('-h', '--help'): @@ -247,9 +253,7 @@ def print_help(is_local): def print_local_help(): - print('''usage: sslocal [-h] -s SERVER_ADDR [-p SERVER_PORT] - [-b LOCAL_ADDR] [-l LOCAL_PORT] -k PASSWORD [-m METHOD] - [-t TIMEOUT] [-c CONFIG] [--fast-open] [-v] -[d] [-q] + print('''usage: sslocal [OPTION]... A fast tunnel proxy that helps you bypass firewalls. You can supply configurations via either config file or command line arguments. @@ -270,6 +274,7 @@ General options: -d start/stop/restart daemon mode --pid-file PID_FILE pid file for daemon mode --log-file LOG_FILE log file for daemon mode + --user USER username to run as -v, -vv verbose mode -q, -qq quiet mode, only show warnings/errors @@ -278,9 +283,7 @@ Online help: def print_server_help(): - print('''usage: ssserver [-h] [-s SERVER_ADDR] [-p SERVER_PORT] -k PASSWORD - -m METHOD [-t TIMEOUT] [-c CONFIG] [--fast-open] - [--workers WORKERS] [-v] [-d start] [-q] + print('''usage: ssserver [OPTION]... A fast tunnel proxy that helps you bypass firewalls. You can supply configurations via either config file or command line arguments. @@ -301,6 +304,7 @@ General options: -d start/stop/restart daemon mode --pid-file PID_FILE pid file for daemon mode --log-file LOG_FILE log file for daemon mode + --user USER username to run as -v, -vv verbose mode -q, -qq quiet mode, only show warnings/errors From 7aa37cad8e3360fad28a2c95eaa577d94adc8070 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 15:46:22 +0800 Subject: [PATCH 20/39] check cipher before daemon start fix #280 --- README.md | 13 ++++++++++--- shadowsocks/local.py | 5 +---- shadowsocks/server.py | 4 +--- shadowsocks/utils.py | 3 +++ 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index aba545b..24b7ed1 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,19 @@ See [Install Server on Windows] ### Usage - ssserver -p 8000 -k password -m rc4-md5 + ssserver -p 443 -k password -m rc4-md5 To run in the background: - ssserver -p 8000 -k password -m rc4-md5 -d start - ssserver -p 8000 -k password -m rc4-md5 -d stop + sudo ssserver -p 443 -k password -m rc4-md5 --user nobody -d start + +To stop: + + sudo ssserver -d stop + +To check the log: + + sudo less /var/log/shadowsocks.log Check all the options via `-h`. You can also use a [Configuration] file instead. diff --git a/shadowsocks/local.py b/shadowsocks/local.py index 87c5215..3cf3da7 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -30,8 +30,7 @@ import logging import signal sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) -from shadowsocks import utils, daemon, encrypt, eventloop, tcprelay, udprelay,\ - asyncdns +from shadowsocks import utils, daemon, eventloop, tcprelay, udprelay, asyncdns def main(): @@ -49,8 +48,6 @@ def main(): utils.print_shadowsocks() - encrypt.try_cipher(config['password'], config['method']) - try: logging.info("starting local at %s:%d" % (config['local_address'], config['local_port'])) diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 345ec0b..eca43ee 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -30,8 +30,7 @@ import logging import signal sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) -from shadowsocks import utils, daemon, encrypt, eventloop, tcprelay, udprelay,\ - asyncdns +from shadowsocks import utils, daemon, eventloop, tcprelay, udprelay, asyncdns def main(): @@ -57,7 +56,6 @@ def main(): else: config['port_password'][str(server_port)] = config['password'] - encrypt.try_cipher(config['password'], config['method']) tcp_servers = [] udp_servers = [] dns_resolver = asyncdns.DNSResolver() diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index b12f54d..0e50089 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -30,6 +30,7 @@ import sys import getopt import logging from shadowsocks.common import to_bytes, to_str, IPNetwork +from shadowsocks import encrypt VERBOSE_LEVEL = 5 @@ -95,6 +96,8 @@ def check_config(config): logging.error('user can be used only on Unix') sys.exit(1) + encrypt.try_cipher(config['password'], config['method']) + def get_config(is_local): logging.basicConfig(level=logging.INFO, From 73f21ffbf61258afb8d38dc0a4d5bdfca0b0cc67 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 15:50:41 +0800 Subject: [PATCH 21/39] do not check config for daemon stop --- shadowsocks/daemon.py | 3 --- shadowsocks/utils.py | 10 +++++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index 3d2b448..9961ea7 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -43,9 +43,6 @@ def daemon_exec(config): command = 'start' pid_file = config['pid-file'] log_file = config['log-file'] - command = common.to_str(command) - pid_file = common.to_str(pid_file) - log_file = common.to_str(log_file) if command == 'start': daemon_start(pid_file, log_file) elif command == 'stop': diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 0e50089..3e2fb57 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -70,6 +70,10 @@ def find_config(): def check_config(config): + if config.get('daemon', None) == 'stop': + # no need to specify configuration for daemon stop + return + if config.get('local_address', '') in [b'0.0.0.0']: logging.warn('warning: local set to listen on 0.0.0.0, it\'s not safe') if config.get('server', '') in [b'127.0.0.1', b'localhost']: @@ -165,11 +169,11 @@ def get_config(is_local): print_server_help() sys.exit(0) elif key == '-d': - config['daemon'] = value + config['daemon'] = to_str(value) elif key == '--pid-file': - config['pid-file'] = value + config['pid-file'] = to_str(value) elif key == '--log-file': - config['log-file'] = value + config['log-file'] = to_str(value) elif key == '-q': v_count -= 1 config['verbose'] = v_count From 1c814654367f1ac5afe7bd23439f6d57792e30d7 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 15:57:44 +0800 Subject: [PATCH 22/39] fix command line --- shadowsocks/utils.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 3e2fb57..417348c 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -69,11 +69,28 @@ def find_config(): return None -def check_config(config): +def check_config(config, is_local): if config.get('daemon', None) == 'stop': # no need to specify configuration for daemon stop return + if is_local and not config.get('password', None): + logging.error('password not specified') + print_help(is_local) + sys.exit(2) + + if not is_local and not config.get('password', None) \ + and not config.get('port_password', None): + logging.error('password or port_password not specified') + print_help(is_local) + sys.exit(2) + + if 'local_port' in config: + config['local_port'] = int(config['local_port']) + + if 'server_port' in config and type(config['server_port']) != list: + config['server_port'] = int(config['server_port']) + if config.get('local_address', '') in [b'0.0.0.0']: logging.warn('warning: local set to listen on 0.0.0.0, it\'s not safe') if config.get('server', '') in [b'127.0.0.1', b'localhost']: @@ -214,23 +231,6 @@ def get_config(is_local): sys.exit(2) config['server_port'] = config.get('server_port', 8388) - if is_local and not config.get('password', None): - logging.error('password not specified') - print_help(is_local) - sys.exit(2) - - if not is_local and not config.get('password', None) \ - and not config.get('port_password', None): - logging.error('password or port_password not specified') - print_help(is_local) - sys.exit(2) - - if 'local_port' in config: - config['local_port'] = int(config['local_port']) - - if 'server_port' in config and type(config['server_port']) != list: - config['server_port'] = int(config['server_port']) - logging.getLogger('').handlers = [] logging.addLevelName(VERBOSE_LEVEL, 'VERBOSE') if config['verbose'] >= 2: @@ -247,7 +247,7 @@ def get_config(is_local): format='%(asctime)s %(levelname)-8s %(message)s', datefmt='%Y-%m-%d %H:%M:%S') - check_config(config) + check_config(config, is_local) return config From fbf15cb94201a9f3279d5234c00436b9f2166fdd Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 15:59:44 +0800 Subject: [PATCH 23/39] update command tests --- tests/test_command.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_command.sh b/tests/test_command.sh index 0995299..2b8b68a 100755 --- a/tests/test_command.sh +++ b/tests/test_command.sh @@ -30,10 +30,10 @@ $LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d sto assert "$LOCAL 2>&1 -m rc4-md5 -p 8388 -k testrc4 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": server addr not specified" $LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop -assert "$LOCAL 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": password not specified" +assert "$LOCAL 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" " password not specified" $LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop -assert "$SERVER 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": password or port_password not specified" +assert "$SERVER 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" " password or port_password not specified" $LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop assert "$SERVER 2>&1 --forbidden-ip 127.0.0.1/4a -m rc4-md5 -k 12345 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": Not a valid CIDR notation: 127.0.0.1/4a" From 0cd261dd109ab86ce6842dcd46692cb115c16145 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 16:22:56 +0800 Subject: [PATCH 24/39] catch KeyError --- shadowsocks/local.py | 4 ++-- shadowsocks/server.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shadowsocks/local.py b/shadowsocks/local.py index 3cf3da7..c1a84f1 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -72,12 +72,12 @@ def main(): daemon.set_user(config.get('user', None)) loop.run() - except (KeyboardInterrupt, IOError, OSError) as e: + except Exception as e: logging.error(e) if config['verbose']: import traceback traceback.print_exc() - os._exit(1) + sys.exit(1) if __name__ == '__main__': main() diff --git a/shadowsocks/server.py b/shadowsocks/server.py index eca43ee..41c70ca 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -87,12 +87,12 @@ def main(): daemon.set_user(config.get('user', None)) loop.run() - except (KeyboardInterrupt, IOError, OSError) as e: + except Exception as e: logging.error(e) if config['verbose']: import traceback traceback.print_exc() - os._exit(1) + sys.exit(1) if int(config['workers']) > 1: if os.name == 'posix': From 27a0c7754d335725b478e0fe5a3019b52fa6d024 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 16:23:17 +0800 Subject: [PATCH 25/39] bump --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 33cc44c..ff9ac69 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with codecs.open('README.rst', encoding='utf-8') as f: setup( name="shadowsocks", - version="2.6.6", + version="2.6.7", license='MIT', description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', From 2c59ffb952efab2ff16f28a3bba5217e8611fa05 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 16:42:37 +0800 Subject: [PATCH 26/39] update rst --- README.rst | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 510b233..40c9ad1 100644 --- a/README.rst +++ b/README.rst @@ -12,7 +12,6 @@ Install ~~~~~~~ Debian / Ubuntu: -^^^^^^^^^^^^^^^^ :: @@ -20,7 +19,6 @@ Debian / Ubuntu: pip install shadowsocks CentOS: -^^^^^^^ :: @@ -28,7 +26,6 @@ CentOS: pip install shadowsocks Windows: -^^^^^^^^ See `Install Server on Windows `__ @@ -38,14 +35,25 @@ Usage :: - ssserver -p 8000 -k password -m rc4-md5 + ssserver -p 443 -k password -m rc4-md5 To run in the background: :: - ssserver -p 8000 -k password -m rc4-md5 -d start - ssserver -p 8000 -k password -m rc4-md5 -d stop + sudo ssserver -p 443 -k password -m rc4-md5 --user nobody -d start + +To stop: + +:: + + sudo ssserver -d stop + +To check the log: + +:: + + sudo less /var/log/shadowsocks.log Check all the options via ``-h``. You can also use a `Configuration `__ @@ -88,4 +96,4 @@ Bugs and Issues .. |Build Status| image:: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat :target: https://travis-ci.org/shadowsocks/shadowsocks .. |Coverage Status| image:: https://jenkins.shadowvpn.org/result/shadowsocks - :target: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/htmlcov/index.html + :target: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/PYENV/py34/label/linux/htmlcov/index.html From 96a86c028df463b8a051711111b486d544415b3b Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 17:29:57 +0800 Subject: [PATCH 27/39] update CHANGES --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 6e0331f..8004a3f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +2.6.7 2015-02-02 +- Support --user +- Support CIDR format in --forbidden-ip +- Minor fixes + 2.6.6 2015-01-23 - Fix a crash in forbidden list From ce805f0aeaea03646e01b623c4e2185f63a3562f Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 3 Feb 2015 14:10:36 +0800 Subject: [PATCH 28/39] license under Apache License v2.0 --- LICENSE | 215 ++++++++++++++++++++++++++++++--- README.md | 15 ++- README.rst | 16 ++- setup.py | 6 +- shadowsocks/__init__.py | 28 ++--- shadowsocks/asyncdns.py | 28 ++--- shadowsocks/common.py | 28 ++--- shadowsocks/crypto/__init__.py | 28 ++--- shadowsocks/crypto/openssl.py | 28 ++--- shadowsocks/crypto/rc4_md5.py | 28 ++--- shadowsocks/crypto/sodium.py | 28 ++--- shadowsocks/crypto/table.py | 28 ++--- shadowsocks/crypto/util.py | 28 ++--- shadowsocks/daemon.py | 28 ++--- shadowsocks/encrypt.py | 28 ++--- shadowsocks/eventloop.py | 28 ++--- shadowsocks/local.py | 28 ++--- shadowsocks/lru_cache.py | 28 ++--- shadowsocks/server.py | 28 ++--- shadowsocks/tcprelay.py | 28 ++--- shadowsocks/udprelay.py | 28 ++--- shadowsocks/utils.py | 28 ++--- tests/coverage_server.py | 14 +++ tests/nose_plugin.py | 16 +++ tests/test.py | 28 ++--- 25 files changed, 469 insertions(+), 345 deletions(-) diff --git a/LICENSE b/LICENSE index 76886fa..d645695 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,202 @@ -Shadowsocks -Copyright (c) 2012-2015 clowwindy + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. + 1. Definitions. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 24b7ed1..aee2bdc 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,20 @@ You can find all the documentation in the [Wiki]. License ------- -MIT + +Copyright 2015 clowwindy + +Licensed under the Apache License, Version 2.0 (the "License"); you may +not use this file except in compliance with the License. You may obtain +a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. Bugs and Issues ---------------- diff --git a/README.rst b/README.rst index 40c9ad1..bf2a3ec 100644 --- a/README.rst +++ b/README.rst @@ -81,7 +81,21 @@ You can find all the documentation in the License ------- -MIT +Copyright 2015 clowwindy + +Licensed under the Apache License, Version 2.0 (the "License"); you may +not use this file except in compliance with the License. You may obtain +a copy of the License at + +:: + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. Bugs and Issues --------------- diff --git a/setup.py b/setup.py index ff9ac69..9485ce2 100644 --- a/setup.py +++ b/setup.py @@ -7,8 +7,8 @@ with codecs.open('README.rst', encoding='utf-8') as f: setup( name="shadowsocks", - version="2.6.7", - license='MIT', + version="2.6.8", + license='http://www.apache.org/licenses/LICENSE-2.0', description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', author_email='clowwindy42@gmail.com', @@ -24,7 +24,7 @@ setup( ssserver = shadowsocks.server:main """, classifiers=[ - 'License :: OSI Approved :: MIT License', + 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', diff --git a/shadowsocks/__init__.py b/shadowsocks/__init__.py index 5ba5908..dc3abd4 100644 --- a/shadowsocks/__init__.py +++ b/shadowsocks/__init__.py @@ -1,24 +1,18 @@ #!/usr/bin/python - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2012-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/asyncdns.py b/shadowsocks/asyncdns.py index 6fee6b9..73fab9c 100644 --- a/shadowsocks/asyncdns.py +++ b/shadowsocks/asyncdns.py @@ -1,25 +1,19 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2014-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/common.py b/shadowsocks/common.py index 1c10671..1977dcd 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -1,25 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2013-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/crypto/__init__.py b/shadowsocks/crypto/__init__.py index 6251321..401c7b7 100644 --- a/shadowsocks/crypto/__init__.py +++ b/shadowsocks/crypto/__init__.py @@ -1,24 +1,18 @@ #!/usr/bin/env python - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/crypto/openssl.py b/shadowsocks/crypto/openssl.py index bb11627..d801730 100644 --- a/shadowsocks/crypto/openssl.py +++ b/shadowsocks/crypto/openssl.py @@ -1,24 +1,18 @@ #!/usr/bin/env python - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/crypto/rc4_md5.py b/shadowsocks/crypto/rc4_md5.py index 33d481d..40c344d 100644 --- a/shadowsocks/crypto/rc4_md5.py +++ b/shadowsocks/crypto/rc4_md5.py @@ -1,24 +1,18 @@ #!/usr/bin/env python - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/crypto/sodium.py b/shadowsocks/crypto/sodium.py index 74fbb33..0c1b2f1 100644 --- a/shadowsocks/crypto/sodium.py +++ b/shadowsocks/crypto/sodium.py @@ -1,24 +1,18 @@ #!/usr/bin/env python - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/crypto/table.py b/shadowsocks/crypto/table.py index 08c1205..555fd0a 100644 --- a/shadowsocks/crypto/table.py +++ b/shadowsocks/crypto/table.py @@ -1,24 +1,18 @@ # !/usr/bin/env python - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/crypto/util.py b/shadowsocks/crypto/util.py index 1242f8e..e579455 100644 --- a/shadowsocks/crypto/util.py +++ b/shadowsocks/crypto/util.py @@ -1,24 +1,18 @@ #!/usr/bin/env python - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index 9961ea7..a73e927 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -1,25 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2014-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/encrypt.py b/shadowsocks/encrypt.py index 3dd9264..9365a98 100644 --- a/shadowsocks/encrypt.py +++ b/shadowsocks/encrypt.py @@ -1,24 +1,18 @@ #!/usr/bin/env python - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2012-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/eventloop.py b/shadowsocks/eventloop.py index 304b229..839842d 100644 --- a/shadowsocks/eventloop.py +++ b/shadowsocks/eventloop.py @@ -1,25 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2013-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. # from ssloop # https://github.com/clowwindy/ssloop diff --git a/shadowsocks/local.py b/shadowsocks/local.py index c1a84f1..9520c68 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -1,25 +1,19 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2012-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/lru_cache.py b/shadowsocks/lru_cache.py index 4523399..27c0738 100644 --- a/shadowsocks/lru_cache.py +++ b/shadowsocks/lru_cache.py @@ -1,25 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 41c70ca..195f17b 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -1,25 +1,19 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index d68e424..03cd9b5 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -1,25 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- - -# Copyright (c) 2015 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index ff6391c..9e5463e 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -1,25 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. # SOCKS5 UDP Request # +----+------+------+----------+----------+----------+ diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 417348c..4a462f8 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -1,25 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement diff --git a/tests/coverage_server.py b/tests/coverage_server.py index d752cff..23cc8cd 100644 --- a/tests/coverage_server.py +++ b/tests/coverage_server.py @@ -1,4 +1,18 @@ #!/usr/bin/env python +# +# Copyright 2015 clowwindy +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. if __name__ == '__main__': import tornado.ioloop diff --git a/tests/nose_plugin.py b/tests/nose_plugin.py index ad32cf0..86b1a86 100644 --- a/tests/nose_plugin.py +++ b/tests/nose_plugin.py @@ -1,3 +1,19 @@ +#!/usr/bin/env python +# +# Copyright 2015 clowwindy +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + import nose from nose.plugins.base import Plugin diff --git a/tests/test.py b/tests/test.py index 5ad94c9..4953476 100755 --- a/tests/test.py +++ b/tests/test.py @@ -1,25 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- - -# Copyright (c) 2014 clowwindy # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import absolute_import, division, print_function, \ with_statement From 318d88ec89d39d8b5ef458861c6beaab2ddb107a Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 3 Feb 2015 18:09:07 +0800 Subject: [PATCH 29/39] use string more --- shadowsocks/crypto/openssl.py | 64 +++++++++++++++++------------------ shadowsocks/crypto/rc4_md5.py | 6 ++-- shadowsocks/crypto/sodium.py | 16 ++++----- shadowsocks/crypto/table.py | 6 ++-- shadowsocks/encrypt.py | 18 +++++----- shadowsocks/utils.py | 6 ++-- 6 files changed, 57 insertions(+), 59 deletions(-) diff --git a/shadowsocks/crypto/openssl.py b/shadowsocks/crypto/openssl.py index d801730..d2216c8 100644 --- a/shadowsocks/crypto/openssl.py +++ b/shadowsocks/crypto/openssl.py @@ -111,31 +111,31 @@ class OpenSSLCrypto(object): ciphers = { - b'aes-128-cfb': (16, 16, OpenSSLCrypto), - b'aes-192-cfb': (24, 16, OpenSSLCrypto), - b'aes-256-cfb': (32, 16, OpenSSLCrypto), - b'aes-128-ofb': (16, 16, OpenSSLCrypto), - b'aes-192-ofb': (24, 16, OpenSSLCrypto), - b'aes-256-ofb': (32, 16, OpenSSLCrypto), - b'aes-128-ctr': (16, 16, OpenSSLCrypto), - b'aes-192-ctr': (24, 16, OpenSSLCrypto), - b'aes-256-ctr': (32, 16, OpenSSLCrypto), - b'aes-128-cfb8': (16, 16, OpenSSLCrypto), - b'aes-192-cfb8': (24, 16, OpenSSLCrypto), - b'aes-256-cfb8': (32, 16, OpenSSLCrypto), - b'aes-128-cfb1': (16, 16, OpenSSLCrypto), - b'aes-192-cfb1': (24, 16, OpenSSLCrypto), - b'aes-256-cfb1': (32, 16, OpenSSLCrypto), - b'bf-cfb': (16, 8, OpenSSLCrypto), - b'camellia-128-cfb': (16, 16, OpenSSLCrypto), - b'camellia-192-cfb': (24, 16, OpenSSLCrypto), - b'camellia-256-cfb': (32, 16, OpenSSLCrypto), - b'cast5-cfb': (16, 8, OpenSSLCrypto), - b'des-cfb': (8, 8, OpenSSLCrypto), - b'idea-cfb': (16, 8, OpenSSLCrypto), - b'rc2-cfb': (16, 8, OpenSSLCrypto), - b'rc4': (16, 0, OpenSSLCrypto), - b'seed-cfb': (16, 16, OpenSSLCrypto), + 'aes-128-cfb': (16, 16, OpenSSLCrypto), + 'aes-192-cfb': (24, 16, OpenSSLCrypto), + 'aes-256-cfb': (32, 16, OpenSSLCrypto), + 'aes-128-ofb': (16, 16, OpenSSLCrypto), + 'aes-192-ofb': (24, 16, OpenSSLCrypto), + 'aes-256-ofb': (32, 16, OpenSSLCrypto), + 'aes-128-ctr': (16, 16, OpenSSLCrypto), + 'aes-192-ctr': (24, 16, OpenSSLCrypto), + 'aes-256-ctr': (32, 16, OpenSSLCrypto), + 'aes-128-cfb8': (16, 16, OpenSSLCrypto), + 'aes-192-cfb8': (24, 16, OpenSSLCrypto), + 'aes-256-cfb8': (32, 16, OpenSSLCrypto), + 'aes-128-cfb1': (16, 16, OpenSSLCrypto), + 'aes-192-cfb1': (24, 16, OpenSSLCrypto), + 'aes-256-cfb1': (32, 16, OpenSSLCrypto), + 'bf-cfb': (16, 8, OpenSSLCrypto), + 'camellia-128-cfb': (16, 16, OpenSSLCrypto), + 'camellia-192-cfb': (24, 16, OpenSSLCrypto), + 'camellia-256-cfb': (32, 16, OpenSSLCrypto), + 'cast5-cfb': (16, 8, OpenSSLCrypto), + 'des-cfb': (8, 8, OpenSSLCrypto), + 'idea-cfb': (16, 8, OpenSSLCrypto), + 'rc2-cfb': (16, 8, OpenSSLCrypto), + 'rc4': (16, 0, OpenSSLCrypto), + 'seed-cfb': (16, 16, OpenSSLCrypto), } @@ -148,31 +148,31 @@ def run_method(method): def test_aes_128_cfb(): - run_method(b'aes-128-cfb') + run_method('aes-128-cfb') def test_aes_256_cfb(): - run_method(b'aes-256-cfb') + run_method('aes-256-cfb') def test_aes_128_cfb8(): - run_method(b'aes-128-cfb8') + run_method('aes-128-cfb8') def test_aes_256_ofb(): - run_method(b'aes-256-ofb') + run_method('aes-256-ofb') def test_aes_256_ctr(): - run_method(b'aes-256-ctr') + run_method('aes-256-ctr') def test_bf_cfb(): - run_method(b'bf-cfb') + run_method('bf-cfb') def test_rc4(): - run_method(b'rc4') + run_method('rc4') if __name__ == '__main__': diff --git a/shadowsocks/crypto/rc4_md5.py b/shadowsocks/crypto/rc4_md5.py index 40c344d..1f07a82 100644 --- a/shadowsocks/crypto/rc4_md5.py +++ b/shadowsocks/crypto/rc4_md5.py @@ -34,15 +34,15 @@ def create_cipher(alg, key, iv, op, key_as_bytes=0, d=None, salt=None, ciphers = { - b'rc4-md5': (16, 16, create_cipher), + 'rc4-md5': (16, 16, create_cipher), } def test(): from shadowsocks.crypto import util - cipher = create_cipher(b'rc4-md5', b'k' * 32, b'i' * 16, 1) - decipher = create_cipher(b'rc4-md5', b'k' * 32, b'i' * 16, 0) + cipher = create_cipher('rc4-md5', b'k' * 32, b'i' * 16, 1) + decipher = create_cipher('rc4-md5', b'k' * 32, b'i' * 16, 0) util.run_cipher(cipher, decipher) diff --git a/shadowsocks/crypto/sodium.py b/shadowsocks/crypto/sodium.py index 0c1b2f1..ae86fef 100644 --- a/shadowsocks/crypto/sodium.py +++ b/shadowsocks/crypto/sodium.py @@ -64,9 +64,9 @@ class SodiumCrypto(object): self.iv = iv self.key_ptr = c_char_p(key) self.iv_ptr = c_char_p(iv) - if cipher_name == b'salsa20': + if cipher_name == 'salsa20': self.cipher = libsodium.crypto_stream_salsa20_xor_ic - elif cipher_name == b'chacha20': + elif cipher_name == 'chacha20': self.cipher = libsodium.crypto_stream_chacha20_xor_ic else: raise Exception('Unknown cipher') @@ -95,22 +95,22 @@ class SodiumCrypto(object): ciphers = { - b'salsa20': (32, 8, SodiumCrypto), - b'chacha20': (32, 8, SodiumCrypto), + 'salsa20': (32, 8, SodiumCrypto), + 'chacha20': (32, 8, SodiumCrypto), } def test_salsa20(): - cipher = SodiumCrypto(b'salsa20', b'k' * 32, b'i' * 16, 1) - decipher = SodiumCrypto(b'salsa20', b'k' * 32, b'i' * 16, 0) + cipher = SodiumCrypto('salsa20', b'k' * 32, b'i' * 16, 1) + decipher = SodiumCrypto('salsa20', b'k' * 32, b'i' * 16, 0) util.run_cipher(cipher, decipher) def test_chacha20(): - cipher = SodiumCrypto(b'chacha20', b'k' * 32, b'i' * 16, 1) - decipher = SodiumCrypto(b'chacha20', b'k' * 32, b'i' * 16, 0) + cipher = SodiumCrypto('chacha20', b'k' * 32, b'i' * 16, 1) + decipher = SodiumCrypto('chacha20', b'k' * 32, b'i' * 16, 0) util.run_cipher(cipher, decipher) diff --git a/shadowsocks/crypto/table.py b/shadowsocks/crypto/table.py index 555fd0a..bc693f5 100644 --- a/shadowsocks/crypto/table.py +++ b/shadowsocks/crypto/table.py @@ -67,7 +67,7 @@ class TableCipher(object): ciphers = { - b'table': (0, 0, TableCipher) + 'table': (0, 0, TableCipher) } @@ -163,8 +163,8 @@ def test_table_result(): def test_encryption(): from shadowsocks.crypto import util - cipher = TableCipher(b'table', b'test', b'', 1) - decipher = TableCipher(b'table', b'test', b'', 0) + cipher = TableCipher('table', b'test', b'', 1) + decipher = TableCipher('table', b'test', b'', 0) util.run_cipher(cipher, decipher) diff --git a/shadowsocks/encrypt.py b/shadowsocks/encrypt.py index 9365a98..4e87f41 100644 --- a/shadowsocks/encrypt.py +++ b/shadowsocks/encrypt.py @@ -22,6 +22,7 @@ import sys import hashlib import logging +from shadowsocks import common from shadowsocks.crypto import rc4_md5, openssl, sodium, table @@ -46,8 +47,6 @@ def try_cipher(key, method=None): def EVP_BytesToKey(password, key_len, iv_len): # equivalent to OpenSSL's EVP_BytesToKey() with count 1 # so that we make the same key and iv as nodejs version - if hasattr(password, 'encode'): - password = password.encode('utf-8') cached_key = '%s-%d-%d' % (password, key_len, iv_len) r = cached_keys.get(cached_key, None) if r: @@ -95,8 +94,7 @@ class Encryptor(object): return len(self.cipher_iv) def get_cipher(self, password, method, op, iv): - if hasattr(password, 'encode'): - password = password.encode('utf-8') + password = common.to_bytes(password) m = self._method_info if m[0] > 0: key, iv_ = EVP_BytesToKey(password, m[0], m[1]) @@ -153,12 +151,12 @@ def encrypt_all(password, method, op, data): CIPHERS_TO_TEST = [ - b'aes-128-cfb', - b'aes-256-cfb', - b'rc4-md5', - b'salsa20', - b'chacha20', - b'table', + 'aes-128-cfb', + 'aes-256-cfb', + 'rc4-md5', + 'salsa20', + 'chacha20', + 'table', ] diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 4a462f8..c261148 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -154,11 +154,11 @@ def get_config(is_local): elif key == '-l': config['local_port'] = int(value) elif key == '-s': - config['server'] = to_bytes(value) + config['server'] = to_str(value) elif key == '-m': - config['method'] = to_bytes(value) + config['method'] = to_str(value) elif key == '-b': - config['local_address'] = to_bytes(value) + config['local_address'] = to_str(value) elif key == '-v': v_count += 1 # '-vv' turns on more verbose mode From 6d09cd21ca6acd506a42858b123ece1ab4e6d423 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 3 Feb 2015 18:13:01 +0800 Subject: [PATCH 30/39] fix openssl --- shadowsocks/crypto/openssl.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shadowsocks/crypto/openssl.py b/shadowsocks/crypto/openssl.py index d2216c8..3775b6c 100644 --- a/shadowsocks/crypto/openssl.py +++ b/shadowsocks/crypto/openssl.py @@ -20,6 +20,7 @@ from __future__ import absolute_import, division, print_function, \ from ctypes import c_char_p, c_int, c_long, byref,\ create_string_buffer, c_void_p +from shadowsocks import common from shadowsocks.crypto import util __all__ = ['ciphers'] @@ -58,7 +59,7 @@ def load_openssl(): def load_cipher(cipher_name): - func_name = b'EVP_' + cipher_name.replace(b'-', b'_') + func_name = 'EVP_' + cipher_name.replace('-', '_') if bytes != str: func_name = str(func_name, 'utf-8') cipher = getattr(libcrypto, func_name, None) @@ -73,6 +74,7 @@ class OpenSSLCrypto(object): self._ctx = None if not loaded: load_openssl() + cipher_name = common.to_bytes(cipher_name) cipher = libcrypto.EVP_get_cipherbyname(cipher_name) if not cipher: cipher = load_cipher(cipher_name) From e564f17505f6e935b7893b42d357aae672942646 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 3 Feb 2015 18:23:22 +0800 Subject: [PATCH 31/39] fix bytes in utils --- shadowsocks/utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index c261148..24e9b46 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -90,10 +90,10 @@ def check_config(config, is_local): if config.get('server', '') in [b'127.0.0.1', b'localhost']: logging.warn('warning: server set to listen on %s:%s, are you sure?' % (to_str(config['server']), config['server_port'])) - if (config.get('method', '') or '').lower() == b'table': + if (config.get('method', '') or '').lower() == 'table': logging.warn('warning: table is not safe; please use a safer cipher, ' 'like AES-256-CFB') - if (config.get('method', '') or '').lower() == b'rc4': + if (config.get('method', '') or '').lower() == 'rc4': logging.warn('warning: RC4 is not safe; please use a safer cipher, ' 'like AES-256-CFB') if config.get('timeout', 300) < 100: @@ -198,8 +198,8 @@ def get_config(is_local): print_help(is_local) sys.exit(2) - config['password'] = config.get('password', '') - config['method'] = config.get('method', b'aes-256-cfb') + config['password'] = to_bytes(config.get('password', b'')) + config['method'] = to_str(config.get('method', 'aes-256-cfb')) config['port_password'] = config.get('port_password', None) config['timeout'] = int(config.get('timeout', 300)) config['fast_open'] = config.get('fast_open', False) @@ -208,7 +208,7 @@ def get_config(is_local): config['log-file'] = config.get('log-file', '/var/log/shadowsocks.log') config['workers'] = config.get('workers', 1) config['verbose'] = config.get('verbose', False) - config['local_address'] = config.get('local_address', '127.0.0.1') + config['local_address'] = to_str(config.get('local_address', '127.0.0.1')) config['local_port'] = config.get('local_port', 1080) if is_local: if config.get('server', None) is None: From e71ce6c758643f06c05de06d5dd10cb7fe205378 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 3 Feb 2015 18:45:36 +0800 Subject: [PATCH 32/39] fix server check --- shadowsocks/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 24e9b46..96abdb6 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -87,7 +87,7 @@ def check_config(config, is_local): if config.get('local_address', '') in [b'0.0.0.0']: logging.warn('warning: local set to listen on 0.0.0.0, it\'s not safe') - if config.get('server', '') in [b'127.0.0.1', b'localhost']: + if config.get('server', '') in ['127.0.0.1', 'localhost']: logging.warn('warning: server set to listen on %s:%s, are you sure?' % (to_str(config['server']), config['server_port'])) if (config.get('method', '') or '').lower() == 'table': @@ -215,8 +215,10 @@ def get_config(is_local): logging.error('server addr not specified') print_local_help() sys.exit(2) + else: + config['server'] = to_str(config['server']) else: - config['server'] = config.get('server', '0.0.0.0') + config['server'] = to_str(config.get('server', '0.0.0.0')) try: config['forbidden_ip'] = \ IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1/128')) From 783a6ef7f294b71e22b4d485545b4a7c32a651c6 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 9 Feb 2015 13:50:46 +0800 Subject: [PATCH 33/39] support multiple server ip on client side --- .jenkins.sh | 1 + shadowsocks/tcprelay.py | 3 ++- shadowsocks/udprelay.py | 3 ++- tests/client-multi-server-ip.json | 10 ++++++++++ 4 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 tests/client-multi-server-ip.json diff --git a/.jenkins.sh b/.jenkins.sh index 73c4abc..a811983 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -41,6 +41,7 @@ run_test python tests/test.py --with-coverage -c tests/salsa20.json run_test python tests/test.py --with-coverage -c tests/chacha20.json run_test python tests/test.py --with-coverage -c tests/table.json run_test python tests/test.py --with-coverage -c tests/server-multi-ports.json +run_test python tests/test.py --with-coverage -s tests/aes.json -c tests/client-multi-server-ip.json run_test python tests/test.py --with-coverage -s tests/server-multi-passwd.json -c tests/server-multi-passwd-client-side.json run_test python tests/test.py --with-coverage -c tests/workers.json run_test python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-client-side.json diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 03cd9b5..f65b282 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -144,8 +144,9 @@ class TCPRelayHandler(object): server_port = self._config['server_port'] if type(server_port) == list: server_port = random.choice(server_port) + if type(server) == list: + server = random.choice(server) logging.debug('chosen server: %s:%d', server, server_port) - # TODO support multiple server IP return server, server_port def _update_activity(self): diff --git a/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index 9e5463e..0499f0e 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -127,8 +127,9 @@ class UDPRelay(object): server_port = self._config['server_port'] if type(server_port) == list: server_port = random.choice(server_port) + if type(server) == list: + server = random.choice(server) logging.debug('chosen server: %s:%d', server, server_port) - # TODO support multiple server IP return server, server_port def _close_client(self, client): diff --git a/tests/client-multi-server-ip.json b/tests/client-multi-server-ip.json new file mode 100644 index 0000000..1823c2a --- /dev/null +++ b/tests/client-multi-server-ip.json @@ -0,0 +1,10 @@ +{ + "server":["127.0.0.1", "127.0.0.1"], + "server_port":8388, + "local_port":1081, + "password":"aes_password", + "timeout":60, + "method":"aes-256-cfb", + "local_address":"127.0.0.1", + "fast_open":false +} From dfd81af8445a4d9768cae45246de1c35102013ac Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 9 Feb 2015 14:43:11 +0800 Subject: [PATCH 34/39] support --version --- .jenkins.sh | 2 +- shadowsocks/local.py | 2 -- shadowsocks/server.py | 2 -- shadowsocks/tcprelay.py | 2 +- shadowsocks/utils.py | 16 +++++++++++----- tests/test_command.sh | 3 +++ 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/.jenkins.sh b/.jenkins.sh index a811983..2cd02a2 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -27,7 +27,7 @@ function run_test { python --version coverage erase mkdir tmp -run_test pep8 . +run_test pep8 --ignore=E402 . run_test pyflakes . run_test coverage run tests/nose_plugin.py -v run_test python setup.py sdist diff --git a/shadowsocks/local.py b/shadowsocks/local.py index 9520c68..b75fe9b 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -40,8 +40,6 @@ def main(): daemon.daemon_exec(config) - utils.print_shadowsocks() - try: logging.info("starting local at %s:%d" % (config['local_address'], config['local_port'])) diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 195f17b..34aab2a 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -34,8 +34,6 @@ def main(): daemon.daemon_exec(config) - utils.print_shadowsocks() - if config['port_password']: if config['password']: logging.warn('warning: port_password should not be used with ' diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index f65b282..12792f7 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -328,7 +328,7 @@ class TCPRelayHandler(object): addrs = socket.getaddrinfo(ip, port, 0, socket.SOCK_STREAM, socket.SOL_TCP) if len(addrs) == 0: - raise Exception("getaddrinfo failed for %s:%d" % (ip, port)) + raise Exception("getaddrinfo failed for %s:%d" % (ip, port)) af, socktype, proto, canonname, sa = addrs[0] if self._forbidden_iplist: if common.to_str(sa[0]) in self._forbidden_iplist: diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 96abdb6..71c6d94 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -50,7 +50,7 @@ def print_shadowsocks(): version = pkg_resources.get_distribution('shadowsocks').version except Exception: pass - print('shadowsocks %s' % version) + print('Shadowsocks %s' % version) def find_config(): @@ -119,11 +119,12 @@ def get_config(is_local): format='%(levelname)-s: %(message)s') if is_local: shortopts = 'hd:s:b:p:k:l:m:c:t:vq' - longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'user='] + longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'user=', + 'version'] else: shortopts = 'hd:s:p:k:m:c:t:vq' longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers=', - 'forbidden-ip=', 'user='] + 'forbidden-ip=', 'user=', 'version'] try: config_path = find_config() optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts) @@ -179,6 +180,9 @@ def get_config(is_local): else: print_server_help() sys.exit(0) + elif key == '--version': + print_shadowsocks() + sys.exit(0) elif key == '-d': config['daemon'] = to_str(value) elif key == '--pid-file': @@ -262,7 +266,6 @@ A fast tunnel proxy that helps you bypass firewalls. You can supply configurations via either config file or command line arguments. Proxy options: - -h, --help show this help message and exit -c CONFIG path to config file -s SERVER_ADDR server address -p SERVER_PORT server port, default: 8388 @@ -274,12 +277,14 @@ Proxy options: --fast-open use TCP_FASTOPEN, requires Linux 3.7+ General options: + -h, --help show this help message and exit -d start/stop/restart daemon mode --pid-file PID_FILE pid file for daemon mode --log-file LOG_FILE log file for daemon mode --user USER username to run as -v, -vv verbose mode -q, -qq quiet mode, only show warnings/errors + --version show version information Online help: ''') @@ -292,7 +297,6 @@ A fast tunnel proxy that helps you bypass firewalls. You can supply configurations via either config file or command line arguments. Proxy options: - -h, --help show this help message and exit -c CONFIG path to config file -s SERVER_ADDR server address, default: 0.0.0.0 -p SERVER_PORT server port, default: 8388 @@ -304,12 +308,14 @@ Proxy options: --forbidden-ip IPLIST comma seperated IP list forbidden to connect General options: + -h, --help show this help message and exit -d start/stop/restart daemon mode --pid-file PID_FILE pid file for daemon mode --log-file LOG_FILE log file for daemon mode --user USER username to run as -v, -vv verbose mode -q, -qq quiet mode, only show warnings/errors + --version show version information Online help: ''') diff --git a/tests/test_command.sh b/tests/test_command.sh index 2b8b68a..be05704 100755 --- a/tests/test_command.sh +++ b/tests/test_command.sh @@ -6,6 +6,9 @@ PYTHON="coverage run -a -p" LOCAL="$PYTHON shadowsocks/local.py" SERVER="$PYTHON shadowsocks/server.py" +assert "$LOCAL --version 2>&1 | grep Shadowsocks | awk -F\" \" '{print \$1}'" "Shadowsocks" +assert "$SERVER --version 2>&1 | grep Shadowsocks | awk -F\" \" '{print \$1}'" "Shadowsocks" + assert "$LOCAL 2>&1 | grep ERROR" "ERROR: config not specified" assert "$LOCAL 2>&1 | grep usage | cut -d: -f1" "usage" From 48ddc1714ba774d34073e0e34c7e662583365b83 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 10 Feb 2015 17:02:31 +0800 Subject: [PATCH 35/39] move jenkins.sh --- .travis.yml | 2 +- .jenkins.sh => tests/jenkins.sh | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename .jenkins.sh => tests/jenkins.sh (100%) diff --git a/.travis.yml b/.travis.yml index 7a222ea..c5caa33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,4 +17,4 @@ before_install: - sudo tests/libsodium/install.sh - sudo tests/setup_tc.sh script: - - ./.jenkins.sh + - tests/jenkins.sh diff --git a/.jenkins.sh b/tests/jenkins.sh similarity index 100% rename from .jenkins.sh rename to tests/jenkins.sh From cb7062e1c1bcbec1b2a699a016a774e4d757389b Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 10 Feb 2015 17:16:24 +0800 Subject: [PATCH 36/39] implement utils.print_exception() Previously we used logging.error(e) and traceback.print_exc() to output error stack trace. The problem is, we want to output the stack trace only when verbose > 0. The if statement scattered around the code. So we replaced them with the new utils.print_exception() call. --- shadowsocks/asyncdns.py | 6 ++---- shadowsocks/daemon.py | 10 +++++----- shadowsocks/eventloop.py | 6 +++--- shadowsocks/local.py | 5 +---- shadowsocks/server.py | 5 +---- shadowsocks/tcprelay.py | 12 +++++------- shadowsocks/udprelay.py | 4 ++-- shadowsocks/utils.py | 13 +++++++++++++ 8 files changed, 32 insertions(+), 29 deletions(-) diff --git a/shadowsocks/asyncdns.py b/shadowsocks/asyncdns.py index 73fab9c..9467490 100644 --- a/shadowsocks/asyncdns.py +++ b/shadowsocks/asyncdns.py @@ -25,7 +25,7 @@ import struct import re import logging -from shadowsocks import common, lru_cache, eventloop +from shadowsocks import common, lru_cache, eventloop, utils CACHE_SWEEP_INTERVAL = 30 @@ -221,9 +221,7 @@ def parse_response(data): response.answers.append((an[1], an[2], an[3])) return response except Exception as e: - import traceback - traceback.print_exc() - logging.error(e) + utils.print_exception(e) return None diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index a73e927..c23ec58 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -23,7 +23,7 @@ import sys import logging import signal import time -from shadowsocks import common +from shadowsocks import common, utils # this module is ported from ShadowVPN daemon.c @@ -58,7 +58,7 @@ def write_pid_file(pid_file, pid): fd = os.open(pid_file, os.O_RDWR | os.O_CREAT, stat.S_IRUSR | stat.S_IWUSR) except OSError as e: - logging.error(e) + utils.print_exception(e) return -1 flags = fcntl.fcntl(fd, fcntl.F_GETFD) assert flags != -1 @@ -127,7 +127,7 @@ def daemon_start(pid_file, log_file): freopen(log_file, 'a', sys.stdout) freopen(log_file, 'a', sys.stderr) except IOError as e: - logging.error(e) + utils.print_exception(e) sys.exit(1) @@ -140,7 +140,7 @@ def daemon_stop(pid_file): if not buf: logging.error('not running') except IOError as e: - logging.error(e) + utils.print_exception(e) if e.errno == errno.ENOENT: # always exit 0 if we are sure daemon is not running logging.error('not running') @@ -155,7 +155,7 @@ def daemon_stop(pid_file): logging.error('not running') # always exit 0 if we are sure daemon is not running return - logging.error(e) + utils.print_exception(e) sys.exit(1) else: logging.error('pid is not positive: %d', pid) diff --git a/shadowsocks/eventloop.py b/shadowsocks/eventloop.py index 839842d..77c64ef 100644 --- a/shadowsocks/eventloop.py +++ b/shadowsocks/eventloop.py @@ -28,6 +28,8 @@ import errno import logging from collections import defaultdict +from shadowsocks import utils + __all__ = ['EventLoop', 'POLL_NULL', 'POLL_IN', 'POLL_OUT', 'POLL_ERR', 'POLL_HUP', 'POLL_NVAL', 'EVENT_NAMES'] @@ -223,9 +225,7 @@ class EventLoop(object): try: handler(events) except (OSError, IOError) as e: - logging.error(e) - import traceback - traceback.print_exc() + utils.print_exception(e) if self._handlers_to_remove: for handler in self._handlers_to_remove: self._handlers.remove(handler) diff --git a/shadowsocks/local.py b/shadowsocks/local.py index b75fe9b..a4c853e 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -65,10 +65,7 @@ def main(): daemon.set_user(config.get('user', None)) loop.run() except Exception as e: - logging.error(e) - if config['verbose']: - import traceback - traceback.print_exc() + utils.print_exception(e) sys.exit(1) if __name__ == '__main__': diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 34aab2a..27515b5 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -80,10 +80,7 @@ def main(): daemon.set_user(config.get('user', None)) loop.run() except Exception as e: - logging.error(e) - if config['verbose']: - import traceback - traceback.print_exc() + utils.print_exception(e) sys.exit(1) if int(config['workers']) > 1: diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 12792f7..a23a58c 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -203,9 +203,7 @@ class TCPRelayHandler(object): errno.EWOULDBLOCK): uncomplete = True else: - logging.error(e) - if self._config['verbose']: - traceback.print_exc() + utils.print_exception(e) self.destroy() return False if uncomplete: @@ -259,7 +257,7 @@ class TCPRelayHandler(object): self._config['fast_open'] = False self.destroy() else: - logging.error(e) + utils.print_exception(e) if self._config['verbose']: traceback.print_exc() self.destroy() @@ -383,7 +381,7 @@ class TCPRelayHandler(object): self._update_stream(STREAM_DOWN, WAIT_STATUS_READING) return except Exception as e: - logging.error(e) + utils.print_exception(e) if self._config['verbose']: traceback.print_exc() self.destroy() @@ -445,7 +443,7 @@ class TCPRelayHandler(object): try: self._write_to_sock(data, self._local_sock) except Exception as e: - logging.error(e) + utils.print_exception(e) if self._config['verbose']: traceback.print_exc() # TODO use logging when debug completed @@ -683,7 +681,7 @@ class TCPRelay(object): errno.EWOULDBLOCK): continue else: - logging.error(e) + utils.print_exception(e) if self._config['verbose']: traceback.print_exc() else: diff --git a/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index 0499f0e..80c44bb 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -69,7 +69,7 @@ import struct import errno import random -from shadowsocks import encrypt, eventloop, lru_cache, common +from shadowsocks import encrypt, eventloop, lru_cache, common, utils from shadowsocks.common import parse_header, pack_addr @@ -208,7 +208,7 @@ class UDPRelay(object): if err in (errno.EINPROGRESS, errno.EAGAIN): pass else: - logging.error(e) + utils.print_exception(e) def _handle_client(self, sock): data, r_addr = sock.recvfrom(BUF_SIZE) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 71c6d94..175feef 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -29,6 +29,8 @@ from shadowsocks import encrypt VERBOSE_LEVEL = 5 +verbose = 0 + def check_python(): info = sys.version_info @@ -43,6 +45,14 @@ def check_python(): sys.exit(1) +def print_exception(e): + global verbose + logging.error(e) + if verbose > 0: + import traceback + traceback.print_exc() + + def print_shadowsocks(): version = '' try: @@ -115,6 +125,8 @@ def check_config(config, is_local): def get_config(is_local): + global verbose + logging.basicConfig(level=logging.INFO, format='%(levelname)-s: %(message)s') if is_local: @@ -243,6 +255,7 @@ def get_config(is_local): level = logging.ERROR else: level = logging.INFO + verbose = config['verbose'] logging.basicConfig(level=level, format='%(asctime)s %(levelname)-8s %(message)s', datefmt='%Y-%m-%d %H:%M:%S') From d774286dc0e6d0b2f8b4f20528a69f7b4ac45a18 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 10 Feb 2015 17:26:10 +0800 Subject: [PATCH 37/39] rename utils module into shell Since utils is ambiguous, we want to give the module a more clear role. --- shadowsocks/asyncdns.py | 4 ++-- shadowsocks/daemon.py | 10 +++++----- shadowsocks/eventloop.py | 4 ++-- shadowsocks/local.py | 8 ++++---- shadowsocks/server.py | 8 ++++---- shadowsocks/{utils.py => shell.py} | 0 shadowsocks/tcprelay.py | 16 ++++++++-------- shadowsocks/udprelay.py | 4 ++-- 8 files changed, 27 insertions(+), 27 deletions(-) rename shadowsocks/{utils.py => shell.py} (100%) diff --git a/shadowsocks/asyncdns.py b/shadowsocks/asyncdns.py index 9467490..7e4a4ed 100644 --- a/shadowsocks/asyncdns.py +++ b/shadowsocks/asyncdns.py @@ -25,7 +25,7 @@ import struct import re import logging -from shadowsocks import common, lru_cache, eventloop, utils +from shadowsocks import common, lru_cache, eventloop, shell CACHE_SWEEP_INTERVAL = 30 @@ -221,7 +221,7 @@ def parse_response(data): response.answers.append((an[1], an[2], an[3])) return response except Exception as e: - utils.print_exception(e) + shell.print_exception(e) return None diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index c23ec58..8dc5608 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -23,7 +23,7 @@ import sys import logging import signal import time -from shadowsocks import common, utils +from shadowsocks import common, shell # this module is ported from ShadowVPN daemon.c @@ -58,7 +58,7 @@ def write_pid_file(pid_file, pid): fd = os.open(pid_file, os.O_RDWR | os.O_CREAT, stat.S_IRUSR | stat.S_IWUSR) except OSError as e: - utils.print_exception(e) + shell.print_exception(e) return -1 flags = fcntl.fcntl(fd, fcntl.F_GETFD) assert flags != -1 @@ -127,7 +127,7 @@ def daemon_start(pid_file, log_file): freopen(log_file, 'a', sys.stdout) freopen(log_file, 'a', sys.stderr) except IOError as e: - utils.print_exception(e) + shell.print_exception(e) sys.exit(1) @@ -140,7 +140,7 @@ def daemon_stop(pid_file): if not buf: logging.error('not running') except IOError as e: - utils.print_exception(e) + shell.print_exception(e) if e.errno == errno.ENOENT: # always exit 0 if we are sure daemon is not running logging.error('not running') @@ -155,7 +155,7 @@ def daemon_stop(pid_file): logging.error('not running') # always exit 0 if we are sure daemon is not running return - utils.print_exception(e) + shell.print_exception(e) sys.exit(1) else: logging.error('pid is not positive: %d', pid) diff --git a/shadowsocks/eventloop.py b/shadowsocks/eventloop.py index 77c64ef..42f9205 100644 --- a/shadowsocks/eventloop.py +++ b/shadowsocks/eventloop.py @@ -28,7 +28,7 @@ import errno import logging from collections import defaultdict -from shadowsocks import utils +from shadowsocks import shell __all__ = ['EventLoop', 'POLL_NULL', 'POLL_IN', 'POLL_OUT', 'POLL_ERR', @@ -225,7 +225,7 @@ class EventLoop(object): try: handler(events) except (OSError, IOError) as e: - utils.print_exception(e) + shell.print_exception(e) if self._handlers_to_remove: for handler in self._handlers_to_remove: self._handlers.remove(handler) diff --git a/shadowsocks/local.py b/shadowsocks/local.py index a4c853e..4255a2e 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -24,11 +24,11 @@ import logging import signal sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) -from shadowsocks import utils, daemon, eventloop, tcprelay, udprelay, asyncdns +from shadowsocks import shell, daemon, eventloop, tcprelay, udprelay, asyncdns def main(): - utils.check_python() + shell.check_python() # fix py2exe if hasattr(sys, "frozen") and sys.frozen in \ @@ -36,7 +36,7 @@ def main(): p = os.path.dirname(os.path.abspath(sys.executable)) os.chdir(p) - config = utils.get_config(True) + config = shell.get_config(True) daemon.daemon_exec(config) @@ -65,7 +65,7 @@ def main(): daemon.set_user(config.get('user', None)) loop.run() except Exception as e: - utils.print_exception(e) + shell.print_exception(e) sys.exit(1) if __name__ == '__main__': diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 27515b5..429a20a 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -24,13 +24,13 @@ import logging import signal sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) -from shadowsocks import utils, daemon, eventloop, tcprelay, udprelay, asyncdns +from shadowsocks import shell, daemon, eventloop, tcprelay, udprelay, asyncdns def main(): - utils.check_python() + shell.check_python() - config = utils.get_config(False) + config = shell.get_config(False) daemon.daemon_exec(config) @@ -80,7 +80,7 @@ def main(): daemon.set_user(config.get('user', None)) loop.run() except Exception as e: - utils.print_exception(e) + shell.print_exception(e) sys.exit(1) if int(config['workers']) > 1: diff --git a/shadowsocks/utils.py b/shadowsocks/shell.py similarity index 100% rename from shadowsocks/utils.py rename to shadowsocks/shell.py diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index a23a58c..c9ed7bd 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -26,7 +26,7 @@ import logging import traceback import random -from shadowsocks import encrypt, eventloop, utils, common +from shadowsocks import encrypt, eventloop, shell, common from shadowsocks.common import parse_header # we clear at most TIMEOUTS_CLEAN_SIZE timeouts each time @@ -203,7 +203,7 @@ class TCPRelayHandler(object): errno.EWOULDBLOCK): uncomplete = True else: - utils.print_exception(e) + shell.print_exception(e) self.destroy() return False if uncomplete: @@ -257,7 +257,7 @@ class TCPRelayHandler(object): self._config['fast_open'] = False self.destroy() else: - utils.print_exception(e) + shell.print_exception(e) if self._config['verbose']: traceback.print_exc() self.destroy() @@ -381,7 +381,7 @@ class TCPRelayHandler(object): self._update_stream(STREAM_DOWN, WAIT_STATUS_READING) return except Exception as e: - utils.print_exception(e) + shell.print_exception(e) if self._config['verbose']: traceback.print_exc() self.destroy() @@ -443,7 +443,7 @@ class TCPRelayHandler(object): try: self._write_to_sock(data, self._local_sock) except Exception as e: - utils.print_exception(e) + shell.print_exception(e) if self._config['verbose']: traceback.print_exc() # TODO use logging when debug completed @@ -630,7 +630,7 @@ class TCPRelay(object): # we just need a sorted last_activity queue and it's faster than heapq # in fact we can do O(1) insertion/remove so we invent our own if self._timeouts: - logging.log(utils.VERBOSE_LEVEL, 'sweeping timeouts') + logging.log(shell.VERBOSE_LEVEL, 'sweeping timeouts') now = time.time() length = len(self._timeouts) pos = self._timeout_offset @@ -663,7 +663,7 @@ class TCPRelay(object): # handle events and dispatch to handlers for sock, fd, event in events: if sock: - logging.log(utils.VERBOSE_LEVEL, 'fd %d %s', fd, + logging.log(shell.VERBOSE_LEVEL, 'fd %d %s', fd, eventloop.EVENT_NAMES.get(event, event)) if sock == self._server_socket: if event & eventloop.POLL_ERR: @@ -681,7 +681,7 @@ class TCPRelay(object): errno.EWOULDBLOCK): continue else: - utils.print_exception(e) + shell.print_exception(e) if self._config['verbose']: traceback.print_exc() else: diff --git a/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index 80c44bb..98bfaaa 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -69,7 +69,7 @@ import struct import errno import random -from shadowsocks import encrypt, eventloop, lru_cache, common, utils +from shadowsocks import encrypt, eventloop, lru_cache, common, shell from shadowsocks.common import parse_header, pack_addr @@ -208,7 +208,7 @@ class UDPRelay(object): if err in (errno.EINPROGRESS, errno.EAGAIN): pass else: - utils.print_exception(e) + shell.print_exception(e) def _handle_client(self, sock): data, r_addr = sock.recvfrom(BUF_SIZE) From 581d6e687f96d05c11f79c50c1abba1be96b9803 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 10 Feb 2015 17:38:01 +0800 Subject: [PATCH 38/39] use localhost in test.py Since now the unit tests is huge, using third party website is not polite. So use localhost instead. --- tests/test.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test.py b/tests/test.py index 4953476..29b57d4 100755 --- a/tests/test.py +++ b/tests/test.py @@ -28,6 +28,8 @@ from subprocess import Popen, PIPE python = ['python'] +default_url = 'http://localhost/' + parser = argparse.ArgumentParser(description='test Shadowsocks') parser.add_argument('-c', '--client-conf', type=str, default=None) parser.add_argument('-s', '--server-conf', type=str, default=None) @@ -36,7 +38,7 @@ parser.add_argument('-b', '--server-args', type=str, default=None) parser.add_argument('--with-coverage', action='store_true', default=None) parser.add_argument('--should-fail', action='store_true', default=None) parser.add_argument('--tcp-only', action='store_true', default=None) -parser.add_argument('--url', type=str, default='http://www.example.com/') +parser.add_argument('--url', type=str, default=default_url) parser.add_argument('--dns', type=str, default='8.8.8.8') config = parser.parse_args() @@ -59,6 +61,8 @@ if config.client_args: server_args.extend(config.server_args.split()) else: server_args.extend(config.client_args.split()) +if config.url == default_url: + server_args.extend(['--forbidden-ip', '']) p1 = Popen(server_args, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) p2 = Popen(client_args, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) From 42ce2569c4774cd021ed88dd7b9393d4cb51595d Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 10 Feb 2015 17:43:06 +0800 Subject: [PATCH 39/39] also test with real website Now that localhost is used in tests, DNS code is uncovered. Use clients1.google.com/generate_204 to test if a real website works. --- tests/jenkins.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/jenkins.sh b/tests/jenkins.sh index 2cd02a2..71d5b1c 100755 --- a/tests/jenkins.sh +++ b/tests/jenkins.sh @@ -49,6 +49,9 @@ run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0 run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --workers 1" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -qq -b 127.0.0.1" run_test python tests/test.py --with-coverage --should-fail --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=127.0.0.1,::1,8.8.8.8" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" +# test if DNS works +run_test python tests/test.py --with-coverage -c tests/aes.json --url="https://clients1.google.com/generate_204" + # test localhost is in the forbidden list by default run_test python tests/test.py --with-coverage --should-fail --tcp-only --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1"