Skip to content

Commit 8f40227

Browse files
committed
merge manyuser branch
1 parent a37623b commit 8f40227

File tree

13 files changed

+679
-63
lines changed

13 files changed

+679
-63
lines changed

shadowsocks/manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def __init__(self, config):
4444
self._statistics = collections.defaultdict(int)
4545
self._control_client_addr = None
4646
try:
47-
manager_address = config['manager_address']
47+
manager_address = common.to_str(config['manager_address'])
4848
if ':' in manager_address:
4949
addr = manager_address.rsplit(':', 1)
5050
addr = addr[0], int(addr[1])

shadowsocks/obfs.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env python
2+
#
3+
# Copyright 2015-2015 breakwa11
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License. You may obtain
7+
# a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
# License for the specific language governing permissions and limitations
15+
# under the License.
16+
17+
from __future__ import absolute_import, division, print_function, \
18+
with_statement
19+
20+
import os
21+
import sys
22+
import hashlib
23+
import logging
24+
25+
from shadowsocks import common
26+
from shadowsocks.obfsplugin import plain, http_simple, verify_simple
27+
28+
29+
method_supported = {}
30+
method_supported.update(plain.obfs)
31+
method_supported.update(http_simple.obfs)
32+
method_supported.update(verify_simple.obfs)
33+
34+
class Obfs(object):
35+
def __init__(self, method):
36+
self.method = method
37+
self._method_info = self.get_method_info(method)
38+
if self._method_info:
39+
self.obfs = self.get_obfs(method)
40+
else:
41+
logging.error('method %s not supported' % method)
42+
sys.exit(1)
43+
44+
def get_method_info(self, method):
45+
method = method.lower()
46+
m = method_supported.get(method)
47+
return m
48+
49+
def get_obfs(self, method):
50+
m = self._method_info
51+
return m[0](method)
52+
53+
def client_pre_encrypt(self, buf):
54+
return self.obfs.client_pre_encrypt(buf)
55+
56+
def client_encode(self, buf):
57+
return self.obfs.client_encode(buf)
58+
59+
def client_decode(self, buf):
60+
return self.obfs.client_decode(buf)
61+
62+
def client_post_decrypt(self, buf):
63+
return self.obfs.client_post_decrypt(buf)
64+
65+
def server_pre_encrypt(self, buf):
66+
return self.obfs.server_pre_encrypt(buf)
67+
68+
def server_encode(self, buf):
69+
return self.obfs.server_encode(buf)
70+
71+
def server_decode(self, buf):
72+
return self.obfs.server_decode(buf)
73+
74+
def server_post_decrypt(self, buf):
75+
return self.obfs.server_post_decrypt(buf)
76+

shadowsocks/obfsplugin/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env python
2+
#
3+
# Copyright 2015 clowwindy
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License. You may obtain
7+
# a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
# License for the specific language governing permissions and limitations
15+
# under the License.
16+
17+
from __future__ import absolute_import, division, print_function, \
18+
with_statement

shadowsocks/obfsplugin/http_simple.py

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
#!/usr/bin/env python
2+
#
3+
# Copyright 2015-2015 breakwa11
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License. You may obtain
7+
# a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
# License for the specific language governing permissions and limitations
15+
# under the License.
16+
17+
from __future__ import absolute_import, division, print_function, \
18+
with_statement
19+
20+
import os
21+
import sys
22+
import hashlib
23+
import logging
24+
import binascii
25+
import base64
26+
import datetime
27+
28+
from shadowsocks.obfsplugin import plain
29+
from shadowsocks import common
30+
from shadowsocks.common import to_bytes, to_str, ord
31+
32+
def create_http_obfs(method):
33+
return http_simple(method)
34+
35+
def create_http2_obfs(method):
36+
return http2_simple(method)
37+
38+
def create_tls_obfs(method):
39+
return tls_simple(method)
40+
41+
def create_random_head_obfs(method):
42+
return random_head(method)
43+
44+
obfs = {
45+
'http_simple': (create_http_obfs,),
46+
'http2_simple': (create_http2_obfs,),
47+
'tls_simple': (create_tls_obfs,),
48+
'random_head': (create_random_head_obfs,),
49+
}
50+
51+
def match_begin(str1, str2):
52+
if len(str1) >= len(str2):
53+
if str1[:len(str2)] == str2:
54+
return True
55+
return False
56+
57+
class http_simple(plain.plain):
58+
def __init__(self, method):
59+
self.method = method
60+
self.has_sent_header = False
61+
self.has_recv_header = False
62+
self.host = None
63+
self.port = 0
64+
self.recv_buffer = b''
65+
66+
def client_encode(self, buf):
67+
# TODO
68+
return buf
69+
70+
def client_decode(self, buf):
71+
# TODO
72+
return (buf, False)
73+
74+
def server_encode(self, buf):
75+
if self.has_sent_header:
76+
return buf
77+
78+
header = b'HTTP/1.1 200 OK\r\nServer: openresty\r\nDate: '
79+
header += to_bytes(datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S GMT'))
80+
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'
81+
self.has_sent_header = True
82+
return header + buf
83+
84+
def get_data_from_http_header(self, buf):
85+
ret_buf = b''
86+
lines = buf.split(b'\r\n')
87+
if lines and len(lines) > 4:
88+
hex_items = lines[0].split(b'%')
89+
if hex_items and len(hex_items) > 1:
90+
for index in range(1, len(hex_items)):
91+
if len(hex_items[index]) != 2:
92+
ret_buf += binascii.unhexlify(hex_items[index][:2])
93+
break
94+
ret_buf += binascii.unhexlify(hex_items[index])
95+
return ret_buf
96+
return b''
97+
98+
def server_decode(self, buf):
99+
if self.has_recv_header:
100+
return (buf, True, False)
101+
102+
buf = self.recv_buffer + buf
103+
if len(buf) > 10:
104+
if match_begin(buf, b'GET /') or match_begin(buf, b'POST /'):
105+
if len(buf) > 65536:
106+
self.has_sent_header = True
107+
self.has_recv_header = True
108+
self.recv_buffer = None
109+
return (buf, True, False)
110+
else: #not http header, run on original protocol
111+
self.has_sent_header = True
112+
self.has_recv_header = True
113+
self.recv_buffer = None
114+
return (buf, True, False)
115+
else:
116+
self.recv_buffer = buf
117+
return (b'', True, False)
118+
119+
datas = buf.split(b'\r\n\r\n', 1)
120+
if datas and len(datas) > 1:
121+
ret_buf = self.get_data_from_http_header(buf)
122+
ret_buf += datas[1]
123+
if len(ret_buf) >= 15:
124+
self.has_recv_header = True
125+
return (ret_buf, True, False)
126+
self.recv_buffer = buf
127+
return (b'', True, False)
128+
else:
129+
self.recv_buffer = buf
130+
return (b'', True, False)
131+
self.has_sent_header = True
132+
self.has_recv_header = True
133+
return (buf, True, False)
134+
135+
class http2_simple(plain.plain):
136+
def __init__(self, method):
137+
self.method = method
138+
self.has_sent_header = False
139+
self.has_recv_header = False
140+
self.host = None
141+
self.port = 0
142+
self.recv_buffer = b''
143+
144+
def client_encode(self, buf):
145+
# TODO
146+
return buf
147+
148+
def client_decode(self, buf):
149+
# TODO
150+
return (buf, False)
151+
152+
def server_encode(self, buf):
153+
if self.has_sent_header:
154+
return buf
155+
156+
header = b'HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nUpgrade: h2c\r\n\r\n'
157+
self.has_sent_header = True
158+
return header + buf
159+
160+
def server_decode(self, buf):
161+
if self.has_recv_header:
162+
return (buf, True, False)
163+
164+
buf = self.recv_buffer + buf
165+
if len(buf) > 10:
166+
if match_begin(buf, b'GET /'):
167+
pass
168+
else: #not http header, run on original protocol
169+
self.has_sent_header = True
170+
self.has_recv_header = True
171+
self.recv_buffer = None
172+
return (buf, True, False)
173+
else:
174+
self.recv_buffer = buf
175+
return (b'', True, False)
176+
177+
datas = buf.split(b'\r\n\r\n', 1)
178+
if datas and len(datas) > 1 and len(datas[0]) >= 4:
179+
lines = buf.split(b'\r\n')
180+
if lines and len(lines) >= 4:
181+
if match_begin(lines[4], b'HTTP2-Settings: '):
182+
ret_buf = base64.urlsafe_b64decode(lines[4][16:])
183+
ret_buf += datas[1]
184+
self.has_recv_header = True
185+
return (ret_buf, True, False)
186+
self.recv_buffer = buf
187+
return (b'', True, False)
188+
else:
189+
self.recv_buffer = buf
190+
return (b'', True, False)
191+
self.has_sent_header = True
192+
self.has_recv_header = True
193+
return (buf, True, False)
194+
195+
class tls_simple(plain.plain):
196+
def __init__(self, method):
197+
self.method = method
198+
self.has_sent_header = False
199+
self.has_recv_header = False
200+
201+
def client_encode(self, buf):
202+
return buf
203+
204+
def client_decode(self, buf):
205+
# (buffer_to_recv, is_need_to_encode_and_send_back)
206+
return (buf, False)
207+
208+
def server_encode(self, buf):
209+
if self.has_sent_header:
210+
return buf
211+
self.has_sent_header = True
212+
# TODO
213+
#server_hello = b''
214+
return b'\x16\x03\x01'
215+
216+
def server_decode(self, buf):
217+
if self.has_recv_header:
218+
return (buf, True, False)
219+
220+
self.has_recv_header = True
221+
if not match_begin(buf, b'\x16\x03\x01'):
222+
self.has_sent_header = True
223+
return (buf, True, False)
224+
# (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back)
225+
return (b'', False, True)
226+
227+
class random_head(plain.plain):
228+
def __init__(self, method):
229+
self.method = method
230+
self.has_sent_header = False
231+
self.has_recv_header = False
232+
233+
def client_encode(self, buf):
234+
return buf
235+
236+
def client_decode(self, buf):
237+
# (buffer_to_recv, is_need_to_encode_and_send_back)
238+
return (buf, False)
239+
240+
def server_encode(self, buf):
241+
if self.has_sent_header:
242+
return buf
243+
self.has_sent_header = True
244+
return os.urandom(common.ord(os.urandom(1)[0]) % 96 + 4)
245+
246+
def server_decode(self, buf):
247+
if self.has_recv_header:
248+
return (buf, True, False)
249+
250+
self.has_recv_header = True
251+
crc = binascii.crc32(buf) & 0xffffffff
252+
if crc != 0xffffffff:
253+
self.has_sent_header = True
254+
return (buf, True, False)
255+
# (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back)
256+
return (b'', False, True)
257+

0 commit comments

Comments
 (0)