summaryrefslogtreecommitdiff
path: root/tests/integration/test_accesslist.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/integration/test_accesslist.py')
-rw-r--r--tests/integration/test_accesslist.py100
1 files changed, 100 insertions, 0 deletions
diff --git a/tests/integration/test_accesslist.py b/tests/integration/test_accesslist.py
new file mode 100644
index 0000000..41ff654
--- /dev/null
+++ b/tests/integration/test_accesslist.py
@@ -0,0 +1,100 @@
1"""The blacklist, loaded from a file at startup.
2
3A blacklisted info hash must be refused at both the announce and the scrape
4entry, and the file parser has to cope with the shapes an operator actually
5produces: an empty file, and a last line with no newline after it.
6"""
7
8import struct
9import unittest
10
11from harness import TrackerTestCase
12
13BLOCKED = b"\xb1" * 20
14ALLOWED = b"\xa1" * 20
15
16
17def listing(*hashes):
18 return b"".join(h.hex().upper().encode() + b"\n" for h in hashes)
19
20
21class Blacklist(TrackerTestCase):
22 config = "access.blacklist {dir}/blacklist\n"
23 files = {"blacklist": listing(BLOCKED)}
24
25 def test_a_blacklisted_hash_is_refused(self):
26 answer = self.tracker.announce(BLOCKED, port=6881, left=0)
27 self.assertIn(b"failure reason", answer)
28 self.assertIn(b"not authorized", answer[b"failure reason"])
29
30 def test_a_blacklisted_hash_never_becomes_a_torrent(self):
31 before = self.tracker.torrent_count()
32 self.tracker.announce(BLOCKED, port=6881, left=0)
33 self.assertEqual(self.tracker.torrent_count(), before, "the refused hash was stored anyway")
34
35 def test_any_other_hash_is_served(self):
36 answer = self.tracker.announce(ALLOWED, port=6881, left=0)
37 self.assertNotIn(b"failure reason", answer)
38 self.assertEqual(answer[b"complete"], 1)
39
40 def test_the_block_is_an_error_packet_over_udp(self):
41 """BEP 15 has an error action, which is what a refusal should use.
42 It used to answer the announce header with no body behind it, a
43 packet too short for any client to read."""
44 sock, connection_id = self.tracker.udp_connect()
45 sock.close()
46 request = struct.pack(
47 ">QII20s20sQQQIIIiH", connection_id, 1, 7, BLOCKED, b"p" * 20, 0, 0, 0, 0, 0, 0, -1, 6881
48 )
49 answer = self.tracker.udp_raw(request)
50 self.assertIsNotNone(answer)
51 action, transaction = struct.unpack(">II", answer[:8])
52 self.assertEqual(action, 3, "action 3 is the error packet")
53 self.assertEqual(transaction, 7)
54 self.assertIn(b"not authorized", answer[8:])
55
56 def test_a_blocked_hash_is_not_stored_when_it_arrives_over_udp(self):
57 sock, connection_id = self.tracker.udp_connect()
58 sock.close()
59 self.tracker.udp_raw(
60 struct.pack(">QII20s20sQQQIIIiH", connection_id, 1, 8, BLOCKED, b"p" * 20, 0, 0, 0, 0, 0, 0, -1, 6881)
61 )
62 self.assertEqual(self.tracker.torrent_count(), 0)
63
64
65class BlacklistWithoutTrailingNewline(TrackerTestCase):
66 """A single hash with no newline after it used to size the list at zero
67 and then write into it, which took the tracker down before it listened."""
68
69 config = "access.blacklist {dir}/blacklist\n"
70 files = {"blacklist": BLOCKED.hex().upper().encode()}
71
72 def test_the_last_line_counts_even_without_its_newline(self):
73 answer = self.tracker.announce(BLOCKED, port=6881, left=0)
74 self.assertIn(b"failure reason", answer)
75
76 def test_and_the_tracker_still_serves_everything_else(self):
77 self.assertEqual(self.tracker.announce(ALLOWED, port=6881, left=0)[b"complete"], 1)
78
79
80class EmptyBlacklist(TrackerTestCase):
81 config = "access.blacklist {dir}/blacklist\n"
82 files = {"blacklist": b""}
83
84 def test_an_empty_file_blocks_nothing_and_starts_cleanly(self):
85 self.assertEqual(self.tracker.announce(BLOCKED, port=6881, left=0)[b"complete"], 1)
86 self.assertEqual(self.tracker.announce(ALLOWED, port=6882, left=0)[b"complete"], 1)
87
88
89class MissingBlacklist(TrackerTestCase):
90 """The file is named but absent: the tracker warns and serves everything,
91 because it may be created later."""
92
93 config = "access.blacklist {dir}/absent\n"
94
95 def test_a_missing_file_blocks_nothing(self):
96 self.assertEqual(self.tracker.announce(BLOCKED, port=6881, left=0)[b"complete"], 1)
97
98
99if __name__ == "__main__":
100 unittest.main()