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
111 struct Socks5Request *next;
116 struct Socks5Request *prev;
121 struct GNUNET_NETWORK_Handle *sock;
126 struct GNUNET_NETWORK_Handle *remote_sock;
134 * Client socket read task
136 GNUNET_SCHEDULER_TaskIdentifier rtask;
139 * Server socket read task
141 GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
144 * Client socket write task
146 GNUNET_SCHEDULER_TaskIdentifier wtask;
149 * Server socket write task
151 GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
164 * Length of data in read buffer
166 unsigned int rbuf_len;
169 * Length of data in write buffer
171 unsigned int wbuf_len;
174 * This handle is scheduled for cleanup?
179 * Shall we close the client socket on cleanup?
186 * A structure for all running Httpds
193 struct MhdHttpList *prev;
198 struct MhdHttpList *next;
201 * is this an ssl daemon?
206 * the domain name to server (only important for SSL)
213 struct MHD_Daemon *daemon;
216 * Optional proxy certificate used
218 struct ProxyGNSCertificate *proxy_cert;
223 GNUNET_SCHEDULER_TaskIdentifier httpd_task;
229 * A structure for MHD<->cURL streams
236 struct ProxyCurlTask *prev;
241 struct ProxyCurlTask *next;
249 * Optional header replacements for curl (LEHO)
251 struct curl_slist *headers;
254 * Optional resolver replacements for curl (LEHO)
256 struct curl_slist *resolver;
261 long curl_response_code;
266 char url[MAX_HTTP_URI_LENGTH];
269 * The cURL write buffer / MHD read buffer
271 char buffer[CURL_MAX_WRITE_SIZE];
274 * Read pos of the data in the buffer
276 char *buffer_read_ptr;
279 * Write pos in the buffer
281 char *buffer_write_ptr;
286 struct MHD_Connection *connection;
291 size_t put_read_offset;
292 size_t put_read_size;
297 struct MHD_PostProcessor *post_handler;
300 struct ProxyUploadData *upload_data_head;
301 struct ProxyUploadData *upload_data_tail;
304 * the type of POST encoding
308 struct curl_httppost *httppost;
310 struct curl_httppost *httppost_last;
313 * Number of bytes in buffer
315 unsigned int bytes_in_buffer;
318 GNUNET_SCHEDULER_TaskIdentifier pp_task;
320 /* The associated daemon list entry */
321 struct MhdHttpList *mhd;
323 /* The associated response */
324 struct MHD_Response *response;
327 struct ProxySetCookieHeader *set_cookies_head;
330 struct ProxySetCookieHeader *set_cookies_tail;
332 /* The authority of the corresponding host (site of origin) */
335 /* The hostname (Host header field) */
338 /* The LEgacy HOstname (can be empty) */
347 * The buffer status (BUF_WAIT_FOR_CURL or BUF_WAIT_FOR_MHD)
349 enum BufferStatus buf_status;
372 * Indicates wheather the download is in progress
374 int download_in_progress;
377 * Indicates wheather the download was successful
379 int download_is_finished;
382 * Indicates wheather the download failed
394 * Struct for set-cookies
396 struct ProxySetCookieHeader
401 struct ProxySetCookieHeader *next;
406 struct ProxySetCookieHeader *prev;
416 * Post data structure
418 struct ProxyUploadData
423 struct ProxyUploadData *next;
428 struct ProxyUploadData *prev;
436 size_t content_length;
456 * The port the proxy is running on (default 7777)
458 static unsigned long port = GNUNET_GNS_PROXY_PORT;
461 * The CA file (pem) to use for the proxy CA
463 static char* cafile_opt;
466 * The listen socket of the proxy
468 static struct GNUNET_NETWORK_Handle *lsock;
473 static GNUNET_SCHEDULER_TaskIdentifier ltask;
476 * The cURL download task
478 static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
481 * Number of current mhd connections
483 static unsigned int total_mhd_connections;
486 * The cURL multi handle
488 static CURLM *curl_multi;
491 * Handle to the GNS service
493 static struct GNUNET_GNS_Handle *gns_handle;
496 * DLL for ProxyCurlTasks
498 static struct ProxyCurlTask *ctasks_head;
501 * DLL for ProxyCurlTasks
503 static struct ProxyCurlTask *ctasks_tail;
506 * DLL for http/https daemons
508 static struct MhdHttpList *mhd_httpd_head;
511 * DLL for http/https daemons
513 static struct MhdHttpList *mhd_httpd_tail;
516 * DLL of active socks requests.
518 static struct Socks5Request *s5r_head;
521 * DLL of active socks requests.
523 static struct Socks5Request *s5r_tail;
526 * The users local GNS master zone
528 static struct GNUNET_CRYPTO_EccPublicSignKey local_gns_zone;
531 * The users local shorten zone
533 static struct GNUNET_CRYPTO_EccPrivateKey local_shorten_zone;
536 * Is shortening enabled?
538 static int do_shorten;
541 * The CA for SSL certificate generation
543 static struct ProxyCA proxy_ca;
546 * Daemon for HTTP (we have one per SSL certificate, and then one for all HTTP connections;
547 * this is the one for HTTP, not HTTPS).
549 static struct MHD_Daemon *httpd;
552 * Shorten zone private key
554 static struct GNUNET_CRYPTO_EccPrivateKey shorten_zonekey;
557 * Response we return on cURL failures.
559 static struct MHD_Response *curl_failure_response;
562 * Connection to identity service.
564 static struct GNUNET_IDENTITY_Handle *identity;
567 * Request for our ego.
569 static struct GNUNET_IDENTITY_Operation *id_op;
574 static const struct GNUNET_CONFIGURATION_Handle *cfg;
577 * Clean up s5r handles
579 * @param s5r the handle to destroy
582 cleanup_s5r (struct Socks5Request *s5r)
584 if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
585 GNUNET_SCHEDULER_cancel (s5r->rtask);
586 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdwtask)
587 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
588 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
589 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
590 if (NULL != s5r->remote_sock)
591 GNUNET_NETWORK_socket_close (s5r->remote_sock);
592 if ( (NULL != s5r->sock) &&
593 (GNUNET_YES == s5r->cleanup_sock) )
594 GNUNET_NETWORK_socket_close (s5r->sock);
595 GNUNET_CONTAINER_DLL_remove (s5r_head,
603 * Checks if name is in tld
605 * @param name the name to check
606 * @param tld the TLD to check for (must NOT begin with ".")
607 * @return #GNUNET_YES or #GNUNET_NO
610 is_tld (const char* name, const char* tld)
612 size_t name_len = strlen (name);
613 size_t tld_len = strlen (tld);
615 GNUNET_break ('.' != tld[0]);
616 return ( (tld_len < name_len) &&
617 ( ('.' == name[name_len - tld_len - 1]) || (name_len == tld_len) ) &&
619 name + (name_len - tld_len),
625 con_post_data_iter (void *cls,
626 enum MHD_ValueKind kind,
628 const char *filename,
629 const char *content_type,
630 const char *transfer_encoding,
635 struct ProxyCurlTask* ctask = cls;
636 struct ProxyUploadData* pdata;
640 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
641 "Got POST data (file: %s, content type: %s): '%s=%.*s' at offset %llu size %llu\n",
642 filename, content_type,
643 key, (int) size, data,
644 (unsigned long long) off,
645 (unsigned long long) size);
646 GNUNET_assert (NULL != ctask->post_type);
648 if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
651 ctask->is_httppost = GNUNET_YES;
655 pdata = GNUNET_new (struct ProxyUploadData);
656 pdata->key = GNUNET_strdup (key);
658 if (NULL != filename)
659 pdata->filename = GNUNET_strdup (filename);
660 if (NULL != content_type)
661 pdata->content_type = GNUNET_strdup (content_type);
662 pdata->value = GNUNET_malloc (size);
663 pdata->total_bytes = size;
664 memcpy (pdata->value, data, size);
665 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
666 ctask->upload_data_tail,
669 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
670 "Copied %llu bytes of POST Data\n",
671 (unsigned long long) size);
675 pdata = ctask->upload_data_tail;
676 new_value = GNUNET_malloc (size + pdata->total_bytes);
677 memcpy (new_value, pdata->value, pdata->total_bytes);
678 memcpy (new_value+off, data, size);
679 GNUNET_free (pdata->value);
680 pdata->value = new_value;
681 pdata->total_bytes += size;
686 if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
692 ctask->is_httppost = GNUNET_NO;
694 if (NULL != ctask->curl)
695 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
699 enc = curl_easy_escape (ctask->curl, key, 0);
706 pdata = GNUNET_new (struct ProxyUploadData);
707 pdata->value = GNUNET_malloc (strlen (enc) + 3);
708 if (NULL != ctask->upload_data_head)
710 pdata->value[0] = '&';
711 memcpy (pdata->value+1, enc, strlen (enc));
714 memcpy (pdata->value, enc, strlen (enc));
715 pdata->value[strlen (pdata->value)] = '=';
716 pdata->bytes_left = strlen (pdata->value);
717 pdata->total_bytes = pdata->bytes_left;
720 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
721 "Escaped POST key: '%s'\n",
724 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
725 ctask->upload_data_tail,
730 enc = curl_easy_escape (ctask->curl, data, 0);
736 pdata = GNUNET_new (struct ProxyUploadData);
737 pdata->value = GNUNET_malloc (strlen (enc) + 1);
738 memcpy (pdata->value, enc, strlen (enc));
739 pdata->bytes_left = strlen (pdata->value);
740 pdata->total_bytes = pdata->bytes_left;
743 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
744 "Escaped POST value: '%s'\n",
747 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
748 ctask->upload_data_tail,
755 * Read HTTP request header field 'Host'
757 * @param cls buffer to write to
758 * @param kind value kind
759 * @param key field key
760 * @param value field value
761 * @return #MHD_NO when Host found
764 con_val_iter (void *cls,
765 enum MHD_ValueKind kind,
769 struct ProxyCurlTask *ctask = cls;
770 char* buf = ctask->host;
776 if (0 == strcmp ("Host", key))
778 port = strchr (value, ':');
781 strncpy (buf, value, port-value);
783 if ((1 != sscanf (port, "%u", &uport)) ||
784 (uport > UINT16_MAX) ||
786 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
787 "Unable to parse port!\n");
789 ctask->port = (uint16_t) uport;
796 if (0 == strcmp (MHD_HTTP_HEADER_ACCEPT_ENCODING, key))
801 if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_TYPE,
804 if (0 == strncasecmp (value,
805 MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
806 strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
807 ctask->post_type = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
808 else if (0 == strncasecmp (value,
809 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
810 strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
811 ctask->post_type = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
813 ctask->post_type = NULL;
817 cstr = GNUNET_malloc (strlen (key) + strlen (hdr_val) + 3);
818 GNUNET_snprintf (cstr, strlen (key) + strlen (hdr_val) + 3,
819 "%s: %s", key, hdr_val);
821 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
822 "Client Header: %s\n", cstr);
824 ctask->headers = curl_slist_append (ctask->headers, cstr);
832 * Callback for MHD response
835 * @param pos in buffer
837 * @param max space in buffer
838 * @return number of bytes written
841 mhd_content_cb (void *cls,
848 * Check HTTP response header for mime
850 * @param buffer curl buffer
851 * @param size curl blocksize
852 * @param nmemb curl blocknumber
854 * @return size of read bytes
857 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
859 size_t bytes = size * nmemb;
860 struct ProxyCurlTask *ctask = cls;
861 int cookie_hdr_len = strlen (MHD_HTTP_HEADER_SET_COOKIE);
862 char hdr_generic[bytes+1];
863 char new_cookie_hdr[bytes+strlen (ctask->leho)+1];
864 char new_location[MAX_HTTP_URI_LENGTH+500];
874 char cors_hdr[strlen (ctask->leho) + strlen ("https://")];
876 if (NULL == ctask->response)
878 /* FIXME: get total size from curl (if available) */
879 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
880 "Creating response for %s\n", ctask->url);
881 ctask->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
882 sizeof (ctask->buffer),
887 /* if we have a leho add a CORS header */
888 if (0 != strcmp ("", ctask->leho))
890 /* We could also allow ssl and http here */
891 if (ctask->mhd->is_ssl)
892 sprintf (cors_hdr, "https://%s", ctask->leho);
894 sprintf (cors_hdr, "http://%s", ctask->leho);
896 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
897 "MHD: Adding CORS header field %s\n",
900 if (GNUNET_NO == MHD_add_response_header (ctask->response,
901 "Access-Control-Allow-Origin",
904 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
905 "MHD: Error adding CORS header field %s\n",
909 ctask->ready_to_queue = GNUNET_YES;
911 if (cookie_hdr_len > bytes)
914 memcpy (hdr_generic, buffer, bytes);
915 hdr_generic[bytes] = '\0';
917 if ('\n' == hdr_generic[bytes-1])
918 hdr_generic[bytes-1] = '\0';
920 if (hdr_generic[bytes-2] == '\r')
921 hdr_generic[bytes-2] = '\0';
923 if (0 == memcmp (hdr_generic,
924 MHD_HTTP_HEADER_SET_COOKIE,
927 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
928 "Looking for cookie in: `%s'\n", hdr_generic);
929 ndup = GNUNET_strdup (hdr_generic+cookie_hdr_len+1);
930 memset (new_cookie_hdr, 0, sizeof (new_cookie_hdr));
931 for (tok = strtok (ndup, ";"); tok != NULL; tok = strtok (NULL, ";"))
933 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
934 "Got Cookie token: %s\n", tok);
935 //memcpy (new_cookie_hdr+offset, tok, strlen (tok));
936 if (0 == memcmp (tok, " domain", strlen (" domain")))
938 cookie_domain = tok + strlen (" domain") + 1;
940 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
941 "Got Set-Cookie Domain: %s\n", cookie_domain);
943 if (strlen (cookie_domain) < strlen (ctask->leho))
945 delta_cdomain = strlen (ctask->leho) - strlen (cookie_domain);
946 if (0 == strcmp (cookie_domain, ctask->leho + (delta_cdomain)))
948 GNUNET_snprintf (new_cookie_hdr+offset,
949 sizeof (new_cookie_hdr),
950 " domain=%s", ctask->authority);
951 offset += strlen (" domain=") + strlen (ctask->authority);
952 new_cookie_hdr[offset] = ';';
957 else if (strlen (cookie_domain) == strlen (ctask->leho))
959 if (0 == strcmp (cookie_domain, ctask->leho))
961 GNUNET_snprintf (new_cookie_hdr+offset,
962 sizeof (new_cookie_hdr),
963 " domain=%s", ctask->host);
964 offset += strlen (" domain=") + strlen (ctask->host);
965 new_cookie_hdr[offset] = ';';
970 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
971 "Cookie domain invalid\n");
975 memcpy (new_cookie_hdr+offset, tok, strlen (tok));
976 offset += strlen (tok);
977 new_cookie_hdr[offset] = ';';
983 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
984 "Got Set-Cookie HTTP header %s\n", new_cookie_hdr);
986 if (GNUNET_NO == MHD_add_response_header (ctask->response,
987 MHD_HTTP_HEADER_SET_COOKIE,
990 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
991 "MHD: Error adding set-cookie header field %s\n",
992 hdr_generic+cookie_hdr_len+1);
997 ndup = GNUNET_strdup (hdr_generic);
998 hdr_type = strtok (ndup, ":");
1000 if (NULL == hdr_type)
1006 hdr_val = strtok (NULL, "");
1008 if (NULL == hdr_val)
1016 if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
1018 if (ctask->mhd->is_ssl)
1020 sprintf (leho_host, "https://%s", ctask->leho);
1021 sprintf (real_host, "https://%s", ctask->host);
1025 sprintf (leho_host, "http://%s", ctask->leho);
1026 sprintf (real_host, "http://%s", ctask->host);
1029 if (0 == memcmp (leho_host, hdr_val, strlen (leho_host)))
1031 sprintf (new_location, "%s%s", real_host, hdr_val+strlen (leho_host));
1032 hdr_val = new_location;
1036 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1037 "Trying to set %s: %s\n",
1040 if (GNUNET_NO == MHD_add_response_header (ctask->response,
1044 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1045 "MHD: Error adding %s header field %s\n",
1057 * @param hd a http daemon list entry
1060 run_httpd (struct MhdHttpList *hd);
1072 * Task run whenever HTTP server operations are pending.
1075 * @param tc sched context
1078 do_httpd (void *cls,
1079 const struct GNUNET_SCHEDULER_TaskContext *tc);
1083 run_mhd_now (struct MhdHttpList *hd)
1085 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
1087 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1088 "MHD: killing old task\n");
1089 GNUNET_SCHEDULER_cancel (hd->httpd_task);
1091 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1092 "MHD: Scheduling MHD now\n");
1093 hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, hd);
1098 * Ask cURL for the select sets and schedule download
1101 curl_download_prepare (void);
1105 * Callback to free content
1107 * @param cls content to free
1108 * @param tc task context
1111 mhd_content_free (void *cls,
1112 const struct GNUNET_SCHEDULER_TaskContext *tc)
1114 struct ProxyCurlTask *ctask = cls;
1115 struct ProxyUploadData *pdata;
1117 if (NULL != ctask->headers)
1118 curl_slist_free_all (ctask->headers);
1120 if (NULL != ctask->headers)
1121 curl_slist_free_all (ctask->resolver);
1123 if (NULL != ctask->response)
1124 MHD_destroy_response (ctask->response);
1126 if (NULL != ctask->post_handler)
1127 MHD_destroy_post_processor (ctask->post_handler);
1129 if (GNUNET_SCHEDULER_NO_TASK != ctask->pp_task)
1130 GNUNET_SCHEDULER_cancel (ctask->pp_task);
1132 for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
1134 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1135 ctask->upload_data_tail,
1137 GNUNET_free_non_null (pdata->filename);
1138 GNUNET_free_non_null (pdata->content_type);
1139 GNUNET_free_non_null (pdata->key);
1140 GNUNET_free_non_null (pdata->value);
1141 GNUNET_free (pdata);
1143 GNUNET_free (ctask);
1148 * Callback for MHD response
1150 * @param cls closure
1151 * @param pos in buffer
1153 * @param max space in buffer
1154 * @return number of bytes written
1157 mhd_content_cb (void *cls,
1162 struct ProxyCurlTask *ctask = cls;
1164 size_t bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1166 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1167 "MHD: content cb for %s. To copy: %u\n",
1168 ctask->url, (unsigned int) bytes_to_copy);
1169 if ((GNUNET_YES == ctask->download_is_finished) &&
1170 (GNUNET_NO == ctask->download_error) &&
1171 (0 == bytes_to_copy))
1173 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1174 "MHD: sending response for %s\n", ctask->url);
1175 ctask->download_in_progress = GNUNET_NO;
1176 run_mhd_now (ctask->mhd);
1177 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1178 total_mhd_connections--;
1179 return MHD_CONTENT_READER_END_OF_STREAM;
1182 if ((GNUNET_YES == ctask->download_error) &&
1183 (GNUNET_YES == ctask->download_is_finished) &&
1184 (0 == bytes_to_copy))
1186 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1187 "MHD: sending error response\n");
1188 ctask->download_in_progress = GNUNET_NO;
1189 run_mhd_now (ctask->mhd);
1190 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1191 total_mhd_connections--;
1192 return MHD_CONTENT_READER_END_WITH_ERROR;
1195 if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
1199 bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1201 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1202 "MHD: copied: %d left: %u, space left in buf: %d\n",
1204 (unsigned int) bytes_to_copy, (int) (max - copied));
1206 if (GNUNET_NO == ctask->download_is_finished)
1208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1209 "MHD: Purging buffer\n");
1210 memmove (ctask->buffer, ctask->buffer_read_ptr, bytes_to_copy);
1211 ctask->buffer_read_ptr = ctask->buffer;
1212 ctask->buffer_write_ptr = ctask->buffer + bytes_to_copy;
1213 ctask->buffer[bytes_to_copy] = '\0';
1216 if (bytes_to_copy + copied > max)
1217 bytes_to_copy = max - copied;
1218 memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1219 ctask->buffer_read_ptr += bytes_to_copy;
1220 copied += bytes_to_copy;
1221 ctask->buf_status = BUF_WAIT_FOR_CURL;
1223 if (NULL != ctask->curl)
1224 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
1226 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1227 "MHD: copied %d bytes\n", (int) copied);
1228 run_mhd_now (ctask->mhd);
1234 * Handle data from cURL
1236 * @param ptr pointer to the data
1237 * @param size number of blocks of data
1238 * @param nmemb blocksize
1239 * @param ctx the curlproxytask
1240 * @return number of bytes handled
1243 curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx)
1245 const char *cbuf = ptr;
1246 size_t total = size * nmemb;
1247 struct ProxyCurlTask *ctask = ctx;
1248 size_t buf_space = sizeof (ctask->buffer) - (ctask->buffer_write_ptr - ctask->buffer);
1250 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1251 "CURL: Got %d. %d free in buffer\n",
1256 ctask->buf_status = BUF_WAIT_FOR_MHD;
1257 run_mhd_now (ctask->mhd);
1258 return CURL_WRITEFUNC_PAUSE;
1260 if (total > buf_space)
1262 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1263 "CURL: Copying %d bytes to buffer (%s)\n",
1265 memcpy (ctask->buffer_write_ptr, cbuf, total);
1266 ctask->bytes_in_buffer += total;
1267 ctask->buffer_write_ptr += total;
1268 if (ctask->bytes_in_buffer > 0)
1270 ctask->buf_status = BUF_WAIT_FOR_MHD;
1271 run_mhd_now (ctask->mhd);
1278 * cURL callback for put data
1281 put_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1283 struct ProxyCurlTask *ctask = cls;
1284 struct ProxyUploadData *pdata = ctask->upload_data_head;
1285 size_t len = size * nmemb;
1289 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1290 "CURL: put read callback\n");
1293 return CURL_READFUNC_PAUSE;
1296 if (NULL == pdata->value)
1298 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1299 "CURL: Terminating PUT\n");
1301 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1302 ctask->upload_data_tail,
1304 GNUNET_free (pdata);
1308 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1309 "CURL: read callback value %s\n", pdata->value);
1311 to_copy = pdata->bytes_left;
1315 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1316 memcpy (buf, pos, to_copy);
1317 pdata->bytes_left -= to_copy;
1318 if (pdata->bytes_left <= 0)
1320 GNUNET_free (pdata->value);
1321 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1322 ctask->upload_data_tail,
1324 GNUNET_free (pdata);
1331 * cURL callback for post data
1334 post_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1336 struct ProxyCurlTask *ctask = cls;
1337 struct ProxyUploadData *pdata = ctask->upload_data_head;
1338 size_t len = size * nmemb;
1342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1343 "CURL: read callback\n");
1346 return CURL_READFUNC_PAUSE;
1349 if (NULL == pdata->value)
1351 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1352 "CURL: Terminating POST data\n");
1354 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1355 ctask->upload_data_tail,
1357 GNUNET_free (pdata);
1361 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1362 "CURL: read callback value %s\n", pdata->value);
1364 to_copy = pdata->bytes_left;
1368 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1369 memcpy (buf, pos, to_copy);
1370 pdata->bytes_left -= to_copy;
1371 if (pdata->bytes_left <= 0)
1373 GNUNET_free (pdata->value);
1374 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1375 ctask->upload_data_tail,
1377 GNUNET_free (pdata);
1384 * Task that is run when we are ready to receive more data
1387 * @param cls closure
1388 * @param tc task context
1391 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1395 * Ask cURL for the select sets and schedule download
1398 curl_download_prepare ()
1405 struct GNUNET_NETWORK_FDSet *grs;
1406 struct GNUNET_NETWORK_FDSet *gws;
1408 struct GNUNET_TIME_Relative rtime;
1414 if (CURLM_OK != (mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max)))
1416 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1417 "%s failed at %s:%d: `%s'\n",
1418 "curl_multi_fdset", __FILE__, __LINE__,
1419 curl_multi_strerror (mret));
1420 //TODO cleanup here?
1424 GNUNET_break (CURLM_OK == curl_multi_timeout (curl_multi, &to));
1425 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1426 "cURL multi fds: max=%d timeout=%lld\n", max, (long long) to);
1428 rtime = GNUNET_TIME_UNIT_FOREVER_REL;
1430 rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1431 grs = GNUNET_NETWORK_fdset_create ();
1432 gws = GNUNET_NETWORK_fdset_create ();
1433 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1434 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1435 if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
1436 GNUNET_SCHEDULER_cancel (curl_download_task);
1439 curl_download_task =
1440 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1443 &curl_task_download, curl_multi);
1445 else if (NULL != ctasks_head)
1447 /* as specified in curl docs */
1448 curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
1449 &curl_task_download,
1452 GNUNET_NETWORK_fdset_destroy (gws);
1453 GNUNET_NETWORK_fdset_destroy (grs);
1458 * Task that is run when we are ready to receive more data
1461 * @param cls closure
1462 * @param tc task context
1465 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1469 struct CURLMsg *msg;
1471 struct ProxyCurlTask *ctask;
1474 struct ProxyCurlTask *clean_head = NULL;
1475 struct ProxyCurlTask *clean_tail = NULL;
1477 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1479 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1481 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1482 "Shutdown requested while trying to download\n");
1486 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1494 mret = curl_multi_perform (curl_multi, &running);
1496 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1497 "Running curl tasks: %d\n", running);
1498 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1500 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1501 "CTask: %s\n", ctask->url);
1508 msg = curl_multi_info_read (curl_multi, &msgnum);
1509 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1510 "Messages left: %d\n", msgnum);
1517 if ((msg->data.result != CURLE_OK) &&
1518 (msg->data.result != CURLE_GOT_NOTHING))
1520 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1521 "Download curl failed");
1523 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1525 if (NULL == ctask->curl)
1528 if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
1531 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1532 "CURL: Download failed for task %s: %s.\n",
1534 curl_easy_strerror (msg->data.result));
1535 ctask->download_is_finished = GNUNET_YES;
1536 ctask->download_error = GNUNET_YES;
1537 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1538 CURLINFO_RESPONSE_CODE,
1540 ctask->curl_response_code = resp_code;
1541 ctask->ready_to_queue = MHD_YES;
1542 ctask->buf_status = BUF_WAIT_FOR_MHD;
1543 run_mhd_now (ctask->mhd);
1545 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1547 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1550 GNUNET_assert (ctask != NULL);
1554 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1555 "CURL: download completed.\n");
1557 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1559 if (NULL == ctask->curl)
1562 if (0 != memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)))
1565 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1566 "CURL: completed task %s found.\n", ctask->url);
1567 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1568 CURLINFO_RESPONSE_CODE,
1570 ctask->curl_response_code = resp_code;
1573 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1574 "CURL: Completed ctask!\n");
1575 if (GNUNET_SCHEDULER_NO_TASK == ctask->pp_task)
1577 ctask->buf_status = BUF_WAIT_FOR_MHD;
1578 run_mhd_now (ctask->mhd);
1581 ctask->ready_to_queue = MHD_YES;
1582 ctask->download_is_finished = GNUNET_YES;
1584 /* We MUST not modify the multi handle else we loose messages */
1585 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1587 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1591 GNUNET_assert (ctask != NULL);
1593 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1594 "CURL: %s\n", curl_easy_strerror(msg->data.result));
1600 } while (msgnum > 0);
1602 for (ctask=clean_head; NULL != ctask; ctask = ctask->next)
1604 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1605 "CURL: Removing task %s.\n", ctask->url);
1606 curl_multi_remove_handle (curl_multi, ctask->curl);
1607 curl_easy_cleanup (ctask->curl);
1612 for (ctask=ctasks_head; NULL != ctask; ctask = ctask->next)
1614 GNUNET_assert (num_ctasks == running);
1616 } while (mret == CURLM_CALL_MULTI_PERFORM);
1618 if (mret != CURLM_OK)
1620 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CURL: %s failed at %s:%d: `%s'\n",
1621 "curl_multi_perform", __FILE__, __LINE__,
1622 curl_multi_strerror (mret));
1624 curl_download_prepare();
1629 * Process LEHO lookup
1631 * @param cls the ctask
1632 * @param rd_count number of records returned
1633 * @param rd record data
1636 process_leho_lookup (void *cls,
1638 const struct GNUNET_NAMESTORE_RecordData *rd)
1640 struct ProxyCurlTask *ctask = cls;
1641 char hosthdr[262]; //256 + "Host: "
1645 struct hostent *phost;
1647 char resolvename[512];
1650 strcpy (ctask->leho, "");
1653 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1654 "No LEHO present!\n");
1656 for (i=0; i<rd_count; i++)
1658 if (rd[i].record_type != GNUNET_NAMESTORE_TYPE_LEHO)
1661 memcpy (ctask->leho, rd[i].data, rd[i].data_size);
1663 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1664 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
1667 if (0 != strcmp (ctask->leho, ""))
1669 sprintf (hosthdr, "%s%s:%d", "Host: ", ctask->leho, ctask->port);
1670 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1671 "New HTTP header value: %s\n", hosthdr);
1672 ctask->headers = curl_slist_append (ctask->headers, hosthdr);
1673 GNUNET_assert (NULL != ctask->headers);
1674 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers)))
1675 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1678 if (ctask->mhd->is_ssl)
1680 phost = (struct hostent*)gethostbyname (ctask->host);
1684 ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
1685 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1686 "SSL target server: %s\n", ssl_ip);
1687 sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
1688 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1689 "Curl resolve: %s\n", resolvename);
1690 ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
1691 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver)))
1692 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1693 sprintf (curlurl, "https://%s:%d%s", ctask->leho, ctask->port, ctask->url);
1694 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl)))
1695 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1699 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1700 "gethostbyname failed for %s!\n",
1702 ctask->download_is_finished = GNUNET_YES;
1703 ctask->download_error = GNUNET_YES;
1708 if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
1710 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1711 "%s failed at %s:%d: `%s'\n",
1712 "curl_multi_add_handle", __FILE__, __LINE__,
1713 curl_multi_strerror (mret));
1714 ctask->download_is_finished = GNUNET_YES;
1715 ctask->download_error = GNUNET_YES;
1718 GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1720 curl_download_prepare ();
1725 * Initialize download and trigger curl
1727 * @param cls the proxycurltask
1728 * @param auth_name the name of the authority (site of origin) of ctask->host
1731 process_get_authority (void *cls,
1732 const char* auth_name)
1734 struct ProxyCurlTask *ctask = cls;
1736 if (NULL == auth_name)
1738 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1739 "Get authority failed!\n");
1740 strcpy (ctask->authority, "");
1744 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1745 "Get authority yielded %s\n", auth_name);
1746 strcpy (ctask->authority, auth_name);
1749 GNUNET_GNS_lookup (gns_handle,
1752 GNUNET_NAMESTORE_TYPE_LEHO,
1753 GNUNET_YES /* Only cached for performance */,
1755 &process_leho_lookup,
1761 mhd_log_callback (void* cls,
1764 struct ProxyCurlTask *ctask;
1766 ctask = GNUNET_new (struct ProxyCurlTask);
1767 strcpy (ctask->url, url);
1773 * Main MHD callback for handling requests.
1776 * @param con MHD connection handle
1777 * @param url the url in the request
1778 * @param meth the HTTP method used ("GET", "PUT", etc.)
1779 * @param ver the HTTP version string (i.e. "HTTP/1.1")
1780 * @param upload_data the data being uploaded (excluding HEADERS,
1781 * for a POST that fits into memory and that is encoded
1782 * with a supported encoding, the POST data will NOT be
1783 * given in upload_data and is instead available as
1784 * part of MHD_get_connection_values; very large POST
1785 * data *will* be made available incrementally in
1787 * @param upload_data_size set initially to the size of the
1788 * @a upload_data provided; the method must update this
1789 * value to the number of bytes NOT processed;
1790 * @param con_cls pointer to location where we store the 'struct Request'
1791 * @return #MHD_YES if the connection was handled successfully,
1792 * #MHD_NO if the socket must be closed due to a serious
1793 * error while handling the request
1796 create_response (void *cls,
1797 struct MHD_Connection *con,
1801 const char *upload_data,
1802 size_t *upload_data_size,
1805 struct MhdHttpList* hd = cls;
1806 char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow!
1809 struct ProxyCurlTask *ctask = *con_cls;
1810 struct ProxyUploadData *fin_post;
1811 struct curl_forms forms[5];
1812 struct ProxyUploadData *upload_data_iter;
1815 if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) &&
1816 (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) &&
1817 (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)) &&
1818 (0 != strcasecmp (meth, MHD_HTTP_METHOD_HEAD)))
1820 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1821 "MHD: %s NOT IMPLEMENTED!\n", meth);
1826 if (GNUNET_NO == ctask->accepted)
1829 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1830 "Got %s request for %s\n", meth, url);
1832 ctask->curl = curl_easy_init();
1833 ctask->curl_running = GNUNET_NO;
1834 if (NULL == ctask->curl)
1836 ret = MHD_queue_response (con,
1838 curl_failure_response);
1839 GNUNET_free (ctask);
1843 if (ctask->mhd->is_ssl)
1844 ctask->port = HTTPS_PORT;
1846 ctask->port = HTTP_PORT;
1848 MHD_get_connection_values (con,
1850 &con_val_iter, ctask);
1852 curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1853 curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
1854 curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1855 curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
1856 curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0);
1857 curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1859 if (GNUNET_NO == ctask->mhd->is_ssl)
1861 sprintf (curlurl, "http://%s:%d%s", ctask->host, ctask->port, ctask->url);
1862 curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1866 curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
1867 curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
1868 curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
1870 /* Add GNS header */
1871 ctask->headers = curl_slist_append (ctask->headers,
1873 ctask->accepted = GNUNET_YES;
1874 ctask->download_in_progress = GNUNET_YES;
1875 ctask->buf_status = BUF_WAIT_FOR_CURL;
1876 ctask->connection = con;
1877 ctask->curl_response_code = MHD_HTTP_OK;
1878 ctask->buffer_read_ptr = ctask->buffer;
1879 ctask->buffer_write_ptr = ctask->buffer;
1880 ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
1883 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
1885 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1886 "Setting up PUT\n");
1888 curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1);
1889 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
1890 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &put_read_callback);
1891 ctask->headers = curl_slist_append (ctask->headers,
1892 "Transfer-Encoding: chunked");
1895 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
1897 //FIXME handle multipart
1898 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1899 "Setting up POST processor\n");
1900 ctask->post_handler = MHD_create_post_processor (con,
1902 &con_post_data_iter,
1904 ctask->headers = curl_slist_append (ctask->headers,
1905 "Transfer-Encoding: chunked");
1909 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
1911 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1912 "Setting NOBODY\n");
1913 curl_easy_setopt (ctask->curl, CURLOPT_NOBODY, 1);
1917 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1918 "MHD: Adding new curl task for %s\n", ctask->host);
1920 GNUNET_GNS_get_authority (gns_handle,
1922 &process_get_authority,
1924 ctask->ready_to_queue = GNUNET_NO;
1925 ctask->fin = GNUNET_NO;
1926 ctask->curl_running = GNUNET_YES;
1930 ctask = (struct ProxyCurlTask *) *con_cls;
1931 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
1933 if (0 != *upload_data_size)
1936 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1937 "Invoking POST processor\n");
1938 MHD_post_process (ctask->post_handler,
1939 upload_data, *upload_data_size);
1940 *upload_data_size = 0;
1941 if ((GNUNET_NO == ctask->is_httppost) &&
1942 (GNUNET_NO == ctask->curl_running))
1944 curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
1945 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION,
1946 &post_read_callback);
1947 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
1949 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1950 "MHD: Adding new curl task for %s\n", ctask->host);
1952 GNUNET_GNS_get_authority (gns_handle,
1954 &process_get_authority,
1956 ctask->ready_to_queue = GNUNET_NO;
1957 ctask->fin = GNUNET_NO;
1958 ctask->curl_running = GNUNET_YES;
1962 else if (GNUNET_NO == ctask->post_done)
1964 if (GNUNET_YES == ctask->is_httppost)
1966 for (upload_data_iter = ctask->upload_data_head;
1967 NULL != upload_data_iter;
1968 upload_data_iter = upload_data_iter->next)
1971 if (NULL != upload_data_iter->filename)
1973 forms[i].option = CURLFORM_FILENAME;
1974 forms[i].value = upload_data_iter->filename;
1975 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1976 "Adding filename %s\n",
1980 if (NULL != upload_data_iter->content_type)
1982 forms[i].option = CURLFORM_CONTENTTYPE;
1983 forms[i].value = upload_data_iter->content_type;
1984 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1985 "Adding content type %s\n",
1989 forms[i].option = CURLFORM_PTRCONTENTS;
1990 forms[i].value = upload_data_iter->value;
1991 forms[i+1].option = CURLFORM_END;
1993 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1994 "Adding formdata for %s (len=%lld)\n",
1995 upload_data_iter->key,
1996 upload_data_iter->total_bytes);
1998 curl_formadd(&ctask->httppost, &ctask->httppost_last,
1999 CURLFORM_COPYNAME, upload_data_iter->key,
2000 CURLFORM_CONTENTSLENGTH, upload_data_iter->total_bytes,
2001 CURLFORM_ARRAY, forms,
2004 curl_easy_setopt (ctask->curl, CURLOPT_HTTPPOST,
2007 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2008 "MHD: Adding new curl task for %s\n", ctask->host);
2010 GNUNET_GNS_get_authority (gns_handle,
2012 &process_get_authority,
2014 ctask->ready_to_queue = GNUNET_YES;
2015 ctask->fin = GNUNET_NO;
2016 ctask->curl_running = GNUNET_YES;
2017 ctask->post_done = GNUNET_YES;
2021 fin_post = GNUNET_new (struct ProxyUploadData);
2022 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
2023 ctask->upload_data_tail,
2025 ctask->post_done = GNUNET_YES;
2030 if (GNUNET_YES != ctask->ready_to_queue)
2031 return MHD_YES; /* wait longer */
2033 if (GNUNET_YES == ctask->fin)
2036 ctask->fin = GNUNET_YES;
2037 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2038 "MHD: Queueing response for %s\n", ctask->url);
2039 ret = MHD_queue_response (con, ctask->curl_response_code, ctask->response);
2040 run_mhd_now (ctask->mhd);
2051 struct MhdHttpList *hd;
2053 for (hd=mhd_httpd_head; NULL != hd; hd = hd->next)
2062 * @param hd the daemon to run
2065 run_httpd (struct MhdHttpList *hd)
2070 struct GNUNET_NETWORK_FDSet *wrs;
2071 struct GNUNET_NETWORK_FDSet *wws;
2072 struct GNUNET_NETWORK_FDSet *wes;
2075 MHD_UNSIGNED_LONG_LONG timeout;
2076 struct GNUNET_TIME_Relative tv;
2081 wrs = GNUNET_NETWORK_fdset_create ();
2082 wes = GNUNET_NETWORK_fdset_create ();
2083 wws = GNUNET_NETWORK_fdset_create ();
2085 GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
2088 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2089 "MHD fds: max=%d\n", max);
2091 haveto = MHD_get_timeout (hd->daemon, &timeout);
2093 if (MHD_YES == haveto)
2094 tv.rel_value_us = (uint64_t) timeout * 1000LL;
2096 tv = GNUNET_TIME_UNIT_FOREVER_REL;
2097 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
2098 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
2099 GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
2101 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2102 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2104 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
2107 GNUNET_NETWORK_fdset_destroy (wrs);
2108 GNUNET_NETWORK_fdset_destroy (wws);
2109 GNUNET_NETWORK_fdset_destroy (wes);
2114 * Task run whenever HTTP server operations are pending.
2117 * @param tc sched context
2120 do_httpd (void *cls,
2121 const struct GNUNET_SCHEDULER_TaskContext *tc)
2123 struct MhdHttpList *hd = cls;
2125 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2126 "MHD: Main loop\n");
2127 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2128 MHD_run (hd->daemon);
2134 * Read data from socket
2136 * @param cls the closure
2137 * @param tc scheduler context
2140 do_s5r_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2144 * Read from remote end
2146 * @param cls closure
2147 * @param tc scheduler context
2150 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2154 * Write data to remote socket
2156 * @param cls the closure
2157 * @param tc scheduler context
2160 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2162 struct Socks5Request *s5r = cls;
2165 s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2166 if ( (NULL != tc->read_ready) &&
2167 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
2168 ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
2171 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2172 "Successfully sent %d bytes to remote socket\n",
2177 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send");
2183 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2190 * Write data to socket
2192 * @param cls the closure
2193 * @param tc scheduler context
2196 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2198 struct Socks5Request *s5r = cls;
2201 s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2203 if ((NULL != tc->read_ready) &&
2204 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
2205 ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
2208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2209 "Successfully sent %d bytes to socket\n",
2214 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
2215 s5r->cleanup = GNUNET_YES;
2216 s5r->cleanup_sock = GNUNET_YES;
2221 if (GNUNET_YES == s5r->cleanup)
2227 if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
2228 (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
2230 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2232 &do_read_remote, s5r);
2237 * Read from remote end
2239 * @param cls closure
2240 * @param tc scheduler context
2243 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2245 struct Socks5Request *s5r = cls;
2247 s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2248 if ((NULL != tc->write_ready) &&
2249 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
2250 (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
2251 sizeof (s5r->wbuf))))
2253 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2254 "Successfully read %d bytes from remote socket\n",
2259 if (0 == s5r->wbuf_len)
2260 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2261 "0 bytes received from remote... graceful shutdown!\n");
2266 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2273 * Adds a socket to MHD
2275 * @param h the handle to the socket to add
2276 * @param daemon the daemon to add the fd to
2277 * @return whatever #MHD_add_connection returns
2280 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
2283 struct sockaddr *addr;
2286 fd = dup (GNUNET_NETWORK_get_fd (h));
2287 addr = GNUNET_NETWORK_get_addr (h);
2288 len = GNUNET_NETWORK_get_addrlen (h);
2290 return MHD_add_connection (daemon, fd, addr, len);
2295 * Read file in filename
2297 * @param filename file to read
2298 * @param size pointer where filesize is stored
2299 * @return NULL on error
2302 load_file (const char* filename,
2309 GNUNET_DISK_file_size (filename, &fsize,
2310 GNUNET_YES, GNUNET_YES))
2312 if (fsize > MAX_PEM_SIZE)
2314 *size = (unsigned int) fsize;
2315 buffer = GNUNET_malloc (*size);
2316 if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize))
2318 GNUNET_free (buffer);
2326 * Load PEM key from file
2328 * @param key where to store the data
2329 * @param keyfile path to the PEM file
2330 * @return GNUNET_OK on success
2333 load_key_from_file (gnutls_x509_privkey_t key, const char* keyfile)
2335 gnutls_datum_t key_data;
2338 key_data.data = load_file (keyfile, &key_data.size);
2339 ret = gnutls_x509_privkey_import (key, &key_data,
2340 GNUTLS_X509_FMT_PEM);
2341 if (GNUTLS_E_SUCCESS != ret)
2343 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2344 _("Unable to import private key from file `%s'\n"),
2348 GNUNET_free (key_data.data);
2349 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2354 * Load cert from file
2356 * @param crt struct to store data in
2357 * @param certfile path to pem file
2358 * @return #GNUNET_OK on success
2361 load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
2363 gnutls_datum_t cert_data;
2366 cert_data.data = load_file (certfile, &cert_data.size);
2367 ret = gnutls_x509_crt_import (crt, &cert_data,
2368 GNUTLS_X509_FMT_PEM);
2369 if (GNUTLS_E_SUCCESS != ret)
2371 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2372 _("Unable to import certificate %s\n"), certfile);
2375 GNUNET_free (cert_data.data);
2376 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2381 * Generate new certificate for specific name
2383 * @param name the subject name to generate a cert for
2384 * @return a struct holding the PEM data
2386 static struct ProxyGNSCertificate *
2387 generate_gns_certificate (const char *name)
2390 unsigned int serial;
2391 size_t key_buf_size;
2392 size_t cert_buf_size;
2393 gnutls_x509_crt_t request;
2397 ret = gnutls_x509_crt_init (&request);
2399 if (GNUTLS_E_SUCCESS != ret)
2404 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
2406 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
2408 struct ProxyGNSCertificate *pgc =
2409 GNUNET_new (struct ProxyGNSCertificate);
2411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
2413 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
2415 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
2417 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
2418 0, name, strlen (name));
2419 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_version (request, 3));
2421 ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
2423 etime = time (NULL);
2424 tm_data = localtime (&etime);
2426 ret = gnutls_x509_crt_set_serial (request,
2430 ret = gnutls_x509_crt_set_activation_time (request,
2433 etime = mktime (tm_data);
2440 ret = gnutls_x509_crt_set_expiration_time (request,
2442 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
2444 ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
2446 key_buf_size = sizeof (pgc->key);
2447 cert_buf_size = sizeof (pgc->cert);
2449 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exporting certificate...\n");
2451 gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
2452 pgc->cert, &cert_buf_size);
2454 gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
2455 pgc->key, &key_buf_size);
2458 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
2459 gnutls_x509_crt_deinit (request);
2467 * Accept policy for mhdaemons
2470 * @param addr the sockaddr
2471 * @param addrlen the sockaddr length
2472 * @return MHD_NO if sockaddr is wrong or number of connections is too high
2475 accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen)
2477 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2478 "In MHD accept policy cb\n");
2482 if (addr->sa_family == AF_UNIX)
2486 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2487 "Connection accepted\n");
2494 * Adds a socket to an SSL MHD instance It is important that the
2495 * domain name is correct. In most cases we need to start a new daemon.
2497 * @param h the handle to add to a daemon
2498 * @param domain the domain the SSL daemon has to serve
2499 * @return #MHD_YES on success
2502 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h,
2505 struct MhdHttpList *hd;
2506 struct ProxyGNSCertificate *pgc;
2508 for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2509 if (0 == strcmp (hd->domain, domain))
2513 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2514 "Starting fresh MHD HTTPS instance for domain `%s'\n",
2516 pgc = generate_gns_certificate (domain);
2517 hd = GNUNET_new (struct MhdHttpList);
2518 hd->is_ssl = GNUNET_YES;
2519 strcpy (hd->domain, domain); /* FIXME: avoid fixed-sized buffers... */
2520 hd->proxy_cert = pgc;
2521 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET,
2524 &create_response, hd,
2525 MHD_OPTION_CONNECTION_LIMIT,
2526 MHD_MAX_CONNECTIONS,
2527 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2528 MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2529 MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2530 MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2531 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2534 /* FIXME: rather than assert, handle error! */
2535 GNUNET_assert (NULL != hd->daemon);
2536 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2538 return add_handle_to_mhd (h, hd->daemon);
2543 * Read data from incoming Socks5 connection
2545 * @param cls the closure with the `struct Socks5Request`
2546 * @param tc the scheduler context
2549 do_s5r_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2551 struct Socks5Request *s5r = cls;
2552 struct socks5_client_hello *c_hello;
2553 struct socks5_server_hello *s_hello;
2554 struct socks5_client_request *c_req;
2555 struct socks5_server_response *s_resp;
2560 struct hostent *phost;
2562 struct sockaddr_in remote_addr;
2563 struct in_addr *r_sin_addr;
2565 s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
2566 if ( (NULL != tc->read_ready) &&
2567 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) )
2568 s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
2569 sizeof (s5r->rbuf));
2572 if (0 == s5r->rbuf_len)
2574 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2575 "socks5 client disconnected.\n");
2579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2580 "Processing socks data in state %d\n",
2585 /* FIXME: failed to check if we got enough data yet! */
2586 c_hello = (struct socks5_client_hello*) &s5r->rbuf;
2587 GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
2588 s_hello = (struct socks5_server_hello*) &s5r->wbuf;
2589 s5r->wbuf_len = sizeof( struct socks5_server_hello );
2590 s_hello->version = c_hello->version;
2591 s_hello->auth_method = SOCKS_AUTH_NONE;
2592 /* Write response to client */
2593 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2596 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2599 s5r->state = SOCKS5_REQUEST;
2601 case SOCKS5_REQUEST:
2602 /* FIXME: failed to check if we got enough data yet!? */
2603 c_req = (struct socks5_client_request *) &s5r->rbuf;
2604 s_resp = (struct socks5_server_response *) &s5r->wbuf;
2605 //Only 10 byte for ipv4 response!
2606 s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
2607 GNUNET_assert (c_req->addr_type == 3);
2608 dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
2609 memset(domain, 0, sizeof(domain));
2610 strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
2611 req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
2612 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2613 "Requested connection is to %s:%d\n",
2616 if (is_tld (domain, GNUNET_GNS_TLD) ||
2617 is_tld (domain, GNUNET_GNS_TLD_ZKEY))
2621 if (ntohs (req_port) == HTTPS_PORT)
2623 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2624 "Requested connection is HTTPS\n");
2625 ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
2627 else if (NULL != httpd)
2629 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2630 "Requested connection is HTTP\n");
2631 ret = add_handle_to_mhd ( s5r->sock, httpd );
2636 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2637 _("Failed to start HTTP server\n"));
2638 s_resp->version = 0x05;
2639 s_resp->reply = 0x01;
2640 s5r->cleanup = GNUNET_YES;
2641 s5r->cleanup_sock = GNUNET_YES;
2643 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2649 /* Signal success */
2650 s_resp->version = 0x05;
2651 s_resp->reply = 0x00;
2652 s_resp->reserved = 0x00;
2653 s_resp->addr_type = 0x01;
2655 s5r->cleanup = GNUNET_YES;
2656 s5r->cleanup_sock = GNUNET_NO;
2658 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2666 /* non-GNS TLD, use DNS to resolve */
2667 /* FIXME: make asynchronous! */
2668 phost = (struct hostent *) gethostbyname (domain);
2671 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2672 "Resolve %s error!\n", domain );
2673 s_resp->version = 0x05;
2674 s_resp->reply = 0x01;
2675 s5r->cleanup = GNUNET_YES;
2676 s5r->cleanup_sock = GNUNET_YES;
2678 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2684 s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
2687 r_sin_addr = (struct in_addr*)(phost->h_addr);
2688 remote_ip = r_sin_addr->s_addr;
2689 memset(&remote_addr, 0, sizeof(remote_addr));
2690 remote_addr.sin_family = AF_INET;
2691 #if HAVE_SOCKADDR_IN_SIN_LEN
2692 remote_addr.sin_len = sizeof (remote_addr);
2694 remote_addr.sin_addr.s_addr = remote_ip;
2695 remote_addr.sin_port = req_port;
2697 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2698 "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
2702 GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
2703 (const struct sockaddr*)&remote_addr,
2704 sizeof (remote_addr)))
2705 && (errno != EINPROGRESS))
2707 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
2708 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2709 "socket request error...\n");
2710 s_resp->version = 0x05;
2711 s_resp->reply = 0x01;
2713 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2719 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2720 "new remote connection\n");
2721 s_resp->version = 0x05;
2722 s_resp->reply = 0x00;
2723 s_resp->reserved = 0x00;
2724 s_resp->addr_type = 0x01;
2725 s5r->state = SOCKS5_DATA_TRANSFER;
2727 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2731 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2736 case SOCKS5_DATA_TRANSFER:
2738 if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
2740 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2741 "Closing connection to client\n");
2745 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2746 "forwarding %d bytes from client\n", s5r->rbuf_len);
2748 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2750 &do_write_remote, s5r);
2751 if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
2754 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2756 &do_read_remote, s5r);
2768 * Accept new incoming connections
2770 * @param cls the closure
2771 * @param tc the scheduler context
2774 do_accept (void *cls,
2775 const struct GNUNET_SCHEDULER_TaskContext *tc)
2777 struct GNUNET_NETWORK_Handle *s;
2778 struct Socks5Request *s5r;
2780 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2783 s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
2786 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
2789 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2790 "Got an inbound connection, waiting for data\n");
2791 s5r = GNUNET_new (struct Socks5Request);
2792 GNUNET_CONTAINER_DLL_insert (s5r_head,
2796 s5r->state = SOCKS5_INIT;
2797 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2804 * Task run on shutdown
2806 * @param cls closure
2807 * @param tc task context
2810 do_shutdown (void *cls,
2811 const struct GNUNET_SCHEDULER_TaskContext *tc)
2813 struct MhdHttpList *hd;
2814 struct MhdHttpList *tmp_hd;
2815 struct ProxyCurlTask *ctask;
2816 struct ProxyCurlTask *ctask_tmp;
2817 struct ProxyUploadData *pdata;
2819 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2820 "Shutting down...\n");
2821 for (hd = mhd_httpd_head; NULL != hd; hd = tmp_hd)
2824 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2826 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2827 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2829 if (NULL != hd->daemon)
2831 MHD_stop_daemon (hd->daemon);
2834 GNUNET_free_non_null (hd->proxy_cert);
2837 for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
2839 ctask_tmp = ctask->next;
2840 if (NULL != ctask->curl)
2842 curl_easy_cleanup (ctask->curl);
2845 if (NULL != ctask->headers)
2847 curl_slist_free_all (ctask->headers);
2848 ctask->headers = NULL;
2850 if (NULL != ctask->resolver)
2852 curl_slist_free_all (ctask->resolver);
2853 ctask->resolver = NULL;
2855 if (NULL != ctask->response)
2857 MHD_destroy_response (ctask->response);
2858 ctask->response = NULL;
2860 for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
2862 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
2863 ctask->upload_data_tail,
2865 GNUNET_free_non_null (pdata->filename);
2866 GNUNET_free_non_null (pdata->content_type);
2867 GNUNET_free_non_null (pdata->key);
2868 GNUNET_free_non_null (pdata->value);
2869 GNUNET_free (pdata);
2871 GNUNET_free (ctask);
2875 GNUNET_NETWORK_socket_close (lsock);
2880 GNUNET_IDENTITY_cancel (id_op);
2883 if (NULL != identity)
2885 GNUNET_IDENTITY_disconnect (identity);
2888 if (NULL != curl_multi)
2890 curl_multi_cleanup (curl_multi);
2893 if (NULL != gns_handle)
2895 GNUNET_GNS_disconnect (gns_handle);
2898 if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
2900 GNUNET_SCHEDULER_cancel (curl_download_task);
2901 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
2903 if (GNUNET_SCHEDULER_NO_TASK != ltask)
2905 GNUNET_SCHEDULER_cancel (ltask);
2906 ltask = GNUNET_SCHEDULER_NO_TASK;
2908 gnutls_global_deinit ();
2913 * Continue initialization after we have our zone information.
2918 struct MhdHttpList *hd;
2919 struct sockaddr_in sa;
2921 /* Open listen socket for socks proxy */
2922 /* FIXME: support IPv6! */
2923 memset (&sa, 0, sizeof (sa));
2924 sa.sin_family = AF_INET;
2925 sa.sin_port = htons (port);
2926 #if HAVE_SOCKADDR_IN_SIN_LEN
2927 sa.sin_len = sizeof (sa);
2929 lsock = GNUNET_NETWORK_socket_create (AF_INET,
2934 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
2935 GNUNET_SCHEDULER_shutdown ();
2939 GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
2942 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
2943 GNUNET_SCHEDULER_shutdown ();
2946 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
2948 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
2951 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2952 lsock, &do_accept, NULL);
2954 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
2956 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2957 "cURL global init failed!\n");
2958 GNUNET_SCHEDULER_shutdown ();
2961 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2962 "Proxy listens on port %u\n",
2965 /* start MHD daemon for HTTP */
2966 hd = GNUNET_new (struct MhdHttpList);
2967 strcpy (hd->domain, "");
2968 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
2971 &create_response, hd,
2972 MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
2973 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2974 MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2975 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
2977 if (NULL == hd->daemon)
2980 GNUNET_SCHEDULER_shutdown ();
2984 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2986 /* start loop running all MHD instances */
2992 * Method called to inform about the egos of the shorten zone of this peer.
2994 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
2995 * this function is only called ONCE, and 'NULL' being passed in
2996 * @a ego does indicate an error (i.e. name is taken or no default
2997 * value is known). If @a ego is non-NULL and if '*ctx'
2998 * is set in those callbacks, the value WILL be passed to a subsequent
2999 * call to the identity callback of #GNUNET_IDENTITY_connect (if
3000 * that one was not NULL).
3002 * @param cls closure, NULL
3003 * @param ego ego handle
3004 * @param ctx context for application to store data for this ego
3005 * (during the lifetime of this process, initially NULL)
3006 * @param name name assigned by the user for this ego,
3007 * NULL if the user just deleted the ego and it
3008 * must thus no longer be used
3011 identity_shorten_cb (void *cls,
3012 struct GNUNET_IDENTITY_Ego *ego,
3019 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3020 _("No ego configured for `shorten-zone`\n"));
3024 local_shorten_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
3025 do_shorten = GNUNET_YES;
3032 * Method called to inform about the egos of the master zone of this peer.
3034 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
3035 * this function is only called ONCE, and 'NULL' being passed in
3036 * @a ego does indicate an error (i.e. name is taken or no default
3037 * value is known). If @a ego is non-NULL and if '*ctx'
3038 * is set in those callbacks, the value WILL be passed to a subsequent
3039 * call to the identity callback of #GNUNET_IDENTITY_connect (if
3040 * that one was not NULL).
3042 * @param cls closure, NULL
3043 * @param ego ego handle
3044 * @param ctx context for application to store data for this ego
3045 * (during the lifetime of this process, initially NULL)
3046 * @param name name assigned by the user for this ego,
3047 * NULL if the user just deleted the ego and it
3048 * must thus no longer be used
3051 identity_master_cb (void *cls,
3052 struct GNUNET_IDENTITY_Ego *ego,
3059 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3060 _("No ego configured for `master-zone`\n"));
3061 GNUNET_SCHEDULER_shutdown ();
3064 GNUNET_IDENTITY_ego_get_public_key (ego,
3066 id_op = GNUNET_IDENTITY_get (identity,
3068 &identity_shorten_cb,
3074 * Main function that will be run
3076 * @param cls closure
3077 * @param args remaining command-line arguments
3078 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3079 * @param c configuration
3082 run (void *cls, char *const *args, const char *cfgfile,
3083 const struct GNUNET_CONFIGURATION_Handle *c)
3085 char* cafile_cfg = NULL;
3089 if (NULL == (curl_multi = curl_multi_init ()))
3091 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3092 "Failed to create cURL multi handle!\n");
3095 cafile = cafile_opt;
3098 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3102 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
3107 cafile = cafile_cfg;
3109 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3110 "Using %s as CA\n", cafile);
3112 gnutls_global_init ();
3113 gnutls_x509_crt_init (&proxy_ca.cert);
3114 gnutls_x509_privkey_init (&proxy_ca.key);
3116 if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
3117 (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
3119 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3120 _("Failed to load SSL/TLS key and certificate from `%s'\n"),
3122 // FIXME: release resources...
3123 GNUNET_free_non_null (cafile_cfg);
3126 GNUNET_free_non_null (cafile_cfg);
3127 if (NULL == (gns_handle = GNUNET_GNS_connect (cfg)))
3129 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3130 "Unable to connect to GNS!\n");
3133 identity = GNUNET_IDENTITY_connect (cfg,
3135 id_op = GNUNET_IDENTITY_get (identity,
3137 &identity_master_cb,
3139 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
3140 &do_shutdown, NULL);
3145 * The main function for gnunet-gns-proxy.
3147 * @param argc number of arguments from the command line
3148 * @param argv command line arguments
3149 * @return 0 ok, 1 on error
3152 main (int argc, char *const *argv)
3154 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
3156 gettext_noop ("listen on specified port (default: 7777)"), 1,
3157 &GNUNET_GETOPT_set_ulong, &port},
3158 {'a', "authority", NULL,
3159 gettext_noop ("pem file to use as CA"), 1,
3160 &GNUNET_GETOPT_set_string, &cafile_opt},
3161 GNUNET_GETOPT_OPTION_END
3163 static const char* page =
3164 "<html><head><title>gnunet-gns-proxy</title>"
3165 "</head><body>cURL fail</body></html>";
3168 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
3170 GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
3171 curl_failure_response = MHD_create_response_from_buffer (strlen (page),
3173 MHD_RESPMEM_PERSISTENT);
3177 GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
3178 _("GNUnet GNS proxy"),
3180 &run, NULL)) ? 0 : 1;
3181 MHD_destroy_response (curl_failure_response);
3182 GNUNET_free_non_null ((char *) argv);
3186 /* end of gnunet-gns-proxy.c */