Browse Source

merge manyuser branch

master
breakwa11 9 years ago
parent
commit
8f402277e6
  1. 2
      shadowsocks/manager.py
  2. 76
      shadowsocks/obfs.py
  3. 18
      shadowsocks/obfsplugin/__init__.py
  4. 257
      shadowsocks/obfsplugin/http_simple.py
  5. 61
      shadowsocks/obfsplugin/plain.py
  6. 113
      shadowsocks/obfsplugin/verify_simple.py
  7. 5
      shadowsocks/run.sh
  8. 14
      shadowsocks/server.py
  9. 1
      shadowsocks/shell.py
  10. 3
      shadowsocks/stop.sh
  11. 3
      shadowsocks/tail.sh
  12. 103
      shadowsocks/tcprelay.py
  13. 70
      shadowsocks/udprelay.py

2
shadowsocks/manager.py

@ -44,7 +44,7 @@ class Manager(object):
self._statistics = collections.defaultdict(int)
self._control_client_addr = None
try:
manager_address = config['manager_address']
manager_address = common.to_str(config['manager_address'])
if ':' in manager_address:
addr = manager_address.rsplit(':', 1)
addr = addr[0], int(addr[1])

76
shadowsocks/obfs.py

@ -0,0 +1,76 @@
#!/usr/bin/env python
#
# Copyright 2015-2015 breakwa11
#
# 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.
from __future__ import absolute_import, division, print_function, \
with_statement
import os
import sys
import hashlib
import logging
from shadowsocks import common
from shadowsocks.obfsplugin import plain, http_simple, verify_simple
method_supported = {}
method_supported.update(plain.obfs)
method_supported.update(http_simple.obfs)
method_supported.update(verify_simple.obfs)
class Obfs(object):
def __init__(self, method):
self.method = method
self._method_info = self.get_method_info(method)
if self._method_info:
self.obfs = self.get_obfs(method)
else:
logging.error('method %s not supported' % method)
sys.exit(1)
def get_method_info(self, method):
method = method.lower()
m = method_supported.get(method)
return m
def get_obfs(self, method):
m = self._method_info
return m[0](method)
def client_pre_encrypt(self, buf):
return self.obfs.client_pre_encrypt(buf)
def client_encode(self, buf):
return self.obfs.client_encode(buf)
def client_decode(self, buf):
return self.obfs.client_decode(buf)
def client_post_decrypt(self, buf):
return self.obfs.client_post_decrypt(buf)
def server_pre_encrypt(self, buf):
return self.obfs.server_pre_encrypt(buf)
def server_encode(self, buf):
return self.obfs.server_encode(buf)
def server_decode(self, buf):
return self.obfs.server_decode(buf)
def server_post_decrypt(self, buf):
return self.obfs.server_post_decrypt(buf)

18
shadowsocks/obfsplugin/__init__.py

@ -0,0 +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.
from __future__ import absolute_import, division, print_function, \
with_statement

257
shadowsocks/obfsplugin/http_simple.py

@ -0,0 +1,257 @@
#!/usr/bin/env python
#
# Copyright 2015-2015 breakwa11
#
# 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.
from __future__ import absolute_import, division, print_function, \
with_statement
import os
import sys
import hashlib
import logging
import binascii
import base64
import datetime
from shadowsocks.obfsplugin import plain
from shadowsocks import common
from shadowsocks.common import to_bytes, to_str, ord
def create_http_obfs(method):
return http_simple(method)
def create_http2_obfs(method):
return http2_simple(method)
def create_tls_obfs(method):
return tls_simple(method)
def create_random_head_obfs(method):
return random_head(method)
obfs = {
'http_simple': (create_http_obfs,),
'http2_simple': (create_http2_obfs,),
'tls_simple': (create_tls_obfs,),
'random_head': (create_random_head_obfs,),
}
def match_begin(str1, str2):
if len(str1) >= len(str2):
if str1[:len(str2)] == str2:
return True
return False
class http_simple(plain.plain):
def __init__(self, method):
self.method = method
self.has_sent_header = False
self.has_recv_header = False
self.host = None
self.port = 0
self.recv_buffer = b''
def client_encode(self, buf):
# TODO
return buf
def client_decode(self, buf):
# TODO
return (buf, False)
def server_encode(self, buf):
if self.has_sent_header:
return buf
header = b'HTTP/1.1 200 OK\r\nServer: openresty\r\nDate: '
header += to_bytes(datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S GMT'))
header += b'\r\nContent-Type: text/plain; charset=utf-8\r\nTransfer-Encoding: chunked\r\nConnection: keep-alive\r\nKeep-Alive: timeout=20\r\nVary: Accept-Encoding\r\nContent-Encoding: gzip\r\n\r\n'
self.has_sent_header = True
return header + buf
def get_data_from_http_header(self, buf):
ret_buf = b''
lines = buf.split(b'\r\n')
if lines and len(lines) > 4:
hex_items = lines[0].split(b'%')
if hex_items and len(hex_items) > 1:
for index in range(1, len(hex_items)):
if len(hex_items[index]) != 2:
ret_buf += binascii.unhexlify(hex_items[index][:2])
break
ret_buf += binascii.unhexlify(hex_items[index])
return ret_buf
return b''
def server_decode(self, buf):
if self.has_recv_header:
return (buf, True, False)
buf = self.recv_buffer + buf
if len(buf) > 10:
if match_begin(buf, b'GET /') or match_begin(buf, b'POST /'):
if len(buf) > 65536:
self.has_sent_header = True
self.has_recv_header = True
self.recv_buffer = None
return (buf, True, False)
else: #not http header, run on original protocol
self.has_sent_header = True
self.has_recv_header = True
self.recv_buffer = None
return (buf, True, False)
else:
self.recv_buffer = buf
return (b'', True, False)
datas = buf.split(b'\r\n\r\n', 1)
if datas and len(datas) > 1:
ret_buf = self.get_data_from_http_header(buf)
ret_buf += datas[1]
if len(ret_buf) >= 15:
self.has_recv_header = True
return (ret_buf, True, False)
self.recv_buffer = buf
return (b'', True, False)
else:
self.recv_buffer = buf
return (b'', True, False)
self.has_sent_header = True
self.has_recv_header = True
return (buf, True, False)
class http2_simple(plain.plain):
def __init__(self, method):
self.method = method
self.has_sent_header = False
self.has_recv_header = False
self.host = None
self.port = 0
self.recv_buffer = b''
def client_encode(self, buf):
# TODO
return buf
def client_decode(self, buf):
# TODO
return (buf, False)
def server_encode(self, buf):
if self.has_sent_header:
return buf
header = b'HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nUpgrade: h2c\r\n\r\n'
self.has_sent_header = True
return header + buf
def server_decode(self, buf):
if self.has_recv_header:
return (buf, True, False)
buf = self.recv_buffer + buf
if len(buf) > 10:
if match_begin(buf, b'GET /'):
pass
else: #not http header, run on original protocol
self.has_sent_header = True
self.has_recv_header = True
self.recv_buffer = None
return (buf, True, False)
else:
self.recv_buffer = buf
return (b'', True, False)
datas = buf.split(b'\r\n\r\n', 1)
if datas and len(datas) > 1 and len(datas[0]) >= 4:
lines = buf.split(b'\r\n')
if lines and len(lines) >= 4:
if match_begin(lines[4], b'HTTP2-Settings: '):
ret_buf = base64.urlsafe_b64decode(lines[4][16:])
ret_buf += datas[1]
self.has_recv_header = True
return (ret_buf, True, False)
self.recv_buffer = buf
return (b'', True, False)
else:
self.recv_buffer = buf
return (b'', True, False)
self.has_sent_header = True
self.has_recv_header = True
return (buf, True, False)
class tls_simple(plain.plain):
def __init__(self, method):
self.method = method
self.has_sent_header = False
self.has_recv_header = False
def client_encode(self, buf):
return buf
def client_decode(self, buf):
# (buffer_to_recv, is_need_to_encode_and_send_back)
return (buf, False)
def server_encode(self, buf):
if self.has_sent_header:
return buf
self.has_sent_header = True
# TODO
#server_hello = b''
return b'\x16\x03\x01'
def server_decode(self, buf):
if self.has_recv_header:
return (buf, True, False)
self.has_recv_header = True
if not match_begin(buf, b'\x16\x03\x01'):
self.has_sent_header = True
return (buf, True, False)
# (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back)
return (b'', False, True)
class random_head(plain.plain):
def __init__(self, method):
self.method = method
self.has_sent_header = False
self.has_recv_header = False
def client_encode(self, buf):
return buf
def client_decode(self, buf):
# (buffer_to_recv, is_need_to_encode_and_send_back)
return (buf, False)
def server_encode(self, buf):
if self.has_sent_header:
return buf
self.has_sent_header = True
return os.urandom(common.ord(os.urandom(1)[0]) % 96 + 4)
def server_decode(self, buf):
if self.has_recv_header:
return (buf, True, False)
self.has_recv_header = True
crc = binascii.crc32(buf) & 0xffffffff
if crc != 0xffffffff:
self.has_sent_header = True
return (buf, True, False)
# (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back)
return (b'', False, True)

61
shadowsocks/obfsplugin/plain.py

@ -0,0 +1,61 @@
#!/usr/bin/env python
#
# Copyright 2015-2015 breakwa11
#
# 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.
from __future__ import absolute_import, division, print_function, \
with_statement
import os
import sys
import hashlib
import logging
def create_obfs(method):
return plain(method)
obfs = {
'plain': (create_obfs,),
}
class plain(object):
def __init__(self, method):
self.method = method
def client_pre_encrypt(self, buf):
return buf
def client_encode(self, buf):
return buf
def client_decode(self, buf):
# (buffer_to_recv, is_need_to_encode_and_send_back)
return (buf, False)
def client_post_decrypt(self, buf):
return buf
def server_pre_encrypt(self, buf):
return buf
def server_encode(self, buf):
return buf
def server_decode(self, buf):
# (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back)
return (buf, True, False)
def server_post_decrypt(self, buf):
return buf

113
shadowsocks/obfsplugin/verify_simple.py

@ -0,0 +1,113 @@
#!/usr/bin/env python
#
# Copyright 2015-2015 breakwa11
#
# 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.
from __future__ import absolute_import, division, print_function, \
with_statement
import os
import sys
import hashlib
import logging
import binascii
import base64
import datetime
import struct
from shadowsocks.obfsplugin import plain
from shadowsocks import common
from shadowsocks.common import to_bytes, to_str, ord
def create_verify_obfs(method):
return verify_simple(method)
obfs = {
'verify_simple': (create_verify_obfs,),
}
def match_begin(str1, str2):
if len(str1) >= len(str2):
if str1[:len(str2)] == str2:
return True
return False
class verify_simple(plain.plain):
def __init__(self, method):
self.method = method
self.recv_buf = b''
self.unit_len = 8100
def pack_data(self, buf):
if len(buf) == 0:
return b''
rnd_data = os.urandom(common.ord(os.urandom(1)[0]) % 16)
data = common.chr(len(rnd_data) + 1) + rnd_data + buf
data = struct.pack('>H', len(data) + 6) + data
crc = (0xffffffff - binascii.crc32(data)) & 0xffffffff
data += struct.pack('<I', crc)
return data
def client_pre_encrypt(self, buf):
ret = b''
while len(buf) > self.unit_len:
ret += self.pack_data(buf[:self.unit_len])
buf = buf[self.unit_len:]
ret += self.pack_data(buf)
return ret
def client_encode(self, buf):
return buf
def client_decode(self, buf):
# (buffer_to_recv, is_need_to_encode_and_send_back)
return (buf, False)
def client_post_decrypt(self, buf):
return buf
def server_pre_encrypt(self, buf):
ret = b''
while len(buf) > self.unit_len:
ret += self.pack_data(buf[:self.unit_len])
buf = buf[self.unit_len:]
ret += self.pack_data(buf)
return ret
def server_encode(self, buf):
return buf
def server_decode(self, buf):
# (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back)
return (buf, True, False)
def server_post_decrypt(self, buf):
self.recv_buf += buf
out_buf = b''
while len(self.recv_buf) > 2:
length = struct.unpack('>H', self.recv_buf[:2])[0]
if length >= 8192:
raise Exception('server_post_decrype data error')
if length > len(self.recv_buf):
break
if (binascii.crc32(self.recv_buf[:length]) & 0xffffffff) != 0xffffffff:
raise Exception('server_post_decrype data uncorrect CRC32')
pos = common.ord(self.recv_buf[2]) + 2
out_buf += self.recv_buf[pos:length - 4]
self.recv_buf = self.recv_buf[length:]
return out_buf

5
shadowsocks/run.sh

@ -0,0 +1,5 @@
#!/bin/bash
cd `dirname $0`
eval $(ps -ef | grep "[0-9] python server\\.py a" | awk '{print "kill "$2}')
nohup python server.py a >> ssserver.log 2>&1 &

14
shadowsocks/server.py

@ -59,22 +59,29 @@ def main():
dns_resolver = asyncdns.DNSResolver()
port_password = config['port_password']
del config['port_password']
for port, password in port_password.items():
for port, password_obfs in port_password.items():
if type(password_obfs) == list:
password = password_obfs[0]
obfs = password_obfs[1]
else:
password = password_obfs
obfs = config["obfs"]
a_config = config.copy()
ipv6_ok = False
logging.info("server start with password [%s] method [%s]" % (password, a_config['method']))
logging.info("server start with password [%s] obfs [%s] method [%s]" % (password, obfs, a_config['method']))
if 'server_ipv6' in a_config:
try:
if len(a_config['server_ipv6']) > 2 and a_config['server_ipv6'][0] == "[" and a_config['server_ipv6'][-1] == "]":
a_config['server_ipv6'] = a_config['server_ipv6'][1:-1]
a_config['server_port'] = int(port)
a_config['password'] = password
a_config['obfs'] = obfs
a_config['server'] = a_config['server_ipv6']
logging.info("starting server at %s:%d" %
(a_config['server'], int(port)))
tcp_servers.append(tcprelay.TCPRelay(a_config, dns_resolver, False))
udp_servers.append(udprelay.UDPRelay(a_config, dns_resolver, False))
if a_config['server_ipv6'] == "::":
if a_config['server_ipv6'] == b"::":
ipv6_ok = True
except Exception as e:
shell.print_exception(e)
@ -83,6 +90,7 @@ def main():
a_config = config.copy()
a_config['server_port'] = int(port)
a_config['password'] = password
a_config['obfs'] = obfs
logging.info("starting server at %s:%d" %
(a_config['server'], int(port)))
tcp_servers.append(tcprelay.TCPRelay(a_config, dns_resolver, False))

1
shadowsocks/shell.py

@ -216,6 +216,7 @@ def get_config(is_local):
config['password'] = to_bytes(config.get('password', b''))
config['method'] = to_str(config.get('method', 'aes-256-cfb'))
config['obfs'] = to_str(config.get('obfs', 'plain'))
config['port_password'] = config.get('port_password', None)
config['timeout'] = int(config.get('timeout', 300))
config['fast_open'] = config.get('fast_open', False)

3
shadowsocks/stop.sh

@ -0,0 +1,3 @@
#!/bin/bash
eval $(ps -ef | grep "[0-9] python server\\.py a" | awk '{print "kill "$2}')

3
shadowsocks/tail.sh

@ -0,0 +1,3 @@
#!/bin/bash
tail -f ssserver.log

103
shadowsocks/tcprelay.py

@ -27,12 +27,14 @@ import binascii
import traceback
import random
from shadowsocks import encrypt, eventloop, shell, common
from shadowsocks import encrypt, obfs, eventloop, shell, common
from shadowsocks.common import pre_parse_header, parse_header
# set it 'False' to use both new protocol and the original shadowsocks protocal
# set it 'True' to use new protocol ONLY, to avoid GFW detecting
FORCE_NEW_PROTOCOL = False
# set it 'True' if run as a local client and connect to a server which support new protocol
CLIENT_NEW_PROTOCOL = False
# we clear at most TIMEOUTS_CLEAN_SIZE timeouts each time
TIMEOUTS_CLEAN_SIZE = 512
@ -114,10 +116,12 @@ class TCPRelayHandler(object):
self._stage = STAGE_INIT
self._encryptor = encrypt.Encryptor(config['password'],
config['method'])
self._encrypt_correct = True
self._obfs = obfs.Obfs(config['obfs'])
self._fastopen_connected = False
self._data_to_write_to_local = []
self._data_to_write_to_remote = []
self._udp_data_send_buffer = ''
self._udp_data_send_buffer = b''
self._upstream_status = WAIT_STATUS_READING
self._downstream_status = WAIT_STATUS_INIT
self._client_address = local_sock.getpeername()[:2]
@ -196,7 +200,7 @@ class TCPRelayHandler(object):
# write data to sock
# if only some of the data are written, put remaining in the buffer
# and update the stream to wait for writing
if not data or not sock:
if not sock:
return False
#logging.debug("_write_to_sock %s %s %s" % (self._remote_sock, sock, self._remote_udp))
uncomplete = False
@ -248,11 +252,20 @@ class TCPRelayHandler(object):
return True
else:
try:
if self._is_local:
pass
else:
if sock == self._local_sock and self._encrypt_correct:
obfs_encode = self._obfs.server_encode(data)
data = obfs_encode
l = len(data)
if l > 0:
s = sock.send(data)
if s < l:
data = data[s:]
uncomplete = True
else:
return
except (OSError, IOError) as e:
error_no = eventloop.errno_from_exception(e)
if error_no in (errno.EAGAIN, errno.EINPROGRESS,
@ -281,9 +294,36 @@ class TCPRelayHandler(object):
logging.error('write_all_to_sock:unknown socket')
return True
def _get_redirect_host(self, client_address, ogn_data):
# test
host_list = [(b"www.bing.com", 80), (b"www.microsoft.com", 80), (b"www.baidu.com", 443), (b"www.qq.com", 80), (b"www.csdn.net", 80), (b"1.2.3.4", 1000)]
hash_code = binascii.crc32(ogn_data)
addrs = socket.getaddrinfo(client_address[0], client_address[1], 0, socket.SOCK_STREAM, socket.SOL_TCP)
af, socktype, proto, canonname, sa = addrs[0]
address_bytes = common.inet_pton(af, sa[0])
if len(address_bytes) == 16:
addr = struct.unpack('>Q', address_bytes[8:])[0]
if len(address_bytes) == 4:
addr = struct.unpack('>I', address_bytes)[0]
else:
addr = 0
return host_list[((hash_code & 0xffffffff) + addr + 3) % len(host_list)]
def _handel_protocol_error(self, client_address, ogn_data):
#raise Exception('can not parse header')
logging.warn("Protocol ERROR, TCP ogn data %s from %s:%d" % (binascii.hexlify(ogn_data), client_address[0], client_address[1]))
self._encrypt_correct = False
#create redirect or disconnect by hash code
host, port = self._get_redirect_host(client_address, ogn_data)
data = b"\x03" + common.chr(len(host)) + host + struct.pack('>H', port)
logging.warn("TCP data redir %s:%d %s" % (host, port, binascii.hexlify(data)))
return data + ogn_data
def _handle_stage_connecting(self, data):
if self._is_local:
data = self._obfs.client_pre_encrypt(data)
data = self._encryptor.encrypt(data)
data = self._obfs.client_encode(data)
self._data_to_write_to_remote.append(data)
if self._is_local and not self._fastopen_connected and \
self._config['fast_open']:
@ -347,17 +387,18 @@ class TCPRelayHandler(object):
return
before_parse_data = data
if FORCE_NEW_PROTOCOL and ord(data[0]) != 0x88:
logging.warn("TCP data %s decrypt %s" % (binascii.hexlify(ogn_data), binascii.hexlify(before_parse_data)))
raise Exception('can not parse header')
if self._is_local:
header_result = parse_header(data)
else:
if FORCE_NEW_PROTOCOL and common.ord(data[0]) != 0x88:
data = self._handel_protocol_error(self._client_address, ogn_data)
data = pre_parse_header(data)
if data is None:
logging.warn("TCP data %s decrypt %s" % (binascii.hexlify(ogn_data), binascii.hexlify(before_parse_data)))
raise Exception('can not parse header')
data = self._handel_protocol_error(self._client_address, ogn_data)
header_result = parse_header(data)
if header_result is None:
logging.warn("TCP data %s decrypt %s" % (binascii.hexlify(ogn_data), binascii.hexlify(before_parse_data)))
raise Exception('can not parse header')
data = self._handel_protocol_error(self._client_address, ogn_data)
header_result = parse_header(data)
connecttype, remote_addr, remote_port, header_length = header_result
logging.info('%s connecting %s:%d from %s:%d' %
((connecttype == 0) and 'TCP' or 'UDP',
@ -373,6 +414,13 @@ class TCPRelayHandler(object):
self._write_to_sock((b'\x05\x00\x00\x01'
b'\x00\x00\x00\x00\x10\x10'),
self._local_sock)
if CLIENT_NEW_PROTOCOL:
rnd_len = random.randint(1, 32)
total_len = 7 + rnd_len + len(data)
data = b'\x88' + struct.pack('>H', total_len) + chr(rnd_len) + (b' ' * (rnd_len - 1)) + data
crc = (0xffffffff - binascii.crc32(data)) & 0xffffffff
data += struct.pack('<I', crc)
data = self._obfs.client_pre_encrypt(data)
data_to_send = self._encryptor.encrypt(data)
self._data_to_write_to_remote.append(data_to_send)
# notice here may go into _handle_dns_resolved directly
@ -505,13 +553,27 @@ class TCPRelayHandler(object):
ogn_data = data
self._update_activity(len(data))
if not is_local:
data = self._encryptor.decrypt(data)
if self._encrypt_correct:
obfs_decode = self._obfs.server_decode(data)
if obfs_decode[2]:
self._write_to_sock(b'', self._local_sock)
if obfs_decode[1]:
data = self._encryptor.decrypt(obfs_decode[0])
else:
data = obfs_decode[0]
try:
data = self._obfs.server_post_decrypt(data)
except Exception as e:
shell.print_exception(e)
self.destroy()
if not data:
return
self._server.server_transfer_ul += len(data)
if self._stage == STAGE_STREAM:
if self._is_local:
data = self._obfs.client_pre_encrypt(data)
data = self._encryptor.encrypt(data)
data = self._obfs.client_encode(data)
self._write_to_sock(data, self._remote_sock)
return
elif is_local and self._stage == STAGE_INIT:
@ -537,10 +599,10 @@ class TCPRelayHandler(object):
port = struct.pack('>H', addr[1])
try:
ip = socket.inet_aton(addr[0])
data = '\x00\x01' + ip + port + data
data = b'\x00\x01' + ip + port + data
except Exception as e:
ip = socket.inet_pton(socket.AF_INET6, addr[0])
data = '\x00\x04' + ip + port + data
data = b'\x00\x04' + ip + port + data
data = struct.pack('>H', len(data) + 2) + data
#logging.info('UDP over TCP recvfrom %s:%d %d bytes to %s:%d' % (addr[0], addr[1], len(data), self._client_address[0], self._client_address[1]))
else:
@ -555,8 +617,14 @@ class TCPRelayHandler(object):
self._server.server_transfer_dl += len(data)
self._update_activity(len(data))
if self._is_local:
data = self._encryptor.decrypt(data)
else:
obfs_decode = self._obfs.client_decode(data)
if obfs_decode[1]:
self._write_to_sock(b'', self._remote_sock)
data = self._encryptor.decrypt(obfs_decode[0])
data = self._obfs.client_post_decrypt(data)
else:
if self._encrypt_correct:
data = self._obfs.server_pre_encrypt(data)
data = self._encryptor.encrypt(data)
try:
self._write_to_sock(data, self._local_sock)
@ -672,7 +740,6 @@ class TCPRelayHandler(object):
self._dns_resolver.remove_callback(self._handle_dns_resolved)
self._server.remove_handler(self)
class TCPRelay(object):
def __init__(self, config, dns_resolver, is_local, stat_callback=None):
self._config = config
@ -681,8 +748,8 @@ class TCPRelay(object):
self._closed = False
self._eventloop = None
self._fd_to_handlers = {}
self.server_transfer_ul = 0L
self.server_transfer_dl = 0L
self.server_transfer_ul = 0
self.server_transfer_dl = 0
self._timeout = config['timeout']
self._timeouts = [] # a list for all the handlers

70
shadowsocks/udprelay.py

@ -115,15 +115,15 @@ CMD_POST_64 = 6
CMD_SYN_STATUS_64 = 7
CMD_DISCONNECT = 8
CMD_VER_STR = "\x08"
CMD_VER_STR = b"\x08"
RSP_STATE_EMPTY = ""
RSP_STATE_REJECT = "\x00"
RSP_STATE_CONNECTED = "\x01"
RSP_STATE_CONNECTEDREMOTE = "\x02"
RSP_STATE_ERROR = "\x03"
RSP_STATE_DISCONNECT = "\x04"
RSP_STATE_REDIRECT = "\x05"
RSP_STATE_EMPTY = b""
RSP_STATE_REJECT = b"\x00"
RSP_STATE_CONNECTED = b"\x01"
RSP_STATE_CONNECTEDREMOTE = b"\x02"
RSP_STATE_ERROR = b"\x03"
RSP_STATE_DISCONNECT = b"\x04"
RSP_STATE_REDIRECT = b"\x05"
class UDPLocalAddress(object):
def __init__(self, addr):
@ -196,16 +196,20 @@ class RecvQueue(object):
if self.end_id == pack_id:
self.end_id = pack_id + 1
elif self.end_id < pack_id:
for eid in xrange(self.end_id, pack_id):
eid = self.end_id
while eid < pack_id:
self.miss_queue.add(eid)
eid += 1
self.end_id = pack_id + 1
else:
self.miss_queue.remove(pack_id)
def set_end(self, end_id):
if end_id > self.end_id:
for eid in xrange(self.end_id, end_id):
eid = self.end_id
while eid < pack_id:
self.miss_queue.add(eid)
eid += 1
self.end_id = end_id
def get_begin_id(self):
@ -302,10 +306,10 @@ class TCPRelayHandler(object):
#loop.add(local_sock, eventloop.POLL_IN | eventloop.POLL_ERR)
self.last_activity = 0
self._update_activity()
self._random_mtu_size = [random.randint(POST_MTU_MIN, POST_MTU_MAX) for i in xrange(1024)]
self._random_mtu_size = [random.randint(POST_MTU_MIN, POST_MTU_MAX) for i in range(1024)]
self._random_mtu_index = 0
self._rand_data = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" * 4
self._rand_data = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" * 4
def __hash__(self):
# default __hash__ is id / 16
@ -474,7 +478,7 @@ class TCPRelayHandler(object):
addr = self.get_local_address()
for i in xrange(2):
for i in range(2):
rsp_data = self._pack_rsp_data(CMD_RSP_CONNECT_REMOTE, RSP_STATE_CONNECTEDREMOTE)
self._write_to_sock(rsp_data, self._local_sock, addr)
@ -585,29 +589,29 @@ class TCPRelayHandler(object):
def _pack_rsp_data(self, cmd, data):
reqid_str = struct.pack(">H", self._request_id)
return ''.join([CMD_VER_STR, chr(cmd), reqid_str, data, self._rand_data[:random.randint(0, len(self._rand_data))], reqid_str])
return b''.join([CMD_VER_STR, common.chr(cmd), reqid_str, data, self._rand_data[:random.randint(0, len(self._rand_data))], reqid_str])
def _pack_rnd_data(self, data):
length = random.randint(0, len(self._rand_data))
if length == 0:
return data
elif length == 1:
return "\x81" + data
return b"\x81" + data
elif length < 256:
return "\x80" + chr(length) + self._rand_data[:length - 2] + data
return b"\x80" + common.chr(length) + self._rand_data[:length - 2] + data
else:
return "\x82" + struct.pack(">H", length) + self._rand_data[:length - 3] + data
return b"\x82" + struct.pack(">H", length) + self._rand_data[:length - 3] + data
def _pack_post_data(self, cmd, pack_id, data):
reqid_str = struct.pack(">H", self._request_id)
recv_id = self._recvqueue.get_begin_id()
rsp_data = ''.join([CMD_VER_STR, chr(cmd), reqid_str, struct.pack(">I", recv_id), struct.pack(">I", pack_id), data, reqid_str])
rsp_data = b''.join([CMD_VER_STR, common.chr(cmd), reqid_str, struct.pack(">I", recv_id), struct.pack(">I", pack_id), data, reqid_str])
return rsp_data
def _pack_post_data_64(self, cmd, send_id, pack_id, data):
reqid_str = struct.pack(">H", self._request_id)
recv_id = self._recvqueue.get_begin_id()
rsp_data = ''.join([CMD_VER_STR, chr(cmd), reqid_str, struct.pack(">Q", recv_id), struct.pack(">Q", pack_id), data, reqid_str])
rsp_data = b''.join([CMD_VER_STR, common.chr(cmd), reqid_str, struct.pack(">Q", recv_id), struct.pack(">Q", pack_id), data, reqid_str])
return rsp_data
def sweep_timeout(self):
@ -615,7 +619,7 @@ class TCPRelayHandler(object):
if self._stage == STAGE_STREAM:
pack_id, missing = self._recvqueue.get_missing_id(0)
logging.info("sweep_timeout %s %s" % (pack_id, missing))
data = ''
data = b''
for pid in missing:
data += struct.pack(">H", pid)
rsp_data = self._pack_post_data(CMD_SYN_STATUS, pack_id, data)
@ -638,7 +642,7 @@ class TCPRelayHandler(object):
# post CMD_SYN_STATUS
send_id = self._sendingqueue.get_end_id()
post_pack_id, missing = self._recvqueue.get_missing_id(0)
pack_ids_data = ''
pack_ids_data = b''
for pid in missing:
pack_ids_data += struct.pack(">H", pid)
@ -671,7 +675,7 @@ class TCPRelayHandler(object):
if self._stage == STAGE_RSP_ID:
if cmd == CMD_CONNECT:
for i in xrange(2):
for i in range(2):
rsp_data = self._pack_rsp_data(CMD_RSP_CONNECT, RSP_STATE_CONNECTED)
self._write_to_sock(rsp_data, self._local_sock, addr)
elif cmd == CMD_CONNECT_REMOTE:
@ -695,7 +699,7 @@ class TCPRelayHandler(object):
if cmd == CMD_CONNECT_REMOTE:
local_id = data[0:4]
if self._local_id == local_id:
for i in xrange(2):
for i in range(2):
rsp_data = self._pack_rsp_data(CMD_RSP_CONNECT_REMOTE, RSP_STATE_CONNECTEDREMOTE)
self._write_to_sock(rsp_data, self._local_sock, addr)
else:
@ -873,8 +877,8 @@ class UDPRelay(object):
self._dns_cache = lru_cache.LRUCache(timeout=300)
self._eventloop = None
self._closed = False
self.server_transfer_ul = 0L
self.server_transfer_dl = 0L
self.server_transfer_ul = 0
self.server_transfer_dl = 0
self._sockets = set()
self._fd_to_handlers = {}
@ -927,7 +931,7 @@ class UDPRelay(object):
def _pre_parse_udp_header(self, data):
if data is None:
return
datatype = ord(data[0])
datatype = common.ord(data[0])
if datatype == 0x8:
if len(data) >= 8:
crc = binascii.crc32(data) & 0xffffffff
@ -935,17 +939,17 @@ class UDPRelay(object):
logging.warn('uncorrect CRC32, maybe wrong password or '
'encryption method')
return None
cmd = ord(data[1])
cmd = common.ord(data[1])
request_id = struct.unpack('>H', data[2:4])[0]
data = data[4:-4]
return (cmd, request_id, data)
elif len(data) >= 6 and ord(data[1]) == 0x0:
elif len(data) >= 6 and common.ord(data[1]) == 0x0:
crc = binascii.crc32(data) & 0xffffffff
if crc != 0xffffffff:
logging.warn('uncorrect CRC32, maybe wrong password or '
'encryption method')
return None
cmd = ord(data[1])
cmd = common.ord(data[1])
data = data[2:-4]
return (cmd, 0, data)
else:
@ -955,9 +959,9 @@ class UDPRelay(object):
return data
def _pack_rsp_data(self, cmd, request_id, data):
_rand_data = "123456789abcdefghijklmnopqrstuvwxyz" * 2
_rand_data = b"123456789abcdefghijklmnopqrstuvwxyz" * 2
reqid_str = struct.pack(">H", request_id)
return ''.join([CMD_VER_STR, chr(cmd), reqid_str, data, _rand_data[:random.randint(0, len(_rand_data))], reqid_str])
return b''.join([CMD_VER_STR, common.chr(cmd), reqid_str, data, _rand_data[:random.randint(0, len(_rand_data))], reqid_str])
def _handle_server(self):
server = self._server_socket
@ -994,12 +998,12 @@ class UDPRelay(object):
try:
if data[0] == 0:
if len(data[2]) >= 4:
for i in xrange(64):
for i in range(64):
req_id = random.randint(1, 65535)
if req_id not in self._reqid_to_hd:
break
if req_id in self._reqid_to_hd:
for i in xrange(64):
for i in range(64):
req_id = random.randint(1, 65535)
if type(self._reqid_to_hd[req_id]) is tuple:
break

Loading…
Cancel
Save