-fix URIs
[oweals/gnunet.git] / src / hostlist / gnunet-daemon-hostlist_server.c
1 /*
2      This file is part of GNUnet.
3      (C) 2008, 2009, 2010, 2014 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 hostlist/gnunet-daemon-hostlist_server.c
23  * @author Christian Grothoff
24  * @author Matthias Wachs
25  * @author David Barksdale
26  * @brief application to provide an integrated hostlist HTTP server
27  */
28 #include "platform.h"
29 #include <microhttpd.h>
30 #include "gnunet-daemon-hostlist_server.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_peerinfo_service.h"
33 #include "gnunet-daemon-hostlist.h"
34 #include "gnunet_resolver_service.h"
35
36
37 /**
38  * How long until our hostlist advertisment transmission via CORE should
39  * time out?
40  */
41 #define GNUNET_ADV_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
42
43
44 /**
45  * Handle to the HTTP server as provided by libmicrohttpd for IPv6.
46  */
47 static struct MHD_Daemon *daemon_handle_v6;
48
49 /**
50  * Handle to the HTTP server as provided by libmicrohttpd for IPv4.
51  */
52 static struct MHD_Daemon *daemon_handle_v4;
53
54 /**
55  * Our configuration.
56  */
57 static const struct GNUNET_CONFIGURATION_Handle *cfg;
58
59 /**
60  * For keeping statistics.
61  */
62 static struct GNUNET_STATISTICS_Handle *stats;
63
64 /**
65  * Handle to the core service (NULL until we've connected to it).
66  */
67 static struct GNUNET_CORE_Handle *core;
68
69 /**
70  * Handle to the peerinfo notify service (NULL until we've connected to it).
71  */
72 static struct GNUNET_PEERINFO_NotifyContext *notify;
73
74 /**
75  * Our primary task for IPv4.
76  */
77 static GNUNET_SCHEDULER_TaskIdentifier hostlist_task_v4;
78
79 /**
80  * Our primary task for IPv6.
81  */
82 static GNUNET_SCHEDULER_TaskIdentifier hostlist_task_v6;
83
84 /**
85  * Our canonical response.
86  */
87 static struct MHD_Response *response;
88
89 /**
90  * Handle for accessing peerinfo service.
91  */
92 static struct GNUNET_PEERINFO_Handle *peerinfo;
93
94 /**
95  * Set if we are allowed to advertise our hostlist to others.
96  */
97 static int advertising;
98
99 /**
100  * Buffer for the hostlist address
101  */
102 static char *hostlist_uri;
103
104
105 /**
106  * Context for host processor.
107  */
108 struct HostSet
109 {
110   unsigned int size;
111
112   char *data;
113
114   struct GNUNET_PEERINFO_IteratorContext *pitr;
115 };
116
117
118 /**
119  * NULL if we are not currenlty iterating over peer information.
120  */
121 static struct HostSet *builder;
122
123
124 /**
125  * Function that assembles our response.
126  */
127 static void
128 finish_response ()
129 {
130   if (NULL != response)
131     MHD_destroy_response (response);
132   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
133               "Creating hostlist response with %u bytes\n",
134               (unsigned int) builder->size);
135   response =
136       MHD_create_response_from_data (builder->size, builder->data, MHD_YES,
137                                      MHD_NO);
138   if ((NULL == daemon_handle_v4) && (NULL == daemon_handle_v6))
139   {
140     MHD_destroy_response (response);
141     response = NULL;
142   }
143   GNUNET_STATISTICS_set (stats, gettext_noop ("bytes in hostlist"),
144                          builder->size, GNUNET_YES);
145   GNUNET_free (builder);
146   builder = NULL;
147 }
148
149
150 /**
151  * Set @a cls to #GNUNET_YES (we have an address!).
152  *
153  * @param cls closure, an `int *`
154  * @param address the address (ignored)
155  * @param expiration expiration time (call is ignored if this is in the past)
156  * @return  #GNUNET_SYSERR to stop iterating (unless expiration has occured)
157  */
158 static int
159 check_has_addr (void *cls,
160                 const struct GNUNET_HELLO_Address *address,
161                 struct GNUNET_TIME_Absolute expiration)
162 {
163   int *arg = cls;
164
165   if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
166   {
167     GNUNET_STATISTICS_update (stats,
168                               gettext_noop ("expired addresses encountered"), 1,
169                               GNUNET_YES);
170     return GNUNET_YES;          /* ignore this address */
171   }
172   *arg = GNUNET_YES;
173   return GNUNET_SYSERR;
174 }
175
176
177 /**
178  * Callback that processes each of the known HELLOs for the
179  * hostlist response construction.
180  *
181  * @param cls closure, NULL
182  * @param peer id of the peer, NULL for last call
183  * @param hello hello message for the peer (can be NULL)
184  * @param error message
185  */
186 static void
187 host_processor (void *cls,
188                 const struct GNUNET_PeerIdentity *peer,
189                 const struct GNUNET_HELLO_Message *hello,
190                 const char *err_msg)
191 {
192   size_t old;
193   size_t s;
194   int has_addr;
195
196   if (NULL != err_msg)
197   {
198     GNUNET_assert (NULL == peer);
199     builder->pitr = NULL;
200     GNUNET_free_non_null (builder->data);
201     GNUNET_free (builder);
202     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
203                 _("Error in communication with PEERINFO service: %s\n"),
204                 err_msg);
205     return;
206   }
207   if (NULL == peer)
208   {
209     builder->pitr = NULL;
210     finish_response ();
211     return;
212   }
213   if (NULL == hello)
214     return;
215   has_addr = GNUNET_NO;
216   GNUNET_HELLO_iterate_addresses (hello,
217                                   GNUNET_NO,
218                                   &check_has_addr,
219                                   &has_addr);
220   if (GNUNET_NO == has_addr)
221   {
222     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
223                 "HELLO for peer `%4s' has no address, not suitable for hostlist!\n",
224                 GNUNET_i2s (peer));
225     GNUNET_STATISTICS_update (stats,
226                               gettext_noop
227                               ("HELLOs without addresses encountered (ignored)"),
228                               1, GNUNET_NO);
229     return;
230   }
231   old = builder->size;
232   s = GNUNET_HELLO_size (hello);
233   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
234               "Received %u bytes of `%s' from peer `%s' for hostlist.\n",
235               (unsigned int) s,
236               "HELLO",
237               GNUNET_i2s (peer));
238   if ((old + s >= GNUNET_MAX_MALLOC_CHECKED) ||
239       (old + s >= MAX_BYTES_PER_HOSTLISTS))
240   {
241     GNUNET_STATISTICS_update (stats,
242                               gettext_noop
243                               ("bytes not included in hostlist (size limit)"),
244                               s, GNUNET_NO);
245     return;                     /* too large, skip! */
246   }
247   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
248               "Adding peer `%s' to hostlist (%u bytes)\n",
249               GNUNET_i2s (peer),
250               (unsigned int) s);
251   GNUNET_array_grow (builder->data, builder->size, old + s);
252   memcpy (&builder->data[old], hello, s);
253 }
254
255
256 /**
257  * Hostlist access policy (very permissive, allows everything).
258  * Returns #MHD_NO only if we are not yet ready to serve.
259  *
260  * @param cls closure
261  * @param addr address information from the client
262  * @param addrlen length of @a addr
263  * @return #MHD_YES if connection is allowed, #MHD_NO if not (we are not ready)
264  */
265 static int
266 accept_policy_callback (void *cls,
267                         const struct sockaddr *addr,
268                         socklen_t addrlen)
269 {
270   if (NULL == response)
271   {
272     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
273                 "Received request for hostlist, but I am not yet ready; rejecting!\n");
274     return MHD_NO;
275   }
276   return MHD_YES;               /* accept all */
277 }
278
279
280 /**
281  * Add headers to a request indicating that we allow Cross-Origin Resource
282  * Sharing.
283  *
284  * @param response response to add headers to
285  */
286 static void
287 add_cors_headers (struct MHD_Response *response)
288 {
289   MHD_add_response_header (response,
290                            "Access-Control-Allow-Origin",
291                            "*");
292   MHD_add_response_header (response,
293                            "Access-Control-Allow-Methods",
294                            "GET, OPTIONS");
295   MHD_add_response_header (response,
296                            "Access-Control-Max-Age",
297                            "86400");
298 }
299
300
301 /**
302  * Main request handler.
303  *
304  * @param cls argument given together with the function
305  *        pointer when the handler was registered with MHD
306  * @param url the requested url
307  * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
308  *        #MHD_HTTP_METHOD_PUT, etc.)
309  * @param version the HTTP version string (i.e.
310  *        #MHD_HTTP_VERSION_1_1)
311  * @param upload_data the data being uploaded (excluding HEADERS,
312  *        for a POST that fits into memory and that is encoded
313  *        with a supported encoding, the POST data will NOT be
314  *        given in upload_data and is instead available as
315  *        part of #MHD_get_connection_values; very large POST
316  *        data *will* be made available incrementally in
317  *        @a upload_data)
318  * @param upload_data_size set initially to the size of the
319  *        @a upload_data provided; the method must update this
320  *        value to the number of bytes NOT processed;
321  * @param con_cls pointer that the callback can set to some
322  *        address and that will be preserved by MHD for future
323  *        calls for this request; since the access handler may
324  *        be called many times (i.e., for a PUT/POST operation
325  *        with plenty of upload data) this allows the application
326  *        to easily associate some request-specific state.
327  *        If necessary, this state can be cleaned up in the
328  *        global #MHD_RequestCompletedCallback (which
329  *        can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
330  *        Initially, `*con_cls` will be NULL.
331  * @return #MHD_YES if the connection was handled successfully,
332  *         #MHD_NO if the socket must be closed due to a serios
333  *         error while handling the request
334  */
335 static int
336 access_handler_callback (void *cls,
337                          struct MHD_Connection *connection,
338                          const char *url,
339                          const char *method,
340                          const char *version,
341                          const char *upload_data,
342                          size_t *upload_data_size,
343                          void **con_cls)
344 {
345   static int dummy;
346
347   /* CORS pre-flight request */
348   if (0 == strcmp (MHD_HTTP_METHOD_OPTIONS, method))
349   {
350     struct MHD_Response *options_response;
351     int rc;
352
353     options_response = MHD_create_response_from_buffer (0, NULL,
354                                                         MHD_RESPMEM_PERSISTENT);
355     add_cors_headers(options_response);
356     rc = MHD_queue_response (connection, MHD_HTTP_OK, options_response);
357     MHD_destroy_response (options_response);
358     return rc;
359   }
360   if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
361   {
362     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
363                 _("Refusing `%s' request to hostlist server\n"), method);
364     GNUNET_STATISTICS_update (stats,
365                               gettext_noop
366                               ("hostlist requests refused (not HTTP GET)"), 1,
367                               GNUNET_YES);
368     return MHD_NO;
369   }
370   if (NULL == *con_cls)
371   {
372     (*con_cls) = &dummy;
373     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
374                 "Sending 100 CONTINUE reply\n");
375     return MHD_YES;             /* send 100 continue */
376   }
377   if (0 != *upload_data_size)
378   {
379     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
380                 _("Refusing `%s' request with %llu bytes of upload data\n"),
381                 method, (unsigned long long) *upload_data_size);
382     GNUNET_STATISTICS_update (stats,
383                               gettext_noop
384                               ("hostlist requests refused (upload data)"), 1,
385                               GNUNET_YES);
386     return MHD_NO;              /* do not support upload data */
387   }
388   if (NULL == response)
389   {
390     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
391                 _("Could not handle hostlist request since I do not have a response yet\n"));
392     GNUNET_STATISTICS_update (stats,
393                               gettext_noop
394                               ("hostlist requests refused (not ready)"), 1,
395                               GNUNET_YES);
396     return MHD_NO;              /* internal error, no response yet */
397   }
398   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
399               _("Received request for our hostlist\n"));
400   GNUNET_STATISTICS_update (stats,
401                             gettext_noop ("hostlist requests processed"),
402                             1, GNUNET_YES);
403   add_cors_headers (response);
404   return MHD_queue_response (connection, MHD_HTTP_OK, response);
405 }
406
407
408 /**
409  * Handler called by CORE when CORE is ready to transmit message
410  *
411  * @param cls closure
412  * @param size size of buffer to copy message to
413  * @param buf buffer to copy message to
414  * @return number of bytes copied to @a buf
415  */
416 static size_t
417 adv_transmit_ready (void *cls,
418                     size_t size,
419                     void *buf)
420 {
421   static uint64_t hostlist_adv_count;
422   size_t transmission_size;
423   size_t uri_size;              /* Including \0 termination! */
424   struct GNUNET_MessageHeader header;
425   char *cbuf;
426
427   if (NULL == buf)
428   {
429     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
430                 "Transmission failed, buffer invalid!\n");
431     return 0;
432   }
433   uri_size = strlen (hostlist_uri) + 1;
434   transmission_size = sizeof (struct GNUNET_MessageHeader) + uri_size;
435   header.type = htons (GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
436   header.size = htons (transmission_size);
437   GNUNET_assert (size >= transmission_size);
438   memcpy (buf, &header, sizeof (struct GNUNET_MessageHeader));
439   cbuf = buf;
440   memcpy (&cbuf[sizeof (struct GNUNET_MessageHeader)], hostlist_uri, uri_size);
441   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
442               "Sent advertisement message: Copied %u bytes into buffer!\n",
443               (unsigned int) transmission_size);
444   hostlist_adv_count++;
445   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " # Sent advertisement message: %u\n",
446               hostlist_adv_count);
447   GNUNET_STATISTICS_update (stats,
448                             gettext_noop ("# hostlist advertisements send"), 1,
449                             GNUNET_NO);
450   return transmission_size;
451 }
452
453
454 /**
455  * Method called whenever a given peer connects.
456  *
457  * @param cls closure
458  * @param peer peer identity this notification is about
459  */
460 static void
461 connect_handler (void *cls,
462                  const struct GNUNET_PeerIdentity *peer)
463 {
464   size_t size;
465
466   if (!advertising)
467     return;
468   if (NULL == hostlist_uri)
469     return;
470   size = strlen (hostlist_uri) + 1;
471   if (size + sizeof (struct GNUNET_MessageHeader) >=
472       GNUNET_SERVER_MAX_MESSAGE_SIZE)
473   {
474     GNUNET_break (0);
475     return;
476   }
477   size += sizeof (struct GNUNET_MessageHeader);
478   if (NULL == core)
479   {
480     GNUNET_break (0);
481     return;
482   }
483   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
484               "Asked CORE to transmit advertisement message with a size of %u bytes to peer `%s'\n",
485               size,
486               GNUNET_i2s (peer));
487   if (NULL ==
488       GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES,
489                                          GNUNET_CORE_PRIO_BEST_EFFORT,
490                                          GNUNET_ADV_TIMEOUT,
491                                          peer,
492                                          size,
493                                          &adv_transmit_ready, NULL))
494   {
495     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
496                 _("Advertisement message could not be queued by core\n"));
497   }
498 }
499
500
501 /**
502  * Method called whenever a given peer disconnects.
503  *
504  * @param cls closure
505  * @param peer peer identity this notification is about
506  */
507 static void
508 disconnect_handler (void *cls,
509                     const struct GNUNET_PeerIdentity *peer)
510 {
511   /* nothing to do */
512 }
513
514
515 /**
516  * PEERINFO calls this function to let us know about a possible peer
517  * that we might want to connect to.
518  *
519  * @param cls closure (not used)
520  * @param peer potential peer to connect to
521  * @param hello HELLO for this peer (or NULL)
522  * @param err_msg NULL if successful, otherwise contains error message
523  */
524 static void
525 process_notify (void *cls,
526                 const struct GNUNET_PeerIdentity *peer,
527                 const struct GNUNET_HELLO_Message *hello,
528                 const char *err_msg)
529 {
530   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
531               "Peerinfo is notifying us to rebuild our hostlist\n");
532   if (NULL != err_msg)
533     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
534                 _("Error in communication with PEERINFO service: %s\n"),
535                 err_msg);
536   if (NULL != builder)
537   {
538     /* restart re-build already in progress ... */
539     if (NULL != builder->pitr)
540     {
541       GNUNET_PEERINFO_iterate_cancel (builder->pitr);
542       builder->pitr = NULL;
543     }
544     GNUNET_free_non_null (builder->data);
545     builder->size = 0;
546     builder->data = NULL;
547   }
548   else
549   {
550     builder = GNUNET_new (struct HostSet);
551   }
552   GNUNET_assert (NULL != peerinfo);
553   builder->pitr =
554       GNUNET_PEERINFO_iterate (peerinfo, GNUNET_NO, NULL, GNUNET_TIME_UNIT_MINUTES,
555                                &host_processor, NULL);
556 }
557
558
559 /**
560  * Function that queries MHD's select sets and
561  * starts the task waiting for them.
562  */
563 static GNUNET_SCHEDULER_TaskIdentifier
564 prepare_daemon (struct MHD_Daemon *daemon_handle);
565
566
567 /**
568  * Call MHD to process pending requests and then go back
569  * and schedule the next run.
570  *
571  * @param cls the `struct MHD_Daemon` of the HTTP server to run
572  * @param tc scheduler context
573  */
574 static void
575 run_daemon (void *cls,
576             const struct GNUNET_SCHEDULER_TaskContext *tc)
577 {
578   struct MHD_Daemon *daemon_handle = cls;
579
580   if (daemon_handle == daemon_handle_v4)
581     hostlist_task_v4 = GNUNET_SCHEDULER_NO_TASK;
582   else
583     hostlist_task_v6 = GNUNET_SCHEDULER_NO_TASK;
584
585   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
586     return;
587   GNUNET_assert (MHD_YES == MHD_run (daemon_handle));
588   if (daemon_handle == daemon_handle_v4)
589     hostlist_task_v4 = prepare_daemon (daemon_handle);
590   else
591     hostlist_task_v6 = prepare_daemon (daemon_handle);
592 }
593
594
595 /**
596  * Function that queries MHD's select sets and
597  * starts the task waiting for them.
598  *
599  * @param daemon_handle HTTP server to prepare to run
600  */
601 static GNUNET_SCHEDULER_TaskIdentifier
602 prepare_daemon (struct MHD_Daemon *daemon_handle)
603 {
604   GNUNET_SCHEDULER_TaskIdentifier ret;
605   fd_set rs;
606   fd_set ws;
607   fd_set es;
608   struct GNUNET_NETWORK_FDSet *wrs;
609   struct GNUNET_NETWORK_FDSet *wws;
610   int max;
611   MHD_UNSIGNED_LONG_LONG timeout;
612   int haveto;
613   struct GNUNET_TIME_Relative tv;
614
615   FD_ZERO (&rs);
616   FD_ZERO (&ws);
617   FD_ZERO (&es);
618   wrs = GNUNET_NETWORK_fdset_create ();
619   wws = GNUNET_NETWORK_fdset_create ();
620   max = -1;
621   GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
622   haveto = MHD_get_timeout (daemon_handle, &timeout);
623   if (haveto == MHD_YES)
624     tv.rel_value_us = (uint64_t) timeout * 1000LL;
625   else
626     tv = GNUNET_TIME_UNIT_FOREVER_REL;
627   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
628   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
629   ret =
630       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
631                                    tv, wrs, wws,
632                                    &run_daemon, daemon_handle);
633   GNUNET_NETWORK_fdset_destroy (wrs);
634   GNUNET_NETWORK_fdset_destroy (wws);
635   return ret;
636 }
637
638
639 /**
640  * Start server offering our hostlist.
641  *
642  * @param c configuration to use
643  * @param st statistics handle to use
644  * @param co core handle to use
645  * @param server_ch[OUT] set to handler for CORE connect events
646  * @param server_dh[OUT] set to handler for CORE disconnect events
647  * @param advertise #GNUNET_YES if we should advertise our hostlist
648  * @return #GNUNET_OK on success
649  */
650 int
651 GNUNET_HOSTLIST_server_start (const struct GNUNET_CONFIGURATION_Handle *c,
652                               struct GNUNET_STATISTICS_Handle *st,
653                               struct GNUNET_CORE_Handle *co,
654                               GNUNET_CORE_ConnectEventHandler *server_ch,
655                               GNUNET_CORE_DisconnectEventHandler *server_dh,
656                               int advertise)
657 {
658   unsigned long long port;
659   char *hostname;
660   char *ipv4;
661   char *ipv6;
662   size_t size;
663   struct in_addr i4;
664   struct in6_addr i6;
665   struct sockaddr_in v4;
666   struct sockaddr_in6 v6;
667   const struct sockaddr *sa4;
668   const struct sockaddr *sa6;
669
670   advertising = advertise;
671   if (! advertising)
672     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
673                 "Advertising not enabled on this hostlist server\n");
674   else
675     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
676                 "Advertising enabled on this hostlist server\n");
677   cfg = c;
678   stats = st;
679   peerinfo = GNUNET_PEERINFO_connect (cfg);
680   if (NULL == peerinfo)
681   {
682     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
683                 _("Could not access PEERINFO service.  Exiting.\n"));
684     return GNUNET_SYSERR;
685   }
686   if (GNUNET_OK !=
687       GNUNET_CONFIGURATION_get_value_number (cfg,
688                                              "HOSTLIST",
689                                              "HTTPPORT",
690                                              &port))
691     return GNUNET_SYSERR;
692   if ((0 == port) || (port > UINT16_MAX))
693   {
694     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
695                 _("Invalid port number %llu.  Exiting.\n"),
696                 port);
697     return GNUNET_SYSERR;
698   }
699
700   if (GNUNET_SYSERR ==
701       GNUNET_CONFIGURATION_get_value_string (cfg,
702                                              "HOSTLIST",
703                                              "EXTERNAL_DNS_NAME",
704                                              &hostname))
705     hostname = GNUNET_RESOLVER_local_fqdn_get ();
706   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
707               _("Hostlist service starts on %s:%llu\n"),
708               hostname, port);
709   if (NULL != hostname)
710   {
711     size = strlen (hostname);
712     if (size + 15 > MAX_URL_LEN)
713     {
714       GNUNET_break (0);
715     }
716     else
717     {
718       GNUNET_asprintf (&hostlist_uri,
719                        "http://%s:%u/", hostname,
720                        (unsigned int) port);
721       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
722                   _("Address to obtain hostlist: `%s'\n"),
723                   hostlist_uri);
724     }
725     GNUNET_free (hostname);
726   }
727
728   if (GNUNET_CONFIGURATION_have_value (cfg, "HOSTLIST", "BINDTOIPV4"))
729   {
730     GNUNET_break (GNUNET_OK ==
731                   GNUNET_CONFIGURATION_get_value_string (cfg, "HOSTLIST",
732                                                          "BINDTOIP", &ipv4));
733   }
734   else
735     ipv4 = NULL;
736   if (GNUNET_CONFIGURATION_have_value (cfg, "HOSTLIST", "BINDTOIPV6"))
737   {
738     GNUNET_break (GNUNET_OK ==
739                   GNUNET_CONFIGURATION_get_value_string (cfg, "HOSTLIST",
740                                                          "BINDTOIP", &ipv6));
741   }
742   else
743     ipv6 = NULL;
744   sa4 = NULL;
745   if (NULL != ipv4)
746   {
747     if (1 == inet_pton (AF_INET, ipv4, &i4))
748     {
749       memset (&v4, 0, sizeof (v4));
750       v4.sin_family = AF_INET;
751       v4.sin_addr = i4;
752       v4.sin_port = htons (port);
753 #if HAVE_SOCKADDR_IN_SIN_LEN
754       v4.sin_len = sizeof (v4);
755 #endif
756       sa4 = (const struct sockaddr *) &v4;
757     }
758     else
759       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
760                   _("`%s' is not a valid IPv4 address! Ignoring BINDTOIPV4.\n"),
761                   ipv4);
762     GNUNET_free (ipv4);
763   }
764   sa6 = NULL;
765   if (NULL != ipv6)
766   {
767     if (1 == inet_pton (AF_INET6, ipv6, &i6))
768     {
769       memset (&v6, 0, sizeof (v6));
770       v6.sin6_family = AF_INET6;
771       v6.sin6_addr = i6;
772       v6.sin6_port = htons (port);
773 #if HAVE_SOCKADDR_IN_SIN_LEN
774       v6.sin6_len = sizeof (v6);
775 #endif
776       sa6 = (const struct sockaddr *) &v6;
777     }
778     else
779       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
780                   _("`%s' is not a valid IPv6 address! Ignoring BINDTOIPV6.\n"),
781                   ipv6);
782     GNUNET_free (ipv6);
783   }
784
785   daemon_handle_v6 = MHD_start_daemon (MHD_USE_IPv6 | MHD_USE_DEBUG,
786                                        (uint16_t) port,
787                                        &accept_policy_callback, NULL,
788                                        &access_handler_callback, NULL,
789                                        MHD_OPTION_CONNECTION_LIMIT,
790                                        (unsigned int) 16,
791                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT,
792                                        (unsigned int) 1,
793                                        MHD_OPTION_CONNECTION_TIMEOUT,
794                                        (unsigned int) 16,
795                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT,
796                                        (size_t) (16 * 1024),
797                                        MHD_OPTION_SOCK_ADDR,
798                                        sa6,
799                                        MHD_OPTION_END);
800   daemon_handle_v4 = MHD_start_daemon (MHD_NO_FLAG | MHD_USE_DEBUG,
801                                        (uint16_t) port,
802                                        &accept_policy_callback, NULL,
803                                        &access_handler_callback, NULL,
804                                        MHD_OPTION_CONNECTION_LIMIT,
805                                        (unsigned int) 16,
806                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT,
807                                        (unsigned int) 1,
808                                        MHD_OPTION_CONNECTION_TIMEOUT,
809                                        (unsigned int) 16,
810                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT,
811                                        (size_t) (16 * 1024),
812                                        MHD_OPTION_SOCK_ADDR,
813                                        sa4,
814                                        MHD_OPTION_END);
815
816   if ( (NULL == daemon_handle_v6) &&
817        (NULL == daemon_handle_v4) )
818   {
819     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
820                 _("Could not start hostlist HTTP server on port %u\n"),
821                 (unsigned short) port);
822     return GNUNET_SYSERR;
823   }
824
825   core = co;
826   *server_ch = &connect_handler;
827   *server_dh = &disconnect_handler;
828   if (NULL != daemon_handle_v4)
829     hostlist_task_v4 = prepare_daemon (daemon_handle_v4);
830   if (NULL != daemon_handle_v6)
831     hostlist_task_v6 = prepare_daemon (daemon_handle_v6);
832   notify = GNUNET_PEERINFO_notify (cfg, GNUNET_NO,
833                                    &process_notify, NULL);
834   return GNUNET_OK;
835 }
836
837
838 /**
839  * Stop server offering our hostlist.
840  */
841 void
842 GNUNET_HOSTLIST_server_stop ()
843 {
844   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hostlist server shutdown\n");
845   if (GNUNET_SCHEDULER_NO_TASK != hostlist_task_v6)
846   {
847     GNUNET_SCHEDULER_cancel (hostlist_task_v6);
848     hostlist_task_v6 = GNUNET_SCHEDULER_NO_TASK;
849   }
850   if (GNUNET_SCHEDULER_NO_TASK != hostlist_task_v4)
851   {
852     GNUNET_SCHEDULER_cancel (hostlist_task_v4);
853     hostlist_task_v4 = GNUNET_SCHEDULER_NO_TASK;
854   }
855   if (NULL != daemon_handle_v4)
856   {
857     MHD_stop_daemon (daemon_handle_v4);
858     daemon_handle_v4 = NULL;
859   }
860   if (NULL != daemon_handle_v6)
861   {
862     MHD_stop_daemon (daemon_handle_v6);
863     daemon_handle_v6 = NULL;
864   }
865   if (NULL != response)
866   {
867     MHD_destroy_response (response);
868     response = NULL;
869   }
870   if (NULL != notify)
871   {
872     GNUNET_PEERINFO_notify_cancel (notify);
873     notify = NULL;
874   }
875   if (NULL != builder)
876   {
877     if (NULL != builder->pitr)
878     {
879       GNUNET_PEERINFO_iterate_cancel (builder->pitr);
880       builder->pitr = NULL;
881     }
882     GNUNET_free_non_null (builder->data);
883     GNUNET_free (builder);
884   }
885   if (NULL != peerinfo)
886   {
887     GNUNET_PEERINFO_disconnect (peerinfo);
888     peerinfo = NULL;
889   }
890   cfg = NULL;
891   stats = NULL;
892   core = NULL;
893 }
894
895 /* end of gnunet-daemon-hostlist_server.c */