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 * DLL for Network Handles
188 struct NetworkHandleList
193 struct NetworkHandleList *next;
198 struct NetworkHandleList *prev;
203 struct GNUNET_NETWORK_Handle *h;
208 * A structure for all running Httpds
215 struct MhdHttpList *prev;
220 struct MhdHttpList *next;
223 * is this an ssl daemon?
228 * the domain name to server (only important for SSL)
235 struct MHD_Daemon *daemon;
238 * Optional proxy certificate used
240 struct ProxyGNSCertificate *proxy_cert;
245 GNUNET_SCHEDULER_TaskIdentifier httpd_task;
248 * Handles associated with this daemon
250 struct NetworkHandleList *socket_handles_head;
253 * Handles associated with this daemon
255 struct NetworkHandleList *socket_handles_tail;
260 * A structure for MHD<->cURL streams
267 struct ProxyCurlTask *prev;
272 struct ProxyCurlTask *next;
280 * Optional header replacements for curl (LEHO)
282 struct curl_slist *headers;
285 * Optional resolver replacements for curl (LEHO)
287 struct curl_slist *resolver;
292 long curl_response_code;
297 char url[MAX_HTTP_URI_LENGTH];
300 * The cURL write buffer / MHD read buffer
302 char buffer[CURL_MAX_WRITE_SIZE];
305 * Read pos of the data in the buffer
307 char *buffer_read_ptr;
310 * Write pos in the buffer
312 char *buffer_write_ptr;
317 struct MHD_Connection *connection;
322 size_t put_read_offset;
323 size_t put_read_size;
328 struct MHD_PostProcessor *post_handler;
331 struct ProxyUploadData *upload_data_head;
332 struct ProxyUploadData *upload_data_tail;
335 * the type of POST encoding
339 struct curl_httppost *httppost;
341 struct curl_httppost *httppost_last;
344 * Number of bytes in buffer
346 unsigned int bytes_in_buffer;
349 GNUNET_SCHEDULER_TaskIdentifier pp_task;
351 /* The associated daemon list entry */
352 struct MhdHttpList *mhd;
354 /* The associated response */
355 struct MHD_Response *response;
358 struct ProxySetCookieHeader *set_cookies_head;
361 struct ProxySetCookieHeader *set_cookies_tail;
363 /* The authority of the corresponding host (site of origin) */
366 /* The hostname (Host header field) */
369 /* The LEgacy HOstname (can be empty) */
378 * The buffer status (BUF_WAIT_FOR_CURL or BUF_WAIT_FOR_MHD)
380 enum BufferStatus buf_status;
403 * Indicates wheather the download is in progress
405 int download_in_progress;
408 * Indicates wheather the download was successful
410 int download_is_finished;
413 * Indicates wheather the download failed
425 * Struct for set-cookies
427 struct ProxySetCookieHeader
432 struct ProxySetCookieHeader *next;
437 struct ProxySetCookieHeader *prev;
447 * Post data structure
449 struct ProxyUploadData
454 struct ProxyUploadData *next;
459 struct ProxyUploadData *prev;
467 size_t content_length;
487 * The port the proxy is running on (default 7777)
489 static unsigned long port = GNUNET_GNS_PROXY_PORT;
492 * The CA file (pem) to use for the proxy CA
494 static char* cafile_opt;
497 * The listen socket of the proxy
499 static struct GNUNET_NETWORK_Handle *lsock;
504 static GNUNET_SCHEDULER_TaskIdentifier ltask;
507 * The cURL download task
509 static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
512 * Number of current mhd connections
514 static unsigned int total_mhd_connections;
517 * The cURL multi handle
519 static CURLM *curl_multi;
522 * Handle to the GNS service
524 static struct GNUNET_GNS_Handle *gns_handle;
527 * DLL for ProxyCurlTasks
529 static struct ProxyCurlTask *ctasks_head;
532 * DLL for ProxyCurlTasks
534 static struct ProxyCurlTask *ctasks_tail;
537 * DLL for http/https daemons
539 static struct MhdHttpList *mhd_httpd_head;
542 * DLL for http/https daemons
544 static struct MhdHttpList *mhd_httpd_tail;
547 * DLL of active socks requests.
549 static struct Socks5Request *s5r_head;
552 * DLL of active socks requests.
554 static struct Socks5Request *s5r_tail;
557 * The users local GNS master zone
559 static struct GNUNET_CRYPTO_EccPublicSignKey local_gns_zone;
562 * The users local shorten zone
564 static struct GNUNET_CRYPTO_EccPrivateKey local_shorten_zone;
567 * Is shortening enabled?
569 static int do_shorten;
572 * The CA for SSL certificate generation
574 static struct ProxyCA proxy_ca;
577 * Daemon for HTTP (we have one per SSL certificate, and then one for all HTTP connections;
578 * this is the one for HTTP, not HTTPS).
580 static struct MHD_Daemon *httpd;
583 * Shorten zone private key
585 static struct GNUNET_CRYPTO_EccPrivateKey shorten_zonekey;
588 * Response we return on cURL failures.
590 static struct MHD_Response *curl_failure_response;
593 * Connection to identity service.
595 static struct GNUNET_IDENTITY_Handle *identity;
598 * Request for our ego.
600 static struct GNUNET_IDENTITY_Operation *id_op;
605 static const struct GNUNET_CONFIGURATION_Handle *cfg;
608 * Clean up s5r handles
610 * @param s5r the handle to destroy
613 cleanup_s5r (struct Socks5Request *s5r)
615 if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
616 GNUNET_SCHEDULER_cancel (s5r->rtask);
617 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdwtask)
618 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
619 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
620 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
621 if (NULL != s5r->remote_sock)
622 GNUNET_NETWORK_socket_close (s5r->remote_sock);
623 if ( (NULL != s5r->sock) &&
624 (GNUNET_YES == s5r->cleanup_sock) )
625 GNUNET_NETWORK_socket_close (s5r->sock);
626 GNUNET_CONTAINER_DLL_remove (s5r_head,
634 * Checks if name is in tld
636 * @param name the name to check
637 * @param tld the TLD to check for (must NOT begin with ".")
638 * @return #GNUNET_YES or #GNUNET_NO
641 is_tld (const char* name, const char* tld)
643 size_t name_len = strlen (name);
644 size_t tld_len = strlen (tld);
646 GNUNET_break ('.' != tld[0]);
647 return ( (tld_len < name_len) &&
648 ( ('.' == name[name_len - tld_len - 1]) || (name_len == tld_len) ) &&
650 name + (name_len - tld_len),
656 con_post_data_iter (void *cls,
657 enum MHD_ValueKind kind,
659 const char *filename,
660 const char *content_type,
661 const char *transfer_encoding,
666 struct ProxyCurlTask* ctask = cls;
667 struct ProxyUploadData* pdata;
671 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
672 "Got POST data (file: %s, content type: %s): '%s=%.*s' at offset %llu size %llu\n",
673 filename, content_type,
674 key, (int) size, data,
675 (unsigned long long) off,
676 (unsigned long long) size);
677 GNUNET_assert (NULL != ctask->post_type);
679 if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
682 ctask->is_httppost = GNUNET_YES;
686 pdata = GNUNET_new (struct ProxyUploadData);
687 pdata->key = GNUNET_strdup (key);
689 if (NULL != filename)
690 pdata->filename = GNUNET_strdup (filename);
691 if (NULL != content_type)
692 pdata->content_type = GNUNET_strdup (content_type);
693 pdata->value = GNUNET_malloc (size);
694 pdata->total_bytes = size;
695 memcpy (pdata->value, data, size);
696 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
697 ctask->upload_data_tail,
700 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
701 "Copied %llu bytes of POST Data\n",
702 (unsigned long long) size);
706 pdata = ctask->upload_data_tail;
707 new_value = GNUNET_malloc (size + pdata->total_bytes);
708 memcpy (new_value, pdata->value, pdata->total_bytes);
709 memcpy (new_value+off, data, size);
710 GNUNET_free (pdata->value);
711 pdata->value = new_value;
712 pdata->total_bytes += size;
717 if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
723 ctask->is_httppost = GNUNET_NO;
725 if (NULL != ctask->curl)
726 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
730 enc = curl_easy_escape (ctask->curl, key, 0);
737 pdata = GNUNET_new (struct ProxyUploadData);
738 pdata->value = GNUNET_malloc (strlen (enc) + 3);
739 if (NULL != ctask->upload_data_head)
741 pdata->value[0] = '&';
742 memcpy (pdata->value+1, enc, strlen (enc));
745 memcpy (pdata->value, enc, strlen (enc));
746 pdata->value[strlen (pdata->value)] = '=';
747 pdata->bytes_left = strlen (pdata->value);
748 pdata->total_bytes = pdata->bytes_left;
751 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
752 "Escaped POST key: '%s'\n",
755 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
756 ctask->upload_data_tail,
761 enc = curl_easy_escape (ctask->curl, data, 0);
767 pdata = GNUNET_new (struct ProxyUploadData);
768 pdata->value = GNUNET_malloc (strlen (enc) + 1);
769 memcpy (pdata->value, enc, strlen (enc));
770 pdata->bytes_left = strlen (pdata->value);
771 pdata->total_bytes = pdata->bytes_left;
774 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
775 "Escaped POST value: '%s'\n",
778 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
779 ctask->upload_data_tail,
786 * Read HTTP request header field 'Host'
788 * @param cls buffer to write to
789 * @param kind value kind
790 * @param key field key
791 * @param value field value
792 * @return #MHD_NO when Host found
795 con_val_iter (void *cls,
796 enum MHD_ValueKind kind,
800 struct ProxyCurlTask *ctask = cls;
801 char* buf = ctask->host;
807 if (0 == strcmp ("Host", key))
809 port = strchr (value, ':');
812 strncpy (buf, value, port-value);
814 if ((1 != sscanf (port, "%u", &uport)) ||
815 (uport > UINT16_MAX) ||
817 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
818 "Unable to parse port!\n");
820 ctask->port = (uint16_t) uport;
827 if (0 == strcmp (MHD_HTTP_HEADER_ACCEPT_ENCODING, key))
832 if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_TYPE,
835 if (0 == strncasecmp (value,
836 MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
837 strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
838 ctask->post_type = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
839 else if (0 == strncasecmp (value,
840 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
841 strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
842 ctask->post_type = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
844 ctask->post_type = NULL;
848 cstr = GNUNET_malloc (strlen (key) + strlen (hdr_val) + 3);
849 GNUNET_snprintf (cstr, strlen (key) + strlen (hdr_val) + 3,
850 "%s: %s", key, hdr_val);
852 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
853 "Client Header: %s\n", cstr);
855 ctask->headers = curl_slist_append (ctask->headers, cstr);
863 * Callback for MHD response
866 * @param pos in buffer
868 * @param max space in buffer
869 * @return number of bytes written
872 mhd_content_cb (void *cls,
879 * Check HTTP response header for mime
881 * @param buffer curl buffer
882 * @param size curl blocksize
883 * @param nmemb curl blocknumber
885 * @return size of read bytes
888 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
890 size_t bytes = size * nmemb;
891 struct ProxyCurlTask *ctask = cls;
892 int cookie_hdr_len = strlen (MHD_HTTP_HEADER_SET_COOKIE);
893 char hdr_generic[bytes+1];
894 char new_cookie_hdr[bytes+strlen (ctask->leho)+1];
895 char new_location[MAX_HTTP_URI_LENGTH+500];
905 char cors_hdr[strlen (ctask->leho) + strlen ("https://")];
907 if (NULL == ctask->response)
909 /* FIXME: get total size from curl (if available) */
910 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
911 "Creating response for %s\n", ctask->url);
912 ctask->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
913 sizeof (ctask->buffer),
918 /* if we have a leho add a CORS header */
919 if (0 != strcmp ("", ctask->leho))
921 /* We could also allow ssl and http here */
922 if (ctask->mhd->is_ssl)
923 sprintf (cors_hdr, "https://%s", ctask->leho);
925 sprintf (cors_hdr, "http://%s", ctask->leho);
927 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
928 "MHD: Adding CORS header field %s\n",
931 if (GNUNET_NO == MHD_add_response_header (ctask->response,
932 "Access-Control-Allow-Origin",
935 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
936 "MHD: Error adding CORS header field %s\n",
940 ctask->ready_to_queue = GNUNET_YES;
942 if (cookie_hdr_len > bytes)
945 memcpy (hdr_generic, buffer, bytes);
946 hdr_generic[bytes] = '\0';
948 if ('\n' == hdr_generic[bytes-1])
949 hdr_generic[bytes-1] = '\0';
951 if (hdr_generic[bytes-2] == '\r')
952 hdr_generic[bytes-2] = '\0';
954 if (0 == memcmp (hdr_generic,
955 MHD_HTTP_HEADER_SET_COOKIE,
958 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
959 "Looking for cookie in: `%s'\n", hdr_generic);
960 ndup = GNUNET_strdup (hdr_generic+cookie_hdr_len+1);
961 memset (new_cookie_hdr, 0, sizeof (new_cookie_hdr));
962 for (tok = strtok (ndup, ";"); tok != NULL; tok = strtok (NULL, ";"))
964 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
965 "Got Cookie token: %s\n", tok);
966 //memcpy (new_cookie_hdr+offset, tok, strlen (tok));
967 if (0 == memcmp (tok, " domain", strlen (" domain")))
969 cookie_domain = tok + strlen (" domain") + 1;
971 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
972 "Got Set-Cookie Domain: %s\n", cookie_domain);
974 if (strlen (cookie_domain) < strlen (ctask->leho))
976 delta_cdomain = strlen (ctask->leho) - strlen (cookie_domain);
977 if (0 == strcmp (cookie_domain, ctask->leho + (delta_cdomain)))
979 GNUNET_snprintf (new_cookie_hdr+offset,
980 sizeof (new_cookie_hdr),
981 " domain=%s", ctask->authority);
982 offset += strlen (" domain=") + strlen (ctask->authority);
983 new_cookie_hdr[offset] = ';';
988 else if (strlen (cookie_domain) == strlen (ctask->leho))
990 if (0 == strcmp (cookie_domain, ctask->leho))
992 GNUNET_snprintf (new_cookie_hdr+offset,
993 sizeof (new_cookie_hdr),
994 " domain=%s", ctask->host);
995 offset += strlen (" domain=") + strlen (ctask->host);
996 new_cookie_hdr[offset] = ';';
1001 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1002 "Cookie domain invalid\n");
1006 memcpy (new_cookie_hdr+offset, tok, strlen (tok));
1007 offset += strlen (tok);
1008 new_cookie_hdr[offset] = ';';
1014 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1015 "Got Set-Cookie HTTP header %s\n", new_cookie_hdr);
1017 if (GNUNET_NO == MHD_add_response_header (ctask->response,
1018 MHD_HTTP_HEADER_SET_COOKIE,
1021 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1022 "MHD: Error adding set-cookie header field %s\n",
1023 hdr_generic+cookie_hdr_len+1);
1028 ndup = GNUNET_strdup (hdr_generic);
1029 hdr_type = strtok (ndup, ":");
1031 if (NULL == hdr_type)
1037 hdr_val = strtok (NULL, "");
1039 if (NULL == hdr_val)
1047 if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
1049 if (ctask->mhd->is_ssl)
1051 sprintf (leho_host, "https://%s", ctask->leho);
1052 sprintf (real_host, "https://%s", ctask->host);
1056 sprintf (leho_host, "http://%s", ctask->leho);
1057 sprintf (real_host, "http://%s", ctask->host);
1060 if (0 == memcmp (leho_host, hdr_val, strlen (leho_host)))
1062 sprintf (new_location, "%s%s", real_host, hdr_val+strlen (leho_host));
1063 hdr_val = new_location;
1067 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1068 "Trying to set %s: %s\n",
1071 if (GNUNET_NO == MHD_add_response_header (ctask->response,
1075 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1076 "MHD: Error adding %s header field %s\n",
1088 * @param hd a http daemon list entry
1091 run_httpd (struct MhdHttpList *hd);
1103 * Task run whenever HTTP server operations are pending.
1106 * @param tc sched context
1109 do_httpd (void *cls,
1110 const struct GNUNET_SCHEDULER_TaskContext *tc);
1114 run_mhd_now (struct MhdHttpList *hd)
1116 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
1118 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1119 "MHD: killing old task\n");
1120 GNUNET_SCHEDULER_cancel (hd->httpd_task);
1122 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1123 "MHD: Scheduling MHD now\n");
1124 hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, hd);
1129 * Ask cURL for the select sets and schedule download
1132 curl_download_prepare (void);
1136 * Callback to free content
1138 * @param cls content to free
1139 * @param tc task context
1142 mhd_content_free (void *cls,
1143 const struct GNUNET_SCHEDULER_TaskContext *tc)
1145 struct ProxyCurlTask *ctask = cls;
1146 struct ProxyUploadData *pdata;
1148 if (NULL != ctask->headers)
1149 curl_slist_free_all (ctask->headers);
1151 if (NULL != ctask->headers)
1152 curl_slist_free_all (ctask->resolver);
1154 if (NULL != ctask->response)
1155 MHD_destroy_response (ctask->response);
1157 if (NULL != ctask->post_handler)
1158 MHD_destroy_post_processor (ctask->post_handler);
1160 if (GNUNET_SCHEDULER_NO_TASK != ctask->pp_task)
1161 GNUNET_SCHEDULER_cancel (ctask->pp_task);
1163 for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
1165 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1166 ctask->upload_data_tail,
1168 GNUNET_free_non_null (pdata->filename);
1169 GNUNET_free_non_null (pdata->content_type);
1170 GNUNET_free_non_null (pdata->key);
1171 GNUNET_free_non_null (pdata->value);
1172 GNUNET_free (pdata);
1174 GNUNET_free (ctask);
1179 * Callback for MHD response
1181 * @param cls closure
1182 * @param pos in buffer
1184 * @param max space in buffer
1185 * @return number of bytes written
1188 mhd_content_cb (void *cls,
1193 struct ProxyCurlTask *ctask = cls;
1195 size_t bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1197 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1198 "MHD: content cb for %s. To copy: %u\n",
1199 ctask->url, (unsigned int) bytes_to_copy);
1200 if ((GNUNET_YES == ctask->download_is_finished) &&
1201 (GNUNET_NO == ctask->download_error) &&
1202 (0 == bytes_to_copy))
1204 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1205 "MHD: sending response for %s\n", ctask->url);
1206 ctask->download_in_progress = GNUNET_NO;
1207 run_mhd_now (ctask->mhd);
1208 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1209 total_mhd_connections--;
1210 return MHD_CONTENT_READER_END_OF_STREAM;
1213 if ((GNUNET_YES == ctask->download_error) &&
1214 (GNUNET_YES == ctask->download_is_finished) &&
1215 (0 == bytes_to_copy))
1217 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1218 "MHD: sending error response\n");
1219 ctask->download_in_progress = GNUNET_NO;
1220 run_mhd_now (ctask->mhd);
1221 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1222 total_mhd_connections--;
1223 return MHD_CONTENT_READER_END_WITH_ERROR;
1226 if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
1230 bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1232 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1233 "MHD: copied: %d left: %u, space left in buf: %d\n",
1235 (unsigned int) bytes_to_copy, (int) (max - copied));
1237 if (GNUNET_NO == ctask->download_is_finished)
1239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1240 "MHD: Purging buffer\n");
1241 memmove (ctask->buffer, ctask->buffer_read_ptr, bytes_to_copy);
1242 ctask->buffer_read_ptr = ctask->buffer;
1243 ctask->buffer_write_ptr = ctask->buffer + bytes_to_copy;
1244 ctask->buffer[bytes_to_copy] = '\0';
1247 if (bytes_to_copy + copied > max)
1248 bytes_to_copy = max - copied;
1249 memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1250 ctask->buffer_read_ptr += bytes_to_copy;
1251 copied += bytes_to_copy;
1252 ctask->buf_status = BUF_WAIT_FOR_CURL;
1254 if (NULL != ctask->curl)
1255 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
1257 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1258 "MHD: copied %d bytes\n", (int) copied);
1259 run_mhd_now (ctask->mhd);
1265 * Handle data from cURL
1267 * @param ptr pointer to the data
1268 * @param size number of blocks of data
1269 * @param nmemb blocksize
1270 * @param ctx the curlproxytask
1271 * @return number of bytes handled
1274 curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx)
1276 const char *cbuf = ptr;
1277 size_t total = size * nmemb;
1278 struct ProxyCurlTask *ctask = ctx;
1279 size_t buf_space = sizeof (ctask->buffer) - (ctask->buffer_write_ptr - ctask->buffer);
1281 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1282 "CURL: Got %d. %d free in buffer\n",
1287 ctask->buf_status = BUF_WAIT_FOR_MHD;
1288 run_mhd_now (ctask->mhd);
1289 return CURL_WRITEFUNC_PAUSE;
1291 if (total > buf_space)
1293 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1294 "CURL: Copying %d bytes to buffer (%s)\n",
1296 memcpy (ctask->buffer_write_ptr, cbuf, total);
1297 ctask->bytes_in_buffer += total;
1298 ctask->buffer_write_ptr += total;
1299 if (ctask->bytes_in_buffer > 0)
1301 ctask->buf_status = BUF_WAIT_FOR_MHD;
1302 run_mhd_now (ctask->mhd);
1309 * cURL callback for put data
1312 put_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1314 struct ProxyCurlTask *ctask = cls;
1315 struct ProxyUploadData *pdata = ctask->upload_data_head;
1316 size_t len = size * nmemb;
1320 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1321 "CURL: put read callback\n");
1324 return CURL_READFUNC_PAUSE;
1327 if (NULL == pdata->value)
1329 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1330 "CURL: Terminating PUT\n");
1332 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1333 ctask->upload_data_tail,
1335 GNUNET_free (pdata);
1339 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1340 "CURL: read callback value %s\n", pdata->value);
1342 to_copy = pdata->bytes_left;
1346 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1347 memcpy (buf, pos, to_copy);
1348 pdata->bytes_left -= to_copy;
1349 if (pdata->bytes_left <= 0)
1351 GNUNET_free (pdata->value);
1352 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1353 ctask->upload_data_tail,
1355 GNUNET_free (pdata);
1362 * cURL callback for post data
1365 post_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1367 struct ProxyCurlTask *ctask = cls;
1368 struct ProxyUploadData *pdata = ctask->upload_data_head;
1369 size_t len = size * nmemb;
1373 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1374 "CURL: read callback\n");
1377 return CURL_READFUNC_PAUSE;
1380 if (NULL == pdata->value)
1382 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1383 "CURL: Terminating POST data\n");
1385 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1386 ctask->upload_data_tail,
1388 GNUNET_free (pdata);
1392 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1393 "CURL: read callback value %s\n", pdata->value);
1395 to_copy = pdata->bytes_left;
1399 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1400 memcpy (buf, pos, to_copy);
1401 pdata->bytes_left -= to_copy;
1402 if (pdata->bytes_left <= 0)
1404 GNUNET_free (pdata->value);
1405 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1406 ctask->upload_data_tail,
1408 GNUNET_free (pdata);
1415 * Task that is run when we are ready to receive more data
1418 * @param cls closure
1419 * @param tc task context
1422 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1426 * Ask cURL for the select sets and schedule download
1429 curl_download_prepare ()
1436 struct GNUNET_NETWORK_FDSet *grs;
1437 struct GNUNET_NETWORK_FDSet *gws;
1439 struct GNUNET_TIME_Relative rtime;
1445 if (CURLM_OK != (mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max)))
1447 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1448 "%s failed at %s:%d: `%s'\n",
1449 "curl_multi_fdset", __FILE__, __LINE__,
1450 curl_multi_strerror (mret));
1451 //TODO cleanup here?
1455 GNUNET_break (CURLM_OK == curl_multi_timeout (curl_multi, &to));
1456 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1457 "cURL multi fds: max=%d timeout=%lld\n", max, (long long) to);
1459 rtime = GNUNET_TIME_UNIT_FOREVER_REL;
1461 rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1462 grs = GNUNET_NETWORK_fdset_create ();
1463 gws = GNUNET_NETWORK_fdset_create ();
1464 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1465 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1466 if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
1467 GNUNET_SCHEDULER_cancel (curl_download_task);
1470 curl_download_task =
1471 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1474 &curl_task_download, curl_multi);
1476 else if (NULL != ctasks_head)
1478 /* as specified in curl docs */
1479 curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
1480 &curl_task_download,
1483 GNUNET_NETWORK_fdset_destroy (gws);
1484 GNUNET_NETWORK_fdset_destroy (grs);
1489 * Task that is run when we are ready to receive more data
1492 * @param cls closure
1493 * @param tc task context
1496 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1500 struct CURLMsg *msg;
1502 struct ProxyCurlTask *ctask;
1505 struct ProxyCurlTask *clean_head = NULL;
1506 struct ProxyCurlTask *clean_tail = NULL;
1508 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1510 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1512 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1513 "Shutdown requested while trying to download\n");
1517 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1525 mret = curl_multi_perform (curl_multi, &running);
1527 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1528 "Running curl tasks: %d\n", running);
1529 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1531 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1532 "CTask: %s\n", ctask->url);
1539 msg = curl_multi_info_read (curl_multi, &msgnum);
1540 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1541 "Messages left: %d\n", msgnum);
1548 if ((msg->data.result != CURLE_OK) &&
1549 (msg->data.result != CURLE_GOT_NOTHING))
1551 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1552 "Download curl failed");
1554 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1556 if (NULL == ctask->curl)
1559 if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
1562 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1563 "CURL: Download failed for task %s: %s.\n",
1565 curl_easy_strerror (msg->data.result));
1566 ctask->download_is_finished = GNUNET_YES;
1567 ctask->download_error = GNUNET_YES;
1568 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1569 CURLINFO_RESPONSE_CODE,
1571 ctask->curl_response_code = resp_code;
1572 ctask->ready_to_queue = MHD_YES;
1573 ctask->buf_status = BUF_WAIT_FOR_MHD;
1574 run_mhd_now (ctask->mhd);
1576 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1578 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1581 GNUNET_assert (ctask != NULL);
1585 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1586 "CURL: download completed.\n");
1588 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1590 if (NULL == ctask->curl)
1593 if (0 != memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)))
1596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1597 "CURL: completed task %s found.\n", ctask->url);
1598 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1599 CURLINFO_RESPONSE_CODE,
1601 ctask->curl_response_code = resp_code;
1604 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1605 "CURL: Completed ctask!\n");
1606 if (GNUNET_SCHEDULER_NO_TASK == ctask->pp_task)
1608 ctask->buf_status = BUF_WAIT_FOR_MHD;
1609 run_mhd_now (ctask->mhd);
1612 ctask->ready_to_queue = MHD_YES;
1613 ctask->download_is_finished = GNUNET_YES;
1615 /* We MUST not modify the multi handle else we loose messages */
1616 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1618 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1622 GNUNET_assert (ctask != NULL);
1624 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1625 "CURL: %s\n", curl_easy_strerror(msg->data.result));
1631 } while (msgnum > 0);
1633 for (ctask=clean_head; NULL != ctask; ctask = ctask->next)
1635 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1636 "CURL: Removing task %s.\n", ctask->url);
1637 curl_multi_remove_handle (curl_multi, ctask->curl);
1638 curl_easy_cleanup (ctask->curl);
1643 for (ctask=ctasks_head; NULL != ctask; ctask = ctask->next)
1645 GNUNET_assert (num_ctasks == running);
1647 } while (mret == CURLM_CALL_MULTI_PERFORM);
1649 if (mret != CURLM_OK)
1651 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CURL: %s failed at %s:%d: `%s'\n",
1652 "curl_multi_perform", __FILE__, __LINE__,
1653 curl_multi_strerror (mret));
1655 curl_download_prepare();
1660 * Process LEHO lookup
1662 * @param cls the ctask
1663 * @param rd_count number of records returned
1664 * @param rd record data
1667 process_leho_lookup (void *cls,
1669 const struct GNUNET_NAMESTORE_RecordData *rd)
1671 struct ProxyCurlTask *ctask = cls;
1672 char hosthdr[262]; //256 + "Host: "
1676 struct hostent *phost;
1678 char resolvename[512];
1681 strcpy (ctask->leho, "");
1684 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1685 "No LEHO present!\n");
1687 for (i=0; i<rd_count; i++)
1689 if (rd[i].record_type != GNUNET_NAMESTORE_TYPE_LEHO)
1692 memcpy (ctask->leho, rd[i].data, rd[i].data_size);
1694 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1695 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
1698 if (0 != strcmp (ctask->leho, ""))
1700 sprintf (hosthdr, "%s%s:%d", "Host: ", ctask->leho, ctask->port);
1701 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1702 "New HTTP header value: %s\n", hosthdr);
1703 ctask->headers = curl_slist_append (ctask->headers, hosthdr);
1704 GNUNET_assert (NULL != ctask->headers);
1705 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers)))
1706 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1709 if (ctask->mhd->is_ssl)
1711 phost = (struct hostent*)gethostbyname (ctask->host);
1715 ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
1716 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1717 "SSL target server: %s\n", ssl_ip);
1718 sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
1719 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1720 "Curl resolve: %s\n", resolvename);
1721 ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
1722 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver)))
1723 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1724 sprintf (curlurl, "https://%s:%d%s", ctask->leho, ctask->port, ctask->url);
1725 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl)))
1726 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1730 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1731 "gethostbyname failed for %s!\n",
1733 ctask->download_is_finished = GNUNET_YES;
1734 ctask->download_error = GNUNET_YES;
1739 if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
1741 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1742 "%s failed at %s:%d: `%s'\n",
1743 "curl_multi_add_handle", __FILE__, __LINE__,
1744 curl_multi_strerror (mret));
1745 ctask->download_is_finished = GNUNET_YES;
1746 ctask->download_error = GNUNET_YES;
1749 GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1751 curl_download_prepare ();
1756 * Initialize download and trigger curl
1758 * @param cls the proxycurltask
1759 * @param auth_name the name of the authority (site of origin) of ctask->host
1762 process_get_authority (void *cls,
1763 const char* auth_name)
1765 struct ProxyCurlTask *ctask = cls;
1767 if (NULL == auth_name)
1769 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1770 "Get authority failed!\n");
1771 strcpy (ctask->authority, "");
1775 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1776 "Get authority yielded %s\n", auth_name);
1777 strcpy (ctask->authority, auth_name);
1780 GNUNET_GNS_lookup (gns_handle,
1783 GNUNET_NAMESTORE_TYPE_LEHO,
1784 GNUNET_YES /* Only cached for performance */,
1786 &process_leho_lookup,
1792 mhd_log_callback (void* cls,
1795 struct ProxyCurlTask *ctask;
1797 ctask = GNUNET_new (struct ProxyCurlTask);
1798 strcpy (ctask->url, url);
1804 * Main MHD callback for handling requests.
1807 * @param con MHD connection handle
1808 * @param url the url in the request
1809 * @param meth the HTTP method used ("GET", "PUT", etc.)
1810 * @param ver the HTTP version string (i.e. "HTTP/1.1")
1811 * @param upload_data the data being uploaded (excluding HEADERS,
1812 * for a POST that fits into memory and that is encoded
1813 * with a supported encoding, the POST data will NOT be
1814 * given in upload_data and is instead available as
1815 * part of MHD_get_connection_values; very large POST
1816 * data *will* be made available incrementally in
1818 * @param upload_data_size set initially to the size of the
1819 * @a upload_data provided; the method must update this
1820 * value to the number of bytes NOT processed;
1821 * @param con_cls pointer to location where we store the 'struct Request'
1822 * @return #MHD_YES if the connection was handled successfully,
1823 * #MHD_NO if the socket must be closed due to a serious
1824 * error while handling the request
1827 create_response (void *cls,
1828 struct MHD_Connection *con,
1832 const char *upload_data,
1833 size_t *upload_data_size,
1836 struct MhdHttpList* hd = cls;
1837 char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow!
1840 struct ProxyCurlTask *ctask = *con_cls;
1841 struct ProxyUploadData *fin_post;
1842 struct curl_forms forms[5];
1843 struct ProxyUploadData *upload_data_iter;
1846 if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) &&
1847 (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) &&
1848 (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)) &&
1849 (0 != strcasecmp (meth, MHD_HTTP_METHOD_HEAD)))
1851 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1852 "MHD: %s NOT IMPLEMENTED!\n", meth);
1857 if (GNUNET_NO == ctask->accepted)
1860 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1861 "Got %s request for %s\n", meth, url);
1863 ctask->curl = curl_easy_init();
1864 ctask->curl_running = GNUNET_NO;
1865 if (NULL == ctask->curl)
1867 ret = MHD_queue_response (con,
1869 curl_failure_response);
1870 GNUNET_free (ctask);
1874 if (ctask->mhd->is_ssl)
1875 ctask->port = HTTPS_PORT;
1877 ctask->port = HTTP_PORT;
1879 MHD_get_connection_values (con,
1881 &con_val_iter, ctask);
1883 curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1884 curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
1885 curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1886 curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
1887 curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0);
1888 curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1890 if (GNUNET_NO == ctask->mhd->is_ssl)
1892 sprintf (curlurl, "http://%s:%d%s", ctask->host, ctask->port, ctask->url);
1893 curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1897 curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
1898 curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
1899 curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
1901 /* Add GNS header */
1902 ctask->headers = curl_slist_append (ctask->headers,
1904 ctask->accepted = GNUNET_YES;
1905 ctask->download_in_progress = GNUNET_YES;
1906 ctask->buf_status = BUF_WAIT_FOR_CURL;
1907 ctask->connection = con;
1908 ctask->curl_response_code = MHD_HTTP_OK;
1909 ctask->buffer_read_ptr = ctask->buffer;
1910 ctask->buffer_write_ptr = ctask->buffer;
1911 ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
1914 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
1916 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1917 "Setting up PUT\n");
1919 curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1);
1920 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
1921 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &put_read_callback);
1922 ctask->headers = curl_slist_append (ctask->headers,
1923 "Transfer-Encoding: chunked");
1926 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
1928 //FIXME handle multipart
1929 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1930 "Setting up POST processor\n");
1931 ctask->post_handler = MHD_create_post_processor (con,
1933 &con_post_data_iter,
1935 ctask->headers = curl_slist_append (ctask->headers,
1936 "Transfer-Encoding: chunked");
1940 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
1942 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1943 "Setting NOBODY\n");
1944 curl_easy_setopt (ctask->curl, CURLOPT_NOBODY, 1);
1948 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1949 "MHD: Adding new curl task for %s\n", ctask->host);
1951 GNUNET_GNS_get_authority (gns_handle,
1953 &process_get_authority,
1955 ctask->ready_to_queue = GNUNET_NO;
1956 ctask->fin = GNUNET_NO;
1957 ctask->curl_running = GNUNET_YES;
1961 ctask = (struct ProxyCurlTask *) *con_cls;
1962 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
1964 if (0 != *upload_data_size)
1967 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1968 "Invoking POST processor\n");
1969 MHD_post_process (ctask->post_handler,
1970 upload_data, *upload_data_size);
1971 *upload_data_size = 0;
1972 if ((GNUNET_NO == ctask->is_httppost) &&
1973 (GNUNET_NO == ctask->curl_running))
1975 curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
1976 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION,
1977 &post_read_callback);
1978 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
1980 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1981 "MHD: Adding new curl task for %s\n", ctask->host);
1983 GNUNET_GNS_get_authority (gns_handle,
1985 &process_get_authority,
1987 ctask->ready_to_queue = GNUNET_NO;
1988 ctask->fin = GNUNET_NO;
1989 ctask->curl_running = GNUNET_YES;
1993 else if (GNUNET_NO == ctask->post_done)
1995 if (GNUNET_YES == ctask->is_httppost)
1997 for (upload_data_iter = ctask->upload_data_head;
1998 NULL != upload_data_iter;
1999 upload_data_iter = upload_data_iter->next)
2002 if (NULL != upload_data_iter->filename)
2004 forms[i].option = CURLFORM_FILENAME;
2005 forms[i].value = upload_data_iter->filename;
2006 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2007 "Adding filename %s\n",
2011 if (NULL != upload_data_iter->content_type)
2013 forms[i].option = CURLFORM_CONTENTTYPE;
2014 forms[i].value = upload_data_iter->content_type;
2015 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2016 "Adding content type %s\n",
2020 forms[i].option = CURLFORM_PTRCONTENTS;
2021 forms[i].value = upload_data_iter->value;
2022 forms[i+1].option = CURLFORM_END;
2024 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2025 "Adding formdata for %s (len=%lld)\n",
2026 upload_data_iter->key,
2027 upload_data_iter->total_bytes);
2029 curl_formadd(&ctask->httppost, &ctask->httppost_last,
2030 CURLFORM_COPYNAME, upload_data_iter->key,
2031 CURLFORM_CONTENTSLENGTH, upload_data_iter->total_bytes,
2032 CURLFORM_ARRAY, forms,
2035 curl_easy_setopt (ctask->curl, CURLOPT_HTTPPOST,
2038 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2039 "MHD: Adding new curl task for %s\n", ctask->host);
2041 GNUNET_GNS_get_authority (gns_handle,
2043 &process_get_authority,
2045 ctask->ready_to_queue = GNUNET_YES;
2046 ctask->fin = GNUNET_NO;
2047 ctask->curl_running = GNUNET_YES;
2048 ctask->post_done = GNUNET_YES;
2052 fin_post = GNUNET_new (struct ProxyUploadData);
2053 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
2054 ctask->upload_data_tail,
2056 ctask->post_done = GNUNET_YES;
2061 if (GNUNET_YES != ctask->ready_to_queue)
2062 return MHD_YES; /* wait longer */
2064 if (GNUNET_YES == ctask->fin)
2067 ctask->fin = GNUNET_YES;
2068 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2069 "MHD: Queueing response for %s\n", ctask->url);
2070 ret = MHD_queue_response (con, ctask->curl_response_code, ctask->response);
2071 run_mhd_now (ctask->mhd);
2082 struct MhdHttpList *hd;
2084 for (hd=mhd_httpd_head; NULL != hd; hd = hd->next)
2093 * @param hd the daemon to run
2096 run_httpd (struct MhdHttpList *hd)
2101 struct GNUNET_NETWORK_FDSet *wrs;
2102 struct GNUNET_NETWORK_FDSet *wws;
2103 struct GNUNET_NETWORK_FDSet *wes;
2106 MHD_UNSIGNED_LONG_LONG timeout;
2107 struct GNUNET_TIME_Relative tv;
2112 wrs = GNUNET_NETWORK_fdset_create ();
2113 wes = GNUNET_NETWORK_fdset_create ();
2114 wws = GNUNET_NETWORK_fdset_create ();
2116 GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
2119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2120 "MHD fds: max=%d\n", max);
2122 haveto = MHD_get_timeout (hd->daemon, &timeout);
2124 if (MHD_YES == haveto)
2125 tv.rel_value_us = (uint64_t) timeout * 1000LL;
2127 tv = GNUNET_TIME_UNIT_FOREVER_REL;
2128 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
2129 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
2130 GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
2132 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2133 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2135 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
2138 GNUNET_NETWORK_fdset_destroy (wrs);
2139 GNUNET_NETWORK_fdset_destroy (wws);
2140 GNUNET_NETWORK_fdset_destroy (wes);
2145 * Task run whenever HTTP server operations are pending.
2148 * @param tc sched context
2151 do_httpd (void *cls,
2152 const struct GNUNET_SCHEDULER_TaskContext *tc)
2154 struct MhdHttpList *hd = cls;
2156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2157 "MHD: Main loop\n");
2158 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2159 MHD_run (hd->daemon);
2165 * Read data from socket
2167 * @param cls the closure
2168 * @param tc scheduler context
2171 do_s5r_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2175 * Read from remote end
2177 * @param cls closure
2178 * @param tc scheduler context
2181 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2185 * Write data to remote socket
2187 * @param cls the closure
2188 * @param tc scheduler context
2191 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2193 struct Socks5Request *s5r = cls;
2196 s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2197 if ( (NULL != tc->read_ready) &&
2198 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
2199 ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
2202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2203 "Successfully sent %d bytes to remote socket\n",
2208 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send");
2214 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2221 * Write data to socket
2223 * @param cls the closure
2224 * @param tc scheduler context
2227 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2229 struct Socks5Request *s5r = cls;
2232 s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2234 if ((NULL != tc->read_ready) &&
2235 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
2236 ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
2239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2240 "Successfully sent %d bytes to socket\n",
2245 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
2246 s5r->cleanup = GNUNET_YES;
2247 s5r->cleanup_sock = GNUNET_YES;
2252 if (GNUNET_YES == s5r->cleanup)
2258 if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
2259 (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
2261 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2263 &do_read_remote, s5r);
2268 * Read from remote end
2270 * @param cls closure
2271 * @param tc scheduler context
2274 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2276 struct Socks5Request *s5r = cls;
2278 s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2279 if ((NULL != tc->write_ready) &&
2280 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
2281 (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
2282 sizeof (s5r->wbuf))))
2284 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2285 "Successfully read %d bytes from remote socket\n",
2290 if (0 == s5r->wbuf_len)
2291 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2292 "0 bytes received from remote... graceful shutdown!\n");
2297 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2304 * Adds a socket to MHD
2306 * @param h the handle to the socket to add
2307 * @param daemon the daemon to add the fd to
2308 * @return whatever MHD_add_connection returns
2311 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
2314 struct sockaddr *addr;
2317 fd = dup (GNUNET_NETWORK_get_fd (h));
2318 addr = GNUNET_NETWORK_get_addr (h);
2319 len = GNUNET_NETWORK_get_addrlen (h);
2321 return MHD_add_connection (daemon, fd, addr, len);
2326 * Read file in filename
2328 * @param filename file to read
2329 * @param size pointer where filesize is stored
2330 * @return NULL on error
2333 load_file (const char* filename,
2340 GNUNET_DISK_file_size (filename, &fsize,
2341 GNUNET_YES, GNUNET_YES))
2343 if (fsize > MAX_PEM_SIZE)
2345 *size = (unsigned int) fsize;
2346 buffer = GNUNET_malloc (*size);
2347 if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize))
2349 GNUNET_free (buffer);
2357 * Load PEM key from file
2359 * @param key where to store the data
2360 * @param keyfile path to the PEM file
2361 * @return GNUNET_OK on success
2364 load_key_from_file (gnutls_x509_privkey_t key, const char* keyfile)
2366 gnutls_datum_t key_data;
2369 key_data.data = load_file (keyfile, &key_data.size);
2370 ret = gnutls_x509_privkey_import (key, &key_data,
2371 GNUTLS_X509_FMT_PEM);
2372 if (GNUTLS_E_SUCCESS != ret)
2374 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2375 _("Unable to import private key from file `%s'\n"),
2379 GNUNET_free (key_data.data);
2380 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2385 * Load cert from file
2387 * @param crt struct to store data in
2388 * @param certfile path to pem file
2389 * @return #GNUNET_OK on success
2392 load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
2394 gnutls_datum_t cert_data;
2397 cert_data.data = load_file (certfile, &cert_data.size);
2398 ret = gnutls_x509_crt_import (crt, &cert_data,
2399 GNUTLS_X509_FMT_PEM);
2400 if (GNUTLS_E_SUCCESS != ret)
2402 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2403 _("Unable to import certificate %s\n"), certfile);
2406 GNUNET_free (cert_data.data);
2407 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2412 * Generate new certificate for specific name
2414 * @param name the subject name to generate a cert for
2415 * @return a struct holding the PEM data
2417 static struct ProxyGNSCertificate *
2418 generate_gns_certificate (const char *name)
2421 unsigned int serial;
2422 size_t key_buf_size;
2423 size_t cert_buf_size;
2424 gnutls_x509_crt_t request;
2428 ret = gnutls_x509_crt_init (&request);
2430 if (GNUTLS_E_SUCCESS != ret)
2435 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
2437 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
2439 struct ProxyGNSCertificate *pgc =
2440 GNUNET_new (struct ProxyGNSCertificate);
2442 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
2444 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
2446 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
2448 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
2449 0, name, strlen (name));
2450 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_version (request, 3));
2452 ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
2454 etime = time (NULL);
2455 tm_data = localtime (&etime);
2457 ret = gnutls_x509_crt_set_serial (request,
2461 ret = gnutls_x509_crt_set_activation_time (request,
2464 etime = mktime (tm_data);
2471 ret = gnutls_x509_crt_set_expiration_time (request,
2473 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
2475 ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
2477 key_buf_size = sizeof (pgc->key);
2478 cert_buf_size = sizeof (pgc->cert);
2480 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exporting certificate...\n");
2482 gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
2483 pgc->cert, &cert_buf_size);
2485 gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
2486 pgc->key, &key_buf_size);
2489 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
2490 gnutls_x509_crt_deinit (request);
2498 * Accept policy for mhdaemons
2501 * @param addr the sockaddr
2502 * @param addrlen the sockaddr length
2503 * @return MHD_NO if sockaddr is wrong or number of connections is too high
2506 accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen)
2508 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2509 "In MHD accept policy cb\n");
2513 if (addr->sa_family == AF_UNIX)
2517 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2518 "Connection accepted\n");
2525 * Adds a socket to an SSL MHD instance
2526 * It is important the the domain name is
2527 * correct. In most cases we need to start a new daemon
2529 * @param h the handle to add to a daemon
2530 * @param domain the domain the ssl daemon has to serve
2531 * @return #MHD_YES on success
2534 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, const char* domain)
2536 struct MhdHttpList *hd;
2537 struct ProxyGNSCertificate *pgc;
2538 struct NetworkHandleList *nh;
2540 for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2541 if (0 == strcmp (hd->domain, domain))
2546 pgc = generate_gns_certificate (domain);
2548 hd = GNUNET_new (struct MhdHttpList);
2549 hd->is_ssl = GNUNET_YES;
2550 strcpy (hd->domain, domain);
2551 hd->proxy_cert = pgc;
2554 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2555 "No previous SSL instance found... starting new one for %s\n",
2557 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET,
2560 &create_response, hd,
2561 MHD_OPTION_CONNECTION_LIMIT,
2562 MHD_MAX_CONNECTIONS,
2563 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2564 MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2565 MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2566 MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2567 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2570 GNUNET_assert (hd->daemon != NULL);
2571 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2573 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2576 nh = GNUNET_new (struct NetworkHandleList);
2579 GNUNET_CONTAINER_DLL_insert (hd->socket_handles_head,
2580 hd->socket_handles_tail,
2583 return add_handle_to_mhd (h, hd->daemon);
2588 * Read data from incoming Socks5 connection
2590 * @param cls the closure with the `struct Socks5Request`
2591 * @param tc the scheduler context
2594 do_s5r_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2596 struct Socks5Request *s5r = cls;
2597 struct socks5_client_hello *c_hello;
2598 struct socks5_server_hello *s_hello;
2599 struct socks5_client_request *c_req;
2600 struct socks5_server_response *s_resp;
2605 struct hostent *phost;
2607 struct sockaddr_in remote_addr;
2608 struct in_addr *r_sin_addr;
2609 struct NetworkHandleList *nh;
2611 s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
2612 if ((NULL != tc->write_ready) &&
2613 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
2614 (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
2615 sizeof (s5r->rbuf))))
2617 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2618 "Successfully read %d bytes from socket\n",
2623 if (s5r->rbuf_len != 0)
2624 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
2626 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
2631 if (s5r->state == SOCKS5_INIT)
2633 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2635 c_hello = (struct socks5_client_hello*)&s5r->rbuf;
2637 GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
2639 s_hello = (struct socks5_server_hello*)&s5r->wbuf;
2640 s5r->wbuf_len = sizeof( struct socks5_server_hello );
2642 s_hello->version = c_hello->version;
2643 s_hello->auth_method = SOCKS_AUTH_NONE;
2645 /* Write response to client */
2646 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2650 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2654 s5r->state = SOCKS5_REQUEST;
2658 if (s5r->state == SOCKS5_REQUEST)
2660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2661 "Processing SOCKS5 request\n");
2662 c_req = (struct socks5_client_request*)&s5r->rbuf;
2663 s_resp = (struct socks5_server_response*)&s5r->wbuf;
2664 //Only 10byte for ipv4 response!
2665 s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
2667 GNUNET_assert (c_req->addr_type == 3);
2669 dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
2670 memset(domain, 0, sizeof(domain));
2671 strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
2672 req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
2674 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2675 "Requested connection is %s:%d\n",
2679 if (is_tld (domain, GNUNET_GNS_TLD) ||
2680 is_tld (domain, GNUNET_GNS_TLD_ZKEY))
2682 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2683 "Requested connection is gnunet tld\n",
2687 if (ntohs(req_port) == HTTPS_PORT)
2689 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2690 "Requested connection is HTTPS\n");
2691 ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
2693 else if (NULL != httpd)
2695 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2696 "Requested connection is HTTP\n");
2697 nh = GNUNET_new (struct NetworkHandleList);
2700 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head->socket_handles_head,
2701 mhd_httpd_head->socket_handles_tail,
2704 ret = add_handle_to_mhd ( s5r->sock, httpd );
2709 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2710 _("Failed to start HTTP server\n"));
2711 s_resp->version = 0x05;
2712 s_resp->reply = 0x01;
2713 s5r->cleanup = GNUNET_YES;
2714 s5r->cleanup_sock = GNUNET_YES;
2716 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2722 /* Signal success */
2723 s_resp->version = 0x05;
2724 s_resp->reply = 0x00;
2725 s_resp->reserved = 0x00;
2726 s_resp->addr_type = 0x01;
2728 s5r->cleanup = GNUNET_YES;
2729 s5r->cleanup_sock = GNUNET_NO;
2731 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2739 phost = (struct hostent*)gethostbyname (domain);
2742 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2743 "Resolve %s error!\n", domain );
2744 s_resp->version = 0x05;
2745 s_resp->reply = 0x01;
2746 s5r->cleanup = GNUNET_YES;
2747 s5r->cleanup_sock = GNUNET_YES;
2749 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2755 s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
2758 r_sin_addr = (struct in_addr*)(phost->h_addr);
2759 remote_ip = r_sin_addr->s_addr;
2760 memset(&remote_addr, 0, sizeof(remote_addr));
2761 remote_addr.sin_family = AF_INET;
2762 #if HAVE_SOCKADDR_IN_SIN_LEN
2763 remote_addr.sin_len = sizeof (remote_addr);
2765 remote_addr.sin_addr.s_addr = remote_ip;
2766 remote_addr.sin_port = req_port;
2768 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2769 "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
2773 GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
2774 (const struct sockaddr*)&remote_addr,
2775 sizeof (remote_addr)))
2776 && (errno != EINPROGRESS))
2778 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
2779 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2780 "socket request error...\n");
2781 s_resp->version = 0x05;
2782 s_resp->reply = 0x01;
2784 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2791 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2792 "new remote connection\n");
2794 s_resp->version = 0x05;
2795 s_resp->reply = 0x00;
2796 s_resp->reserved = 0x00;
2797 s_resp->addr_type = 0x01;
2799 s5r->state = SOCKS5_DATA_TRANSFER;
2802 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2806 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2814 if (s5r->state == SOCKS5_DATA_TRANSFER)
2816 if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
2818 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2819 "Closing connection to client\n");
2824 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2825 "forwarding %d bytes from client\n", s5r->rbuf_len);
2828 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2830 &do_write_remote, s5r);
2832 if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
2835 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2837 &do_read_remote, s5r);
2844 * Accept new incoming connections
2846 * @param cls the closure
2847 * @param tc the scheduler context
2850 do_accept (void *cls,
2851 const struct GNUNET_SCHEDULER_TaskContext *tc)
2853 struct GNUNET_NETWORK_Handle *s;
2854 struct Socks5Request *s5r;
2856 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2859 s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
2862 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
2865 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2866 "Got an inbound connection, waiting for data\n");
2867 s5r = GNUNET_new (struct Socks5Request);
2868 GNUNET_CONTAINER_DLL_insert (s5r_head,
2872 s5r->state = SOCKS5_INIT;
2873 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2880 * Task run on shutdown
2882 * @param cls closure
2883 * @param tc task context
2886 do_shutdown (void *cls,
2887 const struct GNUNET_SCHEDULER_TaskContext *tc)
2889 struct MhdHttpList *hd;
2890 struct MhdHttpList *tmp_hd;
2891 struct NetworkHandleList *nh;
2892 struct NetworkHandleList *tmp_nh;
2893 struct ProxyCurlTask *ctask;
2894 struct ProxyCurlTask *ctask_tmp;
2895 struct ProxyUploadData *pdata;
2897 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2898 "Shutting down...\n");
2899 for (hd = mhd_httpd_head; NULL != hd; hd = tmp_hd)
2902 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2904 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2905 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2907 if (NULL != hd->daemon)
2909 MHD_stop_daemon (hd->daemon);
2912 for (nh = hd->socket_handles_head; NULL != nh; nh = tmp_nh)
2915 GNUNET_NETWORK_socket_close (nh->h);
2918 GNUNET_free_non_null (hd->proxy_cert);
2921 for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
2923 ctask_tmp = ctask->next;
2924 if (NULL != ctask->curl)
2926 curl_easy_cleanup (ctask->curl);
2929 if (NULL != ctask->headers)
2931 curl_slist_free_all (ctask->headers);
2932 ctask->headers = NULL;
2934 if (NULL != ctask->resolver)
2936 curl_slist_free_all (ctask->resolver);
2937 ctask->resolver = NULL;
2939 if (NULL != ctask->response)
2941 MHD_destroy_response (ctask->response);
2942 ctask->response = NULL;
2944 for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
2946 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
2947 ctask->upload_data_tail,
2949 GNUNET_free_non_null (pdata->filename);
2950 GNUNET_free_non_null (pdata->content_type);
2951 GNUNET_free_non_null (pdata->key);
2952 GNUNET_free_non_null (pdata->value);
2953 GNUNET_free (pdata);
2955 GNUNET_free (ctask);
2959 GNUNET_NETWORK_socket_close (lsock);
2964 GNUNET_IDENTITY_cancel (id_op);
2967 if (NULL != identity)
2969 GNUNET_IDENTITY_disconnect (identity);
2972 if (NULL != curl_multi)
2974 curl_multi_cleanup (curl_multi);
2977 if (NULL != gns_handle)
2979 GNUNET_GNS_disconnect (gns_handle);
2982 if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
2984 GNUNET_SCHEDULER_cancel (curl_download_task);
2985 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
2987 if (GNUNET_SCHEDULER_NO_TASK != ltask)
2989 GNUNET_SCHEDULER_cancel (ltask);
2990 ltask = GNUNET_SCHEDULER_NO_TASK;
2992 gnutls_global_deinit ();
2997 * Continue initialization after we have our zone information.
3002 struct MhdHttpList *hd;
3003 struct sockaddr_in sa;
3005 /* Open listen socket for socks proxy */
3006 /* FIXME: support IPv6! */
3007 memset (&sa, 0, sizeof (sa));
3008 sa.sin_family = AF_INET;
3009 sa.sin_port = htons (port);
3010 #if HAVE_SOCKADDR_IN_SIN_LEN
3011 sa.sin_len = sizeof (sa);
3013 lsock = GNUNET_NETWORK_socket_create (AF_INET,
3018 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
3019 GNUNET_SCHEDULER_shutdown ();
3023 GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
3026 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
3027 GNUNET_SCHEDULER_shutdown ();
3030 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
3032 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
3035 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3036 lsock, &do_accept, NULL);
3038 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
3040 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3041 "cURL global init failed!\n");
3042 GNUNET_SCHEDULER_shutdown ();
3045 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3046 "Proxy listens on port %u\n",
3049 /* start MHD daemon for HTTP */
3050 hd = GNUNET_new (struct MhdHttpList);
3051 strcpy (hd->domain, "");
3052 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
3055 &create_response, hd,
3056 MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3057 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3058 MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
3059 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3061 if (NULL == hd->daemon)
3064 GNUNET_SCHEDULER_shutdown ();
3068 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
3070 /* start loop running all MHD instances */
3076 * Method called to inform about the egos of the shorten zone of this peer.
3078 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
3079 * this function is only called ONCE, and 'NULL' being passed in
3080 * @a ego does indicate an error (i.e. name is taken or no default
3081 * value is known). If @a ego is non-NULL and if '*ctx'
3082 * is set in those callbacks, the value WILL be passed to a subsequent
3083 * call to the identity callback of #GNUNET_IDENTITY_connect (if
3084 * that one was not NULL).
3086 * @param cls closure, NULL
3087 * @param ego ego handle
3088 * @param ctx context for application to store data for this ego
3089 * (during the lifetime of this process, initially NULL)
3090 * @param name name assigned by the user for this ego,
3091 * NULL if the user just deleted the ego and it
3092 * must thus no longer be used
3095 identity_shorten_cb (void *cls,
3096 struct GNUNET_IDENTITY_Ego *ego,
3103 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3104 _("No ego configured for `shorten-zone`\n"));
3108 local_shorten_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
3109 do_shorten = GNUNET_YES;
3116 * Method called to inform about the egos of the master zone of this peer.
3118 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
3119 * this function is only called ONCE, and 'NULL' being passed in
3120 * @a ego does indicate an error (i.e. name is taken or no default
3121 * value is known). If @a ego is non-NULL and if '*ctx'
3122 * is set in those callbacks, the value WILL be passed to a subsequent
3123 * call to the identity callback of #GNUNET_IDENTITY_connect (if
3124 * that one was not NULL).
3126 * @param cls closure, NULL
3127 * @param ego ego handle
3128 * @param ctx context for application to store data for this ego
3129 * (during the lifetime of this process, initially NULL)
3130 * @param name name assigned by the user for this ego,
3131 * NULL if the user just deleted the ego and it
3132 * must thus no longer be used
3135 identity_master_cb (void *cls,
3136 struct GNUNET_IDENTITY_Ego *ego,
3143 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3144 _("No ego configured for `master-zone`\n"));
3145 GNUNET_SCHEDULER_shutdown ();
3148 GNUNET_IDENTITY_ego_get_public_key (ego,
3150 id_op = GNUNET_IDENTITY_get (identity,
3152 &identity_shorten_cb,
3158 * Main function that will be run
3160 * @param cls closure
3161 * @param args remaining command-line arguments
3162 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3163 * @param c configuration
3166 run (void *cls, char *const *args, const char *cfgfile,
3167 const struct GNUNET_CONFIGURATION_Handle *c)
3169 char* cafile_cfg = NULL;
3173 if (NULL == (curl_multi = curl_multi_init ()))
3175 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3176 "Failed to create cURL multi handle!\n");
3179 cafile = cafile_opt;
3182 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3186 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
3191 cafile = cafile_cfg;
3193 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3194 "Using %s as CA\n", cafile);
3196 gnutls_global_init ();
3197 gnutls_x509_crt_init (&proxy_ca.cert);
3198 gnutls_x509_privkey_init (&proxy_ca.key);
3200 if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
3201 (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
3203 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3204 _("Failed to load SSL/TLS key and certificate from `%s'\n"),
3206 // FIXME: release resources...
3207 GNUNET_free_non_null (cafile_cfg);
3210 GNUNET_free_non_null (cafile_cfg);
3211 if (NULL == (gns_handle = GNUNET_GNS_connect (cfg)))
3213 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3214 "Unable to connect to GNS!\n");
3217 identity = GNUNET_IDENTITY_connect (cfg,
3219 id_op = GNUNET_IDENTITY_get (identity,
3221 &identity_master_cb,
3223 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
3224 &do_shutdown, NULL);
3229 * The main function for gnunet-gns-proxy.
3231 * @param argc number of arguments from the command line
3232 * @param argv command line arguments
3233 * @return 0 ok, 1 on error
3236 main (int argc, char *const *argv)
3238 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
3240 gettext_noop ("listen on specified port (default: 7777)"), 1,
3241 &GNUNET_GETOPT_set_ulong, &port},
3242 {'a', "authority", NULL,
3243 gettext_noop ("pem file to use as CA"), 1,
3244 &GNUNET_GETOPT_set_string, &cafile_opt},
3245 GNUNET_GETOPT_OPTION_END
3247 static const char* page =
3248 "<html><head><title>gnunet-gns-proxy</title>"
3249 "</head><body>cURL fail</body></html>";
3252 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
3254 GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
3255 curl_failure_response = MHD_create_response_from_buffer (strlen (page),
3257 MHD_RESPMEM_PERSISTENT);
3261 GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
3262 _("GNUnet GNS proxy"),
3264 &run, NULL)) ? 0 : 1;
3265 MHD_destroy_response (curl_failure_response);
3266 GNUNET_free_non_null ((char *) argv);
3270 /* end of gnunet-gns-proxy.c */