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