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