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 |
@@ -1,3 +1,41 @@
|
||||
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
|
||||
|
||||
+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-128-ctr
|
||||
ENV PROTOCOL auth_aes128_md5
|
||||
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
|
||||
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/shadowsocksr-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
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Config
|
||||
API_INTERFACE = 'sspanelv2' #mudbjson, sspanelv2, sspanelv3, sspanelv3ssr, glzjinmod, 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
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"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,
|
||||
|
||||
+76
-61
@@ -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
|
||||
|
||||
@@ -88,6 +88,7 @@ class TransferBase(object):
|
||||
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']
|
||||
@@ -110,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
|
||||
@@ -118,12 +122,14 @@ class TransferBase(object):
|
||||
logging.error('more than one user use the same port [%s]' % (port,))
|
||||
continue
|
||||
|
||||
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:
|
||||
allow_users[port] = passwd
|
||||
if 'protocol' in cfg and 'protocol_param' in cfg and common.to_str(cfg['protocol']) in ['auth_aes128_md5', 'auth_aes128_sha1']:
|
||||
if '#' in common.to_str(cfg['protocol_param']):
|
||||
mu_servers[port] = passwd
|
||||
del allow_users[port]
|
||||
if port not in mu_servers:
|
||||
allow_users[port] = cfg
|
||||
|
||||
cfgchange = False
|
||||
if port in ServerPool.get_instance().tcp_servers_pool:
|
||||
@@ -135,7 +141,7 @@ class TransferBase(object):
|
||||
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]):
|
||||
if (name in cfg) and ((name not in relay._config) or not self.cmp(cfg[name], relay._config[name])):
|
||||
cfgchange = True
|
||||
break
|
||||
|
||||
@@ -150,7 +156,7 @@ class TransferBase(object):
|
||||
self.new_server(port, passwd, cfg)
|
||||
else:
|
||||
if ServerPool.get_instance().server_is_run(port) > 0:
|
||||
if not allow:
|
||||
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)
|
||||
@@ -161,7 +167,7 @@ class TransferBase(object):
|
||||
self.force_update_transfer.add(port)
|
||||
new_servers[port] = (passwd, cfg)
|
||||
|
||||
elif allow and port > 0 and port < 65536 and ServerPool.get_instance().server_run_status(port) is False:
|
||||
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:
|
||||
@@ -406,13 +412,19 @@ class DbTransfer(TransferBase):
|
||||
class Dbv3Transfer(DbTransfer):
|
||||
def __init__(self):
|
||||
super(Dbv3Transfer, self).__init__()
|
||||
self.key_list += ['id', 'method']
|
||||
self.ss_node_info_name = 'ss_node_info_log'
|
||||
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']
|
||||
if get_config().API_INTERFACE == 'glzjinmod':
|
||||
self.key_list += ['obfs', 'protocol']
|
||||
self.ss_node_info_name = 'ss_node_info'
|
||||
self.start_time = time.time()
|
||||
|
||||
def update_all_user(self, dt_transfer):
|
||||
@@ -454,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
|
||||
@@ -482,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 `" + 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')
|
||||
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
|
||||
@@ -514,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)):
|
||||
|
||||
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 &
|
||||
|
||||
|
||||
+22
-24
@@ -214,11 +214,11 @@ def print_server_help():
|
||||
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
|
||||
@@ -232,6 +232,8 @@ Options:
|
||||
-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
|
||||
@@ -239,7 +241,7 @@ General options:
|
||||
|
||||
|
||||
def main():
|
||||
shortopts = 'adeclu:i: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 = {}
|
||||
@@ -249,32 +251,24 @@ def main():
|
||||
'+2': 'tls1.2_ticket_auth_compatible',
|
||||
'2': 'tls1.2_ticket_auth'}
|
||||
fast_set_protocol = {'0': 'origin',
|
||||
'+ota': 'verify_sha1_compatible',
|
||||
'ota': 'verify_sha1',
|
||||
'a1': 'auth_sha1',
|
||||
'+a1': 'auth_sha1_compatible',
|
||||
'a2': 'auth_sha1_v2',
|
||||
'+a2': 'auth_sha1_v2_compatible',
|
||||
'a4': 'auth_sha1_v4',
|
||||
'+a4': 'auth_sha1_v4_compatible',
|
||||
'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:
|
||||
@@ -310,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]
|
||||
|
||||
@@ -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 &
|
||||
|
||||
|
||||
@@ -55,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()
|
||||
|
||||
+1
-2
@@ -28,6 +28,7 @@ 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
|
||||
|
||||
@@ -80,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):
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -214,7 +218,7 @@ class EventLoop(object):
|
||||
if handler is not None:
|
||||
handler = handler[1]
|
||||
try:
|
||||
handle = handle or 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()
|
||||
|
||||
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
|
||||
|
||||
|
||||
+7
-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):
|
||||
@@ -98,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)
|
||||
|
||||
@@ -37,18 +37,9 @@ 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_sha1(method):
|
||||
return auth_sha1(method)
|
||||
|
||||
def create_auth_sha1_v2(method):
|
||||
return auth_sha1_v2(method)
|
||||
|
||||
def create_auth_sha1_v4(method):
|
||||
return auth_sha1_v4(method)
|
||||
|
||||
def create_auth_aes128(method):
|
||||
return auth_aes128(method)
|
||||
|
||||
def create_auth_aes128_md5(method):
|
||||
return auth_aes128_sha1(method, hashlib.md5)
|
||||
|
||||
@@ -56,13 +47,8 @@ def create_auth_aes128_sha1(method):
|
||||
return auth_aes128_sha1(method, hashlib.sha1)
|
||||
|
||||
obfs_map = {
|
||||
'auth_sha1': (create_auth_sha1,),
|
||||
'auth_sha1_compatible': (create_auth_sha1,),
|
||||
'auth_sha1_v2': (create_auth_sha1_v2,),
|
||||
'auth_sha1_v2_compatible': (create_auth_sha1_v2,),
|
||||
'auth_sha1_v4': (create_auth_sha1_v4,),
|
||||
'auth_sha1_v4_compatible': (create_auth_sha1_v4,),
|
||||
'auth_aes128': (create_auth_aes128,),
|
||||
'auth_aes128_md5': (create_auth_aes128_md5,),
|
||||
'auth_aes128_sha1': (create_auth_aes128_sha1,),
|
||||
}
|
||||
@@ -73,10 +59,6 @@ def match_begin(str1, str2):
|
||||
return True
|
||||
return False
|
||||
|
||||
class obfs_verify_data(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
class auth_base(plain.plain):
|
||||
def __init__(self, method):
|
||||
super(auth_base, self).__init__(method)
|
||||
@@ -157,239 +139,6 @@ class client_queue(object):
|
||||
self.front += 1
|
||||
return True
|
||||
|
||||
class obfs_auth_data(object):
|
||||
def __init__(self):
|
||||
self.client_id = {}
|
||||
self.startup_time = int(time.time() - 30) & 0xFFFFFFFF
|
||||
self.local_client_id = b''
|
||||
self.connection_id = 0
|
||||
self.set_max_client(64) # max active client count
|
||||
|
||||
def update(self, client_id, connection_id):
|
||||
if client_id in self.client_id:
|
||||
self.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, 256)
|
||||
|
||||
def insert(self, client_id, connection_id):
|
||||
if client_id not in self.client_id or not self.client_id[client_id].enable:
|
||||
active = 0
|
||||
for c_id in self.client_id:
|
||||
if self.client_id[c_id].is_active():
|
||||
active += 1
|
||||
if active >= self.max_client:
|
||||
logging.warn('obfs auth: max active clients exceeded')
|
||||
return False
|
||||
|
||||
if len(self.client_id) < self.max_client:
|
||||
if client_id not in self.client_id:
|
||||
self.client_id[client_id] = client_queue(connection_id)
|
||||
else:
|
||||
self.client_id[client_id].re_enable(connection_id)
|
||||
return self.client_id[client_id].insert(connection_id)
|
||||
keys = self.client_id.keys()
|
||||
random.shuffle(keys)
|
||||
for c_id in keys:
|
||||
if not self.client_id[c_id].is_active() and self.client_id[c_id].enable:
|
||||
if len(self.client_id) >= self.max_buffer:
|
||||
del self.client_id[c_id]
|
||||
else:
|
||||
self.client_id[c_id].enable = False
|
||||
if client_id not in self.client_id:
|
||||
self.client_id[client_id] = client_queue(connection_id)
|
||||
else:
|
||||
self.client_id[client_id].re_enable(connection_id)
|
||||
return self.client_id[client_id].insert(connection_id)
|
||||
logging.warn('obfs auth: no inactive client [assert]')
|
||||
return False
|
||||
else:
|
||||
return self.client_id[client_id].insert(connection_id)
|
||||
|
||||
class auth_sha1(auth_base):
|
||||
def __init__(self, method):
|
||||
super(auth_sha1, self).__init__(method)
|
||||
self.recv_buf = b''
|
||||
self.unit_len = 8000
|
||||
self.decrypt_packet_num = 0
|
||||
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 # time dif (second) setting
|
||||
self.no_compatible_method = 'auth_sha1'
|
||||
|
||||
def init_data(self):
|
||||
return obfs_auth_data()
|
||||
|
||||
def set_server_info(self, server_info):
|
||||
self.server_info = server_info
|
||||
try:
|
||||
max_client = int(server_info.protocol_param)
|
||||
except:
|
||||
max_client = 64
|
||||
self.server_info.data.set_max_client(max_client)
|
||||
|
||||
def pack_data(self, buf):
|
||||
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
|
||||
adler32 = zlib.adler32(data) & 0xFFFFFFFF
|
||||
data += struct.pack('<I', adler32)
|
||||
return data
|
||||
|
||||
def pack_auth_data(self, buf):
|
||||
if len(buf) == 0:
|
||||
return b''
|
||||
rnd_data = os.urandom(common.ord(os.urandom(1)[0]) % 128)
|
||||
data = common.chr(len(rnd_data) + 1) + rnd_data + buf
|
||||
data = struct.pack('>H', len(data) + 16) + data
|
||||
crc = binascii.crc32(self.server_info.key) & 0xFFFFFFFF
|
||||
data = struct.pack('<I', crc) + data
|
||||
data += hmac.new(self.server_info.iv + self.server_info.key, data, hashlib.sha1).digest()[:10]
|
||||
return data
|
||||
|
||||
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''
|
||||
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_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 struct.pack('<I', zlib.adler32(self.recv_buf[:length - 4]) & 0xFFFFFFFF) != self.recv_buf[length - 4:length]:
|
||||
self.raw_trans = True
|
||||
self.recv_buf = b''
|
||||
raise Exception('client_post_decrypt data uncorrect checksum')
|
||||
|
||||
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):
|
||||
if self.raw_trans:
|
||||
return 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''
|
||||
if not self.has_recv_header:
|
||||
if len(self.recv_buf) < 6:
|
||||
return (b'', False)
|
||||
crc = struct.pack('<I', binascii.crc32(self.server_info.key) & 0xFFFFFFFF)
|
||||
if crc != self.recv_buf[:4]:
|
||||
return self.not_match_return(self.recv_buf)
|
||||
length = struct.unpack('>H', self.recv_buf[4:6])[0]
|
||||
if length > 2048:
|
||||
return self.not_match_return(self.recv_buf)
|
||||
if length > len(self.recv_buf):
|
||||
return (b'', False)
|
||||
sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, self.recv_buf[:length - 10], hashlib.sha1).digest()[:10]
|
||||
if sha1data != self.recv_buf[length - 10:length]:
|
||||
logging.error('auth_sha1 data uncorrect auth HMAC-SHA1')
|
||||
return self.not_match_return(self.recv_buf)
|
||||
pos = common.ord(self.recv_buf[6]) + 6
|
||||
out_buf = self.recv_buf[pos:length - 10]
|
||||
if len(out_buf) < 12:
|
||||
logging.info('auth_sha1: too short, data %s' % (binascii.hexlify(self.recv_buf),))
|
||||
return self.not_match_return(self.recv_buf)
|
||||
utc_time = struct.unpack('<I', out_buf[:4])[0]
|
||||
client_id = struct.unpack('<I', out_buf[4:8])[0]
|
||||
connection_id = struct.unpack('<I', out_buf[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 \
|
||||
or common.int32(utc_time - self.server_info.data.startup_time) < -self.max_time_dif / 2:
|
||||
logging.info('auth_sha1: wrong timestamp, time_dif %d, data %s' % (time_dif, binascii.hexlify(out_buf),))
|
||||
return self.not_match_return(self.recv_buf)
|
||||
elif self.server_info.data.insert(client_id, connection_id):
|
||||
self.has_recv_header = True
|
||||
out_buf = out_buf[12:]
|
||||
self.client_id = client_id
|
||||
self.connection_id = connection_id
|
||||
else:
|
||||
logging.info('auth_sha1: auth fail, data %s' % (binascii.hexlify(out_buf),))
|
||||
return self.not_match_return(self.recv_buf)
|
||||
self.recv_buf = self.recv_buf[length:]
|
||||
self.has_recv_header = True
|
||||
|
||||
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:
|
||||
logging.info('auth_sha1: over size')
|
||||
return (b'E'*2048, False)
|
||||
else:
|
||||
raise Exception('server_post_decrype data error')
|
||||
if length > len(self.recv_buf):
|
||||
break
|
||||
|
||||
if struct.pack('<I', zlib.adler32(self.recv_buf[:length - 4]) & 0xFFFFFFFF) != self.recv_buf[length - 4:length]:
|
||||
logging.info('auth_sha1: checksum error, data %s' % (binascii.hexlify(self.recv_buf[:length]),))
|
||||
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 checksum')
|
||||
|
||||
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.server_info.data.update(self.client_id, self.connection_id)
|
||||
self.decrypt_packet_num += 1
|
||||
return (out_buf, False)
|
||||
|
||||
class obfs_auth_v2_data(object):
|
||||
def __init__(self):
|
||||
self.client_id = lru_cache.LRUCache()
|
||||
@@ -429,207 +178,6 @@ class obfs_auth_v2_data(object):
|
||||
else:
|
||||
return self.client_id[client_id].insert(connection_id)
|
||||
|
||||
class auth_sha1_v2(auth_base):
|
||||
def __init__(self, method):
|
||||
super(auth_sha1_v2, self).__init__(method)
|
||||
self.recv_buf = b''
|
||||
self.unit_len = 8100
|
||||
self.decrypt_packet_num = 0
|
||||
self.raw_trans = False
|
||||
self.has_sent_header = False
|
||||
self.has_recv_header = False
|
||||
self.client_id = 0
|
||||
self.connection_id = 0
|
||||
self.salt = b"auth_sha1_v2"
|
||||
self.no_compatible_method = 'auth_sha1_v2'
|
||||
|
||||
def init_data(self):
|
||||
return obfs_auth_v2_data()
|
||||
|
||||
def set_server_info(self, server_info):
|
||||
self.server_info = server_info
|
||||
try:
|
||||
max_client = int(server_info.protocol_param)
|
||||
except:
|
||||
max_client = 64
|
||||
self.server_info.data.set_max_client(max_client)
|
||||
|
||||
def rnd_data(self, buf_size):
|
||||
if buf_size > 1300:
|
||||
return b'\x01'
|
||||
|
||||
if buf_size > 400:
|
||||
rnd_data = os.urandom(common.ord(os.urandom(1)[0]) % 128)
|
||||
return common.chr(len(rnd_data) + 1) + rnd_data
|
||||
|
||||
rnd_data = os.urandom(struct.unpack('>H', os.urandom(2))[0] % 1024)
|
||||
return common.chr(255) + struct.pack('>H', len(rnd_data) + 3) + rnd_data
|
||||
|
||||
def pack_data(self, buf):
|
||||
data = self.rnd_data(len(buf)) + buf
|
||||
data = struct.pack('>H', len(data) + 6) + data
|
||||
adler32 = zlib.adler32(data) & 0xFFFFFFFF
|
||||
data += struct.pack('<I', adler32)
|
||||
return data
|
||||
|
||||
def pack_auth_data(self, buf):
|
||||
if len(buf) == 0:
|
||||
return b''
|
||||
data = self.rnd_data(len(buf)) + buf
|
||||
data = struct.pack('>H', len(data) + 16) + data
|
||||
crc = binascii.crc32(self.salt + self.server_info.key) & 0xFFFFFFFF
|
||||
data = struct.pack('<I', crc) + data
|
||||
data += hmac.new(self.server_info.iv + self.server_info.key, data, hashlib.sha1).digest()[:10]
|
||||
return data
|
||||
|
||||
def auth_data(self):
|
||||
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(8)
|
||||
logging.debug("local_client_id %s" % (binascii.hexlify(self.server_info.data.local_client_id),))
|
||||
self.server_info.data.connection_id = struct.unpack('<Q', self.server_info.data.local_client_id)[0] % 0xFFFFFD
|
||||
self.server_info.data.connection_id += 1
|
||||
return b''.join([self.server_info.data.local_client_id,
|
||||
struct.pack('<I', self.server_info.data.connection_id)])
|
||||
|
||||
def client_pre_encrypt(self, buf):
|
||||
ret = b''
|
||||
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_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 struct.pack('<I', zlib.adler32(self.recv_buf[:length - 4]) & 0xFFFFFFFF) != self.recv_buf[length - 4:length]:
|
||||
self.raw_trans = True
|
||||
self.recv_buf = b''
|
||||
raise Exception('client_post_decrypt data uncorrect checksum')
|
||||
|
||||
pos = common.ord(self.recv_buf[2])
|
||||
if pos < 255:
|
||||
pos += 2
|
||||
else:
|
||||
pos = struct.unpack('>H', self.recv_buf[3:5])[0] + 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):
|
||||
if self.raw_trans:
|
||||
return 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''
|
||||
sendback = False
|
||||
|
||||
if not self.has_recv_header:
|
||||
if len(self.recv_buf) < 6:
|
||||
return (b'', False)
|
||||
crc = struct.pack('<I', binascii.crc32(self.salt + self.server_info.key) & 0xFFFFFFFF)
|
||||
if crc != self.recv_buf[:4]:
|
||||
return self.not_match_return(self.recv_buf)
|
||||
length = struct.unpack('>H', self.recv_buf[4:6])[0]
|
||||
if length > 2048:
|
||||
return self.not_match_return(self.recv_buf)
|
||||
if length > len(self.recv_buf):
|
||||
return (b'', False)
|
||||
sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, self.recv_buf[:length - 10], hashlib.sha1).digest()[:10]
|
||||
if sha1data != self.recv_buf[length - 10:length]:
|
||||
logging.error('auth_sha1_v2 data uncorrect auth HMAC-SHA1')
|
||||
return self.not_match_return(self.recv_buf)
|
||||
pos = common.ord(self.recv_buf[6])
|
||||
if pos < 255:
|
||||
pos += 6
|
||||
else:
|
||||
pos = struct.unpack('>H', self.recv_buf[7:9])[0] + 6
|
||||
out_buf = self.recv_buf[pos:length - 10]
|
||||
if len(out_buf) < 12:
|
||||
logging.info('auth_sha1_v2: too short, data %s' % (binascii.hexlify(self.recv_buf),))
|
||||
return self.not_match_return(self.recv_buf)
|
||||
client_id = struct.unpack('<Q', out_buf[:8])[0]
|
||||
connection_id = struct.unpack('<I', out_buf[8:12])[0]
|
||||
if self.server_info.data.insert(client_id, connection_id):
|
||||
self.has_recv_header = True
|
||||
out_buf = out_buf[12:]
|
||||
self.client_id = client_id
|
||||
self.connection_id = connection_id
|
||||
else:
|
||||
logging.info('auth_sha1_v2: auth fail, data %s' % (binascii.hexlify(out_buf),))
|
||||
return self.not_match_return(self.recv_buf)
|
||||
self.recv_buf = self.recv_buf[length:]
|
||||
self.has_recv_header = True
|
||||
sendback = True
|
||||
|
||||
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:
|
||||
logging.info('auth_sha1_v2: over size')
|
||||
return (b'E'*2048, False)
|
||||
else:
|
||||
raise Exception('server_post_decrype data error')
|
||||
if length > len(self.recv_buf):
|
||||
break
|
||||
|
||||
if struct.pack('<I', zlib.adler32(self.recv_buf[:length - 4]) & 0xFFFFFFFF) != self.recv_buf[length - 4:length]:
|
||||
logging.info('auth_sha1_v2: checksum error, data %s' % (binascii.hexlify(self.recv_buf[:length]),))
|
||||
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 checksum')
|
||||
|
||||
pos = common.ord(self.recv_buf[2])
|
||||
if pos < 255:
|
||||
pos += 2
|
||||
else:
|
||||
pos = struct.unpack('>H', self.recv_buf[3:5])[0] + 2
|
||||
out_buf += self.recv_buf[pos:length - 4]
|
||||
self.recv_buf = self.recv_buf[length:]
|
||||
if pos == length - 4:
|
||||
sendback = True
|
||||
|
||||
if out_buf:
|
||||
self.server_info.data.update(self.client_id, self.connection_id)
|
||||
self.decrypt_packet_num += 1
|
||||
return (out_buf, sendback)
|
||||
|
||||
class auth_sha1_v4(auth_base):
|
||||
def __init__(self, method):
|
||||
super(auth_sha1_v4, self).__init__(method)
|
||||
@@ -856,265 +404,6 @@ class auth_sha1_v4(auth_base):
|
||||
self.decrypt_packet_num += 1
|
||||
return (out_buf, sendback)
|
||||
|
||||
class auth_aes128(auth_base):
|
||||
def __init__(self, method):
|
||||
super(auth_aes128, self).__init__(method)
|
||||
self.recv_buf = b''
|
||||
self.unit_len = 8100
|
||||
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_aes128"
|
||||
self.no_compatible_method = 'auth_aes128'
|
||||
self.extra_wait_size = struct.unpack('>H', os.urandom(2))[0] % 1024
|
||||
self.pack_id = 0
|
||||
self.recv_id = 0
|
||||
|
||||
def init_data(self):
|
||||
return obfs_auth_v2_data()
|
||||
|
||||
def get_overhead(self, direction): # direction: true for c->s false for s->c
|
||||
return 9
|
||||
|
||||
def set_server_info(self, server_info):
|
||||
self.server_info = server_info
|
||||
try:
|
||||
max_client = int(server_info.protocol_param)
|
||||
except:
|
||||
max_client = 64
|
||||
self.server_info.data.set_max_client(max_client)
|
||||
|
||||
def rnd_data(self, buf_size):
|
||||
if buf_size > 1200:
|
||||
return b'\x01'
|
||||
|
||||
if self.pack_id > 4:
|
||||
rnd_data = os.urandom(common.ord(os.urandom(1)[0]) % 32)
|
||||
elif buf_size > 900:
|
||||
rnd_data = os.urandom(common.ord(os.urandom(1)[0]) % 128)
|
||||
else:
|
||||
rnd_data = os.urandom(struct.unpack('>H', os.urandom(2))[0] % 512)
|
||||
|
||||
if len(rnd_data) < 128:
|
||||
return common.chr(len(rnd_data) + 1) + rnd_data
|
||||
else:
|
||||
return common.chr(255) + struct.pack('<H', len(rnd_data) + 3) + rnd_data
|
||||
|
||||
def pack_data(self, buf):
|
||||
data = self.rnd_data(len(buf)) + buf
|
||||
data_len = len(data) + 8
|
||||
crc = binascii.crc32(struct.pack('<H', data_len)) & 0xFFFF
|
||||
data = struct.pack('<H', crc) + data
|
||||
data = struct.pack('<H', data_len) + data
|
||||
adler32 = (zlib.adler32(data) & 0xFFFFFFFF) ^ self.pack_id
|
||||
self.pack_id = (self.pack_id + 1) & 0xFFFFFFFF
|
||||
data += struct.pack('<I', adler32)
|
||||
return data
|
||||
|
||||
def pack_auth_data(self, auth_data, buf):
|
||||
if len(buf) == 0:
|
||||
return b''
|
||||
if len(buf) > 400:
|
||||
rnd_len = common.ord(os.urandom(1)[0]) % 512
|
||||
else:
|
||||
rnd_len = struct.unpack('<H', os.urandom(2))[0] % 1024
|
||||
data = auth_data
|
||||
data_len = 4 + 16 + 10 + len(buf) + rnd_len + 4
|
||||
data = data + struct.pack('<H', data_len) + struct.pack('<H', rnd_len)
|
||||
uid = os.urandom(4)
|
||||
encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(uid + self.server_info.key)) + self.salt, 'aes-128-cbc', b'\x00' * 16)
|
||||
data = uid + encryptor.encrypt(data)[16:]
|
||||
data += hmac.new(self.server_info.iv + self.server_info.key, data, hashlib.sha1).digest()[:10]
|
||||
data += os.urandom(rnd_len) + buf
|
||||
data += struct.pack('<I', (zlib.adler32(data) & 0xFFFFFFFF))
|
||||
return data
|
||||
|
||||
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''
|
||||
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_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) > 4:
|
||||
crc = struct.pack('<H', binascii.crc32(self.recv_buf[:2]) & 0xFFFF)
|
||||
if crc != self.recv_buf[2:4]:
|
||||
raise Exception('client_post_decrypt data uncorrect crc')
|
||||
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 struct.pack('<I', (zlib.adler32(self.recv_buf[:length - 4]) & 0xFFFFFFFF) ^ self.recv_id) != self.recv_buf[length - 4:length]:
|
||||
self.raw_trans = True
|
||||
self.recv_buf = b''
|
||||
raise Exception('client_post_decrypt data uncorrect checksum')
|
||||
|
||||
self.recv_id = (self.recv_id + 1) & 0xFFFFFFFF
|
||||
pos = common.ord(self.recv_buf[4])
|
||||
if pos < 255:
|
||||
pos += 4
|
||||
else:
|
||||
pos = struct.unpack('<H', self.recv_buf[5:7])[0] + 4
|
||||
out_buf += self.recv_buf[pos:length - 4]
|
||||
self.recv_buf = self.recv_buf[length:]
|
||||
|
||||
return out_buf
|
||||
|
||||
def server_pre_encrypt(self, buf):
|
||||
if self.raw_trans:
|
||||
return 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''
|
||||
sendback = False
|
||||
|
||||
if not self.has_recv_header:
|
||||
if len(self.recv_buf) < 30:
|
||||
return (b'', False)
|
||||
sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, self.recv_buf[:20], hashlib.sha1).digest()[:10]
|
||||
if sha1data != self.recv_buf[20:30]:
|
||||
logging.error('auth_aes128 data uncorrect auth HMAC-SHA1 from %s:%d, data %s' % (self.server_info.client, self.server_info.client_port, binascii.hexlify(self.recv_buf)))
|
||||
if len(self.recv_buf) < 30 + self.extra_wait_size:
|
||||
return (b'', False)
|
||||
return self.not_match_return(self.recv_buf)
|
||||
|
||||
user_key = self.recv_buf[:4]
|
||||
encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(user_key + self.server_info.key)) + self.salt, 'aes-128-cbc')
|
||||
head = encryptor.decrypt(b'\x00' * 16 + self.recv_buf[4:20] + b'\x00') # need an extra byte or recv empty
|
||||
length = struct.unpack('<H', head[12:14])[0]
|
||||
if len(self.recv_buf) < length:
|
||||
return (b'', False)
|
||||
|
||||
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]
|
||||
rnd_len = struct.unpack('<H', head[14:16])[0]
|
||||
if struct.pack('<I', zlib.adler32(self.recv_buf[:length - 4]) & 0xFFFFFFFF) != self.recv_buf[length - 4:length]:
|
||||
logging.info('auth_aes128: checksum error, data %s' % (binascii.hexlify(self.recv_buf[:length]),))
|
||||
return self.not_match_return(self.recv_buf)
|
||||
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('auth_aes128: wrong timestamp, time_dif %d, data %s' % (time_dif, binascii.hexlify(head),))
|
||||
return self.not_match_return(self.recv_buf)
|
||||
elif self.server_info.data.insert(client_id, connection_id):
|
||||
self.has_recv_header = True
|
||||
out_buf = self.recv_buf[30 + rnd_len:length - 4]
|
||||
self.client_id = client_id
|
||||
self.connection_id = connection_id
|
||||
else:
|
||||
logging.info('auth_aes128: auth fail, data %s' % (binascii.hexlify(out_buf),))
|
||||
return self.not_match_return(self.recv_buf)
|
||||
self.recv_buf = self.recv_buf[length:]
|
||||
self.has_recv_header = True
|
||||
sendback = True
|
||||
|
||||
while len(self.recv_buf) > 4:
|
||||
crc = struct.pack('<H', binascii.crc32(self.recv_buf[:2]) & 0xFFFF)
|
||||
if crc != self.recv_buf[2:4]:
|
||||
self.raw_trans = True
|
||||
logging.info('auth_aes128: wrong crc')
|
||||
if self.recv_id == 0:
|
||||
logging.info('auth_aes128: wrong crc')
|
||||
return (b'E'*2048, False)
|
||||
else:
|
||||
raise Exception('server_post_decrype data error')
|
||||
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.recv_id == 0:
|
||||
logging.info('auth_aes128: over size')
|
||||
return (b'E'*2048, False)
|
||||
else:
|
||||
raise Exception('server_post_decrype data error')
|
||||
if length > len(self.recv_buf):
|
||||
break
|
||||
|
||||
if struct.pack('<I', (zlib.adler32(self.recv_buf[:length - 4]) & 0xFFFFFFFF) ^ self.recv_id) != self.recv_buf[length - 4:length]:
|
||||
logging.info('auth_aes128: checksum error, data %s' % (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 = common.ord(self.recv_buf[4])
|
||||
if pos < 255:
|
||||
pos += 4
|
||||
else:
|
||||
pos = struct.unpack('<H', self.recv_buf[5:7])[0] + 4
|
||||
out_buf += self.recv_buf[pos:length - 4]
|
||||
self.recv_buf = self.recv_buf[length:]
|
||||
if pos == length - 4:
|
||||
sendback = True
|
||||
|
||||
if out_buf:
|
||||
self.server_info.data.update(self.client_id, self.connection_id)
|
||||
return (out_buf, sendback)
|
||||
|
||||
def client_udp_pre_encrypt(self, buf):
|
||||
return buf + struct.pack('<I', zlib.adler32(buf) & 0xFFFFFFFF)
|
||||
|
||||
def client_udp_post_decrypt(self, buf):
|
||||
length = len(buf)
|
||||
data = buf[:-4]
|
||||
if struct.pack('<I', zlib.adler32(data) & 0xFFFFFFFF) != buf[length - 4:]:
|
||||
return b''
|
||||
return data
|
||||
|
||||
def server_udp_pre_encrypt(self, buf):
|
||||
return buf + struct.pack('<I', zlib.adler32(buf) & 0xFFFFFFFF)
|
||||
|
||||
def server_udp_post_decrypt(self, buf):
|
||||
length = len(buf)
|
||||
data = buf[:-4]
|
||||
if struct.pack('<I', zlib.adler32(data) & 0xFFFFFFFF) != buf[length - 4:]:
|
||||
return (b'', None)
|
||||
return (data, None)
|
||||
|
||||
class obfs_auth_mu_data(object):
|
||||
def __init__(self):
|
||||
self.user_id = {}
|
||||
@@ -1212,12 +501,13 @@ class auth_aes128_sha1(auth_base):
|
||||
def rnd_data_len(self, buf_size, full_buf_size):
|
||||
if full_buf_size >= self.server_info.buffer_size:
|
||||
return 0
|
||||
rev_len = self.server_info.tcp_mss - buf_size - 9
|
||||
tcp_mss = self.server_info.tcp_mss
|
||||
rev_len = tcp_mss - buf_size - 9
|
||||
if rev_len == 0:
|
||||
return 0
|
||||
if rev_len < 0:
|
||||
if rev_len > -self.server_info.tcp_mss:
|
||||
return self.trapezoid_random_int(rev_len + self.server_info.tcp_mss, -0.3)
|
||||
if rev_len > -tcp_mss:
|
||||
return self.trapezoid_random_int(rev_len + tcp_mss, -0.3)
|
||||
return common.ord(os.urandom(1)[0]) % 32
|
||||
if buf_size > 900:
|
||||
return struct.unpack('>H', os.urandom(2))[0] % rev_len
|
||||
@@ -1477,7 +767,7 @@ class auth_aes128_sha1(auth_base):
|
||||
return b''
|
||||
return buf[:-4]
|
||||
|
||||
def server_udp_pre_encrypt(self, buf):
|
||||
def server_udp_pre_encrypt(self, buf, uid):
|
||||
user_key = self.server_info.key
|
||||
return buf + hmac.new(user_key, buf, self.hashfunc).digest()[:4]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -219,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:
|
||||
@@ -250,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):
|
||||
@@ -97,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")
|
||||
@@ -163,7 +166,7 @@ class tls_ticket_auth(plain.plain):
|
||||
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))
|
||||
@@ -172,24 +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 = 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
|
||||
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.overhead > 0:
|
||||
self.server_info.overhead -= self.overhead
|
||||
self.overhead = 0
|
||||
if self.method == 'tls1.2_ticket_auth':
|
||||
if self.method in ['tls1.2_ticket_auth', 'tls1.2_ticket_fastauth']:
|
||||
return (b'E'*2048, False, False)
|
||||
return (buf, True, False)
|
||||
|
||||
@@ -197,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:
|
||||
@@ -211,11 +218,11 @@ 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
|
||||
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')
|
||||
@@ -224,12 +231,14 @@ class tls_ticket_auth(plain.plain):
|
||||
raise Exception('server_decode data error')
|
||||
verify_len = struct.unpack('>H', buf[3:5])[0] + 1 # 11 - 10
|
||||
if len(verify) < verify_len + 10:
|
||||
raise Exception('server_decode data error')
|
||||
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')
|
||||
self.recv_buffer = verify[verify_len + 10:]
|
||||
self.handshake_status = 8
|
||||
return self.server_decode(b'')
|
||||
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
|
||||
@@ -240,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)
|
||||
@@ -287,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)
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ 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):
|
||||
|
||||
@@ -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'', None)
|
||||
sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, buf[:-10], hashlib.sha1).digest()[:10]
|
||||
if sha1data != buf[-10:]:
|
||||
return (b'', None)
|
||||
return (to_bytes(chr(ord(buf[0]) & 0xEF)) + buf[1:-10], None)
|
||||
else:
|
||||
return (buf, None)
|
||||
|
||||
|
||||
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 &
|
||||
|
||||
|
||||
@@ -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,16 +86,16 @@ 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)
|
||||
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:
|
||||
|
||||
@@ -111,12 +111,6 @@ def check_config(config, is_local):
|
||||
if config.get('server', '') in ['127.0.0.1', 'localhost']:
|
||||
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.warning('warning: table is not safe; please use a safer cipher, '
|
||||
'like AES-256-CFB')
|
||||
if (config.get('method', '') or '').lower() == 'rc4':
|
||||
logging.warning('warning: RC4 is not safe; please use a safer cipher, '
|
||||
'like AES-256-CFB')
|
||||
if config.get('timeout', 300) < 100:
|
||||
logging.warning('warning: your timeout %d seems too short' %
|
||||
int(config.get('timeout')))
|
||||
@@ -245,6 +239,7 @@ def get_config(is_local):
|
||||
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))
|
||||
|
||||
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
+248
-125
@@ -28,9 +28,8 @@ import traceback
|
||||
import random
|
||||
import platform
|
||||
import threading
|
||||
from collections import deque
|
||||
|
||||
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
|
||||
@@ -91,7 +90,7 @@ WAIT_STATUS_READING = 1
|
||||
WAIT_STATUS_WRITING = 2
|
||||
WAIT_STATUS_READWRITING = WAIT_STATUS_READING | WAIT_STATUS_WRITING
|
||||
|
||||
NETWORK_MTU = 1492
|
||||
NETWORK_MTU = 1500
|
||||
TCP_MSS = NETWORK_MTU - 40
|
||||
BUF_SIZE = 32 * 1024
|
||||
UDP_MAX_BUF_SIZE = 65536
|
||||
@@ -99,8 +98,7 @@ UDP_MAX_BUF_SIZE = 65536
|
||||
class SpeedTester(object):
|
||||
def __init__(self, max_speed = 0):
|
||||
self.max_speed = max_speed * 1024
|
||||
self.timeout = 1
|
||||
self._cache = deque()
|
||||
self.last_time = time.time()
|
||||
self.sum_len = 0
|
||||
|
||||
def update_limit(self, max_speed):
|
||||
@@ -108,19 +106,21 @@ class SpeedTester(object):
|
||||
|
||||
def add(self, data_len):
|
||||
if self.max_speed > 0:
|
||||
self._cache.append((time.time(), data_len))
|
||||
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:
|
||||
if self.sum_len > 0:
|
||||
cut_t = time.time()
|
||||
t = max(cut_t - self._cache[0][0], 0.01)
|
||||
speed = self.sum_len / t
|
||||
if self._cache[0][0] + self.timeout < cut_t:
|
||||
self.sum_len -= self._cache[0][1]
|
||||
self._cache.popleft()
|
||||
return speed >= self.max_speed
|
||||
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):
|
||||
@@ -132,38 +132,31 @@ 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._tcp_mss = TCP_MSS
|
||||
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
|
||||
|
||||
try:
|
||||
self._tcp_mss = local_sock.getsockopt(socket.SOL_TCP, socket.TCP_MAXSEG)
|
||||
logging.debug("TCP MSS = %d" % (self._tcp_mss,))
|
||||
except:
|
||||
pass
|
||||
|
||||
server_info = obfs.server_info(server.obfs_data)
|
||||
server_info.host = config['server']
|
||||
server_info.port = server._listen_port
|
||||
@@ -180,6 +173,7 @@ class TCPRelayHandler(object):
|
||||
server_info.head_len = 30
|
||||
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)
|
||||
|
||||
server_info = obfs.server_info(server.protocol_data)
|
||||
@@ -198,6 +192,7 @@ class TCPRelayHandler(object):
|
||||
server_info.head_len = 30
|
||||
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"])
|
||||
@@ -213,27 +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
|
||||
@@ -254,9 +253,33 @@ 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
|
||||
@@ -301,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:
|
||||
@@ -309,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])
|
||||
@@ -329,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()
|
||||
@@ -402,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"]
|
||||
|
||||
@@ -563,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
|
||||
|
||||
@@ -577,7 +621,7 @@ 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:
|
||||
@@ -589,10 +633,14 @@ class TCPRelayHandler(object):
|
||||
server_info.buffer_size = self._recv_buffer_size
|
||||
server_info = self._protocol.get_server_info()
|
||||
server_info.buffer_size = self._recv_buffer_size
|
||||
connecttype, remote_addr, remote_port, header_length = header_result
|
||||
common.connect_log('%s connecting %s:%d via port %d by UID %d' %
|
||||
((connecttype == 0) and 'TCP' or 'UDP',
|
||||
common.to_str(remote_addr), remote_port, self._server._listen_port, self._user_id))
|
||||
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
|
||||
@@ -673,13 +721,15 @@ class TCPRelayHandler(object):
|
||||
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:
|
||||
@@ -702,7 +752,6 @@ class TCPRelayHandler(object):
|
||||
if result:
|
||||
ip = result[1]
|
||||
if ip:
|
||||
|
||||
try:
|
||||
self._stage = STAGE_CONNECTING
|
||||
remote_addr = ip
|
||||
@@ -740,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)
|
||||
@@ -759,15 +812,21 @@ 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):
|
||||
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
|
||||
s = buffer_size % self._tcp_mss + self._overhead
|
||||
if s > self._tcp_mss:
|
||||
return buffer_size + s - self._tcp_mss
|
||||
if buffer_size > frame_size:
|
||||
buffer_size = int(buffer_size / frame_size) * frame_size
|
||||
return buffer_size
|
||||
|
||||
def _on_local_read(self):
|
||||
@@ -777,7 +836,7 @@ class TCPRelayHandler(object):
|
||||
return
|
||||
is_local = self._is_local
|
||||
if is_local:
|
||||
recv_buffer_size = self._get_read_size(self._local_sock, self._recv_buffer_size)
|
||||
recv_buffer_size = self._get_read_size(self._local_sock, self._recv_buffer_size, True)
|
||||
else:
|
||||
recv_buffer_size = BUF_SIZE
|
||||
data = None
|
||||
@@ -799,6 +858,10 @@ class TCPRelayHandler(object):
|
||||
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]))
|
||||
@@ -806,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)
|
||||
@@ -833,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:
|
||||
@@ -844,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 \
|
||||
@@ -873,17 +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:
|
||||
if self._is_local:
|
||||
recv_buffer_size = BUF_SIZE
|
||||
else:
|
||||
recv_buffer_size = self._get_read_size(self._remote_sock, self._recv_buffer_size)
|
||||
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
|
||||
@@ -912,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]))
|
||||
@@ -973,7 +1043,7 @@ class TCPRelayHandler(object):
|
||||
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:
|
||||
@@ -982,41 +1052,43 @@ class TCPRelayHandler(object):
|
||||
if self._user is not None and self._user not in self._server.server_users:
|
||||
self.destroy()
|
||||
return True
|
||||
# order is important
|
||||
if sock == self._remote_sock or sock == self._remote_sock_v6:
|
||||
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 True
|
||||
if event & (eventloop.POLL_IN | eventloop.POLL_HUP):
|
||||
if not self.speed_tester_d.isExceed():
|
||||
if not self._server.speed_tester_d(self._user_id).isExceed():
|
||||
handle = True
|
||||
self._on_remote_read(sock == self._remote_sock)
|
||||
if self._stage == STAGE_DESTROYED:
|
||||
return True
|
||||
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 True
|
||||
if event & (eventloop.POLL_IN | eventloop.POLL_HUP):
|
||||
if not self.speed_tester_u.isExceed():
|
||||
if not self._server.speed_tester_u(self._user_id).isExceed():
|
||||
handle = True
|
||||
self._on_local_read()
|
||||
if self._stage == STAGE_DESTROYED:
|
||||
return True
|
||||
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
|
||||
|
||||
@@ -1048,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:
|
||||
@@ -1078,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):
|
||||
@@ -1092,6 +1180,7 @@ class TCPRelay(object):
|
||||
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
|
||||
@@ -1116,7 +1205,7 @@ class TCPRelay(object):
|
||||
listen_port = config['server_port']
|
||||
self._listen_port = listen_port
|
||||
|
||||
if common.to_bytes(config['protocol']) in [b"auth_aes128_md5", b"auth_aes128_sha1"]:
|
||||
if common.to_str(config['protocol']) in obfs.mu_protocol():
|
||||
self._update_users(None, None)
|
||||
|
||||
addrs = socket.getaddrinfo(listen_addr, listen_port, 0,
|
||||
@@ -1137,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
|
||||
|
||||
@@ -1181,9 +1271,9 @@ class TCPRelay(object):
|
||||
self.del_user(uid)
|
||||
else:
|
||||
passwd = items[1]
|
||||
self.add_user(uid, passwd)
|
||||
self.add_user(uid, {'password':passwd})
|
||||
|
||||
def update_user(self, id, passwd):
|
||||
def _update_user(self, id, passwd):
|
||||
uid = struct.pack('<I', id)
|
||||
self.add_user(uid, passwd)
|
||||
|
||||
@@ -1196,12 +1286,25 @@ class TCPRelay(object):
|
||||
uid = struct.pack('<I', id)
|
||||
self.add_user(uid, users[id])
|
||||
|
||||
def add_user(self, user, passwd): # user: binstr[4], passwd: str
|
||||
self.server_users[user] = common.to_bytes(passwd)
|
||||
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, user):
|
||||
if user in self.server_users:
|
||||
del self.server_users[user]
|
||||
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:
|
||||
@@ -1298,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))
|
||||
@@ -1305,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()
|
||||
@@ -1322,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)
|
||||
@@ -1347,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()
|
||||
|
||||
+107
-950
File diff suppressed because it is too large
Load Diff
@@ -16,5 +16,5 @@
|
||||
# under the License.
|
||||
|
||||
def version():
|
||||
return '3.1.2 2017-04-07'
|
||||
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