Skip to content

Commit

Permalink
python 3 support; not stable yet
Browse files Browse the repository at this point in the history
  • Loading branch information
clowwindy committed Oct 31, 2014
1 parent 0814888 commit 2a53b67
Show file tree
Hide file tree
Showing 17 changed files with 204 additions and 115 deletions.
23 changes: 23 additions & 0 deletions shadowsocks/__init__.py
Original file line number Diff line number Diff line change
@@ -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 changes: 25 additions & 18 deletions shadowsocks/asyncdns.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -33,7 +36,7 @@

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()

Expand Down Expand Up @@ -77,17 +80,17 @@


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):
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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):
Expand All @@ -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):
Expand Down Expand Up @@ -287,11 +292,13 @@ def _parse_resolv(self):
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
Expand All @@ -310,7 +317,7 @@ def _parse_hosts(self):
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
Expand Down
37 changes: 29 additions & 8 deletions shadowsocks/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
Expand All @@ -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?")

Expand Down
3 changes: 3 additions & 0 deletions shadowsocks/crypto/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 30 additions & 27 deletions shadowsocks/crypto/ctypes_openssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -117,31 +120,31 @@ def clean(self):


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


Expand All @@ -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)
Expand All @@ -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


Expand Down
29 changes: 16 additions & 13 deletions shadowsocks/crypto/m2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 changes: 3 additions & 1 deletion shadowsocks/crypto/rc4_md5.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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),
}
Loading

0 comments on commit 2a53b67

Please sign in to comment.