Disable forced gzip by default
[opentracker.git] / ot_udp.c
blob97ccd38a7363125923f9ea6ddeb9e51916dbd04e
1 /* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
4 $id$ */
6 /* System */
7 #include <arpa/inet.h>
8 #include <pthread.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
13 /* Libowfat */
14 #include "io.h"
15 #include "ip6.h"
16 #include "socket.h"
18 /* Opentracker */
19 #include "ot_rijndael.h"
20 #include "ot_stats.h"
21 #include "ot_udp.h"
22 #include "trackerlogic.h"
24 #if 0
25 static const uint8_t g_static_connid[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff };
26 #endif
27 static uint32_t g_rijndael_round_key[44] = {0};
28 static uint32_t g_key_of_the_hour[2] = {0};
29 static ot_time g_hour_of_the_key;
31 static void udp_generate_rijndael_round_key() {
32 uint32_t key[16];
33 #ifdef WANT_ARC4RANDOM
34 arc4random_buf(&key[0], sizeof(key));
35 #else
36 key[0] = random();
37 key[1] = random();
38 key[2] = random();
39 key[3] = random();
40 #endif
41 rijndaelKeySetupEnc128(g_rijndael_round_key, (uint8_t *)key);
43 #ifdef WANT_ARC4RANDOM
44 g_key_of_the_hour[0] = arc4random();
45 #else
46 g_key_of_the_hour[0] = random();
47 #endif
48 g_hour_of_the_key = g_now_minutes;
51 /* Generate current and previous connection id for ip */
52 static void udp_make_connectionid(uint32_t connid[2], const ot_ip6 remoteip, int age) {
53 uint32_t plain[4], crypt[4];
54 int i;
55 if (g_now_minutes + 60 > g_hour_of_the_key) {
56 g_hour_of_the_key = g_now_minutes;
57 g_key_of_the_hour[1] = g_key_of_the_hour[0];
58 #ifdef WANT_ARC4RANDOM
59 g_key_of_the_hour[0] = arc4random();
60 #else
61 g_key_of_the_hour[0] = random();
62 #endif
65 memcpy(plain, remoteip, sizeof(plain));
66 for (i = 0; i < 4; ++i)
67 plain[i] ^= g_key_of_the_hour[age];
68 rijndaelEncrypt128(g_rijndael_round_key, (uint8_t *)remoteip, (uint8_t *)crypt);
69 connid[0] = crypt[0] ^ crypt[1];
70 connid[1] = crypt[2] ^ crypt[3];
73 /* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */
74 int handle_udp6(int64 serversocket, struct ot_workstruct *ws) {
75 ot_ip6 remoteip;
76 uint32_t *inpacket = (uint32_t *)ws->inbuf;
77 uint32_t *outpacket = (uint32_t *)ws->outbuf;
78 uint32_t left, event, scopeid;
79 uint32_t connid[2];
80 uint32_t action;
81 uint16_t port, remoteport;
82 size_t byte_count, scrape_count;
84 byte_count = socket_recv6(serversocket, ws->inbuf, G_INBUF_SIZE, remoteip, &remoteport, &scopeid);
85 if (!byte_count)
86 return 0;
88 stats_issue_event(EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip);
89 stats_issue_event(EVENT_READ, FLAG_UDP, byte_count);
91 /* Minimum udp tracker packet size, also catches error */
92 if (byte_count < 16)
93 return 1;
95 /* Get action to take. Ignore error messages and broken packets */
96 action = ntohl(inpacket[2]);
97 if (action > 2)
98 return 1;
100 /* Generate the connection id we give out and expect to and from
101 the requesting ip address, this prevents udp spoofing */
102 udp_make_connectionid(connid, remoteip, 0);
104 /* Initialise hash pointer */
105 ws->hash = NULL;
106 ws->peer_id = NULL;
108 /* If action is not 0 (connect), then we expect the derived
109 connection id in first 64 bit */
110 if ((action > 0) && (inpacket[0] != connid[0] || inpacket[1] != connid[1])) {
111 /* If connection id does not match, try the one that was
112 valid in the previous hour. Only if this also does not
113 match, return an error packet */
114 udp_make_connectionid(connid, remoteip, 1);
115 if (inpacket[0] != connid[0] || inpacket[1] != connid[1]) {
116 const size_t s = sizeof("Connection ID missmatch.");
117 outpacket[0] = htonl(3);
118 outpacket[1] = inpacket[3];
119 memcpy(&outpacket[2], "Connection ID missmatch.", s);
120 socket_send6(serversocket, ws->outbuf, 8 + s, remoteip, remoteport, 0);
121 stats_issue_event(EVENT_CONNID_MISSMATCH, FLAG_UDP, 8 + s);
122 return 1;
126 switch (action) {
127 case 0: /* This is a connect action */
128 /* look for udp bittorrent magic id */
129 if ((ntohl(inpacket[0]) != 0x00000417) || (ntohl(inpacket[1]) != 0x27101980))
130 return 1;
132 outpacket[0] = 0;
133 outpacket[1] = inpacket[3];
134 outpacket[2] = connid[0];
135 outpacket[3] = connid[1];
137 socket_send6(serversocket, ws->outbuf, 16, remoteip, remoteport, 0);
138 stats_issue_event(EVENT_CONNECT, FLAG_UDP, 16);
139 break;
140 case 1: /* This is an announce action */
141 /* Minimum udp announce packet size */
142 if (byte_count < 98)
143 return 1;
145 /* We do only want to know, if it is zero */
146 left = inpacket[64 / 4] | inpacket[68 / 4];
148 event = ntohl(inpacket[80 / 4]);
149 port = *(uint16_t *)(((char *)inpacket) + 96);
150 ws->hash = (ot_hash *)(((char *)inpacket) + 16);
152 OT_SETIP(ws->peer, remoteip);
153 OT_SETPORT(ws->peer, &port);
154 OT_PEERFLAG(ws->peer) = 0;
156 switch (event) {
157 case 1:
158 OT_PEERFLAG(ws->peer) |= PEER_FLAG_COMPLETED;
159 break;
160 case 3:
161 OT_PEERFLAG(ws->peer) |= PEER_FLAG_STOPPED;
162 break;
163 default:
164 break;
167 if (!left)
168 OT_PEERFLAG(ws->peer) |= PEER_FLAG_SEEDING;
170 outpacket[0] = htonl(1); /* announce action */
171 outpacket[1] = inpacket[12 / 4];
173 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_STOPPED) { /* Peer is gone. */
174 ws->reply = ws->outbuf;
175 ws->reply_size = remove_peer_from_torrent(FLAG_UDP, ws);
176 } else {
177 /* Limit amount of peers to OT_MAX_PEERS_UDP */
178 uint32_t numwant = ntohl(inpacket[92 / 4]);
179 size_t max_peers = ip6_isv4mapped(remoteip) ? OT_MAX_PEERS_UDP4 : OT_MAX_PEERS_UDP6;
180 if (numwant > max_peers)
181 numwant = max_peers;
183 ws->reply = ws->outbuf + 8;
184 ws->reply_size = 8 + add_peer_to_torrent_and_return_peers(FLAG_UDP, ws, numwant);
187 socket_send6(serversocket, ws->outbuf, ws->reply_size, remoteip, remoteport, 0);
188 stats_issue_event(EVENT_ANNOUNCE, FLAG_UDP, ws->reply_size);
189 break;
191 case 2: /* This is a scrape action */
192 outpacket[0] = htonl(2); /* scrape action */
193 outpacket[1] = inpacket[12 / 4];
195 for (scrape_count = 0; (scrape_count * 20 < byte_count - 16) && (scrape_count <= 74); scrape_count++)
196 return_udp_scrape_for_torrent(*(ot_hash *)(((char *)inpacket) + 16 + 20 * scrape_count), ((char *)outpacket) + 8 + 12 * scrape_count);
198 socket_send6(serversocket, ws->outbuf, 8 + 12 * scrape_count, remoteip, remoteport, 0);
199 stats_issue_event(EVENT_SCRAPE, FLAG_UDP, scrape_count);
200 break;
202 return 1;
205 static void *udp_worker(void *args) {
206 int64 sock = (int64)args;
207 struct ot_workstruct ws;
208 memset(&ws, 0, sizeof(ws));
210 ws.inbuf = malloc(G_INBUF_SIZE);
211 ws.outbuf = malloc(G_OUTBUF_SIZE);
212 #ifdef _DEBUG_HTTPERROR
213 ws.debugbuf = malloc(G_DEBUGBUF_SIZE);
214 #endif
216 while (g_opentracker_running)
217 handle_udp6(sock, &ws);
219 free(ws.inbuf);
220 free(ws.outbuf);
221 #ifdef _DEBUG_HTTPERROR
222 free(ws.debugbuf);
223 #endif
224 return NULL;
227 void udp_init(int64 sock, unsigned int worker_count) {
228 pthread_t thread_id;
229 if (!g_rijndael_round_key[0])
230 udp_generate_rijndael_round_key();
231 #ifdef _DEBUG
232 fprintf(stderr, " installing %d workers on udp socket %ld\n", worker_count, (unsigned long)sock);
233 #endif
234 while (worker_count--)
235 pthread_create(&thread_id, NULL, udp_worker, (void *)sock);