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