diff options
| -rw-r--r-- | src/ot_udp.c | 8 | ||||
| -rw-r--r-- | src/trackerlogic.c | 6 | ||||
| -rw-r--r-- | src/trackerlogic.h | 3 | ||||
| -rw-r--r-- | tests/integration/test_accesslist.py | 18 |
4 files changed, 25 insertions, 10 deletions
diff --git a/src/ot_udp.c b/src/ot_udp.c index e67ca1f..9e8e3c4 100644 --- a/src/ot_udp.c +++ b/src/ot_udp.c | |||
| @@ -176,11 +176,13 @@ int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) { | |||
| 176 | ws->reply = ws->outbuf + 8; | 176 | ws->reply = ws->outbuf + 8; |
| 177 | ws->reply_size = add_peer_to_torrent_and_return_peers(FLAG_UDP, ws, numwant); | 177 | ws->reply_size = add_peer_to_torrent_and_return_peers(FLAG_UDP, ws, numwant); |
| 178 | 178 | ||
| 179 | /* The announce path answers a refusal itself, so nothing is left here | ||
| 180 | but a tracker that could not get the memory to track the torrent */ | ||
| 179 | if (!ws->reply_size) { | 181 | if (!ws->reply_size) { |
| 180 | const char err_unauth[] = "Requested download is not authorized for use with this tracker."; | 182 | const char err_resources[] = "Tracker is out of resources, try again later."; |
| 181 | size_t s = sizeof(err_unauth); | 183 | size_t s = sizeof(err_resources); |
| 182 | outpacket[0] = htonl(3); | 184 | outpacket[0] = htonl(3); |
| 183 | memcpy(&outpacket[2], &err_unauth[0], s); | 185 | memcpy(&outpacket[2], &err_resources[0], s); |
| 184 | socket_send(serversocket, ws->outbuf, 8 + s, remoteip, remoteport); | 186 | socket_send(serversocket, ws->outbuf, 8 + s, remoteip, remoteport); |
| 185 | return 1; | 187 | return 1; |
| 186 | } | 188 | } |
diff --git a/src/trackerlogic.c b/src/trackerlogic.c index 7e8d969..2313379 100644 --- a/src/trackerlogic.c +++ b/src/trackerlogic.c | |||
| @@ -94,6 +94,12 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru | |||
| 94 | memcpy(ws->reply, invalid_hash, strlen(invalid_hash)); | 94 | memcpy(ws->reply, invalid_hash, strlen(invalid_hash)); |
| 95 | return strlen(invalid_hash); | 95 | return strlen(invalid_hash); |
| 96 | } | 96 | } |
| 97 | if (proto == FLAG_UDP) { | ||
| 98 | ((uint32_t *)ws->reply)[0] = htonl(OT_CLIENT_REJECT_INTERVAL_RANDOM); | ||
| 99 | ((uint32_t *)ws->reply)[1] = 0; | ||
| 100 | ((uint32_t *)ws->reply)[2] = 0; | ||
| 101 | return 12; | ||
| 102 | } | ||
| 97 | return 0; | 103 | return 0; |
| 98 | } | 104 | } |
| 99 | 105 | ||
diff --git a/src/trackerlogic.h b/src/trackerlogic.h index f6aa8cc..4f970ba 100644 --- a/src/trackerlogic.h +++ b/src/trackerlogic.h | |||
| @@ -39,12 +39,15 @@ typedef struct { | |||
| 39 | #define OT_CLIENT_TIMEOUT_SEND (60 * 15) | 39 | #define OT_CLIENT_TIMEOUT_SEND (60 * 15) |
| 40 | #define OT_CLIENT_REQUEST_INTERVAL (60 * 30) | 40 | #define OT_CLIENT_REQUEST_INTERVAL (60 * 30) |
| 41 | #define OT_CLIENT_REQUEST_VARIATION (60 * 6) | 41 | #define OT_CLIENT_REQUEST_VARIATION (60 * 6) |
| 42 | #define OT_CLIENT_REJECT_INTERVAL (60 * 120) | ||
| 42 | 43 | ||
| 43 | #define OT_TORRENT_TIMEOUT_HOURS 24 | 44 | #define OT_TORRENT_TIMEOUT_HOURS 24 |
| 44 | #define OT_TORRENT_TIMEOUT (60 * OT_TORRENT_TIMEOUT_HOURS) | 45 | #define OT_TORRENT_TIMEOUT (60 * OT_TORRENT_TIMEOUT_HOURS) |
| 45 | 46 | ||
| 46 | #define OT_CLIENT_REQUEST_INTERVAL_RANDOM \ | 47 | #define OT_CLIENT_REQUEST_INTERVAL_RANDOM \ |
| 47 | (OT_CLIENT_REQUEST_INTERVAL - OT_CLIENT_REQUEST_VARIATION / 2 + (int)(nrand48(ws->rand48_state) % OT_CLIENT_REQUEST_VARIATION)) | 48 | (OT_CLIENT_REQUEST_INTERVAL - OT_CLIENT_REQUEST_VARIATION / 2 + (int)(nrand48(ws->rand48_state) % OT_CLIENT_REQUEST_VARIATION)) |
| 49 | #define OT_CLIENT_REJECT_INTERVAL_RANDOM \ | ||
| 50 | (OT_CLIENT_REJECT_INTERVAL - OT_CLIENT_REQUEST_VARIATION / 2 + (int)(nrand48(ws->rand48_state) % OT_CLIENT_REQUEST_VARIATION)) | ||
| 48 | 51 | ||
| 49 | /* If WANT_MODEST_FULLSCRAPES is on, ip addresses may not | 52 | /* If WANT_MODEST_FULLSCRAPES is on, ip addresses may not |
| 50 | fullscrape more frequently than this amount in seconds */ | 53 | fullscrape more frequently than this amount in seconds */ |
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() |
