Skip to content

Commit 99b4121

Browse files
author
clowwindy
committed
fix problem when UDP client requesting both IPv4 and IPv6
1 parent c34c994 commit 99b4121

File tree

4 files changed

+74
-26
lines changed

4 files changed

+74
-26
lines changed

shadowsocks/udprelay.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@
7676
BUF_SIZE = 65536
7777

7878

79-
def client_key(source_addr, dest_addr):
80-
return '%s:%s' % (source_addr[0], source_addr[1])
79+
def client_key(source_addr, server_af):
80+
# notice this is server af, not dest af
81+
return '%s:%s:%d' % (source_addr[0], source_addr[1], server_af)
8182

8283

8384
class UDPRelay(object):
@@ -169,27 +170,29 @@ def _handle_server(self):
169170
else:
170171
server_addr, server_port = dest_addr, dest_port
171172

172-
key = client_key(r_addr, (dest_addr, dest_port))
173+
addrs = socket.getaddrinfo(server_addr, server_port, 0,
174+
socket.SOCK_DGRAM, socket.SOL_UDP)
175+
if not addrs:
176+
# drop
177+
return
178+
179+
af, socktype, proto, canonname, sa = addrs[0]
180+
key = client_key(r_addr, af)
181+
logging.debug(key)
173182
client = self._cache.get(key, None)
174183
if not client:
175184
# TODO async getaddrinfo
176-
addrs = socket.getaddrinfo(server_addr, server_port, 0,
177-
socket.SOCK_DGRAM, socket.SOL_UDP)
178-
if addrs:
179-
af, socktype, proto, canonname, sa = addrs[0]
180-
if self._forbidden_iplist:
181-
if common.to_str(sa[0]) in self._forbidden_iplist:
182-
logging.debug('IP %s is in forbidden list, drop' %
183-
common.to_str(sa[0]))
184-
# drop
185-
return
186-
client = socket.socket(af, socktype, proto)
187-
client.setblocking(False)
188-
self._cache[key] = client
189-
self._client_fd_to_server_addr[client.fileno()] = r_addr
190-
else:
191-
# drop
192-
return
185+
if self._forbidden_iplist:
186+
if common.to_str(sa[0]) in self._forbidden_iplist:
187+
logging.debug('IP %s is in forbidden list, drop' %
188+
common.to_str(sa[0]))
189+
# drop
190+
return
191+
client = socket.socket(af, socktype, proto)
192+
client.setblocking(False)
193+
self._cache[key] = client
194+
self._client_fd_to_server_addr[client.fileno()] = r_addr
195+
193196
self._sockets.add(client.fileno())
194197
self._eventloop.add(client, eventloop.POLL_IN)
195198

tests/jenkins.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ function run_test {
2424
return 0
2525
}
2626

27-
pip install PySocks
28-
2927
python --version
3028
coverage erase
3129
mkdir tmp

tests/test_udp_src.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import socks
55

66
if __name__ == '__main__':
7+
# Test 1: same source port IPv4
78
sock_out = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM,
89
socket.SOL_UDP)
910
sock_out.set_proxy(socks.SOCKS5, '127.0.0.1', 1081)
@@ -17,10 +18,10 @@
1718
sock_in1.bind(('127.0.0.1', 9001))
1819
sock_in2.bind(('127.0.0.1', 9002))
1920

20-
sock_out.sendto('data', ('127.0.0.1', 9001))
21+
sock_out.sendto(b'data', ('127.0.0.1', 9001))
2122
result1 = sock_in1.recvfrom(8)
2223

23-
sock_out.sendto('data', ('127.0.0.1', 9002))
24+
sock_out.sendto(b'data', ('127.0.0.1', 9002))
2425
result2 = sock_in2.recvfrom(8)
2526

2627
sock_out.close()
@@ -29,3 +30,49 @@
2930

3031
# make sure they're from the same source port
3132
assert result1 == result2
33+
34+
# Test 2: same source port IPv6
35+
# try again from the same port but IPv6
36+
sock_out = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM,
37+
socket.SOL_UDP)
38+
sock_out.set_proxy(socks.SOCKS5, '127.0.0.1', 1081)
39+
sock_out.bind(('127.0.0.1', 9000))
40+
41+
sock_in1 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM,
42+
socket.SOL_UDP)
43+
sock_in2 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM,
44+
socket.SOL_UDP)
45+
46+
sock_in1.bind(('::1', 9001))
47+
sock_in2.bind(('::1', 9002))
48+
49+
sock_out.sendto(b'data', ('::1', 9001))
50+
result1 = sock_in1.recvfrom(8)
51+
52+
sock_out.sendto(b'data', ('::1', 9002))
53+
result2 = sock_in2.recvfrom(8)
54+
55+
sock_out.close()
56+
sock_in1.close()
57+
sock_in2.close()
58+
59+
# make sure they're from the same source port
60+
assert result1 == result2
61+
62+
# Test 3: different source ports IPv6
63+
sock_out = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM,
64+
socket.SOL_UDP)
65+
sock_out.set_proxy(socks.SOCKS5, '127.0.0.1', 1081)
66+
sock_out.bind(('127.0.0.1', 9003))
67+
68+
sock_in1 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM,
69+
socket.SOL_UDP)
70+
sock_in1.bind(('::1', 9001))
71+
sock_out.sendto(b'data', ('::1', 9001))
72+
result3 = sock_in1.recvfrom(8)
73+
74+
# make sure they're from different source ports
75+
assert result1 != result3
76+
77+
sock_out.close()
78+
sock_in1.close()

tests/test_udp_src.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ PYTHON="coverage run -p -a"
44

55
mkdir -p tmp
66

7-
$PYTHON shadowsocks/local.py -c tests/aes.json &
7+
$PYTHON shadowsocks/local.py -c tests/aes.json -v &
88
LOCAL=$!
99

10-
$PYTHON shadowsocks/server.py -c tests/aes.json --forbidden-ip "" &
10+
$PYTHON shadowsocks/server.py -c tests/aes.json --forbidden-ip "" -v &
1111
SERVER=$!
1212

1313
sleep 3

0 commit comments

Comments
 (0)