2 This file is part of GNUnet.
3 (C) 2012-2013 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
21 * @author Martin Schanzenbach
22 * @file src/gns/gnunet-gns-proxy.c
23 * @brief HTTP(S) proxy that rewrites URIs and fakes certificats to make GNS work
24 * with legacy browsers
27 #include <microhttpd.h>
28 #include <curl/curl.h>
29 #include <gnutls/gnutls.h>
30 #include <gnutls/x509.h>
31 #include <gnutls/abstract.h>
32 #include <gnutls/crypto.h>
34 #include "gnunet_util_lib.h"
35 #include "gnunet_gns_service.h"
36 #include "gns_proxy_proto.h"
39 #define HAVE_MHD_NO_LISTEN_SOCKET (MHD_VERSION >= 0x00091401)
41 #define GNUNET_GNS_PROXY_PORT 7777
42 #define MHD_MAX_CONNECTIONS 300
43 #define MAX_HTTP_URI_LENGTH 2048
44 #define POSTBUFFERSIZE 4096
46 /* MHD/cURL defines */
54 #define HTML_HDR_CONTENT "Content-Type: text/html"
56 /* buffer padding for proper RE matching */
57 #define CURL_BUF_PADDING 1000
60 //#define RE_DOTPLUS "<a href=\"http://(([A-Za-z]+[.])+)([+])"
61 #define RE_A_HREF "href=\"https?://(([A-Za-z0-9]+[.])+)([+]|[a-z]+)"
62 #define RE_N_MATCHES 4
64 /* The usual suspects */
66 #define HTTPS_PORT 443
70 * A structure for CA cert/key
75 gnutls_x509_crt_t cert;
78 gnutls_x509_privkey_t key;
81 #define MAX_PEM_SIZE (10 * 1024)
84 * Structure for GNS certificates
86 struct ProxyGNSCertificate
88 /* The certificate as PEM */
89 char cert[MAX_PEM_SIZE];
91 /* The private key as PEM */
92 char key[MAX_PEM_SIZE];
97 * A structure for socks requests
101 /* The client socket */
102 struct GNUNET_NETWORK_Handle *sock;
104 /* The server socket */
105 struct GNUNET_NETWORK_Handle *remote_sock;
107 /* The socks state */
110 /* Client socket read task */
111 GNUNET_SCHEDULER_TaskIdentifier rtask;
113 /* Server socket read task */
114 GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
116 /* Client socket write task */
117 GNUNET_SCHEDULER_TaskIdentifier wtask;
119 /* Server socket write task */
120 GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
128 /* Length of data in read buffer */
129 unsigned int rbuf_len;
131 /* Length of data in write buffer */
132 unsigned int wbuf_len;
134 /* This handle is scheduled for cleanup? */
137 /* Shall we close the client socket on cleanup? */
142 * DLL for Network Handles
144 struct NetworkHandleList
147 struct NetworkHandleList *next;
150 struct NetworkHandleList *prev;
153 struct GNUNET_NETWORK_Handle *h;
157 * A structure for all running Httpds
162 struct MhdHttpList *prev;
165 struct MhdHttpList *next;
167 /* is this an ssl daemon? */
170 /* the domain name to server (only important for SSL) */
173 /* The daemon handle */
174 struct MHD_Daemon *daemon;
176 /* Optional proxy certificate used */
177 struct ProxyGNSCertificate *proxy_cert;
180 GNUNET_SCHEDULER_TaskIdentifier httpd_task;
182 /* Handles associated with this daemon */
183 struct NetworkHandleList *socket_handles_head;
185 /* Handles associated with this daemon */
186 struct NetworkHandleList *socket_handles_tail;
190 * A structure for MHD<->cURL streams
195 struct ProxyCurlTask *prev;
198 struct ProxyCurlTask *next;
203 /* Optional header replacements for curl (LEHO) */
204 struct curl_slist *headers;
206 /* Optional resolver replacements for curl (LEHO) */
207 struct curl_slist *resolver;
209 /* curl response code */
210 long curl_response_code;
212 /* The URL to fetch */
213 char url[MAX_HTTP_URI_LENGTH];
215 /* The cURL write buffer / MHD read buffer */
216 char buffer[CURL_MAX_WRITE_SIZE + CURL_BUF_PADDING];
218 /* Read pos of the data in the buffer */
219 char *buffer_read_ptr;
221 /* Write pos in the buffer */
222 char *buffer_write_ptr;
225 struct MHD_Connection *connection;
228 size_t put_read_offset;
229 size_t put_read_size;
232 struct MHD_PostProcessor *post_handler;
235 struct ProxyUploadData *upload_data_head;
236 struct ProxyUploadData *upload_data_tail;
238 /* the type of POST encoding */
241 struct curl_httppost *httppost;
243 struct curl_httppost *httppost_last;
245 /* Number of bytes in buffer */
246 unsigned int bytes_in_buffer;
249 GNUNET_SCHEDULER_TaskIdentifier pp_task;
252 struct ProxyREMatch *pp_match_head;
255 struct ProxyREMatch *pp_match_tail;
257 /* The associated daemon list entry */
258 struct MhdHttpList *mhd;
260 /* The associated response */
261 struct MHD_Response *response;
264 struct ProxySetCookieHeader *set_cookies_head;
267 struct ProxySetCookieHeader *set_cookies_tail;
269 /* The authority of the corresponding host (site of origin) */
272 /* The hostname (Host header field) */
275 /* The LEgacy HOstname (can be empty) */
281 /* The buffer status (BUF_WAIT_FOR_CURL or BUF_WAIT_FOR_MHD) */
282 enum BufferStatus buf_status;
284 /* connection status */
287 /* is curl running? */
293 /* Already accepted */
296 /* Indicates wheather the download is in progress */
297 int download_in_progress;
299 /* Indicates wheather the download was successful */
300 int download_is_finished;
302 /* Indicates wheather the download failed */
305 /* Indicates wheather we need to parse HTML */
308 /* Indicates wheather we are postprocessing the HTML right now */
309 int is_postprocessing;
311 /* Indicates wheather postprocessing has finished */
321 * Struct for RE matches in postprocessing of HTML
326 struct ProxyREMatch *next;
329 struct ProxyREMatch *prev;
331 /* start of match in buffer */
334 /* end of match in buffer */
337 /* associated proxycurltask */
338 struct ProxyCurlTask *ctask;
347 struct GNUNET_GNS_ShortenRequest *shorten_task;
358 * Struct for set-cookies
360 struct ProxySetCookieHeader
363 struct ProxySetCookieHeader *next;
366 struct ProxySetCookieHeader *prev;
373 * Post data structure
375 struct ProxyUploadData
378 struct ProxyUploadData *next;
381 struct ProxyUploadData *prev;
389 size_t content_length;
402 /* The port the proxy is running on (default 7777) */
403 static unsigned long port = GNUNET_GNS_PROXY_PORT;
405 /* The CA file (pem) to use for the proxy CA */
406 static char* cafile_opt;
408 /* The listen socket of the proxy */
409 static struct GNUNET_NETWORK_Handle *lsock;
411 /* The listen task ID */
412 static GNUNET_SCHEDULER_TaskIdentifier ltask;
414 /* The cURL download task */
415 static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
417 /* The non SSL httpd daemon handle */
418 static struct MHD_Daemon *httpd;
420 /* Number of current mhd connections */
421 static unsigned int total_mhd_connections;
423 /* The cURL multi handle */
424 static CURLM *curl_multi;
426 /* Handle to the GNS service */
427 static struct GNUNET_GNS_Handle *gns_handle;
429 /* DLL for ProxyCurlTasks */
430 static struct ProxyCurlTask *ctasks_head;
432 /* DLL for ProxyCurlTasks */
433 static struct ProxyCurlTask *ctasks_tail;
435 /* DLL for http daemons */
436 static struct MhdHttpList *mhd_httpd_head;
438 /* DLL for http daemons */
439 static struct MhdHttpList *mhd_httpd_tail;
441 /* Handle to the regex for dotplus (.+) replacement in HTML */
442 static regex_t re_dotplus;
444 /* The users local GNS zone hash */
445 static struct GNUNET_CRYPTO_ShortHashCode *local_gns_zone;
447 /* The users local private zone */
448 static struct GNUNET_CRYPTO_ShortHashCode *local_private_zone;
450 /* The users local shorten zone */
451 static struct GNUNET_CRYPTO_ShortHashCode *local_shorten_zone;
453 /* The CA for SSL certificate generation */
454 static struct ProxyCA proxy_ca;
456 /* UNIX domain socket for mhd */
457 #if !HAVE_MHD_NO_LISTEN_SOCKET
458 static struct GNUNET_NETWORK_Handle *mhd_unix_socket;
461 /* Shorten zone private key */
462 static struct GNUNET_CRYPTO_EccPrivateKey *shorten_zonekey;
466 * Checks if name is in tld
468 * @param name the name to check
469 * @param tld the TLD to check for (must NOT begin with ".")
470 * @return GNUNET_YES or GNUNET_NO
473 is_tld (const char* name, const char* tld)
475 size_t name_len = strlen (name);
476 size_t tld_len = strlen (tld);
478 GNUNET_break ('.' != tld[0]);
479 return ( (tld_len < name_len) &&
480 ( ('.' == name[name_len - tld_len - 1]) || (name_len == tld_len) ) &&
482 name + (name_len - tld_len),
488 con_post_data_iter (void *cls,
489 enum MHD_ValueKind kind,
491 const char *filename,
492 const char *content_type,
493 const char *transfer_encoding,
498 struct ProxyCurlTask* ctask = cls;
499 struct ProxyUploadData* pdata;
503 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
504 "Got POST data (file: %s, content type: %s): '%s=%.*s' at offset %llu size %llu\n",
505 filename, content_type,
506 key, (int) size, data,
507 (unsigned long long) off,
508 (unsigned long long) size);
509 GNUNET_assert (NULL != ctask->post_type);
511 if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
514 ctask->is_httppost = GNUNET_YES;
518 pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
519 pdata->key = GNUNET_strdup (key);
521 if (NULL != filename)
522 pdata->filename = GNUNET_strdup (filename);
523 if (NULL != content_type)
524 pdata->content_type = GNUNET_strdup (content_type);
525 pdata->value = GNUNET_malloc (size);
526 pdata->total_bytes = size;
527 memcpy (pdata->value, data, size);
528 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
529 ctask->upload_data_tail,
532 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
533 "Copied %llu bytes of POST Data\n",
534 (unsigned long long) size);
538 pdata = ctask->upload_data_tail;
539 new_value = GNUNET_malloc (size + pdata->total_bytes);
540 memcpy (new_value, pdata->value, pdata->total_bytes);
541 memcpy (new_value+off, data, size);
542 GNUNET_free (pdata->value);
543 pdata->value = new_value;
544 pdata->total_bytes += size;
549 if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
555 ctask->is_httppost = GNUNET_NO;
557 if (NULL != ctask->curl)
558 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
562 enc = curl_easy_escape (ctask->curl, key, 0);
569 pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
570 pdata->value = GNUNET_malloc (strlen (enc) + 3);
571 if (NULL != ctask->upload_data_head)
573 pdata->value[0] = '&';
574 memcpy (pdata->value+1, enc, strlen (enc));
577 memcpy (pdata->value, enc, strlen (enc));
578 pdata->value[strlen (pdata->value)] = '=';
579 pdata->bytes_left = strlen (pdata->value);
580 pdata->total_bytes = pdata->bytes_left;
583 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
584 "Escaped POST key: '%s'\n",
587 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
588 ctask->upload_data_tail,
593 enc = curl_easy_escape (ctask->curl, data, 0);
599 pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
600 pdata->value = GNUNET_malloc (strlen (enc) + 1);
601 memcpy (pdata->value, enc, strlen (enc));
602 pdata->bytes_left = strlen (pdata->value);
603 pdata->total_bytes = pdata->bytes_left;
606 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
607 "Escaped POST value: '%s'\n",
610 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
611 ctask->upload_data_tail,
618 * Read HTTP request header field 'Host'
620 * @param cls buffer to write to
621 * @param kind value kind
622 * @param key field key
623 * @param value field value
624 * @return MHD_NO when Host found
627 con_val_iter (void *cls,
628 enum MHD_ValueKind kind,
632 struct ProxyCurlTask *ctask = cls;
633 char* buf = ctask->host;
639 if (0 == strcmp ("Host", key))
641 port = strchr (value, ':');
644 strncpy (buf, value, port-value);
646 if ((1 != sscanf (port, "%u", &uport)) ||
647 (uport > UINT16_MAX) ||
649 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
650 "Unable to parse port!\n");
652 ctask->port = (uint16_t) uport;
659 if (0 == strcmp (MHD_HTTP_HEADER_ACCEPT_ENCODING, key))
664 if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_TYPE,
667 if (0 == strncasecmp (value,
668 MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
669 strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
670 ctask->post_type = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
671 else if (0 == strncasecmp (value,
672 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
673 strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
674 ctask->post_type = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
676 ctask->post_type = NULL;
680 cstr = GNUNET_malloc (strlen (key) + strlen (hdr_val) + 3);
681 GNUNET_snprintf (cstr, strlen (key) + strlen (hdr_val) + 3,
682 "%s: %s", key, hdr_val);
684 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
685 "Client Header: %s\n", cstr);
687 ctask->headers = curl_slist_append (ctask->headers, cstr);
695 * Callback for MHD response
698 * @param pos in buffer
700 * @param max space in buffer
701 * @return number of bytes written
704 mhd_content_cb (void *cls,
711 * Check HTTP response header for mime
713 * @param buffer curl buffer
714 * @param size curl blocksize
715 * @param nmemb curl blocknumber
717 * @return size of read bytes
720 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
722 size_t bytes = size * nmemb;
723 struct ProxyCurlTask *ctask = cls;
724 int html_mime_len = strlen (HTML_HDR_CONTENT);
725 int cookie_hdr_len = strlen (MHD_HTTP_HEADER_SET_COOKIE);
726 char hdr_mime[html_mime_len+1];
727 char hdr_generic[bytes+1];
728 char new_cookie_hdr[bytes+strlen (ctask->leho)+1];
729 char new_location[MAX_HTTP_URI_LENGTH+500];
739 char cors_hdr[strlen (ctask->leho) + strlen ("https://")];
741 if (NULL == ctask->response)
743 /* FIXME: get total size from curl (if available) */
744 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
745 "Creating response for %s\n", ctask->url);
746 ctask->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
747 sizeof (ctask->buffer),
752 /* if we have a leho add a CORS header */
753 if (0 != strcmp ("", ctask->leho))
755 /* We could also allow ssl and http here */
756 if (ctask->mhd->is_ssl)
757 sprintf (cors_hdr, "https://%s", ctask->leho);
759 sprintf (cors_hdr, "http://%s", ctask->leho);
761 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
762 "MHD: Adding CORS header field %s\n",
765 if (GNUNET_NO == MHD_add_response_header (ctask->response,
766 "Access-Control-Allow-Origin",
769 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
770 "MHD: Error adding CORS header field %s\n",
774 ctask->ready_to_queue = GNUNET_YES;
777 if (html_mime_len <= bytes)
779 memcpy (hdr_mime, buffer, html_mime_len);
780 hdr_mime[html_mime_len] = '\0';
782 if (0 == strcmp (hdr_mime, HTML_HDR_CONTENT))
784 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
785 "Got HTML HTTP response header\n");
786 ctask->parse_content = GNUNET_YES;
790 if (cookie_hdr_len > bytes)
793 memcpy (hdr_generic, buffer, bytes);
794 hdr_generic[bytes] = '\0';
796 if ('\n' == hdr_generic[bytes-1])
797 hdr_generic[bytes-1] = '\0';
799 if (hdr_generic[bytes-2] == '\r')
800 hdr_generic[bytes-2] = '\0';
802 if (0 == memcmp (hdr_generic,
803 MHD_HTTP_HEADER_SET_COOKIE,
806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
807 "Looking for cookie in: `%s'\n", hdr_generic);
808 ndup = GNUNET_strdup (hdr_generic+cookie_hdr_len+1);
809 memset (new_cookie_hdr, 0, sizeof (new_cookie_hdr));
810 for (tok = strtok (ndup, ";"); tok != NULL; tok = strtok (NULL, ";"))
812 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
813 "Got Cookie token: %s\n", tok);
814 //memcpy (new_cookie_hdr+offset, tok, strlen (tok));
815 if (0 == memcmp (tok, " domain", strlen (" domain")))
817 cookie_domain = tok + strlen (" domain") + 1;
819 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
820 "Got Set-Cookie Domain: %s\n", cookie_domain);
822 if (strlen (cookie_domain) < strlen (ctask->leho))
824 delta_cdomain = strlen (ctask->leho) - strlen (cookie_domain);
825 if (0 == strcmp (cookie_domain, ctask->leho + (delta_cdomain)))
827 GNUNET_snprintf (new_cookie_hdr+offset,
828 sizeof (new_cookie_hdr),
829 " domain=%s", ctask->authority);
830 offset += strlen (" domain=") + strlen (ctask->authority);
831 new_cookie_hdr[offset] = ';';
836 else if (strlen (cookie_domain) == strlen (ctask->leho))
838 if (0 == strcmp (cookie_domain, ctask->leho))
840 GNUNET_snprintf (new_cookie_hdr+offset,
841 sizeof (new_cookie_hdr),
842 " domain=%s", ctask->host);
843 offset += strlen (" domain=") + strlen (ctask->host);
844 new_cookie_hdr[offset] = ';';
849 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
850 "Cookie domain invalid\n");
854 memcpy (new_cookie_hdr+offset, tok, strlen (tok));
855 offset += strlen (tok);
856 new_cookie_hdr[offset] = ';';
862 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
863 "Got Set-Cookie HTTP header %s\n", new_cookie_hdr);
865 if (GNUNET_NO == MHD_add_response_header (ctask->response,
866 MHD_HTTP_HEADER_SET_COOKIE,
869 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
870 "MHD: Error adding set-cookie header field %s\n",
871 hdr_generic+cookie_hdr_len+1);
876 ndup = GNUNET_strdup (hdr_generic);
877 hdr_type = strtok (ndup, ":");
879 if (NULL == hdr_type)
885 hdr_val = strtok (NULL, "");
895 if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
897 if (ctask->mhd->is_ssl)
899 sprintf (leho_host, "https://%s", ctask->leho);
900 sprintf (real_host, "https://%s", ctask->host);
904 sprintf (leho_host, "http://%s", ctask->leho);
905 sprintf (real_host, "http://%s", ctask->host);
908 if (0 == memcmp (leho_host, hdr_val, strlen (leho_host)))
910 sprintf (new_location, "%s%s", real_host, hdr_val+strlen (leho_host));
911 hdr_val = new_location;
915 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
916 "Trying to set %s: %s\n",
919 if (GNUNET_NO == MHD_add_response_header (ctask->response,
923 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
924 "MHD: Error adding %s header field %s\n",
936 * @param hd a http daemon list entry
939 run_httpd (struct MhdHttpList *hd);
951 * Task run whenever HTTP server operations are pending.
954 * @param tc sched context
958 const struct GNUNET_SCHEDULER_TaskContext *tc);
962 run_mhd_now (struct MhdHttpList *hd)
964 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
966 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
967 "MHD: killing old task\n");
968 GNUNET_SCHEDULER_cancel (hd->httpd_task);
970 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
971 "MHD: Scheduling MHD now\n");
972 hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, hd);
977 * Ask cURL for the select sets and schedule download
980 curl_download_prepare (void);
984 * Callback to free content
986 * @param cls content to free
987 * @param tc task context
990 mhd_content_free (void *cls,
991 const struct GNUNET_SCHEDULER_TaskContext *tc)
993 struct ProxyCurlTask *ctask = cls;
994 struct ProxyUploadData *pdata;
996 GNUNET_assert (NULL == ctask->pp_match_head);
997 if (NULL != ctask->headers)
998 curl_slist_free_all (ctask->headers);
1000 if (NULL != ctask->headers)
1001 curl_slist_free_all (ctask->resolver);
1003 if (NULL != ctask->response)
1004 MHD_destroy_response (ctask->response);
1006 if (NULL != ctask->post_handler)
1007 MHD_destroy_post_processor (ctask->post_handler);
1009 if (GNUNET_SCHEDULER_NO_TASK != ctask->pp_task)
1010 GNUNET_SCHEDULER_cancel (ctask->pp_task);
1012 for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
1014 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1015 ctask->upload_data_tail,
1017 GNUNET_free_non_null (pdata->filename);
1018 GNUNET_free_non_null (pdata->content_type);
1019 GNUNET_free_non_null (pdata->key);
1020 GNUNET_free_non_null (pdata->value);
1021 GNUNET_free (pdata);
1023 GNUNET_free (ctask);
1028 * Callback for MHD response
1030 * @param cls closure
1031 * @param pos in buffer
1033 * @param max space in buffer
1034 * @return number of bytes written
1037 mhd_content_cb (void *cls,
1042 struct ProxyCurlTask *ctask = cls;
1043 struct ProxyREMatch *re_match;
1045 size_t bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1047 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1048 "MHD: content cb for %s. To copy: %u\n",
1049 ctask->url, (unsigned int) bytes_to_copy);
1050 if ((GNUNET_YES == ctask->download_is_finished) &&
1051 (GNUNET_NO == ctask->download_error) &&
1052 (0 == bytes_to_copy))
1054 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1055 "MHD: sending response for %s\n", ctask->url);
1056 ctask->download_in_progress = GNUNET_NO;
1057 run_mhd_now (ctask->mhd);
1058 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1059 total_mhd_connections--;
1060 return MHD_CONTENT_READER_END_OF_STREAM;
1063 if ((GNUNET_YES == ctask->download_error) &&
1064 (GNUNET_YES == ctask->download_is_finished) &&
1065 (0 == bytes_to_copy))
1067 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1068 "MHD: sending error response\n");
1069 ctask->download_in_progress = GNUNET_NO;
1070 run_mhd_now (ctask->mhd);
1071 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1072 total_mhd_connections--;
1073 return MHD_CONTENT_READER_END_WITH_ERROR;
1076 if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
1080 for (re_match = ctask->pp_match_head; NULL != re_match; re_match = ctask->pp_match_head)
1082 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1083 "MHD: Processing PP %s\n",
1084 re_match->hostname);
1085 bytes_to_copy = re_match->start - ctask->buffer_read_ptr;
1086 if (bytes_to_copy+copied > max)
1088 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1089 "MHD: buffer in response too small for %u. Using available space (%d). (%s)\n",
1090 (unsigned int) bytes_to_copy,
1093 memcpy (buf+copied, ctask->buffer_read_ptr, max-copied);
1094 ctask->buffer_read_ptr += max-copied;
1096 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1097 "MHD: copied %d bytes\n", (int) copied);
1101 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1102 "MHD: copying %u bytes to mhd response at offset %d\n",
1103 (unsigned int) bytes_to_copy, ctask->buffer_read_ptr);
1104 memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1105 copied += bytes_to_copy;
1107 if (GNUNET_NO == re_match->done)
1109 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1110 "MHD: Waiting for PP of %s\n", re_match->hostname);
1111 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1112 "MHD: copied %d bytes\n", (int) copied);
1113 ctask->buffer_read_ptr += bytes_to_copy;
1117 if (strlen (re_match->result) > (max - copied))
1119 //FIXME partially copy domain here
1120 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1121 "MHD: buffer in response too small for %s! (%s)\n",
1124 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1125 "MHD: copied %d bytes\n", (int) copied);
1126 ctask->buffer_read_ptr += bytes_to_copy;
1130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1131 "MHD: Adding PP result %s to buffer\n",
1133 memcpy (buf + copied, re_match->result, strlen (re_match->result));
1134 copied += strlen (re_match->result);
1135 ctask->buffer_read_ptr = re_match->end;
1136 GNUNET_CONTAINER_DLL_remove (ctask->pp_match_head,
1137 ctask->pp_match_tail,
1139 GNUNET_free (re_match);
1142 bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1144 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1145 "MHD: copied: %d left: %u, space left in buf: %d\n",
1147 (unsigned int) bytes_to_copy, (int) (max - copied));
1149 if (GNUNET_NO == ctask->download_is_finished)
1151 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1152 "MHD: Purging buffer\n");
1153 memmove (ctask->buffer, ctask->buffer_read_ptr, bytes_to_copy);
1154 ctask->buffer_read_ptr = ctask->buffer;
1155 ctask->buffer_write_ptr = ctask->buffer + bytes_to_copy;
1156 ctask->buffer[bytes_to_copy] = '\0';
1159 if (bytes_to_copy + copied > max)
1160 bytes_to_copy = max - copied;
1161 memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1162 ctask->buffer_read_ptr += bytes_to_copy;
1163 copied += bytes_to_copy;
1164 ctask->buf_status = BUF_WAIT_FOR_CURL;
1166 if (NULL != ctask->curl)
1167 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
1169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1170 "MHD: copied %d bytes\n", (int) copied);
1171 run_mhd_now (ctask->mhd);
1177 * Shorten result callback
1179 * @param cls the proxycurltask
1180 * @param short_name the shortened name (NULL on error)
1183 process_shorten (void* cls, const char* short_name)
1185 struct ProxyREMatch *re_match = cls;
1186 char result[sizeof (re_match->result)];
1188 if (NULL == short_name)
1190 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1191 "PP: Unable to shorten %s\n",
1192 re_match->hostname);
1193 GNUNET_CONTAINER_DLL_remove (re_match->ctask->pp_match_head,
1194 re_match->ctask->pp_match_tail,
1196 GNUNET_free (re_match);
1200 if (0 == strcmp (short_name, re_match->ctask->leho))
1201 strcpy (result, re_match->ctask->host);
1203 strcpy (result, short_name);
1205 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1206 "PP: Shorten %s -> %s\n",
1210 if (re_match->is_ssl)
1211 sprintf (re_match->result, "href=\"https://%s", result);
1213 sprintf (re_match->result, "href=\"http://%s", result);
1215 re_match->done = GNUNET_YES;
1216 run_mhd_now (re_match->ctask->mhd);
1221 * Postprocess data in buffer. From read ptr to write ptr
1223 * @param cls the curlproxytask
1224 * @param tc task context
1227 postprocess_buffer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1229 struct ProxyCurlTask *ctask = cls;
1230 struct ProxyREMatch *re_match;
1231 char* re_ptr = ctask->buffer_read_ptr;
1232 char re_hostname[255];
1233 regmatch_t m[RE_N_MATCHES];
1235 ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
1237 if (GNUNET_YES != ctask->parse_content)
1239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1240 "PP: Not parsing content\n");
1241 ctask->buf_status = BUF_WAIT_FOR_MHD;
1242 run_mhd_now (ctask->mhd);
1246 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1247 "PP: We need to parse the HTML\n");
1249 /* 0 means match found */
1250 while (0 == regexec (&re_dotplus, re_ptr, RE_N_MATCHES, m, 0))
1252 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1253 "PP: regex match\n");
1255 GNUNET_assert (m[1].rm_so != -1);
1257 memset (re_hostname, 0, sizeof (re_hostname));
1258 memcpy (re_hostname, re_ptr+m[1].rm_so, (m[3].rm_eo-m[1].rm_so));
1261 re_match = GNUNET_malloc (sizeof (struct ProxyREMatch));
1262 re_match->start = re_ptr + m[0].rm_so;
1263 re_match->end = re_ptr + m[3].rm_eo;
1264 re_match->done = GNUNET_NO;
1265 re_match->ctask = ctask;
1267 if ('s' == *(re_ptr+m[1].rm_so-strlen("://")-1)) //FIXME strcmp
1268 re_match->is_ssl = GNUNET_YES;
1270 re_match->is_ssl = GNUNET_NO;
1272 strcpy (re_match->hostname, re_hostname);
1273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1274 "PP: Got hostname %s\n", re_hostname);
1275 re_ptr += m[3].rm_eo;
1277 if (GNUNET_YES == is_tld (re_match->hostname, GNUNET_GNS_TLD_PLUS))
1279 re_match->hostname[strlen(re_match->hostname)-1] = '\0';
1280 strcpy (re_match->hostname+strlen(re_match->hostname),
1284 re_match->shorten_task = GNUNET_GNS_shorten_zone (gns_handle,
1290 re_match); //FIXME cancel appropriately
1292 GNUNET_CONTAINER_DLL_insert_tail (ctask->pp_match_head,
1293 ctask->pp_match_tail,
1297 ctask->buf_status = BUF_WAIT_FOR_MHD;
1298 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1299 "PP: No more matches\n");
1300 run_mhd_now (ctask->mhd);
1305 * Handle data from cURL
1307 * @param ptr pointer to the data
1308 * @param size number of blocks of data
1309 * @param nmemb blocksize
1310 * @param ctx the curlproxytask
1311 * @return number of bytes handled
1314 curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx)
1316 const char *cbuf = ptr;
1317 size_t total = size * nmemb;
1318 struct ProxyCurlTask *ctask = ctx;
1319 size_t buf_space = sizeof (ctask->buffer) -
1320 (ctask->buffer_write_ptr-ctask->buffer);
1322 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1323 "CURL: Got %d. %d free in buffer\n",
1326 if (BUF_WAIT_FOR_CURL != ctask->buf_status)
1327 return CURL_WRITEFUNC_PAUSE;
1329 if (total > (buf_space - CURL_BUF_PADDING))
1331 if (ctask->buf_status == BUF_WAIT_FOR_CURL)
1333 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1334 "CURL: Buffer full starting postprocessing\n");
1335 ctask->buf_status = BUF_WAIT_FOR_PP;
1336 ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer,
1338 return CURL_WRITEFUNC_PAUSE;
1341 /* we should not get called in that case */
1342 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1343 "CURL: called out of context and no space in buffer!\n");
1344 return CURL_WRITEFUNC_PAUSE;
1347 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1348 "CURL: Copying %d bytes to buffer (%s)\n", total, ctask->url);
1349 memcpy (ctask->buffer_write_ptr, cbuf, total);
1350 ctask->bytes_in_buffer += total;
1351 ctask->buffer_write_ptr += total;
1352 ctask->buffer_write_ptr[0] = '\0';
1359 * cURL callback for put data
1362 put_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1364 struct ProxyCurlTask *ctask = cls;
1365 struct ProxyUploadData *pdata = ctask->upload_data_head;
1366 size_t len = size * nmemb;
1370 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1371 "CURL: put read callback\n");
1374 return CURL_READFUNC_PAUSE;
1377 if (NULL == pdata->value)
1379 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1380 "CURL: Terminating PUT\n");
1382 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1383 ctask->upload_data_tail,
1385 GNUNET_free (pdata);
1389 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1390 "CURL: read callback value %s\n", pdata->value);
1392 to_copy = pdata->bytes_left;
1396 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1397 memcpy (buf, pos, to_copy);
1398 pdata->bytes_left -= to_copy;
1399 if (pdata->bytes_left <= 0)
1401 GNUNET_free (pdata->value);
1402 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1403 ctask->upload_data_tail,
1405 GNUNET_free (pdata);
1412 * cURL callback for post data
1415 post_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1417 struct ProxyCurlTask *ctask = cls;
1418 struct ProxyUploadData *pdata = ctask->upload_data_head;
1419 size_t len = size * nmemb;
1423 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1424 "CURL: read callback\n");
1427 return CURL_READFUNC_PAUSE;
1430 if (NULL == pdata->value)
1432 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1433 "CURL: Terminating POST data\n");
1435 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1436 ctask->upload_data_tail,
1438 GNUNET_free (pdata);
1442 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1443 "CURL: read callback value %s\n", pdata->value);
1445 to_copy = pdata->bytes_left;
1449 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1450 memcpy (buf, pos, to_copy);
1451 pdata->bytes_left -= to_copy;
1452 if (pdata->bytes_left <= 0)
1454 GNUNET_free (pdata->value);
1455 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1456 ctask->upload_data_tail,
1458 GNUNET_free (pdata);
1465 * Task that is run when we are ready to receive more data
1468 * @param cls closure
1469 * @param tc task context
1472 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1476 * Ask cURL for the select sets and schedule download
1479 curl_download_prepare ()
1486 struct GNUNET_NETWORK_FDSet *grs;
1487 struct GNUNET_NETWORK_FDSet *gws;
1489 struct GNUNET_TIME_Relative rtime;
1495 if (CURLM_OK != (mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max)))
1497 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1498 "%s failed at %s:%d: `%s'\n",
1499 "curl_multi_fdset", __FILE__, __LINE__,
1500 curl_multi_strerror (mret));
1501 //TODO cleanup here?
1505 GNUNET_break (CURLM_OK == curl_multi_timeout (curl_multi, &to));
1506 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1507 "cURL multi fds: max=%d timeout=%lld\n", max, (long long) to);
1509 rtime = GNUNET_TIME_UNIT_FOREVER_REL;
1511 rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1512 grs = GNUNET_NETWORK_fdset_create ();
1513 gws = GNUNET_NETWORK_fdset_create ();
1514 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1515 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1516 if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
1517 GNUNET_SCHEDULER_cancel (curl_download_task);
1520 curl_download_task =
1521 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1524 &curl_task_download, curl_multi);
1526 else if (NULL != ctasks_head)
1528 /* as specified in curl docs */
1529 curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
1530 &curl_task_download,
1533 GNUNET_NETWORK_fdset_destroy (gws);
1534 GNUNET_NETWORK_fdset_destroy (grs);
1539 * Task that is run when we are ready to receive more data
1542 * @param cls closure
1543 * @param tc task context
1546 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1550 struct CURLMsg *msg;
1552 struct ProxyCurlTask *ctask;
1555 struct ProxyCurlTask *clean_head = NULL;
1556 struct ProxyCurlTask *clean_tail = NULL;
1558 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1560 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1562 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1563 "Shutdown requested while trying to download\n");
1567 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1575 mret = curl_multi_perform (curl_multi, &running);
1577 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1578 "Running curl tasks: %d\n", running);
1580 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1582 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1583 "CTask: %s\n", ctask->url);
1587 if (num_ctasks != running)
1589 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1590 "%d ctasks, %d curl running\n", num_ctasks, running);
1596 msg = curl_multi_info_read (curl_multi, &msgnum);
1597 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1598 "Messages left: %d\n", msgnum);
1605 if ((msg->data.result != CURLE_OK) &&
1606 (msg->data.result != CURLE_GOT_NOTHING))
1608 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1609 "Download curl failed");
1611 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1613 if (NULL == ctask->curl)
1616 if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
1619 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1620 "CURL: Download failed for task %s: %s.\n",
1622 curl_easy_strerror (msg->data.result));
1623 ctask->download_is_finished = GNUNET_YES;
1624 ctask->download_error = GNUNET_YES;
1625 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1626 CURLINFO_RESPONSE_CODE,
1628 ctask->curl_response_code = resp_code;
1629 ctask->ready_to_queue = MHD_YES;
1630 ctask->buf_status = BUF_WAIT_FOR_MHD;
1631 run_mhd_now (ctask->mhd);
1633 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1635 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1638 GNUNET_assert (ctask != NULL);
1642 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1643 "CURL: download completed.\n");
1645 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1647 if (NULL == ctask->curl)
1650 if (0 != memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)))
1653 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1654 "CURL: completed task %s found.\n", ctask->url);
1655 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1656 CURLINFO_RESPONSE_CODE,
1658 ctask->curl_response_code = resp_code;
1661 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1662 "CURL: Completed ctask!\n");
1663 if (GNUNET_SCHEDULER_NO_TASK == ctask->pp_task)
1665 ctask->buf_status = BUF_WAIT_FOR_PP;
1666 ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer,
1670 ctask->ready_to_queue = MHD_YES;
1671 ctask->download_is_finished = GNUNET_YES;
1673 /* We MUST not modify the multi handle else we loose messages */
1674 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1676 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1680 GNUNET_assert (ctask != NULL);
1682 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1683 "CURL: %s\n", curl_easy_strerror(msg->data.result));
1689 } while (msgnum > 0);
1691 for (ctask=clean_head; NULL != ctask; ctask = ctask->next)
1693 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1694 "CURL: Removing task %s.\n", ctask->url);
1695 curl_multi_remove_handle (curl_multi, ctask->curl);
1696 curl_easy_cleanup (ctask->curl);
1701 for (ctask=ctasks_head; NULL != ctask; ctask = ctask->next)
1704 if (num_ctasks != running)
1706 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1707 "CURL: %d tasks, %d running\n", num_ctasks, running);
1710 GNUNET_assert ( num_ctasks == running );
1712 } while (mret == CURLM_CALL_MULTI_PERFORM);
1714 if (mret != CURLM_OK)
1716 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CURL: %s failed at %s:%d: `%s'\n",
1717 "curl_multi_perform", __FILE__, __LINE__,
1718 curl_multi_strerror (mret));
1720 curl_download_prepare();
1725 * Process LEHO lookup
1727 * @param cls the ctask
1728 * @param rd_count number of records returned
1729 * @param rd record data
1732 process_leho_lookup (void *cls,
1734 const struct GNUNET_NAMESTORE_RecordData *rd)
1736 struct ProxyCurlTask *ctask = cls;
1737 char hosthdr[262]; //256 + "Host: "
1741 struct hostent *phost;
1743 char resolvename[512];
1746 strcpy (ctask->leho, "");
1749 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1750 "No LEHO present!\n");
1752 for (i=0; i<rd_count; i++)
1754 if (rd[i].record_type != GNUNET_NAMESTORE_TYPE_LEHO)
1757 memcpy (ctask->leho, rd[i].data, rd[i].data_size);
1759 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1760 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
1763 if (0 != strcmp (ctask->leho, ""))
1765 sprintf (hosthdr, "%s%s:%d", "Host: ", ctask->leho, ctask->port);
1766 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1767 "New HTTP header value: %s\n", hosthdr);
1768 ctask->headers = curl_slist_append (ctask->headers, hosthdr);
1769 GNUNET_assert (NULL != ctask->headers);
1770 ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers);
1771 if (CURLE_OK != ret)
1773 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s failed at %s:%d: `%s'\n",
1774 "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret));
1779 if (ctask->mhd->is_ssl)
1781 phost = (struct hostent*)gethostbyname (ctask->host);
1785 ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
1786 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1787 "SSL target server: %s\n", ssl_ip);
1788 sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
1789 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1790 "Curl resolve: %s\n", resolvename);
1791 ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
1792 curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver);
1793 sprintf (curlurl, "https://%s:%d%s", ctask->leho, ctask->port, ctask->url);
1794 curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1798 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1799 "gethostbyname failed for %s!\n", ctask->host);
1800 ctask->download_is_finished = GNUNET_YES;
1801 ctask->download_error = GNUNET_YES;
1806 if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
1808 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1809 "%s failed at %s:%d: `%s'\n",
1810 "curl_multi_add_handle", __FILE__, __LINE__,
1811 curl_multi_strerror (mret));
1812 ctask->download_is_finished = GNUNET_YES;
1813 ctask->download_error = GNUNET_YES;
1816 GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1818 curl_download_prepare ();
1824 * Initialize download and trigger curl
1826 * @param cls the proxycurltask
1827 * @param auth_name the name of the authority (site of origin) of ctask->host
1831 process_get_authority (void *cls,
1832 const char* auth_name)
1834 struct ProxyCurlTask *ctask = cls;
1836 if (NULL == auth_name)
1838 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1839 "Get authority failed!\n");
1840 strcpy (ctask->authority, "");
1844 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1845 "Get authority yielded %s\n", auth_name);
1846 strcpy (ctask->authority, auth_name);
1849 GNUNET_GNS_lookup_zone (gns_handle,
1852 GNUNET_NAMESTORE_TYPE_LEHO,
1853 GNUNET_YES, //Only cached for performance
1855 &process_leho_lookup,
1861 mhd_log_callback (void* cls, const char* url)
1863 struct ProxyCurlTask *ctask;
1865 ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
1866 strcpy (ctask->url, url);
1872 * Main MHD callback for handling requests.
1875 * @param con MHD connection handle
1876 * @param url the url in the request
1877 * @param meth the HTTP method used ("GET", "PUT", etc.)
1878 * @param ver the HTTP version string (i.e. "HTTP/1.1")
1879 * @param upload_data the data being uploaded (excluding HEADERS,
1880 * for a POST that fits into memory and that is encoded
1881 * with a supported encoding, the POST data will NOT be
1882 * given in upload_data and is instead available as
1883 * part of MHD_get_connection_values; very large POST
1884 * data *will* be made available incrementally in
1886 * @param upload_data_size set initially to the size of the
1887 * upload_data provided; the method must update this
1888 * value to the number of bytes NOT processed;
1889 * @param con_cls pointer to location where we store the 'struct Request'
1890 * @return MHD_YES if the connection was handled successfully,
1891 * MHD_NO if the socket must be closed due to a serious
1892 * error while handling the request
1895 create_response (void *cls,
1896 struct MHD_Connection *con,
1900 const char *upload_data,
1901 size_t *upload_data_size,
1904 struct MhdHttpList* hd = cls;
1905 const char* page = "<html><head><title>gnunet-gns-proxy</title>"
1906 "</head><body>cURL fail</body></html>";
1908 char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow!
1911 struct ProxyCurlTask *ctask = *con_cls;
1912 struct ProxyUploadData *fin_post;
1913 struct curl_forms forms[5];
1914 struct ProxyUploadData *upload_data_iter;
1917 if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) &&
1918 (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) &&
1919 (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)) &&
1920 (0 != strcasecmp (meth, MHD_HTTP_METHOD_HEAD)))
1922 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1923 "MHD: %s NOT IMPLEMENTED!\n", meth);
1928 if (GNUNET_NO == ctask->accepted)
1931 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1932 "Got %s request for %s\n", meth, url);
1934 ctask->curl = curl_easy_init();
1935 ctask->curl_running = GNUNET_NO;
1936 if (NULL == ctask->curl)
1938 ctask->response = MHD_create_response_from_buffer (strlen (page),
1940 MHD_RESPMEM_PERSISTENT);
1941 ret = MHD_queue_response (con,
1944 MHD_destroy_response (ctask->response);
1945 GNUNET_free (ctask);
1949 if (ctask->mhd->is_ssl)
1950 ctask->port = HTTPS_PORT;
1952 ctask->port = HTTP_PORT;
1954 MHD_get_connection_values (con,
1956 &con_val_iter, ctask);
1958 curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1959 curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
1960 curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1961 curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
1962 curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0);
1963 curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1965 if (GNUNET_NO == ctask->mhd->is_ssl)
1967 sprintf (curlurl, "http://%s:%d%s", ctask->host, ctask->port, ctask->url);
1968 curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1972 curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
1973 curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
1974 curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
1976 /* Add GNS header */
1977 ctask->headers = curl_slist_append (ctask->headers,
1979 ctask->accepted = GNUNET_YES;
1980 ctask->download_in_progress = GNUNET_YES;
1981 ctask->buf_status = BUF_WAIT_FOR_CURL;
1982 ctask->connection = con;
1983 ctask->curl_response_code = MHD_HTTP_OK;
1984 ctask->buffer_read_ptr = ctask->buffer;
1985 ctask->buffer_write_ptr = ctask->buffer;
1986 ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
1989 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
1991 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1992 "Setting up PUT\n");
1994 curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1);
1995 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
1996 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &put_read_callback);
1997 ctask->headers = curl_slist_append (ctask->headers,
1998 "Transfer-Encoding: chunked");
2001 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2003 //FIXME handle multipart
2004 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2005 "Setting up POST processor\n");
2006 ctask->post_handler = MHD_create_post_processor (con,
2008 &con_post_data_iter,
2010 ctask->headers = curl_slist_append (ctask->headers,
2011 "Transfer-Encoding: chunked");
2015 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
2017 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2018 "Setting NOBODY\n");
2019 curl_easy_setopt (ctask->curl, CURLOPT_NOBODY, 1);
2023 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2024 "MHD: Adding new curl task for %s\n", ctask->host);
2026 GNUNET_GNS_get_authority (gns_handle,
2028 &process_get_authority,
2030 ctask->ready_to_queue = GNUNET_NO;
2031 ctask->fin = GNUNET_NO;
2032 ctask->curl_running = GNUNET_YES;
2036 ctask = (struct ProxyCurlTask *) *con_cls;
2037 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2039 if (0 != *upload_data_size)
2042 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2043 "Invoking POST processor\n");
2044 MHD_post_process (ctask->post_handler,
2045 upload_data, *upload_data_size);
2046 *upload_data_size = 0;
2047 if ((GNUNET_NO == ctask->is_httppost) &&
2048 (GNUNET_NO == ctask->curl_running))
2050 curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
2051 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION,
2052 &post_read_callback);
2053 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
2055 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2056 "MHD: Adding new curl task for %s\n", ctask->host);
2058 GNUNET_GNS_get_authority (gns_handle,
2060 &process_get_authority,
2062 ctask->ready_to_queue = GNUNET_NO;
2063 ctask->fin = GNUNET_NO;
2064 ctask->curl_running = GNUNET_YES;
2068 else if (GNUNET_NO == ctask->post_done)
2070 if (GNUNET_YES == ctask->is_httppost)
2072 for (upload_data_iter = ctask->upload_data_head;
2073 NULL != upload_data_iter;
2074 upload_data_iter = upload_data_iter->next)
2077 if (NULL != upload_data_iter->filename)
2079 forms[i].option = CURLFORM_FILENAME;
2080 forms[i].value = upload_data_iter->filename;
2081 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2082 "Adding filename %s\n",
2086 if (NULL != upload_data_iter->content_type)
2088 forms[i].option = CURLFORM_CONTENTTYPE;
2089 forms[i].value = upload_data_iter->content_type;
2090 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2091 "Adding content type %s\n",
2095 forms[i].option = CURLFORM_PTRCONTENTS;
2096 forms[i].value = upload_data_iter->value;
2097 forms[i+1].option = CURLFORM_END;
2099 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2100 "Adding formdata for %s (len=%lld)\n",
2101 upload_data_iter->key,
2102 upload_data_iter->total_bytes);
2104 curl_formadd(&ctask->httppost, &ctask->httppost_last,
2105 CURLFORM_COPYNAME, upload_data_iter->key,
2106 CURLFORM_CONTENTSLENGTH, upload_data_iter->total_bytes,
2107 CURLFORM_ARRAY, forms,
2110 curl_easy_setopt (ctask->curl, CURLOPT_HTTPPOST,
2113 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2114 "MHD: Adding new curl task for %s\n", ctask->host);
2116 GNUNET_GNS_get_authority (gns_handle,
2118 &process_get_authority,
2120 ctask->ready_to_queue = GNUNET_YES;
2121 ctask->fin = GNUNET_NO;
2122 ctask->curl_running = GNUNET_YES;
2123 ctask->post_done = GNUNET_YES;
2127 fin_post = GNUNET_malloc (sizeof (struct ProxyUploadData));
2128 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
2129 ctask->upload_data_tail,
2131 ctask->post_done = GNUNET_YES;
2136 if (GNUNET_YES != ctask->ready_to_queue)
2137 return MHD_YES; /* wait longer */
2139 if (GNUNET_YES == ctask->fin)
2142 ctask->fin = GNUNET_YES;
2143 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2144 "MHD: Queueing response for %s\n", ctask->url);
2145 ret = MHD_queue_response (con, ctask->curl_response_code, ctask->response);
2146 run_mhd_now (ctask->mhd);
2157 struct MhdHttpList *hd;
2159 for (hd=mhd_httpd_head; NULL != hd; hd = hd->next)
2165 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
2171 * @param hd the daemon to run
2174 run_httpd (struct MhdHttpList *hd)
2179 struct GNUNET_NETWORK_FDSet *wrs;
2180 struct GNUNET_NETWORK_FDSet *wws;
2181 struct GNUNET_NETWORK_FDSet *wes;
2184 UNSIGNED_MHD_LONG_LONG timeout;
2185 struct GNUNET_TIME_Relative tv;
2190 wrs = GNUNET_NETWORK_fdset_create ();
2191 wes = GNUNET_NETWORK_fdset_create ();
2192 wws = GNUNET_NETWORK_fdset_create ();
2194 GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
2197 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2198 "MHD fds: max=%d\n", max);
2200 haveto = MHD_get_timeout (hd->daemon, &timeout);
2202 if (MHD_YES == haveto)
2203 tv.rel_value_us = (uint64_t) timeout * 1000LL;
2205 tv = GNUNET_TIME_UNIT_FOREVER_REL;
2206 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
2207 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
2208 GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
2210 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2211 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2213 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
2216 GNUNET_NETWORK_fdset_destroy (wrs);
2217 GNUNET_NETWORK_fdset_destroy (wws);
2218 GNUNET_NETWORK_fdset_destroy (wes);
2223 * Task run whenever HTTP server operations are pending.
2226 * @param tc sched context
2229 do_httpd (void *cls,
2230 const struct GNUNET_SCHEDULER_TaskContext *tc)
2232 struct MhdHttpList *hd = cls;
2234 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2235 "MHD: Main loop\n");
2236 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2237 MHD_run (hd->daemon);
2243 * Read data from socket
2245 * @param cls the closure
2246 * @param tc scheduler context
2249 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2253 * Read from remote end
2255 * @param cls closure
2256 * @param tc scheduler context
2259 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2263 * Write data to remote socket
2265 * @param cls the closure
2266 * @param tc scheduler context
2269 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2271 struct Socks5Request *s5r = cls;
2274 s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2276 if ((NULL != tc->read_ready) &&
2277 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
2278 ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
2281 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2282 "Successfully sent %d bytes to remote socket\n",
2287 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
2288 if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2289 GNUNET_SCHEDULER_cancel (s5r->rtask);
2290 if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
2291 GNUNET_SCHEDULER_cancel (s5r->wtask);
2292 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2293 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2294 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2295 GNUNET_NETWORK_socket_close (s5r->sock);
2301 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2308 * Clean up s5r handles
2310 * @param s5r the handle to destroy
2313 cleanup_s5r (struct Socks5Request *s5r)
2315 if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2316 GNUNET_SCHEDULER_cancel (s5r->rtask);
2317 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdwtask)
2318 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2319 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2320 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2321 if (NULL != s5r->remote_sock)
2322 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2323 if ((NULL != s5r->sock) && (s5r->cleanup_sock == GNUNET_YES))
2324 GNUNET_NETWORK_socket_close (s5r->sock);
2331 * Write data to socket
2333 * @param cls the closure
2334 * @param tc scheduler context
2337 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2339 struct Socks5Request *s5r = cls;
2342 s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2344 if ((NULL != tc->read_ready) &&
2345 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
2346 ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
2349 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2350 "Successfully sent %d bytes to socket\n",
2355 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
2356 s5r->cleanup = GNUNET_YES;
2357 s5r->cleanup_sock = GNUNET_YES;
2362 if (GNUNET_YES == s5r->cleanup)
2368 if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
2369 (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
2371 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2373 &do_read_remote, s5r);
2378 * Read from remote end
2380 * @param cls closure
2381 * @param tc scheduler context
2384 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2386 struct Socks5Request *s5r = cls;
2388 s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2389 if ((NULL != tc->write_ready) &&
2390 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
2391 (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
2392 sizeof (s5r->wbuf))))
2394 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2395 "Successfully read %d bytes from remote socket\n",
2400 if (0 == s5r->wbuf_len)
2401 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2402 "0 bytes received from remote... graceful shutdown!\n");
2403 if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2404 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2405 if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
2406 GNUNET_SCHEDULER_cancel (s5r->rtask);
2408 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2409 s5r->remote_sock = NULL;
2410 GNUNET_NETWORK_socket_close (s5r->sock);
2416 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2423 * Adds a socket to MHD
2425 * @param h the handle to the socket to add
2426 * @param daemon the daemon to add the fd to
2427 * @return whatever MHD_add_connection returns
2430 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
2433 struct sockaddr *addr;
2436 fd = dup (GNUNET_NETWORK_get_fd (h));
2437 addr = GNUNET_NETWORK_get_addr (h);
2438 len = GNUNET_NETWORK_get_addrlen (h);
2440 return MHD_add_connection (daemon, fd, addr, len);
2445 * Read file in filename
2447 * @param filename file to read
2448 * @param size pointer where filesize is stored
2449 * @return NULL on error
2452 load_file (const char* filename,
2459 GNUNET_DISK_file_size (filename, &fsize,
2460 GNUNET_YES, GNUNET_YES))
2462 if (fsize > MAX_PEM_SIZE)
2464 *size = (unsigned int) fsize;
2465 buffer = GNUNET_malloc (*size);
2466 if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize))
2468 GNUNET_free (buffer);
2476 * Load PEM key from file
2478 * @param key where to store the data
2479 * @param keyfile path to the PEM file
2480 * @return GNUNET_OK on success
2483 load_key_from_file (gnutls_x509_privkey_t key, const char* keyfile)
2485 gnutls_datum_t key_data;
2488 key_data.data = load_file (keyfile, &key_data.size);
2489 ret = gnutls_x509_privkey_import (key, &key_data,
2490 GNUTLS_X509_FMT_PEM);
2491 if (GNUTLS_E_SUCCESS != ret)
2493 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2494 _("Unable to import private key from file `%s'\n"),
2498 GNUNET_free (key_data.data);
2499 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2504 * Load cert from file
2506 * @param crt struct to store data in
2507 * @param certfile path to pem file
2508 * @return GNUNET_OK on success
2511 load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
2513 gnutls_datum_t cert_data;
2516 cert_data.data = load_file (certfile, &cert_data.size);
2517 ret = gnutls_x509_crt_import (crt, &cert_data,
2518 GNUTLS_X509_FMT_PEM);
2519 if (GNUTLS_E_SUCCESS != ret)
2521 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2522 _("Unable to import certificate %s\n"), certfile);
2525 GNUNET_free (cert_data.data);
2526 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2531 * Generate new certificate for specific name
2533 * @param name the subject name to generate a cert for
2534 * @return a struct holding the PEM data
2536 static struct ProxyGNSCertificate *
2537 generate_gns_certificate (const char *name)
2540 unsigned int serial;
2541 size_t key_buf_size;
2542 size_t cert_buf_size;
2543 gnutls_x509_crt_t request;
2547 ret = gnutls_x509_crt_init (&request);
2549 if (GNUTLS_E_SUCCESS != ret)
2554 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
2556 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
2558 struct ProxyGNSCertificate *pgc =
2559 GNUNET_malloc (sizeof (struct ProxyGNSCertificate));
2561 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
2563 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
2565 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
2567 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
2568 0, name, strlen (name));
2569 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_version (request, 3));
2571 ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
2573 etime = time (NULL);
2574 tm_data = localtime (&etime);
2577 ret = gnutls_x509_crt_set_serial (request,
2581 ret = gnutls_x509_crt_set_activation_time (request,
2584 etime = mktime (tm_data);
2591 ret = gnutls_x509_crt_set_expiration_time (request,
2593 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
2595 ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
2597 key_buf_size = sizeof (pgc->key);
2598 cert_buf_size = sizeof (pgc->cert);
2600 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exporting certificate...\n");
2602 gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
2603 pgc->cert, &cert_buf_size);
2605 gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
2606 pgc->key, &key_buf_size);
2609 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
2610 gnutls_x509_crt_deinit (request);
2618 * Accept policy for mhdaemons
2621 * @param addr the sockaddr
2622 * @param addrlen the sockaddr length
2623 * @return MHD_NO if sockaddr is wrong or number of connections is too high
2626 accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen)
2628 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2629 "In MHD accept policy cb\n");
2633 if (addr->sa_family == AF_UNIX)
2637 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2638 "Connection accepted\n");
2645 * Adds a socket to an SSL MHD instance
2646 * It is important the the domain name is
2647 * correct. In most cases we need to start a new daemon
2649 * @param h the handle to add to a daemon
2650 * @param domain the domain the ssl daemon has to serve
2651 * @return MHD_YES on success
2654 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, const char* domain)
2656 struct MhdHttpList *hd;
2657 struct ProxyGNSCertificate *pgc;
2658 struct NetworkHandleList *nh;
2660 for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2661 if (0 == strcmp (hd->domain, domain))
2666 pgc = generate_gns_certificate (domain);
2668 hd = GNUNET_malloc (sizeof (struct MhdHttpList));
2669 hd->is_ssl = GNUNET_YES;
2670 strcpy (hd->domain, domain);
2671 hd->proxy_cert = pgc;
2674 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2675 "No previous SSL instance found... starting new one for %s\n",
2677 #if HAVE_MHD_NO_LISTEN_SOCKET
2678 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET,
2681 &create_response, hd,
2682 MHD_OPTION_CONNECTION_LIMIT,
2683 MHD_MAX_CONNECTIONS,
2684 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2685 MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2686 MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2687 MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2688 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2692 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL,
2693 4444 /* dummy port */,
2695 &create_response, hd,
2696 MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
2697 MHD_OPTION_CONNECTION_LIMIT,
2698 MHD_MAX_CONNECTIONS,
2699 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2700 MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2701 MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2702 MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2703 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2707 GNUNET_assert (hd->daemon != NULL);
2708 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2710 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2713 nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2716 GNUNET_CONTAINER_DLL_insert (hd->socket_handles_head,
2717 hd->socket_handles_tail,
2720 return add_handle_to_mhd (h, hd->daemon);
2725 * Read data from incoming connection
2727 * @param cls the closure
2728 * @param tc the scheduler context
2731 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2733 struct Socks5Request *s5r = cls;
2734 struct socks5_client_hello *c_hello;
2735 struct socks5_server_hello *s_hello;
2736 struct socks5_client_request *c_req;
2737 struct socks5_server_response *s_resp;
2742 struct hostent *phost;
2744 struct sockaddr_in remote_addr;
2745 struct in_addr *r_sin_addr;
2746 struct NetworkHandleList *nh;
2748 s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
2750 if ((NULL != tc->write_ready) &&
2751 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
2752 (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
2753 sizeof (s5r->rbuf))))
2755 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2756 "Successfully read %d bytes from socket\n",
2761 if (s5r->rbuf_len != 0)
2762 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
2764 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
2766 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2767 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2768 if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
2769 GNUNET_SCHEDULER_cancel (s5r->wtask);
2770 if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2771 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2772 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2773 GNUNET_NETWORK_socket_close (s5r->sock);
2778 if (s5r->state == SOCKS5_INIT)
2780 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2782 c_hello = (struct socks5_client_hello*)&s5r->rbuf;
2784 GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
2786 s_hello = (struct socks5_server_hello*)&s5r->wbuf;
2787 s5r->wbuf_len = sizeof( struct socks5_server_hello );
2789 s_hello->version = c_hello->version;
2790 s_hello->auth_method = SOCKS_AUTH_NONE;
2792 /* Write response to client */
2793 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2797 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2801 s5r->state = SOCKS5_REQUEST;
2805 if (s5r->state == SOCKS5_REQUEST)
2807 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2808 "Processing SOCKS5 request\n");
2809 c_req = (struct socks5_client_request*)&s5r->rbuf;
2810 s_resp = (struct socks5_server_response*)&s5r->wbuf;
2811 //Only 10byte for ipv4 response!
2812 s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
2814 GNUNET_assert (c_req->addr_type == 3);
2816 dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
2817 memset(domain, 0, sizeof(domain));
2818 strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
2819 req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
2821 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2822 "Requested connection is %s:%d\n",
2826 if (is_tld (domain, GNUNET_GNS_TLD) ||
2827 is_tld (domain, GNUNET_GNS_TLD_ZKEY))
2829 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2830 "Requested connection is gnunet tld\n",
2834 if (ntohs(req_port) == HTTPS_PORT)
2836 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2837 "Requested connection is HTTPS\n");
2838 ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
2840 else if (NULL != httpd)
2842 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2843 "Requested connection is HTTP\n");
2844 nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2847 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head->socket_handles_head,
2848 mhd_httpd_head->socket_handles_tail,
2851 ret = add_handle_to_mhd ( s5r->sock, httpd );
2856 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2857 _("Failed to start HTTP server\n"));
2858 s_resp->version = 0x05;
2859 s_resp->reply = 0x01;
2860 s5r->cleanup = GNUNET_YES;
2861 s5r->cleanup_sock = GNUNET_YES;
2863 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2869 /* Signal success */
2870 s_resp->version = 0x05;
2871 s_resp->reply = 0x00;
2872 s_resp->reserved = 0x00;
2873 s_resp->addr_type = 0x01;
2875 s5r->cleanup = GNUNET_YES;
2876 s5r->cleanup_sock = GNUNET_NO;
2878 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2886 phost = (struct hostent*)gethostbyname (domain);
2889 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2890 "Resolve %s error!\n", domain );
2891 s_resp->version = 0x05;
2892 s_resp->reply = 0x01;
2893 s5r->cleanup = GNUNET_YES;
2894 s5r->cleanup_sock = GNUNET_YES;
2896 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2902 s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
2905 r_sin_addr = (struct in_addr*)(phost->h_addr);
2906 remote_ip = r_sin_addr->s_addr;
2907 memset(&remote_addr, 0, sizeof(remote_addr));
2908 remote_addr.sin_family = AF_INET;
2909 #if HAVE_SOCKADDR_IN_SIN_LEN
2910 remote_addr.sin_len = sizeof (remote_addr);
2912 remote_addr.sin_addr.s_addr = remote_ip;
2913 remote_addr.sin_port = req_port;
2915 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2916 "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
2920 GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
2921 (const struct sockaddr*)&remote_addr,
2922 sizeof (remote_addr)))
2923 && (errno != EINPROGRESS))
2925 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
2926 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2927 "socket request error...\n");
2928 s_resp->version = 0x05;
2929 s_resp->reply = 0x01;
2931 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2938 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2939 "new remote connection\n");
2941 s_resp->version = 0x05;
2942 s_resp->reply = 0x00;
2943 s_resp->reserved = 0x00;
2944 s_resp->addr_type = 0x01;
2946 s5r->state = SOCKS5_DATA_TRANSFER;
2949 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2953 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2961 if (s5r->state == SOCKS5_DATA_TRANSFER)
2963 if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
2965 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2966 "Closing connection to client\n");
2967 if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
2968 GNUNET_SCHEDULER_cancel (s5r->rtask);
2969 if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2970 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2971 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2972 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2973 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2974 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2976 if (s5r->remote_sock != NULL)
2977 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2978 GNUNET_NETWORK_socket_close (s5r->sock);
2983 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2984 "forwarding %d bytes from client\n", s5r->rbuf_len);
2987 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2989 &do_write_remote, s5r);
2991 if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
2994 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2996 &do_read_remote, s5r);
3003 * Accept new incoming connections
3005 * @param cls the closure
3006 * @param tc the scheduler context
3009 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3011 struct GNUNET_NETWORK_Handle *s;
3012 struct Socks5Request *s5r;
3014 ltask = GNUNET_SCHEDULER_NO_TASK;
3015 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
3018 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3022 s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
3026 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
3030 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3031 "Got an inbound connection, waiting for data\n");
3033 s5r = GNUNET_malloc (sizeof (struct Socks5Request));
3035 s5r->state = SOCKS5_INIT;
3036 s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
3037 s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
3038 s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
3039 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3046 * Task run on shutdown
3048 * @param cls closure
3049 * @param tc task context
3052 do_shutdown (void *cls,
3053 const struct GNUNET_SCHEDULER_TaskContext *tc)
3055 struct MhdHttpList *hd;
3056 struct MhdHttpList *tmp_hd;
3057 struct NetworkHandleList *nh;
3058 struct NetworkHandleList *tmp_nh;
3059 struct ProxyCurlTask *ctask;
3060 struct ProxyCurlTask *ctask_tmp;
3061 struct ProxyUploadData *pdata;
3063 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3064 "Shutting down...\n");
3065 if (NULL != local_gns_zone)
3066 GNUNET_free (local_gns_zone);
3067 if (NULL != local_private_zone)
3068 GNUNET_free (local_private_zone);
3069 if (NULL != local_shorten_zone)
3070 GNUNET_free (local_shorten_zone);
3072 if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
3074 GNUNET_SCHEDULER_cancel (curl_download_task);
3075 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
3078 for (hd = mhd_httpd_head; hd != NULL; hd = tmp_hd)
3082 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3083 "Stopping daemon\n");
3085 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
3087 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3088 "Stopping select task %d\n",
3090 GNUNET_SCHEDULER_cancel (hd->httpd_task);
3091 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
3093 if (NULL != hd->daemon)
3095 MHD_stop_daemon (hd->daemon);
3098 for (nh = hd->socket_handles_head; nh != NULL; nh = tmp_nh)
3102 GNUNET_NETWORK_socket_close (nh->h);
3107 if (NULL != hd->proxy_cert)
3109 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3110 "Free certificate\n");
3111 GNUNET_free (hd->proxy_cert);
3117 for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
3119 ctask_tmp = ctask->next;
3121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3122 "Cleaning up cURL task\n");
3124 if (ctask->curl != NULL)
3125 curl_easy_cleanup (ctask->curl);
3127 if (NULL != ctask->headers)
3128 curl_slist_free_all (ctask->headers);
3129 if (NULL != ctask->resolver)
3130 curl_slist_free_all (ctask->resolver);
3132 if (NULL != ctask->response)
3133 MHD_destroy_response (ctask->response);
3135 pdata = ctask->upload_data_head;
3137 //FIXME free pdata here
3138 for (; pdata != NULL; pdata = ctask->upload_data_head)
3140 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
3141 ctask->upload_data_tail,
3143 GNUNET_free_non_null (pdata->filename);
3144 GNUNET_free_non_null (pdata->content_type);
3145 GNUNET_free_non_null (pdata->key);
3146 GNUNET_free_non_null (pdata->value);
3147 GNUNET_free (pdata);
3149 GNUNET_free (ctask);
3151 curl_multi_cleanup (curl_multi);
3152 GNUNET_GNS_disconnect (gns_handle);
3153 gnutls_global_deinit ();
3158 * Compiles a regex for us
3160 * @param re ptr to re struct
3161 * @param rt the expression to compile
3162 * @return 0 on success
3165 compile_regex (regex_t *re, const char* rt)
3170 status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
3173 regerror (status, re, err, 1024);
3174 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3175 "Regex error compiling '%s': %s\n", rt, err);
3183 * Loads the users local zone key
3185 * @return GNUNET_YES on success
3188 load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg)
3191 struct GNUNET_CRYPTO_EccPrivateKey *key;
3192 struct GNUNET_CRYPTO_EccPublicKey pkey;
3193 struct GNUNET_CRYPTO_ShortHashCode *zone;
3194 struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
3196 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3197 "ZONEKEY", &keyfile))
3199 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3200 "Unable to load zone key config value!\n");
3204 if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
3206 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3207 "Unable to load zone key %s!\n", keyfile);
3208 GNUNET_free(keyfile);
3212 key = GNUNET_CRYPTO_ecc_key_create_from_file (keyfile);
3213 GNUNET_CRYPTO_ecc_key_get_public (key, &pkey);
3214 local_gns_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3215 GNUNET_CRYPTO_short_hash (&pkey,
3216 sizeof (struct GNUNET_CRYPTO_EccPublicKey),
3218 zone = local_gns_zone;
3219 GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3220 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3221 "Using zone: %s!\n", &zonename);
3223 GNUNET_free(keyfile);
3226 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3227 "PRIVATE_ZONEKEY", &keyfile))
3229 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3230 "Unable to load private zone key config value!\n");
3233 if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile)))
3235 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3236 "Unable to load private zone key %s!\n", keyfile);
3237 GNUNET_free(keyfile);
3241 key = GNUNET_CRYPTO_ecc_key_create_from_file (keyfile);
3242 GNUNET_CRYPTO_ecc_key_get_public (key, &pkey);
3243 local_private_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3244 GNUNET_CRYPTO_short_hash (&pkey,
3245 sizeof (struct GNUNET_CRYPTO_EccPublicKey),
3246 local_private_zone);
3247 GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3248 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3249 "Using private zone: %s!\n", &zonename);
3251 GNUNET_free(keyfile);
3255 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3256 "SHORTEN_ZONEKEY", &keyfile))
3258 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3259 "Unable to load shorten zone key config value!\n");
3262 if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile)))
3264 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3265 "Unable to load shorten zone key %s!\n", keyfile);
3266 GNUNET_free(keyfile);
3270 key = GNUNET_CRYPTO_ecc_key_create_from_file (keyfile);
3271 GNUNET_CRYPTO_ecc_key_get_public (key, &pkey);
3272 local_shorten_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3273 GNUNET_CRYPTO_short_hash (&pkey,
3274 sizeof(struct GNUNET_CRYPTO_EccPublicKey),
3275 local_shorten_zone);
3276 GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3277 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3278 "Using shorten zone: %s!\n", &zonename);
3280 GNUNET_free(keyfile);
3288 * Main function that will be run
3290 * @param cls closure
3291 * @param args remaining command-line arguments
3292 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3293 * @param cfg configuration
3296 run (void *cls, char *const *args, const char *cfgfile,
3297 const struct GNUNET_CONFIGURATION_Handle *cfg)
3299 struct sockaddr_in sa;
3300 struct MhdHttpList *hd;
3301 char* cafile_cfg = NULL;
3303 #if !HAVE_MHD_NO_LISTEN_SOCKET
3305 char* proxy_sockfile;
3306 struct sockaddr_un mhd_unix_sock_addr;
3309 if (NULL == (curl_multi = curl_multi_init ()))
3311 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3312 "Failed to create cURL multo handle!\n");
3316 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3318 cafile = cafile_opt;
3321 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3325 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3326 "Unable to load proxy CA config value!\n");
3327 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3328 "No proxy CA provided!\n");
3331 cafile = cafile_cfg;
3333 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3334 "Using %s as CA\n", cafile);
3336 gnutls_global_init ();
3337 gnutls_x509_crt_init (&proxy_ca.cert);
3338 gnutls_x509_privkey_init (&proxy_ca.key);
3340 if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
3341 (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
3343 // FIXME: release resources...
3347 GNUNET_free_non_null (cafile_cfg);
3349 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3350 "Loading Template\n");
3352 compile_regex (&re_dotplus, (char*) RE_A_HREF);
3354 if (NULL == (gns_handle = GNUNET_GNS_connect (cfg)))
3356 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3357 "Unable to connect to GNS!\n");
3360 if (GNUNET_NO == load_local_zone_key (cfg))
3362 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3363 "Unable to load zone!\n");
3367 memset (&sa, 0, sizeof (sa));
3368 sa.sin_family = AF_INET;
3369 sa.sin_port = htons (port);
3370 #if HAVE_SOCKADDR_IN_SIN_LEN
3371 sa.sin_len = sizeof (sa);
3374 lsock = GNUNET_NETWORK_socket_create (AF_INET,
3378 if ((NULL == lsock) ||
3380 GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
3383 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3384 "Failed to create listen socket bound to `%s'",
3385 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
3387 GNUNET_NETWORK_socket_close (lsock);
3391 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
3393 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3394 "Failed to listen on socket bound to `%s'",
3395 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
3398 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3399 lsock, &do_accept, NULL);
3401 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
3403 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3404 "cURL global init failed!\n");
3408 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3409 "Proxy listens on port %u\n",
3411 #if ! HAVE_MHD_NO_LISTEN_SOCKET
3412 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3416 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3417 "Specify PROXY_UNIXPATH in gns-proxy config section!\n");
3420 if (NULL == (mhd_unix_socket = GNUNET_NETWORK_socket_create (AF_UNIX,
3424 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3425 "Unable to create unix domain socket!\n");
3429 mhd_unix_sock_addr.sun_family = AF_UNIX;
3430 strcpy (mhd_unix_sock_addr.sun_path, proxy_sockfile);
3433 mhd_unix_sock_addr.sun_path[0] = '\0';
3435 #if HAVE_SOCKADDR_IN_SIN_LEN
3436 mhd_unix_sock_addr.sun_len = (u_char) sizeof (struct sockaddr_un);
3439 len = strlen (proxy_sockfile) + sizeof(AF_UNIX);
3440 GNUNET_free (proxy_sockfile);
3442 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (mhd_unix_socket,
3443 (struct sockaddr*)&mhd_unix_sock_addr,
3446 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3447 "Unable to bind unix domain socket!\n");
3451 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (mhd_unix_socket,
3454 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3455 "Unable to listen on unix domain socket!\n");
3460 hd = GNUNET_malloc (sizeof (struct MhdHttpList));
3461 hd->is_ssl = GNUNET_NO;
3462 strcpy (hd->domain, "");
3464 #if HAVE_MHD_NO_LISTEN_SOCKET
3465 httpd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
3468 &create_response, hd,
3469 MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3470 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3471 MHD_OPTION_NOTIFY_COMPLETED,
3473 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3476 httpd = MHD_start_daemon (MHD_USE_DEBUG,
3477 4444 /* Dummy port */,
3479 &create_response, hd,
3480 MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
3481 MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3482 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3483 MHD_OPTION_NOTIFY_COMPLETED,
3485 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3488 GNUNET_break (httpd != NULL);
3490 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
3491 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
3493 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
3494 &do_shutdown, NULL);
3499 * The main function for gnunet-gns-proxy.
3501 * @param argc number of arguments from the command line
3502 * @param argv command line arguments
3503 * @return 0 ok, 1 on error
3506 main (int argc, char *const *argv)
3508 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
3510 gettext_noop ("listen on specified port (default: 7777)"), 1,
3511 &GNUNET_GETOPT_set_ulong, &port},
3512 {'a', "authority", NULL,
3513 gettext_noop ("pem file to use as CA"), 1,
3514 &GNUNET_GETOPT_set_string, &cafile_opt},
3515 GNUNET_GETOPT_OPTION_END
3519 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
3521 GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
3524 GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
3525 _("GNUnet GNS proxy"),
3527 &run, NULL)) ? 0 : 1;
3528 GNUNET_free_non_null ((char *) argv);
3532 /* end of gnunet-gns-proxy.c */