- fix error messages
[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
211   if (NULL ==
212       http_common_plugin_address_to_string (NULL, type,
213                                             address, addrlen))
214   {
215     asc (asc_cls, NULL);
216     return;
217   }
218   asc (asc_cls, http_common_plugin_address_to_string (NULL,
219                                                       type,
220                                                       address, addrlen));
221   asc (asc_cls, NULL);
222 }
223
224
225 const char *
226 http_common_plugin_address_to_url (void *cls,
227                                    const void *addr,
228                                    size_t addrlen)
229 {
230   static char rbuf[1024];
231   const struct HttpAddress *address = addr;
232   const char * addr_str;
233
234   if (NULL == addr)
235   {
236     GNUNET_break (0);
237     return NULL;
238   }
239   if (0 >= addrlen)
240   {
241     GNUNET_break (0);
242     return NULL;
243   }
244   if (addrlen != http_common_address_get_size (address))
245   {
246     GNUNET_break (0);
247     return NULL;
248   }
249   addr_str = (char *) &address[1];
250
251   if (addr_str[ntohl(address->urlen) -1] != '\0')
252     return NULL;
253
254   memcpy (rbuf, &address[1], ntohl(address->urlen));
255   return rbuf;
256 }
257
258
259 /**
260  * Function called for a quick conversion of the binary address to
261  * a numeric address.  Note that the caller must not free the
262  * address and that the next call to this function is allowed
263  * to override the address again.
264  *
265  * @param cls closure
266  * @param plugin the plugin
267  * @param addr binary address
268  * @param addrlen length of the address
269  * @return string representing the same address
270  */
271 const char *
272 http_common_plugin_address_to_string (void *cls,
273                                       const char *plugin,
274                                       const void *addr,
275                                       size_t addrlen)
276 {
277   static char rbuf[1024];
278   const struct HttpAddress *address = addr;
279   const char * addr_str;
280   char *res;
281
282   GNUNET_assert (NULL != plugin);
283
284   if (NULL == addr)
285       return NULL;
286   if (0 == addrlen)
287     return TRANSPORT_SESSION_INBOUND_STRING;
288   if (addrlen != http_common_address_get_size (address))
289         return NULL;
290   addr_str = (char *) &address[1];
291
292   if (addr_str[ntohl(address->urlen) -1] != '\0')
293     return NULL;
294   GNUNET_asprintf (&res, "%s.%u.%s", plugin, ntohl(address->options), &address[1]);
295   if (strlen(res) + 1 < 500)
296   {
297     memcpy (rbuf, res, strlen(res) + 1);
298     GNUNET_free (res);
299     return rbuf;
300   }
301   GNUNET_break (0);
302   GNUNET_free (res);
303   return NULL;
304 }
305
306
307 /**
308  * Function called to convert a string address to
309  * a binary address.
310  *
311  * @param cls closure ('struct Plugin*')
312  * @param addr string address
313  * @param addrlen length of the @a addr
314  * @param buf location to store the buffer
315  *        If the function returns #GNUNET_SYSERR, its contents are undefined.
316  * @param added length of created address
317  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
318  */
319 int
320 http_common_plugin_string_to_address (void *cls,
321                                       const char *addr,
322                                       uint16_t addrlen,
323                                       void **buf,
324                                       size_t *added)
325 {
326   struct HttpAddress *a;
327   char *address;
328   char *plugin;
329   char *optionstr;
330   size_t urlen;
331   uint32_t options;
332
333   /* Format protocol.options.address:port */
334   address = NULL;
335   plugin = NULL;
336   optionstr = NULL;
337   if ((NULL == addr) || (addrlen == 0))
338   {
339     GNUNET_break (0);
340     return GNUNET_SYSERR;
341   }
342   if ('\0' != addr[addrlen - 1])
343   {
344     GNUNET_break (0);
345     return GNUNET_SYSERR;
346   }
347   if (strlen (addr) != addrlen - 1)
348   {
349     GNUNET_break (0);
350     return GNUNET_SYSERR;
351   }
352   plugin = GNUNET_strdup (addr);
353   optionstr = strchr (plugin, '.');
354   if (NULL == optionstr)
355   {
356     GNUNET_break (0);
357     GNUNET_free (plugin);
358     return GNUNET_SYSERR;
359   }
360   optionstr[0] = '\0';
361   optionstr ++;
362   options = atol (optionstr); /* 0 on conversion error, that's ok */
363   address = strchr (optionstr, '.');
364   if (NULL == address)
365   {
366     GNUNET_break (0);
367     GNUNET_free (plugin);
368     return GNUNET_SYSERR;
369   }
370   address[0] = '\0';
371   address ++;
372   urlen = strlen (address) + 1;
373
374   a = GNUNET_malloc (sizeof (struct HttpAddress) + urlen);
375   a->options = htonl(options);
376   a->urlen = htonl(urlen);
377   memcpy (&a[1], address, urlen);
378
379   (*buf) = a;
380   (*added) = sizeof (struct HttpAddress) + urlen;
381   GNUNET_free (plugin);
382   return GNUNET_OK;
383 }
384
385
386 /**
387  * Create a HTTP address from a socketaddr
388  *
389  * @param protocol protocol
390  * @param addr sockaddr * address
391  * @param addrlen length of the address
392  * @return the HttpAddress
393  */
394 struct HttpAddress *
395 http_common_address_from_socket (const char *protocol,
396                                  const struct sockaddr *addr,
397                                  socklen_t addrlen)
398 {
399   struct HttpAddress *address = NULL;
400   char *res;
401   size_t len;
402
403   GNUNET_asprintf(&res,
404                   "%s://%s",
405                   protocol,
406                   GNUNET_a2s (addr, addrlen));
407   len = strlen (res)+1;
408   address = GNUNET_malloc (sizeof (struct HttpAddress) + len);
409   address->options = htonl (HTTP_OPTIONS_NONE);
410   address->urlen = htonl (len);
411   memcpy (&address[1], res, len);
412   GNUNET_free (res);
413   return address;
414 }
415
416
417 /**
418  * Create a socketaddr from a HTTP address
419  *
420  * @param addr a `sockaddr *` address
421  * @param addrlen length of the @a addr
422  * @param res the result:
423  *   #GNUNET_SYSERR, invalid input,
424  *   #GNUNET_YES: could convert to ip,
425  *   #GNUNET_NO: valid input but could not convert to ip (hostname?)
426  * @return the string
427  */
428 struct sockaddr *
429 http_common_socket_from_address (const void *addr,
430                                  size_t addrlen,
431                                  int *res)
432 {
433   const struct HttpAddress *ha;
434   struct SplittedHTTPAddress * spa;
435   struct sockaddr_storage *s;
436   char * to_conv;
437   size_t urlen;
438
439   (*res) = GNUNET_SYSERR;
440   ha = (const struct HttpAddress *) addr;
441   if (NULL == addr)
442   {
443     GNUNET_break(0);
444     return NULL ;
445   }
446   if (0 >= addrlen)
447   {
448     GNUNET_break(0);
449     return NULL ;
450   }
451   if (addrlen < sizeof(struct HttpAddress))
452   {
453     GNUNET_break(0);
454     return NULL ;
455   }
456   urlen = ntohl (ha->urlen);
457   if (sizeof(struct HttpAddress) + urlen != addrlen)
458   {
459     /* This is a legacy addresses */
460     return NULL ;
461   }
462   if (addrlen < sizeof(struct HttpAddress) + urlen)
463   {
464     /* This is a legacy addresses */
465     return NULL ;
466   }
467   if (((char *) addr)[addrlen - 1] != '\0')
468   {
469     GNUNET_break(0);
470     return NULL ;
471   }
472   spa = http_split_address ((const char *) &ha[1]);
473   if (NULL == spa)
474   {
475       (*res) = GNUNET_SYSERR;
476       return NULL;
477   }
478
479   s = GNUNET_new (struct sockaddr_storage);
480   GNUNET_asprintf (&to_conv, "%s:%u", spa->host, spa->port);
481   if (GNUNET_SYSERR == GNUNET_STRINGS_to_address_ip (to_conv, strlen(to_conv), s))
482   {
483     /* could be a hostname */
484         GNUNET_free (s);
485     (*res) = GNUNET_NO;
486     s = NULL;
487   }
488   else if ((AF_INET != s->ss_family) && (AF_INET6 != s->ss_family))
489   {
490
491                 GNUNET_free (s);
492                 (*res) = GNUNET_SYSERR;
493                 s = NULL;
494   }
495   else
496   {
497                 (*res) = GNUNET_YES;
498   }
499         http_clean_splitted (spa);
500   GNUNET_free (to_conv);
501   return (struct sockaddr *) s;
502 }
503
504
505 /**
506  * Get the length of an address
507  *
508  * @param addr address
509  * @return the size
510  */
511 size_t
512 http_common_address_get_size (const struct HttpAddress * addr)
513 {
514  return sizeof (struct HttpAddress) + ntohl(addr->urlen);
515 }
516
517
518 /**
519  * Compare addr1 to addr2
520  *
521  * @param addr1 address1
522  * @param addrlen1 address 1 length
523  * @param addr2 address2
524  * @param addrlen2 address 2 length
525  * @return #GNUNET_YES if equal, #GNUNET_NO if not, #GNUNET_SYSERR on error
526  */
527 size_t
528 http_common_cmp_addresses (const void *addr1,
529                            size_t addrlen1,
530                            const void *addr2,
531                            size_t addrlen2)
532 {
533   const char *a1 = addr1;
534   const char *a2 = addr2;
535   const struct HttpAddress *ha1;
536   const struct HttpAddress *ha2;
537   ha1 = (const struct HttpAddress *) a1;
538   ha2 = (const struct HttpAddress *) a2;
539
540   if (NULL == a1)
541       return GNUNET_SYSERR;
542   if (0 >= addrlen1)
543     return GNUNET_SYSERR;
544   if (a1[addrlen1-1] != '\0')
545     return GNUNET_SYSERR;
546
547   if (NULL == a2)
548       return GNUNET_SYSERR;
549   if (0 >= addrlen2)
550     return GNUNET_SYSERR;
551   if (a2[addrlen2-1] != '\0')
552     return GNUNET_SYSERR;
553
554   if (addrlen1 != addrlen2)
555     return GNUNET_NO;
556   if (ha1->urlen != ha2->urlen)
557     return GNUNET_NO;
558
559   if (0 == strcmp ((const char *) &ha1[1],(const char *) &ha2[1]))
560     return GNUNET_YES;
561   return GNUNET_NO;
562 }
563
564
565
566 /* end of plugin_transport_http_common.c */