diff --git a/.travis.yml b/.travis.yml index a9cbc64..015dd75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,11 @@ language: python python: - - "2.6" - - "2.7" + - 2.6 + - 2.7 + - pypy before_install: - sudo apt-get update -qq - sudo apt-get install -qq libevent-dev python-gevent - pip install gevent - - pip install simplejson script: - python test.py -branches: - only: - - master - - dev diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0d9e69a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +Shadowsocks + +Copyright (c) 2013 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. \ No newline at end of file diff --git a/README.md b/README.md index 51e6ef1..4090cce 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ shadowsocks =========== [![Build Status](https://travis-ci.org/clowwindy/shadowsocks.png)](https://travis-ci.org/clowwindy/shadowsocks) -Current version: 1.2.3 +Current version: 1.3.0 shadowsocks is a lightweight tunnel proxy which can help you get through firewalls @@ -15,20 +15,35 @@ First, make sure you have Python 2.6 or 2.7. $ python --version Python 2.6.8 + +Install Shadowsocks. + pip install shadowsocks + +Create a file named `config.json`, with the following content. -Then edit `config.json`, change the following values: + { + "server":"my_server_ip", + "server_port":8388, + "local_port":1080, + "password":"barfoo!", + "timeout":600, + "method":null + } - server your server ip or hostname +Explaination of the fields: + + server your server IP (IPv4/IPv6), notice that your server will listen to this IP server_port server port local_port local port password a password used to encrypt transfer + timeout in seconds method encryption method, "bf-cfb", "aes-256-cfb", "des-cfb", "rc4", etc. Default is table +`cd` into the directory of `config.json`. Run `ssserver` on your server. To run it in the background, run +`nohup ssserver > log &`. -Put all the files on your server. Run `python server.py` on your server. To run it in the background, run `nohup python server.py > log &`. - -Put all the files on your client machine. Run `python local.py` on your client machine. +On your client machine, run `sslocal`. Change the proxy setting in your browser into @@ -36,6 +51,15 @@ Change the proxy setting in your browser into hostname: 127.0.0.1 port: your local_port +Command line args +------------------ + +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 + ssserver -c /etc/shadowsocks/config.json + Encryption ------------ @@ -49,15 +73,6 @@ Others: pip install M2Crypto - -Command line args ------------------ - -You can use args to override settings from `config.json`. - - python local.py -s server_name -p server_port -l local_port -k password -m bf-cfb -b bind_address -6 - python server.py -p server_port -k password -m bf-cfb -6 - Performance ------------ diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..ca13248 --- /dev/null +++ b/setup.py @@ -0,0 +1,21 @@ +from setuptools import setup, find_packages + +setup( + name = "shadowsocks", + version = "1.3.0", + license = 'MIT', + description = "a lightweight tunnel proxy", + author = 'clowwindy42@gmail.com', + url = 'https://github.com/clowwindy/shadowsocks', + packages = ['shadowsocks'], + package_data={ + 'shadowsocks': ['README.md', 'LICENSE', 'config.json'] + }, + install_requires = ['setuptools', + ], + entry_points=""" + [console_scripts] + sslocal = shadowsocks.local:main + ssserver = shadowsocks.server:main + """, +) diff --git a/shadowsocks/__init__.py b/shadowsocks/__init__.py new file mode 100644 index 0000000..013e4b7 --- /dev/null +++ b/shadowsocks/__init__.py @@ -0,0 +1 @@ +#!/usr/bin/python diff --git a/encrypt.py b/shadowsocks/encrypt.py similarity index 100% rename from encrypt.py rename to shadowsocks/encrypt.py diff --git a/local.py b/shadowsocks/local.py similarity index 83% rename from local.py rename to shadowsocks/local.py index bd40e62..e73c299 100755 --- a/local.py +++ b/shadowsocks/local.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright (c) 2012 clowwindy +# Copyright (c) 2013 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 @@ -43,6 +43,7 @@ import os import logging import getopt import encrypt +import utils def send_all(sock, data): @@ -142,28 +143,48 @@ class Socks5Server(SocketServer.StreamRequestHandler): logging.warn(e) -if __name__ == '__main__': +def main(): + global SERVER, REMOTE_PORT, PORT, KEY, METHOD, LOCAL, IPv6 + + logging.basicConfig(level=logging.DEBUG, + format='%(asctime)s %(levelname)-8s %(message)s', + datefmt='%Y-%m-%d %H:%M:%S', filemode='a+') + + # fix py2exe + if hasattr(sys, "frozen") and sys.frozen in \ + ("windows_exe", "console_exe"): + p = os.path.dirname(os.path.abspath(sys.executable)) + os.chdir(p) + version = '' try: - os.chdir(os.path.dirname(__file__) or '.') - except NameError: - # fix py2exe - if hasattr(sys, "frozen") and sys.frozen in \ - ("windows_exe", "console_exe"): - p = os.path.dirname(os.path.abspath(sys.executable)) - os.chdir(p) - print 'shadowsocks v1.2.3' + import pkg_resources + version = pkg_resources.get_distribution('shadowsocks').version + except ImportError: + pass + print 'shadowsocks %s' % version - with open('config.json', 'rb') as f: - config = json.load(f) - SERVER = config['server'] - REMOTE_PORT = config['server_port'] - PORT = config['local_port'] - KEY = config['password'] - METHOD = config.get('method', None) - LOCAL = config.get('local', '') + METHOD = None + LOCAL = '' IPv6 = False + + config_path = utils.find_config() + optlist, args = getopt.getopt(sys.argv[1:], 's:b:p:k:l:m:c:6') + for key, value in optlist: + if key == '-c': + config_path = value - optlist, args = getopt.getopt(sys.argv[1:], 's:b:p:k:l:m:6') + if config_path: + logging.info('loading config from %s' % config_path) + with open(config_path, 'rb') as f: + config = json.load(f) + SERVER = config['server'] + REMOTE_PORT = config['server_port'] + PORT = config['local_port'] + KEY = config['password'] + METHOD = config.get('method', None) + LOCAL = config.get('local', '') + + optlist, args = getopt.getopt(sys.argv[1:], 's:b:p:k:l:m:c:6') for key, value in optlist: if key == '-p': REMOTE_PORT = int(value) @@ -180,10 +201,6 @@ if __name__ == '__main__': elif key == '-6': IPv6 = True - logging.basicConfig(level=logging.DEBUG, - format='%(asctime)s %(levelname)-8s %(message)s', - datefmt='%Y-%m-%d %H:%M:%S', filemode='a+') - encrypt.init_table(KEY, METHOD) try: @@ -197,3 +214,6 @@ if __name__ == '__main__': except KeyboardInterrupt: server.shutdown() sys.exit(0) + +if __name__ == '__main__': + main() diff --git a/server.py b/shadowsocks/server.py similarity index 86% rename from server.py rename to shadowsocks/server.py index d656733..0a20428 100755 --- a/server.py +++ b/shadowsocks/server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright (c) 2012 clowwindy +# Copyright (c) 2013 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 @@ -43,6 +43,7 @@ import os import logging import getopt import encrypt +import utils def send_all(sock, data): @@ -123,21 +124,40 @@ class Socks5Server(SocketServer.StreamRequestHandler): except socket.error, e: logging.warn(e) -if __name__ == '__main__': - os.chdir(os.path.dirname(__file__) or '.') - - print 'shadowsocks v1.2.3' +def main(): + global SERVER, PORT, KEY, METHOD, IPv6 + + logging.basicConfig(level=logging.DEBUG, + format='%(asctime)s %(levelname)-8s %(message)s', + datefmt='%Y-%m-%d %H:%M:%S', filemode='a+') + + version = '' + try: + import pkg_resources + version = pkg_resources.get_distribution('shadowsocks').version + except ImportError: + pass + print 'shadowsocks %s' % version + METHOD = None + IPv6 = False + + config_path = utils.find_config() + optlist, args = getopt.getopt(sys.argv[1:], 'p:k:m:c:6') + for key, value in optlist: + if key == '-c': + config_path = value with open('config.json', 'rb') as f: config = json.load(f) - SERVER = config['server'] - PORT = config['server_port'] - KEY = config['password'] - METHOD = config.get('method', None) - IPv6 = False + if config_path: + logging.info('loading config from %s' % config_path) + SERVER = config['server'] + PORT = config['server_port'] + KEY = config['password'] + METHOD = config.get('method', None) - optlist, args = getopt.getopt(sys.argv[1:], 'p:k:m:6') + optlist, args = getopt.getopt(sys.argv[1:], 'p:k:m:c:6') for key, value in optlist: if key == '-p': PORT = int(value) @@ -148,10 +168,6 @@ if __name__ == '__main__': elif key == '-6': IPv6 = True - logging.basicConfig(level=logging.DEBUG, - format='%(asctime)s %(levelname)-8s %(message)s', - datefmt='%Y-%m-%d %H:%M:%S', filemode='a+') - encrypt.init_table(KEY, METHOD) if IPv6: ThreadingTCPServer.address_family = socket.AF_INET6 @@ -161,3 +177,6 @@ if __name__ == '__main__': server.serve_forever() except socket.error, e: logging.error(e) + +if __name__ == '__main__': + main() diff --git a/shadowsocks/utils.py b/shadowsocks/utils.py new file mode 100644 index 0000000..79d6589 --- /dev/null +++ b/shadowsocks/utils.py @@ -0,0 +1,14 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import os + + +def find_config(): + config_path = 'config.json' + if os.path.exists(config_path): + return config_path + config_path = os.path.join(os.path.dirname(__file__), '../', 'config.json') + if os.path.exists(config_path): + return config_path + return None diff --git a/test.py b/test.py index ab3bbda..4e32676 100755 --- a/test.py +++ b/test.py @@ -82,9 +82,9 @@ decrypt_table = string.maketrans(encrypt_table, string.maketrans('', '')) for i in range(0, 256): assert(target2[0][i] == ord(encrypt_table[i])) assert(target2[1][i] == ord(decrypt_table[i])) -p1 = Popen(['python', 'server.py'], shell=False, bufsize=0, stdin=PIPE, +p1 = Popen(['python', 'shadowsocks/server.py'], shell=False, bufsize=0, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) -p2 = Popen(['python', 'local.py'], shell=False, bufsize=0, stdin=PIPE, +p2 = Popen(['python', 'shadowsocks/local.py'], shell=False, bufsize=0, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) p3 = None