From a272b9dbda2fa837355cbc6e3a2ebc4ae4276c01 Mon Sep 17 00:00:00 2001 From: Newnius Date: Fri, 2 Feb 2018 15:02:09 +0800 Subject: [PATCH] add ssr --- shadowsocksr/3.3.3/Dockerfile | 11 + shadowsocksr/3.3.3/README.md | 25 + shadowsocksr/3.3.3/shadowsocksr/asyncmgr.py | 99 ++ .../3.3.3/shadowsocksr/configloader.py | 15 + .../3.3.3/shadowsocksr/importloader.py | 24 + shadowsocksr/3.3.3/shadowsocksr/server.py | 66 + .../3.3.3/shadowsocksr/server_pool.py | 293 ++++ shadowsocksr/3.3.3/shadowsocksr/setup.py | 39 + .../shadowsocksr/shadowsocks/__init__.py | 18 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 268 bytes .../__pycache__/asyncdns.cpython-37.pyc | Bin 0 -> 12004 bytes .../__pycache__/common.cpython-37.pyc | Bin 0 -> 11558 bytes .../__pycache__/daemon.cpython-37.pyc | Bin 0 -> 4359 bytes .../__pycache__/encrypt.cpython-37.pyc | Bin 0 -> 5410 bytes .../__pycache__/eventloop.cpython-37.pyc | Bin 0 -> 7602 bytes .../__pycache__/lru_cache.cpython-37.pyc | Bin 0 -> 3847 bytes .../__pycache__/manager.cpython-37.pyc | Bin 0 -> 7261 bytes .../__pycache__/obfs.cpython-37.pyc | Bin 0 -> 4312 bytes .../__pycache__/shell.cpython-37.pyc | Bin 0 -> 11815 bytes .../__pycache__/tcprelay.cpython-37.pyc | Bin 0 -> 34596 bytes .../__pycache__/udprelay.cpython-37.pyc | Bin 0 -> 15312 bytes .../__pycache__/version.cpython-37.pyc | Bin 0 -> 259 bytes .../shadowsocksr/shadowsocks/asyncdns.py | 555 +++++++ .../3.3.3/shadowsocksr/shadowsocks/common.py | 418 +++++ .../shadowsocks/crypto/__init__.py | 18 + .../shadowsocks/crypto/__init__.pyc | Bin 0 -> 301 bytes .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 275 bytes .../crypto/__pycache__/openssl.cpython-37.pyc | Bin 0 -> 4877 bytes .../crypto/__pycache__/rc4_md5.cpython-37.pyc | Bin 0 -> 1060 bytes .../crypto/__pycache__/sodium.cpython-37.pyc | Bin 0 -> 2936 bytes .../crypto/__pycache__/table.cpython-37.pyc | Bin 0 -> 6600 bytes .../crypto/__pycache__/util.cpython-37.pyc | Bin 0 -> 2683 bytes .../shadowsocks/crypto/ctypes_libsodium.py | 135 ++ .../shadowsocks/crypto/ctypes_openssl.py | 188 +++ .../shadowsocks/crypto/openssl.py | 199 +++ .../shadowsocks/crypto/openssl.pyc | Bin 0 -> 6812 bytes .../shadowsocks/crypto/rc4_md5.py | 52 + .../shadowsocks/crypto/rc4_md5.pyc | Bin 0 -> 1387 bytes .../shadowsocksr/shadowsocks/crypto/sodium.py | 140 ++ .../shadowsocks/crypto/sodium.pyc | Bin 0 -> 3806 bytes .../shadowsocksr/shadowsocks/crypto/table.py | 181 ++ .../shadowsocksr/shadowsocks/crypto/table.pyc | Bin 0 -> 8478 bytes .../shadowsocksr/shadowsocks/crypto/util.py | 139 ++ .../shadowsocksr/shadowsocks/crypto/util.pyc | Bin 0 -> 3621 bytes .../3.3.3/shadowsocksr/shadowsocks/daemon.py | 208 +++ .../3.3.3/shadowsocksr/shadowsocks/encrypt.py | 236 +++ .../shadowsocksr/shadowsocks/encrypt_test.py | 51 + .../shadowsocksr/shadowsocks/eventloop.py | 258 +++ .../3.3.3/shadowsocksr/shadowsocks/local.py | 81 + .../shadowsocksr/shadowsocks/lru_cache.py | 179 ++ .../3.3.3/shadowsocksr/shadowsocks/manager.py | 291 ++++ .../3.3.3/shadowsocksr/shadowsocks/obfs.py | 114 ++ .../shadowsocks/obfsplugin/__init__.py | 18 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 279 bytes .../__pycache__/auth.cpython-37.pyc | Bin 0 -> 29290 bytes .../__pycache__/auth_chain.cpython-37.pyc | Bin 0 -> 23272 bytes .../__pycache__/http_simple.cpython-37.pyc | Bin 0 -> 18423 bytes .../__pycache__/obfs_tls.cpython-37.pyc | Bin 0 -> 11080 bytes .../__pycache__/plain.cpython-37.pyc | Bin 0 -> 3442 bytes .../__pycache__/verify.cpython-37.pyc | Bin 0 -> 6688 bytes .../shadowsocks/obfsplugin/auth.py | 787 +++++++++ .../shadowsocks/obfsplugin/auth_chain.py | 632 +++++++ .../shadowsocks/obfsplugin/http_simple.py | 314 ++++ .../shadowsocks/obfsplugin/obfs_tls.py | 303 ++++ .../shadowsocks/obfsplugin/plain.py | 104 ++ .../shadowsocks/obfsplugin/plain.pyc | Bin 0 -> 4677 bytes .../shadowsocks/obfsplugin/verify.py | 154 ++ .../shadowsocksr/shadowsocks/ordereddict.py | 214 +++ .../3.3.3/shadowsocksr/shadowsocks/server.py | 215 +++ .../3.3.3/shadowsocksr/shadowsocks/shell.py | 445 +++++ .../shadowsocksr/shadowsocks/tcprelay.py | 1472 +++++++++++++++++ .../shadowsocksr/shadowsocks/udprelay.py | 656 ++++++++ .../3.3.3/shadowsocksr/shadowsocks/version.py | 20 + shadowsocksr/3.3.3/shadowsocksr/switchrule.py | 8 + .../3.3.3/shadowsocksr/utils/README.md | 9 + .../3.3.3/shadowsocksr/utils/autoban.py | 53 + .../utils/fail2ban/shadowsocks.conf | 5 + 77 files changed, 9442 insertions(+) create mode 100644 shadowsocksr/3.3.3/Dockerfile create mode 100644 shadowsocksr/3.3.3/README.md create mode 100644 shadowsocksr/3.3.3/shadowsocksr/asyncmgr.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/configloader.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/importloader.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/server.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/server_pool.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/setup.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__init__.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/__init__.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/asyncdns.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/common.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/daemon.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/encrypt.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/eventloop.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/lru_cache.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/manager.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/obfs.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/shell.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/tcprelay.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/udprelay.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/version.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/asyncdns.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/common.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__init__.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__init__.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__pycache__/__init__.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__pycache__/openssl.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__pycache__/rc4_md5.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__pycache__/sodium.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__pycache__/table.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__pycache__/util.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/ctypes_libsodium.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/ctypes_openssl.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/openssl.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/openssl.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/rc4_md5.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/rc4_md5.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/sodium.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/sodium.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/table.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/table.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/util.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/util.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/daemon.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/encrypt.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/encrypt_test.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/eventloop.py create mode 100755 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/local.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/lru_cache.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/manager.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfs.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__init__.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/__init__.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/auth.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/auth_chain.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/http_simple.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/obfs_tls.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/plain.cpython-37.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/verify.cpython-37.pyc create mode 100755 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/auth.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/auth_chain.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/http_simple.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/obfs_tls.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/plain.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/plain.pyc create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/verify.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/ordereddict.py create mode 100755 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/server.py create mode 100755 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/shell.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/tcprelay.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/udprelay.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/shadowsocks/version.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/switchrule.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/utils/README.md create mode 100755 shadowsocksr/3.3.3/shadowsocksr/utils/autoban.py create mode 100644 shadowsocksr/3.3.3/shadowsocksr/utils/fail2ban/shadowsocks.conf diff --git a/shadowsocksr/3.3.3/Dockerfile b/shadowsocksr/3.3.3/Dockerfile new file mode 100644 index 0000000..58d52d9 --- /dev/null +++ b/shadowsocksr/3.3.3/Dockerfile @@ -0,0 +1,11 @@ +FROM alpine:3.7 + +MAINTAINER Newnius + +ADD shadowsocksr /usr/local/shadowsocksr + +WORKDIR /usr/local/shadowsocksr/shadowsocks + +RUN ln -s /usr/local/shadowsocksr/shadowsocks/server.py /usr/bin/ssrserver + +RUN ln -s /usr/local/shadowsocksr/shadowsocks/local.py /usr/bin/ssrlocal diff --git a/shadowsocksr/3.3.3/README.md b/shadowsocksr/3.3.3/README.md new file mode 100644 index 0000000..83cc452 --- /dev/null +++ b/shadowsocksr/3.3.3/README.md @@ -0,0 +1,25 @@ +# ShadowsocksR + +__Across the Great Wall, we can reach every corner in the world.__ + +## Run as server +``` +docker run \ +-d \ +--restart always \ +--publish 1081:1080 \ +--name ssrserver \ +newnius/shadowsocksr:3.3.3 \ +ssrserver -p 1080 -k PASSWORD -m METHOD +``` + +## Run as client +``` +docker run \ +-d \ +--restart always \ +--publish 1082:1080 \ +--name ssrlocal \ +newnius/shadowsocksr:3.3.3 \ +ssrlocal -s SERVER_IP -p 1081 -l 1080 -k PASSWORD -m METHOD +``` diff --git a/shadowsocksr/3.3.3/shadowsocksr/asyncmgr.py b/shadowsocksr/3.3.3/shadowsocksr/asyncmgr.py new file mode 100644 index 0000000..9bf4d09 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/asyncmgr.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2014 clowwindy +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import time +import os +import socket +import struct +import re +import logging +from shadowsocks import common +from shadowsocks import lru_cache +from shadowsocks import eventloop +import server_pool +import Config + +class ServerMgr(object): + + def __init__(self): + self._loop = None + self._request_id = 1 + self._hosts = {} + self._hostname_status = {} + self._hostname_to_cb = {} + self._cb_to_hostname = {} + self._last_time = time.time() + self._sock = None + self._servers = None + + def add_to_loop(self, loop): + if self._loop: + raise Exception('already add to loop') + self._loop = loop + # TODO when dns server is IPv6 + self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, + socket.SOL_UDP) + self._sock.bind((Config.MANAGE_BIND_IP, Config.MANAGE_PORT)) + self._sock.setblocking(False) + loop.add(self._sock, eventloop.POLL_IN, self) + + def _handle_data(self, sock): + data, addr = sock.recvfrom(128) + #manage pwd:port:passwd:action + args = data.split(':') + if len(args) < 4: + return + if args[0] == Config.MANAGE_PASS: + if args[3] == '0': + server_pool.ServerPool.get_instance().cb_del_server(args[1]) + elif args[3] == '1': + server_pool.ServerPool.get_instance().new_server(args[1], args[2]) + + def handle_event(self, sock, fd, event): + if sock != self._sock: + return + if event & eventloop.POLL_ERR: + logging.error('mgr socket err') + self._loop.remove(self._sock) + self._sock.close() + # TODO when dns server is IPv6 + self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, + socket.SOL_UDP) + self._sock.setblocking(False) + self._loop.add(self._sock, eventloop.POLL_IN, self) + else: + self._handle_data(sock) + + def close(self): + if self._sock: + if self._loop: + self._loop.remove(self._sock) + self._sock.close() + self._sock = None + + +def test(): + pass + +if __name__ == '__main__': + test() diff --git a/shadowsocksr/3.3.3/shadowsocksr/configloader.py b/shadowsocksr/3.3.3/shadowsocksr/configloader.py new file mode 100644 index 0000000..cf9d619 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/configloader.py @@ -0,0 +1,15 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +import importloader + +g_config = None + +def load_config(): + global g_config + g_config = importloader.loads(['userapiconfig', 'apiconfig']) + +def get_config(): + return g_config + +load_config() + diff --git a/shadowsocksr/3.3.3/shadowsocksr/importloader.py b/shadowsocksr/3.3.3/shadowsocksr/importloader.py new file mode 100644 index 0000000..c917cb7 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/importloader.py @@ -0,0 +1,24 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +def load(name): + try: + obj = __import__(name) + reload(obj) + return obj + except: + pass + + try: + import importlib + obj = importlib.__import__(name) + importlib.reload(obj) + return obj + except: + pass + +def loads(namelist): + for name in namelist: + obj = load(name) + if obj is not None: + return obj diff --git a/shadowsocksr/3.3.3/shadowsocksr/server.py b/shadowsocksr/3.3.3/shadowsocksr/server.py new file mode 100644 index 0000000..ba863b6 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/server.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright 2015 breakwall +# +# 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. + +import time +import sys +import threading +import os + +if __name__ == '__main__': + import inspect + os.chdir(os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe())))) + +import server_pool +import db_transfer +from shadowsocks import shell +from configloader import load_config, get_config + +class MainThread(threading.Thread): + def __init__(self, obj): + super(MainThread, self).__init__() + self.daemon = True + self.obj = obj + + def run(self): + self.obj.thread_db(self.obj) + + def stop(self): + self.obj.thread_db_stop() + +def main(): + shell.check_python() + if False: + db_transfer.DbTransfer.thread_db() + else: + if get_config().API_INTERFACE == 'mudbjson': + thread = MainThread(db_transfer.MuJsonTransfer) + elif get_config().API_INTERFACE == 'sspanelv2': + thread = MainThread(db_transfer.DbTransfer) + else: + thread = MainThread(db_transfer.Dbv3Transfer) + thread.start() + try: + while thread.is_alive(): + thread.join(10.0) + except (KeyboardInterrupt, IOError, OSError) as e: + import traceback + traceback.print_exc() + thread.stop() + +if __name__ == '__main__': + main() + diff --git a/shadowsocksr/3.3.3/shadowsocksr/server_pool.py b/shadowsocksr/3.3.3/shadowsocksr/server_pool.py new file mode 100644 index 0000000..d159817 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/server_pool.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2014 clowwindy +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +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): + super(MainThread, self).__init__() + self.params = params + + def run(self): + ServerPool._loop(*self.params) + +class ServerPool(object): + + instance = None + + def __init__(self): + shell.check_python() + self.config = shell.get_config(False) + self.dns_resolver = asyncdns.DNSResolver() + if not self.config.get('dns_ipv6', False): + asyncdns.IPV6_CONNECTION_SUPPORT = False + + self.mgr = None #asyncmgr.ServerMgr() + + self.tcp_servers_pool = {} + self.tcp_ipv6_servers_pool = {} + self.udp_servers_pool = {} + self.udp_ipv6_servers_pool = {} + self.stat_counter = {} + + self.loop = eventloop.EventLoop() + self.thread = MainThread( (self.loop, self.dns_resolver, self.mgr) ) + self.thread.start() + + @staticmethod + def get_instance(): + if ServerPool.instance is None: + ServerPool.instance = ServerPool() + return ServerPool.instance + + def stop(self): + self.loop.stop() + + @staticmethod + def _loop(loop, dns_resolver, mgr): + try: + if mgr is not None: + mgr.add_to_loop(loop) + dns_resolver.add_to_loop(loop) + loop.run() + except (KeyboardInterrupt, IOError, OSError) as e: + logging.error(e) + traceback.print_exc() + os.exit(0) + except Exception as e: + logging.error(e) + traceback.print_exc() + + def server_is_run(self, port): + port = int(port) + ret = 0 + if port in self.tcp_servers_pool: + ret = 1 + if port in self.tcp_ipv6_servers_pool: + ret |= 2 + return ret + + def server_run_status(self, port): + if 'server' in self.config: + if port not in self.tcp_servers_pool: + return False + if 'server_ipv6' in self.config: + if port not in self.tcp_ipv6_servers_pool: + return False + return True + + def new_server(self, port, user_config): + ret = True + port = int(port) + ipv6_ok = False + + if 'server_ipv6' in self.config: + if port in self.tcp_ipv6_servers_pool: + logging.info("server already at %s:%d" % (self.config['server_ipv6'], port)) + return 'this port server is already running' + else: + a_config = self.config.copy() + a_config.update(user_config) + if len(a_config['server_ipv6']) > 2 and a_config['server_ipv6'][0] == "[" and a_config['server_ipv6'][-1] == "]": + a_config['server_ipv6'] = a_config['server_ipv6'][1:-1] + a_config['server'] = a_config['server_ipv6'] + a_config['server_port'] = port + a_config['max_connect'] = 128 + a_config['method'] = common.to_str(a_config['method']) + try: + logging.info("starting server at [%s]:%d" % (common.to_str(a_config['server']), port)) + + tcp_server = tcprelay.TCPRelay(a_config, self.dns_resolver, False, stat_counter=self.stat_counter) + tcp_server.add_to_loop(self.loop) + self.tcp_ipv6_servers_pool.update({port: tcp_server}) + + udp_server = udprelay.UDPRelay(a_config, self.dns_resolver, False, stat_counter=self.stat_counter) + udp_server.add_to_loop(self.loop) + self.udp_ipv6_servers_pool.update({port: udp_server}) + + if common.to_str(a_config['server_ipv6']) == "::": + ipv6_ok = True + except Exception as e: + logging.warn("IPV6 %s " % (e,)) + + if 'server' in self.config: + if port in self.tcp_servers_pool: + logging.info("server already at %s:%d" % (common.to_str(self.config['server']), port)) + return 'this port server is already running' + else: + a_config = self.config.copy() + a_config.update(user_config) + a_config['server_port'] = port + a_config['max_connect'] = 128 + a_config['method'] = common.to_str(a_config['method']) + try: + logging.info("starting server at %s:%d" % (common.to_str(a_config['server']), port)) + + tcp_server = tcprelay.TCPRelay(a_config, self.dns_resolver, False) + tcp_server.add_to_loop(self.loop) + self.tcp_servers_pool.update({port: tcp_server}) + + udp_server = udprelay.UDPRelay(a_config, self.dns_resolver, False) + udp_server.add_to_loop(self.loop) + self.udp_servers_pool.update({port: udp_server}) + + except Exception as e: + if not ipv6_ok: + logging.warn("IPV4 %s " % (e,)) + + return True + + def del_server(self, port): + port = int(port) + logging.info("del server at %d" % port) + try: + udpsock = socket(AF_INET, SOCK_DGRAM) + udpsock.sendto('%s:%s:0:0' % (get_config().MANAGE_PASS, port), (get_config().MANAGE_BIND_IP, get_config().MANAGE_PORT)) + udpsock.close() + except Exception as e: + logging.warn(e) + return True + + def cb_del_server(self, port): + port = int(port) + + if port not in self.tcp_servers_pool: + logging.info("stopped server at %s:%d already stop" % (self.config['server'], port)) + else: + logging.info("stopped server at %s:%d" % (self.config['server'], port)) + try: + self.tcp_servers_pool[port].close(True) + del self.tcp_servers_pool[port] + except Exception as e: + logging.warn(e) + try: + self.udp_servers_pool[port].close(True) + del self.udp_servers_pool[port] + except Exception as e: + logging.warn(e) + + if 'server_ipv6' in self.config: + if port not in self.tcp_ipv6_servers_pool: + logging.info("stopped server at [%s]:%d already stop" % (self.config['server_ipv6'], port)) + else: + logging.info("stopped server at [%s]:%d" % (self.config['server_ipv6'], port)) + try: + self.tcp_ipv6_servers_pool[port].close(True) + del self.tcp_ipv6_servers_pool[port] + except Exception as e: + logging.warn(e) + try: + self.udp_ipv6_servers_pool[port].close(True) + del self.udp_ipv6_servers_pool[port] + except Exception as e: + logging.warn(e) + + 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('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 My=^RcPiD>k0}oUdNB{r; literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/common.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/common.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7f4d9c311ecfbee039d367321cf1a09295a6e0b GIT binary patch literal 11558 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 literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/daemon.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/daemon.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85563428deb038efae4fdebb6f9503f20f54d657 GIT binary patch literal 4359 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 T_~!&ZFBL!6_G^AX@$~-y=LlUA literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/eventloop.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/eventloop.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e3b690c4ab660ca42439f4fc8dd486d303fcadf5 GIT binary patch literal 7602 zcmcgx&2J;gb?@pgHYrM?MjtcU9dFv}vAr~G&H5vbu^I1bJj3zGqoKxdZKz$X zp>_3!&ZuAYjfNphvti1z&?v~#YFM%?Hj1(=HA*Nof390@l;yj+Ki^$wETC?1lNY$f zi@e0=c)4vg7P-=>@cG6PUuaz8i;ZPoX{_+2#;RB0rnkfk-ZgG{%e?5V@RGO6=Q?I% zjbCeA|GvVP`N~fezT(|@sWsNoUgc|OugUf+XkX_B+JVZJg3v`2O>%=-g({$J3%j5=!;G-a$194Gm`DoPITymksEnk zuNT#;ESck8%N_U;?=&McjquR({iIlXj`8gv=qJS|JKNh%{oU>Dq#&zX^~92uop<-g zwc76Pxc2bfC)peC-Q7;+YVXzR`%eAthAL`gd3Lhf# zlm-K*Rn8h3SGfib=v*iM;8bxYC`~#Slm$8)lol`Zd6Y%Iz!y=Lcm*6=N=lDT2HwD< zeRUL2(j=1Tp^`p&4=G$h7AYfkp~Q-#b|}}lf(2L|>^4Ks_nJ{+9?P|of-|n@PK$?N zp^gcyPZV(z%l!JC+k;Tt_JgMD-wqF59-M|j^B7%IjoTX=Gfj_p?}NsT{)@zNoKCM3 zInFyo8gWlyC3e0%*~vyWqbWx*!9*$`$=w{F(pH9gtVZCecs=K3EwVv2s)xS+UV(oL#6)IeG9DDhA%DbwNz576mD6V!Hjl z*W=F(BkO{`OIp&9fd5%^V8yh?4g z#S^_7a4#`hzS|BHQ!W`M%#(~xGa!Tlj*Z`y+h0JJ!gOY_HJL8+ZalY1r%w;@&Onkc z{}n1Rc6zLcGFOHSyN6tG1)Kx_F0`Q<>8VJ*WJB$2J66G^`{1>HWPn%tkr@?^tYcL? zi?!G=lyxO4?kJjaM>%G9oX zj#TMAL5xa_4u`BJW~<{zo{*<4Y?@9&AU;o-JY=H-#0j&*EJU5I7YritB}_;P!V3XF zq4+XQ{{m%_@Co%g(YK9(kf=~_jyk)>%FqK7In`F!`QoMhG&P!whsyj!Uqd5S=^SHr z2bw)%F@x;mJTEi?{IQmcS1?b!L79}`B80T|o^$v9{i-GfGNtntO}Ip?CA@Yg1UG(! z!K)QEn~@70313bku^WVeK#&RYAT(q0()M?!$dnXHS|yp~l+63J?b?2=s!maQiOABR zml5&7gv>Q|zBMye z9Ojm$$VtbqhHTRER5>qz<&)1#EHRpX5PDUXvSXGL;#+vrqi;$8VCR*oFUt4o)j0y; zp4;^tCn-4&=ot7^FFVf3!1dD+@f9p0NW4<9d6R0i@stJMq#Au|N?dXQm&)~*7!1iK zTB@#Ex>dC1k>e*vn;6I+B6Slfyn}3F9=Qrrr*VzzC}AMEiP9ja1*J)j3(5jHFDNZO z4_RMGN_*0rkx9IowM1p4yaan4HZEaFWP8dgYM~dw6bi?O?4xp;RnBDPGnh~8I;M>f zn2=suuxt~8CYDVe4yIU0xn+Bf2JAE)JgEw4HKDTpK5%)c4Rv5MBc1@8jkEh?;aX~} zMJ94RDzwzL2DJXl&^)^pn_OF0(7G`!#70~oq(+~?>)yc#AR;89_22!8pZSf?GkJvG^LYs+9`;2I>hD z2|G>LnYb-m{j0E6klgl>!qjk90Ip?rlPw^fFJ2;a1{MfrB`j>BGIlmr=x9bN4kdLl zv`uIS`t+$jay#yz&&>KV?JcAmqMat1%ek~9>7M2OXybnfRip;Kz{HSVAqgvw(~O*D zSU3a7`Gy|gO$o-zu_@L-szf@uB`J64{VIA@vL$)1hTiX?SIc^{ZRzst5>t5H;JG(B z!`}nf5<>oZvWpUSk*4$2sU!K5;i5L*5t zeIP^TspZGWToiwf3F6z7{RL$be@TS@Xz2Y8CU=mefLN+!0C?%q1&uV3@`nUs6De#V z`yJT5A!8B-;7wx^V;qy1Oj6_(z62mH@oRh;ESjbBGO&ouz_ zx**8Qk(pSL+ut9f5h)S;)0e7vC4!>Kh(+UU$a>$ABPvF|5$R*JR?%UT+ zhJGso-o0dh!(_Zuh%7=nV1BVpd5OdGcjltWSeIW{DdhMncp1x0`Oy z?gf$E>h!qn_FmZTbGPHW2fk-#k!WaldUhx+kKK2h$8OtuaK1VSJH56o9eW!B6BpFH!3U+;I8s$MDVm)hmeHrDN2*kQ>Khep}gvIvVFmhyl+39eSA78_(8i(6@z;RgZ5Y@8-^ri%5sxWEoJSWPzg!sC@3R$ zv;=q==c=Vr&BX8gS}lvGbDLJr!G`22OLDQl%OPf{{;kr&0mF=06kNPykO{4x%LBbe zj^i>?cncYw)(1D=$4L)WT87+5PSy-wZcF>AdZLqun}%vkd>?OAwM5-`Q`!QYC6PI! zkF?cam+p{RZwB4|+y6peA~7_T^VcW4x@@0n_Elcib7z?zJabp2ZY+I6l8~_k|9YLsWA8j;v_-2 z;m!!sF4OXR0t`e!WZqi*--s4@Xyn{Y=|MK(YFa8|S}t2pz!pZtpCKD-XJTcleCdFa zGL9fu_ihZlp!hw4(hS*CrFWz&krwGK^|&Pdfhj|cDd(_pvG$Th=1~F3!k8&OVavGz>qsn4)yL=@*SL&P8u zZJ5Kt&;n%(qs15(%NOdf7~?|uxF|kGX{;;5;)}JoII6^ET!^g;t^e~F_d|#&KAzD0 z5fj#!>PoaUx(3<+2D9Hj`S=;$d8$Oqqh(-SDOSOYuf`>Od*ueM362e{@`E$-QCC4* zc{mr(y~G|qVkh51^n!WLN{sKsMrdJ;HEz;3POw9%g{4_AVDvb4YTxP~iW=gDsE-qzmMPCY5??A3$_L{iw=Nh>SB&R*cOM9_7- z+&~+$w_((tY(1%=*sAaEifxjT$H-uX6Ci}}3N6%Y?;(JjgdHgLP`dtSduf=#u)|_2s?I9trMij>$u(x9wuaed zcnQGADf@vHb9ErsIafLg%t#IBf_h>t#&(vR=Fd>Rg5$uYa-9FcluZAYCI~bhrAzLG z&vvWH=n29+u}|51ls%=4Fho3~tWDV=Wm54cnY8d027Zbpukyf$<&qAc+rUMKh9Bj( zX11+iu0(-1P>HZ{>Frj)0JuWPRbf-k)pgw6k;^RIX5D=Ro;?x4b6ik{c68|3Qs!)u zMEIn3?k(EE!4a&B27&uB^;|mY_tE#iNFkXC zrIfnDB~?&=e#Ru$bo(RE_;pn1MloVXDiY9n$Pm}Sr;Cgc#T|$|$bYR@3rPulARP_* zxPPV!E}%+#JKN6Q&b>!9{4=$;PB*s z&@P#40%{?A_-igq@6+d|{uW9h4O)xi!}3L1fH;}ngOG(3^OO-Dh(*fi#yK%lfu4j$ zGJ=#$msC$}Q*M~FGO_N!%;Q$`?NziwihOTq1X9_rtd=zdIDoJG2UyE~mac1+jmjI9 I_pRmM0zOtiuK)l5 literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/lru_cache.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/lru_cache.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64fc51fdfc38073f63fc3ac2fb80a72f317af8e7 GIT binary patch literal 3847 zcma)9&2J<}6|d^A>G|+@*6a1!3yDl1fq}@DAb^mZ^@e0akaitwz0ro2P^;b5V~?k2 zdbX-2&RSy>#VZa-;lu@mkbMA&KY=Tpa)n!qLnKbVaYh{Yz3Q3v*lR`fs9$}&S5>cm z@1x(`o13dKJb&|buzi!Uf6-w25a%9V^#zDvf~Ty{1Kzg+t8WK(-w7N}^H%Bxu4%o% zGi@cPB)+hFmi(m|SA(i)YeCJl^`M?q4;rU)hBlM>={(-n89U?j3vMJea2EtK{F})Q z;hb4#7H7{|SnsB8e=%6(>w1GT{YFKVhOG{O3&giFA?+!x)Cx~iKE^5#%>2RiJG z2C+8d!>-;Bm5y}MPX>A`_rtIsbq8U1WUsBQw|QQFUy4K~;+-y5Fo&_2PMJP7I`{CZ z4oJcRE?8g*9@t`8IKq9-14nqG0*zhaiz-@A)I=R^MKr`5T3@g>n#BIzqg^hczLlkI&}u6tCFyY|@B3)g8pxQBS;)o~DDN^*OWP;ZI%9U~YHw_honOP8WF8#Mx7wAw zqPzVh8|l0mr)1}2SmB=e`TXd{6v(xyRBSP3mDX2Mh9xaO@a0Vcc5)%-YgZh zW^}X-zD7s6g4Ne3Cz7;4_FeFm2f{4E^%AP3P^lOxRVqeaWYA}$=Oe+8FD z7{=K^Be$q9e1Hv<@icyPYsQ)v5;2DeJ=9)0SQ7&Lgck=}xr6J-;&@&hf8}I~0EmR4 ziTlZ^mptEa|Nj&1G#Px^ft4_XL>NAT>eE}cW^QSi<#D7?dBJaBFukDQJx8v7+@kBg z!+!1^BgA&_$uPOd+LDA!+mc^HV_X1zimt-?S1@opU8(d3U>7FmP2S)~%~C1DE{#no zjKaux;h)f%#=hXk_Q$_KTI4+@f2Ap)tP>7JE&19uGpbrfRcD($U3*gG0|(ygBEZ~b z8rwZCA8k|58574Vdd9D)qSKshI5j^(10)N3FN z$;?42YjO*A@FqP9LrQQp%sJrA*vBlt-afy_c9-m2azwbI1J@2ERuJKe3i&cL@1rhRU)K|{5lc3n*27A?-0508q=e% zq4Yss$E#@6uxk~c9?!SU&uuIji5N`tP%0^7_W>e7qwW>fXZ)DKHcUA5l@^FRs&3UQ znt#ft^V&MKg=-=THb0}f5`|E&qTVx7h*VlFDro%;W~8gL(vy8X?0ZBjNDk*zFh;KU zlwZ3768xIICMW;(%07QD_qk5be-l^uM(6*Uel2oET{NDvV-6hV##}T%vnEK)8zkn- z^XH1Yh>ICh`jP(|EGWaYw)Vbfo_}n%d67cMt#cmj74Uztc}^BMx6SPC-tT@(&*z)( zmieBN6TLfl6(wv98yjVLG|lag+g3h5sV{d;iJ6GJ`Ib_Nq_X|Kl$pf&*VP~XblD$i zfAR!MHb+Z3QF?8|6q4%h+Ei$w-(NQ2=oghJS8dODmHa-In-I-il~UvxUws3!SYQH> z3bYx&B_~DMhr>?LQ$Ge_yao+g+&6Vb1O1|sYw?zuDMpRSsKFbyWBG1lk_?F}e}rAy zcPQ{WBR!G`JV_`^LRr!sCDrzMJ5-kv~igN70j8naXYTOyw0+AVUb{&M?yZ zc|~+3MQ`roZ$ny4p{VS1(s3S?BN^_x+@URC=kvj)jcVH_1;Py-fzLpjL znW)e=A)YrMj&!t}CJ&?G5FeQWT)Nc^bY;lr{zj2s?k+$=t%5AugqjcRmifzk*|LDA P#}}%OweZAW_xb+-_vJ9L literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/manager.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/manager.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71b303d461e55a2aa2478ea9b5725f0e3a56f59f GIT binary patch literal 7261 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 Dz}@Rl literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/obfs.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/obfs.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b601c612a6dc919ad506d77d47e8fc29b6e26c02 GIT binary patch literal 4312 zcmbVPUys{F5ck?n;>-Wb-R17u(uUH~rodfWQ3R-hlS%~=(n$y;B%{jBWt@u-I}U5R znnYEp6a%oGN$TPcZ@62y!e*15)Rx4}x`_qc;&6=kD zO_ls-Vqp)T=r0tY0rRzh*(}g)Juqw|Fm01jyY3h4g49L3D0Rs$Np0B{YQtX%%63_P zYx{d=uj`3Fa+bxqOe9Ymp@#IEB7;@2>a3yeoLC3>F_6PqB+Pl96qB+o6w7UU0s-w>ks%UeyR+xj#XhfXwr)vr;D`= z;mNw6CzeQ_NA zz^~570ZfdCnGv>-(iJC?q$y6M3RLmE1vRLnUV#QQQI}yATBs|q2J5J+umNqNeCl%1%qTHslQ7VH*LX<*!GigSTtM4j5-j4K#w_H)Ng9gVV`wE|kwqXrPm{BmN_0%`{ zordK#oT$p4xYiEc9%O_NE8uZgZfNuVX=VQgpPZ7bk`}6sr4h9HzK2^SZXT}Vj{5v~ z9OpQUgJdv|;0AjCRza{S5uCTNG>%$k12Kf)=5vFV@OcQddF(3?S}KIJ`&)Yl_f6h! zq37>j%$kb!{?^{#%!VEG{ODEQ-)i83)Y8)jJSZ=)7wE7tP*$;u1_xRxAM3*cjCH-?n8o*Zm+n* zS7o-cUi;lmPPZ1mL4pn&>G_`yYMf3?yhDOc3Y>OoPTM7?9gEYR!D;E{w03e@w>YgF zoF*`*8N`XXoY=xCHJrl136PRF376O)Pmp9JqWoISTCoiIFIZ+)O6xOH_Udlab!L#n z32wL!r|ffL8>cbz0t%srkuaj;NR)=XXy|(fqU48%co09to0)Piq?3@{R$6u_TIrdc zm&lw&Vub|VS9yg*mBdvPqL_^F`0Lu~B{=AfeF!7K+{p*&|H2(w3nTcNj literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/shell.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/shell.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1cca9c244efb3aad4362282432dec5dea4b45d3 GIT binary patch literal 11815 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 TF8yOAhq9SI23T+ZNILO<-7CNr literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/tcprelay.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/tcprelay.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f4b7b7864a670df7045a3d6a75493c3111d203a GIT binary patch literal 34596 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 M_uxyd?f*mnAF0f6cK`qY literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/udprelay.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/__pycache__/udprelay.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7304022a071c753e2565aa7caee977c877d551ac GIT binary patch literal 15312 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 Gg`kg2NmoQ8Snr7#@Q-FaYE;H~?`m50FS 63: + return None + results.append(common.chr(l)) + results.append(label) + results.append(b'\0') + return b''.join(results) + + +def build_request(address, qtype): + request_id = os.urandom(2) + header = struct.pack('!BBHHHH', 1, 0, 1, 0, 0, 0) + addr = build_address(address) + qtype_qclass = struct.pack('!HH', qtype, QCLASS_IN) + return request_id + header + addr + qtype_qclass + + +def parse_ip(addrtype, data, length, offset): + if addrtype == QTYPE_A: + return socket.inet_ntop(socket.AF_INET, data[offset:offset + length]) + elif addrtype == QTYPE_AAAA: + return socket.inet_ntop(socket.AF_INET6, data[offset:offset + length]) + elif addrtype in [QTYPE_CNAME, QTYPE_NS]: + return parse_name(data, offset)[1] + else: + return data[offset:offset + length] + + +def parse_name(data, offset): + p = offset + labels = [] + l = common.ord(data[p]) + while l > 0: + if (l & (128 + 64)) == (128 + 64): + # pointer + pointer = struct.unpack('!H', data[p:p + 2])[0] + pointer &= 0x3FFF + r = parse_name(data, pointer) + labels.append(r[1]) + p += 2 + # pointer is the end + return p - offset, b'.'.join(labels) + else: + labels.append(data[p + 1:p + 1 + l]) + p += 1 + l + l = common.ord(data[p]) + return p - offset + 1, b'.'.join(labels) + + +# rfc1035 +# record +# 1 1 1 1 1 1 +# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +# | | +# / / +# / NAME / +# | | +# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +# | TYPE | +# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +# | CLASS | +# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +# | TTL | +# | | +# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +# | RDLENGTH | +# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| +# / RDATA / +# / / +# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +def parse_record(data, offset, question=False): + nlen, name = parse_name(data, offset) + if not question: + record_type, record_class, record_ttl, record_rdlength = struct.unpack( + '!HHiH', data[offset + nlen:offset + nlen + 10] + ) + ip = parse_ip(record_type, data, record_rdlength, offset + nlen + 10) + return nlen + 10 + record_rdlength, \ + (name, ip, record_type, record_class, record_ttl) + else: + record_type, record_class = struct.unpack( + '!HH', data[offset + nlen:offset + nlen + 4] + ) + return nlen + 4, (name, None, record_type, record_class, None, None) + + +def parse_header(data): + if len(data) >= 12: + header = struct.unpack('!HBBHHHH', data[:12]) + res_id = header[0] + res_qr = header[1] & 128 + res_tc = header[1] & 2 + res_ra = header[2] & 128 + res_rcode = header[2] & 15 + # assert res_tc == 0 + # assert res_rcode in [0, 3] + res_qdcount = header[3] + res_ancount = header[4] + res_nscount = header[5] + res_arcount = header[6] + return (res_id, res_qr, res_tc, res_ra, res_rcode, res_qdcount, + res_ancount, res_nscount, res_arcount) + return None + + +def parse_response(data): + try: + if len(data) >= 12: + header = parse_header(data) + if not header: + return None + res_id, res_qr, res_tc, res_ra, res_rcode, res_qdcount, \ + res_ancount, res_nscount, res_arcount = header + + qds = [] + ans = [] + offset = 12 + for i in range(0, res_qdcount): + l, r = parse_record(data, offset, True) + offset += l + if r: + qds.append(r) + for i in range(0, res_ancount): + l, r = parse_record(data, offset) + offset += l + if r: + ans.append(r) + for i in range(0, res_nscount): + l, r = parse_record(data, offset) + offset += l + for i in range(0, res_arcount): + l, r = parse_record(data, offset) + offset += l + response = DNSResponse() + if qds: + response.hostname = qds[0][0] + for an in qds: + response.questions.append((an[1], an[2], an[3])) + for an in ans: + response.answers.append((an[1], an[2], an[3])) + return response + except Exception as e: + shell.print_exception(e) + return None + + +def is_valid_hostname(hostname): + if len(hostname) > 255: + return False + if hostname[-1] == b'.': + hostname = hostname[:-1] + return all(VALID_HOSTNAME.match(x) for x in hostname.split(b'.')) + + +class DNSResponse(object): + def __init__(self): + self.hostname = None + self.questions = [] # each: (addr, type, class) + self.answers = [] # each: (addr, type, class) + + def __str__(self): + return '%s: %s' % (self.hostname, str(self.answers)) + + +STATUS_IPV4 = 0 +STATUS_IPV6 = 1 + + +class DNSResolver(object): + + def __init__(self): + self._loop = None + self._hosts = {} + self._hostname_status = {} + self._hostname_to_cb = {} + self._cb_to_hostname = {} + self._cache = lru_cache.LRUCache(timeout=300) + self._sock = None + self._servers = None + self._parse_resolv() + self._parse_hosts() + # TODO monitor hosts change and reload hosts + # TODO parse /etc/gai.conf and follow its rules + + def _parse_resolv(self): + self._servers = [] + try: + with open('dns.conf', 'rb') as f: + content = f.readlines() + for line in content: + line = line.strip() + if line: + parts = line.split(b' ', 1) + if len(parts) >= 2: + server = parts[0] + port = int(parts[1]) + else: + server = parts[0] + port = 53 + if common.is_ip(server) == socket.AF_INET: + if type(server) != str: + server = server.decode('utf8') + self._servers.append((server, port)) + except IOError: + pass + if not self._servers: + try: + with open('/etc/resolv.conf', 'rb') as f: + content = f.readlines() + for line in content: + line = line.strip() + if line: + if line.startswith(b'nameserver'): + parts = line.split() + if len(parts) >= 2: + server = parts[1] + if common.is_ip(server) == socket.AF_INET: + if type(server) != str: + server = server.decode('utf8') + self._servers.append((server, 53)) + except IOError: + pass + if not self._servers: + self._servers = [('8.8.4.4', 53), ('8.8.8.8', 53)] + logging.info('dns server: %s' % (self._servers,)) + + def _parse_hosts(self): + etc_path = '/etc/hosts' + if 'WINDIR' in os.environ: + etc_path = os.environ['WINDIR'] + '/system32/drivers/etc/hosts' + try: + 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] + if common.is_ip(ip): + for i in range(1, len(parts)): + hostname = parts[i] + if hostname: + self._hosts[hostname] = ip + except IOError: + self._hosts['localhost'] = '127.0.0.1' + + def add_to_loop(self, loop): + if self._loop: + raise Exception('already add to loop') + self._loop = loop + # TODO when dns server is IPv6 + self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, + socket.SOL_UDP) + self._sock.setblocking(False) + loop.add(self._sock, eventloop.POLL_IN, self) + loop.add_periodic(self.handle_periodic) + + def _call_callback(self, hostname, ip, error=None): + callbacks = self._hostname_to_cb.get(hostname, []) + for callback in callbacks: + if callback in self._cb_to_hostname: + del self._cb_to_hostname[callback] + if ip or error: + callback((hostname, ip), error) + else: + callback((hostname, None), + 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: + del self._hostname_status[hostname] + + def _handle_data(self, data): + response = parse_response(data) + if response and response.hostname: + hostname = response.hostname + ip = None + for answer in response.answers: + if answer[1] in (QTYPE_A, QTYPE_AAAA) and \ + answer[2] == QCLASS_IN: + ip = answer[0] + break + if IPV6_CONNECTION_SUPPORT: + if not ip and self._hostname_status.get(hostname, STATUS_IPV4) \ + == STATUS_IPV6: + self._hostname_status[hostname] = STATUS_IPV4 + self._send_req(hostname, QTYPE_A) + else: + if ip: + self._cache[hostname] = ip + self._call_callback(hostname, ip) + elif self._hostname_status.get(hostname, None) == STATUS_IPV4: + for question in response.questions: + if question[1] == QTYPE_A: + self._call_callback(hostname, None) + break + else: + if not ip and self._hostname_status.get(hostname, STATUS_IPV6) \ + == STATUS_IPV4: + self._hostname_status[hostname] = STATUS_IPV6 + self._send_req(hostname, QTYPE_AAAA) + else: + if ip: + self._cache[hostname] = ip + self._call_callback(hostname, ip) + elif self._hostname_status.get(hostname, None) == STATUS_IPV6: + for question in response.questions: + if question[1] == QTYPE_AAAA: + self._call_callback(hostname, None) + break + + def handle_event(self, sock, fd, event): + if sock != self._sock: + return + if event & eventloop.POLL_ERR: + logging.error('dns socket err') + self._loop.remove(self._sock) + self._sock.close() + # TODO when dns server is IPv6 + self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, + socket.SOL_UDP) + self._sock.setblocking(False) + self._loop.add(self._sock, eventloop.POLL_IN, self) + else: + data, addr = sock.recvfrom(1024) + if addr not in self._servers: + logging.warn('received a packet other than our dns') + return + self._handle_data(data) + + def handle_periodic(self): + self._cache.sweep() + + def remove_callback(self, callback): + hostname = self._cb_to_hostname.get(callback) + if hostname: + del self._cb_to_hostname[callback] + arr = self._hostname_to_cb.get(hostname, None) + if arr: + arr.remove(callback) + if not arr: + del self._hostname_to_cb[hostname] + if hostname in self._hostname_status: + del self._hostname_status[hostname] + + def _send_req(self, hostname, qtype): + req = build_request(hostname, qtype) + for server in self._servers: + logging.debug('resolving %s with type %d using server %s', + hostname, qtype, server) + self._sock.sendto(req, server) + + def resolve(self, hostname, callback): + if type(hostname) != bytes: + hostname = hostname.encode('utf8') + if not hostname: + callback(None, Exception('empty hostname')) + elif common.is_ip(hostname): + callback((hostname, hostname), None) + elif hostname in self._hosts: + logging.debug('hit hosts: %s', hostname) + ip = self._hosts[hostname] + callback((hostname, ip), None) + elif hostname in self._cache: + logging.debug('hit cache: %s', hostname) + ip = self._cache[hostname] + callback((hostname, ip), None) + else: + if not is_valid_hostname(hostname): + callback(None, Exception('invalid hostname: %s' % hostname)) + return + if False: + addrs = socket.getaddrinfo(hostname, 0, 0, + socket.SOCK_DGRAM, socket.SOL_UDP) + if addrs: + af, socktype, proto, canonname, sa = addrs[0] + logging.debug('DNS resolve %s %s' % (hostname, sa[0]) ) + self._cache[hostname] = sa[0] + callback((hostname, sa[0]), None) + return + arr = self._hostname_to_cb.get(hostname, None) + if not arr: + if IPV6_CONNECTION_SUPPORT: + self._hostname_status[hostname] = STATUS_IPV6 + self._send_req(hostname, QTYPE_AAAA) + else: + self._hostname_status[hostname] = STATUS_IPV4 + self._send_req(hostname, QTYPE_A) + self._hostname_to_cb[hostname] = [callback] + self._cb_to_hostname[callback] = hostname + else: + arr.append(callback) + # TODO send again only if waited too long + if IPV6_CONNECTION_SUPPORT: + self._send_req(hostname, QTYPE_AAAA) + else: + self._send_req(hostname, QTYPE_A) + + def close(self): + if self._sock: + if self._loop: + self._loop.remove_periodic(self.handle_periodic) + self._loop.remove(self._sock) + self._sock.close() + self._sock = None + + +def test(): + dns_resolver = DNSResolver() + loop = eventloop.EventLoop() + dns_resolver.add_to_loop(loop) + + global counter + counter = 0 + + def make_callback(): + global counter + + def callback(result, error): + global counter + # TODO: what can we assert? + print(result, error) + counter += 1 + if counter == 9: + dns_resolver.close() + loop.stop() + a_callback = callback + return a_callback + + assert(make_callback() != make_callback()) + + dns_resolver.resolve(b'google.com', make_callback()) + dns_resolver.resolve('google.com', make_callback()) + dns_resolver.resolve('example.com', make_callback()) + dns_resolver.resolve('ipv6.google.com', make_callback()) + dns_resolver.resolve('www.facebook.com', make_callback()) + dns_resolver.resolve('ns2.google.com', make_callback()) + dns_resolver.resolve('invalid.@!#$%^&$@.hostname', make_callback()) + dns_resolver.resolve('toooooooooooooooooooooooooooooooooooooooooooooooooo' + 'ooooooooooooooooooooooooooooooooooooooooooooooooooo' + 'long.hostname', make_callback()) + dns_resolver.resolve('toooooooooooooooooooooooooooooooooooooooooooooooooo' + 'ooooooooooooooooooooooooooooooooooooooooooooooooooo' + 'ooooooooooooooooooooooooooooooooooooooooooooooooooo' + 'ooooooooooooooooooooooooooooooooooooooooooooooooooo' + 'ooooooooooooooooooooooooooooooooooooooooooooooooooo' + 'ooooooooooooooooooooooooooooooooooooooooooooooooooo' + 'long.hostname', make_callback()) + + loop.run() + + +if __name__ == '__main__': + test() + diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/common.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/common.py new file mode 100644 index 0000000..c4484c0 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/common.py @@ -0,0 +1,418 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright 2013-2015 clowwindy +# +# 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 socket +import struct +import logging +import binascii +import re + +from shadowsocks import lru_cache + +def compat_ord(s): + if type(s) == int: + return s + return _ord(s) + + +def compat_chr(d): + if bytes == str: + return _chr(d) + return bytes([d]) + + +_ord = ord +_chr = chr +ord = compat_ord +chr = compat_chr + +connect_log = logging.debug + +def to_bytes(s): + if bytes != str: + if type(s) == str: + return s.encode('utf-8') + return s + + +def to_str(s): + if bytes != str: + if type(s) == bytes: + return s.decode('utf-8') + return s + +def int32(x): + if x > 0xFFFFFFFF or x < 0: + x &= 0xFFFFFFFF + if x > 0x7FFFFFFF: + x = int(0x100000000 - x) + if x < 0x80000000: + return -x + else: + return -2147483648 + return x + +def inet_ntop(family, ipstr): + if family == socket.AF_INET: + return to_bytes(socket.inet_ntoa(ipstr)) + elif family == socket.AF_INET6: + import re + v6addr = ':'.join(('%02X%02X' % (ord(i), ord(j))).lstrip('0') + for i, j in zip(ipstr[::2], ipstr[1::2])) + v6addr = re.sub('::+', '::', v6addr, count=1) + return to_bytes(v6addr) + + +def inet_pton(family, addr): + addr = to_str(addr) + if family == socket.AF_INET: + return socket.inet_aton(addr) + elif family == socket.AF_INET6: + if '.' in addr: # a v4 addr + v4addr = addr[addr.rindex(':') + 1:] + v4addr = socket.inet_aton(v4addr) + v4addr = ['%02X' % ord(x) for x in v4addr] + v4addr.insert(2, ':') + newaddr = addr[:addr.rindex(':') + 1] + ''.join(v4addr) + return inet_pton(family, newaddr) + dbyts = [0] * 8 # 8 groups + grps = addr.split(':') + for i, v in enumerate(grps): + if v: + dbyts[i] = int(v, 16) + else: + for j, w in enumerate(grps[::-1]): + if w: + dbyts[7 - j] = int(w, 16) + else: + break + break + return b''.join((chr(i // 256) + chr(i % 256)) for i in dbyts) + else: + raise RuntimeError("What family?") + + +def is_ip(address): + for family in (socket.AF_INET, socket.AF_INET6): + try: + if type(address) != str: + address = address.decode('utf8') + inet_pton(family, address) + return family + except (TypeError, ValueError, OSError, IOError): + pass + return False + + +def match_regex(regex, text): + regex = re.compile(regex) + for item in regex.findall(text): + return True + return False + + +def patch_socket(): + if not hasattr(socket, 'inet_pton'): + socket.inet_pton = inet_pton + + if not hasattr(socket, 'inet_ntop'): + socket.inet_ntop = inet_ntop + + +patch_socket() + + +ADDRTYPE_IPV4 = 1 +ADDRTYPE_IPV6 = 4 +ADDRTYPE_HOST = 3 + + +def pack_addr(address): + address_str = to_str(address) + for family in (socket.AF_INET, socket.AF_INET6): + try: + r = socket.inet_pton(family, address_str) + if family == socket.AF_INET6: + return b'\x04' + r + else: + return b'\x01' + r + except (TypeError, ValueError, OSError, IOError): + pass + if len(address) > 255: + address = address[:255] # TODO + return b'\x03' + chr(len(address)) + address + +def pre_parse_header(data): + if not data: + return None + datatype = ord(data[0]) + if datatype == 0x80: + if len(data) <= 2: + return None + rand_data_size = ord(data[1]) + if rand_data_size + 2 >= len(data): + logging.warn('header too short, maybe wrong password or ' + 'encryption method') + return None + data = data[rand_data_size + 2:] + elif datatype == 0x81: + data = data[1:] + elif datatype == 0x82: + if len(data) <= 3: + return None + rand_data_size = struct.unpack('>H', data[1:3])[0] + if rand_data_size + 3 >= len(data): + logging.warn('header too short, maybe wrong password or ' + 'encryption method') + return None + data = data[rand_data_size + 3:] + elif datatype == 0x88 or (~datatype & 0xff) == 0x88: + if len(data) <= 7 + 7: + return None + data_size = struct.unpack('>H', data[1:3])[0] + ogn_data = data + data = data[:data_size] + crc = binascii.crc32(data) & 0xffffffff + if crc != 0xffffffff: + logging.warn('uncorrect CRC32, maybe wrong password or ' + 'encryption method') + return None + start_pos = 3 + ord(data[3]) + data = data[start_pos:-4] + if data_size < len(ogn_data): + data += ogn_data[data_size:] + return data + +def parse_header(data): + addrtype = ord(data[0]) + dest_addr = None + dest_port = None + header_length = 0 + connecttype = (addrtype & 0x8) and 1 or 0 + addrtype &= ~0x8 + if addrtype == ADDRTYPE_IPV4: + if len(data) >= 7: + dest_addr = socket.inet_ntoa(data[1:5]) + dest_port = struct.unpack('>H', data[5:7])[0] + header_length = 7 + else: + logging.warn('header is too short') + elif addrtype == ADDRTYPE_HOST: + if len(data) > 2: + addrlen = ord(data[1]) + if len(data) >= 4 + addrlen: + dest_addr = data[2:2 + addrlen] + dest_port = struct.unpack('>H', data[2 + addrlen:4 + + addrlen])[0] + header_length = 4 + addrlen + else: + logging.warn('header is too short') + else: + logging.warn('header is too short') + elif addrtype == ADDRTYPE_IPV6: + if len(data) >= 19: + dest_addr = socket.inet_ntop(socket.AF_INET6, data[1:17]) + dest_port = struct.unpack('>H', data[17:19])[0] + header_length = 19 + else: + logging.warn('header is too short') + else: + logging.warn('unsupported addrtype %d, maybe wrong password or ' + 'encryption method' % addrtype) + if dest_addr is None: + return None + return connecttype, addrtype, to_bytes(dest_addr), dest_port, header_length + + +class IPNetwork(object): + ADDRLENGTH = {socket.AF_INET: 32, socket.AF_INET6: 128, False: 0} + + def __init__(self, addrs): + self.addrs_str = addrs + self._network_list_v4 = [] + self._network_list_v6 = [] + if type(addrs) == str: + addrs = addrs.split(',') + list(map(self.add_network, addrs)) + + def add_network(self, addr): + if addr is "": + return + block = addr.split('/') + addr_family = is_ip(block[0]) + addr_len = IPNetwork.ADDRLENGTH[addr_family] + if addr_family is socket.AF_INET: + ip, = struct.unpack("!I", socket.inet_aton(block[0])) + elif addr_family is socket.AF_INET6: + hi, lo = struct.unpack("!QQ", inet_pton(addr_family, block[0])) + ip = (hi << 64) | lo + else: + raise Exception("Not a valid CIDR notation: %s" % addr) + if len(block) is 1: + prefix_size = 0 + while (ip & 1) == 0 and ip is not 0: + ip >>= 1 + prefix_size += 1 + logging.warn("You did't specify CIDR routing prefix size for %s, " + "implicit treated as %s/%d" % (addr, addr, addr_len)) + elif block[1].isdigit() and int(block[1]) <= addr_len: + prefix_size = addr_len - int(block[1]) + ip >>= prefix_size + else: + raise Exception("Not a valid CIDR notation: %s" % addr) + if addr_family is socket.AF_INET: + self._network_list_v4.append((ip, prefix_size)) + else: + self._network_list_v6.append((ip, prefix_size)) + + def __contains__(self, addr): + addr_family = is_ip(addr) + if addr_family is socket.AF_INET: + ip, = struct.unpack("!I", socket.inet_aton(addr)) + return any(map(lambda n_ps: n_ps[0] == ip >> n_ps[1], + self._network_list_v4)) + elif addr_family is socket.AF_INET6: + hi, lo = struct.unpack("!QQ", inet_pton(addr_family, addr)) + ip = (hi << 64) | lo + return any(map(lambda n_ps: n_ps[0] == ip >> n_ps[1], + self._network_list_v6)) + else: + return False + + 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) + self.range = set() + range_str = to_str(range_str).split(',') + for item in range_str: + try: + int_range = item.split('-') + if len(int_range) == 1: + if item: + self.range.add(int(item)) + elif len(int_range) == 2: + int_range[0] = int(int_range[0]) + int_range[1] = int(int_range[1]) + if int_range[0] < 0: + int_range[0] = 0 + if int_range[1] > 65535: + int_range[1] = 65535 + i = int_range[0] + while i <= int_range[1]: + self.range.add(i) + i += 1 + except Exception as e: + logging.error(e) + + def __contains__(self, val): + return val in self.range + + 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) + assert inet_ntop(socket.AF_INET, b) == ipv4 + ipv6 = b'2404:6800:4005:805::1011' + b = inet_pton(socket.AF_INET6, ipv6) + assert inet_ntop(socket.AF_INET6, b) == ipv6 + + +def test_parse_header(): + assert parse_header(b'\x03\x0ewww.google.com\x00\x50') == \ + (0, b'www.google.com', 80, 18) + assert parse_header(b'\x01\x08\x08\x08\x08\x00\x35') == \ + (0, b'8.8.8.8', 53, 7) + assert parse_header((b'\x04$\x04h\x00@\x05\x08\x05\x00\x00\x00\x00\x00' + b'\x00\x10\x11\x00\x50')) == \ + (0, b'2404:6800:4005:805::1011', 80, 19) + + +def test_pack_header(): + assert pack_addr(b'8.8.8.8') == b'\x01\x08\x08\x08\x08' + assert pack_addr(b'2404:6800:4005:805::1011') == \ + b'\x04$\x04h\x00@\x05\x08\x05\x00\x00\x00\x00\x00\x00\x10\x11' + assert pack_addr(b'www.google.com') == b'\x03\x0ewww.google.com' + + +def test_ip_network(): + ip_network = IPNetwork('127.0.0.0/24,::ff:1/112,::1,192.168.1.1,192.0.2.0') + assert '127.0.0.1' in ip_network + assert '127.0.1.1' not in ip_network + assert ':ff:ffff' in ip_network + assert '::ffff:1' not in ip_network + assert '::1' in ip_network + assert '::2' not in ip_network + assert '192.168.1.1' in ip_network + assert '192.168.1.2' not in ip_network + assert '192.0.2.1' in ip_network + assert '192.0.3.1' in ip_network # 192.0.2.0 is treated as 192.0.2.0/23 + assert 'www.google.com' not in ip_network + + +if __name__ == '__main__': + test_inet_conv() + test_parse_header() + test_pack_header() + test_ip_network() diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__init__.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__init__.py new file mode 100644 index 0000000..401c7b7 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__init__.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# +# Copyright 2015 clowwindy +# +# 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 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__init__.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..203857879972b73aeda069a1f5ca45fa865c072b GIT binary patch literal 301 zcmY+8QBK1!3_zW>NN5t9xCH7~djW)Yf_;=P`eT(QXckS}CGMsjZ};GXJ8%IU1k#A& zmpsdHTz=iHE+2=N5q?*p=l0V;deDp2ZD0cM26_X&fW81Jie!REp5~M(=ncB;TDks31Ty!f98GkY?sMB J-D|_70pI(3M_m8_ literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__pycache__/__init__.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cea2d93b43eec18ca49ceccd190be9144a0a65ac GIT binary patch literal 275 zcmY+7J5B^K3_v}f6{D3FM9&4LnOp#&ZD?ue(pbYJXrs+%lsK&RNEDocTcoAp3RE~q zNMOlNezs-rce^ccybmPbT7Zu#zrhZB&UC|2pc)IVk&SC*>pI!FUiJ;B_7TSJlQ&@O z%hmjpg3YASxHUv7gEB8Bnc&Oi8jUA>&VHdEKN&h;qM5YQo*u?c*&>fAM~xWk^Vn7N z6&uDY#EV*2eBFzjqOf6^tw@Jig;NU4kz0R6Ow&JI;(4VY5REs4IIZV0+`6E$)pyr? HG;zW=&1g*+ literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__pycache__/openssl.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__pycache__/openssl.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bbcec482fafaf1f3458509140e5ae69a563e7290 GIT binary patch literal 4877 zcmc&%&2t+`6`!6NjYc0<{1rPNPNHOY*@#MP5_VI83R|JF0TzNbY<9P{=F&Bq9@%4$ zG@_^F#46>oUhWhJP84NRT;RZgqBwA%DB!P{D<}U8d*b(cMz-V%Va;m>;f1ax7K?tV=>LE6W&J(>*?vC0 zEGBxVdt+w)`CqMh7sZNLh4+`l1#uDmWpPPdM!zQ3#1-^c#8q(({Z-Kv*U?`SH^dw0 zo8nFJ7W(Vq4e_-d^XE+dExf#MAl}Y1qxJR?<+f#K=lo8Z#6uPGs5eNY%3KlcMroAv zv&KM1eZ{-OexNk}JW^XcRlW*)VPCbJ%nf+3YZdJi?p) z35_xim|(&b)}iH_$`rR+YnV64h(7^9!7D%}z! zbx7v0PHEqi+sf`T$Qr!XuNL$hg8`Sjz~s+>do zym~k43y!s=FZbjEL|NtWiy$0Ou4N6poF`#fhN;>cgxTULOa1VcSC&-LXd%}lfTlfh300*J`UTYl;*CFLSb)-jlJ0wzP1EuAvhm0_4wKl*O_ecstq zVJer%I#sRA1+;h?jl#?Vl{sfoO`^ zSYMxE^9*bx2z#2w>w~?lPAl_5a0Bb6Uq@qD4l|jK}YP@6$C>%3k>{~C*u_fl3{2?=Q)=^ko(I*kvRaNbPC)Z2w7oS6VCwXp#7thtgsi4r&d1(| zAg!Y@*gUH<8$LTsw!qK$WJ92RdIy9u(yC%qdEkq&&)}7HV2|PHp-El|w!`GQDhZQz z?wD;;eX`n;f;3t^Nq19*YAE~7P|8HM%JLeKVs1NCAm!y3a>jwFFEb5&B50D_)QcwUNDh{f%t zC`u(=4O^AWPQ$n>-=?|NPPmtu(QalY19=k?uM7zvDCs%TF7=WUdm1S&#{q}B#y`bi z`X(9!(5DSBkJiPT*M4SRq}&xd>WD12 z>Jo=5Vu9TuuYk(85N(otA6+17gBpTn9$PAOS!vgghhfW>cZsu6%YCm=)RSbWI2O!U zd^&i}P*?qJ%tv(eJ50~+V;RyhMq zsjPFJRXOh^Vi*%$=lrKbKh8(wJ**@jQ1cyXD0EXXYZ6O^U#_G11aC@MLAsXRx@&3a z5hd}6KyuWih7?+BqnaP4x9{A&e>>;|1@o=DC(PY<@0~DOt=pMWn=o2!!jM{W%wV-+ zhSZK3y_%9!t4$ca8kqX=w{<)B3ywbSm@CXq#>k6q=gy2~!4;Z!MoyF0lAZ5x)9rq37h9zrms@T_+8*0f@^Vf(nS8JTnC#uabo`W_t>G-41pV zO@0-mvw2(_6$cbJ5W^AA-$Sz~3i3yw&uFs9ud}&u+Rgia zoHF|3Ga1#Jn*79Od!5ab(2}1}{_~W{_Wx!wQ1Yvrgz{geOn!JKlf_nkowb?{YNty% z{OgpVr)M!FA4B_fR$Ofbd;afJCVm2%N_)?b`kc4kpyJ*g;x>%pE@@*(DhHA-6OxL8 z=LGtX*UA4gectDXIh`c>|3Fro`s-oQ*e0Spzr9Y88Kf3hQTi+XQ&|>-$ zM)%~*Njlph5iT$d9r2=+I0% mXf9YytB!wEj%hR7v@Q3B>$;Ws^~sImY<0$1~&g%;#_Ba6FC?$dAx4F+k|2bM6FubO=iqpac@QKqY3l zB#e}l(b8icc5zY+*Z_7~_+`KXhzPp}Q@Ca<(EY>>%jnJayRA#E19KCB~H`T&$c8@xrAQ5S7UhqoA` z-Xgaag|parn}NVxEJ#PU9$=+&P2sM}4t~Lg65j^PR4)s8VrePfS-))rV6wiDi(DJ! zDzfocKz4lmkf+Vsjx&`5 zYQ7`xB_!7UppX#9@P$}ChuF;{r`(GYa^Zlyh3gm5t?~AK1}D8ZhTJ&yRDS?NU>F-1 zr%PzLA%Y4|41|B>UE+HWgGV~Fp&jnXmbg&^x`VlLO@!`xt&zojRs9;GtN)00c;!E$ zLYL38PaY67-7%u};i!Kpi&b0QvXRg}Zv7oOFq8St>Km|A&)}ltf`!~}|hGKW9~<|4+US$phxEE=sH zyVjRD+&OUMkR16PRB_-Z=*r3G+&Iy#SwDfIib|oeTHURdR;{nQ<&}2ZC(wTJbnyHV zA-|(=+-ktAKrep-B8j9a$!I_`7O;#5JaYnvqMoI-%njVE9@H~0@UlkG$o#<1nn5#b z1ubjC({|PgI>0*{G?quEwX zxVo-A-KcuswL0Ip09r8Kz};`<8fAD3L^_ktT5eXt+Oh!Y<~8UhEgcTz=W> zsx~-jTKy!K0xVRh_EZ;Djr)(Hc!0>LI>-t_mzcVWOT9OU72c6@ku*^6g=z!!rMY(g zJb)kkogV1=KTIdF?vD?x02HE{c3S{Zl*xW>Ii%`b$XQHm636einmxrZVPKzi^ zm&?si7Q1B;ZG-BJu)MT%POQ$bTwTo4V9#`LV|2e#-GGhDTR;f+s7pQSF^@WwLH9u# zbBvwv9k6o2M!f`U&czY~#R~Ki4;zz!N)j-PCrBbLA&OvhY|6N%^}r&vY5|R~G+@IK zcd-$vfYGG%HN{jLu@Mn$#DQ|{jMyOst_B>poI8TZg(SVB>suboH1eCEVW3$9jc+vz zpy8nT7Bo$(@pP+-ZCL}keH?G~ab!V4jUnA{A9HUbdi$NP^6k9X%@?cuQWVbG>!>R* z*n2U2JKi%a*|BkB>L7`MR>n5SW7Vq}r;O7+Q~{gG zA`i1zT}QiD(T){^(*RM|P~{1zABh&?K1db|0x$w-@c9;01lD%uvHifp8MKzlt2V8T2h0T&}qHb55q|+n>m^6SB)<#_2lK|Z`tehnW z0Nhh7BDF)RJ>08W1N${J0EN3=AqeVL^O+04!x6UVh`k4qA*ilxwZPu~-uG-C3j|*~ z!oxmC3`nnScpB&G4d9J`@AEHKKNAl=4eqOXP}oFM3&_2W1mCJEv0nqua45S9^TBWx z?)Dt@Ci0F9b!aHm(m^OCRNIU9?G6WE5(Uv&d@p9;rmnP?zvjC z2Jix0h^Y|97a}Xv4q&^$L&+(R`A`c#W8X7VDe9Xn3XZIe;W_4o;;SNL16O5CbFnjNyxH3$^`) zauF78|4S<;!6>=Vju62rmAG}Gs@J|6=Aw%J9<#u~!y1p%=JY;n?6)-3?Bj=_gzZYK z+k|##hp7*MdvU(vMgDhR)g4&=7hl!;pdCA@?xMv7|Jv5S<65+jN3aeu>);B=FU-LU zLGr)5+CAs0z_R=Jg1;YvjQNv=NiIb1DrQiBsD}zNNMSH5js(*}VO$$GvJZEa^Y*2; z?=RN3suqqN zR*>M4)w@Vek+ob^y6;p4;4c0HQeuso<2QJVxll+P=5U95i<3P_aoTaby5~W+W&Ka2 C9mp3bah`Qd2Czaa&|63A*5AE}`$uqy5nVp^a-uL~zvpe6o zoSZBTe^2?H!0g_dwwD_%e;%xi!(#6QL=!!##q@w4GXh4;445%bz@u}&5%mVV>eUzU zsn?7^hL{z4z#q{9nc=LkS3C`db#~ZW)h^Hu%noG7v%@)RD~DU+72h%~kSiJD$7}nk znq*4WGEK6=9U9C)M{Ku~Y;0$%?L5g5Eu2?ws@INq&ArhGbdvVc0iVv7j*^Gh&fzZ7 zsoJof3+Fdzx)$zCiZN3w$WI|6t-#~950%=9Xsr_tMq)Jy%W?e@SrD-!iMX3vV@2Xl zu)H>2=BVw35vMX}J0T|=3&))jH=`_4QyI2wH!~Kh4m(yTZkOpiFaDT1FBTJvodg`M zNpIHb^`zcpB(-K;*RW<{kKuTlyrL(KW@E0gLaR3&pBQ2^7%PnBM!7Dgc$WE{49DMS zH0TZbB&|VTVJsTxWH4n7#w2ZCj}>~OQI)w|TdtQIDuI5K zjQtpg);dH38$K+Zu-H9;3N5M4%dgiPF|?#ET3787qr0}wnD4_L1Wbg?Bc@1M_QRUG{S_pM#hn{04lP4tTmYY!-vMZyJQrC>h2-jCzBT&cnNu(ldJFd@mtlBcX*2Zf>Wz|*& zujPeA6=BzmhU2cshK~gvt25uwW3FzydZ`*$foU#86zdKFSmIIaXpALiyrxXord z|1>KdTUI0mSt_hee4U)&kHuU?2bkw*^!6los%a z7sK|s?arab6IC;pF&q4AX693e1+I~(`E~IAU{Hl0WQA;+ts(5X{5In*Y&oQi zk@JtQEUyZnFIJLTm1gCk4KVyFlyN3og6k&N4+p~5b2HWLEDB+*0&H7@fC9rB%=bL- zIP|MATHWwYF&3M*6ixT(*+yMn+X1HaFG2JOham@pZdNcDOGs^$>)FBJ{Mt}7{f;#h zCsF5YbDdTjtburU6c&{WzwY;_J^!}=StW`1(dy6ph)N$*ekeg|JOy5r35$0GRXMAW zYej?_<>8?+kjYro%}mqlIvu{&(Q9_ZMU&|pRr;do^jpJ^ausI(cWtiHvCUOl6K|cw zPo7MSApU!fPr)IRu(agZw74hL0Vps6o;Xrw@!r##brv39I_ZoJ#$B21#$OEe{MNew*XFJD1w>f{1RO1&(R3*{nNDi=$GER##*QdusI zvO-qMDp@USy*T}VU9Wr;l+#ol~O>(o`B2CgPx5@^&O>UQs za);b0n`E=xC4ZDZ$=$L=?vZ=tKDl2WkO$={XE`YUl3&*1 z=8HvNt5;|kaU(`_9mak#a(FAwu?ShX6CUivD7K;2&%%<<@MJTt@hYxx3o1AOwfq>- z&%yOFF}i8UP6*?x$N7h&<_+*9h?)&S&K}1IN240MkW~vYeG1QtkM(smLKEqh}tee3~Ny7iKy@vWNH+Q`5M)E1XVo|@lS_+*CPY>!Ws{9 zxEjtLkL>2d9~XA6gpXs8&##cPLO3)5d1yjT`ohJ9aJLp7*TABEFmgFe+z30SpoSxm z-|fg_Z}_+$CRd>@J(10==#}$Pg{$Qkc-aMxy#+6_VOA+>wFuLuX1idQf51>!|l;T)PV@avjcBj0oqzhD#97Ae^lSu3L`m4?u3_qp~|tr)h9! z9Y*;nVxNsMe}KAW!j0qL#zPp_a*VtJwdjrUZi5%sAm=f}v;dX_;lU=N zw`}BSJIpIY?7d*rxrp#H)bn9@bqjL%gX)BpxFdami0(vd+$95*Y5UM=xv2OExbZryJ*e)E-=U|5 zp}$wb{+}gEJJda)0^{9|@pQwr*29I>$V@+s=XOND3Rk%oam_$3`@@kP$YwLHbvf*) zL?w42mJ87x2NBg~7;qL$Isp-#jLg3Qn|7m$k08oI)O|UeGBL&{QR@$o|5H$7X6Zp> z`zq9Y1B@Ag`n5+FXCmj*VB&r7WISxV61FxXGY)LZRri!v5Z4)q=PP6@8`T~I58px^ zpGBRb=<-ic*)51Z3HPR=UQ>{{$C0nQQQ;?$=}oZWF=Vw4PQ_5kHK_LI@M$b6cmuq= zM`dz0%)1nM9|~h%gSX${{xT8fjY79v1gF18#dg9|7k#q;=HG^TKaG9}p?9XskE#Rr zqS7D2gnh8(W4JRIp8kZ&-U?ecqC?iffzhx&tonQ#x?m|PJQ=tBYG520I@ z!Swy;kndnqPxRn8)c8$!I|$AVK@WY2&Rc>meh)VO495?^>@Mh~6)<`$?41wy=fRhY z(NUeyiyqkcB6{|LL-zy&<29NL$Hc3to;^&*lf=UPT-5W2XVKXX9+|O?Sp_@*PpJ3e zc~$SOCB0a*q{-I?z1mpow^us8i}^IWtMy$^n|CYu?ppu+%31t|YPquaf4VXrD_YNe z_{y2KM_t+Y4_DrE=*oL$sb7`@efZ^6V)4gcfo^rgOKL2^A8M&{h?T^f#2<*+L|1~( zKB;erw}|5jKEkGMB5o%hBz_?-CuR~q5Vr!>Z@6|G@pob?aTmdd-PCwu0Ko@=RA=Hl z;xmH3dsB-E{`gOABvJ&Q6jIj{*AeT84+%cLr7j}gApS%gOMFc%B*qc@h`$gOL<12g z_%N1whH!~7#7<%)aSXx7ozxoQ9pWnDOJW=G6v1E2sT+vR#KS~8;$Ot8M1Nuhv4=Q= z_$#3j#wYL;e{-jPB)Skwi9W;@;y!|p0;wIuRAL&@kC;!~LGXuo${=cq)x>4Q1maoZ zV`4wio49~bzsFDF+Svpjh*I|wvxt8ZR})VYmk?g!AaOC_CmtbkiED|E2$Oh>xP>^A zm_vL({EfJgIG@-+TtVa!=Ml?@bBVKv81YBq9wI{I5W|U2i3^D*h#*l)G!h4h=ZGkA zKk*OZHKK~RlPDpk6Q>g|6TOJji06rGh?fY92orY`a|u2er4|ui5$%ah#M{I}#0=s+ z;zZ(6VjxjQJV10LULkHK1`z=wlX#LCLimX8gooHoOeB^7YECQVa5G7~NK7VvOJop} zh!F7taVqgGkx%p_MicXhONmNiEwPtaPm~i2h~>nUL=o`?@q40%s3WEj)x_(B5UYrH ziT8=yh|h@xQBUk5nutFW-xHq@&BWuxcH$@EXF$y(*^IJ0kthvWeNuRW)YJ}aVPBtu z3^%XM#9|@l3}cD7x^G(jc!s_N&tNeVZti&74qKeYoorbNtH7rY4P;;*1CwUMT2_Wz z{rQmrL^hGtid?L@9UaWvrRS@HR@knMIyAy&r8P`=^wxVF^}n~?7yElE_SX5&<9Z%i zxZd)~f#%`$qphVs&7)gB!L_#AxQdad=VR%m_iJ6VMk0ZfRQ;JCZw2(t9tq%=Nb8fj zW8gQEdRA}nVCJu7OUD-GzM3!-$f;^QJs*cYWziOvs1RYrif=dsU1>^KGc?W=7)&Sc+Ud*RYCt z9?UkyLXmhdSkRU8P35&tt%dm}%fNvaGhulM=0nZNM&cD#5ANy0JsH7ZC>q7#$Ez*O z=!P7}N}Dx|yS$}~F^;rvCvb-^QCbx)b5vYv%Ea@dPr{*_&y3h1y`~4l-b?{^M)8n7x&-CM+e*<2hsh0o% literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__pycache__/util.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/__pycache__/util.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da51b6f5cbf57a14e04746c8d9319d8aa9b5ad39 GIT binary patch literal 2683 zcmZuzTW=gm6|U;8zD&=>7mV%fu8>)61QR4%+La*U5|T}VxI`f$kU&dh7*DnBj(fVt zRW*snszxY=H-r$sV9Xm5|AK$8kM)Fvgp_9_9uU&3C3;k+Pn~o6)b0CD&E0M{ zVR-%)oAjT{jQxW;7mo+!E}BjuB$K>gW1jM{mwIDA^~WL=oa|l^q(Soz(@^?S{FbGW z3}lFREF&4?-I6Vt;GJY`**^5tvn)Af_}Y8Q`klAP&-%hG4R>`_Oieb($CFAK7t8!r zuJfvNy@|?8GuWG!Bh&O>=jLFb&Cq1ytTc~DoO0k1XaU-NH2n%B3y^ zmE?_Q>@Yrq;0Z|J>V| zYPC~TqhYb54~DXOt*g-?OjkNP+uPT=BQ=|tYG-QlVtX=kOM7`K2SvWChH5q_%_i;Z zIs^*@-{d{s;R&S2V`x3D$TjlGk7o(3gVr3z0ggy=$qH;9-d+JD)j$vqC@{Wdr`!k= z?0JWg+O}+;*W?M0Ke7ym^+6rka30#IITC2nx6uZp-qG#n?D(z?5p}zcP4qIB0tmA< zn72$~<5S*5y|Dhv_Pk@`GhVlBOTBZ@jHzARgSVe8BX;CfFPEGsgxd|^SB{|aVS4--Vc;NT6SS~igF8|;uWq;J?S z5c6}i=U^mp>aik5^tluFA3S>Ge4L>3N^`Pt1ro5`+JU&Kw{M-SEF4!&(S;MgIz@=RwyFOjt!9C;L3?6L+2_ao!Bp`T^Ah;_0SmQqN3X0 z#}u8&%e~5p*F#mh*2CjbHlf1NUrJXemDc%gkrAo~L>HZQbv9HZ6qIHGX$PJK-r-g6 z(`1_I1`S`+*5!xsh1$$kwJG#Nh%`p1$yBMG4-LEorL`jVG}K&(FH-l*5Vz3uBM3%3 z@`Sg{Lr;KN319U{E_)p=c+V3e_7X4T^h+9#;H&Uo@K1O#MK`=`GLNW=(?k0jqcvQ7 zZ^1>6Dthgq4ihg?0-LvA3+dN^6mbwq*hv zo$^Dbevfh)%1}lqsBOzoL?a-zW!oF{ z4vhs)U*`S$wY-#p3Uuds?rS;!3{Ed**u{*&7Fvhx+GLZx&(^UbtonRB8xf22x=G$( zAHbgE9ebjw8kVve=X4Y1bi14+CevmMSC2ApB{no?zhUfOP}s?eo@AN)=GINQwL3%D zj_$V?SpFK^E?ndS>T{Iupt;h(%&bf!^5BAV;dCN#r!81OrPKB9X`Ly;?n9+2)$f5p z`Q(6Z1eq-~McLIhi5-YE`Sj6`@Be7<^atrfj9E=_abUD!n3rmmQu<&3t{_^0BlD)~G1u!lY5o?A}&zSsK2oDyPImfEkoUw6NG?|WZ&Px)Vq z^MC*Lw_k-a{8aG&m*07ke_s-b@b#o4()Fb2Nw*};l61?`EK9c{&5C%`C`o5Vx>adb zr8_IlS?SJ6b56Q7Y1X7WFU@)B9+KuEr(KrLf^-+9x#;Q@=^U2sk~Ei4pOI%+X<4E> z60AtPD#1~SsuHY8G%H_=G}k4XQ{k9IH5HCaG%xW9X?qeKV#^zmubL+%IW5rwzwt?F zmnB-1yV2pP`V#6`Xxx7U^=bW;g8phj|7byft)RbN&_7ntKVHy3QP4kG&_7ksKV8s2 zQ_w$K&_7quuRHzoBmZ0|jK5eIf2lD3a$)?BP(LsD4*mV(LVu%BzvAjL(*MBK$MLu- zcblgqx~4%!EU&9@MxqTB&PueY!a0dqt9 zAx69&pnFHQ8+U>Dgy?TnP}n6DK`T!?0~?#9+fOrV=g<}<&yze!d-gCY{VeHO(;oCf zJ8F87*d3GGz{cIUXB$<{#c`ntcY@6H?Tl71&ay8wowWB%8(aHX+_p#An8g^6Sup)G z(;BqfaWM z%bY|=i;BD{OHdM9Vz%$iNp?eQMUaVaJo&vR@Dhwf?`~Dh>`0y)$>^$$zVp(qs^%dv z3#y4;r+_Xu$)a{0*5#`52J!fYI!InFvBciq(p1axaH^rg5v|Uv^2*2tOs@9tq6O=^ z$!B%)f2-!|DBf_gUE_lV>tc^d0PYYVpa$=3Ij?5>eVb}j;$Z*7TN;&zUp_U@Vr!gd zTl>AB8{?@(I!PY+-`Nw(CG}9${XNd$rUcTj!*10PN47)p|A@m+DR`mup|qzeSev)gu_V*h0i&BBGw)mDJZTU_Y!O3mkP=td&Vhp z%T;v_Rr#VH0mQn3}Fe0{gbu@h9n|2n*b{_5i$6KG6*1nB%pO*T} zC|w-MVe;g6u`Qzo6@BW@vq7wJ?4&^yM-6nzq{x4P>SmUvb~E1zqVz?chI@H7UD@2& zc&qBdyO|=OdEVLR@2kr>yBW4!z?}I9T&P_0%3jS|@Rmvo-a@J7t>L#)TGAieC@A9NXQbT^g zKIXFOW4HDvsOE&accpoIKyCRlod%zf;6W;IDiWTqEXyvurf!CJ zoh#w>Jx{X7q#QVY%z01);ccrMwsd#MnWqyt*0b0SvR*yTvNUVZvp%8VUt~d)YA*U` zDXpPUoMyob=UBR~6NCSE{5m@ss_cNh$n?nqkb|br6V!6jhrogJKe)rK4B6*_ za!g1aerVHu?itr0#5j8?2)~8^fO@*GV*eltW)=eEtamFhx9! zf;vAzr;GwskzMQ%RoR_oh!6(`4e*|k{1G$WHKGMkWuxJ)!<_MCeH=AV2J->bd?)`~FpSF(uH2gHNWEoII!KZ=(5tflh=OL&q5NCFJ}P3IR(H=*Lkm z;kOF76Yw?sR=hRs3u$%*zwz;C9~E#2a7EzYRG|Q)TvjeA%N~;I=R_8ef0XAY?SMv5 z8J?BE8w40&Ge_W|i||#L0Vxj0MCOcm=-ySx0-g0~vPo!JgPiBnHuW?3!av6rKCjY- zqnMMd-eRFF#OLv^qtH{#^Pn?`bz|`Fu#LUgo&!C_4yipIScA!xggOTt8(Kfm0|9n6 zax*^M9`QGiNUV7$N+F46^i!rFP0@vVm-8a#6Z^Nc^lw9S5})XR?WGHiiQNE+|U3F={HLQbw=A}x8VrDbn&r!jf}Fh-AP#&pwY(20%l ze+qT~V;1*WXrv$gt$WwQR+x~-CXGM2 zrH%FJ##?vpvT>rsk?q!`&OuXk)t{cG7gH%oLVeedD~2oeYVmh^qo=(fjf-b?D7mSUptNmN8t^iGo$KK`fZ z(F2@9(A~k9BiW!gys}M>ceo?&q>-L?xk7ygwEkynN_ht_P z_yd}a6>0XLVCesDb}_l%uibk6F1Y*NpWn&w5x>&kV72#a_ggTbzp>(9{%^BGoBhCM zW5vI|li55yIbwdl7Bi}vkF7Q_{kL~9)_;gm@7GMysG5oW;vW+m^)`c3NRaf5*=k&- z7HQGo1ugFAJeZ&+KFQrDrTE-h-1|O#fi-#CrKfFoj^<+eK1APwOuaDjIk&zyn`+!} zZ;ITO;0}>|k7wL#6$LW)vPVHDkKMIQkMb9}$Tb!>I4R?uwszytx($Xj;gf{@U$NL? z@fnM!EPl;`kLGW)c!1)4kTo6gGk3)Mh)j~>q$(H68|7thxl}7vy=tjiu3f0j)oSb4 S%N|I)@Md literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/rc4_md5.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/rc4_md5.py new file mode 100644 index 0000000..b0105ec --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/rc4_md5.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# +# Copyright 2015 clowwindy +# +# 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 hashlib + +from shadowsocks.crypto import openssl + +__all__ = ['ciphers'] + + +def create_cipher(alg, key, iv, op, key_as_bytes=0, d=None, salt=None, + i=1, padding=1): + md5 = hashlib.md5() + md5.update(key) + md5.update(iv) + rc4_key = md5.digest() + return openssl.OpenSSLCrypto(b'rc4', rc4_key, b'', op) + + +ciphers = { + 'rc4-md5': (16, 16, create_cipher), + 'rc4-md5-6': (16, 6, create_cipher), +} + + +def test(): + from shadowsocks.crypto import util + + cipher = create_cipher('rc4-md5', b'k' * 32, b'i' * 16, 1) + decipher = create_cipher('rc4-md5', b'k' * 32, b'i' * 16, 0) + + util.run_cipher(cipher, decipher) + + +if __name__ == '__main__': + test() diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/rc4_md5.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/rc4_md5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b58ae614e68c1df90de7aaee4c02cd1da9c8582 GIT binary patch literal 1387 zcmb_b%Wl&^6uo1o&7%noPyvMnh*gBtWCL9xKHc0UbkJ2rgZpmt&(rwC`G;N~Rpc(eHq~P(o7>jUi(*-tk$RN|? zE9uY|pW+rBa@@X*J9IeRp?HTbh^AdidK7nQ;ZgOSk}iECQn|c-GU%aM!xIb|Fm4wn zJhnx;bcr@uSyaxokc-W!v8Kq~9Ry`%a;Lv6^T=JM&WxLD>q3`gN$$o2#fLRviZaQq zO~sE)IZrBUxEQrzc5rXuK4RG3!*N6>iu*fn)4EM39<327Qk%fL3Vd1BA=jW20HbK# zVVu^;H)t~OaoE_gt)iDW74tDa(#AZr^VA$mHH%+4E-cF!y^9;0Sz=uva4!%ljqE&(i!)n93tL?eM#JHaFp@?`d=#b|mMh0oL{)+Tbxpw& zBy0zUysqlu-&Lm1Ic5wS53tcNES5XlBn}QE1jLwLarOxTuxBBQ)X$+Tzz{={4HoPk zBnfeXl5<5j3!n?sq5#ssy3b6n!LKKu#zcS~*UA(zF?>sB((646lne6=rC<+6h7fr< z9>@Im3mzj`>--+PYk&@G(1J%u2M=K!qf;&O4Pc@tsft8u6zdNose@alOwN6gJomd^-)sAAwcGMlU)^uC H+g{IBGg-oELo2PBBI<~gPBisKu64n+Su8jvzzA|`w&)LAH5#Isc-zn&o!2 zmk+b3!WDBDKUOR~sdQV|x1n$aTR-^jCA+&y^GjR%Yh&D+t87D-DHm-cWF>n8Vd z!t@D5!eX0ErF{e4XZH*2;CH&=YyUnXiEVH6UYcW|`Q-#Gz>O)Br?K_X14TCcO)-om zR{cDT;-~`?oi^nisOv?Z+x2oMjPipr@9vewi^}@i+8NbteBH%zZEz@s!p=EZuJ>&? zEwA%Rep@wETTOdyRaHKoX=o$K^H1b?*eVz!f!~xY-IsixV4CrNk1>52LJWcdph+kI zP*x)Nq&GVr%w5h1KdeY0`~m@mP|lE5V^VnL(K8~0))&F&1fvsh<9?I@R1}O;tMp8& zh>7NV&h&0whBYX=CnirgXkSdtvFTkg`C|H6Os%o0%_uu5JTNFPI>|4!+2OQq2%1JX zc3d#RNI41f7%=ZM97;ytx7l8nA7rat;RVNZ+^;!edKr!2VlO^KeS-vrbUrv= z0D=I?m?TTAF%KY@%m)ORH37Z`AjhCPdAzpQT19rjsR$YP6=RW5x7Nrp zn}umvkcF2W@OE2tTqT9>3i`2!+n#}af_B@o54W$Yn;owCjf7te5XYy(D3Of!<4ki! zg2z!qUwwW5OSAb!@IWs?74py*S$c~b4v1r|Ymj6*PBMLUtQ6sa9H06f?%@zdAHL@@feS(Q)>ew*-<8 zApnvmC-(v_RJdXtSLo-r zF>u^Mdg-HYp;G`Rujr0Q_E`)wYoCpZ)5OjnDj@i;yJ(YNWpV>#%IG*cz5yfUQc{p zh4ue7SaN{97%amF>GQJ@6D)%~!Xz_hqr=%B@kwuJhXwNqx=3^RXf9sOEG)-pS8jTa zO1Y%nRgJHLj$`@KCU->KYmueXyVsh#U%4XV@y3MxK31CL8FCM0e-ClNjor~MJpLYA z<&fus$hUUmu65PQe4{_+<`Zr{<>m&OQ}Bs&9tB#5+s_=;-r?<*?D?91)1UDgVCkw? a^{akkb+O}vxo6aTrBQ3t8@0Bo;rlo2F!J31 literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/table.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/table.py new file mode 100644 index 0000000..60c2f24 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/table.py @@ -0,0 +1,181 @@ +# !/usr/bin/env python +# +# Copyright 2015 clowwindy +# +# 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 string +import struct +import hashlib + + +__all__ = ['ciphers'] + +cached_tables = {} + +if hasattr(string, 'maketrans'): + maketrans = string.maketrans + translate = string.translate +else: + maketrans = bytes.maketrans + translate = bytes.translate + + +def get_table(key): + m = hashlib.md5() + m.update(key) + s = m.digest() + a, b = struct.unpack('ku;)Xl88+AaDD2_?q0WKt&Ff$Rc&{lC@>TX@C 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 literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/util.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/util.py new file mode 100644 index 0000000..212df86 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/util.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# +# Copyright 2015 clowwindy +# +# 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 logging + + +def find_library_nt(name): + # modified from ctypes.util + # ctypes.util.find_library just returns first result he found + # but we want to try them all + # because on Windows, users may have both 32bit and 64bit version installed + results = [] + for directory in os.environ['PATH'].split(os.pathsep): + fname = os.path.join(directory, name) + if os.path.isfile(fname): + results.append(fname) + if fname.lower().endswith(".dll"): + continue + fname = fname + ".dll" + if os.path.isfile(fname): + results.append(fname) + return results + + +def find_library(possible_lib_names, search_symbol, library_name): + import ctypes.util + from ctypes import CDLL + + paths = [] + + if type(possible_lib_names) not in (list, tuple): + possible_lib_names = [possible_lib_names] + + lib_names = [] + for lib_name in possible_lib_names: + lib_names.append(lib_name) + lib_names.append('lib' + lib_name) + + for name in lib_names: + if os.name == "nt": + paths.extend(find_library_nt(name)) + else: + path = ctypes.util.find_library(name) + if path: + paths.append(path) + + if not paths: + # We may get here when find_library fails because, for example, + # the user does not have sufficient privileges to access those + # tools underlying find_library on linux. + import glob + + for name in lib_names: + patterns = [ + '/usr/local/lib*/lib%s.*' % name, + '/usr/lib*/lib%s.*' % name, + 'lib%s.*' % name, + '%s.dll' % name] + + for pat in patterns: + files = glob.glob(pat) + if files: + paths.extend(files) + for path in paths: + try: + lib = CDLL(path) + if hasattr(lib, search_symbol): + logging.info('loading %s from %s', library_name, path) + return lib + else: + logging.warn('can\'t find symbol %s in %s', search_symbol, + path) + except Exception: + if path == paths[-1]: + raise + return None + + +def run_cipher(cipher, decipher): + from os import urandom + import random + import time + + BLOCK_SIZE = 16384 + rounds = 1 * 1024 + plain = urandom(BLOCK_SIZE * rounds) + + results = [] + pos = 0 + print('test start') + start = time.time() + while pos < len(plain): + l = random.randint(100, 32768) + c = cipher.update(plain[pos:pos + l]) + results.append(c) + pos += l + pos = 0 + c = b''.join(results) + results = [] + while pos < len(plain): + l = random.randint(100, 32768) + results.append(decipher.update(c[pos:pos + l])) + pos += l + end = time.time() + print('speed: %d bytes/s' % (BLOCK_SIZE * rounds / (end - start))) + assert b''.join(results) == plain + + +def test_find_library(): + assert find_library('c', 'strcpy', 'libc') is not None + assert find_library(['c'], 'strcpy', 'libc') is not None + assert find_library(('c',), 'strcpy', 'libc') is not None + assert find_library(('crypto', 'eay32'), 'EVP_CipherUpdate', + 'libcrypto') is not None + assert find_library('notexist', 'strcpy', 'libnotexist') is None + assert find_library('c', 'symbol_not_exist', 'c') is None + assert find_library(('notexist', 'c', 'crypto', 'eay32'), + 'EVP_CipherUpdate', 'libc') is not None + + +if __name__ == '__main__': + test_find_library() diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/util.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/crypto/util.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb89704df9d48e757af4469ae87b14cae8c4815b GIT binary patch literal 3621 zcmb_fZEqVz5S~3d=l3*(zEPy$AS$ea5>X_O5Q0KeL`4nMnW)97W?m9Fti4l}8&BRpi(@asnvS@NN=;&8Dv_1lN zcs@O$QJab`XfFKAH0=kcfjK@sOx^92u;5+G>wo99ci%KDY(;6>sY9@QcDb=n{9c$TbXbkUYaVG3K z3kI=FtYb4yt+6#IqeRD{&Gl4st)O_3VG8wg*Zq# z_TP{QIOm8sw{ZY0BXcFg=g4YKi50RXIcY`MCd6{+&`E{#8>~E1G)MSF^0+j4mX$mE zPthEQ?@C5g_^?&c02*c$y1&Ec?4{T9ud>|qX}MS4>ei-ar@ZK~0x>W%)O9yVn1B`<1ce@a;7 z^d`;9i{;H%2{4`EgQyVd*9u43U!w@I08$8{gzZ|TE~_7*2s@xo)xFWjs%+Qkq)z%L zg%`}>C2G)PylX$JJ4BcA7Zs-GW0H_LAz?cW&Lnsp8m^13^n>%>^{K z$4J364})|YJy-Z~*=$`gkKF59)-t|MEjXoM3J98YR_8&KWJm4GrhTaM0e;OTsKX$8 z+O|1s+h#gA$Wt~*GCY&N@amj}jcu@@oepEO#knpxEZUy*GvK&eB1sct1z&bNOkNLhVZuLcNthn9jrRl|LQfcDW1Sf}U~ce4%xK0t2kC$0Aa4WnUBTDf!h=*(tvRl9 zQ8gV`weUXYwA2}O-dP6)ops!D(`h*s#qXB%xax6Ol`!s%dQ_F2rfQ(QrdCzR6uye@ zJ*t{4s0#EXWf-OC6x&jQ7C0{vcndTG+5#%(05>oNq`P#Mfm@L(2k=7%yTH^H>;j#A zlrGR6SmHy58+Hv;g)t{Y`UfE%RCCmE&=DLqnCQ5|;R6if)1V8R>_WH+Ojb~ZAhba* z!rLgJUf~x|2e%c(276}NDowioaC=p8*?h=tm1z=`z|P_80q0CZ#ahJS?;yc@fwd%D zut~8SAT3bZ#mCR{nB@f_B5;YZ3fy}~YQsM{gpohiK^EnMgs!1tGP5LnoiI{>k?L(+ zH?&P6>G%Sv34w%2%rK6lm)e)3_QBM~X4}ZX4#U2H?lvTWZs9?oWfN3bK?L7!3hu&H zh8MzCtm9M}4T;4Wiy`R z^;>UWf6MRQ+`CaMDj%c%7i&p_B=gqTkjWH)7qvugc}r4xhSCcX`X8YuE7Rl5FV_7D zoEcs*0X|Iupvz#C+TWC$z$HLy740+l^37lqt){x9LLS90-d(vB`~m^T#fWkkODIS< z#*-SE4Zd3;DT-bc|A!4;T{6I}_MzeXOTA5o9`JO3^3Rrffl!Qa4Ht=?p zY=`d;fDhl7D~KelkG~~$tYhEzFqS;o0*>C3AUJMslOJ5Ii 0: + # parent waits for its child + time.sleep(5) + sys.exit(0) + + # child signals its parent to exit + ppid = os.getppid() + pid = os.getpid() + if write_pid_file(pid_file, pid) != 0: + os.kill(ppid, signal.SIGINT) + sys.exit(1) + + os.setsid() + signal.signal(signal.SIG_IGN, signal.SIGHUP) + + print('started') + os.kill(ppid, signal.SIGTERM) + + sys.stdin.close() + try: + freopen(log_file, 'a', sys.stdout) + freopen(log_file, 'a', sys.stderr) + except IOError as e: + shell.print_exception(e) + sys.exit(1) + + +def daemon_stop(pid_file): + import errno + try: + with open(pid_file) as f: + buf = f.read() + pid = common.to_str(buf) + if not buf: + logging.error('not running') + except IOError as e: + shell.print_exception(e) + if e.errno == errno.ENOENT: + # always exit 0 if we are sure daemon is not running + logging.error('not running') + return + sys.exit(1) + pid = int(pid) + if pid > 0: + try: + os.kill(pid, signal.SIGTERM) + except OSError as e: + if e.errno == errno.ESRCH: + logging.error('not running') + # always exit 0 if we are sure daemon is not running + return + shell.print_exception(e) + sys.exit(1) + else: + logging.error('pid is not positive: %d', pid) + + # sleep for maximum 10s + for i in range(0, 200): + try: + # query for the pid + os.kill(pid, 0) + except OSError as e: + if e.errno == errno.ESRCH: + break + time.sleep(0.05) + else: + logging.error('timed out when stopping pid %d', pid) + sys.exit(1) + print('stopped') + os.unlink(pid_file) + + +def set_user(username): + if username is None: + return + + import pwd + import grp + + try: + pwrec = pwd.getpwnam(username) + except KeyError: + logging.error('user not found: %s' % username) + raise + user = pwrec[0] + uid = pwrec[2] + gid = pwrec[3] + + cur_uid = os.getuid() + if uid == cur_uid: + return + if cur_uid != 0: + logging.error('can not set user as nonroot user') + # will raise later + + # inspired by supervisor + if hasattr(os, 'setgroups'): + groups = [grprec[2] for grprec in grp.getgrall() if user in grprec[3]] + groups.insert(0, gid) + os.setgroups(groups) + os.setgid(gid) + os.setuid(uid) diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/encrypt.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/encrypt.py new file mode 100644 index 0000000..44f9052 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/encrypt.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python +# +# Copyright 2012-2015 clowwindy +# +# 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 + +from shadowsocks import common +from shadowsocks.crypto import rc4_md5, openssl, sodium, table + + +method_supported = {} +method_supported.update(rc4_md5.ciphers) +method_supported.update(openssl.ciphers) +method_supported.update(sodium.ciphers) +method_supported.update(table.ciphers) + + +def random_string(length): + try: + return os.urandom(length) + except NotImplementedError as e: + return openssl.rand_bytes(length) + +cached_keys = {} + + +def try_cipher(key, method=None): + Encryptor(key, method) + + +def EVP_BytesToKey(password, key_len, iv_len): + # equivalent to OpenSSL's EVP_BytesToKey() with count 1 + # so that we make the same key and iv as nodejs version + if hasattr(password, 'encode'): + password = password.encode('utf-8') + cached_key = '%s-%d-%d' % (password, key_len, iv_len) + r = cached_keys.get(cached_key, None) + if r: + return r + m = [] + i = 0 + while len(b''.join(m)) < (key_len + iv_len): + md5 = hashlib.md5() + data = password + if i > 0: + data = m[i - 1] + password + md5.update(data) + m.append(md5.digest()) + i += 1 + ms = b''.join(m) + key = ms[:key_len] + iv = ms[key_len:key_len + iv_len] + cached_keys[cached_key] = (key, iv) + return key, iv + + +class Encryptor(object): + def __init__(self, key, method, iv = None): + self.key = key + self.method = method + self.iv = None + self.iv_sent = False + self.cipher_iv = b'' + self.iv_buf = b'' + self.cipher_key = b'' + self.decipher = None + method = method.lower() + self._method_info = self.get_method_info(method) + if self._method_info: + if iv is None or len(iv) != self._method_info[1]: + self.cipher = self.get_cipher(key, method, 1, + random_string(self._method_info[1])) + else: + self.cipher = self.get_cipher(key, method, 1, iv) + else: + logging.error('method %s not supported' % method) + sys.exit(1) + + def get_method_info(self, method): + method = method.lower() + m = method_supported.get(method) + return m + + def iv_len(self): + return len(self.cipher_iv) + + def get_cipher(self, password, method, op, iv): + password = common.to_bytes(password) + m = self._method_info + if m[0] > 0: + key, iv_ = EVP_BytesToKey(password, m[0], m[1]) + else: + # key_length == 0 indicates we should use the key directly + key, iv = password, b'' + + iv = iv[:m[1]] + if op == 1: + # this iv is for cipher not decipher + self.cipher_iv = iv[:m[1]] + self.cipher_key = key + return m[2](method, key, iv, op) + + def encrypt(self, buf): + if len(buf) == 0: + return buf + if self.iv_sent: + return self.cipher.update(buf) + else: + self.iv_sent = True + return self.cipher_iv + self.cipher.update(buf) + + def decrypt(self, buf): + if len(buf) == 0: + return buf + if self.decipher is not None: #optimize + return self.decipher.update(buf) + + decipher_iv_len = self._method_info[1] + if len(self.iv_buf) <= decipher_iv_len: + self.iv_buf += buf + if len(self.iv_buf) > decipher_iv_len: + decipher_iv = self.iv_buf[:decipher_iv_len] + self.decipher = self.get_cipher(self.key, self.method, 0, + iv=decipher_iv) + buf = self.iv_buf[decipher_iv_len:] + del self.iv_buf + return self.decipher.update(buf) + else: + return b'' + +def encrypt_all(password, method, op, data): + result = [] + method = method.lower() + (key_len, iv_len, m) = method_supported[method] + if key_len > 0: + key, _ = EVP_BytesToKey(password, key_len, iv_len) + else: + key = password + if op: + iv = random_string(iv_len) + result.append(iv) + else: + iv = data[:iv_len] + data = data[iv_len:] + cipher = m(method, key, iv, op) + result.append(cipher.update(data)) + return b''.join(result) + +def encrypt_key(password, method): + method = method.lower() + (key_len, iv_len, m) = method_supported[method] + if key_len > 0: + key, _ = EVP_BytesToKey(password, key_len, iv_len) + else: + key = password + return key + +def encrypt_iv_len(method): + method = method.lower() + (key_len, iv_len, m) = method_supported[method] + return iv_len + +def encrypt_new_iv(method): + method = method.lower() + (key_len, iv_len, m) = method_supported[method] + return random_string(iv_len) + +def encrypt_all_iv(key, method, op, data, ref_iv): + result = [] + method = method.lower() + (key_len, iv_len, m) = method_supported[method] + if op: + iv = ref_iv[0] + result.append(iv) + else: + iv = data[:iv_len] + data = data[iv_len:] + ref_iv[0] = iv + cipher = m(method, key, iv, op) + result.append(cipher.update(data)) + return b''.join(result) + + +CIPHERS_TO_TEST = [ + 'aes-128-cfb', + 'aes-256-cfb', + 'rc4-md5', + 'salsa20', + 'chacha20', + 'table', +] + + +def test_encryptor(): + from os import urandom + plain = urandom(10240) + for method in CIPHERS_TO_TEST: + logging.warn(method) + encryptor = Encryptor(b'key', method) + decryptor = Encryptor(b'key', method) + cipher = encryptor.encrypt(plain) + plain2 = decryptor.decrypt(cipher) + assert plain == plain2 + + +def test_encrypt_all(): + from os import urandom + plain = urandom(10240) + for method in CIPHERS_TO_TEST: + logging.warn(method) + cipher = encrypt_all(b'key', method, 1, plain) + plain2 = encrypt_all(b'key', method, 0, cipher) + assert plain == plain2 + + +if __name__ == '__main__': + test_encrypt_all() + test_encryptor() diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/encrypt_test.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/encrypt_test.py new file mode 100644 index 0000000..d5e5077 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/encrypt_test.py @@ -0,0 +1,51 @@ +from __future__ import absolute_import, division, print_function, \ + with_statement + +import sys +import os + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) + + +from shadowsocks.crypto import rc4_md5 +from shadowsocks.crypto import openssl +from shadowsocks.crypto import sodium +from shadowsocks.crypto import table + +def run(func): + try: + func() + except: + pass + +def run_n(func, name): + try: + func(name) + except: + pass + +def main(): + print("\n""rc4_md5") + rc4_md5.test() + print("\n""aes-256-cfb") + openssl.test_aes_256_cfb() + print("\n""aes-128-cfb") + openssl.test_aes_128_cfb() + print("\n""bf-cfb") + run(openssl.test_bf_cfb) + print("\n""camellia-128-cfb") + run_n(openssl.run_method, "camellia-128-cfb") + print("\n""cast5-cfb") + run_n(openssl.run_method, "cast5-cfb") + print("\n""idea-cfb") + run_n(openssl.run_method, "idea-cfb") + print("\n""seed-cfb") + run_n(openssl.run_method, "seed-cfb") + print("\n""salsa20") + run(sodium.test_salsa20) + print("\n""chacha20") + run(sodium.test_chacha20) + +if __name__ == '__main__': + main() + diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/eventloop.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/eventloop.py new file mode 100644 index 0000000..341620e --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/eventloop.py @@ -0,0 +1,258 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright 2013-2015 clowwindy +# +# 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 ssloop +# https://github.com/clowwindy/ssloop + +from __future__ import absolute_import, division, print_function, \ + with_statement + +import os +import time +import socket +import select +import errno +import logging +from collections import defaultdict + +from shadowsocks import shell + + +__all__ = ['EventLoop', 'POLL_NULL', 'POLL_IN', 'POLL_OUT', 'POLL_ERR', + 'POLL_HUP', 'POLL_NVAL', 'EVENT_NAMES'] + +POLL_NULL = 0x00 +POLL_IN = 0x01 +POLL_OUT = 0x04 +POLL_ERR = 0x08 +POLL_HUP = 0x10 +POLL_NVAL = 0x20 + + +EVENT_NAMES = { + POLL_NULL: 'POLL_NULL', + POLL_IN: 'POLL_IN', + POLL_OUT: 'POLL_OUT', + POLL_ERR: 'POLL_ERR', + POLL_HUP: 'POLL_HUP', + POLL_NVAL: 'POLL_NVAL', +} + +# we check timeouts every TIMEOUT_PRECISION seconds +TIMEOUT_PRECISION = 2 + + +class KqueueLoop(object): + + MAX_EVENTS = 1024 + + def __init__(self): + self._kqueue = select.kqueue() + self._fds = {} + + def _control(self, fd, mode, flags): + events = [] + if mode & POLL_IN: + events.append(select.kevent(fd, select.KQ_FILTER_READ, flags)) + if mode & POLL_OUT: + events.append(select.kevent(fd, select.KQ_FILTER_WRITE, flags)) + for e in events: + self._kqueue.control([e], 0) + + def poll(self, timeout): + if timeout < 0: + timeout = None # kqueue behaviour + events = self._kqueue.control(None, KqueueLoop.MAX_EVENTS, timeout) + results = defaultdict(lambda: POLL_NULL) + for e in events: + fd = e.ident + if e.filter == select.KQ_FILTER_READ: + results[fd] |= POLL_IN + elif e.filter == select.KQ_FILTER_WRITE: + results[fd] |= POLL_OUT + return results.items() + + def register(self, fd, mode): + self._fds[fd] = mode + self._control(fd, mode, select.KQ_EV_ADD) + + def unregister(self, fd): + self._control(fd, self._fds[fd], select.KQ_EV_DELETE) + del self._fds[fd] + + def modify(self, fd, mode): + self.unregister(fd) + self.register(fd, mode) + + def close(self): + self._kqueue.close() + + +class SelectLoop(object): + + def __init__(self): + self._r_list = set() + self._w_list = set() + self._x_list = set() + + def poll(self, timeout): + r, w, x = select.select(self._r_list, self._w_list, self._x_list, + timeout) + results = defaultdict(lambda: POLL_NULL) + for p in [(r, POLL_IN), (w, POLL_OUT), (x, POLL_ERR)]: + for fd in p[0]: + results[fd] |= p[1] + return results.items() + + def register(self, fd, mode): + if mode & POLL_IN: + self._r_list.add(fd) + if mode & POLL_OUT: + self._w_list.add(fd) + if mode & POLL_ERR: + self._x_list.add(fd) + + def unregister(self, fd): + if fd in self._r_list: + self._r_list.remove(fd) + if fd in self._w_list: + self._w_list.remove(fd) + if fd in self._x_list: + self._x_list.remove(fd) + + def modify(self, fd, mode): + self.unregister(fd) + self.register(fd, mode) + + def close(self): + pass + + +class EventLoop(object): + def __init__(self): + if hasattr(select, 'epoll'): + self._impl = select.epoll() + model = 'epoll' + elif hasattr(select, 'kqueue'): + self._impl = KqueueLoop() + model = 'kqueue' + elif hasattr(select, 'select'): + self._impl = SelectLoop() + model = 'select' + else: + raise Exception('can not find any available functions in select ' + 'package') + self._fdmap = {} # (f, handler) + self._last_time = time.time() + self._periodic_callbacks = [] + self._stopping = False + logging.debug('using event model: %s', model) + + def poll(self, timeout=None): + events = self._impl.poll(timeout) + return [(self._fdmap[fd][0], fd, event) for fd, event in events] + + def add(self, f, mode, handler): + fd = f.fileno() + self._fdmap[fd] = (f, handler) + self._impl.register(fd, mode) + + def remove(self, f): + fd = f.fileno() + 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) + + def remove_periodic(self, callback): + self._periodic_callbacks.remove(callback) + + def modify(self, f, mode): + fd = f.fileno() + self._impl.modify(fd, mode) + + def stop(self): + self._stopping = True + + def run(self): + events = [] + while not self._stopping: + asap = False + try: + events = self.poll(TIMEOUT_PRECISION) + except (OSError, IOError) as e: + if errno_from_exception(e) in (errno.EPIPE, errno.EINTR): + # EPIPE: Happens when the client closes the connection + # EINTR: Happens when received a signal + # handles them as soon as possible + asap = True + logging.debug('poll:%s', e) + else: + logging.error('poll:%s', e) + import traceback + 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: + handle = handler.handle_event(sock, fd, event) or handle + except (OSError, IOError) as e: + shell.print_exception(e) + now = time.time() + if asap or now - self._last_time >= TIMEOUT_PRECISION: + 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() + + +# from tornado +def errno_from_exception(e): + """Provides the errno from an Exception object. + + There are cases that the errno attribute was not set so we pull + the errno out of the args but if someone instatiates an Exception + without any args you will get a tuple error. So this function + abstracts all that behavior to give you a safe way to get the + errno. + """ + + if hasattr(e, 'errno'): + return e.errno + elif e.args: + return e.args[0] + else: + return None + + +# from tornado +def get_sock_error(sock): + error_number = sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + return socket.error(error_number, os.strerror(error_number)) diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/local.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/local.py new file mode 100755 index 0000000..9f54d93 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/local.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2012-2015 clowwindy +# +# 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 sys +import os +import logging +import signal + +if __name__ == '__main__': + import inspect + file_path = os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe()))) + sys.path.insert(0, os.path.join(file_path, '../')) + +from shadowsocks import shell, daemon, eventloop, tcprelay, udprelay, asyncdns + + +def main(): + shell.check_python() + + # fix py2exe + if hasattr(sys, "frozen") and sys.frozen in \ + ("windows_exe", "console_exe"): + p = os.path.dirname(os.path.abspath(sys.executable)) + os.chdir(p) + + config = shell.get_config(True) + + if not config.get('dns_ipv6', False): + asyncdns.IPV6_CONNECTION_SUPPORT = False + + daemon.daemon_exec(config) + logging.info("local start with protocol[%s] password [%s] method [%s] obfs [%s] obfs_param [%s]" % + (config['protocol'], config['password'], config['method'], config['obfs'], config['obfs_param'])) + + try: + logging.info("starting local at %s:%d" % + (config['local_address'], config['local_port'])) + + dns_resolver = asyncdns.DNSResolver() + tcp_server = tcprelay.TCPRelay(config, dns_resolver, True) + udp_server = udprelay.UDPRelay(config, dns_resolver, True) + loop = eventloop.EventLoop() + dns_resolver.add_to_loop(loop) + tcp_server.add_to_loop(loop) + udp_server.add_to_loop(loop) + + def handler(signum, _): + logging.warn('received SIGQUIT, doing graceful shutting down..') + tcp_server.close(next_tick=True) + udp_server.close(next_tick=True) + signal.signal(getattr(signal, 'SIGQUIT', signal.SIGTERM), handler) + + def int_handler(signum, _): + sys.exit(1) + signal.signal(signal.SIGINT, int_handler) + + daemon.set_user(config.get('user', None)) + loop.run() + except Exception as e: + shell.print_exception(e) + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/lru_cache.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/lru_cache.py new file mode 100644 index 0000000..ab0d210 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/lru_cache.py @@ -0,0 +1,179 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright 2015 clowwindy +# +# 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 collections +import logging +import time + +if __name__ == '__main__': + import os, sys, inspect + file_path = os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe()))) + sys.path.insert(0, os.path.join(file_path, '../')) + +try: + from collections import OrderedDict +except: + from shadowsocks.ordereddict import OrderedDict + +# this LRUCache is optimized for concurrency, not QPS +# n: concurrency, keys stored in the cache +# m: visits not timed out, proportional to QPS * timeout +# get & set is O(1), not O(n). thus we can support very large n +# sweep is O((n - m)) or O(1024) at most, +# no metter how large the cache or timeout value is + +SWEEP_MAX_ITEMS = 1024 + +class LRUCache(collections.MutableMapping): + """This class is not thread safe""" + + def __init__(self, timeout=60, close_callback=None, *args, **kwargs): + self.timeout = timeout + self.close_callback = close_callback + self._store = {} + self._keys_to_last_time = OrderedDict() + self.update(dict(*args, **kwargs)) # use the free update to set keys + + def __getitem__(self, key): + # O(1) + t = time.time() + last_t = self._keys_to_last_time[key] + del self._keys_to_last_time[key] + self._keys_to_last_time[key] = t + return self._store[key] + + def __setitem__(self, key, value): + # O(1) + t = time.time() + if key in self._keys_to_last_time: + del self._keys_to_last_time[key] + self._keys_to_last_time[key] = t + self._store[key] = value + + def __delitem__(self, key): + # O(1) + last_t = self._keys_to_last_time[key] + del self._store[key] + del self._keys_to_last_time[key] + + def __contains__(self, key): + return key in self._store + + def __iter__(self): + return iter(self._store) + + def __len__(self): + return len(self._store) + + def first(self): + if len(self._keys_to_last_time) > 0: + for key in self._keys_to_last_time: + return key + + def sweep(self, sweep_item_cnt = SWEEP_MAX_ITEMS): + # O(n - m) + now = time.time() + c = 0 + while c < sweep_item_cnt: + if len(self._keys_to_last_time) == 0: + break + for key in self._keys_to_last_time: + break + last_t = self._keys_to_last_time[key] + if now - last_t <= self.timeout: + break + value = self._store[key] + del self._store[key] + del self._keys_to_last_time[key] + if self.close_callback is not None: + self.close_callback(value) + c += 1 + if c: + logging.debug('%d keys swept' % c) + return c < SWEEP_MAX_ITEMS + + def clear(self, keep): + now = time.time() + c = 0 + while len(self._keys_to_last_time) > keep: + if len(self._keys_to_last_time) == 0: + break + for key in self._keys_to_last_time: + break + last_t = self._keys_to_last_time[key] + value = self._store[key] + if self.close_callback is not None: + self.close_callback(value) + del self._store[key] + del self._keys_to_last_time[key] + c += 1 + if c: + logging.debug('%d keys swept' % c) + return c < SWEEP_MAX_ITEMS + +def test(): + c = LRUCache(timeout=0.3) + + c['a'] = 1 + assert c['a'] == 1 + c['a'] = 1 + + time.sleep(0.5) + c.sweep() + assert 'a' not in c + + c['a'] = 2 + c['b'] = 3 + time.sleep(0.2) + c.sweep() + assert c['a'] == 2 + assert c['b'] == 3 + + time.sleep(0.2) + c.sweep() + c['b'] + time.sleep(0.2) + c.sweep() + assert 'a' not in c + assert c['b'] == 3 + + time.sleep(0.5) + c.sweep() + assert 'a' not in c + assert 'b' not in c + + global close_cb_called + close_cb_called = False + + def close_cb(t): + global close_cb_called + assert not close_cb_called + close_cb_called = True + + c = LRUCache(timeout=0.1, close_callback=close_cb) + c['s'] = 1 + c['s'] + time.sleep(0.1) + c['s'] + time.sleep(0.3) + c.sweep() + +if __name__ == '__main__': + test() diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/manager.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/manager.py new file mode 100644 index 0000000..80d0a32 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/manager.py @@ -0,0 +1,291 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright 2015 clowwindy +# +# 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 errno +import traceback +import socket +import logging +import json +import collections + +from shadowsocks import common, eventloop, tcprelay, udprelay, asyncdns, shell + + +BUF_SIZE = 1506 +STAT_SEND_LIMIT = 50 + + +class Manager(object): + + def __init__(self, config): + self._config = config + self._relays = {} # (tcprelay, udprelay) + self._loop = eventloop.EventLoop() + self._dns_resolver = asyncdns.DNSResolver() + self._dns_resolver.add_to_loop(self._loop) + + self._statistics = collections.defaultdict(int) + self._control_client_addr = None + try: + manager_address = common.to_str(config['manager_address']) + if ':' in manager_address: + addr = manager_address.rsplit(':', 1) + addr = addr[0], int(addr[1]) + addrs = socket.getaddrinfo(addr[0], addr[1]) + if addrs: + family = addrs[0][0] + else: + logging.error('invalid address: %s', manager_address) + exit(1) + else: + addr = manager_address + family = socket.AF_UNIX + self._control_socket = socket.socket(family, + socket.SOCK_DGRAM) + self._control_socket.bind(addr) + self._control_socket.setblocking(False) + except (OSError, IOError) as e: + logging.error(e) + logging.error('can not bind to manager address') + exit(1) + self._loop.add(self._control_socket, + eventloop.POLL_IN, self) + self._loop.add_periodic(self.handle_periodic) + + port_password = config['port_password'] + del config['port_password'] + for port, password in port_password.items(): + a_config = config.copy() + a_config['server_port'] = int(port) + a_config['password'] = password + self.add_port(a_config) + + def add_port(self, config): + port = int(config['server_port']) + servers = self._relays.get(port, None) + if servers: + logging.error("server already exists at %s:%d" % (config['server'], + port)) + return + logging.info("adding server at %s:%d" % (config['server'], port)) + t = tcprelay.TCPRelay(config, self._dns_resolver, False, + stat_callback=self.stat_callback) + u = udprelay.UDPRelay(config, self._dns_resolver, False, + stat_callback=self.stat_callback) + t.add_to_loop(self._loop) + u.add_to_loop(self._loop) + self._relays[port] = (t, u) + + def remove_port(self, config): + port = int(config['server_port']) + servers = self._relays.get(port, None) + if servers: + logging.info("removing server at %s:%d" % (config['server'], port)) + t, u = servers + t.close(next_tick=False) + u.close(next_tick=False) + del self._relays[port] + else: + logging.error("server not exist at %s:%d" % (config['server'], + port)) + + def handle_event(self, sock, fd, event): + if sock == self._control_socket and event == eventloop.POLL_IN: + data, self._control_client_addr = sock.recvfrom(BUF_SIZE) + parsed = self._parse_command(data) + if parsed: + command, config = parsed + a_config = self._config.copy() + if config: + # let the command override the configuration file + a_config.update(config) + if 'server_port' not in a_config: + logging.error('can not find server_port in config') + else: + if command == 'add': + self.add_port(a_config) + self._send_control_data(b'ok') + elif command == 'remove': + self.remove_port(a_config) + self._send_control_data(b'ok') + elif command == 'ping': + self._send_control_data(b'pong') + else: + logging.error('unknown command %s', command) + + def _parse_command(self, data): + # commands: + # add: {"server_port": 8000, "password": "foobar"} + # remove: {"server_port": 8000"} + data = common.to_str(data) + parts = data.split(':', 1) + if len(parts) < 2: + return data, None + command, config_json = parts + try: + config = shell.parse_json_in_str(config_json) + return command, config + except Exception as e: + logging.error(e) + return None + + def stat_callback(self, port, data_len): + self._statistics[port] += data_len + + def handle_periodic(self): + r = {} + i = 0 + + def send_data(data_dict): + if data_dict: + # use compact JSON format (without space) + data = common.to_bytes(json.dumps(data_dict, + separators=(',', ':'))) + self._send_control_data(b'stat: ' + data) + + for k, v in self._statistics.items(): + r[k] = v + i += 1 + # split the data into segments that fit in UDP packets + if i >= STAT_SEND_LIMIT: + send_data(r) + r.clear() + i = 0 + if len(r) > 0 : + send_data(r) + self._statistics.clear() + + def _send_control_data(self, data): + if self._control_client_addr: + try: + self._control_socket.sendto(data, self._control_client_addr) + except (socket.error, OSError, IOError) as e: + error_no = eventloop.errno_from_exception(e) + if error_no in (errno.EAGAIN, errno.EINPROGRESS, + errno.EWOULDBLOCK): + return + else: + shell.print_exception(e) + if self._config['verbose']: + traceback.print_exc() + + def run(self): + self._loop.run() + + +def run(config): + Manager(config).run() + + +def test(): + import time + import threading + import struct + from shadowsocks import encrypt + + logging.basicConfig(level=5, + format='%(asctime)s %(levelname)-8s %(message)s', + datefmt='%Y-%m-%d %H:%M:%S') + enc = [] + eventloop.TIMEOUT_PRECISION = 1 + + def run_server(): + config = shell.get_config(True) + config = config.copy() + a_config = { + 'server': '127.0.0.1', + 'local_port': 1081, + 'port_password': { + '8381': 'foobar1', + '8382': 'foobar2' + }, + 'method': 'aes-256-cfb', + 'manager_address': '127.0.0.1:6001', + 'timeout': 60, + 'fast_open': False, + 'verbose': 2 + } + config.update(a_config) + manager = Manager(config) + enc.append(manager) + manager.run() + + t = threading.Thread(target=run_server) + t.start() + time.sleep(1) + manager = enc[0] + cli = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + cli.connect(('127.0.0.1', 6001)) + + # test add and remove + time.sleep(1) + cli.send(b'add: {"server_port":7001, "password":"asdfadsfasdf"}') + time.sleep(1) + assert 7001 in manager._relays + data, addr = cli.recvfrom(1506) + assert b'ok' in data + + cli.send(b'remove: {"server_port":8381}') + time.sleep(1) + assert 8381 not in manager._relays + data, addr = cli.recvfrom(1506) + assert b'ok' in data + logging.info('add and remove test passed') + + # test statistics for TCP + header = common.pack_addr(b'google.com') + struct.pack('>H', 80) + data = encrypt.encrypt_all(b'asdfadsfasdf', 'aes-256-cfb', 1, + header + b'GET /\r\n\r\n') + tcp_cli = socket.socket() + tcp_cli.connect(('127.0.0.1', 7001)) + tcp_cli.send(data) + tcp_cli.recv(4096) + tcp_cli.close() + + data, addr = cli.recvfrom(1506) + data = common.to_str(data) + assert data.startswith('stat: ') + data = data.split('stat:')[1] + stats = shell.parse_json_in_str(data) + assert '7001' in stats + logging.info('TCP statistics test passed') + + # test statistics for UDP + header = common.pack_addr(b'127.0.0.1') + struct.pack('>H', 80) + data = encrypt.encrypt_all(b'foobar2', 'aes-256-cfb', 1, + header + b'test') + udp_cli = socket.socket(type=socket.SOCK_DGRAM) + udp_cli.sendto(data, ('127.0.0.1', 8382)) + tcp_cli.close() + + data, addr = cli.recvfrom(1506) + data = common.to_str(data) + assert data.startswith('stat: ') + data = data.split('stat:')[1] + stats = json.loads(data) + assert '8382' in stats + logging.info('UDP statistics test passed') + + manager._loop.stop() + t.join() + + +if __name__ == '__main__': + test() diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfs.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfs.py new file mode 100644 index 0000000..3dfdb14 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfs.py @@ -0,0 +1,114 @@ +#!/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 + +from shadowsocks import common +from shadowsocks.obfsplugin import plain, http_simple, obfs_tls, verify, auth, auth_chain + + +method_supported = {} +method_supported.update(plain.obfs_map) +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): + self.data = data + +class obfs(object): + def __init__(self, method): + method = common.to_str(method) + self.method = method + self._method_info = self.get_method_info(method) + if self._method_info: + self.obfs = self.get_obfs(method) + else: + raise Exception('obfs plugin [%s] not supported' % method) + + def init_data(self): + return self.obfs.init_data() + + 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) + return m + + def get_obfs(self, method): + 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) + + def client_encode(self, buf): + return self.obfs.client_encode(buf) + + def client_decode(self, buf): + return self.obfs.client_decode(buf) + + def client_post_decrypt(self, buf): + return self.obfs.client_post_decrypt(buf) + + def server_pre_encrypt(self, buf): + return self.obfs.server_pre_encrypt(buf) + + def server_encode(self, buf): + return self.obfs.server_encode(buf) + + def server_decode(self, buf): + return self.obfs.server_decode(buf) + + def server_post_decrypt(self, buf): + return self.obfs.server_post_decrypt(buf) + + def client_udp_pre_encrypt(self, buf): + return self.obfs.client_udp_pre_encrypt(buf) + + def client_udp_post_decrypt(self, buf): + return self.obfs.client_udp_post_decrypt(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) + + def dispose(self): + self.obfs.dispose() + del self.obfs + diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__init__.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__init__.py new file mode 100644 index 0000000..401c7b7 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__init__.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# +# Copyright 2015 clowwindy +# +# 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 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/__init__.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac92b0fbb9a3d3e74f9b4b4f36817458b2cb08f0 GIT binary patch literal 279 zcmY+7J5B>Z3_!h~h*hM3=()f)I~PDv+B7tD+gO{ONit$Ss~HawN21^q++tcPu0Vwc z2?;Fu$m_JMcua1lAHQfiW2A|cRgNC|RbC^HDFuZX%Ja|^ zv;`~1D}<|37kuqSia{7ZPe#P^B>g4&`H5S9L=3|}^@n3zOgd>N5Vg~UI4m!@ySH8@ LqqetvIC8=_6xmL+ literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/auth.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/auth.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50b3a40aa476de69d1ca780e4b4d42069c6d6e89 GIT binary patch literal 29290 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 zos+52�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 literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/auth_chain.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/auth_chain.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2832bb3422a549954d4270dfe137a0d1982e03d GIT binary patch literal 23272 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 literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/plain.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/plain.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce241a3763ff2d50dfe017f6423fb14b08d449cf GIT binary patch literal 3442 zcmbtW-EZSW5ck?n;(Rq}`hC~?;I14Ofm$IUfe<#5l!L3Io!QNd=Qkhg-8pVHYX&~QJE>d4*&no+ zKQ>N|aU>%oU;qn^h`B5>T{E&=E3#di(f?*xa!Znz-Lm8rw<5XYI>@cC8r9qy>e^6x zW4Lu*A21qF{>5|~FAS)F^Tq&&H?J&r4b1P2Zgqy3M%T_-{z(#tlazZwG>%1@IS`x& zNf3{+wXq0BsW+I6`l(#M4$`5Qq<+dHK1zFCmRYfYJ|k235I1prg(Gp0q{h^^Vu;2_ zw=@p@V1$!$#M5C6zZs&6N`E~+nk3>VjQf6glni}{Ung<@4FA#=M~8=nWqdM7#^K~N z7#&HghvSQ^-WMEQ^GJ4=Tp(WzChADLrl=sx%CQLWr9MgK52+pFNPb1a4VQu8n!v!s zR9awxjT{pTW#lE5N-RSaYPhaI9U90TXu=xuDzu=DyawygL0*Rq*hJodE!alhgdNyL zz6N`62YCzb!anjg+=Khb*Wm#?MBah-;1Ti-cnk-~H{pGFf_w`;z;r&9!SrQ(=reH| zGVg7iOxe`%aOWV!U{wB1K^4jB2^Z&Fc)@58cg@UBcsP&}SqnvI$7YU)Izj4r2e?XT zs~W7#F4t7L=fXnmss7Rj(gv!#+53}TlxDk3tj*^dPF13SpZZTxn%C^IrC=7aLaeKB z1pTRSOo@|KArM?{T!lt^O~}w1r#$sy%)*fS@S%pi24fABPoLad9rlIQcf=3kV8RpH9{a-glGi2d<(>?Mr?b|KGG^&J;dY@JL7 z%b1;h7~tXHjRp7ks4p(Y=_eZOt_u0O(*Hsh+h}qbxtSY9)EM}u8t6s=RLKi?i!9SO zGG)L9bVi!J=X$vR3JRP+KVN{Fmqkz|FMz%+y({rH=9!C=)I+0`-)9R{cDYvwb*yRK z9=nroQmwRKYS4LUuPfaixtSYPrTba~&9hvP7w{}pmWPu1faqKwSXf-E_wqkfH@B;* z_hNy&sBS@CzR&x(KLa>gFpRFmjR%fR82=H^nc?svAL0Qf#IR6$(@(r8b z*g;pxM%U@hD3|iOpHWLUqYBRmc(y@=ijIwq74ngwYr1P|b*=&*c;|jN;aQa~e=ofl z^X0-f>Bg4QB)H^1qK#w|iNPAoW^LvmRin-3hkW6UUU!?ir;#6V&&z6_7sW6MY2NU> zpC*2&B%*~j#X1pkTo8uXBCs|@{Q z7zQUL?q|*yDwWSDYe@&m Qu7y$1&uo~EZL^y7H~%lmMF0Q* literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/verify.cpython-37.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/__pycache__/verify.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d36a947cd6fd2cf07ee78161f4b44e5ca4bb86f9 GIT binary patch literal 6688 zcmeHMOOM;g5oVK=M7?I@@jPws_>q^D#|*OB>~0drFyQQL0tAxdG^33Vxdg~iV0E+E?CPre zs=B)MpxLZhxc=(p{`wWm`UgEsE*F&txV0xNOIlJSRw{gv+PejU*)pq*SM_vbuJrzgUhDh*@)gcb^L8SH{}wZm!j39RkYkh%d+&);zc*{d`sFtx7w}Oq@T8HmOBSJO9puq z#A!cMx$$KDG}dv}GfRCH_wt}S=!H2yKa2CjKayGa&yl3jh+k=Zkyg~w=`dDz|AnS0C*y1JhX9>=}iV)&i@ ziCGI(1P;MG!>`e_XnvMZEl||}3Aq6kDhX6P`ER09OdeF6isO>HI|7`Fr{CFIXg#*o zT?ye>@y`CEJqY9^QLk+q7y9`&)3;f&f-GZDQ%^g2co-Z+nDGu8SWlK{iB;hWwThMy z_w=G!J;1H$O=S5(TD~oXv;h)FI#8`^TBL)itew0wXAm+9%S6ISi;cna!E{|6CEfEg z^nxJn#d#2Xm-w=J#Bl38kDW=ceF8@i^g3x21f~`QX(k5=)f++ZWY9^9H);c;&9EYa zE7FKs19=BGv+@K#&*{LyOS<$=K_)u*)anY>gGYLhF7)Bbk}RVv$qG3J+`Vw7O5sXy zcXlDa`Y8YzqCiUR_oNjbz!PO5GEb1gWwDe-1&Rxq{%phg5m1Sgr+)PY|t`3$*t4Er0qg+ zD3~d$cdBDkWs6E+PM|m*Q_W=z!p2PLoIh^Y^H@MXx#5ZxVPh*Rh%lfq(Kv zT<3btaV5;p7@SPAQ*bKtZ@4kW?|28nU1HNm<&2y^3- zv~8ydh2)&ZrPy~qE>)>pKyN-qpRt$?tiv^NLku_1i8<{&^%s(pf-uWTnv&v${rvqx z+L3i6j$L(c1cQ>a61mWkAlY7t_eeUJ`v|Sn@vcRbGBskyReg9ytYM#)_kvU~#548so_}saAc?bM$15 z=jbuG>ieu-Mea!8VyMN|Z!GII+4a$Id!mj^=g3=GkGBXgx3Ju2ilf`}KfES~Y**Dc zP^h~^*jl@&new1Vz%kVio`+GN5^nV^YOhg@EMTj?j)t~7gVstm$k~z0@Zrb#;d$#` zT`;XK2GJBNEqJL5(~^;|jlI^xvu)oHt2VtE-uVKf&qz77`u_&V)psD+c{Qwz7m^u} z|BAHmMv!0pZYu5^$p4Pf801TnrJq4`26P)h|NR7-(g8UC%s@xrCf}9}%vZK7je%YU z(0zcj0-%qi$3UlmPqCl`H?UPMv3BHTS-SxH&lvWVm!7Qi=y06>2>Uw>`@ivwOR~YR zFCoGfDNGY748_z1*jJSJKWf|BLelfW<#nlnOATCV;8FvZ8o1QJ|D6W*hTD^D=`1^p zF6O}MT_g>Qb71uzAw_p_kIVosC(aY1uhDLszuF zGb^(%i@yRCoqwq+5jqJ}bs}_#Zah92PCI=*yf)?R;3x`nKE*8#L`fTGiYjV!K4ZLl i#ZmEn+AZlFkWI&fH5dM1(FT5-w&&IyuiSE1HvSECI1bAI literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/auth.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/auth.py new file mode 100755 index 0000000..a745e09 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/auth.py @@ -0,0 +1,787 @@ +#!/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 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_sha1_v4(method): + return auth_sha1_v4(method) + +def create_auth_aes128_md5(method): + return auth_aes128_sha1(method, hashlib.md5) + +def create_auth_aes128_sha1(method): + return auth_aes128_sha1(method, hashlib.sha1) + +obfs_map = { + 'auth_sha1_v4': (create_auth_sha1_v4,), + 'auth_sha1_v4_compatible': (create_auth_sha1_v4,), + 'auth_aes128_md5': (create_auth_aes128_md5,), + 'auth_aes128_sha1': (create_auth_aes128_sha1,), +} + +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 = 7 + + 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() + + def update(self): + self.last_update = time.time() + + def is_active(self): + return time.time() - self.last_update < 60 * 3 + + 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 + return True + +class obfs_auth_v2_data(object): + def __init__(self): + self.client_id = lru_cache.LRUCache() + self.local_client_id = b'' + self.connection_id = 0 + self.set_max_client(64) # max active client count + + def update(self, client_id, connection_id): + if client_id in self.client_id: + self.client_id[client_id].update() + + def set_max_client(self, max_client): + self.max_client = max_client + self.max_buffer = max(self.max_client * 2, 1024) + + def insert(self, client_id, connection_id): + if self.client_id.get(client_id, None) is None or not self.client_id[client_id].enable: + if self.client_id.first() is None or len(self.client_id) < self.max_client: + if client_id not in self.client_id: + #TODO: check + self.client_id[client_id] = client_queue(connection_id) + else: + self.client_id[client_id].re_enable(connection_id) + return self.client_id[client_id].insert(connection_id) + + if not self.client_id[self.client_id.first()].is_active(): + del self.client_id[self.client_id.first()] + if client_id not in self.client_id: + #TODO: check + self.client_id[client_id] = client_queue(connection_id) + else: + self.client_id[client_id].re_enable(connection_id) + return self.client_id[client_id].insert(connection_id) + + logging.warn('auth_sha1_v2: no inactive client') + return False + else: + return self.client_id[client_id].insert(connection_id) + +class auth_sha1_v4(auth_base): + def __init__(self, method): + super(auth_sha1_v4, self).__init__(method) + self.recv_buf = b'' + self.unit_len = 8100 + self.decrypt_packet_num = 0 + self.raw_trans = False + self.has_sent_header = False + self.has_recv_header = False + self.client_id = 0 + self.connection_id = 0 + self.max_time_dif = 60 * 60 * 24 # time dif (second) setting + self.salt = b"auth_sha1_v4" + self.no_compatible_method = 'auth_sha1_v4' + + def init_data(self): + return obfs_auth_v2_data() + + def set_server_info(self, server_info): + self.server_info = server_info + try: + max_client = int(server_info.protocol_param) + except: + max_client = 64 + self.server_info.data.set_max_client(max_client) + + def rnd_data(self, buf_size): + if buf_size > 1200: + return b'\x01' + + if buf_size > 400: + rnd_data = os.urandom(common.ord(os.urandom(1)[0]) % 256) + else: + rnd_data = os.urandom(struct.unpack('>H', os.urandom(2))[0] % 512) + + if len(rnd_data) < 128: + return common.chr(len(rnd_data) + 1) + rnd_data + else: + return common.chr(255) + struct.pack('>H', len(rnd_data) + 3) + rnd_data + + def pack_data(self, buf): + data = self.rnd_data(len(buf)) + buf + data_len = len(data) + 8 + crc = binascii.crc32(struct.pack('>H', data_len)) & 0xFFFF + data = struct.pack('H', data_len) + data + adler32 = zlib.adler32(data) & 0xFFFFFFFF + data += struct.pack('H', data_len) + self.salt + self.server_info.key) & 0xFFFFFFFF + data = struct.pack('H', data_len) + data + data += hmac.new(self.server_info.iv + self.server_info.key, data, hashlib.sha1).digest()[:10] + return data + + def auth_data(self): + utc_time = int(time.time()) & 0xFFFFFFFF + if self.server_info.data.connection_id > 0xFF000000: + self.server_info.data.local_client_id = b'' + if not self.server_info.data.local_client_id: + self.server_info.data.local_client_id = os.urandom(4) + logging.debug("local_client_id %s" % (binascii.hexlify(self.server_info.data.local_client_id),)) + self.server_info.data.connection_id = struct.unpack(' self.unit_len: + ret += self.pack_data(buf[:self.unit_len]) + buf = buf[self.unit_len:] + ret += self.pack_data(buf) + return ret + + def client_post_decrypt(self, buf): + if self.raw_trans: + return buf + self.recv_buf += buf + out_buf = b'' + while len(self.recv_buf) > 4: + crc = struct.pack('H', self.recv_buf[:2])[0] + if length >= 8192 or length < 7: + self.raw_trans = True + self.recv_buf = b'' + raise Exception('client_post_decrypt data error') + if length > len(self.recv_buf): + break + + if struct.pack('H', self.recv_buf[5:7])[0] + 4 + out_buf += self.recv_buf[pos:length - 4] + self.recv_buf = self.recv_buf[length:] + + if out_buf: + self.decrypt_packet_num += 1 + return out_buf + + def server_pre_encrypt(self, buf): + if self.raw_trans: + return buf + ret = b'' + while len(buf) > self.unit_len: + ret += self.pack_data(buf[:self.unit_len]) + buf = buf[self.unit_len:] + ret += self.pack_data(buf) + return ret + + def server_post_decrypt(self, buf): + if self.raw_trans: + return (buf, False) + self.recv_buf += buf + out_buf = b'' + sendback = False + + if not self.has_recv_header: + if len(self.recv_buf) <= 6: + return (b'', False) + crc = struct.pack('H', self.recv_buf[:2])[0] + if length > len(self.recv_buf): + return (b'', False) + sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, self.recv_buf[:length - 10], hashlib.sha1).digest()[:10] + if sha1data != self.recv_buf[length - 10:length]: + logging.error('auth_sha1_v4 data uncorrect auth HMAC-SHA1') + return self.not_match_return(self.recv_buf) + pos = common.ord(self.recv_buf[6]) + if pos < 255: + pos += 6 + else: + pos = struct.unpack('>H', self.recv_buf[7:9])[0] + 6 + out_buf = self.recv_buf[pos:length - 10] + if len(out_buf) < 12: + logging.info('auth_sha1_v4: too short, data %s' % (binascii.hexlify(self.recv_buf),)) + return self.not_match_return(self.recv_buf) + utc_time = struct.unpack(' self.max_time_dif: + logging.info('auth_sha1_v4: wrong timestamp, time_dif %d, data %s' % (time_dif, binascii.hexlify(out_buf),)) + return self.not_match_return(self.recv_buf) + elif self.server_info.data.insert(client_id, connection_id): + self.has_recv_header = True + out_buf = out_buf[12:] + self.client_id = client_id + self.connection_id = connection_id + else: + logging.info('auth_sha1_v4: auth fail, data %s' % (binascii.hexlify(out_buf),)) + return self.not_match_return(self.recv_buf) + self.recv_buf = self.recv_buf[length:] + self.has_recv_header = True + sendback = True + + while len(self.recv_buf) > 4: + crc = struct.pack('H', self.recv_buf[:2])[0] + if length >= 8192 or length < 7: + self.raw_trans = True + self.recv_buf = b'' + if self.decrypt_packet_num == 0: + logging.info('auth_sha1_v4: over size') + return (b'E'*2048, False) + else: + raise Exception('server_post_decrype data error') + if length > len(self.recv_buf): + break + + if struct.pack('H', self.recv_buf[5:7])[0] + 4 + out_buf += self.recv_buf[pos:length - 4] + self.recv_buf = self.recv_buf[length:] + if pos == length - 4: + sendback = True + + if out_buf: + self.server_info.data.update(self.client_id, self.connection_id) + self.decrypt_packet_num += 1 + return (out_buf, sendback) + +class obfs_auth_mu_data(object): + def __init__(self): + 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('auth_aes128: no inactive client') + return False + else: + return local_client_id[client_id].insert(connection_id) + +class auth_aes128_sha1(auth_base): + def __init__(self, method, hashfunc): + super(auth_aes128_sha1, self).__init__(method) + self.hashfunc = hashfunc + self.recv_buf = b'' + self.unit_len = 8100 + self.raw_trans = False + self.has_sent_header = False + self.has_recv_header = False + self.client_id = 0 + self.connection_id = 0 + self.max_time_dif = 60 * 60 * 24 # time dif (second) setting + self.salt = hashfunc == hashlib.md5 and b"auth_aes128_md5" or b"auth_aes128_sha1" + self.no_compatible_method = hashfunc == hashlib.md5 and "auth_aes128_md5" or 'auth_aes128_sha1' + self.extra_wait_size = struct.unpack('>H', os.urandom(2))[0] % 1024 + self.pack_id = 1 + self.recv_id = 1 + self.user_id = None + self.user_key = None + self.last_rnd_len = 0 + self.overhead = 9 + + def init_data(self): + return obfs_auth_mu_data() + + 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, full_buf_size): + if full_buf_size >= self.server_info.buffer_size: + return 0 + tcp_mss = self.server_info.tcp_mss + rev_len = tcp_mss - buf_size - 9 + if rev_len == 0: + return 0 + if rev_len < 0: + if rev_len > -tcp_mss: + return self.trapezoid_random_int(rev_len + tcp_mss, -0.3) + return common.ord(os.urandom(1)[0]) % 32 + if buf_size > 900: + return struct.unpack('>H', os.urandom(2))[0] % rev_len + return self.trapezoid_random_int(rev_len, -0.3) + + def rnd_data(self, buf_size, full_buf_size): + data_len = self.rnd_data_len(buf_size, full_buf_size) + + if data_len < 128: + return common.chr(data_len + 1) + os.urandom(data_len) + + return common.chr(255) + struct.pack(' 400: + rnd_len = struct.unpack(' 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(' self.unit_len: + ret += self.pack_data(buf[:self.unit_len], ogn_data_len) + buf = buf[self.unit_len:] + ret += self.pack_data(buf, ogn_data_len) + self.last_rnd_len = ogn_data_len + 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('= 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 hmac.new(mac_key, self.recv_buf[:length - 4], self.hashfunc).digest()[:4] != self.recv_buf[length - 4:length]: + self.raw_trans = True + self.recv_buf = b'' + raise Exception('client_post_decrypt data uncorrect checksum') + + self.recv_id = (self.recv_id + 1) & 0xFFFFFFFF + pos = common.ord(self.recv_buf[4]) + if pos < 255: + pos += 4 + else: + pos = struct.unpack(' self.unit_len: + ret += self.pack_data(buf[:self.unit_len], ogn_data_len) + buf = buf[self.unit_len:] + ret += self.pack_data(buf, ogn_data_len) + self.last_rnd_len = ogn_data_len + 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) >= 7 or len(self.recv_buf) in [2, 3]: + recv_len = min(len(self.recv_buf), 7) + mac_key = self.server_info.recv_iv + self.server_info.key + sha1data = hmac.new(mac_key, self.recv_buf[:1], self.hashfunc).digest()[:recv_len - 1] + if sha1data != self.recv_buf[1:recv_len]: + return self.not_match_return(self.recv_buf) + + if len(self.recv_buf) < 31: + return (b'', False) + sha1data = hmac.new(mac_key, self.recv_buf[7:27], self.hashfunc).digest()[:4] + if sha1data != self.recv_buf[27:31]: + logging.error('%s data uncorrect auth HMAC-SHA1 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) < 31 + self.extra_wait_size: + return (b'', False) + return self.not_match_return(self.recv_buf) + + uid = self.recv_buf[7:11] + if uid in self.server_info.users: + self.user_id = uid + self.user_key = self.hashfunc(self.server_info.users[uid]).digest() + self.server_info.update_user_func(uid) + else: + if not self.server_info.users: + self.user_key = self.server_info.key + else: + self.user_key = self.server_info.recv_iv + encryptor = encrypt.Encryptor(to_bytes(base64.b64encode(self.user_key)) + self.salt, 'aes-128-cbc') + head = encryptor.decrypt(b'\x00' * 16 + self.recv_buf[11:27] + b'\x00') # need an extra byte or recv empty + length = struct.unpack(' 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 + out_buf = self.recv_buf[31 + rnd_len:length - 4] + 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.recv_buf = self.recv_buf[length:] + self.has_recv_header = True + sendback = True + + while len(self.recv_buf) > 4: + mac_key = self.user_key + struct.pack('= 8192 or length < 7: + 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 > len(self.recv_buf): + break + + if hmac.new(mac_key, self.recv_buf[:length - 4], self.hashfunc).digest()[:4] != self.recv_buf[length - 4:length]: + 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 = common.ord(self.recv_buf[4]) + if pos < 255: + pos += 4 + else: + pos = struct.unpack('> 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('= 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(' 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(' 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('= 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(' 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(' 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('= 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('= len(str2): + if str1[:len(str2)] == str2: + return True + return False + +class http_simple(plain.plain): + def __init__(self, method): + self.method = method + self.has_sent_header = False + self.has_recv_header = False + self.host = None + self.port = 0 + self.recv_buffer = b'' + self.user_agent = [b"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0", + b"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/44.0", + b"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36", + b"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/27.0.1453.93 Chrome/27.0.1453.93 Safari/537.36", + b"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0", + b"Mozilla/5.0 (compatible; WOW64; MSIE 10.0; Windows NT 6.2)", + b"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", + b"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)", + b"Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko", + b"Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/BuildID) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36", + b"Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3", + b"Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3"] + + def encode_head(self, buf): + hexstr = binascii.hexlify(buf) + chs = [] + for i in range(0, len(hexstr), 2): + chs.append(b"%" + hexstr[i:i+2]) + return b''.join(chs) + + def client_encode(self, buf): + if self.has_sent_header: + return buf + head_size = len(self.server_info.iv) + self.server_info.head_len + if len(buf) - head_size > 64: + headlen = head_size + random.randint(0, 64) + else: + headlen = len(buf) + headdata = buf[:headlen] + buf = buf[headlen:] + port = b'' + if self.server_info.port != 80: + port = b':' + to_bytes(str(self.server_info.port)) + body = None + hosts = (self.server_info.obfs_param or self.server_info.host) + pos = hosts.find("#") + if pos >= 0: + body = hosts[pos + 1:].replace("\n", "\r\n") + body = body.replace("\\n", "\r\n") + hosts = hosts[:pos] + hosts = hosts.split(',') + host = random.choice(hosts) + http_head = b"GET /" + self.encode_head(headdata) + b" HTTP/1.1\r\n" + http_head += b"Host: " + to_bytes(host) + port + b"\r\n" + if body: + http_head += body + "\r\n\r\n" + else: + 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\nDNT: 1\r\nConnection: keep-alive\r\n\r\n" + self.has_sent_header = True + return http_head + buf + + def client_decode(self, buf): + if self.has_recv_header: + return (buf, False) + pos = buf.find(b'\r\n\r\n') + if pos >= 0: + self.has_recv_header = True + return (buf[pos + 4:], False) + else: + return (b'', False) + + def server_encode(self, buf): + if self.has_sent_header: + return buf + + header = b'HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Encoding: gzip\r\nContent-Type: text/html\r\nDate: ' + header += to_bytes(datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S GMT')) + header += b'\r\nServer: nginx\r\nVary: Accept-Encoding\r\n\r\n' + self.has_sent_header = True + return header + buf + + def get_data_from_http_header(self, buf): + ret_buf = b'' + lines = buf.split(b'\r\n') + if lines and len(lines) > 1: + hex_items = lines[0].split(b'%') + if hex_items and len(hex_items) > 1: + for index in range(1, len(hex_items)): + if len(hex_items[index]) < 2: + ret_buf += binascii.unhexlify('0' + hex_items[index]) + break + elif len(hex_items[index]) > 2: + ret_buf += binascii.unhexlify(hex_items[index][:2]) + break + else: + ret_buf += binascii.unhexlify(hex_items[index]) + return ret_buf + return b'' + + def get_host_from_http_header(self, buf): + ret_buf = b'' + lines = buf.split(b'\r\n') + if lines and len(lines) > 1: + for line in lines: + if match_begin(line, b"Host: "): + return common.to_str(line[6:]) + + def not_match_return(self, buf): + self.has_sent_header = True + self.has_recv_header = True + if self.method == 'http_simple': + 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) + + self.recv_buffer += buf + buf = self.recv_buffer + if len(buf) > 10: + if match_begin(buf, b'GET ') or match_begin(buf, b'POST '): + if len(buf) > 65536: + self.recv_buffer = None + logging.warn('http_simple: over size') + return self.not_match_return(buf) + else: #not http header, run on original protocol + self.recv_buffer = None + logging.debug('http_simple: not match begin') + return self.not_match_return(buf) + else: + return (b'', True, False) + + if b'\r\n\r\n' in buf: + datas = buf.split(b'\r\n\r\n', 1) + ret_buf = self.get_data_from_http_header(buf) + host = self.get_host_from_http_header(buf) + if host and self.server_info.obfs_param: + pos = host.find(":") + if pos >= 0: + host = host[:pos] + hosts = self.server_info.obfs_param.split(',') + if host not in hosts: + return self.not_match_return(buf) + if len(ret_buf) < 4: + return self.error_return(buf) + if len(datas) > 1: + ret_buf += datas[1] + if len(ret_buf) >= 13: + self.has_recv_header = True + return (ret_buf, True, False) + return self.not_match_return(buf) + else: + return (b'', True, False) + +class http_post(http_simple): + def __init__(self, method): + super(http_post, self).__init__(method) + + def boundary(self): + return to_bytes(''.join([random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") for i in range(32)])) + + def client_encode(self, buf): + if self.has_sent_header: + return buf + head_size = len(self.server_info.iv) + self.server_info.head_len + if len(buf) - head_size > 64: + headlen = head_size + random.randint(0, 64) + else: + headlen = len(buf) + headdata = buf[:headlen] + buf = buf[headlen:] + port = b'' + if self.server_info.port != 80: + port = b':' + to_bytes(str(self.server_info.port)) + body = None + hosts = (self.server_info.obfs_param or self.server_info.host) + pos = hosts.find("#") + if pos >= 0: + body = hosts[pos + 1:].replace("\\n", "\r\n") + hosts = hosts[:pos] + hosts = hosts.split(',') + host = random.choice(hosts) + http_head = b"POST /" + self.encode_head(headdata) + b" HTTP/1.1\r\n" + http_head += b"Host: " + to_bytes(host) + port + b"\r\n" + if body: + http_head += body + "\r\n\r\n" + else: + 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 += b"Connection: keep-alive\r\n\r\n" + self.has_sent_header = True + return http_head + buf + + def not_match_return(self, buf): + self.has_sent_header = True + self.has_recv_header = True + if self.method == 'http_post': + return (b'E'*2048, False, False) + return (buf, True, False) + +class random_head(plain.plain): + def __init__(self, method): + self.method = method + self.has_sent_header = False + self.has_recv_header = False + self.raw_trans_sent = False + self.raw_trans_recv = False + self.send_buffer = b'' + + def client_encode(self, buf): + if self.raw_trans_sent: + return buf + self.send_buffer += buf + if not self.has_sent_header: + self.has_sent_header = True + data = os.urandom(common.ord(os.urandom(1)[0]) % 96 + 4) + crc = (0xffffffff - binascii.crc32(data)) & 0xffffffff + return data + struct.pack('= len(str2): + if str1[:len(str2)] == str2: + return True + return False + +class obfs_auth_data(object): + def __init__(self): + 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): + self.method = method + self.handshake_status = 0 + self.send_buffer = b'' + self.recv_buffer = b'' + 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 + data = b"\x00\x00" + struct.pack('>H', len(data) + 2) + struct.pack('>H', len(data)) + data + return data + + def pack_auth_data(self, client_id): + utc_time = int(time.time()) & 0xFFFFFFFF + data = struct.pack('>I', utc_time) + os.urandom(18) + data += hmac.new(self.server_info.key + client_id, data, hashlib.sha1).digest()[:10] + return data + + def client_encode(self, buf): + if self.handshake_status == -1: + return buf + if self.handshake_status == 8: + ret = b'' + while len(buf) > 2048: + size = min(struct.unpack('>H', os.urandom(2))[0] % 4096 + 100, len(buf)) + ret += b"\x17" + self.tls_version + struct.pack('>H', size) + buf[:size] + buf = buf[size:] + if len(buf) > 0: + ret += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf + return ret + 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") + ext = binascii.unhexlify(b"ff01000100") + host = self.server_info.obfs_param or self.server_info.host + if host and host[-1] in string.digits: + host = '' + hosts = host.split(',') + host = random.choice(hosts) + ext += self.sni(host) + ext += b"\x00\x17\x00\x00" + 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") + ext += binascii.unhexlify(b"75500000") + ext += binascii.unhexlify(b"000b00020100") + ext += binascii.unhexlify(b"000a0006000400170018") + data += struct.pack('>H', len(ext)) + ext + data = b"\x01\x00" + struct.pack('>H', len(data)) + data + data = b"\x16\x03\x01" + struct.pack('>H', len(data)) + data + return data + elif self.handshake_status == 1 and len(buf) == 0: + data = b"\x14" + self.tls_version + b"\x00\x01\x01" #ChangeCipherSpec + data += b"\x16" + self.tls_version + b"\x00\x20" + os.urandom(22) #Finished + data += hmac.new(self.server_info.key + self.server_info.data.client_id, data, hashlib.sha1).digest()[:10] + ret = data + self.send_buffer + self.send_buffer = b'' + self.handshake_status = 8 + return ret + return b'' + + def client_decode(self, buf): + if self.handshake_status == -1: + return (buf, False) + + if self.handshake_status == 8: + ret = b'' + self.recv_buffer += buf + while len(self.recv_buffer) > 5: + if ord(self.recv_buffer[0]) != 0x17: + logging.info("data = %s" % (binascii.hexlify(self.recv_buffer))) + raise Exception('server_decode appdata error') + size = struct.unpack('>H', self.recv_buffer[3:5])[0] + if len(self.recv_buffer) < size + 5: + break + buf = self.recv_buffer[5:size+5] + ret += buf + self.recv_buffer = self.recv_buffer[size+5:] + return (ret, False) + + if len(buf) < 11 + 32 + 1 + 32: + raise Exception('client_decode data error') + 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) == 8: + ret = b'' + while len(buf) > 2048: + size = min(struct.unpack('>H', os.urandom(2))[0] % 4096 + 100, len(buf)) + ret += b"\x17" + self.tls_version + struct.pack('>H', size) + buf[:size] + buf = buf[size:] + if len(buf) > 0: + ret += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf + return ret + 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" + 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 + 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 + self.overhead = 0 + if self.method == 'tls1.2_ticket_auth': + return (b'E'*2048, False, False) + return (buf, True, False) + + def server_decode(self, buf): + if self.handshake_status == -1: + return (buf, True, False) + + if (self.handshake_status & 4) == 4: + ret = b'' + self.recv_buffer += buf + while len(self.recv_buffer) > 5: + if ord(self.recv_buffer[0]) != 0x17 or ord(self.recv_buffer[1]) != 0x3 or ord(self.recv_buffer[2]) != 0x3: + logging.info("data = %s" % (binascii.hexlify(self.recv_buffer))) + raise Exception('server_decode appdata error') + size = struct.unpack('>H', self.recv_buffer[3:5])[0] + if len(self.recv_buffer) < size + 5: + break + ret += self.recv_buffer[5:size+5] + self.recv_buffer = self.recv_buffer[size+5:] + return (ret, True, False) + + if (self.handshake_status & 1) == 1: + self.recv_buffer += buf + buf = self.recv_buffer + verify = buf + 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"): #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') + 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 + buf = self.recv_buffer + ogn_buf = buf + if len(buf) < 3: + return (b'', False, False) + if not match_begin(buf, b'\x16\x03\x01'): + return self.decode_error_return(ogn_buf) + buf = buf[3:] + header_len = struct.unpack('>H', buf[:2])[0] + if header_len > len(buf) - 2: + return (b'', False, False) + + 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) + buf = buf[2:] + if struct.unpack('>H', buf[:2])[0] != len(buf) - 2: + logging.info("tls_auth wrong message size") + return self.decode_error_return(ogn_buf) + buf = buf[2:] + if not match_begin(buf, self.tls_version): + logging.info("tls_auth wrong tls version") + return self.decode_error_return(ogn_buf) + buf = buf[2:] + verifyid = buf[:32] + buf = buf[32:] + sessionid_len = ord(buf[0]) + if sessionid_len < 32: + logging.info("tls_auth wrong sessionid_len") + return self.decode_error_return(ogn_buf) + sessionid = buf[1:sessionid_len + 1] + buf = buf[sessionid_len+1:] + self.client_id = sessionid + sha1 = hmac.new(self.server_info.key + sessionid, verifyid[:22], hashlib.sha1).digest()[:10] + utc_time = struct.unpack('>I', verifyid[:4])[0] + time_dif = common.int32((int(time.time()) & 0xffffffff) - utc_time) + if self.server_info.obfs_param: + try: + self.max_time_dif = int(self.server_info.obfs_param) + except: + pass + if self.max_time_dif > 0 and (time_dif < -self.max_time_dif or time_dif > self.max_time_dif \ + or common.int32(utc_time - self.server_info.data.startup_time) < -self.max_time_dif / 2): + logging.info("tls_auth wrong time") + return self.decode_error_return(ogn_buf) + if sha1 != verifyid[22:]: + logging.info("tls_auth wrong sha1") + return self.decode_error_return(ogn_buf) + if self.server_info.data.client_data.get(verifyid[:22]): + logging.info("replay attack detect, id = %s" % (binascii.hexlify(verifyid))) + 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) + diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/plain.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/plain.py new file mode 100644 index 0000000..8c6355c --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/plain.py @@ -0,0 +1,104 @@ +#!/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 + +from shadowsocks.common import ord + +def create_obfs(method): + return plain(method) + +obfs_map = { + 'plain': (create_obfs,), + 'origin': (create_obfs,), +} + +class plain(object): + def __init__(self, method): + self.method = method + self.server_info = None + + 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 + + def client_pre_encrypt(self, buf): + return buf + + def client_encode(self, buf): + return buf + + def client_decode(self, buf): + # (buffer_to_recv, is_need_to_encode_and_send_back) + return (buf, False) + + def client_post_decrypt(self, buf): + return buf + + def server_pre_encrypt(self, buf): + return buf + + def server_encode(self, buf): + return buf + + def server_decode(self, buf): + # (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back) + return (buf, True, False) + + def server_post_decrypt(self, buf): + return (buf, False) + + def client_udp_pre_encrypt(self, buf): + return buf + + def client_udp_post_decrypt(self, buf): + return buf + + def server_udp_pre_encrypt(self, buf, uid): + return buf + + def server_udp_post_decrypt(self, buf): + return (buf, None) + + def dispose(self): + pass + + def get_head_size(self, buf, def_value): + if len(buf) < 2: + return def_value + head_type = ord(buf[0]) & 0x7 + if head_type == 1: + return 7 + if head_type == 4: + return 19 + if head_type == 3: + return 4 + ord(buf[1]) + return def_value + diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/plain.pyc b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/plain.pyc new file mode 100644 index 0000000000000000000000000000000000000000..63c08f9e4709be5bde1849d4340c5de4f8c8a24a GIT binary patch literal 4677 zcmd5=?QR=I6us-Uo!E)<)zDHtMM144q(n%lNENDDKp=q>iA^QcmC$PA8OPJ?dd9&;uD+!M#5dJqAFh zY_oBjY~mxEC3B}Elg_f-IozVmsj()Tx(#m3ax-<&cs?Dus_VINlgPT*=~PeM!O+8G z2t<~v5gNE!e0`3<^x1Jl=M2ST3>YiF(qVU&#AaH|(B)>T-6T^(_DGC<%-(*UWp3Y2 zVwF9&+33XP&Bp%T-V4p_Xl!T6{MbzQW%9k*nOoyoM!9Ch5r*I7rUgLq0)TLMbe0?b zPE9Oueg^!%v7wGTYJGtpc_WN4A%_C%fR$ECM?@8X`GuqF@uY%H#V*F|rI_k){|mTc?eYC2 zU?&H_@^BklPhnpsX$lQ0mA`S@DihmZ3qw> z65Hdh3S}iQl8HvwS#BH)kEg~yV2wFnq+g%fu*_{V}f znvWOG{0`4MN(}GN(Ja@Io{sXfnfsV~{~yo*8_L2_g3J;nBg~Zkmo)8;vd#9Usj>ZfeCq*E50Gm z$JY!c!wejr&5S|GlhU z304TUQbI$p-_(e&hhRZlxl_&6thuKiTr;G%E+K6M_1gwi$QBoN(K?g5d`{tp;wS}8 zlI73M;uCZPx75I;;6A~~rIYotaMeG~g8*68vKJZR+yvc-^LGu@h;Mb(ac0QX`NhM_ zOs|mCDPw1u)gil8EI}+%XIz4c%T}q=Jk)8NP@fRxH|dDW87F_Gx#)wake)8#@ zi=Q@~v?zaou0ymt(*7D1kF-~$J;6XZMO&u0b%;1B^-e+EAsW(0Ux5#>reuLXA{6a%VHWl!OJ@syxA(ANBVP$nLTsZ^vtUyWTq6 z;@a_suW`Q=)loE!QyoPPIZPC#nVKiu43MA)D71qum~9xGjRB?%@bv&X3tor04f6)f zTQG0KyaV$t%zH3*VBUwh$L8Yo#am1{Ej2S9Vehi(dcAhfZ~RuR>QlysNSe!cAJ5&K zGtwyFE(*YS02Krv+ri_71mB)n2T@F7J4wuu_(^umRr;}$^x@(^0(+w@O|wEB0lp(K vN39>F@vNxtkDhUjCD@k|6?x*P1+Nc5qln^;zs_Rd-_};E=lfpY8~A?%Z#n}K literal 0 HcmV?d00001 diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/verify.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/verify.py new file mode 100644 index 0000000..0dc0ca6 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/obfsplugin/verify.py @@ -0,0 +1,154 @@ +#!/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 struct +import zlib +import hmac +import hashlib + +import shadowsocks +from shadowsocks import common +from shadowsocks.obfsplugin import plain +from shadowsocks.common import to_bytes, to_str, ord, chr + +def create_verify_deflate(method): + return verify_deflate(method) + +obfs_map = { + 'verify_deflate': (create_verify_deflate,), +} + +def match_begin(str1, str2): + if len(str1) >= len(str2): + if str1[:len(str2)] == str2: + return True + return False + +class obfs_verify_data(object): + def __init__(self): + pass + +class verify_base(plain.plain): + def __init__(self, method): + super(verify_base, self).__init__(method) + self.method = method + + def init_data(self): + return obfs_verify_data() + + 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) + +class verify_deflate(verify_base): + def __init__(self, method): + super(verify_deflate, self).__init__(method) + self.recv_buf = b'' + self.unit_len = 32700 + self.decrypt_packet_num = 0 + self.raw_trans = False + + def pack_data(self, buf): + if len(buf) == 0: + return b'' + data = zlib.compress(buf) + data = struct.pack('>H', len(data)) + data[2:] + 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 >= 32768 or length < 6: + self.raw_trans = True + self.recv_buf = b'' + raise Exception('client_post_decrypt data error') + if length > len(self.recv_buf): + break + + out_buf += zlib.decompress(b'x\x9c' + self.recv_buf[2:length]) + 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 >= 32768 or length < 6: + 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 + + out_buf += zlib.decompress(b'\x78\x9c' + self.recv_buf[2:length]) + self.recv_buf = self.recv_buf[length:] + + if out_buf: + self.decrypt_packet_num += 1 + return (out_buf, False) + diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/ordereddict.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/ordereddict.py new file mode 100644 index 0000000..e1918f5 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/ordereddict.py @@ -0,0 +1,214 @@ +import collections + +################################################################################ +### OrderedDict +################################################################################ + +class OrderedDict(dict): + 'Dictionary that remembers insertion order' + # An inherited dict maps keys to values. + # The inherited dict provides __getitem__, __len__, __contains__, and get. + # The remaining methods are order-aware. + # Big-O running times for all methods are the same as regular dictionaries. + + # The internal self.__map dict maps keys to links in a doubly linked list. + # The circular doubly linked list starts and ends with a sentinel element. + # The sentinel element never gets deleted (this simplifies the algorithm). + # Each link is stored as a list of length three: [PREV, NEXT, KEY]. + + def __init__(*args, **kwds): + '''Initialize an ordered dictionary. The signature is the same as + regular dictionaries, but keyword arguments are not recommended because + their insertion order is arbitrary. + + ''' + if not args: + raise TypeError("descriptor '__init__' of 'OrderedDict' object " + "needs an argument") + self = args[0] + args = args[1:] + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__root + except AttributeError: + self.__root = root = [] # sentinel node + root[:] = [root, root, None] + self.__map = {} + self.__update(*args, **kwds) + + def __setitem__(self, key, value, dict_setitem=dict.__setitem__): + 'od.__setitem__(i, y) <==> od[i]=y' + # Setting a new item creates a new link at the end of the linked list, + # and the inherited dictionary is updated with the new key/value pair. + if key not in self: + root = self.__root + last = root[0] + last[1] = root[0] = self.__map[key] = [last, root, key] + return dict_setitem(self, key, value) + + def __delitem__(self, key, dict_delitem=dict.__delitem__): + 'od.__delitem__(y) <==> del od[y]' + # Deleting an existing item uses self.__map to find the link which gets + # removed by updating the links in the predecessor and successor nodes. + dict_delitem(self, key) + link_prev, link_next, _ = self.__map.pop(key) + link_prev[1] = link_next # update link_prev[NEXT] + link_next[0] = link_prev # update link_next[PREV] + + def __iter__(self): + 'od.__iter__() <==> iter(od)' + # Traverse the linked list in order. + root = self.__root + curr = root[1] # start at the first node + while curr is not root: + yield curr[2] # yield the curr[KEY] + curr = curr[1] # move to next node + + def __reversed__(self): + 'od.__reversed__() <==> reversed(od)' + # Traverse the linked list in reverse order. + root = self.__root + curr = root[0] # start at the last node + while curr is not root: + yield curr[2] # yield the curr[KEY] + curr = curr[0] # move to previous node + + def clear(self): + 'od.clear() -> None. Remove all items from od.' + root = self.__root + root[:] = [root, root, None] + self.__map.clear() + dict.clear(self) + + # -- the following methods do not depend on the internal structure -- + + def keys(self): + 'od.keys() -> list of keys in od' + return list(self) + + def values(self): + 'od.values() -> list of values in od' + return [self[key] for key in self] + + def items(self): + 'od.items() -> list of (key, value) pairs in od' + return [(key, self[key]) for key in self] + + def iterkeys(self): + 'od.iterkeys() -> an iterator over the keys in od' + return iter(self) + + def itervalues(self): + 'od.itervalues -> an iterator over the values in od' + for k in self: + yield self[k] + + def iteritems(self): + 'od.iteritems -> an iterator over the (key, value) pairs in od' + for k in self: + yield (k, self[k]) + + update = collections.MutableMapping.update + + __update = update # let subclasses override update without breaking __init__ + + __marker = object() + + def pop(self, key, default=__marker): + '''od.pop(k[,d]) -> v, remove specified key and return the corresponding + value. If key is not found, d is returned if given, otherwise KeyError + is raised. + + ''' + if key in self: + result = self[key] + del self[key] + return result + if default is self.__marker: + raise KeyError(key) + return default + + def setdefault(self, key, default=None): + 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' + if key in self: + return self[key] + self[key] = default + return default + + def popitem(self, last=True): + '''od.popitem() -> (k, v), return and remove a (key, value) pair. + Pairs are returned in LIFO order if last is true or FIFO order if false. + + ''' + if not self: + raise KeyError('dictionary is empty') + key = next(reversed(self) if last else iter(self)) + value = self.pop(key) + return key, value + + def __repr__(self, _repr_running={}): + 'od.__repr__() <==> repr(od)' + call_key = id(self), _get_ident() + if call_key in _repr_running: + return '...' + _repr_running[call_key] = 1 + try: + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + finally: + del _repr_running[call_key] + + def __reduce__(self): + 'Return state information for pickling' + items = [[k, self[k]] for k in self] + inst_dict = vars(self).copy() + for k in vars(OrderedDict()): + inst_dict.pop(k, None) + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def copy(self): + 'od.copy() -> a shallow copy of od' + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S. + If not specified, the value defaults to None. + + ''' + self = cls() + for key in iterable: + self[key] = value + return self + + def __eq__(self, other): + '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive + while comparison to a regular mapping is order-insensitive. + + ''' + if isinstance(other, OrderedDict): + return dict.__eq__(self, other) and all(_imap(_eq, self, other)) + return dict.__eq__(self, other) + + def __ne__(self, other): + 'od.__ne__(y) <==> od!=y' + return not self == other + + # -- the following methods support python 3.x style dictionary views -- + + def viewkeys(self): + "od.viewkeys() -> a set-like object providing a view on od's keys" + return KeysView(self) + + def viewvalues(self): + "od.viewvalues() -> an object providing a view on od's values" + return ValuesView(self) + + def viewitems(self): + "od.viewitems() -> a set-like object providing a view on od's items" + return ItemsView(self) + diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/server.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/server.py new file mode 100755 index 0000000..c18ad1c --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/server.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2015 clowwindy +# +# 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 sys +import os +import logging +import signal + +if __name__ == '__main__': + import inspect + file_path = os.path.dirname(os.path.realpath(inspect.getfile(inspect.currentframe()))) + sys.path.insert(0, os.path.join(file_path, '../')) + +from shadowsocks import shell, daemon, eventloop, tcprelay, udprelay, \ + asyncdns, manager, common + + +def main(): + shell.check_python() + + config = shell.get_config(False) + + shell.log_shadowsocks_version() + + daemon.daemon_exec(config) + + try: + import resource + logging.info('current process RLIMIT_NOFILE resource: soft %d hard %d' % resource.getrlimit(resource.RLIMIT_NOFILE)) + except ImportError: + pass + + if config['port_password']: + pass + else: + config['port_password'] = {} + server_port = config['server_port'] + if type(server_port) == list: + for a_server_port in server_port: + config['port_password'][a_server_port] = config['password'] + else: + config['port_password'][str(server_port)] = config['password'] + + if not config.get('dns_ipv6', False): + asyncdns.IPV6_CONNECTION_SUPPORT = False + + if config.get('manager_address', 0): + logging.info('entering manager mode') + manager.run(config) + return + + tcp_servers = [] + udp_servers = [] + dns_resolver = asyncdns.DNSResolver() + if int(config['workers']) > 1: + stat_counter_dict = None + else: + stat_counter_dict = {} + port_password = config['port_password'] + config_password = config.get('password', 'm') + del config['port_password'] + for port, password_obfs in port_password.items(): + method = config["method"] + protocol = config.get("protocol", 'origin') + protocol_param = config.get("protocol_param", '') + obfs = config.get("obfs", 'plain') + obfs_param = config.get("obfs_param", '') + bind = config.get("out_bind", '') + bindv6 = config.get("out_bindv6", '') + if type(password_obfs) == list: + password = password_obfs[0] + obfs = common.to_str(password_obfs[1]) + if len(password_obfs) > 2: + protocol = common.to_str(password_obfs[2]) + elif type(password_obfs) == dict: + password = password_obfs.get('password', config_password) + 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() + ipv6_ok = False + logging.info("server start with protocol[%s] password [%s] method [%s] obfs [%s] obfs_param [%s]" % + (protocol, password, method, obfs, obfs_param)) + if 'server_ipv6' in a_config: + try: + if len(a_config['server_ipv6']) > 2 and a_config['server_ipv6'][0] == "[" and a_config['server_ipv6'][-1] == "]": + a_config['server_ipv6'] = a_config['server_ipv6'][1:-1] + a_config['server_port'] = int(port) + a_config['password'] = password + a_config['method'] = method + a_config['protocol'] = protocol + a_config['protocol_param'] = protocol_param + a_config['obfs'] = obfs + a_config['obfs_param'] = obfs_param + a_config['out_bind'] = bind + a_config['out_bindv6'] = bindv6 + a_config['server'] = a_config['server_ipv6'] + logging.info("starting server at [%s]:%d" % + (a_config['server'], int(port))) + tcp_servers.append(tcprelay.TCPRelay(a_config, dns_resolver, False, stat_counter=stat_counter_dict)) + udp_servers.append(udprelay.UDPRelay(a_config, dns_resolver, False, stat_counter=stat_counter_dict)) + if a_config['server_ipv6'] == b"::": + ipv6_ok = True + except Exception as e: + shell.print_exception(e) + + try: + a_config = config.copy() + a_config['server_port'] = int(port) + a_config['password'] = password + a_config['method'] = method + a_config['protocol'] = protocol + a_config['protocol_param'] = protocol_param + a_config['obfs'] = obfs + a_config['obfs_param'] = obfs_param + a_config['out_bind'] = bind + a_config['out_bindv6'] = bindv6 + logging.info("starting server at %s:%d" % + (a_config['server'], int(port))) + tcp_servers.append(tcprelay.TCPRelay(a_config, dns_resolver, False, stat_counter=stat_counter_dict)) + udp_servers.append(udprelay.UDPRelay(a_config, dns_resolver, False, stat_counter=stat_counter_dict)) + except Exception as e: + if not ipv6_ok: + shell.print_exception(e) + + def run_server(): + def child_handler(signum, _): + logging.warn('received SIGQUIT, doing graceful shutting down..') + list(map(lambda s: s.close(next_tick=True), + tcp_servers + udp_servers)) + signal.signal(getattr(signal, 'SIGQUIT', signal.SIGTERM), + child_handler) + + def int_handler(signum, _): + sys.exit(1) + signal.signal(signal.SIGINT, int_handler) + + try: + loop = eventloop.EventLoop() + dns_resolver.add_to_loop(loop) + list(map(lambda s: s.add_to_loop(loop), tcp_servers + udp_servers)) + + daemon.set_user(config.get('user', None)) + loop.run() + except Exception as e: + shell.print_exception(e) + sys.exit(1) + + if int(config['workers']) > 1: + if os.name == 'posix': + children = [] + is_child = False + for i in range(0, int(config['workers'])): + r = os.fork() + if r == 0: + logging.info('worker started') + is_child = True + run_server() + break + else: + children.append(r) + if not is_child: + def handler(signum, _): + for pid in children: + try: + os.kill(pid, signum) + os.waitpid(pid, 0) + except OSError: # child may already exited + pass + sys.exit() + signal.signal(signal.SIGTERM, handler) + signal.signal(signal.SIGQUIT, handler) + signal.signal(signal.SIGINT, handler) + + # master + for a_tcp_server in tcp_servers: + a_tcp_server.close() + for a_udp_server in udp_servers: + a_udp_server.close() + dns_resolver.close() + + for child in children: + os.waitpid(child, 0) + else: + logging.warn('worker is only available on Unix/Linux') + run_server() + else: + run_server() + + +if __name__ == '__main__': + main() diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/shell.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/shell.py new file mode 100755 index 0000000..6246d98 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/shell.py @@ -0,0 +1,445 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright 2015 clowwindy +# +# 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 json +import sys +import getopt +import logging +from shadowsocks.common import to_bytes, to_str, IPNetwork, PortRange +from shadowsocks import encrypt + + +VERBOSE_LEVEL = 5 + +verbose = 0 + + +def check_python(): + info = sys.version_info + if info[0] == 2 and not info[1] >= 6: + print('Python 2.6+ required') + sys.exit(1) + elif info[0] == 3 and not info[1] >= 3: + print('Python 3.3+ required') + sys.exit(1) + elif info[0] not in [2, 3]: + print('Python version not supported') + sys.exit(1) + + +def print_exception(e): + global verbose + logging.error(e) + if verbose > 0: + 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(): + print('ShadowsocksR %s' % __version()) + +def log_shadowsocks_version(): + logging.info('ShadowsocksR %s' % __version()) + + +def find_config(): + user_config_path = 'user-config.json' + config_path = 'config.json' + + 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': + # no need to specify configuration for daemon stop + return + + if is_local and not config.get('password', None): + logging.error('password not specified') + print_help(is_local) + sys.exit(2) + + if not is_local and not config.get('password', None) \ + and not config.get('port_password', None): + logging.error('password or port_password not specified') + print_help(is_local) + sys.exit(2) + + if 'local_port' in config: + config['local_port'] = int(config['local_port']) + + if 'server_port' in config and type(config['server_port']) != list: + config['server_port'] = int(config['server_port']) + + if config.get('local_address', '') in [b'0.0.0.0']: + 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.warning('warning: server set to listen on %s:%s, are you sure?' % + (to_str(config['server']), config['server_port'])) + if config.get('timeout', 300) < 100: + logging.warning('warning: your timeout %d seems too short' % + int(config.get('timeout'))) + if config.get('timeout', 300) > 600: + 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 ' + 'config.json!') + sys.exit(1) + if config.get('user', None) is not None: + if os.name != 'posix': + logging.error('user can be used only on Unix') + sys.exit(1) + + encrypt.try_cipher(config['password'], config['method']) + + +def get_config(is_local): + global verbose + config = {} + config_path = None + logging.basicConfig(level=logging.INFO, + format='%(levelname)-s: %(message)s') + if is_local: + shortopts = 'hd:s:b:p:k:l:m:O:o:G:g:c:t:vq' + longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'user=', + 'version'] + else: + shortopts = 'hd:s:p:k:m:O:o:G:g:c:t:vq' + longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers=', + 'forbidden-ip=', 'user=', 'manager-address=', 'version'] + try: + 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.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', str(e)) + sys.exit(1) + + + v_count = 0 + for key, value in optlist: + if key == '-p': + config['server_port'] = int(value) + elif key == '-k': + config['password'] = to_bytes(value) + elif key == '-l': + config['local_port'] = int(value) + elif key == '-s': + config['server'] = to_str(value) + elif key == '-m': + config['method'] = to_str(value) + elif key == '-O': + config['protocol'] = to_str(value) + elif key == '-o': + config['obfs'] = to_str(value) + elif key == '-G': + config['protocol_param'] = to_str(value) + elif key == '-g': + config['obfs_param'] = to_str(value) + elif key == '-b': + config['local_address'] = to_str(value) + elif key == '-v': + v_count += 1 + # '-vv' turns on more verbose mode + config['verbose'] = v_count + elif key == '-t': + config['timeout'] = int(value) + elif key == '--fast-open': + config['fast_open'] = True + elif key == '--workers': + config['workers'] = int(value) + elif key == '--manager-address': + config['manager_address'] = value + elif key == '--user': + config['user'] = to_str(value) + elif key == '--forbidden-ip': + config['forbidden_ip'] = to_str(value) + + elif key == '-d': + config['daemon'] = to_str(value) + elif key == '--pid-file': + config['pid-file'] = to_str(value) + elif key == '--log-file': + config['log-file'] = to_str(value) + 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) + sys.exit(2) + + if not config: + logging.error('config not specified') + print_help(is_local) + sys.exit(2) + + config['password'] = to_bytes(config.get('password', b'')) + config['method'] = to_str(config.get('method', 'aes-256-cfb')) + config['protocol'] = to_str(config.get('protocol', 'origin')) + config['protocol_param'] = to_str(config.get('protocol_param', '')) + 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/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')) + config['local_port'] = config.get('local_port', 1080) + if is_local: + if config.get('server', None) is None: + logging.error('server addr not specified') + print_local_help() + sys.exit(2) + else: + config['server'] = to_str(config['server']) + else: + config['server'] = to_str(config.get('server', '0.0.0.0')) + try: + config['forbidden_ip'] = \ + IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1/128')) + except Exception as e: + logging.error(e) + sys.exit(2) + try: + config['forbidden_port'] = PortRange(config.get('forbidden_port', '')) + except Exception as e: + logging.error(e) + sys.exit(2) + try: + config['ignore_bind'] = \ + 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) + config['server_port'] = config.get('server_port', 8388) + + logging.getLogger('').handlers = [] + logging.addLevelName(VERBOSE_LEVEL, 'VERBOSE') + if config['verbose'] >= 2: + level = VERBOSE_LEVEL + elif config['verbose'] == 1: + level = logging.DEBUG + elif config['verbose'] == -1: + level = logging.WARN + elif config['verbose'] <= -2: + level = logging.ERROR + else: + level = logging.INFO + verbose = config['verbose'] + logging.basicConfig(level=level, + format='%(asctime)s %(levelname)-8s %(filename)s:%(lineno)s %(message)s', + datefmt='%Y-%m-%d %H:%M:%S') + + check_config(config, is_local) + + return config + + +def print_help(is_local): + if is_local: + print_local_help() + else: + print_server_help() + + +def print_local_help(): + print('''usage: sslocal [OPTION]... +A fast tunnel proxy that helps you bypass firewalls. + +You can supply configurations via either config file or command line arguments. + +Proxy options: + -c CONFIG path to config file + -s SERVER_ADDR server address + -p SERVER_PORT server port, default: 8388 + -b LOCAL_ADDR local binding address, default: 127.0.0.1 + -l LOCAL_PORT local port, default: 1080 + -k PASSWORD password + -m METHOD encryption method, default: aes-256-cfb + -o OBFS obfsplugin, default: http_simple + -t TIMEOUT timeout in seconds, default: 300 + --fast-open use TCP_FASTOPEN, requires Linux 3.7+ + +General options: + -h, --help show this help message and exit + -d start/stop/restart daemon mode + --pid-file PID_FILE pid file for daemon mode + --log-file LOG_FILE log file for daemon mode + --user USER username to run as + -v, -vv verbose mode + -q, -qq quiet mode, only show warnings/errors + --version show version information + +Online help: +''') + + +def print_server_help(): + print('''usage: ssserver [OPTION]... +A fast tunnel proxy that helps you bypass firewalls. + +You can supply configurations via either config file or command line arguments. + +Proxy options: + -c CONFIG path to config file + -s SERVER_ADDR server address, default: 0.0.0.0 + -p SERVER_PORT server port, default: 8388 + -k PASSWORD password + -m METHOD encryption method, default: aes-256-cfb + -o OBFS obfsplugin, default: http_simple + -t TIMEOUT timeout in seconds, default: 300 + --fast-open use TCP_FASTOPEN, requires Linux 3.7+ + --workers WORKERS number of workers, available on Unix/Linux + --forbidden-ip IPLIST comma seperated IP list forbidden to connect + --manager-address ADDR optional server manager UDP address, see wiki + +General options: + -h, --help show this help message and exit + -d start/stop/restart daemon mode + --pid-file PID_FILE pid file for daemon mode + --log-file LOG_FILE log file for daemon mode + --user USER username to run as + -v, -vv verbose mode + -q, -qq quiet mode, only show warnings/errors + --version show version information + +Online help: +''') + + +def _decode_list(data): + rv = [] + for item in data: + if hasattr(item, 'encode'): + item = item.encode('utf-8') + elif isinstance(item, list): + item = _decode_list(item) + elif isinstance(item, dict): + item = _decode_dict(item) + rv.append(item) + return rv + + +def _decode_dict(data): + rv = {} + for key, value in data.items(): + if hasattr(value, 'encode'): + value = value.encode('utf-8') + elif isinstance(value, list): + value = _decode_list(value) + elif isinstance(value, dict): + value = _decode_dict(value) + rv[key] = value + return rv + +class JSFormat: + def __init__(self): + self.state = 0 + + def push(self, ch): + ch = ord(ch) + if self.state == 0: + if ch == ord('"'): + self.state = 1 + return to_str(chr(ch)) + elif ch == ord('/'): + self.state = 3 + else: + return to_str(chr(ch)) + elif self.state == 1: + if ch == ord('"'): + self.state = 0 + return to_str(chr(ch)) + elif ch == ord('\\'): + self.state = 2 + return to_str(chr(ch)) + elif self.state == 2: + self.state = 1 + if ch == ord('"'): + return to_str(chr(ch)) + return "\\" + to_str(chr(ch)) + elif self.state == 3: + if ch == ord('/'): + self.state = 4 + else: + return "/" + to_str(chr(ch)) + elif self.state == 4: + if ch == ord('\n'): + self.state = 0 + return "\n" + return "" + +def remove_comment(json): + fmt = JSFormat() + return "".join([fmt.push(c) for c in json]) + + +def parse_json_in_str(data): + # parse json and convert everything from unicode to str + return json.loads(data, object_hook=_decode_dict) diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/tcprelay.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/tcprelay.py new file mode 100644 index 0000000..506e1ce --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/tcprelay.py @@ -0,0 +1,1472 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright 2015 clowwindy +# +# 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 time +import socket +import errno +import struct +import logging +import binascii +import traceback +import random +import platform +import threading + +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 +TIMEOUTS_CLEAN_SIZE = 512 + +MSG_FASTOPEN = 0x20000000 + +# SOCKS command definition +CMD_CONNECT = 1 +CMD_BIND = 2 +CMD_UDP_ASSOCIATE = 3 + +# for each opening port, we have a TCP Relay + +# for each connection, we have a TCP Relay Handler to handle the connection + +# for each handler, we have 2 sockets: +# local: connected to the client +# remote: connected to remote server + +# for each handler, it could be at one of several stages: + +# as sslocal: +# stage 0 SOCKS hello received from local, send hello to local +# stage 1 addr received from local, query DNS for remote +# stage 2 UDP assoc +# stage 3 DNS resolved, connect to remote +# stage 4 still connecting, more data from local received +# stage 5 remote connected, piping local and remote + +# as ssserver: +# stage 0 just jump to stage 1 +# stage 1 addr received from local, query DNS for remote +# stage 3 DNS resolved, connect to remote +# stage 4 still connecting, more data from local received +# stage 5 remote connected, piping local and remote + +STAGE_INIT = 0 +STAGE_ADDR = 1 +STAGE_UDP_ASSOC = 2 +STAGE_DNS = 3 +STAGE_CONNECTING = 4 +STAGE_STREAM = 5 +STAGE_DESTROYED = -1 + +# for each handler, we have 2 stream directions: +# upstream: from client to server direction +# read local and write to remote +# downstream: from server to client direction +# read remote and write to local + +STREAM_UP = 0 +STREAM_DOWN = 1 + +# for each stream, it's waiting for reading, or writing, or both +WAIT_STATUS_INIT = 0 +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): + self._server = server + self._fd_to_handlers = fd_to_handlers + self._loop = loop + 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._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 = '' + server_info.obfs_param = config['obfs_param'] + server_info.iv = self._encryptor.cipher_iv + server_info.recv_iv = b'' + 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 = self._tcp_mss + server_info.buffer_size = self._recv_buffer_size + server_info.overhead = self._overhead + self._obfs.set_server_info(server_info) + + server_info = obfs.server_info(server.protocol_data) + 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'] + server_info.obfs_param = '' + server_info.iv = self._encryptor.cipher_iv + server_info.recv_iv = b'' + 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 = 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"]) + self._is_redirect = False + self._bind = config.get('out_bind', '') + self._bindv6 = config.get('out_bindv6', '') + self._ignore_bind_list = config.get('ignore_bind', []) + + self._fastopen_connected = False + self._data_to_write_to_local = [] + self._data_to_write_to_remote = [] + self._udp_data_send_buffer = b'' + self._upstream_status = WAIT_STATUS_READING + self._downstream_status = WAIT_STATUS_INIT + self._remote_address = 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() + + 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 + # we want to eliminate collisions + return id(self) + + @property + def remote_address(self): + return self._remote_address + + def _get_a_server(self): + server = self._config['server'] + server_port = self._config['server_port'] + if type(server_port) == list: + server_port = random.choice(server_port) + if type(server) == list: + server = random.choice(server) + 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(' 6: + length = struct.unpack('>H', self._udp_data_send_buffer[:2])[0] + + if length > len(self._udp_data_send_buffer): + break + + data = self._udp_data_send_buffer[:length] + self._udp_data_send_buffer = self._udp_data_send_buffer[length:] + + frag = common.ord(data[2]) + if frag != 0: + logging.warn('drop a message since frag is %d' % (frag,)) + continue + else: + data = data[3:] + header_result = parse_header(data) + if header_result is None: + continue + 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: + 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() + #logging.error(trace) + error_no = eventloop.errno_from_exception(e) + if error_no in (errno.EAGAIN, errno.EINPROGRESS, + errno.EWOULDBLOCK): + uncomplete = True + else: + shell.print_exception(e) + logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1])) + self.destroy() + return False + return True + else: + try: + if self._encrypt_correct: + if sock == self._remote_sock: + self._server.add_transfer_u(self._user, len(data)) + self._update_activity(len(data)) + if data: + l = len(data) + s = sock.send(data) + if s < l: + data = data[s:] + uncomplete = True + else: + return + except (OSError, IOError) as e: + error_no = eventloop.errno_from_exception(e) + if error_no in (errno.EAGAIN, errno.EINPROGRESS, + errno.EWOULDBLOCK): + uncomplete = True + else: + #traceback.print_exc() + shell.print_exception(e) + logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1])) + self.destroy() + return False + except Exception as e: + shell.print_exception(e) + logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1])) + self.destroy() + return False + if uncomplete: + if sock == self._local_sock: + self._data_to_write_to_local.append(data) + self._update_stream(STREAM_DOWN, WAIT_STATUS_WRITING) + elif sock == self._remote_sock: + self._data_to_write_to_remote.append(data) + self._update_stream(STREAM_UP, WAIT_STATUS_WRITING) + else: + logging.error('write_all_to_sock:unknown socket from %s:%d' % (self._client_address[0], self._client_address[1])) + else: + if sock == self._local_sock: + self._update_stream(STREAM_DOWN, WAIT_STATUS_READING) + elif sock == self._remote_sock: + self._update_stream(STREAM_UP, WAIT_STATUS_READING) + else: + 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"] + + if type(host_list) != list: + host_list = [host_list] + + items_sum = common.to_str(host_list[0]).rsplit('#', 1) + if len(items_sum) < 2: + hash_code = binascii.crc32(ogn_data) + addrs = socket.getaddrinfo(client_address[0], client_address[1], 0, socket.SOCK_STREAM, socket.SOL_TCP) + af, socktype, proto, canonname, sa = addrs[0] + address_bytes = common.inet_pton(af, sa[0]) + if af == socket.AF_INET6: + addr = struct.unpack('>Q', address_bytes[8:])[0] + elif af == socket.AF_INET: + addr = struct.unpack('>I', address_bytes)[0] + else: + addr = 0 + + host_port = [] + match_port = False + for host in host_list: + items = common.to_str(host).rsplit(':', 1) + if len(items) > 1: + try: + port = int(items[1]) + if port == self._server._listen_port: + match_port = True + host_port.append((items[0], port)) + except: + pass + else: + host_port.append((host, 80)) + + if match_port: + last_host_port = host_port + host_port = [] + for host in last_host_port: + if host[1] == self._server._listen_port: + host_port.append(host) + + return host_port[((hash_code & 0xffffffff) + addr) % len(host_port)] + + else: + host_port = [] + for host in host_list: + 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 items_match[1] != "*": + try: + 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: + continue + if len(items) > 1: + try: + port = int(items[1]) + return (items[0], port) + except: + pass + else: + return (items[0], 80) + + 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 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) + if port == 0: + raise Exception('can not parse header') + data = b"\x03" + common.to_bytes(common.chr(len(host))) + common.to_bytes(host) + struct.pack('>H', port) + self._is_redirect = True + logging.warn("TCP data redir %s:%d %s" % (host, port, binascii.hexlify(data))) + return data + ogn_data + + def _handle_stage_connecting(self, data): + if self._is_local: + if self._encryptor is not None: + data = self._protocol.client_pre_encrypt(data) + data = self._encryptor.encrypt(data) + data = self._obfs.client_encode(data) + if data: + self._data_to_write_to_remote.append(data) + if self._is_local and not self._fastopen_connected and \ + self._config['fast_open']: + # for sslocal and fastopen, we basically wait for data and use + # sendto to connect + try: + # only connect once + self._fastopen_connected = True + remote_sock = \ + self._create_remote_socket(self._chosen_server[0], + self._chosen_server[1]) + self._loop.add(remote_sock, eventloop.POLL_ERR, self._server) + data = b''.join(self._data_to_write_to_remote) + l = len(data) + s = remote_sock.sendto(data, MSG_FASTOPEN, self._chosen_server) + if s < l: + data = data[s:] + self._data_to_write_to_remote = [data] + else: + self._data_to_write_to_remote = [] + self._update_stream(STREAM_UP, WAIT_STATUS_READWRITING) + except (OSError, IOError) as e: + if eventloop.errno_from_exception(e) == errno.EINPROGRESS: + # in this case data is not sent at all + self._update_stream(STREAM_UP, WAIT_STATUS_READWRITING) + elif eventloop.errno_from_exception(e) == errno.ENOTCONN: + logging.error('fast open not supported on this OS') + self._config['fast_open'] = False + self.destroy() + else: + 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() + + def _get_head_size(self, buf, def_value): + if len(buf) < 2: + return def_value + head_type = common.ord(buf[0]) & 0xF + if head_type == 1: + return 7 + if head_type == 4: + return 19 + if head_type == 3: + return 4 + common.ord(buf[1]) + return def_value + + def _handle_stage_addr(self, ogn_data, data): + try: + if self._is_local: + cmd = common.ord(data[1]) + if cmd == CMD_UDP_ASSOCIATE: + logging.debug('UDP associate') + if self._local_sock.family == socket.AF_INET6: + header = b'\x05\x00\x00\x04' + else: + header = b'\x05\x00\x00\x01' + addr, port = self._local_sock.getsockname()[:2] + addr_to_send = socket.inet_pton(self._local_sock.family, + addr) + port_to_send = struct.pack('>H', port) + self._write_to_sock(header + addr_to_send + port_to_send, + self._local_sock) + self._stage = STAGE_UDP_ASSOC + # just wait for the client to disconnect + return + elif cmd == CMD_CONNECT: + # just trim VER CMD RSV + data = data[3:] + else: + logging.error('invalid command %d', cmd) + self.destroy() + return + + before_parse_data = data + if self._is_local: + header_result = parse_header(data) + else: + data = pre_parse_header(data) + if data is None: + data = self._handel_protocol_error(self._client_address, ogn_data) + header_result = parse_header(data) + if header_result is not None: + try: + 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) + 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 + self._update_stream(STREAM_UP, WAIT_STATUS_WRITING) + self._stage = STAGE_DNS + if self._is_local: + # forward address to remote + self._write_to_sock((b'\x05\x00\x00\x01' + b'\x00\x00\x00\x00\x10\x10'), + self._local_sock) + head_len = self._get_head_size(data, 30) + self._obfs.obfs.server_info.head_len = head_len + self._protocol.obfs.server_info.head_len = head_len + if self._encryptor is not None: + data = self._protocol.client_pre_encrypt(data) + data_to_send = self._encryptor.encrypt(data) + data_to_send = self._obfs.client_encode(data_to_send) + if data_to_send: + self._data_to_write_to_remote.append(data_to_send) + # notice here may go into _handle_dns_resolved directly + self._dns_resolver.resolve(self._chosen_server[0], + self._handle_dns_resolved) + else: + if len(data) > header_length: + self._data_to_write_to_remote.append(data[header_length:]) + # notice here may go into _handle_dns_resolved directly + self._dns_resolver.resolve(remote_addr, + self._handle_dns_resolved) + except Exception as e: + self._log_error(e) + if self._config['verbose']: + traceback.print_exc() + self.destroy() + + def _socket_bind_addr(self, sock, af): + bind_addr = '' + if self._bind and af == socket.AF_INET: + bind_addr = self._bind + elif self._bindv6 and af == socket.AF_INET6: + bind_addr = self._bindv6 + else: + bind_addr = self._accept_address[0] + + bind_addr = bind_addr.replace("::ffff:", "") + if bind_addr in self._ignore_bind_list: + bind_addr = None + if bind_addr: + local_addrs = socket.getaddrinfo(bind_addr, 0, 0, socket.SOCK_STREAM, socket.SOL_TCP) + if local_addrs[0][0] == af: + logging.debug("bind %s" % (bind_addr,)) + try: + sock.bind((bind_addr, 0)) + except Exception as e: + logging.warn("bind %s fail" % (bind_addr,)) + + def _create_remote_socket(self, ip, port): + if self._remote_udp: + addrs_v6 = socket.getaddrinfo("::", 0, 0, socket.SOCK_DGRAM, socket.SOL_UDP) + addrs = socket.getaddrinfo("0.0.0.0", 0, 0, socket.SOCK_DGRAM, socket.SOL_UDP) + else: + addrs = socket.getaddrinfo(ip, port, 0, socket.SOCK_STREAM, socket.SOL_TCP) + if len(addrs) == 0: + raise Exception("getaddrinfo failed for %s:%d" % (ip, port)) + af, socktype, proto, canonname, sa = addrs[0] + if not self._remote_udp and not self._is_redirect: + 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 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 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._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._remotev6_sock_fd = remote_sock_v6.fileno() + self._fd_to_handlers[self._remotev6_sock_fd] = self + + remote_sock.setblocking(False) + if self._remote_udp: + remote_sock_v6.setblocking(False) + + if not self._is_local: + self._socket_bind_addr(remote_sock, af) + self._socket_bind_addr(remote_sock_v6, af) + else: + remote_sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) + if not self._is_local: + self._socket_bind_addr(remote_sock, af) + return remote_sock + + def _handle_dns_resolved(self, result, error): + if error: + self._log_error(error) + self.destroy() + return + if result: + ip = result[1] + if ip: + try: + self._stage = STAGE_CONNECTING + remote_addr = ip + if self._is_local: + remote_port = self._chosen_server[1] + else: + remote_port = self._remote_address[1] + + if self._is_local and self._config['fast_open']: + # for fastopen: + # wait for more data to arrive and send them in one SYN + self._stage = STAGE_CONNECTING + # we don't have to wait for remote since it's not + # created + self._update_stream(STREAM_UP, WAIT_STATUS_READING) + # TODO when there is already data in this packet + else: + # else do connect + remote_sock = self._create_remote_socket(remote_addr, + remote_port) + if self._remote_udp: + self._loop.add(remote_sock, + eventloop.POLL_IN, + self._server) + if self._remote_sock_v6: + self._loop.add(self._remote_sock_v6, + eventloop.POLL_IN, + self._server) + else: + try: + remote_sock.connect((remote_addr, remote_port)) + except (OSError, IOError) as e: + if eventloop.errno_from_exception(e) in (errno.EINPROGRESS, + errno.EWOULDBLOCK): + 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) + self._stage = STAGE_CONNECTING + self._update_stream(STREAM_UP, WAIT_STATUS_READWRITING) + self._update_stream(STREAM_DOWN, WAIT_STATUS_READING) + if self._remote_udp: + while self._data_to_write_to_remote: + data = self._data_to_write_to_remote[0] + del self._data_to_write_to_remote[0] + self._write_to_sock(data, self._remote_sock) + return + 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() + + 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)) + if up: + buffer_size = min(buffer_size, self._recv_u_max_size) + self._recv_u_max_size = min(self._recv_u_max_size + self._tcp_mss - self._overhead, BUF_SIZE) + else: + buffer_size = min(buffer_size, self._recv_d_max_size) + self._recv_d_max_size = min(self._recv_d_max_size + self._tcp_mss - self._overhead, BUF_SIZE) + if buffer_size == recv_buffer_size: + return buffer_size + frame_size = self._tcp_mss - self._overhead + 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(recv_buffer_size) + except (OSError, IOError) as e: + if eventloop.errno_from_exception(e) in \ + (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK): + return + 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) + except Exception as e: + shell.print_exception(e) + logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1])) + self.destroy() + return + if obfs_decode[2]: + data = self._obfs.server_encode(b'') + 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) + self._protocol.obfs.server_info.recv_iv = obfs_decode[0][:iv_len] + data = self._encryptor.decrypt(obfs_decode[0]) + else: + data = obfs_decode[0] + try: + data, sendback = self._protocol.server_post_decrypt(data) + if sendback: + backdata = self._protocol.server_pre_encrypt(b'') + backdata = self._encryptor.encrypt(backdata) + backdata = self._obfs.server_encode(backdata) + try: + self._write_to_sock(backdata, 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 + except Exception as e: + 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: + return + if self._stage == STAGE_STREAM: + if self._is_local: + if self._encryptor is not None: + data = self._protocol.client_pre_encrypt(data) + data = self._encryptor.encrypt(data) + data = self._obfs.client_encode(data) + self._write_to_sock(data, self._remote_sock) + 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 + elif self._stage == STAGE_CONNECTING: + self._handle_stage_connecting(data) + elif (is_local and self._stage == STAGE_ADDR) or \ + (not is_local and self._stage == STAGE_INIT): + self._handle_stage_addr(ogn_data, data) + + def _on_remote_read(self, is_remote_sock): + # handle all remote read events + data = None + try: + if self._remote_udp: + if is_remote_sock: + data, addr = self._remote_sock.recvfrom(UDP_MAX_BUF_SIZE) + else: + data, addr = self._remote_sock_v6.recvfrom(UDP_MAX_BUF_SIZE) + port = struct.pack('>H', addr[1]) + try: + ip = socket.inet_aton(addr[0]) + data = b'\x00\x01' + ip + port + data + except Exception as e: + ip = socket.inet_pton(socket.AF_INET6, addr[0]) + data = b'\x00\x04' + ip + port + data + size = len(data) + 2 + data = struct.pack('>H', size) + data + #logging.info('UDP over TCP recvfrom %s:%d %d bytes to %s:%d' % (addr[0], addr[1], len(data), self._client_address[0], self._client_address[1])) + else: + if self._is_local: + recv_buffer_size = BUF_SIZE + else: + recv_buffer_size = self._get_read_size(self._remote_sock, self._recv_buffer_size, 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 + return + 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: + obfs_decode = self._obfs.client_decode(data) + except Exception as e: + shell.print_exception(e) + logging.error("exception from %s:%d" % (self._client_address[0], self._client_address[1])) + self.destroy() + return + if obfs_decode[1]: + send_back = self._obfs.client_encode(b'') + self._write_to_sock(send_back, self._remote_sock) + if not self._protocol.obfs.server_info.recv_iv: + iv_len = len(self._protocol.obfs.server_info.iv) + self._protocol.obfs.server_info.recv_iv = obfs_decode[0][:iv_len] + 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])) + self.destroy() + return + else: + if self._encrypt_correct: + data = self._protocol.server_pre_encrypt(data) + data = self._encryptor.encrypt(data) + data = self._obfs.server_encode(data) + self._server.add_transfer_d(self._user, len(data)) + self._update_activity(len(data)) + else: + return + 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() + + def _on_local_write(self): + # handle local writable event + if self._data_to_write_to_local: + data = b''.join(self._data_to_write_to_local) + self._data_to_write_to_local = [] + self._write_to_sock(data, self._local_sock) + else: + self._update_stream(STREAM_DOWN, WAIT_STATUS_READING) + + def _on_remote_write(self): + # handle remote writable event + self._stage = STAGE_STREAM + if self._data_to_write_to_remote: + data = b''.join(self._data_to_write_to_remote) + self._data_to_write_to_remote = [] + self._write_to_sock(data, self._remote_sock) + else: + self._update_stream(STREAM_UP, WAIT_STATUS_READING) + + def _on_local_error(self): + if self._local_sock: + 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): + if self._remote_sock: + 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, 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 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() + 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 fd == self._local_sock_fd: + if event & eventloop.POLL_ERR: + handle = True + self._on_local_error() + 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' % + (e, self._client_address[0], self._client_address[1])) + + def stage(self): + return self._stage + + def destroy(self): + # destroy the handler and release any resources + # promises: + # 1. destroy won't make another destroy() call inside + # 2. destroy releases resources so it prevents future call to destroy + # 3. destroy won't raise any exceptions + # if any of the promises are broken, it indicates a bug has been + # introduced! mostly likely memory leaks, etc + if self._stage == STAGE_DESTROYED: + # this couldn't happen + logging.debug('already destroyed') + return + self._stage = STAGE_DESTROYED + if self._remote_address: + logging.debug('destroy: %s:%d' % + self._remote_address) + else: + logging.debug('destroy') + if self._remote_sock: + logging.debug('destroying remote') + try: + self._loop.removefd(self._remote_sock_fd) + except Exception as e: + 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_v6') + try: + self._loop.removefd(self._remotev6_sock_fd) + except Exception as e: + 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') + 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: + self._obfs.dispose() + self._obfs = None + if self._protocol: + self._protocol.dispose() + self._protocol = None + self._encryptor = None + self._dns_resolver.remove_callback(self._handle_dns_resolved) + self._server.remove_handler(self) + 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): + self._config = config + self._is_local = is_local + self._dns_resolver = dns_resolver + self._closed = False + self._eventloop = None + 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() + + if config.get('connect_verbose_info', 0) > 0: + common.connect_log = logging.info + + self._timeout = config['timeout'] + self._timeout_cache = lru_cache.LRUCache(timeout=self._timeout, + close_callback=self._close_tcp_client) + + if is_local: + listen_addr = config['local_address'] + listen_port = config['local_port'] + else: + listen_addr = config['server'] + 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: + raise Exception("can't get addrinfo for %s:%d" % + (listen_addr, listen_port)) + af, socktype, proto, canonname, sa = addrs[0] + server_socket = socket.socket(af, socktype, proto) + server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server_socket.bind(sa) + server_socket.setblocking(False) + if config['fast_open']: + try: + server_socket.setsockopt(socket.SOL_TCP, 23, 5) + except socket.error: + logging.error('warning: fast open is not available') + 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 + + def add_to_loop(self, loop): + if self._eventloop: + raise Exception('already add to loop') + if self._closed: + raise Exception('already closed') + self._eventloop = loop + self._eventloop.add(self._server_socket, + eventloop.POLL_IN | eventloop.POLL_ERR, self) + self._eventloop.add_periodic(self.handle_periodic) + + def remove_handler(self, client): + if hash(client) in self._timeout_cache: + del self._timeout_cache[hash(client)] + + def add_connection(self, val): + 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('= stat_dict.get(-1, 0) + connections_step: + logging.info('port %d connections up to %d' % (port, newval)) + stat_dict[-1] = stat_dict.get(-1, 0) + connections_step + elif newval <= stat_dict.get(-1, 0) - connections_step: + logging.info('port %d connections down to %d' % (port, newval)) + stat_dict[-1] = stat_dict.get(-1, 0) - connections_step + + def stat_add(self, local_addr, val): + if self._stat_counter is not None: + if self._listen_port not in self._stat_counter: + self._stat_counter[self._listen_port] = {} + newval = self._stat_counter[self._listen_port].get(local_addr, 0) + val + logging.debug('port %d addr %s connections %d' % (self._listen_port, local_addr, newval)) + self._stat_counter[self._listen_port][local_addr] = newval + self.update_stat(self._listen_port, self._stat_counter[self._listen_port], val) + if newval <= 0: + if local_addr in self._stat_counter[self._listen_port]: + del self._stat_counter[self._listen_port][local_addr] + + newval = self._stat_counter.get(0, 0) + val + self._stat_counter[0] = newval + logging.debug('Total connections %d' % newval) + + connections_step = 50 + if newval >= self._stat_counter.get(-1, 0) + connections_step: + logging.info('Total connections up to %d' % newval) + self._stat_counter[-1] = self._stat_counter.get(-1, 0) + connections_step + elif newval <= self._stat_counter.get(-1, 0) - connections_step: + logging.info('Total connections down to %d' % newval) + self._stat_counter[-1] = self._stat_counter.get(-1, 0) - connections_step + + def update_activity(self, client, data_len): + if data_len and self._stat_callback: + self._stat_callback(self._listen_port, data_len) + + self._timeout_cache[hash(client)] = client + + def _sweep_timeout(self): + self._timeout_cache.sweep() + + def _close_tcp_client(self, client): + if client.remote_address: + logging.debug('timed out: %s:%d' % + client.remote_address) + else: + logging.debug('timed out') + client.destroy() + + 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)) + if sock == self._server_socket: + if event & eventloop.POLL_ERR: + # TODO + raise Exception('server_socket error') + handler = None + handle = True + try: + logging.debug('accept') + conn = self._server_socket.accept() + handler = TCPRelayHandler(self, self._fd_to_handlers, + self._eventloop, conn[0], self._config, + self._dns_resolver, self._is_local) + if handler.stage() == STAGE_DESTROYED: + conn[0].close() + except (OSError, IOError) as e: + error_no = eventloop.errno_from_exception(e) + if error_no in (errno.EAGAIN, errno.EINPROGRESS, + errno.EWOULDBLOCK): + return + else: + 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: + 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.removefd(self._server_socket_fd) + self._server_socket.close() + self._server_socket = None + logging.info('closed TCP port %d', self._listen_port) + for handler in list(self._fd_to_handlers.values()): + handler.destroy() + self._sweep_timeout() + + def close(self, next_tick=False): + logging.debug('TCP close') + self._closed = True + if not next_tick: + if self._eventloop: + self._eventloop.remove_periodic(self.handle_periodic) + self._eventloop.removefd(self._server_socket_fd) + self._server_socket.close() + for handler in list(self._fd_to_handlers.values()): + handler.destroy() diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/udprelay.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/udprelay.py new file mode 100644 index 0000000..b9606cd --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/udprelay.py @@ -0,0 +1,656 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright 2015 clowwindy +# +# 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. + +# SOCKS5 UDP Request +# +----+------+------+----------+----------+----------+ +# |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | +# +----+------+------+----------+----------+----------+ +# | 2 | 1 | 1 | Variable | 2 | Variable | +# +----+------+------+----------+----------+----------+ + +# SOCKS5 UDP Response +# +----+------+------+----------+----------+----------+ +# |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | +# +----+------+------+----------+----------+----------+ +# | 2 | 1 | 1 | Variable | 2 | Variable | +# +----+------+------+----------+----------+----------+ + +# shadowsocks UDP Request (before encrypted) +# +------+----------+----------+----------+ +# | ATYP | DST.ADDR | DST.PORT | DATA | +# +------+----------+----------+----------+ +# | 1 | Variable | 2 | Variable | +# +------+----------+----------+----------+ + +# shadowsocks UDP Response (before encrypted) +# +------+----------+----------+----------+ +# | ATYP | DST.ADDR | DST.PORT | DATA | +# +------+----------+----------+----------+ +# | 1 | Variable | 2 | Variable | +# +------+----------+----------+----------+ + +# shadowsocks UDP Request and Response (after encrypted) +# +-------+--------------+ +# | IV | PAYLOAD | +# +-------+--------------+ +# | Fixed | Variable | +# +-------+--------------+ + +# HOW TO NAME THINGS +# ------------------ +# `dest` means destination server, which is from DST fields in the SOCKS5 +# request +# `local` means local server of shadowsocks +# `remote` means remote server of shadowsocks +# `client` means UDP clients that connects to other servers +# `server` means the UDP server that handles user requests + +from __future__ import absolute_import, division, print_function, \ + with_statement + +import time +import socket +import logging +import struct +import errno +import random +import binascii +import traceback +import threading + +from shadowsocks import encrypt, obfs, eventloop, lru_cache, common, shell +from shadowsocks.common import pre_parse_header, parse_header, pack_addr + +# for each handler, we have 2 stream directions: +# upstream: from client to server direction +# read local and write to remote +# downstream: from server to client direction +# read remote and write to local + +STREAM_UP = 0 +STREAM_DOWN = 1 + +# for each stream, it's waiting for reading, or writing, or both +WAIT_STATUS_INIT = 0 +WAIT_STATUS_READING = 1 +WAIT_STATUS_WRITING = 2 +WAIT_STATUS_READWRITING = WAIT_STATUS_READING | WAIT_STATUS_WRITING + +BUF_SIZE = 65536 +DOUBLE_SEND_BEG_IDS = 16 +POST_MTU_MIN = 500 +POST_MTU_MAX = 1400 +SENDING_WINDOW_SIZE = 8192 + +STAGE_INIT = 0 +STAGE_RSP_ID = 1 +STAGE_DNS = 2 +STAGE_CONNECTING = 3 +STAGE_STREAM = 4 +STAGE_DESTROYED = -1 + +CMD_CONNECT = 0 +CMD_RSP_CONNECT = 1 +CMD_CONNECT_REMOTE = 2 +CMD_RSP_CONNECT_REMOTE = 3 +CMD_POST = 4 +CMD_SYN_STATUS = 5 +CMD_POST_64 = 6 +CMD_SYN_STATUS_64 = 7 +CMD_DISCONNECT = 8 + +CMD_VER_STR = b"\x08" + +RSP_STATE_EMPTY = b"" +RSP_STATE_REJECT = b"\x00" +RSP_STATE_CONNECTED = b"\x01" +RSP_STATE_CONNECTEDREMOTE = b"\x02" +RSP_STATE_ERROR = b"\x03" +RSP_STATE_DISCONNECT = b"\x04" +RSP_STATE_REDIRECT = b"\x05" + +def client_key(source_addr, server_af): + # notice this is server af, not dest af + return '%s:%s:%d' % (source_addr[0], source_addr[1], server_af) + +class UDPRelay(object): + def __init__(self, config, dns_resolver, is_local, stat_callback=None, stat_counter=None): + self._config = config + if config.get('connect_verbose_info', 0) > 0: + common.connect_log = logging.info + if is_local: + self._listen_addr = config['local_address'] + self._listen_port = config['local_port'] + self._remote_addr = config['server'] + self._remote_port = config['server_port'] + else: + self._listen_addr = config['server'] + self._listen_port = config['server_port'] + self._remote_addr = None + self._remote_port = None + self._dns_resolver = dns_resolver + self._password = common.to_bytes(config['password']) + self._method = config['method'] + self._timeout = config['timeout'] + self._is_local = is_local + self._udp_cache_size = config['udp_cache'] + self._cache = lru_cache.LRUCache(timeout=config['udp_timeout'], + close_callback=self._close_client_pair) + self._cache_dns_client = lru_cache.LRUCache(timeout=10, + close_callback=self._close_client_pair) + self._client_fd_to_server_addr = {} + #self._dns_cache = lru_cache.LRUCache(timeout=1800) + self._eventloop = None + self._closed = False + self.server_transfer_ul = 0 + self.server_transfer_dl = 0 + self.server_users = {} + self.server_user_transfer_ul = {} + self.server_user_transfer_dl = {} + + if common.to_bytes(config['protocol']) in obfs.mu_protocol(): + self._update_users(None, None) + + self.protocol_data = obfs.obfs(config['protocol']).init_data() + self._protocol = obfs.obfs(config['protocol']) + server_info = obfs.server_info(self.protocol_data) + server_info.host = self._listen_addr + server_info.port = self._listen_port + server_info.users = self.server_users + server_info.protocol_param = config['protocol_param'] + server_info.obfs_param = '' + server_info.iv = b'' + server_info.recv_iv = b'' + server_info.key_str = common.to_bytes(config['password']) + server_info.key = encrypt.encrypt_key(self._password, self._method) + server_info.head_len = 30 + server_info.tcp_mss = 1452 + server_info.buffer_size = BUF_SIZE + server_info.overhead = 0 + self._protocol.set_server_info(server_info) + + self._sockets = set() + self._fd_to_handlers = {} + self._reqid_to_hd = {} + self._data_to_write_to_server_socket = [] + + self._timeout_cache = lru_cache.LRUCache(timeout=self._timeout, + close_callback=self._close_tcp_client) + + self._bind = config.get('out_bind', '') + self._bindv6 = config.get('out_bindv6', '') + self._ignore_bind_list = config.get('ignore_bind', []) + + 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 + + addrs = socket.getaddrinfo(self._listen_addr, self._listen_port, 0, + socket.SOCK_DGRAM, socket.SOL_UDP) + if len(addrs) == 0: + raise Exception("can't get addrinfo for %s:%d" % + (self._listen_addr, self._listen_port)) + af, socktype, proto, canonname, sa = addrs[0] + server_socket = socket.socket(af, socktype, proto) + server_socket.bind((self._listen_addr, self._listen_port)) + server_socket.setblocking(False) + self._server_socket = server_socket + self._stat_callback = stat_callback + + def _get_a_server(self): + server = self._config['server'] + server_port = self._config['server_port'] + if type(server_port) == list: + server_port = random.choice(server_port) + if type(server) == list: + server = random.choice(server) + logging.debug('chosen server: %s:%d', server, server_port) + return server, server_port + + def get_ud(self): + return (self.server_transfer_ul, self.server_transfer_dl) + + def get_users_ud(self): + ret = (self.server_user_transfer_ul.copy(), self.server_user_transfer_dl.copy()) + return ret + + 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: + 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(' header_length + 13 and data[header_length + 4 : header_length + 12] == b"\x00\x01\x00\x00\x00\x00\x00\x00": + is_dns = True + else: + pass + if sa[1] == 53 and is_dns: #DNS + logging.debug("DNS query %s from %s:%d" % (common.to_str(sa[0]), r_addr[0], r_addr[1])) + self._cache_dns_client[key] = (client, uid) + else: + self._cache[key] = (client, uid) + self._client_fd_to_server_addr[client.fileno()] = (r_addr, af) + + self._sockets.add(client.fileno()) + self._eventloop.add(client, eventloop.POLL_IN, self) + + logging.debug('UDP port %5d sockets %d' % (self._listen_port, len(self._sockets))) + + if uid is not None: + user_id = struct.unpack(' 255: + # drop + return + data = pack_addr(r_addr[0]) + struct.pack('>H', r_addr[1]) + data + ref_iv = [encrypt.encrypt_new_iv(self._method)] + self._protocol.obfs.server_info.iv = ref_iv[0] + data = self._protocol.server_udp_pre_encrypt(data, client_uid) + response = encrypt.encrypt_all_iv(self._protocol.obfs.server_info.key, self._method, 1, + data, ref_iv) + if not response: + return + else: + ref_iv = [0] + data = encrypt.encrypt_all_iv(self._protocol.obfs.server_info.key, self._method, 0, + data, ref_iv) + if not data: + return + self._protocol.obfs.server_info.recv_iv = ref_iv[0] + data = self._protocol.client_udp_post_decrypt(data) + header_result = parse_header(data) + if header_result is None: + return + #connecttype, dest_addr, dest_port, header_length = header_result + #logging.debug('UDP handle_client %s:%d to %s:%d' % (common.to_str(r_addr[0]), r_addr[1], dest_addr, dest_port)) + + response = b'\x00\x00\x00' + data + + if client_addr: + if client_uid: + self.add_transfer_d(client_uid, len(response)) + else: + self.server_transfer_dl += len(response) + self.write_to_server_socket(response, client_addr[0]) + if client_dns_pair: + logging.debug("remove dns client %s:%d" % (client_addr[0][0], client_addr[0][1])) + del self._cache_dns_client[key] + self._close_client(client_dns_pair[0]) + else: + # this packet is from somewhere else we know + # simply drop that packet + pass + + def write_to_server_socket(self, data, addr): + uncomplete = False + retry = 0 + try: + self._server_socket.sendto(data, addr) + data = None + while self._data_to_write_to_server_socket: + data_buf = self._data_to_write_to_server_socket[0] + retry = data_buf[1] + 1 + del self._data_to_write_to_server_socket[0] + data, addr = data_buf[0] + self._server_socket.sendto(data, addr) + except (OSError, IOError) as e: + error_no = eventloop.errno_from_exception(e) + uncomplete = True + if error_no in (errno.EWOULDBLOCK,): + pass + else: + shell.print_exception(e) + return False + #if uncomplete and data is not None and retry < 3: + # self._data_to_write_to_server_socket.append([(data, addr), retry]) + #''' + + def add_to_loop(self, loop): + if self._eventloop: + raise Exception('already add to loop') + if self._closed: + raise Exception('already closed') + self._eventloop = loop + + server_socket = self._server_socket + self._eventloop.add(server_socket, + eventloop.POLL_IN | eventloop.POLL_ERR, self) + loop.add_periodic(self.handle_periodic) + + def remove_handler(self, client): + if hash(client) in self._timeout_cache: + del self._timeout_cache[hash(client)] + + def update_activity(self, client): + self._timeout_cache[hash(client)] = client + + def _sweep_timeout(self): + self._timeout_cache.sweep() + + def _close_tcp_client(self, client): + if client.remote_address: + logging.debug('timed out: %s:%d' % + client.remote_address) + else: + logging.debug('timed out') + client.destroy() + client.destroy_local() + + def handle_event(self, sock, fd, event): + if sock == self._server_socket: + if event & eventloop.POLL_ERR: + logging.error('UDP server_socket err') + try: + self._handle_server() + except Exception as e: + shell.print_exception(e) + if self._config['verbose']: + traceback.print_exc() + elif sock and (fd in self._sockets): + if event & eventloop.POLL_ERR: + logging.error('UDP client_socket err') + try: + self._handle_client(sock) + except Exception as e: + shell.print_exception(e) + if self._config['verbose']: + traceback.print_exc() + else: + if sock: + handler = self._fd_to_handlers.get(fd, None) + if handler: + handler.handle_event(sock, event) + else: + logging.warn('poll removed fd') + + def handle_periodic(self): + if self._closed: + self._cache.clear(0) + self._cache_dns_client.clear(0) + if self._eventloop: + self._eventloop.remove_periodic(self.handle_periodic) + self._eventloop.remove(self._server_socket) + if self._server_socket: + self._server_socket.close() + self._server_socket = None + logging.info('closed UDP port %d', self._listen_port) + else: + before_sweep_size = len(self._sockets) + self._cache.sweep() + self._cache_dns_client.sweep() + if before_sweep_size != len(self._sockets): + logging.debug('UDP port %5d sockets %d' % (self._listen_port, len(self._sockets))) + self._sweep_timeout() + + def close(self, next_tick=False): + logging.debug('UDP close') + self._closed = True + if not next_tick: + if self._eventloop: + self._eventloop.remove_periodic(self.handle_periodic) + self._eventloop.remove(self._server_socket) + self._server_socket.close() + self._cache.clear(0) + self._cache_dns_client.clear(0) diff --git a/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/version.py b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/version.py new file mode 100644 index 0000000..74eac22 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/shadowsocks/version.py @@ -0,0 +1,20 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# 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 +# 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. + +def version(): + return '3.3.3 2017-06-03' + diff --git a/shadowsocksr/3.3.3/shadowsocksr/switchrule.py b/shadowsocksr/3.3.3/shadowsocksr/switchrule.py new file mode 100644 index 0000000..6687e12 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/switchrule.py @@ -0,0 +1,8 @@ +def getKeys(key_list): + return key_list + #return key_list + ['plan'] # append the column name 'plan' + +def isTurnOn(row): + return True + #return row['plan'] == 'B' # then judge here + diff --git a/shadowsocksr/3.3.3/shadowsocksr/utils/README.md b/shadowsocksr/3.3.3/shadowsocksr/utils/README.md new file mode 100644 index 0000000..f624309 --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/utils/README.md @@ -0,0 +1,9 @@ +Useful Tools +=========== + +autoban.py +---------- + +Automatically ban IPs that try to brute force crack the server. + +See https://github.com/shadowsocks/shadowsocks/wiki/Ban-Brute-Force-Crackers diff --git a/shadowsocksr/3.3.3/shadowsocksr/utils/autoban.py b/shadowsocksr/3.3.3/shadowsocksr/utils/autoban.py new file mode 100755 index 0000000..1bbb65c --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/utils/autoban.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2015 clowwindy +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import absolute_import, division, print_function, \ + with_statement + +import os +import sys +import argparse + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='See README') + parser.add_argument('-c', '--count', default=3, type=int, + help='with how many failure times it should be ' + 'considered as an attack') + config = parser.parse_args() + ips = {} + banned = set() + for line in sys.stdin: + if 'can not parse header when' in line: + ip = line.split()[-1].split(':')[0] + if ip not in ips: + ips[ip] = 1 + print(ip) + sys.stdout.flush() + else: + ips[ip] += 1 + if ip not in banned and ips[ip] >= config.count: + banned.add(ip) + cmd = 'iptables -A INPUT -s %s -j DROP' % ip + print(cmd, file=sys.stderr) + sys.stderr.flush() + os.system(cmd) diff --git a/shadowsocksr/3.3.3/shadowsocksr/utils/fail2ban/shadowsocks.conf b/shadowsocksr/3.3.3/shadowsocksr/utils/fail2ban/shadowsocks.conf new file mode 100644 index 0000000..9b1c7ec --- /dev/null +++ b/shadowsocksr/3.3.3/shadowsocksr/utils/fail2ban/shadowsocks.conf @@ -0,0 +1,5 @@ +[Definition] + +_daemon = shadowsocks + +failregex = ^\s+ERROR\s+can not parse header when handling connection from :\d+$