90 Commits
Author SHA1 Message Date
破娃酱 fd723a92c4 bump 2017-07-27 15:50:44 +08:00
破娃酱 b3cf97c44e adjust 2017-07-27 14:59:49 +08:00
破娃酱 e3ec6bb5f8 adjust 2017-07-27 14:20:57 +08:00
破娃酱 e8b4bb05a0 adjust 2017-07-27 13:25:57 +08:00
破娃酱 604cbf8a78 more small packet 2017-07-27 11:00:40 +08:00
破娃酱 cb1dcacd4b refine recv size
try more data_size_list
2017-07-26 12:13:26 +08:00
破娃酱 7dddf07da4 fix overhead 2017-07-26 10:20:17 +08:00
破娃酱 838caa3f1d fix overhead 2017-07-26 09:28:13 +08:00
破娃酱 38ef6a2546 add auth_chain_b 2017-07-26 02:49:26 +08:00
破娃酱 ab236ff03e fix compatible of tls1.2_ticket_fastauth 2017-07-25 17:15:49 +08:00
破娃酱 4198ec84ce not filter mu port 2017-07-16 11:56:17 +08:00
破娃酱 0c1a9368c9 fix initmudbjson.sh, remove LRUCache init log 2017-07-12 22:28:02 +08:00
破娃酱 339c1ccee2 fix connections ref 2017-07-03 14:49:10 +08:00
破娃酱 a629d1baec update initmudbjson.sh 2017-06-28 20:10:15 +08:00
破娃酱 bc930031a9 update initmudbjson.sh 2017-06-28 16:47:05 +08:00
破娃酱 1a2c7ed933 update initmudbjson.sh 2017-06-28 16:25:21 +08:00
破娃酱 ab8765191e update initmudbjson.sh 2017-06-28 16:12:14 +08:00
破娃酱 e21f01755a update initmudbjson.sh 2017-06-28 16:05:43 +08:00
破娃酱 57ba4f5f67 add initmudbjson.sh 2017-06-28 15:54:14 +08:00
破娃酱 727d218ff2 fix var name 2017-06-22 19:29:34 +08:00
muicoder adbaafa18a fix default work 2017-06-22 12:36:56 +08:00
破娃酱 7c6c671a5e default work at "~" 2017-06-22 10:25:57 +08:00
破娃酱 0d40a52a95 Merge pull request #253 from muicoder/patch-1
better
2017-06-20 10:48:38 +08:00
muicoder d8d19a99fc better 2017-06-19 23:07:37 +08:00
破娃酱 19c868242a update mujson_mgr.py usage info 2017-06-18 15:43:46 +08:00
破娃酱 f6752fb21b refine Dockerfile 2017-06-17 00:27:09 +08:00
破娃酱 0e045a36e7 fix http_post under py3 2017-06-16 21:51:45 +08:00
破娃酱 7b62f9f530 fix http_post under py3 2017-06-16 21:45:56 +08:00
破娃酱 b655222857 fix http_post under py3 2017-06-16 21:32:52 +08:00
破娃酱 71625e6415 refine Dockerfile 2017-06-16 21:12:02 +08:00
破娃酱 44185c9fff fix KeyError 2017-06-05 11:42:11 +08:00
破娃酱 82f8fef28a allow set speed limit in runtime 2017-06-04 18:34:12 +08:00
破娃酱 f22ef99d85 do not destroy 2017-06-03 23:09:09 +08:00
破娃酱 47c6e16833 fix a mem leak 2017-06-03 23:06:12 +08:00
破娃酱 8826f515b6 fix async DNS 2017-06-03 21:47:17 +08:00
破娃酱 7c3d4e01b1 bump 3.3.3 2017-06-03 13:55:07 +08:00
破娃酱 280c762760 add tls1.2_ticket_fastauth 2017-06-03 13:51:35 +08:00
破娃酱 2cf5f1982e add DNS cache 2017-06-01 00:11:15 +08:00
破娃酱 b89931f9fc fix cmp in Python 3 2017-05-31 17:46:07 +08:00
破娃酱 f31a81665c refine UDP over TCP 2017-05-30 21:32:51 +08:00
破娃酱 92722c2ea9 refine _get_read_size 2017-05-29 08:51:14 +08:00
破娃酱 a3315bcd6a Revert "fix overhead size"
This reverts commit 587453c881.
2017-05-28 15:00:02 +08:00
破娃酱 587453c881 fix overhead size 2017-05-27 21:28:43 +08:00
破娃酱 c1364c3afe init user_id 2017-05-27 13:06:35 +08:00
破娃酱 77628acde4 allow fast sending request 2017-05-20 21:59:50 +08:00
破娃酱 542a2e62d9 bump 3.3.2 2017-05-20 19:04:51 +08:00
破娃酱 3b0200581c refine error detector 2017-05-20 18:39:26 +08:00
破娃酱 24623b896d fix str type 2017-05-20 14:50:27 +08:00
破娃酱 86de572caf revert http reply 2017-05-19 01:38:40 +08:00
破娃酱 7a68e4e637 more reply addr 2017-05-19 00:50:47 +08:00
破娃酱 6e3fdd0d62 bump 2017-05-18 17:43:37 +08:00
破娃酱 1dc6c4be45 http reply 2017-05-18 17:40:50 +08:00
破娃酱 d6e1f73c9f fix old version of OpenSSL 2017-05-17 20:13:17 +08:00
破娃酱 85b6930d7d fix old version of OpenSSL
ignore warning
2017-05-17 19:59:48 +08:00
破娃酱 96200d7d76 fix key size 2017-05-14 16:42:48 +08:00
破娃酱 67065a212f chmod +x 2017-05-12 19:00:07 +08:00
破娃酱 c1f1281b83 let py2 works 2017-05-12 18:52:22 +08:00
破娃酱 abe4ac3b90 Async DNS query under UDP 2017-05-12 18:11:38 +08:00
破娃酱 6c162b13c4 fix stop script 2017-05-12 17:13:58 +08:00
破娃酱 709e67afe4 bump 2017-05-11 20:51:24 +08:00
破娃酱 19e2e7422d fd instead 2017-05-11 00:07:34 +08:00
破娃酱 5e14119b41 removefd 2017-05-10 20:10:23 +08:00
破娃酱 a1f56d1233 refine log 2017-05-10 10:27:29 +08:00
破娃酱 be696555a7 new speed tester 2017-05-09 23:20:37 +08:00
破娃酱 45faa93dd3 close unknown socket 2017-05-09 21:46:37 +08:00
破娃酱 7599e53a6f more log for debug 2017-05-09 20:02:24 +08:00
破娃酱 8aa3fcd207 add cymysql setup script 2017-05-09 03:45:59 +08:00
破娃酱 26f360c204 update mgr 2017-05-07 23:44:59 +08:00
破娃酱 5cb9445dfd chmod +x 2017-05-05 02:30:08 +08:00
破娃酱 2388802e3f fix run script 2017-05-04 17:43:51 +08:00
破娃酱 0969937fd1 fix bytes type 2017-05-04 14:31:24 +08:00
破娃酱 d968f01245 parse comment
var 'rows' not init before return
2017-05-04 10:56:14 +08:00
破娃酱 03b1a53b9a run with newest python version 2017-05-02 12:40:23 +08:00
破娃酱 1e85d14a48 optimize 2017-04-30 21:26:36 +08:00
破娃酱 c3bb64bc2c add interface legendsockssr 2017-04-30 08:35:32 +08:00
LI JIAHAO 3c308ca876 force additional ports only with ss-panel (#218) 2017-04-29 08:32:35 +08:00
破娃酱 f5f4bd0339 force tcp_mss in range (500, 1500] 2017-04-29 02:46:25 +08:00
破娃酱 f3a3ab4b84 log UDP bind port 2017-04-28 21:46:47 +08:00
破娃酱 5896eac1a8 fix auth_chain_a UDP bug 2017-04-28 21:43:18 +08:00
破娃酱 582d7b7256 connect_log include local addr & port 2017-04-27 21:28:23 +08:00
破娃酱 8322cd9d8f bump 2017-04-27 18:17:52 +08:00
破娃酱 73af0eeea0 fix auth_chain_a 2017-04-25 17:00:08 +08:00
破娃酱 caadb606ba impl auth_chain client 2017-04-25 16:38:04 +08:00
破娃酱 774134ffad remove auth_sha1, auth_sha1_v2, verify_simple, auth_simple, verify_sha1
remove TCP over UDP
add UDP part of auth_chain_a
2017-04-25 10:27:51 +08:00
破娃酱 db6629a130 test new protocol 2017-04-22 13:12:56 +08:00
破娃酱 7deba0b995 remove auth_aes128, fix bugs 2017-04-22 12:41:13 +08:00
Handyman 036df8aac6 Use thread.join() instead (#219) 2017-04-14 17:37:32 +08:00
破娃酱 1e7e707024 set MSS up to 1500 2017-04-09 18:33:29 +08:00
破娃酱 2b64802fed fix data length range 2017-04-09 18:27:13 +08:00
破娃酱 a3a8ed996c fix a mistake 2017-04-08 22:02:35 +08:00
37 changed files with 1366 additions and 2165 deletions
+38
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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
View File
+24
View File
@@ -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
Regular → Executable
+3 -2
View File
@@ -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
View File
@@ -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]
Regular → Executable
+3 -2
View File
@@ -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 &
+1 -1
View File
@@ -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
View File
@@ -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):
+6
View File
@@ -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
+3 -1
View File
@@ -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
View File
@@ -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)
+1 -4
View File
@@ -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:
+1 -1
View File
@@ -74,7 +74,7 @@ class NoneCipher(object):
ciphers = {
'none': (16, 0, NoneCipher),
'table': (0, 0, TableCipher)
'table': (16, 0, TableCipher)
}
+5 -1
View File
@@ -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
View File
@@ -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 &
-1
View File
@@ -29,7 +29,6 @@ if __name__ == '__main__':
try:
from collections import OrderedDict
print("loaded collections.OrderedDict")
except:
from shadowsocks.ordereddict import OrderedDict
+7 -3
View File
@@ -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)
+5 -715
View File
@@ -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]
+692
View File
@@ -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
+2 -2
View File
@@ -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
+29 -16
View File
@@ -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)
+1 -1
View File
@@ -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):
-206
View File
@@ -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
View File
@@ -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 &
+8 -8
View File
@@ -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:
+1 -6
View File
@@ -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
View File
@@ -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
View File
+248 -125
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -16,5 +16,5 @@
# under the License.
def version():
return '3.1.2 2017-04-07'
return '3.4.0 2017-07-27'
Regular → Executable
+3 -1
View File
@@ -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}')
Regular → Executable
View File