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.py102
1 files changed, 102 insertions, 0 deletions
diff --git a/tests/integration/test_accesslist.py b/tests/integration/test_accesslist.py
new file mode 100644
index 0000000..b4e134e
--- /dev/null
+++ b/tests/integration/test_accesslist.py
@@ -0,0 +1,102 @@
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_holds_over_udp_but_the_answer_is_malformed(self):
41 """The UDP path has no spelling for "not authorized": the announce
42 writes no body, so the tracker sends the eight byte header alone
43 where BEP 15 calls for at least twenty. Pinned rather than blessed —
44 an error packet or silence would both be defensible, a short announce
45 is neither."""
46 sock, connection_id = self.tracker.udp_connect()
47 sock.close()
48 request = struct.pack(
49 ">QII20s20sQQQIIIiH", connection_id, 1, 7, BLOCKED, b"p" * 20, 0, 0, 0, 0, 0, 0, -1, 6881
50 )
51 answer = self.tracker.udp_raw(request)
52 self.assertIsNotNone(answer)
53 self.assertEqual(len(answer), 8, "header only, no interval or counts")
54 action, transaction = struct.unpack(">II", answer)
55 self.assertEqual(action, 1, "it still claims to be an announce")
56 self.assertEqual(transaction, 7)
57
58 def test_a_blocked_hash_is_not_stored_when_it_arrives_over_udp(self):
59 sock, connection_id = self.tracker.udp_connect()
60 sock.close()
61 self.tracker.udp_raw(
62 struct.pack(">QII20s20sQQQIIIiH", connection_id, 1, 8, BLOCKED, b"p" * 20, 0, 0, 0, 0, 0, 0, -1, 6881)
63 )
64 self.assertEqual(self.tracker.torrent_count(), 0)
65
66
67class BlacklistWithoutTrailingNewline(TrackerTestCase):
68 """A single hash with no newline after it used to size the list at zero
69 and then write into it, which took the tracker down before it listened."""
70
71 config = "access.blacklist {dir}/blacklist\n"
72 files = {"blacklist": BLOCKED.hex().upper().encode()}
73
74 def test_the_last_line_counts_even_without_its_newline(self):
75 answer = self.tracker.announce(BLOCKED, port=6881, left=0)
76 self.assertIn(b"failure reason", answer)
77
78 def test_and_the_tracker_still_serves_everything_else(self):
79 self.assertEqual(self.tracker.announce(ALLOWED, port=6881, left=0)[b"complete"], 1)
80
81
82class EmptyBlacklist(TrackerTestCase):
83 config = "access.blacklist {dir}/blacklist\n"
84 files = {"blacklist": b""}
85
86 def test_an_empty_file_blocks_nothing_and_starts_cleanly(self):
87 self.assertEqual(self.tracker.announce(BLOCKED, port=6881, left=0)[b"complete"], 1)
88 self.assertEqual(self.tracker.announce(ALLOWED, port=6882, left=0)[b"complete"], 1)
89
90
91class MissingBlacklist(TrackerTestCase):
92 """The file is named but absent: the tracker warns and serves everything,
93 because it may be created later."""
94
95 config = "access.blacklist {dir}/absent\n"
96
97 def test_a_missing_file_blocks_nothing(self):
98 self.assertEqual(self.tracker.announce(BLOCKED, port=6881, left=0)[b"complete"], 1)
99
100
101if __name__ == "__main__":
102 unittest.main()