1 /*
2 * nut-core.c - gnutella core implementation
3 *
4 * Copyright (C) 2011-2013 Thien-Thi Nguyen
5 * Copyright (C) 2000, 2002 Stefan Jahn <stefan@lkcc.org>
6 *
7 * This is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3, or (at your option)
10 * any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this package. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27
28 #ifndef __MINGW32__
29 # include <sys/types.h>
30 # include <sys/socket.h>
31 #endif
32
33 #include "networking-headers.h"
34 #include "libserveez.h"
35 #include "gnutella.h"
36 #include "nut-core.h"
37
38 /*
39 * Gnutella client structure creator.
40 */
41 nut_client_t *
nut_create_client(void)42 nut_create_client (void)
43 {
44 return svz_calloc (sizeof (nut_client_t));
45 }
46
47 /*
48 * Parses a `host:port' combination from the given character string
49 * @var{addr} and stores the @var{port} in network byte order. If the `:port'
50 * part is not given, a default port is delivered. Returns NULL if the
51 * string is invalid and otherwise the hostname. The caller is responsible
52 * for freeing the returned string.
53 */
54 char *
nut_parse_host(char * addr,in_port_t * port)55 nut_parse_host (char *addr, in_port_t *port)
56 {
57 char *p, *host;
58
59 /* create a local copy of the given address string */
60 p = host = svz_strdup (addr);
61 if (!host)
62 {
63 /* address string was NULL or empty */
64 return NULL;
65 }
66
67 /* find separating colon */
68 while (*p != ':' && *p)
69 p++;
70 if (*p)
71 {
72 *p = '\0';
73 p++;
74 }
75 else
76 p = NULL;
77
78 /* convert and store both of the parsed values */
79 *port = htons (p
80 ? svz_atoi (p)
81 : NUT_PORT);
82 return host;
83 }
84
85 /*
86 * This routine parses a `a.b.c.d:port' combination from the given
87 * character string @var{addr} and stores both of the values in @var{ip}
88 * and @var{port} in network byte order. If `:port' is not given, a default
89 * port is delivered. Returns -1 on errors and otherwise zero.
90 */
91 int
nut_parse_addr(char * addr,in_addr_t * ip,in_port_t * port)92 nut_parse_addr (char *addr, in_addr_t *ip, in_port_t *port)
93 {
94 char *p, *colon, *host;
95
96 /* create a local copy of the given address string */
97 p = host = svz_strdup (addr);
98 if (!host)
99 {
100 /* address string was NULL or empty */
101 return -1;
102 }
103
104 /* validate IP characters */
105 while (((*p >= '0' && *p <= '9') || *p == '.') && *p)
106 p++;
107 if ((*p < '0' || *p > '9') && *p != '.' && *p && *p != ':')
108 {
109 svz_free (host);
110 return -1;
111 }
112
113 /* find separating colon */
114 colon = p;
115 while (*colon != ':' && *colon)
116 colon++;
117 if (*colon)
118 {
119 *colon = '\0';
120 colon++;
121 }
122 else
123 colon = NULL;
124
125 /* convert and store both of the parsed values */
126 *ip = inet_addr (host);
127 *port = htons (colon
128 ? svz_atoi (colon)
129 : NUT_PORT);
130 svz_free (host);
131 return 0;
132 }
133
134 /*
135 * This function creates a hash key for a given IP and PORT information
136 * for the host catcher hash. Both values must be given in network byte
137 * order.
138 */
139 char *
nut_client_key(in_addr_t ip,in_port_t port)140 nut_client_key (in_addr_t ip, in_port_t port)
141 {
142 static char key[32];
143
144 sprintf (key, "%s:%u", svz_inet_ntoa (ip), ntohs (port));
145 return key;
146 }
147
148 /* These definitions are for the GUID creating functions in Win32. */
149 #ifdef __MINGW32__
150 CreateGuidProc CreateGuid = NULL;
151 HMODULE oleHandle = NULL;
152 #endif /* __MINGW32__ */
153
154 /*
155 * This routine randomly calculates a Globally Unique Identifier (GUID)
156 * and stores it in the given argument.
157 */
158 void
nut_calc_guid(uint8_t * guid)159 nut_calc_guid (uint8_t *guid)
160 {
161 int n;
162
163 #ifdef __MINGW32__
164 if (CreateGuid != NULL)
165 {
166 CreateGuid (guid);
167 return;
168 }
169 else
170 #endif /* __MINGW32__ */
171
172 for (n = 0; n < NUT_GUID_SIZE; n++)
173 {
174 /* guid[n] = 256 * rand () / RAND_MAX; */
175 guid[n] = (uint8_t) ((rand () >> 1) & 0xff);
176 }
177 }
178
179 /*
180 * The following routine delivers a text representation of the given
181 * GUID. The format is taken from he M$ headers.
182 */
183 char *
nut_print_guid(uint8_t * guid)184 nut_print_guid (uint8_t *guid)
185 {
186 static char id[NUT_GUID_SIZE * 2 + 4];
187
188 sprintf (id,
189 "%02X%02X%02X%02X-"
190 "%02X%02X-"
191 "%02X%02X-"
192 "%02X%02X%02X%02X%02X%02X%02X%02X",
193 guid[0], guid[1], guid[2], guid[3],
194 guid[4], guid[5],
195 guid[6], guid[7],
196 guid[8], guid[9], guid[10], guid[11],
197 guid[12], guid[13], guid[14], guid[15]);
198
199 return id;
200 }
201
202 char *
nut_text_guid(uint8_t * guid)203 nut_text_guid (uint8_t *guid)
204 {
205 static char id[NUT_GUID_SIZE * 2 + 1];
206 char *w;
207 int n;
208
209 for (n = 0, w = id; n < NUT_GUID_SIZE; n++, w += 2)
210 sprintf (w, "%02X", guid[n]);
211 return id;
212 }
213
214 /*
215 * Convert gnutella header to binary data and back.
216 */
217 nut_header_t *
nut_get_header(uint8_t * data)218 nut_get_header (uint8_t *data)
219 {
220 static nut_header_t hdr;
221 unsigned int uint32;
222
223 memcpy (hdr.id, data, NUT_GUID_SIZE);
224 data += NUT_GUID_SIZE;
225 hdr.function = *data++;
226 hdr.ttl = *data++;
227 hdr.hop = *data++;
228 memcpy (&uint32, data, SIZEOF_UINT32);
229 hdr.length = ltohl (uint32);
230 return (&hdr);
231 }
232
233 uint8_t *
nut_put_header(nut_header_t * hdr)234 nut_put_header (nut_header_t *hdr)
235 {
236 static uint8_t buffer[SIZEOF_NUT_HEADER];
237 uint8_t *data = buffer;
238 unsigned int uint32;
239
240 memcpy (data, hdr->id, NUT_GUID_SIZE);
241 data += NUT_GUID_SIZE;
242 *data++ = hdr->function;
243 *data++ = hdr->ttl;
244 *data++ = hdr->hop;
245 uint32 = htoll (hdr->length);
246 memcpy (data, &uint32, SIZEOF_UINT32);
247 return buffer;
248 }
249
250 /*
251 * Convert gnutella ping response to binary data and back.
252 */
253 nut_pong_t *
nut_get_pong(uint8_t * data)254 nut_get_pong (uint8_t *data)
255 {
256 static nut_pong_t reply;
257 uint16_t uint16;
258 unsigned int uint32;
259
260 memcpy (&uint16, data, SIZEOF_UINT16);
261 reply.port = ltons (uint16);
262 data += SIZEOF_UINT16;
263 memcpy (&reply.ip, data, SIZEOF_UINT32);
264 data += SIZEOF_UINT32;
265 memcpy (&uint32, data, SIZEOF_UINT32);
266 reply.files = ltohl (uint32);
267 data += SIZEOF_UINT32;
268 memcpy (&uint32, data, SIZEOF_UINT32);
269 reply.size = ltohl (uint32);
270 return (&reply);
271 }
272
273 uint8_t *
nut_put_pong(nut_pong_t * reply)274 nut_put_pong (nut_pong_t *reply)
275 {
276 static uint8_t buffer[SIZEOF_NUT_PONG];
277 uint8_t *data = buffer;
278 uint16_t uint16;
279 unsigned int uint32;
280
281 uint16 = ntols (reply->port);
282 memcpy (data, &uint16, SIZEOF_UINT16);
283 data += SIZEOF_UINT16;
284 memcpy (data, &reply->ip, SIZEOF_UINT32);
285 data += SIZEOF_UINT32;
286 uint32 = htoll (reply->files);
287 memcpy (data, &uint32, SIZEOF_UINT32);
288 data += SIZEOF_UINT32;
289 uint32 = htoll (reply->size);
290 memcpy (data, &uint32, SIZEOF_UINT32);
291 return buffer;
292 }
293
294 /*
295 * Convert gnutella search query to binary data and back.
296 */
297 nut_query_t *
nut_get_query(uint8_t * data)298 nut_get_query (uint8_t *data)
299 {
300 static nut_query_t query;
301 uint16_t uint16;
302
303 memcpy (&uint16, data, SIZEOF_UINT16);
304 query.speed = ltohs (uint16);
305 return (&query);
306 }
307
308 uint8_t *
nut_put_query(nut_query_t * query)309 nut_put_query (nut_query_t *query)
310 {
311 static uint8_t buffer[SIZEOF_NUT_QUERY];
312 uint8_t *data = buffer;
313 uint16_t uint16;
314
315 uint16 = htols (query->speed);
316 memcpy (data, &uint16, SIZEOF_UINT16);
317 return buffer;
318 }
319
320 /*
321 * Convert gnutella file records to binary data and back.
322 */
323 nut_record_t *
nut_get_record(uint8_t * data)324 nut_get_record (uint8_t *data)
325 {
326 static nut_record_t record;
327 unsigned int uint32;
328
329 memcpy (&uint32, data, SIZEOF_UINT32);
330 record.index = ltohl (uint32);
331 data += SIZEOF_UINT32;
332 memcpy (&uint32, data, SIZEOF_UINT32);
333 record.size = ltohl (uint32);
334 return (&record);
335 }
336
337 uint8_t *
nut_put_record(nut_record_t * record)338 nut_put_record (nut_record_t *record)
339 {
340 static uint8_t buffer[SIZEOF_NUT_RECORD];
341 uint8_t *data = buffer;
342 unsigned int uint32;
343
344 uint32 = htoll (record->index);
345 memcpy (data, &uint32, SIZEOF_UINT32);
346 data += SIZEOF_UINT32;
347 uint32 = htoll (record->size);
348 memcpy (data, &uint32, SIZEOF_UINT32);
349 return buffer;
350 }
351
352 /*
353 * Convert gnutella query hits to binary data and back.
354 */
355 nut_reply_t *
nut_get_reply(uint8_t * data)356 nut_get_reply (uint8_t *data)
357 {
358 static nut_reply_t reply;
359 uint16_t uint16;
360
361 reply.records = *data++;
362 memcpy (&uint16, data, SIZEOF_UINT16);
363 reply.port = ltons (uint16);
364 data += SIZEOF_UINT16;
365 memcpy (&reply.ip, data, SIZEOF_UINT32);
366 data += SIZEOF_UINT32;
367 memcpy (&uint16, data, SIZEOF_UINT16);
368 reply.speed = ltohs (uint16);
369 return (&reply);
370 }
371
372 uint8_t *
nut_put_reply(nut_reply_t * reply)373 nut_put_reply (nut_reply_t *reply)
374 {
375 static uint8_t buffer[SIZEOF_NUT_REPLY];
376 uint8_t *data = buffer;
377 uint16_t uint16;
378
379 *data++ = reply->records;
380 uint16 = ntols (reply->port);
381 memcpy (data, &uint16, SIZEOF_UINT16);
382 data += SIZEOF_UINT16;
383 memcpy (data, &reply->ip, SIZEOF_UINT32);
384 data += SIZEOF_UINT32;
385 uint16 = htols (reply->speed);
386 memcpy (data, &uint16, SIZEOF_UINT16);
387 return buffer;
388 }
389
390 /*
391 * Convert gnutella push request to binary data and back.
392 */
393 nut_push_t *
nut_get_push(uint8_t * data)394 nut_get_push (uint8_t *data)
395 {
396 static nut_push_t push;
397 unsigned int uint32;
398 uint16_t uint16;
399
400 memcpy (push.id, data, NUT_GUID_SIZE);
401 data += NUT_GUID_SIZE;
402 memcpy (&uint32, data, SIZEOF_UINT32);
403 push.index = ltohl (uint32);
404 data += SIZEOF_UINT32;
405 memcpy (&push.ip, data, SIZEOF_UINT32);
406 data += SIZEOF_UINT32;
407 memcpy (&uint16, data, SIZEOF_UINT16);
408 push.port = ltons (uint16);
409 return (&push);
410 }
411
412 uint8_t *
nut_put_push(nut_push_t * push)413 nut_put_push (nut_push_t *push)
414 {
415 static uint8_t buffer[SIZEOF_NUT_PUSH];
416 uint8_t *data = buffer;
417 unsigned int uint32;
418 uint16_t uint16;
419
420 memcpy (data, push->id, NUT_GUID_SIZE);
421 data += NUT_GUID_SIZE;
422 uint32 = htoll (push->index);
423 memcpy (data, &uint32, SIZEOF_UINT32);
424 data += SIZEOF_UINT32;
425 memcpy (data, &push->ip, SIZEOF_UINT32);
426 data += SIZEOF_UINT32;
427 uint16 = ntols (push->port);
428 memcpy (data, &uint16, SIZEOF_UINT16);
429 return buffer;
430 }
431
432 /*
433 * Canonizes a given filename and converts it to something printable.
434 */
435 void
nut_canonize_file(char * file)436 nut_canonize_file (char *file)
437 {
438 while (*file)
439 {
440 if (!isalnum ((uint8_t) *file) && !isprint ((uint8_t) *file))
441 *file = '_';
442 file++;
443 }
444 }
445
446 /*
447 * This routine parses a given gnutella (HTTP) header for certain
448 * properties and delivers either a property value which must be
449 * ‘svz_free’d afterwards or NULL.
450 */
451 char *
nut_parse_property(char * header,int len,char * property)452 nut_parse_property (char *header, int len, char *property)
453 {
454 char *h = header, *p = property, *value, *end = header + len;
455
456 while (h < end)
457 {
458 /* find beginning of property */
459 while (h < end && tolower (*h) != tolower (*p))
460 h++;
461 if (h >= end)
462 return NULL;
463
464 /* compare whole property name */
465 header = h;
466 while (h < end && *p && tolower (*h++) == tolower (*p++));
467 if (h >= end)
468 return NULL;
469 if (*p)
470 {
471 /* fallback to property's first character */
472 h = header + 1;
473 p = property;
474 continue;
475 }
476
477 /* parse property value */
478 while (h < end && *h++ == ' ');
479 if (h >= end || *(h - 1) != ':')
480 return NULL;
481 while (h < end && *h == ' ')
482 h++;
483 header = h;
484 while (h < end && *h != '\r' && *h != '\n')
485 h++;
486 if (h >= end || h == header)
487 return NULL;
488
489 /* copy property value */
490 len = h - header;
491 value = svz_malloc (len + 1);
492 memcpy (value, header, len);
493 value[len] = '\0';
494 return value;
495 }
496 return NULL;
497 }
498