Browse Source

fix on python3

http2 obfs
dev
breakwa11 9 years ago
parent
commit
71338a7385
  1. 2
      shadowsocks/manager.py
  2. 127
      shadowsocks/obfsplugin/http_simple.py
  3. 1
      shadowsocks/shell.py
  4. 16
      shadowsocks/tcprelay.py
  5. 38
      shadowsocks/udprelay.py

2
shadowsocks/manager.py

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

127
shadowsocks/obfsplugin/http_simple.py

@ -22,23 +22,35 @@ import sys
import hashlib import hashlib
import logging import logging
import binascii import binascii
import base64
import datetime import datetime
from shadowsocks.common import to_bytes, to_str
def create_obfs(method): def create_http_obfs(method):
return http_simple(method) return http_simple(method)
def create_http2_obfs(method):
return http2_simple(method)
obfs = { obfs = {
'http_simple': (create_obfs,), 'http_simple': (create_http_obfs,),
'http2_simple': (create_http2_obfs,),
} }
def match_begin(str1, str2):
if len(str1) >= len(str2):
if str1[:len(str2)] == str2:
return True
return False
class http_simple(object): class http_simple(object):
def __init__(self, method): def __init__(self, method):
self.method = method self.method = method
self.has_sent_header = False self.has_sent_header = False
self.has_recv_header = False self.has_recv_header = False
self.host = "" self.host = None
self.port = 0 self.port = 0
self.recv_buffer = "" self.recv_buffer = b''
def client_encode(self, buf): def client_encode(self, buf):
# TODO # TODO
@ -52,19 +64,33 @@ class http_simple(object):
if self.has_sent_header: if self.has_sent_header:
return buf return buf
else: else:
header = "HTTP/1.1 200 OK\r\nServer: openresty\r\nDate: " header = b'HTTP/1.1 200 OK\r\nServer: openresty\r\nDate: '
header += datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S GMT') header += to_bytes(datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S GMT'))
header += '''\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''' 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 self.has_sent_header = True
return header + buf 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): def server_decode(self, buf):
if self.has_recv_header: if self.has_recv_header:
return (buf, True, False) return (buf, True, False)
else: else:
buf = self.recv_buffer + buf buf = self.recv_buffer + buf
if len(buf) > 10: if len(buf) > 10:
if buf[:5] == "GET /" or buf[:6] == "POST /": if match_begin(buf, b'GET /') or match_begin(buf, b'POST /'):
pass pass
else: #not http header, run on original protocol else: #not http header, run on original protocol
self.has_sent_header = True self.has_sent_header = True
@ -73,26 +99,81 @@ class http_simple(object):
return (buf, True, False) return (buf, True, False)
else: else:
self.recv_buffer = buf self.recv_buffer = buf
return ("", True, False) return (b'', True, False)
datas = buf.split('\r\n\r\n', 1) datas = buf.split(b'\r\n\r\n', 1)
if datas and len(datas) > 1 and len(datas[1]) >= 7: ret_buf = b''
lines = buf.split('\r\n') if datas and len(datas) > 1:
if lines and len(lines) > 4: ret_buf = self.get_data_from_http_header(buf)
hex_items = lines[0].split('%') ret_buf += datas[1]
if hex_items and len(hex_items) > 1: if len(ret_buf) >= 15:
ret_buf = "" self.has_recv_header = True
for index in xrange(1, len(hex_items)): return (ret_buf, True, False)
if len(hex_items[index]) != 2: self.recv_buffer = buf
ret_buf += binascii.unhexlify(hex_items[index][:2]) return (b'', True, False)
break else:
ret_buf += binascii.unhexlify(hex_items[index]) 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(object):
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
else:
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)
else:
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] ret_buf += datas[1]
self.has_recv_header = True self.has_recv_header = True
return (ret_buf, True, False) return (ret_buf, True, False)
self.recv_buffer = buf
return (b'', True, False)
else: else:
self.recv_buffer = buf self.recv_buffer = buf
return ("", True, False) return (b'', True, False)
self.has_sent_header = True self.has_sent_header = True
self.has_recv_header = True self.has_recv_header = True
return (buf, True, False) return (buf, True, False)

1
shadowsocks/shell.py

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

16
shadowsocks/tcprelay.py

@ -117,11 +117,11 @@ class TCPRelayHandler(object):
self._encryptor = encrypt.Encryptor(config['password'], self._encryptor = encrypt.Encryptor(config['password'],
config['method']) config['method'])
self._encrypt_correct = True self._encrypt_correct = True
self._obfs = obfs.Obfs(config.get('obfs', 'plain')) self._obfs = obfs.Obfs(config['obfs'])
self._fastopen_connected = False self._fastopen_connected = False
self._data_to_write_to_local = [] self._data_to_write_to_local = []
self._data_to_write_to_remote = [] self._data_to_write_to_remote = []
self._udp_data_send_buffer = '' self._udp_data_send_buffer = b''
self._upstream_status = WAIT_STATUS_READING self._upstream_status = WAIT_STATUS_READING
self._downstream_status = WAIT_STATUS_INIT self._downstream_status = WAIT_STATUS_INIT
self._client_address = local_sock.getpeername()[:2] self._client_address = local_sock.getpeername()[:2]
@ -293,7 +293,7 @@ class TCPRelayHandler(object):
def _get_redirect_host(self, client_address, ogn_data): def _get_redirect_host(self, client_address, ogn_data):
# test # 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)] 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) hash_code = binascii.crc32(ogn_data)
addrs = socket.getaddrinfo(client_address[0], client_address[1], 0, socket.SOCK_STREAM, socket.SOL_TCP) addrs = socket.getaddrinfo(client_address[0], client_address[1], 0, socket.SOCK_STREAM, socket.SOL_TCP)
af, socktype, proto, canonname, sa = addrs[0] af, socktype, proto, canonname, sa = addrs[0]
@ -312,7 +312,7 @@ class TCPRelayHandler(object):
self._encrypt_correct = False self._encrypt_correct = False
#create redirect or disconnect by hash code #create redirect or disconnect by hash code
host, port = self._get_redirect_host(client_address, ogn_data) host, port = self._get_redirect_host(client_address, ogn_data)
data = "\x03" + chr(len(host)) + host + struct.pack('>H', port) 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))) logging.warn("TCP data redir %s:%d %s" % (host, port, binascii.hexlify(data)))
return data + ogn_data return data + ogn_data
@ -551,7 +551,7 @@ class TCPRelayHandler(object):
if self._encrypt_correct: if self._encrypt_correct:
obfs_decode = self._obfs.server_decode(data) obfs_decode = self._obfs.server_decode(data)
if obfs_decode[2]: if obfs_decode[2]:
self._write_to_sock("", self._local_sock) self._write_to_sock(b'', self._local_sock)
if obfs_decode[1]: if obfs_decode[1]:
data = self._encryptor.decrypt(obfs_decode[0]) data = self._encryptor.decrypt(obfs_decode[0])
else: else:
@ -588,10 +588,10 @@ class TCPRelayHandler(object):
port = struct.pack('>H', addr[1]) port = struct.pack('>H', addr[1])
try: try:
ip = socket.inet_aton(addr[0]) ip = socket.inet_aton(addr[0])
data = '\x00\x01' + ip + port + data data = b'\x00\x01' + ip + port + data
except Exception as e: except Exception as e:
ip = socket.inet_pton(socket.AF_INET6, addr[0]) 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 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])) #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: else:
@ -608,7 +608,7 @@ class TCPRelayHandler(object):
if self._is_local: if self._is_local:
obfs_decode = self._obfs.client_decode(data) obfs_decode = self._obfs.client_decode(data)
if obfs_decode[1]: if obfs_decode[1]:
self._write_to_sock("", self._remote_sock) self._write_to_sock(b'', self._remote_sock)
data = self._encryptor.decrypt(obfs_decode[0]) data = self._encryptor.decrypt(obfs_decode[0])
else: else:
if self._encrypt_correct: if self._encrypt_correct:

38
shadowsocks/udprelay.py

@ -115,15 +115,15 @@ CMD_POST_64 = 6
CMD_SYN_STATUS_64 = 7 CMD_SYN_STATUS_64 = 7
CMD_DISCONNECT = 8 CMD_DISCONNECT = 8
CMD_VER_STR = "\x08" CMD_VER_STR = b"\x08"
RSP_STATE_EMPTY = "" RSP_STATE_EMPTY = b""
RSP_STATE_REJECT = "\x00" RSP_STATE_REJECT = b"\x00"
RSP_STATE_CONNECTED = "\x01" RSP_STATE_CONNECTED = b"\x01"
RSP_STATE_CONNECTEDREMOTE = "\x02" RSP_STATE_CONNECTEDREMOTE = b"\x02"
RSP_STATE_ERROR = "\x03" RSP_STATE_ERROR = b"\x03"
RSP_STATE_DISCONNECT = "\x04" RSP_STATE_DISCONNECT = b"\x04"
RSP_STATE_REDIRECT = "\x05" RSP_STATE_REDIRECT = b"\x05"
class UDPLocalAddress(object): class UDPLocalAddress(object):
def __init__(self, addr): def __init__(self, addr):
@ -309,7 +309,7 @@ class TCPRelayHandler(object):
self._random_mtu_size = [random.randint(POST_MTU_MIN, POST_MTU_MAX) for i in range(1024)] self._random_mtu_size = [random.randint(POST_MTU_MIN, POST_MTU_MAX) for i in range(1024)]
self._random_mtu_index = 0 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): def __hash__(self):
# default __hash__ is id / 16 # default __hash__ is id / 16
@ -589,29 +589,29 @@ class TCPRelayHandler(object):
def _pack_rsp_data(self, cmd, data): def _pack_rsp_data(self, cmd, data):
reqid_str = struct.pack(">H", self._request_id) 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): def _pack_rnd_data(self, data):
length = random.randint(0, len(self._rand_data)) length = random.randint(0, len(self._rand_data))
if length == 0: if length == 0:
return data return data
elif length == 1: elif length == 1:
return "\x81" + data return b"\x81" + data
elif length < 256: 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: 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): def _pack_post_data(self, cmd, pack_id, data):
reqid_str = struct.pack(">H", self._request_id) reqid_str = struct.pack(">H", self._request_id)
recv_id = self._recvqueue.get_begin_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 return rsp_data
def _pack_post_data_64(self, cmd, send_id, pack_id, data): def _pack_post_data_64(self, cmd, send_id, pack_id, data):
reqid_str = struct.pack(">H", self._request_id) reqid_str = struct.pack(">H", self._request_id)
recv_id = self._recvqueue.get_begin_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 return rsp_data
def sweep_timeout(self): def sweep_timeout(self):
@ -619,7 +619,7 @@ class TCPRelayHandler(object):
if self._stage == STAGE_STREAM: if self._stage == STAGE_STREAM:
pack_id, missing = self._recvqueue.get_missing_id(0) pack_id, missing = self._recvqueue.get_missing_id(0)
logging.info("sweep_timeout %s %s" % (pack_id, missing)) logging.info("sweep_timeout %s %s" % (pack_id, missing))
data = '' data = b''
for pid in missing: for pid in missing:
data += struct.pack(">H", pid) data += struct.pack(">H", pid)
rsp_data = self._pack_post_data(CMD_SYN_STATUS, pack_id, data) rsp_data = self._pack_post_data(CMD_SYN_STATUS, pack_id, data)
@ -642,7 +642,7 @@ class TCPRelayHandler(object):
# post CMD_SYN_STATUS # post CMD_SYN_STATUS
send_id = self._sendingqueue.get_end_id() send_id = self._sendingqueue.get_end_id()
post_pack_id, missing = self._recvqueue.get_missing_id(0) post_pack_id, missing = self._recvqueue.get_missing_id(0)
pack_ids_data = '' pack_ids_data = b''
for pid in missing: for pid in missing:
pack_ids_data += struct.pack(">H", pid) pack_ids_data += struct.pack(">H", pid)
@ -959,9 +959,9 @@ class UDPRelay(object):
return data return data
def _pack_rsp_data(self, cmd, request_id, 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) 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): def _handle_server(self):
server = self._server_socket server = self._server_socket

Loading…
Cancel
Save