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 "gnunet_identity_service.h"
37 #include "gns_proxy_proto.h"
41 #define GNUNET_GNS_PROXY_PORT 7777
43 #define MHD_MAX_CONNECTIONS 300
45 #define MAX_HTTP_URI_LENGTH 2048
47 #define POSTBUFFERSIZE 4096
51 #define HTTPS_PORT 443
56 * @param level log level
57 * @param fun name of curl_easy-function that gave the error
58 * @param rc return code from curl
60 #define LOG_CURL_EASY(level,fun,rc) GNUNET_log(level, _("%s failed at %s:%d: `%s'\n"), fun, __FILE__, __LINE__, curl_easy_strerror (rc))
72 * A structure for CA cert/key
79 gnutls_x509_crt_t cert;
84 gnutls_x509_privkey_t key;
87 #define MAX_PEM_SIZE (10 * 1024)
90 * Structure for GNS certificates
92 struct ProxyGNSCertificate
94 /* The certificate as PEM */
95 char cert[MAX_PEM_SIZE];
97 /* The private key as PEM */
98 char key[MAX_PEM_SIZE];
103 * A structure for socks requests
110 struct GNUNET_NETWORK_Handle *sock;
115 struct GNUNET_NETWORK_Handle *remote_sock;
123 * Client socket read task
125 GNUNET_SCHEDULER_TaskIdentifier rtask;
128 * Server socket read task
130 GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
133 * Client socket write task
135 GNUNET_SCHEDULER_TaskIdentifier wtask;
138 * Server socket write task
140 GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
153 * Length of data in read buffer
155 unsigned int rbuf_len;
158 * Length of data in write buffer
160 unsigned int wbuf_len;
163 * This handle is scheduled for cleanup?
168 * Shall we close the client socket on cleanup?
175 * DLL for Network Handles
177 struct NetworkHandleList
182 struct NetworkHandleList *next;
187 struct NetworkHandleList *prev;
192 struct GNUNET_NETWORK_Handle *h;
196 * A structure for all running Httpds
203 struct MhdHttpList *prev;
208 struct MhdHttpList *next;
211 * is this an ssl daemon?
216 * the domain name to server (only important for SSL)
223 struct MHD_Daemon *daemon;
226 * Optional proxy certificate used
228 struct ProxyGNSCertificate *proxy_cert;
233 GNUNET_SCHEDULER_TaskIdentifier httpd_task;
236 * Handles associated with this daemon
238 struct NetworkHandleList *socket_handles_head;
241 * Handles associated with this daemon
243 struct NetworkHandleList *socket_handles_tail;
247 * A structure for MHD<->cURL streams
254 struct ProxyCurlTask *prev;
259 struct ProxyCurlTask *next;
267 * Optional header replacements for curl (LEHO)
269 struct curl_slist *headers;
272 * Optional resolver replacements for curl (LEHO)
274 struct curl_slist *resolver;
279 long curl_response_code;
284 char url[MAX_HTTP_URI_LENGTH];
287 * The cURL write buffer / MHD read buffer
289 char buffer[CURL_MAX_WRITE_SIZE];
292 * Read pos of the data in the buffer
294 char *buffer_read_ptr;
297 * Write pos in the buffer
299 char *buffer_write_ptr;
304 struct MHD_Connection *connection;
309 size_t put_read_offset;
310 size_t put_read_size;
315 struct MHD_PostProcessor *post_handler;
318 struct ProxyUploadData *upload_data_head;
319 struct ProxyUploadData *upload_data_tail;
322 * the type of POST encoding
326 struct curl_httppost *httppost;
328 struct curl_httppost *httppost_last;
331 * Number of bytes in buffer
333 unsigned int bytes_in_buffer;
336 GNUNET_SCHEDULER_TaskIdentifier pp_task;
338 /* The associated daemon list entry */
339 struct MhdHttpList *mhd;
341 /* The associated response */
342 struct MHD_Response *response;
345 struct ProxySetCookieHeader *set_cookies_head;
348 struct ProxySetCookieHeader *set_cookies_tail;
350 /* The authority of the corresponding host (site of origin) */
353 /* The hostname (Host header field) */
356 /* The LEgacy HOstname (can be empty) */
365 * The buffer status (BUF_WAIT_FOR_CURL or BUF_WAIT_FOR_MHD)
367 enum BufferStatus buf_status;
390 * Indicates wheather the download is in progress
392 int download_in_progress;
395 * Indicates wheather the download was successful
397 int download_is_finished;
400 * Indicates wheather the download failed
412 * Struct for set-cookies
414 struct ProxySetCookieHeader
419 struct ProxySetCookieHeader *next;
424 struct ProxySetCookieHeader *prev;
433 * Post data structure
435 struct ProxyUploadData
440 struct ProxyUploadData *next;
445 struct ProxyUploadData *prev;
453 size_t content_length;
473 * The port the proxy is running on (default 7777)
475 static unsigned long port = GNUNET_GNS_PROXY_PORT;
478 * The CA file (pem) to use for the proxy CA
480 static char* cafile_opt;
483 * The listen socket of the proxy
485 static struct GNUNET_NETWORK_Handle *lsock;
490 static GNUNET_SCHEDULER_TaskIdentifier ltask;
493 * The cURL download task
495 static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
498 * The non SSL httpd daemon handle
500 static struct MHD_Daemon *httpd;
503 * Number of current mhd connections
505 static unsigned int total_mhd_connections;
508 * The cURL multi handle
510 static CURLM *curl_multi;
513 * Handle to the GNS service
515 static struct GNUNET_GNS_Handle *gns_handle;
518 * DLL for ProxyCurlTasks
520 static struct ProxyCurlTask *ctasks_head;
523 * DLL for ProxyCurlTasks
525 static struct ProxyCurlTask *ctasks_tail;
528 * DLL for http daemons
530 static struct MhdHttpList *mhd_httpd_head;
533 * DLL for http daemons
535 static struct MhdHttpList *mhd_httpd_tail;
538 * The users local GNS master zone
540 static struct GNUNET_CRYPTO_EccPublicSignKey local_gns_zone;
543 * The users local shorten zone
545 static struct GNUNET_CRYPTO_EccPrivateKey local_shorten_zone;
548 * Is shortening enabled?
550 static int do_shorten;
553 * The CA for SSL certificate generation
555 static struct ProxyCA proxy_ca;
558 * UNIX domain socket for mhd
560 static struct GNUNET_NETWORK_Handle *mhd_unix_socket;
563 * Shorten zone private key
565 static struct GNUNET_CRYPTO_EccPrivateKey shorten_zonekey;
568 * Response we return on cURL failures.
570 static struct MHD_Response *curl_failure_response;
573 * Connection to identity service.
575 static struct GNUNET_IDENTITY_Handle *identity;
578 * Request for our ego.
580 static struct GNUNET_IDENTITY_Operation *id_op;
585 static const struct GNUNET_CONFIGURATION_Handle *cfg;
589 * Checks if name is in tld
591 * @param name the name to check
592 * @param tld the TLD to check for (must NOT begin with ".")
593 * @return #GNUNET_YES or #GNUNET_NO
596 is_tld (const char* name, const char* tld)
598 size_t name_len = strlen (name);
599 size_t tld_len = strlen (tld);
601 GNUNET_break ('.' != tld[0]);
602 return ( (tld_len < name_len) &&
603 ( ('.' == name[name_len - tld_len - 1]) || (name_len == tld_len) ) &&
605 name + (name_len - tld_len),
611 con_post_data_iter (void *cls,
612 enum MHD_ValueKind kind,
614 const char *filename,
615 const char *content_type,
616 const char *transfer_encoding,
621 struct ProxyCurlTask* ctask = cls;
622 struct ProxyUploadData* pdata;
626 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
627 "Got POST data (file: %s, content type: %s): '%s=%.*s' at offset %llu size %llu\n",
628 filename, content_type,
629 key, (int) size, data,
630 (unsigned long long) off,
631 (unsigned long long) size);
632 GNUNET_assert (NULL != ctask->post_type);
634 if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
637 ctask->is_httppost = GNUNET_YES;
641 pdata = GNUNET_new (struct ProxyUploadData);
642 pdata->key = GNUNET_strdup (key);
644 if (NULL != filename)
645 pdata->filename = GNUNET_strdup (filename);
646 if (NULL != content_type)
647 pdata->content_type = GNUNET_strdup (content_type);
648 pdata->value = GNUNET_malloc (size);
649 pdata->total_bytes = size;
650 memcpy (pdata->value, data, size);
651 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
652 ctask->upload_data_tail,
655 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
656 "Copied %llu bytes of POST Data\n",
657 (unsigned long long) size);
661 pdata = ctask->upload_data_tail;
662 new_value = GNUNET_malloc (size + pdata->total_bytes);
663 memcpy (new_value, pdata->value, pdata->total_bytes);
664 memcpy (new_value+off, data, size);
665 GNUNET_free (pdata->value);
666 pdata->value = new_value;
667 pdata->total_bytes += size;
672 if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
678 ctask->is_httppost = GNUNET_NO;
680 if (NULL != ctask->curl)
681 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
685 enc = curl_easy_escape (ctask->curl, key, 0);
692 pdata = GNUNET_new (struct ProxyUploadData);
693 pdata->value = GNUNET_malloc (strlen (enc) + 3);
694 if (NULL != ctask->upload_data_head)
696 pdata->value[0] = '&';
697 memcpy (pdata->value+1, enc, strlen (enc));
700 memcpy (pdata->value, enc, strlen (enc));
701 pdata->value[strlen (pdata->value)] = '=';
702 pdata->bytes_left = strlen (pdata->value);
703 pdata->total_bytes = pdata->bytes_left;
706 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
707 "Escaped POST key: '%s'\n",
710 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
711 ctask->upload_data_tail,
716 enc = curl_easy_escape (ctask->curl, data, 0);
722 pdata = GNUNET_new (struct ProxyUploadData);
723 pdata->value = GNUNET_malloc (strlen (enc) + 1);
724 memcpy (pdata->value, enc, strlen (enc));
725 pdata->bytes_left = strlen (pdata->value);
726 pdata->total_bytes = pdata->bytes_left;
729 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
730 "Escaped POST value: '%s'\n",
733 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
734 ctask->upload_data_tail,
741 * Read HTTP request header field 'Host'
743 * @param cls buffer to write to
744 * @param kind value kind
745 * @param key field key
746 * @param value field value
747 * @return #MHD_NO when Host found
750 con_val_iter (void *cls,
751 enum MHD_ValueKind kind,
755 struct ProxyCurlTask *ctask = cls;
756 char* buf = ctask->host;
762 if (0 == strcmp ("Host", key))
764 port = strchr (value, ':');
767 strncpy (buf, value, port-value);
769 if ((1 != sscanf (port, "%u", &uport)) ||
770 (uport > UINT16_MAX) ||
772 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
773 "Unable to parse port!\n");
775 ctask->port = (uint16_t) uport;
782 if (0 == strcmp (MHD_HTTP_HEADER_ACCEPT_ENCODING, key))
787 if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_TYPE,
790 if (0 == strncasecmp (value,
791 MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
792 strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
793 ctask->post_type = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
794 else if (0 == strncasecmp (value,
795 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
796 strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
797 ctask->post_type = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
799 ctask->post_type = NULL;
803 cstr = GNUNET_malloc (strlen (key) + strlen (hdr_val) + 3);
804 GNUNET_snprintf (cstr, strlen (key) + strlen (hdr_val) + 3,
805 "%s: %s", key, hdr_val);
807 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
808 "Client Header: %s\n", cstr);
810 ctask->headers = curl_slist_append (ctask->headers, cstr);
818 * Callback for MHD response
821 * @param pos in buffer
823 * @param max space in buffer
824 * @return number of bytes written
827 mhd_content_cb (void *cls,
834 * Check HTTP response header for mime
836 * @param buffer curl buffer
837 * @param size curl blocksize
838 * @param nmemb curl blocknumber
840 * @return size of read bytes
843 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
845 size_t bytes = size * nmemb;
846 struct ProxyCurlTask *ctask = cls;
847 int cookie_hdr_len = strlen (MHD_HTTP_HEADER_SET_COOKIE);
848 char hdr_generic[bytes+1];
849 char new_cookie_hdr[bytes+strlen (ctask->leho)+1];
850 char new_location[MAX_HTTP_URI_LENGTH+500];
860 char cors_hdr[strlen (ctask->leho) + strlen ("https://")];
862 if (NULL == ctask->response)
864 /* FIXME: get total size from curl (if available) */
865 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
866 "Creating response for %s\n", ctask->url);
867 ctask->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
868 sizeof (ctask->buffer),
873 /* if we have a leho add a CORS header */
874 if (0 != strcmp ("", ctask->leho))
876 /* We could also allow ssl and http here */
877 if (ctask->mhd->is_ssl)
878 sprintf (cors_hdr, "https://%s", ctask->leho);
880 sprintf (cors_hdr, "http://%s", ctask->leho);
882 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
883 "MHD: Adding CORS header field %s\n",
886 if (GNUNET_NO == MHD_add_response_header (ctask->response,
887 "Access-Control-Allow-Origin",
890 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
891 "MHD: Error adding CORS header field %s\n",
895 ctask->ready_to_queue = GNUNET_YES;
897 if (cookie_hdr_len > bytes)
900 memcpy (hdr_generic, buffer, bytes);
901 hdr_generic[bytes] = '\0';
903 if ('\n' == hdr_generic[bytes-1])
904 hdr_generic[bytes-1] = '\0';
906 if (hdr_generic[bytes-2] == '\r')
907 hdr_generic[bytes-2] = '\0';
909 if (0 == memcmp (hdr_generic,
910 MHD_HTTP_HEADER_SET_COOKIE,
913 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
914 "Looking for cookie in: `%s'\n", hdr_generic);
915 ndup = GNUNET_strdup (hdr_generic+cookie_hdr_len+1);
916 memset (new_cookie_hdr, 0, sizeof (new_cookie_hdr));
917 for (tok = strtok (ndup, ";"); tok != NULL; tok = strtok (NULL, ";"))
919 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
920 "Got Cookie token: %s\n", tok);
921 //memcpy (new_cookie_hdr+offset, tok, strlen (tok));
922 if (0 == memcmp (tok, " domain", strlen (" domain")))
924 cookie_domain = tok + strlen (" domain") + 1;
926 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
927 "Got Set-Cookie Domain: %s\n", cookie_domain);
929 if (strlen (cookie_domain) < strlen (ctask->leho))
931 delta_cdomain = strlen (ctask->leho) - strlen (cookie_domain);
932 if (0 == strcmp (cookie_domain, ctask->leho + (delta_cdomain)))
934 GNUNET_snprintf (new_cookie_hdr+offset,
935 sizeof (new_cookie_hdr),
936 " domain=%s", ctask->authority);
937 offset += strlen (" domain=") + strlen (ctask->authority);
938 new_cookie_hdr[offset] = ';';
943 else if (strlen (cookie_domain) == strlen (ctask->leho))
945 if (0 == strcmp (cookie_domain, ctask->leho))
947 GNUNET_snprintf (new_cookie_hdr+offset,
948 sizeof (new_cookie_hdr),
949 " domain=%s", ctask->host);
950 offset += strlen (" domain=") + strlen (ctask->host);
951 new_cookie_hdr[offset] = ';';
956 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
957 "Cookie domain invalid\n");
961 memcpy (new_cookie_hdr+offset, tok, strlen (tok));
962 offset += strlen (tok);
963 new_cookie_hdr[offset] = ';';
969 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
970 "Got Set-Cookie HTTP header %s\n", new_cookie_hdr);
972 if (GNUNET_NO == MHD_add_response_header (ctask->response,
973 MHD_HTTP_HEADER_SET_COOKIE,
976 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
977 "MHD: Error adding set-cookie header field %s\n",
978 hdr_generic+cookie_hdr_len+1);
983 ndup = GNUNET_strdup (hdr_generic);
984 hdr_type = strtok (ndup, ":");
986 if (NULL == hdr_type)
992 hdr_val = strtok (NULL, "");
1002 if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
1004 if (ctask->mhd->is_ssl)
1006 sprintf (leho_host, "https://%s", ctask->leho);
1007 sprintf (real_host, "https://%s", ctask->host);
1011 sprintf (leho_host, "http://%s", ctask->leho);
1012 sprintf (real_host, "http://%s", ctask->host);
1015 if (0 == memcmp (leho_host, hdr_val, strlen (leho_host)))
1017 sprintf (new_location, "%s%s", real_host, hdr_val+strlen (leho_host));
1018 hdr_val = new_location;
1022 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1023 "Trying to set %s: %s\n",
1026 if (GNUNET_NO == MHD_add_response_header (ctask->response,
1030 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1031 "MHD: Error adding %s header field %s\n",
1043 * @param hd a http daemon list entry
1046 run_httpd (struct MhdHttpList *hd);
1058 * Task run whenever HTTP server operations are pending.
1061 * @param tc sched context
1064 do_httpd (void *cls,
1065 const struct GNUNET_SCHEDULER_TaskContext *tc);
1069 run_mhd_now (struct MhdHttpList *hd)
1071 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
1073 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1074 "MHD: killing old task\n");
1075 GNUNET_SCHEDULER_cancel (hd->httpd_task);
1077 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1078 "MHD: Scheduling MHD now\n");
1079 hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, hd);
1084 * Ask cURL for the select sets and schedule download
1087 curl_download_prepare (void);
1091 * Callback to free content
1093 * @param cls content to free
1094 * @param tc task context
1097 mhd_content_free (void *cls,
1098 const struct GNUNET_SCHEDULER_TaskContext *tc)
1100 struct ProxyCurlTask *ctask = cls;
1101 struct ProxyUploadData *pdata;
1103 if (NULL != ctask->headers)
1104 curl_slist_free_all (ctask->headers);
1106 if (NULL != ctask->headers)
1107 curl_slist_free_all (ctask->resolver);
1109 if (NULL != ctask->response)
1110 MHD_destroy_response (ctask->response);
1112 if (NULL != ctask->post_handler)
1113 MHD_destroy_post_processor (ctask->post_handler);
1115 if (GNUNET_SCHEDULER_NO_TASK != ctask->pp_task)
1116 GNUNET_SCHEDULER_cancel (ctask->pp_task);
1118 for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
1120 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1121 ctask->upload_data_tail,
1123 GNUNET_free_non_null (pdata->filename);
1124 GNUNET_free_non_null (pdata->content_type);
1125 GNUNET_free_non_null (pdata->key);
1126 GNUNET_free_non_null (pdata->value);
1127 GNUNET_free (pdata);
1129 GNUNET_free (ctask);
1134 * Callback for MHD response
1136 * @param cls closure
1137 * @param pos in buffer
1139 * @param max space in buffer
1140 * @return number of bytes written
1143 mhd_content_cb (void *cls,
1148 struct ProxyCurlTask *ctask = cls;
1150 size_t bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1152 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1153 "MHD: content cb for %s. To copy: %u\n",
1154 ctask->url, (unsigned int) bytes_to_copy);
1155 if ((GNUNET_YES == ctask->download_is_finished) &&
1156 (GNUNET_NO == ctask->download_error) &&
1157 (0 == bytes_to_copy))
1159 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1160 "MHD: sending response for %s\n", ctask->url);
1161 ctask->download_in_progress = GNUNET_NO;
1162 run_mhd_now (ctask->mhd);
1163 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1164 total_mhd_connections--;
1165 return MHD_CONTENT_READER_END_OF_STREAM;
1168 if ((GNUNET_YES == ctask->download_error) &&
1169 (GNUNET_YES == ctask->download_is_finished) &&
1170 (0 == bytes_to_copy))
1172 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1173 "MHD: sending error response\n");
1174 ctask->download_in_progress = GNUNET_NO;
1175 run_mhd_now (ctask->mhd);
1176 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1177 total_mhd_connections--;
1178 return MHD_CONTENT_READER_END_WITH_ERROR;
1181 if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
1185 bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1188 "MHD: copied: %d left: %u, space left in buf: %d\n",
1190 (unsigned int) bytes_to_copy, (int) (max - copied));
1192 if (GNUNET_NO == ctask->download_is_finished)
1194 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1195 "MHD: Purging buffer\n");
1196 memmove (ctask->buffer, ctask->buffer_read_ptr, bytes_to_copy);
1197 ctask->buffer_read_ptr = ctask->buffer;
1198 ctask->buffer_write_ptr = ctask->buffer + bytes_to_copy;
1199 ctask->buffer[bytes_to_copy] = '\0';
1202 if (bytes_to_copy + copied > max)
1203 bytes_to_copy = max - copied;
1204 memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1205 ctask->buffer_read_ptr += bytes_to_copy;
1206 copied += bytes_to_copy;
1207 ctask->buf_status = BUF_WAIT_FOR_CURL;
1209 if (NULL != ctask->curl)
1210 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
1212 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1213 "MHD: copied %d bytes\n", (int) copied);
1214 run_mhd_now (ctask->mhd);
1220 * Handle data from cURL
1222 * @param ptr pointer to the data
1223 * @param size number of blocks of data
1224 * @param nmemb blocksize
1225 * @param ctx the curlproxytask
1226 * @return number of bytes handled
1229 curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx)
1231 const char *cbuf = ptr;
1232 size_t total = size * nmemb;
1233 struct ProxyCurlTask *ctask = ctx;
1234 size_t buf_space = sizeof (ctask->buffer) - (ctask->buffer_write_ptr - ctask->buffer);
1236 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1237 "CURL: Got %d. %d free in buffer\n",
1242 ctask->buf_status = BUF_WAIT_FOR_MHD;
1243 run_mhd_now (ctask->mhd);
1244 return CURL_WRITEFUNC_PAUSE;
1246 if (total > buf_space)
1248 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1249 "CURL: Copying %d bytes to buffer (%s)\n",
1251 memcpy (ctask->buffer_write_ptr, cbuf, total);
1252 ctask->bytes_in_buffer += total;
1253 ctask->buffer_write_ptr += total;
1254 if (ctask->bytes_in_buffer > 0)
1256 ctask->buf_status = BUF_WAIT_FOR_MHD;
1257 run_mhd_now (ctask->mhd);
1264 * cURL callback for put data
1267 put_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1269 struct ProxyCurlTask *ctask = cls;
1270 struct ProxyUploadData *pdata = ctask->upload_data_head;
1271 size_t len = size * nmemb;
1275 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1276 "CURL: put read callback\n");
1279 return CURL_READFUNC_PAUSE;
1282 if (NULL == pdata->value)
1284 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1285 "CURL: Terminating PUT\n");
1287 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1288 ctask->upload_data_tail,
1290 GNUNET_free (pdata);
1294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1295 "CURL: read callback value %s\n", pdata->value);
1297 to_copy = pdata->bytes_left;
1301 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1302 memcpy (buf, pos, to_copy);
1303 pdata->bytes_left -= to_copy;
1304 if (pdata->bytes_left <= 0)
1306 GNUNET_free (pdata->value);
1307 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1308 ctask->upload_data_tail,
1310 GNUNET_free (pdata);
1317 * cURL callback for post data
1320 post_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1322 struct ProxyCurlTask *ctask = cls;
1323 struct ProxyUploadData *pdata = ctask->upload_data_head;
1324 size_t len = size * nmemb;
1328 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1329 "CURL: read callback\n");
1332 return CURL_READFUNC_PAUSE;
1335 if (NULL == pdata->value)
1337 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1338 "CURL: Terminating POST data\n");
1340 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1341 ctask->upload_data_tail,
1343 GNUNET_free (pdata);
1347 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1348 "CURL: read callback value %s\n", pdata->value);
1350 to_copy = pdata->bytes_left;
1354 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1355 memcpy (buf, pos, to_copy);
1356 pdata->bytes_left -= to_copy;
1357 if (pdata->bytes_left <= 0)
1359 GNUNET_free (pdata->value);
1360 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1361 ctask->upload_data_tail,
1363 GNUNET_free (pdata);
1370 * Task that is run when we are ready to receive more data
1373 * @param cls closure
1374 * @param tc task context
1377 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1381 * Ask cURL for the select sets and schedule download
1384 curl_download_prepare ()
1391 struct GNUNET_NETWORK_FDSet *grs;
1392 struct GNUNET_NETWORK_FDSet *gws;
1394 struct GNUNET_TIME_Relative rtime;
1400 if (CURLM_OK != (mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max)))
1402 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1403 "%s failed at %s:%d: `%s'\n",
1404 "curl_multi_fdset", __FILE__, __LINE__,
1405 curl_multi_strerror (mret));
1406 //TODO cleanup here?
1410 GNUNET_break (CURLM_OK == curl_multi_timeout (curl_multi, &to));
1411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1412 "cURL multi fds: max=%d timeout=%lld\n", max, (long long) to);
1414 rtime = GNUNET_TIME_UNIT_FOREVER_REL;
1416 rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1417 grs = GNUNET_NETWORK_fdset_create ();
1418 gws = GNUNET_NETWORK_fdset_create ();
1419 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1420 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1421 if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
1422 GNUNET_SCHEDULER_cancel (curl_download_task);
1425 curl_download_task =
1426 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1429 &curl_task_download, curl_multi);
1431 else if (NULL != ctasks_head)
1433 /* as specified in curl docs */
1434 curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
1435 &curl_task_download,
1438 GNUNET_NETWORK_fdset_destroy (gws);
1439 GNUNET_NETWORK_fdset_destroy (grs);
1444 * Task that is run when we are ready to receive more data
1447 * @param cls closure
1448 * @param tc task context
1451 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1455 struct CURLMsg *msg;
1457 struct ProxyCurlTask *ctask;
1460 struct ProxyCurlTask *clean_head = NULL;
1461 struct ProxyCurlTask *clean_tail = NULL;
1463 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1465 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1467 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1468 "Shutdown requested while trying to download\n");
1472 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1480 mret = curl_multi_perform (curl_multi, &running);
1482 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1483 "Running curl tasks: %d\n", running);
1484 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1486 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1487 "CTask: %s\n", ctask->url);
1494 msg = curl_multi_info_read (curl_multi, &msgnum);
1495 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1496 "Messages left: %d\n", msgnum);
1503 if ((msg->data.result != CURLE_OK) &&
1504 (msg->data.result != CURLE_GOT_NOTHING))
1506 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1507 "Download curl failed");
1509 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1511 if (NULL == ctask->curl)
1514 if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
1517 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1518 "CURL: Download failed for task %s: %s.\n",
1520 curl_easy_strerror (msg->data.result));
1521 ctask->download_is_finished = GNUNET_YES;
1522 ctask->download_error = GNUNET_YES;
1523 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1524 CURLINFO_RESPONSE_CODE,
1526 ctask->curl_response_code = resp_code;
1527 ctask->ready_to_queue = MHD_YES;
1528 ctask->buf_status = BUF_WAIT_FOR_MHD;
1529 run_mhd_now (ctask->mhd);
1531 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1533 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1536 GNUNET_assert (ctask != NULL);
1540 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1541 "CURL: download completed.\n");
1543 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1545 if (NULL == ctask->curl)
1548 if (0 != memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)))
1551 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1552 "CURL: completed task %s found.\n", ctask->url);
1553 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1554 CURLINFO_RESPONSE_CODE,
1556 ctask->curl_response_code = resp_code;
1559 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1560 "CURL: Completed ctask!\n");
1561 if (GNUNET_SCHEDULER_NO_TASK == ctask->pp_task)
1563 ctask->buf_status = BUF_WAIT_FOR_MHD;
1564 run_mhd_now (ctask->mhd);
1567 ctask->ready_to_queue = MHD_YES;
1568 ctask->download_is_finished = GNUNET_YES;
1570 /* We MUST not modify the multi handle else we loose messages */
1571 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1573 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1577 GNUNET_assert (ctask != NULL);
1579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1580 "CURL: %s\n", curl_easy_strerror(msg->data.result));
1586 } while (msgnum > 0);
1588 for (ctask=clean_head; NULL != ctask; ctask = ctask->next)
1590 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1591 "CURL: Removing task %s.\n", ctask->url);
1592 curl_multi_remove_handle (curl_multi, ctask->curl);
1593 curl_easy_cleanup (ctask->curl);
1598 for (ctask=ctasks_head; NULL != ctask; ctask = ctask->next)
1600 GNUNET_assert (num_ctasks == running);
1602 } while (mret == CURLM_CALL_MULTI_PERFORM);
1604 if (mret != CURLM_OK)
1606 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CURL: %s failed at %s:%d: `%s'\n",
1607 "curl_multi_perform", __FILE__, __LINE__,
1608 curl_multi_strerror (mret));
1610 curl_download_prepare();
1615 * Process LEHO lookup
1617 * @param cls the ctask
1618 * @param rd_count number of records returned
1619 * @param rd record data
1622 process_leho_lookup (void *cls,
1624 const struct GNUNET_NAMESTORE_RecordData *rd)
1626 struct ProxyCurlTask *ctask = cls;
1627 char hosthdr[262]; //256 + "Host: "
1631 struct hostent *phost;
1633 char resolvename[512];
1636 strcpy (ctask->leho, "");
1639 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1640 "No LEHO present!\n");
1642 for (i=0; i<rd_count; i++)
1644 if (rd[i].record_type != GNUNET_NAMESTORE_TYPE_LEHO)
1647 memcpy (ctask->leho, rd[i].data, rd[i].data_size);
1649 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1650 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
1653 if (0 != strcmp (ctask->leho, ""))
1655 sprintf (hosthdr, "%s%s:%d", "Host: ", ctask->leho, ctask->port);
1656 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1657 "New HTTP header value: %s\n", hosthdr);
1658 ctask->headers = curl_slist_append (ctask->headers, hosthdr);
1659 GNUNET_assert (NULL != ctask->headers);
1660 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers)))
1661 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1664 if (ctask->mhd->is_ssl)
1666 phost = (struct hostent*)gethostbyname (ctask->host);
1670 ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
1671 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1672 "SSL target server: %s\n", ssl_ip);
1673 sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
1674 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1675 "Curl resolve: %s\n", resolvename);
1676 ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
1677 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver)))
1678 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1679 sprintf (curlurl, "https://%s:%d%s", ctask->leho, ctask->port, ctask->url);
1680 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl)))
1681 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1685 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1686 "gethostbyname failed for %s!\n",
1688 ctask->download_is_finished = GNUNET_YES;
1689 ctask->download_error = GNUNET_YES;
1694 if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
1696 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1697 "%s failed at %s:%d: `%s'\n",
1698 "curl_multi_add_handle", __FILE__, __LINE__,
1699 curl_multi_strerror (mret));
1700 ctask->download_is_finished = GNUNET_YES;
1701 ctask->download_error = GNUNET_YES;
1704 GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1706 curl_download_prepare ();
1711 * Initialize download and trigger curl
1713 * @param cls the proxycurltask
1714 * @param auth_name the name of the authority (site of origin) of ctask->host
1717 process_get_authority (void *cls,
1718 const char* auth_name)
1720 struct ProxyCurlTask *ctask = cls;
1722 if (NULL == auth_name)
1724 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1725 "Get authority failed!\n");
1726 strcpy (ctask->authority, "");
1730 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1731 "Get authority yielded %s\n", auth_name);
1732 strcpy (ctask->authority, auth_name);
1735 GNUNET_GNS_lookup (gns_handle,
1738 GNUNET_NAMESTORE_TYPE_LEHO,
1739 GNUNET_YES /* Only cached for performance */,
1741 &process_leho_lookup,
1747 mhd_log_callback (void* cls,
1750 struct ProxyCurlTask *ctask;
1752 ctask = GNUNET_new (struct ProxyCurlTask);
1753 strcpy (ctask->url, url);
1759 * Main MHD callback for handling requests.
1762 * @param con MHD connection handle
1763 * @param url the url in the request
1764 * @param meth the HTTP method used ("GET", "PUT", etc.)
1765 * @param ver the HTTP version string (i.e. "HTTP/1.1")
1766 * @param upload_data the data being uploaded (excluding HEADERS,
1767 * for a POST that fits into memory and that is encoded
1768 * with a supported encoding, the POST data will NOT be
1769 * given in upload_data and is instead available as
1770 * part of MHD_get_connection_values; very large POST
1771 * data *will* be made available incrementally in
1773 * @param upload_data_size set initially to the size of the
1774 * @a upload_data provided; the method must update this
1775 * value to the number of bytes NOT processed;
1776 * @param con_cls pointer to location where we store the 'struct Request'
1777 * @return #MHD_YES if the connection was handled successfully,
1778 * #MHD_NO if the socket must be closed due to a serious
1779 * error while handling the request
1782 create_response (void *cls,
1783 struct MHD_Connection *con,
1787 const char *upload_data,
1788 size_t *upload_data_size,
1791 struct MhdHttpList* hd = cls;
1792 char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow!
1795 struct ProxyCurlTask *ctask = *con_cls;
1796 struct ProxyUploadData *fin_post;
1797 struct curl_forms forms[5];
1798 struct ProxyUploadData *upload_data_iter;
1801 if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) &&
1802 (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) &&
1803 (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)) &&
1804 (0 != strcasecmp (meth, MHD_HTTP_METHOD_HEAD)))
1806 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1807 "MHD: %s NOT IMPLEMENTED!\n", meth);
1812 if (GNUNET_NO == ctask->accepted)
1815 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1816 "Got %s request for %s\n", meth, url);
1818 ctask->curl = curl_easy_init();
1819 ctask->curl_running = GNUNET_NO;
1820 if (NULL == ctask->curl)
1822 ret = MHD_queue_response (con,
1824 curl_failure_response);
1825 GNUNET_free (ctask);
1829 if (ctask->mhd->is_ssl)
1830 ctask->port = HTTPS_PORT;
1832 ctask->port = HTTP_PORT;
1834 MHD_get_connection_values (con,
1836 &con_val_iter, ctask);
1838 curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1839 curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
1840 curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1841 curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
1842 curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0);
1843 curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1845 if (GNUNET_NO == ctask->mhd->is_ssl)
1847 sprintf (curlurl, "http://%s:%d%s", ctask->host, ctask->port, ctask->url);
1848 curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1852 curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
1853 curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
1854 curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
1856 /* Add GNS header */
1857 ctask->headers = curl_slist_append (ctask->headers,
1859 ctask->accepted = GNUNET_YES;
1860 ctask->download_in_progress = GNUNET_YES;
1861 ctask->buf_status = BUF_WAIT_FOR_CURL;
1862 ctask->connection = con;
1863 ctask->curl_response_code = MHD_HTTP_OK;
1864 ctask->buffer_read_ptr = ctask->buffer;
1865 ctask->buffer_write_ptr = ctask->buffer;
1866 ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
1869 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
1871 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1872 "Setting up PUT\n");
1874 curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1);
1875 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
1876 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &put_read_callback);
1877 ctask->headers = curl_slist_append (ctask->headers,
1878 "Transfer-Encoding: chunked");
1881 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
1883 //FIXME handle multipart
1884 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1885 "Setting up POST processor\n");
1886 ctask->post_handler = MHD_create_post_processor (con,
1888 &con_post_data_iter,
1890 ctask->headers = curl_slist_append (ctask->headers,
1891 "Transfer-Encoding: chunked");
1895 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
1897 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1898 "Setting NOBODY\n");
1899 curl_easy_setopt (ctask->curl, CURLOPT_NOBODY, 1);
1903 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1904 "MHD: Adding new curl task for %s\n", ctask->host);
1906 GNUNET_GNS_get_authority (gns_handle,
1908 &process_get_authority,
1910 ctask->ready_to_queue = GNUNET_NO;
1911 ctask->fin = GNUNET_NO;
1912 ctask->curl_running = GNUNET_YES;
1916 ctask = (struct ProxyCurlTask *) *con_cls;
1917 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
1919 if (0 != *upload_data_size)
1922 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1923 "Invoking POST processor\n");
1924 MHD_post_process (ctask->post_handler,
1925 upload_data, *upload_data_size);
1926 *upload_data_size = 0;
1927 if ((GNUNET_NO == ctask->is_httppost) &&
1928 (GNUNET_NO == ctask->curl_running))
1930 curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
1931 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION,
1932 &post_read_callback);
1933 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
1935 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1936 "MHD: Adding new curl task for %s\n", ctask->host);
1938 GNUNET_GNS_get_authority (gns_handle,
1940 &process_get_authority,
1942 ctask->ready_to_queue = GNUNET_NO;
1943 ctask->fin = GNUNET_NO;
1944 ctask->curl_running = GNUNET_YES;
1948 else if (GNUNET_NO == ctask->post_done)
1950 if (GNUNET_YES == ctask->is_httppost)
1952 for (upload_data_iter = ctask->upload_data_head;
1953 NULL != upload_data_iter;
1954 upload_data_iter = upload_data_iter->next)
1957 if (NULL != upload_data_iter->filename)
1959 forms[i].option = CURLFORM_FILENAME;
1960 forms[i].value = upload_data_iter->filename;
1961 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1962 "Adding filename %s\n",
1966 if (NULL != upload_data_iter->content_type)
1968 forms[i].option = CURLFORM_CONTENTTYPE;
1969 forms[i].value = upload_data_iter->content_type;
1970 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1971 "Adding content type %s\n",
1975 forms[i].option = CURLFORM_PTRCONTENTS;
1976 forms[i].value = upload_data_iter->value;
1977 forms[i+1].option = CURLFORM_END;
1979 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1980 "Adding formdata for %s (len=%lld)\n",
1981 upload_data_iter->key,
1982 upload_data_iter->total_bytes);
1984 curl_formadd(&ctask->httppost, &ctask->httppost_last,
1985 CURLFORM_COPYNAME, upload_data_iter->key,
1986 CURLFORM_CONTENTSLENGTH, upload_data_iter->total_bytes,
1987 CURLFORM_ARRAY, forms,
1990 curl_easy_setopt (ctask->curl, CURLOPT_HTTPPOST,
1993 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1994 "MHD: Adding new curl task for %s\n", ctask->host);
1996 GNUNET_GNS_get_authority (gns_handle,
1998 &process_get_authority,
2000 ctask->ready_to_queue = GNUNET_YES;
2001 ctask->fin = GNUNET_NO;
2002 ctask->curl_running = GNUNET_YES;
2003 ctask->post_done = GNUNET_YES;
2007 fin_post = GNUNET_new (struct ProxyUploadData);
2008 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
2009 ctask->upload_data_tail,
2011 ctask->post_done = GNUNET_YES;
2016 if (GNUNET_YES != ctask->ready_to_queue)
2017 return MHD_YES; /* wait longer */
2019 if (GNUNET_YES == ctask->fin)
2022 ctask->fin = GNUNET_YES;
2023 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2024 "MHD: Queueing response for %s\n", ctask->url);
2025 ret = MHD_queue_response (con, ctask->curl_response_code, ctask->response);
2026 run_mhd_now (ctask->mhd);
2037 struct MhdHttpList *hd;
2039 for (hd=mhd_httpd_head; NULL != hd; hd = hd->next)
2048 * @param hd the daemon to run
2051 run_httpd (struct MhdHttpList *hd)
2056 struct GNUNET_NETWORK_FDSet *wrs;
2057 struct GNUNET_NETWORK_FDSet *wws;
2058 struct GNUNET_NETWORK_FDSet *wes;
2061 MHD_UNSIGNED_LONG_LONG timeout;
2062 struct GNUNET_TIME_Relative tv;
2067 wrs = GNUNET_NETWORK_fdset_create ();
2068 wes = GNUNET_NETWORK_fdset_create ();
2069 wws = GNUNET_NETWORK_fdset_create ();
2071 GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
2074 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2075 "MHD fds: max=%d\n", max);
2077 haveto = MHD_get_timeout (hd->daemon, &timeout);
2079 if (MHD_YES == haveto)
2080 tv.rel_value_us = (uint64_t) timeout * 1000LL;
2082 tv = GNUNET_TIME_UNIT_FOREVER_REL;
2083 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
2084 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
2085 GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
2087 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2088 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2090 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
2093 GNUNET_NETWORK_fdset_destroy (wrs);
2094 GNUNET_NETWORK_fdset_destroy (wws);
2095 GNUNET_NETWORK_fdset_destroy (wes);
2100 * Task run whenever HTTP server operations are pending.
2103 * @param tc sched context
2106 do_httpd (void *cls,
2107 const struct GNUNET_SCHEDULER_TaskContext *tc)
2109 struct MhdHttpList *hd = cls;
2111 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2112 "MHD: Main loop\n");
2113 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2114 MHD_run (hd->daemon);
2120 * Read data from socket
2122 * @param cls the closure
2123 * @param tc scheduler context
2126 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2130 * Read from remote end
2132 * @param cls closure
2133 * @param tc scheduler context
2136 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2140 * Write data to remote socket
2142 * @param cls the closure
2143 * @param tc scheduler context
2146 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2148 struct Socks5Request *s5r = cls;
2151 s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2153 if ((NULL != tc->read_ready) &&
2154 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
2155 ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
2158 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2159 "Successfully sent %d bytes to remote socket\n",
2164 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
2165 if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2166 GNUNET_SCHEDULER_cancel (s5r->rtask);
2167 if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
2168 GNUNET_SCHEDULER_cancel (s5r->wtask);
2169 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2170 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2171 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2172 GNUNET_NETWORK_socket_close (s5r->sock);
2178 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2185 * Clean up s5r handles
2187 * @param s5r the handle to destroy
2190 cleanup_s5r (struct Socks5Request *s5r)
2192 if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2193 GNUNET_SCHEDULER_cancel (s5r->rtask);
2194 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdwtask)
2195 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2196 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2197 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2198 if (NULL != s5r->remote_sock)
2199 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2200 if ((NULL != s5r->sock) && (s5r->cleanup_sock == GNUNET_YES))
2201 GNUNET_NETWORK_socket_close (s5r->sock);
2208 * Write data to socket
2210 * @param cls the closure
2211 * @param tc scheduler context
2214 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2216 struct Socks5Request *s5r = cls;
2219 s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2221 if ((NULL != tc->read_ready) &&
2222 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
2223 ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
2226 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2227 "Successfully sent %d bytes to socket\n",
2232 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
2233 s5r->cleanup = GNUNET_YES;
2234 s5r->cleanup_sock = GNUNET_YES;
2239 if (GNUNET_YES == s5r->cleanup)
2245 if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
2246 (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
2248 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2250 &do_read_remote, s5r);
2255 * Read from remote end
2257 * @param cls closure
2258 * @param tc scheduler context
2261 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2263 struct Socks5Request *s5r = cls;
2265 s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2266 if ((NULL != tc->write_ready) &&
2267 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
2268 (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
2269 sizeof (s5r->wbuf))))
2271 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2272 "Successfully read %d bytes from remote socket\n",
2277 if (0 == s5r->wbuf_len)
2278 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2279 "0 bytes received from remote... graceful shutdown!\n");
2280 if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2281 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2282 if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
2283 GNUNET_SCHEDULER_cancel (s5r->rtask);
2285 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2286 s5r->remote_sock = NULL;
2287 GNUNET_NETWORK_socket_close (s5r->sock);
2293 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2300 * Adds a socket to MHD
2302 * @param h the handle to the socket to add
2303 * @param daemon the daemon to add the fd to
2304 * @return whatever MHD_add_connection returns
2307 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
2310 struct sockaddr *addr;
2313 fd = dup (GNUNET_NETWORK_get_fd (h));
2314 addr = GNUNET_NETWORK_get_addr (h);
2315 len = GNUNET_NETWORK_get_addrlen (h);
2317 return MHD_add_connection (daemon, fd, addr, len);
2322 * Read file in filename
2324 * @param filename file to read
2325 * @param size pointer where filesize is stored
2326 * @return NULL on error
2329 load_file (const char* filename,
2336 GNUNET_DISK_file_size (filename, &fsize,
2337 GNUNET_YES, GNUNET_YES))
2339 if (fsize > MAX_PEM_SIZE)
2341 *size = (unsigned int) fsize;
2342 buffer = GNUNET_malloc (*size);
2343 if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize))
2345 GNUNET_free (buffer);
2353 * Load PEM key from file
2355 * @param key where to store the data
2356 * @param keyfile path to the PEM file
2357 * @return GNUNET_OK on success
2360 load_key_from_file (gnutls_x509_privkey_t key, const char* keyfile)
2362 gnutls_datum_t key_data;
2365 key_data.data = load_file (keyfile, &key_data.size);
2366 ret = gnutls_x509_privkey_import (key, &key_data,
2367 GNUTLS_X509_FMT_PEM);
2368 if (GNUTLS_E_SUCCESS != ret)
2370 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2371 _("Unable to import private key from file `%s'\n"),
2375 GNUNET_free (key_data.data);
2376 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2381 * Load cert from file
2383 * @param crt struct to store data in
2384 * @param certfile path to pem file
2385 * @return #GNUNET_OK on success
2388 load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
2390 gnutls_datum_t cert_data;
2393 cert_data.data = load_file (certfile, &cert_data.size);
2394 ret = gnutls_x509_crt_import (crt, &cert_data,
2395 GNUTLS_X509_FMT_PEM);
2396 if (GNUTLS_E_SUCCESS != ret)
2398 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2399 _("Unable to import certificate %s\n"), certfile);
2402 GNUNET_free (cert_data.data);
2403 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2408 * Generate new certificate for specific name
2410 * @param name the subject name to generate a cert for
2411 * @return a struct holding the PEM data
2413 static struct ProxyGNSCertificate *
2414 generate_gns_certificate (const char *name)
2417 unsigned int serial;
2418 size_t key_buf_size;
2419 size_t cert_buf_size;
2420 gnutls_x509_crt_t request;
2424 ret = gnutls_x509_crt_init (&request);
2426 if (GNUTLS_E_SUCCESS != ret)
2431 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
2433 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
2435 struct ProxyGNSCertificate *pgc =
2436 GNUNET_new (struct ProxyGNSCertificate);
2438 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
2440 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
2442 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
2444 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
2445 0, name, strlen (name));
2446 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_version (request, 3));
2448 ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
2450 etime = time (NULL);
2451 tm_data = localtime (&etime);
2453 ret = gnutls_x509_crt_set_serial (request,
2457 ret = gnutls_x509_crt_set_activation_time (request,
2460 etime = mktime (tm_data);
2467 ret = gnutls_x509_crt_set_expiration_time (request,
2469 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
2471 ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
2473 key_buf_size = sizeof (pgc->key);
2474 cert_buf_size = sizeof (pgc->cert);
2476 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exporting certificate...\n");
2478 gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
2479 pgc->cert, &cert_buf_size);
2481 gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
2482 pgc->key, &key_buf_size);
2485 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
2486 gnutls_x509_crt_deinit (request);
2494 * Accept policy for mhdaemons
2497 * @param addr the sockaddr
2498 * @param addrlen the sockaddr length
2499 * @return MHD_NO if sockaddr is wrong or number of connections is too high
2502 accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen)
2504 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2505 "In MHD accept policy cb\n");
2509 if (addr->sa_family == AF_UNIX)
2513 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2514 "Connection accepted\n");
2521 * Adds a socket to an SSL MHD instance
2522 * It is important the the domain name is
2523 * correct. In most cases we need to start a new daemon
2525 * @param h the handle to add to a daemon
2526 * @param domain the domain the ssl daemon has to serve
2527 * @return #MHD_YES on success
2530 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, const char* domain)
2532 struct MhdHttpList *hd;
2533 struct ProxyGNSCertificate *pgc;
2534 struct NetworkHandleList *nh;
2536 for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2537 if (0 == strcmp (hd->domain, domain))
2542 pgc = generate_gns_certificate (domain);
2544 hd = GNUNET_new (struct MhdHttpList);
2545 hd->is_ssl = GNUNET_YES;
2546 strcpy (hd->domain, domain);
2547 hd->proxy_cert = pgc;
2550 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2551 "No previous SSL instance found... starting new one for %s\n",
2553 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET,
2556 &create_response, hd,
2557 MHD_OPTION_CONNECTION_LIMIT,
2558 MHD_MAX_CONNECTIONS,
2559 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2560 MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2561 MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2562 MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2563 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2566 GNUNET_assert (hd->daemon != NULL);
2567 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2569 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2572 nh = GNUNET_new (struct NetworkHandleList);
2575 GNUNET_CONTAINER_DLL_insert (hd->socket_handles_head,
2576 hd->socket_handles_tail,
2579 return add_handle_to_mhd (h, hd->daemon);
2584 * Read data from incoming connection
2586 * @param cls the closure
2587 * @param tc the scheduler context
2590 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2592 struct Socks5Request *s5r = cls;
2593 struct socks5_client_hello *c_hello;
2594 struct socks5_server_hello *s_hello;
2595 struct socks5_client_request *c_req;
2596 struct socks5_server_response *s_resp;
2601 struct hostent *phost;
2603 struct sockaddr_in remote_addr;
2604 struct in_addr *r_sin_addr;
2605 struct NetworkHandleList *nh;
2607 s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
2609 if ((NULL != tc->write_ready) &&
2610 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
2611 (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
2612 sizeof (s5r->rbuf))))
2614 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2615 "Successfully read %d bytes from socket\n",
2620 if (s5r->rbuf_len != 0)
2621 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
2623 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
2625 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2626 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2627 if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
2628 GNUNET_SCHEDULER_cancel (s5r->wtask);
2629 if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2630 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2631 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2632 GNUNET_NETWORK_socket_close (s5r->sock);
2637 if (s5r->state == SOCKS5_INIT)
2639 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2641 c_hello = (struct socks5_client_hello*)&s5r->rbuf;
2643 GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
2645 s_hello = (struct socks5_server_hello*)&s5r->wbuf;
2646 s5r->wbuf_len = sizeof( struct socks5_server_hello );
2648 s_hello->version = c_hello->version;
2649 s_hello->auth_method = SOCKS_AUTH_NONE;
2651 /* Write response to client */
2652 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2656 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2660 s5r->state = SOCKS5_REQUEST;
2664 if (s5r->state == SOCKS5_REQUEST)
2666 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2667 "Processing SOCKS5 request\n");
2668 c_req = (struct socks5_client_request*)&s5r->rbuf;
2669 s_resp = (struct socks5_server_response*)&s5r->wbuf;
2670 //Only 10byte for ipv4 response!
2671 s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
2673 GNUNET_assert (c_req->addr_type == 3);
2675 dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
2676 memset(domain, 0, sizeof(domain));
2677 strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
2678 req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
2680 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2681 "Requested connection is %s:%d\n",
2685 if (is_tld (domain, GNUNET_GNS_TLD) ||
2686 is_tld (domain, GNUNET_GNS_TLD_ZKEY))
2688 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2689 "Requested connection is gnunet tld\n",
2693 if (ntohs(req_port) == HTTPS_PORT)
2695 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2696 "Requested connection is HTTPS\n");
2697 ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
2699 else if (NULL != httpd)
2701 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2702 "Requested connection is HTTP\n");
2703 nh = GNUNET_new (struct NetworkHandleList);
2706 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head->socket_handles_head,
2707 mhd_httpd_head->socket_handles_tail,
2710 ret = add_handle_to_mhd ( s5r->sock, httpd );
2715 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2716 _("Failed to start HTTP server\n"));
2717 s_resp->version = 0x05;
2718 s_resp->reply = 0x01;
2719 s5r->cleanup = GNUNET_YES;
2720 s5r->cleanup_sock = GNUNET_YES;
2722 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2728 /* Signal success */
2729 s_resp->version = 0x05;
2730 s_resp->reply = 0x00;
2731 s_resp->reserved = 0x00;
2732 s_resp->addr_type = 0x01;
2734 s5r->cleanup = GNUNET_YES;
2735 s5r->cleanup_sock = GNUNET_NO;
2737 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2745 phost = (struct hostent*)gethostbyname (domain);
2748 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2749 "Resolve %s error!\n", domain );
2750 s_resp->version = 0x05;
2751 s_resp->reply = 0x01;
2752 s5r->cleanup = GNUNET_YES;
2753 s5r->cleanup_sock = GNUNET_YES;
2755 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2761 s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
2764 r_sin_addr = (struct in_addr*)(phost->h_addr);
2765 remote_ip = r_sin_addr->s_addr;
2766 memset(&remote_addr, 0, sizeof(remote_addr));
2767 remote_addr.sin_family = AF_INET;
2768 #if HAVE_SOCKADDR_IN_SIN_LEN
2769 remote_addr.sin_len = sizeof (remote_addr);
2771 remote_addr.sin_addr.s_addr = remote_ip;
2772 remote_addr.sin_port = req_port;
2774 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2775 "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
2779 GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
2780 (const struct sockaddr*)&remote_addr,
2781 sizeof (remote_addr)))
2782 && (errno != EINPROGRESS))
2784 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
2785 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2786 "socket request error...\n");
2787 s_resp->version = 0x05;
2788 s_resp->reply = 0x01;
2790 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2797 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2798 "new remote connection\n");
2800 s_resp->version = 0x05;
2801 s_resp->reply = 0x00;
2802 s_resp->reserved = 0x00;
2803 s_resp->addr_type = 0x01;
2805 s5r->state = SOCKS5_DATA_TRANSFER;
2808 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2812 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2820 if (s5r->state == SOCKS5_DATA_TRANSFER)
2822 if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
2824 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2825 "Closing connection to client\n");
2826 if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
2827 GNUNET_SCHEDULER_cancel (s5r->rtask);
2828 if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2829 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2830 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2831 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2832 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2833 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2835 if (s5r->remote_sock != NULL)
2836 GNUNET_NETWORK_socket_close (s5r->remote_sock);
2837 GNUNET_NETWORK_socket_close (s5r->sock);
2842 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2843 "forwarding %d bytes from client\n", s5r->rbuf_len);
2846 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2848 &do_write_remote, s5r);
2850 if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
2853 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2855 &do_read_remote, s5r);
2862 * Accept new incoming connections
2864 * @param cls the closure
2865 * @param tc the scheduler context
2868 do_accept (void *cls,
2869 const struct GNUNET_SCHEDULER_TaskContext *tc)
2871 struct GNUNET_NETWORK_Handle *s;
2872 struct Socks5Request *s5r;
2874 ltask = GNUNET_SCHEDULER_NO_TASK;
2875 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
2877 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2880 s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
2883 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
2886 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2887 "Got an inbound connection, waiting for data\n");
2888 s5r = GNUNET_new (struct Socks5Request);
2890 s5r->state = SOCKS5_INIT;
2891 s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2892 s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2893 s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2894 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2901 * Task run on shutdown
2903 * @param cls closure
2904 * @param tc task context
2907 do_shutdown (void *cls,
2908 const struct GNUNET_SCHEDULER_TaskContext *tc)
2910 struct MhdHttpList *hd;
2911 struct MhdHttpList *tmp_hd;
2912 struct NetworkHandleList *nh;
2913 struct NetworkHandleList *tmp_nh;
2914 struct ProxyCurlTask *ctask;
2915 struct ProxyCurlTask *ctask_tmp;
2916 struct ProxyUploadData *pdata;
2918 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2919 "Shutting down...\n");
2920 if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
2922 GNUNET_SCHEDULER_cancel (curl_download_task);
2923 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
2926 for (hd = mhd_httpd_head; hd != NULL; hd = tmp_hd)
2930 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2931 "Stopping daemon\n");
2933 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2935 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2936 "Stopping select task %d\n",
2938 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2939 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2941 if (NULL != hd->daemon)
2943 MHD_stop_daemon (hd->daemon);
2946 for (nh = hd->socket_handles_head; nh != NULL; nh = tmp_nh)
2950 GNUNET_NETWORK_socket_close (nh->h);
2955 if (NULL != hd->proxy_cert)
2957 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2958 "Free certificate\n");
2959 GNUNET_free (hd->proxy_cert);
2965 for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
2967 ctask_tmp = ctask->next;
2969 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2970 "Cleaning up cURL task\n");
2972 if (ctask->curl != NULL)
2973 curl_easy_cleanup (ctask->curl);
2975 if (NULL != ctask->headers)
2976 curl_slist_free_all (ctask->headers);
2977 if (NULL != ctask->resolver)
2978 curl_slist_free_all (ctask->resolver);
2980 if (NULL != ctask->response)
2981 MHD_destroy_response (ctask->response);
2983 pdata = ctask->upload_data_head;
2985 //FIXME free pdata here
2986 for (; pdata != NULL; pdata = ctask->upload_data_head)
2988 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
2989 ctask->upload_data_tail,
2991 GNUNET_free_non_null (pdata->filename);
2992 GNUNET_free_non_null (pdata->content_type);
2993 GNUNET_free_non_null (pdata->key);
2994 GNUNET_free_non_null (pdata->value);
2995 GNUNET_free (pdata);
2997 GNUNET_free (ctask);
3001 GNUNET_NETWORK_socket_close (lsock);
3006 GNUNET_IDENTITY_cancel (id_op);
3009 if (NULL != identity)
3011 GNUNET_IDENTITY_disconnect (identity);
3014 curl_multi_cleanup (curl_multi);
3015 GNUNET_GNS_disconnect (gns_handle);
3016 gnutls_global_deinit ();
3021 * Continue initialization after we have our zone information.
3026 struct MhdHttpList *hd;
3028 struct sockaddr_in sa;
3029 char* proxy_sockfile;
3030 struct sockaddr_un mhd_unix_sock_addr;
3032 memset (&sa, 0, sizeof (sa));
3033 sa.sin_family = AF_INET;
3034 sa.sin_port = htons (port);
3035 #if HAVE_SOCKADDR_IN_SIN_LEN
3036 sa.sin_len = sizeof (sa);
3039 lsock = GNUNET_NETWORK_socket_create (AF_INET,
3044 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
3045 GNUNET_SCHEDULER_shutdown ();
3049 GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
3052 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
3053 GNUNET_SCHEDULER_shutdown ();
3057 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
3059 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
3062 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3063 lsock, &do_accept, NULL);
3065 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
3067 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3068 "cURL global init failed!\n");
3069 GNUNET_SCHEDULER_shutdown ();
3073 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3074 "Proxy listens on port %u\n",
3076 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3080 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "gns-proxy", "PROXY_UNIXPATH");
3081 GNUNET_SCHEDULER_shutdown ();
3084 if (NULL == (mhd_unix_socket = GNUNET_NETWORK_socket_create (AF_UNIX,
3088 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3089 "Unable to create UNIX domain socket!\n");
3090 GNUNET_SCHEDULER_shutdown ();
3094 mhd_unix_sock_addr.sun_family = AF_UNIX;
3095 strcpy (mhd_unix_sock_addr.sun_path, proxy_sockfile);
3098 mhd_unix_sock_addr.sun_path[0] = '\0';
3100 #if HAVE_SOCKADDR_IN_SIN_LEN
3101 mhd_unix_sock_addr.sun_len = (u_char) sizeof (struct sockaddr_un);
3104 len = strlen (proxy_sockfile) + sizeof(AF_UNIX);
3105 GNUNET_free (proxy_sockfile);
3107 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (mhd_unix_socket,
3108 (struct sockaddr *) &mhd_unix_sock_addr,
3111 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
3112 GNUNET_SCHEDULER_shutdown ();
3116 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (mhd_unix_socket,
3119 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
3120 GNUNET_SCHEDULER_shutdown ();
3124 hd = GNUNET_new (struct MhdHttpList);
3125 hd->is_ssl = GNUNET_NO;
3126 strcpy (hd->domain, "");
3128 httpd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
3131 &create_response, hd,
3132 MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3133 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3134 MHD_OPTION_NOTIFY_COMPLETED,
3136 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3141 GNUNET_SCHEDULER_shutdown ();
3145 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
3146 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
3152 * Method called to inform about the egos of the shorten zone of this peer.
3154 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
3155 * this function is only called ONCE, and 'NULL' being passed in
3156 * @a ego does indicate an error (i.e. name is taken or no default
3157 * value is known). If @a ego is non-NULL and if '*ctx'
3158 * is set in those callbacks, the value WILL be passed to a subsequent
3159 * call to the identity callback of #GNUNET_IDENTITY_connect (if
3160 * that one was not NULL).
3162 * @param cls closure, NULL
3163 * @param ego ego handle
3164 * @param ctx context for application to store data for this ego
3165 * (during the lifetime of this process, initially NULL)
3166 * @param name name assigned by the user for this ego,
3167 * NULL if the user just deleted the ego and it
3168 * must thus no longer be used
3171 identity_shorten_cb (void *cls,
3172 struct GNUNET_IDENTITY_Ego *ego,
3179 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3180 _("No ego configured for `shorten-zone`\n"));
3184 local_shorten_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
3185 do_shorten = GNUNET_YES;
3192 * Method called to inform about the egos of the master zone of this peer.
3194 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
3195 * this function is only called ONCE, and 'NULL' being passed in
3196 * @a ego does indicate an error (i.e. name is taken or no default
3197 * value is known). If @a ego is non-NULL and if '*ctx'
3198 * is set in those callbacks, the value WILL be passed to a subsequent
3199 * call to the identity callback of #GNUNET_IDENTITY_connect (if
3200 * that one was not NULL).
3202 * @param cls closure, NULL
3203 * @param ego ego handle
3204 * @param ctx context for application to store data for this ego
3205 * (during the lifetime of this process, initially NULL)
3206 * @param name name assigned by the user for this ego,
3207 * NULL if the user just deleted the ego and it
3208 * must thus no longer be used
3211 identity_master_cb (void *cls,
3212 struct GNUNET_IDENTITY_Ego *ego,
3219 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3220 _("No ego configured for `master-zone`\n"));
3221 GNUNET_SCHEDULER_shutdown ();
3224 GNUNET_IDENTITY_ego_get_public_key (ego,
3226 id_op = GNUNET_IDENTITY_get (identity,
3228 &identity_shorten_cb,
3234 * Main function that will be run
3236 * @param cls closure
3237 * @param args remaining command-line arguments
3238 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3239 * @param c configuration
3242 run (void *cls, char *const *args, const char *cfgfile,
3243 const struct GNUNET_CONFIGURATION_Handle *c)
3245 char* cafile_cfg = NULL;
3249 if (NULL == (curl_multi = curl_multi_init ()))
3251 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3252 "Failed to create cURL multi handle!\n");
3255 cafile = cafile_opt;
3258 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3262 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
3267 cafile = cafile_cfg;
3269 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3270 "Using %s as CA\n", cafile);
3272 gnutls_global_init ();
3273 gnutls_x509_crt_init (&proxy_ca.cert);
3274 gnutls_x509_privkey_init (&proxy_ca.key);
3276 if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
3277 (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
3279 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3280 _("Failed to load SSL/TLS key and certificate from `%s'\n"),
3282 // FIXME: release resources...
3285 GNUNET_free_non_null (cafile_cfg);
3286 if (NULL == (gns_handle = GNUNET_GNS_connect (cfg)))
3288 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3289 "Unable to connect to GNS!\n");
3292 identity = GNUNET_IDENTITY_connect (cfg,
3294 id_op = GNUNET_IDENTITY_get (identity,
3296 &identity_master_cb,
3298 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
3299 &do_shutdown, NULL);
3304 * The main function for gnunet-gns-proxy.
3306 * @param argc number of arguments from the command line
3307 * @param argv command line arguments
3308 * @return 0 ok, 1 on error
3311 main (int argc, char *const *argv)
3313 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
3315 gettext_noop ("listen on specified port (default: 7777)"), 1,
3316 &GNUNET_GETOPT_set_ulong, &port},
3317 {'a', "authority", NULL,
3318 gettext_noop ("pem file to use as CA"), 1,
3319 &GNUNET_GETOPT_set_string, &cafile_opt},
3320 GNUNET_GETOPT_OPTION_END
3322 static const char* page =
3323 "<html><head><title>gnunet-gns-proxy</title>"
3324 "</head><body>cURL fail</body></html>";
3327 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
3329 GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
3330 curl_failure_response = MHD_create_response_from_buffer (strlen (page),
3332 MHD_RESPMEM_PERSISTENT);
3336 GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
3337 _("GNUnet GNS proxy"),
3339 &run, NULL)) ? 0 : 1;
3340 MHD_destroy_response (curl_failure_response);
3341 GNUNET_free_non_null ((char *) argv);
3345 /* end of gnunet-gns-proxy.c */