From 134497c24ffc4a62452cd839f50bc6e4bfab885c Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 21 Dec 2014 12:47:07 +0800 Subject: [PATCH 001/146] implement daemon --- shadowsocks/daemon.py | 173 ++++++++++++++++++++++++++++++++++++++++++ shadowsocks/local.py | 5 +- shadowsocks/server.py | 5 +- shadowsocks/utils.py | 93 ++++++++++++++--------- 4 files changed, 240 insertions(+), 36 deletions(-) create mode 100644 shadowsocks/daemon.py diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py new file mode 100644 index 0000000..d4320e0 --- /dev/null +++ b/shadowsocks/daemon.py @@ -0,0 +1,173 @@ +#!/usr/bin/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. + +from __future__ import absolute_import, division, print_function, \ + with_statement + +import os +import sys +import logging +import signal +import time +from shadowsocks import common + +# this module is ported from ShadowVPN daemon.c + + +def daemon_exec(config): + if 'daemon' in config: + if os.name != 'posix': + raise Exception('daemon mode is only supported in unix') + command = config['daemon'] + if not command: + command = 'start' + pid_file = config['pid-file'] + log_file = config['log-file'] + command = common.to_str(command) + pid_file = common.to_str(pid_file) + log_file = common.to_str(log_file) + if command == 'start': + daemon_start(pid_file, log_file) + elif command == 'stop': + daemon_stop(pid_file) + # always exit after daemon_stop + sys.exit(0) + elif command == 'restart': + daemon_stop(pid_file) + daemon_start(pid_file, log_file) + else: + raise Exception('unsupported daemon command %s' % command) + + +def write_pid_file(pid_file, pid): + import fcntl + import stat + + try: + fd = os.open(pid_file, os.O_RDWR | os.O_CREAT, + stat.S_IRUSR | stat.S_IWUSR) + except OSError as e: + logging.error(e) + return -1 + flags = fcntl.fcntl(fd, fcntl.F_GETFD) + assert flags != -1 + flags |= fcntl.FD_CLOEXEC + r = fcntl.fcntl(fd, fcntl.F_SETFD, flags) + assert r != -1 + # There is no platform independent way to implement fcntl(fd, F_SETLK, &fl) + # via fcntl.fcntl. So use lockf instead + try: + fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB, 0, 0, os.SEEK_SET) + except IOError: + r = os.read(fd, 32) + if r: + logging.error('already started at pid %s' % common.to_str(r)) + else: + logging.error('already started') + os.close(fd) + return -1 + os.ftruncate(fd, 0) + os.write(fd, common.to_bytes(str(pid))) + return 0 + + +def freopen(f, mode, stream): + oldf = open(f, mode) + oldfd = oldf.fileno() + newfd = stream.fileno() + os.close(newfd) + os.dup2(oldfd, newfd) + + +def daemon_start(pid_file, log_file): + # fork only once because we are sure parent will exit + pid = os.fork() + assert pid != -1 + + def handle_exit(signum, _): + sys.exit(0) + + if pid > 0: + # parent waits for its child + signal.signal(signal.SIGINT, handle_exit) + 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) + + print('started') + os.kill(ppid, signal.SIGINT) + + sys.stdin.close() + freopen(log_file, 'a', sys.stdout) + freopen(log_file, 'a', sys.stderr) + + +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: + logging.error(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 + logging.error(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) diff --git a/shadowsocks/local.py b/shadowsocks/local.py index e778ea7..6a97f07 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -30,7 +30,8 @@ import logging import signal sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) -from shadowsocks import utils, encrypt, eventloop, tcprelay, udprelay, asyncdns +from shadowsocks import utils, daemon, encrypt, eventloop, tcprelay, udprelay,\ + asyncdns def main(): @@ -44,6 +45,8 @@ def main(): config = utils.get_config(True) + daemon.daemon_exec(config) + utils.print_shadowsocks() encrypt.try_cipher(config['password'], config['method']) diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 9abdf9c..e7acc5e 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -30,7 +30,8 @@ import logging import signal sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) -from shadowsocks import utils, encrypt, eventloop, tcprelay, udprelay, asyncdns +from shadowsocks import utils, daemon, encrypt, eventloop, tcprelay, udprelay,\ + asyncdns def main(): @@ -38,6 +39,8 @@ def main(): config = utils.get_config(False) + daemon.daemon_exec(config) + utils.print_shadowsocks() if config['port_password']: diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 7808d8f..7caa243 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -70,9 +70,9 @@ def find_config(): def check_config(config): if config.get('local_address', '') in [b'0.0.0.0']: - logging.warn('warning: local set to listen 0.0.0.0, which is not safe') + logging.warn('warning: local set to listen on 0.0.0.0, it\'s not safe') if config.get('server', '') in [b'127.0.0.1', b'localhost']: - logging.warn('warning: server set to listen %s:%s, are you sure?' % + logging.warn('warning: server set to listen on %s:%s, are you sure?' % (config['server'], config['server_port'])) if (config.get('method', '') or '').lower() == b'table': logging.warn('warning: table is not safe; please use a safer cipher, ' @@ -96,11 +96,11 @@ def get_config(is_local): logging.basicConfig(level=logging.INFO, format='%(levelname)-s: %(message)s') if is_local: - shortopts = 'hs:b:p:k:l:m:c:t:vq' - longopts = ['fast-open'] + shortopts = 'hd:s:b:p:k:l:m:c:t:vq' + longopts = ['help', 'fast-open', 'pid-file=', 'log-file='] else: - shortopts = 'hs:p:k:m:c:t:vq' - longopts = ['fast-open', 'workers='] + shortopts = 'hd:s:p:k:m:c:t:vq' + longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers='] try: config_path = find_config() optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts) @@ -146,12 +146,18 @@ def get_config(is_local): config['fast_open'] = True elif key == '--workers': config['workers'] = int(value) - elif key == '-h': + elif key in ('-h', '--help'): if is_local: print_local_help() else: print_server_help() sys.exit(0) + elif key == '-d': + config['daemon'] = value + elif key == '--pid-file': + config['pid-file'] = value + elif key == '--log-file': + config['log-file'] = value elif key == '-q': v_count -= 1 config['verbose'] = v_count @@ -171,6 +177,9 @@ def get_config(is_local): config['timeout'] = int(config.get('timeout', 300)) config['fast_open'] = config.get('fast_open', False) config['workers'] = config.get('workers', 1) + config['pid-file'] = config.get('pid-file', '/var/run/shadowsocks.pid') + config['log-file'] = config.get('log-file', '/var/log/shadowsocks.log') + config['workers'] = config.get('workers', 1) config['verbose'] = config.get('verbose', False) config['local_address'] = config.get('local_address', '127.0.0.1') config['local_port'] = config.get('local_port', 1080) @@ -231,21 +240,29 @@ def print_help(is_local): def print_local_help(): print('''usage: sslocal [-h] -s SERVER_ADDR [-p SERVER_PORT] [-b LOCAL_ADDR] [-l LOCAL_PORT] -k PASSWORD [-m METHOD] - [-t TIMEOUT] [-c CONFIG] [--fast-open] [-v] [-q] + [-t TIMEOUT] [-c CONFIG] [--fast-open] [-v] -[d] [-q] +A fast tunnel proxy that helps you bypass firewalls. -optional arguments: - -h, --help show this help message and exit - -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 - -t TIMEOUT timeout in seconds, default: 300 - -c CONFIG path to config file - --fast-open use TCP_FASTOPEN, requires Linux 3.7+ - -v, -vv verbose mode - -q, -qq quiet mode, only show warnings/errors +You can supply configurations via either config file or command line arguments. + +Proxy options: + -h, --help show this help message and exit + -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 + -t TIMEOUT timeout in seconds, default: 300 + --fast-open use TCP_FASTOPEN, requires Linux 3.7+ + +General options: + -d start/stop/restart daemon mode + --pid-file PID_FILE pid file for daemon mode + --log-file LOG_FILE log file for daemon mode + -v, -vv verbose mode + -q, -qq quiet mode, only show warnings/errors Online help: ''') @@ -254,20 +271,28 @@ Online help: def print_server_help(): print('''usage: ssserver [-h] [-s SERVER_ADDR] [-p SERVER_PORT] -k PASSWORD -m METHOD [-t TIMEOUT] [-c CONFIG] [--fast-open] - [--workers WORKERS] [-v] [-q] + [--workers WORKERS] [-v] [-d start] [-q] +A fast tunnel proxy that helps you bypass firewalls. -optional arguments: - -h, --help show this help message and exit - -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 - -t TIMEOUT timeout in seconds, default: 300 - -c CONFIG path to config file - --fast-open use TCP_FASTOPEN, requires Linux 3.7+ - --workers WORKERS number of workers, available on Unix/Linux - -v, -vv verbose mode - -q, -qq quiet mode, only show warnings/errors +You can supply configurations via either config file or command line arguments. + +Proxy options: + -h, --help show this help message and exit + -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 + -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 + +General options: + -d start/stop/restart daemon mode + --pid-file PID_FILE pid file for daemon mode + --log-file LOG_FILE log file for daemon mode + -v, -vv verbose mode + -q, -qq quiet mode, only show warnings/errors Online help: ''') From 28347b685e3e38fdf68aa88732f77e840d4e5c2d Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 21 Dec 2014 13:46:27 +0800 Subject: [PATCH 002/146] add unit test for daemon --- .travis.yml | 1 + shadowsocks/daemon.py | 16 ++++++++++++---- tests/test_daemon.sh | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100755 tests/test_daemon.sh diff --git a/.travis.yml b/.travis.yml index 9d7a9bb..1ceac69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ script: - pyflakes . - coverage run tests/nose_plugin.py -v - python setup.py sdist + - tests/test_daemon.sh - python tests/test.py --with-coverage -c tests/aes.json - python tests/test.py --with-coverage -c tests/aes-ctr.json - python tests/test.py --with-coverage -c tests/aes-cfb1.json diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index d4320e0..2eedf03 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -105,11 +105,14 @@ def daemon_start(pid_file, log_file): assert pid != -1 def handle_exit(signum, _): - sys.exit(0) + if signum == signal.SIGTERM: + sys.exit(0) + sys.exit(1) if pid > 0: # parent waits for its child signal.signal(signal.SIGINT, handle_exit) + signal.signal(signal.SIGTERM, handle_exit) time.sleep(5) sys.exit(0) @@ -121,11 +124,16 @@ def daemon_start(pid_file, log_file): sys.exit(1) print('started') - os.kill(ppid, signal.SIGINT) + os.kill(ppid, signal.SIGTERM) sys.stdin.close() - freopen(log_file, 'a', sys.stdout) - freopen(log_file, 'a', sys.stderr) + try: + freopen(log_file, 'a', sys.stdout) + freopen(log_file, 'a', sys.stderr) + except IOError as e: + logging.error(e) + os.kill(ppid, signal.SIGINT) + sys.exit(1) def daemon_stop(pid_file): diff --git a/tests/test_daemon.sh b/tests/test_daemon.sh new file mode 100755 index 0000000..b05208d --- /dev/null +++ b/tests/test_daemon.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +function test { + expected=$1 + shift + echo "running test: $command $@" + $command $@ + status=$? + if [ $status -ne $expected ]; then + echo "exit $status != $expected" + exit 1 + fi + echo "exit status $status == $expected" + echo OK + return +} + +for module in local server +do + +command="coverage run -p -a shadowsocks/$module.py" + +test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log + +test 0 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log + +test 0 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +test 1 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log + +test 0 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +test 0 -c tests/aes.json -d restart --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log + +test 0 -c tests/aes.json -d restart --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log + +test 1 -c tests/aes.json -d start --pid-file /tmp/not_exist/shadowsocks.pid --log-file /tmp/shadowsocks.log +test 1 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/not_exist/shadowsocks.log + +done From 6dbabdea9d70ebbf7f662105c65198f1c4bbade9 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 21 Dec 2014 13:54:01 +0800 Subject: [PATCH 003/146] bump --- CHANGES | 3 +++ setup.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 4c07066..3662f50 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +2.6 2014-12-21 +- Add daemon support + 2.5 2014-12-11 - Add salsa20 and chacha20 diff --git a/setup.py b/setup.py index 5103c70..224cb21 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with codecs.open('README.rst', encoding='utf-8') as f: setup( name="shadowsocks", - version="2.5", + version="2.6", license='MIT', description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', From 35c3b0b41c96aa1cfc53bb25f5c040562707b783 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 21 Dec 2014 14:06:28 +0800 Subject: [PATCH 004/146] refine documentation --- README.md | 43 ++++++++++++++--------------------- README.rst | 52 +++++++++++++++++-------------------------- shadowsocks/daemon.py | 2 +- 3 files changed, 39 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 7ee4bea..5fa677e 100644 --- a/README.md +++ b/README.md @@ -56,8 +56,7 @@ Example: "password":"mypassword", "timeout":300, "method":"aes-256-cfb", - "fast_open": false, - "workers": 1 + "fast_open": false } Explanation of the fields: @@ -74,40 +73,35 @@ Explanation of the fields: | fast_open | use [TCP_FASTOPEN], true / false | | workers | number of workers, available on Unix/Linux | -Run `ssserver -c /etc/shadowsocks.json` on your server. To run it in the -background, use [Supervisor]. +On your server: -On your client machine, use the same configuration as your server, and -start your client. +To run in the foreground: -If you use Chrome, it's recommended to use [SwitchySharp]. Change the proxy -settings to + ssserver -c /etc/shadowsocks.json - protocol: socks5 - hostname: 127.0.0.1 - port: your local_port +To run in the background: -If you can't install [SwitchySharp], you can launch Chrome with the following -arguments to force Chrome to use the proxy: + sudo ssserver -c /etc/shadowsocks.json -d start + sudo ssserver -c /etc/shadowsocks.json -d stop - Chrome.exe --proxy-server="socks5://127.0.0.1:1080" --host-resolver-rules="MAP * 0.0.0.0 , EXCLUDE localhost" +On your client machine, use the same configuration as your server. Check the +README of your client for more information. -If you can't even download Chrome, find a friend to download a -[Chrome Standalone] installer for you. +Command Line Options +-------------------- -Command line args ------------------- - -You can use args to override settings from `config.json`. +Check the options via `-h`.You can use args to override settings from +`config.json`. sslocal -s server_name -p server_port -l local_port -k password -m bf-cfb ssserver -p server_port -k password -m bf-cfb --workers 2 - ssserver -c /etc/shadowsocks/config.json + ssserver -c /etc/shadowsocks/config.json -d start --pid-file=/tmp/shadowsocks.pid + ssserver -c /etc/shadowsocks/config.json -d stop --pid-file=/tmp/shadowsocks.pid List all available args with `-h`. -Wiki ----- +Documentation +------------- You can find all the documentation in the wiki: https://github.com/clowwindy/shadowsocks/wiki @@ -127,7 +121,6 @@ Bugs and Issues [Android]: https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#android [Build Status]: https://img.shields.io/travis/clowwindy/shadowsocks/master.svg?style=flat [Chinese Readme]: https://github.com/clowwindy/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E -[Chrome Standalone]: https://support.google.com/installer/answer/126299 [Debian sid]: https://packages.debian.org/unstable/python/shadowsocks [the package]: https://pypi.python.org/pypi/shadowsocks [Encryption]: https://github.com/clowwindy/shadowsocks/wiki/Encryption @@ -139,9 +132,7 @@ Bugs and Issues [OS X]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Shadowsocks-for-OSX-Help [PyPI]: https://pypi.python.org/pypi/shadowsocks [PyPI version]: https://img.shields.io/pypi/v/shadowsocks.svg?style=flat -[Supervisor]: https://github.com/clowwindy/shadowsocks/wiki/Configure-Shadowsocks-with-Supervisor [TCP_FASTOPEN]: https://github.com/clowwindy/shadowsocks/wiki/TCP-Fast-Open [Travis CI]: https://travis-ci.org/clowwindy/shadowsocks [Troubleshooting]: https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting -[SwitchySharp]: https://chrome.google.com/webstore/detail/proxy-switchysharp/dpplabbmogkhghncfbfdeeokoefdjegm [Windows]: https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#windows diff --git a/README.rst b/README.rst index 19d5b2a..fab5e1f 100644 --- a/README.rst +++ b/README.rst @@ -50,9 +50,10 @@ CentOS: Windows: ^^^^^^^^ -Download OpenSSL for Windows and install. Then install shadowsocks via -easy\_install and pip as Linux. If you don't know how to use them, you -can directly download `the +Download `OpenSSL for +Windows `__ and install. +Then install shadowsocks via easy\_install and pip as Linux. If you +don't know how to use them, you can directly download `the package `__, and use ``python shadowsocks/server.py`` instead of ``ssserver`` command below. @@ -71,8 +72,7 @@ On your server create a config file ``/etc/shadowsocks.json``. Example: "password":"mypassword", "timeout":300, "method":"aes-256-cfb", - "fast_open": false, - "workers": 1 + "fast_open": false } Explanation of the fields: @@ -99,51 +99,41 @@ Explanation of the fields: | workers | number of workers, available on Unix/Linux | +------------------+---------------------------------------------------------------------------------------------------------+ -Run ``ssserver -c /etc/shadowsocks.json`` on your server. To run it in -the background, use -`Supervisor `__. +On your server: -On your client machine, use the same configuration as your server, and -start your client. - -If you use Chrome, it's recommended to use -`SwitchySharp `__. -Change the proxy settings to +To run in the foreground: :: - protocol: socks5 - hostname: 127.0.0.1 - port: your local_port + ssserver -c /etc/shadowsocks.json -If you can't install -`SwitchySharp `__, -you can launch Chrome with the following arguments to force Chrome to -use the proxy: +To run in the background: :: - Chrome.exe --proxy-server="socks5://127.0.0.1:1080" --host-resolver-rules="MAP * 0.0.0.0 , EXCLUDE localhost" + sudo ssserver -c /etc/shadowsocks.json -d start + sudo ssserver -c /etc/shadowsocks.json -d stop -If you can't even download Chrome, find a friend to download a `Chrome -Standalone `__ -installer for you. +On your client machine, use the same configuration as your server. Check +the README of your client for more information. -Command line args ------------------ +Command Line Options +-------------------- -You can use args to override settings from ``config.json``. +Check the options via ``-h``.You can use args to override settings from +``config.json``. :: sslocal -s server_name -p server_port -l local_port -k password -m bf-cfb ssserver -p server_port -k password -m bf-cfb --workers 2 - ssserver -c /etc/shadowsocks/config.json + ssserver -c /etc/shadowsocks/config.json -d start --pid-file=/tmp/pid + ssserver -c /etc/shadowsocks/config.json -d stop --pid-file=/tmp/pid List all available args with ``-h``. -Wiki ----- +Documentation +------------- You can find all the documentation in the wiki: https://github.com/clowwindy/shadowsocks/wiki diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index 2eedf03..d694e94 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -37,7 +37,7 @@ from shadowsocks import common def daemon_exec(config): if 'daemon' in config: if os.name != 'posix': - raise Exception('daemon mode is only supported in unix') + raise Exception('daemon mode is only supported on Unix') command = config['daemon'] if not command: command = 'start' From 2733e6a4ba1f2e8c312d06103c9f425ab25c5ad1 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 21 Dec 2014 14:08:11 +0800 Subject: [PATCH 005/146] update readme --- README.md | 2 -- README.rst | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5fa677e..9489eed 100644 --- a/README.md +++ b/README.md @@ -98,8 +98,6 @@ Check the options via `-h`.You can use args to override settings from ssserver -c /etc/shadowsocks/config.json -d start --pid-file=/tmp/shadowsocks.pid ssserver -c /etc/shadowsocks/config.json -d stop --pid-file=/tmp/shadowsocks.pid -List all available args with `-h`. - Documentation ------------- diff --git a/README.rst b/README.rst index fab5e1f..a586f2a 100644 --- a/README.rst +++ b/README.rst @@ -127,10 +127,8 @@ Check the options via ``-h``.You can use args to override settings from sslocal -s server_name -p server_port -l local_port -k password -m bf-cfb ssserver -p server_port -k password -m bf-cfb --workers 2 - ssserver -c /etc/shadowsocks/config.json -d start --pid-file=/tmp/pid - ssserver -c /etc/shadowsocks/config.json -d stop --pid-file=/tmp/pid - -List all available args with ``-h``. + ssserver -c /etc/shadowsocks/config.json -d start --pid-file=/tmp/shadowsocks.pid + ssserver -c /etc/shadowsocks/config.json -d stop --pid-file=/tmp/shadowsocks.pid Documentation ------------- From fb789d8e9fd574ad9d48662f8b5afdc5a745b1fb Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 21 Dec 2014 14:09:07 +0800 Subject: [PATCH 006/146] update readme --- README.md | 4 ++-- README.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9489eed..7a37b26 100644 --- a/README.md +++ b/README.md @@ -81,8 +81,8 @@ To run in the foreground: To run in the background: - sudo ssserver -c /etc/shadowsocks.json -d start - sudo ssserver -c /etc/shadowsocks.json -d stop + ssserver -c /etc/shadowsocks.json -d start + ssserver -c /etc/shadowsocks.json -d stop On your client machine, use the same configuration as your server. Check the README of your client for more information. diff --git a/README.rst b/README.rst index a586f2a..003b358 100644 --- a/README.rst +++ b/README.rst @@ -111,8 +111,8 @@ To run in the background: :: - sudo ssserver -c /etc/shadowsocks.json -d start - sudo ssserver -c /etc/shadowsocks.json -d stop + ssserver -c /etc/shadowsocks.json -d start + ssserver -c /etc/shadowsocks.json -d stop On your client machine, use the same configuration as your server. Check the README of your client for more information. From dae2624b307e083bd9fbd70b7bf3a71e47ca3317 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 21 Dec 2014 14:49:51 +0800 Subject: [PATCH 007/146] add setsid --- shadowsocks/daemon.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index d694e94..9e252e0 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -126,6 +126,9 @@ def daemon_start(pid_file, log_file): print('started') os.kill(ppid, signal.SIGTERM) + os.setsid() + signal.signal(signal.SIG_IGN, signal.SIGHUP) + sys.stdin.close() try: freopen(log_file, 'a', sys.stdout) From 2b4c3619d6fd29cf80c203454eaee659b8c301ea Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 21 Dec 2014 14:52:00 +0800 Subject: [PATCH 008/146] reorder setsid and kill --- shadowsocks/daemon.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index 9e252e0..ec6676c 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -123,12 +123,12 @@ def daemon_start(pid_file, log_file): os.kill(ppid, signal.SIGINT) sys.exit(1) - print('started') - os.kill(ppid, signal.SIGTERM) - 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) From be2ab378ffd8c4e234b42cf7602d764ef6ef5398 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 22 Dec 2014 16:33:23 +0800 Subject: [PATCH 009/146] add jenkins --- .gitignore | 1 + .jenkins.sh | 53 ++++++++++++++++++++++++++++++++++++++++++++ .travis.yml | 22 +----------------- tests/test_daemon.sh | 28 +++++++++++------------ 4 files changed, 69 insertions(+), 35 deletions(-) create mode 100755 .jenkins.sh diff --git a/.gitignore b/.gitignore index 357232f..fb96264 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ develop-eggs pip-log.txt # Unit test / coverage reports +htmlcov .coverage .tox diff --git a/.jenkins.sh b/.jenkins.sh new file mode 100755 index 0000000..1206b00 --- /dev/null +++ b/.jenkins.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +result=0 + +function run_test { + printf '\e[0;36m' + echo "running test: $command $@" + printf '\e[0m' + + $command $@ + status=$? + if [ $status -ne 0 ]; then + printf '\e[0;31m' + echo "test failed: $command $@" + printf '\e[0m' + echo + result=1 + else + printf '\e[0;32m' + echo OK + printf '\e[0m' + echo + fi + return 0 +} + +coverage erase +run_test pep8 . +run_test pyflakes . +run_test coverage run tests/nose_plugin.py -v +run_test python setup.py sdist +run_test tests/test_daemon.sh +run_test python tests/test.py --with-coverage -c tests/aes.json +run_test python tests/test.py --with-coverage -c tests/aes-ctr.json +run_test python tests/test.py --with-coverage -c tests/aes-cfb1.json +run_test python tests/test.py --with-coverage -c tests/aes-cfb8.json +run_test python tests/test.py --with-coverage -c tests/rc4-md5.json +run_test python tests/test.py --with-coverage -c tests/salsa20.json +run_test python tests/test.py --with-coverage -c tests/chacha20.json +run_test python tests/test.py --with-coverage -c tests/salsa20-ctr.json +run_test python tests/test.py --with-coverage -c tests/table.json +run_test python tests/test.py --with-coverage -c tests/server-multi-ports.json +run_test python tests/test.py --with-coverage -s tests/server-multi-passwd.json -c tests/server-multi-passwd-client-side.json +run_test python tests/test.py --with-coverage -c tests/workers.json +run_test python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-client-side.json +run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388" -a "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -l 1081" +run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081" +coverage combine && coverage report --include=shadowsocks/* +coverage combine && coverage report --include=shadowsocks/* +rm -rf htmlcov +coverage html --include=shadowsocks/* + +exit $result diff --git a/.travis.yml b/.travis.yml index 1ceac69..3b094f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,24 +14,4 @@ before_install: - sudo tests/socksify/install.sh - sudo tests/libsodium/install.sh script: - - pep8 . - - pyflakes . - - coverage run tests/nose_plugin.py -v - - python setup.py sdist - - tests/test_daemon.sh - - python tests/test.py --with-coverage -c tests/aes.json - - python tests/test.py --with-coverage -c tests/aes-ctr.json - - python tests/test.py --with-coverage -c tests/aes-cfb1.json - - python tests/test.py --with-coverage -c tests/aes-cfb8.json - - python tests/test.py --with-coverage -c tests/rc4-md5.json - - python tests/test.py --with-coverage -c tests/salsa20.json - - python tests/test.py --with-coverage -c tests/chacha20.json - - python tests/test.py --with-coverage -c tests/salsa20-ctr.json - - python tests/test.py --with-coverage -c tests/table.json - - python tests/test.py --with-coverage -c tests/server-multi-ports.json - - python tests/test.py --with-coverage -s tests/server-multi-passwd.json -c tests/server-multi-passwd-client-side.json - - python tests/test.py --with-coverage -c tests/workers.json - - python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-client-side.json - - python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388" -a "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -l 1081" - - python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081" - - coverage combine && coverage report --include=shadowsocks/* + - ./.jenkins.sh diff --git a/tests/test_daemon.sh b/tests/test_daemon.sh index b05208d..fdfe517 100755 --- a/tests/test_daemon.sh +++ b/tests/test_daemon.sh @@ -1,6 +1,6 @@ #!/bin/bash -function test { +function run_test { expected=$1 shift echo "running test: $command $@" @@ -20,23 +20,23 @@ do command="coverage run -p -a shadowsocks/$module.py" -test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -test 0 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -test 0 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -test 1 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 1 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -test 0 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -test 0 -c tests/aes.json -d restart --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d restart --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -test 0 -c tests/aes.json -d restart --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d restart --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -test 1 -c tests/aes.json -d start --pid-file /tmp/not_exist/shadowsocks.pid --log-file /tmp/shadowsocks.log -test 1 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/not_exist/shadowsocks.log +run_test 1 -c tests/aes.json -d start --pid-file /tmp/not_exist/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 1 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/not_exist/shadowsocks.log done From c7b5a5a0110e8090a226e8af329c57bfb8af4cc2 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 22 Dec 2014 16:45:46 +0800 Subject: [PATCH 010/146] fix ci --- .jenkins.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.jenkins.sh b/.jenkins.sh index 1206b00..256fe38 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -7,7 +7,7 @@ function run_test { echo "running test: $command $@" printf '\e[0m' - $command $@ + $command "$@" status=$? if [ $status -ne 0 ]; then printf '\e[0;31m' @@ -46,7 +46,6 @@ run_test python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-c run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388" -a "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -l 1081" run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081" coverage combine && coverage report --include=shadowsocks/* -coverage combine && coverage report --include=shadowsocks/* rm -rf htmlcov coverage html --include=shadowsocks/* From 536b7d1ee637f9fbc78eb923656838cb3219673e Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 22 Dec 2014 17:09:37 +0800 Subject: [PATCH 011/146] use SIGINT instead in tests Conflicts: tests/test.py --- shadowsocks/local.py | 5 +++++ shadowsocks/server.py | 5 +++++ tests/test.py | 4 +++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/shadowsocks/local.py b/shadowsocks/local.py index 6a97f07..994b6d8 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -68,6 +68,11 @@ def main(): 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) + loop.run() except (KeyboardInterrupt, IOError, OSError) as e: logging.error(e) diff --git a/shadowsocks/server.py b/shadowsocks/server.py index e7acc5e..c5a00ca 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -77,6 +77,11 @@ def main(): 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) diff --git a/tests/test.py b/tests/test.py index 5314d6e..721d484 100755 --- a/tests/test.py +++ b/tests/test.py @@ -138,7 +138,9 @@ try: finally: for p in [p1, p2]: try: - os.kill(p.pid, signal.SIGQUIT) + print('kill', file=sys.stderr) + os.kill(p.pid, signal.SIGINT) + print('waitpid', file=sys.stderr) os.waitpid(p.pid, 0) except OSError: pass From be1d1d50323f0f67dc58e33dc76e24b22907e1a1 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 22 Dec 2014 17:29:58 +0800 Subject: [PATCH 012/146] add SIGINT in workers --- shadowsocks/server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/shadowsocks/server.py b/shadowsocks/server.py index c5a00ca..8eed4ad 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -118,6 +118,7 @@ def main(): 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: From 072afd68f2a8a98fe6d6706dafb7ff1f7093a602 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 22 Dec 2014 17:39:52 +0800 Subject: [PATCH 013/146] use local tmp dir --- .jenkins.sh | 2 ++ tests/test_daemon.sh | 28 +++++++++++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/.jenkins.sh b/.jenkins.sh index 256fe38..a67f606 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -25,6 +25,7 @@ function run_test { } coverage erase +mkdir tmp run_test pep8 . run_test pyflakes . run_test coverage run tests/nose_plugin.py -v @@ -47,6 +48,7 @@ run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0 run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081" coverage combine && coverage report --include=shadowsocks/* rm -rf htmlcov +rm -rf tmp coverage html --include=shadowsocks/* exit $result diff --git a/tests/test_daemon.sh b/tests/test_daemon.sh index fdfe517..02c6cf0 100755 --- a/tests/test_daemon.sh +++ b/tests/test_daemon.sh @@ -20,23 +20,25 @@ do command="coverage run -p -a shadowsocks/$module.py" -run_test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +mkdir -p tmp -run_test 0 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -run_test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d stop --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log -run_test 0 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -run_test 1 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -run_test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d start --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d stop --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log -run_test 0 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -run_test 0 -c tests/aes.json -d restart --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -run_test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d start --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log +run_test 1 -c tests/aes.json -d start --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d stop --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log -run_test 0 -c tests/aes.json -d restart --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log -run_test 0 -c tests/aes.json -d stop --pid-file /tmp/shadowsocks.pid --log-file /tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d start --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d restart --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d stop --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log -run_test 1 -c tests/aes.json -d start --pid-file /tmp/not_exist/shadowsocks.pid --log-file /tmp/shadowsocks.log -run_test 1 -c tests/aes.json -d start --pid-file /tmp/shadowsocks.pid --log-file /tmp/not_exist/shadowsocks.log +run_test 0 -c tests/aes.json -d restart --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log +run_test 0 -c tests/aes.json -d stop --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log + +run_test 1 -c tests/aes.json -d start --pid-file tmp/not_exist/shadowsocks.pid --log-file tmp/shadowsocks.log +run_test 1 -c tests/aes.json -d start --pid-file tmp/shadowsocks.pid --log-file tmp/not_exist/shadowsocks.log done From 5ea8403e56043ea0ad50efbd8c255d32119a1b59 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 22 Dec 2014 17:58:12 +0800 Subject: [PATCH 014/146] fix daemon and add fastopen tests --- .jenkins.sh | 7 +++++++ shadowsocks/daemon.py | 13 +++++++------ tests/test.py | 2 -- tests/test_daemon.sh | 1 - 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.jenkins.sh b/.jenkins.sh index a67f606..a8bdf15 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -46,6 +46,13 @@ run_test python tests/test.py --with-coverage -c tests/workers.json run_test python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-client-side.json run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388" -a "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -l 1081" run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081" + +if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then + if [ 3 -eq `cat /proc/sys/net/ipv4/tcp_fastopen` ] ; then + run_test python tests/test.py --with-coverage -c tests/fastopen.json + fi +fi + coverage combine && coverage report --include=shadowsocks/* rm -rf htmlcov rm -rf tmp diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index ec6676c..d206ccf 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -100,19 +100,21 @@ def freopen(f, mode, stream): def daemon_start(pid_file, log_file): - # fork only once because we are sure parent will exit - pid = os.fork() - assert pid != -1 def handle_exit(signum, _): if signum == signal.SIGTERM: sys.exit(0) sys.exit(1) + signal.signal(signal.SIGINT, handle_exit) + signal.signal(signal.SIGTERM, handle_exit) + + # fork only once because we are sure parent will exit + pid = os.fork() + assert pid != -1 + if pid > 0: # parent waits for its child - signal.signal(signal.SIGINT, handle_exit) - signal.signal(signal.SIGTERM, handle_exit) time.sleep(5) sys.exit(0) @@ -135,7 +137,6 @@ def daemon_start(pid_file, log_file): freopen(log_file, 'a', sys.stderr) except IOError as e: logging.error(e) - os.kill(ppid, signal.SIGINT) sys.exit(1) diff --git a/tests/test.py b/tests/test.py index 721d484..0b63a18 100755 --- a/tests/test.py +++ b/tests/test.py @@ -138,9 +138,7 @@ try: finally: for p in [p1, p2]: try: - print('kill', file=sys.stderr) os.kill(p.pid, signal.SIGINT) - print('waitpid', file=sys.stderr) os.waitpid(p.pid, 0) except OSError: pass diff --git a/tests/test_daemon.sh b/tests/test_daemon.sh index 02c6cf0..40f35ef 100755 --- a/tests/test_daemon.sh +++ b/tests/test_daemon.sh @@ -39,6 +39,5 @@ run_test 0 -c tests/aes.json -d restart --pid-file tmp/shadowsocks.pid --log-fil run_test 0 -c tests/aes.json -d stop --pid-file tmp/shadowsocks.pid --log-file tmp/shadowsocks.log run_test 1 -c tests/aes.json -d start --pid-file tmp/not_exist/shadowsocks.pid --log-file tmp/shadowsocks.log -run_test 1 -c tests/aes.json -d start --pid-file tmp/shadowsocks.pid --log-file tmp/not_exist/shadowsocks.log done From f1b084be060f5a3129e7fff4fab2c6aee7696ebf Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 23 Dec 2014 13:09:51 +0800 Subject: [PATCH 015/146] add large file test --- .jenkins.sh | 2 ++ .travis.yml | 4 +++- tests/test_large_file.sh | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100755 tests/test_large_file.sh diff --git a/.jenkins.sh b/.jenkins.sh index a8bdf15..bae7f79 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -53,6 +53,8 @@ if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then fi fi +run_test tests/test_large_file.sh + coverage combine && coverage report --include=shadowsocks/* rm -rf htmlcov rm -rf tmp diff --git a/.travis.yml b/.travis.yml index 3b094f2..2f9bd13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,9 @@ cache: - dante-1.4.0 before_install: - sudo apt-get update -qq - - sudo apt-get install -qq build-essential libssl-dev swig python-m2crypto python-numpy dnsutils + - sudo apt-get install -qq build-essential libssl-dev swig python-m2crypto python-numpy dnsutils iproute nginx + - sudo dd if=/dev/urandom of=/usr/share/nginx/www/file bs=1M count=10 + - sudo service nginx restart - pip install m2crypto salsa20 pep8 pyflakes nose coverage - sudo tests/socksify/install.sh - sudo tests/libsodium/install.sh diff --git a/tests/test_large_file.sh b/tests/test_large_file.sh new file mode 100755 index 0000000..3124ac9 --- /dev/null +++ b/tests/test_large_file.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +DEV=lo +PORT=8388 +DELAY=100ms + +PYTHON="coverage run -p -a" +URL=http://127.0.0.1/file + +mkdir -p tmp + +type tc > /dev/null && ( + tc qdisc add dev $DEV root handle 1: prio + tc qdisc add dev $DEV parent 1:3 handle 30: netem delay $DELAY + tc filter add dev $DEV parent 1:0 protocol ip u32 match ip dport $PORT 0xffff flowid 1:3 + tc filter add dev $DEV parent 1:0 protocol ip u32 match ip sport $PORT 0xffff flowid 1:3 + tc qdisc show dev lo +) + +$PYTHON shadowsocks/local.py -c tests/aes.json & +LOCAL=$! + +$PYTHON shadowsocks/server.py -c tests/aes.json & +SERVER=$! + +sleep 3 + +curl -o tmp/expected $URL +curl -o tmp/result --socks5-hostname 127.0.0.1:1081 $URL + +kill $LOCAL +kill $SERVER + +type tc > /dev/null && tc qdisc del dev lo root + +sleep 2 + +diff tmp/expected tmp/result || exit 1 From 9b3944c954326fd40ef4f641ea92137ae9d1feef Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 23 Dec 2014 13:18:30 +0800 Subject: [PATCH 016/146] fix large file test --- tests/test_large_file.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_large_file.sh b/tests/test_large_file.sh index 3124ac9..66cc13d 100755 --- a/tests/test_large_file.sh +++ b/tests/test_large_file.sh @@ -9,7 +9,7 @@ URL=http://127.0.0.1/file mkdir -p tmp -type tc > /dev/null && ( +type tc 2> /dev/null && ( tc qdisc add dev $DEV root handle 1: prio tc qdisc add dev $DEV parent 1:3 handle 30: netem delay $DELAY tc filter add dev $DEV parent 1:0 protocol ip u32 match ip dport $PORT 0xffff flowid 1:3 @@ -25,13 +25,13 @@ SERVER=$! sleep 3 -curl -o tmp/expected $URL -curl -o tmp/result --socks5-hostname 127.0.0.1:1081 $URL +time curl -o tmp/expected $URL +time curl -o tmp/result --socks5-hostname 127.0.0.1:1081 $URL kill $LOCAL kill $SERVER -type tc > /dev/null && tc qdisc del dev lo root +type tc 2> /dev/null && tc qdisc del dev lo root sleep 2 From 2cc7ee5053bc89f18377c754f348c5cf735d1a6f Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 23 Dec 2014 13:57:31 +0800 Subject: [PATCH 017/146] alter tc rule --- tests/test_large_file.sh | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/test_large_file.sh b/tests/test_large_file.sh index 66cc13d..66e4f94 100755 --- a/tests/test_large_file.sh +++ b/tests/test_large_file.sh @@ -10,10 +10,17 @@ URL=http://127.0.0.1/file mkdir -p tmp type tc 2> /dev/null && ( - tc qdisc add dev $DEV root handle 1: prio - tc qdisc add dev $DEV parent 1:3 handle 30: netem delay $DELAY - tc filter add dev $DEV parent 1:0 protocol ip u32 match ip dport $PORT 0xffff flowid 1:3 - tc filter add dev $DEV parent 1:0 protocol ip u32 match ip sport $PORT 0xffff flowid 1:3 + tc qdisc add dev $DEV root handle 1: htb + tc class add dev $DEV parent 1: classid 1:1 htb rate 2mbps + tc class add dev $DEV parent 1:1 classid 1:6 htb rate 2mbps ceil 1mbps prio 0 + tc filter add dev $DEV parent 1:0 prio 0 protocol ip handle 6 fw flowid 1:6 + + tc filter add dev $DEV parent 1:0 protocol ip u32 match ip dport $PORT 0xffff flowid 1:6 + tc filter add dev $DEV parent 1:0 protocol ip u32 match ip sport $PORT 0xffff flowid 1:6 + +# iptables -D OUTPUT -t mangle -p tcp --sport 8388 -j MARK --set-mark 6 +# iptables -A OUTPUT -t mangle -p tcp --sport 8388 -j MARK --set-mark 6 + tc qdisc show dev lo ) From cd07001471fede6f5e3fab2fdfbc8b2d0843e51d Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 23 Dec 2014 14:05:20 +0800 Subject: [PATCH 018/146] use SIGINT instead in large file test --- tests/test_large_file.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_large_file.sh b/tests/test_large_file.sh index 66e4f94..14d3002 100755 --- a/tests/test_large_file.sh +++ b/tests/test_large_file.sh @@ -35,8 +35,8 @@ sleep 3 time curl -o tmp/expected $URL time curl -o tmp/result --socks5-hostname 127.0.0.1:1081 $URL -kill $LOCAL -kill $SERVER +kill -s SIGINT $LOCAL +kill -s SIGINT $SERVER type tc 2> /dev/null && tc qdisc del dev lo root From 9cfffa360e2194f7cc6ae1b88c79057acff1b54e Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 23 Dec 2014 14:20:46 +0800 Subject: [PATCH 019/146] sudo tc setup --- .travis.yml | 1 + tests/setup_tc.sh | 18 ++++++++++++++++++ tests/test_large_file.sh | 21 --------------------- 3 files changed, 19 insertions(+), 21 deletions(-) create mode 100755 tests/setup_tc.sh diff --git a/.travis.yml b/.travis.yml index 2f9bd13..4a88b77 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,5 +15,6 @@ before_install: - pip install m2crypto salsa20 pep8 pyflakes nose coverage - sudo tests/socksify/install.sh - sudo tests/libsodium/install.sh + - sudo tests/setup_tc.sh script: - ./.jenkins.sh diff --git a/tests/setup_tc.sh b/tests/setup_tc.sh new file mode 100755 index 0000000..1a5fa20 --- /dev/null +++ b/tests/setup_tc.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +DEV=lo +PORT=8388 +DELAY=100ms + +type tc 2> /dev/null && ( + tc qdisc add dev $DEV root handle 1: htb + tc class add dev $DEV parent 1: classid 1:1 htb rate 2mbps + tc class add dev $DEV parent 1:1 classid 1:6 htb rate 2mbps ceil 1mbps prio 0 + tc filter add dev $DEV parent 1:0 prio 0 protocol ip handle 6 fw flowid 1:6 + + tc filter add dev $DEV parent 1:0 protocol ip u32 match ip dport $PORT 0xffff flowid 1:6 + tc filter add dev $DEV parent 1:0 protocol ip u32 match ip sport $PORT 0xffff flowid 1:6 + + tc qdisc show dev lo +) + diff --git a/tests/test_large_file.sh b/tests/test_large_file.sh index 14d3002..e8acd79 100755 --- a/tests/test_large_file.sh +++ b/tests/test_large_file.sh @@ -1,29 +1,10 @@ #!/bin/bash -DEV=lo -PORT=8388 -DELAY=100ms - PYTHON="coverage run -p -a" URL=http://127.0.0.1/file mkdir -p tmp -type tc 2> /dev/null && ( - tc qdisc add dev $DEV root handle 1: htb - tc class add dev $DEV parent 1: classid 1:1 htb rate 2mbps - tc class add dev $DEV parent 1:1 classid 1:6 htb rate 2mbps ceil 1mbps prio 0 - tc filter add dev $DEV parent 1:0 prio 0 protocol ip handle 6 fw flowid 1:6 - - tc filter add dev $DEV parent 1:0 protocol ip u32 match ip dport $PORT 0xffff flowid 1:6 - tc filter add dev $DEV parent 1:0 protocol ip u32 match ip sport $PORT 0xffff flowid 1:6 - -# iptables -D OUTPUT -t mangle -p tcp --sport 8388 -j MARK --set-mark 6 -# iptables -A OUTPUT -t mangle -p tcp --sport 8388 -j MARK --set-mark 6 - - tc qdisc show dev lo -) - $PYTHON shadowsocks/local.py -c tests/aes.json & LOCAL=$! @@ -38,8 +19,6 @@ time curl -o tmp/result --socks5-hostname 127.0.0.1:1081 $URL kill -s SIGINT $LOCAL kill -s SIGINT $SERVER -type tc 2> /dev/null && tc qdisc del dev lo root - sleep 2 diff tmp/expected tmp/result || exit 1 From 1d0c8b1800c6775b4bad02bcbb4529848d334d1a Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 24 Dec 2014 16:47:14 +0800 Subject: [PATCH 020/146] add coverage --- .jenkins.sh | 2 ++ README.md | 6 +++++- tests/coverage_server.py | 23 +++++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/coverage_server.py diff --git a/.jenkins.sh b/.jenkins.sh index bae7f79..5630511 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -60,4 +60,6 @@ rm -rf htmlcov rm -rf tmp coverage html --include=shadowsocks/* +coverage report --include=shadowsocks/* | tail -n1 | rev | cut -d' ' -f 1 | rev > /tmp/shadowsocks-coverage + exit $result diff --git a/README.md b/README.md index 7a37b26..621d2d9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ shadowsocks =========== -[![PyPI version]][PyPI] [![Build Status]][Travis CI] +[![PyPI version]][PyPI] +[![Build Status]][Travis CI] +[![Coverage Status]][Coverage] A fast tunnel proxy that helps you bypass firewalls. @@ -119,6 +121,8 @@ Bugs and Issues [Android]: https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#android [Build Status]: https://img.shields.io/travis/clowwindy/shadowsocks/master.svg?style=flat [Chinese Readme]: https://github.com/clowwindy/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E +[Coverage Status]: http://192.81.132.184/result/shadowsocks +[Coverage]: http://192.81.132.184/job/Shadowsocks/ws/htmlcov/index.html [Debian sid]: https://packages.debian.org/unstable/python/shadowsocks [the package]: https://pypi.python.org/pypi/shadowsocks [Encryption]: https://github.com/clowwindy/shadowsocks/wiki/Encryption diff --git a/tests/coverage_server.py b/tests/coverage_server.py new file mode 100644 index 0000000..4bb53df --- /dev/null +++ b/tests/coverage_server.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +import tornado.ioloop +import tornado.web +import urllib + + +class MainHandler(tornado.web.RequestHandler): + def get(self): + with open('/tmp/shadowsocks-coverage', 'rb') as f: + coverage = f.read().strip() + self.redirect(('https://img.shields.io/badge/' + 'coverage-%s-brightgreen.svg' + '?style=flat') % + urllib.quote(coverage)) + +application = tornado.web.Application([ + (r"/shadowsocks", MainHandler), +]) + +if __name__ == "__main__": + application.listen(8888, address='127.0.0.1') + tornado.ioloop.IOLoop.instance().start() From b785d95f66b08ba6308567ac5143ffdcc62b9947 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 24 Dec 2014 17:06:15 +0800 Subject: [PATCH 021/146] fix tests --- tests/coverage_server.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/coverage_server.py b/tests/coverage_server.py index 4bb53df..7a135c5 100644 --- a/tests/coverage_server.py +++ b/tests/coverage_server.py @@ -1,23 +1,23 @@ #!/usr/bin/env python -import tornado.ioloop -import tornado.web -import urllib +if __name__ == '__main__': + import tornado.ioloop + import tornado.web + import urllib + class MainHandler(tornado.web.RequestHandler): + def get(self): + with open('/tmp/shadowsocks-coverage', 'rb') as f: + coverage = f.read().strip() + self.redirect(('https://img.shields.io/badge/' + 'coverage-%s-brightgreen.svg' + '?style=flat') % + urllib.quote(coverage)) -class MainHandler(tornado.web.RequestHandler): - def get(self): - with open('/tmp/shadowsocks-coverage', 'rb') as f: - coverage = f.read().strip() - self.redirect(('https://img.shields.io/badge/' - 'coverage-%s-brightgreen.svg' - '?style=flat') % - urllib.quote(coverage)) + application = tornado.web.Application([ + (r"/shadowsocks", MainHandler), + ]) -application = tornado.web.Application([ - (r"/shadowsocks", MainHandler), -]) - -if __name__ == "__main__": - application.listen(8888, address='127.0.0.1') - tornado.ioloop.IOLoop.instance().start() + if __name__ == "__main__": + application.listen(8888, address='127.0.0.1') + tornado.ioloop.IOLoop.instance().start() From c6bc912c11df07db02e8492e10d92e1e680ac177 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 24 Dec 2014 18:02:14 +0800 Subject: [PATCH 022/146] add command line tests --- .jenkins.sh | 4 +- tests/assert.sh | 148 ++++++++++++++++++++++++++++++++++++++++++ tests/test_command.sh | 40 ++++++++++++ 3 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 tests/assert.sh create mode 100755 tests/test_command.sh diff --git a/.jenkins.sh b/.jenkins.sh index 5630511..56e2acc 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -44,8 +44,8 @@ run_test python tests/test.py --with-coverage -c tests/server-multi-ports.json run_test python tests/test.py --with-coverage -s tests/server-multi-passwd.json -c tests/server-multi-passwd-client-side.json run_test python tests/test.py --with-coverage -c tests/workers.json run_test python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-client-side.json -run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388" -a "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -l 1081" -run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081" +run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -q" -a "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -vv" +run_test python tests/test.py --with-coverage -b "-b 127.0.0.1 -m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --workers 1" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -qq" if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then if [ 3 -eq `cat /proc/sys/net/ipv4/tcp_fastopen` ] ; then diff --git a/tests/assert.sh b/tests/assert.sh new file mode 100644 index 0000000..b0c679c --- /dev/null +++ b/tests/assert.sh @@ -0,0 +1,148 @@ +#!/bin/bash +# assert.sh 1.0 - bash unit testing framework +# Copyright (C) 2009, 2010, 2011, 2012 Robert Lehmann +# +# http://github.com/lehmannro/assert.sh +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +export DISCOVERONLY=${DISCOVERONLY:-} +export DEBUG=${DEBUG:-} +export STOP=${STOP:-} +export INVARIANT=${INVARIANT:-} +export CONTINUE=${CONTINUE:-} + +args="$(getopt -n "$0" -l \ + verbose,help,stop,discover,invariant,continue vhxdic $*)" \ +|| exit -1 +for arg in $args; do + case "$arg" in + -h) + echo "$0 [-vxidc]" \ + "[--verbose] [--stop] [--invariant] [--discover] [--continue]" + echo "`sed 's/./ /g' <<< "$0"` [-h] [--help]" + exit 0;; + --help) + cat < [stdin] + (( tests_ran++ )) || : + [[ -n "$DISCOVERONLY" ]] && return || true + # printf required for formatting + printf -v expected "x${2:-}" # x required to overwrite older results + result="$(eval 2>/dev/null $1 <<< ${3:-})" || true + # Note: $expected is already decorated + if [[ "x$result" == "$expected" ]]; then + [[ -n "$DEBUG" ]] && echo -n . || true + return + fi + result="$(sed -e :a -e '$!N;s/\n/\\n/;ta' <<< "$result")" + [[ -z "$result" ]] && result="nothing" || result="\"$result\"" + [[ -z "$2" ]] && expected="nothing" || expected="\"$2\"" + _assert_fail "expected $expected${_indent}got $result" "$1" "$3" +} + +assert_raises() { + # assert_raises [stdin] + (( tests_ran++ )) || : + [[ -n "$DISCOVERONLY" ]] && return || true + status=0 + (eval $1 <<< ${3:-}) > /dev/null 2>&1 || status=$? + expected=${2:-0} + if [[ "$status" -eq "$expected" ]]; then + [[ -n "$DEBUG" ]] && echo -n . || true + return + fi + _assert_fail "program terminated with code $status instead of $expected" "$1" "$3" +} + +_assert_fail() { + # _assert_fail + [[ -n "$DEBUG" ]] && echo -n X + report="test #$tests_ran \"$2${3:+ <<< $3}\" failed:${_indent}$1" + if [[ -n "$STOP" ]]; then + [[ -n "$DEBUG" ]] && echo + echo "$report" + exit 1 + fi + tests_errors[$tests_failed]="$report" + (( tests_failed++ )) || : +} + +_assert_reset +: ${tests_suite_status:=0} # remember if any of the tests failed so far +_assert_cleanup() { + local status=$? + # modify exit code if it's not already non-zero + [[ $status -eq 0 && -z $CONTINUE ]] && exit $tests_suite_status +} +trap _assert_cleanup EXIT diff --git a/tests/test_command.sh b/tests/test_command.sh new file mode 100755 index 0000000..fc23665 --- /dev/null +++ b/tests/test_command.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +. tests/assert.sh + +PYTHON="coverage run -a -p" +LOCAL="$PYTHON shadowsocks/local.py" +SERVER="$PYTHON shadowsocks/server.py" + +assert "$LOCAL 2>&1 | grep ERROR" "ERROR: config not specified" +assert "$LOCAL 2>&1 | grep usage | cut -d: -f1" "usage" + +assert "$SERVER 2>&1 | grep ERROR" "ERROR: config not specified" +assert "$SERVER 2>&1 | grep usage | cut -d: -f1" "usage" + +assert "$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d start | grep WARNING | awk -F\"WARNING\" '{print \$2}'" " warning: server set to listen on 127.0.0.1:8388, are you sure?" +$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop + +assert "$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 0.0.0.0 -p 8388 -t10 -d start | grep WARNING | awk -F\"WARNING\" '{print \$2}'" " warning: your timeout 10 seems too short" +$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop + +assert "$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 0.0.0.0 -p 8388 -t1000 -d start | grep WARNING | awk -F\"WARNING\" '{print \$2}'" " warning: your timeout 1000 seems too long" +$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop + +assert "$LOCAL 2>&1 -m rc4 -k testrc4 -s 0.0.0.0 -p 8388 -d start | grep WARNING | awk -F\"WARNING\" '{print \$2}'" " warning: RC4 is not safe; please use a safer cipher, like AES-256-CFB" +$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop + +assert "$LOCAL 2>&1 -m rc4-md5 -k mypassword -s 0.0.0.0 -p 8388 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" " DON'T USE DEFAULT PASSWORD! Please change it in your config.json!" +$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop + +assert "$LOCAL 2>&1 -m rc4-md5 -p 8388 -k testrc4 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": server addr not specified" +$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop + +assert "$LOCAL 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": password not specified" +$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop + +assert "$SERVER 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": password or port_password not specified" +$SERVER 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop + + +assert_end command From ab975d1753bcdc41db380b4a4399c634c0632f32 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 24 Dec 2014 18:08:16 +0800 Subject: [PATCH 023/146] fix tests --- .jenkins.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.jenkins.sh b/.jenkins.sh index 56e2acc..23ed2c7 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -45,7 +45,7 @@ run_test python tests/test.py --with-coverage -s tests/server-multi-passwd.json run_test python tests/test.py --with-coverage -c tests/workers.json run_test python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-client-side.json run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -q" -a "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -vv" -run_test python tests/test.py --with-coverage -b "-b 127.0.0.1 -m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --workers 1" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -qq" +run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --workers 1" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -qq -b 127.0.0.1" if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then if [ 3 -eq `cat /proc/sys/net/ipv4/tcp_fastopen` ] ; then From d228ffbe84de78ce7492d493900ad2e6a285cc01 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 24 Dec 2014 18:10:12 +0800 Subject: [PATCH 024/146] add command line test --- .jenkins.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.jenkins.sh b/.jenkins.sh index 23ed2c7..7b381a8 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -55,6 +55,8 @@ fi run_test tests/test_large_file.sh +run_test tests/test_command.sh + coverage combine && coverage report --include=shadowsocks/* rm -rf htmlcov rm -rf tmp From dd140814a8d10ccceb249c00668bd85ca6252b91 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 24 Dec 2014 18:15:52 +0800 Subject: [PATCH 025/146] suppress some warnings --- tests/test_command.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/test_command.sh b/tests/test_command.sh index fc23665..eba4c8c 100755 --- a/tests/test_command.sh +++ b/tests/test_command.sh @@ -13,28 +13,28 @@ assert "$SERVER 2>&1 | grep ERROR" "ERROR: config not specified" assert "$SERVER 2>&1 | grep usage | cut -d: -f1" "usage" assert "$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d start | grep WARNING | awk -F\"WARNING\" '{print \$2}'" " warning: server set to listen on 127.0.0.1:8388, are you sure?" -$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop +$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop assert "$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 0.0.0.0 -p 8388 -t10 -d start | grep WARNING | awk -F\"WARNING\" '{print \$2}'" " warning: your timeout 10 seems too short" -$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop +$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop assert "$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 0.0.0.0 -p 8388 -t1000 -d start | grep WARNING | awk -F\"WARNING\" '{print \$2}'" " warning: your timeout 1000 seems too long" -$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop +$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop assert "$LOCAL 2>&1 -m rc4 -k testrc4 -s 0.0.0.0 -p 8388 -d start | grep WARNING | awk -F\"WARNING\" '{print \$2}'" " warning: RC4 is not safe; please use a safer cipher, like AES-256-CFB" -$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop +$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop assert "$LOCAL 2>&1 -m rc4-md5 -k mypassword -s 0.0.0.0 -p 8388 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" " DON'T USE DEFAULT PASSWORD! Please change it in your config.json!" -$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop +$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop assert "$LOCAL 2>&1 -m rc4-md5 -p 8388 -k testrc4 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": server addr not specified" -$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop +$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop assert "$LOCAL 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": password not specified" -$LOCAL 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop +$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop assert "$SERVER 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": password or port_password not specified" -$SERVER 2>&1 -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop +$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop assert_end command From 30f4f78557e55197b8cba9d761f3cd388fc7def2 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 24 Dec 2014 18:23:35 +0800 Subject: [PATCH 026/146] fix travis --- .travis.yml | 2 +- shadowsocks/utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4a88b77..4fbe78c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ cache: - dante-1.4.0 before_install: - sudo apt-get update -qq - - sudo apt-get install -qq build-essential libssl-dev swig python-m2crypto python-numpy dnsutils iproute nginx + - sudo apt-get install -qq build-essential libssl-dev swig python-m2crypto python-numpy dnsutils iproute nginx bc - sudo dd if=/dev/urandom of=/usr/share/nginx/www/file bs=1M count=10 - sudo service nginx restart - pip install m2crypto salsa20 pep8 pyflakes nose coverage diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 7caa243..ff6801c 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -29,7 +29,7 @@ import json import sys import getopt import logging -from shadowsocks.common import to_bytes +from shadowsocks.common import to_bytes, to_str VERBOSE_LEVEL = 5 @@ -73,7 +73,7 @@ def check_config(config): logging.warn('warning: local set to listen on 0.0.0.0, it\'s not safe') if config.get('server', '') in [b'127.0.0.1', b'localhost']: logging.warn('warning: server set to listen on %s:%s, are you sure?' % - (config['server'], config['server_port'])) + (to_str(config['server']), config['server_port'])) if (config.get('method', '') or '').lower() == b'table': logging.warn('warning: table is not safe; please use a safer cipher, ' 'like AES-256-CFB') From 99c8bbafe72cdd2e7cdcf078911309be5bec9a3e Mon Sep 17 00:00:00 2001 From: clowwindy Date: Thu, 25 Dec 2014 16:20:15 +0800 Subject: [PATCH 027/146] fix fastopen and add a new test --- .jenkins.sh | 4 ++++ shadowsocks/tcprelay.py | 11 +++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.jenkins.sh b/.jenkins.sh index 7b381a8..4c85f1c 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -49,6 +49,10 @@ run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 1 if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then if [ 3 -eq `cat /proc/sys/net/ipv4/tcp_fastopen` ] ; then + # we have to run it twice: + # the first time there's no syn cookie + # the second time there is syn cookie + run_test python tests/test.py --with-coverage -c tests/fastopen.json run_test python tests/test.py --with-coverage -c tests/fastopen.json fi fi diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 146714a..95cbef5 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -241,19 +241,18 @@ class TCPRelayHandler(object): self._create_remote_socket(self._chosen_server[0], self._chosen_server[1]) self._loop.add(remote_sock, eventloop.POLL_ERR) - data = b''.join(self._data_to_write_to_local) + 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_local = [data] - self._update_stream(STREAM_UP, WAIT_STATUS_READWRITING) + self._data_to_write_to_remote = [data] else: - self._data_to_write_to_local = [] - self._update_stream(STREAM_UP, WAIT_STATUS_READING) - self._stage = STAGE_STREAM + 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') From e8501da271356af4c7fb1aefddcb8385f7b1dd77 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Thu, 25 Dec 2014 16:23:43 +0800 Subject: [PATCH 028/146] fix vim --- shadowsocks/tcprelay.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 95cbef5..567f515 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -249,7 +249,7 @@ class TCPRelayHandler(object): self._data_to_write_to_remote = [data] else: self._data_to_write_to_remote = [] - self._update_stream(STREAM_UP, WAIT_STATUS_READWRITING) + 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 From 9e6e884b12f91ec54487f5851458369ae5802fb9 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Fri, 26 Dec 2014 20:58:22 +0800 Subject: [PATCH 029/146] bump --- CHANGES | 4 ++++ setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 3662f50..6822082 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +2.6.1 2014-12-26 +- Fix a problem with TCP Fast Open on local side +- Fix sometimes daemon_start returns wrong exit status + 2.6 2014-12-21 - Add daemon support diff --git a/setup.py b/setup.py index 224cb21..442a3ac 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with codecs.open('README.rst', encoding='utf-8') as f: setup( name="shadowsocks", - version="2.6", + version="2.6.1", license='MIT', description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', From 937693519a098238d28f2efb812c99d42889a17f Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 27 Dec 2014 23:29:07 +0800 Subject: [PATCH 030/146] update url in readme --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 621d2d9..4365844 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ Documentation ------------- You can find all the documentation in the wiki: -https://github.com/clowwindy/shadowsocks/wiki +https://github.com/shadowsocks/shadowsocks/wiki License ------- @@ -118,23 +118,23 @@ Bugs and Issues * [Mailing list] -[Android]: https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#android -[Build Status]: https://img.shields.io/travis/clowwindy/shadowsocks/master.svg?style=flat -[Chinese Readme]: https://github.com/clowwindy/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E +[Android]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#android +[Build Status]: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat +[Chinese Readme]: https://github.com/shadowsocks/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E [Coverage Status]: http://192.81.132.184/result/shadowsocks [Coverage]: http://192.81.132.184/job/Shadowsocks/ws/htmlcov/index.html [Debian sid]: https://packages.debian.org/unstable/python/shadowsocks [the package]: https://pypi.python.org/pypi/shadowsocks -[Encryption]: https://github.com/clowwindy/shadowsocks/wiki/Encryption +[Encryption]: https://github.com/shadowsocks/shadowsocks/wiki/Encryption [iOS]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help -[Issue Tracker]: https://github.com/clowwindy/shadowsocks/issues?state=open +[Issue Tracker]: https://github.com/shadowsocks/shadowsocks/issues?state=open [Mailing list]: http://groups.google.com/group/shadowsocks [OpenSSL for Windows]: http://slproweb.com/products/Win32OpenSSL.html -[OpenWRT]: https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#openwrt +[OpenWRT]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#openwrt [OS X]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Shadowsocks-for-OSX-Help [PyPI]: https://pypi.python.org/pypi/shadowsocks [PyPI version]: https://img.shields.io/pypi/v/shadowsocks.svg?style=flat -[TCP_FASTOPEN]: https://github.com/clowwindy/shadowsocks/wiki/TCP-Fast-Open -[Travis CI]: https://travis-ci.org/clowwindy/shadowsocks -[Troubleshooting]: https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting -[Windows]: https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#windows +[TCP_FASTOPEN]: https://github.com/shadowsocks/shadowsocks/wiki/TCP-Fast-Open +[Travis CI]: https://travis-ci.org/shadowsocks/shadowsocks +[Troubleshooting]: https://github.com/shadowsocks/shadowsocks/wiki/Troubleshooting +[Windows]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#windows From 7f19c316405018700bf61299cfd5177d0721b9ed Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 27 Dec 2014 23:29:48 +0800 Subject: [PATCH 031/146] update project url --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 442a3ac..c2b7d88 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', author_email='clowwindy42@gmail.com', - url='https://github.com/clowwindy/shadowsocks', + url='https://github.com/shadowsocks/shadowsocks', packages=['shadowsocks', 'shadowsocks.crypto'], package_data={ 'shadowsocks': ['README.rst', 'LICENSE'] From 5a5158b33f24d012496060a6b6b9a061bc982ee4 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 27 Dec 2014 23:36:09 +0800 Subject: [PATCH 032/146] update url --- README.rst | 64 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/README.rst b/README.rst index 003b358..41afc36 100644 --- a/README.rst +++ b/README.rst @@ -1,11 +1,11 @@ shadowsocks =========== -|PyPI version| |Build Status| +|PyPI version| |Build Status| |Coverage Status| A fast tunnel proxy that helps you bypass firewalls. -`中文说明 `__ +`中文说明 `__ Install ------- @@ -16,12 +16,12 @@ server. Client ~~~~~~ -- `Windows `__ +- `Windows `__ / `OS X `__ -- `Android `__ +- `Android `__ / `iOS `__ -- `OpenWRT `__ +- `OpenWRT `__ Server ~~~~~~ @@ -77,27 +77,27 @@ On your server create a config file ``/etc/shadowsocks.json``. Example: Explanation of the fields: -+------------------+---------------------------------------------------------------------------------------------------------+ -| Name | Explanation | -+==================+=========================================================================================================+ -| server | the address your server listens | -+------------------+---------------------------------------------------------------------------------------------------------+ -| server\_port | server port | -+------------------+---------------------------------------------------------------------------------------------------------+ -| local\_address | the address your local listens | -+------------------+---------------------------------------------------------------------------------------------------------+ -| local\_port | local port | -+------------------+---------------------------------------------------------------------------------------------------------+ -| password | password used for encryption | -+------------------+---------------------------------------------------------------------------------------------------------+ -| timeout | in seconds | -+------------------+---------------------------------------------------------------------------------------------------------+ -| method | default: "aes-256-cfb", see `Encryption `__ | -+------------------+---------------------------------------------------------------------------------------------------------+ -| fast\_open | use `TCP\_FASTOPEN `__, true / false | -+------------------+---------------------------------------------------------------------------------------------------------+ -| workers | number of workers, available on Unix/Linux | -+------------------+---------------------------------------------------------------------------------------------------------+ ++------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Explanation | ++==================+===========================================================================================================+ +| server | the address your server listens | ++------------------+-----------------------------------------------------------------------------------------------------------+ +| server\_port | server port | ++------------------+-----------------------------------------------------------------------------------------------------------+ +| local\_address | the address your local listens | ++------------------+-----------------------------------------------------------------------------------------------------------+ +| local\_port | local port | ++------------------+-----------------------------------------------------------------------------------------------------------+ +| password | password used for encryption | ++------------------+-----------------------------------------------------------------------------------------------------------+ +| timeout | in seconds | ++------------------+-----------------------------------------------------------------------------------------------------------+ +| method | default: "aes-256-cfb", see `Encryption `__ | ++------------------+-----------------------------------------------------------------------------------------------------------+ +| fast\_open | use `TCP\_FASTOPEN `__, true / false | ++------------------+-----------------------------------------------------------------------------------------------------------+ +| workers | number of workers, available on Unix/Linux | ++------------------+-----------------------------------------------------------------------------------------------------------+ On your server: @@ -134,7 +134,7 @@ Documentation ------------- You can find all the documentation in the wiki: -https://github.com/clowwindy/shadowsocks/wiki +https://github.com/shadowsocks/shadowsocks/wiki License ------- @@ -144,12 +144,14 @@ MIT Bugs and Issues --------------- -- `Troubleshooting `__ +- `Troubleshooting `__ - `Issue - Tracker `__ + Tracker `__ - `Mailing list `__ .. |PyPI version| image:: https://img.shields.io/pypi/v/shadowsocks.svg?style=flat :target: https://pypi.python.org/pypi/shadowsocks -.. |Build Status| image:: https://img.shields.io/travis/clowwindy/shadowsocks/master.svg?style=flat - :target: https://travis-ci.org/clowwindy/shadowsocks +.. |Build Status| image:: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat + :target: https://travis-ci.org/shadowsocks/shadowsocks +.. |Coverage Status| image:: http://192.81.132.184/result/shadowsocks + :target: http://192.81.132.184/job/Shadowsocks/ws/htmlcov/index.html From d91f7d85d4adcc91cf61a2211bd59588c7421f09 Mon Sep 17 00:00:00 2001 From: v3aqb Date: Sun, 28 Dec 2014 08:30:29 +0800 Subject: [PATCH 033/146] Update ctypes_libsodium.py --- shadowsocks/crypto/ctypes_libsodium.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks/crypto/ctypes_libsodium.py b/shadowsocks/crypto/ctypes_libsodium.py index efecfd4..69b9de9 100644 --- a/shadowsocks/crypto/ctypes_libsodium.py +++ b/shadowsocks/crypto/ctypes_libsodium.py @@ -42,7 +42,7 @@ def load_libsodium(): global loaded, libsodium, buf from ctypes.util import find_library - for p in ('sodium',): + for p in ('sodium', 'libsodium'): libsodium_path = find_library(p) if libsodium_path: break From 45ee594d587fa6606043ed08638405d2910fc0ae Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 28 Dec 2014 12:29:20 +0800 Subject: [PATCH 034/146] coverage server now supports more projects --- tests/coverage_server.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/coverage_server.py b/tests/coverage_server.py index 7a135c5..5a9ce57 100644 --- a/tests/coverage_server.py +++ b/tests/coverage_server.py @@ -6,16 +6,19 @@ if __name__ == '__main__': import urllib class MainHandler(tornado.web.RequestHandler): - def get(self): - with open('/tmp/shadowsocks-coverage', 'rb') as f: - coverage = f.read().strip() - self.redirect(('https://img.shields.io/badge/' - 'coverage-%s-brightgreen.svg' - '?style=flat') % - urllib.quote(coverage)) + def get(self, project): + try: + with open('/tmp/%s-coverage' % project, 'rb') as f: + coverage = f.read().strip() + self.redirect(('https://img.shields.io/badge/' + 'coverage-%s-brightgreen.svg' + '?style=flat') % + urllib.quote(coverage)) + except IOError: + raise tornado.web.HTTPError(404) application = tornado.web.Application([ - (r"/shadowsocks", MainHandler), + (r"/([a-zA-Z0-9\\-_]+)", MainHandler), ]) if __name__ == "__main__": From 70559a1030fe88211a119057587580153792fd78 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 28 Dec 2014 12:35:17 +0800 Subject: [PATCH 035/146] fix pep8 --- tests/coverage_server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/coverage_server.py b/tests/coverage_server.py index 5a9ce57..5cd22ae 100644 --- a/tests/coverage_server.py +++ b/tests/coverage_server.py @@ -11,9 +11,9 @@ if __name__ == '__main__': with open('/tmp/%s-coverage' % project, 'rb') as f: coverage = f.read().strip() self.redirect(('https://img.shields.io/badge/' - 'coverage-%s-brightgreen.svg' - '?style=flat') % - urllib.quote(coverage)) + 'coverage-%s-brightgreen.svg' + '?style=flat') % + urllib.quote(coverage)) except IOError: raise tornado.web.HTTPError(404) From 5a5c8b9c7e6a3bdeec3be69554df654c05ffeca5 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 28 Dec 2014 15:00:29 +0800 Subject: [PATCH 036/146] fix coverage server --- tests/coverage_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/coverage_server.py b/tests/coverage_server.py index 5cd22ae..ca3830b 100644 --- a/tests/coverage_server.py +++ b/tests/coverage_server.py @@ -18,7 +18,7 @@ if __name__ == '__main__': raise tornado.web.HTTPError(404) application = tornado.web.Application([ - (r"/([a-zA-Z0-9\\-_]+)", MainHandler), + (r"/([a-zA-Z0-9\-_]+)", MainHandler), ]) if __name__ == "__main__": From 1423fe1921536bfb830b12202762140d545d34ac Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 28 Dec 2014 15:06:22 +0800 Subject: [PATCH 037/146] support color --- tests/coverage_server.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/coverage_server.py b/tests/coverage_server.py index ca3830b..2df55e3 100644 --- a/tests/coverage_server.py +++ b/tests/coverage_server.py @@ -10,10 +10,15 @@ if __name__ == '__main__': try: with open('/tmp/%s-coverage' % project, 'rb') as f: coverage = f.read().strip() + n = int(coverage.strip('%')) + if n > 80: + color = 'brightgreen' + else: + color = 'yellow' self.redirect(('https://img.shields.io/badge/' - 'coverage-%s-brightgreen.svg' + 'coverage-%s-%s.svg' '?style=flat') % - urllib.quote(coverage)) + (urllib.quote(coverage), color)) except IOError: raise tornado.web.HTTPError(404) From 291adf8b8588ffba79cf1e24f403624b0d8a257f Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 3 Jan 2015 12:48:03 +0800 Subject: [PATCH 038/146] log client address --- shadowsocks/tcprelay.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 567f515..91c3f00 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -121,6 +121,7 @@ class TCPRelayHandler(object): self._data_to_write_to_remote = [] self._upstream_status = WAIT_STATUS_READING self._downstream_status = WAIT_STATUS_INIT + self._client_address = local_sock.getpeername()[:2] self._remote_address = None if is_local: self._chosen_server = self._get_a_server() @@ -294,8 +295,9 @@ class TCPRelayHandler(object): if header_result is None: raise Exception('can not parse header') addrtype, remote_addr, remote_port, header_length = header_result - logging.info('connecting %s:%d' % (common.to_str(remote_addr), - remote_port)) + logging.info('connecting %s:%d from %s:%d' % + (common.to_str(remote_addr), remote_port, + self._client_address[0], self._client_address[1])) self._remote_address = (remote_addr, remote_port) # pause reading self._update_stream(STREAM_UP, WAIT_STATUS_WRITING) @@ -317,7 +319,7 @@ class TCPRelayHandler(object): self._dns_resolver.resolve(remote_addr, self._handle_dns_resolved) except Exception as e: - logging.error(e) + self._log_error(e) if self._config['verbose']: traceback.print_exc() # TODO use logging when debug completed @@ -338,7 +340,7 @@ class TCPRelayHandler(object): def _handle_dns_resolved(self, result, error): if error: - logging.error(error) + self._log_error(error) self.destroy() return if result: @@ -507,6 +509,10 @@ class TCPRelayHandler(object): else: logging.warn('unknown socket') + def _log_error(self, e): + logging.error('%s when handling connection from %s:%d' % + (e, self._client_address[0], self._client_address[1])) + def destroy(self): # destroy the handler and release any resources # promises: From bac675d7f68ee21e0ba5e884ba1c845040202c03 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 3 Jan 2015 13:24:40 +0800 Subject: [PATCH 039/146] 2015 --- LICENSE | 2 +- shadowsocks/tcprelay.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 98f608b..76886fa 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ Shadowsocks -Copyright (c) 2014 clowwindy +Copyright (c) 2012-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 diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 91c3f00..79bd1a5 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright (c) 2014 clowwindy +# 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 From e68b3e430bb8a0d22c40218780e06bb8ec59baf7 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 3 Jan 2015 13:30:16 +0800 Subject: [PATCH 040/146] add autoban --- utils/README.md | 12 ++++++++++++ utils/autoban.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 utils/README.md create mode 100755 utils/autoban.py diff --git a/utils/README.md b/utils/README.md new file mode 100644 index 0000000..b30165b --- /dev/null +++ b/utils/README.md @@ -0,0 +1,12 @@ +Useful Tools +=========== + +autoban.py +---------- + +Automatically ban IPs that try to brute force crack the server. + + python autoban.py < /var/log/shadowsocks.log + +Use `-c` to specify with how many failure times it should be considered an +attack. Default is 3. diff --git a/utils/autoban.py b/utils/autoban.py new file mode 100755 index 0000000..9c81f6e --- /dev/null +++ b/utils/autoban.py @@ -0,0 +1,51 @@ +#!/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 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) + 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) + os.system(cmd) From e0189ab1de4a37c1887e9dfda6325d1f0cbe319f Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 3 Jan 2015 13:50:08 +0800 Subject: [PATCH 041/146] update readme --- utils/README.md | 8 +++++++- utils/autoban.py | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/utils/README.md b/utils/README.md index b30165b..8441748 100644 --- a/utils/README.md +++ b/utils/README.md @@ -8,5 +8,11 @@ Automatically ban IPs that try to brute force crack the server. python autoban.py < /var/log/shadowsocks.log -Use `-c` to specify with how many failure times it should be considered an +Use `-c` to specify with how many failure times it should be considered as an attack. Default is 3. + +To continue watching for the log file: + + nohup tail -f /var/log/shadowsocks.log | python autoban.py >log 2>log & + +Use with caution. Avoid to ban yourself. diff --git a/utils/autoban.py b/utils/autoban.py index 9c81f6e..d04ebbe 100755 --- a/utils/autoban.py +++ b/utils/autoban.py @@ -32,7 +32,7 @@ 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 an attack') + 'considered as an attack') config = parser.parse_args() ips = {} banned = set() From 98f73e44d0190e99a10b8bd355c27656a52812be Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 3 Jan 2015 13:56:19 +0800 Subject: [PATCH 042/146] bump --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c2b7d88..2bfc2c5 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with codecs.open('README.rst', encoding='utf-8') as f: setup( name="shadowsocks", - version="2.6.1", + version="2.6.2", license='MIT', description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', From dde25f89e1f4c6366b9019fc38217a0fc05e9156 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 3 Jan 2015 14:00:33 +0800 Subject: [PATCH 043/146] Update README.md --- utils/README.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/utils/README.md b/utils/README.md index 8441748..f624309 100644 --- a/utils/README.md +++ b/utils/README.md @@ -6,13 +6,4 @@ autoban.py Automatically ban IPs that try to brute force crack the server. - python autoban.py < /var/log/shadowsocks.log - -Use `-c` to specify with how many failure times it should be considered as an -attack. Default is 3. - -To continue watching for the log file: - - nohup tail -f /var/log/shadowsocks.log | python autoban.py >log 2>log & - -Use with caution. Avoid to ban yourself. +See https://github.com/shadowsocks/shadowsocks/wiki/Ban-Brute-Force-Crackers From b8f18257761bbad03375aa4c0fe49f5fa6478b56 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 3 Jan 2015 15:51:10 +0800 Subject: [PATCH 044/146] update CHANGES --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 6822082..62aec8a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +2.6.2 2015-01-03 +- Log client IP + 2.6.1 2014-12-26 - Fix a problem with TCP Fast Open on local side - Fix sometimes daemon_start returns wrong exit status From e7984930d1a0fd29b420f7b822a55396d7ad1e6f Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 3 Jan 2015 16:48:05 +0800 Subject: [PATCH 045/146] use HTTPS --- README.md | 4 ++-- README.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4365844..d650eb3 100644 --- a/README.md +++ b/README.md @@ -121,8 +121,8 @@ Bugs and Issues [Android]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#android [Build Status]: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat [Chinese Readme]: https://github.com/shadowsocks/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E -[Coverage Status]: http://192.81.132.184/result/shadowsocks -[Coverage]: http://192.81.132.184/job/Shadowsocks/ws/htmlcov/index.html +[Coverage Status]: https://jenkins.shadowvpn.org/result/shadowsocks +[Coverage]: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/htmlcov/index.html [Debian sid]: https://packages.debian.org/unstable/python/shadowsocks [the package]: https://pypi.python.org/pypi/shadowsocks [Encryption]: https://github.com/shadowsocks/shadowsocks/wiki/Encryption diff --git a/README.rst b/README.rst index 41afc36..da9b227 100644 --- a/README.rst +++ b/README.rst @@ -153,5 +153,5 @@ Bugs and Issues :target: https://pypi.python.org/pypi/shadowsocks .. |Build Status| image:: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat :target: https://travis-ci.org/shadowsocks/shadowsocks -.. |Coverage Status| image:: http://192.81.132.184/result/shadowsocks - :target: http://192.81.132.184/job/Shadowsocks/ws/htmlcov/index.html +.. |Coverage Status| image:: https://jenkins.shadowvpn.org/result/shadowsocks + :target: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/htmlcov/index.html From 8be120518b6d2b31cd04ed624a4b5cfae5c563ff Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 4 Jan 2015 12:46:07 +0800 Subject: [PATCH 046/146] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d650eb3..31fa5ae 100644 --- a/README.md +++ b/README.md @@ -128,8 +128,8 @@ Bugs and Issues [Encryption]: https://github.com/shadowsocks/shadowsocks/wiki/Encryption [iOS]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help [Issue Tracker]: https://github.com/shadowsocks/shadowsocks/issues?state=open -[Mailing list]: http://groups.google.com/group/shadowsocks -[OpenSSL for Windows]: http://slproweb.com/products/Win32OpenSSL.html +[Mailing list]: https://groups.google.com/group/shadowsocks +[OpenSSL for Windows]: https://slproweb.com/products/Win32OpenSSL.html [OpenWRT]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#openwrt [OS X]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Shadowsocks-for-OSX-Help [PyPI]: https://pypi.python.org/pypi/shadowsocks From 510dc64bf2ffac5e9984aef1e3aabfdf42036733 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 4 Jan 2015 23:17:01 +0800 Subject: [PATCH 047/146] Update README.md --- README.md | 58 +++++++++---------------------------------------------- 1 file changed, 9 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 31fa5ae..b10d9f7 100644 --- a/README.md +++ b/README.md @@ -39,66 +39,25 @@ source list. #### Windows: -Download [OpenSSL for Windows] and install. Then install shadowsocks via -easy_install and pip as Linux. If you don't know how to use them, you can -directly download [the package], and use `python shadowsocks/server.py` -instead of `ssserver` command below. +Install [OpenSSL for Windows]. Download [the package]. +Use `python shadowsocks/server.py` instead of `ssserver` command below. -Configuration -------------- - -On your server create a config file `/etc/shadowsocks.json`. -Example: - - { - "server":"my_server_ip", - "server_port":8388, - "local_address": "127.0.0.1", - "local_port":1080, - "password":"mypassword", - "timeout":300, - "method":"aes-256-cfb", - "fast_open": false - } - -Explanation of the fields: - -| Name | Explanation | -| ------------- | ----------------------------------------------- | -| server | the address your server listens | -| server_port | server port | -| local_address | the address your local listens | -| local_port | local port | -| password | password used for encryption | -| timeout | in seconds | -| method | default: "aes-256-cfb", see [Encryption] | -| fast_open | use [TCP_FASTOPEN], true / false | -| workers | number of workers, available on Unix/Linux | +Usage +----- On your server: -To run in the foreground: - - ssserver -c /etc/shadowsocks.json + ssserver -p 8000 -k password -m rc4-md5 To run in the background: - ssserver -c /etc/shadowsocks.json -d start - ssserver -c /etc/shadowsocks.json -d stop + ssserver -p 8000 -k password -m rc4-md5 -d start + ssserver -p 8000 -k password -m rc4-md5 -d stop On your client machine, use the same configuration as your server. Check the README of your client for more information. -Command Line Options --------------------- - -Check the options via `-h`.You can use args to override settings from -`config.json`. - - sslocal -s server_name -p server_port -l local_port -k password -m bf-cfb - ssserver -p server_port -k password -m bf-cfb --workers 2 - ssserver -c /etc/shadowsocks/config.json -d start --pid-file=/tmp/shadowsocks.pid - ssserver -c /etc/shadowsocks/config.json -d stop --pid-file=/tmp/shadowsocks.pid +Check the options via `-h`. You can also use a [Configuration] file instead. Documentation ------------- @@ -121,6 +80,7 @@ Bugs and Issues [Android]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#android [Build Status]: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat [Chinese Readme]: https://github.com/shadowsocks/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E +[Configuration]: https://github.com/shadowsocks/shadowsocks/wiki/Configuration-via-Config-File [Coverage Status]: https://jenkins.shadowvpn.org/result/shadowsocks [Coverage]: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/htmlcov/index.html [Debian sid]: https://packages.debian.org/unstable/python/shadowsocks From 1db1e4f4e99d349e918f6601dabe5e4e12d596cd Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 4 Jan 2015 23:22:26 +0800 Subject: [PATCH 048/146] Update README.md --- README.md | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index b10d9f7..511a29a 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,6 @@ A fast tunnel proxy that helps you bypass firewalls. Install ------- -You'll have a client on your local side, and setup a server on a -remote server. - -### Client - -* [Windows] / [OS X] -* [Android] / [iOS] -* [OpenWRT] - ### Server #### Debian / Ubuntu: @@ -39,13 +30,14 @@ source list. #### Windows: -Install [OpenSSL for Windows]. Download [the package]. -Use `python shadowsocks/server.py` instead of `ssserver` command below. +1. Install [OpenSSL for Windows]. +2. Download [the package]. +3. Use `python shadowsocks/server.py` instead of `ssserver` command below. Usage ----- -On your server: +### Server ssserver -p 8000 -k password -m rc4-md5 @@ -54,10 +46,17 @@ To run in the background: ssserver -p 8000 -k password -m rc4-md5 -d start ssserver -p 8000 -k password -m rc4-md5 -d stop -On your client machine, use the same configuration as your server. Check the -README of your client for more information. +Check all the options via `-h`. You can also use a [Configuration] file +instead. -Check the options via `-h`. You can also use a [Configuration] file instead. +### Client + +* [Windows] / [OS X] +* [Android] / [iOS] +* [OpenWRT] + +Use GUI clients on your local PC/phones. Check the README of your client +for more information. Documentation ------------- From d01ef02b5a96f2e7b4614b3fc9156b476bece0f7 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 4 Jan 2015 23:27:58 +0800 Subject: [PATCH 049/146] Update README.md --- README.md | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 511a29a..e76de83 100644 --- a/README.md +++ b/README.md @@ -9,35 +9,26 @@ A fast tunnel proxy that helps you bypass firewalls. [中文说明][Chinese Readme] -Install -------- +Server +------ -### Server +### Install #### Debian / Ubuntu: apt-get install python-pip pip install shadowsocks -Or simply `apt-get install shadowsocks` if you have [Debian sid] in your -source list. - #### CentOS: - yum install python-setuptools - easy_install pip + yum install python-setuptools && easy_install pip pip install shadowsocks #### Windows: -1. Install [OpenSSL for Windows]. -2. Download [the package]. -3. Use `python shadowsocks/server.py` instead of `ssserver` command below. +See [Install Server on Windows] -Usage ------ - -### Server +### Usage ssserver -p 8000 -k password -m rc4-md5 @@ -49,7 +40,8 @@ To run in the background: Check all the options via `-h`. You can also use a [Configuration] file instead. -### Client +Client +------ * [Windows] / [OS X] * [Android] / [iOS] @@ -83,12 +75,11 @@ Bugs and Issues [Coverage Status]: https://jenkins.shadowvpn.org/result/shadowsocks [Coverage]: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/htmlcov/index.html [Debian sid]: https://packages.debian.org/unstable/python/shadowsocks -[the package]: https://pypi.python.org/pypi/shadowsocks [Encryption]: https://github.com/shadowsocks/shadowsocks/wiki/Encryption [iOS]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help [Issue Tracker]: https://github.com/shadowsocks/shadowsocks/issues?state=open +[Install Server on Windows]: https://github.com/shadowsocks/shadowsocks/wiki/Install-Shadowsocks-Server-on-Windows [Mailing list]: https://groups.google.com/group/shadowsocks -[OpenSSL for Windows]: https://slproweb.com/products/Win32OpenSSL.html [OpenWRT]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#openwrt [OS X]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Shadowsocks-for-OSX-Help [PyPI]: https://pypi.python.org/pypi/shadowsocks From a3faf2c5fb5e8150bd738fcf2503052b6c7dc8b8 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 4 Jan 2015 23:30:29 +0800 Subject: [PATCH 050/146] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e76de83..2ddff7a 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,7 @@ for more information. Documentation ------------- -You can find all the documentation in the wiki: -https://github.com/shadowsocks/shadowsocks/wiki +You can find all the documentation in the [Wiki]. License ------- @@ -87,4 +86,5 @@ Bugs and Issues [TCP_FASTOPEN]: https://github.com/shadowsocks/shadowsocks/wiki/TCP-Fast-Open [Travis CI]: https://travis-ci.org/shadowsocks/shadowsocks [Troubleshooting]: https://github.com/shadowsocks/shadowsocks/wiki/Troubleshooting +[Wiki]: https://github.com/shadowsocks/shadowsocks/wiki [Windows]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#windows From f7a63a0ad7f248d825b2c08e9ef2050743f5885f Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 4 Jan 2015 23:35:41 +0800 Subject: [PATCH 051/146] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 2ddff7a..2a174fd 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,6 @@ Bugs and Issues [Coverage Status]: https://jenkins.shadowvpn.org/result/shadowsocks [Coverage]: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/htmlcov/index.html [Debian sid]: https://packages.debian.org/unstable/python/shadowsocks -[Encryption]: https://github.com/shadowsocks/shadowsocks/wiki/Encryption [iOS]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help [Issue Tracker]: https://github.com/shadowsocks/shadowsocks/issues?state=open [Install Server on Windows]: https://github.com/shadowsocks/shadowsocks/wiki/Install-Shadowsocks-Server-on-Windows @@ -83,7 +82,6 @@ Bugs and Issues [OS X]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Shadowsocks-for-OSX-Help [PyPI]: https://pypi.python.org/pypi/shadowsocks [PyPI version]: https://img.shields.io/pypi/v/shadowsocks.svg?style=flat -[TCP_FASTOPEN]: https://github.com/shadowsocks/shadowsocks/wiki/TCP-Fast-Open [Travis CI]: https://travis-ci.org/shadowsocks/shadowsocks [Troubleshooting]: https://github.com/shadowsocks/shadowsocks/wiki/Troubleshooting [Wiki]: https://github.com/shadowsocks/shadowsocks/wiki From 85cbac127d9a5892c3dbdded8774d48fc016b4a5 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 4 Jan 2015 23:44:03 +0800 Subject: [PATCH 052/146] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a174fd..bb4ab0d 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Bugs and Issues [Issue Tracker]: https://github.com/shadowsocks/shadowsocks/issues?state=open [Install Server on Windows]: https://github.com/shadowsocks/shadowsocks/wiki/Install-Shadowsocks-Server-on-Windows [Mailing list]: https://groups.google.com/group/shadowsocks -[OpenWRT]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#openwrt +[OpenWRT]: https://github.com/shadowsocks/openwrt-shadowsocks [OS X]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Shadowsocks-for-OSX-Help [PyPI]: https://pypi.python.org/pypi/shadowsocks [PyPI version]: https://img.shields.io/pypi/v/shadowsocks.svg?style=flat From 0bc0117cd7203d773b6d6cc8a57320f8117ea742 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 3 Jan 2015 17:19:12 +0800 Subject: [PATCH 053/146] update help url --- shadowsocks/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index ff6801c..475bf10 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -264,7 +264,7 @@ General options: -v, -vv verbose mode -q, -qq quiet mode, only show warnings/errors -Online help: +Online help: ''') @@ -294,7 +294,7 @@ General options: -v, -vv verbose mode -q, -qq quiet mode, only show warnings/errors -Online help: +Online help: ''') From b12ba5383c873bfddf4595276a7fa1c8fd0b2089 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 6 Jan 2015 19:35:31 +0800 Subject: [PATCH 054/146] Update CONTRIBUTING.md --- CONTRIBUTING.md | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c164423..8abe204 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,38 +1,27 @@ How to contribute ================= -在你提交问题前,请先[自行诊断]一下。提交时附上诊断过程中的问题和下列结果, -否则如果我们无法重现你的问题,也就不能帮助你。 +Pull Requests +------------- -Before you submit issues, please read [Troubleshooting] and take a few minutes -to read this guide. - -问题反馈 -------- - -请提交下面的信息: - -1. 你是如何搭建环境的?(操作系统,Shadowsocks 版本) -2. 操作步骤是什么? -3. 浏览器里的现象是什么?一直转菊花,还是有提示错误? -4. 发生错误时,客户端和服务端最后一部分日志。 -5. 其它你认为可能和问题有关的信息。 - -如果你不清楚其中某条的含义, 可以直接跳过那一条。 +1. Pull requests are welcome. If you would like to add a large feature +or make a significant change, make sure to open an issue to dicuss with +people first. +2. Follow PEP8. +3. Make sure to pass the unit tests. Write unit tests for new modules if +needed. Issues ------ -Please include the following information in your submission: +1. Only bugs and feature requests are accepted here. +2. We'll only work on important features. If the feature you're asking only +benefits a few people, you'd better implement the feature yourself and send us +a pull request, or ask some of your friends to do so. +3. We don't answer questions of any other types here. Since very few people +are watching the issue tracker here, you'll probably get no help from here. +Read [Troubleshooting] and get help from forums or [mailing lists]. -1. How did you set up your environment? (OS, version of Shadowsocks) -2. Steps to reproduce the problem. -3. What happened in your browser? Just no response, or any error message? -4. 10 lines of log on the local side of shadowsocks when the error happened. -5. 10 lines of log on the server side of shadowsocks when the error happened. -6. Any other useful information. - -Skip any of them if you don't know its meaning. [Troubleshooting]: https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting -[自行诊断]: https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting +[mailing lists]: https://groups.google.com/forum/#!forum/shadowsocks From 09f8f1ccc8cd09ad395388fe24edbc55c2a05a41 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 6 Jan 2015 19:36:34 +0800 Subject: [PATCH 055/146] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8abe204..eb16f6c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ Pull Requests ------------- 1. Pull requests are welcome. If you would like to add a large feature -or make a significant change, make sure to open an issue to dicuss with +or make a significant change, make sure to open an issue to discuss with people first. 2. Follow PEP8. 3. Make sure to pass the unit tests. Write unit tests for new modules if From ae57ebde68e57155b1fc21a0c4b4e030c0135774 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 6 Jan 2015 19:39:23 +0800 Subject: [PATCH 056/146] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index bb4ab0d..352addf 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,6 @@ shadowsocks A fast tunnel proxy that helps you bypass firewalls. -[中文说明][Chinese Readme] - Server ------ @@ -69,7 +67,6 @@ Bugs and Issues [Android]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#android [Build Status]: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat -[Chinese Readme]: https://github.com/shadowsocks/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E [Configuration]: https://github.com/shadowsocks/shadowsocks/wiki/Configuration-via-Config-File [Coverage Status]: https://jenkins.shadowvpn.org/result/shadowsocks [Coverage]: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/htmlcov/index.html From 69d1d876bc0d858dabc4aa1fb4575630d00728e2 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 6 Jan 2015 19:39:39 +0800 Subject: [PATCH 057/146] Update README.rst --- README.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.rst b/README.rst index da9b227..97d1d9f 100644 --- a/README.rst +++ b/README.rst @@ -5,8 +5,6 @@ shadowsocks A fast tunnel proxy that helps you bypass firewalls. -`中文说明 `__ - Install ------- From a90c354f42a4749773f2923ec1bdb1ab10cc15c4 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 6 Jan 2015 19:41:20 +0800 Subject: [PATCH 058/146] update README --- README.rst | 120 +++++++++++++---------------------------------------- 1 file changed, 28 insertions(+), 92 deletions(-) diff --git a/README.rst b/README.rst index 97d1d9f..510b233 100644 --- a/README.rst +++ b/README.rst @@ -5,24 +5,11 @@ shadowsocks A fast tunnel proxy that helps you bypass firewalls. -Install -------- - -You'll have a client on your local side, and setup a server on a remote -server. - -Client -~~~~~~ - -- `Windows `__ - / `OS - X `__ -- `Android `__ - / `iOS `__ -- `OpenWRT `__ - Server -~~~~~~ +------ + +Install +~~~~~~~ Debian / Ubuntu: ^^^^^^^^^^^^^^^^ @@ -32,107 +19,56 @@ Debian / Ubuntu: apt-get install python-pip pip install shadowsocks -Or simply ``apt-get install shadowsocks`` if you have `Debian -sid `__ in your -source list. - CentOS: ^^^^^^^ :: - yum install python-setuptools - easy_install pip + yum install python-setuptools && easy_install pip pip install shadowsocks Windows: ^^^^^^^^ -Download `OpenSSL for -Windows `__ and install. -Then install shadowsocks via easy\_install and pip as Linux. If you -don't know how to use them, you can directly download `the -package `__, and use -``python shadowsocks/server.py`` instead of ``ssserver`` command below. +See `Install Server on +Windows `__ -Configuration -------------- - -On your server create a config file ``/etc/shadowsocks.json``. Example: +Usage +~~~~~ :: - { - "server":"my_server_ip", - "server_port":8388, - "local_address": "127.0.0.1", - "local_port":1080, - "password":"mypassword", - "timeout":300, - "method":"aes-256-cfb", - "fast_open": false - } - -Explanation of the fields: - -+------------------+-----------------------------------------------------------------------------------------------------------+ -| Name | Explanation | -+==================+===========================================================================================================+ -| server | the address your server listens | -+------------------+-----------------------------------------------------------------------------------------------------------+ -| server\_port | server port | -+------------------+-----------------------------------------------------------------------------------------------------------+ -| local\_address | the address your local listens | -+------------------+-----------------------------------------------------------------------------------------------------------+ -| local\_port | local port | -+------------------+-----------------------------------------------------------------------------------------------------------+ -| password | password used for encryption | -+------------------+-----------------------------------------------------------------------------------------------------------+ -| timeout | in seconds | -+------------------+-----------------------------------------------------------------------------------------------------------+ -| method | default: "aes-256-cfb", see `Encryption `__ | -+------------------+-----------------------------------------------------------------------------------------------------------+ -| fast\_open | use `TCP\_FASTOPEN `__, true / false | -+------------------+-----------------------------------------------------------------------------------------------------------+ -| workers | number of workers, available on Unix/Linux | -+------------------+-----------------------------------------------------------------------------------------------------------+ - -On your server: - -To run in the foreground: - -:: - - ssserver -c /etc/shadowsocks.json + ssserver -p 8000 -k password -m rc4-md5 To run in the background: :: - ssserver -c /etc/shadowsocks.json -d start - ssserver -c /etc/shadowsocks.json -d stop + ssserver -p 8000 -k password -m rc4-md5 -d start + ssserver -p 8000 -k password -m rc4-md5 -d stop -On your client machine, use the same configuration as your server. Check -the README of your client for more information. +Check all the options via ``-h``. You can also use a +`Configuration `__ +file instead. -Command Line Options --------------------- +Client +------ -Check the options via ``-h``.You can use args to override settings from -``config.json``. +- `Windows `__ + / `OS + X `__ +- `Android `__ + / `iOS `__ +- `OpenWRT `__ -:: - - sslocal -s server_name -p server_port -l local_port -k password -m bf-cfb - ssserver -p server_port -k password -m bf-cfb --workers 2 - ssserver -c /etc/shadowsocks/config.json -d start --pid-file=/tmp/shadowsocks.pid - ssserver -c /etc/shadowsocks/config.json -d stop --pid-file=/tmp/shadowsocks.pid +Use GUI clients on your local PC/phones. Check the README of your client +for more information. Documentation ------------- -You can find all the documentation in the wiki: -https://github.com/shadowsocks/shadowsocks/wiki +You can find all the documentation in the +`Wiki `__. License ------- @@ -145,7 +81,7 @@ Bugs and Issues - `Troubleshooting `__ - `Issue Tracker `__ -- `Mailing list `__ +- `Mailing list `__ .. |PyPI version| image:: https://img.shields.io/pypi/v/shadowsocks.svg?style=flat :target: https://pypi.python.org/pypi/shadowsocks From 363965c364709c31afedccb15c9bb924ddb9874e Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 6 Jan 2015 20:13:45 +0800 Subject: [PATCH 059/146] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eb16f6c..04aaa02 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -How to contribute +How to Contribute ================= Pull Requests From 4598e09b78cb92fca58eae5f270ecd69d0c61bdd Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 7 Jan 2015 00:24:40 +0800 Subject: [PATCH 060/146] now works on OpenWRT --- shadowsocks/crypto/ctypes_libsodium.py | 5 +++++ shadowsocks/crypto/ctypes_openssl.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/shadowsocks/crypto/ctypes_libsodium.py b/shadowsocks/crypto/ctypes_libsodium.py index 69b9de9..e74d577 100644 --- a/shadowsocks/crypto/ctypes_libsodium.py +++ b/shadowsocks/crypto/ctypes_libsodium.py @@ -42,11 +42,16 @@ def load_libsodium(): global loaded, libsodium, buf from ctypes.util import find_library + libsodium_path = None for p in ('sodium', 'libsodium'): libsodium_path = find_library(p) if libsodium_path: break else: + import glob + for libsodium_path in glob.glob('/usr/lib/libsodium.*'): + pass + if libsodium_path is None: raise Exception('libsodium not found') logging.info('loading libsodium from %s', libsodium_path) libsodium = CDLL(libsodium_path) diff --git a/shadowsocks/crypto/ctypes_openssl.py b/shadowsocks/crypto/ctypes_openssl.py index 0ef8ce0..9e0dfca 100644 --- a/shadowsocks/crypto/ctypes_openssl.py +++ b/shadowsocks/crypto/ctypes_openssl.py @@ -39,11 +39,16 @@ def load_openssl(): global loaded, libcrypto, buf from ctypes.util import find_library + libcrypto_path = None for p in ('crypto', 'eay32', 'libeay32'): libcrypto_path = find_library(p) if libcrypto_path: break else: + import glob + for libcrypto_path in glob.glob('/usr/lib/libcrypto.*'): + pass + if libcrypto_path is None: raise Exception('libcrypto(OpenSSL) not found') logging.info('loading libcrypto from %s', libcrypto_path) libcrypto = CDLL(libcrypto_path) From b6efc0efd900d4a27d3805a0b5fd38aa1105474a Mon Sep 17 00:00:00 2001 From: clowwindy Date: Thu, 8 Jan 2015 04:18:38 +0800 Subject: [PATCH 061/146] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 352addf..7b351ba 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ Bugs and Issues * [Mailing list] + [Android]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#android [Build Status]: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat [Configuration]: https://github.com/shadowsocks/shadowsocks/wiki/Configuration-via-Config-File From 176e97bb452d5715417a6e9e7cb87a2ff68a935f Mon Sep 17 00:00:00 2001 From: fa08c <0xfa08c@gmail.com> Date: Thu, 8 Jan 2015 11:03:12 +0800 Subject: [PATCH 062/146] Fixing some minor issues --- shadowsocks/crypto/ctypes_openssl.py | 2 +- shadowsocks/eventloop.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks/crypto/ctypes_openssl.py b/shadowsocks/crypto/ctypes_openssl.py index 9e0dfca..5ddff15 100644 --- a/shadowsocks/crypto/ctypes_openssl.py +++ b/shadowsocks/crypto/ctypes_openssl.py @@ -83,9 +83,9 @@ def load_cipher(cipher_name): class CtypesCrypto(object): def __init__(self, cipher_name, key, iv, op): + self._ctx = None if not loaded: load_openssl() - self._ctx = None cipher = libcrypto.EVP_get_cipherbyname(cipher_name) if not cipher: cipher = load_cipher(cipher_name) diff --git a/shadowsocks/eventloop.py b/shadowsocks/eventloop.py index 55c30bb..2b2459f 100644 --- a/shadowsocks/eventloop.py +++ b/shadowsocks/eventloop.py @@ -234,7 +234,7 @@ class EventLoop(object): traceback.print_exc() for handler in self._handlers_to_remove: self._handlers.remove(handler) - self._handlers_to_remove = [] + self._handlers_to_remove = [] self._iterating = False From 8b2deb01d8967cd9afb70600cd0488c5fa34afee Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 8 Jan 2015 11:22:34 +0800 Subject: [PATCH 063/146] Set default method as bytes. Default method as string may cause false error report. --- shadowsocks/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 475bf10..0247d0f 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -172,7 +172,7 @@ def get_config(is_local): sys.exit(2) config['password'] = config.get('password', '') - config['method'] = config.get('method', 'aes-256-cfb') + config['method'] = config.get('method', b'aes-256-cfb') config['port_password'] = config.get('port_password', None) config['timeout'] = int(config.get('timeout', 300)) config['fast_open'] = config.get('fast_open', False) From 18da3554ff3e51aaa53a6b051fc8e35b04cda6b8 Mon Sep 17 00:00:00 2001 From: fa08c <0xfa08c@gmail.com> Date: Thu, 8 Jan 2015 08:27:28 -0600 Subject: [PATCH 064/146] Minor fix: never use except: unless you reraise --- shadowsocks/crypto/rc4_md5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks/crypto/rc4_md5.py b/shadowsocks/crypto/rc4_md5.py index 3062dcc..aa22b16 100644 --- a/shadowsocks/crypto/rc4_md5.py +++ b/shadowsocks/crypto/rc4_md5.py @@ -39,7 +39,7 @@ def create_cipher(alg, key, iv, op, key_as_bytes=0, d=None, salt=None, try: from shadowsocks.crypto import ctypes_openssl return ctypes_openssl.CtypesCrypto(b'rc4', rc4_key, b'', op) - except: + except Exception: import M2Crypto.EVP return M2Crypto.EVP.Cipher(b'rc4', rc4_key, b'', op, key_as_bytes=0, d='md5', salt=None, i=1, From 3294a92a61463abd8527eb1e3f83b1666b476204 Mon Sep 17 00:00:00 2001 From: fa08c <0xfa08c@gmail.com> Date: Sat, 10 Jan 2015 22:26:20 +0800 Subject: [PATCH 065/146] Search libcrypto.so in more locations Some linux distro may store .so in some alternative locations. Now we try harder to find them. --- shadowsocks/crypto/ctypes_openssl.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/shadowsocks/crypto/ctypes_openssl.py b/shadowsocks/crypto/ctypes_openssl.py index 5ddff15..6e80461 100644 --- a/shadowsocks/crypto/ctypes_openssl.py +++ b/shadowsocks/crypto/ctypes_openssl.py @@ -45,9 +45,29 @@ def load_openssl(): if libcrypto_path: break else: + # 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 libcrypto_path in glob.glob('/usr/lib/libcrypto.*'): - pass + import sys + + patterns = ['/usr/lib/libcrypto.*'] + + # Some linux distros may store so in alternative locations + if sys.maxsize > 2 ** 32: + # Python is 64-bit + patterns.extend(['/usr/lib64/libcrypto.*']) + else: + # Python is 32-bit + patterns.extend(['/usr/lib32/libcrypto.*']) + + for pat in patterns: + files = glob.glob(pat) + if files: + libcrypto_path = files[0] + break + if libcrypto_path is None: raise Exception('libcrypto(OpenSSL) not found') logging.info('loading libcrypto from %s', libcrypto_path) From 6eadfca78ec2e731e9f7f9445c1143899009ed58 Mon Sep 17 00:00:00 2001 From: fa08c <0xfa08c@gmail.com> Date: Sun, 11 Jan 2015 15:19:31 +0800 Subject: [PATCH 066/146] Removing trailing whites --- shadowsocks/crypto/ctypes_openssl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks/crypto/ctypes_openssl.py b/shadowsocks/crypto/ctypes_openssl.py index 6e80461..22238c0 100644 --- a/shadowsocks/crypto/ctypes_openssl.py +++ b/shadowsocks/crypto/ctypes_openssl.py @@ -45,8 +45,8 @@ def load_openssl(): if libcrypto_path: break else: - # We may get here when find_library fails because, for example, - # the user does not have sufficient privileges to access those + # 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 From ebfd1486d884190dd26b7af5c694b9dadccd85c3 Mon Sep 17 00:00:00 2001 From: fa08c <0xfa08c@gmail.com> Date: Mon, 12 Jan 2015 10:32:03 +0800 Subject: [PATCH 067/146] Removing the overhead of creating a new list per event --- shadowsocks/eventloop.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shadowsocks/eventloop.py b/shadowsocks/eventloop.py index 2b2459f..304b229 100644 --- a/shadowsocks/eventloop.py +++ b/shadowsocks/eventloop.py @@ -232,9 +232,10 @@ class EventLoop(object): logging.error(e) import traceback traceback.print_exc() - for handler in self._handlers_to_remove: - self._handlers.remove(handler) - self._handlers_to_remove = [] + if self._handlers_to_remove: + for handler in self._handlers_to_remove: + self._handlers.remove(handler) + self._handlers_to_remove = [] self._iterating = False From 3d03dbf7169dc4d16e4728bd74710573a2a86795 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 12 Jan 2015 14:00:35 +0800 Subject: [PATCH 068/146] extract find_library --- shadowsocks/crypto/ctypes_libsodium.py | 28 +++-------- shadowsocks/crypto/ctypes_openssl.py | 44 ++++------------- shadowsocks/crypto/util.py | 65 ++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 57 deletions(-) diff --git a/shadowsocks/crypto/ctypes_libsodium.py b/shadowsocks/crypto/ctypes_libsodium.py index e74d577..7e9f6a4 100644 --- a/shadowsocks/crypto/ctypes_libsodium.py +++ b/shadowsocks/crypto/ctypes_libsodium.py @@ -23,10 +23,11 @@ from __future__ import absolute_import, division, print_function, \ with_statement -import logging -from ctypes import CDLL, c_char_p, c_int, c_ulonglong, byref, \ +from ctypes import c_char_p, c_int, c_ulonglong, byref, \ create_string_buffer, c_void_p +from shadowsocks.crypto import util + __all__ = ['ciphers'] libsodium = None @@ -41,21 +42,11 @@ BLOCK_SIZE = 64 def load_libsodium(): global loaded, libsodium, buf - from ctypes.util import find_library - libsodium_path = None - for p in ('sodium', 'libsodium'): - libsodium_path = find_library(p) - if libsodium_path: - break - else: - import glob - for libsodium_path in glob.glob('/usr/lib/libsodium.*'): - pass - if libsodium_path is None: + libsodium = util.find_library('sodium', 'crypto_stream_salsa20_xor_ic', + 'libsodium') + if libsodium is None: raise Exception('libsodium not found') - logging.info('loading libsodium from %s', libsodium_path) - libsodium = CDLL(libsodium_path) - libsodium.sodium_init.restype = c_int + libsodium.crypto_stream_salsa20_xor_ic.restype = c_int libsodium.crypto_stream_salsa20_xor_ic.argtypes = (c_void_p, c_char_p, c_ulonglong, @@ -67,8 +58,6 @@ def load_libsodium(): c_char_p, c_ulonglong, c_char_p) - libsodium.sodium_init() - buf = create_string_buffer(buf_size) loaded = True @@ -118,8 +107,6 @@ ciphers = { def test_salsa20(): - from shadowsocks.crypto import util - cipher = Salsa20Crypto(b'salsa20', b'k' * 32, b'i' * 16, 1) decipher = Salsa20Crypto(b'salsa20', b'k' * 32, b'i' * 16, 0) @@ -127,7 +114,6 @@ def test_salsa20(): def test_chacha20(): - from shadowsocks.crypto import util cipher = Salsa20Crypto(b'chacha20', b'k' * 32, b'i' * 16, 1) decipher = Salsa20Crypto(b'chacha20', b'k' * 32, b'i' * 16, 0) diff --git a/shadowsocks/crypto/ctypes_openssl.py b/shadowsocks/crypto/ctypes_openssl.py index 22238c0..3bdd99d 100644 --- a/shadowsocks/crypto/ctypes_openssl.py +++ b/shadowsocks/crypto/ctypes_openssl.py @@ -23,10 +23,11 @@ from __future__ import absolute_import, division, print_function, \ with_statement -import logging -from ctypes import CDLL, c_char_p, c_int, c_long, byref,\ +from ctypes import c_char_p, c_int, c_long, byref,\ create_string_buffer, c_void_p +from shadowsocks.crypto import util + __all__ = ['ciphers'] libcrypto = None @@ -38,40 +39,12 @@ buf_size = 2048 def load_openssl(): global loaded, libcrypto, buf - from ctypes.util import find_library - libcrypto_path = None - for p in ('crypto', 'eay32', 'libeay32'): - libcrypto_path = find_library(p) - if libcrypto_path: - break - else: - # 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 - import sys - - patterns = ['/usr/lib/libcrypto.*'] - - # Some linux distros may store so in alternative locations - if sys.maxsize > 2 ** 32: - # Python is 64-bit - patterns.extend(['/usr/lib64/libcrypto.*']) - else: - # Python is 32-bit - patterns.extend(['/usr/lib32/libcrypto.*']) - - for pat in patterns: - files = glob.glob(pat) - if files: - libcrypto_path = files[0] - break - - if libcrypto_path is None: + libcrypto = util.find_library(('crypto', 'eay32'), + 'EVP_get_cipherbyname', + 'libcrypto') + if libcrypto is None: raise Exception('libcrypto(OpenSSL) not found') - logging.info('loading libcrypto from %s', libcrypto_path) - libcrypto = CDLL(libcrypto_path) + libcrypto.EVP_get_cipherbyname.restype = c_void_p libcrypto.EVP_CIPHER_CTX_new.restype = c_void_p @@ -173,7 +146,6 @@ ciphers = { def run_method(method): - from shadowsocks.crypto import util cipher = CtypesCrypto(method, b'k' * 32, b'i' * 16, 1) decipher = CtypesCrypto(method, b'k' * 32, b'i' * 16, 0) diff --git a/shadowsocks/crypto/util.py b/shadowsocks/crypto/util.py index 3bac1db..68cfabd 100644 --- a/shadowsocks/crypto/util.py +++ b/shadowsocks/crypto/util.py @@ -20,6 +20,57 @@ # 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 logging + + +def find_library(possible_lib_names, search_symbol, library_name): + from ctypes.util import find_library + from ctypes import CDLL + + paths = [] + + if type(possible_lib_names) is not list: + possible_lib_names = [possible_lib_names] + + for name in possible_lib_names: + path = 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 possible_lib_names: + patterns = [ + '/usr/local/lib*/lib%s.*' % name, + '/usr/lib*/lib%s.*' % name, + 'lib%s.*' % name, + '%s.dll' % name, + 'lib%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: + pass + return None + def run_cipher(cipher, decipher): from os import urandom @@ -49,3 +100,17 @@ def run_cipher(cipher, decipher): 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('crypto', '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'], + 'EVP_CipherUpdate', 'libc') is not None + + +if __name__ == '__main__': + test_find_library() From e582b2b9298755bb0bd9f3ce2d0aa6c26a46bbc7 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 12 Jan 2015 14:05:20 +0800 Subject: [PATCH 069/146] rename ctypes_* and remove salsa20-ctr --- .../crypto/{ctypes_openssl.py => openssl.py} | 56 +++---- shadowsocks/crypto/rc4_md5.py | 4 +- shadowsocks/crypto/salsa20_ctr.py | 140 ------------------ .../crypto/{ctypes_libsodium.py => sodium.py} | 14 +- shadowsocks/encrypt.py | 10 +- 5 files changed, 41 insertions(+), 183 deletions(-) rename shadowsocks/crypto/{ctypes_openssl.py => openssl.py} (78%) delete mode 100644 shadowsocks/crypto/salsa20_ctr.py rename shadowsocks/crypto/{ctypes_libsodium.py => sodium.py} (91%) diff --git a/shadowsocks/crypto/ctypes_openssl.py b/shadowsocks/crypto/openssl.py similarity index 78% rename from shadowsocks/crypto/ctypes_openssl.py rename to shadowsocks/crypto/openssl.py index 3bdd99d..bb11627 100644 --- a/shadowsocks/crypto/ctypes_openssl.py +++ b/shadowsocks/crypto/openssl.py @@ -74,7 +74,7 @@ def load_cipher(cipher_name): return None -class CtypesCrypto(object): +class OpenSSLCrypto(object): def __init__(self, cipher_name, key, iv, op): self._ctx = None if not loaded: @@ -117,38 +117,38 @@ class CtypesCrypto(object): ciphers = { - b'aes-128-cfb': (16, 16, CtypesCrypto), - b'aes-192-cfb': (24, 16, CtypesCrypto), - b'aes-256-cfb': (32, 16, CtypesCrypto), - b'aes-128-ofb': (16, 16, CtypesCrypto), - b'aes-192-ofb': (24, 16, CtypesCrypto), - b'aes-256-ofb': (32, 16, CtypesCrypto), - b'aes-128-ctr': (16, 16, CtypesCrypto), - b'aes-192-ctr': (24, 16, CtypesCrypto), - b'aes-256-ctr': (32, 16, CtypesCrypto), - b'aes-128-cfb8': (16, 16, CtypesCrypto), - b'aes-192-cfb8': (24, 16, CtypesCrypto), - b'aes-256-cfb8': (32, 16, CtypesCrypto), - b'aes-128-cfb1': (16, 16, CtypesCrypto), - b'aes-192-cfb1': (24, 16, CtypesCrypto), - b'aes-256-cfb1': (32, 16, CtypesCrypto), - b'bf-cfb': (16, 8, CtypesCrypto), - b'camellia-128-cfb': (16, 16, CtypesCrypto), - b'camellia-192-cfb': (24, 16, CtypesCrypto), - b'camellia-256-cfb': (32, 16, CtypesCrypto), - b'cast5-cfb': (16, 8, CtypesCrypto), - b'des-cfb': (8, 8, CtypesCrypto), - b'idea-cfb': (16, 8, CtypesCrypto), - b'rc2-cfb': (16, 8, CtypesCrypto), - b'rc4': (16, 0, CtypesCrypto), - b'seed-cfb': (16, 16, CtypesCrypto), + b'aes-128-cfb': (16, 16, OpenSSLCrypto), + b'aes-192-cfb': (24, 16, OpenSSLCrypto), + b'aes-256-cfb': (32, 16, OpenSSLCrypto), + b'aes-128-ofb': (16, 16, OpenSSLCrypto), + b'aes-192-ofb': (24, 16, OpenSSLCrypto), + b'aes-256-ofb': (32, 16, OpenSSLCrypto), + b'aes-128-ctr': (16, 16, OpenSSLCrypto), + b'aes-192-ctr': (24, 16, OpenSSLCrypto), + b'aes-256-ctr': (32, 16, OpenSSLCrypto), + b'aes-128-cfb8': (16, 16, OpenSSLCrypto), + b'aes-192-cfb8': (24, 16, OpenSSLCrypto), + b'aes-256-cfb8': (32, 16, OpenSSLCrypto), + b'aes-128-cfb1': (16, 16, OpenSSLCrypto), + b'aes-192-cfb1': (24, 16, OpenSSLCrypto), + b'aes-256-cfb1': (32, 16, OpenSSLCrypto), + b'bf-cfb': (16, 8, OpenSSLCrypto), + b'camellia-128-cfb': (16, 16, OpenSSLCrypto), + b'camellia-192-cfb': (24, 16, OpenSSLCrypto), + b'camellia-256-cfb': (32, 16, OpenSSLCrypto), + b'cast5-cfb': (16, 8, OpenSSLCrypto), + b'des-cfb': (8, 8, OpenSSLCrypto), + b'idea-cfb': (16, 8, OpenSSLCrypto), + b'rc2-cfb': (16, 8, OpenSSLCrypto), + b'rc4': (16, 0, OpenSSLCrypto), + b'seed-cfb': (16, 16, OpenSSLCrypto), } def run_method(method): - cipher = CtypesCrypto(method, b'k' * 32, b'i' * 16, 1) - decipher = CtypesCrypto(method, b'k' * 32, b'i' * 16, 0) + cipher = OpenSSLCrypto(method, b'k' * 32, b'i' * 16, 1) + decipher = OpenSSLCrypto(method, b'k' * 32, b'i' * 16, 0) util.run_cipher(cipher, decipher) diff --git a/shadowsocks/crypto/rc4_md5.py b/shadowsocks/crypto/rc4_md5.py index aa22b16..728412d 100644 --- a/shadowsocks/crypto/rc4_md5.py +++ b/shadowsocks/crypto/rc4_md5.py @@ -37,8 +37,8 @@ def create_cipher(alg, key, iv, op, key_as_bytes=0, d=None, salt=None, rc4_key = md5.digest() try: - from shadowsocks.crypto import ctypes_openssl - return ctypes_openssl.CtypesCrypto(b'rc4', rc4_key, b'', op) + from shadowsocks.crypto import openssl + return openssl.OpenSSLCrypto(b'rc4', rc4_key, b'', op) except Exception: import M2Crypto.EVP return M2Crypto.EVP.Cipher(b'rc4', rc4_key, b'', op, diff --git a/shadowsocks/crypto/salsa20_ctr.py b/shadowsocks/crypto/salsa20_ctr.py deleted file mode 100644 index 0ea13b8..0000000 --- a/shadowsocks/crypto/salsa20_ctr.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python - -# 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. - -from __future__ import absolute_import, division, print_function, \ - with_statement - -import struct -import logging -import sys - -slow_xor = False -imported = False - -salsa20 = None -numpy = None - -BLOCK_SIZE = 16384 - - -def run_imports(): - global imported, slow_xor, salsa20, numpy - if not imported: - imported = True - try: - numpy = __import__('numpy') - except ImportError: - logging.error('can not import numpy, using SLOW XOR') - logging.error('please install numpy if you use salsa20') - slow_xor = True - try: - salsa20 = __import__('salsa20') - except ImportError: - logging.error('you have to install salsa20 before you use salsa20') - sys.exit(1) - - -def numpy_xor(a, b): - if slow_xor: - return py_xor_str(a, b) - dtype = numpy.byte - if len(a) % 4 == 0: - dtype = numpy.uint32 - elif len(a) % 2 == 0: - dtype = numpy.uint16 - - ab = numpy.frombuffer(a, dtype=dtype) - bb = numpy.frombuffer(b, dtype=dtype) - c = numpy.bitwise_xor(ab, bb) - r = c.tostring() - return r - - -def py_xor_str(a, b): - c = [] - if bytes == str: - for i in range(0, len(a)): - c.append(chr(ord(a[i]) ^ ord(b[i]))) - return ''.join(c) - else: - for i in range(0, len(a)): - c.append(a[i] ^ b[i]) - return bytes(c) - - -class Salsa20Cipher(object): - """a salsa20 CTR implemetation, provides m2crypto like cipher API""" - - def __init__(self, alg, key, iv, op, key_as_bytes=0, d=None, salt=None, - i=1, padding=1): - run_imports() - if alg != b'salsa20-ctr': - raise Exception('unknown algorithm') - self._key = key - self._nonce = struct.unpack('= BLOCK_SIZE: - self._next_stream() - self._pos = 0 - if not data: - break - return b''.join(results) - - -ciphers = { - b'salsa20-ctr': (32, 8, Salsa20Cipher), -} - - -def test(): - from shadowsocks.crypto import util - - cipher = Salsa20Cipher(b'salsa20-ctr', b'k' * 32, b'i' * 8, 1) - decipher = Salsa20Cipher(b'salsa20-ctr', b'k' * 32, b'i' * 8, 1) - - util.run_cipher(cipher, decipher) - - -if __name__ == '__main__': - test() diff --git a/shadowsocks/crypto/ctypes_libsodium.py b/shadowsocks/crypto/sodium.py similarity index 91% rename from shadowsocks/crypto/ctypes_libsodium.py rename to shadowsocks/crypto/sodium.py index 7e9f6a4..74fbb33 100644 --- a/shadowsocks/crypto/ctypes_libsodium.py +++ b/shadowsocks/crypto/sodium.py @@ -62,7 +62,7 @@ def load_libsodium(): loaded = True -class Salsa20Crypto(object): +class SodiumCrypto(object): def __init__(self, cipher_name, key, iv, op): if not loaded: load_libsodium() @@ -101,22 +101,22 @@ class Salsa20Crypto(object): ciphers = { - b'salsa20': (32, 8, Salsa20Crypto), - b'chacha20': (32, 8, Salsa20Crypto), + b'salsa20': (32, 8, SodiumCrypto), + b'chacha20': (32, 8, SodiumCrypto), } def test_salsa20(): - cipher = Salsa20Crypto(b'salsa20', b'k' * 32, b'i' * 16, 1) - decipher = Salsa20Crypto(b'salsa20', b'k' * 32, b'i' * 16, 0) + cipher = SodiumCrypto(b'salsa20', b'k' * 32, b'i' * 16, 1) + decipher = SodiumCrypto(b'salsa20', b'k' * 32, b'i' * 16, 0) util.run_cipher(cipher, decipher) def test_chacha20(): - cipher = Salsa20Crypto(b'chacha20', b'k' * 32, b'i' * 16, 1) - decipher = Salsa20Crypto(b'chacha20', b'k' * 32, b'i' * 16, 0) + cipher = SodiumCrypto(b'chacha20', b'k' * 32, b'i' * 16, 1) + decipher = SodiumCrypto(b'chacha20', b'k' * 32, b'i' * 16, 0) util.run_cipher(cipher, decipher) diff --git a/shadowsocks/encrypt.py b/shadowsocks/encrypt.py index ba02101..8b6b6d4 100644 --- a/shadowsocks/encrypt.py +++ b/shadowsocks/encrypt.py @@ -28,16 +28,14 @@ import sys import hashlib import logging -from shadowsocks.crypto import m2, rc4_md5, salsa20_ctr,\ - ctypes_openssl, ctypes_libsodium, table +from shadowsocks.crypto import m2, rc4_md5, openssl, sodium, table method_supported = {} method_supported.update(rc4_md5.ciphers) -method_supported.update(salsa20_ctr.ciphers) -method_supported.update(ctypes_openssl.ciphers) -method_supported.update(ctypes_libsodium.ciphers) -# let M2Crypto override ctypes_openssl +method_supported.update(openssl.ciphers) +method_supported.update(sodium.ciphers) +# let M2Crypto override openssl method_supported.update(m2.ciphers) method_supported.update(table.ciphers) From a4b0ea5b8fb80217c438219eb6522b7aa17db865 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 12 Jan 2015 14:08:21 +0800 Subject: [PATCH 070/146] fix find_library --- shadowsocks/crypto/util.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/shadowsocks/crypto/util.py b/shadowsocks/crypto/util.py index 68cfabd..c8cbab5 100644 --- a/shadowsocks/crypto/util.py +++ b/shadowsocks/crypto/util.py @@ -32,7 +32,7 @@ def find_library(possible_lib_names, search_symbol, library_name): paths = [] - if type(possible_lib_names) is not list: + if type(possible_lib_names) not in (list, tuple): possible_lib_names = [possible_lib_names] for name in possible_lib_names: @@ -105,10 +105,11 @@ def run_cipher(cipher, decipher): 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', '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'], + assert find_library(('notexist', 'c', 'crypto'), 'EVP_CipherUpdate', 'libc') is not None From 80b8bd70147a28b87456c2326af366226877e205 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 12 Jan 2015 14:11:33 +0800 Subject: [PATCH 071/146] remove M2Crypto completely --- .travis.yml | 4 +- shadowsocks/crypto/m2.py | 119 ---------------------------------- shadowsocks/crypto/rc4_md5.py | 11 +--- shadowsocks/encrypt.py | 10 +-- 4 files changed, 6 insertions(+), 138 deletions(-) delete mode 100644 shadowsocks/crypto/m2.py diff --git a/.travis.yml b/.travis.yml index 4fbe78c..7a222ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,10 +9,10 @@ cache: - dante-1.4.0 before_install: - sudo apt-get update -qq - - sudo apt-get install -qq build-essential libssl-dev swig python-m2crypto python-numpy dnsutils iproute nginx bc + - sudo apt-get install -qq build-essential dnsutils iproute nginx bc - sudo dd if=/dev/urandom of=/usr/share/nginx/www/file bs=1M count=10 - sudo service nginx restart - - pip install m2crypto salsa20 pep8 pyflakes nose coverage + - pip install pep8 pyflakes nose coverage - sudo tests/socksify/install.sh - sudo tests/libsodium/install.sh - sudo tests/setup_tc.sh diff --git a/shadowsocks/crypto/m2.py b/shadowsocks/crypto/m2.py deleted file mode 100644 index 5ad48a8..0000000 --- a/shadowsocks/crypto/m2.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python - -# 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. - -from __future__ import absolute_import, division, print_function, \ - with_statement - -import sys -import logging - -__all__ = ['ciphers'] - -has_m2 = True -try: - __import__('M2Crypto') -except ImportError: - has_m2 = False -if bytes != str: - has_m2 = False - - -def create_cipher(alg, key, iv, op, key_as_bytes=0, d=None, salt=None, i=1, - padding=1): - - import M2Crypto.EVP - return M2Crypto.EVP.Cipher(alg.replace('-', '_'), key, iv, op, - key_as_bytes=0, d='md5', salt=None, i=1, - padding=1) - - -def err(alg, key, iv, op, key_as_bytes=0, d=None, salt=None, i=1, padding=1): - logging.error(('M2Crypto is required to use %s, please run' - ' `apt-get install python-m2crypto`') % alg) - sys.exit(1) - - -if has_m2: - ciphers = { - b'aes-128-cfb': (16, 16, create_cipher), - b'aes-192-cfb': (24, 16, create_cipher), - b'aes-256-cfb': (32, 16, create_cipher), - b'bf-cfb': (16, 8, create_cipher), - b'camellia-128-cfb': (16, 16, create_cipher), - b'camellia-192-cfb': (24, 16, create_cipher), - b'camellia-256-cfb': (32, 16, create_cipher), - b'cast5-cfb': (16, 8, create_cipher), - b'des-cfb': (8, 8, create_cipher), - b'idea-cfb': (16, 8, create_cipher), - b'rc2-cfb': (16, 8, create_cipher), - b'rc4': (16, 0, create_cipher), - b'seed-cfb': (16, 16, create_cipher), - } -else: - ciphers = {} - - -def run_method(method): - from shadowsocks.crypto import util - - cipher = create_cipher(method, b'k' * 32, b'i' * 16, 1) - decipher = create_cipher(method, b'k' * 32, b'i' * 16, 0) - - util.run_cipher(cipher, decipher) - - -def check_env(): - # skip this test on pypy and Python 3 - try: - import __pypy__ - del __pypy__ - from nose.plugins.skip import SkipTest - raise SkipTest - except ImportError: - pass - if bytes != str: - from nose.plugins.skip import SkipTest - raise SkipTest - - -def test_aes_128_cfb(): - check_env() - run_method(b'aes-128-cfb') - - -def test_aes_256_cfb(): - check_env() - run_method(b'aes-256-cfb') - - -def test_bf_cfb(): - check_env() - run_method(b'bf-cfb') - - -def test_rc4(): - check_env() - run_method(b'rc4') - - -if __name__ == '__main__': - test_aes_128_cfb() diff --git a/shadowsocks/crypto/rc4_md5.py b/shadowsocks/crypto/rc4_md5.py index 728412d..33d481d 100644 --- a/shadowsocks/crypto/rc4_md5.py +++ b/shadowsocks/crypto/rc4_md5.py @@ -25,6 +25,7 @@ from __future__ import absolute_import, division, print_function, \ import hashlib +from shadowsocks.crypto import openssl __all__ = ['ciphers'] @@ -35,15 +36,7 @@ def create_cipher(alg, key, iv, op, key_as_bytes=0, d=None, salt=None, md5.update(key) md5.update(iv) rc4_key = md5.digest() - - try: - from shadowsocks.crypto import openssl - return openssl.OpenSSLCrypto(b'rc4', rc4_key, b'', op) - except Exception: - import M2Crypto.EVP - return M2Crypto.EVP.Cipher(b'rc4', rc4_key, b'', op, - key_as_bytes=0, d='md5', salt=None, i=1, - padding=1) + return openssl.OpenSSLCrypto(b'rc4', rc4_key, b'', op) ciphers = { diff --git a/shadowsocks/encrypt.py b/shadowsocks/encrypt.py index 8b6b6d4..3dd9264 100644 --- a/shadowsocks/encrypt.py +++ b/shadowsocks/encrypt.py @@ -28,24 +28,18 @@ import sys import hashlib import logging -from shadowsocks.crypto import m2, rc4_md5, openssl, sodium, table +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) -# let M2Crypto override openssl -method_supported.update(m2.ciphers) method_supported.update(table.ciphers) def random_string(length): - try: - import M2Crypto.Rand - return M2Crypto.Rand.rand_bytes(length) - except ImportError: - return os.urandom(length) + return os.urandom(length) cached_keys = {} From f29bfb0cc772c755f0a9648c79e741b4c0dda919 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 12 Jan 2015 14:15:41 +0800 Subject: [PATCH 072/146] remove salsa20-ctr test --- .jenkins.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/.jenkins.sh b/.jenkins.sh index 4c85f1c..7aa3257 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -38,7 +38,6 @@ run_test python tests/test.py --with-coverage -c tests/aes-cfb8.json run_test python tests/test.py --with-coverage -c tests/rc4-md5.json run_test python tests/test.py --with-coverage -c tests/salsa20.json run_test python tests/test.py --with-coverage -c tests/chacha20.json -run_test python tests/test.py --with-coverage -c tests/salsa20-ctr.json run_test python tests/test.py --with-coverage -c tests/table.json run_test python tests/test.py --with-coverage -c tests/server-multi-ports.json run_test python tests/test.py --with-coverage -s tests/server-multi-passwd.json -c tests/server-multi-passwd-client-side.json From eb94bd1cc34eb4b228e675011236d090329d5a7f Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 12 Jan 2015 22:30:03 +0800 Subject: [PATCH 073/146] support forbidden iplist --- shadowsocks/tcprelay.py | 9 +++++++++ shadowsocks/utils.py | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 79bd1a5..6afcad4 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -123,6 +123,10 @@ class TCPRelayHandler(object): self._downstream_status = WAIT_STATUS_INIT self._client_address = local_sock.getpeername()[:2] self._remote_address = None + if 'forbidden_ip' in self._config: + self._forbidden_iplist = self._config['forbidden_ip'] + else: + self._forbidden_iplist = None if is_local: self._chosen_server = self._get_a_server() fd_to_handlers[local_sock.fileno()] = self @@ -331,6 +335,10 @@ class TCPRelayHandler(object): if len(addrs) == 0: raise Exception("getaddrinfo failed for %s:%d" % (ip, port)) af, socktype, proto, canonname, sa = addrs[0] + if self._forbidden_iplist: + if common.to_str(sa[0]) in self._forbidden_iplist: + raise Exception('IP %s is in forbidden list, reject' % + common.to_str(sa[0])) remote_sock = socket.socket(af, socktype, proto) self._remote_sock = remote_sock self._fd_to_handlers[remote_sock.fileno()] = self @@ -346,6 +354,7 @@ class TCPRelayHandler(object): if result: ip = result[1] if ip: + try: self._stage = STAGE_CONNECTING remote_addr = ip diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 0247d0f..a51c965 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -100,7 +100,8 @@ def get_config(is_local): longopts = ['help', 'fast-open', 'pid-file=', 'log-file='] else: shortopts = 'hd:s:p:k:m:c:t:vq' - longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers='] + longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers=', + 'forbidden-ip='] try: config_path = find_config() optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts) @@ -146,6 +147,8 @@ def get_config(is_local): config['fast_open'] = True elif key == '--workers': config['workers'] = int(value) + elif key == '--forbidden-ip': + config['forbidden_ip'] = to_str(value).split(',') elif key in ('-h', '--help'): if is_local: print_local_help() @@ -286,6 +289,7 @@ Proxy options: -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 General options: -d start/stop/restart daemon mode From 4312eb9e58f9438b76870101169be5e065695c6c Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 12 Jan 2015 23:02:59 +0800 Subject: [PATCH 074/146] add forbidden support for UDP and add tests --- .jenkins.sh | 1 + shadowsocks/tcprelay.py | 4 ++-- shadowsocks/udprelay.py | 11 +++++++++++ tests/test.py | 28 ++++++++++++++++++++-------- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/.jenkins.sh b/.jenkins.sh index 7aa3257..bb58e04 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -45,6 +45,7 @@ run_test python tests/test.py --with-coverage -c tests/workers.json run_test python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-client-side.json run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -q" -a "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -vv" run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --workers 1" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -qq -b 127.0.0.1" +run_test python tests/test.py --with-coverage --should-fail --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=127.0.0.1,::1,8.8.8.8" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then if [ 3 -eq `cat /proc/sys/net/ipv4/tcp_fastopen` ] ; then diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 6afcad4..c148208 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -123,8 +123,8 @@ class TCPRelayHandler(object): self._downstream_status = WAIT_STATUS_INIT self._client_address = local_sock.getpeername()[:2] self._remote_address = None - if 'forbidden_ip' in self._config: - self._forbidden_iplist = self._config['forbidden_ip'] + if 'forbidden_ip' in config: + self._forbidden_iplist = config['forbidden_ip'] else: self._forbidden_iplist = None if is_local: diff --git a/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index 2b8b12f..ccc0413 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -112,6 +112,11 @@ class UDPRelay(object): self._closed = False self._last_time = time.time() self._sockets = set() + print(config) + if 'forbidden_ip' in config: + self._forbidden_iplist = config['forbidden_ip'] + else: + self._forbidden_iplist = None addrs = socket.getaddrinfo(self._listen_addr, self._listen_port, 0, socket.SOCK_DGRAM, socket.SOL_UDP) @@ -178,6 +183,12 @@ class UDPRelay(object): socket.SOCK_DGRAM, socket.SOL_UDP) if addrs: af, socktype, proto, canonname, sa = addrs[0] + if self._forbidden_iplist: + if common.to_str(sa[0]) in self._forbidden_iplist: + logging.warn('IP %s is in forbidden list, drop' % + common.to_str(sa[0])) + # drop + return client = socket.socket(af, socktype, proto) client.setblocking(False) self._cache[key] = client diff --git a/tests/test.py b/tests/test.py index 0b63a18..f160366 100755 --- a/tests/test.py +++ b/tests/test.py @@ -40,6 +40,9 @@ parser.add_argument('-s', '--server-conf', type=str, default=None) parser.add_argument('-a', '--client-args', type=str, default=None) parser.add_argument('-b', '--server-args', type=str, default=None) parser.add_argument('--with-coverage', action='store_true', default=None) +parser.add_argument('--should-fail', action='store_true', default=None) +parser.add_argument('--url', type=str, default='http://www.example.com/') +parser.add_argument('--dns', type=str, default='8.8.8.8') config = parser.parse_args() @@ -87,6 +90,7 @@ try: for fd in r: line = fd.readline() + sys.stderr.write(line) if not line: if stage == 2 and fd == p3.stdout: stage = 3 @@ -94,7 +98,6 @@ try: stage = 5 if bytes != str: line = str(line, 'utf8') - sys.stdout.write(line) if line.find('starting local') >= 0: local_ready = True if line.find('starting server') >= 0: @@ -103,7 +106,7 @@ try: if stage == 1: time.sleep(2) - p3 = Popen(['curl', 'http://www.example.com/', '-v', '-L', + p3 = Popen(['curl', config.url, '-v', '-L', '--socks5-hostname', '127.0.0.1:1081', '-m', '15', '--connect-timeout', '10'], stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) @@ -118,9 +121,13 @@ try: fdset.remove(p3.stdout) fdset.remove(p3.stderr) r = p3.wait() - if r != 0: - sys.exit(1) - p4 = Popen(['socksify', 'dig', '@8.8.8.8', 'www.google.com'], + if config.should_fail: + if r == 0: + sys.exit(1) + else: + if r != 0: + sys.exit(1) + p4 = Popen(['socksify', 'dig', '@%s' % config.dns, 'www.google.com'], stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) if p4 is not None: fdset.append(p4.stdout) @@ -131,9 +138,14 @@ try: if stage == 5: r = p4.wait() - if r != 0: - sys.exit(1) - print('test passed') + if config.should_fail: + if r == 0: + sys.exit(1) + print('test passed (expecting failure)') + else: + if r != 0: + sys.exit(1) + print('test passed') break finally: for p in [p1, p2]: From cc36de5a2f694b6eab020b3649fc9927f86acfd9 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 12 Jan 2015 23:05:19 +0800 Subject: [PATCH 075/146] fix pep8 --- tests/test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test.py b/tests/test.py index f160366..d213780 100755 --- a/tests/test.py +++ b/tests/test.py @@ -127,7 +127,8 @@ try: else: if r != 0: sys.exit(1) - p4 = Popen(['socksify', 'dig', '@%s' % config.dns, 'www.google.com'], + p4 = Popen(['socksify', 'dig', '@%s' % config.dns, + 'www.google.com'], stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) if p4 is not None: fdset.append(p4.stdout) From f7316c004738e95e7f64e0030dd5a31f855d2f56 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 12 Jan 2015 23:08:27 +0800 Subject: [PATCH 076/146] change logging level for UDP warning --- shadowsocks/udprelay.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index ccc0413..1bdd153 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -185,8 +185,8 @@ class UDPRelay(object): af, socktype, proto, canonname, sa = addrs[0] if self._forbidden_iplist: if common.to_str(sa[0]) in self._forbidden_iplist: - logging.warn('IP %s is in forbidden list, drop' % - common.to_str(sa[0])) + logging.debug('IP %s is in forbidden list, drop' % + common.to_str(sa[0])) # drop return client = socket.socket(af, socktype, proto) From 9fe2f4ef16c9951289fb19c416c48e6463763c90 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 12 Jan 2015 23:11:23 +0800 Subject: [PATCH 077/146] bump --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2bfc2c5..725ec71 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with codecs.open('README.rst', encoding='utf-8') as f: setup( name="shadowsocks", - version="2.6.2", + version="2.6.3", license='MIT', description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', From 32a6b8fd7a0ca7999e7c17d2f1b1b9692c342f6d Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 12 Jan 2015 23:13:29 +0800 Subject: [PATCH 078/146] update CHANGES --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 62aec8a..b5c9062 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +2.6.3 2015-01-03 +- Support --forbidden-ip to ban some IP, i.e. localhost +- Search OpenSSL and libsodium harder +- Now works on OpenWRT + 2.6.2 2015-01-03 - Log client IP From 5e476843ec8fc708033170a68a9c3bc425ecfb95 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 12 Jan 2015 23:19:24 +0800 Subject: [PATCH 079/146] fix python3 --- tests/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test.py b/tests/test.py index d213780..933027b 100755 --- a/tests/test.py +++ b/tests/test.py @@ -90,7 +90,6 @@ try: for fd in r: line = fd.readline() - sys.stderr.write(line) if not line: if stage == 2 and fd == p3.stdout: stage = 3 @@ -98,6 +97,7 @@ try: stage = 5 if bytes != str: line = str(line, 'utf8') + sys.stderr.write(line) if line.find('starting local') >= 0: local_ready = True if line.find('starting server') >= 0: From 53a7e4d0e4a8909a9825ad956a0a4c7265561dad Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 12 Jan 2015 23:21:16 +0800 Subject: [PATCH 080/146] remove print --- shadowsocks/udprelay.py | 1 - 1 file changed, 1 deletion(-) diff --git a/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index 1bdd153..ff6391c 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -112,7 +112,6 @@ class UDPRelay(object): self._closed = False self._last_time = time.time() self._sockets = set() - print(config) if 'forbidden_ip' in config: self._forbidden_iplist = config['forbidden_ip'] else: From 5c05a74727ee22a06859c5f50ed0af88ad65cdd4 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 13 Jan 2015 00:42:27 +0800 Subject: [PATCH 081/146] fix a potential name conflict --- shadowsocks/crypto/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks/crypto/util.py b/shadowsocks/crypto/util.py index c8cbab5..5425908 100644 --- a/shadowsocks/crypto/util.py +++ b/shadowsocks/crypto/util.py @@ -27,7 +27,7 @@ import logging def find_library(possible_lib_names, search_symbol, library_name): - from ctypes.util import find_library + import ctypes.util from ctypes import CDLL paths = [] @@ -36,7 +36,7 @@ def find_library(possible_lib_names, search_symbol, library_name): possible_lib_names = [possible_lib_names] for name in possible_lib_names: - path = find_library(name) + path = ctypes.util.find_library(name) if path: paths.append(path) From af6c6f3f238fe866e0d9a0b21f49d0edfd97852b Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 14 Jan 2015 12:59:43 +0800 Subject: [PATCH 082/146] also search lib* for library names --- shadowsocks/crypto/util.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/shadowsocks/crypto/util.py b/shadowsocks/crypto/util.py index 5425908..6d7d222 100644 --- a/shadowsocks/crypto/util.py +++ b/shadowsocks/crypto/util.py @@ -35,7 +35,12 @@ def find_library(possible_lib_names, search_symbol, library_name): if type(possible_lib_names) not in (list, tuple): possible_lib_names = [possible_lib_names] - for name in 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: path = ctypes.util.find_library(name) if path: paths.append(path) @@ -46,7 +51,7 @@ def find_library(possible_lib_names, search_symbol, library_name): # tools underlying find_library on linux. import glob - for name in possible_lib_names: + for name in lib_names: patterns = [ '/usr/local/lib*/lib%s.*' % name, '/usr/lib*/lib%s.*' % name, @@ -106,10 +111,11 @@ 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', 'EVP_CipherUpdate', 'libcrypto') 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'), + assert find_library(('notexist', 'c', 'crypto', 'eay32'), 'EVP_CipherUpdate', 'libc') is not None From 5179e018e2f14b377ca1c8b95f4c42bfb0c3b9c1 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 14 Jan 2015 13:12:12 +0800 Subject: [PATCH 083/146] bump --- CHANGES | 5 ++++- setup.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index b5c9062..27c8562 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,7 @@ -2.6.3 2015-01-03 +2.6.4 2015-01-14 +- Also search lib* when searching libraries + +2.6.3 2015-01-12 - Support --forbidden-ip to ban some IP, i.e. localhost - Search OpenSSL and libsodium harder - Now works on OpenWRT diff --git a/setup.py b/setup.py index 725ec71..c287c5c 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with codecs.open('README.rst', encoding='utf-8') as f: setup( name="shadowsocks", - version="2.6.3", + version="2.6.4", license='MIT', description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', From bd22e3ef753f0530a0124d15be6d29f13681ccf1 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Fri, 16 Jan 2015 16:50:18 +0800 Subject: [PATCH 084/146] try every dll that matches by name in PATH --- shadowsocks/crypto/util.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/shadowsocks/crypto/util.py b/shadowsocks/crypto/util.py index 6d7d222..1242f8e 100644 --- a/shadowsocks/crypto/util.py +++ b/shadowsocks/crypto/util.py @@ -23,9 +23,28 @@ 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 @@ -41,9 +60,12 @@ def find_library(possible_lib_names, search_symbol, library_name): lib_names.append('lib' + lib_name) for name in lib_names: - path = ctypes.util.find_library(name) - if path: - paths.append(path) + 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, @@ -56,8 +78,7 @@ def find_library(possible_lib_names, search_symbol, library_name): '/usr/local/lib*/lib%s.*' % name, '/usr/lib*/lib%s.*' % name, 'lib%s.*' % name, - '%s.dll' % name, - 'lib%s.dll' % name] + '%s.dll' % name] for pat in patterns: files = glob.glob(pat) From 13413267dcc8dc16f667fcf4996c93a8fec7aa87 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Fri, 16 Jan 2015 17:06:48 +0800 Subject: [PATCH 085/146] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7b351ba..0e45c91 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,17 @@ Server ### Install -#### Debian / Ubuntu: +Debian / Ubuntu: apt-get install python-pip pip install shadowsocks -#### CentOS: +CentOS: yum install python-setuptools && easy_install pip pip install shadowsocks -#### Windows: +Windows: See [Install Server on Windows] From 6efb3d00e414a69c77f018dab1c853d808a7c4e4 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Fri, 16 Jan 2015 18:52:01 +0800 Subject: [PATCH 086/146] fix #264 --- shadowsocks/asyncdns.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/shadowsocks/asyncdns.py b/shadowsocks/asyncdns.py index 18222a6..6f60dc9 100644 --- a/shadowsocks/asyncdns.py +++ b/shadowsocks/asyncdns.py @@ -93,11 +93,12 @@ def build_address(address): return b''.join(results) -def build_request(address, qtype, request_id): - header = struct.pack('!HBBHHHH', request_id, 1, 0, 1, 0, 0, 0) +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 header + addr + qtype_qclass + return request_id + header + addr + qtype_qclass def parse_ip(addrtype, data, length, offset): @@ -270,7 +271,6 @@ class DNSResolver(object): def __init__(self): self._loop = None - self._request_id = 1 self._hosts = {} self._hostname_status = {} self._hostname_to_cb = {} @@ -412,10 +412,7 @@ class DNSResolver(object): del self._hostname_status[hostname] def _send_req(self, hostname, qtype): - self._request_id += 1 - if self._request_id > 32768: - self._request_id = 1 - req = build_request(hostname, qtype, self._request_id) + req = build_request(hostname, qtype) for server in self._servers: logging.debug('resolving %s with type %d using server %s', hostname, qtype, server) From 2e9ce11ea15c303b769c2ab5e327c7372cfef2c7 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 18 Jan 2015 18:32:04 +0800 Subject: [PATCH 087/146] bump --- CHANGES | 3 +++ setup.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 27c8562..e90ad72 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +2.6.5 2015-01-18 +- Try both 32 bit and 64 bit dll on Windows + 2.6.4 2015-01-14 - Also search lib* when searching libraries diff --git a/setup.py b/setup.py index c287c5c..8c022ea 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with codecs.open('README.rst', encoding='utf-8') as f: setup( name="shadowsocks", - version="2.6.4", + version="2.6.5", license='MIT', description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', From 5e5d25efd989903648f7e6d1d5865eb6f890b1c8 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 19 Jan 2015 16:20:08 +0800 Subject: [PATCH 088/146] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 04aaa02..fbdb9c1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,6 +21,8 @@ a pull request, or ask some of your friends to do so. 3. We don't answer questions of any other types here. Since very few people are watching the issue tracker here, you'll probably get no help from here. Read [Troubleshooting] and get help from forums or [mailing lists]. +4. Issues in languages other than English will be Google translated into English +later. [Troubleshooting]: https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting From f4052fbc84904bd377965adbb5ff7e58b4754e8e Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 21 Jan 2015 14:32:21 +0800 Subject: [PATCH 089/146] fix MANIFEST.in --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 1dc4c8e..1882dd7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ -recursive-include *.py +recursive-include shadowsocks *.py include README.rst include LICENSE From 0e6a4cd8ff118b9362be8a33c961290081a8373f Mon Sep 17 00:00:00 2001 From: clowwindy Date: Fri, 23 Jan 2015 14:06:01 +0800 Subject: [PATCH 090/146] output python version in unit tests --- .jenkins.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.jenkins.sh b/.jenkins.sh index bb58e04..28583f9 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -24,6 +24,7 @@ function run_test { return 0 } +python --version coverage erase mkdir tmp run_test pep8 . From 1f8819f790e20ff30a8d6357d9a2509bc3229d17 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Fri, 23 Jan 2015 17:33:48 +0800 Subject: [PATCH 091/146] fix #270 --- .jenkins.sh | 2 +- shadowsocks/tcprelay.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.jenkins.sh b/.jenkins.sh index 28583f9..f14969f 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -46,7 +46,7 @@ run_test python tests/test.py --with-coverage -c tests/workers.json run_test python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-client-side.json run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -q" -a "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -vv" run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --workers 1" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -qq -b 127.0.0.1" -run_test python tests/test.py --with-coverage --should-fail --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=127.0.0.1,::1,8.8.8.8" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" +run_test python tests/test.py --with-coverage --should-fail --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=127.0.0.1,::1,8.8.8.8" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then if [ 3 -eq `cat /proc/sys/net/ipv4/tcp_fastopen` ] ; then diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index c148208..3b48901 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -387,7 +387,7 @@ class TCPRelayHandler(object): self._update_stream(STREAM_UP, WAIT_STATUS_READWRITING) self._update_stream(STREAM_DOWN, WAIT_STATUS_READING) return - except (OSError, IOError) as e: + except Exception as e: logging.error(e) if self._config['verbose']: traceback.print_exc() From 70ebd2ef285cc52f903dce2ea990936a8581f731 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Fri, 23 Jan 2015 17:35:32 +0800 Subject: [PATCH 092/146] bump --- CHANGES | 3 +++ setup.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index e90ad72..6e0331f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +2.6.6 2015-01-23 +- Fix a crash in forbidden list + 2.6.5 2015-01-18 - Try both 32 bit and 64 bit dll on Windows diff --git a/setup.py b/setup.py index 8c022ea..33cc44c 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with codecs.open('README.rst', encoding='utf-8') as f: setup( name="shadowsocks", - version="2.6.5", + version="2.6.6", license='MIT', description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', From 51f47ccb9167721527aac69a909d9ea83c93929c Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 24 Jan 2015 14:28:39 +0800 Subject: [PATCH 093/146] update coverage url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0e45c91..efc4406 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Bugs and Issues [Build Status]: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat [Configuration]: https://github.com/shadowsocks/shadowsocks/wiki/Configuration-via-Config-File [Coverage Status]: https://jenkins.shadowvpn.org/result/shadowsocks -[Coverage]: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/htmlcov/index.html +[Coverage]: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/PYENV/py34/label/linux01/htmlcov/index.html [Debian sid]: https://packages.debian.org/unstable/python/shadowsocks [iOS]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help [Issue Tracker]: https://github.com/shadowsocks/shadowsocks/issues?state=open From 4a2d98b28005ff2e53ab874d460eb5e463876d89 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 24 Jan 2015 14:43:11 +0800 Subject: [PATCH 094/146] update coverage url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index efc4406..aba545b 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Bugs and Issues [Build Status]: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat [Configuration]: https://github.com/shadowsocks/shadowsocks/wiki/Configuration-via-Config-File [Coverage Status]: https://jenkins.shadowvpn.org/result/shadowsocks -[Coverage]: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/PYENV/py34/label/linux01/htmlcov/index.html +[Coverage]: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/PYENV/py34/label/linux/htmlcov/index.html [Debian sid]: https://packages.debian.org/unstable/python/shadowsocks [iOS]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help [Issue Tracker]: https://github.com/shadowsocks/shadowsocks/issues?state=open From 5316b3bf115ce915bfa182afdf926045625304fc Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 28 Jan 2015 17:17:05 +0800 Subject: [PATCH 095/146] update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fb96264..6c1b61e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,7 @@ pip-log.txt # Unit test / coverage reports htmlcov -.coverage +.coverage* .tox #Translations From ada97ab6d9ae3b9ad1e6d91ab92cc6f8c37259c2 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sat, 31 Jan 2015 02:58:40 +0800 Subject: [PATCH 096/146] improve comments --- shadowsocks/tcprelay.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 3b48901..d68e424 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -43,30 +43,22 @@ TIMEOUT_PRECISION = 4 MSG_FASTOPEN = 0x20000000 -# SOCKS CMD defination +# SOCKS command definition CMD_CONNECT = 1 CMD_BIND = 2 CMD_UDP_ASSOCIATE = 3 -# TCP Relay can be either sslocal or ssserver -# for sslocal it is called is_local=True - # 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, we have 2 streams: -# upstream: from client to server direction -# read local and write to remote -# downstream: from server to client direction -# read remote and write to local - # for each handler, it could be at one of several stages: -# sslocal: +# 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 @@ -74,7 +66,7 @@ CMD_UDP_ASSOCIATE = 3 # stage 4 still connecting, more data from local received # stage 5 remote connected, piping local and remote -# ssserver: +# 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 @@ -89,11 +81,16 @@ STAGE_CONNECTING = 4 STAGE_STREAM = 5 STAGE_DESTROYED = -1 -# stream direction +# 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 -# stream wait status, indicating it's waiting for reading, etc +# for each stream, it's waiting for reading, or writing, or both WAIT_STATUS_INIT = 0 WAIT_STATUS_READING = 1 WAIT_STATUS_WRITING = 2 @@ -112,6 +109,9 @@ class TCPRelayHandler(object): self._remote_sock = None self._config = config self._dns_resolver = dns_resolver + + # TCP Relay works as either sslocal or ssserver + # if is_local, this is sslocal self._is_local = is_local self._stage = STAGE_INIT self._encryptor = encrypt.Encryptor(config['password'], From 8783e0e9aea644f3839bfbcf9763090a1c39d0f1 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sat, 31 Jan 2015 11:56:17 +0800 Subject: [PATCH 097/146] Move is_ip from asyncdns to common Since implement CIDR forbidden need this function, move it to common file seems to be a better choice. --- shadowsocks/asyncdns.py | 18 +++--------------- shadowsocks/common.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/shadowsocks/asyncdns.py b/shadowsocks/asyncdns.py index 6f60dc9..6fee6b9 100644 --- a/shadowsocks/asyncdns.py +++ b/shadowsocks/asyncdns.py @@ -233,18 +233,6 @@ def parse_response(data): return None -def is_ip(address): - for family in (socket.AF_INET, socket.AF_INET6): - try: - if type(address) != str: - address = address.decode('utf8') - socket.inet_pton(family, address) - return family - except (TypeError, ValueError, OSError, IOError): - pass - return False - - def is_valid_hostname(hostname): if len(hostname) > 255: return False @@ -296,7 +284,7 @@ class DNSResolver(object): parts = line.split() if len(parts) >= 2: server = parts[1] - if is_ip(server) == socket.AF_INET: + if common.is_ip(server) == socket.AF_INET: if type(server) != str: server = server.decode('utf8') self._servers.append(server) @@ -316,7 +304,7 @@ class DNSResolver(object): parts = line.split() if len(parts) >= 2: ip = parts[0] - if is_ip(ip): + if common.is_ip(ip): for i in range(1, len(parts)): hostname = parts[i] if hostname: @@ -423,7 +411,7 @@ class DNSResolver(object): hostname = hostname.encode('utf8') if not hostname: callback(None, Exception('empty hostname')) - elif is_ip(hostname): + elif common.is_ip(hostname): callback((hostname, hostname), None) elif hostname in self._hosts: logging.debug('hit hosts: %s', hostname) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index e4f698c..2be17e5 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -101,6 +101,18 @@ def inet_pton(family, addr): 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 patch_socket(): if not hasattr(socket, 'inet_pton'): socket.inet_pton = inet_pton From 100ebcf064f4a8e5840012bdccef106794208569 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sat, 31 Jan 2015 19:50:10 +0800 Subject: [PATCH 098/146] Add IPNetwork class to support CIDR calculation Usage: Use IPNetwork(str|list) to create an IPNetwork object. Use operator 'in' to determine whether the specified IP address is in the IP network or not, like: >>> '192.168.1.1' in IPNetwork('192.168.1.0/24') True Both IPv4 and IPv6 address are supported. Note: When using string to initialize the IPNetwork, a comma seperated IP network list should be provided. Currently, IPNetwork just support standard CIDR like: x.x.x.x/y eg. 192.168.1.0/24 ::x/y eg. ::1/10 If pure IP address was provided, it will be treated as implicit IP network, like 192.168.0.0 will be treated as 192.168.0.0/16 and 192.168.1.1 will be treated as 192.168.1.1/32 This implicit translate may cause some unexpected behavior, like user provide 192.168.2.0 and expect it will be treated as 192.168.2.0/24 but actually it will be translated to 192.168.2.0/23 because there are 9 continuous 0 from right. In order to avoid confusion, a warning message will be displayed when pure IP address was provided. Other variants of CIDR are not supported yet. --- shadowsocks/common.py | 67 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index 2be17e5..d582923 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -184,6 +184,57 @@ def parse_header(data): return addrtype, to_bytes(dest_addr), dest_port, header_length +class IPNetwork(object): + ADDRLENGTH = {socket.AF_INET: 32, socket.AF_INET6: 128} + + def __init__(self, addrs): + self._network_list_v4 = [] + self._network_list_v6 = [] + if type(addrs) == str: + addrs = addrs.split(',') + map(self.add_network, addrs) + + def add_network(self, addr): + block = addr.split('/') + addr_family = is_ip(block[0]) + 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 SyntaxError("Not a valid CIDR notation: %s" % addr) + if len(block) is 1: + prefix_size = 0 + while ((ip & 1) == 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, + IPNetwork.ADDRLENGTH[addr_family] - prefix_size)) + elif block[1].isdigit() and int(block[1]) <= IPNetwork.ADDRLENGTH[addr_family]: + prefix_size = IPNetwork.ADDRLENGTH[addr_family] - int(block[1]) + ip >>= prefix_size + else: + raise SyntaxError("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 (naddr, ps): naddr == ip >> ps, 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 (naddr, ps): naddr == ip >> ps, self._network_list_v6)) + else: + return False + + def test_inet_conv(): ipv4 = b'8.8.4.4' b = inet_pton(socket.AF_INET, ipv4) @@ -210,7 +261,23 @@ def test_pack_header(): 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.168.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.168.2.1' in ip_network + assert '192.168.3.1' in ip_network # 192.168.2.0 is treated as 192.168.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() From 8af359ae05d6fa60b9f60e6f732c6e02bb4b6b93 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sat, 31 Jan 2015 20:32:09 +0800 Subject: [PATCH 099/146] Use IPNetwork supporting forbidden ip feature in config utils This commit also make "forbidden_ip" field available in config file. If no forbidden ip specified in command line and config file, default to "127.0.0.0/8,::1". --- shadowsocks/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index a51c965..0c77125 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -29,7 +29,7 @@ import json import sys import getopt import logging -from shadowsocks.common import to_bytes, to_str +from shadowsocks.common import to_bytes, to_str, IPNetwork VERBOSE_LEVEL = 5 @@ -186,6 +186,7 @@ def get_config(is_local): config['verbose'] = config.get('verbose', False) config['local_address'] = config.get('local_address', '127.0.0.1') config['local_port'] = config.get('local_port', 1080) + config['forbidden_ip'] = IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1')) if is_local: if config.get('server', None) is None: logging.error('server addr not specified') From aa28796524eb7ff9ccb9a73afa4353079d2a9f71 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 1 Feb 2015 00:17:03 +0800 Subject: [PATCH 100/146] Make common fit PEP8 --- shadowsocks/common.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index d582923..0f94e07 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -185,7 +185,7 @@ def parse_header(data): class IPNetwork(object): - ADDRLENGTH = {socket.AF_INET: 32, socket.AF_INET6: 128} + ADDRLENGTH = {socket.AF_INET: 32, socket.AF_INET6: 128, False: 0} def __init__(self, addrs): self._network_list_v4 = [] @@ -197,6 +197,7 @@ class IPNetwork(object): def add_network(self, addr): 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: @@ -210,10 +211,9 @@ class IPNetwork(object): ip >>= 1 prefix_size += 1 logging.warn("You did't specify CIDR routing prefix size for %s, " - "implicit treated as %s/%d" % (addr, addr, - IPNetwork.ADDRLENGTH[addr_family] - prefix_size)) - elif block[1].isdigit() and int(block[1]) <= IPNetwork.ADDRLENGTH[addr_family]: - prefix_size = IPNetwork.ADDRLENGTH[addr_family] - int(block[1]) + "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 SyntaxError("Not a valid CIDR notation: %s" % addr) @@ -226,11 +226,13 @@ class IPNetwork(object): addr_family = is_ip(addr) if addr_family is socket.AF_INET: ip, = struct.unpack("!I", socket.inet_aton(addr)) - return any(map(lambda (naddr, ps): naddr == ip >> ps, self._network_list_v4)) + return any(map(lambda (n, ps): n == ip >> ps, + 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 (naddr, ps): naddr == ip >> ps, self._network_list_v6)) + return any(map(lambda (n, ps): n == ip >> ps, + self._network_list_v6)) else: return False @@ -262,7 +264,7 @@ def test_pack_header(): def test_ip_network(): - ip_network = IPNetwork('127.0.0.0/24,::ff:1/112,::1,192.168.1.1,192.168.2.0') + 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 @@ -271,8 +273,8 @@ def test_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.168.2.1' in ip_network - assert '192.168.3.1' in ip_network # 192.168.2.0 is treated as 192.168.2.0/23 + 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 From b11d8489862765a2aa13d1594c855c4e652f8ccf Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 1 Feb 2015 00:42:12 +0800 Subject: [PATCH 101/146] Fix for Python3 lambda behavior change In Python3, lambda no longer support use tuple as syntax. So, ugly changes is inevitable. --- shadowsocks/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index 0f94e07..68ca025 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -226,12 +226,12 @@ class IPNetwork(object): 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 == ip >> ps, + 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 == ip >> ps, + return any(map(lambda n_ps: n_ps[0] == ip >> n_ps[1], self._network_list_v6)) else: return False From 070108f78bb81f4dc6391fd575d5268f51481cea Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 1 Feb 2015 00:48:15 +0800 Subject: [PATCH 102/146] Disable forbidden ip feature for local shadowsocks Since forbidden ip is server-side only, disable it for local-side. This commit also supress warning about IPv6 loopback because I can confirm ::1/128 is the only loopback address, not like IPv4. --- shadowsocks/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 0c77125..6ea3daa 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -186,7 +186,6 @@ def get_config(is_local): config['verbose'] = config.get('verbose', False) config['local_address'] = config.get('local_address', '127.0.0.1') config['local_port'] = config.get('local_port', 1080) - config['forbidden_ip'] = IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1')) if is_local: if config.get('server', None) is None: logging.error('server addr not specified') @@ -194,6 +193,8 @@ def get_config(is_local): sys.exit(2) else: config['server'] = config.get('server', '0.0.0.0') + config['forbidden_ip'] = \ + IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1/128')) config['server_port'] = config.get('server_port', 8388) if is_local and not config.get('password', None): From 79b9b53dbea59627d43ec7ee4eb2dd54777460be Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 1 Feb 2015 01:57:17 +0800 Subject: [PATCH 103/146] Never process empty string and prevent infinite loop If user provide an empty string as network range, inet_pton will treate it as an IPv6 unspecified address, it seems a bug but I can't confirm. Then empty string will be converted to 0, 0 & 1 always be zero, so it caused dead loop. --- shadowsocks/common.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index 68ca025..d4a9a67 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -195,6 +195,8 @@ class IPNetwork(object): 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] @@ -207,7 +209,7 @@ class IPNetwork(object): raise SyntaxError("Not a valid CIDR notation: %s" % addr) if len(block) is 1: prefix_size = 0 - while ((ip & 1) == 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, " From a0aa9173a8001b5c20e270d7b5dcec28172cfa3f Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 1 Feb 2015 02:37:30 +0800 Subject: [PATCH 104/146] Fix for Python3 map changed behavior In Python3, map returns an iterator instead of list in Python2, which cause map "lazier" than before, wrap with list() force it running. --- shadowsocks/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index d4a9a67..0c4e278 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -192,7 +192,7 @@ class IPNetwork(object): self._network_list_v6 = [] if type(addrs) == str: addrs = addrs.split(',') - map(self.add_network, addrs) + list(map(self.add_network, addrs)) def add_network(self, addr): if addr is "": From 17624d0b99731a5947a2df9090039ad531883cc7 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 1 Feb 2015 02:40:48 +0800 Subject: [PATCH 105/146] Fix large file test since forbidden ip list is default to localhost Use an empty forbidden ip list to override default list should work. --- tests/test_large_file.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_large_file.sh b/tests/test_large_file.sh index e8acd79..33bcb59 100755 --- a/tests/test_large_file.sh +++ b/tests/test_large_file.sh @@ -8,7 +8,7 @@ mkdir -p tmp $PYTHON shadowsocks/local.py -c tests/aes.json & LOCAL=$! -$PYTHON shadowsocks/server.py -c tests/aes.json & +$PYTHON shadowsocks/server.py -c tests/aes.json --forbidden-ip "" & SERVER=$! sleep 3 From 453a9c61a6135087b72b247d325157cc67a15921 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 09:09:35 +0800 Subject: [PATCH 106/146] show CIDR error more friendly --- shadowsocks/common.py | 4 ++-- shadowsocks/utils.py | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/shadowsocks/common.py b/shadowsocks/common.py index 0c4e278..1c10671 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -206,7 +206,7 @@ class IPNetwork(object): hi, lo = struct.unpack("!QQ", inet_pton(addr_family, block[0])) ip = (hi << 64) | lo else: - raise SyntaxError("Not a valid CIDR notation: %s" % addr) + 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: @@ -218,7 +218,7 @@ class IPNetwork(object): prefix_size = addr_len - int(block[1]) ip >>= prefix_size else: - raise SyntaxError("Not a valid CIDR notation: %s" % addr) + 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: diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 6ea3daa..f791a2a 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -193,8 +193,12 @@ def get_config(is_local): sys.exit(2) else: config['server'] = config.get('server', '0.0.0.0') - config['forbidden_ip'] = \ - IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1/128')) + 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) config['server_port'] = config.get('server_port', 8388) if is_local and not config.get('password', None): From c39bbbe237f230df62c17c751731f64743d92838 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 09:13:29 +0800 Subject: [PATCH 107/146] test invalid CIDR --- tests/test_command.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_command.sh b/tests/test_command.sh index eba4c8c..0995299 100755 --- a/tests/test_command.sh +++ b/tests/test_command.sh @@ -36,5 +36,7 @@ $LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d sto assert "$SERVER 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": password or port_password not specified" $LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop +assert "$SERVER 2>&1 --forbidden-ip 127.0.0.1/4a -m rc4-md5 -k 12345 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": Not a valid CIDR notation: 127.0.0.1/4a" +$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop assert_end command From b77f419336ee2f2dd5b3823bdde92124c95d7a94 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 09:23:45 +0800 Subject: [PATCH 108/146] test if localhost is in the default forbidden list --- .jenkins.sh | 6 ++++++ tests/test.py | 3 +++ 2 files changed, 9 insertions(+) diff --git a/.jenkins.sh b/.jenkins.sh index f14969f..c67fcdb 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -48,6 +48,12 @@ run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0 run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --workers 1" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -qq -b 127.0.0.1" run_test python tests/test.py --with-coverage --should-fail --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=127.0.0.1,::1,8.8.8.8" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" +# test localhost is in the forbidden list by default +run_test python tests/test.py --with-coverage --should-fail --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" + +# test localhost available when forbidden list is empty +run_test python tests/test.py --with-coverage --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=''" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" + if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then if [ 3 -eq `cat /proc/sys/net/ipv4/tcp_fastopen` ] ; then # we have to run it twice: diff --git a/tests/test.py b/tests/test.py index 933027b..5ad94c9 100755 --- a/tests/test.py +++ b/tests/test.py @@ -41,6 +41,7 @@ parser.add_argument('-a', '--client-args', type=str, default=None) parser.add_argument('-b', '--server-args', type=str, default=None) parser.add_argument('--with-coverage', action='store_true', default=None) parser.add_argument('--should-fail', action='store_true', default=None) +parser.add_argument('--tcp-only', action='store_true', default=None) parser.add_argument('--url', type=str, default='http://www.example.com/') parser.add_argument('--dns', type=str, default='8.8.8.8') @@ -127,6 +128,8 @@ try: else: if r != 0: sys.exit(1) + if config.tcp_only: + break p4 = Popen(['socksify', 'dig', '@%s' % config.dns, 'www.google.com'], stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) From 4aba904d6ec205f30c187b331a69b4cb8661d5f5 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 09:24:49 +0800 Subject: [PATCH 109/146] fix typo --- .jenkins.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.jenkins.sh b/.jenkins.sh index c67fcdb..510732a 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -51,7 +51,7 @@ run_test python tests/test.py --with-coverage --should-fail --url="http://localh # test localhost is in the forbidden list by default run_test python tests/test.py --with-coverage --should-fail --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" -# test localhost available when forbidden list is empty +# test localhost is available when forbidden list is empty run_test python tests/test.py --with-coverage --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=''" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then From 5a7225c54b6cd2f0cbef2566de07f76dc2f64c70 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 09:27:18 +0800 Subject: [PATCH 110/146] fix a problem in test arguments --- .jenkins.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.jenkins.sh b/.jenkins.sh index 510732a..8d06b1d 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -52,7 +52,7 @@ run_test python tests/test.py --with-coverage --should-fail --url="http://localh run_test python tests/test.py --with-coverage --should-fail --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" # test localhost is available when forbidden list is empty -run_test python tests/test.py --with-coverage --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=''" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" +run_test python tests/test.py --with-coverage --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then if [ 3 -eq `cat /proc/sys/net/ipv4/tcp_fastopen` ] ; then From 3b7755bd5ebe451c8070f68696e5477b116d99ec Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 09:38:32 +0800 Subject: [PATCH 111/146] fix a travis issue --- .jenkins.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.jenkins.sh b/.jenkins.sh index 8d06b1d..73c4abc 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -46,13 +46,13 @@ run_test python tests/test.py --with-coverage -c tests/workers.json run_test python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-client-side.json run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -q" -a "-m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -vv" run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --workers 1" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -qq -b 127.0.0.1" -run_test python tests/test.py --with-coverage --should-fail --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=127.0.0.1,::1,8.8.8.8" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" +run_test python tests/test.py --with-coverage --should-fail --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=127.0.0.1,::1,8.8.8.8" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" # test localhost is in the forbidden list by default -run_test python tests/test.py --with-coverage --should-fail --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" +run_test python tests/test.py --with-coverage --should-fail --tcp-only --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" # test localhost is available when forbidden list is empty -run_test python tests/test.py --with-coverage --tcp-only --url="http://localhost/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" +run_test python tests/test.py --with-coverage --tcp-only --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" if [ -f /proc/sys/net/ipv4/tcp_fastopen ] ; then if [ 3 -eq `cat /proc/sys/net/ipv4/tcp_fastopen` ] ; then From da65d0a2eee60ee98f3dd9d58971c354695f5816 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Sun, 1 Feb 2015 17:15:10 +0800 Subject: [PATCH 112/146] update coverage_server --- tests/coverage_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/coverage_server.py b/tests/coverage_server.py index 2df55e3..d752cff 100644 --- a/tests/coverage_server.py +++ b/tests/coverage_server.py @@ -11,7 +11,7 @@ if __name__ == '__main__': with open('/tmp/%s-coverage' % project, 'rb') as f: coverage = f.read().strip() n = int(coverage.strip('%')) - if n > 80: + if n >= 80: color = 'brightgreen' else: color = 'yellow' From ae99698b4e2df939b95dbb58b5e748ce85cfa1f0 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 15:38:55 +0800 Subject: [PATCH 113/146] implement --user fix @278 --- shadowsocks/daemon.py | 32 ++++++++++++++++++++++++++++++++ shadowsocks/local.py | 1 + shadowsocks/server.py | 2 ++ shadowsocks/utils.py | 22 +++++++++++++--------- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index d206ccf..3d2b448 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -183,3 +183,35 @@ def daemon_stop(pid_file): 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/shadowsocks/local.py b/shadowsocks/local.py index 994b6d8..87c5215 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -73,6 +73,7 @@ def main(): sys.exit(1) signal.signal(signal.SIGINT, int_handler) + daemon.set_user(config.get('user', None)) loop.run() except (KeyboardInterrupt, IOError, OSError) as e: logging.error(e) diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 8eed4ad..345ec0b 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -86,6 +86,8 @@ def main(): 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 (KeyboardInterrupt, IOError, OSError) as e: logging.error(e) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index f791a2a..b12f54d 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -89,7 +89,11 @@ def check_config(config): if config.get('password') in [b'mypassword']: logging.error('DON\'T USE DEFAULT PASSWORD! Please change it in your ' 'config.json!') - exit(1) + 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) def get_config(is_local): @@ -97,11 +101,11 @@ def get_config(is_local): format='%(levelname)-s: %(message)s') if is_local: shortopts = 'hd:s:b:p:k:l:m:c:t:vq' - longopts = ['help', 'fast-open', 'pid-file=', 'log-file='] + longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'user='] else: shortopts = 'hd:s:p:k:m:c:t:vq' longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers=', - 'forbidden-ip='] + 'forbidden-ip=', 'user='] try: config_path = find_config() optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts) @@ -147,6 +151,8 @@ def get_config(is_local): config['fast_open'] = True elif key == '--workers': config['workers'] = int(value) + elif key == '--user': + config['user'] = to_str(value) elif key == '--forbidden-ip': config['forbidden_ip'] = to_str(value).split(',') elif key in ('-h', '--help'): @@ -247,9 +253,7 @@ def print_help(is_local): def print_local_help(): - print('''usage: sslocal [-h] -s SERVER_ADDR [-p SERVER_PORT] - [-b LOCAL_ADDR] [-l LOCAL_PORT] -k PASSWORD [-m METHOD] - [-t TIMEOUT] [-c CONFIG] [--fast-open] [-v] -[d] [-q] + 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. @@ -270,6 +274,7 @@ General options: -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 @@ -278,9 +283,7 @@ Online help: def print_server_help(): - print('''usage: ssserver [-h] [-s SERVER_ADDR] [-p SERVER_PORT] -k PASSWORD - -m METHOD [-t TIMEOUT] [-c CONFIG] [--fast-open] - [--workers WORKERS] [-v] [-d start] [-q] + 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. @@ -301,6 +304,7 @@ General options: -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 From 7aa37cad8e3360fad28a2c95eaa577d94adc8070 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 15:46:22 +0800 Subject: [PATCH 114/146] check cipher before daemon start fix #280 --- README.md | 13 ++++++++++--- shadowsocks/local.py | 5 +---- shadowsocks/server.py | 4 +--- shadowsocks/utils.py | 3 +++ 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index aba545b..24b7ed1 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,19 @@ See [Install Server on Windows] ### Usage - ssserver -p 8000 -k password -m rc4-md5 + ssserver -p 443 -k password -m rc4-md5 To run in the background: - ssserver -p 8000 -k password -m rc4-md5 -d start - ssserver -p 8000 -k password -m rc4-md5 -d stop + sudo ssserver -p 443 -k password -m rc4-md5 --user nobody -d start + +To stop: + + sudo ssserver -d stop + +To check the log: + + sudo less /var/log/shadowsocks.log Check all the options via `-h`. You can also use a [Configuration] file instead. diff --git a/shadowsocks/local.py b/shadowsocks/local.py index 87c5215..3cf3da7 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -30,8 +30,7 @@ import logging import signal sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) -from shadowsocks import utils, daemon, encrypt, eventloop, tcprelay, udprelay,\ - asyncdns +from shadowsocks import utils, daemon, eventloop, tcprelay, udprelay, asyncdns def main(): @@ -49,8 +48,6 @@ def main(): utils.print_shadowsocks() - encrypt.try_cipher(config['password'], config['method']) - try: logging.info("starting local at %s:%d" % (config['local_address'], config['local_port'])) diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 345ec0b..eca43ee 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -30,8 +30,7 @@ import logging import signal sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) -from shadowsocks import utils, daemon, encrypt, eventloop, tcprelay, udprelay,\ - asyncdns +from shadowsocks import utils, daemon, eventloop, tcprelay, udprelay, asyncdns def main(): @@ -57,7 +56,6 @@ def main(): else: config['port_password'][str(server_port)] = config['password'] - encrypt.try_cipher(config['password'], config['method']) tcp_servers = [] udp_servers = [] dns_resolver = asyncdns.DNSResolver() diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index b12f54d..0e50089 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -30,6 +30,7 @@ import sys import getopt import logging from shadowsocks.common import to_bytes, to_str, IPNetwork +from shadowsocks import encrypt VERBOSE_LEVEL = 5 @@ -95,6 +96,8 @@ def check_config(config): logging.error('user can be used only on Unix') sys.exit(1) + encrypt.try_cipher(config['password'], config['method']) + def get_config(is_local): logging.basicConfig(level=logging.INFO, From 73f21ffbf61258afb8d38dc0a4d5bdfca0b0cc67 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 15:50:41 +0800 Subject: [PATCH 115/146] do not check config for daemon stop --- shadowsocks/daemon.py | 3 --- shadowsocks/utils.py | 10 +++++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index 3d2b448..9961ea7 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -43,9 +43,6 @@ def daemon_exec(config): command = 'start' pid_file = config['pid-file'] log_file = config['log-file'] - command = common.to_str(command) - pid_file = common.to_str(pid_file) - log_file = common.to_str(log_file) if command == 'start': daemon_start(pid_file, log_file) elif command == 'stop': diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 0e50089..3e2fb57 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -70,6 +70,10 @@ def find_config(): def check_config(config): + if config.get('daemon', None) == 'stop': + # no need to specify configuration for daemon stop + return + if config.get('local_address', '') in [b'0.0.0.0']: logging.warn('warning: local set to listen on 0.0.0.0, it\'s not safe') if config.get('server', '') in [b'127.0.0.1', b'localhost']: @@ -165,11 +169,11 @@ def get_config(is_local): print_server_help() sys.exit(0) elif key == '-d': - config['daemon'] = value + config['daemon'] = to_str(value) elif key == '--pid-file': - config['pid-file'] = value + config['pid-file'] = to_str(value) elif key == '--log-file': - config['log-file'] = value + config['log-file'] = to_str(value) elif key == '-q': v_count -= 1 config['verbose'] = v_count From 1c814654367f1ac5afe7bd23439f6d57792e30d7 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 15:57:44 +0800 Subject: [PATCH 116/146] fix command line --- shadowsocks/utils.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 3e2fb57..417348c 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -69,11 +69,28 @@ def find_config(): return None -def check_config(config): +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.warn('warning: local set to listen on 0.0.0.0, it\'s not safe') if config.get('server', '') in [b'127.0.0.1', b'localhost']: @@ -214,23 +231,6 @@ def get_config(is_local): sys.exit(2) config['server_port'] = config.get('server_port', 8388) - 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']) - logging.getLogger('').handlers = [] logging.addLevelName(VERBOSE_LEVEL, 'VERBOSE') if config['verbose'] >= 2: @@ -247,7 +247,7 @@ def get_config(is_local): format='%(asctime)s %(levelname)-8s %(message)s', datefmt='%Y-%m-%d %H:%M:%S') - check_config(config) + check_config(config, is_local) return config From fbf15cb94201a9f3279d5234c00436b9f2166fdd Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 15:59:44 +0800 Subject: [PATCH 117/146] update command tests --- tests/test_command.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_command.sh b/tests/test_command.sh index 0995299..2b8b68a 100755 --- a/tests/test_command.sh +++ b/tests/test_command.sh @@ -30,10 +30,10 @@ $LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d sto assert "$LOCAL 2>&1 -m rc4-md5 -p 8388 -k testrc4 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": server addr not specified" $LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop -assert "$LOCAL 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": password not specified" +assert "$LOCAL 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" " password not specified" $LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop -assert "$SERVER 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": password or port_password not specified" +assert "$SERVER 2>&1 -m rc4-md5 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" " password or port_password not specified" $LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop assert "$SERVER 2>&1 --forbidden-ip 127.0.0.1/4a -m rc4-md5 -k 12345 -p 8388 -s 0.0.0.0 -d start | grep ERROR | awk -F\"ERROR\" '{print \$2}'" ": Not a valid CIDR notation: 127.0.0.1/4a" From 0cd261dd109ab86ce6842dcd46692cb115c16145 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 16:22:56 +0800 Subject: [PATCH 118/146] catch KeyError --- shadowsocks/local.py | 4 ++-- shadowsocks/server.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shadowsocks/local.py b/shadowsocks/local.py index 3cf3da7..c1a84f1 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -72,12 +72,12 @@ def main(): daemon.set_user(config.get('user', None)) loop.run() - except (KeyboardInterrupt, IOError, OSError) as e: + except Exception as e: logging.error(e) if config['verbose']: import traceback traceback.print_exc() - os._exit(1) + sys.exit(1) if __name__ == '__main__': main() diff --git a/shadowsocks/server.py b/shadowsocks/server.py index eca43ee..41c70ca 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -87,12 +87,12 @@ def main(): daemon.set_user(config.get('user', None)) loop.run() - except (KeyboardInterrupt, IOError, OSError) as e: + except Exception as e: logging.error(e) if config['verbose']: import traceback traceback.print_exc() - os._exit(1) + sys.exit(1) if int(config['workers']) > 1: if os.name == 'posix': From 27a0c7754d335725b478e0fe5a3019b52fa6d024 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 16:23:17 +0800 Subject: [PATCH 119/146] bump --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 33cc44c..ff9ac69 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with codecs.open('README.rst', encoding='utf-8') as f: setup( name="shadowsocks", - version="2.6.6", + version="2.6.7", license='MIT', description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', From 2c59ffb952efab2ff16f28a3bba5217e8611fa05 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 16:42:37 +0800 Subject: [PATCH 120/146] update rst --- README.rst | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 510b233..40c9ad1 100644 --- a/README.rst +++ b/README.rst @@ -12,7 +12,6 @@ Install ~~~~~~~ Debian / Ubuntu: -^^^^^^^^^^^^^^^^ :: @@ -20,7 +19,6 @@ Debian / Ubuntu: pip install shadowsocks CentOS: -^^^^^^^ :: @@ -28,7 +26,6 @@ CentOS: pip install shadowsocks Windows: -^^^^^^^^ See `Install Server on Windows `__ @@ -38,14 +35,25 @@ Usage :: - ssserver -p 8000 -k password -m rc4-md5 + ssserver -p 443 -k password -m rc4-md5 To run in the background: :: - ssserver -p 8000 -k password -m rc4-md5 -d start - ssserver -p 8000 -k password -m rc4-md5 -d stop + sudo ssserver -p 443 -k password -m rc4-md5 --user nobody -d start + +To stop: + +:: + + sudo ssserver -d stop + +To check the log: + +:: + + sudo less /var/log/shadowsocks.log Check all the options via ``-h``. You can also use a `Configuration `__ @@ -88,4 +96,4 @@ Bugs and Issues .. |Build Status| image:: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat :target: https://travis-ci.org/shadowsocks/shadowsocks .. |Coverage Status| image:: https://jenkins.shadowvpn.org/result/shadowsocks - :target: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/htmlcov/index.html + :target: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/PYENV/py34/label/linux/htmlcov/index.html From 96a86c028df463b8a051711111b486d544415b3b Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 2 Feb 2015 17:29:57 +0800 Subject: [PATCH 121/146] update CHANGES --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 6e0331f..8004a3f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +2.6.7 2015-02-02 +- Support --user +- Support CIDR format in --forbidden-ip +- Minor fixes + 2.6.6 2015-01-23 - Fix a crash in forbidden list From ce805f0aeaea03646e01b623c4e2185f63a3562f Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 3 Feb 2015 14:10:36 +0800 Subject: [PATCH 122/146] license under Apache License v2.0 --- LICENSE | 215 ++++++++++++++++++++++++++++++--- README.md | 15 ++- README.rst | 16 ++- setup.py | 6 +- shadowsocks/__init__.py | 28 ++--- shadowsocks/asyncdns.py | 28 ++--- shadowsocks/common.py | 28 ++--- shadowsocks/crypto/__init__.py | 28 ++--- shadowsocks/crypto/openssl.py | 28 ++--- shadowsocks/crypto/rc4_md5.py | 28 ++--- shadowsocks/crypto/sodium.py | 28 ++--- shadowsocks/crypto/table.py | 28 ++--- shadowsocks/crypto/util.py | 28 ++--- shadowsocks/daemon.py | 28 ++--- shadowsocks/encrypt.py | 28 ++--- shadowsocks/eventloop.py | 28 ++--- shadowsocks/local.py | 28 ++--- shadowsocks/lru_cache.py | 28 ++--- shadowsocks/server.py | 28 ++--- shadowsocks/tcprelay.py | 28 ++--- shadowsocks/udprelay.py | 28 ++--- shadowsocks/utils.py | 28 ++--- tests/coverage_server.py | 14 +++ tests/nose_plugin.py | 16 +++ tests/test.py | 28 ++--- 25 files changed, 469 insertions(+), 345 deletions(-) diff --git a/LICENSE b/LICENSE index 76886fa..d645695 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,202 @@ -Shadowsocks -Copyright (c) 2012-2015 clowwindy + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -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: + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. + 1. Definitions. -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. \ No newline at end of file + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/README.md b/README.md index 24b7ed1..aee2bdc 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,20 @@ You can find all the documentation in the [Wiki]. License ------- -MIT + +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. Bugs and Issues ---------------- diff --git a/README.rst b/README.rst index 40c9ad1..bf2a3ec 100644 --- a/README.rst +++ b/README.rst @@ -81,7 +81,21 @@ You can find all the documentation in the License ------- -MIT +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. Bugs and Issues --------------- diff --git a/setup.py b/setup.py index ff9ac69..9485ce2 100644 --- a/setup.py +++ b/setup.py @@ -7,8 +7,8 @@ with codecs.open('README.rst', encoding='utf-8') as f: setup( name="shadowsocks", - version="2.6.7", - license='MIT', + version="2.6.8", + license='http://www.apache.org/licenses/LICENSE-2.0', description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', author_email='clowwindy42@gmail.com', @@ -24,7 +24,7 @@ setup( ssserver = shadowsocks.server:main """, classifiers=[ - 'License :: OSI Approved :: MIT License', + 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', diff --git a/shadowsocks/__init__.py b/shadowsocks/__init__.py index 5ba5908..dc3abd4 100644 --- a/shadowsocks/__init__.py +++ b/shadowsocks/__init__.py @@ -1,24 +1,18 @@ #!/usr/bin/python - -# 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: +# Copyright 2012-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/asyncdns.py b/shadowsocks/asyncdns.py index 6fee6b9..73fab9c 100644 --- a/shadowsocks/asyncdns.py +++ b/shadowsocks/asyncdns.py @@ -1,25 +1,19 @@ #!/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: +# Copyright 2014-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/common.py b/shadowsocks/common.py index 1c10671..1977dcd 100644 --- a/shadowsocks/common.py +++ b/shadowsocks/common.py @@ -1,25 +1,19 @@ #!/usr/bin/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: +# Copyright 2013-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/crypto/__init__.py b/shadowsocks/crypto/__init__.py index 6251321..401c7b7 100644 --- a/shadowsocks/crypto/__init__.py +++ b/shadowsocks/crypto/__init__.py @@ -1,24 +1,18 @@ #!/usr/bin/env python - -# 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: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/crypto/openssl.py b/shadowsocks/crypto/openssl.py index bb11627..d801730 100644 --- a/shadowsocks/crypto/openssl.py +++ b/shadowsocks/crypto/openssl.py @@ -1,24 +1,18 @@ #!/usr/bin/env python - -# 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: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/crypto/rc4_md5.py b/shadowsocks/crypto/rc4_md5.py index 33d481d..40c344d 100644 --- a/shadowsocks/crypto/rc4_md5.py +++ b/shadowsocks/crypto/rc4_md5.py @@ -1,24 +1,18 @@ #!/usr/bin/env python - -# 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: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/crypto/sodium.py b/shadowsocks/crypto/sodium.py index 74fbb33..0c1b2f1 100644 --- a/shadowsocks/crypto/sodium.py +++ b/shadowsocks/crypto/sodium.py @@ -1,24 +1,18 @@ #!/usr/bin/env python - -# 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: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/crypto/table.py b/shadowsocks/crypto/table.py index 08c1205..555fd0a 100644 --- a/shadowsocks/crypto/table.py +++ b/shadowsocks/crypto/table.py @@ -1,24 +1,18 @@ # !/usr/bin/env python - -# 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: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/crypto/util.py b/shadowsocks/crypto/util.py index 1242f8e..e579455 100644 --- a/shadowsocks/crypto/util.py +++ b/shadowsocks/crypto/util.py @@ -1,24 +1,18 @@ #!/usr/bin/env python - -# 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: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/daemon.py b/shadowsocks/daemon.py index 9961ea7..a73e927 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -1,25 +1,19 @@ #!/usr/bin/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: +# Copyright 2014-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/encrypt.py b/shadowsocks/encrypt.py index 3dd9264..9365a98 100644 --- a/shadowsocks/encrypt.py +++ b/shadowsocks/encrypt.py @@ -1,24 +1,18 @@ #!/usr/bin/env python - -# 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: +# Copyright 2012-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/eventloop.py b/shadowsocks/eventloop.py index 304b229..839842d 100644 --- a/shadowsocks/eventloop.py +++ b/shadowsocks/eventloop.py @@ -1,25 +1,19 @@ #!/usr/bin/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: +# Copyright 2013-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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 diff --git a/shadowsocks/local.py b/shadowsocks/local.py index c1a84f1..9520c68 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -1,25 +1,19 @@ #!/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: +# Copyright 2012-2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/lru_cache.py b/shadowsocks/lru_cache.py index 4523399..27c0738 100644 --- a/shadowsocks/lru_cache.py +++ b/shadowsocks/lru_cache.py @@ -1,25 +1,19 @@ #!/usr/bin/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: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/server.py b/shadowsocks/server.py index 41c70ca..195f17b 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -1,25 +1,19 @@ #!/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: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index d68e424..03cd9b5 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -1,25 +1,19 @@ #!/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: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index ff6391c..9e5463e 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -1,25 +1,19 @@ #!/usr/bin/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: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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 # +----+------+------+----------+----------+----------+ diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 417348c..4a462f8 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -1,25 +1,19 @@ #!/usr/bin/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: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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/tests/coverage_server.py b/tests/coverage_server.py index d752cff..23cc8cd 100644 --- a/tests/coverage_server.py +++ b/tests/coverage_server.py @@ -1,4 +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. if __name__ == '__main__': import tornado.ioloop diff --git a/tests/nose_plugin.py b/tests/nose_plugin.py index ad32cf0..86b1a86 100644 --- a/tests/nose_plugin.py +++ b/tests/nose_plugin.py @@ -1,3 +1,19 @@ +#!/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. + import nose from nose.plugins.base import Plugin diff --git a/tests/test.py b/tests/test.py index 5ad94c9..4953476 100755 --- a/tests/test.py +++ b/tests/test.py @@ -1,25 +1,19 @@ #!/usr/bin/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: +# Copyright 2015 clowwindy # -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. +# 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 # -# 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. +# 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 From 318d88ec89d39d8b5ef458861c6beaab2ddb107a Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 3 Feb 2015 18:09:07 +0800 Subject: [PATCH 123/146] use string more --- shadowsocks/crypto/openssl.py | 64 +++++++++++++++++------------------ shadowsocks/crypto/rc4_md5.py | 6 ++-- shadowsocks/crypto/sodium.py | 16 ++++----- shadowsocks/crypto/table.py | 6 ++-- shadowsocks/encrypt.py | 18 +++++----- shadowsocks/utils.py | 6 ++-- 6 files changed, 57 insertions(+), 59 deletions(-) diff --git a/shadowsocks/crypto/openssl.py b/shadowsocks/crypto/openssl.py index d801730..d2216c8 100644 --- a/shadowsocks/crypto/openssl.py +++ b/shadowsocks/crypto/openssl.py @@ -111,31 +111,31 @@ class OpenSSLCrypto(object): ciphers = { - b'aes-128-cfb': (16, 16, OpenSSLCrypto), - b'aes-192-cfb': (24, 16, OpenSSLCrypto), - b'aes-256-cfb': (32, 16, OpenSSLCrypto), - b'aes-128-ofb': (16, 16, OpenSSLCrypto), - b'aes-192-ofb': (24, 16, OpenSSLCrypto), - b'aes-256-ofb': (32, 16, OpenSSLCrypto), - b'aes-128-ctr': (16, 16, OpenSSLCrypto), - b'aes-192-ctr': (24, 16, OpenSSLCrypto), - b'aes-256-ctr': (32, 16, OpenSSLCrypto), - b'aes-128-cfb8': (16, 16, OpenSSLCrypto), - b'aes-192-cfb8': (24, 16, OpenSSLCrypto), - b'aes-256-cfb8': (32, 16, OpenSSLCrypto), - b'aes-128-cfb1': (16, 16, OpenSSLCrypto), - b'aes-192-cfb1': (24, 16, OpenSSLCrypto), - b'aes-256-cfb1': (32, 16, OpenSSLCrypto), - b'bf-cfb': (16, 8, OpenSSLCrypto), - b'camellia-128-cfb': (16, 16, OpenSSLCrypto), - b'camellia-192-cfb': (24, 16, OpenSSLCrypto), - b'camellia-256-cfb': (32, 16, OpenSSLCrypto), - b'cast5-cfb': (16, 8, OpenSSLCrypto), - b'des-cfb': (8, 8, OpenSSLCrypto), - b'idea-cfb': (16, 8, OpenSSLCrypto), - b'rc2-cfb': (16, 8, OpenSSLCrypto), - b'rc4': (16, 0, OpenSSLCrypto), - b'seed-cfb': (16, 16, OpenSSLCrypto), + 'aes-128-cfb': (16, 16, OpenSSLCrypto), + 'aes-192-cfb': (24, 16, OpenSSLCrypto), + 'aes-256-cfb': (32, 16, OpenSSLCrypto), + 'aes-128-ofb': (16, 16, OpenSSLCrypto), + 'aes-192-ofb': (24, 16, OpenSSLCrypto), + 'aes-256-ofb': (32, 16, OpenSSLCrypto), + 'aes-128-ctr': (16, 16, OpenSSLCrypto), + 'aes-192-ctr': (24, 16, OpenSSLCrypto), + 'aes-256-ctr': (32, 16, OpenSSLCrypto), + 'aes-128-cfb8': (16, 16, OpenSSLCrypto), + 'aes-192-cfb8': (24, 16, OpenSSLCrypto), + 'aes-256-cfb8': (32, 16, OpenSSLCrypto), + 'aes-128-cfb1': (16, 16, OpenSSLCrypto), + 'aes-192-cfb1': (24, 16, OpenSSLCrypto), + 'aes-256-cfb1': (32, 16, OpenSSLCrypto), + 'bf-cfb': (16, 8, OpenSSLCrypto), + 'camellia-128-cfb': (16, 16, OpenSSLCrypto), + 'camellia-192-cfb': (24, 16, OpenSSLCrypto), + 'camellia-256-cfb': (32, 16, OpenSSLCrypto), + 'cast5-cfb': (16, 8, OpenSSLCrypto), + 'des-cfb': (8, 8, OpenSSLCrypto), + 'idea-cfb': (16, 8, OpenSSLCrypto), + 'rc2-cfb': (16, 8, OpenSSLCrypto), + 'rc4': (16, 0, OpenSSLCrypto), + 'seed-cfb': (16, 16, OpenSSLCrypto), } @@ -148,31 +148,31 @@ def run_method(method): def test_aes_128_cfb(): - run_method(b'aes-128-cfb') + run_method('aes-128-cfb') def test_aes_256_cfb(): - run_method(b'aes-256-cfb') + run_method('aes-256-cfb') def test_aes_128_cfb8(): - run_method(b'aes-128-cfb8') + run_method('aes-128-cfb8') def test_aes_256_ofb(): - run_method(b'aes-256-ofb') + run_method('aes-256-ofb') def test_aes_256_ctr(): - run_method(b'aes-256-ctr') + run_method('aes-256-ctr') def test_bf_cfb(): - run_method(b'bf-cfb') + run_method('bf-cfb') def test_rc4(): - run_method(b'rc4') + run_method('rc4') if __name__ == '__main__': diff --git a/shadowsocks/crypto/rc4_md5.py b/shadowsocks/crypto/rc4_md5.py index 40c344d..1f07a82 100644 --- a/shadowsocks/crypto/rc4_md5.py +++ b/shadowsocks/crypto/rc4_md5.py @@ -34,15 +34,15 @@ def create_cipher(alg, key, iv, op, key_as_bytes=0, d=None, salt=None, ciphers = { - b'rc4-md5': (16, 16, create_cipher), + 'rc4-md5': (16, 16, create_cipher), } def test(): from shadowsocks.crypto import util - cipher = create_cipher(b'rc4-md5', b'k' * 32, b'i' * 16, 1) - decipher = create_cipher(b'rc4-md5', b'k' * 32, b'i' * 16, 0) + 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) diff --git a/shadowsocks/crypto/sodium.py b/shadowsocks/crypto/sodium.py index 0c1b2f1..ae86fef 100644 --- a/shadowsocks/crypto/sodium.py +++ b/shadowsocks/crypto/sodium.py @@ -64,9 +64,9 @@ class SodiumCrypto(object): self.iv = iv self.key_ptr = c_char_p(key) self.iv_ptr = c_char_p(iv) - if cipher_name == b'salsa20': + if cipher_name == 'salsa20': self.cipher = libsodium.crypto_stream_salsa20_xor_ic - elif cipher_name == b'chacha20': + elif cipher_name == 'chacha20': self.cipher = libsodium.crypto_stream_chacha20_xor_ic else: raise Exception('Unknown cipher') @@ -95,22 +95,22 @@ class SodiumCrypto(object): ciphers = { - b'salsa20': (32, 8, SodiumCrypto), - b'chacha20': (32, 8, SodiumCrypto), + 'salsa20': (32, 8, SodiumCrypto), + 'chacha20': (32, 8, SodiumCrypto), } def test_salsa20(): - cipher = SodiumCrypto(b'salsa20', b'k' * 32, b'i' * 16, 1) - decipher = SodiumCrypto(b'salsa20', b'k' * 32, b'i' * 16, 0) + cipher = SodiumCrypto('salsa20', b'k' * 32, b'i' * 16, 1) + decipher = SodiumCrypto('salsa20', b'k' * 32, b'i' * 16, 0) util.run_cipher(cipher, decipher) def test_chacha20(): - cipher = SodiumCrypto(b'chacha20', b'k' * 32, b'i' * 16, 1) - decipher = SodiumCrypto(b'chacha20', b'k' * 32, b'i' * 16, 0) + cipher = SodiumCrypto('chacha20', b'k' * 32, b'i' * 16, 1) + decipher = SodiumCrypto('chacha20', b'k' * 32, b'i' * 16, 0) util.run_cipher(cipher, decipher) diff --git a/shadowsocks/crypto/table.py b/shadowsocks/crypto/table.py index 555fd0a..bc693f5 100644 --- a/shadowsocks/crypto/table.py +++ b/shadowsocks/crypto/table.py @@ -67,7 +67,7 @@ class TableCipher(object): ciphers = { - b'table': (0, 0, TableCipher) + 'table': (0, 0, TableCipher) } @@ -163,8 +163,8 @@ def test_table_result(): def test_encryption(): from shadowsocks.crypto import util - cipher = TableCipher(b'table', b'test', b'', 1) - decipher = TableCipher(b'table', b'test', b'', 0) + cipher = TableCipher('table', b'test', b'', 1) + decipher = TableCipher('table', b'test', b'', 0) util.run_cipher(cipher, decipher) diff --git a/shadowsocks/encrypt.py b/shadowsocks/encrypt.py index 9365a98..4e87f41 100644 --- a/shadowsocks/encrypt.py +++ b/shadowsocks/encrypt.py @@ -22,6 +22,7 @@ import sys import hashlib import logging +from shadowsocks import common from shadowsocks.crypto import rc4_md5, openssl, sodium, table @@ -46,8 +47,6 @@ def try_cipher(key, method=None): 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: @@ -95,8 +94,7 @@ class Encryptor(object): return len(self.cipher_iv) def get_cipher(self, password, method, op, iv): - if hasattr(password, 'encode'): - password = password.encode('utf-8') + password = common.to_bytes(password) m = self._method_info if m[0] > 0: key, iv_ = EVP_BytesToKey(password, m[0], m[1]) @@ -153,12 +151,12 @@ def encrypt_all(password, method, op, data): CIPHERS_TO_TEST = [ - b'aes-128-cfb', - b'aes-256-cfb', - b'rc4-md5', - b'salsa20', - b'chacha20', - b'table', + 'aes-128-cfb', + 'aes-256-cfb', + 'rc4-md5', + 'salsa20', + 'chacha20', + 'table', ] diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 4a462f8..c261148 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -154,11 +154,11 @@ def get_config(is_local): elif key == '-l': config['local_port'] = int(value) elif key == '-s': - config['server'] = to_bytes(value) + config['server'] = to_str(value) elif key == '-m': - config['method'] = to_bytes(value) + config['method'] = to_str(value) elif key == '-b': - config['local_address'] = to_bytes(value) + config['local_address'] = to_str(value) elif key == '-v': v_count += 1 # '-vv' turns on more verbose mode From 6d09cd21ca6acd506a42858b123ece1ab4e6d423 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 3 Feb 2015 18:13:01 +0800 Subject: [PATCH 124/146] fix openssl --- shadowsocks/crypto/openssl.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shadowsocks/crypto/openssl.py b/shadowsocks/crypto/openssl.py index d2216c8..3775b6c 100644 --- a/shadowsocks/crypto/openssl.py +++ b/shadowsocks/crypto/openssl.py @@ -20,6 +20,7 @@ from __future__ import absolute_import, division, print_function, \ from ctypes import c_char_p, c_int, c_long, byref,\ create_string_buffer, c_void_p +from shadowsocks import common from shadowsocks.crypto import util __all__ = ['ciphers'] @@ -58,7 +59,7 @@ def load_openssl(): def load_cipher(cipher_name): - func_name = b'EVP_' + cipher_name.replace(b'-', b'_') + func_name = 'EVP_' + cipher_name.replace('-', '_') if bytes != str: func_name = str(func_name, 'utf-8') cipher = getattr(libcrypto, func_name, None) @@ -73,6 +74,7 @@ class OpenSSLCrypto(object): self._ctx = None if not loaded: load_openssl() + cipher_name = common.to_bytes(cipher_name) cipher = libcrypto.EVP_get_cipherbyname(cipher_name) if not cipher: cipher = load_cipher(cipher_name) From e564f17505f6e935b7893b42d357aae672942646 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 3 Feb 2015 18:23:22 +0800 Subject: [PATCH 125/146] fix bytes in utils --- shadowsocks/utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index c261148..24e9b46 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -90,10 +90,10 @@ def check_config(config, is_local): if config.get('server', '') in [b'127.0.0.1', b'localhost']: logging.warn('warning: server set to listen on %s:%s, are you sure?' % (to_str(config['server']), config['server_port'])) - if (config.get('method', '') or '').lower() == b'table': + if (config.get('method', '') or '').lower() == 'table': logging.warn('warning: table is not safe; please use a safer cipher, ' 'like AES-256-CFB') - if (config.get('method', '') or '').lower() == b'rc4': + if (config.get('method', '') or '').lower() == 'rc4': logging.warn('warning: RC4 is not safe; please use a safer cipher, ' 'like AES-256-CFB') if config.get('timeout', 300) < 100: @@ -198,8 +198,8 @@ def get_config(is_local): print_help(is_local) sys.exit(2) - config['password'] = config.get('password', '') - config['method'] = config.get('method', b'aes-256-cfb') + config['password'] = to_bytes(config.get('password', b'')) + config['method'] = to_str(config.get('method', 'aes-256-cfb')) config['port_password'] = config.get('port_password', None) config['timeout'] = int(config.get('timeout', 300)) config['fast_open'] = config.get('fast_open', False) @@ -208,7 +208,7 @@ def get_config(is_local): config['log-file'] = config.get('log-file', '/var/log/shadowsocks.log') config['workers'] = config.get('workers', 1) config['verbose'] = config.get('verbose', False) - config['local_address'] = config.get('local_address', '127.0.0.1') + 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: From e71ce6c758643f06c05de06d5dd10cb7fe205378 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 3 Feb 2015 18:45:36 +0800 Subject: [PATCH 126/146] fix server check --- shadowsocks/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 24e9b46..96abdb6 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -87,7 +87,7 @@ def check_config(config, is_local): if config.get('local_address', '') in [b'0.0.0.0']: logging.warn('warning: local set to listen on 0.0.0.0, it\'s not safe') - if config.get('server', '') in [b'127.0.0.1', b'localhost']: + if config.get('server', '') in ['127.0.0.1', 'localhost']: logging.warn('warning: server set to listen on %s:%s, are you sure?' % (to_str(config['server']), config['server_port'])) if (config.get('method', '') or '').lower() == 'table': @@ -215,8 +215,10 @@ def get_config(is_local): logging.error('server addr not specified') print_local_help() sys.exit(2) + else: + config['server'] = to_str(config['server']) else: - config['server'] = config.get('server', '0.0.0.0') + 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')) From 783a6ef7f294b71e22b4d485545b4a7c32a651c6 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 9 Feb 2015 13:50:46 +0800 Subject: [PATCH 127/146] support multiple server ip on client side --- .jenkins.sh | 1 + shadowsocks/tcprelay.py | 3 ++- shadowsocks/udprelay.py | 3 ++- tests/client-multi-server-ip.json | 10 ++++++++++ 4 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 tests/client-multi-server-ip.json diff --git a/.jenkins.sh b/.jenkins.sh index 73c4abc..a811983 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -41,6 +41,7 @@ run_test python tests/test.py --with-coverage -c tests/salsa20.json run_test python tests/test.py --with-coverage -c tests/chacha20.json run_test python tests/test.py --with-coverage -c tests/table.json run_test python tests/test.py --with-coverage -c tests/server-multi-ports.json +run_test python tests/test.py --with-coverage -s tests/aes.json -c tests/client-multi-server-ip.json run_test python tests/test.py --with-coverage -s tests/server-multi-passwd.json -c tests/server-multi-passwd-client-side.json run_test python tests/test.py --with-coverage -c tests/workers.json run_test python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-client-side.json diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 03cd9b5..f65b282 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -144,8 +144,9 @@ class TCPRelayHandler(object): 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) - # TODO support multiple server IP return server, server_port def _update_activity(self): diff --git a/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index 9e5463e..0499f0e 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -127,8 +127,9 @@ class UDPRelay(object): 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) - # TODO support multiple server IP return server, server_port def _close_client(self, client): diff --git a/tests/client-multi-server-ip.json b/tests/client-multi-server-ip.json new file mode 100644 index 0000000..1823c2a --- /dev/null +++ b/tests/client-multi-server-ip.json @@ -0,0 +1,10 @@ +{ + "server":["127.0.0.1", "127.0.0.1"], + "server_port":8388, + "local_port":1081, + "password":"aes_password", + "timeout":60, + "method":"aes-256-cfb", + "local_address":"127.0.0.1", + "fast_open":false +} From dfd81af8445a4d9768cae45246de1c35102013ac Mon Sep 17 00:00:00 2001 From: clowwindy Date: Mon, 9 Feb 2015 14:43:11 +0800 Subject: [PATCH 128/146] support --version --- .jenkins.sh | 2 +- shadowsocks/local.py | 2 -- shadowsocks/server.py | 2 -- shadowsocks/tcprelay.py | 2 +- shadowsocks/utils.py | 16 +++++++++++----- tests/test_command.sh | 3 +++ 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/.jenkins.sh b/.jenkins.sh index a811983..2cd02a2 100755 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -27,7 +27,7 @@ function run_test { python --version coverage erase mkdir tmp -run_test pep8 . +run_test pep8 --ignore=E402 . run_test pyflakes . run_test coverage run tests/nose_plugin.py -v run_test python setup.py sdist diff --git a/shadowsocks/local.py b/shadowsocks/local.py index 9520c68..b75fe9b 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -40,8 +40,6 @@ def main(): daemon.daemon_exec(config) - utils.print_shadowsocks() - try: logging.info("starting local at %s:%d" % (config['local_address'], config['local_port'])) diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 195f17b..34aab2a 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -34,8 +34,6 @@ def main(): daemon.daemon_exec(config) - utils.print_shadowsocks() - if config['port_password']: if config['password']: logging.warn('warning: port_password should not be used with ' diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index f65b282..12792f7 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -328,7 +328,7 @@ class TCPRelayHandler(object): 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)) + raise Exception("getaddrinfo failed for %s:%d" % (ip, port)) af, socktype, proto, canonname, sa = addrs[0] if self._forbidden_iplist: if common.to_str(sa[0]) in self._forbidden_iplist: diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 96abdb6..71c6d94 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -50,7 +50,7 @@ def print_shadowsocks(): version = pkg_resources.get_distribution('shadowsocks').version except Exception: pass - print('shadowsocks %s' % version) + print('Shadowsocks %s' % version) def find_config(): @@ -119,11 +119,12 @@ def get_config(is_local): format='%(levelname)-s: %(message)s') if is_local: shortopts = 'hd:s:b:p:k:l:m:c:t:vq' - longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'user='] + longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'user=', + 'version'] else: shortopts = 'hd:s:p:k:m:c:t:vq' longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers=', - 'forbidden-ip=', 'user='] + 'forbidden-ip=', 'user=', 'version'] try: config_path = find_config() optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts) @@ -179,6 +180,9 @@ def get_config(is_local): else: print_server_help() sys.exit(0) + elif key == '--version': + print_shadowsocks() + sys.exit(0) elif key == '-d': config['daemon'] = to_str(value) elif key == '--pid-file': @@ -262,7 +266,6 @@ A fast tunnel proxy that helps you bypass firewalls. You can supply configurations via either config file or command line arguments. Proxy options: - -h, --help show this help message and exit -c CONFIG path to config file -s SERVER_ADDR server address -p SERVER_PORT server port, default: 8388 @@ -274,12 +277,14 @@ Proxy options: --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: ''') @@ -292,7 +297,6 @@ A fast tunnel proxy that helps you bypass firewalls. You can supply configurations via either config file or command line arguments. Proxy options: - -h, --help show this help message and exit -c CONFIG path to config file -s SERVER_ADDR server address, default: 0.0.0.0 -p SERVER_PORT server port, default: 8388 @@ -304,12 +308,14 @@ Proxy options: --forbidden-ip IPLIST comma seperated IP list forbidden to connect 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: ''') diff --git a/tests/test_command.sh b/tests/test_command.sh index 2b8b68a..be05704 100755 --- a/tests/test_command.sh +++ b/tests/test_command.sh @@ -6,6 +6,9 @@ PYTHON="coverage run -a -p" LOCAL="$PYTHON shadowsocks/local.py" SERVER="$PYTHON shadowsocks/server.py" +assert "$LOCAL --version 2>&1 | grep Shadowsocks | awk -F\" \" '{print \$1}'" "Shadowsocks" +assert "$SERVER --version 2>&1 | grep Shadowsocks | awk -F\" \" '{print \$1}'" "Shadowsocks" + assert "$LOCAL 2>&1 | grep ERROR" "ERROR: config not specified" assert "$LOCAL 2>&1 | grep usage | cut -d: -f1" "usage" From 48ddc1714ba774d34073e0e34c7e662583365b83 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 10 Feb 2015 17:02:31 +0800 Subject: [PATCH 129/146] move jenkins.sh --- .travis.yml | 2 +- .jenkins.sh => tests/jenkins.sh | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename .jenkins.sh => tests/jenkins.sh (100%) diff --git a/.travis.yml b/.travis.yml index 7a222ea..c5caa33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,4 +17,4 @@ before_install: - sudo tests/libsodium/install.sh - sudo tests/setup_tc.sh script: - - ./.jenkins.sh + - tests/jenkins.sh diff --git a/.jenkins.sh b/tests/jenkins.sh similarity index 100% rename from .jenkins.sh rename to tests/jenkins.sh From cb7062e1c1bcbec1b2a699a016a774e4d757389b Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 10 Feb 2015 17:16:24 +0800 Subject: [PATCH 130/146] implement utils.print_exception() Previously we used logging.error(e) and traceback.print_exc() to output error stack trace. The problem is, we want to output the stack trace only when verbose > 0. The if statement scattered around the code. So we replaced them with the new utils.print_exception() call. --- shadowsocks/asyncdns.py | 6 ++---- shadowsocks/daemon.py | 10 +++++----- shadowsocks/eventloop.py | 6 +++--- shadowsocks/local.py | 5 +---- shadowsocks/server.py | 5 +---- shadowsocks/tcprelay.py | 12 +++++------- shadowsocks/udprelay.py | 4 ++-- shadowsocks/utils.py | 13 +++++++++++++ 8 files changed, 32 insertions(+), 29 deletions(-) diff --git a/shadowsocks/asyncdns.py b/shadowsocks/asyncdns.py index 73fab9c..9467490 100644 --- a/shadowsocks/asyncdns.py +++ b/shadowsocks/asyncdns.py @@ -25,7 +25,7 @@ import struct import re import logging -from shadowsocks import common, lru_cache, eventloop +from shadowsocks import common, lru_cache, eventloop, utils CACHE_SWEEP_INTERVAL = 30 @@ -221,9 +221,7 @@ def parse_response(data): response.answers.append((an[1], an[2], an[3])) return response except Exception as e: - import traceback - traceback.print_exc() - logging.error(e) + utils.print_exception(e) return None diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index a73e927..c23ec58 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -23,7 +23,7 @@ import sys import logging import signal import time -from shadowsocks import common +from shadowsocks import common, utils # this module is ported from ShadowVPN daemon.c @@ -58,7 +58,7 @@ def write_pid_file(pid_file, pid): fd = os.open(pid_file, os.O_RDWR | os.O_CREAT, stat.S_IRUSR | stat.S_IWUSR) except OSError as e: - logging.error(e) + utils.print_exception(e) return -1 flags = fcntl.fcntl(fd, fcntl.F_GETFD) assert flags != -1 @@ -127,7 +127,7 @@ def daemon_start(pid_file, log_file): freopen(log_file, 'a', sys.stdout) freopen(log_file, 'a', sys.stderr) except IOError as e: - logging.error(e) + utils.print_exception(e) sys.exit(1) @@ -140,7 +140,7 @@ def daemon_stop(pid_file): if not buf: logging.error('not running') except IOError as e: - logging.error(e) + utils.print_exception(e) if e.errno == errno.ENOENT: # always exit 0 if we are sure daemon is not running logging.error('not running') @@ -155,7 +155,7 @@ def daemon_stop(pid_file): logging.error('not running') # always exit 0 if we are sure daemon is not running return - logging.error(e) + utils.print_exception(e) sys.exit(1) else: logging.error('pid is not positive: %d', pid) diff --git a/shadowsocks/eventloop.py b/shadowsocks/eventloop.py index 839842d..77c64ef 100644 --- a/shadowsocks/eventloop.py +++ b/shadowsocks/eventloop.py @@ -28,6 +28,8 @@ import errno import logging from collections import defaultdict +from shadowsocks import utils + __all__ = ['EventLoop', 'POLL_NULL', 'POLL_IN', 'POLL_OUT', 'POLL_ERR', 'POLL_HUP', 'POLL_NVAL', 'EVENT_NAMES'] @@ -223,9 +225,7 @@ class EventLoop(object): try: handler(events) except (OSError, IOError) as e: - logging.error(e) - import traceback - traceback.print_exc() + utils.print_exception(e) if self._handlers_to_remove: for handler in self._handlers_to_remove: self._handlers.remove(handler) diff --git a/shadowsocks/local.py b/shadowsocks/local.py index b75fe9b..a4c853e 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -65,10 +65,7 @@ def main(): daemon.set_user(config.get('user', None)) loop.run() except Exception as e: - logging.error(e) - if config['verbose']: - import traceback - traceback.print_exc() + utils.print_exception(e) sys.exit(1) if __name__ == '__main__': diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 34aab2a..27515b5 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -80,10 +80,7 @@ def main(): daemon.set_user(config.get('user', None)) loop.run() except Exception as e: - logging.error(e) - if config['verbose']: - import traceback - traceback.print_exc() + utils.print_exception(e) sys.exit(1) if int(config['workers']) > 1: diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 12792f7..a23a58c 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -203,9 +203,7 @@ class TCPRelayHandler(object): errno.EWOULDBLOCK): uncomplete = True else: - logging.error(e) - if self._config['verbose']: - traceback.print_exc() + utils.print_exception(e) self.destroy() return False if uncomplete: @@ -259,7 +257,7 @@ class TCPRelayHandler(object): self._config['fast_open'] = False self.destroy() else: - logging.error(e) + utils.print_exception(e) if self._config['verbose']: traceback.print_exc() self.destroy() @@ -383,7 +381,7 @@ class TCPRelayHandler(object): self._update_stream(STREAM_DOWN, WAIT_STATUS_READING) return except Exception as e: - logging.error(e) + utils.print_exception(e) if self._config['verbose']: traceback.print_exc() self.destroy() @@ -445,7 +443,7 @@ class TCPRelayHandler(object): try: self._write_to_sock(data, self._local_sock) except Exception as e: - logging.error(e) + utils.print_exception(e) if self._config['verbose']: traceback.print_exc() # TODO use logging when debug completed @@ -683,7 +681,7 @@ class TCPRelay(object): errno.EWOULDBLOCK): continue else: - logging.error(e) + utils.print_exception(e) if self._config['verbose']: traceback.print_exc() else: diff --git a/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index 0499f0e..80c44bb 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -69,7 +69,7 @@ import struct import errno import random -from shadowsocks import encrypt, eventloop, lru_cache, common +from shadowsocks import encrypt, eventloop, lru_cache, common, utils from shadowsocks.common import parse_header, pack_addr @@ -208,7 +208,7 @@ class UDPRelay(object): if err in (errno.EINPROGRESS, errno.EAGAIN): pass else: - logging.error(e) + utils.print_exception(e) def _handle_client(self, sock): data, r_addr = sock.recvfrom(BUF_SIZE) diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py index 71c6d94..175feef 100644 --- a/shadowsocks/utils.py +++ b/shadowsocks/utils.py @@ -29,6 +29,8 @@ from shadowsocks import encrypt VERBOSE_LEVEL = 5 +verbose = 0 + def check_python(): info = sys.version_info @@ -43,6 +45,14 @@ def check_python(): sys.exit(1) +def print_exception(e): + global verbose + logging.error(e) + if verbose > 0: + import traceback + traceback.print_exc() + + def print_shadowsocks(): version = '' try: @@ -115,6 +125,8 @@ def check_config(config, is_local): def get_config(is_local): + global verbose + logging.basicConfig(level=logging.INFO, format='%(levelname)-s: %(message)s') if is_local: @@ -243,6 +255,7 @@ def get_config(is_local): level = logging.ERROR else: level = logging.INFO + verbose = config['verbose'] logging.basicConfig(level=level, format='%(asctime)s %(levelname)-8s %(message)s', datefmt='%Y-%m-%d %H:%M:%S') From d774286dc0e6d0b2f8b4f20528a69f7b4ac45a18 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 10 Feb 2015 17:26:10 +0800 Subject: [PATCH 131/146] rename utils module into shell Since utils is ambiguous, we want to give the module a more clear role. --- shadowsocks/asyncdns.py | 4 ++-- shadowsocks/daemon.py | 10 +++++----- shadowsocks/eventloop.py | 4 ++-- shadowsocks/local.py | 8 ++++---- shadowsocks/server.py | 8 ++++---- shadowsocks/{utils.py => shell.py} | 0 shadowsocks/tcprelay.py | 16 ++++++++-------- shadowsocks/udprelay.py | 4 ++-- 8 files changed, 27 insertions(+), 27 deletions(-) rename shadowsocks/{utils.py => shell.py} (100%) diff --git a/shadowsocks/asyncdns.py b/shadowsocks/asyncdns.py index 9467490..7e4a4ed 100644 --- a/shadowsocks/asyncdns.py +++ b/shadowsocks/asyncdns.py @@ -25,7 +25,7 @@ import struct import re import logging -from shadowsocks import common, lru_cache, eventloop, utils +from shadowsocks import common, lru_cache, eventloop, shell CACHE_SWEEP_INTERVAL = 30 @@ -221,7 +221,7 @@ def parse_response(data): response.answers.append((an[1], an[2], an[3])) return response except Exception as e: - utils.print_exception(e) + shell.print_exception(e) return None diff --git a/shadowsocks/daemon.py b/shadowsocks/daemon.py index c23ec58..8dc5608 100644 --- a/shadowsocks/daemon.py +++ b/shadowsocks/daemon.py @@ -23,7 +23,7 @@ import sys import logging import signal import time -from shadowsocks import common, utils +from shadowsocks import common, shell # this module is ported from ShadowVPN daemon.c @@ -58,7 +58,7 @@ def write_pid_file(pid_file, pid): fd = os.open(pid_file, os.O_RDWR | os.O_CREAT, stat.S_IRUSR | stat.S_IWUSR) except OSError as e: - utils.print_exception(e) + shell.print_exception(e) return -1 flags = fcntl.fcntl(fd, fcntl.F_GETFD) assert flags != -1 @@ -127,7 +127,7 @@ def daemon_start(pid_file, log_file): freopen(log_file, 'a', sys.stdout) freopen(log_file, 'a', sys.stderr) except IOError as e: - utils.print_exception(e) + shell.print_exception(e) sys.exit(1) @@ -140,7 +140,7 @@ def daemon_stop(pid_file): if not buf: logging.error('not running') except IOError as e: - utils.print_exception(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') @@ -155,7 +155,7 @@ def daemon_stop(pid_file): logging.error('not running') # always exit 0 if we are sure daemon is not running return - utils.print_exception(e) + shell.print_exception(e) sys.exit(1) else: logging.error('pid is not positive: %d', pid) diff --git a/shadowsocks/eventloop.py b/shadowsocks/eventloop.py index 77c64ef..42f9205 100644 --- a/shadowsocks/eventloop.py +++ b/shadowsocks/eventloop.py @@ -28,7 +28,7 @@ import errno import logging from collections import defaultdict -from shadowsocks import utils +from shadowsocks import shell __all__ = ['EventLoop', 'POLL_NULL', 'POLL_IN', 'POLL_OUT', 'POLL_ERR', @@ -225,7 +225,7 @@ class EventLoop(object): try: handler(events) except (OSError, IOError) as e: - utils.print_exception(e) + shell.print_exception(e) if self._handlers_to_remove: for handler in self._handlers_to_remove: self._handlers.remove(handler) diff --git a/shadowsocks/local.py b/shadowsocks/local.py index a4c853e..4255a2e 100755 --- a/shadowsocks/local.py +++ b/shadowsocks/local.py @@ -24,11 +24,11 @@ import logging import signal sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) -from shadowsocks import utils, daemon, eventloop, tcprelay, udprelay, asyncdns +from shadowsocks import shell, daemon, eventloop, tcprelay, udprelay, asyncdns def main(): - utils.check_python() + shell.check_python() # fix py2exe if hasattr(sys, "frozen") and sys.frozen in \ @@ -36,7 +36,7 @@ def main(): p = os.path.dirname(os.path.abspath(sys.executable)) os.chdir(p) - config = utils.get_config(True) + config = shell.get_config(True) daemon.daemon_exec(config) @@ -65,7 +65,7 @@ def main(): daemon.set_user(config.get('user', None)) loop.run() except Exception as e: - utils.print_exception(e) + shell.print_exception(e) sys.exit(1) if __name__ == '__main__': diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 27515b5..429a20a 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -24,13 +24,13 @@ import logging import signal sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) -from shadowsocks import utils, daemon, eventloop, tcprelay, udprelay, asyncdns +from shadowsocks import shell, daemon, eventloop, tcprelay, udprelay, asyncdns def main(): - utils.check_python() + shell.check_python() - config = utils.get_config(False) + config = shell.get_config(False) daemon.daemon_exec(config) @@ -80,7 +80,7 @@ def main(): daemon.set_user(config.get('user', None)) loop.run() except Exception as e: - utils.print_exception(e) + shell.print_exception(e) sys.exit(1) if int(config['workers']) > 1: diff --git a/shadowsocks/utils.py b/shadowsocks/shell.py similarity index 100% rename from shadowsocks/utils.py rename to shadowsocks/shell.py diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index a23a58c..c9ed7bd 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -26,7 +26,7 @@ import logging import traceback import random -from shadowsocks import encrypt, eventloop, utils, common +from shadowsocks import encrypt, eventloop, shell, common from shadowsocks.common import parse_header # we clear at most TIMEOUTS_CLEAN_SIZE timeouts each time @@ -203,7 +203,7 @@ class TCPRelayHandler(object): errno.EWOULDBLOCK): uncomplete = True else: - utils.print_exception(e) + shell.print_exception(e) self.destroy() return False if uncomplete: @@ -257,7 +257,7 @@ class TCPRelayHandler(object): self._config['fast_open'] = False self.destroy() else: - utils.print_exception(e) + shell.print_exception(e) if self._config['verbose']: traceback.print_exc() self.destroy() @@ -381,7 +381,7 @@ class TCPRelayHandler(object): self._update_stream(STREAM_DOWN, WAIT_STATUS_READING) return except Exception as e: - utils.print_exception(e) + shell.print_exception(e) if self._config['verbose']: traceback.print_exc() self.destroy() @@ -443,7 +443,7 @@ class TCPRelayHandler(object): try: self._write_to_sock(data, self._local_sock) except Exception as e: - utils.print_exception(e) + shell.print_exception(e) if self._config['verbose']: traceback.print_exc() # TODO use logging when debug completed @@ -630,7 +630,7 @@ class TCPRelay(object): # we just need a sorted last_activity queue and it's faster than heapq # in fact we can do O(1) insertion/remove so we invent our own if self._timeouts: - logging.log(utils.VERBOSE_LEVEL, 'sweeping timeouts') + logging.log(shell.VERBOSE_LEVEL, 'sweeping timeouts') now = time.time() length = len(self._timeouts) pos = self._timeout_offset @@ -663,7 +663,7 @@ class TCPRelay(object): # handle events and dispatch to handlers for sock, fd, event in events: if sock: - logging.log(utils.VERBOSE_LEVEL, 'fd %d %s', fd, + 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: @@ -681,7 +681,7 @@ class TCPRelay(object): errno.EWOULDBLOCK): continue else: - utils.print_exception(e) + shell.print_exception(e) if self._config['verbose']: traceback.print_exc() else: diff --git a/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index 80c44bb..98bfaaa 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -69,7 +69,7 @@ import struct import errno import random -from shadowsocks import encrypt, eventloop, lru_cache, common, utils +from shadowsocks import encrypt, eventloop, lru_cache, common, shell from shadowsocks.common import parse_header, pack_addr @@ -208,7 +208,7 @@ class UDPRelay(object): if err in (errno.EINPROGRESS, errno.EAGAIN): pass else: - utils.print_exception(e) + shell.print_exception(e) def _handle_client(self, sock): data, r_addr = sock.recvfrom(BUF_SIZE) From 581d6e687f96d05c11f79c50c1abba1be96b9803 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 10 Feb 2015 17:38:01 +0800 Subject: [PATCH 132/146] use localhost in test.py Since now the unit tests is huge, using third party website is not polite. So use localhost instead. --- tests/test.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test.py b/tests/test.py index 4953476..29b57d4 100755 --- a/tests/test.py +++ b/tests/test.py @@ -28,6 +28,8 @@ from subprocess import Popen, PIPE python = ['python'] +default_url = 'http://localhost/' + parser = argparse.ArgumentParser(description='test Shadowsocks') parser.add_argument('-c', '--client-conf', type=str, default=None) parser.add_argument('-s', '--server-conf', type=str, default=None) @@ -36,7 +38,7 @@ parser.add_argument('-b', '--server-args', type=str, default=None) parser.add_argument('--with-coverage', action='store_true', default=None) parser.add_argument('--should-fail', action='store_true', default=None) parser.add_argument('--tcp-only', action='store_true', default=None) -parser.add_argument('--url', type=str, default='http://www.example.com/') +parser.add_argument('--url', type=str, default=default_url) parser.add_argument('--dns', type=str, default='8.8.8.8') config = parser.parse_args() @@ -59,6 +61,8 @@ if config.client_args: server_args.extend(config.server_args.split()) else: server_args.extend(config.client_args.split()) +if config.url == default_url: + server_args.extend(['--forbidden-ip', '']) p1 = Popen(server_args, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) p2 = Popen(client_args, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) From 42ce2569c4774cd021ed88dd7b9393d4cb51595d Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 10 Feb 2015 17:43:06 +0800 Subject: [PATCH 133/146] also test with real website Now that localhost is used in tests, DNS code is uncovered. Use clients1.google.com/generate_204 to test if a real website works. --- tests/jenkins.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/jenkins.sh b/tests/jenkins.sh index 2cd02a2..71d5b1c 100755 --- a/tests/jenkins.sh +++ b/tests/jenkins.sh @@ -49,6 +49,9 @@ run_test python tests/test.py --with-coverage -b "-m rc4-md5 -k testrc4 -s 127.0 run_test python tests/test.py --with-coverage -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --workers 1" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -qq -b 127.0.0.1" run_test python tests/test.py --with-coverage --should-fail --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 --forbidden-ip=127.0.0.1,::1,8.8.8.8" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" +# test if DNS works +run_test python tests/test.py --with-coverage -c tests/aes.json --url="https://clients1.google.com/generate_204" + # test localhost is in the forbidden list by default run_test python tests/test.py --with-coverage --should-fail --tcp-only --url="http://127.0.0.1/" -b "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388" -a "-m aes-256-cfb -k testrc4 -s 127.0.0.1 -p 8388 -l 1081 -t 30 -b 127.0.0.1" From 0e14f3bbefbfb7a37be29e6861864225fe819873 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 10 Feb 2015 18:01:53 +0800 Subject: [PATCH 134/146] fix travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c5caa33..f29cb96 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ before_install: - sudo apt-get update -qq - sudo apt-get install -qq build-essential dnsutils iproute nginx bc - sudo dd if=/dev/urandom of=/usr/share/nginx/www/file bs=1M count=10 + - sudo sh -c "echo '127.0.0.1 localhost' > /etc/hosts" - sudo service nginx restart - pip install pep8 pyflakes nose coverage - sudo tests/socksify/install.sh From 1b7ab23f78d33730b5f793271bca15985f97a722 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 10 Feb 2015 18:25:36 +0800 Subject: [PATCH 135/146] release 2.6.8 --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 8004a3f..2946ecf 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +2.6.8 2015-02-10 +- Support multiple server ip on client side +- Support --version +- Minor fixes + 2.6.7 2015-02-02 - Support --user - Support CIDR format in --forbidden-ip From 294556f8bc972dd8ccf4f6b9e6eba6574725d5c0 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 10 Feb 2015 18:26:06 +0800 Subject: [PATCH 136/146] bump 2.6.9 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9485ce2..512ff63 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with codecs.open('README.rst', encoding='utf-8') as f: setup( name="shadowsocks", - version="2.6.8", + version="2.6.9", license='http://www.apache.org/licenses/LICENSE-2.0', description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', From edb7822a7b53540833b68810fc73608b212f7125 Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Wed, 11 Feb 2015 10:02:35 +0800 Subject: [PATCH 137/146] convert remote_address to str so it will be printed more correctly on python 3 --- shadowsocks/tcprelay.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index c9ed7bd..4834883 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -295,7 +295,7 @@ class TCPRelayHandler(object): logging.info('connecting %s:%d from %s:%d' % (common.to_str(remote_addr), remote_port, self._client_address[0], self._client_address[1])) - self._remote_address = (remote_addr, remote_port) + self._remote_address = (common.to_str(remote_addr), remote_port) # pause reading self._update_stream(STREAM_UP, WAIT_STATUS_WRITING) self._stage = STAGE_DNS From 4172639d48ceb42c4daec81eb6683ac3dc50b265 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Thu, 12 Feb 2015 14:18:18 +0800 Subject: [PATCH 138/146] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aee2bdc..3c04db3 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Bugs and Issues -[Android]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#android +[Android]: https://github.com/shadowsocks/shadowsocks-android [Build Status]: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat [Configuration]: https://github.com/shadowsocks/shadowsocks/wiki/Configuration-via-Config-File [Coverage Status]: https://jenkins.shadowvpn.org/result/shadowsocks @@ -103,4 +103,4 @@ Bugs and Issues [Travis CI]: https://travis-ci.org/shadowsocks/shadowsocks [Troubleshooting]: https://github.com/shadowsocks/shadowsocks/wiki/Troubleshooting [Wiki]: https://github.com/shadowsocks/shadowsocks/wiki -[Windows]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#windows +[Windows]: https://github.com/shadowsocks/shadowsocks-csharp From b6e6e14b8a4d864982b4aaf3f4ae6430368cfd0d Mon Sep 17 00:00:00 2001 From: Joshua Lund Date: Sat, 14 Feb 2015 21:33:40 -0700 Subject: [PATCH 139/146] Use AES in the Usage example instead of RC4. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3c04db3..76d759a 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,11 @@ See [Install Server on Windows] ### Usage - ssserver -p 443 -k password -m rc4-md5 + ssserver -p 443 -k password -m aes-256-cfb To run in the background: - sudo ssserver -p 443 -k password -m rc4-md5 --user nobody -d start + sudo ssserver -p 443 -k password -m aes-256-cfb --user nobody -d start To stop: From d3831bef8cf9029a6dc49b1ddbeddc29ec403807 Mon Sep 17 00:00:00 2001 From: lazybios Date: Sun, 1 Mar 2015 14:14:35 +0800 Subject: [PATCH 140/146] remove duplicate code from shell.py --- shadowsocks/shell.py | 1 - 1 file changed, 1 deletion(-) diff --git a/shadowsocks/shell.py b/shadowsocks/shell.py index 175feef..2195c1a 100644 --- a/shadowsocks/shell.py +++ b/shadowsocks/shell.py @@ -157,7 +157,6 @@ def get_config(is_local): else: config = {} - optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts) v_count = 0 for key, value in optlist: if key == '-p': From e17279e5bf0b9a04835531bb75b9c54905da1542 Mon Sep 17 00:00:00 2001 From: Kim Wong Date: Sat, 14 Mar 2015 07:35:43 +0800 Subject: [PATCH 141/146] remove duplicated line (refer line 221) --- shadowsocks/shell.py | 1 - 1 file changed, 1 deletion(-) diff --git a/shadowsocks/shell.py b/shadowsocks/shell.py index 2195c1a..f8ae81f 100644 --- a/shadowsocks/shell.py +++ b/shadowsocks/shell.py @@ -221,7 +221,6 @@ def get_config(is_local): config['workers'] = config.get('workers', 1) config['pid-file'] = config.get('pid-file', '/var/run/shadowsocks.pid') config['log-file'] = config.get('log-file', '/var/log/shadowsocks.log') - config['workers'] = config.get('workers', 1) config['verbose'] = config.get('verbose', False) config['local_address'] = to_str(config.get('local_address', '127.0.0.1')) config['local_port'] = config.get('local_port', 1080) From ea7a3e1b585745ec94196eee4bcf94b393920917 Mon Sep 17 00:00:00 2001 From: sky Date: Sun, 3 May 2015 14:34:54 +0800 Subject: [PATCH 142/146] flush autoban output --- utils/autoban.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/autoban.py b/utils/autoban.py index d04ebbe..1bbb65c 100755 --- a/utils/autoban.py +++ b/utils/autoban.py @@ -42,10 +42,12 @@ if __name__ == '__main__': 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) From 082c8a80f47764ed040ebd2bebebd8711181f1ea Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 19 May 2015 08:13:47 +0800 Subject: [PATCH 143/146] fix duplicated close in LRUCache close #324 --- shadowsocks/lru_cache.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/shadowsocks/lru_cache.py b/shadowsocks/lru_cache.py index 27c0738..b70f743 100644 --- a/shadowsocks/lru_cache.py +++ b/shadowsocks/lru_cache.py @@ -74,6 +74,7 @@ class LRUCache(collections.MutableMapping): # O(m) now = time.time() c = 0 + values_closed = list() # list is cheaper to create while len(self._last_visits) > 0: least = self._last_visits[0] if now - least <= self.timeout: @@ -83,7 +84,9 @@ class LRUCache(collections.MutableMapping): if key in self._store: if now - self._keys_to_last_time[key] > self.timeout: value = self._store[key] - self.close_callback(value) + if value not in values_closed: + self.close_callback(value) + values_closed.append(value) for key in self._time_to_keys[least]: self._last_visits.popleft() if key in self._store: @@ -126,5 +129,21 @@ def test(): 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() From 405120c59fb86f5364c5470dc647c293133d1506 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 19 May 2015 08:19:25 +0800 Subject: [PATCH 144/146] bump --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 512ff63..38def84 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with codecs.open('README.rst', encoding='utf-8') as f: setup( name="shadowsocks", - version="2.6.9", + version="2.6.10", license='http://www.apache.org/licenses/LICENSE-2.0', description="A fast tunnel proxy that help you get through firewalls", author='clowwindy', From c46234af4140715b83c8f148cfcf97b0974881ef Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 19 May 2015 08:20:56 +0800 Subject: [PATCH 145/146] update CHANGES --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 2946ecf..602a0a4 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +2.6.9 2015-05-19 +- Fix a stability issue on Windows + 2.6.8 2015-02-10 - Support multiple server ip on client side - Support --version From 16db66675bb1160b4104140a7927036c5552333c Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 19 May 2015 08:35:28 +0800 Subject: [PATCH 146/146] optimize LRUCache --- shadowsocks/lru_cache.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shadowsocks/lru_cache.py b/shadowsocks/lru_cache.py index b70f743..401f19b 100644 --- a/shadowsocks/lru_cache.py +++ b/shadowsocks/lru_cache.py @@ -41,6 +41,7 @@ class LRUCache(collections.MutableMapping): self._time_to_keys = collections.defaultdict(list) self._keys_to_last_time = {} self._last_visits = collections.deque() + self._closed_values = set() self.update(dict(*args, **kwargs)) # use the free update to set keys def __getitem__(self, key): @@ -74,7 +75,6 @@ class LRUCache(collections.MutableMapping): # O(m) now = time.time() c = 0 - values_closed = list() # list is cheaper to create while len(self._last_visits) > 0: least = self._last_visits[0] if now - least <= self.timeout: @@ -84,9 +84,9 @@ class LRUCache(collections.MutableMapping): if key in self._store: if now - self._keys_to_last_time[key] > self.timeout: value = self._store[key] - if value not in values_closed: + if value not in self._closed_values: self.close_callback(value) - values_closed.append(value) + self._closed_values.add(value) for key in self._time_to_keys[least]: self._last_visits.popleft() if key in self._store: @@ -96,6 +96,7 @@ class LRUCache(collections.MutableMapping): c += 1 del self._time_to_keys[least] if c: + self._closed_values.clear() logging.debug('%d keys swept' % c)