diff options
Diffstat (limited to 'tests/integration/test_dualstack.py')
| -rw-r--r-- | tests/integration/test_dualstack.py | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/tests/integration/test_dualstack.py b/tests/integration/test_dualstack.py new file mode 100644 index 0000000..cd5ba7d --- /dev/null +++ b/tests/integration/test_dualstack.py | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | """A tracker serving both families from one socket. | ||
| 2 | |||
| 3 | These need WANT_IP_FROM_QUERY_STRING so a peer's address can be dictated | ||
| 4 | rather than being whatever loopback the test happened to come from. | ||
| 5 | """ | ||
| 6 | |||
| 7 | import os | ||
| 8 | import socket | ||
| 9 | import sys | ||
| 10 | import unittest | ||
| 11 | |||
| 12 | sys.path.insert(0, os.path.dirname(__file__)) | ||
| 13 | from harness import TrackerTestCase | ||
| 14 | |||
| 15 | |||
| 16 | def addresses(answer, key): | ||
| 17 | blob, out = answer.get(key, b""), [] | ||
| 18 | size = 6 if key == b"peers" else 18 | ||
| 19 | for offset in range(0, len(blob), size): | ||
| 20 | entry = blob[offset : offset + size] | ||
| 21 | out.append(socket.inet_ntop(socket.AF_INET if size == 6 else socket.AF_INET6, entry[:-2])) | ||
| 22 | return out | ||
| 23 | |||
| 24 | |||
| 25 | class DualStack(TrackerTestCase): | ||
| 26 | def test_an_ipv4_peer_lands_in_the_v4_list(self): | ||
| 27 | info_hash = self.hash_for("v4only") | ||
| 28 | self.tracker.announce(info_hash, ip="10.1.2.3", port=5001, left=100) | ||
| 29 | answer = self.tracker.announce(info_hash, ip="10.1.2.4", port=5002, left=100) | ||
| 30 | |||
| 31 | self.assertEqual(sorted(addresses(answer, b"peers")), ["10.1.2.3", "10.1.2.4"]) | ||
| 32 | self.assertEqual(addresses(answer, b"peers6"), [], "no v6 peer was announced") | ||
| 33 | |||
| 34 | def test_an_ipv6_peer_lands_in_the_v6_list(self): | ||
| 35 | info_hash = self.hash_for("v6only") | ||
| 36 | self.tracker.announce(info_hash, ip="2001:db8::1", port=5001, left=100) | ||
| 37 | answer = self.tracker.announce(info_hash, ip="2001:db8::2", port=5002, left=100) | ||
| 38 | |||
| 39 | self.assertEqual(sorted(addresses(answer, b"peers6")), ["2001:db8::1", "2001:db8::2"]) | ||
| 40 | self.assertEqual(addresses(answer, b"peers"), [], "no v4 peer was announced") | ||
| 41 | |||
| 42 | def test_the_two_families_are_kept_apart_but_both_returned(self): | ||
| 43 | info_hash = self.hash_for("both") | ||
| 44 | self.tracker.announce(info_hash, ip="10.9.9.9", port=6001, left=100) | ||
| 45 | answer = self.tracker.announce(info_hash, ip="2001:db8::9", port=6002, left=100) | ||
| 46 | |||
| 47 | self.assertEqual(addresses(answer, b"peers"), ["10.9.9.9"]) | ||
| 48 | self.assertEqual(addresses(answer, b"peers6"), ["2001:db8::9"]) | ||
| 49 | |||
| 50 | def test_a_v4_mapped_address_is_the_same_peer_as_the_plain_one(self): | ||
| 51 | info_hash = self.hash_for("v4mapped") | ||
| 52 | self.tracker.announce(info_hash, ip="10.5.5.5", port=7001, left=100) | ||
| 53 | answer = self.tracker.announce(info_hash, ip="::ffff:10.5.5.5", port=7001, left=100) | ||
| 54 | |||
| 55 | self.assertEqual(answer[b"incomplete"], 1, "the same address written two ways is one peer") | ||
| 56 | self.assertEqual(addresses(answer, b"peers"), ["10.5.5.5"], "and it is held as v4") | ||
| 57 | |||
| 58 | def test_the_two_families_count_into_one_swarm(self): | ||
| 59 | info_hash = self.hash_for("counts") | ||
| 60 | self.tracker.announce(info_hash, ip="10.4.4.4", port=8001, left=0) | ||
| 61 | answer = self.tracker.announce(info_hash, ip="2001:db8::4", port=8002, left=100) | ||
| 62 | |||
| 63 | self.assertEqual(answer[b"complete"], 1) | ||
| 64 | self.assertEqual(answer[b"incomplete"], 1) | ||
| 65 | self.assertEqual(self.tracker.scrape(info_hash)[b"files"][info_hash][b"complete"], 1) | ||
| 66 | |||
| 67 | def test_stopped_removes_only_the_family_it_came_from(self): | ||
| 68 | info_hash = self.hash_for("stopfamily") | ||
| 69 | self.tracker.announce(info_hash, ip="10.3.3.3", port=9001, left=100) | ||
| 70 | self.tracker.announce(info_hash, ip="2001:db8::3", port=9002, left=100) | ||
| 71 | self.tracker.announce(info_hash, ip="10.3.3.3", port=9001, left=100, event="stopped") | ||
| 72 | |||
| 73 | answer = self.tracker.announce(info_hash, ip="2001:db8::33", port=9003, left=100) | ||
| 74 | self.assertEqual(addresses(answer, b"peers"), [], "the v4 peer left") | ||
| 75 | self.assertIn("2001:db8::3", addresses(answer, b"peers6"), "the v6 one stayed") | ||
| 76 | |||
| 77 | def test_a_malformed_address_is_rejected(self): | ||
| 78 | import urllib.error | ||
| 79 | |||
| 80 | # The whole field has to be the address: a prefix that happens to | ||
| 81 | # parse would quietly record the peer somewhere else | ||
| 82 | for bad in ("999.1.2.3", "nonsense", "2001:db8:::1", "10.1.2.3.4", "10.1.2.3junk", "2001:db8::1%eth0"): | ||
| 83 | with self.assertRaises(urllib.error.HTTPError, msg="ip=%s should be refused" % bad) as caught: | ||
| 84 | self.tracker.announce(self.hash_for("badip"), ip=bad, port=6881, left=0) | ||
| 85 | self.assertEqual(caught.exception.code, 400) | ||
| 86 | |||
| 87 | |||
| 88 | if __name__ == "__main__": | ||
| 89 | unittest.main() | ||
