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