Merge branch 'fixbug-ci'
This commit is contained in:
commit
e1e17c8e23
13 changed files with 97 additions and 23 deletions
|
@ -1,6 +1,10 @@
|
|||
shadowsocks
|
||||
===========
|
||||
|
||||
[![PyPI version]][PyPI]
|
||||
[![Build Status]][Travis CI]
|
||||
[![Coverage Status]][Coverage]
|
||||
|
||||
A fast tunnel proxy that helps you bypass firewalls.
|
||||
|
||||
Features:
|
||||
|
|
|
@ -133,6 +133,42 @@ class Encryptor(object):
|
|||
return self.decipher.update(buf)
|
||||
|
||||
|
||||
def gen_key_iv(password, method):
|
||||
method = method.lower()
|
||||
(key_len, iv_len, m) = method_supported[method]
|
||||
key = None
|
||||
if key_len > 0:
|
||||
key, _ = EVP_BytesToKey(password, key_len, iv_len)
|
||||
else:
|
||||
key = password
|
||||
iv = random_string(iv_len)
|
||||
return key, iv, m
|
||||
|
||||
|
||||
def encrypt_all_m(key, iv, m, method, data):
|
||||
result = []
|
||||
result.append(iv)
|
||||
cipher = m(method, key, iv, 1)
|
||||
result.append(cipher.update(data))
|
||||
return b''.join(result)
|
||||
|
||||
|
||||
def dencrypt_all(password, method, data):
|
||||
result = []
|
||||
method = method.lower()
|
||||
(key_len, iv_len, m) = method_supported[method]
|
||||
key = None
|
||||
if key_len > 0:
|
||||
key, _ = EVP_BytesToKey(password, key_len, iv_len)
|
||||
else:
|
||||
key = password
|
||||
iv = data[:iv_len]
|
||||
data = data[iv_len:]
|
||||
cipher = m(method, key, iv, 0)
|
||||
result.append(cipher.update(data))
|
||||
return b''.join(result), key, iv
|
||||
|
||||
|
||||
def encrypt_all(password, method, op, data):
|
||||
result = []
|
||||
method = method.lower()
|
||||
|
@ -185,6 +221,18 @@ def test_encrypt_all():
|
|||
assert plain == plain2
|
||||
|
||||
|
||||
def test_encrypt_all_m():
|
||||
from os import urandom
|
||||
plain = urandom(10240)
|
||||
for method in CIPHERS_TO_TEST:
|
||||
logging.warn(method)
|
||||
key, iv, m = gen_key_iv(b'key', method)
|
||||
cipher = encrypt_all_m(key, iv, m, method, plain)
|
||||
plain2, key, iv = dencrypt_all(b'key', method, cipher)
|
||||
assert plain == plain2
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_encrypt_all()
|
||||
test_encryptor()
|
||||
test_encrypt_all_m()
|
||||
|
|
|
@ -113,8 +113,8 @@ class TCPRelayHandler(object):
|
|||
self._ota_enable = True
|
||||
else:
|
||||
self._ota_enable = False
|
||||
self._ota_buff_head = ''
|
||||
self._ota_buff_data = ''
|
||||
self._ota_buff_head = b''
|
||||
self._ota_buff_data = b''
|
||||
self._ota_len = 0
|
||||
self._ota_chunk_idx = 0
|
||||
self._fastopen_connected = False
|
||||
|
@ -242,6 +242,8 @@ class TCPRelayHandler(object):
|
|||
if self._ota_enable:
|
||||
self._ota_chunk_data(data,
|
||||
self._data_to_write_to_remote.append)
|
||||
else:
|
||||
self._data_to_write_to_remote.append(data)
|
||||
if self._is_local and not self._fastopen_connected and \
|
||||
self._config['fast_open']:
|
||||
# for sslocal and fastopen, we basically wait for data and use
|
||||
|
@ -336,7 +338,7 @@ class TCPRelayHandler(object):
|
|||
# spec https://shadowsocks.org/en/spec/one-time-auth.html
|
||||
# ATYP & 0x10 == 1, then OTA is enabled.
|
||||
if self._ota_enable:
|
||||
data = chr(ord(data[0]) | ADDRTYPE_AUTH) + data[1:]
|
||||
data = common.chr(addrtype | ADDRTYPE_AUTH) + data[1:]
|
||||
key = self._encryptor.cipher_iv + self._encryptor.key
|
||||
data += onetimeauth_gen(data, key)
|
||||
data_to_send = self._encryptor.encrypt(data)
|
||||
|
@ -453,8 +455,8 @@ class TCPRelayHandler(object):
|
|||
else:
|
||||
data_cb(self._ota_buff_data)
|
||||
self._ota_chunk_idx += 1
|
||||
self._ota_buff_head = ''
|
||||
self._ota_buff_data = ''
|
||||
self._ota_buff_head = b''
|
||||
self._ota_buff_data = b''
|
||||
self._ota_len = 0
|
||||
return
|
||||
|
||||
|
@ -475,6 +477,8 @@ class TCPRelayHandler(object):
|
|||
else:
|
||||
if self._ota_enable:
|
||||
self._ota_chunk_data(data, self._write_to_sock_remote)
|
||||
else:
|
||||
self._write_to_sock(data, self._remote_sock)
|
||||
return
|
||||
|
||||
def _on_local_read(self):
|
||||
|
|
|
@ -150,6 +150,8 @@ class UDPRelay(object):
|
|||
def _handle_server(self):
|
||||
server = self._server_socket
|
||||
data, r_addr = server.recvfrom(BUF_SIZE)
|
||||
key = None
|
||||
iv = None
|
||||
if not data:
|
||||
logging.debug('UDP handle_server: data is empty')
|
||||
if self._stat_callback:
|
||||
|
@ -162,7 +164,9 @@ class UDPRelay(object):
|
|||
else:
|
||||
data = data[3:]
|
||||
else:
|
||||
data = encrypt.encrypt_all(self._password, self._method, 0, data)
|
||||
data, key, iv = encrypt.dencrypt_all(self._password,
|
||||
self._method,
|
||||
data)
|
||||
# decrypt data
|
||||
if not data:
|
||||
logging.debug(
|
||||
|
@ -184,13 +188,11 @@ class UDPRelay(object):
|
|||
logging.warn('UDP one time auth header is too short')
|
||||
return
|
||||
_hash = data[-ONETIMEAUTH_BYTES:]
|
||||
_data = data[header_length: -ONETIMEAUTH_BYTES]
|
||||
_key = self._encryptor.decipher_iv + self._encryptor.key
|
||||
if onetimeauth_verify(_hash, _data, _key) is False:
|
||||
data = data[: -ONETIMEAUTH_BYTES]
|
||||
_key = iv + key
|
||||
if onetimeauth_verify(_hash, data, _key) is False:
|
||||
logging.warn('UDP one time auth fail')
|
||||
return
|
||||
self._one_time_authed = True
|
||||
header_length += ONETIMEAUTH_BYTES
|
||||
addrs = self._dns_cache.get(server_addr, None)
|
||||
if addrs is None:
|
||||
addrs = socket.getaddrinfo(server_addr, server_port, 0,
|
||||
|
@ -221,10 +223,11 @@ class UDPRelay(object):
|
|||
self._eventloop.add(client, eventloop.POLL_IN, self)
|
||||
|
||||
if self._is_local:
|
||||
key, iv, m = encrypt.gen_key_iv(self._password, self._method)
|
||||
# spec https://shadowsocks.org/en/spec/one-time-auth.html
|
||||
if self._one_time_auth_enable:
|
||||
data = self._one_time_auth_chunk_data_gen(data)
|
||||
data = encrypt.encrypt_all(self._password, self._method, 1, data)
|
||||
data = self._ota_chunk_data_gen(key, iv, data)
|
||||
data = encrypt.encrypt_all_m(key, iv, m, self._method, data)
|
||||
if not data:
|
||||
return
|
||||
else:
|
||||
|
@ -275,9 +278,9 @@ class UDPRelay(object):
|
|||
# simply drop that packet
|
||||
pass
|
||||
|
||||
def _one_time_auth_chunk_data_gen(self, data):
|
||||
data = chr(ord(data[0]) | ADDRTYPE_AUTH) + data[1:]
|
||||
key = self._encryptor.cipher_iv + self._encryptor.key
|
||||
def _ota_chunk_data_gen(self, key, iv, data):
|
||||
data = common.chr(common.ord(data[0]) | ADDRTYPE_AUTH) + data[1:]
|
||||
key = iv + key
|
||||
return data + onetimeauth_gen(data, key)
|
||||
|
||||
def add_to_loop(self, loop):
|
||||
|
|
|
@ -45,7 +45,9 @@ run_test python tests/test.py --with-coverage -s tests/aes.json -c tests/client-
|
|||
run_test python tests/test.py --with-coverage -s tests/server-dnsserver.json -c tests/client-multi-server-ip.json
|
||||
run_test python tests/test.py --with-coverage -s tests/server-multi-passwd.json -c tests/server-multi-passwd-client-side.json
|
||||
run_test python tests/test.py --with-coverage -c tests/workers.json
|
||||
run_test python tests/test.py --with-coverage -s tests/ipv6.json -c tests/ipv6-client-side.json
|
||||
run_test python tests/test.py --with-coverage -c tests/rc4-md5-ota.json
|
||||
# travis-ci not support IPv6
|
||||
# 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"
|
||||
|
|
11
tests/rc4-md5-ota.json
Normal file
11
tests/rc4-md5-ota.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"server":"127.0.0.1",
|
||||
"server_port":8388,
|
||||
"local_port":1081,
|
||||
"password":"aes_password",
|
||||
"timeout":60,
|
||||
"method":"rc4-md5",
|
||||
"local_address":"127.0.0.1",
|
||||
"fast_open":false,
|
||||
"one_time_auth":true
|
||||
}
|
|
@ -44,7 +44,7 @@ parser.add_argument('--dns', type=str, default='8.8.8.8')
|
|||
config = parser.parse_args()
|
||||
|
||||
if config.with_coverage:
|
||||
python = ['coverage', 'run', '-p', '-a']
|
||||
python = ['coverage', 'run', '-a']
|
||||
|
||||
client_args = python + ['shadowsocks/local.py', '-v']
|
||||
server_args = python + ['shadowsocks/server.py', '-v']
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
. tests/assert.sh
|
||||
|
||||
PYTHON="coverage run -a -p"
|
||||
PYTHON="coverage run -a"
|
||||
LOCAL="$PYTHON shadowsocks/local.py"
|
||||
SERVER="$PYTHON shadowsocks/server.py"
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ function run_test {
|
|||
for module in local server
|
||||
do
|
||||
|
||||
command="coverage run -p -a shadowsocks/$module.py"
|
||||
command="coverage run -a shadowsocks/$module.py"
|
||||
|
||||
mkdir -p tmp
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
PYTHON="coverage run -p -a"
|
||||
PYTHON="coverage run -a"
|
||||
URL=http://127.0.0.1/file
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
PYTHON="coverage run -p -a"
|
||||
PYTHON="coverage run -a"
|
||||
URL=http://127.0.0.1/file
|
||||
|
||||
mkdir -p tmp
|
||||
|
|
|
@ -36,6 +36,7 @@ if __name__ == '__main__':
|
|||
# make sure they're from the same source port
|
||||
assert result1 == result2
|
||||
|
||||
"""
|
||||
# Test 2: same source port IPv6
|
||||
# try again from the same port but IPv6
|
||||
sock_out = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM,
|
||||
|
@ -81,3 +82,4 @@ if __name__ == '__main__':
|
|||
|
||||
sock_out.close()
|
||||
sock_in1.close()
|
||||
"""
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
PYTHON="coverage run -p -a"
|
||||
PYTHON="coverage run -a"
|
||||
|
||||
mkdir -p tmp
|
||||
|
||||
|
|
Loading…
Reference in a new issue