- use proper signedness
[oweals/gnunet.git] / src / transport / plugin_transport_http_common.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file transport/plugin_transport_http_common.c
23  * @brief functionality shared by http client and server transport service plugin
24  * @author Matthias Wachs
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_transport_plugin.h"
29 #include "plugin_transport_http_common.h"
30
31 struct SplittedHTTPAddress
32 {
33         char *protocol;
34         char *host;
35         char *path;
36         int port;
37 };
38
39
40 static void
41 http_clean_splitted (struct SplittedHTTPAddress *spa)
42 {
43   if (NULL != spa)
44   {
45     GNUNET_free_non_null (spa->protocol);
46     GNUNET_free_non_null (spa->host);
47     GNUNET_free_non_null (spa->path);
48     GNUNET_free_non_null (spa);
49   }
50 }
51
52 struct SplittedHTTPAddress *
53 http_split_address (const char * addr)
54 {
55         struct SplittedHTTPAddress  *sp;
56         char *src = GNUNET_strdup (addr);
57         char *protocol_start = NULL;
58         char *host_start = NULL;
59         char *v6_end = NULL;
60         char *port_start = NULL;
61         char *path_start = NULL;
62         protocol_start = src;
63         sp = GNUNET_new (struct SplittedHTTPAddress);
64
65         /* Address string consists of protocol://host[:port]path*/
66
67         host_start = strstr (src, "://");
68         if (NULL == host_start)
69         {
70                         GNUNET_free (src);
71                         GNUNET_free (sp);
72                         return NULL;
73         }
74
75         host_start[0] = '\0';
76         sp->protocol = GNUNET_strdup (protocol_start);
77
78         host_start += strlen ("://");
79         if (strlen (host_start) == 0)
80         {
81                         GNUNET_free (src);
82                         GNUNET_free (sp->protocol);
83                         GNUNET_free (sp);
84                         return NULL;
85         }
86
87         /* Find path start */
88         path_start = strchr (host_start, '/');
89         if (NULL != path_start)
90         {
91                         sp->path = GNUNET_strdup (path_start);
92                         path_start[0] = '\0';
93         }
94         else
95                 sp->path = GNUNET_strdup ("");
96
97         if (strlen(host_start) < 1)
98         {
99                         GNUNET_free (src);
100                         GNUNET_free (sp->protocol);
101                         GNUNET_free (sp->path);
102                         GNUNET_free (sp);
103                         return NULL;
104         }
105
106         if (NULL != (port_start = strrchr (host_start, ':')))
107         {
108                         /* *We COULD have a port, but also an IPv6 address! */
109                         if (NULL != (v6_end = strchr(host_start, ']')))
110                         {
111                                         if  (v6_end < port_start)
112                                         {
113                                                         /* IPv6 address + port */
114                                                         port_start[0] = '\0';
115                                                         port_start ++;
116                                                         sp->port = atoi (port_start);
117                                                         if ((0 == sp->port) || (65535 < sp->port))
118                                                         {
119                                                                 GNUNET_free (src);
120                                                                 GNUNET_free (sp->protocol);
121                                                                 GNUNET_free (sp->path);
122                                                                 GNUNET_free (sp);
123                                                                 return NULL;
124                                                         }
125                                         }
126                                         else
127                                         {
128                                                         /* IPv6 address + no port */
129                                                         if (0 == strcmp(sp->protocol, "https"))
130                                                                 sp->port = HTTPS_DEFAULT_PORT;
131                                                         else if (0 == strcmp(sp->protocol, "http"))
132                                                                 sp->port = HTTP_DEFAULT_PORT;
133                                         }
134                         }
135                         else
136                         {
137                                         /* No IPv6 address */
138                                         port_start[0] = '\0';
139                                         port_start ++;
140                                         sp->port = atoi (port_start);
141                                         if ((0 == sp->port) || (65535 < sp->port))
142                                         {
143                                                 GNUNET_free (src);
144                                                 GNUNET_free (sp->protocol);
145                                                 GNUNET_free (sp->path);
146                                                 GNUNET_free (sp);
147                                                 return NULL;
148                                         }
149                         }
150         }
151         else
152         {
153                 /* No ':' as port separator, default port for protocol */
154                 if (0 == strcmp(sp->protocol, "https"))
155                         sp->port = HTTPS_DEFAULT_PORT;
156                 else if (0 == strcmp(sp->protocol, "http"))
157                         sp->port = HTTP_DEFAULT_PORT;
158                 else
159                 {
160                                 GNUNET_break (0);
161                                 GNUNET_free (src);
162                                 GNUNET_free (sp->protocol);
163                                 GNUNET_free (sp->path);
164                                 GNUNET_free (sp);
165                                 return NULL;
166                 }
167         }
168         if (strlen (host_start) > 0)
169                         sp->host = GNUNET_strdup (host_start);
170         else
171         {
172                         GNUNET_break (0);
173                         GNUNET_free (src);
174                         GNUNET_free (sp->protocol);
175                         GNUNET_free (sp->path);
176                         GNUNET_free (sp);
177                         return NULL;
178         }
179         GNUNET_free (src);
180         return sp;
181 }
182
183 /**
184  * Convert the transports address to a nice, human-readable
185  * format.
186  *
187  * @param cls closure
188  * @param type name of the transport that generated the address
189  * @param addr one of the addresses of the host, NULL for the last address
190  *        the specific address format depends on the transport
191  * @param addrlen length of the address
192  * @param numeric should (IP) addresses be displayed in numeric form?
193  * @param timeout after how long should we give up?
194  * @param asc function to call on each string
195  * @param asc_cls closure for asc
196  */
197 void
198 http_common_plugin_address_pretty_printer (void *cls, const char *type,
199                                         const void *addr, size_t addrlen,
200                                         int numeric,
201                                         struct GNUNET_TIME_Relative timeout,
202                                         GNUNET_TRANSPORT_AddressStringCallback
203                                         asc, void *asc_cls)
204 {
205         const struct HttpAddress *address = addr;
206
207   if (NULL == http_common_plugin_address_to_string (NULL, (char *) type, address, addrlen))
208   {
209       asc (asc_cls, NULL);
210       return;
211   }
212   asc (asc_cls, http_common_plugin_address_to_string (NULL, (char *) type, address, addrlen));
213   asc (asc_cls, NULL);
214 }
215
216 const char *
217 http_common_plugin_address_to_url (void *cls, const void *addr, size_t addrlen)
218 {
219   static char rbuf[1024];
220         const struct HttpAddress *address = addr;
221   const char * addr_str;
222
223
224
225   if (NULL == addr)
226   {
227         GNUNET_break (0);
228     return NULL;
229   }
230   if (0 >= addrlen)
231   {
232         GNUNET_break (0);
233     return NULL;
234   }
235   if (addrlen != http_common_address_get_size (address))
236   {
237         GNUNET_break (0);
238     return NULL;
239   }
240   addr_str = (char *) &address[1];
241
242   if (addr_str[ntohl(address->urlen) -1] != '\0')
243     return NULL;
244
245   memcpy (rbuf, &address[1], ntohl(address->urlen));
246   return rbuf;
247 }
248
249 /**
250  * Function called for a quick conversion of the binary address to
251  * a numeric address.  Note that the caller must not free the
252  * address and that the next call to this function is allowed
253  * to override the address again.
254  *
255  * @param cls closure
256  * @param plugin the plugin
257  * @param addr binary address
258  * @param addrlen length of the address
259  * @return string representing the same address
260  */
261 const char *
262 http_common_plugin_address_to_string (void *cls, char *plugin, const void *addr, size_t addrlen)
263 {
264   static char rbuf[1024];
265         const struct HttpAddress *address = addr;
266   const char * addr_str;
267   char *res;
268
269   GNUNET_assert (NULL != plugin);
270
271   if (NULL == addr)
272       return NULL;
273   if (0 == addrlen)
274     return TRANSPORT_SESSION_INBOUND_STRING;
275   if (addrlen != http_common_address_get_size (address))
276         return NULL;
277   addr_str = (char *) &address[1];
278
279   if (addr_str[ntohl(address->urlen) -1] != '\0')
280     return NULL;
281   GNUNET_asprintf (&res, "%s.%u.%s", plugin, ntohl(address->options), &address[1]);
282   if (strlen(res) + 1 < 500)
283   {
284         memcpy (rbuf, res, strlen(res) + 1);
285         GNUNET_free (res);
286         return rbuf;
287   }
288   GNUNET_break (0);
289         GNUNET_free (res);
290         return NULL;
291 }
292
293 /**
294  * Function called to convert a string address to
295  * a binary address.
296  *
297  * @param cls closure ('struct Plugin*')
298  * @param addr string address
299  * @param addrlen length of the address
300  * @param buf location to store the buffer
301  *        If the function returns GNUNET_SYSERR, its contents are undefined.
302  * @param added length of created address
303  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
304  */
305 int
306 http_common_plugin_string_to_address (void *cls,
307                         const char *addr,
308                         uint16_t addrlen,
309                         void **buf,
310                         size_t *added)
311 {
312         struct HttpAddress *a;
313   char *address;
314   char *plugin;
315   char *optionstr;
316   size_t urlen;
317   uint32_t options;
318
319   /* Format protocol.options.address:port */
320   address = NULL;
321   plugin = NULL;
322   optionstr = NULL;
323   if ((NULL == addr) || (addrlen == 0))
324   {
325     GNUNET_break (0);
326     return GNUNET_SYSERR;
327   }
328   if ('\0' != addr[addrlen - 1])
329   {
330     GNUNET_break (0);
331     return GNUNET_SYSERR;
332   }
333   if (strlen (addr) != addrlen - 1)
334   {
335     GNUNET_break (0);
336     return GNUNET_SYSERR;
337   }
338   plugin = GNUNET_strdup (addr);
339   optionstr = strchr (plugin, '.');
340   if (NULL == optionstr)
341   {
342     GNUNET_break (0);
343     GNUNET_free (plugin);
344     return GNUNET_SYSERR;
345   }
346   optionstr[0] = '\0';
347   optionstr ++;
348   options = atol (optionstr); /* 0 on conversion error, that's ok */
349   address = strchr (optionstr, '.');
350   if (NULL == address)
351   {
352     GNUNET_break (0);
353     GNUNET_free (plugin);
354     return GNUNET_SYSERR;
355   }
356   address[0] = '\0';
357   address ++;
358   urlen = strlen (address) + 1;
359
360   a = GNUNET_malloc (sizeof (struct HttpAddress) + urlen);
361   a->options = htonl(options);
362   a->urlen = htonl(urlen);
363   memcpy (&a[1], address, urlen);
364
365   (*buf) = a;
366   (*added) = sizeof (struct HttpAddress) + urlen;
367   GNUNET_free (plugin);
368   return GNUNET_OK;
369 }
370
371 /**
372  * Create a HTTP address from a socketaddr
373  *
374  * @param protocol protocol
375  * @param addr sockaddr * address
376  * @param addrlen length of the address
377  * @return the HttpAddress
378  */
379 struct HttpAddress *
380 http_common_address_from_socket (const char *protocol, const struct sockaddr *addr, socklen_t addrlen)
381 {
382   struct HttpAddress *address = NULL;
383   char *res;
384   size_t len;
385
386   GNUNET_asprintf(&res, "%s://%s", protocol, GNUNET_a2s (addr, addrlen));
387   len = strlen (res)+1;
388
389   address = GNUNET_malloc (sizeof (struct HttpAddress) + len);
390   address->options = htonl (HTTP_OPTIONS_NONE);
391   address->urlen = htonl (len);
392   memcpy (&address[1], res, len);
393   GNUNET_free (res);
394
395   return address;
396 }
397
398 /**
399  * Create a socketaddr from a HTTP address
400  *
401  * @param addr sockaddr * address
402  * @param addrlen length of the address
403  * @param res the result:
404  * GNUNET_SYSERR, invalid input,
405  * GNUNET_YES: could convert to ip,
406  * GNUNET_NO: valid input but could not convert to ip (hostname?)
407  * @return the string
408  */
409 struct sockaddr *
410 http_common_socket_from_address (const void *addr, size_t addrlen, int *res)
411 {
412         const struct HttpAddress *ha;
413         struct SplittedHTTPAddress * spa;
414   struct sockaddr_storage *s;
415   (*res) = GNUNET_SYSERR;
416   char * to_conv;
417   size_t urlen;
418
419   ha = (const struct HttpAddress *) addr;
420   if (NULL == addr)
421   {
422     GNUNET_break(0);
423     return NULL ;
424   }
425   if (0 >= addrlen)
426   {
427     GNUNET_break(0);
428     return NULL ;
429   }
430   if (addrlen < sizeof(struct HttpAddress))
431   {
432     GNUNET_break(0);
433     return NULL ;
434   }
435   urlen = ntohl (ha->urlen);
436   if (sizeof(struct HttpAddress) + urlen != addrlen)
437   {
438     /* This is a legacy addresses */
439     return NULL ;
440   }
441   if (addrlen < sizeof(struct HttpAddress) + urlen)
442   {
443     /* This is a legacy addresses */
444     return NULL ;
445   }
446   if (((char *) addr)[addrlen - 1] != '\0')
447   {
448     GNUNET_break(0);
449     return NULL ;
450   }
451   spa = http_split_address ((const char *) &ha[1]);
452   if (NULL == spa)
453   {
454       (*res) = GNUNET_SYSERR;
455       return NULL;
456   }
457
458   s = GNUNET_new (struct sockaddr_storage);
459   GNUNET_asprintf (&to_conv, "%s:%u", spa->host, spa->port);
460   if (GNUNET_SYSERR == GNUNET_STRINGS_to_address_ip (to_conv, strlen(to_conv), s))
461   {
462     /* could be a hostname */
463         GNUNET_free (s);
464     (*res) = GNUNET_NO;
465     s = NULL;
466   }
467   else if ((AF_INET != s->ss_family) && (AF_INET6 != s->ss_family))
468   {
469
470                 GNUNET_free (s);
471                 (*res) = GNUNET_SYSERR;
472                 s = NULL;
473   }
474   else
475   {
476                 (*res) = GNUNET_YES;
477   }
478         http_clean_splitted (spa);
479   GNUNET_free (to_conv);
480   return (struct sockaddr *) s;
481 }
482
483 /**
484  * Get the length of an address
485  *
486  * @param addr address
487  * @return the size
488  */
489 size_t
490 http_common_address_get_size (const struct HttpAddress * addr)
491 {
492  return sizeof (struct HttpAddress) + ntohl(addr->urlen);
493 }
494
495 /**
496  * Compare addr1 to addr2
497  *
498  * @param addr1 address1
499  * @param addrlen1 address 1 length
500  * @param addr2 address2
501  * @param addrlen2 address 2 length
502  * @return GNUNET_YES if equal, GNUNET_NO if not, GNUNET_SYSERR on error
503  */
504 size_t
505 http_common_cmp_addresses (const void *addr1, size_t addrlen1, const void *addr2, size_t addrlen2)
506 {
507         const struct HttpAddress *ha1;
508         const struct HttpAddress *ha2;
509   const char *a1 = (const char *) addr1;
510   const char *a2 = (const char *) addr2;
511   ha1 = (const struct HttpAddress *) a1;
512   ha2 = (const struct HttpAddress *) a2;
513
514   if (NULL == a1)
515       return GNUNET_SYSERR;
516   if (0 >= addrlen1)
517     return GNUNET_SYSERR;
518   if (a1[addrlen1-1] != '\0')
519     return GNUNET_SYSERR;
520
521   if (NULL == a2)
522       return GNUNET_SYSERR;
523   if (0 >= addrlen2)
524     return GNUNET_SYSERR;
525   if (a2[addrlen2-1] != '\0')
526     return GNUNET_SYSERR;
527
528   if (addrlen1 != addrlen2)
529     return GNUNET_NO;
530   if (ha1->urlen != ha2->urlen)
531     return GNUNET_NO;
532
533   if (0 == strcmp ((const char *) &ha1[1],(const char *) &ha2[1]))
534     return GNUNET_YES;
535   return GNUNET_NO;
536 }
537
538
539
540 /* end of plugin_transport_http_common.c */