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