93 Commits
Author SHA1 Message Date
clowwindy 70ebd2ef28 bump 2015-01-23 17:35:32 +08:00
clowwindy 1f8819f790 fix #270 2015-01-23 17:33:48 +08:00
clowwindy 0e6a4cd8ff output python version in unit tests 2015-01-23 14:06:01 +08:00
clowwindy f4052fbc84 fix MANIFEST.in 2015-01-21 14:32:55 +08:00
clowwindy 5e5d25efd9 Update CONTRIBUTING.md 2015-01-19 16:20:08 +08:00
clowwindy 2e9ce11ea1 bump 2015-01-18 18:32:04 +08:00
clowwindy 6efb3d00e4 fix #264 2015-01-16 18:52:40 +08:00
clowwindy 13413267dc Update README.md 2015-01-16 17:06:48 +08:00
clowwindy bd22e3ef75 try every dll that matches by name in PATH 2015-01-16 16:50:18 +08:00
clowwindy 5179e018e2 bump 2015-01-14 13:12:12 +08:00
clowwindy af6c6f3f23 also search lib* for library names 2015-01-14 12:59:43 +08:00
clowwindy 5c05a74727 fix a potential name conflict 2015-01-13 00:42:27 +08:00
clowwindy 53a7e4d0e4 remove print 2015-01-12 23:21:16 +08:00
clowwindy 5e476843ec fix python3 2015-01-12 23:19:24 +08:00
clowwindy 32a6b8fd7a update CHANGES 2015-01-12 23:13:29 +08:00
clowwindy 9fe2f4ef16 bump 2015-01-12 23:11:23 +08:00
clowwindy f7316c0047 change logging level for UDP warning 2015-01-12 23:08:27 +08:00
clowwindy cc36de5a2f fix pep8 2015-01-12 23:05:19 +08:00
clowwindy 4312eb9e58 add forbidden support for UDP and add tests 2015-01-12 23:02:59 +08:00
clowwindy eb94bd1cc3 support forbidden iplist 2015-01-12 22:30:03 +08:00
clowwindy f29bfb0cc7 remove salsa20-ctr test 2015-01-12 14:15:41 +08:00
clowwindy 80b8bd7014 remove M2Crypto completely 2015-01-12 14:11:33 +08:00
clowwindy a4b0ea5b8f fix find_library 2015-01-12 14:08:21 +08:00
clowwindy e582b2b929 rename ctypes_* and remove salsa20-ctr 2015-01-12 14:05:20 +08:00
clowwindy 3d03dbf716 extract find_library 2015-01-12 14:00:35 +08:00
clowwindy 1becc9362d Merge pull request #257 from fa08c/master
Searching libcrypto.so in more locations
2015-01-12 13:07:00 +08:00
fa08c ebfd1486d8 Removing the overhead of creating a new list per event 2015-01-12 10:32:03 +08:00
fa08c 6eadfca78e Removing trailing whites 2015-01-11 15:19:31 +08:00
fa08c 3294a92a61 Search libcrypto.so in more locations
Some linux distro may store .so in some alternative locations. Now we
try harder to find them.
2015-01-10 22:26:20 +08:00
fa08c 2711002142 Merge remote-tracking branch 'upstream/master' 2015-01-10 21:27:41 +08:00
fa08c 18da3554ff Minor fix: never use except: unless you reraise 2015-01-08 08:27:28 -06:00
clowwindy a5fc1c7e5f Merge pull request #248 from peterfyj/master
Set default method as bytes.
2015-01-08 14:22:26 +08:00
peter 8b2deb01d8 Set default method as bytes.
Default method as string may cause false error report.
2015-01-08 11:22:34 +08:00
fa08c 176e97bb45 Fixing some minor issues 2015-01-08 11:03:12 +08:00
clowwindy b6efc0efd9 Update README.md 2015-01-08 04:18:38 +08:00
clowwindy 4598e09b78 now works on OpenWRT 2015-01-07 00:25:28 +08:00
clowwindy 363965c364 Update CONTRIBUTING.md 2015-01-06 20:13:45 +08:00
clowwindy a90c354f42 update README 2015-01-06 19:41:20 +08:00
clowwindy 69d1d876bc Update README.rst 2015-01-06 19:39:39 +08:00
clowwindy ae57ebde68 Update README.md 2015-01-06 19:39:23 +08:00
clowwindy 09f8f1ccc8 Update CONTRIBUTING.md 2015-01-06 19:36:34 +08:00
clowwindy b12ba5383c Update CONTRIBUTING.md 2015-01-06 19:35:31 +08:00
clowwindy 0bc0117cd7 update help url 2015-01-04 23:55:50 +08:00
clowwindy 85cbac127d Update README.md 2015-01-04 23:44:03 +08:00
clowwindy f7a63a0ad7 Update README.md 2015-01-04 23:35:41 +08:00
clowwindy a3faf2c5fb Update README.md 2015-01-04 23:30:29 +08:00
clowwindy d01ef02b5a Update README.md 2015-01-04 23:27:58 +08:00
clowwindy 1db1e4f4e9 Update README.md 2015-01-04 23:22:26 +08:00
clowwindy 510dc64bf2 Update README.md 2015-01-04 23:17:01 +08:00
clowwindy 8be120518b Update README.md 2015-01-04 12:46:07 +08:00
clowwindy e7984930d1 use HTTPS 2015-01-03 16:48:05 +08:00
clowwindy b8f1825776 update CHANGES 2015-01-03 15:53:35 +08:00
clowwindy dde25f89e1 Update README.md 2015-01-03 14:00:33 +08:00
clowwindy 98f73e44d0 bump 2015-01-03 13:56:19 +08:00
clowwindy e0189ab1de update readme 2015-01-03 13:50:08 +08:00
clowwindy e68b3e430b add autoban 2015-01-03 13:30:16 +08:00
clowwindy bac675d7f6 2015 2015-01-03 13:24:40 +08:00
clowwindy 291adf8b85 log client address 2015-01-03 12:48:03 +08:00
clowwindy 1423fe1921 support color 2014-12-28 15:24:38 +08:00
clowwindy 5a5c8b9c7e fix coverage server 2014-12-28 15:24:38 +08:00
clowwindy 70559a1030 fix pep8 2014-12-28 15:24:38 +08:00
clowwindy 45ee594d58 coverage server now supports more projects 2014-12-28 15:24:38 +08:00
clowwindy 707b5c1def Merge pull request #237 from v3aqb/patch-1
Update ctypes_libsodium.py
2014-12-28 14:02:59 +08:00
v3aqb d91f7d85d4 Update ctypes_libsodium.py 2014-12-28 08:30:29 +08:00
clowwindy 5a5158b33f update url 2014-12-27 23:36:09 +08:00
clowwindy 7f19c31640 update project url 2014-12-27 23:29:48 +08:00
clowwindy 937693519a update url in readme 2014-12-27 23:29:07 +08:00
clowwindy 9e6e884b12 bump 2014-12-26 20:58:22 +08:00
clowwindy e8501da271 fix vim 2014-12-25 16:23:43 +08:00
clowwindy 99c8bbafe7 fix fastopen and add a new test 2014-12-25 16:20:15 +08:00
clowwindy 30f4f78557 fix travis 2014-12-24 18:23:35 +08:00
clowwindy dd140814a8 suppress some warnings 2014-12-24 18:15:52 +08:00
clowwindy d228ffbe84 add command line test 2014-12-24 18:10:12 +08:00
clowwindy ab975d1753 fix tests 2014-12-24 18:08:16 +08:00
clowwindy c6bc912c11 add command line tests 2014-12-24 18:02:14 +08:00
clowwindy b785d95f66 fix tests 2014-12-24 17:14:01 +08:00
clowwindy 1d0c8b1800 add coverage 2014-12-24 16:47:14 +08:00
clowwindy 9cfffa360e sudo tc setup 2014-12-23 15:32:29 +08:00
clowwindy cd07001471 use SIGINT instead in large file test 2014-12-23 14:05:20 +08:00
clowwindy 2cc7ee5053 alter tc rule 2014-12-23 13:57:31 +08:00
clowwindy 9b3944c954 fix large file test 2014-12-23 13:18:30 +08:00
clowwindy f1b084be06 add large file test 2014-12-23 13:09:51 +08:00
clowwindy 5ea8403e56 fix daemon and add fastopen tests 2014-12-22 22:42:39 +08:00
clowwindy 072afd68f2 use local tmp dir 2014-12-22 17:39:52 +08:00
clowwindy be1d1d5032 add SIGINT in workers 2014-12-22 17:29:58 +08:00
clowwindy 536b7d1ee6 use SIGINT instead in tests
Conflicts:
	tests/test.py
2014-12-22 17:25:39 +08:00
clowwindy c7b5a5a011 fix ci 2014-12-22 16:45:46 +08:00
clowwindy be2ab378ff add jenkins 2014-12-22 16:33:23 +08:00
clowwindy 2b4c3619d6 reorder setsid and kill 2014-12-21 14:52:00 +08:00
clowwindy dae2624b30 add setsid 2014-12-21 14:49:51 +08:00
clowwindy fb789d8e9f update readme 2014-12-21 14:09:07 +08:00
clowwindy 2733e6a4ba update readme 2014-12-21 14:08:11 +08:00
clowwindy 35c3b0b41c refine documentation 2014-12-21 14:06:28 +08:00
34 changed files with 806 additions and 692 deletions
+1
View File
@@ -17,6 +17,7 @@ develop-eggs
pip-log.txt
# Unit test / coverage reports
htmlcov
.coverage
.tox
Executable
+72
View File
@@ -0,0 +1,72 @@
#!/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
}
python --version
coverage erase
mkdir tmp
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/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 -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"
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
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
coverage html --include=shadowsocks/*
coverage report --include=shadowsocks/* | tail -n1 | rev | cut -d' ' -f 1 | rev > /tmp/shadowsocks-coverage
exit $result
+6 -23
View File
@@ -9,29 +9,12 @@ 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
- pip install m2crypto salsa20 pep8 pyflakes nose coverage
- 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 pep8 pyflakes nose coverage
- sudo tests/socksify/install.sh
- sudo tests/libsodium/install.sh
- sudo tests/setup_tc.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
+21
View File
@@ -1,3 +1,24 @@
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
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
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
2.6 2014-12-21
- Add daemon support
+19 -28
View File
@@ -1,38 +1,29 @@
How to contribute
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 discuss 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].
4. Issues in languages other than English will be Google translated into English
later.
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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -1,3 +1,3 @@
recursive-include *.py
recursive-include shadowsocks *.py
include README.rst
include LICENSE
+51 -112
View File
@@ -1,116 +1,57 @@
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.
[中文说明][Chinese Readme]
Server
------
Install
-------
### Install
You'll have a client on your local side, and setup a server on a
remote server.
Debian / Ubuntu:
### Client
apt-get install python-pip
pip install shadowsocks
CentOS:
yum install python-setuptools && easy_install pip
pip install shadowsocks
Windows:
See [Install Server on Windows]
### Usage
ssserver -p 8000 -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
Check all the options via `-h`. You can also use a [Configuration] file
instead.
Client
------
* [Windows] / [OS X]
* [Android] / [iOS]
* [OpenWRT]
### Server
Use GUI clients on your local PC/phones. Check the README of your client
for more information.
#### 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
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.
Configuration
Documentation
-------------
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,
"workers": 1
}
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 |
Run `ssserver -c /etc/shadowsocks.json` on your server. To run it in the
background, use [Supervisor].
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
protocol: socks5
hostname: 127.0.0.1
port: your local_port
If you can't install [SwitchySharp], you can launch Chrome with the following
arguments to force Chrome to use the proxy:
Chrome.exe --proxy-server="socks5://127.0.0.1:1080" --host-resolver-rules="MAP * 0.0.0.0 , EXCLUDE localhost"
If you can't even download Chrome, find a friend to download a
[Chrome Standalone] installer for you.
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 --workers 2
ssserver -c /etc/shadowsocks/config.json
List all available args with `-h`.
Wiki
----
You can find all the documentation in the wiki:
https://github.com/clowwindy/shadowsocks/wiki
You can find all the documentation in the [Wiki].
License
-------
@@ -124,24 +65,22 @@ 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
[Chrome Standalone]: https://support.google.com/installer/answer/126299
[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
[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/clowwindy/shadowsocks/wiki/Encryption
[iOS]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help
[Issue Tracker]: https://github.com/clowwindy/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
[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/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
[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
[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
+48 -124
View File
@@ -1,30 +1,15 @@
shadowsocks
===========
|PyPI version| |Build Status|
|PyPI version| |Build Status| |Coverage Status|
A fast tunnel proxy that helps you bypass firewalls.
`中文说明 <https://github.com/clowwindy/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E>`__
Server
------
Install
-------
You'll have a client on your local side, and setup a server on a remote
server.
Client
~~~~~~
- `Windows <https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#windows>`__
/ `OS
X <https://github.com/shadowsocks/shadowsocks-iOS/wiki/Shadowsocks-for-OSX-Help>`__
- `Android <https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#android>`__
/ `iOS <https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help>`__
- `OpenWRT <https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#openwrt>`__
Server
~~~~~~
~~~~~~~
Debian / Ubuntu:
^^^^^^^^^^^^^^^^
@@ -34,119 +19,56 @@ Debian / Ubuntu:
apt-get install python-pip
pip install shadowsocks
Or simply ``apt-get install shadowsocks`` if you have `Debian
sid <https://packages.debian.org/unstable/python/shadowsocks>`__ 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 <https://pypi.python.org/pypi/shadowsocks>`__, and use
``python shadowsocks/server.py`` instead of ``ssserver`` command below.
See `Install Server on
Windows <https://github.com/shadowsocks/shadowsocks/wiki/Install-Shadowsocks-Server-on-Windows>`__
Configuration
Usage
~~~~~
::
ssserver -p 8000 -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
Check all the options via ``-h``. You can also use a
`Configuration <https://github.com/shadowsocks/shadowsocks/wiki/Configuration-via-Config-File>`__
file instead.
Client
------
- `Windows <https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#windows>`__
/ `OS
X <https://github.com/shadowsocks/shadowsocks-iOS/wiki/Shadowsocks-for-OSX-Help>`__
- `Android <https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#android>`__
/ `iOS <https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help>`__
- `OpenWRT <https://github.com/shadowsocks/openwrt-shadowsocks>`__
Use GUI clients on your local PC/phones. Check the README of your client
for more information.
Documentation
-------------
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,
"workers": 1
}
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 <https://github.com/clowwindy/shadowsocks/wiki/Encryption>`__ |
+------------------+---------------------------------------------------------------------------------------------------------+
| fast\_open | use `TCP\_FASTOPEN <https://github.com/clowwindy/shadowsocks/wiki/TCP-Fast-Open>`__, 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 <https://github.com/clowwindy/shadowsocks/wiki/Configure-Shadowsocks-with-Supervisor>`__.
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 <https://chrome.google.com/webstore/detail/proxy-switchysharp/dpplabbmogkhghncfbfdeeokoefdjegm>`__.
Change the proxy settings to
::
protocol: socks5
hostname: 127.0.0.1
port: your local_port
If you can't install
`SwitchySharp <https://chrome.google.com/webstore/detail/proxy-switchysharp/dpplabbmogkhghncfbfdeeokoefdjegm>`__,
you can launch Chrome with the following arguments to force Chrome to
use the proxy:
::
Chrome.exe --proxy-server="socks5://127.0.0.1:1080" --host-resolver-rules="MAP * 0.0.0.0 , EXCLUDE localhost"
If you can't even download Chrome, find a friend to download a `Chrome
Standalone <https://support.google.com/installer/answer/126299>`__
installer for you.
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 --workers 2
ssserver -c /etc/shadowsocks/config.json
List all available args with ``-h``.
Wiki
----
You can find all the documentation in the wiki:
https://github.com/clowwindy/shadowsocks/wiki
You can find all the documentation in the
`Wiki <https://github.com/shadowsocks/shadowsocks/wiki>`__.
License
-------
@@ -156,12 +78,14 @@ MIT
Bugs and Issues
---------------
- `Troubleshooting <https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting>`__
- `Troubleshooting <https://github.com/shadowsocks/shadowsocks/wiki/Troubleshooting>`__
- `Issue
Tracker <https://github.com/clowwindy/shadowsocks/issues?state=open>`__
- `Mailing list <http://groups.google.com/group/shadowsocks>`__
Tracker <https://github.com/shadowsocks/shadowsocks/issues?state=open>`__
- `Mailing list <https://groups.google.com/group/shadowsocks>`__
.. |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:: https://jenkins.shadowvpn.org/result/shadowsocks
:target: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/htmlcov/index.html
+2 -2
View File
@@ -7,12 +7,12 @@ with codecs.open('README.rst', encoding='utf-8') as f:
setup(
name="shadowsocks",
version="2.6",
version="2.6.6",
license='MIT',
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']
+5 -8
View File
@@ -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)
-119
View File
@@ -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()
@@ -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,15 +39,12 @@ buf_size = 2048
def load_openssl():
global loaded, libcrypto, buf
from ctypes.util import find_library
for p in ('crypto', 'eay32', 'libeay32'):
libcrypto_path = find_library(p)
if libcrypto_path:
break
else:
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
@@ -76,11 +74,11 @@ 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:
load_openssl()
self._ctx = None
cipher = libcrypto.EVP_get_cipherbyname(cipher_name)
if not cipher:
cipher = load_cipher(cipher_name)
@@ -119,39 +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):
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)
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)
+2 -9
View File
@@ -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 ctypes_openssl
return ctypes_openssl.CtypesCrypto(b'rc4', rc4_key, b'', op)
except:
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 = {
-140
View File
@@ -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('<Q', iv)[0]
self._pos = 0
self._next_stream()
def _next_stream(self):
self._nonce &= 0xFFFFFFFFFFFFFFFF
self._stream = salsa20.Salsa20_keystream(BLOCK_SIZE,
struct.pack('<Q',
self._nonce),
self._key)
self._nonce += 1
def update(self, data):
results = []
while True:
remain = BLOCK_SIZE - self._pos
cur_data = data[:remain]
cur_data_len = len(cur_data)
cur_stream = self._stream[self._pos:self._pos + cur_data_len]
self._pos = self._pos + cur_data_len
data = data[remain:]
results.append(numpy_xor(cur_data, cur_stream))
if self._pos >= 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()
@@ -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,16 +42,11 @@ BLOCK_SIZE = 64
def load_libsodium():
global loaded, libsodium, buf
from ctypes.util import find_library
for p in ('sodium',):
libsodium_path = find_library(p)
if libsodium_path:
break
else:
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,
@@ -62,13 +58,11 @@ def load_libsodium():
c_char_p, c_ulonglong,
c_char_p)
libsodium.sodium_init()
buf = create_string_buffer(buf_size)
loaded = True
class Salsa20Crypto(object):
class SodiumCrypto(object):
def __init__(self, cipher_name, key, iv, op):
if not loaded:
load_libsodium()
@@ -107,25 +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():
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)
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():
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)
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)
+93
View File
@@ -20,6 +20,83 @@
# 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 logging
def find_library_nt(name):
# modified from ctypes.util
# ctypes.util.find_library just returns first result he found
# but we want to try them all
# because on Windows, users may have both 32bit and 64bit version installed
results = []
for directory in os.environ['PATH'].split(os.pathsep):
fname = os.path.join(directory, name)
if os.path.isfile(fname):
results.append(fname)
if fname.lower().endswith(".dll"):
continue
fname = fname + ".dll"
if os.path.isfile(fname):
results.append(fname)
return results
def find_library(possible_lib_names, search_symbol, library_name):
import ctypes.util
from ctypes import CDLL
paths = []
if type(possible_lib_names) not in (list, tuple):
possible_lib_names = [possible_lib_names]
lib_names = []
for lib_name in possible_lib_names:
lib_names.append(lib_name)
lib_names.append('lib' + lib_name)
for name in lib_names:
if os.name == "nt":
paths.extend(find_library_nt(name))
else:
path = ctypes.util.find_library(name)
if path:
paths.append(path)
if not paths:
# We may get here when find_library fails because, for example,
# the user does not have sufficient privileges to access those
# tools underlying find_library on linux.
import glob
for name in lib_names:
patterns = [
'/usr/local/lib*/lib%s.*' % name,
'/usr/lib*/lib%s.*' % name,
'lib%s.*' % name,
'%s.dll' % name]
for pat in patterns:
files = glob.glob(pat)
if files:
paths.extend(files)
for path in paths:
try:
lib = CDLL(path)
if hasattr(lib, search_symbol):
logging.info('loading %s from %s', library_name, path)
return lib
else:
logging.warn('can\'t find symbol %s in %s', search_symbol,
path)
except Exception:
pass
return None
def run_cipher(cipher, decipher):
from os import urandom
@@ -49,3 +126,19 @@ 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(('c',), 'strcpy', 'libc') is not None
assert find_library(('crypto', 'eay32'), 'EVP_CipherUpdate',
'libcrypto') is not None
assert find_library('notexist', 'strcpy', 'libnotexist') is None
assert find_library('c', 'symbol_not_exist', 'c') is None
assert find_library(('notexist', 'c', 'crypto', 'eay32'),
'EVP_CipherUpdate', 'libc') is not None
if __name__ == '__main__':
test_find_library()
+11 -7
View File
@@ -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'
@@ -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)
@@ -123,6 +125,9 @@ def daemon_start(pid_file, log_file):
os.kill(ppid, signal.SIGINT)
sys.exit(1)
os.setsid()
signal.signal(signal.SIG_IGN, signal.SIGHUP)
print('started')
os.kill(ppid, signal.SIGTERM)
@@ -132,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)
+4 -12
View File
@@ -28,26 +28,18 @@ import sys
import hashlib
import logging
from shadowsocks.crypto import m2, rc4_md5, salsa20_ctr,\
ctypes_openssl, ctypes_libsodium, table
from shadowsocks.crypto import 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(m2.ciphers)
method_supported.update(openssl.ciphers)
method_supported.update(sodium.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 = {}
+3 -2
View File
@@ -232,8 +232,9 @@ class EventLoop(object):
logging.error(e)
import traceback
traceback.print_exc()
for handler in self._handlers_to_remove:
self._handlers.remove(handler)
if self._handlers_to_remove:
for handler in self._handlers_to_remove:
self._handlers.remove(handler)
self._handlers_to_remove = []
self._iterating = False
+5
View File
@@ -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)
+6
View File
@@ -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)
@@ -113,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:
+26 -12
View File
@@ -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
@@ -121,7 +121,12 @@ 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 'forbidden_ip' in config:
self._forbidden_iplist = 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
@@ -241,19 +246,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')
@@ -295,8 +299,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)
@@ -318,7 +323,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
@@ -330,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
@@ -339,12 +348,13 @@ class TCPRelayHandler(object):
def _handle_dns_resolved(self, result, error):
if error:
logging.error(error)
self._log_error(error)
self.destroy()
return
if result:
ip = result[1]
if ip:
try:
self._stage = STAGE_CONNECTING
remote_addr = ip
@@ -377,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()
@@ -508,6 +518,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:
+10
View File
@@ -112,6 +112,10 @@ class UDPRelay(object):
self._closed = False
self._last_time = time.time()
self._sockets = set()
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 +182,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.debug('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
+10 -6
View File
@@ -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')
@@ -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()
@@ -172,7 +175,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)
@@ -264,7 +267,7 @@ General options:
-v, -vv verbose mode
-q, -qq quiet mode, only show warnings/errors
Online help: <https://github.com/clowwindy/shadowsocks>
Online help: <https://github.com/shadowsocks/shadowsocks>
''')
@@ -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
@@ -294,7 +298,7 @@ General options:
-v, -vv verbose mode
-q, -qq quiet mode, only show warnings/errors
Online help: <https://github.com/clowwindy/shadowsocks>
Online help: <https://github.com/shadowsocks/shadowsocks>
''')
+148
View File
@@ -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 <http://www.gnu.org/licenses/>.
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 <<EOF
Usage: $0 [options]
Language-agnostic unit tests for subprocesses.
Options:
-v, --verbose generate output for every individual test case
-x, --stop stop running tests after the first failure
-i, --invariant do not measure timings to remain invariant between runs
-d, --discover collect test suites only, do not run any tests
-c, --continue do not modify exit code to test suite status
-h show brief usage information and exit
--help show this help message and exit
EOF
exit 0;;
-v|--verbose)
DEBUG=1;;
-x|--stop)
STOP=1;;
-i|--invariant)
INVARIANT=1;;
-d|--discover)
DISCOVERONLY=1;;
-c|--continue)
CONTINUE=1;;
esac
done
printf -v _indent "\n\t" # local format helper
_assert_reset() {
tests_ran=0
tests_failed=0
tests_errors=()
tests_starttime="$(date +%s.%N)" # seconds_since_epoch.nanoseconds
}
assert_end() {
# assert_end [suite ..]
tests_endtime="$(date +%s.%N)"
tests="$tests_ran ${*:+$* }tests"
[[ -n "$DISCOVERONLY" ]] && echo "collected $tests." && _assert_reset && return
[[ -n "$DEBUG" ]] && echo
[[ -z "$INVARIANT" ]] && report_time=" in $(bc \
<<< "${tests_endtime%.N} - ${tests_starttime%.N}" \
| sed -e 's/\.\([0-9]\{0,3\}\)[0-9]*/.\1/' -e 's/^\./0./')s" \
|| report_time=
if [[ "$tests_failed" -eq 0 ]]; then
echo "all $tests passed$report_time."
else
for error in "${tests_errors[@]}"; do echo "$error"; done
echo "$tests_failed of $tests failed$report_time."
fi
tests_failed_previous=$tests_failed
[[ $tests_failed -gt 0 ]] && tests_suite_status=1
_assert_reset
return $tests_failed_previous
}
assert() {
# assert <command> <expected stdout> [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 <command> <expected code> [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 <failure> <command> <stdin>
[[ -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
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env python
if __name__ == '__main__':
import tornado.ioloop
import tornado.web
import urllib
class MainHandler(tornado.web.RequestHandler):
def get(self, project):
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-%s.svg'
'?style=flat') %
(urllib.quote(coverage), color))
except IOError:
raise tornado.web.HTTPError(404)
application = tornado.web.Application([
(r"/([a-zA-Z0-9\-_]+)", MainHandler),
])
if __name__ == "__main__":
application.listen(8888, address='127.0.0.1')
tornado.ioloop.IOLoop.instance().start()
+18
View File
@@ -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
)
+22 -9
View File
@@ -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()
@@ -94,7 +97,7 @@ try:
stage = 5
if bytes != str:
line = str(line, 'utf8')
sys.stdout.write(line)
sys.stderr.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,14 @@ 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,14 +139,19 @@ 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]:
try:
os.kill(p.pid, signal.SIGQUIT)
os.kill(p.pid, signal.SIGINT)
os.waitpid(p.pid, 0)
except OSError:
pass
+40
View File
@@ -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>/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>/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>/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>/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>/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>/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>/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"
$LOCAL 2>/dev/null 1>/dev/null -m rc4-md5 -k testrc4 -s 127.0.0.1 -p 8388 -d stop
assert_end command
+15 -14
View File
@@ -1,6 +1,6 @@
#!/bin/bash
function test {
function run_test {
expected=$1
shift
echo "running test: $command $@"
@@ -20,23 +20,24 @@ 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
mkdir -p tmp
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 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 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 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 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 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 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
done
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
PYTHON="coverage run -p -a"
URL=http://127.0.0.1/file
mkdir -p tmp
$PYTHON shadowsocks/local.py -c tests/aes.json &
LOCAL=$!
$PYTHON shadowsocks/server.py -c tests/aes.json &
SERVER=$!
sleep 3
time curl -o tmp/expected $URL
time curl -o tmp/result --socks5-hostname 127.0.0.1:1081 $URL
kill -s SIGINT $LOCAL
kill -s SIGINT $SERVER
sleep 2
diff tmp/expected tmp/result || exit 1
+9
View File
@@ -0,0 +1,9 @@
Useful Tools
===========
autoban.py
----------
Automatically ban IPs that try to brute force crack the server.
See https://github.com/shadowsocks/shadowsocks/wiki/Ban-Brute-Force-Crackers
+51
View File
@@ -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 as an attack')
config = parser.parse_args()
ips = {}
banned = set()
for line in sys.stdin:
if 'can not parse header when' in line:
ip = line.split()[-1].split(':')[0]
if ip not in ips:
ips[ip] = 1
print(ip)
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)