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