2 Commits
Author SHA1 Message Date
clowwindy af5455c820 fix test 2014-12-31 13:41:51 +08:00
clowwindy ccd1c0b45c implement auth in encrypt module 2014-12-31 09:21:10 +08:00
24 changed files with 721 additions and 337 deletions
+1 -1
View File
@@ -38,6 +38,7 @@ 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
@@ -45,7 +46,6 @@ 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
+2 -2
View File
@@ -9,10 +9,10 @@ cache:
- dante-1.4.0
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq build-essential dnsutils iproute nginx bc
- 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 pep8 pyflakes nose coverage
- pip install m2crypto salsa20 pep8 pyflakes nose coverage
- sudo tests/socksify/install.sh
- sudo tests/libsodium/install.sh
- sudo tests/setup_tc.sh
-11
View File
@@ -1,14 +1,3 @@
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
+28 -17
View File
@@ -1,27 +1,38 @@
How to Contribute
How to contribute
=================
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 discuss with
people first.
2. Follow PEP8.
3. Make sure to pass the unit tests. Write unit tests for new modules if
needed.
Before you submit issues, please read [Troubleshooting] and take a few minutes
to read this guide.
问题反馈
-------
请提交下面的信息:
1. 你是如何搭建环境的?(操作系统,Shadowsocks 版本)
2. 操作步骤是什么?
3. 浏览器里的现象是什么?一直转菊花,还是有提示错误?
4. 发生错误时,客户端和服务端最后一部分日志。
5. 其它你认为可能和问题有关的信息。
如果你不清楚其中某条的含义, 可以直接跳过那一条。
Issues
------
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].
Please include the following information in your submission:
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
[mailing lists]: https://groups.google.com/forum/#!forum/shadowsocks
[自行诊断]: https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting
+1 -1
View File
@@ -1,6 +1,6 @@
Shadowsocks
Copyright (c) 2012-2015 clowwindy
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
+81 -27
View File
@@ -7,51 +7,104 @@ shadowsocks
A fast tunnel proxy that helps you bypass firewalls.
Server
------
[中文说明][Chinese Readme]
### Install
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:
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:
See [Install Server on 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.
### Usage
Configuration
-------------
ssserver -p 8000 -k password -m rc4-md5
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 |
On your server:
To run in the foreground:
ssserver -c /etc/shadowsocks.json
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
ssserver -c /etc/shadowsocks.json -d start
ssserver -c /etc/shadowsocks.json -d stop
Check all the options via `-h`. You can also use a [Configuration] file
instead.
On your client machine, use the same configuration as your server. Check the
README of your client for more information.
Client
------
Command Line Options
--------------------
* [Windows] / [OS X]
* [Android] / [iOS]
* [OpenWRT]
Check the options via `-h`.You can use args to override settings from
`config.json`.
Use GUI clients on your local PC/phones. Check the README of your client
for more information.
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
Documentation
-------------
You can find all the documentation in the [Wiki].
You can find all the documentation in the wiki:
https://github.com/shadowsocks/shadowsocks/wiki
License
-------
@@ -65,22 +118,23 @@ 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
[Coverage Status]: https://jenkins.shadowvpn.org/result/shadowsocks
[Coverage]: https://jenkins.shadowvpn.org/job/Shadowsocks/ws/htmlcov/index.html
[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/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
[OpenWRT]: https://github.com/shadowsocks/openwrt-shadowsocks
[Mailing list]: http://groups.google.com/group/shadowsocks
[OpenSSL for Windows]: http://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
[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
[Windows]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#windows
+95 -29
View File
@@ -5,11 +5,26 @@ shadowsocks
A fast tunnel proxy that helps you bypass firewalls.
Server
------
`中文说明 <https://github.com/shadowsocks/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E>`__
Install
~~~~~~~
-------
You'll have a client on your local side, and setup a server on a remote
server.
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/shadowsocks/wiki/Ports-and-Clients#openwrt>`__
Server
~~~~~~
Debian / Ubuntu:
^^^^^^^^^^^^^^^^
@@ -19,56 +34,107 @@ 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:
^^^^^^^^
See `Install Server on
Windows <https://github.com/shadowsocks/shadowsocks/wiki/Install-Shadowsocks-Server-on-Windows>`__
Download `OpenSSL for
Windows <http://slproweb.com/products/Win32OpenSSL.html>`__ 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.
Usage
~~~~~
Configuration
-------------
On your server create a config file ``/etc/shadowsocks.json``. Example:
::
ssserver -p 8000 -k password -m rc4-md5
{
"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 <https://github.com/shadowsocks/shadowsocks/wiki/Encryption>`__ |
+------------------+-----------------------------------------------------------------------------------------------------------+
| fast\_open | use `TCP\_FASTOPEN <https://github.com/shadowsocks/shadowsocks/wiki/TCP-Fast-Open>`__, true / false |
+------------------+-----------------------------------------------------------------------------------------------------------+
| workers | number of workers, available on Unix/Linux |
+------------------+-----------------------------------------------------------------------------------------------------------+
On your server:
To run in the foreground:
::
ssserver -c /etc/shadowsocks.json
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
ssserver -c /etc/shadowsocks.json -d start
ssserver -c /etc/shadowsocks.json -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.
On your client machine, use the same configuration as your server. Check
the README of your client for more information.
Client
------
Command Line Options
--------------------
- `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>`__
Check the options via ``-h``.You can use args to override settings from
``config.json``.
Use GUI clients on your local PC/phones. Check the README of your client
for more information.
::
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
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:
https://github.com/shadowsocks/shadowsocks/wiki
License
-------
@@ -81,11 +147,11 @@ Bugs and Issues
- `Troubleshooting <https://github.com/shadowsocks/shadowsocks/wiki/Troubleshooting>`__
- `Issue
Tracker <https://github.com/shadowsocks/shadowsocks/issues?state=open>`__
- `Mailing list <https://groups.google.com/group/shadowsocks>`__
- `Mailing list <http://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/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
.. |Coverage Status| image:: http://192.81.132.184/result/shadowsocks
:target: http://192.81.132.184/job/Shadowsocks/ws/htmlcov/index.html
+1 -1
View File
@@ -7,7 +7,7 @@ with codecs.open('README.rst', encoding='utf-8') as f:
setup(
name="shadowsocks",
version="2.6.4",
version="2.6.1",
license='MIT',
description="A fast tunnel proxy that help you get through firewalls",
author='clowwindy',
@@ -23,12 +23,11 @@
from __future__ import absolute_import, division, print_function, \
with_statement
from ctypes import c_char_p, c_int, c_ulonglong, byref, \
import logging
from ctypes import CDLL, c_char_p, c_int, c_ulonglong, byref, \
create_string_buffer, c_void_p
from shadowsocks.crypto import util
__all__ = ['ciphers']
__all__ = ['ciphers', 'auths']
libsodium = None
loaded = False
@@ -42,11 +41,16 @@ BLOCK_SIZE = 64
def load_libsodium():
global loaded, libsodium, buf
libsodium = util.find_library('sodium', 'crypto_stream_salsa20_xor_ic',
'libsodium')
if libsodium is None:
from ctypes.util import find_library
for p in ('sodium', 'libsodium'):
libsodium_path = find_library(p)
if libsodium_path:
break
else:
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,
@@ -58,11 +62,21 @@ def load_libsodium():
c_char_p, c_ulonglong,
c_char_p)
libsodium.crypto_onetimeauth.restype = c_int
libsodium.crypto_onetimeauth.argtypes = (c_void_p, c_char_p,
c_ulonglong, c_char_p)
libsodium.crypto_onetimeauth_verify.restype = c_int
libsodium.crypto_onetimeauth_verify.argtypes = (c_char_p, c_char_p,
c_ulonglong, c_char_p)
libsodium.sodium_init()
buf = create_string_buffer(buf_size)
loaded = True
class SodiumCrypto(object):
class Salsa20Crypto(object):
def __init__(self, cipher_name, key, iv, op):
if not loaded:
load_libsodium()
@@ -100,23 +114,47 @@ class SodiumCrypto(object):
return buf.raw[padding:padding + l]
class Poly1305(object):
@staticmethod
def auth(method, key, data):
if not loaded:
load_libsodium()
tag_buf = create_string_buffer(16)
libsodium.crypto_onetimeauth(byref(tag_buf), data, len(data), key)
return tag_buf.raw
@staticmethod
def verify(method, key, data, tag):
if not loaded:
load_libsodium()
r = libsodium.crypto_onetimeauth_verify(tag, data, len(data), key)
return r == 0
ciphers = {
b'salsa20': (32, 8, SodiumCrypto),
b'chacha20': (32, 8, SodiumCrypto),
b'salsa20': (32, 8, Salsa20Crypto),
b'chacha20': (32, 8, Salsa20Crypto),
}
auths = {
b'poly1305': (32, 16, Poly1305)
}
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)
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)
util.run_cipher(cipher, decipher)
def test_chacha20():
from shadowsocks.crypto import util
cipher = SodiumCrypto(b'chacha20', b'k' * 32, b'i' * 16, 1)
decipher = SodiumCrypto(b'chacha20', b'k' * 32, b'i' * 16, 0)
cipher = Salsa20Crypto(b'chacha20', b'k' * 32, b'i' * 16, 1)
decipher = Salsa20Crypto(b'chacha20', b'k' * 32, b'i' * 16, 0)
util.run_cipher(cipher, decipher)
@@ -23,11 +23,10 @@
from __future__ import absolute_import, division, print_function, \
with_statement
from ctypes import c_char_p, c_int, c_long, byref,\
import logging
from ctypes import CDLL, c_char_p, c_int, c_long, byref,\
create_string_buffer, c_void_p
from shadowsocks.crypto import util
__all__ = ['ciphers']
libcrypto = None
@@ -39,12 +38,15 @@ buf_size = 2048
def load_openssl():
global loaded, libcrypto, buf
libcrypto = util.find_library(('crypto', 'eay32'),
'EVP_get_cipherbyname',
'libcrypto')
if libcrypto is None:
from ctypes.util import find_library
for p in ('crypto', 'eay32', 'libeay32'):
libcrypto_path = find_library(p)
if libcrypto_path:
break
else:
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
@@ -74,11 +76,11 @@ def load_cipher(cipher_name):
return None
class OpenSSLCrypto(object):
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)
@@ -117,38 +119,39 @@ 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),
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),
}
def run_method(method):
from shadowsocks.crypto import util
cipher = OpenSSLCrypto(method, b'k' * 32, b'i' * 16, 1)
decipher = OpenSSLCrypto(method, b'k' * 32, b'i' * 16, 0)
cipher = CtypesCrypto(method, b'k' * 32, b'i' * 16, 1)
decipher = CtypesCrypto(method, b'k' * 32, b'i' * 16, 0)
util.run_cipher(cipher, decipher)
+43 -27
View File
@@ -1,7 +1,6 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#!/usr/bin/env python
# Copyright (c) 2015 clowwindy
# 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
@@ -24,28 +23,45 @@
from __future__ import absolute_import, division, print_function, \
with_statement
import os
import sys
import argparse
import hmac
import hashlib
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)
from shadowsocks import common
__all__ = ['auths']
class HMAC(object):
@staticmethod
def auth(method, key, data):
digest = common.to_str(method.replace(b'hmac-', b''))
return hmac.new(key, data, getattr(hashlib, digest)).digest()
@staticmethod
def verify(method, key, data, tag):
digest = common.to_str(method.replace(b'hmac-', b''))
t = hmac.new(key, data, getattr(hashlib, digest)).digest()
if hasattr(hmac, 'compare_digest'):
return hmac.compare_digest(t, tag)
else:
return _time_independent_equals(t, tag)
# from tornado
def _time_independent_equals(a, b):
if len(a) != len(b):
return False
result = 0
if type(a[0]) is int: # python3 byte strings
for x, y in zip(a, b):
result |= x ^ y
else: # python2
for x, y in zip(a, b):
result |= ord(x) ^ ord(y)
return result == 0
auths = {
b'hmac-md5': (32, 16, HMAC),
b'hmac-sha256': (32, 32, HMAC),
}
+119
View File
@@ -0,0 +1,119 @@
#!/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()
+9 -2
View File
@@ -25,7 +25,6 @@ from __future__ import absolute_import, division, print_function, \
import hashlib
from shadowsocks.crypto import openssl
__all__ = ['ciphers']
@@ -36,7 +35,15 @@ 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()
return openssl.OpenSSLCrypto(b'rc4', rc4_key, b'', op)
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)
ciphers = {
+140
View File
@@ -0,0 +1,140 @@
#!/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()
-72
View File
@@ -20,62 +20,6 @@
# 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):
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:
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,
'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
@@ -105,19 +49,3 @@ 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()
+86 -22
View File
@@ -28,36 +28,42 @@ import sys
import hashlib
import logging
from shadowsocks.crypto import rc4_md5, openssl, sodium, table
from shadowsocks.crypto import m2, rc4_md5, salsa20_ctr,\
ctypes_openssl, ctypes_libsodium, table, hmac
from shadowsocks import common
method_supported = {}
method_supported.update(rc4_md5.ciphers)
method_supported.update(openssl.ciphers)
method_supported.update(sodium.ciphers)
method_supported.update(table.ciphers)
ciphers_supported = {}
ciphers_supported.update(rc4_md5.ciphers)
ciphers_supported.update(salsa20_ctr.ciphers)
ciphers_supported.update(ctypes_openssl.ciphers)
ciphers_supported.update(ctypes_libsodium.ciphers)
# let M2Crypto override ctypes_openssl
ciphers_supported.update(m2.ciphers)
ciphers_supported.update(table.ciphers)
auths_supported = {}
auths_supported.update(hmac.auths)
auths_supported.update(ctypes_libsodium.auths)
def random_string(length):
return os.urandom(length)
try:
import M2Crypto.Rand
return M2Crypto.Rand.rand_bytes(length)
except ImportError:
return os.urandom(length)
cached_keys = {}
def try_cipher(key, method=None):
def try_cipher(key, method=None, auth=None):
Encryptor(key, method)
auth_create(b'test', key, b'test', auth)
def EVP_BytesToKey(password, key_len, iv_len):
# equivalent to OpenSSL's EVP_BytesToKey() with count 1
# so that we make the same key and iv as nodejs version
if hasattr(password, 'encode'):
password = password.encode('utf-8')
cached_key = '%s-%d-%d' % (password, key_len, iv_len)
r = cached_keys.get(cached_key, None)
if r:
return r
m = []
i = 0
while len(b''.join(m)) < (key_len + iv_len):
@@ -71,7 +77,6 @@ def EVP_BytesToKey(password, key_len, iv_len):
ms = b''.join(m)
key = ms[:key_len]
iv = ms[key_len:key_len + iv_len]
cached_keys[cached_key] = (key, iv)
return key, iv
@@ -94,15 +99,14 @@ class Encryptor(object):
def get_method_info(self, method):
method = method.lower()
m = method_supported.get(method)
m = ciphers_supported.get(method)
return m
def iv_len(self):
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])
@@ -142,7 +146,8 @@ class Encryptor(object):
def encrypt_all(password, method, op, data):
result = []
method = method.lower()
(key_len, iv_len, m) = method_supported[method]
password = common.to_bytes(password)
(key_len, iv_len, m) = ciphers_supported[method]
if key_len > 0:
key, _ = EVP_BytesToKey(password, key_len, iv_len)
else:
@@ -158,6 +163,42 @@ def encrypt_all(password, method, op, data):
return b''.join(result)
def auth_create(data, password, iv, method):
if method is None:
return data
# prepend hmac to data
password = common.to_bytes(password)
method = method.lower()
method_info = auths_supported.get(method)
if not method_info:
logging.error('method %s not supported' % method)
sys.exit(1)
key_len, tag_len, m = method_info
key, _ = EVP_BytesToKey(password + iv, key_len, 0)
tag = m.auth(method, key, data)
return tag + data
def auth_open(data, password, iv, method):
if not method:
return data
# verify hmac and remove the hmac or return None
password = common.to_bytes(password)
method = method.lower()
method_info = auths_supported.get(method)
if not method_info:
logging.error('method %s not supported' % method)
sys.exit(1)
key_len, tag_len, m = method_info
key, _ = EVP_BytesToKey(password + iv, key_len, 0)
if len(data) <= tag_len:
return None
result = data[tag_len:]
if not m.verify(method, key, result, data[:tag_len]):
return None
return result
CIPHERS_TO_TEST = [
b'aes-128-cfb',
b'aes-256-cfb',
@@ -167,6 +208,13 @@ CIPHERS_TO_TEST = [
b'table',
]
AUTHS_TO_TEST = [
None,
b'hmac-md5',
b'hmac-sha256',
b'poly1305',
]
def test_encryptor():
from os import urandom
@@ -190,6 +238,22 @@ def test_encrypt_all():
assert plain == plain2
def test_auth():
from os import urandom
plain = urandom(10240)
for method in AUTHS_TO_TEST:
logging.warn(method)
boxed = auth_create(plain, b'key', b'iv', method)
unboxed = auth_open(boxed, b'key', b'iv', method)
assert plain == unboxed
if method is not None:
b = common.ord(boxed[0])
b ^= 1
attack = common.chr(b) + boxed[1:]
assert auth_open(attack, b'key', b'iv', method) is None
if __name__ == '__main__':
test_encrypt_all()
test_encryptor()
test_auth()
+2 -3
View File
@@ -232,9 +232,8 @@ class EventLoop(object):
logging.error(e)
import traceback
traceback.print_exc()
if self._handlers_to_remove:
for handler in self._handlers_to_remove:
self._handlers.remove(handler)
for handler in self._handlers_to_remove:
self._handlers.remove(handler)
self._handlers_to_remove = []
self._iterating = False
+1 -1
View File
@@ -49,7 +49,7 @@ def main():
utils.print_shadowsocks()
encrypt.try_cipher(config['password'], config['method'])
encrypt.try_cipher(config['password'], config['method'], config['auth'])
try:
logging.info("starting local at %s:%d" %
+1 -1
View File
@@ -57,7 +57,7 @@ def main():
else:
config['port_password'][str(server_port)] = config['password']
encrypt.try_cipher(config['password'], config['method'])
encrypt.try_cipher(config['password'], config['method'], config['auth'])
tcp_servers = []
udp_servers = []
dns_resolver = asyncdns.DNSResolver()
+5 -20
View File
@@ -1,7 +1,7 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2015 clowwindy
# 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
@@ -121,12 +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 '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
@@ -299,9 +294,8 @@ 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 from %s:%d' %
(common.to_str(remote_addr), remote_port,
self._client_address[0], self._client_address[1]))
logging.info('connecting %s:%d' % (common.to_str(remote_addr),
remote_port))
self._remote_address = (remote_addr, remote_port)
# pause reading
self._update_stream(STREAM_UP, WAIT_STATUS_WRITING)
@@ -323,7 +317,7 @@ class TCPRelayHandler(object):
self._dns_resolver.resolve(remote_addr,
self._handle_dns_resolved)
except Exception as e:
self._log_error(e)
logging.error(e)
if self._config['verbose']:
traceback.print_exc()
# TODO use logging when debug completed
@@ -335,10 +329,6 @@ 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
@@ -348,13 +338,12 @@ class TCPRelayHandler(object):
def _handle_dns_resolved(self, result, error):
if error:
self._log_error(error)
logging.error(error)
self.destroy()
return
if result:
ip = result[1]
if ip:
try:
self._stage = STAGE_CONNECTING
remote_addr = ip
@@ -518,10 +507,6 @@ 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,10 +112,6 @@ 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)
@@ -182,12 +178,6 @@ 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
+5 -8
View File
@@ -100,8 +100,7 @@ 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=',
'forbidden-ip=']
longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers=']
try:
config_path = find_config()
optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
@@ -147,8 +146,6 @@ 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()
@@ -175,7 +172,8 @@ def get_config(is_local):
sys.exit(2)
config['password'] = config.get('password', '')
config['method'] = config.get('method', b'aes-256-cfb')
config['method'] = config.get('method', 'aes-256-cfb')
config['auth'] = config.get('auth', None)
config['port_password'] = config.get('port_password', None)
config['timeout'] = int(config.get('timeout', 300))
config['fast_open'] = config.get('fast_open', False)
@@ -267,7 +265,7 @@ General options:
-v, -vv verbose mode
-q, -qq quiet mode, only show warnings/errors
Online help: <https://github.com/shadowsocks/shadowsocks>
Online help: <https://github.com/clowwindy/shadowsocks>
''')
@@ -289,7 +287,6 @@ 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
@@ -298,7 +295,7 @@ General options:
-v, -vv verbose mode
-q, -qq quiet mode, only show warnings/errors
Online help: <https://github.com/shadowsocks/shadowsocks>
Online help: <https://github.com/clowwindy/shadowsocks>
''')
+8 -21
View File
@@ -40,9 +40,6 @@ 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()
@@ -97,7 +94,7 @@ try:
stage = 5
if bytes != str:
line = str(line, 'utf8')
sys.stderr.write(line)
sys.stdout.write(line)
if line.find('starting local') >= 0:
local_ready = True
if line.find('starting server') >= 0:
@@ -106,7 +103,7 @@ try:
if stage == 1:
time.sleep(2)
p3 = Popen(['curl', config.url, '-v', '-L',
p3 = Popen(['curl', 'http://www.example.com/', '-v', '-L',
'--socks5-hostname', '127.0.0.1:1081',
'-m', '15', '--connect-timeout', '10'],
stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
@@ -121,14 +118,9 @@ try:
fdset.remove(p3.stdout)
fdset.remove(p3.stderr)
r = p3.wait()
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'],
if r != 0:
sys.exit(1)
p4 = Popen(['socksify', 'dig', '@8.8.8.8', 'www.google.com'],
stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
if p4 is not None:
fdset.append(p4.stdout)
@@ -139,14 +131,9 @@ try:
if stage == 5:
r = p4.wait()
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')
if r != 0:
sys.exit(1)
print('test passed')
break
finally:
for p in [p1, p2]:
-9
View File
@@ -1,9 +0,0 @@
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