- verboser log, faster start
[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
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_transport_plugin.h"
30 #include "plugin_transport_http_common.h"
31
32 struct SplittedHTTPAddress
33 {
34         char *protocol;
35         char *host;
36         char *path;
37         int port;
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_malloc (sizeof (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 char *saddr = (const char *) addr;
206
207   if ( (NULL == saddr) ||
208        (0 >= addrlen) ||
209        ('\0' != saddr[addrlen-1]) )
210   {
211       asc (asc_cls, NULL);
212       return;
213   }
214   asc (asc_cls, saddr);
215   asc (asc_cls, NULL);
216 }
217
218
219 /**
220  * Function called for a quick conversion of the binary address to
221  * a numeric address.  Note that the caller must not free the
222  * address and that the next call to this function is allowed
223  * to override the address again.
224  *
225  * @param cls closure
226  * @param addr binary address
227  * @param addrlen length of the address
228  * @return string representing the same address
229  */
230 const char *
231 http_common_plugin_address_to_string (void *cls, const void *addr, size_t addrlen)
232 {
233   const char *saddr = (const char *) addr;
234   if (NULL == saddr)
235       return NULL;
236   if (0 >= addrlen)
237     return NULL;
238   if (saddr[addrlen-1] != '\0')
239     return NULL;
240   return saddr;
241 }
242
243 /**
244  * Function called to convert a string address to
245  * a binary address.
246  *
247  * @param cls closure ('struct Plugin*')
248  * @param addr string address
249  * @param addrlen length of the address
250  * @param buf location to store the buffer
251  *        If the function returns GNUNET_SYSERR, its contents are undefined.
252  * @param added length of created address
253  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
254  */
255 int
256 http_common_plugin_string_to_address (void *cls,
257                         const char *addr,
258                         uint16_t addrlen,
259                         void **buf,
260                         size_t *added)
261 {
262   if (NULL == addr)
263       return GNUNET_SYSERR;
264   if (0 >= addrlen)
265     return GNUNET_SYSERR;
266   if (addr[addrlen-1] != '\0')
267     return GNUNET_SYSERR;
268
269   (*buf) = strdup (addr);
270   (*added) = strlen (addr) + 1;
271   return GNUNET_OK;
272 }
273
274 /**
275  * Create a HTTP address from a socketaddr
276  *
277  * @param protocol protocol
278  * @param addr sockaddr * address
279  * @param addrlen length of the address
280  * @return the string
281  */
282 char *
283 http_common_address_from_socket (const char *protocol, const struct sockaddr *addr, socklen_t addrlen)
284 {
285   char *res;
286   GNUNET_asprintf(&res, "%s://%s", protocol, GNUNET_a2s (addr, addrlen));
287   return res;
288 }
289
290 /**
291  * Create a socketaddr from a HTTP address
292  *
293  * @param addr sockaddr * address
294  * @param addrlen length of the address
295  * @param res the result:
296  * GNUNET_SYSERR, invalid input,
297  * GNUNET_YES: could convert to ip,
298  * GNUNET_NO: valid input but could not convert to ip (hostname?)
299  * @return the string
300  */
301 struct sockaddr *
302 http_common_socket_from_address (const void *addr, size_t addrlen, int *res)
303 {
304         struct SplittedHTTPAddress * spa;
305   struct sockaddr_storage *s;
306   (*res) = GNUNET_SYSERR;
307   char * to_conv;
308
309   if (NULL == addr)
310     {
311       GNUNET_break (0);
312       return NULL;
313     }
314   if (0 >= addrlen)
315     {
316       GNUNET_break (0);
317       return NULL;
318     }
319   if (((char *) addr)[addrlen-1] != '\0')
320     {
321       GNUNET_break (0);
322       return NULL;
323     }
324
325   spa = http_split_address (addr);
326   if (NULL == spa)
327   {
328       (*res) = GNUNET_SYSERR;
329       return NULL;
330   }
331
332   s = GNUNET_malloc (sizeof (struct sockaddr_storage));
333   GNUNET_asprintf (&to_conv, "%s:%u", spa->host, spa->port);
334   if (GNUNET_SYSERR == GNUNET_STRINGS_to_address_ip (to_conv, strlen(to_conv), s))
335   {
336     /* could be a hostname */
337         GNUNET_free (s);
338     (*res) = GNUNET_NO;
339     s = NULL;
340   }
341   else if ((AF_INET != s->ss_family) && (AF_INET6 != s->ss_family))
342   {
343
344                 GNUNET_free (s);
345                 (*res) = GNUNET_SYSERR;
346                 s = NULL;
347   }
348   else
349   {
350                 (*res) = GNUNET_YES;
351   }
352         http_clean_splitted (spa);
353   GNUNET_free (to_conv);
354   return (struct sockaddr *) s;
355 }
356
357 /**
358  * Get the length of an address
359  *
360  * @param addr address
361  * @return the size
362  */
363 size_t
364 http_common_address_get_size (const void *addr)
365 {
366  return strlen (addr) + 1;
367 }
368
369 /**
370  * Compare addr1 to addr2
371  *
372  * @param addr1 address1
373  * @param addrlen1 address 1 length
374  * @param addr2 address2
375  * @param addrlen2 address 2 length
376  * @return GNUNET_YES if equal, GNUNET_NO if not, GNUNET_SYSERR on error
377  */
378 size_t
379 http_common_cmp_addresses (const void *addr1, size_t addrlen1, const void *addr2, size_t addrlen2)
380 {
381   const char *a1 = (const char *) addr1;
382   const char *a2 = (const char *) addr2;
383
384   if (NULL == a1)
385       return GNUNET_SYSERR;
386   if (0 >= addrlen1)
387     return GNUNET_SYSERR;
388   if (a1[addrlen1-1] != '\0')
389     return GNUNET_SYSERR;
390
391   if (NULL == a2)
392       return GNUNET_SYSERR;
393   if (0 >= addrlen2)
394     return GNUNET_SYSERR;
395   if (a2[addrlen2-1] != '\0')
396     return GNUNET_SYSERR;
397
398   if (addrlen1 != addrlen2)
399     return GNUNET_NO;
400
401   if (0 == strcmp (addr1, addr2))
402     return GNUNET_YES;
403   return GNUNET_NO;
404 }
405
406
407
408 /* end of plugin_transport_http_common.c */