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 * @author Christian Grothoff
23 * @file src/gns/gnunet-gns-proxy.c
24 * @brief HTTP(S) proxy that rewrites URIs and fakes certificats to make GNS work
25 * with legacy browsers
28 * - make DNS lookup asynchronous
29 * - simplify POST/PUT processing
30 * - double-check queueing logic
31 * - figure out what to do with the 'authority' issue
35 #include <microhttpd.h>
36 #include <curl/curl.h>
37 #include <gnutls/gnutls.h>
38 #include <gnutls/x509.h>
39 #include <gnutls/abstract.h>
40 #include <gnutls/crypto.h>
42 #include "gnunet_util_lib.h"
43 #include "gnunet_gns_service.h"
44 #include "gnunet_identity_service.h"
49 * Default Socks5 listen port.
51 #define GNUNET_GNS_PROXY_PORT 7777
53 #define MHD_MAX_CONNECTIONS 300
56 * Maximum supported length for a URI.
58 #define MAX_HTTP_URI_LENGTH 2048
60 #define POSTBUFFERSIZE 4096
63 * Port for plaintext HTTP.
70 #define HTTPS_PORT 443
73 * Largest allowed size for a PEM certificate.
75 #define MAX_PEM_SIZE (10 * 1024)
81 * @param level log level
82 * @param fun name of curl_easy-function that gave the error
83 * @param rc return code from curl
85 #define LOG_CURL_EASY(level,fun,rc) GNUNET_log(level, _("%s failed at %s:%d: `%s'\n"), fun, __FILE__, __LINE__, curl_easy_strerror (rc))
89 * Which SOCKS version do we speak?
91 #define SOCKS_VERSION_5 0x05
94 * Flag to set for 'no authentication'.
96 #define SOCKS_AUTH_NONE 0
105 * We're waiting to get the request.
123 * State machine for the IO buffer.
133 * A structure for CA cert/key
140 gnutls_x509_crt_t cert;
145 gnutls_x509_privkey_t key;
150 * Structure for GNS certificates
152 struct ProxyGNSCertificate
155 * The certificate as PEM
157 char cert[MAX_PEM_SIZE];
160 * The private key as PEM
162 char key[MAX_PEM_SIZE];
167 * A structure for socks requests
175 struct Socks5Request *next;
180 struct Socks5Request *prev;
185 struct GNUNET_NETWORK_Handle *sock;
190 struct GNUNET_NETWORK_Handle *remote_sock;
195 enum SocksPhase state;
198 * Client socket read task
200 GNUNET_SCHEDULER_TaskIdentifier rtask;
203 * Server socket read task
205 GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
208 * Client socket write task
210 GNUNET_SCHEDULER_TaskIdentifier wtask;
213 * Server socket write task
215 GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
228 * Length of data in read buffer
230 unsigned int rbuf_len;
233 * Length of data in write buffer
235 unsigned int wbuf_len;
238 * This handle is scheduled for cleanup?
243 * Shall we close the client socket on cleanup?
250 * A structure for all running Httpds
257 struct MhdHttpList *prev;
262 struct MhdHttpList *next;
265 * is this an ssl daemon?
270 * the domain name to server (only important for SSL)
277 struct MHD_Daemon *daemon;
280 * Optional proxy certificate used
282 struct ProxyGNSCertificate *proxy_cert;
287 GNUNET_SCHEDULER_TaskIdentifier httpd_task;
293 * A structure for MHD<->cURL streams
300 struct ProxyCurlTask *prev;
305 struct ProxyCurlTask *next;
313 * Optional header replacements for curl (LEHO)
315 struct curl_slist *headers;
318 * Optional resolver replacements for curl (LEHO)
320 struct curl_slist *resolver;
325 long curl_response_code;
330 char url[MAX_HTTP_URI_LENGTH];
333 * The cURL write buffer / MHD read buffer
335 char buffer[CURL_MAX_WRITE_SIZE];
338 * Read pos of the data in the buffer
340 char *buffer_read_ptr;
343 * Write pos in the buffer
345 char *buffer_write_ptr;
350 struct MHD_Connection *connection;
355 size_t put_read_offset;
356 size_t put_read_size;
361 struct MHD_PostProcessor *post_handler;
364 struct ProxyUploadData *upload_data_head;
365 struct ProxyUploadData *upload_data_tail;
368 * the type of POST encoding
372 struct curl_httppost *httppost;
374 struct curl_httppost *httppost_last;
377 * Number of bytes in buffer
379 unsigned int bytes_in_buffer;
382 GNUNET_SCHEDULER_TaskIdentifier pp_task;
384 /* The associated daemon list entry */
385 struct MhdHttpList *mhd;
387 /* The associated response */
388 struct MHD_Response *response;
391 struct ProxySetCookieHeader *set_cookies_head;
394 struct ProxySetCookieHeader *set_cookies_tail;
397 * The authority of the corresponding host (site of origin)
402 * The hostname (Host header field)
407 * The LEgacy HOstname (can be empty)
417 * The buffer status (BUF_WAIT_FOR_CURL or BUF_WAIT_FOR_MHD)
419 enum BufferStatus buf_status;
442 * Indicates wheather the download is in progress
444 int download_in_progress;
447 * Indicates wheather the download was successful
449 int download_is_finished;
452 * Indicates wheather the download failed
464 * Struct for set-cookies
466 struct ProxySetCookieHeader
471 struct ProxySetCookieHeader *next;
476 struct ProxySetCookieHeader *prev;
486 * Post data structure
488 struct ProxyUploadData
493 struct ProxyUploadData *next;
498 struct ProxyUploadData *prev;
506 size_t content_length;
527 * Client hello in Socks5 protocol.
529 struct Socks5ClientHelloMessage
532 * Should be #SOCKS_VERSION_5.
537 * How many authentication methods does the client support.
539 uint8_t num_auth_methods;
542 * FIXME: this is not a message format...
549 * Client socks request in Socks5 protocol.
551 struct Socks5ClientRequestMessage
554 * Should be #SOCKS_VERSION_5.
559 * FIXME: what goes here?
564 * FIXME: what goes here?
569 * FIXME: what goes here?
574 * followed by either an ip4/ipv6 address
575 * or a domain name with a length field in front
581 * Server hello in Socks5 protocol.
583 struct Socks5ServerHelloMessage
586 * Should be #SOCKS_VERSION_5.
591 * FIXME: what goes here?
598 * Server response to client requests in Socks5 protocol.
600 struct Socks5ServerResponseMessage
603 * Should be #SOCKS_VERSION_5.
608 * FIXME: what goes here?
613 * FIXME: what goes here?
618 * FIXME: what goes here?
623 * FIXME: what goes here?
625 uint8_t add_port[18];
630 * The port the proxy is running on (default 7777)
632 static unsigned long port = GNUNET_GNS_PROXY_PORT;
635 * The CA file (pem) to use for the proxy CA
637 static char* cafile_opt;
640 * The listen socket of the proxy
642 static struct GNUNET_NETWORK_Handle *lsock;
647 static GNUNET_SCHEDULER_TaskIdentifier ltask;
650 * The cURL download task
652 static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
655 * Number of current mhd connections
657 static unsigned int total_mhd_connections;
660 * The cURL multi handle
662 static CURLM *curl_multi;
665 * Handle to the GNS service
667 static struct GNUNET_GNS_Handle *gns_handle;
670 * DLL for ProxyCurlTasks
672 static struct ProxyCurlTask *ctasks_head;
675 * DLL for ProxyCurlTasks
677 static struct ProxyCurlTask *ctasks_tail;
680 * DLL for http/https daemons
682 static struct MhdHttpList *mhd_httpd_head;
685 * DLL for http/https daemons
687 static struct MhdHttpList *mhd_httpd_tail;
690 * DLL of active socks requests.
692 static struct Socks5Request *s5r_head;
695 * DLL of active socks requests.
697 static struct Socks5Request *s5r_tail;
700 * The users local GNS master zone
702 static struct GNUNET_CRYPTO_EccPublicSignKey local_gns_zone;
705 * The users local shorten zone
707 static struct GNUNET_CRYPTO_EccPrivateKey local_shorten_zone;
710 * Is shortening enabled?
712 static int do_shorten;
715 * The CA for SSL certificate generation
717 static struct ProxyCA proxy_ca;
720 * Daemon for HTTP (we have one per SSL certificate, and then one for all HTTP connections;
721 * this is the one for HTTP, not HTTPS).
723 static struct MHD_Daemon *httpd;
726 * Shorten zone private key
728 static struct GNUNET_CRYPTO_EccPrivateKey shorten_zonekey;
731 * Response we return on cURL failures.
733 static struct MHD_Response *curl_failure_response;
736 * Connection to identity service.
738 static struct GNUNET_IDENTITY_Handle *identity;
741 * Request for our ego.
743 static struct GNUNET_IDENTITY_Operation *id_op;
748 static const struct GNUNET_CONFIGURATION_Handle *cfg;
751 * Clean up s5r handles
753 * @param s5r the handle to destroy
756 cleanup_s5r (struct Socks5Request *s5r)
758 if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
759 GNUNET_SCHEDULER_cancel (s5r->rtask);
760 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdwtask)
761 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
762 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
763 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
764 if (NULL != s5r->remote_sock)
765 GNUNET_NETWORK_socket_close (s5r->remote_sock);
766 if ( (NULL != s5r->sock) &&
767 (GNUNET_YES == s5r->cleanup_sock) )
768 GNUNET_NETWORK_socket_close (s5r->sock);
769 GNUNET_CONTAINER_DLL_remove (s5r_head,
777 * Checks if name is in tld
779 * @param name the name to check
780 * @param tld the TLD to check for (must NOT begin with ".")
781 * @return #GNUNET_YES or #GNUNET_NO
784 is_tld (const char* name, const char* tld)
786 size_t name_len = strlen (name);
787 size_t tld_len = strlen (tld);
789 GNUNET_break ('.' != tld[0]);
790 return ( (tld_len < name_len) &&
791 ( ('.' == name[name_len - tld_len - 1]) || (name_len == tld_len) ) &&
793 name + (name_len - tld_len),
799 con_post_data_iter (void *cls,
800 enum MHD_ValueKind kind,
802 const char *filename,
803 const char *content_type,
804 const char *transfer_encoding,
809 struct ProxyCurlTask* ctask = cls;
810 struct ProxyUploadData* pdata;
814 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
815 "Got POST data (file: %s, content type: %s): '%s=%.*s' at offset %llu size %llu\n",
816 filename, content_type,
817 key, (int) size, data,
818 (unsigned long long) off,
819 (unsigned long long) size);
820 GNUNET_assert (NULL != ctask->post_type);
822 if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
825 ctask->is_httppost = GNUNET_YES;
829 pdata = GNUNET_new (struct ProxyUploadData);
830 pdata->key = GNUNET_strdup (key);
832 if (NULL != filename)
833 pdata->filename = GNUNET_strdup (filename);
834 if (NULL != content_type)
835 pdata->content_type = GNUNET_strdup (content_type);
836 pdata->value = GNUNET_malloc (size);
837 pdata->total_bytes = size;
838 memcpy (pdata->value, data, size);
839 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
840 ctask->upload_data_tail,
843 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
844 "Copied %llu bytes of POST Data\n",
845 (unsigned long long) size);
849 pdata = ctask->upload_data_tail;
850 new_value = GNUNET_malloc (size + pdata->total_bytes);
851 memcpy (new_value, pdata->value, pdata->total_bytes);
852 memcpy (new_value+off, data, size);
853 GNUNET_free (pdata->value);
854 pdata->value = new_value;
855 pdata->total_bytes += size;
860 if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
866 ctask->is_httppost = GNUNET_NO;
868 if (NULL != ctask->curl)
869 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
873 enc = curl_easy_escape (ctask->curl, key, 0);
880 pdata = GNUNET_new (struct ProxyUploadData);
881 pdata->value = GNUNET_malloc (strlen (enc) + 3);
882 if (NULL != ctask->upload_data_head)
884 pdata->value[0] = '&';
885 memcpy (pdata->value+1, enc, strlen (enc));
888 memcpy (pdata->value, enc, strlen (enc));
889 pdata->value[strlen (pdata->value)] = '=';
890 pdata->bytes_left = strlen (pdata->value);
891 pdata->total_bytes = pdata->bytes_left;
894 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
895 "Escaped POST key: '%s'\n",
898 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
899 ctask->upload_data_tail,
904 enc = curl_easy_escape (ctask->curl, data, 0);
910 pdata = GNUNET_new (struct ProxyUploadData);
911 pdata->value = GNUNET_malloc (strlen (enc) + 1);
912 memcpy (pdata->value, enc, strlen (enc));
913 pdata->bytes_left = strlen (pdata->value);
914 pdata->total_bytes = pdata->bytes_left;
917 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
918 "Escaped POST value: '%s'\n",
921 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
922 ctask->upload_data_tail,
929 * Read HTTP request header field 'Host'
931 * @param cls buffer to write to
932 * @param kind value kind
933 * @param key field key
934 * @param value field value
935 * @return #MHD_NO when Host found
938 con_val_iter (void *cls,
939 enum MHD_ValueKind kind,
943 struct ProxyCurlTask *ctask = cls;
944 char* buf = ctask->host;
950 if (0 == strcmp ("Host", key))
952 port = strchr (value, ':');
955 strncpy (buf, value, port-value);
957 if ((1 != sscanf (port, "%u", &uport)) ||
958 (uport > UINT16_MAX) ||
960 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
961 "Unable to parse port!\n");
963 ctask->port = (uint16_t) uport;
970 if (0 == strcmp (MHD_HTTP_HEADER_ACCEPT_ENCODING, key))
975 if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_TYPE,
978 if (0 == strncasecmp (value,
979 MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
980 strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
981 ctask->post_type = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
982 else if (0 == strncasecmp (value,
983 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
984 strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
985 ctask->post_type = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
987 ctask->post_type = NULL;
991 cstr = GNUNET_malloc (strlen (key) + strlen (hdr_val) + 3);
992 GNUNET_snprintf (cstr, strlen (key) + strlen (hdr_val) + 3,
993 "%s: %s", key, hdr_val);
995 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
996 "Client Header: %s\n", cstr);
998 ctask->headers = curl_slist_append (ctask->headers, cstr);
1006 * Callback for MHD response
1008 * @param cls closure
1009 * @param pos in buffer
1011 * @param max space in buffer
1012 * @return number of bytes written
1015 mhd_content_cb (void *cls,
1022 * Check HTTP response header for mime
1024 * @param buffer curl buffer
1025 * @param size curl blocksize
1026 * @param nmemb curl blocknumber
1028 * @return size of read bytes
1031 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
1033 size_t bytes = size * nmemb;
1034 struct ProxyCurlTask *ctask = cls;
1035 int cookie_hdr_len = strlen (MHD_HTTP_HEADER_SET_COOKIE);
1036 char hdr_generic[bytes+1];
1037 char new_cookie_hdr[bytes+strlen (ctask->leho)+1];
1038 char new_location[MAX_HTTP_URI_LENGTH+500];
1039 char real_host[264];
1040 char leho_host[264];
1043 char* cookie_domain;
1048 char cors_hdr[strlen (ctask->leho) + strlen ("https://")];
1050 if (NULL == ctask->response)
1052 /* FIXME: get total size from curl (if available) */
1053 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1054 "Creating response for %s\n", ctask->url);
1055 ctask->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
1056 sizeof (ctask->buffer),
1061 /* if we have a leho add a CORS header */
1062 if (0 != strcmp ("", ctask->leho))
1064 /* We could also allow ssl and http here */
1065 if (ctask->mhd->is_ssl)
1066 sprintf (cors_hdr, "https://%s", ctask->leho);
1068 sprintf (cors_hdr, "http://%s", ctask->leho);
1070 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1071 "MHD: Adding CORS header field %s\n",
1074 if (GNUNET_NO == MHD_add_response_header (ctask->response,
1075 "Access-Control-Allow-Origin",
1078 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1079 "MHD: Error adding CORS header field %s\n",
1083 ctask->ready_to_queue = GNUNET_YES;
1085 if (cookie_hdr_len > bytes)
1088 memcpy (hdr_generic, buffer, bytes);
1089 hdr_generic[bytes] = '\0';
1091 if ('\n' == hdr_generic[bytes-1])
1092 hdr_generic[bytes-1] = '\0';
1094 if (hdr_generic[bytes-2] == '\r')
1095 hdr_generic[bytes-2] = '\0';
1097 if (0 == memcmp (hdr_generic,
1098 MHD_HTTP_HEADER_SET_COOKIE,
1101 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1102 "Looking for cookie in: `%s'\n", hdr_generic);
1103 ndup = GNUNET_strdup (hdr_generic+cookie_hdr_len+1);
1104 memset (new_cookie_hdr, 0, sizeof (new_cookie_hdr));
1105 for (tok = strtok (ndup, ";"); tok != NULL; tok = strtok (NULL, ";"))
1107 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1108 "Got Cookie token: %s\n", tok);
1109 //memcpy (new_cookie_hdr+offset, tok, strlen (tok));
1110 if (0 == memcmp (tok, " domain", strlen (" domain")))
1112 cookie_domain = tok + strlen (" domain") + 1;
1114 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1115 "Got Set-Cookie Domain: %s\n", cookie_domain);
1117 if (strlen (cookie_domain) < strlen (ctask->leho))
1119 delta_cdomain = strlen (ctask->leho) - strlen (cookie_domain);
1120 if (0 == strcmp (cookie_domain, ctask->leho + (delta_cdomain)))
1122 GNUNET_snprintf (new_cookie_hdr+offset,
1123 sizeof (new_cookie_hdr),
1124 " domain=%s", ctask->authority);
1125 offset += strlen (" domain=") + strlen (ctask->authority);
1126 new_cookie_hdr[offset] = ';';
1131 else if (strlen (cookie_domain) == strlen (ctask->leho))
1133 if (0 == strcmp (cookie_domain, ctask->leho))
1135 GNUNET_snprintf (new_cookie_hdr+offset,
1136 sizeof (new_cookie_hdr),
1137 " domain=%s", ctask->host);
1138 offset += strlen (" domain=") + strlen (ctask->host);
1139 new_cookie_hdr[offset] = ';';
1144 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1145 "Cookie domain invalid\n");
1149 memcpy (new_cookie_hdr+offset, tok, strlen (tok));
1150 offset += strlen (tok);
1151 new_cookie_hdr[offset] = ';';
1157 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1158 "Got Set-Cookie HTTP header %s\n", new_cookie_hdr);
1160 if (GNUNET_NO == MHD_add_response_header (ctask->response,
1161 MHD_HTTP_HEADER_SET_COOKIE,
1164 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1165 "MHD: Error adding set-cookie header field %s\n",
1166 hdr_generic+cookie_hdr_len+1);
1171 ndup = GNUNET_strdup (hdr_generic);
1172 hdr_type = strtok (ndup, ":");
1174 if (NULL == hdr_type)
1180 hdr_val = strtok (NULL, "");
1182 if (NULL == hdr_val)
1190 if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
1192 if (ctask->mhd->is_ssl)
1194 sprintf (leho_host, "https://%s", ctask->leho);
1195 sprintf (real_host, "https://%s", ctask->host);
1199 sprintf (leho_host, "http://%s", ctask->leho);
1200 sprintf (real_host, "http://%s", ctask->host);
1203 if (0 == memcmp (leho_host, hdr_val, strlen (leho_host)))
1205 sprintf (new_location, "%s%s", real_host, hdr_val+strlen (leho_host));
1206 hdr_val = new_location;
1210 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1211 "Trying to set %s: %s\n",
1214 if (GNUNET_NO == MHD_add_response_header (ctask->response,
1218 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1219 "MHD: Error adding %s header field %s\n",
1231 * @param hd a http daemon list entry
1234 run_httpd (struct MhdHttpList *hd);
1246 * Task run whenever HTTP server operations are pending.
1249 * @param tc sched context
1252 do_httpd (void *cls,
1253 const struct GNUNET_SCHEDULER_TaskContext *tc);
1257 run_mhd_now (struct MhdHttpList *hd)
1259 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
1261 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1262 "MHD: killing old task\n");
1263 GNUNET_SCHEDULER_cancel (hd->httpd_task);
1265 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1266 "MHD: Scheduling MHD now\n");
1267 hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, hd);
1272 * Ask cURL for the select sets and schedule download
1275 curl_download_prepare (void);
1279 * Callback to free content
1281 * @param cls content to free
1282 * @param tc task context
1285 mhd_content_free (void *cls,
1286 const struct GNUNET_SCHEDULER_TaskContext *tc)
1288 struct ProxyCurlTask *ctask = cls;
1289 struct ProxyUploadData *pdata;
1291 if (NULL != ctask->headers)
1292 curl_slist_free_all (ctask->headers);
1294 if (NULL != ctask->headers)
1295 curl_slist_free_all (ctask->resolver);
1297 if (NULL != ctask->response)
1298 MHD_destroy_response (ctask->response);
1300 if (NULL != ctask->post_handler)
1301 MHD_destroy_post_processor (ctask->post_handler);
1303 if (GNUNET_SCHEDULER_NO_TASK != ctask->pp_task)
1304 GNUNET_SCHEDULER_cancel (ctask->pp_task);
1306 for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
1308 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1309 ctask->upload_data_tail,
1311 GNUNET_free_non_null (pdata->filename);
1312 GNUNET_free_non_null (pdata->content_type);
1313 GNUNET_free_non_null (pdata->key);
1314 GNUNET_free_non_null (pdata->value);
1315 GNUNET_free (pdata);
1317 GNUNET_free (ctask);
1322 * Callback for MHD response
1324 * @param cls closure
1325 * @param pos in buffer
1327 * @param max space in buffer
1328 * @return number of bytes written
1331 mhd_content_cb (void *cls,
1336 struct ProxyCurlTask *ctask = cls;
1338 size_t bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1340 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1341 "MHD: content cb for %s. To copy: %u\n",
1342 ctask->url, (unsigned int) bytes_to_copy);
1343 if ((GNUNET_YES == ctask->download_is_finished) &&
1344 (GNUNET_NO == ctask->download_error) &&
1345 (0 == bytes_to_copy))
1347 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1348 "MHD: sending response for %s\n", ctask->url);
1349 ctask->download_in_progress = GNUNET_NO;
1350 run_mhd_now (ctask->mhd);
1351 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1352 total_mhd_connections--;
1353 return MHD_CONTENT_READER_END_OF_STREAM;
1356 if ((GNUNET_YES == ctask->download_error) &&
1357 (GNUNET_YES == ctask->download_is_finished) &&
1358 (0 == bytes_to_copy))
1360 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1361 "MHD: sending error response\n");
1362 ctask->download_in_progress = GNUNET_NO;
1363 run_mhd_now (ctask->mhd);
1364 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1365 total_mhd_connections--;
1366 return MHD_CONTENT_READER_END_WITH_ERROR;
1369 if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
1373 bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1375 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1376 "MHD: copied: %d left: %u, space left in buf: %d\n",
1378 (unsigned int) bytes_to_copy, (int) (max - copied));
1380 if (GNUNET_NO == ctask->download_is_finished)
1382 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1383 "MHD: Purging buffer\n");
1384 memmove (ctask->buffer, ctask->buffer_read_ptr, bytes_to_copy);
1385 ctask->buffer_read_ptr = ctask->buffer;
1386 ctask->buffer_write_ptr = ctask->buffer + bytes_to_copy;
1387 ctask->buffer[bytes_to_copy] = '\0';
1390 if (bytes_to_copy + copied > max)
1391 bytes_to_copy = max - copied;
1392 memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1393 ctask->buffer_read_ptr += bytes_to_copy;
1394 copied += bytes_to_copy;
1395 ctask->buf_status = BUF_WAIT_FOR_CURL;
1397 if (NULL != ctask->curl)
1398 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
1400 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1401 "MHD: copied %d bytes\n", (int) copied);
1402 run_mhd_now (ctask->mhd);
1408 * Handle data from cURL
1410 * @param ptr pointer to the data
1411 * @param size number of blocks of data
1412 * @param nmemb blocksize
1413 * @param ctx the curlproxytask
1414 * @return number of bytes handled
1417 curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx)
1419 const char *cbuf = ptr;
1420 size_t total = size * nmemb;
1421 struct ProxyCurlTask *ctask = ctx;
1422 size_t buf_space = sizeof (ctask->buffer) - (ctask->buffer_write_ptr - ctask->buffer);
1424 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1425 "CURL: Got %d. %d free in buffer\n",
1430 ctask->buf_status = BUF_WAIT_FOR_MHD;
1431 run_mhd_now (ctask->mhd);
1432 return CURL_WRITEFUNC_PAUSE;
1434 if (total > buf_space)
1436 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1437 "CURL: Copying %d bytes to buffer (%s)\n",
1439 memcpy (ctask->buffer_write_ptr, cbuf, total);
1440 ctask->bytes_in_buffer += total;
1441 ctask->buffer_write_ptr += total;
1442 if (ctask->bytes_in_buffer > 0)
1444 ctask->buf_status = BUF_WAIT_FOR_MHD;
1445 run_mhd_now (ctask->mhd);
1452 * cURL callback for put data
1455 put_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1457 struct ProxyCurlTask *ctask = cls;
1458 struct ProxyUploadData *pdata = ctask->upload_data_head;
1459 size_t len = size * nmemb;
1463 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1464 "CURL: put read callback\n");
1467 return CURL_READFUNC_PAUSE;
1470 if (NULL == pdata->value)
1472 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1473 "CURL: Terminating PUT\n");
1475 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1476 ctask->upload_data_tail,
1478 GNUNET_free (pdata);
1482 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1483 "CURL: read callback value %s\n", pdata->value);
1485 to_copy = pdata->bytes_left;
1489 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1490 memcpy (buf, pos, to_copy);
1491 pdata->bytes_left -= to_copy;
1492 if (pdata->bytes_left <= 0)
1494 GNUNET_free (pdata->value);
1495 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1496 ctask->upload_data_tail,
1498 GNUNET_free (pdata);
1505 * cURL callback for post data
1508 post_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1510 struct ProxyCurlTask *ctask = cls;
1511 struct ProxyUploadData *pdata = ctask->upload_data_head;
1512 size_t len = size * nmemb;
1516 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1517 "CURL: read callback\n");
1520 return CURL_READFUNC_PAUSE;
1523 if (NULL == pdata->value)
1525 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1526 "CURL: Terminating POST data\n");
1528 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1529 ctask->upload_data_tail,
1531 GNUNET_free (pdata);
1535 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1536 "CURL: read callback value %s\n", pdata->value);
1538 to_copy = pdata->bytes_left;
1542 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1543 memcpy (buf, pos, to_copy);
1544 pdata->bytes_left -= to_copy;
1545 if (pdata->bytes_left <= 0)
1547 GNUNET_free (pdata->value);
1548 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1549 ctask->upload_data_tail,
1551 GNUNET_free (pdata);
1558 * Task that is run when we are ready to receive more data
1561 * @param cls closure
1562 * @param tc task context
1565 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1569 * Ask cURL for the select sets and schedule download
1572 curl_download_prepare ()
1579 struct GNUNET_NETWORK_FDSet *grs;
1580 struct GNUNET_NETWORK_FDSet *gws;
1582 struct GNUNET_TIME_Relative rtime;
1588 if (CURLM_OK != (mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max)))
1590 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1591 "%s failed at %s:%d: `%s'\n",
1592 "curl_multi_fdset", __FILE__, __LINE__,
1593 curl_multi_strerror (mret));
1594 //TODO cleanup here?
1598 GNUNET_break (CURLM_OK == curl_multi_timeout (curl_multi, &to));
1599 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1600 "cURL multi fds: max=%d timeout=%lld\n", max, (long long) to);
1602 rtime = GNUNET_TIME_UNIT_FOREVER_REL;
1604 rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1605 grs = GNUNET_NETWORK_fdset_create ();
1606 gws = GNUNET_NETWORK_fdset_create ();
1607 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1608 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1609 if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
1610 GNUNET_SCHEDULER_cancel (curl_download_task);
1613 curl_download_task =
1614 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1617 &curl_task_download, curl_multi);
1619 else if (NULL != ctasks_head)
1621 /* as specified in curl docs */
1622 curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
1623 &curl_task_download,
1626 GNUNET_NETWORK_fdset_destroy (gws);
1627 GNUNET_NETWORK_fdset_destroy (grs);
1632 * Task that is run when we are ready to receive more data
1635 * @param cls closure
1636 * @param tc task context
1639 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1643 struct CURLMsg *msg;
1645 struct ProxyCurlTask *ctask;
1648 struct ProxyCurlTask *clean_head = NULL;
1649 struct ProxyCurlTask *clean_tail = NULL;
1651 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1653 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1655 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1656 "Shutdown requested while trying to download\n");
1660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1668 mret = curl_multi_perform (curl_multi, &running);
1670 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1671 "Running curl tasks: %d\n", running);
1672 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1674 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1675 "CTask: %s\n", ctask->url);
1682 msg = curl_multi_info_read (curl_multi, &msgnum);
1683 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1684 "Messages left: %d\n", msgnum);
1691 if ((msg->data.result != CURLE_OK) &&
1692 (msg->data.result != CURLE_GOT_NOTHING))
1694 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1695 "Download curl failed");
1697 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1699 if (NULL == ctask->curl)
1702 if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
1705 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1706 "CURL: Download failed for task %s: %s.\n",
1708 curl_easy_strerror (msg->data.result));
1709 ctask->download_is_finished = GNUNET_YES;
1710 ctask->download_error = GNUNET_YES;
1711 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1712 CURLINFO_RESPONSE_CODE,
1714 ctask->curl_response_code = resp_code;
1715 ctask->ready_to_queue = MHD_YES;
1716 ctask->buf_status = BUF_WAIT_FOR_MHD;
1717 run_mhd_now (ctask->mhd);
1719 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1721 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1724 GNUNET_assert (ctask != NULL);
1728 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1729 "CURL: download completed.\n");
1731 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1733 if (NULL == ctask->curl)
1736 if (0 != memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)))
1739 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1740 "CURL: completed task %s found.\n", ctask->url);
1741 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1742 CURLINFO_RESPONSE_CODE,
1744 ctask->curl_response_code = resp_code;
1747 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1748 "CURL: Completed ctask!\n");
1749 if (GNUNET_SCHEDULER_NO_TASK == ctask->pp_task)
1751 ctask->buf_status = BUF_WAIT_FOR_MHD;
1752 run_mhd_now (ctask->mhd);
1755 ctask->ready_to_queue = MHD_YES;
1756 ctask->download_is_finished = GNUNET_YES;
1758 /* We MUST not modify the multi handle else we loose messages */
1759 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1761 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1765 GNUNET_assert (ctask != NULL);
1767 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1768 "CURL: %s\n", curl_easy_strerror(msg->data.result));
1774 } while (msgnum > 0);
1776 for (ctask=clean_head; NULL != ctask; ctask = ctask->next)
1778 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1779 "CURL: Removing task %s.\n", ctask->url);
1780 curl_multi_remove_handle (curl_multi, ctask->curl);
1781 curl_easy_cleanup (ctask->curl);
1786 for (ctask=ctasks_head; NULL != ctask; ctask = ctask->next)
1788 GNUNET_assert (num_ctasks == running);
1790 } while (mret == CURLM_CALL_MULTI_PERFORM);
1792 if (mret != CURLM_OK)
1794 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CURL: %s failed at %s:%d: `%s'\n",
1795 "curl_multi_perform", __FILE__, __LINE__,
1796 curl_multi_strerror (mret));
1798 curl_download_prepare();
1803 * Process LEHO lookup
1805 * @param cls the ctask
1806 * @param rd_count number of records returned
1807 * @param rd record data
1810 process_leho_lookup (void *cls,
1812 const struct GNUNET_NAMESTORE_RecordData *rd)
1814 struct ProxyCurlTask *ctask = cls;
1815 char hosthdr[262]; //256 + "Host: "
1819 struct hostent *phost;
1821 char resolvename[512];
1824 strcpy (ctask->leho, "");
1827 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1828 "No LEHO present!\n");
1830 for (i=0; i<rd_count; i++)
1832 if (rd[i].record_type != GNUNET_NAMESTORE_TYPE_LEHO)
1835 memcpy (ctask->leho, rd[i].data, rd[i].data_size);
1837 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1838 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
1841 if (0 != strcmp (ctask->leho, ""))
1843 sprintf (hosthdr, "%s%s:%d", "Host: ", ctask->leho, ctask->port);
1844 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1845 "New HTTP header value: %s\n", hosthdr);
1846 ctask->headers = curl_slist_append (ctask->headers, hosthdr);
1847 GNUNET_assert (NULL != ctask->headers);
1848 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers)))
1849 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1852 if (ctask->mhd->is_ssl)
1854 phost = (struct hostent*)gethostbyname (ctask->host);
1858 ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
1859 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1860 "SSL target server: %s\n", ssl_ip);
1861 sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
1862 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1863 "Curl resolve: %s\n", resolvename);
1864 ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
1865 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver)))
1866 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1867 sprintf (curlurl, "https://%s:%d%s", ctask->leho, ctask->port, ctask->url);
1868 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl)))
1869 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1873 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1874 "gethostbyname failed for %s!\n",
1876 ctask->download_is_finished = GNUNET_YES;
1877 ctask->download_error = GNUNET_YES;
1882 if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
1884 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1885 "%s failed at %s:%d: `%s'\n",
1886 "curl_multi_add_handle", __FILE__, __LINE__,
1887 curl_multi_strerror (mret));
1888 ctask->download_is_finished = GNUNET_YES;
1889 ctask->download_error = GNUNET_YES;
1892 GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1894 curl_download_prepare ();
1899 * Initialize download and trigger curl
1901 * @param cls the proxycurltask
1902 * @param auth_name the name of the authority (site of origin) of ctask->host
1905 process_get_authority (void *cls,
1906 const char* auth_name)
1908 struct ProxyCurlTask *ctask = cls;
1910 if (NULL == auth_name)
1912 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1913 "Get authority failed!\n");
1914 strcpy (ctask->authority, "");
1918 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1919 "Get authority yielded %s\n", auth_name);
1920 strcpy (ctask->authority, auth_name);
1923 GNUNET_GNS_lookup (gns_handle,
1926 GNUNET_NAMESTORE_TYPE_LEHO,
1927 GNUNET_YES /* Only cached for performance */,
1929 &process_leho_lookup,
1935 mhd_log_callback (void* cls,
1938 struct ProxyCurlTask *ctask;
1940 ctask = GNUNET_new (struct ProxyCurlTask);
1941 strcpy (ctask->url, url);
1947 * Main MHD callback for handling requests.
1950 * @param con MHD connection handle
1951 * @param url the url in the request
1952 * @param meth the HTTP method used ("GET", "PUT", etc.)
1953 * @param ver the HTTP version string (i.e. "HTTP/1.1")
1954 * @param upload_data the data being uploaded (excluding HEADERS,
1955 * for a POST that fits into memory and that is encoded
1956 * with a supported encoding, the POST data will NOT be
1957 * given in upload_data and is instead available as
1958 * part of MHD_get_connection_values; very large POST
1959 * data *will* be made available incrementally in
1961 * @param upload_data_size set initially to the size of the
1962 * @a upload_data provided; the method must update this
1963 * value to the number of bytes NOT processed;
1964 * @param con_cls pointer to location where we store the 'struct Request'
1965 * @return #MHD_YES if the connection was handled successfully,
1966 * #MHD_NO if the socket must be closed due to a serious
1967 * error while handling the request
1970 create_response (void *cls,
1971 struct MHD_Connection *con,
1975 const char *upload_data,
1976 size_t *upload_data_size,
1979 struct MhdHttpList* hd = cls;
1980 char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow!
1983 struct ProxyCurlTask *ctask = *con_cls;
1984 struct ProxyUploadData *fin_post;
1985 struct curl_forms forms[5];
1986 struct ProxyUploadData *upload_data_iter;
1989 if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) &&
1990 (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) &&
1991 (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)) &&
1992 (0 != strcasecmp (meth, MHD_HTTP_METHOD_HEAD)))
1994 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1995 "MHD: %s NOT IMPLEMENTED!\n", meth);
2000 if (GNUNET_NO == ctask->accepted)
2003 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2004 "Got %s request for %s\n", meth, url);
2006 ctask->curl = curl_easy_init();
2007 ctask->curl_running = GNUNET_NO;
2008 if (NULL == ctask->curl)
2010 ret = MHD_queue_response (con,
2012 curl_failure_response);
2013 GNUNET_free (ctask);
2017 if (ctask->mhd->is_ssl)
2018 ctask->port = HTTPS_PORT;
2020 ctask->port = HTTP_PORT;
2022 MHD_get_connection_values (con,
2024 &con_val_iter, ctask);
2026 curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
2027 curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
2028 curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
2029 curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
2030 curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0);
2031 curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
2033 if (GNUNET_NO == ctask->mhd->is_ssl)
2035 sprintf (curlurl, "http://%s:%d%s", ctask->host, ctask->port, ctask->url);
2036 curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
2040 curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
2041 curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
2042 curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
2044 /* Add GNS header */
2045 ctask->headers = curl_slist_append (ctask->headers,
2047 ctask->accepted = GNUNET_YES;
2048 ctask->download_in_progress = GNUNET_YES;
2049 ctask->buf_status = BUF_WAIT_FOR_CURL;
2050 ctask->connection = con;
2051 ctask->curl_response_code = MHD_HTTP_OK;
2052 ctask->buffer_read_ptr = ctask->buffer;
2053 ctask->buffer_write_ptr = ctask->buffer;
2054 ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
2057 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
2059 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2060 "Setting up PUT\n");
2062 curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1);
2063 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
2064 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &put_read_callback);
2065 ctask->headers = curl_slist_append (ctask->headers,
2066 "Transfer-Encoding: chunked");
2069 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2071 //FIXME handle multipart
2072 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2073 "Setting up POST processor\n");
2074 ctask->post_handler = MHD_create_post_processor (con,
2076 &con_post_data_iter,
2078 ctask->headers = curl_slist_append (ctask->headers,
2079 "Transfer-Encoding: chunked");
2083 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
2085 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2086 "Setting NOBODY\n");
2087 curl_easy_setopt (ctask->curl, CURLOPT_NOBODY, 1);
2091 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2092 "MHD: Adding new curl task for %s\n", ctask->host);
2094 GNUNET_GNS_get_authority (gns_handle,
2096 &process_get_authority,
2099 ctask->ready_to_queue = GNUNET_NO;
2100 ctask->fin = GNUNET_NO;
2101 ctask->curl_running = GNUNET_YES;
2105 ctask = (struct ProxyCurlTask *) *con_cls;
2106 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2108 if (0 != *upload_data_size)
2111 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2112 "Invoking POST processor\n");
2113 MHD_post_process (ctask->post_handler,
2114 upload_data, *upload_data_size);
2115 *upload_data_size = 0;
2116 if ((GNUNET_NO == ctask->is_httppost) &&
2117 (GNUNET_NO == ctask->curl_running))
2119 curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
2120 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION,
2121 &post_read_callback);
2122 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
2124 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2125 "MHD: Adding new curl task for %s\n", ctask->host);
2127 GNUNET_GNS_get_authority (gns_handle,
2129 &process_get_authority,
2132 ctask->ready_to_queue = GNUNET_NO;
2133 ctask->fin = GNUNET_NO;
2134 ctask->curl_running = GNUNET_YES;
2138 else if (GNUNET_NO == ctask->post_done)
2140 if (GNUNET_YES == ctask->is_httppost)
2142 for (upload_data_iter = ctask->upload_data_head;
2143 NULL != upload_data_iter;
2144 upload_data_iter = upload_data_iter->next)
2147 if (NULL != upload_data_iter->filename)
2149 forms[i].option = CURLFORM_FILENAME;
2150 forms[i].value = upload_data_iter->filename;
2151 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2152 "Adding filename %s\n",
2156 if (NULL != upload_data_iter->content_type)
2158 forms[i].option = CURLFORM_CONTENTTYPE;
2159 forms[i].value = upload_data_iter->content_type;
2160 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2161 "Adding content type %s\n",
2165 forms[i].option = CURLFORM_PTRCONTENTS;
2166 forms[i].value = upload_data_iter->value;
2167 forms[i+1].option = CURLFORM_END;
2169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2170 "Adding formdata for %s (len=%lld)\n",
2171 upload_data_iter->key,
2172 upload_data_iter->total_bytes);
2174 curl_formadd(&ctask->httppost, &ctask->httppost_last,
2175 CURLFORM_COPYNAME, upload_data_iter->key,
2176 CURLFORM_CONTENTSLENGTH, upload_data_iter->total_bytes,
2177 CURLFORM_ARRAY, forms,
2180 curl_easy_setopt (ctask->curl, CURLOPT_HTTPPOST,
2183 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2184 "MHD: Adding new curl task for %s\n", ctask->host);
2186 GNUNET_GNS_get_authority (gns_handle,
2188 &process_get_authority,
2191 ctask->ready_to_queue = GNUNET_YES;
2192 ctask->fin = GNUNET_NO;
2193 ctask->curl_running = GNUNET_YES;
2194 ctask->post_done = GNUNET_YES;
2198 fin_post = GNUNET_new (struct ProxyUploadData);
2199 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
2200 ctask->upload_data_tail,
2202 ctask->post_done = GNUNET_YES;
2207 if (GNUNET_YES != ctask->ready_to_queue)
2208 return MHD_YES; /* wait longer */
2210 if (GNUNET_YES == ctask->fin)
2213 ctask->fin = GNUNET_YES;
2214 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2215 "MHD: Queueing response for %s\n", ctask->url);
2216 ret = MHD_queue_response (con, ctask->curl_response_code, ctask->response);
2217 run_mhd_now (ctask->mhd);
2228 struct MhdHttpList *hd;
2230 for (hd=mhd_httpd_head; NULL != hd; hd = hd->next)
2239 * @param hd the daemon to run
2242 run_httpd (struct MhdHttpList *hd)
2247 struct GNUNET_NETWORK_FDSet *wrs;
2248 struct GNUNET_NETWORK_FDSet *wws;
2249 struct GNUNET_NETWORK_FDSet *wes;
2252 MHD_UNSIGNED_LONG_LONG timeout;
2253 struct GNUNET_TIME_Relative tv;
2258 wrs = GNUNET_NETWORK_fdset_create ();
2259 wes = GNUNET_NETWORK_fdset_create ();
2260 wws = GNUNET_NETWORK_fdset_create ();
2262 GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
2265 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2266 "MHD fds: max=%d\n", max);
2268 haveto = MHD_get_timeout (hd->daemon, &timeout);
2270 if (MHD_YES == haveto)
2271 tv.rel_value_us = (uint64_t) timeout * 1000LL;
2273 tv = GNUNET_TIME_UNIT_FOREVER_REL;
2274 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
2275 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
2276 GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
2278 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2279 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2281 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
2284 GNUNET_NETWORK_fdset_destroy (wrs);
2285 GNUNET_NETWORK_fdset_destroy (wws);
2286 GNUNET_NETWORK_fdset_destroy (wes);
2291 * Task run whenever HTTP server operations are pending.
2294 * @param tc sched context
2297 do_httpd (void *cls,
2298 const struct GNUNET_SCHEDULER_TaskContext *tc)
2300 struct MhdHttpList *hd = cls;
2302 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2303 "MHD: Main loop\n");
2304 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2305 MHD_run (hd->daemon);
2311 * Read data from socket
2313 * @param cls the closure
2314 * @param tc scheduler context
2317 do_s5r_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2321 * Read from remote end
2323 * @param cls closure
2324 * @param tc scheduler context
2327 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2331 * Write data to remote socket
2333 * @param cls the closure
2334 * @param tc scheduler context
2337 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2339 struct Socks5Request *s5r = cls;
2342 s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2343 if ( (NULL != tc->read_ready) &&
2344 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
2345 ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
2348 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2349 "Successfully sent %d bytes to remote socket\n",
2354 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send");
2360 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2367 * Write data to socket
2369 * @param cls the closure
2370 * @param tc scheduler context
2373 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2375 struct Socks5Request *s5r = cls;
2378 s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2380 if ((NULL != tc->read_ready) &&
2381 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
2382 ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
2385 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2386 "Successfully sent %d bytes to socket\n",
2391 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
2392 s5r->cleanup = GNUNET_YES;
2393 s5r->cleanup_sock = GNUNET_YES;
2398 if (GNUNET_YES == s5r->cleanup)
2404 if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
2405 (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
2407 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2409 &do_read_remote, s5r);
2414 * Read from remote end
2416 * @param cls closure
2417 * @param tc scheduler context
2420 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2422 struct Socks5Request *s5r = cls;
2424 s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2425 if ((NULL != tc->write_ready) &&
2426 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
2427 (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
2428 sizeof (s5r->wbuf))))
2430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2431 "Successfully read %d bytes from remote socket\n",
2436 if (0 == s5r->wbuf_len)
2437 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2438 "0 bytes received from remote... graceful shutdown!\n");
2443 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2450 * Adds a socket to MHD
2452 * @param h the handle to the socket to add
2453 * @param daemon the daemon to add the fd to
2454 * @return whatever #MHD_add_connection returns
2457 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h,
2458 struct MHD_Daemon *daemon)
2461 struct sockaddr *addr;
2465 fd = GNUNET_NETWORK_get_fd (h);
2466 addr = GNUNET_NETWORK_get_addr (h);
2467 len = GNUNET_NETWORK_get_addrlen (h);
2468 ret = MHD_add_connection (daemon, fd, addr, len);
2469 GNUNET_NETWORK_socket_free_memory_only_ (h);
2475 * Read file in filename
2477 * @param filename file to read
2478 * @param size pointer where filesize is stored
2479 * @return NULL on error
2482 load_file (const char* filename,
2489 GNUNET_DISK_file_size (filename, &fsize,
2490 GNUNET_YES, GNUNET_YES))
2492 if (fsize > MAX_PEM_SIZE)
2494 *size = (unsigned int) fsize;
2495 buffer = GNUNET_malloc (*size);
2496 if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize))
2498 GNUNET_free (buffer);
2506 * Load PEM key from file
2508 * @param key where to store the data
2509 * @param keyfile path to the PEM file
2510 * @return #GNUNET_OK on success
2513 load_key_from_file (gnutls_x509_privkey_t key,
2514 const char* keyfile)
2516 gnutls_datum_t key_data;
2519 key_data.data = load_file (keyfile, &key_data.size);
2520 ret = gnutls_x509_privkey_import (key, &key_data,
2521 GNUTLS_X509_FMT_PEM);
2522 if (GNUTLS_E_SUCCESS != ret)
2524 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2525 _("Unable to import private key from file `%s'\n"),
2529 GNUNET_free (key_data.data);
2530 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2535 * Load cert from file
2537 * @param crt struct to store data in
2538 * @param certfile path to pem file
2539 * @return #GNUNET_OK on success
2542 load_cert_from_file (gnutls_x509_crt_t crt,
2543 const char* certfile)
2545 gnutls_datum_t cert_data;
2548 cert_data.data = load_file (certfile, &cert_data.size);
2549 ret = gnutls_x509_crt_import (crt, &cert_data,
2550 GNUTLS_X509_FMT_PEM);
2551 if (GNUTLS_E_SUCCESS != ret)
2553 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2554 _("Unable to import certificate %s\n"), certfile);
2557 GNUNET_free (cert_data.data);
2558 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2563 * Generate new certificate for specific name
2565 * @param name the subject name to generate a cert for
2566 * @return a struct holding the PEM data
2568 static struct ProxyGNSCertificate *
2569 generate_gns_certificate (const char *name)
2571 unsigned int serial;
2572 size_t key_buf_size;
2573 size_t cert_buf_size;
2574 gnutls_x509_crt_t request;
2577 struct ProxyGNSCertificate *pgc;
2579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2580 "Generating cert for `%s'\n",
2582 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_init (&request));
2583 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
2584 pgc = GNUNET_new (struct ProxyGNSCertificate);
2585 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
2587 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
2588 0, "GNU Name System", 4);
2589 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
2590 0, name, strlen (name));
2591 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_version (request, 3));
2592 gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
2593 gnutls_x509_crt_set_serial (request,
2596 etime = time (NULL);
2597 tm_data = localtime (&etime);
2598 gnutls_x509_crt_set_activation_time (request,
2601 etime = mktime (tm_data);
2602 gnutls_x509_crt_set_expiration_time (request,
2604 gnutls_x509_crt_sign (request,
2607 key_buf_size = sizeof (pgc->key);
2608 cert_buf_size = sizeof (pgc->cert);
2609 gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
2610 pgc->cert, &cert_buf_size);
2611 gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
2612 pgc->key, &key_buf_size);
2613 gnutls_x509_crt_deinit (request);
2619 * Adds a socket to an SSL MHD instance It is important that the
2620 * domain name is correct. In most cases we need to start a new daemon.
2622 * @param h the handle to add to a daemon
2623 * @param domain the domain the SSL daemon has to serve
2624 * @return #MHD_YES on success
2627 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h,
2630 struct MhdHttpList *hd;
2631 struct ProxyGNSCertificate *pgc;
2633 for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2634 if (0 == strcmp (hd->domain, domain))
2638 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2639 "Starting fresh MHD HTTPS instance for domain `%s'\n",
2641 pgc = generate_gns_certificate (domain);
2642 hd = GNUNET_new (struct MhdHttpList);
2643 hd->is_ssl = GNUNET_YES;
2644 strcpy (hd->domain, domain); /* FIXME: avoid fixed-sized buffers... */
2645 hd->proxy_cert = pgc;
2646 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET,
2649 &create_response, hd,
2650 MHD_OPTION_CONNECTION_LIMIT,
2651 MHD_MAX_CONNECTIONS,
2652 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2653 MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2654 MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2655 MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2656 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2659 /* FIXME: rather than assert, handle error! */
2660 GNUNET_assert (NULL != hd->daemon);
2661 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2663 return add_handle_to_mhd (h, hd->daemon);
2668 * Read data from incoming Socks5 connection
2670 * @param cls the closure with the `struct Socks5Request`
2671 * @param tc the scheduler context
2674 do_s5r_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2676 struct Socks5Request *s5r = cls;
2677 struct Socks5ClientHelloMessage *c_hello;
2678 struct Socks5ServerHelloMessage *s_hello;
2679 struct Socks5ClientRequestMessage *c_req;
2680 struct Socks5ServerResponseMessage *s_resp;
2685 struct hostent *phost;
2687 struct sockaddr_in remote_addr;
2688 struct in_addr *r_sin_addr;
2690 s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
2691 if ( (NULL != tc->read_ready) &&
2692 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) )
2693 s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
2694 sizeof (s5r->rbuf));
2697 if (0 == s5r->rbuf_len)
2699 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2700 "socks5 client disconnected.\n");
2704 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2705 "Processing socks data in state %d\n",
2710 /* FIXME: failed to check if we got enough data yet! */
2711 c_hello = (struct Socks5ClientHelloMessage*) &s5r->rbuf;
2712 GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
2713 s_hello = (struct Socks5ServerHelloMessage*) &s5r->wbuf;
2714 s5r->wbuf_len = sizeof( struct Socks5ServerHelloMessage );
2715 s_hello->version = c_hello->version;
2716 s_hello->auth_method = SOCKS_AUTH_NONE;
2717 /* Write response to client */
2718 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2721 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2724 s5r->state = SOCKS5_REQUEST;
2726 case SOCKS5_REQUEST:
2727 /* FIXME: failed to check if we got enough data yet!? */
2728 c_req = (struct Socks5ClientRequestMessage *) &s5r->rbuf;
2729 s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf;
2730 //Only 10 byte for ipv4 response!
2731 s5r->wbuf_len = 10;//sizeof (struct Socks5ServerResponseMessage);
2732 GNUNET_assert (c_req->addr_type == 3);
2733 dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
2734 memset(domain, 0, sizeof(domain));
2735 strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
2736 req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
2737 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2738 "Requested connection is to %s:%d\n",
2741 if (is_tld (domain, GNUNET_GNS_TLD) ||
2742 is_tld (domain, GNUNET_GNS_TLD_ZKEY))
2746 if (ntohs (req_port) == HTTPS_PORT)
2748 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2749 "Requested connection is HTTPS\n");
2750 ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
2752 else if (NULL != httpd)
2754 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2755 "Requested connection is HTTP\n");
2756 ret = add_handle_to_mhd (s5r->sock, httpd );
2761 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2762 _("Failed to start HTTP server\n"));
2763 s_resp->version = 0x05;
2764 s_resp->reply = 0x01;
2765 s5r->cleanup = GNUNET_YES;
2766 s5r->cleanup_sock = GNUNET_YES;
2768 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2774 /* Signal success */
2775 s_resp->version = 0x05;
2776 s_resp->reply = 0x00;
2777 s_resp->reserved = 0x00;
2778 s_resp->addr_type = 0x01;
2780 s5r->cleanup = GNUNET_YES;
2781 s5r->cleanup_sock = GNUNET_NO;
2783 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2791 /* non-GNS TLD, use DNS to resolve */
2792 /* FIXME: make asynchronous! */
2793 phost = (struct hostent *) gethostbyname (domain);
2796 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2797 "Resolve %s error!\n", domain );
2798 s_resp->version = 0x05;
2799 s_resp->reply = 0x01;
2800 s5r->cleanup = GNUNET_YES;
2801 s5r->cleanup_sock = GNUNET_YES;
2803 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2809 s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
2812 r_sin_addr = (struct in_addr*)(phost->h_addr);
2813 remote_ip = r_sin_addr->s_addr;
2814 memset(&remote_addr, 0, sizeof(remote_addr));
2815 remote_addr.sin_family = AF_INET;
2816 #if HAVE_SOCKADDR_IN_SIN_LEN
2817 remote_addr.sin_len = sizeof (remote_addr);
2819 remote_addr.sin_addr.s_addr = remote_ip;
2820 remote_addr.sin_port = req_port;
2822 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2823 "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
2827 GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
2828 (const struct sockaddr*)&remote_addr,
2829 sizeof (remote_addr)))
2830 && (errno != EINPROGRESS))
2832 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
2833 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2834 "socket request error...\n");
2835 s_resp->version = 0x05;
2836 s_resp->reply = 0x01;
2838 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2844 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2845 "new remote connection\n");
2846 s_resp->version = 0x05;
2847 s_resp->reply = 0x00;
2848 s_resp->reserved = 0x00;
2849 s_resp->addr_type = 0x01;
2850 s5r->state = SOCKS5_DATA_TRANSFER;
2852 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2856 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2861 case SOCKS5_DATA_TRANSFER:
2863 if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
2865 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2866 "Closing connection to client\n");
2870 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2871 "forwarding %d bytes from client\n", s5r->rbuf_len);
2873 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2875 &do_write_remote, s5r);
2876 if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
2879 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2881 &do_read_remote, s5r);
2893 * Accept new incoming connections
2895 * @param cls the closure
2896 * @param tc the scheduler context
2899 do_accept (void *cls,
2900 const struct GNUNET_SCHEDULER_TaskContext *tc)
2902 struct GNUNET_NETWORK_Handle *s;
2903 struct Socks5Request *s5r;
2905 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2908 s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
2911 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
2914 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2915 "Got an inbound connection, waiting for data\n");
2916 s5r = GNUNET_new (struct Socks5Request);
2917 GNUNET_CONTAINER_DLL_insert (s5r_head,
2921 s5r->state = SOCKS5_INIT;
2922 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2929 * Task run on shutdown
2931 * @param cls closure
2932 * @param tc task context
2935 do_shutdown (void *cls,
2936 const struct GNUNET_SCHEDULER_TaskContext *tc)
2938 struct MhdHttpList *hd;
2939 struct MhdHttpList *tmp_hd;
2940 struct ProxyCurlTask *ctask;
2941 struct ProxyCurlTask *ctask_tmp;
2942 struct ProxyUploadData *pdata;
2944 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2945 "Shutting down...\n");
2946 for (hd = mhd_httpd_head; NULL != hd; hd = tmp_hd)
2949 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2951 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2952 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2954 if (NULL != hd->daemon)
2956 MHD_stop_daemon (hd->daemon);
2959 GNUNET_free_non_null (hd->proxy_cert);
2962 for (ctask=ctasks_head; NULL != ctask; ctask=ctask_tmp)
2964 ctask_tmp = ctask->next;
2965 if (NULL != ctask->curl)
2967 curl_easy_cleanup (ctask->curl);
2970 if (NULL != ctask->headers)
2972 curl_slist_free_all (ctask->headers);
2973 ctask->headers = NULL;
2975 if (NULL != ctask->resolver)
2977 curl_slist_free_all (ctask->resolver);
2978 ctask->resolver = NULL;
2980 if (NULL != ctask->response)
2982 MHD_destroy_response (ctask->response);
2983 ctask->response = NULL;
2985 for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
2987 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
2988 ctask->upload_data_tail,
2990 GNUNET_free_non_null (pdata->filename);
2991 GNUNET_free_non_null (pdata->content_type);
2992 GNUNET_free_non_null (pdata->key);
2993 GNUNET_free_non_null (pdata->value);
2994 GNUNET_free (pdata);
2996 GNUNET_free (ctask);
3000 GNUNET_NETWORK_socket_close (lsock);
3005 GNUNET_IDENTITY_cancel (id_op);
3008 if (NULL != identity)
3010 GNUNET_IDENTITY_disconnect (identity);
3013 if (NULL != curl_multi)
3015 curl_multi_cleanup (curl_multi);
3018 if (NULL != gns_handle)
3020 GNUNET_GNS_disconnect (gns_handle);
3023 if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
3025 GNUNET_SCHEDULER_cancel (curl_download_task);
3026 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
3028 if (GNUNET_SCHEDULER_NO_TASK != ltask)
3030 GNUNET_SCHEDULER_cancel (ltask);
3031 ltask = GNUNET_SCHEDULER_NO_TASK;
3033 gnutls_global_deinit ();
3038 * Continue initialization after we have our zone information.
3043 struct MhdHttpList *hd;
3044 struct sockaddr_in sa;
3046 /* Open listen socket for socks proxy */
3047 /* FIXME: support IPv6! */
3048 memset (&sa, 0, sizeof (sa));
3049 sa.sin_family = AF_INET;
3050 sa.sin_port = htons (port);
3051 #if HAVE_SOCKADDR_IN_SIN_LEN
3052 sa.sin_len = sizeof (sa);
3054 lsock = GNUNET_NETWORK_socket_create (AF_INET,
3059 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
3060 GNUNET_SCHEDULER_shutdown ();
3064 GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
3067 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
3068 GNUNET_SCHEDULER_shutdown ();
3071 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
3073 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
3076 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3077 lsock, &do_accept, NULL);
3079 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
3081 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3082 "cURL global init failed!\n");
3083 GNUNET_SCHEDULER_shutdown ();
3086 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3087 "Proxy listens on port %u\n",
3090 /* start MHD daemon for HTTP */
3091 hd = GNUNET_new (struct MhdHttpList);
3092 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
3095 &create_response, hd,
3096 MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3097 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3098 MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
3099 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3101 if (NULL == hd->daemon)
3104 GNUNET_SCHEDULER_shutdown ();
3108 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
3110 /* start loop running all MHD instances */
3116 * Method called to inform about the egos of the shorten 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_shorten_cb (void *cls,
3136 struct GNUNET_IDENTITY_Ego *ego,
3143 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3144 _("No ego configured for `shorten-zone`\n"));
3148 local_shorten_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
3149 do_shorten = GNUNET_YES;
3156 * Method called to inform about the egos of the master zone of this peer.
3158 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
3159 * this function is only called ONCE, and 'NULL' being passed in
3160 * @a ego does indicate an error (i.e. name is taken or no default
3161 * value is known). If @a ego is non-NULL and if '*ctx'
3162 * is set in those callbacks, the value WILL be passed to a subsequent
3163 * call to the identity callback of #GNUNET_IDENTITY_connect (if
3164 * that one was not NULL).
3166 * @param cls closure, NULL
3167 * @param ego ego handle
3168 * @param ctx context for application to store data for this ego
3169 * (during the lifetime of this process, initially NULL)
3170 * @param name name assigned by the user for this ego,
3171 * NULL if the user just deleted the ego and it
3172 * must thus no longer be used
3175 identity_master_cb (void *cls,
3176 struct GNUNET_IDENTITY_Ego *ego,
3183 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3184 _("No ego configured for `master-zone`\n"));
3185 GNUNET_SCHEDULER_shutdown ();
3188 GNUNET_IDENTITY_ego_get_public_key (ego,
3190 id_op = GNUNET_IDENTITY_get (identity,
3192 &identity_shorten_cb,
3198 * Main function that will be run
3200 * @param cls closure
3201 * @param args remaining command-line arguments
3202 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3203 * @param c configuration
3206 run (void *cls, char *const *args, const char *cfgfile,
3207 const struct GNUNET_CONFIGURATION_Handle *c)
3209 char* cafile_cfg = NULL;
3213 if (NULL == (curl_multi = curl_multi_init ()))
3215 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3216 "Failed to create cURL multi handle!\n");
3219 cafile = cafile_opt;
3222 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3226 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
3231 cafile = cafile_cfg;
3233 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3234 "Using %s as CA\n", cafile);
3236 gnutls_global_init ();
3237 gnutls_x509_crt_init (&proxy_ca.cert);
3238 gnutls_x509_privkey_init (&proxy_ca.key);
3240 if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
3241 (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
3243 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3244 _("Failed to load SSL/TLS key and certificate from `%s'\n"),
3246 // FIXME: release resources...
3247 GNUNET_free_non_null (cafile_cfg);
3250 GNUNET_free_non_null (cafile_cfg);
3251 if (NULL == (gns_handle = GNUNET_GNS_connect (cfg)))
3253 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3254 "Unable to connect to GNS!\n");
3257 identity = GNUNET_IDENTITY_connect (cfg,
3259 id_op = GNUNET_IDENTITY_get (identity,
3261 &identity_master_cb,
3263 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
3264 &do_shutdown, NULL);
3269 * The main function for gnunet-gns-proxy.
3271 * @param argc number of arguments from the command line
3272 * @param argv command line arguments
3273 * @return 0 ok, 1 on error
3276 main (int argc, char *const *argv)
3278 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
3280 gettext_noop ("listen on specified port (default: 7777)"), 1,
3281 &GNUNET_GETOPT_set_ulong, &port},
3282 {'a', "authority", NULL,
3283 gettext_noop ("pem file to use as CA"), 1,
3284 &GNUNET_GETOPT_set_string, &cafile_opt},
3285 GNUNET_GETOPT_OPTION_END
3287 static const char* page =
3288 "<html><head><title>gnunet-gns-proxy</title>"
3289 "</head><body>cURL fail</body></html>";
3292 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
3294 GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
3295 curl_failure_response = MHD_create_response_from_buffer (strlen (page),
3297 MHD_RESPMEM_PERSISTENT);
3301 GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
3302 _("GNUnet GNS proxy"),
3304 &run, NULL)) ? 0 : 1;
3305 MHD_destroy_response (curl_failure_response);
3306 GNUNET_free_non_null ((char *) argv);
3310 /* end of gnunet-gns-proxy.c */