fix problem when UDP client requesting both IPv4 and IPv6
This commit is contained in:
parent
c34c99450f
commit
99b4121fd9
4 changed files with 74 additions and 26 deletions
|
@ -76,8 +76,9 @@ from shadowsocks.common import parse_header, pack_addr
|
||||||
BUF_SIZE = 65536
|
BUF_SIZE = 65536
|
||||||
|
|
||||||
|
|
||||||
def client_key(source_addr, dest_addr):
|
def client_key(source_addr, server_af):
|
||||||
return '%s:%s' % (source_addr[0], source_addr[1])
|
# notice this is server af, not dest af
|
||||||
|
return '%s:%s:%d' % (source_addr[0], source_addr[1], server_af)
|
||||||
|
|
||||||
|
|
||||||
class UDPRelay(object):
|
class UDPRelay(object):
|
||||||
|
@ -169,27 +170,29 @@ class UDPRelay(object):
|
||||||
else:
|
else:
|
||||||
server_addr, server_port = dest_addr, dest_port
|
server_addr, server_port = dest_addr, dest_port
|
||||||
|
|
||||||
key = client_key(r_addr, (dest_addr, dest_port))
|
addrs = socket.getaddrinfo(server_addr, server_port, 0,
|
||||||
|
socket.SOCK_DGRAM, socket.SOL_UDP)
|
||||||
|
if not addrs:
|
||||||
|
# drop
|
||||||
|
return
|
||||||
|
|
||||||
|
af, socktype, proto, canonname, sa = addrs[0]
|
||||||
|
key = client_key(r_addr, af)
|
||||||
|
logging.debug(key)
|
||||||
client = self._cache.get(key, None)
|
client = self._cache.get(key, None)
|
||||||
if not client:
|
if not client:
|
||||||
# TODO async getaddrinfo
|
# TODO async getaddrinfo
|
||||||
addrs = socket.getaddrinfo(server_addr, server_port, 0,
|
if self._forbidden_iplist:
|
||||||
socket.SOCK_DGRAM, socket.SOL_UDP)
|
if common.to_str(sa[0]) in self._forbidden_iplist:
|
||||||
if addrs:
|
logging.debug('IP %s is in forbidden list, drop' %
|
||||||
af, socktype, proto, canonname, sa = addrs[0]
|
common.to_str(sa[0]))
|
||||||
if self._forbidden_iplist:
|
# drop
|
||||||
if common.to_str(sa[0]) in self._forbidden_iplist:
|
return
|
||||||
logging.debug('IP %s is in forbidden list, drop' %
|
client = socket.socket(af, socktype, proto)
|
||||||
common.to_str(sa[0]))
|
client.setblocking(False)
|
||||||
# drop
|
self._cache[key] = client
|
||||||
return
|
self._client_fd_to_server_addr[client.fileno()] = r_addr
|
||||||
client = socket.socket(af, socktype, proto)
|
|
||||||
client.setblocking(False)
|
|
||||||
self._cache[key] = client
|
|
||||||
self._client_fd_to_server_addr[client.fileno()] = r_addr
|
|
||||||
else:
|
|
||||||
# drop
|
|
||||||
return
|
|
||||||
self._sockets.add(client.fileno())
|
self._sockets.add(client.fileno())
|
||||||
self._eventloop.add(client, eventloop.POLL_IN)
|
self._eventloop.add(client, eventloop.POLL_IN)
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,6 @@ function run_test {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pip install PySocks
|
|
||||||
|
|
||||||
python --version
|
python --version
|
||||||
coverage erase
|
coverage erase
|
||||||
mkdir tmp
|
mkdir tmp
|
||||||
|
|
|
@ -4,6 +4,7 @@ import socket
|
||||||
import socks
|
import socks
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
# Test 1: same source port IPv4
|
||||||
sock_out = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM,
|
sock_out = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM,
|
||||||
socket.SOL_UDP)
|
socket.SOL_UDP)
|
||||||
sock_out.set_proxy(socks.SOCKS5, '127.0.0.1', 1081)
|
sock_out.set_proxy(socks.SOCKS5, '127.0.0.1', 1081)
|
||||||
|
@ -17,10 +18,10 @@ if __name__ == '__main__':
|
||||||
sock_in1.bind(('127.0.0.1', 9001))
|
sock_in1.bind(('127.0.0.1', 9001))
|
||||||
sock_in2.bind(('127.0.0.1', 9002))
|
sock_in2.bind(('127.0.0.1', 9002))
|
||||||
|
|
||||||
sock_out.sendto('data', ('127.0.0.1', 9001))
|
sock_out.sendto(b'data', ('127.0.0.1', 9001))
|
||||||
result1 = sock_in1.recvfrom(8)
|
result1 = sock_in1.recvfrom(8)
|
||||||
|
|
||||||
sock_out.sendto('data', ('127.0.0.1', 9002))
|
sock_out.sendto(b'data', ('127.0.0.1', 9002))
|
||||||
result2 = sock_in2.recvfrom(8)
|
result2 = sock_in2.recvfrom(8)
|
||||||
|
|
||||||
sock_out.close()
|
sock_out.close()
|
||||||
|
@ -29,3 +30,49 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# make sure they're from the same source port
|
# make sure they're from the same source port
|
||||||
assert result1 == result2
|
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,
|
||||||
|
socket.SOL_UDP)
|
||||||
|
sock_out.set_proxy(socks.SOCKS5, '127.0.0.1', 1081)
|
||||||
|
sock_out.bind(('127.0.0.1', 9000))
|
||||||
|
|
||||||
|
sock_in1 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM,
|
||||||
|
socket.SOL_UDP)
|
||||||
|
sock_in2 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM,
|
||||||
|
socket.SOL_UDP)
|
||||||
|
|
||||||
|
sock_in1.bind(('::1', 9001))
|
||||||
|
sock_in2.bind(('::1', 9002))
|
||||||
|
|
||||||
|
sock_out.sendto(b'data', ('::1', 9001))
|
||||||
|
result1 = sock_in1.recvfrom(8)
|
||||||
|
|
||||||
|
sock_out.sendto(b'data', ('::1', 9002))
|
||||||
|
result2 = sock_in2.recvfrom(8)
|
||||||
|
|
||||||
|
sock_out.close()
|
||||||
|
sock_in1.close()
|
||||||
|
sock_in2.close()
|
||||||
|
|
||||||
|
# make sure they're from the same source port
|
||||||
|
assert result1 == result2
|
||||||
|
|
||||||
|
# Test 3: different source ports IPv6
|
||||||
|
sock_out = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM,
|
||||||
|
socket.SOL_UDP)
|
||||||
|
sock_out.set_proxy(socks.SOCKS5, '127.0.0.1', 1081)
|
||||||
|
sock_out.bind(('127.0.0.1', 9003))
|
||||||
|
|
||||||
|
sock_in1 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM,
|
||||||
|
socket.SOL_UDP)
|
||||||
|
sock_in1.bind(('::1', 9001))
|
||||||
|
sock_out.sendto(b'data', ('::1', 9001))
|
||||||
|
result3 = sock_in1.recvfrom(8)
|
||||||
|
|
||||||
|
# make sure they're from different source ports
|
||||||
|
assert result1 != result3
|
||||||
|
|
||||||
|
sock_out.close()
|
||||||
|
sock_in1.close()
|
||||||
|
|
|
@ -4,10 +4,10 @@ PYTHON="coverage run -p -a"
|
||||||
|
|
||||||
mkdir -p tmp
|
mkdir -p tmp
|
||||||
|
|
||||||
$PYTHON shadowsocks/local.py -c tests/aes.json &
|
$PYTHON shadowsocks/local.py -c tests/aes.json -v &
|
||||||
LOCAL=$!
|
LOCAL=$!
|
||||||
|
|
||||||
$PYTHON shadowsocks/server.py -c tests/aes.json --forbidden-ip "" &
|
$PYTHON shadowsocks/server.py -c tests/aes.json --forbidden-ip "" -v &
|
||||||
SERVER=$!
|
SERVER=$!
|
||||||
|
|
||||||
sleep 3
|
sleep 3
|
||||||
|
|
Loading…
Reference in a new issue