From 61959945c3591c062ba44dc8639cb32109fff446 Mon Sep 17 00:00:00 2001 From: breakwa11 Date: Thu, 27 Aug 2015 16:41:00 +0800 Subject: [PATCH 1/4] revert encrypt.py new obfs header --- shadowsocks/common.py | 21 ++++++++++++++++++--- shadowsocks/encrypt.py | 6 +----- shadowsocks/tcprelay.py | 2 ++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index 11b0622..cc88d5d 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -21,7 +21,7 @@ from __future__ import absolute_import, division, print_function, \ import socket import struct import logging - +import binascii def compat_ord(s): if type(s) == int: @@ -140,7 +140,7 @@ def pack_addr(address): def pre_parse_header(data): datatype = ord(data[0]) - if datatype == 0x80 : + if datatype == 0x80: if len(data) <= 2: return None rand_data_size = ord(data[1]) @@ -151,7 +151,7 @@ def pre_parse_header(data): data = data[rand_data_size + 2:] elif datatype == 0x81: data = data[1:] - elif datatype == 0x82 : + elif datatype == 0x82: if len(data) <= 3: return None rand_data_size = struct.unpack('>H', data[1:3])[0] @@ -160,6 +160,21 @@ def pre_parse_header(data): 'encryption method') return None data = data[rand_data_size + 3:] + elif datatype == 0x88: + if len(data) <= 7 + 7: + return None + data_size = struct.unpack('>H', data[1:3])[0] + ogn_data = data + data = data[:data_size] + crc = binascii.crc32(data) & 0xffffffff + if crc != 0xffffffff: + logging.warn('uncorrect CRC32, maybe wrong password or ' + 'encryption method') + return None + start_pos = 3 + ord(data[3]) + data = data[start_pos:-4] + if data_size < len(ogn_data): + data += ogn_data[data_size:] return data def parse_header(data): diff --git a/shadowsocks/encrypt.py b/shadowsocks/encrypt.py index 5d11138..834b18c 100644 --- a/shadowsocks/encrypt.py +++ b/shadowsocks/encrypt.py @@ -21,7 +21,6 @@ import os import sys import hashlib import logging -import random from shadowsocks import common from shadowsocks.crypto import rc4_md5, openssl, sodium, table @@ -35,10 +34,7 @@ method_supported.update(table.ciphers) def random_string(length): - try: - return os.urandom(length) - except (AttributeError, NotImplementedError): - return ''.join(chr(random.randrange(255)) for _ in range(length)) + return os.urandom(length) cached_keys = {} diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index e9d78e2..8c53113 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -341,6 +341,8 @@ class TCPRelayHandler(object): logging.error('unknown command %d', cmd) self.destroy() return + if False and ord(data[0]) != 0x88: # force new header + raise Exception('can not parse header') data = pre_parse_header(data) if data is None: raise Exception('can not parse header') From 5249fd528cfaed4c9f04de6270e4365a0075387d Mon Sep 17 00:00:00 2001 From: breakwa11 Date: Fri, 28 Aug 2015 13:35:10 +0800 Subject: [PATCH 2/4] add FORCE_NEW_PROTOCOL switch --- shadowsocks/tcprelay.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 8c53113..cb478f4 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -30,6 +30,10 @@ import random from shadowsocks import encrypt, 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 + # we clear at most TIMEOUTS_CLEAN_SIZE timeouts each time TIMEOUTS_CLEAN_SIZE = 512 @@ -341,7 +345,7 @@ class TCPRelayHandler(object): logging.error('unknown command %d', cmd) self.destroy() return - if False and ord(data[0]) != 0x88: # force new header + if FORCE_NEW_PROTOCOL and ord(data[0]) != 0x88: raise Exception('can not parse header') data = pre_parse_header(data) if data is None: From 56af51b43c8d9d5bed39e2399fe426d0c05e167a Mon Sep 17 00:00:00 2001 From: breakwa11 Date: Tue, 1 Sep 2015 16:58:05 +0800 Subject: [PATCH 3/4] log TCP header if parse error --- shadowsocks/tcprelay.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index cb478f4..314e96d 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -319,7 +319,7 @@ class TCPRelayHandler(object): traceback.print_exc() self.destroy() - def _handle_stage_addr(self, data): + def _handle_stage_addr(self, ogn_data, data): try: if self._is_local: cmd = common.ord(data[1]) @@ -345,13 +345,18 @@ class TCPRelayHandler(object): logging.error('unknown command %d', cmd) self.destroy() 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') 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') 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') connecttype, remote_addr, remote_port, header_length = header_result logging.info('%s connecting %s:%d from %s:%d' % @@ -497,6 +502,7 @@ class TCPRelayHandler(object): if not data: self.destroy() return + ogn_data = data self._update_activity(len(data)) if not is_local: data = self._encryptor.decrypt(data) @@ -517,7 +523,7 @@ class TCPRelayHandler(object): self._handle_stage_connecting(data) elif (is_local and self._stage == STAGE_ADDR) or \ (not is_local and self._stage == STAGE_INIT): - self._handle_stage_addr(data) + self._handle_stage_addr(ogn_data, data) def _on_remote_read(self, is_remote_sock): # handle all remote read events From 55d10ace01eea22c2b9205c2e9a1eec6d4b12e79 Mon Sep 17 00:00:00 2001 From: breakwa11 Date: Wed, 2 Sep 2015 23:57:51 +0800 Subject: [PATCH 4/4] simple reverse proxy --- shadowsocks/tcprelay.py | 42 +++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 314e96d..cecd017 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -114,6 +114,7 @@ class TCPRelayHandler(object): self._stage = STAGE_INIT self._encryptor = encrypt.Encryptor(config['password'], config['method']) + self._encrypt_correct = True self._fastopen_connected = False self._data_to_write_to_local = [] self._data_to_write_to_remote = [] @@ -281,6 +282,31 @@ 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 = [("www.bing.com", 80), ("www.microsoft.com", 80), ("www.baidu.com", 443), ("www.qq.com", 80), ("www.csdn.net", 80), ("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): + logging.warn("Protocol ERROR, TCP ogn data %s" % (binascii.hexlify(ogn_data), )) + self._encrypt_correct = False + #create redirect or disconnect by hash code + host, port = self._get_redirect_host(client_address, ogn_data) + data = "\x03" + chr(len(host)) + host + struct.pack('>H', port) + logging.warn("TCP data redir %s:%d %s" % (host, port, binascii.hexlify(data))) + #raise Exception('can not parse header') + return data + ogn_data + def _handle_stage_connecting(self, data): if self._is_local: data = self._encryptor.encrypt(data) @@ -348,16 +374,14 @@ class TCPRelayHandler(object): 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') + 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', @@ -505,7 +529,8 @@ class TCPRelayHandler(object): ogn_data = data self._update_activity(len(data)) if not is_local: - data = self._encryptor.decrypt(data) + if self._encrypt_correct: + data = self._encryptor.decrypt(data) if not data: return self._server.server_transfer_ul += len(data) @@ -557,7 +582,8 @@ class TCPRelayHandler(object): if self._is_local: data = self._encryptor.decrypt(data) else: - data = self._encryptor.encrypt(data) + if self._encrypt_correct: + data = self._encryptor.encrypt(data) try: self._write_to_sock(data, self._local_sock) except Exception as e: