1 /* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
10 #include <arpa/inet.h>
19 #include "trackerlogic.h"
22 #include "ot_rijndael.h"
25 static const uint8_t g_static_connid
[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff };
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() {
33 #ifdef WANT_ARC4RANDOM
34 arc4random_buf(&key
[0], sizeof(key
));
41 rijndaelKeySetupEnc128( g_rijndael_round_key
, (uint8_t*)key
);
43 #ifdef WANT_ARC4RANDOM
44 g_key_of_the_hour
[0] = arc4random();
46 g_key_of_the_hour
[0] = random();
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];
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();
61 g_key_of_the_hour
[0] = random();
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
) {
75 uint32_t *inpacket
= (uint32_t*)ws
->inbuf
;
76 uint32_t *outpacket
= (uint32_t*)ws
->outbuf
;
77 uint32_t left
, event
, scopeid
;
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 */
93 /* Get action to take. Ignore error messages and broken packets */
94 action
= ntohl( inpacket
[2] );
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 */
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
);
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) )
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 );
137 case 1: /* This is an announce action */
138 /* Minimum udp announce packet size */
139 if( byte_count
< 98 )
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;
154 case 1: OT_PEERFLAG( ws
->peer
) |= PEER_FLAG_COMPLETED
; break;
155 case 3: OT_PEERFLAG( ws
->peer
) |= PEER_FLAG_STOPPED
; break;
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
);
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
);
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
);
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
);
207 while( g_opentracker_running
)
208 handle_udp6( sock
, &ws
);
212 #ifdef _DEBUG_HTTPERROR
218 void udp_init( int64 sock
, unsigned int worker_count
) {
220 if( !g_rijndael_round_key
[0] )
221 udp_generate_rijndael_round_key();
223 fprintf( stderr
, " installing %d workers on udp socket %ld\n", worker_count
, (unsigned long)sock
);
225 while( worker_count
-- )
226 pthread_create( &thread_id
, NULL
, udp_worker
, (void *)sock
);
229 const char *g_version_udp_c
= "$Source$: $Revision$\n";