twister

HTTP fault injector for testing
Log | Files | Refs | README | LICENSE

commit 697db24a3e937a1b5827b533f7b52730f95a1c31
parent edecea4cacfffc1ef82affb8c21a1af3dc418ca4
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 20 Jan 2018 13:53:31 +0100

code compiles

Diffstat:
Msrc/twister/taler-twister.c | 351+++++++++++++++++++++++++++++++++----------------------------------------------
1 file changed, 146 insertions(+), 205 deletions(-)

diff --git a/src/twister/taler-twister.c b/src/twister/taler-twister.c @@ -91,6 +91,39 @@ /** + * State machine for HTTP requests (per request). + */ +enum RequestState +{ + /** + * Starting state. + */ + REQUEST_STATE_WITH_MHD = 0, + + /** + * We've started receiving upload data from MHD. + */ + REQUEST_STATE_UPLOAD_STARTED, + + /** + * We've finished receiving upload data from MHD. + */ + REQUEST_STATE_UPLOAD_DONE, + + /** + * We've finished uploading data via CURL and can now download. + */ + REQUEST_STATE_DOWNLOAD_STARTED, + + /** + * We've finished receiving download data from cURL. + */ + REQUEST_STATE_DOWNLOAD_DONE + +}; + + +/** * A header list */ struct HttpResponseHeader @@ -124,16 +157,6 @@ struct HttpRequest { /** - * DLL. - */ - struct HttpRequest *next; - - /** - * DLL. - */ - struct HttpRequest *prev; - - /** * Client socket read task */ struct GNUNET_SCHEDULER_Task * rtask; @@ -218,6 +241,11 @@ struct HttpRequest */ struct HttpResponseHeader *header_tail; + /** + * Request processing state machine. + */ + enum RequestState state; + }; @@ -243,7 +271,7 @@ static CURLM *curl_multi; /** * The daemon handle */ -static struct MHD_Daemon *daemon; +static struct MHD_Daemon *mhd_daemon; /** * The task ID @@ -251,16 +279,6 @@ static struct MHD_Daemon *daemon; static struct GNUNET_SCHEDULER_Task *httpd_task; /** - * DLL of active socks requests. - */ -static struct HttpRequest *hr_head; - -/** - * DLL of active socks requests. - */ -static struct HttpRequest *hr_tail; - -/** * Response we return on cURL failures. */ static struct MHD_Response *curl_failure_response; @@ -270,17 +288,21 @@ static struct MHD_Response *curl_failure_response; */ static const struct GNUNET_CONFIGURATION_Handle *cfg; +/** + * Destination to which HTTP server we forward requests to. + * Of the format "http://servername:PORT" + */ +static char *target_server_base_url; + /* ************************* Global helpers ********************* */ /** * Run MHD now, we have extra data ready for the callback. - * - * @param hd the daemon to run now. */ static void -run_mhd_now (struct MhdHttpList *hd); +run_mhd_now (void); /* ************************* HTTP handling with cURL *********************** */ @@ -308,8 +330,8 @@ mhd_content_cb (void *cls, struct HttpRequest *hr = cls; size_t bytes_to_copy; - if ( (SOCKS5_SOCKET_UPLOAD_STARTED == hr->state) || - (SOCKS5_SOCKET_UPLOAD_DONE == hr->state) ) + if ( (REQUEST_STATE_UPLOAD_STARTED == hr->state) || + (REQUEST_STATE_UPLOAD_DONE == hr->state) ) { /* we're still not done with the upload, do not yet start the download, the IO buffer is still full @@ -321,7 +343,7 @@ mhd_content_cb (void *cls, bytes_to_copy = GNUNET_MIN (max, hr->io_len); if ( (0 == bytes_to_copy) && - (SOCKS5_SOCKET_DOWNLOAD_DONE != hr->state) ) + (REQUEST_STATE_DOWNLOAD_DONE != hr->state) ) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pausing MHD download, no data available\n"); @@ -333,14 +355,16 @@ mhd_content_cb (void *cls, return 0; /* more data later */ } if ( (0 == bytes_to_copy) && - (SOCKS5_SOCKET_DOWNLOAD_DONE == hr->state) ) + (REQUEST_STATE_DOWNLOAD_DONE == hr->state) ) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Completed MHD download\n"); return MHD_CONTENT_READER_END_OF_STREAM; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Writing %lu/%lu bytes\n", bytes_to_copy, hr->io_len); + "Writing %lu/%lu bytes\n", + (unsigned long) bytes_to_copy, + (unsigned long) hr->io_len); GNUNET_memcpy (buf, hr->io_buf, bytes_to_copy); memmove (hr->io_buf, &hr->io_buf[bytes_to_copy], @@ -371,23 +395,9 @@ curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls) size_t bytes = size * nmemb; char *ndup; const char *hdr_type; - const char *cookie_domain; char *hdr_val; - char *new_cookie_hdr; - char *new_location; - size_t offset; - size_t delta_cdomain; - int domain_matched; char *tok; - /* first, check SSL certificate */ - if ((GNUNET_YES != hr->ssl_checked) && - (HTTPS_PORT == hr->port)) - { - if (GNUNET_OK != check_ssl_certificate (hr)) - return 0; - } - ndup = GNUNET_strndup (buffer, bytes); hdr_type = strtok (ndup, ":"); if (NULL == hdr_type) @@ -406,6 +416,9 @@ curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls) /* custom logic for certain header types */ #if 0 + char *new_location; + char *new_cookie_hdr; + new_cookie_hdr = NULL; if ( (NULL != hr->leho) && (0 == strcasecmp (hdr_type, @@ -477,6 +490,8 @@ curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls) } GNUNET_free (leho_host); } + GNUNET_free_non_null (new_location); + GNUNET_free_non_null (new_cookie_hdr); #endif /* MHD does not allow certain characters in values, remove those */ if (NULL != (tok = strchr (hdr_val, '\n'))) @@ -499,8 +514,6 @@ curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls) header); } GNUNET_free (ndup); - GNUNET_free_non_null (new_cookie_hdr); - GNUNET_free_non_null (new_location); return bytes; } @@ -544,6 +557,7 @@ create_mhd_response_from_hr (struct HttpRequest *hr) header->value)); } +#if 0 if (NULL != hr->leho) { char *cors_hdr; @@ -560,6 +574,7 @@ create_mhd_response_from_hr (struct HttpRequest *hr) cors_hdr)); GNUNET_free (cors_hdr); } +#endif /* force connection to be closed after each request, as we do not support HTTP pipelining (yet, FIXME!) */ /*GNUNET_break (MHD_YES == @@ -588,8 +603,8 @@ curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx) if (NULL == hr->response) GNUNET_assert (GNUNET_OK == create_mhd_response_from_hr (hr)); - if ( (SOCKS5_SOCKET_UPLOAD_STARTED == hr->state) || - (SOCKS5_SOCKET_UPLOAD_DONE == hr->state) ) + if ( (REQUEST_STATE_UPLOAD_STARTED == hr->state) || + (REQUEST_STATE_UPLOAD_DONE == hr->state) ) { /* we're still not done with the upload, do not yet start the download, the IO buffer is still full @@ -601,8 +616,10 @@ curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx) if (sizeof (hr->io_buf) - hr->io_len < total) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Pausing CURL download, not enough space %lu %lu %lu\n", sizeof (hr->io_buf), - hr->io_len, total); + "Pausing CURL download, not enough space %lu %lu %lu\n", + (unsigned long) sizeof (hr->io_buf), + (unsigned long) hr->io_len, + (unsigned long) total); return CURL_WRITEFUNC_PAUSE; /* not enough space */ } GNUNET_memcpy (&hr->io_buf[hr->io_len], @@ -610,7 +627,7 @@ curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx) total); hr->io_len += total; if (hr->io_len == total) - run_mhd_now (hr->hd); + run_mhd_now (); return total; } @@ -633,22 +650,22 @@ curl_upload_cb (void *buf, size_t size, size_t nmemb, void *cls) size_t to_copy; if ( (0 == hr->io_len) && - (SOCKS5_SOCKET_UPLOAD_DONE != hr->state) ) + (REQUEST_STATE_UPLOAD_DONE != hr->state) ) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pausing CURL UPLOAD, need more data\n"); return CURL_READFUNC_PAUSE; } if ( (0 == hr->io_len) && - (SOCKS5_SOCKET_UPLOAD_DONE == hr->state) ) + (REQUEST_STATE_UPLOAD_DONE == hr->state) ) { - hr->state = SOCKS5_SOCKET_DOWNLOAD_STARTED; + hr->state = REQUEST_STATE_DOWNLOAD_STARTED; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Completed CURL UPLOAD\n"); return 0; /* upload finished, can now download */ } - if ( (SOCKS5_SOCKET_UPLOAD_STARTED != hr->state) && - (SOCKS5_SOCKET_UPLOAD_DONE != hr->state) ) + if ( (REQUEST_STATE_UPLOAD_STARTED != hr->state) && + (REQUEST_STATE_UPLOAD_DONE != hr->state) ) { GNUNET_break (0); return CURL_READFUNC_ABORT; @@ -661,7 +678,7 @@ curl_upload_cb (void *buf, size_t size, size_t nmemb, void *cls) hr->io_len - to_copy); hr->io_len -= to_copy; if (hr->io_len + to_copy == sizeof (hr->io_buf)) - run_mhd_now (hr->hd); /* got more space for upload now */ + run_mhd_now (); /* got more space for upload now */ return to_copy; } @@ -785,16 +802,16 @@ curl_task_download (void *cls) "CURL download completed.\n"); if (NULL == hr->response) GNUNET_assert (GNUNET_OK == create_mhd_response_from_hr (hr)); - hr->state = SOCKS5_SOCKET_DOWNLOAD_DONE; - run_mhd_now (hr->hd); + hr->state = REQUEST_STATE_DOWNLOAD_DONE; + run_mhd_now (); break; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Download curl failed: %s\n", curl_easy_strerror (msg->data.result)); /* FIXME: indicate error somehow? close MHD connection badly as well? */ - hr->state = SOCKS5_SOCKET_DOWNLOAD_DONE; - run_mhd_now (hr->hd); + hr->state = REQUEST_STATE_DOWNLOAD_DONE; + run_mhd_now (); break; } if (NULL == hr->response) @@ -831,16 +848,14 @@ curl_task_download (void *cls) /** * Read HTTP request header field from the request. Copies the fields - * over to the 'headers' that will be given to curl. However, 'Host' - * is substituted with the LEHO if present. We also change the - * 'Connection' header value to "close" as the proxy does not support - * pipelining. + * over to the 'headers' that will be given to curl. However, + * substitutions may be applied. * * @param cls our `struct HttpRequest` * @param kind value kind * @param key field key * @param value field value - * @return MHD_YES to continue to iterate + * @return #MHD_YES to continue to iterate */ static int con_val_iter (void *cls, @@ -851,13 +866,11 @@ con_val_iter (void *cls, struct HttpRequest *hr = cls; char *hdr; +#if 0 if ( (0 == strcasecmp (MHD_HTTP_HEADER_HOST, key)) && (NULL != hr->leho) ) value = hr->leho; - if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_LENGTH, key)) - return MHD_YES; - if (0 == strcasecmp (MHD_HTTP_HEADER_ACCEPT_ENCODING, key)) - return MHD_YES; +#endif GNUNET_asprintf (&hdr, "%s: %s", key, @@ -866,7 +879,7 @@ con_val_iter (void *cls, "Adding HEADER `%s' to HTTP request\n", hdr); hr->headers = curl_slist_append (hr->headers, - hdr); + hdr); GNUNET_free (hdr); return MHD_YES; } @@ -907,13 +920,6 @@ create_response (void *cls, { struct HttpRequest *hr = *con_cls; char *curlurl; - char *curl_hosts; - char ipstring[INET6_ADDRSTRLEN]; - char ipaddr[INET6_ADDRSTRLEN + 2]; - const struct sockaddr *sa; - const struct sockaddr_in *s4; - const struct sockaddr_in6 *s6; - uint16_t port; size_t left; if (NULL == hr) @@ -922,55 +928,17 @@ create_response (void *cls, return MHD_NO; } //Fresh connection. - if (SOCKS5_SOCKET_WITH_MHD == hr->state) + if (REQUEST_STATE_WITH_MHD == hr->state) { - /* first time here, initialize curl handle */ - sa = (const struct sockaddr *) &hr->destination_address; - switch (sa->sa_family) - { - case AF_INET: - s4 = (const struct sockaddr_in *) &hr->destination_address; - if (NULL == inet_ntop (AF_INET, - &s4->sin_addr, - ipstring, - sizeof (ipstring))) - { - GNUNET_break (0); - return MHD_NO; - } - GNUNET_snprintf (ipaddr, - sizeof (ipaddr), - "%s", - ipstring); - port = ntohs (s4->sin_port); - break; - case AF_INET6: - s6 = (const struct sockaddr_in6 *) &hr->destination_address; - if (NULL == inet_ntop (AF_INET6, - &s6->sin6_addr, - ipstring, - sizeof (ipstring))) - { - GNUNET_break (0); - return MHD_NO; - } - GNUNET_snprintf (ipaddr, - sizeof (ipaddr), - "%s", - ipstring); - port = ntohs (s6->sin6_port); - break; - default: - GNUNET_break (0); - return MHD_NO; - } if (NULL == hr->curl) hr->curl = curl_easy_init (); if (NULL == hr->curl) return MHD_queue_response (con, MHD_HTTP_INTERNAL_SERVER_ERROR, curl_failure_response); - curl_easy_setopt (hr->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr); + curl_easy_setopt (hr->curl, + CURLOPT_HEADERFUNCTION, + &curl_check_hdr); curl_easy_setopt (hr->curl, CURLOPT_HEADERDATA, hr); curl_easy_setopt (hr->curl, CURLOPT_FOLLOWLOCATION, 0); curl_easy_setopt (hr->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); @@ -982,30 +950,9 @@ create_response (void *cls, curl_easy_setopt (hr->curl, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt (hr->curl, CURLOPT_PRIVATE, hr); curl_easy_setopt (hr->curl, CURLOPT_VERBOSE, 0); - /** - * Pre-populate cache to resolve Hostname. - * This is necessary as the DNS name in the CURLOPT_URL is used - * for SNI http://de.wikipedia.org/wiki/Server_Name_Indication - */ - if (NULL != hr->leho) - { - GNUNET_asprintf (&curl_hosts, - "%s:%d:%s", - hr->leho, - port, - ipaddr); - hr->hosts = curl_slist_append(NULL, curl_hosts); - curl_easy_setopt(hr->curl, CURLOPT_RESOLVE, hr->hosts); - GNUNET_free (curl_hosts); - } GNUNET_asprintf (&curlurl, - (HTTPS_PORT != hr->port) - ? "http://%s:%d%s" - : "https://%s:%d%s", - (NULL != hr->leho) - ? hr->leho - : ipaddr, - port, + "%s%s", + target_server_base_url, hr->url); curl_easy_setopt (hr->curl, CURLOPT_URL, @@ -1013,37 +960,47 @@ create_response (void *cls, GNUNET_free (curlurl); if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT)) { - hr->state = SOCKS5_SOCKET_UPLOAD_STARTED; + hr->state = REQUEST_STATE_UPLOAD_STARTED; curl_easy_setopt (hr->curl, CURLOPT_UPLOAD, 1); - curl_easy_setopt (hr->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb); + curl_easy_setopt (hr->curl, + CURLOPT_WRITEFUNCTION, + &curl_download_cb); curl_easy_setopt (hr->curl, CURLOPT_WRITEDATA, hr); - curl_easy_setopt (hr->curl, CURLOPT_READFUNCTION, &curl_upload_cb); + curl_easy_setopt (hr->curl, + CURLOPT_READFUNCTION, + &curl_upload_cb); curl_easy_setopt (hr->curl, CURLOPT_READDATA, hr); } else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST)) { - hr->state = SOCKS5_SOCKET_UPLOAD_STARTED; + hr->state = REQUEST_STATE_UPLOAD_STARTED; curl_easy_setopt (hr->curl, CURLOPT_POST, 1L); - curl_easy_setopt (hr->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb); + curl_easy_setopt (hr->curl, + CURLOPT_WRITEFUNCTION, + &curl_download_cb); curl_easy_setopt (hr->curl, CURLOPT_WRITEDATA, hr); - curl_easy_setopt (hr->curl, CURLOPT_READFUNCTION, &curl_upload_cb); + curl_easy_setopt (hr->curl, + CURLOPT_READFUNCTION, + &curl_upload_cb); curl_easy_setopt (hr->curl, CURLOPT_READDATA, hr); } else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD)) { - hr->state = SOCKS5_SOCKET_DOWNLOAD_STARTED; + hr->state = REQUEST_STATE_DOWNLOAD_STARTED; curl_easy_setopt (hr->curl, CURLOPT_NOBODY, 1); } else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_OPTIONS)) { - hr->state = SOCKS5_SOCKET_DOWNLOAD_STARTED; + hr->state = REQUEST_STATE_DOWNLOAD_STARTED; curl_easy_setopt (hr->curl, CURLOPT_CUSTOMREQUEST, "OPTIONS"); } else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_GET)) { - hr->state = SOCKS5_SOCKET_DOWNLOAD_STARTED; + hr->state = REQUEST_STATE_DOWNLOAD_STARTED; curl_easy_setopt (hr->curl, CURLOPT_HTTPGET, 1); - curl_easy_setopt (hr->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb); + curl_easy_setopt (hr->curl, + CURLOPT_WRITEFUNCTION, + &curl_download_cb); curl_easy_setopt (hr->curl, CURLOPT_WRITEDATA, hr); } else @@ -1069,21 +1026,7 @@ create_response (void *cls, curl_easy_setopt (hr->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE); } - if (HTTPS_PORT == hr->port) - { - curl_easy_setopt (hr->curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); - if (NULL != hr->dane_data) - curl_easy_setopt (hr->curl, CURLOPT_SSL_VERIFYPEER, 0L); - else - curl_easy_setopt (hr->curl, CURLOPT_SSL_VERIFYPEER, 1L); - /* Disable cURL checking the hostname, as we will check ourselves - as only we have the domain name or the LEHO or the DANE record */ - curl_easy_setopt (hr->curl, CURLOPT_SSL_VERIFYHOST, 0L); - } - else - { - curl_easy_setopt (hr->curl, CURLOPT_USE_SSL, CURLUSESSL_NONE); - } + curl_easy_setopt (hr->curl, CURLOPT_USE_SSL, CURLUSESSL_NONE); if (CURLM_OK != curl_multi_add_handle (curl_multi, hr->curl)) { @@ -1094,8 +1037,11 @@ create_response (void *cls, } MHD_get_connection_values (con, MHD_HEADER_KIND, - &con_val_iter, hr); - curl_easy_setopt (hr->curl, CURLOPT_HTTPHEADER, hr->headers); + &con_val_iter, + hr); + curl_easy_setopt (hr->curl, + CURLOPT_HTTPHEADER, + hr->headers); curl_download_prepare (); return MHD_YES; } @@ -1124,17 +1070,17 @@ create_response (void *cls, curl_easy_pause (hr->curl, CURLPAUSE_CONT); return MHD_YES; } - if (SOCKS5_SOCKET_UPLOAD_STARTED == hr->state) + if (REQUEST_STATE_UPLOAD_STARTED == hr->state) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Finished processing UPLOAD\n"); - hr->state = SOCKS5_SOCKET_UPLOAD_DONE; + hr->state = REQUEST_STATE_UPLOAD_DONE; } if (NULL == hr->response) return MHD_YES; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Queueing response with MHD\n"); - run_mhd_now (hr->hd); + run_mhd_now (); return MHD_queue_response (con, hr->response_code, hr->response); @@ -1246,8 +1192,8 @@ mhd_log_callback (void *cls, static void kill_httpd (void) { - MHD_stop_daemon (daemon); - daemon = NULL; + MHD_stop_daemon (mhd_daemon); + mhd_daemon = NULL; if (NULL != httpd_task) { GNUNET_SCHEDULER_cancel (httpd_task); @@ -1288,16 +1234,16 @@ schedule_httpd (void) FD_ZERO (&es); max = -1; if (MHD_YES != - MHD_get_fdset (daemon, + MHD_get_fdset (mhd_daemon, &rs, &ws, &es, &max)) { - kill_httpd (NULL); + kill_httpd (); return; } - haveto = MHD_get_timeout (daemon, + haveto = MHD_get_timeout (mhd_daemon, &timeout); if (MHD_YES == haveto) tv.rel_value_us = (uint64_t) timeout * 1000LL; @@ -1347,8 +1293,8 @@ static void do_httpd (void *cls) { httpd_task = NULL; - MHD_run (daemon); - schedule_httpd (NULL); + MHD_run (mhd_daemon); + schedule_httpd (); } @@ -1365,23 +1311,6 @@ run_mhd_now (void) } - -/** - * Function called by MHD with errors, suppresses them all. - * - * @param cls closure - * @param fm format string (`printf()`-style) - * @param ap arguments to @a fm - */ -static void -mhd_error_log_callback (void *cls, - const char *fm, - va_list ap) -{ - /* do nothing */ -} - - /* ******************* General / main code ********************* */ @@ -1437,20 +1366,32 @@ run (void *cls, "Failed to create cURL multi handle!\n"); return; } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (c, + "twister", + "DESTINATION_BASE_URL", + &target_server_base_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "twister", + "DESTINATION_BASE_URL"); + GNUNET_SCHEDULER_shutdown (); + return; + } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proxy listens on port %llu\n", port); /* start MHD daemon for HTTP */ - daemon = MHD_start_daemon (MHD_USE_DEBUG, - port, - NULL, NULL, - &create_response, NULL, - MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16, - MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL, - MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL, - MHD_OPTION_END); - if (NULL == daemon) + mhd_daemon = MHD_start_daemon (MHD_USE_DEBUG, + port, + NULL, NULL, + &create_response, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16, + MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL, + MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL, + MHD_OPTION_END); + if (NULL == mhd_daemon) { GNUNET_break (0); GNUNET_SCHEDULER_shutdown ();