160 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
破娃酱 51c8ae4d78 bump 2017-04-07 11:55:06 +08:00
破娃酱 862fa890ee typo 2017-04-05 22:11:55 +08:00
破娃酱 d82c931f8e auto adjust TCP_MAXSEG 2017-04-05 21:46:19 +08:00
破娃酱 8e27035851 fix buffer_size 2017-03-26 23:42:32 +08:00
破娃酱 4d9fca9b37 display UID 2017-03-26 19:07:30 +08:00
破娃酱 90ffd1c6ee display UID 2017-03-26 18:29:13 +08:00
破娃酱 922cf462d6 improve rand 2017-03-26 00:00:01 +08:00
破娃酱 76fd4296a5 bump
improve rand size
2017-03-25 02:17:59 +08:00
破娃酱 63cceb29f9 fix Dockerfile 2017-03-25 01:54:23 +08:00
破娃酱 1b1c1fbac6 improve rand size 2017-03-25 01:53:11 +08:00
破娃酱 6cbcd855ff better rand size 2017-03-24 17:07:52 +08:00
破娃酱 bf0055be40 fix Dockerfile 2017-03-23 17:19:15 +08:00
破娃酱 63a6de5d26 fix host type in http_simple 2017-03-23 13:16:58 +08:00
破娃酱 1c58b10dd0 ignore bind 10.0.0.0/8 and 192.168.0.0/16 2017-03-19 18:28:43 +08:00
破娃酱 6712dd62c0 fix accept any size of Finish message 2017-03-18 21:49:50 +08:00
破娃酱 caaacc7090 accept any size of Finish message 2017-03-18 21:35:39 +08:00
破娃酱 6f35105ea2 fix syntax 2017-03-18 18:00:36 +08:00
破娃酱 e19f9e70e7 add "New session ticket" 2017-03-18 17:58:33 +08:00
破娃酱 e3d6efbc7a bump 2017-03-16 00:39:49 +08:00
破娃酱 1355c273db remove bom 2017-03-16 00:35:19 +08:00
破娃酱 bae7d7a0c2 fix str type under py3 2017-03-02 23:20:06 +08:00
破娃酱 e1b199fc44 add additional_ports in config
using user's password as protocol param in multi-user mode
2017-02-19 10:27:25 +08:00
破娃酱 e2957e672e timeout set to 1 2017-02-14 23:28:49 +08:00
破娃酱 f296751def bandwidth limit of the user 2017-02-14 21:36:38 +08:00
破娃酱 c4c1cd6dc1 refine auth_aes128_* & http_* 2017-02-14 20:23:45 +08:00
破娃酱 74083af472 process DNS 2017-02-14 02:55:33 +08:00
破娃酱 6732244ddf lower latency 2017-02-13 02:14:21 +08:00
破娃酱 2723295101 fix CPU usage 2017-02-13 01:38:48 +08:00
破娃酱 85955d6e6d speed limit in KiB 2017-02-12 22:46:09 +08:00
破娃酱 72ca31dad9 add traffic control for each TCP connection 2017-02-12 22:35:26 +08:00
John 2750af4a2e optimize shell (#173) 2017-02-11 22:56:02 +08:00
John bf8bb8afe2 fix undefined variable bug (#171) 2017-02-05 20:44:43 +08:00
破娃酱 5d479087a8 fix tls1.2_ticket_auth when it as a client 2017-02-05 19:18:50 +08:00
破娃酱 26b4d8bc18 Merge branch manyuser into dev 2017-02-05 15:38:47 +08:00
Dwwwwww 3014b0034c Update README.md (#169)
Improve
2017-02-05 12:36:45 +08:00
Esdeath bf443d72ec refine add "glzjinmod" interface
glzjinmod support these key
2017-02-04 13:01:18 +08:00
破娃酱 31aaea603a add "glzjinmod" interface 2017-02-02 19:50:55 +08:00
破娃酱 390be3bf8d random ticket size for client only 2017-02-02 19:05:18 +08:00
破娃酱 e7a61a934f ignore SIGPIPE 2017-01-25 01:08:00 +08:00
破娃酱 96760ed438 not display RST error 2017-01-23 16:30:35 +08:00
破娃酱 00a31285ed encode protocol param to URL with -i 2017-01-22 13:54:56 +08:00
破娃酱 68693d5ec7 fix bug under py3 2017-01-21 12:46:31 +08:00
Esdeath 7cfbe49806 refine traffic log when redirect enable (#163) 2017-01-18 21:05:15 +08:00
破娃酱 9c794624e0 fix auth_aes128_* client side 2017-01-18 17:00:09 +08:00
破娃酱 ccacef1556 fix bug & update usage 2017-01-17 20:53:54 +08:00
破娃酱 75643635d4 change default setting 2017-01-15 10:05:29 +08:00
破娃酱 4048abaeff refine user transfer 2017-01-13 21:52:17 +08:00
破娃酱 d4b7483ef0 modify mgr fast command 2017-01-12 22:06:20 +08:00
破娃酱 ad892e1223 let 'transfer_enable' be an integer 2017-01-11 18:34:28 +08:00
破娃酱 2de2a553d7 multi-user for mudbjson mode 2017-01-11 14:45:37 +08:00
破娃酱 588d8ad8b3 fix auth_aes128_* client side 2017-01-11 14:43:24 +08:00
破娃酱 278ce30c66 update mu online client manager 2017-01-09 17:48:19 +08:00
破娃酱 25c66dab14 bump 2017-01-08 17:06:08 +08:00
破娃酱 de67c11207 typo 2017-01-08 00:47:44 +08:00
破娃酱 4f52dffcc1 parse protocol_param in py3 2017-01-08 00:26:49 +08:00
破娃酱 cd825c9c2c typo 2017-01-07 22:07:13 +08:00
noisyfox e6410c067b Don't clear file first when saving config file 2017-01-07 21:10:49 +11:00
noisyfox f0b0a31baf Revert commit fd9214b334 2017-01-07 20:32:22 +11:00
noisyfox 6d53546f6e Use super() call 2017-01-07 20:03:20 +11:00
noisyfox 8777152ec9 Run db thread as daemon
so that all sub-thread will inherite this and be killed automatically if main thread crash
2017-01-07 19:58:32 +11:00
noisyfox f05bea4e10 Fix crash on windows 2017-01-07 19:34:39 +11:00
noisyfox 0bf1e46ace Update .gitignore 2017-01-07 19:23:30 +11:00
noisyfox 0f8dd36297 Add initcfg.bat for windows 2017-01-07 19:23:09 +11:00
noisyfox 498f71a21b Save json config as utf8 2017-01-07 19:22:36 +11:00
noisyfox 490ae12128 Fix mujson_mgr.py crash when MUDB_FILE doesn't exist 2017-01-07 19:14:58 +11:00
noisyfox 84fd119f26 Fix mujson_mgr.py crash on windows 2017-01-07 19:14:02 +11:00
noisyfox fd9214b334 Use space instead of tab for indent
see PEP8
2017-01-07 18:50:12 +11:00
破娃酱 b367ad0560 using '#' to split multi-user setting 2017-01-07 01:23:42 +08:00
破娃酱 0ca45be89d update MU auth_aes128_* protocol 2017-01-06 21:02:52 +08:00
破娃酱 959aad3f41 multiuser in single port protocol 2017-01-04 22:46:39 +08:00
40 changed files with 2119 additions and 2299 deletions
+5
View File
@@ -29,3 +29,8 @@ htmlcov
.DS_Store
.idea
# Ignore user config
user-config.json
userapiconfig.py
usermysql.json
+56
View File
@@ -1,3 +1,59 @@
3.4.0 2017-07-27
- add auth_chain_b
- add initmudbjson.sh
- allow set speed limit in runtime
- fix bugs & mem leak
3.3.3 2017-06-03
- add DNS cache
- add tls1.2_ticket_fastauth
- fix bugs
3.3.2 2017-05-20
- revert http reply
- refine tls1.2_ticket_auth error detector
3.3.1 2017-05-18
- fix stop script
- Async DNS query under UDP
- fix old version of OpenSSL
- http reply
3.3.0 2017-05-11
- connect_log include local addr & port
- fix auth_chain_a UDP bug
- add "additional_ports_only"
- add interface legendsockssr
- run with newest python version
- parse comment in hosts
- update mujson_mgr
- add cymysql setup script
- new speed tester
- fix leaks
- bugs fixed
3.2.0 2017-04-27
- add auth_chain_a
- remove auth_aes128, auth_sha1, auth_sha1_v2, verify_simple, auth_simple, verify_sha1
3.1.2 2017-04-07
- display UID
- auto adjust TCP MSS
3.1.1 2017-03-25
- add "New session ticket"
- ignore bind 10.0.0.0/8 and 192.168.0.0/16 by default
- improve rand size under auth_aes128_*
- fix bugs
3.1.0 2017-03-16
- add "glzjinmod" interface
- rate limit
- add additional_ports in config
3.0.4 2017-01-08
- multi-user in single port
3.0.1 2017-01-03
- remove auth_aes128_*_compatible
+21 -22
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-256-cfb
ENV PROTOCOL origin
ENV OBFS http_simple
ENV TIMEOUT 300
ENV DNS_ADDR 8.8.8.8
ENV DNS_ADDR_2 8.8.4.4
ENV SERVER_ADDR 0.0.0.0
ENV SERVER_PORT 51348
ENV PASSWORD psw
ENV METHOD aes-128-ctr
ENV PROTOCOL auth_aes128_md5
ENV PROTOCOLPARAM 32
ENV OBFS tls1.2_ticket_auth_compatible
ENV TIMEOUT 300
ENV DNS_ADDR 8.8.8.8
ENV DNS_ADDR_2 8.8.4.4
ARG BRANCH=manyuser
ARG WORK=~
RUN apk update \
&& apk add python \
RUN apk --no-cache add python \
libsodium \
unzip \
wget \
&& rm -rf /var/cache/apk/*
wget
RUN wget --no-check-certificate https://github.com/breakwa11/shadowsocks/archive/manyuser.zip -O /tmp/manyuser.zip \
&& unzip -d /tmp /tmp/manyuser.zip \
&& mv /tmp/shadowsocks-manyuser/shadowsocks ~/shadowsocks \
&& rm -rf /tmp/*
RUN mkdir -p $WORK && \
wget -qO- --no-check-certificate https://github.com/shadowsocksr/shadowsocksr/archive/$BRANCH.tar.gz | tar -xzf - -C $WORK
WORKDIR ~/shadowsocks
WORKDIR $WORK/shadowsocksr-$BRANCH/shadowsocks
CMD python ~/shadowsocks/server.py -p $SERVER_PORT -k $PASSWORD -m $METHOD -O $PROTOCOL -o $OBFS
EXPOSE $SERVER_PORT
CMD python server.py -p $SERVER_PORT -k $PASSWORD -m $METHOD -O $PROTOCOL -o $OBFS -G $PROTOCOLPARAM
+5 -5
View File
@@ -33,11 +33,11 @@ move to "~/shadowsocksr", then run:
move to "~/shadowsocksr/shadowsocks", then run:
python server.py -p 443 -k password -m aes-128-cfb -O auth_aes128_md5_compatible -o tls1.2_ticket_auth_compatible
python server.py -p 443 -k password -m aes-128-cfb -O auth_aes128_md5 -o tls1.2_ticket_auth_compatible
Check all the options via `-h`.
You can also use a configuration file instead (recommand), move to "~/shadowsocksr" and edit the file "user-config.json", then move to "~/shadowsocksr/shadowsocks" again, just run:
You can also use a configuration file instead (recommend), move to "~/shadowsocksr" and edit the file "user-config.json", then move to "~/shadowsocksr/shadowsocks" again, just run:
python server.py
@@ -57,7 +57,7 @@ To monitor the log:
Client
------
* [Windows] / [OS X]
* [Windows] / [macOS]
* [Android] / [iOS]
* [OpenWRT]
@@ -99,7 +99,7 @@ Bugs and Issues
[iOS]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help
[Issue Tracker]: https://github.com/shadowsocksr/shadowsocksr/issues?state=open
[OpenWRT]: https://github.com/shadowsocks/openwrt-shadowsocks
[OS X]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Shadowsocks-for-OSX-Help
[macOS]: https://github.com/shadowsocksr/ShadowsocksX-NG
[Travis CI]: https://travis-ci.org/shadowsocksr/shadowsocksr
[Windows]: https://github.com/shadowsocksr/shadowsocksr-csharp
[Wiki]: https://github.com/breakwa11/shadowsocks-rss/wiki
[Wiki]: https://github.com/breakwa11/shadowsocks-rss/wiki
+1 -1
View File
@@ -1,5 +1,5 @@
# Config
API_INTERFACE = 'sspanelv2' #mudbjson, sspanelv2, sspanelv3, sspanelv3ssr, muapiv2(not support)
API_INTERFACE = 'sspanelv2' #mudbjson, sspanelv2, sspanelv3, sspanelv3ssr, glzjinmod, legendsockssr, muapiv2(not support)
UPDATE_TIME = 60
SERVER_PUB_ADDR = '127.0.0.1' # mujson_mgr need this to generate ssr link
+9 -3
View File
@@ -4,14 +4,20 @@
"server_port": 8388,
"local_address": "127.0.0.1",
"local_port": 1080,
"password": "m",
"timeout": 120,
"udp_timeout": 60,
"method": "aes-256-cfb",
"method": "aes-128-ctr",
"protocol": "auth_aes128_md5",
"protocol_param": "",
"obfs": "tls1.2_ticket_auth_compatible",
"obfs_param": "",
"speed_limit_per_con": 0,
"speed_limit_per_user": 0,
"additional_ports" : {}, // only works under multi-user mode
"additional_ports_only" : false, // only works under multi-user mode
"timeout": 120,
"udp_timeout": 60,
"dns_ipv6": false,
"connect_verbose_info": 0,
"redirect": "",
+139 -76
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
@@ -24,6 +24,7 @@ class TransferBase(object):
self.port_uid_table = {} #端口到uid的映射(仅v3以上有用)
self.onlineuser_cache = lru_cache.LRUCache(timeout=60*30) #用户在线状态记录
self.pull_ok = False #记录是否已经拉出过数据
self.mu_ports = {}
def load_cfg(self):
pass
@@ -41,7 +42,7 @@ class TransferBase(object):
dt_transfer[id] = [self.last_get_transfer[id][0] - last_transfer[id][0], self.last_get_transfer[id][1] - last_transfer[id][1]]
for id in curr_transfer.keys():
if id in self.force_update_transfer:
if id in self.force_update_transfer or id in self.mu_ports:
continue
#算出与上次记录的流量差值,保存于dt_transfer表
if id in last_transfer:
@@ -85,6 +86,9 @@ class TransferBase(object):
logging.error('load switchrule.py fail')
cur_servers = {}
new_servers = {}
allow_users = {}
mu_servers = {}
config = shell.get_config(False)
for row in rows:
try:
allow = switchrule.isTurnOn(row) and row['enable'] == 1 and row['u'] + row['d'] < row['transfer_enable']
@@ -93,11 +97,13 @@ class TransferBase(object):
port = row['port']
passwd = common.to_bytes(row['passwd'])
if hasattr(passwd, 'encode'):
passwd = passwd.encode('utf-8')
cfg = {'password': passwd}
if 'id' in row:
self.port_uid_table[row['port']] = row['id']
read_config_keys = ['method', 'obfs', 'obfs_param', 'protocol', 'protocol_param', 'forbidden_ip', 'forbidden_port']
read_config_keys = ['method', 'obfs', 'obfs_param', 'protocol', 'protocol_param', 'forbidden_ip', 'forbidden_port', 'speed_limit_per_con', 'speed_limit_per_user']
for name in read_config_keys:
if name in row and row[name]:
cfg[name] = row[name]
@@ -105,7 +111,10 @@ class TransferBase(object):
merge_config_keys = ['password'] + read_config_keys
for name in cfg.keys():
if hasattr(cfg[name], 'encode'):
cfg[name] = cfg[name].encode('utf-8')
try:
cfg[name] = cfg[name].encode('utf-8')
except Exception as e:
logging.warning('encode cfg key "%s" fail, val "%s"' % (name, cfg[name]))
if port not in cur_servers:
cur_servers[port] = passwd
@@ -113,34 +122,53 @@ class TransferBase(object):
logging.error('more than one user use the same port [%s]' % (port,))
continue
if ServerPool.get_instance().server_is_run(port) > 0:
if not allow:
logging.info('db stop server at port [%s]' % (port,))
ServerPool.get_instance().cb_del_server(port)
self.force_update_transfer.add(port)
else:
cfgchange = False
if port in ServerPool.get_instance().tcp_servers_pool:
relay = ServerPool.get_instance().tcp_servers_pool[port]
for name in merge_config_keys:
if name in cfg and not self.cmp(cfg[name], relay._config[name]):
cfgchange = True
break;
if not cfgchange and port in ServerPool.get_instance().tcp_ipv6_servers_pool:
relay = ServerPool.get_instance().tcp_ipv6_servers_pool[port]
for name in merge_config_keys:
if name in cfg and not self.cmp(cfg[name], relay._config[name]):
cfgchange = True
break;
#config changed
if 'protocol' in cfg and 'protocol_param' in cfg and common.to_str(cfg['protocol']) in obfs.mu_protocol():
if '#' in common.to_str(cfg['protocol_param']):
mu_servers[port] = passwd
allow = True
if allow:
if port not in mu_servers:
allow_users[port] = cfg
cfgchange = False
if port in ServerPool.get_instance().tcp_servers_pool:
relay = ServerPool.get_instance().tcp_servers_pool[port]
for name in merge_config_keys:
if name in cfg and not self.cmp(cfg[name], relay._config[name]):
cfgchange = True
break
if not cfgchange and port in ServerPool.get_instance().tcp_ipv6_servers_pool:
relay = ServerPool.get_instance().tcp_ipv6_servers_pool[port]
for name in merge_config_keys:
if (name in cfg) and ((name not in relay._config) or not self.cmp(cfg[name], relay._config[name])):
cfgchange = True
break
if port in mu_servers:
if ServerPool.get_instance().server_is_run(port) > 0:
if cfgchange:
logging.info('db stop server at port [%s] reason: config changed: %s' % (port, cfg))
ServerPool.get_instance().cb_del_server(port)
self.force_update_transfer.add(port)
new_servers[port] = (passwd, cfg)
else:
self.new_server(port, passwd, cfg)
else:
if ServerPool.get_instance().server_is_run(port) > 0:
if config['additional_ports_only'] or not allow:
logging.info('db stop server at port [%s]' % (port,))
ServerPool.get_instance().cb_del_server(port)
self.force_update_transfer.add(port)
else:
if cfgchange:
logging.info('db stop server at port [%s] reason: config changed: %s' % (port, cfg))
ServerPool.get_instance().cb_del_server(port)
self.force_update_transfer.add(port)
new_servers[port] = (passwd, cfg)
elif allow and ServerPool.get_instance().server_run_status(port) is False:
self.new_server(port, passwd, cfg)
elif not config['additional_ports_only'] and allow and port > 0 and port < 65536 and ServerPool.get_instance().server_run_status(port) is False:
self.new_server(port, passwd, cfg)
for row in last_rows:
if row['port'] in cur_servers:
@@ -159,6 +187,12 @@ class TransferBase(object):
passwd, cfg = new_servers[port]
self.new_server(port, passwd, cfg)
logging.debug('db allow users %s \nmu_servers %s' % (allow_users, mu_servers))
for port in mu_servers:
ServerPool.get_instance().update_mu_users(port, allow_users)
self.mu_ports = mu_servers
def clear_cache(self, port):
if port in self.force_update_transfer: del self.force_update_transfer[port]
if port in self.last_get_transfer: del self.last_get_transfer[port]
@@ -198,8 +232,13 @@ class TransferBase(object):
db_instance = obj()
ServerPool.get_instance()
shell.log_shadowsocks_version()
import resource
logging.info('current process RLIMIT_NOFILE resource: soft %d hard %d' % resource.getrlimit(resource.RLIMIT_NOFILE))
try:
import resource
logging.info('current process RLIMIT_NOFILE resource: soft %d hard %d' % resource.getrlimit(resource.RLIMIT_NOFILE))
except:
pass
try:
while True:
load_config()
@@ -209,6 +248,17 @@ class TransferBase(object):
rows = db_instance.pull_db_all_user()
if rows:
db_instance.pull_ok = True
config = shell.get_config(False)
for port in config['additional_ports']:
val = config['additional_ports'][port]
val['port'] = int(port)
val['enable'] = 1
val['transfer_enable'] = 1024 ** 7
val['u'] = 0
val['d'] = 0
if "password" in val:
val["passwd"] = val["password"]
rows.append(val)
db_instance.del_server_out_of_bound_safe(last_rows, rows)
last_rows = rows
except Exception as e:
@@ -250,7 +300,7 @@ class DbTransfer(TransferBase):
import json
config_path = get_config().MYSQL_CONFIG
cfg = None
with open(config_path, 'r+') as f:
with open(config_path, 'rb+') as f:
cfg = json.loads(f.read().decode('utf8'))
if cfg:
@@ -362,8 +412,18 @@ class DbTransfer(TransferBase):
class Dbv3Transfer(DbTransfer):
def __init__(self):
super(Dbv3Transfer, self).__init__()
self.key_list += ['id', 'method']
if get_config().API_INTERFACE == 'sspanelv3ssr':
self.update_node_state = True if get_config().API_INTERFACE != 'legendsockssr' else False
if self.update_node_state:
self.key_list += ['id']
self.key_list += ['method']
if self.update_node_state:
self.ss_node_info_name = 'ss_node_info_log'
if get_config().API_INTERFACE == 'sspanelv3ssr':
self.key_list += ['obfs', 'protocol']
if get_config().API_INTERFACE == 'glzjinmod':
self.key_list += ['obfs', 'protocol']
self.ss_node_info_name = 'ss_node_info'
else:
self.key_list += ['obfs', 'protocol']
self.start_time = time.time()
@@ -406,16 +466,17 @@ class Dbv3Transfer(DbTransfer):
query_sub_when2 += ' WHEN %s THEN d+%s' % (id, int(transfer[1] * self.cfg["transfer_mul"]))
update_transfer[id] = transfer
cur = conn.cursor()
try:
if id in self.port_uid_table:
cur.execute("INSERT INTO `user_traffic_log` (`id`, `user_id`, `u`, `d`, `node_id`, `rate`, `traffic`, `log_time`) VALUES (NULL, '" + \
str(self.port_uid_table[id]) + "', '" + str(transfer[0]) + "', '" + str(transfer[1]) + "', '" + \
str(self.cfg["node_id"]) + "', '" + str(self.cfg["transfer_mul"]) + "', '" + \
self.traffic_format((transfer[0] + transfer[1]) * self.cfg["transfer_mul"]) + "', unix_timestamp()); ")
except:
logging.warn('no `user_traffic_log` in db')
cur.close()
if self.update_node_state:
cur = conn.cursor()
try:
if id in self.port_uid_table:
cur.execute("INSERT INTO `user_traffic_log` (`id`, `user_id`, `u`, `d`, `node_id`, `rate`, `traffic`, `log_time`) VALUES (NULL, '" + \
str(self.port_uid_table[id]) + "', '" + str(transfer[0]) + "', '" + str(transfer[1]) + "', '" + \
str(self.cfg["node_id"]) + "', '" + str(self.cfg["transfer_mul"]) + "', '" + \
self.traffic_format((transfer[0] + transfer[1]) * self.cfg["transfer_mul"]) + "', unix_timestamp()); ")
except:
logging.warn('no `user_traffic_log` in db')
cur.close()
if query_sub_in is not None:
query_sub_in += ',%s' % id
@@ -434,25 +495,26 @@ class Dbv3Transfer(DbTransfer):
logging.error(e)
cur.close()
try:
cur = conn.cursor()
if self.update_node_state:
try:
cur.execute("INSERT INTO `ss_node_online_log` (`id`, `node_id`, `online_user`, `log_time`) VALUES (NULL, '" + \
str(self.cfg["node_id"]) + "', '" + str(alive_user_count) + "', unix_timestamp()); ")
except Exception as e:
logging.error(e)
cur.close()
cur = conn.cursor()
try:
cur.execute("INSERT INTO `ss_node_online_log` (`id`, `node_id`, `online_user`, `log_time`) VALUES (NULL, '" + \
str(self.cfg["node_id"]) + "', '" + str(alive_user_count) + "', unix_timestamp()); ")
except Exception as e:
logging.error(e)
cur.close()
cur = conn.cursor()
try:
cur.execute("INSERT INTO `ss_node_info_log` (`id`, `node_id`, `uptime`, `load`, `log_time`) VALUES (NULL, '" + \
str(self.cfg["node_id"]) + "', '" + str(self.uptime()) + "', '" + \
str(self.load()) + "', unix_timestamp()); ")
except Exception as e:
logging.error(e)
cur.close()
except:
logging.warn('no `ss_node_online_log` or `ss_node_info_log` in db')
cur = conn.cursor()
try:
cur.execute("INSERT INTO `" + self.ss_node_info_name + "` (`id`, `node_id`, `uptime`, `load`, `log_time`) VALUES (NULL, '" + \
str(self.cfg["node_id"]) + "', '" + str(self.uptime()) + "', '" + \
str(self.load()) + "', unix_timestamp()); ")
except Exception as e:
logging.error(e)
cur.close()
except:
logging.warn('no `ss_node_online_log` or `" + self.ss_node_info_name + "` in db')
conn.close()
return update_transfer
@@ -466,31 +528,32 @@ class Dbv3Transfer(DbTransfer):
cur = conn.cursor()
node_info_keys = ['traffic_rate']
try:
cur.execute("SELECT " + ','.join(node_info_keys) +" FROM ss_node where `id`='" + str(self.cfg["node_id"]) + "'")
nodeinfo = cur.fetchone()
except Exception as e:
logging.error(e)
nodeinfo = None
if self.update_node_state:
node_info_keys = ['traffic_rate']
try:
cur.execute("SELECT " + ','.join(node_info_keys) +" FROM ss_node where `id`='" + str(self.cfg["node_id"]) + "'")
nodeinfo = cur.fetchone()
except Exception as e:
logging.error(e)
nodeinfo = None
if nodeinfo == None:
rows = []
if nodeinfo == None:
rows = []
cur.close()
conn.commit()
logging.warn('None result when select node info from ss_node in db, maybe you set the incorrect node id')
return rows
cur.close()
conn.commit()
logging.warn('None result when select node info from ss_node in db, maybe you set the incorrect node id')
return rows
cur.close()
node_info_dict = {}
for column in range(len(nodeinfo)):
node_info_dict[node_info_keys[column]] = nodeinfo[column]
self.cfg['transfer_mul'] = float(node_info_dict['traffic_rate'])
node_info_dict = {}
for column in range(len(nodeinfo)):
node_info_dict[node_info_keys[column]] = nodeinfo[column]
self.cfg['transfer_mul'] = float(node_info_dict['traffic_rate'])
cur = conn.cursor()
try:
cur.execute("SELECT " + ','.join(keys) + " FROM user")
rows = []
cur.execute("SELECT " + ','.join(keys) + " FROM user")
for r in cur.fetchall():
d = {}
for column in range(len(keys)):
+4
View File
@@ -0,0 +1,4 @@
@echo off
If Not Exist "userapiconfig.py" Copy "apiconfig.py" "userapiconfig.py"
If Not Exist "user-config.json" Copy "config.json" "user-config.json"
If Not Exist "usermysql.json" Copy "mysql.json" "usermysql.json"
Regular → Executable
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 &
+102 -60
View File
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import traceback
@@ -10,21 +10,30 @@ import sys
import json
import base64
class MuJsonLoader(object):
def __init__(self):
self.json = None
def load(self, path):
with open(path, 'rb+') as f:
self.json = json.loads(f.read().decode('utf8'))
l = "[]"
try:
with open(path, 'rb+') as f:
l = f.read().decode('utf8')
except:
pass
self.json = json.loads(l)
def save(self, path):
if self.json:
if self.json is not None:
output = json.dumps(self.json, sort_keys=True, indent=4, separators=(',', ': '))
with open(path, 'r+') as f:
f.write(output)
with open(path, 'a'):
pass
with open(path, 'rb+') as f:
f.write(output.encode('utf8'))
f.truncate()
class MuMgr(object):
def __init__(self):
self.config_path = get_config().MUDB_FILE
@@ -37,10 +46,9 @@ class MuMgr(object):
if self.server_addr == '127.0.0.1':
self.server_addr = self.getipaddr()
def getipaddr(self, ifname = 'eth0'):
def getipaddr(self, ifname='eth0'):
import socket
import struct
import fcntl
ret = '127.0.0.1'
try:
ret = socket.gethostbyname(socket.getfqdn(socket.gethostname()))
@@ -48,53 +56,79 @@ class MuMgr(object):
pass
if ret == '127.0.0.1':
try:
import fcntl
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ret = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
except:
pass
return ret
def ssrlink(self, user, encode):
def ssrlink(self, user, encode, muid):
protocol = user.get('protocol', '')
obfs = user.get('obfs', '')
protocol = protocol.replace("_compatible", "")
obfs = obfs.replace("_compatible", "")
link = "%s:%s:%s:%s:%s:%s" % (self.server_addr, user['port'], protocol, user['method'], obfs, common.to_str(base64.urlsafe_b64encode(common.to_bytes(user['passwd']))).replace("=", ""))
return "ssr://" + ( encode and common.to_str(base64.urlsafe_b64encode(common.to_bytes(link))).replace("=", "") or link)
protocol_param = ''
if muid is not None:
protocol_param_ = user.get('protocol_param', '')
param = protocol_param_.split('#')
if len(param) == 2:
for row in self.data.json:
if int(row['port']) == muid:
param = str(muid) + ':' + row['passwd']
protocol_param = '/?protoparam=' + common.to_str(base64.urlsafe_b64encode(common.to_bytes(param))).replace("=", "")
break
link = ("%s:%s:%s:%s:%s:%s" % (self.server_addr, user['port'], protocol, user['method'], obfs, common.to_str(base64.urlsafe_b64encode(common.to_bytes(user['passwd']))).replace("=", ""))) + protocol_param
return "ssr://" + (encode and common.to_str(base64.urlsafe_b64encode(common.to_bytes(link))).replace("=", "") or link)
def userinfo(self, user):
def userinfo(self, user, muid = None):
ret = ""
key_list = ['user', 'port', 'method', 'passwd', 'protocol', 'protocol_param', 'obfs', 'obfs_param', 'transfer_enable', 'u', 'd']
for key in sorted(user):
if key in ['enable']:
if key not in key_list:
key_list.append(key)
for key in key_list:
if key in ['enable'] or key not in user:
continue
ret += '\n'
if key in ['transfer_enable', 'u', 'd'] :
val = user[key]
if (muid is not None) and (key in ['protocol_param']):
for row in self.data.json:
if int(row['port']) == muid:
ret += " %s : %s" % (key, str(muid) + ':' + row['passwd'])
break
elif key in ['transfer_enable', 'u', 'd']:
if muid is not None:
for row in self.data.json:
if int(row['port']) == muid:
val = row[key]
break
else:
val = user[key]
if val / 1024 < 4:
ret += " %s : %s" % (key, val)
elif val / 1024**2 < 4:
elif val / 1024 ** 2 < 4:
val /= float(1024)
ret += " %s : %s K Bytes" % (key, val)
elif val / 1024**3 < 4:
val /= float(1024**2)
elif val / 1024 ** 3 < 4:
val /= float(1024 ** 2)
ret += " %s : %s M Bytes" % (key, val)
else:
val /= float(1024**3)
val /= float(1024 ** 3)
ret += " %s : %s G Bytes" % (key, val)
else:
ret += " %s : %s" % (key, user[key])
ret += "\n " + self.ssrlink(user, False)
ret += "\n " + self.ssrlink(user, True)
ret += "\n " + self.ssrlink(user, False, muid)
ret += "\n " + self.ssrlink(user, True, muid)
return ret
def rand_pass(self):
return ''.join([random.choice('''ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~-_=+(){}[]^&%$@''') for i in range(8)])
def add(self, user):
up = {'enable': 1, 'u': 0, 'd': 0, 'method': "aes-128-cfb",
'protocol': "auth_sha1_v4",
up = {'enable': 1, 'u': 0, 'd': 0, 'method': "aes-128-ctr",
'protocol': "auth_aes128_md5",
'obfs': "tls1.2_ticket_auth_compatible",
'transfer_enable': 1125899906842624}
'transfer_enable': 9007199254740992}
up['passwd'] = self.rand_pass()
up.update(user)
@@ -170,36 +204,44 @@ class MuMgr(object):
if 'port' in user and row['port'] != user['port']:
match = False
if match:
print("### user [%s] info %s" % (row['user'], self.userinfo(row)))
muid = None
if 'muid' in user:
muid = user['muid']
print("### user [%s] info %s" % (row['user'], self.userinfo(row, muid)))
def print_server_help():
print('''usage: python mujson_manage.py -a|-d|-e|-c|-l [OPTION]...
print('''usage: python mujson_manage.py -a|-d|-e|-c|-l [OPTION]...
Actions:
-a ADD add/edit a user
-d DELETE delete a user
-e EDIT edit a user
-c CLEAR set u/d to zero
-l LIST display a user infomation or all users infomation
-a add/edit a user
-d delete a user
-e edit a user
-c set u&d to zero
-l display a user infomation or all users infomation
Options:
-u USER the user name
-p PORT server port
-k PASSWORD password
-m METHOD encryption method, default: aes-128-cfb
-O PROTOCOL protocol plugin, default: auth_sha1_v4
-o OBFS obfs plugin, default: tls1.2_ticket_auth_compatible
-G PROTOCOL_PARAM protocol plugin param
-g OBFS_PARAM obfs plugin param
-t TRANSFER max transfer for G bytes, default: 1048576, can be float point number
-f FORBID set forbidden ports. Example (ban 1~79 and 81~100): -f "1-79,81-100"
-u USER the user name
-p PORT server port (only this option must be set if add a user)
-k PASSWORD password
-m METHOD encryption method, default: aes-128-ctr
-O PROTOCOL protocol plugin, default: auth_aes128_md5
-o OBFS obfs plugin, default: tls1.2_ticket_auth_compatible
-G PROTOCOL_PARAM protocol plugin param
-g OBFS_PARAM obfs plugin param
-t TRANSFER max transfer for G bytes, default: 8388608 (8 PB or 8192 TB)
-f FORBID set forbidden ports. Example (ban 1~79 and 81~100): -f "1-79,81-100"
-i MUID set sub id to display (only work with -l)
-s SPEED set speed_limit_per_con
-S SPEED set speed_limit_per_user
General options:
-h, --help show this help message and exit
-h, --help show this help message and exit
''')
def main():
shortopts = 'adeclu:p:k:O:o:G:g:m:t:f:h'
shortopts = 'adeclu:i:p:k:O:o:G:g:m:t:f:hs:S:'
longopts = ['help']
action = None
user = {}
@@ -209,29 +251,24 @@ def main():
'+2': 'tls1.2_ticket_auth_compatible',
'2': 'tls1.2_ticket_auth'}
fast_set_protocol = {'0': 'origin',
'+1': 'verify_sha1_compatible',
'1': 'verify_sha1',
'2': 'auth_sha1',
'3': 'auth_sha1_v2',
'4': 'auth_sha1_v4',
's4': 'auth_sha1_v4',
'+s4': 'auth_sha1_v4_compatible',
'am': 'auth_aes128_md5',
'as': 'auth_aes128_sha1',
'ca': 'auth_chain_a',
}
fast_set_method = {'a0': 'aes-128-cfb',
'a1': 'aes-192-cfb',
'a2': 'aes-256-cfb',
fast_set_method = {'0': 'none',
'a1c': 'aes-128-cfb',
'a2c': 'aes-192-cfb',
'a3c': 'aes-256-cfb',
'r': 'rc4-md5',
'r6': 'rc4-md5-6',
'c': 'chacha20',
'ci': 'chacha20-ietf',
's': 'salsa20',
'b': 'bf-cfb',
'm0': 'camellia-128-cfb',
'm1': 'camellia-192-cfb',
'm2': 'camellia-256-cfb',
'a0t': 'aes-128-ctr',
'a1t': 'aes-192-ctr',
'a2t': 'aes-256-ctr'}
'a1': 'aes-128-ctr',
'a2': 'aes-192-ctr',
'a3': 'aes-256-ctr'}
try:
optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
for key, value in optlist:
@@ -247,6 +284,8 @@ def main():
action = 0
elif key == '-u':
user['user'] = value
elif key == '-i':
user['muid'] = int(value)
elif key == '-p':
user['port'] = int(value)
elif key == '-k':
@@ -265,6 +304,10 @@ def main():
user['obfs_param'] = value
elif key == '-G':
user['protocol_param'] = value
elif key == '-s':
user['speed_limit_per_con'] = int(value)
elif key == '-S':
user['speed_limit_per_user'] = int(value)
elif key == '-m':
if value in fast_set_method:
user['method'] = fast_set_method[value]
@@ -278,7 +321,7 @@ def main():
val = int(value)
except:
pass
user['transfer_enable'] = val * (1024 ** 3)
user['transfer_enable'] = int(val * 1024) * (1024 ** 2)
elif key in ('-h', '--help'):
print_server_help()
sys.exit(0)
@@ -313,4 +356,3 @@ def main():
if __name__ == '__main__':
main()
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 &
+3 -2
View File
@@ -31,7 +31,8 @@ from configloader import load_config, get_config
class MainThread(threading.Thread):
def __init__(self, obj):
threading.Thread.__init__(self)
super(MainThread, self).__init__()
self.daemon = True
self.obj = obj
def run(self):
@@ -54,7 +55,7 @@ def main():
thread.start()
try:
while thread.is_alive():
time.sleep(10)
thread.join(10.0)
except (KeyboardInterrupt, IOError, OSError) as e:
import traceback
traceback.print_exc()
+62 -11
View File
@@ -23,16 +23,18 @@
import os
import logging
import struct
import time
from shadowsocks import shell, eventloop, tcprelay, udprelay, asyncdns, common
import threading
import sys
import traceback
from socket import *
from configloader import load_config, get_config
class MainThread(threading.Thread):
def __init__(self, params):
threading.Thread.__init__(self)
super(MainThread, self).__init__()
self.params = params
def run(self):
@@ -79,12 +81,10 @@ class ServerPool(object):
loop.run()
except (KeyboardInterrupt, IOError, OSError) as e:
logging.error(e)
import traceback
traceback.print_exc()
os.exit(0)
except Exception as e:
logging.error(e)
import traceback
traceback.print_exc()
def server_is_run(self, port):
@@ -213,23 +213,62 @@ class ServerPool(object):
return True
def update_mu_users(self, port, users):
port = int(port)
if port in self.tcp_servers_pool:
try:
self.tcp_servers_pool[port].update_users(users)
except Exception as e:
logging.warn(e)
try:
self.udp_servers_pool[port].update_users(users)
except Exception as e:
logging.warn(e)
if port in self.tcp_ipv6_servers_pool:
try:
self.tcp_ipv6_servers_pool[port].update_users(users)
except Exception as e:
logging.warn(e)
try:
self.udp_ipv6_servers_pool[port].update_users(users)
except Exception as e:
logging.warn(e)
def get_server_transfer(self, port):
port = int(port)
uid = struct.pack('<I', port)
ret = [0, 0]
if port in self.tcp_servers_pool:
ret[0] = self.tcp_servers_pool[port].server_transfer_ul
ret[1] = self.tcp_servers_pool[port].server_transfer_dl
ret[0], ret[1] = self.tcp_servers_pool[port].get_ud()
if port in self.udp_servers_pool:
ret[0] += self.udp_servers_pool[port].server_transfer_ul
ret[1] += self.udp_servers_pool[port].server_transfer_dl
u, d = self.udp_servers_pool[port].get_ud()
ret[0] += u
ret[1] += d
if port in self.tcp_ipv6_servers_pool:
ret[0] += self.tcp_ipv6_servers_pool[port].server_transfer_ul
ret[1] += self.tcp_ipv6_servers_pool[port].server_transfer_dl
u, d = self.tcp_ipv6_servers_pool[port].get_ud()
ret[0] += u
ret[1] += d
if port in self.udp_ipv6_servers_pool:
ret[0] += self.udp_ipv6_servers_pool[port].server_transfer_ul
ret[1] += self.udp_ipv6_servers_pool[port].server_transfer_dl
u, d = self.udp_ipv6_servers_pool[port].get_ud()
ret[0] += u
ret[1] += d
return ret
def get_server_mu_transfer(self, server):
return server.get_users_ud()
def update_mu_transfer(self, user_dict, u, d):
for uid in u:
port = struct.unpack('<I', uid)[0]
if port not in user_dict:
user_dict[port] = [0, 0]
user_dict[port][0] += u[uid]
for uid in d:
port = struct.unpack('<I', uid)[0]
if port not in user_dict:
user_dict[port] = [0, 0]
user_dict[port][1] += d[uid]
def get_servers_transfer(self):
servers = self.tcp_servers_pool.copy()
servers.update(self.tcp_ipv6_servers_pool)
@@ -238,5 +277,17 @@ class ServerPool(object):
ret = {}
for port in servers.keys():
ret[port] = self.get_server_transfer(port)
for port in self.tcp_servers_pool:
u, d = self.get_server_mu_transfer(self.tcp_servers_pool[port])
self.update_mu_transfer(ret, u, d)
for port in self.tcp_ipv6_servers_pool:
u, d = self.get_server_mu_transfer(self.tcp_ipv6_servers_pool[port])
self.update_mu_transfer(ret, u, d)
for port in self.udp_servers_pool:
u, d = self.get_server_mu_transfer(self.udp_servers_pool[port])
self.update_mu_transfer(ret, u, d)
for port in self.udp_ipv6_servers_pool:
u, d = self.get_server_mu_transfer(self.udp_ipv6_servers_pool[port])
self.update_mu_transfer(ret, u, d)
return ret
+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)
}
+9 -2
View File
@@ -53,7 +53,7 @@ EVENT_NAMES = {
}
# we check timeouts every TIMEOUT_PRECISION seconds
TIMEOUT_PRECISION = 5
TIMEOUT_PRECISION = 2
class KqueueLoop(object):
@@ -176,6 +176,10 @@ class EventLoop(object):
del self._fdmap[fd]
self._impl.unregister(fd)
def removefd(self, fd):
del self._fdmap[fd]
self._impl.unregister(fd)
def add_periodic(self, callback):
self._periodic_callbacks.append(callback)
@@ -208,12 +212,13 @@ class EventLoop(object):
traceback.print_exc()
continue
handle = False
for sock, fd, event in events:
handler = self._fdmap.get(fd, None)
if handler is not None:
handler = handler[1]
try:
handler.handle_event(sock, fd, event)
handle = handler.handle_event(sock, fd, event) or handle
except (OSError, IOError) as e:
shell.print_exception(e)
now = time.time()
@@ -221,6 +226,8 @@ class EventLoop(object):
for callback in self._periodic_callbacks:
callback()
self._last_time = now
if events and not handle:
time.sleep(0.001)
def __del__(self):
self._impl.close()
Regular → Executable
+5 -3
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 &
+2 -3
View File
@@ -29,7 +29,6 @@ if __name__ == '__main__':
try:
from collections import OrderedDict
print("loaded collections.OrderedDict")
except:
from shadowsocks.ordereddict import OrderedDict
@@ -88,11 +87,11 @@ class LRUCache(collections.MutableMapping):
for key in self._keys_to_last_time:
return key
def sweep(self):
def sweep(self, sweep_item_cnt = SWEEP_MAX_ITEMS):
# O(n - m)
now = time.time()
c = 0
while c < SWEEP_MAX_ITEMS:
while c < sweep_item_cnt:
if len(self._keys_to_last_time) == 0:
break
for key in self._keys_to_last_time:
+13 -3
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):
@@ -53,6 +57,9 @@ class obfs(object):
def set_server_info(self, server_info):
return self.obfs.set_server_info(server_info)
def get_server_info(self):
return self.obfs.get_server_info()
def get_method_info(self, method):
method = method.lower()
m = method_supported.get(method)
@@ -62,6 +69,9 @@ class obfs(object):
m = self._method_info
return m[0](method)
def get_overhead(self, direction):
return self.obfs.get_overhead(direction)
def client_pre_encrypt(self, buf):
return self.obfs.client_pre_encrypt(buf)
@@ -92,8 +102,8 @@ class obfs(object):
def client_udp_post_decrypt(self, buf):
return self.obfs.client_udp_post_decrypt(buf)
def server_udp_pre_encrypt(self, buf):
return self.obfs.server_udp_pre_encrypt(buf)
def server_udp_pre_encrypt(self, buf, uid):
return self.obfs.server_udp_pre_encrypt(buf, uid)
def server_udp_post_decrypt(self, buf):
return self.obfs.server_udp_post_decrypt(buf)
Regular → Executable
+144 -741
View File
File diff suppressed because it is too large Load Diff
+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
+10 -5
View File
@@ -159,7 +159,7 @@ class http_simple(plain.plain):
if lines and len(lines) > 1:
for line in lines:
if match_begin(line, b"Host: "):
return line[6:]
return common.to_str(line[6:])
def not_match_return(self, buf):
self.has_sent_header = True
@@ -168,6 +168,11 @@ class http_simple(plain.plain):
return (b'E'*2048, False, False)
return (buf, True, False)
def error_return(self, buf):
self.has_sent_header = True
self.has_recv_header = True
return (b'E'*2048, False, False)
def server_decode(self, buf):
if self.has_recv_header:
return (buf, True, False)
@@ -199,10 +204,10 @@ class http_simple(plain.plain):
if host not in hosts:
return self.not_match_return(buf)
if len(ret_buf) < 4:
return self.not_match_return(buf)
return self.error_return(buf)
if len(datas) > 1:
ret_buf += datas[1]
if len(ret_buf) >= 7:
if len(ret_buf) >= 13:
self.has_recv_header = True
return (ret_buf, True, False)
return self.not_match_return(buf)
@@ -214,7 +219,7 @@ class http_post(http_simple):
super(http_post, self).__init__(method)
def boundary(self):
return b''.join([random.choice(b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") for i in range(32)])
return to_bytes(''.join([random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") for i in range(32)]))
def client_encode(self, buf):
if self.has_sent_header:
@@ -245,7 +250,7 @@ class http_post(http_simple):
http_head += b"User-Agent: " + random.choice(self.user_agent) + b"\r\n"
http_head += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\n"
http_head += b"Content-Type: multipart/form-data; boundary=" + self.boundary() + b"\r\nDNT: 1\r\n"
http_head += "Connection: keep-alive\r\n\r\n"
http_head += b"Connection: keep-alive\r\n\r\n"
self.has_sent_header = True
return http_head + buf
+49 -21
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):
@@ -54,6 +56,7 @@ class obfs_auth_data(object):
self.client_data = lru_cache.LRUCache(60 * 5)
self.client_id = os.urandom(32)
self.startup_time = int(time.time() - 60 * 30) & 0xFFFFFFFF
self.ticket_buf = {}
class tls_ticket_auth(plain.plain):
def __init__(self, method):
@@ -64,10 +67,14 @@ class tls_ticket_auth(plain.plain):
self.client_id = b''
self.max_time_dif = 60 * 60 * 24 # time dif (second) setting
self.tls_version = b'\x03\x03'
self.overhead = 5
def init_data(self):
return obfs_auth_data()
def get_overhead(self, direction): # direction: true for c->s false for s->c
return self.overhead
def sni(self, url):
url = common.to_bytes(url)
data = b"\x00" + struct.pack('>H', len(url)) + url
@@ -92,7 +99,8 @@ class tls_ticket_auth(plain.plain):
if len(buf) > 0:
ret += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf
return ret
self.send_buffer += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf
if len(buf) > 0:
self.send_buffer += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf
if self.handshake_status == 0:
self.handshake_status = 1
data = self.tls_version + self.pack_auth_data(self.server_info.data.client_id) + b"\x20" + self.server_info.data.client_id + binascii.unhexlify(b"001cc02bc02fcca9cca8cc14cc13c00ac014c009c013009c0035002f000a" + b"0100")
@@ -104,7 +112,9 @@ class tls_ticket_auth(plain.plain):
host = random.choice(hosts)
ext += self.sni(host)
ext += b"\x00\x17\x00\x00"
ext += b"\x00\x23\x00\xd0" + os.urandom(208) # ticket
if host not in self.server_info.data.ticket_buf:
self.server_info.data.ticket_buf[host] = os.urandom((struct.unpack('>H', os.urandom(2))[0] % 17 + 8) * 16)
ext += b"\x00\x23" + struct.pack('>H', len(self.server_info.data.ticket_buf[host])) + self.server_info.data.ticket_buf[host]
ext += binascii.unhexlify(b"000d001600140601060305010503040104030301030302010203")
ext += binascii.unhexlify(b"000500050100000000")
ext += binascii.unhexlify(b"00120000")
@@ -149,12 +159,14 @@ class tls_ticket_auth(plain.plain):
verify = buf[11:33]
if hmac.new(self.server_info.key + self.server_info.data.client_id, verify, hashlib.sha1).digest()[:10] != buf[33:43]:
raise Exception('client_decode data error')
if hmac.new(self.server_info.key + self.server_info.data.client_id, buf[:-10], hashlib.sha1).digest()[:10] != buf[-10:]:
raise Exception('client_decode data error')
return (b'', True)
def server_encode(self, buf):
if self.handshake_status == -1:
return buf
if self.handshake_status == 8:
if (self.handshake_status & 8) == 8:
ret = b''
while len(buf) > 2048:
size = min(struct.unpack('>H', os.urandom(2))[0] % 4096 + 100, len(buf))
@@ -163,18 +175,28 @@ class tls_ticket_auth(plain.plain):
if len(buf) > 0:
ret += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf
return ret
self.handshake_status = 3
self.handshake_status |= 8
data = self.tls_version + self.pack_auth_data(self.client_id) + b"\x20" + self.client_id + binascii.unhexlify(b"c02f000005ff01000100")
data = b"\x02\x00" + struct.pack('>H', len(data)) + data #server hello
data = b"\x16\x03\x03" + struct.pack('>H', len(data)) + data
data = b"\x16" + self.tls_version + struct.pack('>H', len(data)) + data
if random.randint(0, 8) < 1:
ticket = os.urandom((struct.unpack('>H', os.urandom(2))[0] % 164) * 2 + 64)
ticket = struct.pack('>H', len(ticket) + 4) + b"\x04\x00" + struct.pack('>H', len(ticket)) + ticket
data += b"\x16" + self.tls_version + ticket #New session ticket
data += b"\x14" + self.tls_version + b"\x00\x01\x01" #ChangeCipherSpec
data += b"\x16" + self.tls_version + b"\x00\x20" + os.urandom(22) #Finished
finish_len = random.choice([32, 40])
data += b"\x16" + self.tls_version + struct.pack('>H', finish_len) + os.urandom(finish_len - 10) #Finished
data += hmac.new(self.server_info.key + self.client_id, data, hashlib.sha1).digest()[:10]
if buf:
data += self.server_encode(buf)
return data
def decode_error_return(self, buf):
self.handshake_status = -1
if self.method == 'tls1.2_ticket_auth':
if self.overhead > 0:
self.server_info.overhead -= self.overhead
self.overhead = 0
if self.method in ['tls1.2_ticket_auth', 'tls1.2_ticket_fastauth']:
return (b'E'*2048, False, False)
return (buf, True, False)
@@ -182,7 +204,7 @@ class tls_ticket_auth(plain.plain):
if self.handshake_status == -1:
return (buf, True, False)
if self.handshake_status == 8:
if (self.handshake_status & 4) == 4:
ret = b''
self.recv_buffer += buf
while len(self.recv_buffer) > 5:
@@ -196,25 +218,27 @@ class tls_ticket_auth(plain.plain):
self.recv_buffer = self.recv_buffer[size+5:]
return (ret, True, False)
if self.handshake_status == 3:
if (self.handshake_status & 1) == 1:
self.recv_buffer += buf
buf = self.recv_buffer
verify = buf
verify_len = 43 - 10
if len(buf) < 43:
if len(buf) < 11:
raise Exception('server_decode data error')
if not match_begin(buf, b"\x14" + self.tls_version + b"\x00\x01\x01"): #ChangeCipherSpec
raise Exception('server_decode data error')
buf = buf[6:]
if not match_begin(buf, b"\x16" + self.tls_version + b"\x00\x20"): #Finished
if not match_begin(buf, b"\x16" + self.tls_version + b"\x00"): #Finished
raise Exception('server_decode data error')
verify_len = struct.unpack('>H', buf[3:5])[0] + 1 # 11 - 10
if len(verify) < verify_len + 10:
return (b'', False, False)
if hmac.new(self.server_info.key + self.client_id, verify[:verify_len], hashlib.sha1).digest()[:10] != verify[verify_len:verify_len+10]:
raise Exception('server_decode data error')
if len(buf) < 37:
raise Exception('server_decode data error')
self.recv_buffer = buf[37:]
self.handshake_status = 8
return self.server_decode(b'')
self.recv_buffer = verify[verify_len + 10:]
status = self.handshake_status
self.handshake_status |= 4
ret = self.server_decode(b'')
return ret;
#raise Exception("handshake data = %s" % (binascii.hexlify(buf)))
self.recv_buffer += buf
@@ -225,12 +249,13 @@ class tls_ticket_auth(plain.plain):
if not match_begin(buf, b'\x16\x03\x01'):
return self.decode_error_return(ogn_buf)
buf = buf[3:]
if struct.unpack('>H', buf[:2])[0] > len(buf) - 2:
header_len = struct.unpack('>H', buf[:2])[0]
if header_len > len(buf) - 2:
return (b'', False, False)
self.recv_buffer = self.recv_buffer[struct.unpack('>H', buf[:2])[0] + 5:]
self.handshake_status = 2
buf = buf[2:]
self.recv_buffer = self.recv_buffer[header_len + 5:]
self.handshake_status = 1
buf = buf[2:header_len + 2]
if not match_begin(buf, b'\x01\x00'): #client hello
logging.info("tls_auth not client hello message")
return self.decode_error_return(ogn_buf)
@@ -272,6 +297,9 @@ class tls_ticket_auth(plain.plain):
return self.decode_error_return(ogn_buf)
self.server_info.data.client_data.sweep()
self.server_info.data.client_data[verifyid[:22]] = sessionid
if len(self.recv_buffer) >= 11:
ret = self.server_decode(b'')
return (ret[0], True, True)
# (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back)
return (b'', False, True)
+8 -2
View File
@@ -40,6 +40,12 @@ class plain(object):
def init_data(self):
return b''
def get_overhead(self, direction): # direction: true for c->s false for s->c
return 0
def get_server_info(self):
return self.server_info
def set_server_info(self, server_info):
self.server_info = server_info
@@ -75,11 +81,11 @@ class plain(object):
def client_udp_post_decrypt(self, buf):
return buf
def server_udp_pre_encrypt(self, buf):
def server_udp_pre_encrypt(self, buf, uid):
return buf
def server_udp_post_decrypt(self, buf):
return buf
return (buf, None)
def dispose(self):
pass
-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''
sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, buf[:-10], hashlib.sha1).digest()[:10]
if sha1data != buf[-10:]:
return b''
return to_bytes(chr(ord(buf[0]) & 0xEF)) + buf[1:-10]
else:
return buf
Regular → Executable
+4 -3
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 &
+10 -10
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,18 +86,18 @@ def main():
bindv6 = config.get("out_bindv6", '')
if type(password_obfs) == list:
password = password_obfs[0]
obfs = password_obfs[1]
obfs = common.to_str(password_obfs[1])
if len(password_obfs) > 2:
protocol = password_obfs[2]
protocol = common.to_str(password_obfs[2])
elif type(password_obfs) == dict:
password = password_obfs.get('password', config_password)
method = password_obfs.get('method', method)
protocol = password_obfs.get('protocol', protocol)
protocol_param = password_obfs.get('protocol_param', protocol_param)
obfs = password_obfs.get('obfs', obfs)
obfs_param = password_obfs.get('obfs_param', obfs_param)
bind = password_obfs.get('bind', bind)
bindv6 = password_obfs.get('bindv6', bindv6)
method = common.to_str(password_obfs.get('method', method))
protocol = common.to_str(password_obfs.get('protocol', protocol))
protocol_param = common.to_str(password_obfs.get('protocol_param', protocol_param))
obfs = common.to_str(password_obfs.get('obfs', obfs))
obfs_param = common.to_str(password_obfs.get('obfs_param', obfs_param))
bind = password_obfs.get('out_bind', bind)
bindv6 = password_obfs.get('out_bindv6', bindv6)
else:
password = password_obfs
a_config = config.copy()
Regular → Executable
+52 -64
View File
@@ -52,49 +52,37 @@ def print_exception(e):
import traceback
traceback.print_exc()
def __version():
version_str = ''
try:
import pkg_resources
version_str = pkg_resources.get_distribution('shadowsocks').version
except Exception:
try:
from shadowsocks import version
version_str = version.version()
except Exception:
pass
return version_str
def print_shadowsocks():
version_str = ''
try:
import pkg_resources
version_str = pkg_resources.get_distribution('shadowsocks').version
except Exception:
try:
from shadowsocks import version
version_str = version.version()
except Exception:
pass
print('ShadowsocksR %s' % version_str)
print('ShadowsocksR %s' % __version())
def log_shadowsocks_version():
version_str = ''
try:
import pkg_resources
version_str = pkg_resources.get_distribution('shadowsocks').version
except Exception:
try:
from shadowsocks import version
version_str = version.version()
except Exception:
pass
logging.info('ShadowsocksR %s' % version_str)
logging.info('ShadowsocksR %s' % __version())
def find_config():
config_path = 'user-config.json'
if os.path.exists(config_path):
return config_path
config_path = os.path.join(os.path.dirname(__file__), '../', 'user-config.json')
if os.path.exists(config_path):
return config_path
user_config_path = 'user-config.json'
config_path = 'config.json'
if os.path.exists(config_path):
return config_path
config_path = os.path.join(os.path.dirname(__file__), '../', 'config.json')
if os.path.exists(config_path):
return config_path
return None
def sub_find(file_name):
if os.path.exists(file_name):
return file_name
file_name = os.path.join(os.path.abspath('..'), file_name)
return file_name if os.path.exists(file_name) else None
return sub_find(user_config_path) or sub_find(config_path)
def check_config(config, is_local):
if config.get('daemon', None) == 'stop':
@@ -119,21 +107,15 @@ def check_config(config, is_local):
config['server_port'] = int(config['server_port'])
if config.get('local_address', '') in [b'0.0.0.0']:
logging.warn('warning: local set to listen on 0.0.0.0, it\'s not safe')
logging.warning('warning: local set to listen on 0.0.0.0, it\'s not safe')
if config.get('server', '') in ['127.0.0.1', 'localhost']:
logging.warn('warning: server set to listen on %s:%s, are you sure?' %
logging.warning('warning: server set to listen on %s:%s, are you sure?' %
(to_str(config['server']), config['server_port']))
if (config.get('method', '') or '').lower() == 'table':
logging.warn('warning: table is not safe; please use a safer cipher, '
'like AES-256-CFB')
if (config.get('method', '') or '').lower() == 'rc4':
logging.warn('warning: RC4 is not safe; please use a safer cipher, '
'like AES-256-CFB')
if config.get('timeout', 300) < 100:
logging.warn('warning: your timeout %d seems too short' %
logging.warning('warning: your timeout %d seems too short' %
int(config.get('timeout')))
if config.get('timeout', 300) > 600:
logging.warn('warning: your timeout %d seems too long' %
logging.warning('warning: your timeout %d seems too long' %
int(config.get('timeout')))
if config.get('password') in [b'mypassword']:
logging.error('DON\'T USE DEFAULT PASSWORD! Please change it in your '
@@ -149,7 +131,8 @@ def check_config(config, is_local):
def get_config(is_local):
global verbose
config = {}
config_path = None
logging.basicConfig(level=logging.INFO,
format='%(levelname)-s: %(message)s')
if is_local:
@@ -161,23 +144,32 @@ def get_config(is_local):
longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers=',
'forbidden-ip=', 'user=', 'manager-address=', 'version']
try:
config_path = find_config()
optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
for key, value in optlist:
if key == '-c':
config_path = value
elif key in ('-h', '--help'):
print_help(is_local)
sys.exit(0)
elif key == '--version':
print_shadowsocks()
sys.exit(0)
else:
continue
if config_path is None:
config_path = find_config()
if config_path:
logging.info('loading config from %s' % config_path)
logging.debug('loading config from %s' % config_path)
with open(config_path, 'rb') as f:
try:
config = parse_json_in_str(remove_comment(f.read().decode('utf8')))
except ValueError as e:
logging.error('found an error in config.json: %s',
e.message)
logging.error('found an error in config.json: %s', str(e))
sys.exit(1)
else:
config = {}
v_count = 0
for key, value in optlist:
@@ -217,15 +209,7 @@ def get_config(is_local):
config['user'] = to_str(value)
elif key == '--forbidden-ip':
config['forbidden_ip'] = to_str(value)
elif key in ('-h', '--help'):
if is_local:
print_local_help()
else:
print_server_help()
sys.exit(0)
elif key == '--version':
print_shadowsocks()
sys.exit(0)
elif key == '-d':
config['daemon'] = to_str(value)
elif key == '--pid-file':
@@ -235,6 +219,8 @@ def get_config(is_local):
elif key == '-q':
v_count -= 1
config['verbose'] = v_count
else:
continue
except getopt.GetoptError as e:
print(e, file=sys.stderr)
print_help(is_local)
@@ -252,13 +238,15 @@ def get_config(is_local):
config['obfs'] = to_str(config.get('obfs', 'plain'))
config['obfs_param'] = to_str(config.get('obfs_param', ''))
config['port_password'] = config.get('port_password', None)
config['additional_ports'] = config.get('additional_ports', {})
config['additional_ports_only'] = config.get('additional_ports_only', False)
config['timeout'] = int(config.get('timeout', 300))
config['udp_timeout'] = int(config.get('udp_timeout', 120))
config['udp_cache'] = int(config.get('udp_cache', 64))
config['fast_open'] = config.get('fast_open', False)
config['workers'] = config.get('workers', 1)
config['pid-file'] = config.get('pid-file', '/var/run/shadowsocks.pid')
config['log-file'] = config.get('log-file', '/var/log/shadowsocks.log')
config['pid-file'] = config.get('pid-file', '/var/run/shadowsocksr.pid')
config['log-file'] = config.get('log-file', '/var/log/shadowsocksr.log')
config['verbose'] = config.get('verbose', False)
config['connect_verbose_info'] = config.get('connect_verbose_info', 0)
config['local_address'] = to_str(config.get('local_address', '127.0.0.1'))
@@ -285,7 +273,7 @@ def get_config(is_local):
sys.exit(2)
try:
config['ignore_bind'] = \
IPNetwork(config.get('ignore_bind', '127.0.0.0/8,::1/128'))
IPNetwork(config.get('ignore_bind', '127.0.0.0/8,::1/128,10.0.0.0/8,192.168.0.0/16'))
except Exception as e:
logging.error(e)
sys.exit(2)
Regular → Executable
+3 -1
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
+435 -116
View File
@@ -27,8 +27,9 @@ import binascii
import traceback
import random
import platform
import threading
from shadowsocks import encrypt, obfs, eventloop, shell, common, lru_cache
from shadowsocks import encrypt, obfs, eventloop, shell, common, lru_cache, version
from shadowsocks.common import pre_parse_header, parse_header
# we clear at most TIMEOUTS_CLEAN_SIZE timeouts each time
@@ -89,9 +90,39 @@ WAIT_STATUS_READING = 1
WAIT_STATUS_WRITING = 2
WAIT_STATUS_READWRITING = WAIT_STATUS_READING | WAIT_STATUS_WRITING
NETWORK_MTU = 1500
TCP_MSS = NETWORK_MTU - 40
BUF_SIZE = 32 * 1024
UDP_MAX_BUF_SIZE = 65536
class SpeedTester(object):
def __init__(self, max_speed = 0):
self.max_speed = max_speed * 1024
self.last_time = time.time()
self.sum_len = 0
def update_limit(self, max_speed):
self.max_speed = max_speed * 1024
def add(self, data_len):
if self.max_speed > 0:
cut_t = time.time()
self.sum_len -= (cut_t - self.last_time) * self.max_speed
if self.sum_len < 0:
self.sum_len = 0
self.last_time = cut_t
self.sum_len += data_len
def isExceed(self):
if self.max_speed > 0:
cut_t = time.time()
self.sum_len -= (cut_t - self.last_time) * self.max_speed
if self.sum_len < 0:
self.sum_len = 0
self.last_time = cut_t
return self.sum_len >= self.max_speed
return False
class TCPRelayHandler(object):
def __init__(self, server, fd_to_handlers, loop, local_sock, config,
dns_resolver, is_local):
@@ -101,28 +132,36 @@ class TCPRelayHandler(object):
self._local_sock = local_sock
self._remote_sock = None
self._remote_sock_v6 = None
self._local_sock_fd = None
self._remote_sock_fd = None
self._remotev6_sock_fd = None
self._remote_udp = False
self._config = config
self._dns_resolver = dns_resolver
self._add_ref = 0
if not self._create_encryptor(config):
return
self._client_address = local_sock.getpeername()[:2]
self._accept_address = local_sock.getsockname()[:2]
self._user = None
self._user_id = server._listen_port
self._update_tcp_mss(local_sock)
# TCP Relay works as either sslocal or ssserver
# if is_local, this is sslocal
self._is_local = is_local
self._stage = STAGE_INIT
try:
self._encryptor = encrypt.Encryptor(config['password'],
config['method'])
except Exception:
self._stage = STAGE_DESTROYED
logging.error('create encryptor fail at port %d', server._listen_port)
return
self._encrypt_correct = True
self._obfs = obfs.obfs(config['obfs'])
self._protocol = obfs.obfs(config['protocol'])
self._overhead = self._obfs.get_overhead(self._is_local) + self._protocol.get_overhead(self._is_local)
self._recv_buffer_size = BUF_SIZE - self._overhead
server_info = obfs.server_info(server.obfs_data)
server_info.host = config['server']
server_info.port = server._listen_port
#server_info.users = server.server_users
#server_info.update_user_func = self._update_user
server_info.client = self._client_address[0]
server_info.client_port = self._client_address[1]
server_info.protocol_param = ''
@@ -132,13 +171,16 @@ class TCPRelayHandler(object):
server_info.key_str = common.to_bytes(config['password'])
server_info.key = self._encryptor.cipher_key
server_info.head_len = 30
server_info.tcp_mss = 1460
server_info.tcp_mss = self._tcp_mss
server_info.buffer_size = self._recv_buffer_size
server_info.overhead = self._overhead
self._obfs.set_server_info(server_info)
self._protocol = obfs.obfs(config['protocol'])
server_info = obfs.server_info(server.protocol_data)
server_info.host = config['server']
server_info.port = server._listen_port
server_info.users = server.server_users
server_info.update_user_func = self._update_user
server_info.client = self._client_address[0]
server_info.client_port = self._client_address[1]
server_info.protocol_param = config['protocol_param']
@@ -148,7 +190,9 @@ class TCPRelayHandler(object):
server_info.key_str = common.to_bytes(config['password'])
server_info.key = self._encryptor.cipher_key
server_info.head_len = 30
server_info.tcp_mss = 1460
server_info.tcp_mss = self._tcp_mss
server_info.buffer_size = self._recv_buffer_size
server_info.overhead = self._overhead
self._protocol.set_server_info(server_info)
self._redir_list = config.get('redirect', ["*#0.0.0.0:0"])
@@ -164,25 +208,31 @@ class TCPRelayHandler(object):
self._upstream_status = WAIT_STATUS_READING
self._downstream_status = WAIT_STATUS_INIT
self._remote_address = None
if 'forbidden_ip' in config:
self._forbidden_iplist = config['forbidden_ip']
else:
self._forbidden_iplist = None
if 'forbidden_port' in config:
self._forbidden_portset = config['forbidden_port']
else:
self._forbidden_portset = None
self._forbidden_iplist = config.get('forbidden_ip', None)
self._forbidden_portset = config.get('forbidden_port', None)
if is_local:
self._chosen_server = self._get_a_server()
fd_to_handlers[local_sock.fileno()] = self
local_sock.setblocking(False)
local_sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
loop.add(local_sock, eventloop.POLL_IN | eventloop.POLL_ERR,
self._server)
self.last_activity = 0
self._update_activity()
self._server.add_connection(1)
self._server.stat_add(self._client_address[0], 1)
self._add_ref = 1
self.speed_tester_u = SpeedTester(config.get("speed_limit_per_con", 0))
self.speed_tester_d = SpeedTester(config.get("speed_limit_per_con", 0))
self._recv_u_max_size = BUF_SIZE
self._recv_d_max_size = BUF_SIZE
self._recv_pack_id = 0
self._udp_send_pack_id = 0
self._udpv6_send_pack_id = 0
local_sock.setblocking(False)
local_sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
self._local_sock_fd = local_sock.fileno()
fd_to_handlers[self._local_sock_fd] = self
loop.add(local_sock, eventloop.POLL_IN | eventloop.POLL_ERR, self._server)
self._stage = STAGE_INIT
def __hash__(self):
# default __hash__ is id / 16
@@ -203,6 +253,34 @@ class TCPRelayHandler(object):
logging.debug('chosen server: %s:%d', server, server_port)
return server, server_port
def _update_tcp_mss(self, local_sock):
self._tcp_mss = TCP_MSS
try:
tcp_mss = local_sock.getsockopt(socket.SOL_TCP, socket.TCP_MAXSEG)
if tcp_mss > 500 and tcp_mss <= 1500:
self._tcp_mss = tcp_mss
logging.debug("TCP MSS = %d" % (self._tcp_mss,))
except:
pass
def _create_encryptor(self, config):
try:
self._encryptor = encrypt.Encryptor(config['password'],
config['method'])
return True
except Exception:
self._stage = STAGE_DESTROYED
logging.error('create encryptor fail at port %d', self._server._listen_port)
def _update_user(self, user):
self._user = user
self._user_id = struct.unpack('<I', user)[0]
if self._user in self._server.server_users_cfg:
cfg = self._server.server_users_cfg[self._user]
speed = cfg.get('speed_limit_per_con', 0)
self.speed_tester_u.update_limit(speed)
self.speed_tester_d.update_limit(speed)
def _update_activity(self, data_len=0):
# tell the TCP Relay we have activities recently
# else it will think we are inactive and timed out
@@ -246,7 +324,6 @@ class TCPRelayHandler(object):
# and update the stream to wait for writing
if not sock:
return False
#logging.debug("_write_to_sock %s %s %s" % (self._remote_sock, sock, self._remote_udp))
uncomplete = False
if self._remote_udp and sock == self._remote_sock:
try:
@@ -254,15 +331,11 @@ class TCPRelayHandler(object):
#logging.info('UDP over TCP sendto %d %s' % (len(data), binascii.hexlify(data)))
while len(self._udp_data_send_buffer) > 6:
length = struct.unpack('>H', self._udp_data_send_buffer[:2])[0]
if length >= 0xff00:
length = struct.unpack('>H', self._udp_data_send_buffer[1:3])[0] + 0xff00
if length > len(self._udp_data_send_buffer):
break
data = self._udp_data_send_buffer[:length]
if length >= 0xff00:
data = data[1:]
self._udp_data_send_buffer = self._udp_data_send_buffer[length:]
frag = common.ord(data[2])
@@ -274,17 +347,16 @@ class TCPRelayHandler(object):
header_result = parse_header(data)
if header_result is None:
continue
connecttype, dest_addr, dest_port, header_length = header_result
addrs = socket.getaddrinfo(dest_addr, dest_port, 0,
socket.SOCK_DGRAM, socket.SOL_UDP)
#logging.info('UDP over TCP sendto %s:%d %d bytes from %s:%d' % (dest_addr, dest_port, len(data), self._client_address[0], self._client_address[1]))
if addrs:
af, socktype, proto, canonname, server_addr = addrs[0]
data = data[header_length:]
if af == socket.AF_INET6:
self._remote_sock_v6.sendto(data, (server_addr[0], dest_port))
connecttype, addrtype, dest_addr, dest_port, header_length = header_result
if (addrtype & 7) == 3:
af = common.is_ip(dest_addr)
if af == False:
handler = common.UDPAsyncDNSHandler(data[header_length:])
handler.resolve(self._dns_resolver, (dest_addr, dest_port), self._handle_server_dns_resolved)
else:
sock.sendto(data, (server_addr[0], dest_port))
return self._handle_server_dns_resolved("", (dest_addr, dest_port), dest_addr, data[header_length:])
else:
return self._handle_server_dns_resolved("", (dest_addr, dest_port), dest_addr, data[header_length:])
except Exception as e:
#trace = traceback.format_exc()
@@ -303,8 +375,8 @@ class TCPRelayHandler(object):
try:
if self._encrypt_correct:
if sock == self._remote_sock:
self._server.server_transfer_ul += len(data)
self._update_activity(len(data))
self._server.add_transfer_u(self._user, len(data))
self._update_activity(len(data))
if data:
l = len(data)
s = sock.send(data)
@@ -347,6 +419,33 @@ class TCPRelayHandler(object):
logging.error('write_all_to_sock:unknown socket from %s:%d' % (self._client_address[0], self._client_address[1]))
return True
def _handle_server_dns_resolved(self, error, remote_addr, server_addr, data):
if error:
return
try:
addrs = socket.getaddrinfo(server_addr, remote_addr[1], 0, socket.SOCK_DGRAM, socket.SOL_UDP)
if not addrs: # drop
return
af, socktype, proto, canonname, sa = addrs[0]
if af == socket.AF_INET6:
self._remote_sock_v6.sendto(data, (server_addr, remote_addr[1]))
if self._udpv6_send_pack_id == 0:
addr, port = self._remote_sock_v6.getsockname()[:2]
common.connect_log('UDPv6 sendto %s(%s):%d from %s:%d by user %d' %
(common.to_str(remote_addr[0]), common.to_str(server_addr), remote_addr[1], addr, port, self._user_id))
self._udpv6_send_pack_id += 1
else:
self._remote_sock.sendto(data, (server_addr, remote_addr[1]))
if self._udp_send_pack_id == 0:
addr, port = self._remote_sock.getsockname()[:2]
common.connect_log('UDP sendto %s(%s):%d from %s:%d by user %d' %
(common.to_str(remote_addr[0]), common.to_str(server_addr), remote_addr[1], addr, port, self._user_id))
self._udp_send_pack_id += 1
return True
except Exception as e:
shell.print_exception(e)
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
def _get_redirect_host(self, client_address, ogn_data):
host_list = self._redir_list or ["*#0.0.0.0:0"]
@@ -396,18 +495,16 @@ class TCPRelayHandler(object):
items_sum = common.to_str(host).rsplit('#', 1)
items_match = common.to_str(items_sum[0]).rsplit(':', 1)
items = common.to_str(items_sum[1]).rsplit(':', 1)
if len(items_match) > 1:
if self._server._listen_port != int(items_match[1]):
continue
match_port = 0
if len(items_match) > 1:
if items_match[1] != "*":
try:
match_port = int(items_match[1])
if self._server._listen_port != int(items_match[1]) and int(items_match[1]) != 0:
continue
except:
pass
if items_match[0] != "*" and common.match_regex(items_match[0], ogn_data) == False and \
not (match_port == self._server._listen_port or match_port == 0):
if items_match[0] != "*" and common.match_regex(
items_match[0], ogn_data) == False:
continue
if len(items) > 1:
try:
@@ -421,7 +518,7 @@ class TCPRelayHandler(object):
return ("0.0.0.0", 0)
def _handel_protocol_error(self, client_address, ogn_data):
logging.warn("Protocol ERROR, TCP ogn data %s from %s:%d via port %d" % (binascii.hexlify(ogn_data), client_address[0], client_address[1], self._server._listen_port))
logging.warn("Protocol ERROR, TCP ogn data %s from %s:%d via port %d by UID %d" % (binascii.hexlify(ogn_data), client_address[0], client_address[1], self._server._listen_port, self._user_id))
self._encrypt_correct = False
#create redirect or disconnect by hash code
host, port = self._get_redirect_host(client_address, ogn_data)
@@ -510,7 +607,7 @@ class TCPRelayHandler(object):
# just trim VER CMD RSV
data = data[3:]
else:
logging.error('unknown command %d', cmd)
logging.error('invalid command %d', cmd)
self.destroy()
return
@@ -524,16 +621,26 @@ class TCPRelayHandler(object):
header_result = parse_header(data)
if header_result is not None:
try:
common.to_str(header_result[1])
common.to_str(header_result[2])
except Exception as e:
header_result = None
if header_result is None:
data = self._handel_protocol_error(self._client_address, ogn_data)
header_result = parse_header(data)
connecttype, remote_addr, remote_port, header_length = header_result
common.connect_log('%s connecting %s:%d via port %d' %
((connecttype == 0) and 'TCP' or 'UDP',
common.to_str(remote_addr), remote_port, self._server._listen_port))
self._overhead = self._obfs.get_overhead(self._is_local) + self._protocol.get_overhead(self._is_local)
self._recv_buffer_size = BUF_SIZE - self._overhead
server_info = self._obfs.get_server_info()
server_info.buffer_size = self._recv_buffer_size
server_info = self._protocol.get_server_info()
server_info.buffer_size = self._recv_buffer_size
connecttype, addrtype, remote_addr, remote_port, header_length = header_result
if connecttype != 0:
pass
#common.connect_log('UDP over TCP by user %d' %
# (self._user_id, ))
else:
common.connect_log('TCP request %s:%d by user %d' %
(common.to_str(remote_addr), remote_port, self._user_id))
self._remote_address = (common.to_str(remote_addr), remote_port)
self._remote_udp = (connecttype != 0)
# pause reading
@@ -602,25 +709,27 @@ class TCPRelayHandler(object):
if self._forbidden_iplist:
if common.to_str(sa[0]) in self._forbidden_iplist:
if self._remote_address:
raise Exception('IP %s is in forbidden list, when connect to %s:%d via port %d' %
(common.to_str(sa[0]), self._remote_address[0], self._remote_address[1], self._server._listen_port))
raise Exception('IP %s is in forbidden list, when connect to %s:%d via port %d by UID %d' %
(common.to_str(sa[0]), self._remote_address[0], self._remote_address[1], self._server._listen_port, self._user_id))
raise Exception('IP %s is in forbidden list, reject' %
common.to_str(sa[0]))
if self._forbidden_portset:
if sa[1] in self._forbidden_portset:
if self._remote_address:
raise Exception('Port %d is in forbidden list, when connect to %s:%d via port %d' %
(sa[1], self._remote_address[0], self._remote_address[1], self._server._listen_port))
raise Exception('Port %d is in forbidden list, when connect to %s:%d via port %d by UID %d' %
(sa[1], self._remote_address[0], self._remote_address[1], self._server._listen_port, self._user_id))
raise Exception('Port %d is in forbidden list, reject' % sa[1])
remote_sock = socket.socket(af, socktype, proto)
self._remote_sock = remote_sock
self._fd_to_handlers[remote_sock.fileno()] = self
self._remote_sock_fd = remote_sock.fileno()
self._fd_to_handlers[self._remote_sock_fd] = self
if self._remote_udp:
af, socktype, proto, canonname, sa = addrs_v6[0]
remote_sock_v6 = socket.socket(af, socktype, proto)
self._remote_sock_v6 = remote_sock_v6
self._fd_to_handlers[remote_sock_v6.fileno()] = self
self._remotev6_sock_fd = remote_sock_v6.fileno()
self._fd_to_handlers[self._remotev6_sock_fd] = self
remote_sock.setblocking(False)
if self._remote_udp:
@@ -643,7 +752,6 @@ class TCPRelayHandler(object):
if result:
ip = result[1]
if ip:
try:
self._stage = STAGE_CONNECTING
remote_addr = ip
@@ -681,6 +789,10 @@ class TCPRelayHandler(object):
pass # always goto here
else:
raise e
addr, port = self._remote_sock.getsockname()[:2]
common.connect_log('TCP connecting %s(%s):%d from %s:%d by user %d' %
(common.to_str(self._remote_address[0]), common.to_str(remote_addr), remote_port, addr, port, self._user_id))
self._loop.add(remote_sock,
eventloop.POLL_ERR | eventloop.POLL_OUT,
self._server)
@@ -700,15 +812,36 @@ class TCPRelayHandler(object):
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
self.destroy()
def _get_read_size(self, sock, recv_buffer_size, up):
if self._overhead == 0:
return recv_buffer_size
buffer_size = len(sock.recv(recv_buffer_size, socket.MSG_PEEK))
frame_size = self._tcp_mss - self._overhead
if up:
buffer_size = min(buffer_size, self._recv_u_max_size)
self._recv_u_max_size = min(self._recv_u_max_size + frame_size, BUF_SIZE)
else:
buffer_size = min(buffer_size, self._recv_d_max_size)
self._recv_d_max_size = min(self._recv_d_max_size + frame_size, BUF_SIZE)
if buffer_size == recv_buffer_size:
return buffer_size
if buffer_size > frame_size:
buffer_size = int(buffer_size / frame_size) * frame_size
return buffer_size
def _on_local_read(self):
# handle all local read events and dispatch them to methods for
# each stage
if not self._local_sock:
return
is_local = self._is_local
if is_local:
recv_buffer_size = self._get_read_size(self._local_sock, self._recv_buffer_size, True)
else:
recv_buffer_size = BUF_SIZE
data = None
try:
data = self._local_sock.recv(BUF_SIZE)
data = self._local_sock.recv(recv_buffer_size)
except (OSError, IOError) as e:
if eventloop.errno_from_exception(e) in \
(errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK):
@@ -716,12 +849,19 @@ class TCPRelayHandler(object):
if not data:
self.destroy()
return
self.speed_tester_u.add(len(data))
self._server.speed_tester_u(self._user_id).add(len(data))
ogn_data = data
if not is_local:
if self._encryptor is not None:
if self._encrypt_correct:
try:
obfs_decode = self._obfs.server_decode(data)
if self._stage == STAGE_INIT:
self._overhead = self._obfs.get_overhead(self._is_local) + self._protocol.get_overhead(self._is_local)
server_info = self._protocol.get_server_info()
server_info.overhead = self._overhead
except Exception as e:
shell.print_exception(e)
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
@@ -729,7 +869,15 @@ class TCPRelayHandler(object):
return
if obfs_decode[2]:
data = self._obfs.server_encode(b'')
self._write_to_sock(data, self._local_sock)
try:
self._write_to_sock(data, self._local_sock)
except Exception as e:
shell.print_exception(e)
if self._config['verbose']:
traceback.print_exc()
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
self.destroy()
return
if obfs_decode[1]:
if not self._protocol.obfs.server_info.recv_iv:
iv_len = len(self._protocol.obfs.server_info.iv)
@@ -756,6 +904,7 @@ class TCPRelayHandler(object):
shell.print_exception(e)
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
self.destroy()
return
else:
return
if not data:
@@ -767,12 +916,10 @@ class TCPRelayHandler(object):
data = self._encryptor.encrypt(data)
data = self._obfs.client_encode(data)
self._write_to_sock(data, self._remote_sock)
return
elif is_local and self._stage == STAGE_INIT:
# TODO check auth method
self._write_to_sock(b'\x05\00', self._local_sock)
self._stage = STAGE_ADDR
return
elif self._stage == STAGE_CONNECTING:
self._handle_stage_connecting(data)
elif (is_local and self._stage == STAGE_ADDR) or \
@@ -796,13 +943,15 @@ class TCPRelayHandler(object):
ip = socket.inet_pton(socket.AF_INET6, addr[0])
data = b'\x00\x04' + ip + port + data
size = len(data) + 2
if size >= 0xff00:
data = common.chr(0xff) + struct.pack('>H', size - 0xff00 + 1) + data
else:
data = struct.pack('>H', size) + data
data = struct.pack('>H', size) + data
#logging.info('UDP over TCP recvfrom %s:%d %d bytes to %s:%d' % (addr[0], addr[1], len(data), self._client_address[0], self._client_address[1]))
else:
data = self._remote_sock.recv(BUF_SIZE)
if self._is_local:
recv_buffer_size = BUF_SIZE
else:
recv_buffer_size = self._get_read_size(self._remote_sock, self._recv_buffer_size, False)
data = self._remote_sock.recv(recv_buffer_size)
self._recv_pack_id += 1
except (OSError, IOError) as e:
if eventloop.errno_from_exception(e) in \
(errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK, 10035): #errno.WSAEWOULDBLOCK
@@ -810,6 +959,9 @@ class TCPRelayHandler(object):
if not data:
self.destroy()
return
self.speed_tester_d.add(len(data))
self._server.speed_tester_d(self._user_id).add(len(data))
if self._encryptor is not None:
if self._is_local:
try:
@@ -828,6 +980,8 @@ class TCPRelayHandler(object):
data = self._encryptor.decrypt(obfs_decode[0])
try:
data = self._protocol.client_post_decrypt(data)
if self._recv_pack_id == 1:
self._tcp_mss = self._protocol.get_server_info().tcp_mss
except Exception as e:
shell.print_exception(e)
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
@@ -838,8 +992,8 @@ class TCPRelayHandler(object):
data = self._protocol.server_pre_encrypt(data)
data = self._encryptor.encrypt(data)
data = self._obfs.server_encode(data)
self._update_activity(len(data))
self._server.server_transfer_dl += len(data)
self._server.add_transfer_d(self._user, len(data))
self._update_activity(len(data))
else:
return
try:
@@ -871,52 +1025,72 @@ class TCPRelayHandler(object):
self._update_stream(STREAM_UP, WAIT_STATUS_READING)
def _on_local_error(self):
logging.debug('got local error')
if self._local_sock:
logging.error(eventloop.get_sock_error(self._local_sock))
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
err = eventloop.get_sock_error(self._local_sock)
if err.errno not in [errno.ECONNRESET, errno.EPIPE]:
logging.error(err)
logging.error("local error, exception from %s:%d" % (self._client_address[0], self._client_address[1]))
self.destroy()
def _on_remote_error(self):
logging.debug('got remote error')
if self._remote_sock:
logging.error(eventloop.get_sock_error(self._remote_sock))
if self._remote_address:
logging.error("when connect to %s:%d" % (self._remote_address[0], self._remote_address[1]))
else:
logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1]))
err = eventloop.get_sock_error(self._remote_sock)
if err.errno not in [errno.ECONNRESET]:
logging.error(err)
if self._remote_address:
logging.error("remote error, when connect to %s:%d" % (self._remote_address[0], self._remote_address[1]))
else:
logging.error("remote error, exception from %s:%d" % (self._client_address[0], self._client_address[1]))
self.destroy()
def handle_event(self, sock, event):
def handle_event(self, sock, fd, event):
# handle all events in this handler and dispatch them to methods
handle = False
if self._stage == STAGE_DESTROYED:
logging.debug('ignore handle_event: destroyed')
return
# order is important
if sock == self._remote_sock or sock == self._remote_sock_v6:
return True
if self._user is not None and self._user not in self._server.server_users:
self.destroy()
return True
if fd == self._remote_sock_fd or fd == self._remotev6_sock_fd:
if event & eventloop.POLL_ERR:
handle = True
self._on_remote_error()
if self._stage == STAGE_DESTROYED:
return
if event & (eventloop.POLL_IN | eventloop.POLL_HUP):
self._on_remote_read(sock == self._remote_sock)
if self._stage == STAGE_DESTROYED:
return
if event & eventloop.POLL_OUT:
elif event & (eventloop.POLL_IN | eventloop.POLL_HUP):
if not self.speed_tester_d.isExceed() and not self._server.speed_tester_d(self._user_id).isExceed():
handle = True
self._on_remote_read(sock == self._remote_sock)
else:
self._recv_d_max_size = self._tcp_mss - self._overhead
elif event & eventloop.POLL_OUT:
handle = True
self._on_remote_write()
elif sock == self._local_sock:
elif fd == self._local_sock_fd:
if event & eventloop.POLL_ERR:
handle = True
self._on_local_error()
if self._stage == STAGE_DESTROYED:
return
if event & (eventloop.POLL_IN | eventloop.POLL_HUP):
self._on_local_read()
if self._stage == STAGE_DESTROYED:
return
if event & eventloop.POLL_OUT:
elif event & (eventloop.POLL_IN | eventloop.POLL_HUP):
if not self.speed_tester_u.isExceed() and not self._server.speed_tester_u(self._user_id).isExceed():
handle = True
self._on_local_read()
else:
self._recv_u_max_size = self._tcp_mss - self._overhead
elif event & eventloop.POLL_OUT:
handle = True
self._on_local_write()
else:
logging.warn('unknown socket from %s:%d' % (self._client_address[0], self._client_address[1]))
try:
self._loop.removefd(fd)
except Exception as e:
shell.print_exception(e)
try:
del self._fd_to_handlers[fd]
except Exception as e:
shell.print_exception(e)
sock.close()
return handle
def _log_error(self, e):
logging.error('%s when handling connection from %s:%d' %
@@ -946,25 +1120,40 @@ class TCPRelayHandler(object):
if self._remote_sock:
logging.debug('destroying remote')
try:
self._loop.remove(self._remote_sock)
self._loop.removefd(self._remote_sock_fd)
except Exception as e:
pass
del self._fd_to_handlers[self._remote_sock.fileno()]
shell.print_exception(e)
try:
if self._remote_sock_fd is not None:
del self._fd_to_handlers[self._remote_sock_fd]
except Exception as e:
shell.print_exception(e)
self._remote_sock.close()
self._remote_sock = None
if self._remote_sock_v6:
logging.debug('destroying remote')
logging.debug('destroying remote_v6')
try:
self._loop.remove(self._remote_sock_v6)
self._loop.removefd(self._remotev6_sock_fd)
except Exception as e:
pass
del self._fd_to_handlers[self._remote_sock_v6.fileno()]
shell.print_exception(e)
try:
if self._remotev6_sock_fd is not None:
del self._fd_to_handlers[self._remotev6_sock_fd]
except Exception as e:
shell.print_exception(e)
self._remote_sock_v6.close()
self._remote_sock_v6 = None
if self._local_sock:
logging.debug('destroying local')
self._loop.remove(self._local_sock)
del self._fd_to_handlers[self._local_sock.fileno()]
try:
self._loop.removefd(self._local_sock_fd)
except Exception as e:
shell.print_exception(e)
try:
if self._local_sock_fd is not None:
del self._fd_to_handlers[self._local_sock_fd]
except Exception as e:
shell.print_exception(e)
self._local_sock.close()
self._local_sock = None
if self._obfs:
@@ -976,8 +1165,9 @@ class TCPRelayHandler(object):
self._encryptor = None
self._dns_resolver.remove_callback(self._handle_dns_resolved)
self._server.remove_handler(self)
self._server.add_connection(-1)
self._server.stat_add(self._client_address[0], -1)
if self._add_ref > 0:
self._server.add_connection(-1)
self._server.stat_add(self._client_address[0], -1)
class TCPRelay(object):
def __init__(self, config, dns_resolver, is_local, stat_callback=None, stat_counter=None):
@@ -989,6 +1179,13 @@ class TCPRelay(object):
self._fd_to_handlers = {}
self.server_transfer_ul = 0
self.server_transfer_dl = 0
self.server_users = {}
self.server_users_cfg = {}
self.server_user_transfer_ul = {}
self.server_user_transfer_dl = {}
self.mu = False
self._speed_tester_u = {}
self._speed_tester_d = {}
self.server_connections = 0
self.protocol_data = obfs.obfs(config['protocol']).init_data()
self.obfs_data = obfs.obfs(config['obfs']).init_data()
@@ -1008,6 +1205,9 @@ class TCPRelay(object):
listen_port = config['server_port']
self._listen_port = listen_port
if common.to_str(config['protocol']) in obfs.mu_protocol():
self._update_users(None, None)
addrs = socket.getaddrinfo(listen_addr, listen_port, 0,
socket.SOCK_STREAM, socket.SOL_TCP)
if len(addrs) == 0:
@@ -1026,6 +1226,7 @@ class TCPRelay(object):
self._config['fast_open'] = False
server_socket.listen(config.get('max_connect', 1024))
self._server_socket = server_socket
self._server_socket_fd = server_socket.fileno()
self._stat_counter = stat_counter
self._stat_callback = stat_callback
@@ -1047,6 +1248,104 @@ class TCPRelay(object):
self.server_connections += val
logging.debug('server port %5d connections = %d' % (self._listen_port, self.server_connections,))
def get_ud(self):
return (self.server_transfer_ul, self.server_transfer_dl)
def get_users_ud(self):
return (self.server_user_transfer_ul.copy(), self.server_user_transfer_dl.copy())
def _update_users(self, protocol_param, acl):
if protocol_param is None:
protocol_param = self._config['protocol_param']
param = common.to_bytes(protocol_param).split(b'#')
if len(param) == 2:
self.mu = True
user_list = param[1].split(b',')
if user_list:
for user in user_list:
items = user.split(b':')
if len(items) == 2:
user_int_id = int(items[0])
uid = struct.pack('<I', user_int_id)
if acl is not None and user_int_id not in acl:
self.del_user(uid)
else:
passwd = items[1]
self.add_user(uid, {'password':passwd})
def _update_user(self, id, passwd):
uid = struct.pack('<I', id)
self.add_user(uid, passwd)
def update_users(self, users):
for uid in list(self.server_users.keys()):
id = struct.unpack('<I', uid)[0]
if id not in users:
self.del_user(uid)
for id in users:
uid = struct.pack('<I', id)
self.add_user(uid, users[id])
def add_user(self, uid, cfg): # user: binstr[4], passwd: str
passwd = cfg['password']
self.server_users[uid] = common.to_bytes(passwd)
self.server_users_cfg[uid] = cfg
speed = cfg.get("speed_limit_per_user", 0)
if uid in self._speed_tester_u:
self._speed_tester_u[uid].update_limit(speed)
else:
self._speed_tester_u[uid] = SpeedTester(speed)
if uid in self._speed_tester_d:
self._speed_tester_d[uid].update_limit(speed)
else:
self._speed_tester_d[uid] = SpeedTester(speed)
def del_user(self, uid):
if uid in self.server_users:
del self.server_users[uid]
if uid in self.server_users_cfg:
del self.server_users_cfg[uid]
def add_transfer_u(self, user, transfer):
if user is None:
self.server_transfer_ul += transfer
else:
if user not in self.server_user_transfer_ul:
self.server_user_transfer_ul[user] = 0
self.server_user_transfer_ul[user] += transfer + self.server_transfer_ul
self.server_transfer_ul = 0
def add_transfer_d(self, user, transfer):
if user is None:
self.server_transfer_dl += transfer
else:
if user not in self.server_user_transfer_dl:
self.server_user_transfer_dl[user] = 0
self.server_user_transfer_dl[user] += transfer + self.server_transfer_dl
self.server_transfer_dl = 0
def speed_tester_u(self, uid):
if uid not in self._speed_tester_u:
if self.mu: #TODO
self._speed_tester_u[uid] = SpeedTester(self._config.get("speed_limit_per_user", 0))
else:
self._speed_tester_u[uid] = SpeedTester(self._config.get("speed_limit_per_user", 0))
return self._speed_tester_u[uid]
def speed_tester_d(self, uid):
if uid not in self._speed_tester_d:
if self.mu: #TODO
self._speed_tester_d[uid] = SpeedTester(self._config.get("speed_limit_per_user", 0))
else:
self._speed_tester_d[uid] = SpeedTester(self._config.get("speed_limit_per_user", 0))
return self._speed_tester_d[uid]
def update_limit(self, uid, max_speed):
if uid in self._speed_tester_u:
self._speed_tester_u[uid].update_limit(max_speed)
if uid in self._speed_tester_d:
self._speed_tester_d[uid].update_limit(max_speed)
def update_stat(self, port, stat_dict, val):
newval = stat_dict.get(0, 0) + val
stat_dict[0] = newval
@@ -1102,6 +1401,7 @@ class TCPRelay(object):
def handle_event(self, sock, fd, event):
# handle events and dispatch to handlers
handle = False
if sock:
logging.log(shell.VERBOSE_LEVEL, 'fd %d %s', fd,
eventloop.EVENT_NAMES.get(event, event))
@@ -1109,6 +1409,8 @@ class TCPRelay(object):
if event & eventloop.POLL_ERR:
# TODO
raise Exception('server_socket error')
handler = None
handle = True
try:
logging.debug('accept')
conn = self._server_socket.accept()
@@ -1126,18 +1428,35 @@ class TCPRelay(object):
shell.print_exception(e)
if self._config['verbose']:
traceback.print_exc()
if handler:
handler.destroy()
else:
if sock:
handler = self._fd_to_handlers.get(fd, None)
if handler:
handler.handle_event(sock, event)
handle = handler.handle_event(sock, fd, event)
else:
logging.warn('unknown fd')
handle = True
try:
self._eventloop.removefd(fd)
except Exception as e:
shell.print_exception(e)
sock.close()
else:
logging.warn('poll removed fd')
handle = True
if fd in self._fd_to_handlers:
try:
del self._fd_to_handlers[fd]
except Exception as e:
shell.print_exception(e)
return handle
def handle_periodic(self):
if self._closed:
if self._server_socket:
self._eventloop.remove(self._server_socket)
self._eventloop.removefd(self._server_socket_fd)
self._server_socket.close()
self._server_socket = None
logging.info('closed TCP port %d', self._listen_port)
@@ -1151,7 +1470,7 @@ class TCPRelay(object):
if not next_tick:
if self._eventloop:
self._eventloop.remove_periodic(self.handle_periodic)
self._eventloop.remove(self._server_socket)
self._eventloop.removefd(self._server_socket_fd)
self._server_socket.close()
for handler in list(self._fd_to_handlers.values()):
handler.destroy()
+187 -925
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright 2016 breakwa11
# Copyright 2017 breakwa11
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@@ -16,5 +16,5 @@
# under the License.
def version():
return '3.0.1 2017-01-03'
return '3.4.0 2017-07-27'
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