2 This file is part of GNUnet.
3 (C) 2012 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 #include <microhttpd.h>
22 #include <curl/curl.h>
23 #include <gnutls/gnutls.h>
24 #include <gnutls/x509.h>
25 #include <gnutls/abstract.h>
26 #include <gnutls/crypto.h>
28 #include "gnunet_util_lib.h"
29 #include "gnunet_gns_service.h"
30 #include "gns_proxy_proto.h"
33 #define HAVE_MHD_NO_LISTEN_SOCKET MHD_VERSION >= 0x00091401
35 #define GNUNET_GNS_PROXY_PORT 7777
36 #define MHD_MAX_CONNECTIONS 300
37 #define MAX_HTTP_URI_LENGTH 2048
38 #define POSTBUFFERSIZE 4096
40 /* MHD/cURL defines */
48 #define HTML_HDR_CONTENT "Content-Type: text/html"
50 /* buffer padding for proper RE matching */
51 #define CURL_BUF_PADDING 1000
54 //#define RE_DOTPLUS "<a href=\"http://(([A-Za-z]+[.])+)([+])"
55 #define RE_A_HREF "href=\"https?://(([A-Za-z0-9]+[.])+)([+]|[a-z]+)"
56 #define RE_N_MATCHES 4
58 /* The usual suspects */
60 #define HTTPS_PORT 443
64 * A structure for CA cert/key
69 gnutls_x509_crt_t cert;
72 gnutls_x509_privkey_t key;
75 #define MAX_PEM_SIZE (10 * 1024)
78 * Structure for GNS certificates
80 struct ProxyGNSCertificate
82 /* The certificate as PEM */
83 char cert[MAX_PEM_SIZE];
85 /* The private key as PEM */
86 char key[MAX_PEM_SIZE];
91 * A structure for socks requests
95 /* The client socket */
96 struct GNUNET_NETWORK_Handle *sock;
98 /* The server socket */
99 struct GNUNET_NETWORK_Handle *remote_sock;
101 /* The socks state */
104 /* Client socket read task */
105 GNUNET_SCHEDULER_TaskIdentifier rtask;
107 /* Server socket read task */
108 GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
110 /* Client socket write task */
111 GNUNET_SCHEDULER_TaskIdentifier wtask;
113 /* Server socket write task */
114 GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
122 /* Length of data in read buffer */
123 unsigned int rbuf_len;
125 /* Length of data in write buffer */
126 unsigned int wbuf_len;
128 /* This handle is scheduled for cleanup? */
131 /* Shall we close the client socket on cleanup? */
136 * DLL for Network Handles
138 struct NetworkHandleList
141 struct NetworkHandleList *next;
144 struct NetworkHandleList *prev;
147 struct GNUNET_NETWORK_Handle *h;
151 * A structure for all running Httpds
156 struct MhdHttpList *prev;
159 struct MhdHttpList *next;
161 /* is this an ssl daemon? */
164 /* the domain name to server (only important for SSL) */
167 /* The daemon handle */
168 struct MHD_Daemon *daemon;
170 /* Optional proxy certificate used */
171 struct ProxyGNSCertificate *proxy_cert;
174 GNUNET_SCHEDULER_TaskIdentifier httpd_task;
176 /* Handles associated with this daemon */
177 struct NetworkHandleList *socket_handles_head;
179 /* Handles associated with this daemon */
180 struct NetworkHandleList *socket_handles_tail;
184 * A structure for MHD<->cURL streams
189 struct ProxyCurlTask *prev;
192 struct ProxyCurlTask *next;
197 /* Optional header replacements for curl (LEHO) */
198 struct curl_slist *headers;
200 /* Optional resolver replacements for curl (LEHO) */
201 struct curl_slist *resolver;
203 /* curl response code */
204 long curl_response_code;
206 /* The URL to fetch */
207 char url[MAX_HTTP_URI_LENGTH];
209 /* The cURL write buffer / MHD read buffer */
210 char buffer[CURL_MAX_WRITE_SIZE + CURL_BUF_PADDING];
212 /* Read pos of the data in the buffer */
213 char *buffer_read_ptr;
215 /* Write pos in the buffer */
216 char *buffer_write_ptr;
219 struct MHD_Connection *connection;
222 size_t put_read_offset;
223 size_t put_read_size;
226 struct MHD_PostProcessor *post_handler;
229 struct ProxyUploadData *upload_data_head;
230 struct ProxyUploadData *upload_data_tail;
232 /* the type of POST encoding */
235 struct curl_httppost *httppost;
237 struct curl_httppost *httppost_last;
239 /* Number of bytes in buffer */
240 unsigned int bytes_in_buffer;
243 GNUNET_SCHEDULER_TaskIdentifier pp_task;
246 struct ProxyREMatch *pp_match_head;
249 struct ProxyREMatch *pp_match_tail;
251 /* The associated daemon list entry */
252 struct MhdHttpList *mhd;
254 /* The associated response */
255 struct MHD_Response *response;
258 struct ProxySetCookieHeader *set_cookies_head;
261 struct ProxySetCookieHeader *set_cookies_tail;
263 /* The authority of the corresponding host (site of origin) */
266 /* The hostname (Host header field) */
269 /* The LEgacy HOstname (can be empty) */
275 /* The buffer status (BUF_WAIT_FOR_CURL or BUF_WAIT_FOR_MHD) */
276 enum BufferStatus buf_status;
278 /* connection status */
281 /* is curl running? */
287 /* Already accepted */
290 /* Indicates wheather the download is in progress */
291 int download_in_progress;
293 /* Indicates wheather the download was successful */
294 int download_is_finished;
296 /* Indicates wheather the download failed */
299 /* Indicates wheather we need to parse HTML */
302 /* Indicates wheather we are postprocessing the HTML right now */
303 int is_postprocessing;
305 /* Indicates wheather postprocessing has finished */
315 * Struct for RE matches in postprocessing of HTML
320 struct ProxyREMatch *next;
323 struct ProxyREMatch *prev;
325 /* start of match in buffer */
328 /* end of match in buffer */
331 /* associated proxycurltask */
332 struct ProxyCurlTask *ctask;
341 struct GNUNET_GNS_ShortenRequest *shorten_task;
352 * Struct for set-cookies
354 struct ProxySetCookieHeader
357 struct ProxySetCookieHeader *next;
360 struct ProxySetCookieHeader *prev;
367 * Post data structure
369 struct ProxyUploadData
372 struct ProxyUploadData *next;
375 struct ProxyUploadData *prev;
383 size_t content_length;
396 /* The port the proxy is running on (default 7777) */
397 static unsigned long port = GNUNET_GNS_PROXY_PORT;
399 /* The CA file (pem) to use for the proxy CA */
400 static char* cafile_opt;
402 /* The listen socket of the proxy */
403 static struct GNUNET_NETWORK_Handle *lsock;
405 /* The listen task ID */
406 static GNUNET_SCHEDULER_TaskIdentifier ltask;
408 /* The cURL download task */
409 static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
411 /* The non SSL httpd daemon handle */
412 static struct MHD_Daemon *httpd;
414 /* Number of current mhd connections */
415 static unsigned int total_mhd_connections;
417 /* The cURL multi handle */
418 static CURLM *curl_multi;
420 /* Handle to the GNS service */
421 static struct GNUNET_GNS_Handle *gns_handle;
423 /* DLL for ProxyCurlTasks */
424 static struct ProxyCurlTask *ctasks_head;
426 /* DLL for ProxyCurlTasks */
427 static struct ProxyCurlTask *ctasks_tail;
429 /* DLL for http daemons */
430 static struct MhdHttpList *mhd_httpd_head;
432 /* DLL for http daemons */
433 static struct MhdHttpList *mhd_httpd_tail;
435 /* Handle to the regex for dotplus (.+) replacement in HTML */
436 static regex_t re_dotplus;
438 /* The users local GNS zone hash */
439 static struct GNUNET_CRYPTO_ShortHashCode *local_gns_zone;
441 /* The users local private zone */
442 static struct GNUNET_CRYPTO_ShortHashCode *local_private_zone;
444 /* The users local shorten zone */
445 static struct GNUNET_CRYPTO_ShortHashCode *local_shorten_zone;
447 /* The CA for SSL certificate generation */
448 static struct ProxyCA proxy_ca;
450 /* UNIX domain socket for mhd */
451 #if !HAVE_MHD_NO_LISTEN_SOCKET
452 static struct GNUNET_NETWORK_Handle *mhd_unix_socket;
455 /* Shorten zone private key */
456 static struct GNUNET_CRYPTO_RsaPrivateKey *shorten_zonekey;
460 * Checks if name is in tld
462 * @param name the name to check
463 * @param tld the TLD to check for (must NOT begin with ".")
464 * @return GNUNET_YES or GNUNET_NO
467 is_tld (const char* name, const char* tld)
469 size_t name_len = strlen (name);
470 size_t tld_len = strlen (tld);
472 GNUNET_break ('.' != tld[0]);
473 return ( (tld_len < name_len) &&
474 ( ('.' == name[name_len - tld_len - 1]) || (name_len == tld_len) ) &&
476 name + (name_len - tld_len),
482 * convert integer to string representation
485 * @return the character
488 i_to_hexchar (unsigned char i)
490 static char hexmap[] = "0123456789abcdef";
492 GNUNET_assert (sizeof (hexmap) > (i & 15));
493 return hexmap[i & 15];
498 // FIXME: use cURL API
499 * Escape given 0-terminated string
501 * @param to_esc string to escapse
502 * @return allocated new escaped string (MUST free!)
505 escape_to_urlenc (const char *to_esc)
507 char *pos = (char*)to_esc;
508 char *res = GNUNET_malloc (strlen (to_esc) * 3 + 1);
513 if (isalnum (*pos) ||
514 ('-' == *pos) || ('_' == *pos) ||
515 ('.' == *pos) || ('~' == *pos))
517 else if (' ' == *pos)
522 *rpos++ = i_to_hexchar (*pos >> 4);
523 *rpos++ = i_to_hexchar (*pos >> 15);
533 con_post_data_iter (void *cls,
534 enum MHD_ValueKind kind,
536 const char *filename,
537 const char *content_type,
538 const char *transfer_encoding,
543 struct ProxyCurlTask* ctask = cls;
544 struct ProxyUploadData* pdata;
548 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
549 "Got POST data (file: %s, content type: %s): '%s=%.*s' at offset %llu size %llu\n",
550 filename, content_type,
551 key, (int) size, data,
552 (unsigned long long) off,
553 (unsigned long long) size);
554 GNUNET_assert (NULL != ctask->post_type);
556 if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
559 ctask->is_httppost = GNUNET_YES;
563 pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
564 pdata->key = GNUNET_strdup (key);
566 if (NULL != filename)
567 pdata->filename = GNUNET_strdup (filename);
568 if (NULL != content_type)
569 pdata->content_type = GNUNET_strdup (content_type);
570 pdata->value = GNUNET_malloc (size);
571 pdata->total_bytes = size;
572 memcpy (pdata->value, data, size);
573 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
574 ctask->upload_data_tail,
577 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
578 "Copied %llu bytes of POST Data\n",
579 (unsigned long long) size);
583 pdata = ctask->upload_data_tail;
584 new_value = GNUNET_malloc (size + pdata->total_bytes);
585 memcpy (new_value, pdata->value, pdata->total_bytes);
586 memcpy (new_value+off, data, size);
587 GNUNET_free (pdata->value);
588 pdata->value = new_value;
589 pdata->total_bytes += size;
594 if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
600 ctask->is_httppost = GNUNET_NO;
602 if (NULL != ctask->curl)
603 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
608 pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
609 enc = escape_to_urlenc (key);
610 pdata->value = GNUNET_malloc (strlen (enc) + 3);
611 if (NULL != ctask->upload_data_head)
613 pdata->value[0] = '&';
614 memcpy (pdata->value+1, enc, strlen (enc));
617 memcpy (pdata->value, enc, strlen (enc));
618 pdata->value[strlen (pdata->value)] = '=';
619 pdata->bytes_left = strlen (pdata->value);
620 pdata->total_bytes = pdata->bytes_left;
623 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
624 "Escaped POST key: '%s'\n",
627 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
628 ctask->upload_data_tail,
633 pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
634 enc = escape_to_urlenc (data);
635 pdata->value = GNUNET_malloc (strlen (enc) + 1);
636 memcpy (pdata->value, enc, strlen (enc));
637 pdata->bytes_left = strlen (pdata->value);
638 pdata->total_bytes = pdata->bytes_left;
641 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
642 "Escaped POST value: '%s'\n",
645 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
646 ctask->upload_data_tail,
653 * Read HTTP request header field 'Host'
655 * @param cls buffer to write to
656 * @param kind value kind
657 * @param key field key
658 * @param value field value
659 * @return MHD_NO when Host found
662 con_val_iter (void *cls,
663 enum MHD_ValueKind kind,
667 struct ProxyCurlTask *ctask = cls;
668 char* buf = ctask->host;
674 if (0 == strcmp ("Host", key))
676 port = strchr (value, ':');
679 strncpy (buf, value, port-value);
681 if ((1 != sscanf (port, "%u", &uport)) ||
682 (uport > UINT16_MAX) ||
684 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
685 "Unable to parse port!\n");
687 ctask->port = (uint16_t) uport;
694 if (0 == strcmp (MHD_HTTP_HEADER_ACCEPT_ENCODING, key))
699 if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_TYPE,
702 if (0 == strncasecmp (value,
703 MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
704 strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
705 ctask->post_type = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
706 else if (0 == strncasecmp (value,
707 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
708 strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
709 ctask->post_type = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
711 ctask->post_type = NULL;
715 cstr = GNUNET_malloc (strlen (key) + strlen (hdr_val) + 3);
716 GNUNET_snprintf (cstr, strlen (key) + strlen (hdr_val) + 3,
717 "%s: %s", key, hdr_val);
719 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
720 "Client Header: %s\n", cstr);
722 ctask->headers = curl_slist_append (ctask->headers, cstr);
730 * Callback for MHD response
733 * @param pos in buffer
735 * @param max space in buffer
736 * @return number of bytes written
739 mhd_content_cb (void *cls,
745 * Check HTTP response header for mime
747 * @param buffer curl buffer
748 * @param size curl blocksize
749 * @param nmemb curl blocknumber
751 * @return size of read bytes
754 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
756 size_t bytes = size * nmemb;
757 struct ProxyCurlTask *ctask = cls;
758 int html_mime_len = strlen (HTML_HDR_CONTENT);
759 int cookie_hdr_len = strlen (MHD_HTTP_HEADER_SET_COOKIE);
760 char hdr_mime[html_mime_len+1];
761 char hdr_generic[bytes+1];
762 char new_cookie_hdr[bytes+strlen (ctask->leho)+1];
763 char new_location[MAX_HTTP_URI_LENGTH+500];
773 char cors_hdr[strlen (ctask->leho) + strlen ("https://")];
775 if (NULL == ctask->response)
777 /* FIXME: get total size from curl (if available) */
778 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
779 "Creating response for %s\n", ctask->url);
780 ctask->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
781 sizeof (ctask->buffer),
786 /* if we have a leho add a CORS header */
787 if (0 != strcmp ("", ctask->leho))
789 /* We could also allow ssl and http here */
790 if (ctask->mhd->is_ssl)
791 sprintf (cors_hdr, "https://%s", ctask->leho);
793 sprintf (cors_hdr, "http://%s", ctask->leho);
795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
796 "MHD: Adding CORS header field %s\n",
799 if (GNUNET_NO == MHD_add_response_header (ctask->response,
800 "Access-Control-Allow-Origin",
803 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
804 "MHD: Error adding CORS header field %s\n",
808 ctask->ready_to_queue = GNUNET_YES;
811 if (html_mime_len <= bytes)
813 memcpy (hdr_mime, buffer, html_mime_len);
814 hdr_mime[html_mime_len] = '\0';
816 if (0 == strcmp (hdr_mime, HTML_HDR_CONTENT))
818 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
819 "Got HTML HTTP response header\n");
820 ctask->parse_content = GNUNET_YES;
824 if (cookie_hdr_len > bytes)
827 memcpy (hdr_generic, buffer, bytes);
828 hdr_generic[bytes] = '\0';
830 if ('\n' == hdr_generic[bytes-1])
831 hdr_generic[bytes-1] = '\0';
833 if (hdr_generic[bytes-2] == '\r')
834 hdr_generic[bytes-2] = '\0';
836 if (0 == memcmp (hdr_generic,
837 MHD_HTTP_HEADER_SET_COOKIE,
840 ndup = GNUNET_strdup (hdr_generic+cookie_hdr_len+1);
841 memset (new_cookie_hdr, 0, sizeof (new_cookie_hdr));
842 tok = strtok (ndup, ";");
844 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
845 "Looking for cookie in : %s\n", hdr_generic);
847 for (; tok != NULL; tok = strtok (NULL, ";"))
849 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
850 "Got Cookie token: %s\n", tok);
851 //memcpy (new_cookie_hdr+offset, tok, strlen (tok));
852 if (0 == memcmp (tok, " domain", strlen (" domain")))
854 cookie_domain = tok + strlen (" domain") + 1;
856 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
857 "Got Set-Cookie Domain: %s\n", cookie_domain);
859 if (strlen (cookie_domain) < strlen (ctask->leho))
861 delta_cdomain = strlen (ctask->leho) - strlen (cookie_domain);
862 if (0 == strcmp (cookie_domain, ctask->leho + (delta_cdomain)))
864 GNUNET_snprintf (new_cookie_hdr+offset,
865 sizeof (new_cookie_hdr),
866 " domain=%s", ctask->authority);
867 offset += strlen (" domain=") + strlen (ctask->authority);
868 new_cookie_hdr[offset] = ';';
873 else if (strlen (cookie_domain) == strlen (ctask->leho))
875 if (0 == strcmp (cookie_domain, ctask->leho))
877 GNUNET_snprintf (new_cookie_hdr+offset,
878 sizeof (new_cookie_hdr),
879 " domain=%s", ctask->host);
880 offset += strlen (" domain=") + strlen (ctask->host);
881 new_cookie_hdr[offset] = ';';
886 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
887 "Cookie domain invalid\n");
891 memcpy (new_cookie_hdr+offset, tok, strlen (tok));
892 offset += strlen (tok);
893 new_cookie_hdr[offset] = ';';
899 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
900 "Got Set-Cookie HTTP header %s\n", new_cookie_hdr);
902 if (GNUNET_NO == MHD_add_response_header (ctask->response,
903 MHD_HTTP_HEADER_SET_COOKIE,
906 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
907 "MHD: Error adding set-cookie header field %s\n",
908 hdr_generic+cookie_hdr_len+1);
913 ndup = GNUNET_strdup (hdr_generic);
914 hdr_type = strtok (ndup, ":");
916 if (NULL == hdr_type)
922 hdr_val = strtok (NULL, "");
932 if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
934 if (ctask->mhd->is_ssl)
936 sprintf (leho_host, "https://%s", ctask->leho);
937 sprintf (real_host, "https://%s", ctask->host);
941 sprintf (leho_host, "http://%s", ctask->leho);
942 sprintf (real_host, "http://%s", ctask->host);
945 if (0 == memcmp (leho_host, hdr_val, strlen (leho_host)))
947 sprintf (new_location, "%s%s", real_host, hdr_val+strlen (leho_host));
948 hdr_val = new_location;
952 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
953 "Trying to set %s: %s\n",
956 if (GNUNET_NO == MHD_add_response_header (ctask->response,
960 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
961 "MHD: Error adding %s header field %s\n",
972 * @param hd a http daemon list entry
975 run_httpd (struct MhdHttpList *hd);
986 * Task run whenever HTTP server operations are pending.
989 * @param tc sched context
993 const struct GNUNET_SCHEDULER_TaskContext *tc);
996 run_mhd_now (struct MhdHttpList *hd)
998 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
1000 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1001 "MHD: killing old task\n");
1002 GNUNET_SCHEDULER_cancel (hd->httpd_task);
1004 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1005 "MHD: Scheduling MHD now\n");
1006 hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, hd);
1010 * Ask cURL for the select sets and schedule download
1013 curl_download_prepare (void);
1016 * Callback to free content
1018 * @param cls content to free
1019 * @param tc task context
1022 mhd_content_free (void *cls,
1023 const struct GNUNET_SCHEDULER_TaskContext *tc)
1025 struct ProxyCurlTask *ctask = cls;
1026 struct ProxyUploadData *pdata;
1028 GNUNET_assert (NULL == ctask->pp_match_head);
1029 if (NULL != ctask->headers)
1030 curl_slist_free_all (ctask->headers);
1032 if (NULL != ctask->headers)
1033 curl_slist_free_all (ctask->resolver);
1035 if (NULL != ctask->response)
1036 MHD_destroy_response (ctask->response);
1038 if (NULL != ctask->post_handler)
1039 MHD_destroy_post_processor (ctask->post_handler);
1041 if (GNUNET_SCHEDULER_NO_TASK != ctask->pp_task)
1042 GNUNET_SCHEDULER_cancel (ctask->pp_task);
1044 for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
1046 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1047 ctask->upload_data_tail,
1049 GNUNET_free_non_null (pdata->filename);
1050 GNUNET_free_non_null (pdata->content_type);
1051 GNUNET_free_non_null (pdata->key);
1052 GNUNET_free_non_null (pdata->value);
1053 GNUNET_free (pdata);
1055 GNUNET_free (ctask);
1060 * Callback for MHD response
1062 * @param cls closure
1063 * @param pos in buffer
1065 * @param max space in buffer
1066 * @return number of bytes written
1069 mhd_content_cb (void *cls,
1074 struct ProxyCurlTask *ctask = cls;
1075 struct ProxyREMatch *re_match;
1077 long long int bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1079 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1080 "MHD: content cb for %s. To copy: %lld\n",
1081 ctask->url, bytes_to_copy);
1082 GNUNET_assert (bytes_to_copy >= 0);
1084 if ((GNUNET_YES == ctask->download_is_finished) &&
1085 (GNUNET_NO == ctask->download_error) &&
1086 (0 == bytes_to_copy))
1088 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1089 "MHD: sending response for %s\n", ctask->url);
1090 ctask->download_in_progress = GNUNET_NO;
1091 run_mhd_now (ctask->mhd);
1092 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1093 total_mhd_connections--;
1094 return MHD_CONTENT_READER_END_OF_STREAM;
1097 if ((GNUNET_YES == ctask->download_error) &&
1098 (GNUNET_YES == ctask->download_is_finished) &&
1099 (0 == bytes_to_copy))
1101 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1102 "MHD: sending error response\n");
1103 ctask->download_in_progress = GNUNET_NO;
1104 run_mhd_now (ctask->mhd);
1105 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1106 total_mhd_connections--;
1107 return MHD_CONTENT_READER_END_WITH_ERROR;
1110 if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
1114 for (re_match = ctask->pp_match_head; NULL != re_match; re_match = ctask->pp_match_head)
1116 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1117 "MHD: Processing PP %s\n",
1118 re_match->hostname);
1119 bytes_to_copy = re_match->start - ctask->buffer_read_ptr;
1120 GNUNET_assert (bytes_to_copy >= 0);
1122 if (bytes_to_copy+copied > max)
1124 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1125 "MHD: buffer in response too small for %d. Using available space (%d). (%s)\n",
1129 memcpy (buf+copied, ctask->buffer_read_ptr, max-copied);
1130 ctask->buffer_read_ptr += max-copied;
1132 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1133 "MHD: copied %d bytes\n", (int) copied);
1137 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1138 "MHD: copying %d bytes to mhd response at offset %d\n",
1139 bytes_to_copy, ctask->buffer_read_ptr);
1140 memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1141 copied += bytes_to_copy;
1143 if (GNUNET_NO == re_match->done)
1145 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1146 "MHD: Waiting for PP of %s\n", re_match->hostname);
1147 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1148 "MHD: copied %d bytes\n", (int) copied);
1149 ctask->buffer_read_ptr += bytes_to_copy;
1153 if (strlen (re_match->result) > (max - copied))
1155 //FIXME partially copy domain here
1156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1157 "MHD: buffer in response too small for %s! (%s)\n",
1160 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1161 "MHD: copied %d bytes\n", (int) copied);
1162 ctask->buffer_read_ptr += bytes_to_copy;
1166 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1167 "MHD: Adding PP result %s to buffer\n",
1169 memcpy (buf + copied, re_match->result, strlen (re_match->result));
1170 copied += strlen (re_match->result);
1171 ctask->buffer_read_ptr = re_match->end;
1172 GNUNET_CONTAINER_DLL_remove (ctask->pp_match_head,
1173 ctask->pp_match_tail,
1175 GNUNET_free (re_match);
1178 bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1180 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1181 "MHD: copied: %d left: %d, space left in buf: %d\n",
1183 bytes_to_copy, (int) (max - copied));
1185 GNUNET_assert (0 <= bytes_to_copy);
1187 if (GNUNET_NO == ctask->download_is_finished)
1189 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1190 "MHD: Purging buffer\n");
1191 memmove (ctask->buffer, ctask->buffer_read_ptr, bytes_to_copy);
1192 ctask->buffer_read_ptr = ctask->buffer;
1193 ctask->buffer_write_ptr = ctask->buffer + bytes_to_copy;
1194 ctask->buffer[bytes_to_copy] = '\0';
1197 if (bytes_to_copy + copied > max)
1198 bytes_to_copy = max-copied;
1200 if (0 > bytes_to_copy)
1203 memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1204 ctask->buffer_read_ptr += bytes_to_copy;
1205 copied += bytes_to_copy;
1206 ctask->buf_status = BUF_WAIT_FOR_CURL;
1208 if (NULL != ctask->curl)
1209 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
1211 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1212 "MHD: copied %d bytes\n", (int) copied);
1213 run_mhd_now (ctask->mhd);
1219 * Shorten result callback
1221 * @param cls the proxycurltask
1222 * @param short_name the shortened name (NULL on error)
1225 process_shorten (void* cls, const char* short_name)
1227 struct ProxyREMatch *re_match = cls;
1228 char result[sizeof (re_match->result)];
1230 if (NULL == short_name)
1232 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1233 "PP: Unable to shorten %s\n",
1234 re_match->hostname);
1235 GNUNET_CONTAINER_DLL_remove (re_match->ctask->pp_match_head,
1236 re_match->ctask->pp_match_tail,
1238 GNUNET_free (re_match);
1242 if (0 == strcmp (short_name, re_match->ctask->leho))
1243 strcpy (result, re_match->ctask->host);
1245 strcpy (result, short_name);
1247 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1248 "PP: Shorten %s -> %s\n",
1252 if (re_match->is_ssl)
1253 sprintf (re_match->result, "href=\"https://%s", result);
1255 sprintf (re_match->result, "href=\"http://%s", result);
1257 re_match->done = GNUNET_YES;
1258 run_mhd_now (re_match->ctask->mhd);
1263 * Postprocess data in buffer. From read ptr to write ptr
1265 * @param cls the curlproxytask
1266 * @param tc task context
1269 postprocess_buffer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1271 struct ProxyCurlTask *ctask = cls;
1272 struct ProxyREMatch *re_match;
1273 char* re_ptr = ctask->buffer_read_ptr;
1274 char re_hostname[255];
1275 regmatch_t m[RE_N_MATCHES];
1277 ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
1279 if (GNUNET_YES != ctask->parse_content)
1281 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1282 "PP: Not parsing content\n");
1283 ctask->buf_status = BUF_WAIT_FOR_MHD;
1284 run_mhd_now (ctask->mhd);
1288 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1289 "PP: We need to parse the HTML\n");
1291 /* 0 means match found */
1292 while (0 == regexec (&re_dotplus, re_ptr, RE_N_MATCHES, m, 0))
1294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1295 "PP: regex match\n");
1297 GNUNET_assert (m[1].rm_so != -1);
1299 memset (re_hostname, 0, sizeof (re_hostname));
1300 memcpy (re_hostname, re_ptr+m[1].rm_so, (m[3].rm_eo-m[1].rm_so));
1303 re_match = GNUNET_malloc (sizeof (struct ProxyREMatch));
1304 re_match->start = re_ptr + m[0].rm_so;
1305 re_match->end = re_ptr + m[3].rm_eo;
1306 re_match->done = GNUNET_NO;
1307 re_match->ctask = ctask;
1309 if ('s' == *(re_ptr+m[1].rm_so-strlen("://")-1)) //FIXME strcmp
1310 re_match->is_ssl = GNUNET_YES;
1312 re_match->is_ssl = GNUNET_NO;
1314 strcpy (re_match->hostname, re_hostname);
1315 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1316 "PP: Got hostname %s\n", re_hostname);
1317 re_ptr += m[3].rm_eo;
1319 if (GNUNET_YES == is_tld (re_match->hostname, GNUNET_GNS_TLD_PLUS))
1321 re_match->hostname[strlen(re_match->hostname)-1] = '\0';
1322 strcpy (re_match->hostname+strlen(re_match->hostname),
1326 re_match->shorten_task = GNUNET_GNS_shorten_zone (gns_handle,
1332 re_match); //FIXME cancel appropriately
1334 GNUNET_CONTAINER_DLL_insert_tail (ctask->pp_match_head,
1335 ctask->pp_match_tail,
1339 ctask->buf_status = BUF_WAIT_FOR_MHD;
1340 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1341 "PP: No more matches\n");
1342 run_mhd_now (ctask->mhd);
1346 * Handle data from cURL
1348 * @param ptr pointer to the data
1349 * @param size number of blocks of data
1350 * @param nmemb blocksize
1351 * @param ctx the curlproxytask
1352 * @return number of bytes handled
1355 curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx)
1357 const char *cbuf = ptr;
1358 size_t total = size * nmemb;
1359 struct ProxyCurlTask *ctask = ctx;
1360 size_t buf_space = sizeof (ctask->buffer) -
1361 (ctask->buffer_write_ptr-ctask->buffer);
1363 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1364 "CURL: Got %d. %d free in buffer\n",
1367 if (BUF_WAIT_FOR_CURL != ctask->buf_status)
1368 return CURL_WRITEFUNC_PAUSE;
1370 if (total > (buf_space - CURL_BUF_PADDING))
1372 if (ctask->buf_status == BUF_WAIT_FOR_CURL)
1374 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1375 "CURL: Buffer full starting postprocessing\n");
1376 ctask->buf_status = BUF_WAIT_FOR_PP;
1377 ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer,
1379 return CURL_WRITEFUNC_PAUSE;
1382 /* we should not get called in that case */
1383 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1384 "CURL: called out of context and no space in buffer!\n");
1385 return CURL_WRITEFUNC_PAUSE;
1388 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1389 "CURL: Copying %d bytes to buffer (%s)\n", total, ctask->url);
1390 memcpy (ctask->buffer_write_ptr, cbuf, total);
1391 ctask->bytes_in_buffer += total;
1392 ctask->buffer_write_ptr += total;
1393 ctask->buffer_write_ptr[0] = '\0';
1400 * cURL callback for put data
1403 put_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1405 struct ProxyCurlTask *ctask = cls;
1406 struct ProxyUploadData *pdata = ctask->upload_data_head;
1407 size_t len = size * nmemb;
1411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1412 "CURL: put read callback\n");
1415 return CURL_READFUNC_PAUSE;
1418 if (NULL == pdata->value)
1420 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1421 "CURL: Terminating PUT\n");
1423 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1424 ctask->upload_data_tail,
1426 GNUNET_free (pdata);
1430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1431 "CURL: read callback value %s\n", pdata->value);
1433 to_copy = pdata->bytes_left;
1437 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1438 memcpy (buf, pos, to_copy);
1439 pdata->bytes_left -= to_copy;
1440 if (pdata->bytes_left <= 0)
1442 GNUNET_free (pdata->value);
1443 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1444 ctask->upload_data_tail,
1446 GNUNET_free (pdata);
1453 * cURL callback for post data
1456 post_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1458 struct ProxyCurlTask *ctask = cls;
1459 struct ProxyUploadData *pdata = ctask->upload_data_head;
1460 size_t len = size * nmemb;
1464 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1465 "CURL: read callback\n");
1468 return CURL_READFUNC_PAUSE;
1471 if (NULL == pdata->value)
1473 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1474 "CURL: Terminating POST data\n");
1476 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1477 ctask->upload_data_tail,
1479 GNUNET_free (pdata);
1483 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1484 "CURL: read callback value %s\n", pdata->value);
1486 to_copy = pdata->bytes_left;
1490 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1491 memcpy (buf, pos, to_copy);
1492 pdata->bytes_left -= to_copy;
1493 if (pdata->bytes_left <= 0)
1495 GNUNET_free (pdata->value);
1496 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1497 ctask->upload_data_tail,
1499 GNUNET_free (pdata);
1505 * Task that is run when we are ready to receive more data
1508 * @param cls closure
1509 * @param tc task context
1512 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1515 * Ask cURL for the select sets and schedule download
1518 curl_download_prepare ()
1525 struct GNUNET_NETWORK_FDSet *grs;
1526 struct GNUNET_NETWORK_FDSet *gws;
1528 struct GNUNET_TIME_Relative rtime;
1534 mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
1536 if (mret != CURLM_OK)
1538 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1539 "%s failed at %s:%d: `%s'\n",
1540 "curl_multi_fdset", __FILE__, __LINE__,
1541 curl_multi_strerror (mret));
1542 //TODO cleanup here?
1546 mret = curl_multi_timeout (curl_multi, &to);
1547 rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1549 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1550 "cURL multi fds: max=%d timeout=%llu\n", max, to);
1552 grs = GNUNET_NETWORK_fdset_create ();
1553 gws = GNUNET_NETWORK_fdset_create ();
1554 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1555 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1556 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1557 "Scheduling task cURL\n");
1559 if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
1560 GNUNET_SCHEDULER_cancel (curl_download_task);
1564 curl_download_task =
1565 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1568 &curl_task_download, curl_multi);
1570 else if (NULL != ctasks_head)
1572 /* as specified in curl docs */
1573 curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
1574 &curl_task_download,
1577 GNUNET_NETWORK_fdset_destroy (gws);
1578 GNUNET_NETWORK_fdset_destroy (grs);
1583 * Task that is run when we are ready to receive more data
1586 * @param cls closure
1587 * @param tc task context
1590 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1594 struct CURLMsg *msg;
1596 struct ProxyCurlTask *ctask;
1600 struct ProxyCurlTask *clean_head = NULL;
1601 struct ProxyCurlTask *clean_tail = NULL;
1603 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1605 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1607 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1608 "Shutdown requested while trying to download\n");
1612 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1620 mret = curl_multi_perform (curl_multi, &running);
1622 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1623 "Running curl tasks: %d\n", running);
1625 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1627 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1628 "CTask: %s\n", ctask->url);
1632 if (num_ctasks != running)
1634 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1635 "%d ctasks, %d curl running\n", num_ctasks, running);
1641 msg = curl_multi_info_read (curl_multi, &msgnum);
1642 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1643 "Messages left: %d\n", msgnum);
1650 if ((msg->data.result != CURLE_OK) &&
1651 (msg->data.result != CURLE_GOT_NOTHING))
1653 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1654 "Download curl failed");
1656 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1658 if (NULL == ctask->curl)
1661 if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
1664 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1665 "CURL: Download failed for task %s: %s.\n",
1667 curl_easy_strerror (msg->data.result));
1668 ctask->download_is_finished = GNUNET_YES;
1669 ctask->download_error = GNUNET_YES;
1670 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1671 CURLINFO_RESPONSE_CODE,
1673 ctask->curl_response_code = resp_code;
1674 ctask->ready_to_queue = MHD_YES;
1675 ctask->buf_status = BUF_WAIT_FOR_MHD;
1676 run_mhd_now (ctask->mhd);
1678 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1680 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1683 GNUNET_assert (ctask != NULL);
1687 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1688 "CURL: download completed.\n");
1690 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1692 if (NULL == ctask->curl)
1695 if (0 != memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)))
1698 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1699 "CURL: completed task %s found.\n", ctask->url);
1700 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1701 CURLINFO_RESPONSE_CODE,
1703 ctask->curl_response_code = resp_code;
1706 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1707 "CURL: Completed ctask!\n");
1708 if (GNUNET_SCHEDULER_NO_TASK == ctask->pp_task)
1710 ctask->buf_status = BUF_WAIT_FOR_PP;
1711 ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer,
1715 ctask->ready_to_queue = MHD_YES;
1716 ctask->download_is_finished = GNUNET_YES;
1718 /* We MUST not modify the multi handle else we loose messages */
1719 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1721 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1725 GNUNET_assert (ctask != NULL);
1727 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1728 "CURL: %s\n", curl_easy_strerror(msg->data.result));
1734 } while (msgnum > 0);
1736 for (ctask=clean_head; NULL != ctask; ctask = ctask->next)
1738 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1739 "CURL: Removing task %s.\n", ctask->url);
1740 curl_multi_remove_handle (curl_multi, ctask->curl);
1741 curl_easy_cleanup (ctask->curl);
1746 for (ctask=ctasks_head; NULL != ctask; ctask = ctask->next)
1749 if (num_ctasks != running)
1751 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1752 "CURL: %d tasks, %d running\n", num_ctasks, running);
1755 GNUNET_assert ( num_ctasks == running );
1757 } while (mret == CURLM_CALL_MULTI_PERFORM);
1759 if (mret != CURLM_OK)
1761 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CURL: %s failed at %s:%d: `%s'\n",
1762 "curl_multi_perform", __FILE__, __LINE__,
1763 curl_multi_strerror (mret));
1765 curl_download_prepare();
1770 * Process LEHO lookup
1772 * @param cls the ctask
1773 * @param rd_count number of records returned
1774 * @param rd record data
1777 process_leho_lookup (void *cls,
1779 const struct GNUNET_NAMESTORE_RecordData *rd)
1781 struct ProxyCurlTask *ctask = cls;
1782 char hosthdr[262]; //256 + "Host: "
1786 struct hostent *phost;
1788 char resolvename[512];
1791 strcpy (ctask->leho, "");
1794 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1795 "No LEHO present!\n");
1797 for (i=0; i<rd_count; i++)
1799 if (rd[i].record_type != GNUNET_GNS_RECORD_LEHO)
1802 memcpy (ctask->leho, rd[i].data, rd[i].data_size);
1804 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1805 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
1808 if (0 != strcmp (ctask->leho, ""))
1810 sprintf (hosthdr, "%s%s:%d", "Host: ", ctask->leho, ctask->port);
1811 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1812 "New HTTP header value: %s\n", hosthdr);
1813 ctask->headers = curl_slist_append (ctask->headers, hosthdr);
1814 GNUNET_assert (NULL != ctask->headers);
1815 ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers);
1816 if (CURLE_OK != ret)
1818 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s failed at %s:%d: `%s'\n",
1819 "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret));
1824 if (ctask->mhd->is_ssl)
1826 phost = (struct hostent*)gethostbyname (ctask->host);
1830 ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
1831 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1832 "SSL target server: %s\n", ssl_ip);
1833 sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
1834 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1835 "Curl resolve: %s\n", resolvename);
1836 ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
1837 curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver);
1838 sprintf (curlurl, "https://%s:%d%s", ctask->leho, ctask->port, ctask->url);
1839 curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1843 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1844 "gethostbyname failed for %s!\n", ctask->host);
1845 ctask->download_is_finished = GNUNET_YES;
1846 ctask->download_error = GNUNET_YES;
1851 if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
1853 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1854 "%s failed at %s:%d: `%s'\n",
1855 "curl_multi_add_handle", __FILE__, __LINE__,
1856 curl_multi_strerror (mret));
1857 ctask->download_is_finished = GNUNET_YES;
1858 ctask->download_error = GNUNET_YES;
1861 GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1863 curl_download_prepare ();
1868 * Initialize download and trigger curl
1870 * @param cls the proxycurltask
1871 * @param auth_name the name of the authority (site of origin) of ctask->host
1875 process_get_authority (void *cls,
1876 const char* auth_name)
1878 struct ProxyCurlTask *ctask = cls;
1880 if (NULL == auth_name)
1882 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1883 "Get authority failed!\n");
1884 strcpy (ctask->authority, "");
1888 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1889 "Get authority yielded %s\n", auth_name);
1890 strcpy (ctask->authority, auth_name);
1893 GNUNET_GNS_lookup_zone (gns_handle,
1896 GNUNET_GNS_RECORD_LEHO,
1897 GNUNET_YES, //Only cached for performance
1899 &process_leho_lookup,
1904 mhd_log_callback (void* cls, const char* url)
1906 struct ProxyCurlTask *ctask;
1908 ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
1909 strcpy (ctask->url, url);
1915 * Main MHD callback for handling requests.
1918 * @param con MHD connection handle
1919 * @param url the url in the request
1920 * @param meth the HTTP method used ("GET", "PUT", etc.)
1921 * @param ver the HTTP version string (i.e. "HTTP/1.1")
1922 * @param upload_data the data being uploaded (excluding HEADERS,
1923 * for a POST that fits into memory and that is encoded
1924 * with a supported encoding, the POST data will NOT be
1925 * given in upload_data and is instead available as
1926 * part of MHD_get_connection_values; very large POST
1927 * data *will* be made available incrementally in
1929 * @param upload_data_size set initially to the size of the
1930 * upload_data provided; the method must update this
1931 * value to the number of bytes NOT processed;
1932 * @param con_cls pointer to location where we store the 'struct Request'
1933 * @return MHD_YES if the connection was handled successfully,
1934 * MHD_NO if the socket must be closed due to a serious
1935 * error while handling the request
1938 create_response (void *cls,
1939 struct MHD_Connection *con,
1943 const char *upload_data,
1944 size_t *upload_data_size,
1947 struct MhdHttpList* hd = cls;
1948 const char* page = "<html><head><title>gnunet-gns-proxy</title>"\
1949 "</head><body>cURL fail</body></html>";
1951 char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow!
1954 struct ProxyCurlTask *ctask = *con_cls;
1955 struct ProxyUploadData *fin_post;
1956 struct curl_forms forms[5];
1957 struct ProxyUploadData *upload_data_iter;
1960 if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) &&
1961 (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) &&
1962 (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)) &&
1963 (0 != strcasecmp (meth, MHD_HTTP_METHOD_HEAD)))
1965 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1966 "MHD: %s NOT IMPLEMENTED!\n", meth);
1971 if (GNUNET_NO == ctask->accepted)
1974 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1975 "Got %s request for %s\n", meth, url);
1977 ctask->curl = curl_easy_init();
1978 ctask->curl_running = GNUNET_NO;
1979 if (NULL == ctask->curl)
1981 ctask->response = MHD_create_response_from_buffer (strlen (page),
1983 MHD_RESPMEM_PERSISTENT);
1984 ret = MHD_queue_response (con,
1987 MHD_destroy_response (ctask->response);
1988 GNUNET_free (ctask);
1992 if (ctask->mhd->is_ssl)
1993 ctask->port = HTTPS_PORT;
1995 ctask->port = HTTP_PORT;
1997 MHD_get_connection_values (con,
1999 &con_val_iter, ctask);
2001 curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
2002 curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
2003 curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
2004 curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
2005 curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0);
2006 curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
2008 if (GNUNET_NO == ctask->mhd->is_ssl)
2010 sprintf (curlurl, "http://%s:%d%s", ctask->host, ctask->port, ctask->url);
2011 curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
2015 curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
2016 curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
2017 curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
2019 /* Add GNS header */
2020 ctask->headers = curl_slist_append (ctask->headers,
2022 ctask->accepted = GNUNET_YES;
2023 ctask->download_in_progress = GNUNET_YES;
2024 ctask->buf_status = BUF_WAIT_FOR_CURL;
2025 ctask->connection = con;
2026 ctask->curl_response_code = MHD_HTTP_OK;
2027 ctask->buffer_read_ptr = ctask->buffer;
2028 ctask->buffer_write_ptr = ctask->buffer;
2029 ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
2032 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
2034 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2035 "Setting up PUT\n");
2037 curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1);
2038 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
2039 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &put_read_callback);
2040 ctask->headers = curl_slist_append (ctask->headers,
2041 "Transfer-Encoding: chunked");
2044 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2046 //FIXME handle multipart
2047 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2048 "Setting up POST processor\n");
2049 ctask->post_handler = MHD_create_post_processor (con,
2051 &con_post_data_iter,
2053 ctask->headers = curl_slist_append (ctask->headers,
2054 "Transfer-Encoding: chunked");
2058 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
2060 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2061 "Setting NOBODY\n");
2062 curl_easy_setopt (ctask->curl, CURLOPT_NOBODY, 1);
2066 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2067 "MHD: Adding new curl task for %s\n", ctask->host);
2069 GNUNET_GNS_get_authority (gns_handle,
2071 &process_get_authority,
2073 ctask->ready_to_queue = GNUNET_NO;
2074 ctask->fin = GNUNET_NO;
2075 ctask->curl_running = GNUNET_YES;
2079 ctask = (struct ProxyCurlTask *) *con_cls;
2080 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2082 if (0 != *upload_data_size)
2085 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2086 "Invoking POST processor\n");
2087 MHD_post_process (ctask->post_handler,
2088 upload_data, *upload_data_size);
2089 *upload_data_size = 0;
2090 if ((GNUNET_NO == ctask->is_httppost) &&
2091 (GNUNET_NO == ctask->curl_running))
2093 curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
2094 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION,
2095 &post_read_callback);
2096 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
2098 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2099 "MHD: Adding new curl task for %s\n", ctask->host);
2101 GNUNET_GNS_get_authority (gns_handle,
2103 &process_get_authority,
2105 ctask->ready_to_queue = GNUNET_NO;
2106 ctask->fin = GNUNET_NO;
2107 ctask->curl_running = GNUNET_YES;
2111 else if (GNUNET_NO == ctask->post_done)
2113 if (GNUNET_YES == ctask->is_httppost)
2115 for (upload_data_iter = ctask->upload_data_head;
2116 NULL != upload_data_iter;
2117 upload_data_iter = upload_data_iter->next)
2120 if (NULL != upload_data_iter->filename)
2122 forms[i].option = CURLFORM_FILENAME;
2123 forms[i].value = upload_data_iter->filename;
2124 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2125 "Adding filename %s\n",
2129 if (NULL != upload_data_iter->content_type)
2131 forms[i].option = CURLFORM_CONTENTTYPE;
2132 forms[i].value = upload_data_iter->content_type;
2133 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2134 "Adding content type %s\n",
2138 forms[i].option = CURLFORM_PTRCONTENTS;
2139 forms[i].value = upload_data_iter->value;
2140 forms[i+1].option = CURLFORM_END;
2142 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2143 "Adding formdata for %s (len=%lld)\n",
2144 upload_data_iter->key,
2145 upload_data_iter->total_bytes);
2147 curl_formadd(&ctask->httppost, &ctask->httppost_last,
2148 CURLFORM_COPYNAME, upload_data_iter->key,
2149 CURLFORM_CONTENTSLENGTH, upload_data_iter->total_bytes,
2150 CURLFORM_ARRAY, forms,
2153 curl_easy_setopt (ctask->curl, CURLOPT_HTTPPOST,
2156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2157 "MHD: Adding new curl task for %s\n", ctask->host);
2159 GNUNET_GNS_get_authority (gns_handle,
2161 &process_get_authority,
2163 ctask->ready_to_queue = GNUNET_YES;
2164 ctask->fin = GNUNET_NO;
2165 ctask->curl_running = GNUNET_YES;
2166 ctask->post_done = GNUNET_YES;
2170 fin_post = GNUNET_malloc (sizeof (struct ProxyUploadData));
2171 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
2172 ctask->upload_data_tail,
2174 ctask->post_done = GNUNET_YES;
2179 if (GNUNET_YES != ctask->ready_to_queue)
2180 return MHD_YES; /* wait longer */
2182 if (GNUNET_YES == ctask->fin)
2185 ctask->fin = GNUNET_YES;
2186 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2187 "MHD: Queueing response for %s\n", ctask->url);
2188 ret = MHD_queue_response (con, ctask->curl_response_code, ctask->response);
2189 run_mhd_now (ctask->mhd);
2200 struct MhdHttpList *hd;
2202 for (hd=mhd_httpd_head; NULL != hd; hd = hd->next)
2210 * @param hd the daemon to run
2213 run_httpd (struct MhdHttpList *hd)
2218 struct GNUNET_NETWORK_FDSet *wrs;
2219 struct GNUNET_NETWORK_FDSet *wws;
2220 struct GNUNET_NETWORK_FDSet *wes;
2223 unsigned MHD_LONG_LONG timeout;
2224 struct GNUNET_TIME_Relative tv;
2229 wrs = GNUNET_NETWORK_fdset_create ();
2230 wes = GNUNET_NETWORK_fdset_create ();
2231 wws = GNUNET_NETWORK_fdset_create ();
2233 GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
2236 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2237 "MHD fds: max=%d\n", max);
2239 haveto = MHD_get_timeout (hd->daemon, &timeout);
2241 if (MHD_YES == haveto)
2242 tv.rel_value = (uint64_t) timeout;
2244 tv = GNUNET_TIME_UNIT_FOREVER_REL;
2245 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
2246 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
2247 GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
2249 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2250 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2252 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
2255 GNUNET_NETWORK_fdset_destroy (wrs);
2256 GNUNET_NETWORK_fdset_destroy (wws);
2257 GNUNET_NETWORK_fdset_destroy (wes);
2262 * Task run whenever HTTP server operations are pending.
2265 * @param tc sched context
2268 do_httpd (void *cls,
2269 const struct GNUNET_SCHEDULER_TaskContext *tc)
2271 struct MhdHttpList *hd = cls;
2273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2274 "MHD: Main loop\n");
2275 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2276 MHD_run (hd->daemon);
2283 * Read data from socket
2285 * @param cls the closure
2286 * @param tc scheduler context
2289 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2292 * Read from remote end
2294 * @param cls closure
2295 * @param tc scheduler context
2298 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2301 * Write data to remote socket
2303 * @param cls the closure
2304 * @param tc scheduler context
2307 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2309 struct Socks5Request *s5r = cls;
2312 s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2314 if ((NULL != tc->read_ready) &&
2315 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
2316 ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
2319 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2320 "Successfully sent %d bytes to remote socket\n",
2325 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
2326 if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2327 GNUNET_SCHEDULER_cancel (s5r->rtask);
2328 if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
2329 GNUNET_SCHEDULER_cancel (s5r->wtask);
2330 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2331 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2332 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2333 GNUNET_NETWORK_socket_close (s5r->sock);
2339 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2346 * Clean up s5r handles
2348 * @param s5r the handle to destroy
2351 cleanup_s5r (struct Socks5Request *s5r)
2353 if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2354 GNUNET_SCHEDULER_cancel (s5r->rtask);
2355 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdwtask)
2356 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2357 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2358 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2359 if (NULL != s5r->remote_sock)
2360 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2361 if ((NULL != s5r->sock) && (s5r->cleanup_sock == GNUNET_YES))
2362 GNUNET_NETWORK_socket_close (s5r->sock);
2368 * Write data to socket
2370 * @param cls the closure
2371 * @param tc scheduler context
2374 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2376 struct Socks5Request *s5r = cls;
2379 s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2381 if ((NULL != tc->read_ready) &&
2382 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
2383 ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
2386 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2387 "Successfully sent %d bytes to socket\n",
2393 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
2394 s5r->cleanup = GNUNET_YES;
2395 s5r->cleanup_sock = GNUNET_YES;
2401 if (GNUNET_YES == s5r->cleanup)
2407 if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
2408 (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
2410 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2412 &do_read_remote, s5r);
2416 * Read from remote end
2418 * @param cls closure
2419 * @param tc scheduler context
2422 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2424 struct Socks5Request *s5r = cls;
2426 s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2429 if ((NULL != tc->write_ready) &&
2430 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
2431 (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
2432 sizeof (s5r->wbuf))))
2434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2435 "Successfully read %d bytes from remote socket\n",
2440 if (s5r->wbuf_len == 0)
2441 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2442 "0 bytes received from remote... graceful shutdown!\n");
2443 if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2444 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2445 if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
2446 GNUNET_SCHEDULER_cancel (s5r->rtask);
2448 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2449 s5r->remote_sock = NULL;
2450 GNUNET_NETWORK_socket_close (s5r->sock);
2456 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2464 * Adds a socket to MHD
2466 * @param h the handle to the socket to add
2467 * @param daemon the daemon to add the fd to
2468 * @return whatever MHD_add_connection returns
2471 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
2474 struct sockaddr *addr;
2477 fd = dup (GNUNET_NETWORK_get_fd (h));
2478 addr = GNUNET_NETWORK_get_addr (h);
2479 len = GNUNET_NETWORK_get_addrlen (h);
2481 return MHD_add_connection (daemon, fd, addr, len);
2485 * Read file in filename
2487 * @param filename file to read
2488 * @param size pointer where filesize is stored
2489 * @return NULL on error
2492 load_file (const char* filename,
2499 GNUNET_DISK_file_size (filename, &fsize,
2500 GNUNET_YES, GNUNET_YES))
2502 if (fsize > MAX_PEM_SIZE)
2504 *size = (unsigned int) fsize;
2505 buffer = GNUNET_malloc (*size);
2506 if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize))
2508 GNUNET_free (buffer);
2516 * Load PEM key from file
2518 * @param key where to store the data
2519 * @param keyfile path to the PEM file
2520 * @return GNUNET_OK on success
2523 load_key_from_file (gnutls_x509_privkey_t key, const char* keyfile)
2525 gnutls_datum_t key_data;
2528 key_data.data = load_file (keyfile, &key_data.size);
2529 ret = gnutls_x509_privkey_import (key, &key_data,
2530 GNUTLS_X509_FMT_PEM);
2531 if (GNUTLS_E_SUCCESS != ret)
2533 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2534 _("Unable to import private key from file `%s'\n"),
2538 GNUNET_free (key_data.data);
2539 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2544 * Load cert from file
2546 * @param crt struct to store data in
2547 * @param certfile path to pem file
2548 * @return GNUNET_OK on success
2551 load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
2553 gnutls_datum_t cert_data;
2556 cert_data.data = load_file (certfile, &cert_data.size);
2557 ret = gnutls_x509_crt_import (crt, &cert_data,
2558 GNUTLS_X509_FMT_PEM);
2559 if (GNUTLS_E_SUCCESS != ret)
2561 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2562 _("Unable to import certificate %s\n"), certfile);
2565 GNUNET_free (cert_data.data);
2566 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2571 * Generate new certificate for specific name
2573 * @param name the subject name to generate a cert for
2574 * @return a struct holding the PEM data
2576 static struct ProxyGNSCertificate *
2577 generate_gns_certificate (const char *name)
2580 unsigned int serial;
2581 size_t key_buf_size;
2582 size_t cert_buf_size;
2583 gnutls_x509_crt_t request;
2587 ret = gnutls_x509_crt_init (&request);
2589 if (GNUTLS_E_SUCCESS != ret)
2594 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
2596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
2598 struct ProxyGNSCertificate *pgc =
2599 GNUNET_malloc (sizeof (struct ProxyGNSCertificate));
2601 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
2603 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
2605 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
2607 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
2608 0, name, strlen (name));
2609 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_version (request, 3));
2611 ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
2613 etime = time (NULL);
2614 tm_data = localtime (&etime);
2617 ret = gnutls_x509_crt_set_serial (request,
2621 ret = gnutls_x509_crt_set_activation_time (request,
2624 etime = mktime (tm_data);
2631 ret = gnutls_x509_crt_set_expiration_time (request,
2633 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
2635 ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
2637 key_buf_size = sizeof (pgc->key);
2638 cert_buf_size = sizeof (pgc->cert);
2640 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exporting certificate...\n");
2642 gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
2643 pgc->cert, &cert_buf_size);
2645 gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
2646 pgc->key, &key_buf_size);
2649 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
2650 gnutls_x509_crt_deinit (request);
2658 * Accept policy for mhdaemons
2661 * @param addr the sockaddr
2662 * @param addrlen the sockaddr length
2663 * @return MHD_NO if sockaddr is wrong or #conns too high
2666 accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen)
2668 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2669 "In MHD accept policy cb\n");
2673 if (addr->sa_family == AF_UNIX)
2677 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2678 "Connection accepted\n");
2685 * Adds a socket to an SSL MHD instance
2686 * It is important the the domain name is
2687 * correct. In most cases we need to start a new daemon
2689 * @param h the handle to add to a daemon
2690 * @param domain the domain the ssl daemon has to serve
2691 * @return MHD_YES on success
2694 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, const char* domain)
2696 struct MhdHttpList *hd = NULL;
2697 struct ProxyGNSCertificate *pgc;
2698 struct NetworkHandleList *nh;
2700 for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2701 if (0 == strcmp (hd->domain, domain))
2706 pgc = generate_gns_certificate (domain);
2708 hd = GNUNET_malloc (sizeof (struct MhdHttpList));
2709 hd->is_ssl = GNUNET_YES;
2710 strcpy (hd->domain, domain);
2711 hd->proxy_cert = pgc;
2714 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2715 "No previous SSL instance found... starting new one for %s\n",
2717 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG
2719 #if HAVE_MHD_NO_LISTEN_SOCKET
2720 | MHD_USE_NO_LISTEN_SOCKET,
2726 &create_response, hd,
2727 #if !HAVE_MHD_NO_LISTEN_SOCKET
2728 MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
2730 MHD_OPTION_CONNECTION_LIMIT,
2731 MHD_MAX_CONNECTIONS,
2732 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2733 MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2734 MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2735 MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2736 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2740 GNUNET_assert (hd->daemon != NULL);
2741 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2743 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2746 nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2749 GNUNET_CONTAINER_DLL_insert (hd->socket_handles_head,
2750 hd->socket_handles_tail,
2753 return add_handle_to_mhd (h, hd->daemon);
2759 * Read data from incoming connection
2761 * @param cls the closure
2762 * @param tc the scheduler context
2765 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2767 struct Socks5Request *s5r = cls;
2768 struct socks5_client_hello *c_hello;
2769 struct socks5_server_hello *s_hello;
2770 struct socks5_client_request *c_req;
2771 struct socks5_server_response *s_resp;
2777 struct hostent *phost;
2779 struct sockaddr_in remote_addr;
2780 struct in_addr *r_sin_addr;
2782 struct NetworkHandleList *nh;
2784 s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
2786 if ((NULL != tc->write_ready) &&
2787 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
2788 (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
2789 sizeof (s5r->rbuf))))
2791 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2792 "Successfully read %d bytes from socket\n",
2797 if (s5r->rbuf_len != 0)
2798 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
2800 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
2802 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2803 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2804 if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
2805 GNUNET_SCHEDULER_cancel (s5r->wtask);
2806 if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2807 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2808 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2809 GNUNET_NETWORK_socket_close (s5r->sock);
2814 if (s5r->state == SOCKS5_INIT)
2816 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2818 c_hello = (struct socks5_client_hello*)&s5r->rbuf;
2820 GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
2822 s_hello = (struct socks5_server_hello*)&s5r->wbuf;
2823 s5r->wbuf_len = sizeof( struct socks5_server_hello );
2825 s_hello->version = c_hello->version;
2826 s_hello->auth_method = SOCKS_AUTH_NONE;
2828 /* Write response to client */
2829 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2833 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2837 s5r->state = SOCKS5_REQUEST;
2841 if (s5r->state == SOCKS5_REQUEST)
2843 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2844 "Processing SOCKS5 request\n");
2845 c_req = (struct socks5_client_request*)&s5r->rbuf;
2846 s_resp = (struct socks5_server_response*)&s5r->wbuf;
2847 //Only 10byte for ipv4 response!
2848 s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
2850 GNUNET_assert (c_req->addr_type == 3);
2852 dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
2853 memset(domain, 0, sizeof(domain));
2854 strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
2855 req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
2857 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2858 "Requested connection is %s:%d\n",
2862 if (is_tld (domain, GNUNET_GNS_TLD) ||
2863 is_tld (domain, GNUNET_GNS_TLD_ZKEY))
2865 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2866 "Requested connection is gnunet tld\n",
2870 if (ntohs(req_port) == HTTPS_PORT)
2872 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2873 "Requested connection is HTTPS\n");
2874 ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
2876 else if (NULL != httpd)
2878 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2879 "Requested connection is HTTP\n");
2880 nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2883 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head->socket_handles_head,
2884 mhd_httpd_head->socket_handles_tail,
2887 ret = add_handle_to_mhd ( s5r->sock, httpd );
2892 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2893 _("Failed to start HTTP server\n"));
2894 s_resp->version = 0x05;
2895 s_resp->reply = 0x01;
2896 s5r->cleanup = GNUNET_YES;
2897 s5r->cleanup_sock = GNUNET_YES;
2899 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2905 /* Signal success */
2906 s_resp->version = 0x05;
2907 s_resp->reply = 0x00;
2908 s_resp->reserved = 0x00;
2909 s_resp->addr_type = 0x01;
2911 s5r->cleanup = GNUNET_YES;
2912 s5r->cleanup_sock = GNUNET_NO;
2914 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2922 phost = (struct hostent*)gethostbyname (domain);
2925 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2926 "Resolve %s error!\n", domain );
2927 s_resp->version = 0x05;
2928 s_resp->reply = 0x01;
2929 s5r->cleanup = GNUNET_YES;
2930 s5r->cleanup_sock = GNUNET_YES;
2932 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2938 s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
2941 r_sin_addr = (struct in_addr*)(phost->h_addr);
2942 remote_ip = r_sin_addr->s_addr;
2943 memset(&remote_addr, 0, sizeof(remote_addr));
2944 remote_addr.sin_family = AF_INET;
2945 #if HAVE_SOCKADDR_IN_SIN_LEN
2946 remote_addr.sin_len = sizeof (remote_addr);
2948 remote_addr.sin_addr.s_addr = remote_ip;
2949 remote_addr.sin_port = req_port;
2951 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2952 "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
2956 GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
2957 (const struct sockaddr*)&remote_addr,
2958 sizeof (remote_addr)))
2959 && (errno != EINPROGRESS))
2961 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
2962 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2963 "socket request error...\n");
2964 s_resp->version = 0x05;
2965 s_resp->reply = 0x01;
2967 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2974 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2975 "new remote connection\n");
2977 s_resp->version = 0x05;
2978 s_resp->reply = 0x00;
2979 s_resp->reserved = 0x00;
2980 s_resp->addr_type = 0x01;
2982 s5r->state = SOCKS5_DATA_TRANSFER;
2985 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2989 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2997 if (s5r->state == SOCKS5_DATA_TRANSFER)
2999 if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
3001 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3002 "Closing connection to client\n");
3003 if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
3004 GNUNET_SCHEDULER_cancel (s5r->rtask);
3005 if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
3006 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
3007 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
3008 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
3009 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
3010 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
3012 if (s5r->remote_sock != NULL)
3013 GNUNET_NETWORK_socket_close (s5r->remote_sock);
3014 GNUNET_NETWORK_socket_close (s5r->sock);
3019 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3020 "forwarding %d bytes from client\n", s5r->rbuf_len);
3023 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
3025 &do_write_remote, s5r);
3027 if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
3030 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3032 &do_read_remote, s5r);
3042 * Accept new incoming connections
3044 * @param cls the closure
3045 * @param tc the scheduler context
3048 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3050 struct GNUNET_NETWORK_Handle *s;
3051 struct Socks5Request *s5r;
3053 ltask = GNUNET_SCHEDULER_NO_TASK;
3054 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
3057 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3061 s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
3065 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
3069 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3070 "Got an inbound connection, waiting for data\n");
3072 s5r = GNUNET_malloc (sizeof (struct Socks5Request));
3074 s5r->state = SOCKS5_INIT;
3075 s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
3076 s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
3077 s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
3078 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3085 * Task run on shutdown
3087 * @param cls closure
3088 * @param tc task context
3091 do_shutdown (void *cls,
3092 const struct GNUNET_SCHEDULER_TaskContext *tc)
3095 struct MhdHttpList *hd;
3096 struct MhdHttpList *tmp_hd;
3097 struct NetworkHandleList *nh;
3098 struct NetworkHandleList *tmp_nh;
3099 struct ProxyCurlTask *ctask;
3100 struct ProxyCurlTask *ctask_tmp;
3101 struct ProxyUploadData *pdata;
3103 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3104 "Shutting down...\n");
3106 gnutls_global_deinit ();
3108 if (NULL != local_gns_zone)
3109 GNUNET_free (local_gns_zone);
3110 if (NULL != local_private_zone)
3111 GNUNET_free (local_private_zone);
3112 if (NULL != local_shorten_zone)
3113 GNUNET_free (local_shorten_zone);
3115 if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
3117 GNUNET_SCHEDULER_cancel (curl_download_task);
3118 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
3121 for (hd = mhd_httpd_head; hd != NULL; hd = tmp_hd)
3125 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3126 "Stopping daemon\n");
3128 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
3130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3131 "Stopping select task %d\n",
3133 GNUNET_SCHEDULER_cancel (hd->httpd_task);
3134 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
3137 if (NULL != hd->daemon)
3139 MHD_stop_daemon (hd->daemon);
3143 for (nh = hd->socket_handles_head; nh != NULL; nh = tmp_nh)
3147 GNUNET_NETWORK_socket_close (nh->h);
3152 if (NULL != hd->proxy_cert)
3154 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3155 "Free certificate\n");
3156 GNUNET_free (hd->proxy_cert);
3162 for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
3164 ctask_tmp = ctask->next;
3166 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3167 "Cleaning up cURL task\n");
3169 if (ctask->curl != NULL)
3170 curl_easy_cleanup (ctask->curl);
3172 if (NULL != ctask->headers)
3173 curl_slist_free_all (ctask->headers);
3174 if (NULL != ctask->resolver)
3175 curl_slist_free_all (ctask->resolver);
3177 if (NULL != ctask->response)
3178 MHD_destroy_response (ctask->response);
3180 pdata = ctask->upload_data_head;
3182 //FIXME free pdata here
3183 for (; pdata != NULL; pdata = ctask->upload_data_head)
3185 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
3186 ctask->upload_data_tail,
3188 GNUNET_free_non_null (pdata->filename);
3189 GNUNET_free_non_null (pdata->content_type);
3190 GNUNET_free_non_null (pdata->key);
3191 GNUNET_free_non_null (pdata->value);
3192 GNUNET_free (pdata);
3195 GNUNET_free (ctask);
3197 curl_multi_cleanup (curl_multi);
3199 GNUNET_GNS_disconnect (gns_handle);
3204 * Compiles a regex for us
3206 * @param re ptr to re struct
3207 * @param rt the expression to compile
3208 * @return 0 on success
3211 compile_regex (regex_t *re, const char* rt)
3216 status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
3219 regerror (status, re, err, 1024);
3220 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3221 "Regex error compiling '%s': %s\n", rt, err);
3229 * Loads the users local zone key
3231 * @return GNUNET_YES on success
3234 load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg)
3237 struct GNUNET_CRYPTO_RsaPrivateKey *key = NULL;
3238 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
3239 struct GNUNET_CRYPTO_ShortHashCode *zone = NULL;
3240 struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
3242 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3243 "ZONEKEY", &keyfile))
3245 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3246 "Unable to load zone key config value!\n");
3250 if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
3252 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3253 "Unable to load zone key %s!\n", keyfile);
3254 GNUNET_free(keyfile);
3258 key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
3259 GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
3260 local_gns_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3261 GNUNET_CRYPTO_short_hash(&pkey,
3262 sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
3264 zone = local_gns_zone;
3265 GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3266 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3267 "Using zone: %s!\n", &zonename);
3268 GNUNET_CRYPTO_rsa_key_free(key);
3269 GNUNET_free(keyfile);
3272 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3273 "PRIVATE_ZONEKEY", &keyfile))
3275 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3276 "Unable to load private zone key config value!\n");
3279 if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile)))
3281 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3282 "Unable to load private zone key %s!\n", keyfile);
3283 GNUNET_free(keyfile);
3287 key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
3288 GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
3289 local_private_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3290 GNUNET_CRYPTO_short_hash(&pkey,
3291 sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
3292 local_private_zone);
3293 GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3295 "Using private zone: %s!\n", &zonename);
3296 GNUNET_CRYPTO_rsa_key_free(key);
3297 GNUNET_free(keyfile);
3301 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3302 "SHORTEN_ZONEKEY", &keyfile))
3304 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3305 "Unable to load shorten zone key config value!\n");
3308 if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile)))
3310 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3311 "Unable to load shorten zone key %s!\n", keyfile);
3312 GNUNET_free(keyfile);
3316 key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
3317 GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
3318 local_shorten_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3319 GNUNET_CRYPTO_short_hash(&pkey,
3320 sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
3321 local_shorten_zone);
3322 GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3323 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3324 "Using shorten zone: %s!\n", &zonename);
3325 GNUNET_CRYPTO_rsa_key_free(key);
3326 GNUNET_free(keyfile);
3333 * Main function that will be run
3335 * @param cls closure
3336 * @param args remaining command-line arguments
3337 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3338 * @param cfg configuration
3341 run (void *cls, char *const *args, const char *cfgfile,
3342 const struct GNUNET_CONFIGURATION_Handle *cfg)
3344 struct sockaddr_in sa;
3345 struct MhdHttpList *hd;
3346 char* cafile_cfg = NULL;
3348 #if !HAVE_MHD_NO_LISTEN_SOCKET
3350 char* proxy_sockfile;
3351 struct sockaddr_un mhd_unix_sock_addr;
3354 curl_multi = curl_multi_init ();
3356 if (NULL == curl_multi)
3358 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3359 "Failed to create cURL multo handle!\n");
3363 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3365 cafile = cafile_opt;
3368 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3372 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3373 "Unable to load proxy CA config value!\n");
3374 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3375 "No proxy CA provided!\n");
3378 cafile = cafile_cfg;
3380 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3381 "Using %s as CA\n", cafile);
3383 gnutls_global_init ();
3384 gnutls_x509_crt_init (&proxy_ca.cert);
3385 gnutls_x509_privkey_init (&proxy_ca.key);
3387 if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
3388 (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
3390 // FIXME: release resources...
3394 GNUNET_free_non_null (cafile_cfg);
3396 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3397 "Loading Template\n");
3399 compile_regex (&re_dotplus, (char*) RE_A_HREF);
3401 gns_handle = GNUNET_GNS_connect (cfg);
3403 if (GNUNET_NO == load_local_zone_key (cfg))
3405 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3406 "Unable to load zone!\n");
3410 if (NULL == gns_handle)
3412 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3413 "Unable to connect to GNS!\n");
3417 memset (&sa, 0, sizeof (sa));
3418 sa.sin_family = AF_INET;
3419 sa.sin_port = htons (port);
3420 #if HAVE_SOCKADDR_IN_SIN_LEN
3421 sa.sin_len = sizeof (sa);
3424 lsock = GNUNET_NETWORK_socket_create (AF_INET,
3428 if ((NULL == lsock) ||
3430 GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
3433 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3434 "Failed to create listen socket bound to `%s'",
3435 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
3437 GNUNET_NETWORK_socket_close (lsock);
3441 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
3443 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3444 "Failed to listen on socket bound to `%s'",
3445 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
3449 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3450 lsock, &do_accept, NULL);
3455 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
3457 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3458 "cURL global init failed!\n");
3462 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3463 "Proxy listens on port %u\n",
3466 mhd_httpd_head = NULL;
3467 mhd_httpd_tail = NULL;
3468 total_mhd_connections = 0;
3469 #if ! HAVE_MHD_NO_LISTEN_SOCKET
3470 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3474 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3475 "Specify PROXY_UNIXPATH in gns-proxy config section!\n");
3479 mhd_unix_socket = GNUNET_NETWORK_socket_create (AF_UNIX,
3483 if (NULL == mhd_unix_socket)
3485 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3486 "Unable to create unix domain socket!\n");
3490 mhd_unix_sock_addr.sun_family = AF_UNIX;
3491 strcpy (mhd_unix_sock_addr.sun_path, proxy_sockfile);
3494 mhd_unix_sock_addr.sun_path[0] = '\0';
3496 #if HAVE_SOCKADDR_IN_SIN_LEN
3497 mhd_unix_sock_addr.sun_len = (u_char) sizeof (struct sockaddr_un);
3500 len = strlen (proxy_sockfile) + sizeof(AF_UNIX);
3502 GNUNET_free (proxy_sockfile);
3504 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (mhd_unix_socket,
3505 (struct sockaddr*)&mhd_unix_sock_addr,
3508 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3509 "Unable to bind unix domain socket!\n");
3513 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (mhd_unix_socket,
3516 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3517 "Unable to listen on unix domain socket!\n");
3522 hd = GNUNET_malloc (sizeof (struct MhdHttpList));
3523 hd->is_ssl = GNUNET_NO;
3524 strcpy (hd->domain, "");
3525 httpd = MHD_start_daemon (MHD_USE_DEBUG
3526 #if HAVE_MHD_NO_LISTEN_SOCKET
3527 | MHD_USE_NO_LISTEN_SOCKET,
3530 , 4444, //Dummy port
3533 &create_response, hd,
3534 #if !HAVE_MHD_NO_LISTEN_SOCKET
3535 MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
3537 MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3538 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3539 MHD_OPTION_NOTIFY_COMPLETED,
3541 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3544 GNUNET_assert (httpd != NULL);
3546 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
3548 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
3552 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
3553 &do_shutdown, NULL);
3559 * The main function for gnunet-gns-proxy.
3561 * @param argc number of arguments from the command line
3562 * @param argv command line arguments
3563 * @return 0 ok, 1 on error
3566 main (int argc, char *const *argv)
3568 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
3570 gettext_noop ("listen on specified port (default: 7777)"), 1,
3571 &GNUNET_GETOPT_set_ulong, &port},
3572 {'a', "authority", NULL,
3573 gettext_noop ("pem file to use as CA"), 1,
3574 &GNUNET_GETOPT_set_string, &cafile_opt},
3575 GNUNET_GETOPT_OPTION_END
3580 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
3584 GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
3587 GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
3588 _("GNUnet GNS proxy"),
3590 &run, NULL)) ? 0 : 1;
3591 GNUNET_free_non_null ((char*)argv);