Browse Source

python 3 support; not stable yet

auth
clowwindy 10 years ago
parent
commit
2a53b67c65
  1. 23
      shadowsocks/__init__.py
  2. 43
      shadowsocks/asyncdns.py
  3. 37
      shadowsocks/common.py
  4. 3
      shadowsocks/crypto/__init__.py
  5. 57
      shadowsocks/crypto/ctypes_openssl.py
  6. 29
      shadowsocks/crypto/m2.py
  7. 4
      shadowsocks/crypto/rc4_md5.py
  8. 13
      shadowsocks/crypto/salsa20_ctr.py
  9. 21
      shadowsocks/encrypt.py
  10. 4
      shadowsocks/eventloop.py
  11. 4
      shadowsocks/local.py
  12. 3
      shadowsocks/lru_cache.py
  13. 10
      shadowsocks/server.py
  14. 17
      shadowsocks/tcprelay.py
  15. 6
      shadowsocks/udprelay.py
  16. 34
      shadowsocks/utils.py
  17. 7
      tests/test.py

23
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

43
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}(?<!-)$", re.IGNORECASE)
VALID_HOSTNAME = re.compile(br"(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
common.patch_socket()
@ -77,17 +80,17 @@ QCLASS_IN = 1
def build_address(address):
address = address.strip('.')
labels = address.split('.')
address = address.strip(b'.')
labels = address.split(b'.')
results = []
for label in labels:
l = len(label)
if l > 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

37
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?")

3
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

57
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

29
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 = {}

4
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),
}

13
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

21
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)
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,6 +130,7 @@ class Encryptor(object):
return len(self.cipher_iv)
def get_cipher(self, password, method, op, iv=None):
if hasattr(password, 'encode'):
password = password.encode('utf-8')
method = method.lower()
m = self.get_cipher_param(method)
@ -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)

4
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

4
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

3
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

10
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')

17
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:

6
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

34
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: <https://github.com/clowwindy/shadowsocks>
'''
''')
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: <https://github.com/clowwindy/shadowsocks>
'''
''')
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)

7
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]:

Loading…
Cancel
Save