/*
This file is part of GNUnet.
- (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
+ (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010 Christian Grothoff (and other contributing authors)
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
#include "gnunet_transport_service.h"
#include <curl/curl.h>
+#define DEBUG_HOSTLIST_CLIENT GNUNET_NO
+
/**
* Number of connections that we must have to NOT download
* hostlists anymore.
*/
static unsigned int connection_count;
+/**
+ * At what time MUST the current hostlist request be done?
+ */
+static struct GNUNET_TIME_Absolute end_time;
+
/**
* Process downloaded bits by calling callback on each HELLO.
break;
if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message*)msg) == msize)
{
+#if DEBUG_HOSTLIST_CLIENT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received valid `%s' message from hostlist server.\n",
"HELLO");
+#endif
GNUNET_TRANSPORT_offer_hello (transport, msg);
}
else
}
+/**
+ * Ask CURL for the select set and then schedule the
+ * receiving task with the scheduler.
+ */
+static void
+run_multi ();
+
+
/**
* Task that is run when we are ready to receive more data from the hostlist
* server.
int running;
struct CURLMsg *msg;
CURLMcode mret;
-
+
if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
{
+#if DEBUG_HOSTLIST_CLIENT
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Shutdown requested while trying to download hostlist from `%s'\n",
+ current_url);
+#endif
clean_up ();
return;
}
- if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
+ if (GNUNET_TIME_absolute_get_remaining (end_time).value == 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_("Timeout trying to download hostlist from `%s'\n"),
clean_up ();
return;
}
+#if DEBUG_HOSTLIST_CLIENT
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Ready for processing hostlist client request\n");
+#endif
do
{
running = 0;
switch (msg->msg)
{
case CURLMSG_DONE:
- if (msg->data.result != CURLE_OK)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Download of hostlist `%s' completed.\n"),
+ current_url);
+ if ( (msg->data.result != CURLE_OK) &&
+ (msg->data.result != CURLE_GOT_NOTHING) )
GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
_("%s failed at %s:%d: `%s'\n"),
"curl_multi_perform", __FILE__,
__LINE__,
- curl_easy_strerror (msg->data.result));
- break;
+ curl_easy_strerror (msg->data.result));
+ clean_up ();
+ return;
default:
break;
}
"curl_multi_perform", __FILE__, __LINE__,
curl_multi_strerror (mret));
clean_up ();
- return;
}
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("Download of hostlist `%s' completed.\n"),
- current_url);
- clean_up ();
+ run_multi ();
}
int max;
struct GNUNET_NETWORK_FDSet *grs;
struct GNUNET_NETWORK_FDSet *gws;
+ long timeout;
+ struct GNUNET_TIME_Relative rtime;
- max = 0;
+ max = -1;
FD_ZERO (&rs);
FD_ZERO (&ws);
FD_ZERO (&es);
clean_up ();
return;
}
+ mret = curl_multi_timeout (multi, &timeout);
+ if (mret != CURLM_OK)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("%s failed at %s:%d: `%s'\n"),
+ "curl_multi_timeout", __FILE__, __LINE__,
+ curl_multi_strerror (mret));
+ clean_up ();
+ return;
+ }
+ rtime = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ timeout));
grs = GNUNET_NETWORK_fdset_create ();
gws = GNUNET_NETWORK_fdset_create ();
- GNUNET_NETWORK_fdset_copy_native (grs, &rs, max);
- GNUNET_NETWORK_fdset_copy_native (gws, &ws, max);
+ GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
+ GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
+#if DEBUG_HOSTLIST_CLIENT
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Scheduling task for hostlist download using cURL\n");
+#endif
current_task
= GNUNET_SCHEDULER_add_select (sched,
GNUNET_SCHEDULER_PRIORITY_DEFAULT,
GNUNET_SCHEDULER_NO_TASK,
- GNUNET_TIME_UNIT_MINUTES,
+ rtime,
grs,
gws,
&multi_ready,
clean_up ();
return;
}
- transport = GNUNET_TRANSPORT_connect (sched, cfg, NULL, NULL, NULL, NULL);
current_url = get_url ();
GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
_("Bootstrapping using hostlist at `%s'.\n"),
return;
}
CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
+ CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
/* no need to abort if the above failed */
CURL_EASY_SETOPT (curl,
CURLOPT_URL,
CURL_EASY_SETOPT (curl,
CURLOPT_FAILONERROR,
1);
+#if 0
+ CURL_EASY_SETOPT (curl,
+ CURLOPT_VERBOSE,
+ 1);
+#endif
CURL_EASY_SETOPT (curl,
CURLOPT_BUFFERSIZE,
GNUNET_SERVER_MAX_MESSAGE_SIZE);
CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
CURL_EASY_SETOPT (curl,
CURLOPT_CONNECTTIMEOUT,
- 150L);
+ 60L);
+ CURL_EASY_SETOPT (curl,
+ CURLOPT_TIMEOUT,
+ 60L);
+#if 0
+ /* this should no longer be needed; we're now single-threaded! */
CURL_EASY_SETOPT (curl,
CURLOPT_NOSIGNAL,
1);
+#endif
multi = curl_multi_init ();
if (multi == NULL)
{
clean_up ();
return;
}
- run_multi (multi);
+ end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
+ run_multi ();
}
hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
else
hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
- if (hostlist_delay.value > GNUNET_TIME_UNIT_HOURS.value * connection_count)
- hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_DAYS,
- connection_count);
+ if (hostlist_delay.value > GNUNET_TIME_UNIT_HOURS.value * (1 + connection_count))
+ hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
+ (1 + connection_count));
GNUNET_STATISTICS_set (stats,
gettext_noop("Minimum time between hostlist downloads"),
hostlist_delay.value,
GNUNET_YES);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Will consider downloading hostlist in %llums\n"),
+ (unsigned long long) delay.value);
current_task = GNUNET_SCHEDULER_add_delayed (sched,
delay,
&check_task,
{
if (stats == NULL)
return; /* in shutdown */
+#if DEBUG_HOSTLIST_CLIENT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Statistics request done, scheduling hostlist download\n");
+#endif
schedule_hostlist_task ();
}
GNUNET_break (0);
return GNUNET_SYSERR;
}
+ transport = GNUNET_TRANSPORT_connect (s, c, NULL, NULL, NULL, NULL);
+ if (NULL == transport)
+ {
+ curl_global_cleanup ();
+ return GNUNET_SYSERR;
+ }
cfg = c;
sched = s;
stats = st;
void
GNUNET_HOSTLIST_client_stop ()
{
+#if DEBUG_HOSTLIST_CLIENT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Hostlist client shutdown\n");
+#endif
if (current_task != GNUNET_SCHEDULER_NO_TASK)
{
GNUNET_SCHEDULER_cancel (sched,
current_task);
- if (transport != NULL)
- {
- GNUNET_TRANSPORT_disconnect (transport);
- transport = NULL;
- }
curl_global_cleanup ();
}
+ if (transport != NULL)
+ {
+ GNUNET_TRANSPORT_disconnect (transport);
+ transport = NULL;
+ }
GNUNET_assert (NULL == transport);
GNUNET_free_non_null (proxy);
proxy = NULL;
#include "gnunet_hello_lib.h"
#include "gnunet_peerinfo_service.h"
+#define DEBUG_HOSTLIST_SERVER GNUNET_NO
+
/**
* How often should we recalculate our response to hostlist requests?
*/
#define RESPONSE_UPDATE_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
/**
- * Handle to the HTTP server as provided by libmicrohttpd.
+ * Handle to the HTTP server as provided by libmicrohttpd for IPv6.
+ */
+static struct MHD_Daemon *daemon_handle_v6;
+
+/**
+ * Handle to the HTTP server as provided by libmicrohttpd for IPv4.
*/
-static struct MHD_Daemon *daemon_handle;
+static struct MHD_Daemon *daemon_handle_v4;
/**
* Our configuration.
static struct GNUNET_SCHEDULER_Handle *sched;
/**
- * Our primary task.
+ * Our primary task for IPv4.
+ */
+static GNUNET_SCHEDULER_TaskIdentifier hostlist_task_v4;
+
+/**
+ * Our primary task for IPv6.
*/
-static GNUNET_SCHEDULER_TaskIdentifier hostlist_task;
+static GNUNET_SCHEDULER_TaskIdentifier hostlist_task_v6;
/**
* Task that updates our HTTP response.
if (response != NULL)
MHD_destroy_response (response);
+#if DEBUG_HOSTLIST_SERVER
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Creating hostlist response with %u bytes\n",
+ (unsigned int) results->size);
+#endif
response = MHD_create_response_from_data (results->size,
results->data, MHD_YES, MHD_NO);
- if (daemon_handle != NULL)
+ if ( (daemon_handle_v4 != NULL) ||
+ (daemon_handle_v6 != NULL) )
{
freq = RESPONSE_UPDATE_FREQUENCY;
if (results->size == 0)
}
else
{
+ /* already past shutdown */
MHD_destroy_response (response);
response = NULL;
}
}
old = results->size;
s = GNUNET_HELLO_size(hello);
+#if DEBUG_HOSTLIST_SERVER
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received %u bytes of `%s' from peer `%s' for hostlist.\n",
+ (unsigned int) s,
+ "HELLO",
+ GNUNET_i2s (peer));
+#endif
if (old + s >= GNUNET_MAX_MALLOC_CHECKED)
return; /* too large, skip! */
GNUNET_array_grow (results->data,
GNUNET_TIME_UNIT_MINUTES,
&host_processor,
results);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("Created new hostlist response with %u bytes\n"),
- results->size);
}
{
if (NULL == response)
{
+#if DEBUG_HOSTLIST_SERVER
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received request for hostlist, but I am not yet ready; rejecting!\n");
+#endif
return MHD_NO;
}
return MHD_YES; /* accept all */
static int dummy;
if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
- return MHD_NO;
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Refusing `%s' request to hostlist server\n"),
+ method);
+ return MHD_NO;
+ }
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Received request for our hostlist\n"));
if (NULL == *con_cls)
{
(*con_cls) = &dummy;
+#if DEBUG_HOSTLIST_SERVER
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ _("Sending 100 CONTINUE reply\n"));
+#endif
return MHD_YES; /* send 100 continue */
}
if (*upload_data_size != 0)
- return MHD_NO; /* do not support upload data */
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Refusing `%s' request with %llu bytes of upload data\n"),
+ method,
+ (unsigned long long) *upload_data_size);
+ return MHD_NO; /* do not support upload data */
+ }
if (response == NULL)
- return MHD_NO; /* internal error, no response yet */
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Could not handle hostlist request since I do not have a response yet\n"));
+ return MHD_NO; /* internal error, no response yet */
+ }
return MHD_queue_response (connection, MHD_HTTP_OK, response);
}
* Function that queries MHD's select sets and
* starts the task waiting for them.
*/
-static void
-prepare_daemon (void);
-
+static GNUNET_SCHEDULER_TaskIdentifier
+prepare_daemon (struct MHD_Daemon *daemon_handle);
/**
* Call MHD to process pending requests and then go back
run_daemon (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
+ struct MHD_Daemon *daemon_handle = cls;
+
+ if (daemon_handle == daemon_handle_v4)
+ hostlist_task_v4 = GNUNET_SCHEDULER_NO_TASK;
+ else
+ hostlist_task_v6 = GNUNET_SCHEDULER_NO_TASK;
+
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ return;
GNUNET_assert (MHD_YES == MHD_run (daemon_handle));
- prepare_daemon ();
+ if (daemon_handle == daemon_handle_v4)
+ hostlist_task_v4 = prepare_daemon (daemon_handle);
+ else
+ hostlist_task_v6 = prepare_daemon (daemon_handle);
}
* Function that queries MHD's select sets and
* starts the task waiting for them.
*/
-static void
-prepare_daemon ()
+static GNUNET_SCHEDULER_TaskIdentifier
+prepare_daemon (struct MHD_Daemon *daemon_handle)
{
+ GNUNET_SCHEDULER_TaskIdentifier ret;
fd_set rs;
fd_set ws;
fd_set es;
GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max);
GNUNET_NETWORK_fdset_copy_native (wws, &ws, max);
GNUNET_NETWORK_fdset_copy_native (wes, &es, max);
- hostlist_task
- = GNUNET_SCHEDULER_add_select (sched,
- GNUNET_SCHEDULER_PRIORITY_HIGH,
- GNUNET_SCHEDULER_NO_TASK,
- tv,
- wrs,
- wws,
- &run_daemon,
- NULL);
+ ret = GNUNET_SCHEDULER_add_select (sched,
+ GNUNET_SCHEDULER_PRIORITY_HIGH,
+ GNUNET_SCHEDULER_NO_TASK,
+ tv,
+ wrs,
+ wws,
+ &run_daemon,
+ daemon_handle);
GNUNET_NETWORK_fdset_destroy (wrs);
GNUNET_NETWORK_fdset_destroy (wws);
GNUNET_NETWORK_fdset_destroy (wes);
+ return ret;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Hostlist service starts on port %llu\n"),
port);
- daemon_handle = MHD_start_daemon (MHD_USE_IPv6,
- (unsigned short) port,
- &accept_policy_callback,
- NULL,
- &access_handler_callback,
- NULL,
- MHD_OPTION_CONNECTION_LIMIT, 16,
- MHD_OPTION_PER_IP_CONNECTION_LIMIT, 1,
- MHD_OPTION_CONNECTION_TIMEOUT, 16,
- MHD_OPTION_CONNECTION_MEMORY_LIMIT,
- 16 * 1024, MHD_OPTION_END);
- if (daemon_handle == NULL)
+ daemon_handle_v6 = MHD_start_daemon (MHD_USE_IPv6
+#if DEBUG_HOSTLIST_SERVER
+ | MHD_USE_DEBUG
+#endif
+ ,
+ (unsigned short) port,
+ &accept_policy_callback,
+ NULL,
+ &access_handler_callback,
+ NULL,
+ MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
+ MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
+ MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
+ MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
+ MHD_OPTION_END);
+ daemon_handle_v4 = MHD_start_daemon (MHD_NO_FLAG
+#if DEBUG_HOSTLIST_SERVER
+ | MHD_USE_DEBUG
+#endif
+ ,
+ (unsigned short) port,
+ &accept_policy_callback,
+ NULL,
+ &access_handler_callback,
+ NULL,
+ MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
+ MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
+ MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
+ MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
+ MHD_OPTION_END);
+
+ if ( (daemon_handle_v6 == NULL) &&
+ (daemon_handle_v4 == NULL) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Could not start hostlist HTTP server on port %u\n"),
(unsigned short) port);
return GNUNET_SYSERR;
}
- prepare_daemon ();
+ if (daemon_handle_v4 != NULL)
+ hostlist_task_v4 = prepare_daemon (daemon_handle_v4);
+ if (daemon_handle_v6 != NULL)
+ hostlist_task_v6 = prepare_daemon (daemon_handle_v6);
response_task = GNUNET_SCHEDULER_add_now (sched,
&update_response,
NULL);
void
GNUNET_HOSTLIST_server_stop ()
{
+#if DEBUG_HOSTLIST_SERVER
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Hostlist server shutdown\n");
- if (GNUNET_SCHEDULER_NO_TASK != hostlist_task)
+#endif
+ if (GNUNET_SCHEDULER_NO_TASK != hostlist_task_v6)
+ {
+ GNUNET_SCHEDULER_cancel (sched, hostlist_task_v6);
+ hostlist_task_v6 = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (GNUNET_SCHEDULER_NO_TASK != hostlist_task_v4)
{
- GNUNET_SCHEDULER_cancel (sched, hostlist_task);
- hostlist_task = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_SCHEDULER_cancel (sched, hostlist_task_v4);
+ hostlist_task_v4 = GNUNET_SCHEDULER_NO_TASK;
}
if (GNUNET_SCHEDULER_NO_TASK != response_task)
{
GNUNET_SCHEDULER_cancel (sched, response_task);
response_task = GNUNET_SCHEDULER_NO_TASK;
}
- if (NULL != daemon_handle)
+ if (NULL != daemon_handle_v4)
+ {
+ MHD_stop_daemon (daemon_handle_v4);
+ daemon_handle_v4 = NULL;
+ }
+ if (NULL != daemon_handle_v6)
{
- MHD_stop_daemon (daemon_handle);
- daemon_handle = NULL;
+ MHD_stop_daemon (daemon_handle_v6);
+ daemon_handle_v6 = NULL;
}
if (response != NULL)
{