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 struct socks5_client_hello
530 uint8_t num_auth_methods;
534 /* Client socks request */
535 struct socks5_client_request
542 * followed by either an ip4/ipv6 address
543 * or a domain name with a length field in front
548 struct socks5_server_hello
554 /* Server response to client requests */
555 struct socks5_server_response
561 uint8_t add_port[18];
566 * The port the proxy is running on (default 7777)
568 static unsigned long port = GNUNET_GNS_PROXY_PORT;
571 * The CA file (pem) to use for the proxy CA
573 static char* cafile_opt;
576 * The listen socket of the proxy
578 static struct GNUNET_NETWORK_Handle *lsock;
583 static GNUNET_SCHEDULER_TaskIdentifier ltask;
586 * The cURL download task
588 static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
591 * Number of current mhd connections
593 static unsigned int total_mhd_connections;
596 * The cURL multi handle
598 static CURLM *curl_multi;
601 * Handle to the GNS service
603 static struct GNUNET_GNS_Handle *gns_handle;
606 * DLL for ProxyCurlTasks
608 static struct ProxyCurlTask *ctasks_head;
611 * DLL for ProxyCurlTasks
613 static struct ProxyCurlTask *ctasks_tail;
616 * DLL for http/https daemons
618 static struct MhdHttpList *mhd_httpd_head;
621 * DLL for http/https daemons
623 static struct MhdHttpList *mhd_httpd_tail;
626 * DLL of active socks requests.
628 static struct Socks5Request *s5r_head;
631 * DLL of active socks requests.
633 static struct Socks5Request *s5r_tail;
636 * The users local GNS master zone
638 static struct GNUNET_CRYPTO_EccPublicSignKey local_gns_zone;
641 * The users local shorten zone
643 static struct GNUNET_CRYPTO_EccPrivateKey local_shorten_zone;
646 * Is shortening enabled?
648 static int do_shorten;
651 * The CA for SSL certificate generation
653 static struct ProxyCA proxy_ca;
656 * Daemon for HTTP (we have one per SSL certificate, and then one for all HTTP connections;
657 * this is the one for HTTP, not HTTPS).
659 static struct MHD_Daemon *httpd;
662 * Shorten zone private key
664 static struct GNUNET_CRYPTO_EccPrivateKey shorten_zonekey;
667 * Response we return on cURL failures.
669 static struct MHD_Response *curl_failure_response;
672 * Connection to identity service.
674 static struct GNUNET_IDENTITY_Handle *identity;
677 * Request for our ego.
679 static struct GNUNET_IDENTITY_Operation *id_op;
684 static const struct GNUNET_CONFIGURATION_Handle *cfg;
687 * Clean up s5r handles
689 * @param s5r the handle to destroy
692 cleanup_s5r (struct Socks5Request *s5r)
694 if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
695 GNUNET_SCHEDULER_cancel (s5r->rtask);
696 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdwtask)
697 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
698 if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
699 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
700 if (NULL != s5r->remote_sock)
701 GNUNET_NETWORK_socket_close (s5r->remote_sock);
702 if ( (NULL != s5r->sock) &&
703 (GNUNET_YES == s5r->cleanup_sock) )
704 GNUNET_NETWORK_socket_close (s5r->sock);
705 GNUNET_CONTAINER_DLL_remove (s5r_head,
713 * Checks if name is in tld
715 * @param name the name to check
716 * @param tld the TLD to check for (must NOT begin with ".")
717 * @return #GNUNET_YES or #GNUNET_NO
720 is_tld (const char* name, const char* tld)
722 size_t name_len = strlen (name);
723 size_t tld_len = strlen (tld);
725 GNUNET_break ('.' != tld[0]);
726 return ( (tld_len < name_len) &&
727 ( ('.' == name[name_len - tld_len - 1]) || (name_len == tld_len) ) &&
729 name + (name_len - tld_len),
735 con_post_data_iter (void *cls,
736 enum MHD_ValueKind kind,
738 const char *filename,
739 const char *content_type,
740 const char *transfer_encoding,
745 struct ProxyCurlTask* ctask = cls;
746 struct ProxyUploadData* pdata;
750 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
751 "Got POST data (file: %s, content type: %s): '%s=%.*s' at offset %llu size %llu\n",
752 filename, content_type,
753 key, (int) size, data,
754 (unsigned long long) off,
755 (unsigned long long) size);
756 GNUNET_assert (NULL != ctask->post_type);
758 if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
761 ctask->is_httppost = GNUNET_YES;
765 pdata = GNUNET_new (struct ProxyUploadData);
766 pdata->key = GNUNET_strdup (key);
768 if (NULL != filename)
769 pdata->filename = GNUNET_strdup (filename);
770 if (NULL != content_type)
771 pdata->content_type = GNUNET_strdup (content_type);
772 pdata->value = GNUNET_malloc (size);
773 pdata->total_bytes = size;
774 memcpy (pdata->value, data, size);
775 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
776 ctask->upload_data_tail,
779 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
780 "Copied %llu bytes of POST Data\n",
781 (unsigned long long) size);
785 pdata = ctask->upload_data_tail;
786 new_value = GNUNET_malloc (size + pdata->total_bytes);
787 memcpy (new_value, pdata->value, pdata->total_bytes);
788 memcpy (new_value+off, data, size);
789 GNUNET_free (pdata->value);
790 pdata->value = new_value;
791 pdata->total_bytes += size;
796 if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
802 ctask->is_httppost = GNUNET_NO;
804 if (NULL != ctask->curl)
805 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
809 enc = curl_easy_escape (ctask->curl, key, 0);
816 pdata = GNUNET_new (struct ProxyUploadData);
817 pdata->value = GNUNET_malloc (strlen (enc) + 3);
818 if (NULL != ctask->upload_data_head)
820 pdata->value[0] = '&';
821 memcpy (pdata->value+1, enc, strlen (enc));
824 memcpy (pdata->value, enc, strlen (enc));
825 pdata->value[strlen (pdata->value)] = '=';
826 pdata->bytes_left = strlen (pdata->value);
827 pdata->total_bytes = pdata->bytes_left;
830 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
831 "Escaped POST key: '%s'\n",
834 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
835 ctask->upload_data_tail,
840 enc = curl_easy_escape (ctask->curl, data, 0);
846 pdata = GNUNET_new (struct ProxyUploadData);
847 pdata->value = GNUNET_malloc (strlen (enc) + 1);
848 memcpy (pdata->value, enc, strlen (enc));
849 pdata->bytes_left = strlen (pdata->value);
850 pdata->total_bytes = pdata->bytes_left;
853 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
854 "Escaped POST value: '%s'\n",
857 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
858 ctask->upload_data_tail,
865 * Read HTTP request header field 'Host'
867 * @param cls buffer to write to
868 * @param kind value kind
869 * @param key field key
870 * @param value field value
871 * @return #MHD_NO when Host found
874 con_val_iter (void *cls,
875 enum MHD_ValueKind kind,
879 struct ProxyCurlTask *ctask = cls;
880 char* buf = ctask->host;
886 if (0 == strcmp ("Host", key))
888 port = strchr (value, ':');
891 strncpy (buf, value, port-value);
893 if ((1 != sscanf (port, "%u", &uport)) ||
894 (uport > UINT16_MAX) ||
896 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
897 "Unable to parse port!\n");
899 ctask->port = (uint16_t) uport;
906 if (0 == strcmp (MHD_HTTP_HEADER_ACCEPT_ENCODING, key))
911 if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_TYPE,
914 if (0 == strncasecmp (value,
915 MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
916 strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
917 ctask->post_type = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
918 else if (0 == strncasecmp (value,
919 MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
920 strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
921 ctask->post_type = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
923 ctask->post_type = NULL;
927 cstr = GNUNET_malloc (strlen (key) + strlen (hdr_val) + 3);
928 GNUNET_snprintf (cstr, strlen (key) + strlen (hdr_val) + 3,
929 "%s: %s", key, hdr_val);
931 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
932 "Client Header: %s\n", cstr);
934 ctask->headers = curl_slist_append (ctask->headers, cstr);
942 * Callback for MHD response
945 * @param pos in buffer
947 * @param max space in buffer
948 * @return number of bytes written
951 mhd_content_cb (void *cls,
958 * Check HTTP response header for mime
960 * @param buffer curl buffer
961 * @param size curl blocksize
962 * @param nmemb curl blocknumber
964 * @return size of read bytes
967 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
969 size_t bytes = size * nmemb;
970 struct ProxyCurlTask *ctask = cls;
971 int cookie_hdr_len = strlen (MHD_HTTP_HEADER_SET_COOKIE);
972 char hdr_generic[bytes+1];
973 char new_cookie_hdr[bytes+strlen (ctask->leho)+1];
974 char new_location[MAX_HTTP_URI_LENGTH+500];
984 char cors_hdr[strlen (ctask->leho) + strlen ("https://")];
986 if (NULL == ctask->response)
988 /* FIXME: get total size from curl (if available) */
989 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
990 "Creating response for %s\n", ctask->url);
991 ctask->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
992 sizeof (ctask->buffer),
997 /* if we have a leho add a CORS header */
998 if (0 != strcmp ("", ctask->leho))
1000 /* We could also allow ssl and http here */
1001 if (ctask->mhd->is_ssl)
1002 sprintf (cors_hdr, "https://%s", ctask->leho);
1004 sprintf (cors_hdr, "http://%s", ctask->leho);
1006 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1007 "MHD: Adding CORS header field %s\n",
1010 if (GNUNET_NO == MHD_add_response_header (ctask->response,
1011 "Access-Control-Allow-Origin",
1014 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1015 "MHD: Error adding CORS header field %s\n",
1019 ctask->ready_to_queue = GNUNET_YES;
1021 if (cookie_hdr_len > bytes)
1024 memcpy (hdr_generic, buffer, bytes);
1025 hdr_generic[bytes] = '\0';
1027 if ('\n' == hdr_generic[bytes-1])
1028 hdr_generic[bytes-1] = '\0';
1030 if (hdr_generic[bytes-2] == '\r')
1031 hdr_generic[bytes-2] = '\0';
1033 if (0 == memcmp (hdr_generic,
1034 MHD_HTTP_HEADER_SET_COOKIE,
1037 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1038 "Looking for cookie in: `%s'\n", hdr_generic);
1039 ndup = GNUNET_strdup (hdr_generic+cookie_hdr_len+1);
1040 memset (new_cookie_hdr, 0, sizeof (new_cookie_hdr));
1041 for (tok = strtok (ndup, ";"); tok != NULL; tok = strtok (NULL, ";"))
1043 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1044 "Got Cookie token: %s\n", tok);
1045 //memcpy (new_cookie_hdr+offset, tok, strlen (tok));
1046 if (0 == memcmp (tok, " domain", strlen (" domain")))
1048 cookie_domain = tok + strlen (" domain") + 1;
1050 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1051 "Got Set-Cookie Domain: %s\n", cookie_domain);
1053 if (strlen (cookie_domain) < strlen (ctask->leho))
1055 delta_cdomain = strlen (ctask->leho) - strlen (cookie_domain);
1056 if (0 == strcmp (cookie_domain, ctask->leho + (delta_cdomain)))
1058 GNUNET_snprintf (new_cookie_hdr+offset,
1059 sizeof (new_cookie_hdr),
1060 " domain=%s", ctask->authority);
1061 offset += strlen (" domain=") + strlen (ctask->authority);
1062 new_cookie_hdr[offset] = ';';
1067 else if (strlen (cookie_domain) == strlen (ctask->leho))
1069 if (0 == strcmp (cookie_domain, ctask->leho))
1071 GNUNET_snprintf (new_cookie_hdr+offset,
1072 sizeof (new_cookie_hdr),
1073 " domain=%s", ctask->host);
1074 offset += strlen (" domain=") + strlen (ctask->host);
1075 new_cookie_hdr[offset] = ';';
1080 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1081 "Cookie domain invalid\n");
1085 memcpy (new_cookie_hdr+offset, tok, strlen (tok));
1086 offset += strlen (tok);
1087 new_cookie_hdr[offset] = ';';
1093 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1094 "Got Set-Cookie HTTP header %s\n", new_cookie_hdr);
1096 if (GNUNET_NO == MHD_add_response_header (ctask->response,
1097 MHD_HTTP_HEADER_SET_COOKIE,
1100 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1101 "MHD: Error adding set-cookie header field %s\n",
1102 hdr_generic+cookie_hdr_len+1);
1107 ndup = GNUNET_strdup (hdr_generic);
1108 hdr_type = strtok (ndup, ":");
1110 if (NULL == hdr_type)
1116 hdr_val = strtok (NULL, "");
1118 if (NULL == hdr_val)
1126 if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
1128 if (ctask->mhd->is_ssl)
1130 sprintf (leho_host, "https://%s", ctask->leho);
1131 sprintf (real_host, "https://%s", ctask->host);
1135 sprintf (leho_host, "http://%s", ctask->leho);
1136 sprintf (real_host, "http://%s", ctask->host);
1139 if (0 == memcmp (leho_host, hdr_val, strlen (leho_host)))
1141 sprintf (new_location, "%s%s", real_host, hdr_val+strlen (leho_host));
1142 hdr_val = new_location;
1146 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1147 "Trying to set %s: %s\n",
1150 if (GNUNET_NO == MHD_add_response_header (ctask->response,
1154 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1155 "MHD: Error adding %s header field %s\n",
1167 * @param hd a http daemon list entry
1170 run_httpd (struct MhdHttpList *hd);
1182 * Task run whenever HTTP server operations are pending.
1185 * @param tc sched context
1188 do_httpd (void *cls,
1189 const struct GNUNET_SCHEDULER_TaskContext *tc);
1193 run_mhd_now (struct MhdHttpList *hd)
1195 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
1197 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1198 "MHD: killing old task\n");
1199 GNUNET_SCHEDULER_cancel (hd->httpd_task);
1201 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1202 "MHD: Scheduling MHD now\n");
1203 hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, hd);
1208 * Ask cURL for the select sets and schedule download
1211 curl_download_prepare (void);
1215 * Callback to free content
1217 * @param cls content to free
1218 * @param tc task context
1221 mhd_content_free (void *cls,
1222 const struct GNUNET_SCHEDULER_TaskContext *tc)
1224 struct ProxyCurlTask *ctask = cls;
1225 struct ProxyUploadData *pdata;
1227 if (NULL != ctask->headers)
1228 curl_slist_free_all (ctask->headers);
1230 if (NULL != ctask->headers)
1231 curl_slist_free_all (ctask->resolver);
1233 if (NULL != ctask->response)
1234 MHD_destroy_response (ctask->response);
1236 if (NULL != ctask->post_handler)
1237 MHD_destroy_post_processor (ctask->post_handler);
1239 if (GNUNET_SCHEDULER_NO_TASK != ctask->pp_task)
1240 GNUNET_SCHEDULER_cancel (ctask->pp_task);
1242 for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
1244 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1245 ctask->upload_data_tail,
1247 GNUNET_free_non_null (pdata->filename);
1248 GNUNET_free_non_null (pdata->content_type);
1249 GNUNET_free_non_null (pdata->key);
1250 GNUNET_free_non_null (pdata->value);
1251 GNUNET_free (pdata);
1253 GNUNET_free (ctask);
1258 * Callback for MHD response
1260 * @param cls closure
1261 * @param pos in buffer
1263 * @param max space in buffer
1264 * @return number of bytes written
1267 mhd_content_cb (void *cls,
1272 struct ProxyCurlTask *ctask = cls;
1274 size_t bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1276 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1277 "MHD: content cb for %s. To copy: %u\n",
1278 ctask->url, (unsigned int) bytes_to_copy);
1279 if ((GNUNET_YES == ctask->download_is_finished) &&
1280 (GNUNET_NO == ctask->download_error) &&
1281 (0 == bytes_to_copy))
1283 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1284 "MHD: sending response for %s\n", ctask->url);
1285 ctask->download_in_progress = GNUNET_NO;
1286 run_mhd_now (ctask->mhd);
1287 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1288 total_mhd_connections--;
1289 return MHD_CONTENT_READER_END_OF_STREAM;
1292 if ((GNUNET_YES == ctask->download_error) &&
1293 (GNUNET_YES == ctask->download_is_finished) &&
1294 (0 == bytes_to_copy))
1296 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1297 "MHD: sending error response\n");
1298 ctask->download_in_progress = GNUNET_NO;
1299 run_mhd_now (ctask->mhd);
1300 GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1301 total_mhd_connections--;
1302 return MHD_CONTENT_READER_END_WITH_ERROR;
1305 if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
1309 bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1311 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1312 "MHD: copied: %d left: %u, space left in buf: %d\n",
1314 (unsigned int) bytes_to_copy, (int) (max - copied));
1316 if (GNUNET_NO == ctask->download_is_finished)
1318 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1319 "MHD: Purging buffer\n");
1320 memmove (ctask->buffer, ctask->buffer_read_ptr, bytes_to_copy);
1321 ctask->buffer_read_ptr = ctask->buffer;
1322 ctask->buffer_write_ptr = ctask->buffer + bytes_to_copy;
1323 ctask->buffer[bytes_to_copy] = '\0';
1326 if (bytes_to_copy + copied > max)
1327 bytes_to_copy = max - copied;
1328 memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1329 ctask->buffer_read_ptr += bytes_to_copy;
1330 copied += bytes_to_copy;
1331 ctask->buf_status = BUF_WAIT_FOR_CURL;
1333 if (NULL != ctask->curl)
1334 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
1336 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1337 "MHD: copied %d bytes\n", (int) copied);
1338 run_mhd_now (ctask->mhd);
1344 * Handle data from cURL
1346 * @param ptr pointer to the data
1347 * @param size number of blocks of data
1348 * @param nmemb blocksize
1349 * @param ctx the curlproxytask
1350 * @return number of bytes handled
1353 curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx)
1355 const char *cbuf = ptr;
1356 size_t total = size * nmemb;
1357 struct ProxyCurlTask *ctask = ctx;
1358 size_t buf_space = sizeof (ctask->buffer) - (ctask->buffer_write_ptr - ctask->buffer);
1360 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1361 "CURL: Got %d. %d free in buffer\n",
1366 ctask->buf_status = BUF_WAIT_FOR_MHD;
1367 run_mhd_now (ctask->mhd);
1368 return CURL_WRITEFUNC_PAUSE;
1370 if (total > buf_space)
1372 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1373 "CURL: Copying %d bytes to buffer (%s)\n",
1375 memcpy (ctask->buffer_write_ptr, cbuf, total);
1376 ctask->bytes_in_buffer += total;
1377 ctask->buffer_write_ptr += total;
1378 if (ctask->bytes_in_buffer > 0)
1380 ctask->buf_status = BUF_WAIT_FOR_MHD;
1381 run_mhd_now (ctask->mhd);
1388 * cURL callback for put data
1391 put_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1393 struct ProxyCurlTask *ctask = cls;
1394 struct ProxyUploadData *pdata = ctask->upload_data_head;
1395 size_t len = size * nmemb;
1399 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1400 "CURL: put read callback\n");
1403 return CURL_READFUNC_PAUSE;
1406 if (NULL == pdata->value)
1408 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1409 "CURL: Terminating PUT\n");
1411 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1412 ctask->upload_data_tail,
1414 GNUNET_free (pdata);
1418 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1419 "CURL: read callback value %s\n", pdata->value);
1421 to_copy = pdata->bytes_left;
1425 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1426 memcpy (buf, pos, to_copy);
1427 pdata->bytes_left -= to_copy;
1428 if (pdata->bytes_left <= 0)
1430 GNUNET_free (pdata->value);
1431 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1432 ctask->upload_data_tail,
1434 GNUNET_free (pdata);
1441 * cURL callback for post data
1444 post_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1446 struct ProxyCurlTask *ctask = cls;
1447 struct ProxyUploadData *pdata = ctask->upload_data_head;
1448 size_t len = size * nmemb;
1452 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1453 "CURL: read callback\n");
1456 return CURL_READFUNC_PAUSE;
1459 if (NULL == pdata->value)
1461 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1462 "CURL: Terminating POST data\n");
1464 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1465 ctask->upload_data_tail,
1467 GNUNET_free (pdata);
1471 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1472 "CURL: read callback value %s\n", pdata->value);
1474 to_copy = pdata->bytes_left;
1478 pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1479 memcpy (buf, pos, to_copy);
1480 pdata->bytes_left -= to_copy;
1481 if (pdata->bytes_left <= 0)
1483 GNUNET_free (pdata->value);
1484 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1485 ctask->upload_data_tail,
1487 GNUNET_free (pdata);
1494 * Task that is run when we are ready to receive more data
1497 * @param cls closure
1498 * @param tc task context
1501 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1505 * Ask cURL for the select sets and schedule download
1508 curl_download_prepare ()
1515 struct GNUNET_NETWORK_FDSet *grs;
1516 struct GNUNET_NETWORK_FDSet *gws;
1518 struct GNUNET_TIME_Relative rtime;
1524 if (CURLM_OK != (mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max)))
1526 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1527 "%s failed at %s:%d: `%s'\n",
1528 "curl_multi_fdset", __FILE__, __LINE__,
1529 curl_multi_strerror (mret));
1530 //TODO cleanup here?
1534 GNUNET_break (CURLM_OK == curl_multi_timeout (curl_multi, &to));
1535 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1536 "cURL multi fds: max=%d timeout=%lld\n", max, (long long) to);
1538 rtime = GNUNET_TIME_UNIT_FOREVER_REL;
1540 rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1541 grs = GNUNET_NETWORK_fdset_create ();
1542 gws = GNUNET_NETWORK_fdset_create ();
1543 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1544 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1545 if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
1546 GNUNET_SCHEDULER_cancel (curl_download_task);
1549 curl_download_task =
1550 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1553 &curl_task_download, curl_multi);
1555 else if (NULL != ctasks_head)
1557 /* as specified in curl docs */
1558 curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
1559 &curl_task_download,
1562 GNUNET_NETWORK_fdset_destroy (gws);
1563 GNUNET_NETWORK_fdset_destroy (grs);
1568 * Task that is run when we are ready to receive more data
1571 * @param cls closure
1572 * @param tc task context
1575 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1579 struct CURLMsg *msg;
1581 struct ProxyCurlTask *ctask;
1584 struct ProxyCurlTask *clean_head = NULL;
1585 struct ProxyCurlTask *clean_tail = NULL;
1587 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1589 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1591 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1592 "Shutdown requested while trying to download\n");
1596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1604 mret = curl_multi_perform (curl_multi, &running);
1606 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1607 "Running curl tasks: %d\n", running);
1608 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1610 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1611 "CTask: %s\n", ctask->url);
1618 msg = curl_multi_info_read (curl_multi, &msgnum);
1619 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1620 "Messages left: %d\n", msgnum);
1627 if ((msg->data.result != CURLE_OK) &&
1628 (msg->data.result != CURLE_GOT_NOTHING))
1630 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1631 "Download curl failed");
1633 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1635 if (NULL == ctask->curl)
1638 if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
1641 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1642 "CURL: Download failed for task %s: %s.\n",
1644 curl_easy_strerror (msg->data.result));
1645 ctask->download_is_finished = GNUNET_YES;
1646 ctask->download_error = GNUNET_YES;
1647 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1648 CURLINFO_RESPONSE_CODE,
1650 ctask->curl_response_code = resp_code;
1651 ctask->ready_to_queue = MHD_YES;
1652 ctask->buf_status = BUF_WAIT_FOR_MHD;
1653 run_mhd_now (ctask->mhd);
1655 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1657 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1660 GNUNET_assert (ctask != NULL);
1664 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1665 "CURL: download completed.\n");
1667 for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1669 if (NULL == ctask->curl)
1672 if (0 != memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)))
1675 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1676 "CURL: completed task %s found.\n", ctask->url);
1677 if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1678 CURLINFO_RESPONSE_CODE,
1680 ctask->curl_response_code = resp_code;
1683 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1684 "CURL: Completed ctask!\n");
1685 if (GNUNET_SCHEDULER_NO_TASK == ctask->pp_task)
1687 ctask->buf_status = BUF_WAIT_FOR_MHD;
1688 run_mhd_now (ctask->mhd);
1691 ctask->ready_to_queue = MHD_YES;
1692 ctask->download_is_finished = GNUNET_YES;
1694 /* We MUST not modify the multi handle else we loose messages */
1695 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1697 GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1701 GNUNET_assert (ctask != NULL);
1703 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1704 "CURL: %s\n", curl_easy_strerror(msg->data.result));
1710 } while (msgnum > 0);
1712 for (ctask=clean_head; NULL != ctask; ctask = ctask->next)
1714 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1715 "CURL: Removing task %s.\n", ctask->url);
1716 curl_multi_remove_handle (curl_multi, ctask->curl);
1717 curl_easy_cleanup (ctask->curl);
1722 for (ctask=ctasks_head; NULL != ctask; ctask = ctask->next)
1724 GNUNET_assert (num_ctasks == running);
1726 } while (mret == CURLM_CALL_MULTI_PERFORM);
1728 if (mret != CURLM_OK)
1730 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CURL: %s failed at %s:%d: `%s'\n",
1731 "curl_multi_perform", __FILE__, __LINE__,
1732 curl_multi_strerror (mret));
1734 curl_download_prepare();
1739 * Process LEHO lookup
1741 * @param cls the ctask
1742 * @param rd_count number of records returned
1743 * @param rd record data
1746 process_leho_lookup (void *cls,
1748 const struct GNUNET_NAMESTORE_RecordData *rd)
1750 struct ProxyCurlTask *ctask = cls;
1751 char hosthdr[262]; //256 + "Host: "
1755 struct hostent *phost;
1757 char resolvename[512];
1760 strcpy (ctask->leho, "");
1763 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1764 "No LEHO present!\n");
1766 for (i=0; i<rd_count; i++)
1768 if (rd[i].record_type != GNUNET_NAMESTORE_TYPE_LEHO)
1771 memcpy (ctask->leho, rd[i].data, rd[i].data_size);
1773 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1774 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
1777 if (0 != strcmp (ctask->leho, ""))
1779 sprintf (hosthdr, "%s%s:%d", "Host: ", ctask->leho, ctask->port);
1780 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1781 "New HTTP header value: %s\n", hosthdr);
1782 ctask->headers = curl_slist_append (ctask->headers, hosthdr);
1783 GNUNET_assert (NULL != ctask->headers);
1784 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers)))
1785 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1788 if (ctask->mhd->is_ssl)
1790 phost = (struct hostent*)gethostbyname (ctask->host);
1794 ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
1795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1796 "SSL target server: %s\n", ssl_ip);
1797 sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
1798 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1799 "Curl resolve: %s\n", resolvename);
1800 ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
1801 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver)))
1802 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1803 sprintf (curlurl, "https://%s:%d%s", ctask->leho, ctask->port, ctask->url);
1804 if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl)))
1805 LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
1809 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1810 "gethostbyname failed for %s!\n",
1812 ctask->download_is_finished = GNUNET_YES;
1813 ctask->download_error = GNUNET_YES;
1818 if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
1820 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1821 "%s failed at %s:%d: `%s'\n",
1822 "curl_multi_add_handle", __FILE__, __LINE__,
1823 curl_multi_strerror (mret));
1824 ctask->download_is_finished = GNUNET_YES;
1825 ctask->download_error = GNUNET_YES;
1828 GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1830 curl_download_prepare ();
1835 * Initialize download and trigger curl
1837 * @param cls the proxycurltask
1838 * @param auth_name the name of the authority (site of origin) of ctask->host
1841 process_get_authority (void *cls,
1842 const char* auth_name)
1844 struct ProxyCurlTask *ctask = cls;
1846 if (NULL == auth_name)
1848 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1849 "Get authority failed!\n");
1850 strcpy (ctask->authority, "");
1854 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1855 "Get authority yielded %s\n", auth_name);
1856 strcpy (ctask->authority, auth_name);
1859 GNUNET_GNS_lookup (gns_handle,
1862 GNUNET_NAMESTORE_TYPE_LEHO,
1863 GNUNET_YES /* Only cached for performance */,
1865 &process_leho_lookup,
1871 mhd_log_callback (void* cls,
1874 struct ProxyCurlTask *ctask;
1876 ctask = GNUNET_new (struct ProxyCurlTask);
1877 strcpy (ctask->url, url);
1883 * Main MHD callback for handling requests.
1886 * @param con MHD connection handle
1887 * @param url the url in the request
1888 * @param meth the HTTP method used ("GET", "PUT", etc.)
1889 * @param ver the HTTP version string (i.e. "HTTP/1.1")
1890 * @param upload_data the data being uploaded (excluding HEADERS,
1891 * for a POST that fits into memory and that is encoded
1892 * with a supported encoding, the POST data will NOT be
1893 * given in upload_data and is instead available as
1894 * part of MHD_get_connection_values; very large POST
1895 * data *will* be made available incrementally in
1897 * @param upload_data_size set initially to the size of the
1898 * @a upload_data provided; the method must update this
1899 * value to the number of bytes NOT processed;
1900 * @param con_cls pointer to location where we store the 'struct Request'
1901 * @return #MHD_YES if the connection was handled successfully,
1902 * #MHD_NO if the socket must be closed due to a serious
1903 * error while handling the request
1906 create_response (void *cls,
1907 struct MHD_Connection *con,
1911 const char *upload_data,
1912 size_t *upload_data_size,
1915 struct MhdHttpList* hd = cls;
1916 char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow!
1919 struct ProxyCurlTask *ctask = *con_cls;
1920 struct ProxyUploadData *fin_post;
1921 struct curl_forms forms[5];
1922 struct ProxyUploadData *upload_data_iter;
1925 if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) &&
1926 (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) &&
1927 (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)) &&
1928 (0 != strcasecmp (meth, MHD_HTTP_METHOD_HEAD)))
1930 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1931 "MHD: %s NOT IMPLEMENTED!\n", meth);
1936 if (GNUNET_NO == ctask->accepted)
1939 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1940 "Got %s request for %s\n", meth, url);
1942 ctask->curl = curl_easy_init();
1943 ctask->curl_running = GNUNET_NO;
1944 if (NULL == ctask->curl)
1946 ret = MHD_queue_response (con,
1948 curl_failure_response);
1949 GNUNET_free (ctask);
1953 if (ctask->mhd->is_ssl)
1954 ctask->port = HTTPS_PORT;
1956 ctask->port = HTTP_PORT;
1958 MHD_get_connection_values (con,
1960 &con_val_iter, ctask);
1962 curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1963 curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
1964 curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1965 curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
1966 curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0);
1967 curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1969 if (GNUNET_NO == ctask->mhd->is_ssl)
1971 sprintf (curlurl, "http://%s:%d%s", ctask->host, ctask->port, ctask->url);
1972 curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1976 curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
1977 curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
1978 curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
1980 /* Add GNS header */
1981 ctask->headers = curl_slist_append (ctask->headers,
1983 ctask->accepted = GNUNET_YES;
1984 ctask->download_in_progress = GNUNET_YES;
1985 ctask->buf_status = BUF_WAIT_FOR_CURL;
1986 ctask->connection = con;
1987 ctask->curl_response_code = MHD_HTTP_OK;
1988 ctask->buffer_read_ptr = ctask->buffer;
1989 ctask->buffer_write_ptr = ctask->buffer;
1990 ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
1993 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
1995 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1996 "Setting up PUT\n");
1998 curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1);
1999 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
2000 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &put_read_callback);
2001 ctask->headers = curl_slist_append (ctask->headers,
2002 "Transfer-Encoding: chunked");
2005 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2007 //FIXME handle multipart
2008 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2009 "Setting up POST processor\n");
2010 ctask->post_handler = MHD_create_post_processor (con,
2012 &con_post_data_iter,
2014 ctask->headers = curl_slist_append (ctask->headers,
2015 "Transfer-Encoding: chunked");
2019 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
2021 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2022 "Setting NOBODY\n");
2023 curl_easy_setopt (ctask->curl, CURLOPT_NOBODY, 1);
2027 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2028 "MHD: Adding new curl task for %s\n", ctask->host);
2030 GNUNET_GNS_get_authority (gns_handle,
2032 &process_get_authority,
2035 ctask->ready_to_queue = GNUNET_NO;
2036 ctask->fin = GNUNET_NO;
2037 ctask->curl_running = GNUNET_YES;
2041 ctask = (struct ProxyCurlTask *) *con_cls;
2042 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2044 if (0 != *upload_data_size)
2047 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2048 "Invoking POST processor\n");
2049 MHD_post_process (ctask->post_handler,
2050 upload_data, *upload_data_size);
2051 *upload_data_size = 0;
2052 if ((GNUNET_NO == ctask->is_httppost) &&
2053 (GNUNET_NO == ctask->curl_running))
2055 curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
2056 curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION,
2057 &post_read_callback);
2058 curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
2060 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2061 "MHD: Adding new curl task for %s\n", ctask->host);
2063 GNUNET_GNS_get_authority (gns_handle,
2065 &process_get_authority,
2068 ctask->ready_to_queue = GNUNET_NO;
2069 ctask->fin = GNUNET_NO;
2070 ctask->curl_running = GNUNET_YES;
2074 else if (GNUNET_NO == ctask->post_done)
2076 if (GNUNET_YES == ctask->is_httppost)
2078 for (upload_data_iter = ctask->upload_data_head;
2079 NULL != upload_data_iter;
2080 upload_data_iter = upload_data_iter->next)
2083 if (NULL != upload_data_iter->filename)
2085 forms[i].option = CURLFORM_FILENAME;
2086 forms[i].value = upload_data_iter->filename;
2087 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2088 "Adding filename %s\n",
2092 if (NULL != upload_data_iter->content_type)
2094 forms[i].option = CURLFORM_CONTENTTYPE;
2095 forms[i].value = upload_data_iter->content_type;
2096 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2097 "Adding content type %s\n",
2101 forms[i].option = CURLFORM_PTRCONTENTS;
2102 forms[i].value = upload_data_iter->value;
2103 forms[i+1].option = CURLFORM_END;
2105 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2106 "Adding formdata for %s (len=%lld)\n",
2107 upload_data_iter->key,
2108 upload_data_iter->total_bytes);
2110 curl_formadd(&ctask->httppost, &ctask->httppost_last,
2111 CURLFORM_COPYNAME, upload_data_iter->key,
2112 CURLFORM_CONTENTSLENGTH, upload_data_iter->total_bytes,
2113 CURLFORM_ARRAY, forms,
2116 curl_easy_setopt (ctask->curl, CURLOPT_HTTPPOST,
2119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2120 "MHD: Adding new curl task for %s\n", ctask->host);
2122 GNUNET_GNS_get_authority (gns_handle,
2124 &process_get_authority,
2127 ctask->ready_to_queue = GNUNET_YES;
2128 ctask->fin = GNUNET_NO;
2129 ctask->curl_running = GNUNET_YES;
2130 ctask->post_done = GNUNET_YES;
2134 fin_post = GNUNET_new (struct ProxyUploadData);
2135 GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
2136 ctask->upload_data_tail,
2138 ctask->post_done = GNUNET_YES;
2143 if (GNUNET_YES != ctask->ready_to_queue)
2144 return MHD_YES; /* wait longer */
2146 if (GNUNET_YES == ctask->fin)
2149 ctask->fin = GNUNET_YES;
2150 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2151 "MHD: Queueing response for %s\n", ctask->url);
2152 ret = MHD_queue_response (con, ctask->curl_response_code, ctask->response);
2153 run_mhd_now (ctask->mhd);
2164 struct MhdHttpList *hd;
2166 for (hd=mhd_httpd_head; NULL != hd; hd = hd->next)
2175 * @param hd the daemon to run
2178 run_httpd (struct MhdHttpList *hd)
2183 struct GNUNET_NETWORK_FDSet *wrs;
2184 struct GNUNET_NETWORK_FDSet *wws;
2185 struct GNUNET_NETWORK_FDSet *wes;
2188 MHD_UNSIGNED_LONG_LONG timeout;
2189 struct GNUNET_TIME_Relative tv;
2194 wrs = GNUNET_NETWORK_fdset_create ();
2195 wes = GNUNET_NETWORK_fdset_create ();
2196 wws = GNUNET_NETWORK_fdset_create ();
2198 GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
2201 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2202 "MHD fds: max=%d\n", max);
2204 haveto = MHD_get_timeout (hd->daemon, &timeout);
2206 if (MHD_YES == haveto)
2207 tv.rel_value_us = (uint64_t) timeout * 1000LL;
2209 tv = GNUNET_TIME_UNIT_FOREVER_REL;
2210 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
2211 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
2212 GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
2214 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2215 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2217 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
2220 GNUNET_NETWORK_fdset_destroy (wrs);
2221 GNUNET_NETWORK_fdset_destroy (wws);
2222 GNUNET_NETWORK_fdset_destroy (wes);
2227 * Task run whenever HTTP server operations are pending.
2230 * @param tc sched context
2233 do_httpd (void *cls,
2234 const struct GNUNET_SCHEDULER_TaskContext *tc)
2236 struct MhdHttpList *hd = cls;
2238 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2239 "MHD: Main loop\n");
2240 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2241 MHD_run (hd->daemon);
2247 * Read data from socket
2249 * @param cls the closure
2250 * @param tc scheduler context
2253 do_s5r_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2257 * Read from remote end
2259 * @param cls closure
2260 * @param tc scheduler context
2263 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2267 * Write data to remote socket
2269 * @param cls the closure
2270 * @param tc scheduler context
2273 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2275 struct Socks5Request *s5r = cls;
2278 s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2279 if ( (NULL != tc->read_ready) &&
2280 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
2281 ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
2284 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2285 "Successfully sent %d bytes to remote socket\n",
2290 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send");
2296 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2303 * Write data to socket
2305 * @param cls the closure
2306 * @param tc scheduler context
2309 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2311 struct Socks5Request *s5r = cls;
2314 s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2316 if ((NULL != tc->read_ready) &&
2317 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
2318 ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
2321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2322 "Successfully sent %d bytes to socket\n",
2327 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
2328 s5r->cleanup = GNUNET_YES;
2329 s5r->cleanup_sock = GNUNET_YES;
2334 if (GNUNET_YES == s5r->cleanup)
2340 if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
2341 (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
2343 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2345 &do_read_remote, s5r);
2350 * Read from remote end
2352 * @param cls closure
2353 * @param tc scheduler context
2356 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2358 struct Socks5Request *s5r = cls;
2360 s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2361 if ((NULL != tc->write_ready) &&
2362 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
2363 (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
2364 sizeof (s5r->wbuf))))
2366 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2367 "Successfully read %d bytes from remote socket\n",
2372 if (0 == s5r->wbuf_len)
2373 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2374 "0 bytes received from remote... graceful shutdown!\n");
2379 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2386 * Adds a socket to MHD
2388 * @param h the handle to the socket to add
2389 * @param daemon the daemon to add the fd to
2390 * @return whatever #MHD_add_connection returns
2393 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h,
2394 struct MHD_Daemon *daemon)
2397 struct sockaddr *addr;
2401 fd = GNUNET_NETWORK_get_fd (h);
2402 addr = GNUNET_NETWORK_get_addr (h);
2403 len = GNUNET_NETWORK_get_addrlen (h);
2404 ret = MHD_add_connection (daemon, fd, addr, len);
2405 GNUNET_NETWORK_socket_free_memory_only_ (h);
2411 * Read file in filename
2413 * @param filename file to read
2414 * @param size pointer where filesize is stored
2415 * @return NULL on error
2418 load_file (const char* filename,
2425 GNUNET_DISK_file_size (filename, &fsize,
2426 GNUNET_YES, GNUNET_YES))
2428 if (fsize > MAX_PEM_SIZE)
2430 *size = (unsigned int) fsize;
2431 buffer = GNUNET_malloc (*size);
2432 if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize))
2434 GNUNET_free (buffer);
2442 * Load PEM key from file
2444 * @param key where to store the data
2445 * @param keyfile path to the PEM file
2446 * @return #GNUNET_OK on success
2449 load_key_from_file (gnutls_x509_privkey_t key,
2450 const char* keyfile)
2452 gnutls_datum_t key_data;
2455 key_data.data = load_file (keyfile, &key_data.size);
2456 ret = gnutls_x509_privkey_import (key, &key_data,
2457 GNUTLS_X509_FMT_PEM);
2458 if (GNUTLS_E_SUCCESS != ret)
2460 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2461 _("Unable to import private key from file `%s'\n"),
2465 GNUNET_free (key_data.data);
2466 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2471 * Load cert from file
2473 * @param crt struct to store data in
2474 * @param certfile path to pem file
2475 * @return #GNUNET_OK on success
2478 load_cert_from_file (gnutls_x509_crt_t crt,
2479 const char* certfile)
2481 gnutls_datum_t cert_data;
2484 cert_data.data = load_file (certfile, &cert_data.size);
2485 ret = gnutls_x509_crt_import (crt, &cert_data,
2486 GNUTLS_X509_FMT_PEM);
2487 if (GNUTLS_E_SUCCESS != ret)
2489 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2490 _("Unable to import certificate %s\n"), certfile);
2493 GNUNET_free (cert_data.data);
2494 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2499 * Generate new certificate for specific name
2501 * @param name the subject name to generate a cert for
2502 * @return a struct holding the PEM data
2504 static struct ProxyGNSCertificate *
2505 generate_gns_certificate (const char *name)
2507 unsigned int serial;
2508 size_t key_buf_size;
2509 size_t cert_buf_size;
2510 gnutls_x509_crt_t request;
2513 struct ProxyGNSCertificate *pgc;
2515 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2516 "Generating cert for `%s'\n",
2518 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_init (&request));
2519 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
2520 pgc = GNUNET_new (struct ProxyGNSCertificate);
2521 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
2523 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
2524 0, "GNU Name System", 4);
2525 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
2526 0, name, strlen (name));
2527 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_version (request, 3));
2528 gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
2529 gnutls_x509_crt_set_serial (request,
2532 etime = time (NULL);
2533 tm_data = localtime (&etime);
2534 gnutls_x509_crt_set_activation_time (request,
2537 etime = mktime (tm_data);
2538 gnutls_x509_crt_set_expiration_time (request,
2540 gnutls_x509_crt_sign (request,
2543 key_buf_size = sizeof (pgc->key);
2544 cert_buf_size = sizeof (pgc->cert);
2545 gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
2546 pgc->cert, &cert_buf_size);
2547 gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
2548 pgc->key, &key_buf_size);
2549 gnutls_x509_crt_deinit (request);
2555 * Adds a socket to an SSL MHD instance It is important that the
2556 * domain name is correct. In most cases we need to start a new daemon.
2558 * @param h the handle to add to a daemon
2559 * @param domain the domain the SSL daemon has to serve
2560 * @return #MHD_YES on success
2563 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h,
2566 struct MhdHttpList *hd;
2567 struct ProxyGNSCertificate *pgc;
2569 for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2570 if (0 == strcmp (hd->domain, domain))
2574 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2575 "Starting fresh MHD HTTPS instance for domain `%s'\n",
2577 pgc = generate_gns_certificate (domain);
2578 hd = GNUNET_new (struct MhdHttpList);
2579 hd->is_ssl = GNUNET_YES;
2580 strcpy (hd->domain, domain); /* FIXME: avoid fixed-sized buffers... */
2581 hd->proxy_cert = pgc;
2582 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET,
2585 &create_response, hd,
2586 MHD_OPTION_CONNECTION_LIMIT,
2587 MHD_MAX_CONNECTIONS,
2588 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2589 MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2590 MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2591 MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2592 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2595 /* FIXME: rather than assert, handle error! */
2596 GNUNET_assert (NULL != hd->daemon);
2597 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2599 return add_handle_to_mhd (h, hd->daemon);
2604 * Read data from incoming Socks5 connection
2606 * @param cls the closure with the `struct Socks5Request`
2607 * @param tc the scheduler context
2610 do_s5r_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2612 struct Socks5Request *s5r = cls;
2613 struct socks5_client_hello *c_hello;
2614 struct socks5_server_hello *s_hello;
2615 struct socks5_client_request *c_req;
2616 struct socks5_server_response *s_resp;
2621 struct hostent *phost;
2623 struct sockaddr_in remote_addr;
2624 struct in_addr *r_sin_addr;
2626 s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
2627 if ( (NULL != tc->read_ready) &&
2628 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) )
2629 s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
2630 sizeof (s5r->rbuf));
2633 if (0 == s5r->rbuf_len)
2635 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2636 "socks5 client disconnected.\n");
2640 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2641 "Processing socks data in state %d\n",
2646 /* FIXME: failed to check if we got enough data yet! */
2647 c_hello = (struct socks5_client_hello*) &s5r->rbuf;
2648 GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
2649 s_hello = (struct socks5_server_hello*) &s5r->wbuf;
2650 s5r->wbuf_len = sizeof( struct socks5_server_hello );
2651 s_hello->version = c_hello->version;
2652 s_hello->auth_method = SOCKS_AUTH_NONE;
2653 /* Write response to client */
2654 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2657 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2660 s5r->state = SOCKS5_REQUEST;
2662 case SOCKS5_REQUEST:
2663 /* FIXME: failed to check if we got enough data yet!? */
2664 c_req = (struct socks5_client_request *) &s5r->rbuf;
2665 s_resp = (struct socks5_server_response *) &s5r->wbuf;
2666 //Only 10 byte for ipv4 response!
2667 s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
2668 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));
2673 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2674 "Requested connection is to %s:%d\n",
2677 if (is_tld (domain, GNUNET_GNS_TLD) ||
2678 is_tld (domain, GNUNET_GNS_TLD_ZKEY))
2682 if (ntohs (req_port) == HTTPS_PORT)
2684 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2685 "Requested connection is HTTPS\n");
2686 ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
2688 else if (NULL != httpd)
2690 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2691 "Requested connection is HTTP\n");
2692 ret = add_handle_to_mhd (s5r->sock, httpd );
2697 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2698 _("Failed to start HTTP server\n"));
2699 s_resp->version = 0x05;
2700 s_resp->reply = 0x01;
2701 s5r->cleanup = GNUNET_YES;
2702 s5r->cleanup_sock = GNUNET_YES;
2704 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2710 /* Signal success */
2711 s_resp->version = 0x05;
2712 s_resp->reply = 0x00;
2713 s_resp->reserved = 0x00;
2714 s_resp->addr_type = 0x01;
2716 s5r->cleanup = GNUNET_YES;
2717 s5r->cleanup_sock = GNUNET_NO;
2719 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2727 /* non-GNS TLD, use DNS to resolve */
2728 /* FIXME: make asynchronous! */
2729 phost = (struct hostent *) gethostbyname (domain);
2732 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2733 "Resolve %s error!\n", domain );
2734 s_resp->version = 0x05;
2735 s_resp->reply = 0x01;
2736 s5r->cleanup = GNUNET_YES;
2737 s5r->cleanup_sock = GNUNET_YES;
2739 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2745 s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
2748 r_sin_addr = (struct in_addr*)(phost->h_addr);
2749 remote_ip = r_sin_addr->s_addr;
2750 memset(&remote_addr, 0, sizeof(remote_addr));
2751 remote_addr.sin_family = AF_INET;
2752 #if HAVE_SOCKADDR_IN_SIN_LEN
2753 remote_addr.sin_len = sizeof (remote_addr);
2755 remote_addr.sin_addr.s_addr = remote_ip;
2756 remote_addr.sin_port = req_port;
2758 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2759 "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
2763 GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
2764 (const struct sockaddr*)&remote_addr,
2765 sizeof (remote_addr)))
2766 && (errno != EINPROGRESS))
2768 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
2769 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2770 "socket request error...\n");
2771 s_resp->version = 0x05;
2772 s_resp->reply = 0x01;
2774 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2780 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2781 "new remote connection\n");
2782 s_resp->version = 0x05;
2783 s_resp->reply = 0x00;
2784 s_resp->reserved = 0x00;
2785 s_resp->addr_type = 0x01;
2786 s5r->state = SOCKS5_DATA_TRANSFER;
2788 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2792 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2797 case SOCKS5_DATA_TRANSFER:
2799 if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
2801 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2802 "Closing connection to client\n");
2806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2807 "forwarding %d bytes from client\n", s5r->rbuf_len);
2809 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2811 &do_write_remote, s5r);
2812 if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
2815 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2817 &do_read_remote, s5r);
2829 * Accept new incoming connections
2831 * @param cls the closure
2832 * @param tc the scheduler context
2835 do_accept (void *cls,
2836 const struct GNUNET_SCHEDULER_TaskContext *tc)
2838 struct GNUNET_NETWORK_Handle *s;
2839 struct Socks5Request *s5r;
2841 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2844 s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
2847 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
2850 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2851 "Got an inbound connection, waiting for data\n");
2852 s5r = GNUNET_new (struct Socks5Request);
2853 GNUNET_CONTAINER_DLL_insert (s5r_head,
2857 s5r->state = SOCKS5_INIT;
2858 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2865 * Task run on shutdown
2867 * @param cls closure
2868 * @param tc task context
2871 do_shutdown (void *cls,
2872 const struct GNUNET_SCHEDULER_TaskContext *tc)
2874 struct MhdHttpList *hd;
2875 struct MhdHttpList *tmp_hd;
2876 struct ProxyCurlTask *ctask;
2877 struct ProxyCurlTask *ctask_tmp;
2878 struct ProxyUploadData *pdata;
2880 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2881 "Shutting down...\n");
2882 for (hd = mhd_httpd_head; NULL != hd; hd = tmp_hd)
2885 if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2887 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2888 hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2890 if (NULL != hd->daemon)
2892 MHD_stop_daemon (hd->daemon);
2895 GNUNET_free_non_null (hd->proxy_cert);
2898 for (ctask=ctasks_head; NULL != ctask; ctask=ctask_tmp)
2900 ctask_tmp = ctask->next;
2901 if (NULL != ctask->curl)
2903 curl_easy_cleanup (ctask->curl);
2906 if (NULL != ctask->headers)
2908 curl_slist_free_all (ctask->headers);
2909 ctask->headers = NULL;
2911 if (NULL != ctask->resolver)
2913 curl_slist_free_all (ctask->resolver);
2914 ctask->resolver = NULL;
2916 if (NULL != ctask->response)
2918 MHD_destroy_response (ctask->response);
2919 ctask->response = NULL;
2921 for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
2923 GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
2924 ctask->upload_data_tail,
2926 GNUNET_free_non_null (pdata->filename);
2927 GNUNET_free_non_null (pdata->content_type);
2928 GNUNET_free_non_null (pdata->key);
2929 GNUNET_free_non_null (pdata->value);
2930 GNUNET_free (pdata);
2932 GNUNET_free (ctask);
2936 GNUNET_NETWORK_socket_close (lsock);
2941 GNUNET_IDENTITY_cancel (id_op);
2944 if (NULL != identity)
2946 GNUNET_IDENTITY_disconnect (identity);
2949 if (NULL != curl_multi)
2951 curl_multi_cleanup (curl_multi);
2954 if (NULL != gns_handle)
2956 GNUNET_GNS_disconnect (gns_handle);
2959 if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
2961 GNUNET_SCHEDULER_cancel (curl_download_task);
2962 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
2964 if (GNUNET_SCHEDULER_NO_TASK != ltask)
2966 GNUNET_SCHEDULER_cancel (ltask);
2967 ltask = GNUNET_SCHEDULER_NO_TASK;
2969 gnutls_global_deinit ();
2974 * Continue initialization after we have our zone information.
2979 struct MhdHttpList *hd;
2980 struct sockaddr_in sa;
2982 /* Open listen socket for socks proxy */
2983 /* FIXME: support IPv6! */
2984 memset (&sa, 0, sizeof (sa));
2985 sa.sin_family = AF_INET;
2986 sa.sin_port = htons (port);
2987 #if HAVE_SOCKADDR_IN_SIN_LEN
2988 sa.sin_len = sizeof (sa);
2990 lsock = GNUNET_NETWORK_socket_create (AF_INET,
2995 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
2996 GNUNET_SCHEDULER_shutdown ();
3000 GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
3003 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
3004 GNUNET_SCHEDULER_shutdown ();
3007 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
3009 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
3012 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3013 lsock, &do_accept, NULL);
3015 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
3017 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3018 "cURL global init failed!\n");
3019 GNUNET_SCHEDULER_shutdown ();
3022 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3023 "Proxy listens on port %u\n",
3026 /* start MHD daemon for HTTP */
3027 hd = GNUNET_new (struct MhdHttpList);
3028 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
3031 &create_response, hd,
3032 MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3033 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3034 MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
3035 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3037 if (NULL == hd->daemon)
3040 GNUNET_SCHEDULER_shutdown ();
3044 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
3046 /* start loop running all MHD instances */
3052 * Method called to inform about the egos of the shorten zone of this peer.
3054 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
3055 * this function is only called ONCE, and 'NULL' being passed in
3056 * @a ego does indicate an error (i.e. name is taken or no default
3057 * value is known). If @a ego is non-NULL and if '*ctx'
3058 * is set in those callbacks, the value WILL be passed to a subsequent
3059 * call to the identity callback of #GNUNET_IDENTITY_connect (if
3060 * that one was not NULL).
3062 * @param cls closure, NULL
3063 * @param ego ego handle
3064 * @param ctx context for application to store data for this ego
3065 * (during the lifetime of this process, initially NULL)
3066 * @param name name assigned by the user for this ego,
3067 * NULL if the user just deleted the ego and it
3068 * must thus no longer be used
3071 identity_shorten_cb (void *cls,
3072 struct GNUNET_IDENTITY_Ego *ego,
3079 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3080 _("No ego configured for `shorten-zone`\n"));
3084 local_shorten_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
3085 do_shorten = GNUNET_YES;
3092 * Method called to inform about the egos of the master zone of this peer.
3094 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
3095 * this function is only called ONCE, and 'NULL' being passed in
3096 * @a ego does indicate an error (i.e. name is taken or no default
3097 * value is known). If @a ego is non-NULL and if '*ctx'
3098 * is set in those callbacks, the value WILL be passed to a subsequent
3099 * call to the identity callback of #GNUNET_IDENTITY_connect (if
3100 * that one was not NULL).
3102 * @param cls closure, NULL
3103 * @param ego ego handle
3104 * @param ctx context for application to store data for this ego
3105 * (during the lifetime of this process, initially NULL)
3106 * @param name name assigned by the user for this ego,
3107 * NULL if the user just deleted the ego and it
3108 * must thus no longer be used
3111 identity_master_cb (void *cls,
3112 struct GNUNET_IDENTITY_Ego *ego,
3119 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3120 _("No ego configured for `master-zone`\n"));
3121 GNUNET_SCHEDULER_shutdown ();
3124 GNUNET_IDENTITY_ego_get_public_key (ego,
3126 id_op = GNUNET_IDENTITY_get (identity,
3128 &identity_shorten_cb,
3134 * Main function that will be run
3136 * @param cls closure
3137 * @param args remaining command-line arguments
3138 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3139 * @param c configuration
3142 run (void *cls, char *const *args, const char *cfgfile,
3143 const struct GNUNET_CONFIGURATION_Handle *c)
3145 char* cafile_cfg = NULL;
3149 if (NULL == (curl_multi = curl_multi_init ()))
3151 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3152 "Failed to create cURL multi handle!\n");
3155 cafile = cafile_opt;
3158 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3162 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
3167 cafile = cafile_cfg;
3169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3170 "Using %s as CA\n", cafile);
3172 gnutls_global_init ();
3173 gnutls_x509_crt_init (&proxy_ca.cert);
3174 gnutls_x509_privkey_init (&proxy_ca.key);
3176 if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
3177 (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
3179 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3180 _("Failed to load SSL/TLS key and certificate from `%s'\n"),
3182 // FIXME: release resources...
3183 GNUNET_free_non_null (cafile_cfg);
3186 GNUNET_free_non_null (cafile_cfg);
3187 if (NULL == (gns_handle = GNUNET_GNS_connect (cfg)))
3189 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3190 "Unable to connect to GNS!\n");
3193 identity = GNUNET_IDENTITY_connect (cfg,
3195 id_op = GNUNET_IDENTITY_get (identity,
3197 &identity_master_cb,
3199 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
3200 &do_shutdown, NULL);
3205 * The main function for gnunet-gns-proxy.
3207 * @param argc number of arguments from the command line
3208 * @param argv command line arguments
3209 * @return 0 ok, 1 on error
3212 main (int argc, char *const *argv)
3214 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
3216 gettext_noop ("listen on specified port (default: 7777)"), 1,
3217 &GNUNET_GETOPT_set_ulong, &port},
3218 {'a', "authority", NULL,
3219 gettext_noop ("pem file to use as CA"), 1,
3220 &GNUNET_GETOPT_set_string, &cafile_opt},
3221 GNUNET_GETOPT_OPTION_END
3223 static const char* page =
3224 "<html><head><title>gnunet-gns-proxy</title>"
3225 "</head><body>cURL fail</body></html>";
3228 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
3230 GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
3231 curl_failure_response = MHD_create_response_from_buffer (strlen (page),
3233 MHD_RESPMEM_PERSISTENT);
3237 GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
3238 _("GNUnet GNS proxy"),
3240 &run, NULL)) ? 0 : 1;
3241 MHD_destroy_response (curl_failure_response);
3242 GNUNET_free_non_null ((char *) argv);
3246 /* end of gnunet-gns-proxy.c */