X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fgns%2Fgnunet-gns-proxy.c;h=cdc18e3f6b156ca8bfab88ffcf423f4ce292c10a;hb=bcbd694ad35e182e5a1486b545ea0706082ee94a;hp=d14e8f8724b759f209d43e50a43ecbef404783f9;hpb=7b41c0d38839006fafa1e691afc740b2b6088dc0;p=oweals%2Fgnunet.git diff --git a/src/gns/gnunet-gns-proxy.c b/src/gns/gnunet-gns-proxy.c index d14e8f872..cdc18e3f6 100644 --- a/src/gns/gnunet-gns-proxy.c +++ b/src/gns/gnunet-gns-proxy.c @@ -34,9 +34,12 @@ #include #include +#define HAVE_MHD_NO_LISTEN_SOCKET MHD_VERSION >= 0x00091401 + #define GNUNET_GNS_PROXY_PORT 7777 #define MHD_MAX_CONNECTIONS 300 #define MAX_HTTP_URI_LENGTH 2048 +#define POSTBUFFERSIZE 4096 /* MHD/cURL defines */ #define BUF_WAIT_FOR_CURL 0 @@ -188,9 +191,15 @@ struct ProxyCurlTask /* DLL for tasks */ struct ProxyCurlTask *next; + /* Already accepted */ + int accepted; + /* Handle to cURL */ CURL *curl; + /* is curl running? */ + int curl_running; + /* Optional header replacements for curl (LEHO) */ struct curl_slist *headers; @@ -251,6 +260,9 @@ struct ProxyCurlTask /* The hostname (Host header field) */ char host[256]; + /* The port */ + uint16_t port; + /* The LEgacy HOstname (can be empty) */ char leho[256]; @@ -278,6 +290,24 @@ struct ProxyCurlTask /*put*/ size_t put_read_offset; size_t put_read_size; + + /*post*/ + struct MHD_PostProcessor *post_handler; + + /* post data */ + struct ProxyUploadData *upload_data_head; + struct ProxyUploadData *upload_data_tail; + + int post_done; + + /* the type of POST encoding */ + char* post_type; + + struct curl_httppost *httppost; + + struct curl_httppost *httppost_last; + + int is_httppost; }; @@ -329,6 +359,36 @@ struct ProxySetCookieHeader char *cookie; }; +/** + * Post data structure + */ +struct ProxyUploadData +{ + /* DLL */ + struct ProxyUploadData *next; + + /* DLL */ + struct ProxyUploadData *prev; + + char *key; + + char *filename; + + char *content_type; + + size_t content_length; + + /* value */ + char *value; + + /* to copy */ + size_t bytes_left; + + /* size */ + size_t total_bytes; +}; + + /* The port the proxy is running on (default 7777) */ static unsigned long port = GNUNET_GNS_PROXY_PORT; @@ -372,13 +432,13 @@ static struct MhdHttpList *mhd_httpd_tail; static regex_t re_dotplus; /* The users local GNS zone hash */ -static struct GNUNET_CRYPTO_ShortHashCode local_gns_zone; +static struct GNUNET_CRYPTO_ShortHashCode *local_gns_zone; /* The users local private zone */ -static struct GNUNET_CRYPTO_ShortHashCode local_private_zone; +static struct GNUNET_CRYPTO_ShortHashCode *local_private_zone; /* The users local shorten zone */ -static struct GNUNET_CRYPTO_ShortHashCode local_shorten_zone; +static struct GNUNET_CRYPTO_ShortHashCode *local_shorten_zone; /* The CA for SSL certificate generation */ static struct ProxyCA proxy_ca; @@ -415,21 +475,183 @@ is_tld(const char* name, const char* tld) return GNUNET_YES; } +/** + * convert integer to string representation + * + * @param i integer + * @return the character + */ +char i_to_hexchar (char i) +{ + static char hexmap[] = "0123456789abcdef"; + GNUNET_assert (sizeof (hexmap) > (i & 15)); + return hexmap[i & 15]; +} + +/** + * Escape given 0-terminated string + * + * @param to_esc string to escapse + * @return allocated new escaped string (MUST free!) + */ +static char* +escape_to_urlenc (const char *to_esc) +{ + char *pos = (char*)to_esc; + char *res = GNUNET_malloc (strlen (to_esc) * 3 + 1); + char *rpos = res; + + while ('\0' != *pos) + { + if (isalnum (*pos) || + ('-' == *pos) || ('_' == *pos) || + ('.' == *pos) || ('~' == *pos)) + *rpos++ = *pos; + else if (' ' == *pos) + *rpos++ = '+'; + else + { + *rpos++ = '%'; + *rpos++ = i_to_hexchar (*pos >> 4); + *rpos++ = i_to_hexchar (*pos >> 15); + } + pos++; + } + *rpos = '\0'; + return res; +} + static int -get_uri_val_iter (void *cls, +con_post_data_iter (void *cls, enum MHD_ValueKind kind, const char *key, - const char *value) + const char *filename, + const char *content_type, + const char *transfer_encoding, + const char *data, + uint64_t off, + size_t size) { - char* buf = cls; + struct ProxyCurlTask* ctask = cls; + struct ProxyUploadData* pdata; + char* enc; + char* new_value; - if (strlen (buf) + strlen (value) + 3 > MAX_HTTP_URI_LENGTH) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got POST data: '%s : %s' at offset %llu size %lld\n", + key, data, off, size); + + GNUNET_assert (NULL != ctask->post_type); + + if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, + ctask->post_type)) + { + ctask->is_httppost = GNUNET_YES; + /* new part */ + if (0 == off) + { + pdata = GNUNET_malloc (sizeof (struct ProxyUploadData)); + pdata->key = strdup (key); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Copied %lld\n"); + + if (NULL != filename) + { + pdata->filename = strdup (filename); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Filename %s\n", filename); + } + + if (NULL != content_type) + { + pdata->content_type = strdup (content_type); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Content-Type %s\n", content_type); + } + + pdata->value = GNUNET_malloc (size); + pdata->total_bytes = size; + memcpy (pdata->value, data, size); + GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head, + ctask->upload_data_tail, + pdata); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Copied %lld bytes of POST Data\n", size); + return MHD_YES; + } + + pdata = ctask->upload_data_tail; + new_value = GNUNET_malloc (size + pdata->total_bytes); + memcpy (new_value, pdata->value, pdata->total_bytes); + memcpy (new_value+off, data, size); + GNUNET_free (pdata->value); + pdata->value = new_value; + pdata->total_bytes += size; + + return MHD_YES; + + } + + if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, + ctask->post_type)) + { return MHD_NO; - sprintf (buf+strlen (buf), "?%s=%s", key, value); + } + + ctask->is_httppost = GNUNET_NO; + + if (NULL != ctask->curl) + curl_easy_pause (ctask->curl, CURLPAUSE_CONT); + + if (0 == off) + { + /* a key */ + pdata = GNUNET_malloc (sizeof (struct ProxyUploadData)); + enc = escape_to_urlenc (key); + pdata->value = GNUNET_malloc (strlen (enc) + 3); + if (NULL != ctask->upload_data_head) + { + pdata->value[0] = '&'; + memcpy (pdata->value+1, enc, strlen (enc)); + } + else + memcpy (pdata->value, enc, strlen (enc)); + pdata->value[strlen (pdata->value)] = '='; + pdata->bytes_left = strlen (pdata->value); + pdata->total_bytes = pdata->bytes_left; + GNUNET_free (enc); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Escaped POST key: '%s'\n", + pdata->value); + + GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head, + ctask->upload_data_tail, + pdata); + } + + /* a value */ + pdata = GNUNET_malloc (sizeof (struct ProxyUploadData)); + enc = escape_to_urlenc (data); + pdata->value = GNUNET_malloc (strlen (enc) + 1); + memcpy (pdata->value, enc, strlen (enc)); + pdata->bytes_left = strlen (pdata->value); + pdata->total_bytes = pdata->bytes_left; + GNUNET_free (enc); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Escaped POST value: '%s'\n", + pdata->value); + GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head, + ctask->upload_data_tail, + pdata); return MHD_YES; } + /** * Read HTTP request header field 'Host' * @@ -447,12 +669,28 @@ con_val_iter (void *cls, { struct ProxyCurlTask *ctask = cls; char* buf = ctask->host; + char* port; char* cstr; const char* hdr_val; + unsigned int uport; if (0 == strcmp ("Host", key)) { - strcpy (buf, value); + port = strstr (value, ":"); + if (NULL != port) + { + strncpy (buf, value, port-value); + port++; + if ((1 != sscanf (port, "%u", &uport)) || + (uport > UINT16_MAX) || + (0 == uport)) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to parse port!\n"); + else + ctask->port = (uint16_t) uport; + } + else + strcpy (buf, value); return MHD_YES; } @@ -461,6 +699,22 @@ con_val_iter (void *cls, else hdr_val = value; + if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_TYPE, + key)) + { + if (0 == strncasecmp (value, + MHD_HTTP_POST_ENCODING_FORM_URLENCODED, + strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED))) + ctask->post_type = MHD_HTTP_POST_ENCODING_FORM_URLENCODED; + else if (0 == strncasecmp (value, + MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, + strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA))) + ctask->post_type = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA; + else + ctask->post_type = NULL; + + } + cstr = GNUNET_malloc (strlen (key) + strlen (hdr_val) + 3); GNUNET_snprintf (cstr, strlen (key) + strlen (hdr_val) + 3, "%s: %s", key, hdr_val); @@ -509,6 +763,9 @@ curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls) char hdr_mime[html_mime_len+1]; char hdr_generic[bytes+1]; char new_cookie_hdr[bytes+strlen (ctask->leho)+1]; + char new_location[MAX_HTTP_URI_LENGTH+500]; + char real_host[264]; + char leho_host[264]; char* ndup; char* tok; char* cookie_domain; @@ -516,6 +773,7 @@ curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls) char* hdr_val; int delta_cdomain; size_t offset = 0; + char cors_hdr[strlen (ctask->leho) + strlen ("https://")]; if (NULL == ctask->response) { @@ -527,8 +785,30 @@ curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls) &mhd_content_cb, ctask, NULL); + + /* if we have a leho add a CORS header */ + if (0 != strcmp ("", ctask->leho)) + { + /* We could also allow ssl and http here */ + if (ctask->mhd->is_ssl) + sprintf (cors_hdr, "https://%s", ctask->leho); + else + sprintf (cors_hdr, "http://%s", ctask->leho); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: Adding CORS header field %s\n", + cors_hdr); + + if (GNUNET_NO == MHD_add_response_header (ctask->response, + "Access-Control-Allow-Origin", + cors_hdr)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "MHD: Error adding CORS header field %s\n", + cors_hdr); + } + } ctask->ready_to_queue = GNUNET_YES; - } if (html_mime_len <= bytes) @@ -650,18 +930,40 @@ curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls) return bytes; } + hdr_val++; + + if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type)) + { + if (ctask->mhd->is_ssl) + { + sprintf (leho_host, "https://%s", ctask->leho); + sprintf (real_host, "https://%s", ctask->host); + } + else + { + sprintf (leho_host, "http://%s", ctask->leho); + sprintf (real_host, "http://%s", ctask->host); + } + + if (0 == memcmp (leho_host, hdr_val, strlen (leho_host))) + { + sprintf (new_location, "%s%s", real_host, hdr_val+strlen (leho_host)); + hdr_val = new_location; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to set %s: %s\n", hdr_type, - hdr_val+1); + hdr_val); if (GNUNET_NO == MHD_add_response_header (ctask->response, hdr_type, - hdr_val+1)) + hdr_val)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MHD: Error adding %s header field %s\n", hdr_type, - hdr_val+1); + hdr_val); } GNUNET_free (ndup); return bytes; @@ -724,8 +1026,9 @@ mhd_content_free (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct ProxyCurlTask *ctask = cls; - GNUNET_assert (NULL == ctask->pp_match_head); + struct ProxyUploadData *pdata; + GNUNET_assert (NULL == ctask->pp_match_head); if (NULL != ctask->headers) curl_slist_free_all (ctask->headers); @@ -735,7 +1038,20 @@ mhd_content_free (void *cls, if (NULL != ctask->response) MHD_destroy_response (ctask->response); + if (NULL != ctask->post_handler) + MHD_destroy_post_processor (ctask->post_handler); + for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head) + { + GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head, + ctask->upload_data_tail, + pdata); + GNUNET_free_non_null (pdata->filename); + GNUNET_free_non_null (pdata->content_type); + GNUNET_free_non_null (pdata->key); + GNUNET_free_non_null (pdata->value); + GNUNET_free (pdata); + } GNUNET_free (ctask); } @@ -756,7 +1072,7 @@ mhd_content_cb (void *cls, size_t max) { struct ProxyCurlTask *ctask = cls; - struct ProxyREMatch *re_match = ctask->pp_match_head; + struct ProxyREMatch *re_match; ssize_t copied = 0; long long int bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr; @@ -767,8 +1083,8 @@ mhd_content_cb (void *cls, if ((GNUNET_YES == ctask->download_is_finished) && (GNUNET_NO == ctask->download_error) && - (0 == bytes_to_copy) && - (BUF_WAIT_FOR_CURL == ctask->buf_status)) + (0 == bytes_to_copy)) /* && + (BUF_WAIT_FOR_CURL == ctask->buf_status))*/ { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "MHD: sending response for %s\n", ctask->url); @@ -781,8 +1097,8 @@ mhd_content_cb (void *cls, if ((GNUNET_YES == ctask->download_error) && (GNUNET_YES == ctask->download_is_finished) && - (0 == bytes_to_copy) && - (BUF_WAIT_FOR_CURL == ctask->buf_status)) + (0 == bytes_to_copy)) /* && + (BUF_WAIT_FOR_CURL == ctask->buf_status))*/ { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "MHD: sending error response\n"); @@ -797,7 +1113,7 @@ mhd_content_cb (void *cls, return 0; copied = 0; - for (; NULL != re_match; re_match = ctask->pp_match_head) + for (re_match = ctask->pp_match_head; NULL != re_match; re_match = ctask->pp_match_head) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MHD: Processing PP %s\n", @@ -900,6 +1216,7 @@ mhd_content_cb (void *cls, return copied; } + /** * Shorten result callback * @@ -1003,9 +1320,9 @@ postprocess_buffer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) re_match->shorten_task = GNUNET_GNS_shorten_zone (gns_handle, re_match->hostname, - &local_private_zone, - &local_shorten_zone, - &local_gns_zone, + local_private_zone, + local_shorten_zone, + local_gns_zone, &process_shorten, re_match); //FIXME cancel appropriately @@ -1042,6 +1359,9 @@ curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx) "CURL: Got %d. %d free in buffer\n", total, buf_space); + if (BUF_WAIT_FOR_CURL != ctask->buf_status) + return CURL_WRITEFUNC_PAUSE; + if (total > (buf_space - CURL_BUF_PADDING)) { if (ctask->buf_status == BUF_WAIT_FOR_CURL) @@ -1070,6 +1390,112 @@ curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx) return total; } + +/** + * cURL callback for put data + */ +static size_t +put_read_callback (void *buf, size_t size, size_t nmemb, void *cls) +{ + struct ProxyCurlTask *ctask = cls; + struct ProxyUploadData *pdata = ctask->upload_data_head; + size_t len = size * nmemb; + size_t to_copy; + char* pos; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CURL: put read callback\n"); + + if (NULL == pdata) + return CURL_READFUNC_PAUSE; + + //fin + if (NULL == pdata->value) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CURL: Terminating PUT\n"); + + GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head, + ctask->upload_data_tail, + pdata); + GNUNET_free (pdata); + return 0; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CURL: read callback value %s\n", pdata->value); + + to_copy = pdata->bytes_left; + if (to_copy > len) + to_copy = len; + + pos = pdata->value + (pdata->total_bytes - pdata->bytes_left); + memcpy (buf, pos, to_copy); + pdata->bytes_left -= to_copy; + if (pdata->bytes_left <= 0) + { + GNUNET_free (pdata->value); + GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head, + ctask->upload_data_tail, + pdata); + GNUNET_free (pdata); + } + return to_copy; +} + + +/** + * cURL callback for post data + */ +static size_t +post_read_callback (void *buf, size_t size, size_t nmemb, void *cls) +{ + struct ProxyCurlTask *ctask = cls; + struct ProxyUploadData *pdata = ctask->upload_data_head; + size_t len = size * nmemb; + size_t to_copy; + char* pos; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CURL: read callback\n"); + + if (NULL == pdata) + return CURL_READFUNC_PAUSE; + + //fin + if (NULL == pdata->value) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CURL: Terminating POST data\n"); + + GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head, + ctask->upload_data_tail, + pdata); + GNUNET_free (pdata); + return 0; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CURL: read callback value %s\n", pdata->value); + + to_copy = pdata->bytes_left; + if (to_copy > len) + to_copy = len; + + pos = pdata->value + (pdata->total_bytes - pdata->bytes_left); + memcpy (buf, pos, to_copy); + pdata->bytes_left -= to_copy; + if (pdata->bytes_left <= 0) + { + GNUNET_free (pdata->value); + GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head, + ctask->upload_data_tail, + pdata); + GNUNET_free (pdata); + } + return to_copy; +} + /** * Task that is run when we are ready to receive more data * from curl @@ -1139,13 +1565,12 @@ curl_download_prepare () else if (NULL != ctasks_head) { /* as specified in curl docs */ - curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, &curl_task_download, curl_multi); } GNUNET_NETWORK_fdset_destroy (gws); GNUNET_NETWORK_fdset_destroy (grs); - } @@ -1176,8 +1601,8 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Shutdown requested while trying to download\n"); - //TODO cleanup - return; + //TODO cleanup + return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ready to dl\n"); @@ -1192,8 +1617,7 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running curl tasks: %d\n", running); - ctask = ctasks_head; - for (; ctask != NULL; ctask = ctask->next) + for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CTask: %s\n", ctask->url); @@ -1208,7 +1632,7 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) do { - ctask = ctasks_head; + msg = curl_multi_info_read (curl_multi, &msgnum); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Messages left: %d\n", msgnum); @@ -1224,7 +1648,7 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Download curl failed"); - for (; ctask != NULL; ctask = ctask->next) + for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next) { if (NULL == ctask->curl) continue; @@ -1258,15 +1682,12 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CURL: download completed.\n"); - for (; ctask != NULL; ctask = ctask->next) + for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next) { if (NULL == ctask->curl) continue; - if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0) - continue; - - if (ctask->buf_status != BUF_WAIT_FOR_CURL) + if (0 != memcmp (msg->easy_handle, ctask->curl, sizeof (CURL))) continue; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -1307,7 +1728,7 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } } while (msgnum > 0); - for (ctask=clean_head; ctask != NULL; ctask = ctask->next) + for (ctask=clean_head; NULL != ctask; ctask = ctask->next) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CURL: Removing task %s.\n", ctask->url); @@ -1317,7 +1738,7 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } num_ctasks=0; - for (ctask=ctasks_head; ctask != NULL; ctask = ctask->next) + for (ctask=ctasks_head; NULL != ctask; ctask = ctask->next) { num_ctasks++; } @@ -1341,6 +1762,7 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) curl_download_prepare(); } + /** * Process LEHO lookup * @@ -1382,7 +1804,7 @@ process_leho_lookup (void *cls, if (0 != strcmp (ctask->leho, "")) { - sprintf (hosthdr, "%s%s", "Host: ", ctask->leho); + sprintf (hosthdr, "%s%s:%d", "Host: ", ctask->leho, ctask->port); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New HTTP header value: %s\n", hosthdr); ctask->headers = curl_slist_append (ctask->headers, hosthdr); @@ -1410,7 +1832,7 @@ process_leho_lookup (void *cls, "Curl resolve: %s\n", resolvename); ctask->resolver = curl_slist_append ( ctask->resolver, resolvename); curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver); - sprintf (curlurl, "https://%s%s", ctask->leho, ctask->url); + sprintf (curlurl, "https://%s:%d%s", ctask->leho, ctask->port, ctask->url); curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl); } else @@ -1467,7 +1889,7 @@ process_get_authority (void *cls, GNUNET_GNS_lookup_zone (gns_handle, ctask->host, - &local_gns_zone, + local_gns_zone, GNUNET_GNS_RECORD_LEHO, GNUNET_YES, //Only cached for performance shorten_zonekey, @@ -1475,6 +1897,16 @@ process_get_authority (void *cls, ctask); } +static void* +mhd_log_callback (void* cls, const char* url) +{ + struct ProxyCurlTask *ctask; + + ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask)); + strcpy (ctask->url, url); + ctask->accepted = GNUNET_NO; + return ctask; +} /** * Main MHD callback for handling requests. @@ -1515,13 +1947,18 @@ create_response (void *cls, char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow! int ret = MHD_YES; + int i; - struct ProxyCurlTask *ctask; + struct ProxyCurlTask *ctask = *con_cls; + struct ProxyUploadData *fin_post; + struct curl_forms forms[5]; + struct ProxyUploadData *upload_data_iter; //FIXME handle if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) && (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) && - (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST))) + (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)) && + (0 != strcasecmp (meth, MHD_HTTP_METHOD_HEAD))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MHD: %s NOT IMPLEMENTED!\n", meth); @@ -1529,16 +1966,14 @@ create_response (void *cls, } - if (NULL == *con_cls) + if (GNUNET_NO == ctask->accepted) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got %s request for %s\n", meth, url); - ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask)); ctask->mhd = hd; - *con_cls = ctask; - ctask->curl = curl_easy_init(); + ctask->curl_running = GNUNET_NO; if (NULL == ctask->curl) { ctask->response = MHD_create_response_from_buffer (strlen (page), @@ -1551,7 +1986,38 @@ create_response (void *cls, GNUNET_free (ctask); return ret; } - + + if (ctask->mhd->is_ssl) + ctask->port = HTTPS_PORT; + else + ctask->port = HTTP_PORT; + + MHD_get_connection_values (con, + MHD_HEADER_KIND, + &con_val_iter, ctask); + + curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr); + curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask); + curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb); + curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask); + curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0); + curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + + if (GNUNET_NO == ctask->mhd->is_ssl) + { + sprintf (curlurl, "http://%s:%d%s", ctask->host, ctask->port, ctask->url); + curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl); + } + + + curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1); + curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L); + curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L); + + /* Add GNS header */ + ctask->headers = curl_slist_append (ctask->headers, + "GNS: YES"); + ctask->accepted = GNUNET_YES; ctask->download_in_progress = GNUNET_YES; ctask->buf_status = BUF_WAIT_FOR_CURL; ctask->connection = con; @@ -1559,78 +2025,44 @@ create_response (void *cls, ctask->buffer_read_ptr = ctask->buffer; ctask->buffer_write_ptr = ctask->buffer; ctask->pp_task = GNUNET_SCHEDULER_NO_TASK; - - MHD_get_connection_values (con, - MHD_HEADER_KIND, - &con_val_iter, ctask); + if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT)) { - if (0 == *upload_data_size) - { - curl_easy_cleanup (ctask->curl); - GNUNET_free (ctask); - return MHD_NO; - } - ctask->put_read_offset = 0; - ctask->put_read_size = *upload_data_size; - curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1); - curl_easy_setopt (ctask->curl, CURLOPT_READDATA, upload_data); - //curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &curl_read_cb); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Setting up PUT\n"); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Got PUT data: %s\n", upload_data); - curl_easy_cleanup (ctask->curl); - GNUNET_free (ctask); - return MHD_NO; + curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1); + curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask); + curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &put_read_callback); + ctask->headers = curl_slist_append (ctask->headers, + "Transfer-Encoding: chunked"); } if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST)) { - if (0 == *upload_data_size) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "NO data for post!\n"); - curl_easy_cleanup (ctask->curl); - GNUNET_free (ctask); - return MHD_NO; - } - curl_easy_setopt (ctask->curl, CURLOPT_POST, 1); - curl_easy_setopt (ctask->curl, CURLOPT_POSTFIELDSIZE, *upload_data_size); - curl_easy_setopt (ctask->curl, CURLOPT_COPYPOSTFIELDS, upload_data); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Got POST data: %s\n", upload_data); - curl_easy_cleanup (ctask->curl); - GNUNET_free (ctask); - return MHD_NO; + //FIXME handle multipart + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Setting up POST processor\n"); + ctask->post_handler = MHD_create_post_processor (con, + POSTBUFFERSIZE, + &con_post_data_iter, + ctask); + ctask->headers = curl_slist_append (ctask->headers, + "Transfer-Encoding: chunked"); + return MHD_YES; } - curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr); - curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask); - curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb); - curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask); - curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0); - curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - - if (GNUNET_NO == ctask->mhd->is_ssl) + if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD)) { - sprintf (curlurl, "http://%s%s", ctask->host, url); - MHD_get_connection_values (con, - MHD_GET_ARGUMENT_KIND, - &get_uri_val_iter, curlurl); - curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Setting NOBODY\n"); + curl_easy_setopt (ctask->curl, CURLOPT_NOBODY, 1); } - strcpy (ctask->url, url); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "MHD: Adding new curl task for %s%s\n", ctask->host, url); - MHD_get_connection_values (con, - MHD_GET_ARGUMENT_KIND, - &get_uri_val_iter, ctask->url); - - curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1); - curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L); - curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L); + "MHD: Adding new curl task for %s\n", ctask->host); GNUNET_GNS_get_authority (gns_handle, ctask->host, @@ -1638,10 +2070,109 @@ create_response (void *cls, ctask); ctask->ready_to_queue = GNUNET_NO; ctask->fin = GNUNET_NO; + ctask->curl_running = GNUNET_YES; return MHD_YES; } ctask = (struct ProxyCurlTask *) *con_cls; + if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST)) + { + if (0 != *upload_data_size) + { + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Invoking POST processor\n"); + MHD_post_process (ctask->post_handler, + upload_data, *upload_data_size); + *upload_data_size = 0; + if ((GNUNET_NO == ctask->is_httppost) && + (GNUNET_NO == ctask->curl_running)) + { + curl_easy_setopt (ctask->curl, CURLOPT_POST, 1); + curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, + &post_read_callback); + curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: Adding new curl task for %s\n", ctask->host); + + GNUNET_GNS_get_authority (gns_handle, + ctask->host, + &process_get_authority, + ctask); + ctask->ready_to_queue = GNUNET_NO; + ctask->fin = GNUNET_NO; + ctask->curl_running = GNUNET_YES; + } + return MHD_YES; + } + else if (GNUNET_NO == ctask->post_done) + { + if (GNUNET_YES == ctask->is_httppost) + { + for (upload_data_iter = ctask->upload_data_head; + NULL != upload_data_iter; + upload_data_iter = upload_data_iter->next) + { + i = 0; + if (NULL != upload_data_iter->filename) + { + forms[i].option = CURLFORM_FILENAME; + forms[i].value = upload_data_iter->filename; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding filename %s\n", + forms[i].value); + i++; + } + if (NULL != upload_data_iter->content_type) + { + forms[i].option = CURLFORM_CONTENTTYPE; + forms[i].value = upload_data_iter->content_type; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding content type %s\n", + forms[i].value); + i++; + } + forms[i].option = CURLFORM_PTRCONTENTS; + forms[i].value = upload_data_iter->value; + forms[i+1].option = CURLFORM_END; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding formdata for %s (len=%lld)\n", + upload_data_iter->key, + upload_data_iter->total_bytes); + + curl_formadd(&ctask->httppost, &ctask->httppost_last, + CURLFORM_COPYNAME, upload_data_iter->key, + CURLFORM_CONTENTSLENGTH, upload_data_iter->total_bytes, + CURLFORM_ARRAY, forms, + CURLFORM_END); + } + curl_easy_setopt (ctask->curl, CURLOPT_HTTPPOST, + ctask->httppost); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: Adding new curl task for %s\n", ctask->host); + + GNUNET_GNS_get_authority (gns_handle, + ctask->host, + &process_get_authority, + ctask); + ctask->ready_to_queue = GNUNET_YES; + ctask->fin = GNUNET_NO; + ctask->curl_running = GNUNET_YES; + ctask->post_done = GNUNET_YES; + return MHD_YES; + } + + fin_post = GNUNET_malloc (sizeof (struct ProxyUploadData)); + GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head, + ctask->upload_data_tail, + fin_post); + ctask->post_done = GNUNET_YES; + return MHD_YES; + } + } if (GNUNET_YES != ctask->ready_to_queue) return MHD_YES; /* wait longer */ @@ -2192,18 +2723,28 @@ add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, const char* domain) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No previous SSL instance found... starting new one for %s\n", domain); - - hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL, 4444, - &accept_cb, NULL, - &create_response, hd, + hd->daemon = MHD_start_daemon (MHD_USE_DEBUG + | MHD_USE_SSL +#if HAVE_MHD_NO_LISTEN_SOCKET + | MHD_USE_NO_LISTEN_SOCKET, + 0, +#else + , 4444, //Dummy +#endif + &accept_cb, NULL, + &create_response, hd, +#if !HAVE_MHD_NO_LISTEN_SOCKET MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket), - MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS, - MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16, - MHD_OPTION_NOTIFY_COMPLETED, - NULL, NULL, - MHD_OPTION_HTTPS_MEM_KEY, pgc->key, - MHD_OPTION_HTTPS_MEM_CERT, pgc->cert, - MHD_OPTION_END); +#endif + MHD_OPTION_CONNECTION_LIMIT, + MHD_MAX_CONNECTIONS, + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16, + MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL, + MHD_OPTION_HTTPS_MEM_KEY, pgc->key, + MHD_OPTION_HTTPS_MEM_CERT, pgc->cert, + MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, + NULL, + MHD_OPTION_END); GNUNET_assert (hd->daemon != NULL); hd->httpd_task = GNUNET_SCHEDULER_NO_TASK; @@ -2566,11 +3107,19 @@ do_shutdown (void *cls, struct NetworkHandleList *tmp_nh; struct ProxyCurlTask *ctask; struct ProxyCurlTask *ctask_tmp; + struct ProxyUploadData *pdata; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down...\n"); gnutls_global_deinit (); + + if (NULL != local_gns_zone) + GNUNET_free (local_gns_zone); + if (NULL != local_private_zone) + GNUNET_free (local_private_zone); + if (NULL != local_shorten_zone) + GNUNET_free (local_shorten_zone); if (GNUNET_SCHEDULER_NO_TASK != curl_download_task) { @@ -2637,6 +3186,20 @@ do_shutdown (void *cls, if (NULL != ctask->response) MHD_destroy_response (ctask->response); + pdata = ctask->upload_data_head; + + //FIXME free pdata here + for (; pdata != NULL; pdata = ctask->upload_data_head) + { + GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head, + ctask->upload_data_tail, + pdata); + GNUNET_free_non_null (pdata->filename); + GNUNET_free_non_null (pdata->content_type); + GNUNET_free_non_null (pdata->key); + GNUNET_free_non_null (pdata->value); + GNUNET_free (pdata); + } GNUNET_free (ctask); } @@ -2703,69 +3266,74 @@ load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg) key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); GNUNET_CRYPTO_rsa_key_get_public (key, &pkey); + local_gns_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode)); GNUNET_CRYPTO_short_hash(&pkey, sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), - &local_gns_zone); - zone = &local_gns_zone; + local_gns_zone); + zone = local_gns_zone; GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Using zone: %s!\n", &zonename); GNUNET_CRYPTO_rsa_key_free(key); GNUNET_free(keyfile); - + keyfile = NULL; + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns", "PRIVATE_ZONEKEY", &keyfile)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to load private zone key config value!\n"); - return GNUNET_NO; } - if (GNUNET_NO == GNUNET_DISK_file_test (keyfile)) + if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to load private zone key %s!\n", keyfile); GNUNET_free(keyfile); - return GNUNET_NO; } - - key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); - GNUNET_CRYPTO_rsa_key_get_public (key, &pkey); - GNUNET_CRYPTO_short_hash(&pkey, - sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), - &local_private_zone); - GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Using private zone: %s!\n", &zonename); - GNUNET_CRYPTO_rsa_key_free(key); - GNUNET_free(keyfile); + else + { + key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); + GNUNET_CRYPTO_rsa_key_get_public (key, &pkey); + local_private_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode)); + GNUNET_CRYPTO_short_hash(&pkey, + sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + local_private_zone); + GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Using private zone: %s!\n", &zonename); + GNUNET_CRYPTO_rsa_key_free(key); + GNUNET_free(keyfile); + } + keyfile = NULL; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns", "SHORTEN_ZONEKEY", &keyfile)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to load shorten zone key config value!\n"); - return GNUNET_NO; } - if (GNUNET_NO == GNUNET_DISK_file_test (keyfile)) + if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to load shorten zone key %s!\n", keyfile); GNUNET_free(keyfile); - return GNUNET_NO; } - - key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); - GNUNET_CRYPTO_rsa_key_get_public (key, &pkey); - GNUNET_CRYPTO_short_hash(&pkey, - sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), - &local_shorten_zone); - GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Using shorten zone: %s!\n", &zonename); - GNUNET_CRYPTO_rsa_key_free(key); - GNUNET_free(keyfile); + else + { + key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); + GNUNET_CRYPTO_rsa_key_get_public (key, &pkey); + local_shorten_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode)); + GNUNET_CRYPTO_short_hash(&pkey, + sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + local_shorten_zone); + GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Using shorten zone: %s!\n", &zonename); + GNUNET_CRYPTO_rsa_key_free(key); + GNUNET_free(keyfile); + } return GNUNET_YES; } @@ -2784,11 +3352,13 @@ run (void *cls, char *const *args, const char *cfgfile, { struct sockaddr_in sa; struct MhdHttpList *hd; - struct sockaddr_un mhd_unix_sock_addr; - size_t len; - char* proxy_sockfile; char* cafile_cfg = NULL; char* cafile; +#if !HAVE_MHD_NO_LISTEN_SOCKET + size_t len; + char* proxy_sockfile; + struct sockaddr_un mhd_unix_sock_addr; +#endif curl_multi = curl_multi_init (); @@ -2816,6 +3386,8 @@ run (void *cls, char *const *args, const char *cfgfile, } cafile = cafile_cfg; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Using %s as CA\n", cafile); gnutls_global_init (); gnutls_x509_crt_init (&proxy_ca.cert); @@ -2903,7 +3475,7 @@ run (void *cls, char *const *args, const char *cfgfile, mhd_httpd_head = NULL; mhd_httpd_tail = NULL; total_mhd_connections = 0; - +#ifndef HAVE_MHD_NO_LISTEN_SOCKET if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy", "PROXY_UNIXPATH", &proxy_sockfile)) @@ -2912,7 +3484,7 @@ run (void *cls, char *const *args, const char *cfgfile, "Specify PROXY_UNIXPATH in gns-proxy config section!\n"); return; } - + mhd_unix_socket = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0); @@ -2954,18 +3526,28 @@ run (void *cls, char *const *args, const char *cfgfile, "Unable to listen on unix domain socket!\n"); return; } +#endif hd = GNUNET_malloc (sizeof (struct MhdHttpList)); hd->is_ssl = GNUNET_NO; strcpy (hd->domain, ""); - httpd = MHD_start_daemon (MHD_USE_DEBUG, 4444, //Dummy port + httpd = MHD_start_daemon (MHD_USE_DEBUG +#if HAVE_MHD_NO_LISTEN_SOCKET + | MHD_USE_NO_LISTEN_SOCKET, + 0, +#else + , 4444, //Dummy port +#endif &accept_cb, NULL, &create_response, hd, +#if !HAVE_MHD_NO_LISTEN_SOCKET MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket), +#endif MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS, MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16, MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL, + MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL, MHD_OPTION_END); GNUNET_assert (httpd != NULL);