summaryrefslogtreecommitdiff
path: root/src/ot_udp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ot_udp.c')
-rw-r--r--src/ot_udp.c226
1 files changed, 226 insertions, 0 deletions
diff --git a/src/ot_udp.c b/src/ot_udp.c
new file mode 100644
index 0000000..644b71d
--- /dev/null
+++ b/src/ot_udp.c
@@ -0,0 +1,226 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4/* System */
5#include <arpa/inet.h>
6#include <pthread.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <unistd.h>
11
12/* Opentracker */
13#include "ot_ip.h"
14#include "ot_random.h"
15#include "ot_rijndael.h"
16#include "ot_socket.h"
17#include "ot_worker.h"
18#include "ot_stats.h"
19#include "ot_udp.h"
20#include "trackerlogic.h"
21
22static uint32_t g_rijndael_round_key[44] = {0};
23static volatile uint32_t g_key_of_the_hour[2] = {0};
24static volatile ot_time g_hour_of_the_key;
25
26
27static void udp_generate_rijndael_round_key(void) {
28 uint32_t key[16];
29 random_bytes(key, sizeof(key));
30 rijndaelKeySetupEnc128(g_rijndael_round_key, (uint8_t *)key);
31
32 g_key_of_the_hour[0] = random_uint32();
33 g_hour_of_the_key = time_now_minutes();
34}
35
36/* Generate current and previous connection id for ip */
37static void udp_make_connectionid(uint32_t connid[2], const ot_ip6 remoteip, int age) {
38 uint32_t plain[4], crypt[4];
39 int i;
40 uint32_t current_key_of_the_hour;
41
42 if (time_now_minutes() - g_hour_of_the_key >= 60) {
43 uint32_t old_key_of_the_hour = g_key_of_the_hour[0];
44 uint32_t new_key_of_the_hour = random_uint32();
45 /* If in the meantime another thread has performed
46 key rotation, do not overwrite their results */
47 if (time_now_minutes() - g_hour_of_the_key >= 60) {
48 /* Upgrade en bloc */
49 g_hour_of_the_key = time_now_minutes();
50 g_key_of_the_hour[0] = new_key_of_the_hour;
51 g_key_of_the_hour[1] = old_key_of_the_hour;
52 }
53 }
54
55 memcpy(plain, remoteip, sizeof(plain));
56 current_key_of_the_hour = g_key_of_the_hour[age];
57 for (i = 0; i < 4; ++i)
58 plain[i] ^= current_key_of_the_hour;
59 rijndaelEncrypt128(g_rijndael_round_key, (uint8_t *)plain, (uint8_t *)crypt);
60 connid[0] = crypt[0] ^ crypt[1];
61 connid[1] = crypt[2] ^ crypt[3];
62}
63
64/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */
65int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) {
66 ot_ip6 remoteip;
67 uint32_t *inpacket = (uint32_t *)ws->inbuf;
68 uint32_t *outpacket = (uint32_t *)ws->outbuf;
69 uint32_t left, event;
70 uint32_t connid[2];
71 uint32_t action;
72 uint16_t port, remoteport;
73 size_t byte_count, scrape_count;
74
75 ssize_t result = socket_recv(serversocket, ws->inbuf, G_INBUF_SIZE, remoteip, &remoteport);
76 if (result <= 0)
77 return 0;
78 byte_count = (size_t)result;
79
80 stats_issue_event(ws, EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip);
81 stats_issue_event(ws, EVENT_READ, FLAG_UDP, byte_count);
82
83 /* Minimum udp tracker packet size, also catches error */
84 if (byte_count < 16)
85 return 1;
86
87 /* Get action to take. Ignore error messages and broken packets */
88 action = ntohl(inpacket[2]);
89 if (action > 2)
90 return 1;
91
92 /* Generate the connection id we give out and expect to and from
93 the requesting ip address, this prevents udp spoofing */
94 udp_make_connectionid(connid, remoteip, 0);
95
96 /* Initialise hash pointer */
97 ws->hash = NULL;
98 ws->peer_id = NULL;
99
100 /* If action is not 0 (connect), then we expect the derived
101 connection id in first 64 bit */
102 if ((action > 0) && (inpacket[0] != connid[0] || inpacket[1] != connid[1])) {
103 /* If connection id does not match, try the one that was
104 valid in the previous hour. Only if this also does not
105 match, return an error packet */
106 udp_make_connectionid(connid, remoteip, 1);
107 if (inpacket[0] != connid[0] || inpacket[1] != connid[1]) {
108 const char err_idmiss[] = "Connection ID missmatch.";
109 size_t s = sizeof(err_idmiss);
110 outpacket[0] = htonl(3);
111 outpacket[1] = inpacket[3];
112 memcpy(&outpacket[2], &err_idmiss[0], s);
113 socket_send(serversocket, ws->outbuf, 8 + s, remoteip, remoteport);
114 stats_issue_event(ws, EVENT_CONNID_MISSMATCH, FLAG_UDP, 8 + s);
115 return 1;
116 }
117 }
118
119 switch (action) {
120 case 0: /* This is a connect action */
121 /* look for udp bittorrent magic id */
122 if ((ntohl(inpacket[0]) != 0x00000417) || (ntohl(inpacket[1]) != 0x27101980))
123 return 1;
124
125 outpacket[0] = 0;
126 outpacket[1] = inpacket[3];
127 outpacket[2] = connid[0];
128 outpacket[3] = connid[1];
129
130 socket_send(serversocket, ws->outbuf, 16, remoteip, remoteport);
131 stats_issue_event(ws, EVENT_CONNECT, FLAG_UDP, 16);
132 break;
133 case 1: /* This is an announce action */
134 /* Minimum udp announce packet size */
135 if (byte_count < 98)
136 return 1;
137
138 /* We do only want to know, if it is zero */
139 left = inpacket[64 / 4] | inpacket[68 / 4];
140
141 event = ntohl(inpacket[80 / 4]);
142 port = *(uint16_t *)(((char *)inpacket) + 96);
143 ws->hash = (ot_hash *)(((char *)inpacket) + 16);
144
145 OT_SETIP(ws->peer, remoteip);
146 OT_SETPORT(ws->peer, &port);
147 OT_PEERFLAG(ws->peer) = 0;
148
149 switch (event) {
150 case 1:
151 OT_PEERFLAG(ws->peer) |= PEER_FLAG_COMPLETED;
152 break;
153 case 3:
154 OT_PEERFLAG(ws->peer) |= PEER_FLAG_STOPPED;
155 break;
156 default:
157 break;
158 }
159
160 if (!left)
161 OT_PEERFLAG(ws->peer) |= PEER_FLAG_SEEDING;
162
163 outpacket[0] = htonl(1); /* announce action */
164 outpacket[1] = inpacket[12 / 4];
165
166 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_STOPPED) { /* Peer is gone. */
167 ws->reply = ws->outbuf;
168 ws->reply_size = remove_peer_from_torrent(FLAG_UDP, ws);
169 } else {
170 /* Limit amount of peers to OT_MAX_PEERS_UDP */
171 uint32_t numwant = ntohl(inpacket[92 / 4]);
172 size_t max_peers = ip_is_v4mapped(remoteip) ? OT_MAX_PEERS_UDP4 : OT_MAX_PEERS_UDP6;
173 if (numwant > max_peers)
174 numwant = max_peers;
175
176 ws->reply = ws->outbuf + 8;
177 ws->reply_size = add_peer_to_torrent_and_return_peers(FLAG_UDP, ws, numwant);
178
179 if (!ws->reply_size) {
180 const char err_unauth[] = "Requested download is not authorized for use with this tracker.";
181 size_t s = sizeof(err_unauth);
182 outpacket[0] = htonl(3);
183 memcpy(&outpacket[2], &err_unauth[0], s);
184 socket_send(serversocket, ws->outbuf, 8 + s, remoteip, remoteport);
185 return 1;
186 }
187 ws->reply_size += 8;
188 }
189
190 socket_send(serversocket, ws->outbuf, ws->reply_size, remoteip, remoteport);
191 stats_issue_event(ws, EVENT_ANNOUNCE, FLAG_UDP, ws->reply_size);
192 break;
193
194 case 2: /* This is a scrape action */
195 outpacket[0] = htonl(2); /* scrape action */
196 outpacket[1] = inpacket[12 / 4];
197
198 for (scrape_count = 0; (scrape_count * 20 < byte_count - 16) && (scrape_count <= 74); scrape_count++)
199 return_udp_scrape_for_torrent(*(ot_hash *)(((char *)inpacket) + 16 + 20 * scrape_count), ((char *)outpacket) + 8 + 12 * scrape_count);
200
201 socket_send(serversocket, ws->outbuf, 8 + 12 * scrape_count, remoteip, remoteport);
202 stats_issue_event(ws, EVENT_SCRAPE, FLAG_UDP, scrape_count);
203 break;
204 }
205 return 1;
206}
207
208static void udp_run(struct ot_worker *me) {
209 while (g_opentracker_running)
210 handle_udp6(me->sock, &me->ws);
211}
212
213void udp_init(void) {
214 if (!g_rijndael_round_key[0])
215 udp_generate_rijndael_round_key();
216}
217
218void udp_worker_install(int64_t sock) {
219#ifdef _DEBUG
220 fprintf(stderr, "Installing a worker on udp socket %ld\n", (unsigned long)sock);
221#endif
222 socket_set_blocking((int)sock, 1);
223
224 if (worker_start(udp_run, sock) == -1)
225 fprintf(stderr, "Warning: could not install a udp worker, this socket stays with the workers already running.\n");
226}