X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fgns%2Fgnunet-gns-proxy.c;h=cdc18e3f6b156ca8bfab88ffcf423f4ce292c10a;hb=bcbd694ad35e182e5a1486b545ea0706082ee94a;hp=243e94c08e05e721981b02ed334391c11754ede3;hpb=818289edf46125a64fa8417f7ff348d3aa5ab2ab;p=oweals%2Fgnunet.git diff --git a/src/gns/gnunet-gns-proxy.c b/src/gns/gnunet-gns-proxy.c index 243e94c08..cdc18e3f6 100644 --- a/src/gns/gnunet-gns-proxy.c +++ b/src/gns/gnunet-gns-proxy.c @@ -34,17 +34,25 @@ #include #include +#define HAVE_MHD_NO_LISTEN_SOCKET MHD_VERSION >= 0x00091401 + #define GNUNET_GNS_PROXY_PORT 7777 -#define MAX_MHD_CONNECTIONS 300 +#define MHD_MAX_CONNECTIONS 300 +#define MAX_HTTP_URI_LENGTH 2048 +#define POSTBUFFERSIZE 4096 /* MHD/cURL defines */ #define BUF_WAIT_FOR_CURL 0 #define BUF_WAIT_FOR_MHD 1 -#define HTML_HDR_CONTENT "Content-Type: text/html\r\n" +#define BUF_WAIT_FOR_PP 2 +#define HTML_HDR_CONTENT "Content-Type: text/html" + +/* buffer padding for proper RE matching */ +#define CURL_BUF_PADDING 1000 /* regexp */ //#define RE_DOTPLUS " (i & 15)); + return hexmap[i & 15]; } - /** - * Check HTTP response header for mime + * Escape given 0-terminated string * - * @param buffer curl buffer - * @param size curl blocksize - * @param nmemb curl blocknumber - * @param cls handle - * @return size of read bytes + * @param to_esc string to escapse + * @return allocated new escaped string (MUST free!) */ -static size_t -curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls) +static char* +escape_to_urlenc (const char *to_esc) { - size_t bytes = size * nmemb; - struct ProxyCurlTask *ctask = cls; - char hdr[bytes+1]; - - memcpy (hdr, buffer, bytes); - hdr[bytes] = '\0'; - - if (0 == strcmp (hdr, HTML_HDR_CONTENT)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got HTML HTTP response header\n"); - ctask->parse_content = GNUNET_YES; + 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++; } - - return bytes; + *rpos = '\0'; + return res; } -/** - * schedule mhd - * - * @param hd a http daemon list entry - */ -static void -run_httpd (struct MhdHttpList *hd); - - -/** - * schedule all mhds - * - */ -static void -run_httpds (void); +static int +con_post_data_iter (void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *filename, + const char *content_type, + const char *transfer_encoding, + const char *data, + uint64_t off, + size_t size) +{ + struct ProxyCurlTask* ctask = cls; + struct ProxyUploadData* pdata; + char* enc; + char* new_value; + + 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); -/** - * Task that simply runs MHD main loop - * - * @param cls NULL - * @param tc task context - */ -static void -run_mhd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ + 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); - struct MhdHttpList *hd = cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Copied %lld\n"); - //for (hd=mhd_httpd_head; hd != NULL; hd = hd->next) - MHD_run (hd->daemon); -} + 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); + } -/** - * Process cURL download bits - * - * @param ptr buffer with data - * @param size size of a record - * @param nmemb number of records downloaded - * @param ctx context - * @return number of processed bytes - */ -static size_t -callback_download (void *ptr, size_t size, size_t nmemb, void *ctx) -{ - const char *cbuf = ptr; - size_t total; - struct ProxyCurlTask *ctask = ctx; + 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); - //MHD_run (httpd); + 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; - total = size*nmemb; + return MHD_YES; - if (total == 0) - { - return total; } - if (total > sizeof (ctask->buffer)) + if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, + ctask->post_type)) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "CURL gave us too much data to handle (%d)!\n", - total); - return 0; + return MHD_NO; } + + ctask->is_httppost = GNUNET_NO; - if (ctask->buf_status == BUF_WAIT_FOR_MHD) + 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, - "CURL: Waiting for MHD (%s)\n", ctask->url); - return CURL_WRITEFUNC_PAUSE; + "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, - "CURL: Copying to MHD (%s, %d)\n", ctask->url, total); - memcpy (ctask->buffer, cbuf, total); - ctask->bytes_in_buffer = total; - ctask->buffer_ptr = ctask->buffer; - - ctask->buf_status = BUF_WAIT_FOR_MHD; - - //GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - // "cURL chunk:\n%s\n", (char*)ctask->buffer); - //run_mhd (NULL, NULL); - GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd); - return total; -} - - - -/** - * Callback to free content - * - * @param cls content to free - */ -static void -mhd_content_free (void *cls) -{ - struct ProxyCurlTask *ctask = cls; - - if (NULL != ctask->headers) - curl_slist_free_all (ctask->headers); - - if (NULL != ctask->curl) - curl_easy_cleanup (ctask->curl); - - ctask->curl = NULL; - - GNUNET_free (ctask); + "Escaped POST value: '%s'\n", + pdata->value); + GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head, + ctask->upload_data_tail, + pdata); + return MHD_YES; } /** - * Shorten result callback + * Read HTTP request header field 'Host' * - * @param cls the proxycurltask - * @param short_name the shortened name (NULL on error) + * @param cls buffer to write to + * @param kind value kind + * @param key field key + * @param value field value + * @return MHD_NO when Host found */ -static void -process_shorten (void* cls, const char* short_name) +static int +con_val_iter (void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value) { struct ProxyCurlTask *ctask = cls; + char* buf = ctask->host; + char* port; + char* cstr; + const char* hdr_val; + unsigned int uport; - char tmp[strlen(ctask->pp_buf)]; //TODO length - - if (NULL == short_name) + if (0 == strcmp ("Host", key)) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "MHD PP: Unable to shorten %s\n", - ctask->pp_buf); - return; + 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; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "MHD PP: Shorten %s -> %s\n", - ctask->pp_buf, - short_name); - - sprintf (tmp, "pp_buf, tmp); - - ctask->pp_finished = GNUNET_YES; - - GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd); -} + if (0 == strcmp ("Accept-Encoding", key)) + hdr_val = ""; + 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; + } -/** - * Postprocessing task that uses GNS to shorten names - * - * @param cls the proxycurltask - * @param tc the task context - * -static void -postprocess_name (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct ProxyCurlTask *ctask = cls; - char tmp[strlen(ctask->pp_buf)]; + cstr = GNUNET_malloc (strlen (key) + strlen (hdr_val) + 3); + GNUNET_snprintf (cstr, strlen (key) + strlen (hdr_val) + 3, + "%s: %s", key, hdr_val); - sprintf ( tmp, "%s%s", ctask->pp_buf, ctask->authority); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client Header: %s\n", cstr); - GNUNET_GNS_shorten (gns_handle, - tmp, - &process_shorten, - ctask); + ctask->headers = curl_slist_append (ctask->headers, cstr); + GNUNET_free (cstr); + return MHD_YES; } -*/ + /** * Callback for MHD response @@ -544,194 +736,765 @@ postprocess_name (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * @param pos in buffer * @param buf buffer * @param max space in buffer + * @return number of bytes written */ static ssize_t mhd_content_cb (void *cls, uint64_t pos, char* buf, - size_t max) + size_t max); + +/** + * Check HTTP response header for mime + * + * @param buffer curl buffer + * @param size curl blocksize + * @param nmemb curl blocknumber + * @param cls handle + * @return size of read bytes + */ +static size_t +curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls) { + size_t bytes = size * nmemb; struct ProxyCurlTask *ctask = cls; - ssize_t copied = 0; - size_t bytes_to_copy; - int nomatch; - char *hostptr; - regmatch_t m[RE_N_MATCHES]; + int html_mime_len = strlen (HTML_HDR_CONTENT); + int cookie_hdr_len = strlen (MHD_HTTP_HEADER_SET_COOKIE); + 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; + char* hdr_type; + char* hdr_val; + int delta_cdomain; + size_t offset = 0; + char cors_hdr[strlen (ctask->leho) + strlen ("https://")]; + + if (NULL == ctask->response) + { + /* FIXME: get total size from curl (if available) */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating response for %s\n", ctask->url); + ctask->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, + sizeof (ctask->buffer), + &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) + { + memcpy (hdr_mime, buffer, html_mime_len); + hdr_mime[html_mime_len] = '\0'; + + if (0 == strcmp (hdr_mime, HTML_HDR_CONTENT)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got HTML HTTP response header\n"); + ctask->parse_content = GNUNET_YES; + } + } + + if (cookie_hdr_len > bytes) + return bytes; + + memcpy (hdr_generic, buffer, bytes); + hdr_generic[bytes] = '\0'; + /* remove crlf */ + if ('\n' == hdr_generic[bytes-1]) + hdr_generic[bytes-1] = '\0'; + + if (hdr_generic[bytes-2] == '\r') + hdr_generic[bytes-2] = '\0'; + + if (0 == memcmp (hdr_generic, + MHD_HTTP_HEADER_SET_COOKIE, + cookie_hdr_len)) + { + ndup = GNUNET_strdup (hdr_generic+cookie_hdr_len+1); + memset (new_cookie_hdr, 0, sizeof (new_cookie_hdr)); + tok = strtok (ndup, ";"); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Looking for cookie in : %s\n", hdr_generic); + + for (; tok != NULL; tok = strtok (NULL, ";")) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Got Cookie token: %s\n", tok); + //memcpy (new_cookie_hdr+offset, tok, strlen (tok)); + if (0 == memcmp (tok, " domain", strlen (" domain"))) + { + cookie_domain = tok + strlen (" domain") + 1; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Got Set-Cookie Domain: %s\n", cookie_domain); + + if (strlen (cookie_domain) < strlen (ctask->leho)) + { + delta_cdomain = strlen (ctask->leho) - strlen (cookie_domain); + if (0 == strcmp (cookie_domain, ctask->leho + (delta_cdomain))) + { + GNUNET_snprintf (new_cookie_hdr+offset, + sizeof (new_cookie_hdr), + " domain=%s", ctask->authority); + offset += strlen (" domain=") + strlen (ctask->authority); + new_cookie_hdr[offset] = ';'; + offset++; + continue; + } + } + else if (strlen (cookie_domain) == strlen (ctask->leho)) + { + if (0 == strcmp (cookie_domain, ctask->leho)) + { + GNUNET_snprintf (new_cookie_hdr+offset, + sizeof (new_cookie_hdr), + " domain=%s", ctask->host); + offset += strlen (" domain=") + strlen (ctask->host); + new_cookie_hdr[offset] = ';'; + offset++; + continue; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Cookie domain invalid\n"); + + + } + memcpy (new_cookie_hdr+offset, tok, strlen (tok)); + offset += strlen (tok); + new_cookie_hdr[offset] = ';'; + offset++; + } + + GNUNET_free (ndup); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Got Set-Cookie HTTP header %s\n", new_cookie_hdr); + + if (GNUNET_NO == MHD_add_response_header (ctask->response, + MHD_HTTP_HEADER_SET_COOKIE, + new_cookie_hdr)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "MHD: Error adding set-cookie header field %s\n", + hdr_generic+cookie_hdr_len+1); + } + return bytes; + } + + ndup = GNUNET_strdup (hdr_generic); + hdr_type = strtok (ndup, ":"); + + if (NULL == hdr_type) + { + GNUNET_free (ndup); + return bytes; + } + + hdr_val = strtok (NULL, ""); + + if (NULL == hdr_val) + { + GNUNET_free (ndup); + 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, - "MHD: content cb\n"); + "Trying to set %s: %s\n", + hdr_type, + hdr_val); + if (GNUNET_NO == MHD_add_response_header (ctask->response, + hdr_type, + hdr_val)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "MHD: Error adding %s header field %s\n", + hdr_type, + hdr_val); + } + GNUNET_free (ndup); + return bytes; +} + +/** + * schedule mhd + * + * @param hd a http daemon list entry + */ +static void +run_httpd (struct MhdHttpList *hd); - if (ctask->download_successful && - (ctask->buf_status == BUF_WAIT_FOR_CURL)) + +/** + * schedule all mhds + * + */ +static void +run_httpds (void); + +/** + * Task run whenever HTTP server operations are pending. + * + * @param cls unused + * @param tc sched context + */ +static void +do_httpd (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc); + +static void +run_mhd_now (struct MhdHttpList *hd) +{ + if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: killing old task\n"); + GNUNET_SCHEDULER_cancel (hd->httpd_task); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: Scheduling MHD now\n"); + hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, hd); +} + +/** + * Ask cURL for the select sets and schedule download + */ +static void +curl_download_prepare (); + +/** + * Callback to free content + * + * @param cls content to free + * @param tc task context + */ +static void +mhd_content_free (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ProxyCurlTask *ctask = cls; + struct ProxyUploadData *pdata; + + GNUNET_assert (NULL == ctask->pp_match_head); + if (NULL != ctask->headers) + curl_slist_free_all (ctask->headers); + + if (NULL != ctask->headers) + curl_slist_free_all (ctask->resolver); + + 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); +} + + +/** + * Callback for MHD response + * + * @param cls closure + * @param pos in buffer + * @param buf buffer + * @param max space in buffer + * @return number of bytes written + */ +static ssize_t +mhd_content_cb (void *cls, + uint64_t pos, + char* buf, + size_t max) +{ + struct ProxyCurlTask *ctask = cls; + struct ProxyREMatch *re_match; + ssize_t copied = 0; + long long int bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: content cb for %s. To copy: %lld\n", + ctask->url, bytes_to_copy); + GNUNET_assert (bytes_to_copy >= 0); + + if ((GNUNET_YES == ctask->download_is_finished) && + (GNUNET_NO == ctask->download_error) && + (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); ctask->download_in_progress = GNUNET_NO; - curl_multi_remove_handle (curl_multi, ctask->curl); - curl_easy_cleanup (ctask->curl); - GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd); + run_mhd_now (ctask->mhd); + GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask); total_mhd_connections--; return MHD_CONTENT_READER_END_OF_STREAM; } - if (ctask->download_error && - (ctask->buf_status == BUF_WAIT_FOR_CURL)) + if ((GNUNET_YES == ctask->download_error) && + (GNUNET_YES == ctask->download_is_finished) && + (0 == bytes_to_copy)) /* && + (BUF_WAIT_FOR_CURL == ctask->buf_status))*/ { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "MHD: sending error response\n"); ctask->download_in_progress = GNUNET_NO; - curl_multi_remove_handle (curl_multi, ctask->curl); - curl_easy_cleanup (ctask->curl); - GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd); + run_mhd_now (ctask->mhd); + GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask); total_mhd_connections--; return MHD_CONTENT_READER_END_WITH_ERROR; } if ( ctask->buf_status == BUF_WAIT_FOR_CURL ) return 0; - - bytes_to_copy = ctask->bytes_in_buffer; - if (ctask->parse_content == GNUNET_YES) + copied = 0; + 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", + re_match->hostname); + bytes_to_copy = re_match->start - ctask->buffer_read_ptr; + GNUNET_assert (bytes_to_copy >= 0); - GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG, - "MHD: We need to parse the HTML %s\n", ctask->buffer_ptr); + if (bytes_to_copy+copied > max) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: buffer in response too small for %d. Using available space (%d). (%s)\n", + bytes_to_copy, + max, + ctask->url); + memcpy (buf+copied, ctask->buffer_read_ptr, max-copied); + ctask->buffer_read_ptr += max-copied; + copied = max; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: copied %d bytes\n", copied); + return copied; + } - nomatch = regexec ( &re_dotplus, ctask->buffer_ptr, RE_N_MATCHES, m, 0); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: copying %d bytes to mhd response at offset %d\n", + bytes_to_copy, ctask->buffer_read_ptr); + memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy); + copied += bytes_to_copy; - if (nomatch) + if (GNUNET_NO == re_match->done) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "MHD RE: No match\n"); + "MHD: Waiting for PP of %s\n", re_match->hostname); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: copied %d bytes\n", copied); + ctask->buffer_read_ptr += bytes_to_copy; + return copied; } - else + + if (strlen (re_match->result) > (max - copied)) { + //FIXME partially copy domain here GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "MHD RE: Match\n"); + "MHD: buffer in response too small for %s! (%s)\n", + re_match->result, + ctask->url); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: copied %d bytes\n", copied); + ctask->buffer_read_ptr += bytes_to_copy; + return copied; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: Adding PP result %s to buffer\n", + re_match->result); + memcpy (buf+copied, re_match->result, strlen (re_match->result)); + copied += strlen (re_match->result); + ctask->buffer_read_ptr = re_match->end; + GNUNET_CONTAINER_DLL_remove (ctask->pp_match_head, + ctask->pp_match_tail, + re_match); + GNUNET_free (re_match); + } - GNUNET_assert (m[1].rm_so != -1); + bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr; - hostptr = ctask->buffer_ptr+m[1].rm_so; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: copied: %d left: %d, space left in buf: %d\n", + copied, + bytes_to_copy, max-copied); + + GNUNET_assert (0 <= bytes_to_copy); - if (m[0].rm_so > 0) - { - bytes_to_copy = m[0].rm_so; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Copying %d bytes.\n", m[0].rm_so); + if (GNUNET_NO == ctask->download_is_finished) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: Purging buffer\n"); + memmove (ctask->buffer, ctask->buffer_read_ptr, bytes_to_copy); + ctask->buffer_read_ptr = ctask->buffer; + ctask->buffer_write_ptr = ctask->buffer + bytes_to_copy; + ctask->buffer[bytes_to_copy] = '\0'; + } + + if (bytes_to_copy+copied > max) + bytes_to_copy = max-copied; + if (0 > bytes_to_copy) + bytes_to_copy = 0; + + memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy); + ctask->buffer_read_ptr += bytes_to_copy; + copied += bytes_to_copy; + ctask->buf_status = BUF_WAIT_FOR_CURL; + + if (NULL != ctask->curl) + curl_easy_pause (ctask->curl, CURLPAUSE_CONT); - } - else - { - if (ctask->is_postprocessing == GNUNET_YES) - { - - /*Done?*/ - if ( ctask->pp_finished == GNUNET_NO ) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "MHD PP: Waiting for PP of %s\n", ctask->pp_buf); - return 0; - } - - ctask->is_postprocessing = GNUNET_NO; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: copied %d bytes\n", copied); + run_mhd_now (ctask->mhd); + return copied; +} - ctask->bytes_in_buffer -= m[0].rm_eo;//(m[1].rm_eo-m[1].rm_so); - ctask->buffer_ptr += m[0].rm_eo;//(m[1].rm_eo-m[1].rm_so); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Skipping next %d bytes in buffer\n", m[0].rm_eo); - GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd); +/** + * Shorten result callback + * + * @param cls the proxycurltask + * @param short_name the shortened name (NULL on error) + */ +static void +process_shorten (void* cls, const char* short_name) +{ + struct ProxyREMatch *re_match = cls; + char result[sizeof (re_match->result)]; + + if (NULL == short_name) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "PP: Unable to shorten %s\n", + re_match->hostname); + GNUNET_CONTAINER_DLL_remove (re_match->ctask->pp_match_head, + re_match->ctask->pp_match_tail, + re_match); + GNUNET_free (re_match); + return; + } - if ( strlen (ctask->pp_buf) <= max ) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Copying postprocessed %s.\n", ctask->pp_buf); - memcpy ( buf, ctask->pp_buf, strlen (ctask->pp_buf) ); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Done %s.\n", buf); - ctask->is_postprocessing = GNUNET_NO; - return strlen (ctask->pp_buf); - } - - return 0; - } + if (0 == strcmp (short_name, re_match->ctask->leho)) + strcpy (result, re_match->ctask->host); + else + strcpy (result, short_name); - memset (ctask->pp_buf, 0, sizeof(ctask->pp_buf)); - - /* If .+ extend with authority */ - if (*(ctask->buffer_ptr+m[1].rm_eo) == '+') - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Links is .+\n"); - memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so)); - strcpy ( ctask->pp_buf+strlen(ctask->pp_buf), - ctask->authority); - } - /* If .zkey simply copy the name */ - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Link is zkey\n"); - memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so + strlen (GNUNET_GNS_TLD_ZKEY))); - } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "PP: Shorten %s -> %s\n", + re_match->hostname, + result); + + if (re_match->ctask->mhd->is_ssl) + sprintf (re_match->result, "href=\"https://%s", result); + else + sprintf (re_match->result, "href=\"http://%s", result); - ctask->is_postprocessing = GNUNET_YES; - ctask->pp_finished = GNUNET_NO; - - GNUNET_GNS_shorten (gns_handle, - ctask->pp_buf, - &process_shorten, - ctask); + re_match->done = GNUNET_YES; + run_mhd_now (re_match->ctask->mhd); +} - //postprocess_name(ctask, NULL); - //ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_name, ctask); - return 0; - } - } - } +/** + * Postprocess data in buffer. From read ptr to write ptr + * + * @param cls the curlproxytask + * @param tc task context + */ +static void +postprocess_buffer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ProxyCurlTask *ctask = cls; + struct ProxyREMatch *re_match; + char* re_ptr = ctask->buffer_read_ptr; + char re_hostname[255]; + regmatch_t m[RE_N_MATCHES]; + + ctask->pp_task = GNUNET_SCHEDULER_NO_TASK; - if ( bytes_to_copy > max ) + if (GNUNET_YES != ctask->parse_content) { - GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG, - "MHD: buffer in response too small! (%s)\n", - ctask->url); - memcpy ( buf, ctask->buffer_ptr, max); - ctask->bytes_in_buffer -= max; - ctask->buffer_ptr += max; - copied = max; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "PP: Not parsing content\n"); + ctask->buf_status = BUF_WAIT_FOR_MHD; + run_mhd_now (ctask->mhd); + return; } - else + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "PP: We need to parse the HTML\n"); + + /* 0 means match found */ + while (0 == regexec (&re_dotplus, re_ptr, RE_N_MATCHES, m, 0)) { - GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG, - "MHD: copying %d bytes to mhd response at offset %d\n", - bytes_to_copy, pos); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "PP: regex match\n"); + + GNUNET_assert (m[1].rm_so != -1); + + memset (re_hostname, 0, sizeof (re_hostname)); + memcpy (re_hostname, re_ptr+m[1].rm_so, (m[3].rm_eo-m[1].rm_so)); + + re_match = GNUNET_malloc (sizeof (struct ProxyREMatch)); + re_match->start = re_ptr + m[0].rm_so; + re_match->end = re_ptr + m[3].rm_eo; + re_match->done = GNUNET_NO; + re_match->ctask = ctask; + strcpy (re_match->hostname, re_hostname); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "PP: Got hostname %s\n", re_hostname); + re_ptr += m[3].rm_eo; - memcpy ( buf, ctask->buffer_ptr, bytes_to_copy ); - copied = bytes_to_copy; - if (bytes_to_copy < ctask->bytes_in_buffer) + if (GNUNET_YES == is_tld (re_match->hostname, GNUNET_GNS_TLD_PLUS)) { - ctask->bytes_in_buffer -= bytes_to_copy; - ctask->buffer_ptr += bytes_to_copy; + re_match->hostname[strlen(re_match->hostname)-1] = '\0'; + strcpy (re_match->hostname+strlen(re_match->hostname), + ctask->authority); } - else + + re_match->shorten_task = GNUNET_GNS_shorten_zone (gns_handle, + re_match->hostname, + local_private_zone, + local_shorten_zone, + local_gns_zone, + &process_shorten, + re_match); //FIXME cancel appropriately + + GNUNET_CONTAINER_DLL_insert_tail (ctask->pp_match_head, + ctask->pp_match_tail, + re_match); + } + + ctask->buf_status = BUF_WAIT_FOR_MHD; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "PP: No more matches\n"); + run_mhd_now (ctask->mhd); +} + +/** + * Handle data from cURL + * + * @param ptr pointer to the data + * @param size number of blocks of data + * @param nmemb blocksize + * @param ctx the curlproxytask + * @return number of bytes handled + */ +static size_t +curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx) +{ + const char *cbuf = ptr; + size_t total = size * nmemb; + struct ProxyCurlTask *ctask = ctx; + size_t buf_space = sizeof (ctask->buffer) - + (ctask->buffer_write_ptr-ctask->buffer); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "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) { - ctask->bytes_in_buffer = 0; - ctask->buf_status = BUF_WAIT_FOR_CURL; - ctask->buffer_ptr = ctask->buffer; - curl_easy_pause (ctask->curl, CURLPAUSE_CONT); - GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CURL: Buffer full starting postprocessing\n"); + ctask->buf_status = BUF_WAIT_FOR_PP; + ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer, + ctask); + return CURL_WRITEFUNC_PAUSE; } + + /* we should not get called in that case */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "CURL: called out of context and no space in buffer!\n"); + return CURL_WRITEFUNC_PAUSE; } - GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CURL: Copying %d bytes to buffer (%s)\n", total, ctask->url); + memcpy (ctask->buffer_write_ptr, cbuf, total); + ctask->bytes_in_buffer += total; + ctask->buffer_write_ptr += total; + ctask->buffer_write_ptr[0] = '\0'; - return copied; + 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 @@ -791,14 +1554,23 @@ curl_download_prepare () if (curl_download_task != GNUNET_SCHEDULER_NO_TASK) GNUNET_SCHEDULER_cancel (curl_download_task); - curl_download_task = - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - rtime, - grs, gws, - &curl_task_download, curl_multi); + if (-1 != max) + { + curl_download_task = + GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, + rtime, + grs, gws, + &curl_task_download, curl_multi); + } + else if (NULL != ctasks_head) + { + /* as specified in curl docs */ + 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); - } @@ -818,15 +1590,19 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) CURLMcode mret; struct ProxyCurlTask *ctask; int num_ctasks; + long resp_code; + + struct ProxyCurlTask *clean_head = NULL; + struct ProxyCurlTask *clean_tail = NULL; curl_download_task = GNUNET_SCHEDULER_NO_TASK; if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + 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"); @@ -841,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); @@ -857,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); @@ -873,21 +1648,31 @@ 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; + if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0) continue; GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Download curl failed for task %s: %s.\n", + "CURL: Download failed for task %s: %s.\n", ctask->url, curl_easy_strerror (msg->data.result)); - ctask->download_successful = GNUNET_NO; + ctask->download_is_finished = GNUNET_YES; ctask->download_error = GNUNET_YES; - //curl_multi_remove_handle (curl_multi, ctask->curl); - //curl_easy_cleanup (ctask->curl); + if (CURLE_OK == curl_easy_getinfo (ctask->curl, + CURLINFO_RESPONSE_CODE, + &resp_code)) + ctask->curl_response_code = resp_code; + ctask->ready_to_queue = MHD_YES; + ctask->buf_status = BUF_WAIT_FOR_MHD; + run_mhd_now (ctask->mhd); + GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail, ctask); + GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask); break; } GNUNET_assert (ctask != NULL); @@ -895,35 +1680,65 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) else { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "cURL download completed.\n"); + "CURL: download completed.\n"); - for (; ctask != NULL; ctask = ctask->next) + for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next) { - if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0) + if (NULL == ctask->curl) + continue; + + if (0 != memcmp (msg->easy_handle, ctask->curl, sizeof (CURL))) continue; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "cURL task %s found.\n", ctask->url); - ctask->download_successful = GNUNET_YES; - //curl_multi_remove_handle (curl_multi, ctask->curl); - //curl_easy_cleanup (ctask->curl); + "CURL: completed task %s found.\n", ctask->url); + if (CURLE_OK == curl_easy_getinfo (ctask->curl, + CURLINFO_RESPONSE_CODE, + &resp_code)) + ctask->curl_response_code = resp_code; + + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CURL: Completed ctask!\n"); + if (GNUNET_SCHEDULER_NO_TASK == ctask->pp_task) + { + ctask->buf_status = BUF_WAIT_FOR_PP; + ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer, + ctask); + } + + ctask->ready_to_queue = MHD_YES; + ctask->download_is_finished = GNUNET_YES; + + /* We MUST not modify the multi handle else we loose messages */ GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail, ctask); + GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask); + break; } GNUNET_assert (ctask != NULL); } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "curl end %s\n", curl_easy_strerror(msg->data.result)); + "CURL: %s\n", curl_easy_strerror(msg->data.result)); break; default: GNUNET_assert (0); break; } } while (msgnum > 0); + + for (ctask=clean_head; NULL != ctask; ctask = ctask->next) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CURL: Removing task %s.\n", ctask->url); + curl_multi_remove_handle (curl_multi, ctask->curl); + curl_easy_cleanup (ctask->curl); + ctask->curl = NULL; + } num_ctasks=0; - for (ctask=ctasks_head; ctask != NULL; ctask = ctask->next) + for (ctask=ctasks_head; NULL != ctask; ctask = ctask->next) { num_ctasks++; } @@ -931,25 +1746,23 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) if (num_ctasks != running) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%d ctasks, %d curl running\n", num_ctasks, running); + "CURL: %d tasks, %d running\n", num_ctasks, running); } GNUNET_assert ( num_ctasks == running ); - run_httpds (); - } while (mret == CURLM_CALL_MULTI_PERFORM); - if (mret != CURLM_OK) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s failed at %s:%d: `%s'\n", + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CURL: %s failed at %s:%d: `%s'\n", "curl_multi_perform", __FILE__, __LINE__, curl_multi_strerror (mret)); } curl_download_prepare(); } + /** * Process LEHO lookup * @@ -972,8 +1785,6 @@ process_leho_lookup (void *cls, char resolvename[512]; char curlurl[512]; - ctask->headers = NULL; - strcpy (ctask->leho, ""); if (rd_count == 0) @@ -993,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); @@ -1017,18 +1828,18 @@ process_leho_lookup (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SSL target server: %s\n", ssl_ip); sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "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 { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "gethostbyname failed for %s!\n", ctask->host); - ctask->download_successful = GNUNET_NO; + ctask->download_is_finished = GNUNET_YES; ctask->download_error = GNUNET_YES; return; } @@ -1040,7 +1851,7 @@ process_leho_lookup (void *cls, "%s failed at %s:%d: `%s'\n", "curl_multi_add_handle", __FILE__, __LINE__, curl_multi_strerror (mret)); - ctask->download_successful = GNUNET_NO; + ctask->download_is_finished = GNUNET_YES; ctask->download_error = GNUNET_YES; return; } @@ -1078,14 +1889,25 @@ process_get_authority (void *cls, GNUNET_GNS_lookup_zone (gns_handle, ctask->host, - &local_gns_zone, - &local_shorten_zone, + local_gns_zone, GNUNET_GNS_RECORD_LEHO, GNUNET_YES, //Only cached for performance + shorten_zonekey, &process_leho_lookup, 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. * @@ -1119,123 +1941,253 @@ create_response (void *cls, size_t *upload_data_size, void **con_cls) { - static int dummy; struct MhdHttpList* hd = cls; const char* page = "gnoxy"\ "cURL fail"; - struct MHD_Response *response = NULL; - char host[265]; - char curlurl[512]; + + 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; - if (0 != strcmp (meth, "GET")) - return MHD_NO; - if (&dummy != *con_cls) + //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_HEAD))) { - *con_cls = &dummy; - return MHD_YES; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "MHD: %s NOT IMPLEMENTED!\n", meth); + return MHD_NO; } - if (0 != *upload_data_size) - return MHD_NO; - *con_cls = NULL; + if (GNUNET_NO == ctask->accepted) + { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "url %s\n", url); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Got %s request for %s\n", meth, url); + ctask->mhd = hd; + ctask->curl = curl_easy_init(); + ctask->curl_running = GNUNET_NO; + if (NULL == ctask->curl) + { + ctask->response = MHD_create_response_from_buffer (strlen (page), + (void*)page, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response (con, + MHD_HTTP_OK, + ctask->response); + MHD_destroy_response (ctask->response); + 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); + } + - MHD_get_connection_values (con, - MHD_HEADER_KIND, - &con_val_iter, host); + 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; + ctask->curl_response_code = MHD_HTTP_OK; + ctask->buffer_read_ptr = ctask->buffer; + ctask->buffer_write_ptr = ctask->buffer; + ctask->pp_task = GNUNET_SCHEDULER_NO_TASK; + - - /* Do cURL */ - ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask)); - ctask->mhd = hd; + if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Setting up PUT\n"); + + 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)) + { + //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; + } - if (curl_multi == NULL) - curl_multi = curl_multi_init (); + if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Setting NOBODY\n"); + curl_easy_setopt (ctask->curl, CURLOPT_NOBODY, 1); + } - ctask->curl = curl_easy_init(); - - if ((ctask->curl == NULL) || (curl_multi == NULL)) - { - response = MHD_create_response_from_buffer (strlen (page), - (void*)page, - MHD_RESPMEM_PERSISTENT); - ret = MHD_queue_response (con, - MHD_HTTP_OK, - response); - MHD_destroy_response (response); - GNUNET_free (ctask); - return ret; + + 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; } - - ctask->prev = NULL; - ctask->next = NULL; - ctask->headers = NULL; - ctask->resolver = NULL; - ctask->buffer_ptr = NULL; - ctask->download_in_progress = GNUNET_YES; - ctask->download_successful = GNUNET_NO; - ctask->buf_status = BUF_WAIT_FOR_CURL; - ctask->bytes_in_buffer = 0; - ctask->parse_content = GNUNET_NO; - - 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, &callback_download); - curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask); - curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt (ctask->curl, CURLOPT_MAXREDIRS, 4); - /* no need to abort if the above failed */ - if (GNUNET_NO == ctask->mhd->is_ssl) - { - sprintf (curlurl, "http://%s%s", host, url); - curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Adding new curl task for %s\n", curlurl); + + 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; + } } - strcpy (ctask->host, host); - strcpy (ctask->url, 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); - - GNUNET_GNS_get_authority (gns_handle, - ctask->host, - &process_get_authority, - ctask); - //download_prepare (ctask); - //curl_download_prepare (); - - response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, - MHD_SIZE_UNKNOWN, - &mhd_content_cb, - ctask, - &mhd_content_free); + if (GNUNET_YES != ctask->ready_to_queue) + return MHD_YES; /* wait longer */ - ret = MHD_queue_response (con, MHD_HTTP_OK, response); - - //MHD_destroy_response (response); + if (GNUNET_YES == ctask->fin) + return MHD_YES; + ctask->fin = GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: Queueing response for %s\n", ctask->url); + ret = MHD_queue_response (con, ctask->curl_response_code, ctask->response); + run_mhd_now (ctask->mhd); return ret; } -/** - * Task run whenever HTTP server operations are pending. - * - * @param cls unused - * @param tc sched context - */ -static void -do_httpd (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc); - /** * run all httpd @@ -1245,13 +2197,15 @@ run_httpds () { struct MhdHttpList *hd; - for (hd=mhd_httpd_head; hd != NULL; hd = hd->next) + for (hd=mhd_httpd_head; NULL != hd; hd = hd->next) run_httpd (hd); } /** * schedule mhd + * + * @param hd the daemon to run */ static void run_httpd (struct MhdHttpList *hd) @@ -1282,7 +2236,7 @@ run_httpd (struct MhdHttpList *hd) haveto = MHD_get_timeout (hd->daemon, &timeout); - if (haveto == MHD_YES) + if (MHD_YES == haveto) tv.rel_value = (uint64_t) timeout; else tv = GNUNET_TIME_UNIT_FOREVER_REL; @@ -1290,7 +2244,7 @@ run_httpd (struct MhdHttpList *hd) GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1); GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1); - if (hd->httpd_task != GNUNET_SCHEDULER_NO_TASK) + if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task) GNUNET_SCHEDULER_cancel (hd->httpd_task); hd->httpd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH, @@ -1314,13 +2268,15 @@ do_httpd (void *cls, { struct MhdHttpList *hd = cls; - hd->httpd_task = GNUNET_SCHEDULER_NO_TASK; - + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MHD: Main loop\n"); + hd->httpd_task = GNUNET_SCHEDULER_NO_TASK; MHD_run (hd->daemon); run_httpd (hd); } + /** * Read data from socket * @@ -1365,12 +2321,11 @@ do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) else { GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote"); - //Really!?!?!? - if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK) + if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask) GNUNET_SCHEDULER_cancel (s5r->rtask); - if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK) + if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask) GNUNET_SCHEDULER_cancel (s5r->wtask); - if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK) + if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask) GNUNET_SCHEDULER_cancel (s5r->fwdrtask); GNUNET_NETWORK_socket_close (s5r->remote_sock); GNUNET_NETWORK_socket_close (s5r->sock); @@ -1393,11 +2348,11 @@ do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) static void cleanup_s5r (struct Socks5Request *s5r) { - if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK) + if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask) GNUNET_SCHEDULER_cancel (s5r->rtask); - if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK) + if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdwtask) GNUNET_SCHEDULER_cancel (s5r->fwdwtask); - if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK) + if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask) GNUNET_SCHEDULER_cancel (s5r->fwdrtask); if (NULL != s5r->remote_sock) @@ -1518,128 +2473,97 @@ add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon) struct sockaddr *addr; socklen_t len; - fd = GNUNET_NETWORK_get_fd (h); + fd = dup (GNUNET_NETWORK_get_fd (h)); addr = GNUNET_NETWORK_get_addr (h); len = GNUNET_NETWORK_get_addrlen (h); return MHD_add_connection (daemon, fd, addr, len); } - -static long -get_file_size (const char* filename) -{ - FILE *fp; - - fp = fopen (filename, "rb"); - if (fp) - { - long size; - - if ((0 != fseek (fp, 0, SEEK_END)) || (-1 == (size = ftell (fp)))) - size = 0; - - fclose (fp); - - return size; - } - - return 0; -} - /** * Read file in filename * * @param filename file to read + * @param size pointer where filesize is stored * @return data */ static char* -load_file (const char* filename, unsigned int* size) +load_file (const char* filename, + unsigned int* size) { - FILE *fp; char *buffer; + uint64_t fsize; - *size = get_file_size (filename); - if (*size == 0) + if (GNUNET_OK != + GNUNET_DISK_file_size (filename, &fsize, + GNUNET_YES, GNUNET_YES)) return NULL; - - fp = fopen (filename, "rb"); - if (!fp) + if (fsize > MAX_PEM_SIZE) return NULL; - + *size = (unsigned int) fsize; buffer = GNUNET_malloc (*size); - if (!buffer) - { - fclose (fp); - return NULL; - } - - if (*size != fread (buffer, 1, *size, fp)) + if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize)) { GNUNET_free (buffer); - buffer = NULL; + return NULL; } - - fclose (fp); return buffer; } -/** SSL stuff **/ /** * Load PEM key from file * * @param key where to store the data * @param keyfile path to the PEM file + * @return GNUNET_OK on success */ -static void -load_key_from_file (gnutls_x509_privkey_t key, char* keyfile) +static int +load_key_from_file (gnutls_x509_privkey_t key, const char* keyfile) { gnutls_datum_t key_data; - key_data.data = NULL; int ret; - key_data.data = (unsigned char*)load_file (keyfile, &key_data.size); - + key_data.data = (unsigned char*) load_file (keyfile, &key_data.size); ret = gnutls_x509_privkey_import (key, &key_data, GNUTLS_X509_FMT_PEM); - if (GNUTLS_E_SUCCESS != ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable to import private key %s(ret=%d)\n", key_data.data, ret); + _("Unable to import private key from file `%s'\n"), + keyfile); GNUNET_break (0); } - GNUNET_free (key_data.data); + return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK; } + /** * Load cert from file * * @param crt struct to store data in * @param certfile path to pem file + * @return GNUNET_OK on success */ -static void +static int load_cert_from_file (gnutls_x509_crt_t crt, char* certfile) { gnutls_datum_t cert_data; cert_data.data = NULL; int ret; - cert_data.data = (unsigned char*)load_file (certfile, &cert_data.size); - + cert_data.data = (unsigned char*) load_file (certfile, &cert_data.size); ret = gnutls_x509_crt_import (crt, &cert_data, - GNUTLS_X509_FMT_PEM); + GNUTLS_X509_FMT_PEM); if (GNUTLS_E_SUCCESS != ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable to import certificate %s(ret=%d)\n", certfile, ret); + _("Unable to import certificate %s\n"), certfile); GNUNET_break (0); } - GNUNET_free (cert_data.data); - + return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK; } @@ -1759,10 +2683,8 @@ accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen) return MHD_NO; } - if (total_mhd_connections >= MAX_MHD_CONNECTIONS) - return MHD_NO; - - total_mhd_connections++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection accepted\n"); return MHD_YES; } @@ -1774,24 +2696,22 @@ accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen) * correct. In most cases we need to start a new daemon * * @param h the handle to add to a daemon - * @praram domain the domain the ssl daemon has to serve + * @param domain the domain the ssl daemon has to serve * @return MHD_YES on success */ static int -add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, char* domain) +add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, const char* domain) { struct MhdHttpList *hd = NULL; struct ProxyGNSCertificate *pgc; + struct NetworkHandleList *nh; - for (hd = mhd_httpd_head; hd != NULL; hd = hd->next) - { + for (hd = mhd_httpd_head; NULL != hd; hd = hd->next) if (0 == strcmp (hd->domain, domain)) break; - } if (NULL == hd) - { - + { pgc = generate_gns_certificate (domain); hd = GNUNET_malloc (sizeof (struct MhdHttpList)); @@ -1803,24 +2723,41 @@ add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, 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, (unsigned int) 128, - 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; GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd); } + + nh = GNUNET_malloc (sizeof (struct NetworkHandleList)); + nh->h = h; + + GNUNET_CONTAINER_DLL_insert (hd->socket_handles_head, + hd->socket_handles_tail, + nh); return add_handle_to_mhd (h, hd->daemon); } @@ -1851,6 +2788,8 @@ do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc) struct sockaddr_in remote_addr; struct in_addr *r_sin_addr; + struct NetworkHandleList *nh; + s5r->rtask = GNUNET_SCHEDULER_NO_TASK; if ((NULL != tc->write_ready) && @@ -1947,6 +2886,13 @@ do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Requested connection is HTTP\n"); + nh = GNUNET_malloc (sizeof (struct NetworkHandleList)); + nh->h = s5r->sock; + + GNUNET_CONTAINER_DLL_insert (mhd_httpd_head->socket_handles_head, + mhd_httpd_head->socket_handles_tail, + nh); + ret = add_handle_to_mhd ( s5r->sock, httpd ); } @@ -2098,8 +3044,6 @@ do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } - //GNUNET_CONTAINER_DLL_remove (s5conns.head, s5conns.tail, s5r); - } @@ -2127,7 +3071,7 @@ do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) if (NULL == s) { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "accept"); + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept"); return; } @@ -2143,7 +3087,6 @@ do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, s5r->sock, &do_read, s5r); - //GNUNET_CONTAINER_DLL_insert (s5conns.head, s5conns.tail, s5r); } @@ -2160,10 +3103,23 @@ do_shutdown (void *cls, struct MhdHttpList *hd; struct MhdHttpList *tmp_hd; + struct NetworkHandleList *nh; + 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) { @@ -2193,6 +3149,15 @@ do_shutdown (void *cls, hd->daemon = NULL; } + for (nh = hd->socket_handles_head; nh != NULL; nh = tmp_nh) + { + tmp_nh = nh->next; + + GNUNET_NETWORK_socket_close (nh->h); + + GNUNET_free (nh); + } + if (NULL != hd->proxy_cert) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -2215,9 +3180,30 @@ do_shutdown (void *cls, ctask->curl = NULL; if (NULL != ctask->headers) curl_slist_free_all (ctask->headers); + if (NULL != ctask->resolver) + curl_slist_free_all (ctask->resolver); + + 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); } + curl_multi_cleanup (curl_multi); GNUNET_GNS_disconnect (gns_handle); } @@ -2280,67 +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; - return GNUNET_YES; -} - -/** - * Loads the users local shorten zone key - * - * @return GNUNET_YES on success - */ -static int -load_local_shorten_key (const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - char *keyfile; - struct GNUNET_CRYPTO_RsaPrivateKey *key = NULL; - struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; - struct GNUNET_CRYPTO_ShortHashCode *zone = NULL; - struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename; + 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"); + } - if (GNUNET_NO == GNUNET_CONFIGURATION_get_value_yesno (cfg, "gns", - "AUTO_IMPORT_PKEY")) + if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile))) { - return GNUNET_NO; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to load private zone key %s!\n", keyfile); + 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", - "AUTO_IMPORT_ZONEKEY", - &keyfile)) + "SHORTEN_ZONEKEY", &keyfile)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable to load shorten key config value! (not fatal)\n"); - return GNUNET_NO; + "Unable to load shorten zone key config value!\n"); } - 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 key %s! (not fatal)\n", keyfile); + "Unable to load shorten zone key %s!\n", keyfile); + 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_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); - zone = &local_gns_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; } @@ -2359,19 +3352,26 @@ 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 = NULL; + curl_multi = curl_multi_init (); + if (NULL == curl_multi) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create cURL multo handle!\n"); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Loading CA\n"); - cafile = cafile_opt; - if (NULL == cafile) { if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy", @@ -2386,20 +3386,25 @@ 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); gnutls_x509_privkey_init (&proxy_ca.key); - load_cert_from_file (proxy_ca.cert, cafile); - load_key_from_file (proxy_ca.key, cafile); + if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) || + (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) ) + { + // FIXME: release resources... + return; + } GNUNET_free_non_null (cafile_cfg); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading Template\n"); - + compile_regex (&re_dotplus, (char*) RE_A_HREF); gns_handle = GNUNET_GNS_connect (cfg); @@ -2411,8 +3416,6 @@ run (void *cls, char *const *args, const char *cfgfile, return; } - use_shorten = load_local_shorten_key (cfg); - if (NULL == gns_handle) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -2472,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)) @@ -2481,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); @@ -2495,7 +3498,14 @@ run (void *cls, char *const *args, const char *cfgfile, mhd_unix_sock_addr.sun_family = AF_UNIX; strcpy (mhd_unix_sock_addr.sun_path, proxy_sockfile); - unlink (proxy_sockfile); + +#if LINUX + mhd_unix_sock_addr.sun_path[0] = '\0'; +#endif +#if HAVE_SOCKADDR_IN_SIN_LEN + mhd_unix_sock_addr.sun_len = (u_char) sizeof (struct sockaddr_un); +#endif + len = strlen (proxy_sockfile) + sizeof(AF_UNIX); GNUNET_free (proxy_sockfile); @@ -2516,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), - MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128, +#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); @@ -2569,6 +3589,7 @@ main (int argc, char *const *argv) if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) return 2; + GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL); ret = (GNUNET_OK == @@ -2576,5 +3597,11 @@ main (int argc, char *const *argv) _("GNUnet GNS proxy"), options, &run, NULL)) ? 0 : 1; + GNUNET_free_non_null ((char*)argv); + return ret; } + + + +