Make chunked transfers use gzip also
[opentracker.git] / ot_udp.c
blobc32a7e2f51a3c8ff4c93c0ab3cb7706413f31188
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 <stdlib.h>
8 #include <pthread.h>
9 #include <string.h>
10 #include <arpa/inet.h>
11 #include <stdio.h>
13 /* Libowfat */
14 #include "socket.h"
15 #include "io.h"
16 #include "ip6.h"
18 /* Opentracker */
19 #include "trackerlogic.h"
20 #include "ot_udp.h"
21 #include "ot_stats.h"
22 #include "ot_rijndael.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 ) plain[i] ^= g_key_of_the_hour[age];
67 rijndaelEncrypt128( g_rijndael_round_key, (uint8_t*)remoteip, (uint8_t*)crypt );
68 connid[0] = crypt[0] ^ crypt[1];
69 connid[1] = crypt[2] ^ crypt[3];
72 /* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */
73 int handle_udp6( int64 serversocket, struct ot_workstruct *ws ) {
74 ot_ip6 remoteip;
75 uint32_t *inpacket = (uint32_t*)ws->inbuf;
76 uint32_t *outpacket = (uint32_t*)ws->outbuf;
77 uint32_t left, event, scopeid;
78 uint32_t connid[2];
79 uint32_t action;
80 uint16_t port, remoteport;
81 size_t byte_count, scrape_count;
83 byte_count = socket_recv6( serversocket, ws->inbuf, G_INBUF_SIZE, remoteip, &remoteport, &scopeid );
84 if( !byte_count ) return 0;
86 stats_issue_event( EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip );
87 stats_issue_event( EVENT_READ, FLAG_UDP, byte_count );
89 /* Minimum udp tracker packet size, also catches error */
90 if( byte_count < 16 )
91 return 1;
93 /* Get action to take. Ignore error messages and broken packets */
94 action = ntohl( inpacket[2] );
95 if( action > 2 )
96 return 1;
98 /* Generate the connection id we give out and expect to and from
99 the requesting ip address, this prevents udp spoofing */
100 udp_make_connectionid( connid, remoteip, 0 );
102 /* Initialise hash pointer */
103 ws->hash = NULL;
104 ws->peer_id = NULL;
106 /* If action is not 0 (connect), then we expect the derived
107 connection id in first 64 bit */
108 if( ( action > 0 ) && ( inpacket[0] != connid[0] || inpacket[1] != connid[1] ) ) {
109 /* If connection id does not match, try the one that was
110 valid in the previous hour. Only if this also does not
111 match, return an error packet */
112 udp_make_connectionid( connid, remoteip, 1 );
113 if( inpacket[0] != connid[0] || inpacket[1] != connid[1] ) {
114 const size_t s = sizeof( "Connection ID missmatch." );
115 outpacket[0] = htonl( 3 ); outpacket[1] = inpacket[3];
116 memcpy( &outpacket[2], "Connection ID missmatch.", s );
117 socket_send6( serversocket, ws->outbuf, 8 + s, remoteip, remoteport, 0 );
118 stats_issue_event( EVENT_CONNID_MISSMATCH, FLAG_UDP, 8 + s );
119 return 1;
123 switch( action ) {
124 case 0: /* This is a connect action */
125 /* look for udp bittorrent magic id */
126 if( (ntohl(inpacket[0]) != 0x00000417) || (ntohl(inpacket[1]) != 0x27101980) )
127 return 1;
129 outpacket[0] = 0;
130 outpacket[1] = inpacket[3];
131 outpacket[2] = connid[0];
132 outpacket[3] = connid[1];
134 socket_send6( serversocket, ws->outbuf, 16, remoteip, remoteport, 0 );
135 stats_issue_event( EVENT_CONNECT, FLAG_UDP, 16 );
136 break;
137 case 1: /* This is an announce action */
138 /* Minimum udp announce packet size */
139 if( byte_count < 98 )
140 return 1;
142 /* We do only want to know, if it is zero */
143 left = inpacket[64/4] | inpacket[68/4];
145 event = ntohl( inpacket[80/4] );
146 port = *(uint16_t*)( ((char*)inpacket) + 96 );
147 ws->hash = (ot_hash*)( ((char*)inpacket) + 16 );
149 OT_SETIP( ws->peer, remoteip );
150 OT_SETPORT( ws->peer, &port );
151 OT_PEERFLAG( ws->peer ) = 0;
153 switch( event ) {
154 case 1: OT_PEERFLAG( ws->peer ) |= PEER_FLAG_COMPLETED; break;
155 case 3: OT_PEERFLAG( ws->peer ) |= PEER_FLAG_STOPPED; break;
156 default: break;
159 if( !left )
160 OT_PEERFLAG( ws->peer ) |= PEER_FLAG_SEEDING;
162 outpacket[0] = htonl( 1 ); /* announce action */
163 outpacket[1] = inpacket[12/4];
165 if( OT_PEERFLAG( ws->peer ) & PEER_FLAG_STOPPED ) { /* Peer is gone. */
166 ws->reply = ws->outbuf;
167 ws->reply_size = remove_peer_from_torrent( FLAG_UDP, ws );
168 } else {
169 /* Limit amount of peers to OT_MAX_PEERS_UDP */
170 uint32_t numwant = ntohl( inpacket[92/4] );
171 size_t max_peers = ip6_isv4mapped(remoteip) ? OT_MAX_PEERS_UDP4 : OT_MAX_PEERS_UDP6;
172 if (numwant > max_peers) numwant = max_peers;
174 ws->reply = ws->outbuf + 8;
175 ws->reply_size = 8 + add_peer_to_torrent_and_return_peers( FLAG_UDP, ws, numwant );
178 socket_send6( serversocket, ws->outbuf, ws->reply_size, remoteip, remoteport, 0 );
179 stats_issue_event( EVENT_ANNOUNCE, FLAG_UDP, ws->reply_size );
180 break;
182 case 2: /* This is a scrape action */
183 outpacket[0] = htonl( 2 ); /* scrape action */
184 outpacket[1] = inpacket[12/4];
186 for( scrape_count = 0; ( scrape_count * 20 < byte_count - 16) && ( scrape_count <= 74 ); scrape_count++ )
187 return_udp_scrape_for_torrent( *(ot_hash*)( ((char*)inpacket) + 16 + 20 * scrape_count ), ((char*)outpacket) + 8 + 12 * scrape_count );
189 socket_send6( serversocket, ws->outbuf, 8 + 12 * scrape_count, remoteip, remoteport, 0 );
190 stats_issue_event( EVENT_SCRAPE, FLAG_UDP, scrape_count );
191 break;
193 return 1;
196 static void* udp_worker( void * args ) {
197 int64 sock = (int64)args;
198 struct ot_workstruct ws;
199 memset( &ws, 0, sizeof(ws) );
201 ws.inbuf=malloc(G_INBUF_SIZE);
202 ws.outbuf=malloc(G_OUTBUF_SIZE);
203 #ifdef _DEBUG_HTTPERROR
204 ws.debugbuf=malloc(G_DEBUGBUF_SIZE);
205 #endif
207 while( g_opentracker_running )
208 handle_udp6( sock, &ws );
210 free( ws.inbuf );
211 free( ws.outbuf );
212 #ifdef _DEBUG_HTTPERROR
213 free( ws.debugbuf );
214 #endif
215 return NULL;
218 void udp_init( int64 sock, unsigned int worker_count ) {
219 pthread_t thread_id;
220 if( !g_rijndael_round_key[0] )
221 udp_generate_rijndael_round_key();
222 #ifdef _DEBUG
223 fprintf( stderr, " installing %d workers on udp socket %ld\n", worker_count, (unsigned long)sock );
224 #endif
225 while( worker_count-- )
226 pthread_create( &thread_id, NULL, udp_worker, (void *)sock );
229 const char *g_version_udp_c = "$Source$: $Revision$\n";