Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd723a92c4 | ||
|
|
b3cf97c44e | ||
|
|
e3ec6bb5f8 | ||
|
|
e8b4bb05a0 | ||
|
|
604cbf8a78 | ||
|
|
cb1dcacd4b | ||
|
|
7dddf07da4 | ||
|
|
838caa3f1d | ||
|
|
38ef6a2546 | ||
|
|
ab236ff03e | ||
|
|
4198ec84ce | ||
|
|
0c1a9368c9 | ||
|
|
339c1ccee2 | ||
|
|
a629d1baec | ||
|
|
bc930031a9 | ||
|
|
1a2c7ed933 | ||
|
|
ab8765191e | ||
|
|
e21f01755a | ||
|
|
57ba4f5f67 | ||
|
|
727d218ff2 | ||
|
|
adbaafa18a | ||
|
|
7c6c671a5e | ||
|
|
0d40a52a95 | ||
|
|
d8d19a99fc | ||
|
|
19c868242a | ||
|
|
f6752fb21b | ||
|
|
0e045a36e7 | ||
|
|
7b62f9f530 | ||
|
|
b655222857 | ||
|
|
71625e6415 | ||
|
|
44185c9fff | ||
|
|
82f8fef28a | ||
|
|
f22ef99d85 | ||
|
|
47c6e16833 | ||
|
|
8826f515b6 | ||
|
|
7c3d4e01b1 | ||
|
|
280c762760 | ||
|
|
2cf5f1982e | ||
|
|
b89931f9fc | ||
|
|
f31a81665c | ||
|
|
92722c2ea9 | ||
|
|
a3315bcd6a | ||
|
|
587453c881 | ||
|
|
c1364c3afe | ||
|
|
77628acde4 | ||
|
|
542a2e62d9 | ||
|
|
3b0200581c | ||
|
|
24623b896d | ||
|
|
86de572caf | ||
|
|
7a68e4e637 | ||
|
|
6e3fdd0d62 | ||
|
|
1dc6c4be45 | ||
|
|
d6e1f73c9f | ||
|
|
85b6930d7d | ||
|
|
96200d7d76 | ||
|
|
67065a212f | ||
|
|
c1f1281b83 | ||
|
|
abe4ac3b90 | ||
|
|
6c162b13c4 | ||
|
|
709e67afe4 | ||
|
|
19e2e7422d | ||
|
|
5e14119b41 | ||
|
|
a1f56d1233 | ||
|
|
be696555a7 | ||
|
|
45faa93dd3 | ||
|
|
7599e53a6f | ||
|
|
8aa3fcd207 | ||
|
|
26f360c204 | ||
|
|
5cb9445dfd | ||
|
|
2388802e3f | ||
|
|
0969937fd1 | ||
|
|
d968f01245 | ||
|
|
03b1a53b9a | ||
|
|
1e85d14a48 | ||
|
|
c3bb64bc2c | ||
|
|
3c308ca876 | ||
|
|
f5f4bd0339 | ||
|
|
f3a3ab4b84 | ||
|
|
5896eac1a8 | ||
|
|
582d7b7256 | ||
|
|
8322cd9d8f | ||
|
|
73af0eeea0 | ||
|
|
caadb606ba | ||
|
|
774134ffad | ||
|
|
db6629a130 | ||
|
|
7deba0b995 | ||
|
|
036df8aac6 | ||
|
|
1e7e707024 | ||
|
|
2b64802fed | ||
|
|
a3a8ed996c | ||
|
|
51c8ae4d78 | ||
|
|
862fa890ee | ||
|
|
d82c931f8e | ||
|
|
8e27035851 | ||
|
|
4d9fca9b37 | ||
|
|
90ffd1c6ee | ||
|
|
922cf462d6 | ||
|
|
76fd4296a5 | ||
|
|
63cceb29f9 | ||
|
|
1b1c1fbac6 | ||
|
|
6cbcd855ff | ||
|
|
bf0055be40 | ||
|
|
63a6de5d26 | ||
|
|
1c58b10dd0 | ||
|
|
6712dd62c0 | ||
|
|
caaacc7090 | ||
|
|
6f35105ea2 | ||
|
|
e19f9e70e7 | ||
|
|
e3d6efbc7a | ||
|
|
1355c273db | ||
|
|
bae7d7a0c2 | ||
|
|
e1b199fc44 | ||
|
|
e2957e672e | ||
|
|
f296751def | ||
|
|
c4c1cd6dc1 | ||
|
|
74083af472 | ||
|
|
6732244ddf | ||
|
|
2723295101 | ||
|
|
85955d6e6d | ||
|
|
72ca31dad9 | ||
|
|
2750af4a2e | ||
|
|
bf8bb8afe2 | ||
|
|
5d479087a8 | ||
|
|
26b4d8bc18 | ||
|
|
3014b0034c | ||
|
|
bf443d72ec | ||
|
|
31aaea603a | ||
|
|
390be3bf8d | ||
|
|
e7a61a934f | ||
|
|
96760ed438 | ||
|
|
00a31285ed | ||
|
|
68693d5ec7 | ||
|
|
7cfbe49806 | ||
|
|
9c794624e0 | ||
|
|
ccacef1556 | ||
|
|
75643635d4 | ||
|
|
4048abaeff | ||
|
|
d4b7483ef0 | ||
|
|
ad892e1223 | ||
|
|
2de2a553d7 | ||
|
|
588d8ad8b3 | ||
|
|
278ce30c66 | ||
|
|
25c66dab14 | ||
|
|
de67c11207 | ||
|
|
4f52dffcc1 | ||
|
|
cd825c9c2c | ||
|
|
e6410c067b | ||
|
|
f0b0a31baf | ||
|
|
6d53546f6e | ||
|
|
8777152ec9 | ||
|
|
f05bea4e10 | ||
|
|
0bf1e46ace | ||
|
|
0f8dd36297 | ||
|
|
498f71a21b | ||
|
|
490ae12128 | ||
|
|
84fd119f26 | ||
|
|
fd9214b334 | ||
|
|
b367ad0560 | ||
|
|
0ca45be89d | ||
|
|
959aad3f41 |
@@ -29,3 +29,8 @@ htmlcov
|
||||
|
||||
.DS_Store
|
||||
.idea
|
||||
|
||||
# Ignore user config
|
||||
user-config.json
|
||||
userapiconfig.py
|
||||
usermysql.json
|
||||
|
||||
@@ -1,3 +1,59 @@
|
||||
3.4.0 2017-07-27
|
||||
- add auth_chain_b
|
||||
- add initmudbjson.sh
|
||||
- allow set speed limit in runtime
|
||||
- fix bugs & mem leak
|
||||
|
||||
3.3.3 2017-06-03
|
||||
- add DNS cache
|
||||
- add tls1.2_ticket_fastauth
|
||||
- fix bugs
|
||||
|
||||
3.3.2 2017-05-20
|
||||
- revert http reply
|
||||
- refine tls1.2_ticket_auth error detector
|
||||
|
||||
3.3.1 2017-05-18
|
||||
- fix stop script
|
||||
- Async DNS query under UDP
|
||||
- fix old version of OpenSSL
|
||||
- http reply
|
||||
|
||||
3.3.0 2017-05-11
|
||||
- connect_log include local addr & port
|
||||
- fix auth_chain_a UDP bug
|
||||
- add "additional_ports_only"
|
||||
- add interface legendsockssr
|
||||
- run with newest python version
|
||||
- parse comment in hosts
|
||||
- update mujson_mgr
|
||||
- add cymysql setup script
|
||||
- new speed tester
|
||||
- fix leaks
|
||||
- bugs fixed
|
||||
|
||||
3.2.0 2017-04-27
|
||||
- add auth_chain_a
|
||||
- remove auth_aes128, auth_sha1, auth_sha1_v2, verify_simple, auth_simple, verify_sha1
|
||||
|
||||
3.1.2 2017-04-07
|
||||
- display UID
|
||||
- auto adjust TCP MSS
|
||||
|
||||
3.1.1 2017-03-25
|
||||
- add "New session ticket"
|
||||
- ignore bind 10.0.0.0/8 and 192.168.0.0/16 by default
|
||||
- improve rand size under auth_aes128_*
|
||||
- fix bugs
|
||||
|
||||
3.1.0 2017-03-16
|
||||
- add "glzjinmod" interface
|
||||
- rate limit
|
||||
- add additional_ports in config
|
||||
|
||||
3.0.4 2017-01-08
|
||||
- multi-user in single port
|
||||
|
||||
3.0.1 2017-01-03
|
||||
- remove auth_aes128_*_compatible
|
||||
|
||||
|
||||
+21
-22
@@ -1,32 +1,31 @@
|
||||
FROM alpine
|
||||
FROM alpine:3.6
|
||||
|
||||
ENV SERVER_ADDR 0.0.0.0
|
||||
ENV SERVER_PORT 51348
|
||||
ENV PASSWORD=
|
||||
ENV METHOD aes-256-cfb
|
||||
ENV PROTOCOL origin
|
||||
ENV OBFS http_simple
|
||||
ENV TIMEOUT 300
|
||||
ENV DNS_ADDR 8.8.8.8
|
||||
ENV DNS_ADDR_2 8.8.4.4
|
||||
ENV SERVER_ADDR 0.0.0.0
|
||||
ENV SERVER_PORT 51348
|
||||
ENV PASSWORD psw
|
||||
ENV METHOD aes-128-ctr
|
||||
ENV PROTOCOL auth_aes128_md5
|
||||
ENV PROTOCOLPARAM 32
|
||||
ENV OBFS tls1.2_ticket_auth_compatible
|
||||
ENV TIMEOUT 300
|
||||
ENV DNS_ADDR 8.8.8.8
|
||||
ENV DNS_ADDR_2 8.8.4.4
|
||||
|
||||
ARG BRANCH=manyuser
|
||||
ARG WORK=~
|
||||
|
||||
|
||||
RUN apk update \
|
||||
&& apk add python \
|
||||
RUN apk --no-cache add python \
|
||||
libsodium \
|
||||
unzip \
|
||||
wget \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
wget
|
||||
|
||||
|
||||
|
||||
RUN wget --no-check-certificate https://github.com/breakwa11/shadowsocks/archive/manyuser.zip -O /tmp/manyuser.zip \
|
||||
&& unzip -d /tmp /tmp/manyuser.zip \
|
||||
&& mv /tmp/shadowsocks-manyuser/shadowsocks ~/shadowsocks \
|
||||
&& rm -rf /tmp/*
|
||||
RUN mkdir -p $WORK && \
|
||||
wget -qO- --no-check-certificate https://github.com/shadowsocksr/shadowsocksr/archive/$BRANCH.tar.gz | tar -xzf - -C $WORK
|
||||
|
||||
|
||||
WORKDIR ~/shadowsocks
|
||||
WORKDIR $WORK/shadowsocksr-$BRANCH/shadowsocks
|
||||
|
||||
|
||||
CMD python ~/shadowsocks/server.py -p $SERVER_PORT -k $PASSWORD -m $METHOD -O $PROTOCOL -o $OBFS
|
||||
EXPOSE $SERVER_PORT
|
||||
CMD python server.py -p $SERVER_PORT -k $PASSWORD -m $METHOD -O $PROTOCOL -o $OBFS -G $PROTOCOLPARAM
|
||||
|
||||
@@ -33,11 +33,11 @@ move to "~/shadowsocksr", then run:
|
||||
|
||||
move to "~/shadowsocksr/shadowsocks", then run:
|
||||
|
||||
python server.py -p 443 -k password -m aes-128-cfb -O auth_aes128_md5_compatible -o tls1.2_ticket_auth_compatible
|
||||
python server.py -p 443 -k password -m aes-128-cfb -O auth_aes128_md5 -o tls1.2_ticket_auth_compatible
|
||||
|
||||
Check all the options via `-h`.
|
||||
|
||||
You can also use a configuration file instead (recommand), move to "~/shadowsocksr" and edit the file "user-config.json", then move to "~/shadowsocksr/shadowsocks" again, just run:
|
||||
You can also use a configuration file instead (recommend), move to "~/shadowsocksr" and edit the file "user-config.json", then move to "~/shadowsocksr/shadowsocks" again, just run:
|
||||
|
||||
python server.py
|
||||
|
||||
@@ -57,7 +57,7 @@ To monitor the log:
|
||||
Client
|
||||
------
|
||||
|
||||
* [Windows] / [OS X]
|
||||
* [Windows] / [macOS]
|
||||
* [Android] / [iOS]
|
||||
* [OpenWRT]
|
||||
|
||||
@@ -99,7 +99,7 @@ Bugs and Issues
|
||||
[iOS]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help
|
||||
[Issue Tracker]: https://github.com/shadowsocksr/shadowsocksr/issues?state=open
|
||||
[OpenWRT]: https://github.com/shadowsocks/openwrt-shadowsocks
|
||||
[OS X]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Shadowsocks-for-OSX-Help
|
||||
[macOS]: https://github.com/shadowsocksr/ShadowsocksX-NG
|
||||
[Travis CI]: https://travis-ci.org/shadowsocksr/shadowsocksr
|
||||
[Windows]: https://github.com/shadowsocksr/shadowsocksr-csharp
|
||||
[Wiki]: https://github.com/breakwa11/shadowsocks-rss/wiki
|
||||
[Wiki]: https://github.com/breakwa11/shadowsocks-rss/wiki
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Config
|
||||
API_INTERFACE = 'sspanelv2' #mudbjson, sspanelv2, sspanelv3, sspanelv3ssr, muapiv2(not support)
|
||||
API_INTERFACE = 'sspanelv2' #mudbjson, sspanelv2, sspanelv3, sspanelv3ssr, glzjinmod, legendsockssr, muapiv2(not support)
|
||||
UPDATE_TIME = 60
|
||||
SERVER_PUB_ADDR = '127.0.0.1' # mujson_mgr need this to generate ssr link
|
||||
|
||||
|
||||
+9
-3
@@ -4,14 +4,20 @@
|
||||
"server_port": 8388,
|
||||
"local_address": "127.0.0.1",
|
||||
"local_port": 1080,
|
||||
|
||||
"password": "m",
|
||||
"timeout": 120,
|
||||
"udp_timeout": 60,
|
||||
"method": "aes-256-cfb",
|
||||
"method": "aes-128-ctr",
|
||||
"protocol": "auth_aes128_md5",
|
||||
"protocol_param": "",
|
||||
"obfs": "tls1.2_ticket_auth_compatible",
|
||||
"obfs_param": "",
|
||||
"speed_limit_per_con": 0,
|
||||
"speed_limit_per_user": 0,
|
||||
|
||||
"additional_ports" : {}, // only works under multi-user mode
|
||||
"additional_ports_only" : false, // only works under multi-user mode
|
||||
"timeout": 120,
|
||||
"udp_timeout": 60,
|
||||
"dns_ipv6": false,
|
||||
"connect_verbose_info": 0,
|
||||
"redirect": "",
|
||||
|
||||
+139
-76
@@ -6,7 +6,7 @@ import time
|
||||
import sys
|
||||
from server_pool import ServerPool
|
||||
import traceback
|
||||
from shadowsocks import common, shell, lru_cache
|
||||
from shadowsocks import common, shell, lru_cache, obfs
|
||||
from configloader import load_config, get_config
|
||||
import importloader
|
||||
|
||||
@@ -24,6 +24,7 @@ class TransferBase(object):
|
||||
self.port_uid_table = {} #端口到uid的映射(仅v3以上有用)
|
||||
self.onlineuser_cache = lru_cache.LRUCache(timeout=60*30) #用户在线状态记录
|
||||
self.pull_ok = False #记录是否已经拉出过数据
|
||||
self.mu_ports = {}
|
||||
|
||||
def load_cfg(self):
|
||||
pass
|
||||
@@ -41,7 +42,7 @@ class TransferBase(object):
|
||||
dt_transfer[id] = [self.last_get_transfer[id][0] - last_transfer[id][0], self.last_get_transfer[id][1] - last_transfer[id][1]]
|
||||
|
||||
for id in curr_transfer.keys():
|
||||
if id in self.force_update_transfer:
|
||||
if id in self.force_update_transfer or id in self.mu_ports:
|
||||
continue
|
||||
#算出与上次记录的流量差值,保存于dt_transfer表
|
||||
if id in last_transfer:
|
||||
@@ -85,6 +86,9 @@ class TransferBase(object):
|
||||
logging.error('load switchrule.py fail')
|
||||
cur_servers = {}
|
||||
new_servers = {}
|
||||
allow_users = {}
|
||||
mu_servers = {}
|
||||
config = shell.get_config(False)
|
||||
for row in rows:
|
||||
try:
|
||||
allow = switchrule.isTurnOn(row) and row['enable'] == 1 and row['u'] + row['d'] < row['transfer_enable']
|
||||
@@ -93,11 +97,13 @@ class TransferBase(object):
|
||||
|
||||
port = row['port']
|
||||
passwd = common.to_bytes(row['passwd'])
|
||||
if hasattr(passwd, 'encode'):
|
||||
passwd = passwd.encode('utf-8')
|
||||
cfg = {'password': passwd}
|
||||
if 'id' in row:
|
||||
self.port_uid_table[row['port']] = row['id']
|
||||
|
||||
read_config_keys = ['method', 'obfs', 'obfs_param', 'protocol', 'protocol_param', 'forbidden_ip', 'forbidden_port']
|
||||
read_config_keys = ['method', 'obfs', 'obfs_param', 'protocol', 'protocol_param', 'forbidden_ip', 'forbidden_port', 'speed_limit_per_con', 'speed_limit_per_user']
|
||||
for name in read_config_keys:
|
||||
if name in row and row[name]:
|
||||
cfg[name] = row[name]
|
||||
@@ -105,7 +111,10 @@ class TransferBase(object):
|
||||
merge_config_keys = ['password'] + read_config_keys
|
||||
for name in cfg.keys():
|
||||
if hasattr(cfg[name], 'encode'):
|
||||
cfg[name] = cfg[name].encode('utf-8')
|
||||
try:
|
||||
cfg[name] = cfg[name].encode('utf-8')
|
||||
except Exception as e:
|
||||
logging.warning('encode cfg key "%s" fail, val "%s"' % (name, cfg[name]))
|
||||
|
||||
if port not in cur_servers:
|
||||
cur_servers[port] = passwd
|
||||
@@ -113,34 +122,53 @@ class TransferBase(object):
|
||||
logging.error('more than one user use the same port [%s]' % (port,))
|
||||
continue
|
||||
|
||||
if ServerPool.get_instance().server_is_run(port) > 0:
|
||||
if not allow:
|
||||
logging.info('db stop server at port [%s]' % (port,))
|
||||
ServerPool.get_instance().cb_del_server(port)
|
||||
self.force_update_transfer.add(port)
|
||||
else:
|
||||
cfgchange = False
|
||||
if port in ServerPool.get_instance().tcp_servers_pool:
|
||||
relay = ServerPool.get_instance().tcp_servers_pool[port]
|
||||
for name in merge_config_keys:
|
||||
if name in cfg and not self.cmp(cfg[name], relay._config[name]):
|
||||
cfgchange = True
|
||||
break;
|
||||
if not cfgchange and port in ServerPool.get_instance().tcp_ipv6_servers_pool:
|
||||
relay = ServerPool.get_instance().tcp_ipv6_servers_pool[port]
|
||||
for name in merge_config_keys:
|
||||
if name in cfg and not self.cmp(cfg[name], relay._config[name]):
|
||||
cfgchange = True
|
||||
break;
|
||||
#config changed
|
||||
if 'protocol' in cfg and 'protocol_param' in cfg and common.to_str(cfg['protocol']) in obfs.mu_protocol():
|
||||
if '#' in common.to_str(cfg['protocol_param']):
|
||||
mu_servers[port] = passwd
|
||||
allow = True
|
||||
|
||||
if allow:
|
||||
if port not in mu_servers:
|
||||
allow_users[port] = cfg
|
||||
|
||||
cfgchange = False
|
||||
if port in ServerPool.get_instance().tcp_servers_pool:
|
||||
relay = ServerPool.get_instance().tcp_servers_pool[port]
|
||||
for name in merge_config_keys:
|
||||
if name in cfg and not self.cmp(cfg[name], relay._config[name]):
|
||||
cfgchange = True
|
||||
break
|
||||
if not cfgchange and port in ServerPool.get_instance().tcp_ipv6_servers_pool:
|
||||
relay = ServerPool.get_instance().tcp_ipv6_servers_pool[port]
|
||||
for name in merge_config_keys:
|
||||
if (name in cfg) and ((name not in relay._config) or not self.cmp(cfg[name], relay._config[name])):
|
||||
cfgchange = True
|
||||
break
|
||||
|
||||
if port in mu_servers:
|
||||
if ServerPool.get_instance().server_is_run(port) > 0:
|
||||
if cfgchange:
|
||||
logging.info('db stop server at port [%s] reason: config changed: %s' % (port, cfg))
|
||||
ServerPool.get_instance().cb_del_server(port)
|
||||
self.force_update_transfer.add(port)
|
||||
new_servers[port] = (passwd, cfg)
|
||||
else:
|
||||
self.new_server(port, passwd, cfg)
|
||||
else:
|
||||
if ServerPool.get_instance().server_is_run(port) > 0:
|
||||
if config['additional_ports_only'] or not allow:
|
||||
logging.info('db stop server at port [%s]' % (port,))
|
||||
ServerPool.get_instance().cb_del_server(port)
|
||||
self.force_update_transfer.add(port)
|
||||
else:
|
||||
if cfgchange:
|
||||
logging.info('db stop server at port [%s] reason: config changed: %s' % (port, cfg))
|
||||
ServerPool.get_instance().cb_del_server(port)
|
||||
self.force_update_transfer.add(port)
|
||||
new_servers[port] = (passwd, cfg)
|
||||
|
||||
elif allow and ServerPool.get_instance().server_run_status(port) is False:
|
||||
self.new_server(port, passwd, cfg)
|
||||
elif not config['additional_ports_only'] and allow and port > 0 and port < 65536 and ServerPool.get_instance().server_run_status(port) is False:
|
||||
self.new_server(port, passwd, cfg)
|
||||
|
||||
for row in last_rows:
|
||||
if row['port'] in cur_servers:
|
||||
@@ -159,6 +187,12 @@ class TransferBase(object):
|
||||
passwd, cfg = new_servers[port]
|
||||
self.new_server(port, passwd, cfg)
|
||||
|
||||
logging.debug('db allow users %s \nmu_servers %s' % (allow_users, mu_servers))
|
||||
for port in mu_servers:
|
||||
ServerPool.get_instance().update_mu_users(port, allow_users)
|
||||
|
||||
self.mu_ports = mu_servers
|
||||
|
||||
def clear_cache(self, port):
|
||||
if port in self.force_update_transfer: del self.force_update_transfer[port]
|
||||
if port in self.last_get_transfer: del self.last_get_transfer[port]
|
||||
@@ -198,8 +232,13 @@ class TransferBase(object):
|
||||
db_instance = obj()
|
||||
ServerPool.get_instance()
|
||||
shell.log_shadowsocks_version()
|
||||
import resource
|
||||
logging.info('current process RLIMIT_NOFILE resource: soft %d hard %d' % resource.getrlimit(resource.RLIMIT_NOFILE))
|
||||
|
||||
try:
|
||||
import resource
|
||||
logging.info('current process RLIMIT_NOFILE resource: soft %d hard %d' % resource.getrlimit(resource.RLIMIT_NOFILE))
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
while True:
|
||||
load_config()
|
||||
@@ -209,6 +248,17 @@ class TransferBase(object):
|
||||
rows = db_instance.pull_db_all_user()
|
||||
if rows:
|
||||
db_instance.pull_ok = True
|
||||
config = shell.get_config(False)
|
||||
for port in config['additional_ports']:
|
||||
val = config['additional_ports'][port]
|
||||
val['port'] = int(port)
|
||||
val['enable'] = 1
|
||||
val['transfer_enable'] = 1024 ** 7
|
||||
val['u'] = 0
|
||||
val['d'] = 0
|
||||
if "password" in val:
|
||||
val["passwd"] = val["password"]
|
||||
rows.append(val)
|
||||
db_instance.del_server_out_of_bound_safe(last_rows, rows)
|
||||
last_rows = rows
|
||||
except Exception as e:
|
||||
@@ -250,7 +300,7 @@ class DbTransfer(TransferBase):
|
||||
import json
|
||||
config_path = get_config().MYSQL_CONFIG
|
||||
cfg = None
|
||||
with open(config_path, 'r+') as f:
|
||||
with open(config_path, 'rb+') as f:
|
||||
cfg = json.loads(f.read().decode('utf8'))
|
||||
|
||||
if cfg:
|
||||
@@ -362,8 +412,18 @@ class DbTransfer(TransferBase):
|
||||
class Dbv3Transfer(DbTransfer):
|
||||
def __init__(self):
|
||||
super(Dbv3Transfer, self).__init__()
|
||||
self.key_list += ['id', 'method']
|
||||
if get_config().API_INTERFACE == 'sspanelv3ssr':
|
||||
self.update_node_state = True if get_config().API_INTERFACE != 'legendsockssr' else False
|
||||
if self.update_node_state:
|
||||
self.key_list += ['id']
|
||||
self.key_list += ['method']
|
||||
if self.update_node_state:
|
||||
self.ss_node_info_name = 'ss_node_info_log'
|
||||
if get_config().API_INTERFACE == 'sspanelv3ssr':
|
||||
self.key_list += ['obfs', 'protocol']
|
||||
if get_config().API_INTERFACE == 'glzjinmod':
|
||||
self.key_list += ['obfs', 'protocol']
|
||||
self.ss_node_info_name = 'ss_node_info'
|
||||
else:
|
||||
self.key_list += ['obfs', 'protocol']
|
||||
self.start_time = time.time()
|
||||
|
||||
@@ -406,16 +466,17 @@ class Dbv3Transfer(DbTransfer):
|
||||
query_sub_when2 += ' WHEN %s THEN d+%s' % (id, int(transfer[1] * self.cfg["transfer_mul"]))
|
||||
update_transfer[id] = transfer
|
||||
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
if id in self.port_uid_table:
|
||||
cur.execute("INSERT INTO `user_traffic_log` (`id`, `user_id`, `u`, `d`, `node_id`, `rate`, `traffic`, `log_time`) VALUES (NULL, '" + \
|
||||
str(self.port_uid_table[id]) + "', '" + str(transfer[0]) + "', '" + str(transfer[1]) + "', '" + \
|
||||
str(self.cfg["node_id"]) + "', '" + str(self.cfg["transfer_mul"]) + "', '" + \
|
||||
self.traffic_format((transfer[0] + transfer[1]) * self.cfg["transfer_mul"]) + "', unix_timestamp()); ")
|
||||
except:
|
||||
logging.warn('no `user_traffic_log` in db')
|
||||
cur.close()
|
||||
if self.update_node_state:
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
if id in self.port_uid_table:
|
||||
cur.execute("INSERT INTO `user_traffic_log` (`id`, `user_id`, `u`, `d`, `node_id`, `rate`, `traffic`, `log_time`) VALUES (NULL, '" + \
|
||||
str(self.port_uid_table[id]) + "', '" + str(transfer[0]) + "', '" + str(transfer[1]) + "', '" + \
|
||||
str(self.cfg["node_id"]) + "', '" + str(self.cfg["transfer_mul"]) + "', '" + \
|
||||
self.traffic_format((transfer[0] + transfer[1]) * self.cfg["transfer_mul"]) + "', unix_timestamp()); ")
|
||||
except:
|
||||
logging.warn('no `user_traffic_log` in db')
|
||||
cur.close()
|
||||
|
||||
if query_sub_in is not None:
|
||||
query_sub_in += ',%s' % id
|
||||
@@ -434,25 +495,26 @@ class Dbv3Transfer(DbTransfer):
|
||||
logging.error(e)
|
||||
cur.close()
|
||||
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
if self.update_node_state:
|
||||
try:
|
||||
cur.execute("INSERT INTO `ss_node_online_log` (`id`, `node_id`, `online_user`, `log_time`) VALUES (NULL, '" + \
|
||||
str(self.cfg["node_id"]) + "', '" + str(alive_user_count) + "', unix_timestamp()); ")
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
cur.close()
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
cur.execute("INSERT INTO `ss_node_online_log` (`id`, `node_id`, `online_user`, `log_time`) VALUES (NULL, '" + \
|
||||
str(self.cfg["node_id"]) + "', '" + str(alive_user_count) + "', unix_timestamp()); ")
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
cur.close()
|
||||
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
cur.execute("INSERT INTO `ss_node_info_log` (`id`, `node_id`, `uptime`, `load`, `log_time`) VALUES (NULL, '" + \
|
||||
str(self.cfg["node_id"]) + "', '" + str(self.uptime()) + "', '" + \
|
||||
str(self.load()) + "', unix_timestamp()); ")
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
cur.close()
|
||||
except:
|
||||
logging.warn('no `ss_node_online_log` or `ss_node_info_log` in db')
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
cur.execute("INSERT INTO `" + self.ss_node_info_name + "` (`id`, `node_id`, `uptime`, `load`, `log_time`) VALUES (NULL, '" + \
|
||||
str(self.cfg["node_id"]) + "', '" + str(self.uptime()) + "', '" + \
|
||||
str(self.load()) + "', unix_timestamp()); ")
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
cur.close()
|
||||
except:
|
||||
logging.warn('no `ss_node_online_log` or `" + self.ss_node_info_name + "` in db')
|
||||
|
||||
conn.close()
|
||||
return update_transfer
|
||||
@@ -466,31 +528,32 @@ class Dbv3Transfer(DbTransfer):
|
||||
|
||||
cur = conn.cursor()
|
||||
|
||||
node_info_keys = ['traffic_rate']
|
||||
try:
|
||||
cur.execute("SELECT " + ','.join(node_info_keys) +" FROM ss_node where `id`='" + str(self.cfg["node_id"]) + "'")
|
||||
nodeinfo = cur.fetchone()
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
nodeinfo = None
|
||||
if self.update_node_state:
|
||||
node_info_keys = ['traffic_rate']
|
||||
try:
|
||||
cur.execute("SELECT " + ','.join(node_info_keys) +" FROM ss_node where `id`='" + str(self.cfg["node_id"]) + "'")
|
||||
nodeinfo = cur.fetchone()
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
nodeinfo = None
|
||||
|
||||
if nodeinfo == None:
|
||||
rows = []
|
||||
if nodeinfo == None:
|
||||
rows = []
|
||||
cur.close()
|
||||
conn.commit()
|
||||
logging.warn('None result when select node info from ss_node in db, maybe you set the incorrect node id')
|
||||
return rows
|
||||
cur.close()
|
||||
conn.commit()
|
||||
logging.warn('None result when select node info from ss_node in db, maybe you set the incorrect node id')
|
||||
return rows
|
||||
cur.close()
|
||||
|
||||
node_info_dict = {}
|
||||
for column in range(len(nodeinfo)):
|
||||
node_info_dict[node_info_keys[column]] = nodeinfo[column]
|
||||
self.cfg['transfer_mul'] = float(node_info_dict['traffic_rate'])
|
||||
node_info_dict = {}
|
||||
for column in range(len(nodeinfo)):
|
||||
node_info_dict[node_info_keys[column]] = nodeinfo[column]
|
||||
self.cfg['transfer_mul'] = float(node_info_dict['traffic_rate'])
|
||||
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
cur.execute("SELECT " + ','.join(keys) + " FROM user")
|
||||
rows = []
|
||||
cur.execute("SELECT " + ','.join(keys) + " FROM user")
|
||||
for r in cur.fetchall():
|
||||
d = {}
|
||||
for column in range(len(keys)):
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
If Not Exist "userapiconfig.py" Copy "apiconfig.py" "userapiconfig.py"
|
||||
If Not Exist "user-config.json" Copy "config.json" "user-config.json"
|
||||
If Not Exist "usermysql.json" Copy "mysql.json" "usermysql.json"
|
||||
Regular → Executable
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
bash initcfg.sh
|
||||
sed -i "s/API_INTERFACE = .\+\?\#/API_INTERFACE = \'mudbjson\' \#/g" userapiconfig.py
|
||||
ip_addr=`ifconfig -a|grep inet|grep -v inet6|grep -v "127.0.0."|grep -v -e "192\.168\..[0-9]\+\.[0-9]\+"|grep -v -e "10\.[0-9]\+\.[0-9]\+\.[0-9]\+"|awk '{print $2}'|tr -d "addr:"`
|
||||
ip_count=`echo $ip_addr|grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$" -c`
|
||||
|
||||
if [[ $ip_count == 1 ]]; then
|
||||
ip_addr=`ip a|grep inet|grep -v inet6|grep -v "127.0.0."|grep -v -e "192\.168\..[0-9]\+\.[0-9]\+"|grep -v -e "10\.[0-9]\+\.[0-9]\+\.[0-9]\+"|awk '{print $2}'`
|
||||
ip_addr=${ip_addr%/*}
|
||||
ip_count=`echo $ip_addr|grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$" -c`
|
||||
fi
|
||||
if [[ $ip_count == 1 ]]; then
|
||||
echo "server IP is "${ip_addr}
|
||||
sed -i "s/SERVER_PUB_ADDR = .\+/SERVER_PUB_ADDR = \'"${ip_addr}"\'/g" userapiconfig.py
|
||||
user_count=`python mujson_mgr.py -l|grep -c -e "[0-9]"`
|
||||
if [[ $user_count == 0 ]]; then
|
||||
port=`python -c 'import random;print(random.randint(10000, 65536))'`
|
||||
python mujson_mgr.py -a -p ${port}
|
||||
fi
|
||||
else
|
||||
echo "unable to detect server IP"
|
||||
fi
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
cd `dirname $0`
|
||||
eval $(ps -ef | grep "[0-9] python server\\.py m" | awk '{print "kill "$2}')
|
||||
python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
|
||||
eval $(ps -ef | grep "[0-9] ${python_ver} server\\.py m" | awk '{print "kill "$2}')
|
||||
ulimit -n 512000
|
||||
nohup python server.py m>> ssserver.log 2>&1 &
|
||||
nohup ${python_ver} server.py m>> ssserver.log 2>&1 &
|
||||
|
||||
|
||||
+102
-60
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/python
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
import traceback
|
||||
@@ -10,21 +10,30 @@ import sys
|
||||
import json
|
||||
import base64
|
||||
|
||||
|
||||
class MuJsonLoader(object):
|
||||
def __init__(self):
|
||||
self.json = None
|
||||
|
||||
def load(self, path):
|
||||
with open(path, 'rb+') as f:
|
||||
self.json = json.loads(f.read().decode('utf8'))
|
||||
l = "[]"
|
||||
try:
|
||||
with open(path, 'rb+') as f:
|
||||
l = f.read().decode('utf8')
|
||||
except:
|
||||
pass
|
||||
self.json = json.loads(l)
|
||||
|
||||
def save(self, path):
|
||||
if self.json:
|
||||
if self.json is not None:
|
||||
output = json.dumps(self.json, sort_keys=True, indent=4, separators=(',', ': '))
|
||||
with open(path, 'r+') as f:
|
||||
f.write(output)
|
||||
with open(path, 'a'):
|
||||
pass
|
||||
with open(path, 'rb+') as f:
|
||||
f.write(output.encode('utf8'))
|
||||
f.truncate()
|
||||
|
||||
|
||||
class MuMgr(object):
|
||||
def __init__(self):
|
||||
self.config_path = get_config().MUDB_FILE
|
||||
@@ -37,10 +46,9 @@ class MuMgr(object):
|
||||
if self.server_addr == '127.0.0.1':
|
||||
self.server_addr = self.getipaddr()
|
||||
|
||||
def getipaddr(self, ifname = 'eth0'):
|
||||
def getipaddr(self, ifname='eth0'):
|
||||
import socket
|
||||
import struct
|
||||
import fcntl
|
||||
ret = '127.0.0.1'
|
||||
try:
|
||||
ret = socket.gethostbyname(socket.getfqdn(socket.gethostname()))
|
||||
@@ -48,53 +56,79 @@ class MuMgr(object):
|
||||
pass
|
||||
if ret == '127.0.0.1':
|
||||
try:
|
||||
import fcntl
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
ret = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
|
||||
except:
|
||||
pass
|
||||
return ret
|
||||
|
||||
def ssrlink(self, user, encode):
|
||||
def ssrlink(self, user, encode, muid):
|
||||
protocol = user.get('protocol', '')
|
||||
obfs = user.get('obfs', '')
|
||||
protocol = protocol.replace("_compatible", "")
|
||||
obfs = obfs.replace("_compatible", "")
|
||||
link = "%s:%s:%s:%s:%s:%s" % (self.server_addr, user['port'], protocol, user['method'], obfs, common.to_str(base64.urlsafe_b64encode(common.to_bytes(user['passwd']))).replace("=", ""))
|
||||
return "ssr://" + ( encode and common.to_str(base64.urlsafe_b64encode(common.to_bytes(link))).replace("=", "") or link)
|
||||
protocol_param = ''
|
||||
if muid is not None:
|
||||
protocol_param_ = user.get('protocol_param', '')
|
||||
param = protocol_param_.split('#')
|
||||
if len(param) == 2:
|
||||
for row in self.data.json:
|
||||
if int(row['port']) == muid:
|
||||
param = str(muid) + ':' + row['passwd']
|
||||
protocol_param = '/?protoparam=' + common.to_str(base64.urlsafe_b64encode(common.to_bytes(param))).replace("=", "")
|
||||
break
|
||||
link = ("%s:%s:%s:%s:%s:%s" % (self.server_addr, user['port'], protocol, user['method'], obfs, common.to_str(base64.urlsafe_b64encode(common.to_bytes(user['passwd']))).replace("=", ""))) + protocol_param
|
||||
return "ssr://" + (encode and common.to_str(base64.urlsafe_b64encode(common.to_bytes(link))).replace("=", "") or link)
|
||||
|
||||
def userinfo(self, user):
|
||||
def userinfo(self, user, muid = None):
|
||||
ret = ""
|
||||
key_list = ['user', 'port', 'method', 'passwd', 'protocol', 'protocol_param', 'obfs', 'obfs_param', 'transfer_enable', 'u', 'd']
|
||||
for key in sorted(user):
|
||||
if key in ['enable']:
|
||||
if key not in key_list:
|
||||
key_list.append(key)
|
||||
for key in key_list:
|
||||
if key in ['enable'] or key not in user:
|
||||
continue
|
||||
ret += '\n'
|
||||
if key in ['transfer_enable', 'u', 'd'] :
|
||||
val = user[key]
|
||||
if (muid is not None) and (key in ['protocol_param']):
|
||||
for row in self.data.json:
|
||||
if int(row['port']) == muid:
|
||||
ret += " %s : %s" % (key, str(muid) + ':' + row['passwd'])
|
||||
break
|
||||
elif key in ['transfer_enable', 'u', 'd']:
|
||||
if muid is not None:
|
||||
for row in self.data.json:
|
||||
if int(row['port']) == muid:
|
||||
val = row[key]
|
||||
break
|
||||
else:
|
||||
val = user[key]
|
||||
if val / 1024 < 4:
|
||||
ret += " %s : %s" % (key, val)
|
||||
elif val / 1024**2 < 4:
|
||||
elif val / 1024 ** 2 < 4:
|
||||
val /= float(1024)
|
||||
ret += " %s : %s K Bytes" % (key, val)
|
||||
elif val / 1024**3 < 4:
|
||||
val /= float(1024**2)
|
||||
elif val / 1024 ** 3 < 4:
|
||||
val /= float(1024 ** 2)
|
||||
ret += " %s : %s M Bytes" % (key, val)
|
||||
else:
|
||||
val /= float(1024**3)
|
||||
val /= float(1024 ** 3)
|
||||
ret += " %s : %s G Bytes" % (key, val)
|
||||
else:
|
||||
ret += " %s : %s" % (key, user[key])
|
||||
ret += "\n " + self.ssrlink(user, False)
|
||||
ret += "\n " + self.ssrlink(user, True)
|
||||
ret += "\n " + self.ssrlink(user, False, muid)
|
||||
ret += "\n " + self.ssrlink(user, True, muid)
|
||||
return ret
|
||||
|
||||
def rand_pass(self):
|
||||
return ''.join([random.choice('''ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~-_=+(){}[]^&%$@''') for i in range(8)])
|
||||
|
||||
def add(self, user):
|
||||
up = {'enable': 1, 'u': 0, 'd': 0, 'method': "aes-128-cfb",
|
||||
'protocol': "auth_sha1_v4",
|
||||
up = {'enable': 1, 'u': 0, 'd': 0, 'method': "aes-128-ctr",
|
||||
'protocol': "auth_aes128_md5",
|
||||
'obfs': "tls1.2_ticket_auth_compatible",
|
||||
'transfer_enable': 1125899906842624}
|
||||
'transfer_enable': 9007199254740992}
|
||||
up['passwd'] = self.rand_pass()
|
||||
up.update(user)
|
||||
|
||||
@@ -170,36 +204,44 @@ class MuMgr(object):
|
||||
if 'port' in user and row['port'] != user['port']:
|
||||
match = False
|
||||
if match:
|
||||
print("### user [%s] info %s" % (row['user'], self.userinfo(row)))
|
||||
muid = None
|
||||
if 'muid' in user:
|
||||
muid = user['muid']
|
||||
print("### user [%s] info %s" % (row['user'], self.userinfo(row, muid)))
|
||||
|
||||
|
||||
def print_server_help():
|
||||
print('''usage: python mujson_manage.py -a|-d|-e|-c|-l [OPTION]...
|
||||
print('''usage: python mujson_manage.py -a|-d|-e|-c|-l [OPTION]...
|
||||
|
||||
Actions:
|
||||
-a ADD add/edit a user
|
||||
-d DELETE delete a user
|
||||
-e EDIT edit a user
|
||||
-c CLEAR set u/d to zero
|
||||
-l LIST display a user infomation or all users infomation
|
||||
-a add/edit a user
|
||||
-d delete a user
|
||||
-e edit a user
|
||||
-c set u&d to zero
|
||||
-l display a user infomation or all users infomation
|
||||
|
||||
Options:
|
||||
-u USER the user name
|
||||
-p PORT server port
|
||||
-k PASSWORD password
|
||||
-m METHOD encryption method, default: aes-128-cfb
|
||||
-O PROTOCOL protocol plugin, default: auth_sha1_v4
|
||||
-o OBFS obfs plugin, default: tls1.2_ticket_auth_compatible
|
||||
-G PROTOCOL_PARAM protocol plugin param
|
||||
-g OBFS_PARAM obfs plugin param
|
||||
-t TRANSFER max transfer for G bytes, default: 1048576, can be float point number
|
||||
-f FORBID set forbidden ports. Example (ban 1~79 and 81~100): -f "1-79,81-100"
|
||||
-u USER the user name
|
||||
-p PORT server port (only this option must be set if add a user)
|
||||
-k PASSWORD password
|
||||
-m METHOD encryption method, default: aes-128-ctr
|
||||
-O PROTOCOL protocol plugin, default: auth_aes128_md5
|
||||
-o OBFS obfs plugin, default: tls1.2_ticket_auth_compatible
|
||||
-G PROTOCOL_PARAM protocol plugin param
|
||||
-g OBFS_PARAM obfs plugin param
|
||||
-t TRANSFER max transfer for G bytes, default: 8388608 (8 PB or 8192 TB)
|
||||
-f FORBID set forbidden ports. Example (ban 1~79 and 81~100): -f "1-79,81-100"
|
||||
-i MUID set sub id to display (only work with -l)
|
||||
-s SPEED set speed_limit_per_con
|
||||
-S SPEED set speed_limit_per_user
|
||||
|
||||
General options:
|
||||
-h, --help show this help message and exit
|
||||
-h, --help show this help message and exit
|
||||
''')
|
||||
|
||||
|
||||
def main():
|
||||
shortopts = 'adeclu:p:k:O:o:G:g:m:t:f:h'
|
||||
shortopts = 'adeclu:i:p:k:O:o:G:g:m:t:f:hs:S:'
|
||||
longopts = ['help']
|
||||
action = None
|
||||
user = {}
|
||||
@@ -209,29 +251,24 @@ def main():
|
||||
'+2': 'tls1.2_ticket_auth_compatible',
|
||||
'2': 'tls1.2_ticket_auth'}
|
||||
fast_set_protocol = {'0': 'origin',
|
||||
'+1': 'verify_sha1_compatible',
|
||||
'1': 'verify_sha1',
|
||||
'2': 'auth_sha1',
|
||||
'3': 'auth_sha1_v2',
|
||||
'4': 'auth_sha1_v4',
|
||||
's4': 'auth_sha1_v4',
|
||||
'+s4': 'auth_sha1_v4_compatible',
|
||||
'am': 'auth_aes128_md5',
|
||||
'as': 'auth_aes128_sha1',
|
||||
'ca': 'auth_chain_a',
|
||||
}
|
||||
fast_set_method = {'a0': 'aes-128-cfb',
|
||||
'a1': 'aes-192-cfb',
|
||||
'a2': 'aes-256-cfb',
|
||||
fast_set_method = {'0': 'none',
|
||||
'a1c': 'aes-128-cfb',
|
||||
'a2c': 'aes-192-cfb',
|
||||
'a3c': 'aes-256-cfb',
|
||||
'r': 'rc4-md5',
|
||||
'r6': 'rc4-md5-6',
|
||||
'c': 'chacha20',
|
||||
'ci': 'chacha20-ietf',
|
||||
's': 'salsa20',
|
||||
'b': 'bf-cfb',
|
||||
'm0': 'camellia-128-cfb',
|
||||
'm1': 'camellia-192-cfb',
|
||||
'm2': 'camellia-256-cfb',
|
||||
'a0t': 'aes-128-ctr',
|
||||
'a1t': 'aes-192-ctr',
|
||||
'a2t': 'aes-256-ctr'}
|
||||
'a1': 'aes-128-ctr',
|
||||
'a2': 'aes-192-ctr',
|
||||
'a3': 'aes-256-ctr'}
|
||||
try:
|
||||
optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
|
||||
for key, value in optlist:
|
||||
@@ -247,6 +284,8 @@ def main():
|
||||
action = 0
|
||||
elif key == '-u':
|
||||
user['user'] = value
|
||||
elif key == '-i':
|
||||
user['muid'] = int(value)
|
||||
elif key == '-p':
|
||||
user['port'] = int(value)
|
||||
elif key == '-k':
|
||||
@@ -265,6 +304,10 @@ def main():
|
||||
user['obfs_param'] = value
|
||||
elif key == '-G':
|
||||
user['protocol_param'] = value
|
||||
elif key == '-s':
|
||||
user['speed_limit_per_con'] = int(value)
|
||||
elif key == '-S':
|
||||
user['speed_limit_per_user'] = int(value)
|
||||
elif key == '-m':
|
||||
if value in fast_set_method:
|
||||
user['method'] = fast_set_method[value]
|
||||
@@ -278,7 +321,7 @@ def main():
|
||||
val = int(value)
|
||||
except:
|
||||
pass
|
||||
user['transfer_enable'] = val * (1024 ** 3)
|
||||
user['transfer_enable'] = int(val * 1024) * (1024 ** 2)
|
||||
elif key in ('-h', '--help'):
|
||||
print_server_help()
|
||||
sys.exit(0)
|
||||
@@ -313,4 +356,3 @@ def main():
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
cd `dirname $0`
|
||||
eval $(ps -ef | grep "[0-9] python server\\.py m" | awk '{print "kill "$2}')
|
||||
python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
|
||||
eval $(ps -ef | grep "[0-9] ${python_ver} server\\.py m" | awk '{print "kill "$2}')
|
||||
ulimit -n 512000
|
||||
nohup python server.py m>> /dev/null 2>&1 &
|
||||
nohup ${python_ver} server.py m>> /dev/null 2>&1 &
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ from configloader import load_config, get_config
|
||||
|
||||
class MainThread(threading.Thread):
|
||||
def __init__(self, obj):
|
||||
threading.Thread.__init__(self)
|
||||
super(MainThread, self).__init__()
|
||||
self.daemon = True
|
||||
self.obj = obj
|
||||
|
||||
def run(self):
|
||||
@@ -54,7 +55,7 @@ def main():
|
||||
thread.start()
|
||||
try:
|
||||
while thread.is_alive():
|
||||
time.sleep(10)
|
||||
thread.join(10.0)
|
||||
except (KeyboardInterrupt, IOError, OSError) as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
+62
-11
@@ -23,16 +23,18 @@
|
||||
|
||||
import os
|
||||
import logging
|
||||
import struct
|
||||
import time
|
||||
from shadowsocks import shell, eventloop, tcprelay, udprelay, asyncdns, common
|
||||
import threading
|
||||
import sys
|
||||
import traceback
|
||||
from socket import *
|
||||
from configloader import load_config, get_config
|
||||
|
||||
class MainThread(threading.Thread):
|
||||
def __init__(self, params):
|
||||
threading.Thread.__init__(self)
|
||||
super(MainThread, self).__init__()
|
||||
self.params = params
|
||||
|
||||
def run(self):
|
||||
@@ -79,12 +81,10 @@ class ServerPool(object):
|
||||
loop.run()
|
||||
except (KeyboardInterrupt, IOError, OSError) as e:
|
||||
logging.error(e)
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
os.exit(0)
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def server_is_run(self, port):
|
||||
@@ -213,23 +213,62 @@ class ServerPool(object):
|
||||
|
||||
return True
|
||||
|
||||
def update_mu_users(self, port, users):
|
||||
port = int(port)
|
||||
if port in self.tcp_servers_pool:
|
||||
try:
|
||||
self.tcp_servers_pool[port].update_users(users)
|
||||
except Exception as e:
|
||||
logging.warn(e)
|
||||
try:
|
||||
self.udp_servers_pool[port].update_users(users)
|
||||
except Exception as e:
|
||||
logging.warn(e)
|
||||
if port in self.tcp_ipv6_servers_pool:
|
||||
try:
|
||||
self.tcp_ipv6_servers_pool[port].update_users(users)
|
||||
except Exception as e:
|
||||
logging.warn(e)
|
||||
try:
|
||||
self.udp_ipv6_servers_pool[port].update_users(users)
|
||||
except Exception as e:
|
||||
logging.warn(e)
|
||||
|
||||
def get_server_transfer(self, port):
|
||||
port = int(port)
|
||||
uid = struct.pack('<I', port)
|
||||
ret = [0, 0]
|
||||
if port in self.tcp_servers_pool:
|
||||
ret[0] = self.tcp_servers_pool[port].server_transfer_ul
|
||||
ret[1] = self.tcp_servers_pool[port].server_transfer_dl
|
||||
ret[0], ret[1] = self.tcp_servers_pool[port].get_ud()
|
||||
if port in self.udp_servers_pool:
|
||||
ret[0] += self.udp_servers_pool[port].server_transfer_ul
|
||||
ret[1] += self.udp_servers_pool[port].server_transfer_dl
|
||||
u, d = self.udp_servers_pool[port].get_ud()
|
||||
ret[0] += u
|
||||
ret[1] += d
|
||||
if port in self.tcp_ipv6_servers_pool:
|
||||
ret[0] += self.tcp_ipv6_servers_pool[port].server_transfer_ul
|
||||
ret[1] += self.tcp_ipv6_servers_pool[port].server_transfer_dl
|
||||
u, d = self.tcp_ipv6_servers_pool[port].get_ud()
|
||||
ret[0] += u
|
||||
ret[1] += d
|
||||
if port in self.udp_ipv6_servers_pool:
|
||||
ret[0] += self.udp_ipv6_servers_pool[port].server_transfer_ul
|
||||
ret[1] += self.udp_ipv6_servers_pool[port].server_transfer_dl
|
||||
u, d = self.udp_ipv6_servers_pool[port].get_ud()
|
||||
ret[0] += u
|
||||
ret[1] += d
|
||||
return ret
|
||||
|
||||
def get_server_mu_transfer(self, server):
|
||||
return server.get_users_ud()
|
||||
|
||||
def update_mu_transfer(self, user_dict, u, d):
|
||||
for uid in u:
|
||||
port = struct.unpack('<I', uid)[0]
|
||||
if port not in user_dict:
|
||||
user_dict[port] = [0, 0]
|
||||
user_dict[port][0] += u[uid]
|
||||
for uid in d:
|
||||
port = struct.unpack('<I', uid)[0]
|
||||
if port not in user_dict:
|
||||
user_dict[port] = [0, 0]
|
||||
user_dict[port][1] += d[uid]
|
||||
|
||||
def get_servers_transfer(self):
|
||||
servers = self.tcp_servers_pool.copy()
|
||||
servers.update(self.tcp_ipv6_servers_pool)
|
||||
@@ -238,5 +277,17 @@ class ServerPool(object):
|
||||
ret = {}
|
||||
for port in servers.keys():
|
||||
ret[port] = self.get_server_transfer(port)
|
||||
for port in self.tcp_servers_pool:
|
||||
u, d = self.get_server_mu_transfer(self.tcp_servers_pool[port])
|
||||
self.update_mu_transfer(ret, u, d)
|
||||
for port in self.tcp_ipv6_servers_pool:
|
||||
u, d = self.get_server_mu_transfer(self.tcp_ipv6_servers_pool[port])
|
||||
self.update_mu_transfer(ret, u, d)
|
||||
for port in self.udp_servers_pool:
|
||||
u, d = self.get_server_mu_transfer(self.udp_servers_pool[port])
|
||||
self.update_mu_transfer(ret, u, d)
|
||||
for port in self.udp_ipv6_servers_pool:
|
||||
u, d = self.get_server_mu_transfer(self.udp_ipv6_servers_pool[port])
|
||||
self.update_mu_transfer(ret, u, d)
|
||||
return ret
|
||||
|
||||
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
rm -rf CyMySQL
|
||||
rm -rf cymysql
|
||||
git clone https://github.com/nakagami/CyMySQL.git
|
||||
mv CyMySQL/cymysql ./
|
||||
rm -rf CyMySQL
|
||||
@@ -331,6 +331,8 @@ class DNSResolver(object):
|
||||
with open(etc_path, 'rb') as f:
|
||||
for line in f.readlines():
|
||||
line = line.strip()
|
||||
if b"#" in line:
|
||||
line = line[:line.find(b'#')]
|
||||
parts = line.split()
|
||||
if len(parts) >= 2:
|
||||
ip = parts[0]
|
||||
@@ -362,7 +364,7 @@ class DNSResolver(object):
|
||||
callback((hostname, ip), error)
|
||||
else:
|
||||
callback((hostname, None),
|
||||
Exception('unknown hostname %s' % hostname))
|
||||
Exception('unable to parse hostname %s' % hostname))
|
||||
if hostname in self._hostname_to_cb:
|
||||
del self._hostname_to_cb[hostname]
|
||||
if hostname in self._hostname_status:
|
||||
|
||||
+43
-1
@@ -24,6 +24,8 @@ import logging
|
||||
import binascii
|
||||
import re
|
||||
|
||||
from shadowsocks import lru_cache
|
||||
|
||||
def compat_ord(s):
|
||||
if type(s) == int:
|
||||
return s
|
||||
@@ -236,7 +238,7 @@ def parse_header(data):
|
||||
'encryption method' % addrtype)
|
||||
if dest_addr is None:
|
||||
return None
|
||||
return connecttype, to_bytes(dest_addr), dest_port, header_length
|
||||
return connecttype, addrtype, to_bytes(dest_addr), dest_port, header_length
|
||||
|
||||
|
||||
class IPNetwork(object):
|
||||
@@ -297,6 +299,12 @@ class IPNetwork(object):
|
||||
def __cmp__(self, other):
|
||||
return cmp(self.addrs_str, other.addrs_str)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.addrs_str == other.addrs_str
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.addrs_str != other.addrs_str
|
||||
|
||||
class PortRange(object):
|
||||
def __init__(self, range_str):
|
||||
self.range_str = to_str(range_str)
|
||||
@@ -328,6 +336,40 @@ class PortRange(object):
|
||||
def __cmp__(self, other):
|
||||
return cmp(self.range_str, other.range_str)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.range_str == other.range_str
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.range_str != other.range_str
|
||||
|
||||
class UDPAsyncDNSHandler(object):
|
||||
dns_cache = lru_cache.LRUCache(timeout=1800)
|
||||
def __init__(self, params):
|
||||
self.params = params
|
||||
self.remote_addr = None
|
||||
self.call_back = None
|
||||
|
||||
def resolve(self, dns_resolver, remote_addr, call_back):
|
||||
if remote_addr in UDPAsyncDNSHandler.dns_cache:
|
||||
if call_back:
|
||||
call_back("", remote_addr, UDPAsyncDNSHandler.dns_cache[remote_addr], self.params)
|
||||
else:
|
||||
self.call_back = call_back
|
||||
self.remote_addr = remote_addr
|
||||
dns_resolver.resolve(remote_addr[0], self._handle_dns_resolved)
|
||||
UDPAsyncDNSHandler.dns_cache.sweep()
|
||||
|
||||
def _handle_dns_resolved(self, result, error):
|
||||
if error:
|
||||
logging.error("%s when resolve DNS" % (error,)) #drop
|
||||
return self.call_back(error, self.remote_addr, None, self.params)
|
||||
if result:
|
||||
ip = result[1]
|
||||
if ip:
|
||||
return self.call_back("", self.remote_addr, ip, self.params)
|
||||
logging.warning("can't resolve %s" % (self.remote_addr,))
|
||||
return self.call_back("fail to resolve", self.remote_addr, None, self.params)
|
||||
|
||||
def test_inet_conv():
|
||||
ipv4 = b'8.8.4.4'
|
||||
b = inet_pton(socket.AF_INET, ipv4)
|
||||
|
||||
@@ -67,8 +67,6 @@ def load_openssl():
|
||||
|
||||
def load_cipher(cipher_name):
|
||||
func_name = 'EVP_' + cipher_name.replace('-', '_')
|
||||
if bytes != str:
|
||||
func_name = str(func_name, 'utf-8')
|
||||
cipher = getattr(libcrypto, func_name, None)
|
||||
if cipher:
|
||||
cipher.restype = c_void_p
|
||||
@@ -89,8 +87,7 @@ class OpenSSLCrypto(object):
|
||||
self._ctx = None
|
||||
if not loaded:
|
||||
load_openssl()
|
||||
cipher_name = common.to_bytes(cipher_name)
|
||||
cipher = libcrypto.EVP_get_cipherbyname(cipher_name)
|
||||
cipher = libcrypto.EVP_get_cipherbyname(common.to_bytes(cipher_name))
|
||||
if not cipher:
|
||||
cipher = load_cipher(cipher_name)
|
||||
if not cipher:
|
||||
|
||||
@@ -74,7 +74,7 @@ class NoneCipher(object):
|
||||
|
||||
ciphers = {
|
||||
'none': (16, 0, NoneCipher),
|
||||
'table': (0, 0, TableCipher)
|
||||
'table': (16, 0, TableCipher)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ EVENT_NAMES = {
|
||||
}
|
||||
|
||||
# we check timeouts every TIMEOUT_PRECISION seconds
|
||||
TIMEOUT_PRECISION = 5
|
||||
TIMEOUT_PRECISION = 2
|
||||
|
||||
|
||||
class KqueueLoop(object):
|
||||
@@ -176,6 +176,10 @@ class EventLoop(object):
|
||||
del self._fdmap[fd]
|
||||
self._impl.unregister(fd)
|
||||
|
||||
def removefd(self, fd):
|
||||
del self._fdmap[fd]
|
||||
self._impl.unregister(fd)
|
||||
|
||||
def add_periodic(self, callback):
|
||||
self._periodic_callbacks.append(callback)
|
||||
|
||||
@@ -208,12 +212,13 @@ class EventLoop(object):
|
||||
traceback.print_exc()
|
||||
continue
|
||||
|
||||
handle = False
|
||||
for sock, fd, event in events:
|
||||
handler = self._fdmap.get(fd, None)
|
||||
if handler is not None:
|
||||
handler = handler[1]
|
||||
try:
|
||||
handler.handle_event(sock, fd, event)
|
||||
handle = handler.handle_event(sock, fd, event) or handle
|
||||
except (OSError, IOError) as e:
|
||||
shell.print_exception(e)
|
||||
now = time.time()
|
||||
@@ -221,6 +226,8 @@ class EventLoop(object):
|
||||
for callback in self._periodic_callbacks:
|
||||
callback()
|
||||
self._last_time = now
|
||||
if events and not handle:
|
||||
time.sleep(0.001)
|
||||
|
||||
def __del__(self):
|
||||
self._impl.close()
|
||||
|
||||
Regular → Executable
+5
-3
@@ -1,6 +1,8 @@
|
||||
#!/bin/bash
|
||||
cd `dirname $0`
|
||||
eval $(ps -ef | grep "[0-9] python server\\.py a" | awk '{print "kill "$2}')
|
||||
ulimit -n 4096
|
||||
nohup python server.py a >> ssserver.log 2>&1 &
|
||||
python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
|
||||
eval $(ps -ef | grep "[0-9] ${python_ver} server\\.py a" | awk '{print "kill "$2}')
|
||||
ulimit -n 512000
|
||||
nohup ${python_ver} server.py a>> ssserver.log 2>&1 &
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ if __name__ == '__main__':
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
print("loaded collections.OrderedDict")
|
||||
except:
|
||||
from shadowsocks.ordereddict import OrderedDict
|
||||
|
||||
@@ -88,11 +87,11 @@ class LRUCache(collections.MutableMapping):
|
||||
for key in self._keys_to_last_time:
|
||||
return key
|
||||
|
||||
def sweep(self):
|
||||
def sweep(self, sweep_item_cnt = SWEEP_MAX_ITEMS):
|
||||
# O(n - m)
|
||||
now = time.time()
|
||||
c = 0
|
||||
while c < SWEEP_MAX_ITEMS:
|
||||
while c < sweep_item_cnt:
|
||||
if len(self._keys_to_last_time) == 0:
|
||||
break
|
||||
for key in self._keys_to_last_time:
|
||||
|
||||
+13
-3
@@ -23,7 +23,7 @@ import hashlib
|
||||
import logging
|
||||
|
||||
from shadowsocks import common
|
||||
from shadowsocks.obfsplugin import plain, http_simple, obfs_tls, verify, auth
|
||||
from shadowsocks.obfsplugin import plain, http_simple, obfs_tls, verify, auth, auth_chain
|
||||
|
||||
|
||||
method_supported = {}
|
||||
@@ -32,6 +32,10 @@ method_supported.update(http_simple.obfs_map)
|
||||
method_supported.update(obfs_tls.obfs_map)
|
||||
method_supported.update(verify.obfs_map)
|
||||
method_supported.update(auth.obfs_map)
|
||||
method_supported.update(auth_chain.obfs_map)
|
||||
|
||||
def mu_protocol():
|
||||
return ["auth_aes128_md5", "auth_aes128_sha1", "auth_chain_a"]
|
||||
|
||||
class server_info(object):
|
||||
def __init__(self, data):
|
||||
@@ -53,6 +57,9 @@ class obfs(object):
|
||||
def set_server_info(self, server_info):
|
||||
return self.obfs.set_server_info(server_info)
|
||||
|
||||
def get_server_info(self):
|
||||
return self.obfs.get_server_info()
|
||||
|
||||
def get_method_info(self, method):
|
||||
method = method.lower()
|
||||
m = method_supported.get(method)
|
||||
@@ -62,6 +69,9 @@ class obfs(object):
|
||||
m = self._method_info
|
||||
return m[0](method)
|
||||
|
||||
def get_overhead(self, direction):
|
||||
return self.obfs.get_overhead(direction)
|
||||
|
||||
def client_pre_encrypt(self, buf):
|
||||
return self.obfs.client_pre_encrypt(buf)
|
||||
|
||||
@@ -92,8 +102,8 @@ class obfs(object):
|
||||
def client_udp_post_decrypt(self, buf):
|
||||
return self.obfs.client_udp_post_decrypt(buf)
|
||||
|
||||
def server_udp_pre_encrypt(self, buf):
|
||||
return self.obfs.server_udp_pre_encrypt(buf)
|
||||
def server_udp_pre_encrypt(self, buf, uid):
|
||||
return self.obfs.server_udp_pre_encrypt(buf, uid)
|
||||
|
||||
def server_udp_post_decrypt(self, buf):
|
||||
return self.obfs.server_udp_post_decrypt(buf)
|
||||
|
||||
Regular → Executable
+144
-741
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,692 @@
|
||||
#!/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 time
|
||||
import datetime
|
||||
import random
|
||||
import math
|
||||
import struct
|
||||
import zlib
|
||||
import hmac
|
||||
import hashlib
|
||||
import bisect
|
||||
|
||||
import shadowsocks
|
||||
from shadowsocks import common, lru_cache, encrypt
|
||||
from shadowsocks.obfsplugin import plain
|
||||
from shadowsocks.common import to_bytes, to_str, ord, chr
|
||||
|
||||
def create_auth_chain_a(method):
|
||||
return auth_chain_a(method)
|
||||
|
||||
def create_auth_chain_b(method):
|
||||
return auth_chain_b(method)
|
||||
|
||||
obfs_map = {
|
||||
'auth_chain_a': (create_auth_chain_a,),
|
||||
'auth_chain_b': (create_auth_chain_b,),
|
||||
}
|
||||
|
||||
class xorshift128plus(object):
|
||||
max_int = (1 << 64) - 1
|
||||
mov_mask = (1 << (64 - 23)) - 1
|
||||
|
||||
def __init__(self):
|
||||
self.v0 = 0
|
||||
self.v1 = 0
|
||||
|
||||
def next(self):
|
||||
x = self.v0
|
||||
y = self.v1
|
||||
self.v0 = y
|
||||
x ^= ((x & xorshift128plus.mov_mask) << 23)
|
||||
x ^= (y ^ (x >> 17) ^ (y >> 26)) & xorshift128plus.max_int
|
||||
self.v1 = x
|
||||
return (x + y) & xorshift128plus.max_int
|
||||
|
||||
def init_from_bin(self, bin):
|
||||
bin += b'\0' * 16
|
||||
self.v0 = struct.unpack('<Q', bin[:8])[0]
|
||||
self.v1 = struct.unpack('<Q', bin[8:16])[0]
|
||||
|
||||
def init_from_bin_len(self, bin, length):
|
||||
bin += b'\0' * 16
|
||||
bin = struct.pack('<H', length) + bin[2:]
|
||||
self.v0 = struct.unpack('<Q', bin[:8])[0]
|
||||
self.v1 = struct.unpack('<Q', bin[8:16])[0]
|
||||
|
||||
for i in range(4):
|
||||
self.next()
|
||||
|
||||
def match_begin(str1, str2):
|
||||
if len(str1) >= len(str2):
|
||||
if str1[:len(str2)] == str2:
|
||||
return True
|
||||
return False
|
||||
|
||||
class auth_base(plain.plain):
|
||||
def __init__(self, method):
|
||||
super(auth_base, self).__init__(method)
|
||||
self.method = method
|
||||
self.no_compatible_method = ''
|
||||
self.overhead = 4
|
||||
|
||||
def init_data(self):
|
||||
return ''
|
||||
|
||||
def get_overhead(self, direction): # direction: true for c->s false for s->c
|
||||
return self.overhead
|
||||
|
||||
def set_server_info(self, server_info):
|
||||
self.server_info = server_info
|
||||
|
||||
def client_encode(self, buf):
|
||||
return buf
|
||||
|
||||
def client_decode(self, buf):
|
||||
return (buf, False)
|
||||
|
||||
def server_encode(self, buf):
|
||||
return buf
|
||||
|
||||
def server_decode(self, buf):
|
||||
return (buf, True, False)
|
||||
|
||||
def not_match_return(self, buf):
|
||||
self.raw_trans = True
|
||||
self.overhead = 0
|
||||
if self.method == self.no_compatible_method:
|
||||
return (b'E'*2048, False)
|
||||
return (buf, False)
|
||||
|
||||
class client_queue(object):
|
||||
def __init__(self, begin_id):
|
||||
self.front = begin_id - 64
|
||||
self.back = begin_id + 1
|
||||
self.alloc = {}
|
||||
self.enable = True
|
||||
self.last_update = time.time()
|
||||
self.ref = 0
|
||||
|
||||
def update(self):
|
||||
self.last_update = time.time()
|
||||
|
||||
def addref(self):
|
||||
self.ref += 1
|
||||
|
||||
def delref(self):
|
||||
if self.ref > 0:
|
||||
self.ref -= 1
|
||||
|
||||
def is_active(self):
|
||||
return (self.ref > 0) and (time.time() - self.last_update < 60 * 10)
|
||||
|
||||
def re_enable(self, connection_id):
|
||||
self.enable = True
|
||||
self.front = connection_id - 64
|
||||
self.back = connection_id + 1
|
||||
self.alloc = {}
|
||||
|
||||
def insert(self, connection_id):
|
||||
if not self.enable:
|
||||
logging.warn('obfs auth: not enable')
|
||||
return False
|
||||
if not self.is_active():
|
||||
self.re_enable(connection_id)
|
||||
self.update()
|
||||
if connection_id < self.front:
|
||||
logging.warn('obfs auth: deprecated id, someone replay attack')
|
||||
return False
|
||||
if connection_id > self.front + 0x4000:
|
||||
logging.warn('obfs auth: wrong id')
|
||||
return False
|
||||
if connection_id in self.alloc:
|
||||
logging.warn('obfs auth: duplicate id, someone replay attack')
|
||||
return False
|
||||
if self.back <= connection_id:
|
||||
self.back = connection_id + 1
|
||||
self.alloc[connection_id] = 1
|
||||
while (self.front in self.alloc) or self.front + 0x1000 < self.back:
|
||||
if self.front in self.alloc:
|
||||
del self.alloc[self.front]
|
||||
self.front += 1
|
||||
self.addref()
|
||||
return True
|
||||
|
||||
class obfs_auth_chain_data(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.user_id = {}
|
||||
self.local_client_id = b''
|
||||
self.connection_id = 0
|
||||
self.set_max_client(64) # max active client count
|
||||
|
||||
def update(self, user_id, client_id, connection_id):
|
||||
if user_id not in self.user_id:
|
||||
self.user_id[user_id] = lru_cache.LRUCache()
|
||||
local_client_id = self.user_id[user_id]
|
||||
|
||||
if client_id in local_client_id:
|
||||
local_client_id[client_id].update()
|
||||
|
||||
def set_max_client(self, max_client):
|
||||
self.max_client = max_client
|
||||
self.max_buffer = max(self.max_client * 2, 1024)
|
||||
|
||||
def insert(self, user_id, client_id, connection_id):
|
||||
if user_id not in self.user_id:
|
||||
self.user_id[user_id] = lru_cache.LRUCache()
|
||||
local_client_id = self.user_id[user_id]
|
||||
|
||||
if local_client_id.get(client_id, None) is None or not local_client_id[client_id].enable:
|
||||
if local_client_id.first() is None or len(local_client_id) < self.max_client:
|
||||
if client_id not in local_client_id:
|
||||
#TODO: check
|
||||
local_client_id[client_id] = client_queue(connection_id)
|
||||
else:
|
||||
local_client_id[client_id].re_enable(connection_id)
|
||||
return local_client_id[client_id].insert(connection_id)
|
||||
|
||||
if not local_client_id[local_client_id.first()].is_active():
|
||||
del local_client_id[local_client_id.first()]
|
||||
if client_id not in local_client_id:
|
||||
#TODO: check
|
||||
local_client_id[client_id] = client_queue(connection_id)
|
||||
else:
|
||||
local_client_id[client_id].re_enable(connection_id)
|
||||
return local_client_id[client_id].insert(connection_id)
|
||||
|
||||
logging.warn(self.name + ': no inactive client')
|
||||
return False
|
||||
else:
|
||||
return local_client_id[client_id].insert(connection_id)
|
||||
|
||||
def remove(self, user_id, client_id):
|
||||
if user_id in self.user_id:
|
||||
local_client_id = self.user_id[user_id]
|
||||
if client_id in local_client_id:
|
||||
local_client_id[client_id].delref()
|
||||
|
||||
class auth_chain_a(auth_base):
|
||||
def __init__(self, method):
|
||||
super(auth_chain_a, self).__init__(method)
|
||||
self.hashfunc = hashlib.md5
|
||||
self.recv_buf = b''
|
||||
self.unit_len = 2800
|
||||
self.raw_trans = False
|
||||
self.has_sent_header = False
|
||||
self.has_recv_header = False
|
||||
self.client_id = 0
|
||||
self.connection_id = 0
|
||||
self.max_time_dif = 60 * 60 * 24 # time dif (second) setting
|
||||
self.salt = b"auth_chain_a"
|
||||
self.no_compatible_method = 'auth_chain_a'
|
||||
self.pack_id = 1
|
||||
self.recv_id = 1
|
||||
self.user_id = None
|
||||
self.user_id_num = 0
|
||||
self.user_key = None
|
||||
self.overhead = 4
|
||||
self.client_over_head = 4
|
||||
self.last_client_hash = b''
|
||||
self.last_server_hash = b''
|
||||
self.random_client = xorshift128plus()
|
||||
self.random_server = xorshift128plus()
|
||||
self.encryptor = None
|
||||
|
||||
def init_data(self):
|
||||
return obfs_auth_chain_data(self.method)
|
||||
|
||||
def get_overhead(self, direction): # direction: true for c->s false for s->c
|
||||
return self.overhead
|
||||
|
||||
def set_server_info(self, server_info):
|
||||
self.server_info = server_info
|
||||
try:
|
||||
max_client = int(server_info.protocol_param.split('#')[0])
|
||||
except:
|
||||
max_client = 64
|
||||
self.server_info.data.set_max_client(max_client)
|
||||
|
||||
def trapezoid_random_float(self, d):
|
||||
if d == 0:
|
||||
return random.random()
|
||||
s = random.random()
|
||||
a = 1 - d
|
||||
return (math.sqrt(a * a + 4 * d * s) - a) / (2 * d)
|
||||
|
||||
def trapezoid_random_int(self, max_val, d):
|
||||
v = self.trapezoid_random_float(d)
|
||||
return int(v * max_val)
|
||||
|
||||
def rnd_data_len(self, buf_size, last_hash, random):
|
||||
if buf_size > 1440:
|
||||
return 0
|
||||
random.init_from_bin_len(last_hash, buf_size)
|
||||
if buf_size > 1300:
|
||||
return random.next() % 31
|
||||
if buf_size > 900:
|
||||
return random.next() % 127
|
||||
if buf_size > 400:
|
||||
return random.next() % 521
|
||||
return random.next() % 1021
|
||||
|
||||
def udp_rnd_data_len(self, last_hash, random):
|
||||
random.init_from_bin(last_hash)
|
||||
return random.next() % 127
|
||||
|
||||
def rnd_start_pos(self, rand_len, random):
|
||||
if rand_len > 0:
|
||||
return random.next() % 8589934609 % rand_len
|
||||
return 0
|
||||
|
||||
def rnd_data(self, buf_size, buf, last_hash, random):
|
||||
rand_len = self.rnd_data_len(buf_size, last_hash, random)
|
||||
|
||||
rnd_data_buf = os.urandom(rand_len)
|
||||
|
||||
if buf_size == 0:
|
||||
return rnd_data_buf
|
||||
else:
|
||||
if rand_len > 0:
|
||||
start_pos = self.rnd_start_pos(rand_len, random)
|
||||
return rnd_data_buf[:start_pos] + buf + rnd_data_buf[start_pos:]
|
||||
else:
|
||||
return buf
|
||||
|
||||
def pack_client_data(self, buf):
|
||||
buf = self.encryptor.encrypt(buf)
|
||||
data = self.rnd_data(len(buf), buf, self.last_client_hash, self.random_client)
|
||||
data_len = len(data) + 8
|
||||
mac_key = self.user_key + struct.pack('<I', self.pack_id)
|
||||
length = len(buf) ^ struct.unpack('<H', self.last_client_hash[14:])[0]
|
||||
data = struct.pack('<H', length) + data
|
||||
self.last_client_hash = hmac.new(mac_key, data, self.hashfunc).digest()
|
||||
data += self.last_client_hash[:2]
|
||||
self.pack_id = (self.pack_id + 1) & 0xFFFFFFFF
|
||||
return data
|
||||
|
||||
def pack_server_data(self, buf):
|
||||
buf = self.encryptor.encrypt(buf)
|
||||
data = self.rnd_data(len(buf), buf, self.last_server_hash, self.random_server)
|
||||
data_len = len(data) + 8
|
||||
mac_key = self.user_key + struct.pack('<I', self.pack_id)
|
||||
length = len(buf) ^ struct.unpack('<H', self.last_server_hash[14:])[0]
|
||||
data = struct.pack('<H', length) + data
|
||||
self.last_server_hash = hmac.new(mac_key, data, self.hashfunc).digest()
|
||||
data += self.last_server_hash[:2]
|
||||
self.pack_id = (self.pack_id + 1) & 0xFFFFFFFF
|
||||
return data
|
||||
|
||||
def pack_auth_data(self, auth_data, buf):
|
||||
data = auth_data
|
||||
data_len = 12 + 4 + 16 + 4
|
||||
data = data + (struct.pack('<H', self.server_info.overhead) + struct.pack('<H', 0))
|
||||
mac_key = self.server_info.iv + self.server_info.key
|
||||
|
||||
check_head = os.urandom(4)
|
||||
self.last_client_hash = hmac.new(mac_key, check_head, self.hashfunc).digest()
|
||||
check_head += self.last_client_hash[:8]
|
||||
|
||||
if b':' in to_bytes(self.server_info.protocol_param):
|
||||
try:
|
||||
items = to_bytes(self.server_info.protocol_param).split(b':')
|
||||
self.user_key = items[1]
|
||||
uid = struct.pack('<I', int(items[0]))
|
||||
except:
|
||||
uid = os.urandom(4)
|
||||
else:
|
||||
uid = os.urandom(4)
|
||||
if self.user_key is None:
|
||||
self.user_key = self.server_info.key
|
||||
|
||||
encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(self.user_key)) + self.salt, 'aes-128-cbc', b'\x00' * 16)
|
||||
|
||||
uid = struct.unpack('<I', uid)[0] ^ struct.unpack('<I', self.last_client_hash[8:12])[0]
|
||||
uid = struct.pack('<I', uid)
|
||||
data = uid + encryptor.encrypt(data)[16:]
|
||||
self.last_server_hash = hmac.new(self.user_key, data, self.hashfunc).digest()
|
||||
data = check_head + data + self.last_server_hash[:4]
|
||||
self.encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(self.user_key)) + to_bytes(base64.b64encode(self.last_client_hash)), 'rc4')
|
||||
return data + self.pack_client_data(buf)
|
||||
|
||||
def auth_data(self):
|
||||
utc_time = int(time.time()) & 0xFFFFFFFF
|
||||
if self.server_info.data.connection_id > 0xFF000000:
|
||||
self.server_info.data.local_client_id = b''
|
||||
if not self.server_info.data.local_client_id:
|
||||
self.server_info.data.local_client_id = os.urandom(4)
|
||||
logging.debug("local_client_id %s" % (binascii.hexlify(self.server_info.data.local_client_id),))
|
||||
self.server_info.data.connection_id = struct.unpack('<I', os.urandom(4))[0] & 0xFFFFFF
|
||||
self.server_info.data.connection_id += 1
|
||||
return b''.join([struct.pack('<I', utc_time),
|
||||
self.server_info.data.local_client_id,
|
||||
struct.pack('<I', self.server_info.data.connection_id)])
|
||||
|
||||
def client_pre_encrypt(self, buf):
|
||||
ret = b''
|
||||
ogn_data_len = len(buf)
|
||||
if not self.has_sent_header:
|
||||
head_size = self.get_head_size(buf, 30)
|
||||
datalen = min(len(buf), random.randint(0, 31) + head_size)
|
||||
ret += self.pack_auth_data(self.auth_data(), buf[:datalen])
|
||||
buf = buf[datalen:]
|
||||
self.has_sent_header = True
|
||||
while len(buf) > self.unit_len:
|
||||
ret += self.pack_client_data(buf[:self.unit_len])
|
||||
buf = buf[self.unit_len:]
|
||||
ret += self.pack_client_data(buf)
|
||||
return ret
|
||||
|
||||
def client_post_decrypt(self, buf):
|
||||
if self.raw_trans:
|
||||
return buf
|
||||
self.recv_buf += buf
|
||||
out_buf = b''
|
||||
while len(self.recv_buf) > 4:
|
||||
mac_key = self.user_key + struct.pack('<I', self.recv_id)
|
||||
data_len = struct.unpack('<H', self.recv_buf[:2])[0] ^ struct.unpack('<H', self.last_server_hash[14:16])[0]
|
||||
rand_len = self.rnd_data_len(data_len, self.last_server_hash, self.random_server)
|
||||
length = data_len + rand_len
|
||||
if length >= 4096:
|
||||
self.raw_trans = True
|
||||
self.recv_buf = b''
|
||||
raise Exception('client_post_decrypt data error')
|
||||
|
||||
if length + 4 > len(self.recv_buf):
|
||||
break
|
||||
|
||||
server_hash = hmac.new(mac_key, self.recv_buf[:length + 2], self.hashfunc).digest()
|
||||
if server_hash[:2] != self.recv_buf[length + 2 : length + 4]:
|
||||
logging.info('%s: checksum error, data %s' % (self.no_compatible_method, binascii.hexlify(self.recv_buf[:length])))
|
||||
self.raw_trans = True
|
||||
self.recv_buf = b''
|
||||
raise Exception('client_post_decrypt data uncorrect checksum')
|
||||
|
||||
pos = 2
|
||||
if data_len > 0 and rand_len > 0:
|
||||
pos = 2 + self.rnd_start_pos(rand_len, self.random_server)
|
||||
out_buf += self.encryptor.decrypt(self.recv_buf[pos : data_len + pos])
|
||||
self.last_server_hash = server_hash
|
||||
if self.recv_id == 1:
|
||||
self.server_info.tcp_mss = struct.unpack('<H', out_buf[:2])[0]
|
||||
out_buf = out_buf[2:]
|
||||
self.recv_id = (self.recv_id + 1) & 0xFFFFFFFF
|
||||
self.recv_buf = self.recv_buf[length + 4:]
|
||||
|
||||
return out_buf
|
||||
|
||||
def server_pre_encrypt(self, buf):
|
||||
if self.raw_trans:
|
||||
return buf
|
||||
ret = b''
|
||||
if self.pack_id == 1:
|
||||
tcp_mss = self.server_info.tcp_mss if self.server_info.tcp_mss < 1500 else 1500
|
||||
self.server_info.tcp_mss = tcp_mss
|
||||
buf = struct.pack('<H', tcp_mss) + buf
|
||||
self.unit_len = tcp_mss - self.client_over_head
|
||||
while len(buf) > self.unit_len:
|
||||
ret += self.pack_server_data(buf[:self.unit_len])
|
||||
buf = buf[self.unit_len:]
|
||||
ret += self.pack_server_data(buf)
|
||||
return ret
|
||||
|
||||
def server_post_decrypt(self, buf):
|
||||
if self.raw_trans:
|
||||
return (buf, False)
|
||||
self.recv_buf += buf
|
||||
out_buf = b''
|
||||
sendback = False
|
||||
|
||||
if not self.has_recv_header:
|
||||
if len(self.recv_buf) >= 12 or len(self.recv_buf) in [7, 8]:
|
||||
recv_len = min(len(self.recv_buf), 12)
|
||||
mac_key = self.server_info.recv_iv + self.server_info.key
|
||||
md5data = hmac.new(mac_key, self.recv_buf[:4], self.hashfunc).digest()
|
||||
if md5data[:recv_len - 4] != self.recv_buf[4:recv_len]:
|
||||
return self.not_match_return(self.recv_buf)
|
||||
|
||||
if len(self.recv_buf) < 12 + 24:
|
||||
return (b'', False)
|
||||
|
||||
self.last_client_hash = md5data
|
||||
uid = struct.unpack('<I', self.recv_buf[12:16])[0] ^ struct.unpack('<I', md5data[8:12])[0]
|
||||
self.user_id_num = uid
|
||||
uid = struct.pack('<I', uid)
|
||||
if uid in self.server_info.users:
|
||||
self.user_id = uid
|
||||
self.user_key = self.server_info.users[uid]
|
||||
self.server_info.update_user_func(uid)
|
||||
else:
|
||||
self.user_id_num = 0
|
||||
if not self.server_info.users:
|
||||
self.user_key = self.server_info.key
|
||||
else:
|
||||
self.user_key = self.server_info.recv_iv
|
||||
|
||||
md5data = hmac.new(self.user_key, self.recv_buf[12 : 12 + 20], self.hashfunc).digest()
|
||||
if md5data[:4] != self.recv_buf[32:36]:
|
||||
logging.error('%s data uncorrect auth HMAC-MD5 from %s:%d, data %s' % (self.no_compatible_method, self.server_info.client, self.server_info.client_port, binascii.hexlify(self.recv_buf)))
|
||||
if len(self.recv_buf) < 36:
|
||||
return (b'', False)
|
||||
return self.not_match_return(self.recv_buf)
|
||||
|
||||
self.last_server_hash = md5data
|
||||
encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(self.user_key)) + self.salt, 'aes-128-cbc')
|
||||
head = encryptor.decrypt(b'\x00' * 16 + self.recv_buf[16:32] + b'\x00') # need an extra byte or recv empty
|
||||
self.client_over_head = struct.unpack('<H', head[12:14])[0]
|
||||
|
||||
utc_time = struct.unpack('<I', head[:4])[0]
|
||||
client_id = struct.unpack('<I', head[4:8])[0]
|
||||
connection_id = struct.unpack('<I', head[8:12])[0]
|
||||
time_dif = common.int32(utc_time - (int(time.time()) & 0xffffffff))
|
||||
if time_dif < -self.max_time_dif or time_dif > self.max_time_dif:
|
||||
logging.info('%s: wrong timestamp, time_dif %d, data %s' % (self.no_compatible_method, time_dif, binascii.hexlify(head)))
|
||||
return self.not_match_return(self.recv_buf)
|
||||
elif self.server_info.data.insert(self.user_id, client_id, connection_id):
|
||||
self.has_recv_header = True
|
||||
self.client_id = client_id
|
||||
self.connection_id = connection_id
|
||||
else:
|
||||
logging.info('%s: auth fail, data %s' % (self.no_compatible_method, binascii.hexlify(out_buf)))
|
||||
return self.not_match_return(self.recv_buf)
|
||||
|
||||
self.encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(self.user_key)) + to_bytes(base64.b64encode(self.last_client_hash)), 'rc4')
|
||||
self.recv_buf = self.recv_buf[36:]
|
||||
self.has_recv_header = True
|
||||
sendback = True
|
||||
|
||||
while len(self.recv_buf) > 4:
|
||||
mac_key = self.user_key + struct.pack('<I', self.recv_id)
|
||||
data_len = struct.unpack('<H', self.recv_buf[:2])[0] ^ struct.unpack('<H', self.last_client_hash[14:16])[0]
|
||||
rand_len = self.rnd_data_len(data_len, self.last_client_hash, self.random_client)
|
||||
length = data_len + rand_len
|
||||
if length >= 4096:
|
||||
self.raw_trans = True
|
||||
self.recv_buf = b''
|
||||
if self.recv_id == 0:
|
||||
logging.info(self.no_compatible_method + ': over size')
|
||||
return (b'E'*2048, False)
|
||||
else:
|
||||
raise Exception('server_post_decrype data error')
|
||||
|
||||
if length + 4 > len(self.recv_buf):
|
||||
break
|
||||
|
||||
client_hash = hmac.new(mac_key, self.recv_buf[:length + 2], self.hashfunc).digest()
|
||||
if client_hash[:2] != self.recv_buf[length + 2 : length + 4]:
|
||||
logging.info('%s: checksum error, data %s' % (self.no_compatible_method, binascii.hexlify(self.recv_buf[:length])))
|
||||
self.raw_trans = True
|
||||
self.recv_buf = b''
|
||||
if self.recv_id == 0:
|
||||
return (b'E'*2048, False)
|
||||
else:
|
||||
raise Exception('server_post_decrype data uncorrect checksum')
|
||||
|
||||
self.recv_id = (self.recv_id + 1) & 0xFFFFFFFF
|
||||
pos = 2
|
||||
if data_len > 0 and rand_len > 0:
|
||||
pos = 2 + self.rnd_start_pos(rand_len, self.random_client)
|
||||
out_buf += self.encryptor.decrypt(self.recv_buf[pos : data_len + pos])
|
||||
self.last_client_hash = client_hash
|
||||
self.recv_buf = self.recv_buf[length + 4:]
|
||||
if data_len == 0:
|
||||
sendback = True
|
||||
|
||||
if out_buf:
|
||||
self.server_info.data.update(self.user_id, self.client_id, self.connection_id)
|
||||
return (out_buf, sendback)
|
||||
|
||||
def client_udp_pre_encrypt(self, buf):
|
||||
if self.user_key is None:
|
||||
if b':' in to_bytes(self.server_info.protocol_param):
|
||||
try:
|
||||
items = to_bytes(self.server_info.protocol_param).split(':')
|
||||
self.user_key = self.hashfunc(items[1]).digest()
|
||||
self.user_id = struct.pack('<I', int(items[0]))
|
||||
except:
|
||||
pass
|
||||
if self.user_key is None:
|
||||
self.user_id = os.urandom(4)
|
||||
self.user_key = self.server_info.key
|
||||
authdata = os.urandom(3)
|
||||
mac_key = self.server_info.key
|
||||
md5data = hmac.new(mac_key, authdata, self.hashfunc).digest()
|
||||
uid = struct.unpack('<I', self.user_id)[0] ^ struct.unpack('<I', md5data[:4])[0]
|
||||
uid = struct.pack('<I', uid)
|
||||
rand_len = self.udp_rnd_data_len(md5data, self.random_client)
|
||||
encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(self.user_key)) + to_bytes(base64.b64encode(md5data)), 'rc4')
|
||||
out_buf = encryptor.encrypt(buf)
|
||||
buf = out_buf + os.urandom(rand_len) + authdata + uid
|
||||
return buf + hmac.new(self.user_key, buf, self.hashfunc).digest()[:1]
|
||||
|
||||
def client_udp_post_decrypt(self, buf):
|
||||
if len(buf) <= 8:
|
||||
return (b'', None)
|
||||
if hmac.new(self.user_key, buf[:-1], self.hashfunc).digest()[:1] != buf[-1:]:
|
||||
return (b'', None)
|
||||
mac_key = self.server_info.key
|
||||
md5data = hmac.new(mac_key, buf[-8:-1], self.hashfunc).digest()
|
||||
rand_len = self.udp_rnd_data_len(md5data, self.random_server)
|
||||
encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(self.user_key)) + to_bytes(base64.b64encode(md5data)), 'rc4')
|
||||
return encryptor.decrypt(buf[:-8 - rand_len])
|
||||
|
||||
def server_udp_pre_encrypt(self, buf, uid):
|
||||
if uid in self.server_info.users:
|
||||
user_key = self.server_info.users[uid]
|
||||
else:
|
||||
uid = None
|
||||
if not self.server_info.users:
|
||||
user_key = self.server_info.key
|
||||
else:
|
||||
user_key = self.server_info.recv_iv
|
||||
authdata = os.urandom(7)
|
||||
mac_key = self.server_info.key
|
||||
md5data = hmac.new(mac_key, authdata, self.hashfunc).digest()
|
||||
rand_len = self.udp_rnd_data_len(md5data, self.random_server)
|
||||
encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(user_key)) + to_bytes(base64.b64encode(md5data)), 'rc4')
|
||||
out_buf = encryptor.encrypt(buf)
|
||||
buf = out_buf + os.urandom(rand_len) + authdata
|
||||
return buf + hmac.new(user_key, buf, self.hashfunc).digest()[:1]
|
||||
|
||||
def server_udp_post_decrypt(self, buf):
|
||||
mac_key = self.server_info.key
|
||||
md5data = hmac.new(mac_key, buf[-8:-5], self.hashfunc).digest()
|
||||
uid = struct.unpack('<I', buf[-5:-1])[0] ^ struct.unpack('<I', md5data[:4])[0]
|
||||
uid = struct.pack('<I', uid)
|
||||
if uid in self.server_info.users:
|
||||
user_key = self.server_info.users[uid]
|
||||
else:
|
||||
uid = None
|
||||
if not self.server_info.users:
|
||||
user_key = self.server_info.key
|
||||
else:
|
||||
user_key = self.server_info.recv_iv
|
||||
if hmac.new(user_key, buf[:-1], self.hashfunc).digest()[:1] != buf[-1:]:
|
||||
return (b'', None)
|
||||
rand_len = self.udp_rnd_data_len(md5data, self.random_client)
|
||||
encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(user_key)) + to_bytes(base64.b64encode(md5data)), 'rc4')
|
||||
out_buf = encryptor.decrypt(buf[:-8 - rand_len])
|
||||
return (out_buf, uid)
|
||||
|
||||
def dispose(self):
|
||||
self.server_info.data.remove(self.user_id, self.client_id)
|
||||
|
||||
class auth_chain_b(auth_chain_a):
|
||||
def __init__(self, method):
|
||||
super(auth_chain_b, self).__init__(method)
|
||||
self.salt = b"auth_chain_b"
|
||||
self.no_compatible_method = 'auth_chain_b'
|
||||
self.data_size_list = []
|
||||
self.data_size_list2 = []
|
||||
|
||||
def init_data_size(self, key):
|
||||
if self.data_size_list:
|
||||
self.data_size_list = []
|
||||
self.data_size_list2 = []
|
||||
random = xorshift128plus()
|
||||
random.init_from_bin(key)
|
||||
list_len = random.next() % 8 + 4
|
||||
for i in range(0, list_len):
|
||||
self.data_size_list.append((int)(random.next() % 2340 % 2040 % 1440))
|
||||
self.data_size_list.sort()
|
||||
list_len = random.next() % 16 + 8
|
||||
for i in range(0, list_len):
|
||||
self.data_size_list2.append((int)(random.next() % 2340 % 2040 % 1440))
|
||||
self.data_size_list2.sort()
|
||||
|
||||
def set_server_info(self, server_info):
|
||||
self.server_info = server_info
|
||||
try:
|
||||
max_client = int(server_info.protocol_param.split('#')[0])
|
||||
except:
|
||||
max_client = 64
|
||||
self.server_info.data.set_max_client(max_client)
|
||||
self.init_data_size(self.server_info.key)
|
||||
|
||||
def rnd_data_len(self, buf_size, last_hash, random):
|
||||
if buf_size >= 1440:
|
||||
return 0
|
||||
random.init_from_bin_len(last_hash, buf_size)
|
||||
pos = bisect.bisect_left(self.data_size_list, buf_size + self.server_info.overhead)
|
||||
final_pos = pos + random.next() % (len(self.data_size_list))
|
||||
if final_pos < len(self.data_size_list):
|
||||
return self.data_size_list[final_pos] - buf_size - self.server_info.overhead
|
||||
|
||||
pos = bisect.bisect_left(self.data_size_list2, buf_size + self.server_info.overhead)
|
||||
final_pos = pos + random.next() % (len(self.data_size_list2))
|
||||
if final_pos < len(self.data_size_list2):
|
||||
return self.data_size_list2[final_pos] - buf_size - self.server_info.overhead
|
||||
if final_pos < pos + len(self.data_size_list2) - 1:
|
||||
return 0
|
||||
|
||||
if buf_size > 1300:
|
||||
return random.next() % 31
|
||||
if buf_size > 900:
|
||||
return random.next() % 127
|
||||
if buf_size > 400:
|
||||
return random.next() % 521
|
||||
return random.next() % 1021
|
||||
|
||||
@@ -159,7 +159,7 @@ class http_simple(plain.plain):
|
||||
if lines and len(lines) > 1:
|
||||
for line in lines:
|
||||
if match_begin(line, b"Host: "):
|
||||
return line[6:]
|
||||
return common.to_str(line[6:])
|
||||
|
||||
def not_match_return(self, buf):
|
||||
self.has_sent_header = True
|
||||
@@ -168,6 +168,11 @@ class http_simple(plain.plain):
|
||||
return (b'E'*2048, False, False)
|
||||
return (buf, True, False)
|
||||
|
||||
def error_return(self, buf):
|
||||
self.has_sent_header = True
|
||||
self.has_recv_header = True
|
||||
return (b'E'*2048, False, False)
|
||||
|
||||
def server_decode(self, buf):
|
||||
if self.has_recv_header:
|
||||
return (buf, True, False)
|
||||
@@ -199,10 +204,10 @@ class http_simple(plain.plain):
|
||||
if host not in hosts:
|
||||
return self.not_match_return(buf)
|
||||
if len(ret_buf) < 4:
|
||||
return self.not_match_return(buf)
|
||||
return self.error_return(buf)
|
||||
if len(datas) > 1:
|
||||
ret_buf += datas[1]
|
||||
if len(ret_buf) >= 7:
|
||||
if len(ret_buf) >= 13:
|
||||
self.has_recv_header = True
|
||||
return (ret_buf, True, False)
|
||||
return self.not_match_return(buf)
|
||||
@@ -214,7 +219,7 @@ class http_post(http_simple):
|
||||
super(http_post, self).__init__(method)
|
||||
|
||||
def boundary(self):
|
||||
return b''.join([random.choice(b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") for i in range(32)])
|
||||
return to_bytes(''.join([random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") for i in range(32)]))
|
||||
|
||||
def client_encode(self, buf):
|
||||
if self.has_sent_header:
|
||||
@@ -245,7 +250,7 @@ class http_post(http_simple):
|
||||
http_head += b"User-Agent: " + random.choice(self.user_agent) + b"\r\n"
|
||||
http_head += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\n"
|
||||
http_head += b"Content-Type: multipart/form-data; boundary=" + self.boundary() + b"\r\nDNT: 1\r\n"
|
||||
http_head += "Connection: keep-alive\r\n\r\n"
|
||||
http_head += b"Connection: keep-alive\r\n\r\n"
|
||||
self.has_sent_header = True
|
||||
return http_head + buf
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ def create_tls_ticket_auth_obfs(method):
|
||||
obfs_map = {
|
||||
'tls1.2_ticket_auth': (create_tls_ticket_auth_obfs,),
|
||||
'tls1.2_ticket_auth_compatible': (create_tls_ticket_auth_obfs,),
|
||||
'tls1.2_ticket_fastauth': (create_tls_ticket_auth_obfs,),
|
||||
'tls1.2_ticket_fastauth_compatible': (create_tls_ticket_auth_obfs,),
|
||||
}
|
||||
|
||||
def match_begin(str1, str2):
|
||||
@@ -54,6 +56,7 @@ class obfs_auth_data(object):
|
||||
self.client_data = lru_cache.LRUCache(60 * 5)
|
||||
self.client_id = os.urandom(32)
|
||||
self.startup_time = int(time.time() - 60 * 30) & 0xFFFFFFFF
|
||||
self.ticket_buf = {}
|
||||
|
||||
class tls_ticket_auth(plain.plain):
|
||||
def __init__(self, method):
|
||||
@@ -64,10 +67,14 @@ class tls_ticket_auth(plain.plain):
|
||||
self.client_id = b''
|
||||
self.max_time_dif = 60 * 60 * 24 # time dif (second) setting
|
||||
self.tls_version = b'\x03\x03'
|
||||
self.overhead = 5
|
||||
|
||||
def init_data(self):
|
||||
return obfs_auth_data()
|
||||
|
||||
def get_overhead(self, direction): # direction: true for c->s false for s->c
|
||||
return self.overhead
|
||||
|
||||
def sni(self, url):
|
||||
url = common.to_bytes(url)
|
||||
data = b"\x00" + struct.pack('>H', len(url)) + url
|
||||
@@ -92,7 +99,8 @@ class tls_ticket_auth(plain.plain):
|
||||
if len(buf) > 0:
|
||||
ret += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf
|
||||
return ret
|
||||
self.send_buffer += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf
|
||||
if len(buf) > 0:
|
||||
self.send_buffer += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf
|
||||
if self.handshake_status == 0:
|
||||
self.handshake_status = 1
|
||||
data = self.tls_version + self.pack_auth_data(self.server_info.data.client_id) + b"\x20" + self.server_info.data.client_id + binascii.unhexlify(b"001cc02bc02fcca9cca8cc14cc13c00ac014c009c013009c0035002f000a" + b"0100")
|
||||
@@ -104,7 +112,9 @@ class tls_ticket_auth(plain.plain):
|
||||
host = random.choice(hosts)
|
||||
ext += self.sni(host)
|
||||
ext += b"\x00\x17\x00\x00"
|
||||
ext += b"\x00\x23\x00\xd0" + os.urandom(208) # ticket
|
||||
if host not in self.server_info.data.ticket_buf:
|
||||
self.server_info.data.ticket_buf[host] = os.urandom((struct.unpack('>H', os.urandom(2))[0] % 17 + 8) * 16)
|
||||
ext += b"\x00\x23" + struct.pack('>H', len(self.server_info.data.ticket_buf[host])) + self.server_info.data.ticket_buf[host]
|
||||
ext += binascii.unhexlify(b"000d001600140601060305010503040104030301030302010203")
|
||||
ext += binascii.unhexlify(b"000500050100000000")
|
||||
ext += binascii.unhexlify(b"00120000")
|
||||
@@ -149,12 +159,14 @@ class tls_ticket_auth(plain.plain):
|
||||
verify = buf[11:33]
|
||||
if hmac.new(self.server_info.key + self.server_info.data.client_id, verify, hashlib.sha1).digest()[:10] != buf[33:43]:
|
||||
raise Exception('client_decode data error')
|
||||
if hmac.new(self.server_info.key + self.server_info.data.client_id, buf[:-10], hashlib.sha1).digest()[:10] != buf[-10:]:
|
||||
raise Exception('client_decode data error')
|
||||
return (b'', True)
|
||||
|
||||
def server_encode(self, buf):
|
||||
if self.handshake_status == -1:
|
||||
return buf
|
||||
if self.handshake_status == 8:
|
||||
if (self.handshake_status & 8) == 8:
|
||||
ret = b''
|
||||
while len(buf) > 2048:
|
||||
size = min(struct.unpack('>H', os.urandom(2))[0] % 4096 + 100, len(buf))
|
||||
@@ -163,18 +175,28 @@ class tls_ticket_auth(plain.plain):
|
||||
if len(buf) > 0:
|
||||
ret += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf
|
||||
return ret
|
||||
self.handshake_status = 3
|
||||
self.handshake_status |= 8
|
||||
data = self.tls_version + self.pack_auth_data(self.client_id) + b"\x20" + self.client_id + binascii.unhexlify(b"c02f000005ff01000100")
|
||||
data = b"\x02\x00" + struct.pack('>H', len(data)) + data #server hello
|
||||
data = b"\x16\x03\x03" + struct.pack('>H', len(data)) + data
|
||||
data = b"\x16" + self.tls_version + struct.pack('>H', len(data)) + data
|
||||
if random.randint(0, 8) < 1:
|
||||
ticket = os.urandom((struct.unpack('>H', os.urandom(2))[0] % 164) * 2 + 64)
|
||||
ticket = struct.pack('>H', len(ticket) + 4) + b"\x04\x00" + struct.pack('>H', len(ticket)) + ticket
|
||||
data += b"\x16" + self.tls_version + ticket #New session ticket
|
||||
data += b"\x14" + self.tls_version + b"\x00\x01\x01" #ChangeCipherSpec
|
||||
data += b"\x16" + self.tls_version + b"\x00\x20" + os.urandom(22) #Finished
|
||||
finish_len = random.choice([32, 40])
|
||||
data += b"\x16" + self.tls_version + struct.pack('>H', finish_len) + os.urandom(finish_len - 10) #Finished
|
||||
data += hmac.new(self.server_info.key + self.client_id, data, hashlib.sha1).digest()[:10]
|
||||
if buf:
|
||||
data += self.server_encode(buf)
|
||||
return data
|
||||
|
||||
def decode_error_return(self, buf):
|
||||
self.handshake_status = -1
|
||||
if self.method == 'tls1.2_ticket_auth':
|
||||
if self.overhead > 0:
|
||||
self.server_info.overhead -= self.overhead
|
||||
self.overhead = 0
|
||||
if self.method in ['tls1.2_ticket_auth', 'tls1.2_ticket_fastauth']:
|
||||
return (b'E'*2048, False, False)
|
||||
return (buf, True, False)
|
||||
|
||||
@@ -182,7 +204,7 @@ class tls_ticket_auth(plain.plain):
|
||||
if self.handshake_status == -1:
|
||||
return (buf, True, False)
|
||||
|
||||
if self.handshake_status == 8:
|
||||
if (self.handshake_status & 4) == 4:
|
||||
ret = b''
|
||||
self.recv_buffer += buf
|
||||
while len(self.recv_buffer) > 5:
|
||||
@@ -196,25 +218,27 @@ class tls_ticket_auth(plain.plain):
|
||||
self.recv_buffer = self.recv_buffer[size+5:]
|
||||
return (ret, True, False)
|
||||
|
||||
if self.handshake_status == 3:
|
||||
if (self.handshake_status & 1) == 1:
|
||||
self.recv_buffer += buf
|
||||
buf = self.recv_buffer
|
||||
verify = buf
|
||||
verify_len = 43 - 10
|
||||
if len(buf) < 43:
|
||||
if len(buf) < 11:
|
||||
raise Exception('server_decode data error')
|
||||
if not match_begin(buf, b"\x14" + self.tls_version + b"\x00\x01\x01"): #ChangeCipherSpec
|
||||
raise Exception('server_decode data error')
|
||||
buf = buf[6:]
|
||||
if not match_begin(buf, b"\x16" + self.tls_version + b"\x00\x20"): #Finished
|
||||
if not match_begin(buf, b"\x16" + self.tls_version + b"\x00"): #Finished
|
||||
raise Exception('server_decode data error')
|
||||
verify_len = struct.unpack('>H', buf[3:5])[0] + 1 # 11 - 10
|
||||
if len(verify) < verify_len + 10:
|
||||
return (b'', False, False)
|
||||
if hmac.new(self.server_info.key + self.client_id, verify[:verify_len], hashlib.sha1).digest()[:10] != verify[verify_len:verify_len+10]:
|
||||
raise Exception('server_decode data error')
|
||||
if len(buf) < 37:
|
||||
raise Exception('server_decode data error')
|
||||
self.recv_buffer = buf[37:]
|
||||
self.handshake_status = 8
|
||||
return self.server_decode(b'')
|
||||
self.recv_buffer = verify[verify_len + 10:]
|
||||
status = self.handshake_status
|
||||
self.handshake_status |= 4
|
||||
ret = self.server_decode(b'')
|
||||
return ret;
|
||||
|
||||
#raise Exception("handshake data = %s" % (binascii.hexlify(buf)))
|
||||
self.recv_buffer += buf
|
||||
@@ -225,12 +249,13 @@ class tls_ticket_auth(plain.plain):
|
||||
if not match_begin(buf, b'\x16\x03\x01'):
|
||||
return self.decode_error_return(ogn_buf)
|
||||
buf = buf[3:]
|
||||
if struct.unpack('>H', buf[:2])[0] > len(buf) - 2:
|
||||
header_len = struct.unpack('>H', buf[:2])[0]
|
||||
if header_len > len(buf) - 2:
|
||||
return (b'', False, False)
|
||||
|
||||
self.recv_buffer = self.recv_buffer[struct.unpack('>H', buf[:2])[0] + 5:]
|
||||
self.handshake_status = 2
|
||||
buf = buf[2:]
|
||||
self.recv_buffer = self.recv_buffer[header_len + 5:]
|
||||
self.handshake_status = 1
|
||||
buf = buf[2:header_len + 2]
|
||||
if not match_begin(buf, b'\x01\x00'): #client hello
|
||||
logging.info("tls_auth not client hello message")
|
||||
return self.decode_error_return(ogn_buf)
|
||||
@@ -272,6 +297,9 @@ class tls_ticket_auth(plain.plain):
|
||||
return self.decode_error_return(ogn_buf)
|
||||
self.server_info.data.client_data.sweep()
|
||||
self.server_info.data.client_data[verifyid[:22]] = sessionid
|
||||
if len(self.recv_buffer) >= 11:
|
||||
ret = self.server_decode(b'')
|
||||
return (ret[0], True, True)
|
||||
# (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back)
|
||||
return (b'', False, True)
|
||||
|
||||
|
||||
@@ -40,6 +40,12 @@ class plain(object):
|
||||
def init_data(self):
|
||||
return b''
|
||||
|
||||
def get_overhead(self, direction): # direction: true for c->s false for s->c
|
||||
return 0
|
||||
|
||||
def get_server_info(self):
|
||||
return self.server_info
|
||||
|
||||
def set_server_info(self, server_info):
|
||||
self.server_info = server_info
|
||||
|
||||
@@ -75,11 +81,11 @@ class plain(object):
|
||||
def client_udp_post_decrypt(self, buf):
|
||||
return buf
|
||||
|
||||
def server_udp_pre_encrypt(self, buf):
|
||||
def server_udp_pre_encrypt(self, buf, uid):
|
||||
return buf
|
||||
|
||||
def server_udp_post_decrypt(self, buf):
|
||||
return buf
|
||||
return (buf, None)
|
||||
|
||||
def dispose(self):
|
||||
pass
|
||||
|
||||
@@ -36,23 +36,11 @@ from shadowsocks import common
|
||||
from shadowsocks.obfsplugin import plain
|
||||
from shadowsocks.common import to_bytes, to_str, ord, chr
|
||||
|
||||
def create_verify_obfs(method):
|
||||
return verify_simple(method)
|
||||
|
||||
def create_verify_deflate(method):
|
||||
return verify_deflate(method)
|
||||
|
||||
def create_verify_sha1(method):
|
||||
return verify_sha1(method)
|
||||
|
||||
def create_auth_obfs(method):
|
||||
return auth_simple(method)
|
||||
|
||||
obfs_map = {
|
||||
'verify_simple': (create_verify_obfs,),
|
||||
'verify_deflate': (create_verify_deflate,),
|
||||
'verify_sha1': (create_verify_sha1,),
|
||||
'verify_sha1_compatible': (create_verify_sha1,),
|
||||
}
|
||||
|
||||
def match_begin(str1, str2):
|
||||
@@ -88,100 +76,6 @@ class verify_base(plain.plain):
|
||||
def server_decode(self, buf):
|
||||
return (buf, True, False)
|
||||
|
||||
class verify_simple(verify_base):
|
||||
def __init__(self, method):
|
||||
super(verify_simple, self).__init__(method)
|
||||
self.recv_buf = b''
|
||||
self.unit_len = 8100
|
||||
self.decrypt_packet_num = 0
|
||||
self.raw_trans = False
|
||||
|
||||
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_post_decrypt(self, buf):
|
||||
if self.raw_trans:
|
||||
return 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 or length < 7:
|
||||
self.raw_trans = True
|
||||
self.recv_buf = b''
|
||||
raise Exception('client_post_decrypt data error')
|
||||
if length > len(self.recv_buf):
|
||||
break
|
||||
|
||||
if (binascii.crc32(self.recv_buf[:length]) & 0xffffffff) != 0xffffffff:
|
||||
self.raw_trans = True
|
||||
self.recv_buf = b''
|
||||
raise Exception('client_post_decrypt 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:]
|
||||
|
||||
if out_buf:
|
||||
self.decrypt_packet_num += 1
|
||||
return out_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_post_decrypt(self, buf):
|
||||
if self.raw_trans:
|
||||
return (buf, False)
|
||||
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 or length < 7:
|
||||
self.raw_trans = True
|
||||
self.recv_buf = b''
|
||||
if self.decrypt_packet_num == 0:
|
||||
return (b'E'*2048, False)
|
||||
else:
|
||||
raise Exception('server_post_decrype data error')
|
||||
if length > len(self.recv_buf):
|
||||
break
|
||||
|
||||
if (binascii.crc32(self.recv_buf[:length]) & 0xffffffff) != 0xffffffff:
|
||||
self.raw_trans = True
|
||||
self.recv_buf = b''
|
||||
if self.decrypt_packet_num == 0:
|
||||
return (b'E'*2048, False)
|
||||
else:
|
||||
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:]
|
||||
|
||||
if out_buf:
|
||||
self.decrypt_packet_num += 1
|
||||
return (out_buf, False)
|
||||
|
||||
class verify_deflate(verify_base):
|
||||
def __init__(self, method):
|
||||
super(verify_deflate, self).__init__(method)
|
||||
@@ -258,103 +152,3 @@ class verify_deflate(verify_base):
|
||||
self.decrypt_packet_num += 1
|
||||
return (out_buf, False)
|
||||
|
||||
class verify_sha1(verify_base):
|
||||
def __init__(self, method):
|
||||
super(verify_sha1, self).__init__(method)
|
||||
self.recv_buf = b''
|
||||
self.unit_len = 8100
|
||||
self.raw_trans = False
|
||||
self.pack_id = 0
|
||||
self.recv_id = 0
|
||||
self.has_sent_header = False
|
||||
self.has_recv_header = False
|
||||
|
||||
def pack_data(self, buf):
|
||||
if len(buf) == 0:
|
||||
return b''
|
||||
sha1data = hmac.new(self.server_info.iv + struct.pack('>I', self.pack_id), buf, hashlib.sha1).digest()
|
||||
data = struct.pack('>H', len(buf)) + sha1data[:10] + buf
|
||||
self.pack_id += 1
|
||||
return data
|
||||
|
||||
def pack_auth_data(self, buf):
|
||||
data = chr(ord(buf[0]) | 0x10) + buf[1:]
|
||||
data += hmac.new(self.server_info.iv + self.server_info.key, data, hashlib.sha1).digest()[:10]
|
||||
return data
|
||||
|
||||
def client_pre_encrypt(self, buf):
|
||||
ret = b''
|
||||
if not self.has_sent_header:
|
||||
datalen = self.get_head_size(buf, 30)
|
||||
ret += self.pack_auth_data(buf[:datalen])
|
||||
buf = buf[datalen:]
|
||||
self.has_sent_header = True
|
||||
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_post_decrypt(self, buf):
|
||||
return buf
|
||||
|
||||
def server_pre_encrypt(self, buf):
|
||||
return buf
|
||||
|
||||
def not_match_return(self, buf):
|
||||
self.raw_trans = True
|
||||
if self.method == 'verify_sha1':
|
||||
return (b'E'*2048, False)
|
||||
return (buf, False)
|
||||
|
||||
def server_post_decrypt(self, buf):
|
||||
if self.raw_trans:
|
||||
return (buf, False)
|
||||
self.recv_buf += buf
|
||||
out_buf = b''
|
||||
if not self.has_recv_header:
|
||||
if len(self.recv_buf) < 2:
|
||||
return (b'', False)
|
||||
if (ord(self.recv_buf[0]) & 0x10) != 0x10:
|
||||
return self.not_match_return(self.recv_buf)
|
||||
head_size = self.get_head_size(self.recv_buf, 65536)
|
||||
if len(self.recv_buf) < head_size + 10:
|
||||
return self.not_match_return(self.recv_buf)
|
||||
sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, self.recv_buf[:head_size], hashlib.sha1).digest()[:10]
|
||||
if sha1data != self.recv_buf[head_size:head_size + 10]:
|
||||
logging.error('server_post_decrype data uncorrect auth HMAC-SHA1')
|
||||
return self.not_match_return(self.recv_buf)
|
||||
out_buf = to_bytes(chr(ord(self.recv_buf[0]) & 0xEF)) + self.recv_buf[1:head_size]
|
||||
self.recv_buf = self.recv_buf[head_size + 10:]
|
||||
self.has_recv_header = True
|
||||
while len(self.recv_buf) > 2:
|
||||
length = struct.unpack('>H', self.recv_buf[:2])[0] + 12
|
||||
if length > len(self.recv_buf):
|
||||
break
|
||||
|
||||
data = self.recv_buf[12:length]
|
||||
sha1data = hmac.new(self.server_info.recv_iv + struct.pack('>I', self.recv_id), data, hashlib.sha1).digest()[:10]
|
||||
if sha1data != self.recv_buf[2:12]:
|
||||
raise Exception('server_post_decrype data uncorrect chunk HMAC-SHA1')
|
||||
|
||||
self.recv_id = (self.recv_id + 1) & 0xFFFFFFFF
|
||||
out_buf += data
|
||||
self.recv_buf = self.recv_buf[length:]
|
||||
|
||||
return (out_buf, False)
|
||||
|
||||
def client_udp_pre_encrypt(self, buf):
|
||||
ret = self.pack_auth_data(buf)
|
||||
return chr(ord(buf[0]) | 0x10) + buf[1:]
|
||||
|
||||
def server_udp_post_decrypt(self, buf):
|
||||
if buf and ((ord(buf[0]) & 0x10) == 0x10):
|
||||
if len(buf) <= 11:
|
||||
return b''
|
||||
sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, buf[:-10], hashlib.sha1).digest()[:10]
|
||||
if sha1data != buf[-10:]:
|
||||
return b''
|
||||
return to_bytes(chr(ord(buf[0]) & 0xEF)) + buf[1:-10]
|
||||
else:
|
||||
return buf
|
||||
|
||||
|
||||
Regular → Executable
+4
-3
@@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
cd `dirname $0`
|
||||
eval $(ps -ef | grep "[0-9] python server\\.py a" | awk '{print "kill "$2}')
|
||||
ulimit -n 4096
|
||||
nohup python server.py a >> /dev/null 2>&1 &
|
||||
python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
|
||||
eval $(ps -ef | grep "[0-9] ${python_ver} server\\.py a" | awk '{print "kill "$2}')
|
||||
ulimit -n 512000
|
||||
nohup ${python_ver} server.py a>> /dev/null 2>&1 &
|
||||
|
||||
|
||||
+10
-10
@@ -29,7 +29,7 @@ if __name__ == '__main__':
|
||||
sys.path.insert(0, os.path.join(file_path, '../'))
|
||||
|
||||
from shadowsocks import shell, daemon, eventloop, tcprelay, udprelay, \
|
||||
asyncdns, manager
|
||||
asyncdns, manager, common
|
||||
|
||||
|
||||
def main():
|
||||
@@ -86,18 +86,18 @@ def main():
|
||||
bindv6 = config.get("out_bindv6", '')
|
||||
if type(password_obfs) == list:
|
||||
password = password_obfs[0]
|
||||
obfs = password_obfs[1]
|
||||
obfs = common.to_str(password_obfs[1])
|
||||
if len(password_obfs) > 2:
|
||||
protocol = password_obfs[2]
|
||||
protocol = common.to_str(password_obfs[2])
|
||||
elif type(password_obfs) == dict:
|
||||
password = password_obfs.get('password', config_password)
|
||||
method = password_obfs.get('method', method)
|
||||
protocol = password_obfs.get('protocol', protocol)
|
||||
protocol_param = password_obfs.get('protocol_param', protocol_param)
|
||||
obfs = password_obfs.get('obfs', obfs)
|
||||
obfs_param = password_obfs.get('obfs_param', obfs_param)
|
||||
bind = password_obfs.get('bind', bind)
|
||||
bindv6 = password_obfs.get('bindv6', bindv6)
|
||||
method = common.to_str(password_obfs.get('method', method))
|
||||
protocol = common.to_str(password_obfs.get('protocol', protocol))
|
||||
protocol_param = common.to_str(password_obfs.get('protocol_param', protocol_param))
|
||||
obfs = common.to_str(password_obfs.get('obfs', obfs))
|
||||
obfs_param = common.to_str(password_obfs.get('obfs_param', obfs_param))
|
||||
bind = password_obfs.get('out_bind', bind)
|
||||
bindv6 = password_obfs.get('out_bindv6', bindv6)
|
||||
else:
|
||||
password = password_obfs
|
||||
a_config = config.copy()
|
||||
|
||||
Regular → Executable
+52
-64
@@ -52,49 +52,37 @@ def print_exception(e):
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def __version():
|
||||
version_str = ''
|
||||
try:
|
||||
import pkg_resources
|
||||
version_str = pkg_resources.get_distribution('shadowsocks').version
|
||||
except Exception:
|
||||
try:
|
||||
from shadowsocks import version
|
||||
version_str = version.version()
|
||||
except Exception:
|
||||
pass
|
||||
return version_str
|
||||
|
||||
def print_shadowsocks():
|
||||
version_str = ''
|
||||
try:
|
||||
import pkg_resources
|
||||
version_str = pkg_resources.get_distribution('shadowsocks').version
|
||||
except Exception:
|
||||
try:
|
||||
from shadowsocks import version
|
||||
version_str = version.version()
|
||||
except Exception:
|
||||
pass
|
||||
print('ShadowsocksR %s' % version_str)
|
||||
print('ShadowsocksR %s' % __version())
|
||||
|
||||
def log_shadowsocks_version():
|
||||
version_str = ''
|
||||
try:
|
||||
import pkg_resources
|
||||
version_str = pkg_resources.get_distribution('shadowsocks').version
|
||||
except Exception:
|
||||
try:
|
||||
from shadowsocks import version
|
||||
version_str = version.version()
|
||||
except Exception:
|
||||
pass
|
||||
logging.info('ShadowsocksR %s' % version_str)
|
||||
logging.info('ShadowsocksR %s' % __version())
|
||||
|
||||
|
||||
def find_config():
|
||||
config_path = 'user-config.json'
|
||||
if os.path.exists(config_path):
|
||||
return config_path
|
||||
config_path = os.path.join(os.path.dirname(__file__), '../', 'user-config.json')
|
||||
if os.path.exists(config_path):
|
||||
return config_path
|
||||
|
||||
user_config_path = 'user-config.json'
|
||||
config_path = 'config.json'
|
||||
if os.path.exists(config_path):
|
||||
return config_path
|
||||
config_path = os.path.join(os.path.dirname(__file__), '../', 'config.json')
|
||||
if os.path.exists(config_path):
|
||||
return config_path
|
||||
return None
|
||||
|
||||
def sub_find(file_name):
|
||||
if os.path.exists(file_name):
|
||||
return file_name
|
||||
file_name = os.path.join(os.path.abspath('..'), file_name)
|
||||
return file_name if os.path.exists(file_name) else None
|
||||
|
||||
return sub_find(user_config_path) or sub_find(config_path)
|
||||
|
||||
def check_config(config, is_local):
|
||||
if config.get('daemon', None) == 'stop':
|
||||
@@ -119,21 +107,15 @@ def check_config(config, is_local):
|
||||
config['server_port'] = int(config['server_port'])
|
||||
|
||||
if config.get('local_address', '') in [b'0.0.0.0']:
|
||||
logging.warn('warning: local set to listen on 0.0.0.0, it\'s not safe')
|
||||
logging.warning('warning: local set to listen on 0.0.0.0, it\'s not safe')
|
||||
if config.get('server', '') in ['127.0.0.1', 'localhost']:
|
||||
logging.warn('warning: server set to listen on %s:%s, are you sure?' %
|
||||
logging.warning('warning: server set to listen on %s:%s, are you sure?' %
|
||||
(to_str(config['server']), config['server_port']))
|
||||
if (config.get('method', '') or '').lower() == 'table':
|
||||
logging.warn('warning: table is not safe; please use a safer cipher, '
|
||||
'like AES-256-CFB')
|
||||
if (config.get('method', '') or '').lower() == 'rc4':
|
||||
logging.warn('warning: RC4 is not safe; please use a safer cipher, '
|
||||
'like AES-256-CFB')
|
||||
if config.get('timeout', 300) < 100:
|
||||
logging.warn('warning: your timeout %d seems too short' %
|
||||
logging.warning('warning: your timeout %d seems too short' %
|
||||
int(config.get('timeout')))
|
||||
if config.get('timeout', 300) > 600:
|
||||
logging.warn('warning: your timeout %d seems too long' %
|
||||
logging.warning('warning: your timeout %d seems too long' %
|
||||
int(config.get('timeout')))
|
||||
if config.get('password') in [b'mypassword']:
|
||||
logging.error('DON\'T USE DEFAULT PASSWORD! Please change it in your '
|
||||
@@ -149,7 +131,8 @@ def check_config(config, is_local):
|
||||
|
||||
def get_config(is_local):
|
||||
global verbose
|
||||
|
||||
config = {}
|
||||
config_path = None
|
||||
logging.basicConfig(level=logging.INFO,
|
||||
format='%(levelname)-s: %(message)s')
|
||||
if is_local:
|
||||
@@ -161,23 +144,32 @@ def get_config(is_local):
|
||||
longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers=',
|
||||
'forbidden-ip=', 'user=', 'manager-address=', 'version']
|
||||
try:
|
||||
config_path = find_config()
|
||||
optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
|
||||
for key, value in optlist:
|
||||
if key == '-c':
|
||||
config_path = value
|
||||
elif key in ('-h', '--help'):
|
||||
print_help(is_local)
|
||||
sys.exit(0)
|
||||
elif key == '--version':
|
||||
print_shadowsocks()
|
||||
sys.exit(0)
|
||||
else:
|
||||
continue
|
||||
|
||||
if config_path is None:
|
||||
config_path = find_config()
|
||||
|
||||
|
||||
if config_path:
|
||||
logging.info('loading config from %s' % config_path)
|
||||
logging.debug('loading config from %s' % config_path)
|
||||
with open(config_path, 'rb') as f:
|
||||
try:
|
||||
config = parse_json_in_str(remove_comment(f.read().decode('utf8')))
|
||||
except ValueError as e:
|
||||
logging.error('found an error in config.json: %s',
|
||||
e.message)
|
||||
logging.error('found an error in config.json: %s', str(e))
|
||||
sys.exit(1)
|
||||
else:
|
||||
config = {}
|
||||
|
||||
|
||||
v_count = 0
|
||||
for key, value in optlist:
|
||||
@@ -217,15 +209,7 @@ def get_config(is_local):
|
||||
config['user'] = to_str(value)
|
||||
elif key == '--forbidden-ip':
|
||||
config['forbidden_ip'] = to_str(value)
|
||||
elif key in ('-h', '--help'):
|
||||
if is_local:
|
||||
print_local_help()
|
||||
else:
|
||||
print_server_help()
|
||||
sys.exit(0)
|
||||
elif key == '--version':
|
||||
print_shadowsocks()
|
||||
sys.exit(0)
|
||||
|
||||
elif key == '-d':
|
||||
config['daemon'] = to_str(value)
|
||||
elif key == '--pid-file':
|
||||
@@ -235,6 +219,8 @@ def get_config(is_local):
|
||||
elif key == '-q':
|
||||
v_count -= 1
|
||||
config['verbose'] = v_count
|
||||
else:
|
||||
continue
|
||||
except getopt.GetoptError as e:
|
||||
print(e, file=sys.stderr)
|
||||
print_help(is_local)
|
||||
@@ -252,13 +238,15 @@ def get_config(is_local):
|
||||
config['obfs'] = to_str(config.get('obfs', 'plain'))
|
||||
config['obfs_param'] = to_str(config.get('obfs_param', ''))
|
||||
config['port_password'] = config.get('port_password', None)
|
||||
config['additional_ports'] = config.get('additional_ports', {})
|
||||
config['additional_ports_only'] = config.get('additional_ports_only', False)
|
||||
config['timeout'] = int(config.get('timeout', 300))
|
||||
config['udp_timeout'] = int(config.get('udp_timeout', 120))
|
||||
config['udp_cache'] = int(config.get('udp_cache', 64))
|
||||
config['fast_open'] = config.get('fast_open', False)
|
||||
config['workers'] = config.get('workers', 1)
|
||||
config['pid-file'] = config.get('pid-file', '/var/run/shadowsocks.pid')
|
||||
config['log-file'] = config.get('log-file', '/var/log/shadowsocks.log')
|
||||
config['pid-file'] = config.get('pid-file', '/var/run/shadowsocksr.pid')
|
||||
config['log-file'] = config.get('log-file', '/var/log/shadowsocksr.log')
|
||||
config['verbose'] = config.get('verbose', False)
|
||||
config['connect_verbose_info'] = config.get('connect_verbose_info', 0)
|
||||
config['local_address'] = to_str(config.get('local_address', '127.0.0.1'))
|
||||
@@ -285,7 +273,7 @@ def get_config(is_local):
|
||||
sys.exit(2)
|
||||
try:
|
||||
config['ignore_bind'] = \
|
||||
IPNetwork(config.get('ignore_bind', '127.0.0.0/8,::1/128'))
|
||||
IPNetwork(config.get('ignore_bind', '127.0.0.0/8,::1/128,10.0.0.0/8,192.168.0.0/16'))
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
sys.exit(2)
|
||||
|
||||
Regular → Executable
+3
-1
@@ -1,3 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
eval $(ps -ef | grep "[0-9] python server\\.py a" | awk '{print "kill "$2}')
|
||||
python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
|
||||
eval $(ps -ef | grep "[0-9] ${python_ver} server\\.py a" | awk '{print "kill "$2}')
|
||||
|
||||
|
||||
Regular → Executable
+435
-116
@@ -27,8 +27,9 @@ import binascii
|
||||
import traceback
|
||||
import random
|
||||
import platform
|
||||
import threading
|
||||
|
||||
from shadowsocks import encrypt, obfs, eventloop, shell, common, lru_cache
|
||||
from shadowsocks import encrypt, obfs, eventloop, shell, common, lru_cache, version
|
||||
from shadowsocks.common import pre_parse_header, parse_header
|
||||
|
||||
# we clear at most TIMEOUTS_CLEAN_SIZE timeouts each time
|
||||
@@ -89,9 +90,39 @@ WAIT_STATUS_READING = 1
|
||||
WAIT_STATUS_WRITING = 2
|
||||
WAIT_STATUS_READWRITING = WAIT_STATUS_READING | WAIT_STATUS_WRITING
|
||||
|
||||
NETWORK_MTU = 1500
|
||||
TCP_MSS = NETWORK_MTU - 40
|
||||
BUF_SIZE = 32 * 1024
|
||||
UDP_MAX_BUF_SIZE = 65536
|
||||
|
||||
class SpeedTester(object):
|
||||
def __init__(self, max_speed = 0):
|
||||
self.max_speed = max_speed * 1024
|
||||
self.last_time = time.time()
|
||||
self.sum_len = 0
|
||||
|
||||
def update_limit(self, max_speed):
|
||||
self.max_speed = max_speed * 1024
|
||||
|
||||
def add(self, data_len):
|
||||
if self.max_speed > 0:
|
||||
cut_t = time.time()
|
||||
self.sum_len -= (cut_t - self.last_time) * self.max_speed
|
||||
if self.sum_len < 0:
|
||||
self.sum_len = 0
|
||||
self.last_time = cut_t
|
||||
self.sum_len += data_len
|
||||
|
||||
def isExceed(self):
|
||||
if self.max_speed > 0:
|
||||
cut_t = time.time()
|
||||
self.sum_len -= (cut_t - self.last_time) * self.max_speed
|
||||
if self.sum_len < 0:
|
||||
self.sum_len = 0
|
||||
self.last_time = cut_t
|
||||
return self.sum_len >= self.max_speed
|
||||
return False
|
||||
|
||||
class TCPRelayHandler(object):
|
||||
def __init__(self, server, fd_to_handlers, loop, local_sock, config,
|
||||
dns_resolver, is_local):
|
||||
@@ -101,28 +132,36 @@ class TCPRelayHandler(object):
|
||||
self._local_sock = local_sock
|
||||
self._remote_sock = None
|
||||
self._remote_sock_v6 = None
|
||||
self._local_sock_fd = None
|
||||
self._remote_sock_fd = None
|
||||
self._remotev6_sock_fd = None
|
||||
self._remote_udp = False
|
||||
self._config = config
|
||||
self._dns_resolver = dns_resolver
|
||||
self._add_ref = 0
|
||||
if not self._create_encryptor(config):
|
||||
return
|
||||
|
||||
self._client_address = local_sock.getpeername()[:2]
|
||||
self._accept_address = local_sock.getsockname()[:2]
|
||||
self._user = None
|
||||
self._user_id = server._listen_port
|
||||
self._update_tcp_mss(local_sock)
|
||||
|
||||
# TCP Relay works as either sslocal or ssserver
|
||||
# if is_local, this is sslocal
|
||||
self._is_local = is_local
|
||||
self._stage = STAGE_INIT
|
||||
try:
|
||||
self._encryptor = encrypt.Encryptor(config['password'],
|
||||
config['method'])
|
||||
except Exception:
|
||||
self._stage = STAGE_DESTROYED
|
||||
logging.error('create encryptor fail at port %d', server._listen_port)
|
||||
return
|
||||
self._encrypt_correct = True
|
||||
self._obfs = obfs.obfs(config['obfs'])
|
||||
self._protocol = obfs.obfs(config['protocol'])
|
||||
self._overhead = self._obfs.get_overhead(self._is_local) + self._protocol.get_overhead(self._is_local)
|
||||
self._recv_buffer_size = BUF_SIZE - self._overhead
|
||||
|
||||
server_info = obfs.server_info(server.obfs_data)
|
||||
server_info.host = config['server']
|
||||
server_info.port = server._listen_port
|
||||
#server_info.users = server.server_users
|
||||
#server_info.update_user_func = self._update_user
|
||||
server_info.client = self._client_address[0]
|
||||
server_info.client_port = self._client_address[1]
|
||||
server_info.protocol_param = ''
|
||||
@@ -132,13 +171,16 @@ class TCPRelayHandler(object):
|
||||
server_info.key_str = common.to_bytes(config['password'])
|
||||
server_info.key = self._encryptor.cipher_key
|
||||
server_info.head_len = 30
|
||||
server_info.tcp_mss = 1460
|
||||
server_info.tcp_mss = self._tcp_mss
|
||||
server_info.buffer_size = self._recv_buffer_size
|
||||
server_info.overhead = self._overhead
|
||||
self._obfs.set_server_info(server_info)
|
||||
|
||||
self._protocol = obfs.obfs(config['protocol'])
|
||||
server_info = obfs.server_info(server.protocol_data)
|
||||
server_info.host = config['server']
|
||||
server_info.port = server._listen_port
|
||||
server_info.users = server.server_users
|
||||
server_info.update_user_func = self._update_user
|
||||
server_info.client = self._client_address[0]
|
||||
server_info.client_port = self._client_address[1]
|
||||
server_info.protocol_param = config['protocol_param']
|
||||
@@ -148,7 +190,9 @@ class TCPRelayHandler(object):
|
||||
server_info.key_str = common.to_bytes(config['password'])
|
||||
server_info.key = self._encryptor.cipher_key
|
||||
server_info.head_len = 30
|
||||
server_info.tcp_mss = 1460
|
||||
server_info.tcp_mss = self._tcp_mss
|
||||
server_info.buffer_size = self._recv_buffer_size
|
||||
server_info.overhead = self._overhead
|
||||
self._protocol.set_server_info(server_info)
|
||||
|
||||
self._redir_list = config.get('redirect', ["*#0.0.0.0:0"])
|
||||
@@ -164,25 +208,31 @@ class TCPRelayHandler(object):
|
||||
self._upstream_status = WAIT_STATUS_READING
|
||||
self._downstream_status = WAIT_STATUS_INIT
|
||||
self._remote_address = None
|
||||
if 'forbidden_ip' in config:
|
||||
self._forbidden_iplist = config['forbidden_ip']
|
||||
else:
|
||||
self._forbidden_iplist = None
|
||||
if 'forbidden_port' in config:
|
||||
self._forbidden_portset = config['forbidden_port']
|
||||
else:
|
||||
self._forbidden_portset = None
|
||||
|
||||
self._forbidden_iplist = config.get('forbidden_ip', None)
|
||||
self._forbidden_portset = config.get('forbidden_port', None)
|
||||
if is_local:
|
||||
self._chosen_server = self._get_a_server()
|
||||
fd_to_handlers[local_sock.fileno()] = self
|
||||
local_sock.setblocking(False)
|
||||
local_sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
|
||||
loop.add(local_sock, eventloop.POLL_IN | eventloop.POLL_ERR,
|
||||
self._server)
|
||||
|
||||
self.last_activity = 0
|
||||
self._update_activity()
|
||||
self._server.add_connection(1)
|
||||
self._server.stat_add(self._client_address[0], 1)
|
||||
self._add_ref = 1
|
||||
self.speed_tester_u = SpeedTester(config.get("speed_limit_per_con", 0))
|
||||
self.speed_tester_d = SpeedTester(config.get("speed_limit_per_con", 0))
|
||||
self._recv_u_max_size = BUF_SIZE
|
||||
self._recv_d_max_size = BUF_SIZE
|
||||
self._recv_pack_id = 0
|
||||
self._udp_send_pack_id = 0
|
||||
self._udpv6_send_pack_id = 0
|
||||
|
||||
local_sock.setblocking(False)
|
||||
local_sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
|
||||
self._local_sock_fd = local_sock.fileno()
|
||||
fd_to_handlers[self._local_sock_fd] = self
|
||||
loop.add(local_sock, eventloop.POLL_IN | eventloop.POLL_ERR, self._server)
|
||||
self._stage = STAGE_INIT
|
||||
|
||||
def __hash__(self):
|
||||
# default __hash__ is id / 16
|
||||
@@ -203,6 +253,34 @@ class TCPRelayHandler(object):
|
||||
logging.debug('chosen server: %s:%d', server, server_port)
|
||||
return server, server_port
|
||||
|
||||
def _update_tcp_mss(self, local_sock):
|
||||
self._tcp_mss = TCP_MSS
|
||||
try:
|
||||
tcp_mss = local_sock.getsockopt(socket.SOL_TCP, socket.TCP_MAXSEG)
|
||||
if tcp_mss > 500 and tcp_mss <= 1500:
|
||||
self._tcp_mss = tcp_mss
|
||||
logging.debug("TCP MSS = %d" % (self._tcp_mss,))
|
||||
except:
|
||||
pass
|
||||
|
||||
def _create_encryptor(self, config):
|
||||
try:
|
||||
self._encryptor = encrypt.Encryptor(config['password'],
|
||||
config['method'])
|
||||
return True
|
||||
except Exception:
|
||||
self._stage = STAGE_DESTROYED
|
||||
logging.error('create encryptor fail at port %d', self._server._listen_port)
|
||||
|
||||
def _update_user(self, user):
|
||||
self._user = user
|
||||
self._user_id = struct.unpack('<I', user)[0]
|
||||
if self._user in self._server.server_users_cfg:
|
||||
cfg = self._server.server_users_cfg[self._user]
|
||||
speed = cfg.get('speed_limit_per_con', 0)
|
||||
self.speed_tester_u.update_limit(speed)
|
||||
self.speed_tester_d.update_limit(speed)
|
||||
|
||||
def _update_activity(self, data_len=0):
|
||||
# tell the TCP Relay we have activities recently
|
||||
# else it will think we are inactive and timed out
|
||||
@@ -246,7 +324,6 @@ class TCPRelayHandler(object):
|
||||
# and update the stream to wait for writing
|
||||
if not sock:
|
||||
return False
|
||||
#logging.debug("_write_to_sock %s %s %s" % (self._remote_sock, sock, self._remote_udp))
|
||||
uncomplete = False
|
||||
if self._remote_udp and sock == self._remote_sock:
|
||||
try:
|
||||
@@ -254,15 +331,11 @@ class TCPRelayHandler(object):
|
||||
#logging.info('UDP over TCP sendto %d %s' % (len(data), binascii.hexlify(data)))
|
||||
while len(self._udp_data_send_buffer) > 6:
|
||||
length = struct.unpack('>H', self._udp_data_send_buffer[:2])[0]
|
||||
if length >= 0xff00:
|
||||
length = struct.unpack('>H', self._udp_data_send_buffer[1:3])[0] + 0xff00
|
||||
|
||||
if length > len(self._udp_data_send_buffer):
|
||||
break
|
||||
|
||||
data = self._udp_data_send_buffer[:length]
|
||||
if length >= 0xff00:
|
||||
data = data[1:]
|
||||
self._udp_data_send_buffer = self._udp_data_send_buffer[length:]
|
||||
|
||||
frag = common.ord(data[2])
|
||||
@@ -274,17 +347,16 @@ class TCPRelayHandler(object):
|
||||
header_result = parse_header(data)
|
||||
if header_result is None:
|
||||
continue
|
||||
connecttype, dest_addr, dest_port, header_length = header_result
|
||||
addrs = socket.getaddrinfo(dest_addr, dest_port, 0,
|
||||
socket.SOCK_DGRAM, socket.SOL_UDP)
|
||||
#logging.info('UDP over TCP sendto %s:%d %d bytes from %s:%d' % (dest_addr, dest_port, len(data), self._client_address[0], self._client_address[1]))
|
||||
if addrs:
|
||||
af, socktype, proto, canonname, server_addr = addrs[0]
|
||||
data = data[header_length:]
|
||||
if af == socket.AF_INET6:
|
||||
self._remote_sock_v6.sendto(data, (server_addr[0], dest_port))
|
||||
connecttype, addrtype, dest_addr, dest_port, header_length = header_result
|
||||
if (addrtype & 7) == 3:
|
||||
af = common.is_ip(dest_addr)
|
||||
if af == False:
|
||||
handler = common.UDPAsyncDNSHandler(data[header_length:])
|
||||
handler.resolve(self._dns_resolver, (dest_addr, dest_port), self._handle_server_dns_resolved)
|
||||
else:
|
||||
sock.sendto(data, (server_addr[0], dest_port))
|
||||
return self._handle_server_dns_resolved("", (dest_addr, dest_port), dest_addr, data[header_length:])
|
||||
else:
|
||||
return self._handle_server_dns_resolved("", (dest_addr, dest_port), dest_addr, data[header_length:])
|
||||
|
||||
except Exception as e:
|
||||
#trace = traceback.format_exc()
|
||||
@@ -303,8 +375,8 @@ class TCPRelayHandler(object):
|
||||
try:
|
||||
if self._encrypt_correct:
|
||||
if sock == self._remote_sock:
|
||||
self._server.server_transfer_ul += len(data)
|
||||
self._update_activity(len(data))
|
||||
self._server.add_transfer_u(self._user, len(data))
|
||||
self._update_activity(len(data))
|
||||
if data:
|
||||
l = len(data)
|
||||
s = sock.send(data)
|
||||
@@ -347,6 +419,33 @@ class TCPRelayHandler(object):
|
||||
logging.error('write_all_to_sock:unknown socket from %s:%d' % (self._client_address[0], self._client_address[1]))
|
||||
return True
|
||||
|
||||
def _handle_server_dns_resolved(self, error, remote_addr, server_addr, data):
|
||||
if error:
|
||||
return
|
||||
try:
|
||||
addrs = socket.getaddrinfo(server_addr, remote_addr[1], 0, socket.SOCK_DGRAM, socket.SOL_UDP)
|
||||
if not addrs: # drop
|
||||
return
|
||||
af, socktype, proto, canonname, sa = addrs[0]
|
||||
if af == socket.AF_INET6:
|
||||
self._remote_sock_v6.sendto(data, (server_addr, remote_addr[1]))
|
||||
if self._udpv6_send_pack_id == 0:
|
||||
addr, port = self._remote_sock_v6.getsockname()[:2]
|
||||
common.connect_log('UDPv6 sendto %s(%s):%d from %s:%d by user %d' %
|
||||
(common.to_str(remote_addr[0]), common.to_str(server_addr), remote_addr[1], addr, port, self._user_id))
|
||||
self._udpv6_send_pack_id += 1
|
||||
else:
|
||||
self._remote_sock.sendto(data, (server_addr, remote_addr[1]))
|
||||
if self._udp_send_pack_id == 0:
|
||||
addr, port = self._remote_sock.getsockname()[:2]
|
||||
common.connect_log('UDP sendto %s(%s):%d from %s:%d by user %d' %
|
||||
(common.to_str(remote_addr[0]), common.to_str(server_addr), remote_addr[1], addr, port, self._user_id))
|
||||
self._udp_send_pack_id += 1
|
||||
return True
|
||||
except Exception as e:
|
||||
shell.print_exception(e)
|
||||
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
|
||||
|
||||
def _get_redirect_host(self, client_address, ogn_data):
|
||||
host_list = self._redir_list or ["*#0.0.0.0:0"]
|
||||
|
||||
@@ -396,18 +495,16 @@ class TCPRelayHandler(object):
|
||||
items_sum = common.to_str(host).rsplit('#', 1)
|
||||
items_match = common.to_str(items_sum[0]).rsplit(':', 1)
|
||||
items = common.to_str(items_sum[1]).rsplit(':', 1)
|
||||
if len(items_match) > 1:
|
||||
if self._server._listen_port != int(items_match[1]):
|
||||
continue
|
||||
match_port = 0
|
||||
if len(items_match) > 1:
|
||||
if items_match[1] != "*":
|
||||
try:
|
||||
match_port = int(items_match[1])
|
||||
if self._server._listen_port != int(items_match[1]) and int(items_match[1]) != 0:
|
||||
continue
|
||||
except:
|
||||
pass
|
||||
if items_match[0] != "*" and common.match_regex(items_match[0], ogn_data) == False and \
|
||||
not (match_port == self._server._listen_port or match_port == 0):
|
||||
|
||||
if items_match[0] != "*" and common.match_regex(
|
||||
items_match[0], ogn_data) == False:
|
||||
continue
|
||||
if len(items) > 1:
|
||||
try:
|
||||
@@ -421,7 +518,7 @@ class TCPRelayHandler(object):
|
||||
return ("0.0.0.0", 0)
|
||||
|
||||
def _handel_protocol_error(self, client_address, ogn_data):
|
||||
logging.warn("Protocol ERROR, TCP ogn data %s from %s:%d via port %d" % (binascii.hexlify(ogn_data), client_address[0], client_address[1], self._server._listen_port))
|
||||
logging.warn("Protocol ERROR, TCP ogn data %s from %s:%d via port %d by UID %d" % (binascii.hexlify(ogn_data), client_address[0], client_address[1], self._server._listen_port, self._user_id))
|
||||
self._encrypt_correct = False
|
||||
#create redirect or disconnect by hash code
|
||||
host, port = self._get_redirect_host(client_address, ogn_data)
|
||||
@@ -510,7 +607,7 @@ class TCPRelayHandler(object):
|
||||
# just trim VER CMD RSV
|
||||
data = data[3:]
|
||||
else:
|
||||
logging.error('unknown command %d', cmd)
|
||||
logging.error('invalid command %d', cmd)
|
||||
self.destroy()
|
||||
return
|
||||
|
||||
@@ -524,16 +621,26 @@ class TCPRelayHandler(object):
|
||||
header_result = parse_header(data)
|
||||
if header_result is not None:
|
||||
try:
|
||||
common.to_str(header_result[1])
|
||||
common.to_str(header_result[2])
|
||||
except Exception as e:
|
||||
header_result = None
|
||||
if header_result is None:
|
||||
data = self._handel_protocol_error(self._client_address, ogn_data)
|
||||
header_result = parse_header(data)
|
||||
connecttype, remote_addr, remote_port, header_length = header_result
|
||||
common.connect_log('%s connecting %s:%d via port %d' %
|
||||
((connecttype == 0) and 'TCP' or 'UDP',
|
||||
common.to_str(remote_addr), remote_port, self._server._listen_port))
|
||||
self._overhead = self._obfs.get_overhead(self._is_local) + self._protocol.get_overhead(self._is_local)
|
||||
self._recv_buffer_size = BUF_SIZE - self._overhead
|
||||
server_info = self._obfs.get_server_info()
|
||||
server_info.buffer_size = self._recv_buffer_size
|
||||
server_info = self._protocol.get_server_info()
|
||||
server_info.buffer_size = self._recv_buffer_size
|
||||
connecttype, addrtype, remote_addr, remote_port, header_length = header_result
|
||||
if connecttype != 0:
|
||||
pass
|
||||
#common.connect_log('UDP over TCP by user %d' %
|
||||
# (self._user_id, ))
|
||||
else:
|
||||
common.connect_log('TCP request %s:%d by user %d' %
|
||||
(common.to_str(remote_addr), remote_port, self._user_id))
|
||||
self._remote_address = (common.to_str(remote_addr), remote_port)
|
||||
self._remote_udp = (connecttype != 0)
|
||||
# pause reading
|
||||
@@ -602,25 +709,27 @@ class TCPRelayHandler(object):
|
||||
if self._forbidden_iplist:
|
||||
if common.to_str(sa[0]) in self._forbidden_iplist:
|
||||
if self._remote_address:
|
||||
raise Exception('IP %s is in forbidden list, when connect to %s:%d via port %d' %
|
||||
(common.to_str(sa[0]), self._remote_address[0], self._remote_address[1], self._server._listen_port))
|
||||
raise Exception('IP %s is in forbidden list, when connect to %s:%d via port %d by UID %d' %
|
||||
(common.to_str(sa[0]), self._remote_address[0], self._remote_address[1], self._server._listen_port, self._user_id))
|
||||
raise Exception('IP %s is in forbidden list, reject' %
|
||||
common.to_str(sa[0]))
|
||||
if self._forbidden_portset:
|
||||
if sa[1] in self._forbidden_portset:
|
||||
if self._remote_address:
|
||||
raise Exception('Port %d is in forbidden list, when connect to %s:%d via port %d' %
|
||||
(sa[1], self._remote_address[0], self._remote_address[1], self._server._listen_port))
|
||||
raise Exception('Port %d is in forbidden list, when connect to %s:%d via port %d by UID %d' %
|
||||
(sa[1], self._remote_address[0], self._remote_address[1], self._server._listen_port, self._user_id))
|
||||
raise Exception('Port %d is in forbidden list, reject' % sa[1])
|
||||
remote_sock = socket.socket(af, socktype, proto)
|
||||
self._remote_sock = remote_sock
|
||||
self._fd_to_handlers[remote_sock.fileno()] = self
|
||||
self._remote_sock_fd = remote_sock.fileno()
|
||||
self._fd_to_handlers[self._remote_sock_fd] = self
|
||||
|
||||
if self._remote_udp:
|
||||
af, socktype, proto, canonname, sa = addrs_v6[0]
|
||||
remote_sock_v6 = socket.socket(af, socktype, proto)
|
||||
self._remote_sock_v6 = remote_sock_v6
|
||||
self._fd_to_handlers[remote_sock_v6.fileno()] = self
|
||||
self._remotev6_sock_fd = remote_sock_v6.fileno()
|
||||
self._fd_to_handlers[self._remotev6_sock_fd] = self
|
||||
|
||||
remote_sock.setblocking(False)
|
||||
if self._remote_udp:
|
||||
@@ -643,7 +752,6 @@ class TCPRelayHandler(object):
|
||||
if result:
|
||||
ip = result[1]
|
||||
if ip:
|
||||
|
||||
try:
|
||||
self._stage = STAGE_CONNECTING
|
||||
remote_addr = ip
|
||||
@@ -681,6 +789,10 @@ class TCPRelayHandler(object):
|
||||
pass # always goto here
|
||||
else:
|
||||
raise e
|
||||
addr, port = self._remote_sock.getsockname()[:2]
|
||||
common.connect_log('TCP connecting %s(%s):%d from %s:%d by user %d' %
|
||||
(common.to_str(self._remote_address[0]), common.to_str(remote_addr), remote_port, addr, port, self._user_id))
|
||||
|
||||
self._loop.add(remote_sock,
|
||||
eventloop.POLL_ERR | eventloop.POLL_OUT,
|
||||
self._server)
|
||||
@@ -700,15 +812,36 @@ class TCPRelayHandler(object):
|
||||
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
|
||||
self.destroy()
|
||||
|
||||
def _get_read_size(self, sock, recv_buffer_size, up):
|
||||
if self._overhead == 0:
|
||||
return recv_buffer_size
|
||||
buffer_size = len(sock.recv(recv_buffer_size, socket.MSG_PEEK))
|
||||
frame_size = self._tcp_mss - self._overhead
|
||||
if up:
|
||||
buffer_size = min(buffer_size, self._recv_u_max_size)
|
||||
self._recv_u_max_size = min(self._recv_u_max_size + frame_size, BUF_SIZE)
|
||||
else:
|
||||
buffer_size = min(buffer_size, self._recv_d_max_size)
|
||||
self._recv_d_max_size = min(self._recv_d_max_size + frame_size, BUF_SIZE)
|
||||
if buffer_size == recv_buffer_size:
|
||||
return buffer_size
|
||||
if buffer_size > frame_size:
|
||||
buffer_size = int(buffer_size / frame_size) * frame_size
|
||||
return buffer_size
|
||||
|
||||
def _on_local_read(self):
|
||||
# handle all local read events and dispatch them to methods for
|
||||
# each stage
|
||||
if not self._local_sock:
|
||||
return
|
||||
is_local = self._is_local
|
||||
if is_local:
|
||||
recv_buffer_size = self._get_read_size(self._local_sock, self._recv_buffer_size, True)
|
||||
else:
|
||||
recv_buffer_size = BUF_SIZE
|
||||
data = None
|
||||
try:
|
||||
data = self._local_sock.recv(BUF_SIZE)
|
||||
data = self._local_sock.recv(recv_buffer_size)
|
||||
except (OSError, IOError) as e:
|
||||
if eventloop.errno_from_exception(e) in \
|
||||
(errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK):
|
||||
@@ -716,12 +849,19 @@ class TCPRelayHandler(object):
|
||||
if not data:
|
||||
self.destroy()
|
||||
return
|
||||
|
||||
self.speed_tester_u.add(len(data))
|
||||
self._server.speed_tester_u(self._user_id).add(len(data))
|
||||
ogn_data = data
|
||||
if not is_local:
|
||||
if self._encryptor is not None:
|
||||
if self._encrypt_correct:
|
||||
try:
|
||||
obfs_decode = self._obfs.server_decode(data)
|
||||
if self._stage == STAGE_INIT:
|
||||
self._overhead = self._obfs.get_overhead(self._is_local) + self._protocol.get_overhead(self._is_local)
|
||||
server_info = self._protocol.get_server_info()
|
||||
server_info.overhead = self._overhead
|
||||
except Exception as e:
|
||||
shell.print_exception(e)
|
||||
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
|
||||
@@ -729,7 +869,15 @@ class TCPRelayHandler(object):
|
||||
return
|
||||
if obfs_decode[2]:
|
||||
data = self._obfs.server_encode(b'')
|
||||
self._write_to_sock(data, self._local_sock)
|
||||
try:
|
||||
self._write_to_sock(data, self._local_sock)
|
||||
except Exception as e:
|
||||
shell.print_exception(e)
|
||||
if self._config['verbose']:
|
||||
traceback.print_exc()
|
||||
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
|
||||
self.destroy()
|
||||
return
|
||||
if obfs_decode[1]:
|
||||
if not self._protocol.obfs.server_info.recv_iv:
|
||||
iv_len = len(self._protocol.obfs.server_info.iv)
|
||||
@@ -756,6 +904,7 @@ class TCPRelayHandler(object):
|
||||
shell.print_exception(e)
|
||||
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
|
||||
self.destroy()
|
||||
return
|
||||
else:
|
||||
return
|
||||
if not data:
|
||||
@@ -767,12 +916,10 @@ class TCPRelayHandler(object):
|
||||
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:
|
||||
# TODO check auth method
|
||||
self._write_to_sock(b'\x05\00', self._local_sock)
|
||||
self._stage = STAGE_ADDR
|
||||
return
|
||||
elif self._stage == STAGE_CONNECTING:
|
||||
self._handle_stage_connecting(data)
|
||||
elif (is_local and self._stage == STAGE_ADDR) or \
|
||||
@@ -796,13 +943,15 @@ class TCPRelayHandler(object):
|
||||
ip = socket.inet_pton(socket.AF_INET6, addr[0])
|
||||
data = b'\x00\x04' + ip + port + data
|
||||
size = len(data) + 2
|
||||
if size >= 0xff00:
|
||||
data = common.chr(0xff) + struct.pack('>H', size - 0xff00 + 1) + data
|
||||
else:
|
||||
data = struct.pack('>H', size) + data
|
||||
data = struct.pack('>H', size) + 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:
|
||||
data = self._remote_sock.recv(BUF_SIZE)
|
||||
if self._is_local:
|
||||
recv_buffer_size = BUF_SIZE
|
||||
else:
|
||||
recv_buffer_size = self._get_read_size(self._remote_sock, self._recv_buffer_size, False)
|
||||
data = self._remote_sock.recv(recv_buffer_size)
|
||||
self._recv_pack_id += 1
|
||||
except (OSError, IOError) as e:
|
||||
if eventloop.errno_from_exception(e) in \
|
||||
(errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK, 10035): #errno.WSAEWOULDBLOCK
|
||||
@@ -810,6 +959,9 @@ class TCPRelayHandler(object):
|
||||
if not data:
|
||||
self.destroy()
|
||||
return
|
||||
|
||||
self.speed_tester_d.add(len(data))
|
||||
self._server.speed_tester_d(self._user_id).add(len(data))
|
||||
if self._encryptor is not None:
|
||||
if self._is_local:
|
||||
try:
|
||||
@@ -828,6 +980,8 @@ class TCPRelayHandler(object):
|
||||
data = self._encryptor.decrypt(obfs_decode[0])
|
||||
try:
|
||||
data = self._protocol.client_post_decrypt(data)
|
||||
if self._recv_pack_id == 1:
|
||||
self._tcp_mss = self._protocol.get_server_info().tcp_mss
|
||||
except Exception as e:
|
||||
shell.print_exception(e)
|
||||
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
|
||||
@@ -838,8 +992,8 @@ class TCPRelayHandler(object):
|
||||
data = self._protocol.server_pre_encrypt(data)
|
||||
data = self._encryptor.encrypt(data)
|
||||
data = self._obfs.server_encode(data)
|
||||
self._update_activity(len(data))
|
||||
self._server.server_transfer_dl += len(data)
|
||||
self._server.add_transfer_d(self._user, len(data))
|
||||
self._update_activity(len(data))
|
||||
else:
|
||||
return
|
||||
try:
|
||||
@@ -871,52 +1025,72 @@ class TCPRelayHandler(object):
|
||||
self._update_stream(STREAM_UP, WAIT_STATUS_READING)
|
||||
|
||||
def _on_local_error(self):
|
||||
logging.debug('got local error')
|
||||
if self._local_sock:
|
||||
logging.error(eventloop.get_sock_error(self._local_sock))
|
||||
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
|
||||
err = eventloop.get_sock_error(self._local_sock)
|
||||
if err.errno not in [errno.ECONNRESET, errno.EPIPE]:
|
||||
logging.error(err)
|
||||
logging.error("local error, exception from %s:%d" % (self._client_address[0], self._client_address[1]))
|
||||
self.destroy()
|
||||
|
||||
def _on_remote_error(self):
|
||||
logging.debug('got remote error')
|
||||
if self._remote_sock:
|
||||
logging.error(eventloop.get_sock_error(self._remote_sock))
|
||||
if self._remote_address:
|
||||
logging.error("when connect to %s:%d" % (self._remote_address[0], self._remote_address[1]))
|
||||
else:
|
||||
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
|
||||
err = eventloop.get_sock_error(self._remote_sock)
|
||||
if err.errno not in [errno.ECONNRESET]:
|
||||
logging.error(err)
|
||||
if self._remote_address:
|
||||
logging.error("remote error, when connect to %s:%d" % (self._remote_address[0], self._remote_address[1]))
|
||||
else:
|
||||
logging.error("remote error, exception from %s:%d" % (self._client_address[0], self._client_address[1]))
|
||||
self.destroy()
|
||||
|
||||
def handle_event(self, sock, event):
|
||||
def handle_event(self, sock, fd, event):
|
||||
# handle all events in this handler and dispatch them to methods
|
||||
handle = False
|
||||
if self._stage == STAGE_DESTROYED:
|
||||
logging.debug('ignore handle_event: destroyed')
|
||||
return
|
||||
# order is important
|
||||
if sock == self._remote_sock or sock == self._remote_sock_v6:
|
||||
return True
|
||||
if self._user is not None and self._user not in self._server.server_users:
|
||||
self.destroy()
|
||||
return True
|
||||
if fd == self._remote_sock_fd or fd == self._remotev6_sock_fd:
|
||||
if event & eventloop.POLL_ERR:
|
||||
handle = True
|
||||
self._on_remote_error()
|
||||
if self._stage == STAGE_DESTROYED:
|
||||
return
|
||||
if event & (eventloop.POLL_IN | eventloop.POLL_HUP):
|
||||
self._on_remote_read(sock == self._remote_sock)
|
||||
if self._stage == STAGE_DESTROYED:
|
||||
return
|
||||
if event & eventloop.POLL_OUT:
|
||||
elif event & (eventloop.POLL_IN | eventloop.POLL_HUP):
|
||||
if not self.speed_tester_d.isExceed() and not self._server.speed_tester_d(self._user_id).isExceed():
|
||||
handle = True
|
||||
self._on_remote_read(sock == self._remote_sock)
|
||||
else:
|
||||
self._recv_d_max_size = self._tcp_mss - self._overhead
|
||||
elif event & eventloop.POLL_OUT:
|
||||
handle = True
|
||||
self._on_remote_write()
|
||||
elif sock == self._local_sock:
|
||||
elif fd == self._local_sock_fd:
|
||||
if event & eventloop.POLL_ERR:
|
||||
handle = True
|
||||
self._on_local_error()
|
||||
if self._stage == STAGE_DESTROYED:
|
||||
return
|
||||
if event & (eventloop.POLL_IN | eventloop.POLL_HUP):
|
||||
self._on_local_read()
|
||||
if self._stage == STAGE_DESTROYED:
|
||||
return
|
||||
if event & eventloop.POLL_OUT:
|
||||
elif event & (eventloop.POLL_IN | eventloop.POLL_HUP):
|
||||
if not self.speed_tester_u.isExceed() and not self._server.speed_tester_u(self._user_id).isExceed():
|
||||
handle = True
|
||||
self._on_local_read()
|
||||
else:
|
||||
self._recv_u_max_size = self._tcp_mss - self._overhead
|
||||
elif event & eventloop.POLL_OUT:
|
||||
handle = True
|
||||
self._on_local_write()
|
||||
else:
|
||||
logging.warn('unknown socket from %s:%d' % (self._client_address[0], self._client_address[1]))
|
||||
try:
|
||||
self._loop.removefd(fd)
|
||||
except Exception as e:
|
||||
shell.print_exception(e)
|
||||
try:
|
||||
del self._fd_to_handlers[fd]
|
||||
except Exception as e:
|
||||
shell.print_exception(e)
|
||||
sock.close()
|
||||
|
||||
return handle
|
||||
|
||||
def _log_error(self, e):
|
||||
logging.error('%s when handling connection from %s:%d' %
|
||||
@@ -946,25 +1120,40 @@ class TCPRelayHandler(object):
|
||||
if self._remote_sock:
|
||||
logging.debug('destroying remote')
|
||||
try:
|
||||
self._loop.remove(self._remote_sock)
|
||||
self._loop.removefd(self._remote_sock_fd)
|
||||
except Exception as e:
|
||||
pass
|
||||
del self._fd_to_handlers[self._remote_sock.fileno()]
|
||||
shell.print_exception(e)
|
||||
try:
|
||||
if self._remote_sock_fd is not None:
|
||||
del self._fd_to_handlers[self._remote_sock_fd]
|
||||
except Exception as e:
|
||||
shell.print_exception(e)
|
||||
self._remote_sock.close()
|
||||
self._remote_sock = None
|
||||
if self._remote_sock_v6:
|
||||
logging.debug('destroying remote')
|
||||
logging.debug('destroying remote_v6')
|
||||
try:
|
||||
self._loop.remove(self._remote_sock_v6)
|
||||
self._loop.removefd(self._remotev6_sock_fd)
|
||||
except Exception as e:
|
||||
pass
|
||||
del self._fd_to_handlers[self._remote_sock_v6.fileno()]
|
||||
shell.print_exception(e)
|
||||
try:
|
||||
if self._remotev6_sock_fd is not None:
|
||||
del self._fd_to_handlers[self._remotev6_sock_fd]
|
||||
except Exception as e:
|
||||
shell.print_exception(e)
|
||||
self._remote_sock_v6.close()
|
||||
self._remote_sock_v6 = None
|
||||
if self._local_sock:
|
||||
logging.debug('destroying local')
|
||||
self._loop.remove(self._local_sock)
|
||||
del self._fd_to_handlers[self._local_sock.fileno()]
|
||||
try:
|
||||
self._loop.removefd(self._local_sock_fd)
|
||||
except Exception as e:
|
||||
shell.print_exception(e)
|
||||
try:
|
||||
if self._local_sock_fd is not None:
|
||||
del self._fd_to_handlers[self._local_sock_fd]
|
||||
except Exception as e:
|
||||
shell.print_exception(e)
|
||||
self._local_sock.close()
|
||||
self._local_sock = None
|
||||
if self._obfs:
|
||||
@@ -976,8 +1165,9 @@ class TCPRelayHandler(object):
|
||||
self._encryptor = None
|
||||
self._dns_resolver.remove_callback(self._handle_dns_resolved)
|
||||
self._server.remove_handler(self)
|
||||
self._server.add_connection(-1)
|
||||
self._server.stat_add(self._client_address[0], -1)
|
||||
if self._add_ref > 0:
|
||||
self._server.add_connection(-1)
|
||||
self._server.stat_add(self._client_address[0], -1)
|
||||
|
||||
class TCPRelay(object):
|
||||
def __init__(self, config, dns_resolver, is_local, stat_callback=None, stat_counter=None):
|
||||
@@ -989,6 +1179,13 @@ class TCPRelay(object):
|
||||
self._fd_to_handlers = {}
|
||||
self.server_transfer_ul = 0
|
||||
self.server_transfer_dl = 0
|
||||
self.server_users = {}
|
||||
self.server_users_cfg = {}
|
||||
self.server_user_transfer_ul = {}
|
||||
self.server_user_transfer_dl = {}
|
||||
self.mu = False
|
||||
self._speed_tester_u = {}
|
||||
self._speed_tester_d = {}
|
||||
self.server_connections = 0
|
||||
self.protocol_data = obfs.obfs(config['protocol']).init_data()
|
||||
self.obfs_data = obfs.obfs(config['obfs']).init_data()
|
||||
@@ -1008,6 +1205,9 @@ class TCPRelay(object):
|
||||
listen_port = config['server_port']
|
||||
self._listen_port = listen_port
|
||||
|
||||
if common.to_str(config['protocol']) in obfs.mu_protocol():
|
||||
self._update_users(None, None)
|
||||
|
||||
addrs = socket.getaddrinfo(listen_addr, listen_port, 0,
|
||||
socket.SOCK_STREAM, socket.SOL_TCP)
|
||||
if len(addrs) == 0:
|
||||
@@ -1026,6 +1226,7 @@ class TCPRelay(object):
|
||||
self._config['fast_open'] = False
|
||||
server_socket.listen(config.get('max_connect', 1024))
|
||||
self._server_socket = server_socket
|
||||
self._server_socket_fd = server_socket.fileno()
|
||||
self._stat_counter = stat_counter
|
||||
self._stat_callback = stat_callback
|
||||
|
||||
@@ -1047,6 +1248,104 @@ class TCPRelay(object):
|
||||
self.server_connections += val
|
||||
logging.debug('server port %5d connections = %d' % (self._listen_port, self.server_connections,))
|
||||
|
||||
def get_ud(self):
|
||||
return (self.server_transfer_ul, self.server_transfer_dl)
|
||||
|
||||
def get_users_ud(self):
|
||||
return (self.server_user_transfer_ul.copy(), self.server_user_transfer_dl.copy())
|
||||
|
||||
def _update_users(self, protocol_param, acl):
|
||||
if protocol_param is None:
|
||||
protocol_param = self._config['protocol_param']
|
||||
param = common.to_bytes(protocol_param).split(b'#')
|
||||
if len(param) == 2:
|
||||
self.mu = True
|
||||
user_list = param[1].split(b',')
|
||||
if user_list:
|
||||
for user in user_list:
|
||||
items = user.split(b':')
|
||||
if len(items) == 2:
|
||||
user_int_id = int(items[0])
|
||||
uid = struct.pack('<I', user_int_id)
|
||||
if acl is not None and user_int_id not in acl:
|
||||
self.del_user(uid)
|
||||
else:
|
||||
passwd = items[1]
|
||||
self.add_user(uid, {'password':passwd})
|
||||
|
||||
def _update_user(self, id, passwd):
|
||||
uid = struct.pack('<I', id)
|
||||
self.add_user(uid, passwd)
|
||||
|
||||
def update_users(self, users):
|
||||
for uid in list(self.server_users.keys()):
|
||||
id = struct.unpack('<I', uid)[0]
|
||||
if id not in users:
|
||||
self.del_user(uid)
|
||||
for id in users:
|
||||
uid = struct.pack('<I', id)
|
||||
self.add_user(uid, users[id])
|
||||
|
||||
def add_user(self, uid, cfg): # user: binstr[4], passwd: str
|
||||
passwd = cfg['password']
|
||||
self.server_users[uid] = common.to_bytes(passwd)
|
||||
self.server_users_cfg[uid] = cfg
|
||||
speed = cfg.get("speed_limit_per_user", 0)
|
||||
if uid in self._speed_tester_u:
|
||||
self._speed_tester_u[uid].update_limit(speed)
|
||||
else:
|
||||
self._speed_tester_u[uid] = SpeedTester(speed)
|
||||
if uid in self._speed_tester_d:
|
||||
self._speed_tester_d[uid].update_limit(speed)
|
||||
else:
|
||||
self._speed_tester_d[uid] = SpeedTester(speed)
|
||||
|
||||
def del_user(self, uid):
|
||||
if uid in self.server_users:
|
||||
del self.server_users[uid]
|
||||
if uid in self.server_users_cfg:
|
||||
del self.server_users_cfg[uid]
|
||||
|
||||
def add_transfer_u(self, user, transfer):
|
||||
if user is None:
|
||||
self.server_transfer_ul += transfer
|
||||
else:
|
||||
if user not in self.server_user_transfer_ul:
|
||||
self.server_user_transfer_ul[user] = 0
|
||||
self.server_user_transfer_ul[user] += transfer + self.server_transfer_ul
|
||||
self.server_transfer_ul = 0
|
||||
|
||||
def add_transfer_d(self, user, transfer):
|
||||
if user is None:
|
||||
self.server_transfer_dl += transfer
|
||||
else:
|
||||
if user not in self.server_user_transfer_dl:
|
||||
self.server_user_transfer_dl[user] = 0
|
||||
self.server_user_transfer_dl[user] += transfer + self.server_transfer_dl
|
||||
self.server_transfer_dl = 0
|
||||
|
||||
def speed_tester_u(self, uid):
|
||||
if uid not in self._speed_tester_u:
|
||||
if self.mu: #TODO
|
||||
self._speed_tester_u[uid] = SpeedTester(self._config.get("speed_limit_per_user", 0))
|
||||
else:
|
||||
self._speed_tester_u[uid] = SpeedTester(self._config.get("speed_limit_per_user", 0))
|
||||
return self._speed_tester_u[uid]
|
||||
|
||||
def speed_tester_d(self, uid):
|
||||
if uid not in self._speed_tester_d:
|
||||
if self.mu: #TODO
|
||||
self._speed_tester_d[uid] = SpeedTester(self._config.get("speed_limit_per_user", 0))
|
||||
else:
|
||||
self._speed_tester_d[uid] = SpeedTester(self._config.get("speed_limit_per_user", 0))
|
||||
return self._speed_tester_d[uid]
|
||||
|
||||
def update_limit(self, uid, max_speed):
|
||||
if uid in self._speed_tester_u:
|
||||
self._speed_tester_u[uid].update_limit(max_speed)
|
||||
if uid in self._speed_tester_d:
|
||||
self._speed_tester_d[uid].update_limit(max_speed)
|
||||
|
||||
def update_stat(self, port, stat_dict, val):
|
||||
newval = stat_dict.get(0, 0) + val
|
||||
stat_dict[0] = newval
|
||||
@@ -1102,6 +1401,7 @@ class TCPRelay(object):
|
||||
|
||||
def handle_event(self, sock, fd, event):
|
||||
# handle events and dispatch to handlers
|
||||
handle = False
|
||||
if sock:
|
||||
logging.log(shell.VERBOSE_LEVEL, 'fd %d %s', fd,
|
||||
eventloop.EVENT_NAMES.get(event, event))
|
||||
@@ -1109,6 +1409,8 @@ class TCPRelay(object):
|
||||
if event & eventloop.POLL_ERR:
|
||||
# TODO
|
||||
raise Exception('server_socket error')
|
||||
handler = None
|
||||
handle = True
|
||||
try:
|
||||
logging.debug('accept')
|
||||
conn = self._server_socket.accept()
|
||||
@@ -1126,18 +1428,35 @@ class TCPRelay(object):
|
||||
shell.print_exception(e)
|
||||
if self._config['verbose']:
|
||||
traceback.print_exc()
|
||||
if handler:
|
||||
handler.destroy()
|
||||
else:
|
||||
if sock:
|
||||
handler = self._fd_to_handlers.get(fd, None)
|
||||
if handler:
|
||||
handler.handle_event(sock, event)
|
||||
handle = handler.handle_event(sock, fd, event)
|
||||
else:
|
||||
logging.warn('unknown fd')
|
||||
handle = True
|
||||
try:
|
||||
self._eventloop.removefd(fd)
|
||||
except Exception as e:
|
||||
shell.print_exception(e)
|
||||
sock.close()
|
||||
else:
|
||||
logging.warn('poll removed fd')
|
||||
handle = True
|
||||
if fd in self._fd_to_handlers:
|
||||
try:
|
||||
del self._fd_to_handlers[fd]
|
||||
except Exception as e:
|
||||
shell.print_exception(e)
|
||||
return handle
|
||||
|
||||
def handle_periodic(self):
|
||||
if self._closed:
|
||||
if self._server_socket:
|
||||
self._eventloop.remove(self._server_socket)
|
||||
self._eventloop.removefd(self._server_socket_fd)
|
||||
self._server_socket.close()
|
||||
self._server_socket = None
|
||||
logging.info('closed TCP port %d', self._listen_port)
|
||||
@@ -1151,7 +1470,7 @@ class TCPRelay(object):
|
||||
if not next_tick:
|
||||
if self._eventloop:
|
||||
self._eventloop.remove_periodic(self.handle_periodic)
|
||||
self._eventloop.remove(self._server_socket)
|
||||
self._eventloop.removefd(self._server_socket_fd)
|
||||
self._server_socket.close()
|
||||
for handler in list(self._fd_to_handlers.values()):
|
||||
handler.destroy()
|
||||
|
||||
+187
-925
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2016 breakwa11
|
||||
# Copyright 2017 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
|
||||
@@ -16,5 +16,5 @@
|
||||
# under the License.
|
||||
|
||||
def version():
|
||||
return '3.0.1 2017-01-03'
|
||||
return '3.4.0 2017-07-27'
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
eval $(ps -ef | grep "[0-9] python server\\.py m" | awk '{print "kill "$2}')
|
||||
python_ver=$(ls /usr/bin|grep -e "^python[23]\.[1-9]\+$"|tail -1)
|
||||
eval $(ps -ef | grep "[0-9] ${python_ver} server\\.py m" | awk '{print "kill "$2}')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user