summaryrefslogtreecommitdiff
path: root/tests/integration
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2026-11-05 18:18:45 +0100
committerDirk Engling <erdgeist@erdgeist.org>2026-11-05 18:18:45 +0100
commitc6732a03cbf0efa815e8a857af8118878c053af0 (patch)
tree6e774fd381c845f38de21f9abbfe1debe53dc56d /tests/integration
parent194fc0b2f7aa37ca25a9ea058eac699b2824e95a (diff)
Answer blacklisted torrent announces with an empty packet, that carries a re-announce timeout
Diffstat (limited to 'tests/integration')
-rw-r--r--tests/integration/test_accesslist.py18
1 files changed, 11 insertions, 7 deletions
diff --git a/tests/integration/test_accesslist.py b/tests/integration/test_accesslist.py
index 1c84ccf..7d01264 100644
--- a/tests/integration/test_accesslist.py
+++ b/tests/integration/test_accesslist.py
@@ -38,10 +38,11 @@ class Blacklist(TrackerTestCase):
38 self.assertNotIn(b"failure reason", answer) 38 self.assertNotIn(b"failure reason", answer)
39 self.assertEqual(answer[b"complete"], 1) 39 self.assertEqual(answer[b"complete"], 1)
40 40
41 def test_the_block_is_an_error_packet_over_udp(self): 41 def test_the_block_is_an_empty_announce_over_udp(self):
42 """BEP 15 has an error action, which is what a refusal should use. 42 """An empty swarm with a long interval, not an error. BEP 15 binds a
43 It used to answer the announce header with no body behind it, a 43 client to the interval and says nothing about what to do with an
44 packet too short for any client to read.""" 44 error, so an error puts the client on its own retry schedule, which
45 starts in the tens of seconds."""
45 sock, connection_id = self.tracker.udp_connect() 46 sock, connection_id = self.tracker.udp_connect()
46 sock.close() 47 sock.close()
47 request = struct.pack( 48 request = struct.pack(
@@ -49,10 +50,13 @@ class Blacklist(TrackerTestCase):
49 ) 50 )
50 answer = self.tracker.udp_raw(request) 51 answer = self.tracker.udp_raw(request)
51 self.assertIsNotNone(answer) 52 self.assertIsNotNone(answer)
52 action, transaction = struct.unpack(">II", answer[:8]) 53 self.assertEqual(len(answer), 20, "header and counts, no peers behind them")
53 self.assertEqual(action, 3, "action 3 is the error packet") 54 action, transaction, interval, leechers, seeders = struct.unpack(">IIIII", answer)
55 self.assertEqual(action, 1, "action 1 is the announce packet")
54 self.assertEqual(transaction, 7) 56 self.assertEqual(transaction, 7)
55 self.assertIn(b"not authorized", answer[8:]) 57 self.assertEqual((leechers, seeders), (0, 0))
58 self.assertGreaterEqual(interval, 60 * 60, "far enough out to be worth the trouble")
59 self.assertLessEqual(interval, 60 * 60 * 6, "not so far out that a client clamps it")
56 60
57 def test_a_blocked_hash_is_not_stored_when_it_arrives_over_udp(self): 61 def test_a_blocked_hash_is_not_stored_when_it_arrives_over_udp(self):
58 sock, connection_id = self.tracker.udp_connect() 62 sock, connection_id = self.tracker.udp_connect()