From 2a53b67c65e84124291c8b085f4e70f8127d1c89 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Fri, 31 Oct 2014 18:28:22 +0800 Subject: [PATCH] python 3 support; not stable yet --- shadowsocks/__init__.py | 23 +++++++++++ shadowsocks/asyncdns.py | 43 ++++++++++++--------- shadowsocks/common.py | 37 ++++++++++++++---- shadowsocks/crypto/__init__.py | 3 ++ shadowsocks/crypto/ctypes_openssl.py | 57 +++++++++++++++------------- shadowsocks/crypto/m2.py | 29 +++++++------- shadowsocks/crypto/rc4_md5.py | 4 +- shadowsocks/crypto/salsa20_ctr.py | 13 ++++--- shadowsocks/encrypt.py | 25 ++++++------ shadowsocks/eventloop.py | 4 +- shadowsocks/local.py | 4 ++ shadowsocks/lru_cache.py | 3 ++ shadowsocks/server.py | 10 +++-- shadowsocks/tcprelay.py | 17 +++++---- shadowsocks/udprelay.py | 6 ++- shadowsocks/utils.py | 34 +++++++++-------- tests/test.py | 7 +++- 17 files changed, 204 insertions(+), 115 deletions(-) diff --git a/shadowsocks/__init__.py b/shadowsocks/__init__.py index 013e4b7..5ba5908 100644 --- a/shadowsocks/__init__.py +++ b/shadowsocks/__init__.py @@ -1 +1,24 @@ #!/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: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# 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. + +from __future__ import absolute_import, division, print_function, \ + with_statement diff --git a/shadowsocks/asyncdns.py b/shadowsocks/asyncdns.py index b639dec..4354b1d 100644 --- a/shadowsocks/asyncdns.py +++ b/shadowsocks/asyncdns.py @@ -21,6 +21,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import, division, print_function, \ + with_statement + import time import os import socket @@ -33,7 +36,7 @@ from shadowsocks import common, lru_cache, eventloop CACHE_SWEEP_INTERVAL = 30 -VALID_HOSTNAME = re.compile(r"(?!-)[A-Z\d-]{1,63}(? 63: return None - results.append(chr(l)) + results.append(common.chr(l)) results.append(label) - results.append('\0') - return ''.join(results) + results.append(b'\0') + return b''.join(results) def build_request(address, qtype, request_id): @@ -111,7 +114,7 @@ def parse_ip(addrtype, data, length, offset): def parse_name(data, offset): p = offset labels = [] - l = ord(data[p]) + l = common.ord(data[p]) while l > 0: if (l & (128 + 64)) == (128 + 64): # pointer @@ -121,12 +124,12 @@ def parse_name(data, offset): labels.append(r[1]) p += 2 # pointer is the end - return p - offset, '.'.join(labels) + return p - offset, b'.'.join(labels) else: labels.append(data[p + 1:p + 1 + l]) p += 1 + l - l = ord(data[p]) - return p - offset + 1, '.'.join(labels) + l = common.ord(data[p]) + return p - offset + 1, b'.'.join(labels) # rfc1035 @@ -198,20 +201,20 @@ def parse_response(data): qds = [] ans = [] offset = 12 - for i in xrange(0, res_qdcount): + for i in range(0, res_qdcount): l, r = parse_record(data, offset, True) offset += l if r: qds.append(r) - for i in xrange(0, res_ancount): + for i in range(0, res_ancount): l, r = parse_record(data, offset) offset += l if r: ans.append(r) - for i in xrange(0, res_nscount): + for i in range(0, res_nscount): l, r = parse_record(data, offset) offset += l - for i in xrange(0, res_arcount): + for i in range(0, res_arcount): l, r = parse_record(data, offset) offset += l response = DNSResponse() @@ -232,6 +235,8 @@ def parse_response(data): 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): @@ -242,9 +247,9 @@ def is_ip(address): def is_valid_hostname(hostname): if len(hostname) > 255: return False - if hostname[-1] == ".": + if hostname[-1] == b'.': hostname = hostname[:-1] - return all(VALID_HOSTNAME.match(x) for x in hostname.split(".")) + return all(VALID_HOSTNAME.match(x) for x in hostname.split(b'.')) class DNSResponse(object): @@ -287,11 +292,13 @@ class DNSResolver(object): for line in content: line = line.strip() if line: - if line.startswith('nameserver'): + if line.startswith(b'nameserver'): parts = line.split() if len(parts) >= 2: server = parts[1] if is_ip(server) == socket.AF_INET: + if type(server) != str: + server = server.decode('utf8') self._servers.append(server) except IOError: pass @@ -310,7 +317,7 @@ class DNSResolver(object): if len(parts) >= 2: ip = parts[0] if is_ip(ip): - for i in xrange(1, len(parts)): + for i in range(1, len(parts)): hostname = parts[i] if hostname: self._hosts[hostname] = ip diff --git a/shadowsocks/common.py b/shadowsocks/common.py index 6307ea7..cba287e 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -21,16 +21,37 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import, division, print_function, \ + with_statement + import socket import struct import logging +def compat_ord(s): + if type(s) == int: + return s + return _ord(s) + + +def compat_chr(d): + if bytes == str: + return _chr(d) + return bytes([d]) + + +_ord = ord +_chr = chr +ord = compat_ord +chr = compat_chr + + def inet_ntop(family, ipstr): if family == socket.AF_INET: return socket.inet_ntoa(ipstr) elif family == socket.AF_INET6: - v6addr = ':'.join(('%02X%02X' % (ord(i), ord(j))) + v6addr = b':'.join((b'%02X%02X' % (ord(i), ord(j))) for i, j in zip(ipstr[::2], ipstr[1::2])) return v6addr @@ -39,15 +60,15 @@ def inet_pton(family, addr): if family == socket.AF_INET: return socket.inet_aton(addr) elif family == socket.AF_INET6: - if '.' in addr: # a v4 addr - v4addr = addr[addr.rindex(':') + 1:] + if b'.' in addr: # a v4 addr + v4addr = addr[addr.rindex(b':') + 1:] v4addr = socket.inet_aton(v4addr) - v4addr = map(lambda x: ('%02X' % ord(x)), v4addr) - v4addr.insert(2, ':') - newaddr = addr[:addr.rindex(':') + 1] + ''.join(v4addr) + v4addr = map(lambda x: (b'%02X' % ord(x)), v4addr) + v4addr.insert(2, b':') + newaddr = addr[:addr.rindex(b':') + 1] + b''.join(v4addr) return inet_pton(family, newaddr) dbyts = [0] * 8 # 8 groups - grps = addr.split(':') + grps = addr.split(b':') for i, v in enumerate(grps): if v: dbyts[i] = int(v, 16) @@ -58,7 +79,7 @@ def inet_pton(family, addr): else: break break - return ''.join((chr(i // 256) + chr(i % 256)) for i in dbyts) + return b''.join((chr(i // 256) + chr(i % 256)) for i in dbyts) else: raise RuntimeError("What family?") diff --git a/shadowsocks/crypto/__init__.py b/shadowsocks/crypto/__init__.py index bd3a926..6251321 100644 --- a/shadowsocks/crypto/__init__.py +++ b/shadowsocks/crypto/__init__.py @@ -19,3 +19,6 @@ # 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. + +from __future__ import absolute_import, division, print_function, \ + with_statement diff --git a/shadowsocks/crypto/ctypes_openssl.py b/shadowsocks/crypto/ctypes_openssl.py index 2f4947a..807c54f 100644 --- a/shadowsocks/crypto/ctypes_openssl.py +++ b/shadowsocks/crypto/ctypes_openssl.py @@ -20,6 +20,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import, division, print_function, \ + with_statement + import logging from ctypes import CDLL, c_char_p, c_int, c_long, byref,\ create_string_buffer, c_void_p @@ -117,31 +120,31 @@ class CtypesCrypto(object): ciphers = { - 'aes-128-cfb': (16, 16, CtypesCrypto), - 'aes-192-cfb': (24, 16, CtypesCrypto), - 'aes-256-cfb': (32, 16, CtypesCrypto), - 'aes-128-ofb': (16, 16, CtypesCrypto), - 'aes-192-ofb': (24, 16, CtypesCrypto), - 'aes-256-ofb': (32, 16, CtypesCrypto), - 'aes-128-ctr': (16, 16, CtypesCrypto), - 'aes-192-ctr': (24, 16, CtypesCrypto), - 'aes-256-ctr': (32, 16, CtypesCrypto), - 'aes-128-cfb8': (16, 16, CtypesCrypto), - 'aes-192-cfb8': (24, 16, CtypesCrypto), - 'aes-256-cfb8': (32, 16, CtypesCrypto), - 'aes-128-cfb1': (16, 16, CtypesCrypto), - 'aes-192-cfb1': (24, 16, CtypesCrypto), - 'aes-256-cfb1': (32, 16, CtypesCrypto), - 'bf-cfb': (16, 8, CtypesCrypto), - 'camellia-128-cfb': (16, 16, CtypesCrypto), - 'camellia-192-cfb': (24, 16, CtypesCrypto), - 'camellia-256-cfb': (32, 16, CtypesCrypto), - 'cast5-cfb': (16, 8, CtypesCrypto), - 'des-cfb': (8, 8, CtypesCrypto), - 'idea-cfb': (16, 8, CtypesCrypto), - 'rc2-cfb': (16, 8, CtypesCrypto), - 'rc4': (16, 0, CtypesCrypto), - 'seed-cfb': (16, 16, CtypesCrypto), + b'aes-128-cfb': (16, 16, CtypesCrypto), + b'aes-192-cfb': (24, 16, CtypesCrypto), + b'aes-256-cfb': (32, 16, CtypesCrypto), + b'aes-128-ofb': (16, 16, CtypesCrypto), + b'aes-192-ofb': (24, 16, CtypesCrypto), + b'aes-256-ofb': (32, 16, CtypesCrypto), + b'aes-128-ctr': (16, 16, CtypesCrypto), + b'aes-192-ctr': (24, 16, CtypesCrypto), + b'aes-256-ctr': (32, 16, CtypesCrypto), + b'aes-128-cfb8': (16, 16, CtypesCrypto), + b'aes-192-cfb8': (24, 16, CtypesCrypto), + b'aes-256-cfb8': (32, 16, CtypesCrypto), + b'aes-128-cfb1': (16, 16, CtypesCrypto), + b'aes-192-cfb1': (24, 16, CtypesCrypto), + b'aes-256-cfb1': (32, 16, CtypesCrypto), + b'bf-cfb': (16, 8, CtypesCrypto), + b'camellia-128-cfb': (16, 16, CtypesCrypto), + b'camellia-192-cfb': (24, 16, CtypesCrypto), + b'camellia-256-cfb': (32, 16, CtypesCrypto), + b'cast5-cfb': (16, 8, CtypesCrypto), + b'des-cfb': (8, 8, CtypesCrypto), + b'idea-cfb': (16, 8, CtypesCrypto), + b'rc2-cfb': (16, 8, CtypesCrypto), + b'rc4': (16, 0, CtypesCrypto), + b'seed-cfb': (16, 16, CtypesCrypto), } @@ -167,7 +170,7 @@ def test(): # decipher = Salsa20Cipher('salsa20-ctr', 'k' * 32, 'i' * 8, 1) results = [] pos = 0 - print 'salsa20 test start' + print('salsa20 test start') start = time.time() while pos < len(plain): l = random.randint(100, 32768) @@ -182,7 +185,7 @@ def test(): results.append(decipher.update(c[pos:pos + l])) pos += l end = time.time() - print 'speed: %d bytes/s' % (BLOCK_SIZE * rounds / (end - start)) + print('speed: %d bytes/s' % (BLOCK_SIZE * rounds / (end - start))) assert ''.join(results) == plain diff --git a/shadowsocks/crypto/m2.py b/shadowsocks/crypto/m2.py index c140061..2550a5c 100644 --- a/shadowsocks/crypto/m2.py +++ b/shadowsocks/crypto/m2.py @@ -20,6 +20,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import, division, print_function, \ + with_statement + import sys import logging @@ -49,19 +52,19 @@ def err(alg, key, iv, op, key_as_bytes=0, d=None, salt=None, i=1, padding=1): if has_m2: ciphers = { - 'aes-128-cfb': (16, 16, create_cipher), - 'aes-192-cfb': (24, 16, create_cipher), - 'aes-256-cfb': (32, 16, create_cipher), - 'bf-cfb': (16, 8, create_cipher), - 'camellia-128-cfb': (16, 16, create_cipher), - 'camellia-192-cfb': (24, 16, create_cipher), - 'camellia-256-cfb': (32, 16, create_cipher), - 'cast5-cfb': (16, 8, create_cipher), - 'des-cfb': (8, 8, create_cipher), - 'idea-cfb': (16, 8, create_cipher), - 'rc2-cfb': (16, 8, create_cipher), - 'rc4': (16, 0, create_cipher), - 'seed-cfb': (16, 16, create_cipher), + b'aes-128-cfb': (16, 16, create_cipher), + b'aes-192-cfb': (24, 16, create_cipher), + b'aes-256-cfb': (32, 16, create_cipher), + b'bf-cfb': (16, 8, create_cipher), + b'camellia-128-cfb': (16, 16, create_cipher), + b'camellia-192-cfb': (24, 16, create_cipher), + b'camellia-256-cfb': (32, 16, create_cipher), + b'cast5-cfb': (16, 8, create_cipher), + b'des-cfb': (8, 8, create_cipher), + b'idea-cfb': (16, 8, create_cipher), + b'rc2-cfb': (16, 8, create_cipher), + b'rc4': (16, 0, create_cipher), + b'seed-cfb': (16, 16, create_cipher), } else: ciphers = {} diff --git a/shadowsocks/crypto/rc4_md5.py b/shadowsocks/crypto/rc4_md5.py index 3dac6d5..e8888b1 100644 --- a/shadowsocks/crypto/rc4_md5.py +++ b/shadowsocks/crypto/rc4_md5.py @@ -20,6 +20,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import, division, print_function, \ + with_statement import hashlib @@ -50,5 +52,5 @@ def create_cipher(alg, key, iv, op, key_as_bytes=0, d=None, salt=None, ciphers = { - 'rc4-md5': (16, 16, create_cipher), + b'rc4-md5': (16, 16, create_cipher), } diff --git a/shadowsocks/crypto/salsa20_ctr.py b/shadowsocks/crypto/salsa20_ctr.py index 0a89d6b..d1910ea 100644 --- a/shadowsocks/crypto/salsa20_ctr.py +++ b/shadowsocks/crypto/salsa20_ctr.py @@ -20,6 +20,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import, division, print_function, \ + with_statement + import time import struct import logging @@ -39,13 +42,13 @@ def run_imports(): if not imported: imported = True try: - __import__('numpy') + numpy = __import__('numpy') except ImportError: logging.error('can not import numpy, using SLOW XOR') logging.error('please install numpy if you use salsa20') slow_xor = True try: - __import__('salsa20') + salsa20 = __import__('salsa20') except ImportError: logging.error('you have to install salsa20 before you use salsa20') sys.exit(1) @@ -116,7 +119,7 @@ class Salsa20Cipher(object): ciphers = { - 'salsa20-ctr': (32, 8, Salsa20Cipher), + b'salsa20-ctr': (32, 8, Salsa20Cipher), } @@ -138,7 +141,7 @@ def test(): decipher = Salsa20Cipher('salsa20-ctr', 'k' * 32, 'i' * 8, 1) results = [] pos = 0 - print 'salsa20 test start' + print('salsa20 test start') start = time.time() while pos < len(plain): l = random.randint(100, 32768) @@ -153,7 +156,7 @@ def test(): results.append(decipher.update(c[pos:pos + l])) pos += l end = time.time() - print 'speed: %d bytes/s' % (BLOCK_SIZE * rounds / (end - start)) + print('speed: %d bytes/s' % (BLOCK_SIZE * rounds / (end - start))) assert ''.join(results) == plain diff --git a/shadowsocks/encrypt.py b/shadowsocks/encrypt.py index 45059de..974785c 100644 --- a/shadowsocks/encrypt.py +++ b/shadowsocks/encrypt.py @@ -20,6 +20,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import, division, print_function, \ + with_statement + import os import sys import hashlib @@ -74,23 +77,20 @@ def init_table(key, method=None): string.maketrans('', '')) cached_tables[key] = [encrypt_table, decrypt_table] else: - try: - Encryptor(key, method) # test if the settings if OK - except Exception as e: - logging.error(e) - sys.exit(1) + Encryptor(key, method) # test if the settings if OK 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 - password = str(password) + if hasattr(password, 'encode'): + password = password.encode('utf-8') r = cached_keys.get(password, None) if r: return r m = [] i = 0 - while len(''.join(m)) < (key_len + iv_len): + while len(b''.join(m)) < (key_len + iv_len): md5 = hashlib.md5() data = password if i > 0: @@ -98,7 +98,7 @@ def EVP_BytesToKey(password, key_len, iv_len): md5.update(data) m.append(md5.digest()) i += 1 - ms = ''.join(m) + ms = b''.join(m) key = ms[:key_len] iv = ms[key_len:key_len + iv_len] cached_keys[password] = (key, iv) @@ -107,13 +107,13 @@ def EVP_BytesToKey(password, key_len, iv_len): class Encryptor(object): def __init__(self, key, method=None): - if method == 'table': + if method == b'table': method = None self.key = key self.method = method self.iv = None self.iv_sent = False - self.cipher_iv = '' + self.cipher_iv = b'' self.decipher = None if method: self.cipher = self.get_cipher(key, method, 1, iv=random_string(32)) @@ -130,7 +130,8 @@ class Encryptor(object): return len(self.cipher_iv) def get_cipher(self, password, method, op, iv=None): - password = password.encode('utf-8') + if hasattr(password, 'encode'): + password = password.encode('utf-8') method = method.lower() m = self.get_cipher_param(method) if m: @@ -176,7 +177,7 @@ class Encryptor(object): def encrypt_all(password, method, op, data): - if method is not None and method.lower() == 'table': + if method is not None and method.lower() == b'table': method = None if not method: [encrypt_table, decrypt_table] = init_table(password) diff --git a/shadowsocks/eventloop.py b/shadowsocks/eventloop.py index 4a35950..55c30bb 100644 --- a/shadowsocks/eventloop.py +++ b/shadowsocks/eventloop.py @@ -24,6 +24,8 @@ # from ssloop # https://github.com/clowwindy/ssloop +from __future__ import absolute_import, division, print_function, \ + with_statement import os import socket @@ -100,7 +102,7 @@ class KqueueLoop(object): results[fd] |= POLL_IN elif e.filter == select.KQ_FILTER_WRITE: results[fd] |= POLL_OUT - return results.iteritems() + return results.items() def add_fd(self, fd, mode): self._fds[fd] = mode diff --git a/shadowsocks/local.py b/shadowsocks/local.py index 24e7ad7..0c627a5 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -21,11 +21,15 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import, division, print_function, \ + with_statement + import sys import os import logging import signal +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) from shadowsocks import utils, encrypt, eventloop, tcprelay, udprelay, asyncdns diff --git a/shadowsocks/lru_cache.py b/shadowsocks/lru_cache.py index 1815320..20110ce 100644 --- a/shadowsocks/lru_cache.py +++ b/shadowsocks/lru_cache.py @@ -1,6 +1,9 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function, \ + with_statement + import collections import logging import time diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 2741ac1..0c4c489 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -21,11 +21,15 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import, division, print_function, \ + with_statement + import sys import os import logging import signal +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) from shadowsocks import utils, encrypt, eventloop, tcprelay, udprelay, asyncdns @@ -66,13 +70,13 @@ def main(): def run_server(): def child_handler(signum, _): logging.warn('received SIGQUIT, doing graceful shutting down..') - map(lambda s: s.close(next_tick=True), tcp_servers + udp_servers) + list(map(lambda s: s.close(next_tick=True), tcp_servers + udp_servers)) signal.signal(getattr(signal, 'SIGQUIT', signal.SIGTERM), child_handler) try: loop = eventloop.EventLoop() dns_resolver.add_to_loop(loop) - map(lambda s: s.add_to_loop(loop), tcp_servers + udp_servers) + list(map(lambda s: s.add_to_loop(loop), tcp_servers + udp_servers)) loop.run() except (KeyboardInterrupt, IOError, OSError) as e: logging.error(e) @@ -85,7 +89,7 @@ def main(): if os.name == 'posix': children = [] is_child = False - for i in xrange(0, int(config['workers'])): + for i in range(0, int(config['workers'])): r = os.fork() if r == 0: logging.info('worker started') diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index ec6adbe..4145c84 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -21,6 +21,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import, division, print_function, \ + with_statement + import time import socket import errno @@ -29,7 +32,7 @@ import logging import traceback import random -from shadowsocks import encrypt, eventloop, utils +from shadowsocks import encrypt, eventloop, utils, common from shadowsocks.common import parse_header @@ -231,13 +234,13 @@ class TCPRelayHandler(object): def _handle_stage_hello(self, data): try: if self._is_local: - cmd = ord(data[1]) + cmd = common.ord(data[1]) if cmd == CMD_UDP_ASSOCIATE: logging.debug('UDP associate') if self._local_sock.family == socket.AF_INET6: - header = '\x05\x00\x00\x04' + header = b'\x05\x00\x00\x04' else: - header = '\x05\x00\x00\x01' + header = b'\x05\x00\x00\x01' addr, port = self._local_sock.getsockname() addr_to_send = socket.inet_pton(self._local_sock.family, addr) @@ -265,7 +268,7 @@ class TCPRelayHandler(object): self._stage = STAGE_DNS if self._is_local: # forward address to remote - self._write_to_sock('\x05\x00\x00\x01\x00\x00\x00\x00\x10\x10', + self._write_to_sock(b'\x05\x00\x00\x01\x00\x00\x00\x00\x10\x10', self._local_sock) data_to_send = self._encryptor.encrypt(data) self._data_to_write_to_remote.append(data_to_send) @@ -366,7 +369,7 @@ class TCPRelayHandler(object): return elif is_local and self._stage == STAGE_INIT: # TODO check auth method - self._write_to_sock('\x05\00', self._local_sock) + self._write_to_sock(b'\x05\00', self._local_sock) self._stage = STAGE_HELLO return elif self._stage == STAGE_REPLY: @@ -411,7 +414,7 @@ class TCPRelayHandler(object): def _on_remote_write(self): self._stage = STAGE_STREAM if self._data_to_write_to_remote: - data = ''.join(self._data_to_write_to_remote) + data = b''.join(self._data_to_write_to_remote) self._data_to_write_to_remote = [] self._write_to_sock(data, self._remote_sock) else: diff --git a/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index bfd688a..b4951be 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -65,6 +65,8 @@ # `client` means UDP clients that connects to other servers # `server` means the UDP server that handles user requests +from __future__ import absolute_import, division, print_function, \ + with_statement import time import socket @@ -73,7 +75,7 @@ import struct import errno import random -from shadowsocks import encrypt, eventloop, lru_cache +from shadowsocks import encrypt, eventloop, lru_cache, common from shadowsocks.common import parse_header, pack_addr @@ -146,7 +148,7 @@ class UDPRelay(object): if not data: logging.debug('UDP handle_server: data is empty') if self._is_local: - frag = ord(data[2]) + frag = common.ord(data[2]) if frag != 0: logging.warn('drop a message since frag is not 0') return diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index fee4afc..3edcae9 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -21,6 +21,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import, division, print_function, \ + with_statement + import os import json import sys @@ -33,9 +36,9 @@ VERBOSE_LEVEL = 5 def check_python(): info = sys.version_info - if not (info[0] == 2 and info[1] >= 6): - print 'Python 2.6 or 2.7 required' - sys.exit(1) + # if not (info[0] == 2 and info[1] >= 6): + # print('Python 2.6 or 2.7 required') + # sys.exit(1) def print_shadowsocks(): @@ -45,7 +48,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(): @@ -76,7 +79,7 @@ def check_config(config): if config.get('timeout', 300) > 600: logging.warn('warning: your timeout %d seems too long' % int(config.get('timeout'))) - if config.get('password') in ['mypassword', 'barfoo!']: + if config.get('password') in ['mypassword']: logging.error('DON\'T USE DEFAULT PASSWORD! Please change it in your ' 'config.json!') exit(1) @@ -102,7 +105,8 @@ def get_config(is_local): logging.info('loading config from %s' % config_path) with open(config_path, 'rb') as f: try: - config = json.load(f, object_hook=_decode_dict) + config = json.loads(f.read().decode('utf8'), + object_hook=_decode_dict) except ValueError as e: logging.error('found an error in config.json: %s', e.message) @@ -145,7 +149,7 @@ def get_config(is_local): v_count -= 1 config['verbose'] = v_count except getopt.GetoptError as e: - print >>sys.stderr, e + print(e, file=sys.stderr) print_help(is_local) sys.exit(2) @@ -218,7 +222,7 @@ def print_help(is_local): def print_local_help(): - print '''usage: sslocal [-h] -s SERVER_ADDR [-p SERVER_PORT] + 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] [-q] @@ -237,11 +241,11 @@ optional arguments: -q, -qq quiet mode, only show warnings/errors Online help: -''' +''') def print_server_help(): - print '''usage: ssserver [-h] [-s SERVER_ADDR] [-p SERVER_PORT] -k PASSWORD + print('''usage: ssserver [-h] [-s SERVER_ADDR] [-p SERVER_PORT] -k PASSWORD -m METHOD [-t TIMEOUT] [-c CONFIG] [--fast-open] [--workers WORKERS] [-v] [-q] @@ -259,13 +263,13 @@ optional arguments: -q, -qq quiet mode, only show warnings/errors Online help: -''' +''') def _decode_list(data): rv = [] for item in data: - if isinstance(item, unicode): + if hasattr(item, 'encode'): item = item.encode('utf-8') elif isinstance(item, list): item = _decode_list(item) @@ -277,10 +281,8 @@ def _decode_list(data): def _decode_dict(data): rv = {} - for key, value in data.iteritems(): - if isinstance(key, unicode): - key = key.encode('utf-8') - if isinstance(value, unicode): + for key, value in data.items(): + if hasattr(value, 'encode'): value = value.encode('utf-8') elif isinstance(value, list): value = _decode_list(value) diff --git a/tests/test.py b/tests/test.py index 83c2332..5475372 100755 --- a/tests/test.py +++ b/tests/test.py @@ -1,6 +1,9 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function, \ + with_statement + import sys import os import signal @@ -22,7 +25,7 @@ else: if 'salsa20' in sys.argv[-1]: from shadowsocks.crypto import salsa20_ctr salsa20_ctr.test() - print 'encryption test passed' + print('encryption test passed') p1 = Popen(['python', 'shadowsocks/server.py', '-c', server_config], stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) @@ -69,7 +72,7 @@ try: sys.exit(r) else: sys.exit(1) - print 'test passed' + print('test passed') finally: for p in [p1, p2]: