GNUnet  0.10.x
gnunet-gns-proxy.c
Go to the documentation of this file.
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2012-2018 GNUnet e.V.
4 
5  GNUnet is free software: you can redistribute it and/or modify it
6  under the terms of the GNU Affero General Public License as published
7  by the Free Software Foundation, either version 3 of the License,
8  or (at your option) any later version.
9 
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  Affero General Public License for more details.
14 
15  You should have received a copy of the GNU Affero General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  SPDX-License-Identifier: AGPL3.0-or-later
19 */
30 #include "platform.h"
31 #include <microhttpd.h>
32 /* Just included for the right curl.h */
33 #include "gnunet_curl_lib.h"
34 #include <gnutls/gnutls.h>
35 #include <gnutls/x509.h>
36 #include <gnutls/abstract.h>
37 #include <gnutls/crypto.h>
38 #if HAVE_GNUTLS_DANE
39 #include <gnutls/dane.h>
40 #endif
41 #include <regex.h>
42 #include "gnunet_util_lib.h"
43 #include "gnunet_gns_service.h"
45 #include "gns.h"
46 
47 
48 
52 #define GNUNET_GNS_PROXY_PORT 7777
53 
58 #define MAX_HTTP_URI_LENGTH 2048
59 
64 #define MAX_DANES 32
65 
70 #define IO_BUFFERSIZE CURL_MAX_WRITE_SIZE
71 
77 #define SOCKS_BUFFERSIZE (256 + 32)
78 
82 #define HTTP_PORT 80
83 
87 #define HTTPS_PORT 443
88 
92 #define MAX_PEM_SIZE (10 * 1024)
93 
97 #define MHD_CACHE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
98 
103 #define HTTP_HANDSHAKE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
104 
105 
113 #define LOG_CURL_EASY(level,fun,rc) \
114  GNUNET_log (level, \
115  _("%s failed at %s:%d: `%s'\n"), \
116  fun, \
117  __FILE__, \
118  __LINE__, \
119  curl_easy_strerror (rc))
120 
121 
122 /* *************** Socks protocol definitions (move to TUN?) ****************** */
123 
127 #define SOCKS_VERSION_5 0x05
128 
132 #define SOCKS_AUTH_NONE 0
133 
134 
139 {
144 
149 
154 };
155 
156 
161 {
166 
171 
176 
177 };
178 
179 
184 {
194 };
195 
196 
201 {
205  uint8_t version;
206 
211 
212  /* followed by supported authentication methods, 1 byte per method */
213 
214 };
215 
216 
221 {
225  uint8_t version;
226 
231  uint8_t auth_method;
232 };
233 
234 
239 {
243  uint8_t version;
244 
248  uint8_t command;
249 
253  uint8_t resvd;
254 
258  uint8_t addr_type;
259 
260  /*
261  * Followed by either an ip4/ipv6 address or a domain name with a
262  * length field (uint8_t) in front (depending on @e addr_type).
263  * followed by port number in network byte order (uint16_t).
264  */
265 };
266 
267 
272 {
276  uint8_t version;
277 
281  uint8_t reply;
282 
286  uint8_t reserved;
287 
291  uint8_t addr_type;
292 
293  /*
294  * Followed by either an ip4/ipv6 address or a domain name with a
295  * length field (uint8_t) in front (depending on @e addr_type).
296  * followed by port number in network byte order (uint16_t).
297  */
298 
299 };
300 
301 
302 
303 /* *********************** Datastructures for HTTP handling ****************** */
304 
308 struct ProxyCA
309 {
313  gnutls_x509_crt_t cert;
314 
318  gnutls_x509_privkey_t key;
319 };
320 
321 
326 {
330  char cert[MAX_PEM_SIZE];
331 
336 };
337 
338 
339 
344 {
348  struct MhdHttpList *prev;
349 
353  struct MhdHttpList *next;
354 
358  char *domain;
359 
363  struct MHD_Daemon *daemon;
364 
369 
374 
378  int is_ssl;
379 
380 };
381 
382 
383 /* ***************** Datastructures for Socks handling **************** */
384 
385 
390 {
395 
400 
405 
410 
415 
420 
425 
430 
435 
440 };
441 
442 
447 {
452 
457 
461  char *type;
462 
466  char *value;
467 };
468 
473 {
474 
479 
484 
489 
494 
499 
504 
509 
513  char rbuf[SOCKS_BUFFERSIZE];
514 
518  char wbuf[SOCKS_BUFFERSIZE];
519 
523  char io_buf[IO_BUFFERSIZE];
524 
528  struct MhdHttpList *hd;
529 
533  struct MHD_Connection *con;
534 
538  struct MHD_Response *response;
539 
543  char *domain;
544 
548  char *leho;
549 
553  char *dane_data[MAX_DANES + 1];
554 
558  char *url;
559 
563  CURL *curl;
564 
568  struct curl_slist *headers;
569 
573  struct curl_slist *hosts;
574 
578  unsigned int response_code;
579 
583  int dane_data_len[MAX_DANES + 1];
584 
589  unsigned int num_danes;
590 
594  size_t rbuf_len;
595 
599  size_t wbuf_len;
600 
604  size_t io_len;
605 
609  struct sockaddr_storage destination_address;
610 
615 
619  uint16_t port;
620 
625 
630 
635 
639  int is_gns;
640 
644  int is_tls;
645 
650 
655 };
656 
657 
658 
659 /* *********************** Globals **************************** */
660 
664 static in_addr_t address;
665 
669 static struct in6_addr address6;
670 
674 static uint16_t port = GNUNET_GNS_PROXY_PORT;
675 
679 static char *cafile_opt;
680 
685 
690 
695 
700 
705 
709 static CURLM *curl_multi;
710 
715 
719 static int disable_v6;
720 
725 
730 
735 static struct MhdHttpList *httpd;
736 
740 static struct Socks5Request *s5r_head;
741 
745 static struct Socks5Request *s5r_tail;
746 
750 static struct ProxyCA proxy_ca;
751 
755 static struct MHD_Response *curl_failure_response;
756 
760 static const struct GNUNET_CONFIGURATION_Handle *cfg;
761 
762 
763 /* ************************* Global helpers ********************* */
764 
765 
771 static void
772 run_mhd_now (struct MhdHttpList *hd);
773 
774 
780 static void
782 {
784  "Cleaning up socks request\n");
785  if (NULL != s5r->curl)
786  {
788  "Cleaning up cURL handle\n");
789  curl_multi_remove_handle (curl_multi,
790  s5r->curl);
791  curl_easy_cleanup (s5r->curl);
792  s5r->curl = NULL;
793  }
794  if (s5r->suspended)
795  {
796  s5r->suspended = GNUNET_NO;
797  MHD_resume_connection (s5r->con);
798  }
799  curl_slist_free_all (s5r->headers);
800  if (NULL != s5r->hosts)
801  {
802  curl_slist_free_all (s5r->hosts);
803  }
804  if ( (NULL != s5r->response) &&
805  (curl_failure_response != s5r->response) )
806  {
807  MHD_destroy_response (s5r->response);
808  s5r->response = NULL;
809  }
810  if (NULL != s5r->rtask)
811  {
813  s5r->rtask = NULL;
814  }
815  if (NULL != s5r->timeout_task)
816  {
818  s5r->timeout_task = NULL;
819  }
820  if (NULL != s5r->wtask)
821  {
823  s5r->wtask = NULL;
824  }
825  if (NULL != s5r->gns_lookup)
826  {
828  s5r->gns_lookup = NULL;
829  }
830  if (NULL != s5r->sock)
831  {
832  if (SOCKS5_SOCKET_WITH_MHD <= s5r->state)
834  else
836  s5r->sock = NULL;
837  }
838  GNUNET_CONTAINER_DLL_remove (s5r_head,
839  s5r_tail,
840  s5r);
842  GNUNET_free_non_null (s5r->leho);
843  GNUNET_free_non_null (s5r->url);
844  for (unsigned int i=0;i<s5r->num_danes;i++)
845  GNUNET_free (s5r->dane_data[i]);
846  GNUNET_free (s5r);
847 }
848 
849 
850 /* ************************* HTTP handling with cURL *********************** */
851 
852 static void
854 
855 
867 static ssize_t
868 mhd_content_cb (void *cls,
869  uint64_t pos,
870  char* buf,
871  size_t max)
872 {
873  struct Socks5Request *s5r = cls;
874  size_t bytes_to_copy;
875 
876  if ( (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) ||
877  (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
878  {
879  /* we're still not done with the upload, do not yet
880  start the download, the IO buffer is still full
881  with upload data. */
883  "Pausing MHD download %s%s, not yet ready for download\n",
884  s5r->domain,
885  s5r->url);
886  return 0; /* not yet ready for data download */
887  }
888  bytes_to_copy = GNUNET_MIN (max,
889  s5r->io_len);
890  if ( (0 == bytes_to_copy) &&
892  {
894  "Pausing MHD download %s%s, no data available\n",
895  s5r->domain,
896  s5r->url);
897  if (NULL != s5r->curl)
898  {
900  "Continuing CURL interaction for %s%s\n",
901  s5r->domain,
902  s5r->url);
903  if (GNUNET_YES == s5r->curl_paused)
904  {
905  s5r->curl_paused = GNUNET_NO;
906  curl_easy_pause (s5r->curl,
907  CURLPAUSE_CONT);
908  }
910  }
911  if (GNUNET_NO == s5r->suspended)
912  {
913  MHD_suspend_connection (s5r->con);
914  s5r->suspended = GNUNET_YES;
915  }
916  return 0; /* more data later */
917  }
918  if ( (0 == bytes_to_copy) &&
920  {
922  "Completed MHD download %s%s\n",
923  s5r->domain,
924  s5r->url);
925  return MHD_CONTENT_READER_END_OF_STREAM;
926  }
928  "Writing %llu/%llu bytes to %s%s\n",
929  (unsigned long long) bytes_to_copy,
930  (unsigned long long) s5r->io_len,
931  s5r->domain,
932  s5r->url);
933  GNUNET_memcpy (buf,
934  s5r->io_buf,
935  bytes_to_copy);
936  memmove (s5r->io_buf,
937  &s5r->io_buf[bytes_to_copy],
938  s5r->io_len - bytes_to_copy);
939  s5r->io_len -= bytes_to_copy;
940  if ( (NULL != s5r->curl) &&
941  (GNUNET_YES == s5r->curl_paused) )
942  {
944  "Continuing CURL interaction for %s%s\n",
945  s5r->domain,
946  s5r->url);
947  s5r->curl_paused = GNUNET_NO;
948  curl_easy_pause (s5r->curl,
949  CURLPAUSE_CONT);
950  }
951  return bytes_to_copy;
952 }
953 
954 
963 static int
965 {
966  unsigned int cert_list_size;
967  const gnutls_datum_t *chainp;
968  const struct curl_tlssessioninfo *tlsinfo;
969  char certdn[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 3];
970  size_t size;
971  gnutls_x509_crt_t x509_cert;
972  int rc;
973  const char *name;
974 
975  s5r->ssl_checked = GNUNET_YES;
977  "Checking X.509 certificate\n");
978  if (CURLE_OK !=
979  curl_easy_getinfo (s5r->curl,
980  CURLINFO_TLS_SESSION,
981  &tlsinfo))
982  return GNUNET_SYSERR;
983  if (CURLSSLBACKEND_GNUTLS != tlsinfo->backend)
984  {
986  _("Unsupported CURL TLS backend %d\n"),
987  tlsinfo->backend);
988  return GNUNET_SYSERR;
989  }
990  chainp = gnutls_certificate_get_peers (tlsinfo->internals,
991  &cert_list_size);
992  if ( (! chainp) ||
993  (0 == cert_list_size) )
994  return GNUNET_SYSERR;
995 
996  size = sizeof (certdn);
997  /* initialize an X.509 certificate structure. */
998  gnutls_x509_crt_init (&x509_cert);
999  gnutls_x509_crt_import (x509_cert,
1000  chainp,
1001  GNUTLS_X509_FMT_DER);
1002 
1003  if (0 != (rc = gnutls_x509_crt_get_dn_by_oid (x509_cert,
1004  GNUTLS_OID_X520_COMMON_NAME,
1005  0, /* the first and only one */
1006  0 /* no DER encoding */,
1007  certdn,
1008  &size)))
1009  {
1011  _("Failed to fetch CN from cert: %s\n"),
1012  gnutls_strerror(rc));
1013  gnutls_x509_crt_deinit (x509_cert);
1014  return GNUNET_SYSERR;
1015  }
1016  /* check for TLSA/DANE records */
1017 #if HAVE_GNUTLS_DANE
1018  if (0 != s5r->num_danes)
1019  {
1020  dane_state_t dane_state;
1021  dane_query_t dane_query;
1022  unsigned int verify;
1023 
1024  /* FIXME: add flags to gnutls to NOT read UNBOUND_ROOT_KEY_FILE here! */
1025  if (0 != (rc = dane_state_init (&dane_state,
1026 #ifdef DANE_F_IGNORE_DNSSEC
1027  DANE_F_IGNORE_DNSSEC |
1028 #endif
1029  DANE_F_IGNORE_LOCAL_RESOLVER)))
1030  {
1032  _("Failed to initialize DANE: %s\n"),
1033  dane_strerror(rc));
1034  gnutls_x509_crt_deinit (x509_cert);
1035  return GNUNET_SYSERR;
1036  }
1037  s5r->dane_data[s5r->num_danes] = NULL;
1038  s5r->dane_data_len[s5r->num_danes] = 0;
1039  if (0 != (rc = dane_raw_tlsa (dane_state,
1040  &dane_query,
1041  s5r->dane_data,
1042  s5r->dane_data_len,
1043  GNUNET_YES,
1044  GNUNET_NO)))
1045  {
1047  _("Failed to parse DANE record: %s\n"),
1048  dane_strerror(rc));
1049  dane_state_deinit (dane_state);
1050  gnutls_x509_crt_deinit (x509_cert);
1051  return GNUNET_SYSERR;
1052  }
1053  if (0 != (rc = dane_verify_crt_raw (dane_state,
1054  chainp,
1055  cert_list_size,
1056  gnutls_certificate_type_get (tlsinfo->internals),
1057  dane_query,
1058  0, 0,
1059  &verify)))
1060  {
1062  _("Failed to verify TLS connection using DANE: %s\n"),
1063  dane_strerror(rc));
1064  dane_query_deinit (dane_query);
1065  dane_state_deinit (dane_state);
1066  gnutls_x509_crt_deinit (x509_cert);
1067  return GNUNET_SYSERR;
1068  }
1069  if (0 != verify)
1070  {
1072  _("Failed DANE verification failed with GnuTLS verify status code: %u\n"),
1073  verify);
1074  dane_query_deinit (dane_query);
1075  dane_state_deinit (dane_state);
1076  gnutls_x509_crt_deinit (x509_cert);
1077  return GNUNET_SYSERR;
1078  }
1079  dane_query_deinit (dane_query);
1080  dane_state_deinit (dane_state);
1081  /* success! */
1082  }
1083  else
1084 #endif
1085  {
1086  /* try LEHO or ordinary domain name X509 verification */
1087  name = s5r->domain;
1088  if (NULL != s5r->leho)
1089  name = s5r->leho;
1090  if (NULL != name)
1091  {
1092  if (0 == (rc = gnutls_x509_crt_check_hostname (x509_cert,
1093  name)))
1094  {
1096  _("TLS certificate subject name (%s) does not match `%s': %d\n"),
1097  certdn,
1098  name,
1099  rc);
1100  gnutls_x509_crt_deinit (x509_cert);
1101  return GNUNET_SYSERR;
1102  }
1103  }
1104  else
1105  {
1106  /* we did not even have the domain name!? */
1107  GNUNET_break (0);
1108  return GNUNET_SYSERR;
1109  }
1110  }
1111  gnutls_x509_crt_deinit (x509_cert);
1112  return GNUNET_OK;
1113 }
1114 
1115 
1128 static size_t
1129 curl_check_hdr (void *buffer,
1130  size_t size,
1131  size_t nmemb,
1132  void *cls)
1133 {
1134  struct Socks5Request *s5r = cls;
1135  struct HttpResponseHeader *header;
1136  size_t bytes = size * nmemb;
1137  char *ndup;
1138  const char *hdr_type;
1139  const char *cookie_domain;
1140  char *hdr_val;
1141  char *new_cookie_hdr;
1142  char *new_location;
1143  size_t offset;
1144  size_t delta_cdomain;
1145  int domain_matched;
1146  char *tok;
1147 
1149  "Receiving HTTP response header from CURL\n");
1150  /* first, check TLS certificate */
1151  if ( (GNUNET_YES != s5r->ssl_checked) &&
1152  (GNUNET_YES == s5r->is_tls))
1153  //(HTTPS_PORT == s5r->port))
1154  {
1155  if (GNUNET_OK != check_ssl_certificate (s5r))
1156  return 0;
1157  }
1158  ndup = GNUNET_strndup (buffer,
1159  bytes);
1160  hdr_type = strtok (ndup,
1161  ":");
1162  if (NULL == hdr_type)
1163  {
1164  GNUNET_free (ndup);
1165  return bytes;
1166  }
1167  hdr_val = strtok (NULL,
1168  "");
1169  if (NULL == hdr_val)
1170  {
1171  GNUNET_free (ndup);
1172  return bytes;
1173  }
1174  if (' ' == *hdr_val)
1175  hdr_val++;
1176 
1177  /* custom logic for certain header types */
1178  new_cookie_hdr = NULL;
1179  if ( (NULL != s5r->leho) &&
1180  (0 == strcasecmp (hdr_type,
1181  MHD_HTTP_HEADER_SET_COOKIE)) )
1182 
1183  {
1184  new_cookie_hdr = GNUNET_malloc (strlen (hdr_val) +
1185  strlen (s5r->domain) + 1);
1186  offset = 0;
1187  domain_matched = GNUNET_NO; /* make sure we match domain at most once */
1188  for (tok = strtok (hdr_val, ";"); NULL != tok; tok = strtok (NULL, ";"))
1189  {
1190  if ( (0 == strncasecmp (tok,
1191  " domain",
1192  strlen (" domain"))) &&
1193  (GNUNET_NO == domain_matched) )
1194  {
1195  domain_matched = GNUNET_YES;
1196  cookie_domain = tok + strlen (" domain") + 1;
1197  if (strlen (cookie_domain) < strlen (s5r->leho))
1198  {
1199  delta_cdomain = strlen (s5r->leho) - strlen (cookie_domain);
1200  if (0 == strcasecmp (cookie_domain,
1201  s5r->leho + delta_cdomain))
1202  {
1203  offset += sprintf (new_cookie_hdr + offset,
1204  " domain=%s;",
1205  s5r->domain);
1206  continue;
1207  }
1208  }
1209  else if (0 == strcmp (cookie_domain,
1210  s5r->leho))
1211  {
1212  offset += sprintf (new_cookie_hdr + offset,
1213  " domain=%s;",
1214  s5r->domain);
1215  continue;
1216  }
1217  else if ( ('.' == cookie_domain[0]) &&
1218  (0 == strcmp (&cookie_domain[1],
1219  s5r->leho)) )
1220  {
1221  offset += sprintf (new_cookie_hdr + offset,
1222  " domain=.%s;",
1223  s5r->domain);
1224  continue;
1225  }
1227  _("Cookie domain `%s' supplied by server is invalid\n"),
1228  tok);
1229  }
1230  GNUNET_memcpy (new_cookie_hdr + offset,
1231  tok,
1232  strlen (tok));
1233  offset += strlen (tok);
1234  new_cookie_hdr[offset++] = ';';
1235  }
1236  hdr_val = new_cookie_hdr;
1237  }
1238 
1239  new_location = NULL;
1240  if (0 == strcasecmp (MHD_HTTP_HEADER_TRANSFER_ENCODING,
1241  hdr_type))
1242  {
1243  /* Ignore transfer encoding, set automatically by MHD if required */
1244  goto cleanup;
1245  }
1246  if ((0 == strcasecmp (MHD_HTTP_HEADER_LOCATION,
1247  hdr_type)))
1248  {
1249  char *leho_host;
1250 
1251  GNUNET_asprintf (&leho_host,
1252  (GNUNET_YES != s5r->is_tls) //(HTTPS_PORT != s5r->port)
1253  ? "http://%s"
1254  : "https://%s",
1255  s5r->leho);
1256  if (0 == strncmp (leho_host,
1257  hdr_val,
1258  strlen (leho_host)))
1259  {
1260  GNUNET_asprintf (&new_location,
1261  "%s%s%s",
1262  (GNUNET_YES != s5r->is_tls) //(HTTPS_PORT != s5r->port)
1263  ? "http://"
1264  : "https://",
1265  s5r->domain,
1266  hdr_val + strlen (leho_host));
1267  hdr_val = new_location;
1268  }
1269  GNUNET_free (leho_host);
1270  }
1271  if (0 == strcasecmp (MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
1272  hdr_type))
1273  {
1274  char *leho_host;
1275 
1276  GNUNET_asprintf (&leho_host,
1277  (GNUNET_YES != s5r->is_tls) //(HTTPS_PORT != s5r->port)
1278  ? "http://%s"
1279  : "https://%s",
1280  s5r->leho);
1281  if (0 == strncmp (leho_host,
1282  hdr_val,
1283  strlen (leho_host)))
1284  {
1285  GNUNET_asprintf (&new_location,
1286  "%s%s",
1287  (GNUNET_YES != s5r->is_tls) //(HTTPS_PORT != s5r->port)
1288  ? "http://"
1289  : "https://",
1290  s5r->domain);
1291  hdr_val = new_location;
1292  }
1293  GNUNET_free (leho_host);
1294  }
1295 
1296  /* MHD does not allow certain characters in values, remove those */
1297  if (NULL != (tok = strchr (hdr_val, '\n')))
1298  *tok = '\0';
1299  if (NULL != (tok = strchr (hdr_val, '\r')))
1300  *tok = '\0';
1301  if (NULL != (tok = strchr (hdr_val, '\t')))
1302  *tok = '\0';
1303  if (0 != strlen (hdr_val)) /* Rely in MHD to set those */
1304  {
1306  "Adding header %s: %s to MHD response\n",
1307  hdr_type,
1308  hdr_val);
1309  header = GNUNET_new (struct HttpResponseHeader);
1310  header->type = GNUNET_strdup (hdr_type);
1311  header->value = GNUNET_strdup (hdr_val);
1313  s5r->header_tail,
1314  header);
1315  }
1316  cleanup:
1317  GNUNET_free (ndup);
1318  GNUNET_free_non_null (new_cookie_hdr);
1319  GNUNET_free_non_null (new_location);
1320  return bytes;
1321 }
1322 
1323 
1332 static int
1334 {
1335  long resp_code;
1336  double content_length;
1337 
1338  if (NULL != s5r->response)
1339  {
1341  "Response already set!\n");
1342  return GNUNET_SYSERR;
1343  }
1344 
1345  GNUNET_break (CURLE_OK ==
1346  curl_easy_getinfo (s5r->curl,
1347  CURLINFO_RESPONSE_CODE,
1348  &resp_code));
1349  GNUNET_break (CURLE_OK ==
1350  curl_easy_getinfo (s5r->curl,
1351  CURLINFO_CONTENT_LENGTH_DOWNLOAD,
1352  &content_length));
1354  "Creating MHD response with code %d and size %d for %s%s\n",
1355  (int) resp_code,
1356  (int) content_length,
1357  s5r->domain,
1358  s5r->url);
1359  s5r->response_code = resp_code;
1360  s5r->response = MHD_create_response_from_callback ((-1 == content_length)
1361  ? MHD_SIZE_UNKNOWN
1362  : content_length,
1363  IO_BUFFERSIZE,
1364  &mhd_content_cb,
1365  s5r,
1366  NULL);
1367  for (struct HttpResponseHeader *header = s5r->header_head;
1368  NULL != header;
1369  header = header->next)
1370  {
1371  if (0 == strcasecmp (header->type,
1372  MHD_HTTP_HEADER_CONTENT_LENGTH))
1373  continue; /* MHD won't let us mess with those, for good reason */
1374  if ( (0 == strcasecmp (header->type,
1375  MHD_HTTP_HEADER_TRANSFER_ENCODING)) &&
1376  ( (0 == strcasecmp (header->value,
1377  "identity")) ||
1378  (0 == strcasecmp (header->value,
1379  "chunked")) ) )
1380  continue; /* MHD won't let us mess with those, for good reason */
1381  if (MHD_YES !=
1382  MHD_add_response_header (s5r->response,
1383  header->type,
1384  header->value))
1385  {
1386  GNUNET_break (0);
1388  "Failed to add header `%s:%s'\n",
1389  header->type,
1390  header->value);
1391  }
1392  }
1393  /* force connection to be closed after each request, as we
1394  do not support HTTP pipelining (yet, FIXME!) */
1395  /*GNUNET_break (MHD_YES ==
1396  MHD_add_response_header (s5r->response,
1397  MHD_HTTP_HEADER_CONNECTION,
1398  "close"));*/
1399  MHD_resume_connection (s5r->con);
1400  s5r->suspended = GNUNET_NO;
1401  return GNUNET_OK;
1402 }
1403 
1404 
1415 static size_t
1416 curl_download_cb (void *ptr,
1417  size_t size,
1418  size_t nmemb,
1419  void* ctx)
1420 {
1421  struct Socks5Request *s5r = ctx;
1422  size_t total = size * nmemb;
1423 
1425  "Receiving %ux%u bytes for `%s%s' from cURL to download\n",
1426  (unsigned int) size,
1427  (unsigned int) nmemb,
1428  s5r->domain,
1429  s5r->url);
1430  if (NULL == s5r->response)
1433  if ( (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) &&
1434  (0 == s5r->io_len))
1435  {
1437  "Previous upload finished... starting DOWNLOAD.\n");
1439  }
1440  if ( (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) ||
1441  (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
1442  {
1443  /* we're still not done with the upload, do not yet
1444  start the download, the IO buffer is still full
1445  with upload data. */
1447  "Pausing CURL download `%s%s', waiting for UPLOAD to finish\n",
1448  s5r->domain,
1449  s5r->url);
1450  s5r->curl_paused = GNUNET_YES;
1451  return CURL_WRITEFUNC_PAUSE; /* not yet ready for data download */
1452  }
1453  if (sizeof (s5r->io_buf) - s5r->io_len < total)
1454  {
1456  "Pausing CURL `%s%s' download, not enough space %llu %llu %llu\n",
1457  s5r->domain,
1458  s5r->url,
1459  (unsigned long long) sizeof (s5r->io_buf),
1460  (unsigned long long) s5r->io_len,
1461  (unsigned long long) total);
1462  s5r->curl_paused = GNUNET_YES;
1463  return CURL_WRITEFUNC_PAUSE; /* not enough space */
1464  }
1465  GNUNET_memcpy (&s5r->io_buf[s5r->io_len],
1466  ptr,
1467  total);
1468  s5r->io_len += total;
1469  if (GNUNET_YES == s5r->suspended)
1470  {
1471  MHD_resume_connection (s5r->con);
1472  s5r->suspended = GNUNET_NO;
1473  }
1475  "Received %llu bytes of payload via cURL from %s\n",
1476  (unsigned long long) total,
1477  s5r->domain);
1478  if (s5r->io_len == total)
1479  run_mhd_now (s5r->hd);
1480  return total;
1481 }
1482 
1483 
1494 static size_t
1496  size_t size,
1497  size_t nmemb,
1498  void *cls)
1499 {
1500  struct Socks5Request *s5r = cls;
1501  size_t len = size * nmemb;
1502  size_t to_copy;
1503 
1505  "Receiving %ux%u bytes for `%s%s' from cURL to upload\n",
1506  (unsigned int) size,
1507  (unsigned int) nmemb,
1508  s5r->domain,
1509  s5r->url);
1510 
1511  if ( (0 == s5r->io_len) &&
1512  (SOCKS5_SOCKET_UPLOAD_DONE != s5r->state) )
1513  {
1515  "Pausing CURL UPLOAD %s%s, need more data\n",
1516  s5r->domain,
1517  s5r->url);
1518  return CURL_READFUNC_PAUSE;
1519  }
1520  if ( (0 == s5r->io_len) &&
1521  (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
1522  {
1524  if (GNUNET_YES == s5r->curl_paused)
1525  {
1526  s5r->curl_paused = GNUNET_NO;
1527  curl_easy_pause (s5r->curl,
1528  CURLPAUSE_CONT);
1529  }
1531  "Completed CURL UPLOAD %s%s\n",
1532  s5r->domain,
1533  s5r->url);
1534  return 0; /* upload finished, can now download */
1535  }
1536  if ( (SOCKS5_SOCKET_UPLOAD_STARTED != s5r->state) &&
1537  (SOCKS5_SOCKET_UPLOAD_DONE != s5r->state) )
1538  {
1539  GNUNET_break (0);
1540  return CURL_READFUNC_ABORT;
1541  }
1542  to_copy = GNUNET_MIN (s5r->io_len,
1543  len);
1544  GNUNET_memcpy (buf,
1545  s5r->io_buf,
1546  to_copy);
1547  memmove (s5r->io_buf,
1548  &s5r->io_buf[to_copy],
1549  s5r->io_len - to_copy);
1550  s5r->io_len -= to_copy;
1551  if (s5r->io_len + to_copy == sizeof (s5r->io_buf))
1552  run_mhd_now (s5r->hd); /* got more space for upload now */
1553  return to_copy;
1554 }
1555 
1556 
1557 /* ************************** main loop of cURL interaction ****************** */
1558 
1559 
1566 static void
1567 curl_task_download (void *cls);
1568 
1569 
1573 static void
1575 {
1576  CURLMcode mret;
1577  fd_set rs;
1578  fd_set ws;
1579  fd_set es;
1580  int max;
1581  struct GNUNET_NETWORK_FDSet *grs;
1582  struct GNUNET_NETWORK_FDSet *gws;
1583  long to;
1584  struct GNUNET_TIME_Relative rtime;
1585 
1587  "Scheduling CURL interaction\n");
1588  if (NULL != curl_download_task)
1589  {
1590  GNUNET_SCHEDULER_cancel (curl_download_task);
1591  curl_download_task = NULL;
1592  }
1593  max = -1;
1594  FD_ZERO (&rs);
1595  FD_ZERO (&ws);
1596  FD_ZERO (&es);
1597  if (CURLM_OK != (mret = curl_multi_fdset (curl_multi,
1598  &rs,
1599  &ws,
1600  &es,
1601  &max)))
1602  {
1604  "%s failed at %s:%d: `%s'\n",
1605  "curl_multi_fdset", __FILE__, __LINE__,
1606  curl_multi_strerror (mret));
1607  return;
1608  }
1609  to = -1;
1610  GNUNET_break (CURLM_OK ==
1611  curl_multi_timeout (curl_multi,
1612  &to));
1613  if (-1 == to)
1615  else
1617  to);
1618  if (-1 != max)
1619  {
1620  grs = GNUNET_NETWORK_fdset_create ();
1621  gws = GNUNET_NETWORK_fdset_create ();
1623  &rs,
1624  max + 1);
1626  &ws,
1627  max + 1);
1629  rtime,
1630  grs,
1631  gws,
1633  curl_multi);
1636  }
1637  else
1638  {
1639  curl_download_task = GNUNET_SCHEDULER_add_delayed (rtime,
1641  curl_multi);
1642  }
1643 }
1644 
1645 
1651 static void
1653 {
1654  int running;
1655  int msgnum;
1656  struct CURLMsg *msg;
1657  CURLMcode mret;
1658  struct Socks5Request *s5r;
1659 
1660  curl_download_task = NULL;
1662  "Running CURL interaction\n");
1663  do
1664  {
1665  running = 0;
1666  mret = curl_multi_perform (curl_multi,
1667  &running);
1669  "Checking CURL multi status: %d\n",
1670  mret);
1671  while (NULL != (msg = curl_multi_info_read (curl_multi,
1672  &msgnum)))
1673  {
1674  GNUNET_break (CURLE_OK ==
1675  curl_easy_getinfo (msg->easy_handle,
1676  CURLINFO_PRIVATE,
1677  (char **) &s5r ));
1678  if (NULL == s5r)
1679  {
1680  GNUNET_break (0);
1681  continue;
1682  }
1683  switch (msg->msg)
1684  {
1685  case CURLMSG_NONE:
1686  /* documentation says this is not used */
1687  GNUNET_break (0);
1688  break;
1689  case CURLMSG_DONE:
1690  switch (msg->data.result)
1691  {
1692  case CURLE_OK:
1693  case CURLE_GOT_NOTHING:
1695  "CURL download %s%s completed.\n",
1696  s5r->domain,
1697  s5r->url);
1698  if (NULL == s5r->response)
1699  {
1702  }
1703  s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE;
1704  if (GNUNET_YES == s5r->suspended)
1705  {
1706  MHD_resume_connection (s5r->con);
1707  s5r->suspended = GNUNET_NO;
1708  }
1709  run_mhd_now (s5r->hd);
1710  break;
1711  default:
1713  "Download curl %s%s failed: %s\n",
1714  s5r->domain,
1715  s5r->url,
1716  curl_easy_strerror (msg->data.result));
1717  /* FIXME: indicate error somehow? close MHD connection badly as well? */
1718  s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE;
1719  if (GNUNET_YES == s5r->suspended)
1720  {
1721  MHD_resume_connection (s5r->con);
1722  s5r->suspended = GNUNET_NO;
1723  }
1724  run_mhd_now (s5r->hd);
1725  break;
1726  }
1727  if (NULL == s5r->response)
1728  s5r->response = curl_failure_response;
1729  break;
1730  case CURLMSG_LAST:
1731  /* documentation says this is not used */
1732  GNUNET_break (0);
1733  break;
1734  default:
1735  /* unexpected status code */
1736  GNUNET_break (0);
1737  break;
1738  }
1739  };
1740  } while (mret == CURLM_CALL_MULTI_PERFORM);
1741  if (CURLM_OK != mret)
1743  "%s failed at %s:%d: `%s'\n",
1744  "curl_multi_perform", __FILE__, __LINE__,
1745  curl_multi_strerror (mret));
1746  if (0 == running)
1747  {
1749  "Suspending cURL multi loop, no more events pending\n");
1750  if (NULL != curl_download_task)
1751  {
1752  GNUNET_SCHEDULER_cancel (curl_download_task);
1753  curl_download_task = NULL;
1754  }
1755  return; /* nothing more in progress */
1756  }
1758 }
1759 
1760 
1761 /* ********************************* MHD response generation ******************* */
1762 
1763 
1777 static int
1778 con_val_iter (void *cls,
1779  enum MHD_ValueKind kind,
1780  const char *key,
1781  const char *value)
1782 {
1783  struct Socks5Request *s5r = cls;
1784  char *hdr;
1785 
1786  if ( (0 == strcasecmp (MHD_HTTP_HEADER_HOST,
1787  key)) &&
1788  (NULL != s5r->leho) )
1789  value = s5r->leho;
1790  GNUNET_asprintf (&hdr,
1791  "%s: %s",
1792  key,
1793  value);
1795  "Adding HEADER `%s' to HTTP request\n",
1796  hdr);
1797  s5r->headers = curl_slist_append (s5r->headers,
1798  hdr);
1799  GNUNET_free (hdr);
1800  return MHD_YES;
1801 }
1802 
1803 
1827 static int
1828 create_response (void *cls,
1829  struct MHD_Connection *con,
1830  const char *url,
1831  const char *meth,
1832  const char *ver,
1833  const char *upload_data,
1834  size_t *upload_data_size,
1835  void **con_cls)
1836 {
1837  struct Socks5Request *s5r = *con_cls;
1838  char *curlurl;
1839  char ipstring[INET6_ADDRSTRLEN];
1840  char ipaddr[INET6_ADDRSTRLEN + 2];
1841  const struct sockaddr *sa;
1842  const struct sockaddr_in *s4;
1843  const struct sockaddr_in6 *s6;
1844  uint16_t port;
1845  size_t left;
1846 
1847  if (NULL == s5r)
1848  {
1849  GNUNET_break (0);
1850  return MHD_NO;
1851  }
1852  s5r->con = con;
1853  /* Fresh connection. */
1854  if (SOCKS5_SOCKET_WITH_MHD == s5r->state)
1855  {
1856  /* first time here, initialize curl handle */
1857  if (s5r->is_gns)
1858  {
1859  sa = (const struct sockaddr *) &s5r->destination_address;
1860  switch (sa->sa_family)
1861  {
1862  case AF_INET:
1863  s4 = (const struct sockaddr_in *) &s5r->destination_address;
1864  if (NULL == inet_ntop (AF_INET,
1865  &s4->sin_addr,
1866  ipstring,
1867  sizeof (ipstring)))
1868  {
1869  GNUNET_break (0);
1870  return MHD_NO;
1871  }
1872  GNUNET_snprintf (ipaddr,
1873  sizeof (ipaddr),
1874  "%s",
1875  ipstring);
1876  port = ntohs (s4->sin_port);
1877  break;
1878  case AF_INET6:
1879  s6 = (const struct sockaddr_in6 *) &s5r->destination_address;
1880  if (NULL == inet_ntop (AF_INET6,
1881  &s6->sin6_addr,
1882  ipstring,
1883  sizeof (ipstring)))
1884  {
1885  GNUNET_break (0);
1886  return MHD_NO;
1887  }
1888  GNUNET_snprintf (ipaddr,
1889  sizeof (ipaddr),
1890  "%s",
1891  ipstring);
1892  port = ntohs (s6->sin6_port);
1893  break;
1894  default:
1895  GNUNET_break (0);
1896  return MHD_NO;
1897  }
1898  }
1899  else
1900  {
1901  port = s5r->port;
1902  }
1903  if (NULL == s5r->curl)
1904  s5r->curl = curl_easy_init ();
1905  if (NULL == s5r->curl)
1906  return MHD_queue_response (con,
1907  MHD_HTTP_INTERNAL_SERVER_ERROR,
1909  curl_easy_setopt (s5r->curl,
1910  CURLOPT_HEADERFUNCTION,
1911  &curl_check_hdr);
1912  curl_easy_setopt (s5r->curl,
1913  CURLOPT_HEADERDATA,
1914  s5r);
1915  curl_easy_setopt (s5r->curl,
1916  CURLOPT_FOLLOWLOCATION,
1917  0);
1918  if (s5r->is_gns)
1919  curl_easy_setopt (s5r->curl,
1920  CURLOPT_IPRESOLVE,
1921  CURL_IPRESOLVE_V4);
1922  curl_easy_setopt (s5r->curl,
1923  CURLOPT_CONNECTTIMEOUT,
1924  600L);
1925  curl_easy_setopt (s5r->curl,
1926  CURLOPT_TIMEOUT,
1927  600L);
1928  curl_easy_setopt (s5r->curl,
1929  CURLOPT_NOSIGNAL,
1930  1L);
1931  curl_easy_setopt (s5r->curl,
1932  CURLOPT_HTTP_CONTENT_DECODING,
1933  0);
1934  curl_easy_setopt (s5r->curl,
1935  CURLOPT_NOSIGNAL,
1936  1L);
1937  curl_easy_setopt (s5r->curl,
1938  CURLOPT_PRIVATE,
1939  s5r);
1940  curl_easy_setopt (s5r->curl,
1941  CURLOPT_VERBOSE,
1942  0L);
1948  if (NULL != s5r->leho)
1949  {
1950  char *curl_hosts;
1951 
1952  GNUNET_asprintf (&curl_hosts,
1953  "%s:%d:%s",
1954  s5r->leho,
1955  port,
1956  ipaddr);
1957  s5r->hosts = curl_slist_append (NULL,
1958  curl_hosts);
1959  curl_easy_setopt (s5r->curl,
1960  CURLOPT_RESOLVE,
1961  s5r->hosts);
1962  GNUNET_free (curl_hosts);
1963  }
1964  if (s5r->is_gns)
1965  {
1966  GNUNET_asprintf (&curlurl,
1967  (GNUNET_YES != s5r->is_tls) //(HTTPS_PORT != s5r->port)
1968  ? "http://%s:%d%s"
1969  : "https://%s:%d%s",
1970  (NULL != s5r->leho)
1971  ? s5r->leho
1972  : ipaddr,
1973  port,
1974  s5r->url);
1975  }
1976  else
1977  {
1978  GNUNET_asprintf (&curlurl,
1979  (GNUNET_YES != s5r->is_tls) //(HTTPS_PORT != s5r->port)
1980  ? "http://%s:%d%s"
1981  : "https://%s:%d%s",
1982  s5r->domain,
1983  port,
1984  s5r->url);
1985  }
1986  curl_easy_setopt (s5r->curl,
1987  CURLOPT_URL,
1988  curlurl);
1990  "Launching %s CURL interaction, fetching `%s'\n",
1991  (s5r->is_gns) ? "GNS" : "DNS",
1992  curlurl);
1993  GNUNET_free (curlurl);
1994  if (0 == strcasecmp (meth,
1995  MHD_HTTP_METHOD_PUT))
1996  {
1998  curl_easy_setopt (s5r->curl,
1999  CURLOPT_UPLOAD,
2000  1L);
2001  curl_easy_setopt (s5r->curl,
2002  CURLOPT_WRITEFUNCTION,
2003  &curl_download_cb);
2004  curl_easy_setopt (s5r->curl,
2005  CURLOPT_WRITEDATA,
2006  s5r);
2007  GNUNET_assert (CURLE_OK ==
2008  curl_easy_setopt (s5r->curl,
2009  CURLOPT_READFUNCTION,
2010  &curl_upload_cb));
2011  curl_easy_setopt (s5r->curl,
2012  CURLOPT_READDATA,
2013  s5r);
2014  {
2015  const char *us;
2016  long upload_size = 0;
2017 
2018  us = MHD_lookup_connection_value (con,
2019  MHD_HEADER_KIND,
2020  MHD_HTTP_HEADER_CONTENT_LENGTH);
2021  if ( (1 == sscanf (us,
2022  "%ld",
2023  &upload_size)) &&
2024  (upload_size >= 0) )
2025  {
2026  curl_easy_setopt (s5r->curl,
2027  CURLOPT_INFILESIZE,
2028  upload_size);
2029  }
2030  }
2031  }
2032  else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2033  {
2035  curl_easy_setopt (s5r->curl,
2036  CURLOPT_POST,
2037  1L);
2038  curl_easy_setopt (s5r->curl,
2039  CURLOPT_WRITEFUNCTION,
2040  &curl_download_cb);
2041  curl_easy_setopt (s5r->curl,
2042  CURLOPT_WRITEDATA,
2043  s5r);
2044  curl_easy_setopt (s5r->curl,
2045  CURLOPT_READFUNCTION,
2046  &curl_upload_cb);
2047  curl_easy_setopt (s5r->curl,
2048  CURLOPT_READDATA,
2049  s5r);
2050  {
2051  const char *us;
2052  long upload_size;
2053 
2054  upload_size = 0;
2055  us = MHD_lookup_connection_value (con,
2056  MHD_HEADER_KIND,
2057  MHD_HTTP_HEADER_CONTENT_LENGTH);
2058  if ( (NULL != us) &&
2059  (1 == sscanf (us,
2060  "%ld",
2061  &upload_size)) &&
2062  (upload_size >= 0) )
2063  {
2064  curl_easy_setopt (s5r->curl,
2065  CURLOPT_INFILESIZE,
2066  upload_size);
2067  } else {
2068  curl_easy_setopt (s5r->curl,
2069  CURLOPT_INFILESIZE,
2070  upload_size);
2071  }
2072  }
2073  }
2074  else if (0 == strcasecmp (meth,
2075  MHD_HTTP_METHOD_HEAD))
2076  {
2078  curl_easy_setopt (s5r->curl,
2079  CURLOPT_NOBODY,
2080  1L);
2081  }
2082  else if (0 == strcasecmp (meth,
2083  MHD_HTTP_METHOD_OPTIONS))
2084  {
2086  curl_easy_setopt (s5r->curl,
2087  CURLOPT_CUSTOMREQUEST,
2088  "OPTIONS");
2089  curl_easy_setopt (s5r->curl,
2090  CURLOPT_WRITEFUNCTION,
2091  &curl_download_cb);
2092  curl_easy_setopt (s5r->curl,
2093  CURLOPT_WRITEDATA,
2094  s5r);
2095 
2096  }
2097  else if (0 == strcasecmp (meth,
2098  MHD_HTTP_METHOD_GET))
2099  {
2101  curl_easy_setopt (s5r->curl,
2102  CURLOPT_HTTPGET,
2103  1L);
2104  curl_easy_setopt (s5r->curl,
2105  CURLOPT_WRITEFUNCTION,
2106  &curl_download_cb);
2107  curl_easy_setopt (s5r->curl,
2108  CURLOPT_WRITEDATA,
2109  s5r);
2110  }
2111  else if (0 == strcasecmp (meth,
2112  MHD_HTTP_METHOD_DELETE))
2113  {
2115  curl_easy_setopt (s5r->curl,
2116  CURLOPT_CUSTOMREQUEST,
2117  "DELETE");
2118  curl_easy_setopt (s5r->curl,
2119  CURLOPT_WRITEFUNCTION,
2120  &curl_download_cb);
2121  curl_easy_setopt (s5r->curl,
2122  CURLOPT_WRITEDATA,
2123  s5r);
2124  }
2125  else
2126  {
2128  _("Unsupported HTTP method `%s'\n"),
2129  meth);
2130  curl_easy_cleanup (s5r->curl);
2131  s5r->curl = NULL;
2132  return MHD_NO;
2133  }
2134 
2135  if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_0))
2136  {
2137  curl_easy_setopt (s5r->curl,
2138  CURLOPT_HTTP_VERSION,
2139  CURL_HTTP_VERSION_1_0);
2140  }
2141  else if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_1))
2142  {
2143  curl_easy_setopt (s5r->curl,
2144  CURLOPT_HTTP_VERSION,
2145  CURL_HTTP_VERSION_1_1);
2146  }
2147  else
2148  {
2149  curl_easy_setopt (s5r->curl,
2150  CURLOPT_HTTP_VERSION,
2151  CURL_HTTP_VERSION_NONE);
2152  }
2153 
2154  if (GNUNET_YES == s5r->is_tls) //(HTTPS_PORT == s5r->port)
2155  {
2156  curl_easy_setopt (s5r->curl,
2157  CURLOPT_USE_SSL,
2158  CURLUSESSL_ALL);
2159  if (0 < s5r->num_danes)
2160  curl_easy_setopt (s5r->curl,
2161  CURLOPT_SSL_VERIFYPEER,
2162  0L);
2163  else
2164  curl_easy_setopt (s5r->curl,
2165  CURLOPT_SSL_VERIFYPEER,
2166  1L);
2167  /* Disable cURL checking the hostname, as we will check ourselves
2168  as only we have the domain name or the LEHO or the DANE record */
2169  curl_easy_setopt (s5r->curl,
2170  CURLOPT_SSL_VERIFYHOST,
2171  0L);
2172  }
2173  else
2174  {
2175  curl_easy_setopt (s5r->curl,
2176  CURLOPT_USE_SSL,
2177  CURLUSESSL_NONE);
2178  }
2179 
2180  if (CURLM_OK !=
2181  curl_multi_add_handle (curl_multi,
2182  s5r->curl))
2183  {
2184  GNUNET_break (0);
2185  curl_easy_cleanup (s5r->curl);
2186  s5r->curl = NULL;
2187  return MHD_NO;
2188  }
2189  MHD_get_connection_values (con,
2190  MHD_HEADER_KIND,
2191  (MHD_KeyValueIterator) &con_val_iter,
2192  s5r);
2193  curl_easy_setopt (s5r->curl,
2194  CURLOPT_HTTPHEADER,
2195  s5r->headers);
2197  return MHD_YES;
2198  }
2199 
2200  /* continuing to process request */
2201  if (0 != *upload_data_size)
2202  {
2204  "Processing %u bytes UPLOAD\n",
2205  (unsigned int) *upload_data_size);
2206 
2207  /* FIXME: This must be set or a header with Transfer-Encoding: chunked. Else
2208  * upload callback is not called!
2209  */
2210  curl_easy_setopt (s5r->curl,
2211  CURLOPT_POSTFIELDSIZE,
2212  *upload_data_size);
2213 
2214  left = GNUNET_MIN (*upload_data_size,
2215  sizeof (s5r->io_buf) - s5r->io_len);
2216  GNUNET_memcpy (&s5r->io_buf[s5r->io_len],
2217  upload_data,
2218  left);
2219  s5r->io_len += left;
2220  *upload_data_size -= left;
2221  GNUNET_assert (NULL != s5r->curl);
2222  if (GNUNET_YES == s5r->curl_paused)
2223  {
2224  s5r->curl_paused = GNUNET_NO;
2225  curl_easy_pause (s5r->curl,
2226  CURLPAUSE_CONT);
2227  }
2228  return MHD_YES;
2229  }
2230  if (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state)
2231  {
2233  "Finished processing UPLOAD\n");
2235  }
2236  if (NULL == s5r->response)
2237  {
2239  "Waiting for HTTP response for %s%s...\n",
2240  s5r->domain,
2241  s5r->url);
2242  MHD_suspend_connection (con);
2243  s5r->suspended = GNUNET_YES;
2244  return MHD_YES;
2245  }
2247  "Queueing response for %s%s with MHD\n",
2248  s5r->domain,
2249  s5r->url);
2250  run_mhd_now (s5r->hd);
2251  return MHD_queue_response (con,
2252  s5r->response_code,
2253  s5r->response);
2254 }
2255 
2256 
2257 /* ******************** MHD HTTP setup and event loop ******************** */
2258 
2259 
2269 static void
2270 mhd_completed_cb (void *cls,
2271  struct MHD_Connection *connection,
2272  void **con_cls,
2273  enum MHD_RequestTerminationCode toe)
2274 {
2275  struct Socks5Request *s5r = *con_cls;
2276 
2277  if (NULL == s5r)
2278  return;
2279  if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
2281  "MHD encountered error handling request: %d\n",
2282  toe);
2283  if (NULL != s5r->curl)
2284  {
2286  "Removing cURL handle (MHD interaction complete)\n");
2287  curl_multi_remove_handle (curl_multi,
2288  s5r->curl);
2289  curl_slist_free_all (s5r->headers);
2290  s5r->headers = NULL;
2291  curl_easy_reset (s5r->curl);
2292  s5r->rbuf_len = 0;
2293  s5r->wbuf_len = 0;
2294  s5r->io_len = 0;
2296  }
2297  if ( (NULL != s5r->response) &&
2298  (curl_failure_response != s5r->response) )
2299  MHD_destroy_response (s5r->response);
2300  for (struct HttpResponseHeader *header = s5r->header_head;
2301  NULL != header;
2302  header = s5r->header_head)
2303  {
2305  s5r->header_tail,
2306  header);
2307  GNUNET_free (header->type);
2308  GNUNET_free (header->value);
2309  GNUNET_free (header);
2310  }
2312  "Finished request for %s\n",
2313  s5r->url);
2314  GNUNET_free (s5r->url);
2316  s5r->url = NULL;
2317  s5r->response = NULL;
2318  *con_cls = NULL;
2319 }
2320 
2321 
2331 static void
2333  struct MHD_Connection *connection,
2334  void **con_cls,
2335  enum MHD_ConnectionNotificationCode cnc)
2336 {
2337  struct Socks5Request *s5r;
2338  const union MHD_ConnectionInfo *ci;
2339  int sock;
2340 
2341  switch (cnc)
2342  {
2343  case MHD_CONNECTION_NOTIFY_STARTED:
2344  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection started...\n");
2345  ci = MHD_get_connection_info (connection,
2346  MHD_CONNECTION_INFO_CONNECTION_FD);
2347  if (NULL == ci)
2348  {
2349  GNUNET_break (0);
2350  return;
2351  }
2352  sock = ci->connect_fd;
2353  for (s5r = s5r_head; NULL != s5r; s5r = s5r->next)
2354  {
2355  if (GNUNET_NETWORK_get_fd (s5r->sock) == sock)
2356  {
2358  "Context set...\n");
2359  s5r->ssl_checked = GNUNET_NO;
2360  *con_cls = s5r;
2361  break;
2362  }
2363  }
2364  break;
2365  case MHD_CONNECTION_NOTIFY_CLOSED:
2367  "Connection closed... cleaning up\n");
2368  s5r = *con_cls;
2369  if (NULL == s5r)
2370  {
2372  "Connection stale!\n");
2373  return;
2374  }
2375  cleanup_s5r (s5r);
2377  *con_cls = NULL;
2378  break;
2379  default:
2380  GNUNET_break (0);
2381  }
2382 }
2383 
2397 static void *
2398 mhd_log_callback (void *cls,
2399  const char *url,
2400  struct MHD_Connection *connection)
2401 {
2402  struct Socks5Request *s5r;
2403  const union MHD_ConnectionInfo *ci;
2404 
2405  ci = MHD_get_connection_info (connection,
2406  MHD_CONNECTION_INFO_SOCKET_CONTEXT);
2407  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Processing %s\n", url);
2408  if (NULL == ci)
2409  {
2410  GNUNET_break (0);
2411  return NULL;
2412  }
2413  s5r = ci->socket_context;
2414  if (NULL != s5r->url)
2415  {
2416  GNUNET_break (0);
2417  return NULL;
2418  }
2419  s5r->url = GNUNET_strdup (url);
2420  if (NULL != s5r->timeout_task)
2421  {
2423  s5r->timeout_task = NULL;
2424  }
2426  return s5r;
2427 }
2428 
2429 
2435 static void
2437 {
2438  GNUNET_CONTAINER_DLL_remove (mhd_httpd_head,
2439  mhd_httpd_tail,
2440  hd);
2442  MHD_stop_daemon (hd->daemon);
2443  if (NULL != hd->httpd_task)
2444  {
2446  hd->httpd_task = NULL;
2447  }
2449  if (hd == httpd)
2450  httpd = NULL;
2451  GNUNET_free (hd);
2452 }
2453 
2454 
2460 static void
2461 kill_httpd_task (void *cls)
2462 {
2463  struct MhdHttpList *hd = cls;
2464 
2465  hd->httpd_task = NULL;
2466  kill_httpd (hd);
2467 }
2468 
2469 
2475 static void
2476 do_httpd (void *cls);
2477 
2478 
2486 static void
2488 {
2489  fd_set rs;
2490  fd_set ws;
2491  fd_set es;
2492  struct GNUNET_NETWORK_FDSet *wrs;
2493  struct GNUNET_NETWORK_FDSet *wws;
2494  int max;
2495  int haveto;
2496  MHD_UNSIGNED_LONG_LONG timeout;
2497  struct GNUNET_TIME_Relative tv;
2498 
2499  FD_ZERO (&rs);
2500  FD_ZERO (&ws);
2501  FD_ZERO (&es);
2502  max = -1;
2503  if (MHD_YES !=
2504  MHD_get_fdset (hd->daemon,
2505  &rs,
2506  &ws,
2507  &es,
2508  &max))
2509  {
2510  kill_httpd (hd);
2511  return;
2512  }
2513  haveto = MHD_get_timeout (hd->daemon,
2514  &timeout);
2515  if (MHD_YES == haveto)
2516  tv.rel_value_us = (uint64_t) timeout * 1000LL;
2517  else
2519  if (-1 != max)
2520  {
2521  wrs = GNUNET_NETWORK_fdset_create ();
2522  wws = GNUNET_NETWORK_fdset_create ();
2523  GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
2524  GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
2525  }
2526  else
2527  {
2528  wrs = NULL;
2529  wws = NULL;
2530  }
2531  if (NULL != hd->httpd_task)
2532  {
2534  hd->httpd_task = NULL;
2535  }
2536  if ( (MHD_YES != haveto) &&
2537  (-1 == max) &&
2538  (hd != httpd) )
2539  {
2540  /* daemon is idle, kill after timeout */
2542  &kill_httpd_task,
2543  hd);
2544  }
2545  else
2546  {
2547  hd->httpd_task =
2549  tv, wrs, wws,
2550  &do_httpd, hd);
2551  }
2552  if (NULL != wrs)
2554  if (NULL != wws)
2556 }
2557 
2558 
2564 static void
2565 do_httpd (void *cls)
2566 {
2567  struct MhdHttpList *hd = cls;
2568 
2569  hd->httpd_task = NULL;
2570  MHD_run (hd->daemon);
2571  schedule_httpd (hd);
2572 }
2573 
2574 
2580 static void
2582 {
2583  if (NULL != hd->httpd_task)
2586  hd);
2587 }
2588 
2589 
2597 static void*
2598 load_file (const char* filename,
2599  unsigned int* size)
2600 {
2601  void *buffer;
2602  uint64_t fsize;
2603 
2604  if (GNUNET_OK !=
2605  GNUNET_DISK_file_size (filename,
2606  &fsize,
2607  GNUNET_YES,
2608  GNUNET_YES))
2609  return NULL;
2610  if (fsize > MAX_PEM_SIZE)
2611  return NULL;
2612  *size = (unsigned int) fsize;
2613  buffer = GNUNET_malloc (*size);
2614  if (fsize !=
2615  GNUNET_DISK_fn_read (filename,
2616  buffer,
2617  (size_t) fsize))
2618  {
2619  GNUNET_free (buffer);
2620  return NULL;
2621  }
2622  return buffer;
2623 }
2624 
2625 
2633 static int
2634 load_key_from_file (gnutls_x509_privkey_t key,
2635  const char* keyfile)
2636 {
2637  gnutls_datum_t key_data;
2638  int ret;
2639 
2640  key_data.data = load_file (keyfile,
2641  &key_data.size);
2642  if (NULL == key_data.data)
2643  return GNUNET_SYSERR;
2644  ret = gnutls_x509_privkey_import (key, &key_data,
2645  GNUTLS_X509_FMT_PEM);
2646  if (GNUTLS_E_SUCCESS != ret)
2647  {
2649  _("Unable to import private key from file `%s'\n"),
2650  keyfile);
2651  }
2652  GNUNET_free_non_null (key_data.data);
2653  return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2654 }
2655 
2656 
2664 static int
2665 load_cert_from_file (gnutls_x509_crt_t crt,
2666  const char* certfile)
2667 {
2668  gnutls_datum_t cert_data;
2669  int ret;
2670 
2671  cert_data.data = load_file (certfile,
2672  &cert_data.size);
2673  if (NULL == cert_data.data)
2674  return GNUNET_SYSERR;
2675  ret = gnutls_x509_crt_import (crt,
2676  &cert_data,
2677  GNUTLS_X509_FMT_PEM);
2678  if (GNUTLS_E_SUCCESS != ret)
2679  {
2681  _("Unable to import certificate from `%s'\n"),
2682  certfile);
2683  }
2684  GNUNET_free_non_null (cert_data.data);
2685  return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2686 }
2687 
2688 
2695 static struct ProxyGNSCertificate *
2697 {
2698  unsigned int serial;
2699  size_t key_buf_size;
2700  size_t cert_buf_size;
2701  gnutls_x509_crt_t request;
2702  time_t etime;
2703  struct tm *tm_data;
2704  struct ProxyGNSCertificate *pgc;
2705 
2707  "Generating x.509 certificate for `%s'\n",
2708  name);
2709  GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_init (&request));
2710  GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
2711  pgc = GNUNET_new (struct ProxyGNSCertificate);
2712  gnutls_x509_crt_set_dn_by_oid (request,
2713  GNUTLS_OID_X520_COUNTRY_NAME,
2714  0,
2715  "ZZ",
2716  strlen ("ZZ"));
2717  gnutls_x509_crt_set_dn_by_oid (request,
2718  GNUTLS_OID_X520_ORGANIZATION_NAME,
2719  0,
2720  "GNU Name System",
2721  strlen ("GNU Name System"));
2722  gnutls_x509_crt_set_dn_by_oid (request,
2723  GNUTLS_OID_X520_COMMON_NAME,
2724  0,
2725  name,
2726  strlen (name));
2727  gnutls_x509_crt_set_subject_alternative_name (request,
2728  GNUTLS_SAN_DNSNAME,
2729  name);
2730  GNUNET_break (GNUTLS_E_SUCCESS ==
2731  gnutls_x509_crt_set_version (request,
2732  3));
2733  gnutls_rnd (GNUTLS_RND_NONCE,
2734  &serial,
2735  sizeof (serial));
2736  gnutls_x509_crt_set_serial (request,
2737  &serial,
2738  sizeof (serial));
2739  etime = time (NULL);
2740  tm_data = localtime (&etime);
2741  tm_data->tm_hour--;
2742  etime = mktime(tm_data);
2743  gnutls_x509_crt_set_activation_time (request,
2744  etime);
2745  tm_data->tm_year++;
2746  etime = mktime (tm_data);
2747  gnutls_x509_crt_set_expiration_time (request,
2748  etime);
2749  gnutls_x509_crt_sign2 (request,
2750  proxy_ca.cert,
2751  proxy_ca.key,
2752  GNUTLS_DIG_SHA512,
2753  0);
2754  key_buf_size = sizeof (pgc->key);
2755  cert_buf_size = sizeof (pgc->cert);
2756  gnutls_x509_crt_export (request,
2757  GNUTLS_X509_FMT_PEM,
2758  pgc->cert,
2759  &cert_buf_size);
2760  gnutls_x509_privkey_export (proxy_ca.key,
2761  GNUTLS_X509_FMT_PEM,
2762  pgc->key,
2763  &key_buf_size);
2764  gnutls_x509_crt_deinit (request);
2765  return pgc;
2766 }
2767 
2768 
2776 static void
2778  const char *fm,
2779  va_list ap)
2780 {
2781  /* do nothing */
2782 }
2783 
2784 
2791 static struct MhdHttpList *
2793 {
2794  struct MhdHttpList *hd;
2795  struct ProxyGNSCertificate *pgc;
2796 
2797  if (NULL == domain)
2798  {
2799  GNUNET_break (0);
2800  return NULL;
2801  }
2802  for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2803  if ( (NULL != hd->domain) &&
2804  (0 == strcmp (hd->domain, domain)) )
2805  return hd;
2807  "Starting fresh MHD HTTPS instance for domain `%s'\n",
2808  domain);
2809  pgc = generate_gns_certificate (domain);
2810  hd = GNUNET_new (struct MhdHttpList);
2811  hd->is_ssl = GNUNET_YES;
2812  hd->domain = GNUNET_strdup (domain);
2813  hd->proxy_cert = pgc;
2814  hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET | MHD_ALLOW_SUSPEND_RESUME,
2815  0,
2816  NULL, NULL,
2817  &create_response, hd,
2818  MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2819  MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
2820  MHD_OPTION_NOTIFY_CONNECTION, &mhd_connection_cb, NULL,
2821  MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
2822  MHD_OPTION_EXTERNAL_LOGGER, &mhd_error_log_callback, NULL,
2823  MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2824  MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2825  MHD_OPTION_END);
2826  if (NULL == hd->daemon)
2827  {
2828  GNUNET_free (pgc);
2829  GNUNET_free (hd);
2830  return NULL;
2831  }
2832  GNUNET_CONTAINER_DLL_insert (mhd_httpd_head,
2833  mhd_httpd_tail,
2834  hd);
2835  return hd;
2836 }
2837 
2838 
2846 static void
2848 {
2849  struct Socks5Request *s5r = cls;
2850 
2851  s5r->timeout_task = NULL;
2852  cleanup_s5r (s5r);
2853 }
2854 
2855 
2864 static void
2866 {
2867  struct MhdHttpList *hd;
2868  int fd;
2869  const struct sockaddr *addr;
2870  socklen_t len;
2871  char *domain;
2872 
2873  if (GNUNET_YES == s5r->is_tls)
2874  {
2875  GNUNET_asprintf (&domain,
2876  "%s",
2877  s5r->domain);
2878  hd = lookup_ssl_httpd (domain);
2879  if (NULL == hd)
2880  {
2882  _("Failed to start HTTPS server for `%s'\n"),
2883  s5r->domain);
2884  cleanup_s5r (s5r);
2885  GNUNET_free (domain);
2886  return;
2887  }
2888  } else {
2889  domain = NULL;
2890  GNUNET_assert (NULL != httpd);
2891  hd = httpd;
2892  }
2893  fd = GNUNET_NETWORK_get_fd (s5r->sock);
2894  addr = GNUNET_NETWORK_get_addr (s5r->sock);
2895  len = GNUNET_NETWORK_get_addrlen (s5r->sock);
2897  if (MHD_YES !=
2898  MHD_add_connection (hd->daemon,
2899  fd,
2900  addr,
2901  len))
2902  {
2904  _("Failed to pass client to MHD\n"));
2905  cleanup_s5r (s5r);
2906  GNUNET_free_non_null (domain);
2907  return;
2908  }
2909  s5r->hd = hd;
2910  schedule_httpd (hd);
2913  s5r);
2914  GNUNET_free_non_null (domain);
2915 }
2916 
2917 
2918 /* ********************* SOCKS handling ************************* */
2919 
2920 
2926 static void
2927 do_write (void *cls)
2928 {
2929  struct Socks5Request *s5r = cls;
2930  ssize_t len;
2931 
2932  s5r->wtask = NULL;
2933  len = GNUNET_NETWORK_socket_send (s5r->sock,
2934  s5r->wbuf,
2935  s5r->wbuf_len);
2936  if (len <= 0)
2937  {
2938  /* write error: connection closed, shutdown, etc.; just clean up */
2940  "Write Error\n");
2941  cleanup_s5r (s5r);
2942  return;
2943  }
2944  memmove (s5r->wbuf,
2945  &s5r->wbuf[len],
2946  s5r->wbuf_len - len);
2947  s5r->wbuf_len -= len;
2948  if (s5r->wbuf_len > 0)
2949  {
2950  /* not done writing */
2951  s5r->wtask =
2953  s5r->sock,
2954  &do_write, s5r);
2955  return;
2956  }
2957 
2958  /* we're done writing, continue with state machine! */
2959 
2960  switch (s5r->state)
2961  {
2962  case SOCKS5_INIT:
2963  GNUNET_assert (0);
2964  break;
2965  case SOCKS5_REQUEST:
2966  GNUNET_assert (NULL != s5r->rtask);
2967  break;
2968  case SOCKS5_DATA_TRANSFER:
2969  setup_data_transfer (s5r);
2970  return;
2972  cleanup_s5r (s5r);
2973  return;
2974  default:
2975  GNUNET_break (0);
2976  break;
2977  }
2978 }
2979 
2980 
2987 static void
2989  enum Socks5StatusCode sc)
2990 {
2991  struct Socks5ServerResponseMessage *s_resp;
2992 
2993  s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len];
2994  memset (s_resp, 0, sizeof (struct Socks5ServerResponseMessage));
2995  s_resp->version = SOCKS_VERSION_5;
2996  s_resp->reply = sc;
2998  if (NULL != s5r->wtask)
2999  s5r->wtask =
3001  s5r->sock,
3002  &do_write, s5r);
3003 }
3004 
3005 
3011 static void
3013 {
3014  struct Socks5ServerResponseMessage *s_resp;
3015 
3016  s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len];
3017  s_resp->version = SOCKS_VERSION_5;
3019  s_resp->reserved = 0;
3020  s_resp->addr_type = SOCKS5_AT_IPV4;
3021  /* zero out IPv4 address and port */
3022  memset (&s_resp[1],
3023  0,
3024  sizeof (struct in_addr) + sizeof (uint16_t));
3025  s5r->wbuf_len += sizeof (struct Socks5ServerResponseMessage) +
3026  sizeof (struct in_addr) + sizeof (uint16_t);
3027  if (NULL == s5r->wtask)
3028  s5r->wtask =
3030  s5r->sock,
3031  &do_write, s5r);
3032 }
3033 
3034 
3043 static void
3045  int tld,
3046  uint32_t rd_count,
3047  const struct GNUNET_GNSRECORD_Data *rd)
3048 {
3049  struct Socks5Request *s5r = cls;
3050  const struct GNUNET_GNSRECORD_Data *r;
3051  int got_ip;
3052 
3053  s5r->gns_lookup = NULL;
3054  s5r->is_gns = tld;
3055  got_ip = GNUNET_NO;
3056  for (uint32_t i=0;i<rd_count;i++)
3057  {
3058  r = &rd[i];
3059  switch (r->record_type)
3060  {
3062  {
3063  struct sockaddr_in *in;
3064 
3065  if (sizeof (struct in_addr) != r->data_size)
3066  {
3067  GNUNET_break_op (0);
3068  break;
3069  }
3070  if (GNUNET_YES == got_ip)
3071  break;
3072  if (GNUNET_OK !=
3073  GNUNET_NETWORK_test_pf (PF_INET))
3074  break;
3075  got_ip = GNUNET_YES;
3076  in = (struct sockaddr_in *) &s5r->destination_address;
3077  in->sin_family = AF_INET;
3078  GNUNET_memcpy (&in->sin_addr,
3079  r->data,
3080  r->data_size);
3081  in->sin_port = htons (s5r->port);
3082 #if HAVE_SOCKADDR_IN_SIN_LEN
3083  in->sin_len = sizeof (*in);
3084 #endif
3085  }
3086  break;
3088  {
3089  struct sockaddr_in6 *in;
3090 
3091  if (sizeof (struct in6_addr) != r->data_size)
3092  {
3093  GNUNET_break_op (0);
3094  break;
3095  }
3096  if (GNUNET_YES == got_ip)
3097  break;
3098  if (GNUNET_YES == disable_v6)
3099  break;
3100  if (GNUNET_OK !=
3101  GNUNET_NETWORK_test_pf (PF_INET6))
3102  break;
3103  /* FIXME: allow user to disable IPv6 per configuration option... */
3104  got_ip = GNUNET_YES;
3105  in = (struct sockaddr_in6 *) &s5r->destination_address;
3106  in->sin6_family = AF_INET6;
3107  GNUNET_memcpy (&in->sin6_addr,
3108  r->data,
3109  r->data_size);
3110  in->sin6_port = htons (s5r->port);
3111 #if HAVE_SOCKADDR_IN_SIN_LEN
3112  in->sin6_len = sizeof (*in);
3113 #endif
3114  }
3115  break;
3117  GNUNET_break (0); /* should have been translated within GNS */
3118  break;
3120  GNUNET_free_non_null (s5r->leho);
3121  s5r->leho = GNUNET_strndup (r->data,
3122  r->data_size);
3123  break;
3125  {
3126  const struct GNUNET_GNSRECORD_BoxRecord *box;
3127 
3128  if (r->data_size < sizeof (struct GNUNET_GNSRECORD_BoxRecord))
3129  {
3130  GNUNET_break_op (0);
3131  break;
3132  }
3133  box = r->data;
3134  if ( (ntohl (box->record_type) != GNUNET_DNSPARSER_TYPE_TLSA) ||
3135  (ntohs (box->protocol) != IPPROTO_TCP) ||
3136  (ntohs (box->service) != s5r->port) )
3137  break; /* BOX record does not apply */
3138  if (s5r->num_danes >= MAX_DANES)
3139  {
3140  GNUNET_break (0); /* MAX_DANES too small */
3141  break;
3142  }
3143  s5r->is_tls = GNUNET_YES; /* This should be TLS */
3144  s5r->dane_data_len[s5r->num_danes]
3145  = r->data_size - sizeof (struct GNUNET_GNSRECORD_BoxRecord);
3146  s5r->dane_data[s5r->num_danes]
3147  = GNUNET_memdup (&box[1],
3148  s5r->dane_data_len[s5r->num_danes]);
3149  s5r->num_danes++;
3150  break;
3151  }
3152  default:
3153  /* don't care */
3154  break;
3155  }
3156  }
3157  if ( (GNUNET_YES != got_ip) &&
3158  (GNUNET_YES == tld) )
3159  {
3161  "Name resolution failed to yield useful IP address.\n");
3162  signal_socks_failure (s5r,
3164  return;
3165  }
3166  s5r->state = SOCKS5_DATA_TRANSFER;
3167  signal_socks_success (s5r);
3168 }
3169 
3170 
3177 static void
3179  size_t len)
3180 {
3181  GNUNET_assert (len <= s5r->rbuf_len);
3182  memmove (s5r->rbuf,
3183  &s5r->rbuf[len],
3184  s5r->rbuf_len - len);
3185  s5r->rbuf_len -= len;
3186 }
3187 
3188 
3194 static void
3195 do_s5r_read (void *cls)
3196 {
3197  struct Socks5Request *s5r = cls;
3198  const struct Socks5ClientHelloMessage *c_hello;
3199  struct Socks5ServerHelloMessage *s_hello;
3200  const struct Socks5ClientRequestMessage *c_req;
3201  ssize_t rlen;
3202  size_t alen;
3203  const struct GNUNET_SCHEDULER_TaskContext *tc;
3204 
3205  s5r->rtask = NULL;
3207  if ( (NULL != tc->read_ready) &&
3209  s5r->sock)) )
3210  {
3211  rlen = GNUNET_NETWORK_socket_recv (s5r->sock,
3212  &s5r->rbuf[s5r->rbuf_len],
3213  sizeof (s5r->rbuf) - s5r->rbuf_len);
3214  if (rlen <= 0)
3215  {
3217  "socks5 client disconnected.\n");
3218  cleanup_s5r (s5r);
3219  return;
3220  }
3221  s5r->rbuf_len += rlen;
3222  }
3224  s5r->sock,
3225  &do_s5r_read, s5r);
3227  "Processing %zu bytes of socks data in state %d\n",
3228  s5r->rbuf_len,
3229  s5r->state);
3230  switch (s5r->state)
3231  {
3232  case SOCKS5_INIT:
3233  c_hello = (const struct Socks5ClientHelloMessage*) &s5r->rbuf;
3234  if ( (s5r->rbuf_len < sizeof (struct Socks5ClientHelloMessage)) ||
3235  (s5r->rbuf_len < sizeof (struct Socks5ClientHelloMessage) + c_hello->num_auth_methods) )
3236  return; /* need more data */
3237  if (SOCKS_VERSION_5 != c_hello->version)
3238  {
3240  _("Unsupported socks version %d\n"),
3241  (int) c_hello->version);
3242  cleanup_s5r (s5r);
3243  return;
3244  }
3245  clear_from_s5r_rbuf (s5r,
3246  sizeof (struct Socks5ClientHelloMessage) + c_hello->num_auth_methods);
3247  GNUNET_assert (0 == s5r->wbuf_len);
3248  s_hello = (struct Socks5ServerHelloMessage *) &s5r->wbuf;
3249  s5r->wbuf_len = sizeof (struct Socks5ServerHelloMessage);
3250  s_hello->version = SOCKS_VERSION_5;
3251  s_hello->auth_method = SOCKS_AUTH_NONE;
3252  GNUNET_assert (NULL == s5r->wtask);
3254  s5r->sock,
3255  &do_write, s5r);
3256  s5r->state = SOCKS5_REQUEST;
3257  return;
3258  case SOCKS5_REQUEST:
3259  c_req = (const struct Socks5ClientRequestMessage *) &s5r->rbuf;
3260  if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage))
3261  return;
3262  switch (c_req->command)
3263  {
3264  case SOCKS5_CMD_TCP_STREAM:
3265  /* handled below */
3266  break;
3267  default:
3269  _("Unsupported socks command %d\n"),
3270  (int) c_req->command);
3271  signal_socks_failure (s5r,
3273  return;
3274  }
3275  switch (c_req->addr_type)
3276  {
3277  case SOCKS5_AT_IPV4:
3278  {
3279  const struct in_addr *v4 = (const struct in_addr *) &c_req[1];
3280  const uint16_t *port = (const uint16_t *) &v4[1];
3281  struct sockaddr_in *in;
3282 
3283  s5r->port = ntohs (*port);
3284  alen = sizeof (struct in_addr);
3285  if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
3286  alen + sizeof (uint16_t))
3287  return; /* need more data */
3288  in = (struct sockaddr_in *) &s5r->destination_address;
3289  in->sin_family = AF_INET;
3290  in->sin_addr = *v4;
3291  in->sin_port = *port;
3292 #if HAVE_SOCKADDR_IN_SIN_LEN
3293  in->sin_len = sizeof (*in);
3294 #endif
3295  s5r->state = SOCKS5_DATA_TRANSFER;
3296  }
3297  break;
3298  case SOCKS5_AT_IPV6:
3299  {
3300  const struct in6_addr *v6 = (const struct in6_addr *) &c_req[1];
3301  const uint16_t *port = (const uint16_t *) &v6[1];
3302  struct sockaddr_in6 *in;
3303 
3304  s5r->port = ntohs (*port);
3305  alen = sizeof (struct in6_addr);
3306  if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
3307  alen + sizeof (uint16_t))
3308  return; /* need more data */
3309  in = (struct sockaddr_in6 *) &s5r->destination_address;
3310  in->sin6_family = AF_INET6;
3311  in->sin6_addr = *v6;
3312  in->sin6_port = *port;
3313 #if HAVE_SOCKADDR_IN_SIN_LEN
3314  in->sin6_len = sizeof (*in);
3315 #endif
3316  s5r->state = SOCKS5_DATA_TRANSFER;
3317  }
3318  break;
3319  case SOCKS5_AT_DOMAINNAME:
3320  {
3321  const uint8_t *dom_len;
3322  const char *dom_name;
3323  const uint16_t *port;
3324 
3325  dom_len = (const uint8_t *) &c_req[1];
3326  alen = *dom_len + 1;
3327  if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
3328  alen + sizeof (uint16_t))
3329  return; /* need more data */
3330  dom_name = (const char *) &dom_len[1];
3331  port = (const uint16_t*) &dom_name[*dom_len];
3332  s5r->domain = GNUNET_strndup (dom_name,
3333  *dom_len);
3335  "Requested connection is to %s:%d\n",
3336  //(HTTPS_PORT == s5r->port) ? "s" : "",
3337  s5r->domain,
3338  ntohs (*port));
3339  s5r->state = SOCKS5_RESOLVING;
3340  s5r->port = ntohs (*port);
3341  s5r->is_tls = (HTTPS_PORT == s5r->port) ? GNUNET_YES : GNUNET_NO;
3342  s5r->gns_lookup = GNUNET_GNS_lookup_with_tld (gns_handle,
3343  s5r->domain,
3345  GNUNET_NO /* only cached */,
3347  s5r);
3348  break;
3349  }
3350  default:
3352  _("Unsupported socks address type %d\n"),
3353  (int) c_req->addr_type);
3354  signal_socks_failure (s5r,
3356  return;
3357  }
3358  clear_from_s5r_rbuf (s5r,
3359  sizeof (struct Socks5ClientRequestMessage) +
3360  alen + sizeof (uint16_t));
3361  if (0 != s5r->rbuf_len)
3362  {
3363  /* read more bytes than healthy, why did the client send more!? */
3364  GNUNET_break_op (0);
3365  signal_socks_failure (s5r,
3367  return;
3368  }
3369  if (SOCKS5_DATA_TRANSFER == s5r->state)
3370  {
3371  /* if we are not waiting for GNS resolution, signal success */
3372  signal_socks_success (s5r);
3373  }
3374  /* We are done reading right now */
3376  s5r->rtask = NULL;
3377  return;
3378  case SOCKS5_RESOLVING:
3379  GNUNET_assert (0);
3380  return;
3381  case SOCKS5_DATA_TRANSFER:
3382  GNUNET_assert (0);
3383  return;
3384  default:
3385  GNUNET_assert (0);
3386  return;
3387  }
3388 }
3389 
3390 
3397 static void
3398 do_accept (void *cls)
3399 {
3400  struct GNUNET_NETWORK_Handle *lsock = cls;
3401  struct GNUNET_NETWORK_Handle *s;
3402  struct Socks5Request *s5r;
3403 
3404  GNUNET_assert (NULL != lsock);
3405  if (lsock == lsock4)
3407  lsock,
3408  &do_accept,
3409  lsock);
3410  else if (lsock == lsock6)
3412  lsock,
3413  &do_accept,
3414  lsock);
3415  else
3416  GNUNET_assert (0);
3417  s = GNUNET_NETWORK_socket_accept (lsock,
3418  NULL,
3419  NULL);
3420  if (NULL == s)
3421  {
3423  "accept");
3424  return;
3425  }
3427  "Got an inbound connection, waiting for data\n");
3428  s5r = GNUNET_new (struct Socks5Request);
3429  GNUNET_CONTAINER_DLL_insert (s5r_head,
3430  s5r_tail,
3431  s5r);
3432  s5r->sock = s;
3433  s5r->state = SOCKS5_INIT;
3435  s5r->sock,
3436  &do_s5r_read,
3437  s5r);
3438 }
3439 
3440 
3441 /* ******************* General / main code ********************* */
3442 
3443 
3449 static void
3450 do_shutdown (void *cls)
3451 {
3453  "Shutting down...\n");
3454  /* MHD requires resuming before destroying the daemons */
3455  for (struct Socks5Request *s5r = s5r_head;
3456  NULL != s5r;
3457  s5r = s5r->next)
3458  {
3459  if (s5r->suspended)
3460  {
3461  s5r->suspended = GNUNET_NO;
3462  MHD_resume_connection (s5r->con);
3463  }
3464  }
3465  while (NULL != mhd_httpd_head)
3466  kill_httpd (mhd_httpd_head);
3467  while (NULL != s5r_head)
3468  cleanup_s5r (s5r_head);
3469  if (NULL != lsock4)
3470  {
3471  GNUNET_NETWORK_socket_close (lsock4);
3472  lsock4 = NULL;
3473  }
3474  if (NULL != lsock6)
3475  {
3476  GNUNET_NETWORK_socket_close (lsock6);
3477  lsock6 = NULL;
3478  }
3479  if (NULL != curl_multi)
3480  {
3481  curl_multi_cleanup (curl_multi);
3482  curl_multi = NULL;
3483  }
3484  if (NULL != gns_handle)
3485  {
3486  GNUNET_GNS_disconnect (gns_handle);
3487  gns_handle = NULL;
3488  }
3489  if (NULL != curl_download_task)
3490  {
3491  GNUNET_SCHEDULER_cancel (curl_download_task);
3492  curl_download_task = NULL;
3493  }
3494  if (NULL != ltask4)
3495  {
3496  GNUNET_SCHEDULER_cancel (ltask4);
3497  ltask4 = NULL;
3498  }
3499  if (NULL != ltask6)
3500  {
3501  GNUNET_SCHEDULER_cancel (ltask6);
3502  ltask6 = NULL;
3503  }
3504  gnutls_x509_crt_deinit (proxy_ca.cert);
3505  gnutls_x509_privkey_deinit (proxy_ca.key);
3506  gnutls_global_deinit ();
3507 }
3508 
3509 
3515 static struct GNUNET_NETWORK_Handle *
3517 {
3518  struct GNUNET_NETWORK_Handle *ls;
3519  struct sockaddr_in sa4;
3520  int eno;
3521 
3522  memset (&sa4, 0, sizeof (sa4));
3523  sa4.sin_family = AF_INET;
3524  sa4.sin_port = htons (port);
3525  sa4.sin_addr.s_addr = address;
3526 #if HAVE_SOCKADDR_IN_SIN_LEN
3527  sa4.sin_len = sizeof (sa4);
3528 #endif
3529  ls = GNUNET_NETWORK_socket_create (AF_INET,
3530  SOCK_STREAM,
3531  0);
3532  if (NULL == ls)
3533  return NULL;
3534  if (GNUNET_OK !=
3536  (const struct sockaddr *) &sa4,
3537  sizeof (sa4)))
3538  {
3539  eno = errno;
3541  errno = eno;
3542  return NULL;
3543  }
3544  return ls;
3545 }
3546 
3547 
3553 static struct GNUNET_NETWORK_Handle *
3555 {
3556  struct GNUNET_NETWORK_Handle *ls;
3557  struct sockaddr_in6 sa6;
3558  int eno;
3559 
3560  memset (&sa6, 0, sizeof (sa6));
3561  sa6.sin6_family = AF_INET6;
3562  sa6.sin6_port = htons (port);
3563  sa6.sin6_addr = address6;
3564 #if HAVE_SOCKADDR_IN_SIN_LEN
3565  sa6.sin6_len = sizeof (sa6);
3566 #endif
3567  ls = GNUNET_NETWORK_socket_create (AF_INET6,
3568  SOCK_STREAM,
3569  0);
3570  if (NULL == ls)
3571  return NULL;
3572  if (GNUNET_OK !=
3574  (const struct sockaddr *) &sa6,
3575  sizeof (sa6)))
3576  {
3577  eno = errno;
3579  errno = eno;
3580  return NULL;
3581  }
3582  return ls;
3583 }
3584 
3585 
3594 static void
3595 run (void *cls,
3596  char *const *args,
3597  const char *cfgfile,
3598  const struct GNUNET_CONFIGURATION_Handle *c)
3599 {
3600  char* cafile_cfg = NULL;
3601  char* cafile;
3602  char* addr_str;
3603  struct MhdHttpList *hd;
3604 
3605  cfg = c;
3606 
3607  /* Get address to bind to */
3608  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "gns-proxy",
3609  "BIND_TO",
3610  &addr_str))
3611  {
3612  //No address specified
3614  "Don't know what to bind to...\n");
3615  GNUNET_free (addr_str);
3617  return;
3618  }
3619  if (1 != inet_pton (AF_INET, addr_str, &address))
3620  {
3622  "Unable to parse address %s\n",
3623  addr_str);
3624  GNUNET_free (addr_str);
3626  return;
3627  }
3628  GNUNET_free (addr_str);
3629  /* Get address to bind to */
3630  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "gns-proxy",
3631  "BIND_TO6",
3632  &addr_str))
3633  {
3634  //No address specified
3636  "Don't know what to bind6 to...\n");
3637  GNUNET_free (addr_str);
3639  return;
3640  }
3641  if (1 != inet_pton (AF_INET6, addr_str, &address6))
3642  {
3644  "Unable to parse IPv6 address %s\n",
3645  addr_str);
3646  GNUNET_free (addr_str);
3648  return;
3649  }
3650  GNUNET_free (addr_str);
3651 
3652  if (NULL == (curl_multi = curl_multi_init ()))
3653  {
3655  "Failed to create cURL multi handle!\n");
3656  return;
3657  }
3658  cafile = cafile_opt;
3659  if (NULL == cafile)
3660  {
3661  if (GNUNET_OK !=
3663  "gns-proxy",
3664  "PROXY_CACERT",
3665  &cafile_cfg))
3666  {
3668  "gns-proxy",
3669  "PROXY_CACERT");
3670  return;
3671  }
3672  cafile = cafile_cfg;
3673  }
3675  "Using `%s' as CA\n",
3676  cafile);
3677 
3678  gnutls_global_init ();
3679  gnutls_x509_crt_init (&proxy_ca.cert);
3680  gnutls_x509_privkey_init (&proxy_ca.key);
3681 
3682  if ( (GNUNET_OK !=
3684  cafile)) ||
3685  (GNUNET_OK !=
3687  cafile)) )
3688  {
3690  _("Failed to load X.509 key and certificate from `%s'\n"),
3691  cafile);
3692  gnutls_x509_crt_deinit (proxy_ca.cert);
3693  gnutls_x509_privkey_deinit (proxy_ca.key);
3694  gnutls_global_deinit ();
3695  GNUNET_free_non_null (cafile_cfg);
3696  return;
3697  }
3698  GNUNET_free_non_null (cafile_cfg);
3699  if (NULL == (gns_handle = GNUNET_GNS_connect (cfg)))
3700  {
3702  "Unable to connect to GNS!\n");
3703  gnutls_x509_crt_deinit (proxy_ca.cert);
3704  gnutls_x509_privkey_deinit (proxy_ca.key);
3705  gnutls_global_deinit ();
3706  return;
3707  }
3709  NULL);
3710 
3711  /* Open listen socket for socks proxy */
3712  lsock6 = bind_v6 ();
3713  if (NULL == lsock6)
3714  {
3716  "bind");
3717  }
3718  else
3719  {
3720  if (GNUNET_OK !=
3722  5))
3723  {
3725  "listen");
3726  GNUNET_NETWORK_socket_close (lsock6);
3727  lsock6 = NULL;
3728  }
3729  else
3730  {
3732  lsock6,
3733  &do_accept,
3734  lsock6);
3735  }
3736  }
3737  lsock4 = bind_v4 ();
3738  if (NULL == lsock4)
3739  {
3741  "bind");
3742  }
3743  else
3744  {
3745  if (GNUNET_OK !=
3747  5))
3748  {
3750  "listen");
3751  GNUNET_NETWORK_socket_close (lsock4);
3752  lsock4 = NULL;
3753  }
3754  else
3755  {
3757  lsock4,
3758  &do_accept,
3759  lsock4);
3760  }
3761  }
3762  if ( (NULL == lsock4) &&
3763  (NULL == lsock6) )
3764  {
3766  return;
3767  }
3768  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
3769  {
3771  "cURL global init failed!\n");
3773  return;
3774  }
3776  "Proxy listens on port %u\n",
3777  (unsigned int) port);
3778 
3779  /* start MHD daemon for HTTP */
3780  hd = GNUNET_new (struct MhdHttpList);
3781  hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET | MHD_ALLOW_SUSPEND_RESUME,
3782  0,
3783  NULL, NULL,
3784  &create_response, hd,
3785  MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3786  MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
3787  MHD_OPTION_NOTIFY_CONNECTION, &mhd_connection_cb, NULL,
3788  MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3789  MHD_OPTION_END);
3790  if (NULL == hd->daemon)
3791  {
3792  GNUNET_free (hd);
3794  return;
3795  }
3796  httpd = hd;
3797  GNUNET_CONTAINER_DLL_insert (mhd_httpd_head,
3798  mhd_httpd_tail,
3799  hd);
3800 }
3801 
3802 
3810 int
3811 main (int argc,
3812  char *const *argv)
3813 {
3814  struct GNUNET_GETOPT_CommandLineOption options[] = {
3816  "port",
3817  NULL,
3818  gettext_noop ("listen on specified port (default: 7777)"),
3819  &port),
3821  "authority",
3822  NULL,
3823  gettext_noop ("pem file to use as CA"),
3824  &cafile_opt),
3826  "disable-ivp6",
3827  gettext_noop ("disable use of IPv6"),
3828  &disable_v6),
3829 
3831  };
3832  static const char* page =
3833  "<html><head><title>gnunet-gns-proxy</title>"
3834  "</head><body>cURL fail</body></html>";
3835  int ret;
3836 
3837  if (GNUNET_OK !=
3838  GNUNET_STRINGS_get_utf8_args (argc, argv,
3839  &argc, &argv))
3840  return 2;
3841  GNUNET_log_setup ("gnunet-gns-proxy",
3842  "WARNING",
3843  NULL);
3845  = MHD_create_response_from_buffer (strlen (page),
3846  (void *) page,
3847  MHD_RESPMEM_PERSISTENT);
3848 
3849  ret =
3850  (GNUNET_OK ==
3851  GNUNET_PROGRAM_run (argc, argv,
3852  "gnunet-gns-proxy",
3853  _("GNUnet GNS proxy"),
3854  options,
3855  &run, NULL)) ? 0 : 1;
3856  MHD_destroy_response (curl_failure_response);
3857  GNUNET_free_non_null ((char *) argv);
3858  return ret;
3859 }
3860 
3861 /* end of gnunet-gns-proxy.c */
struct HttpResponseHeader * header_head
Headers from response.
Socket has been passed to MHD, do not close it anymore.
#define GNUNET_CONTAINER_DLL_remove(head, tail, element)
Remove an element from a DLL.
int GNUNET_NETWORK_socket_listen(const struct GNUNET_NETWORK_Handle *desc, int backlog)
Listen on a socket.
Definition: network.c:796
static struct Socks5Request * s5r_tail
DLL of active socks requests.
Connection to the GNS service.
Definition: gns_api.h:35
const struct GNUNET_SCHEDULER_TaskContext * GNUNET_SCHEDULER_get_task_context(void)
Obtain the reasoning why the current task was started.
Definition: scheduler.c:746
int GNUNET_NETWORK_get_fd(const struct GNUNET_NETWORK_Handle *desc)
Return file descriptor for this network handle.
Definition: network.c:1268
static void mhd_connection_cb(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_ConnectionNotificationCode cnc)
Function called when MHD connection is opened or closed.
char * domain
the domain name to server (only important for TLS)
struct sockaddr * GNUNET_NETWORK_get_addr(const struct GNUNET_NETWORK_Handle *desc)
Return the sockaddr for this network handle.
Definition: network.c:1281
#define MHD_CACHE_TIMEOUT
After how long do we clean up unused MHD TLS instances?
uint8_t num_auth_methods
How many authentication methods does the client support.
struct GNUNET_MessageHeader * msg
Definition: 005.c:2
uint16_t service
Service of the boxed record (aka port number), in NBO.
static struct GNUNET_VPN_RedirectionRequest * request
Opaque redirection request handle.
Definition: gnunet-vpn.c:41
uint8_t version
Should be SOCKS_VERSION_5.
static struct MhdHttpList * mhd_httpd_tail
DLL for http/https daemons.
Record type used to box up SRV and TLSA records.
uint64_t rel_value_us
The actual value.
static struct GNUNET_SCHEDULER_Task * ltask4
The listen task ID for IPv4.
#define GNUNET_CONTAINER_DLL_insert(head, tail, element)
Insert an element at the head of a DLL.
ssize_t GNUNET_NETWORK_socket_send(const struct GNUNET_NETWORK_Handle *desc, const void *buffer, size_t length)
Send data (always non-blocking).
Definition: network.c:927
We&#39;re waiting to get the client hello.
static void mhd_error_log_callback(void *cls, const char *fm, va_list ap)
Function called by MHD with errors, suppresses them all.
static void * mhd_log_callback(void *cls, const char *url, struct MHD_Connection *connection)
Function called when MHD first processes an incoming connection.
struct MhdHttpList * next
DLL for httpds.
socklen_t GNUNET_NETWORK_get_addrlen(const struct GNUNET_NETWORK_Handle *desc)
Return sockaddr length for this network handle.
Definition: network.c:1294
char * value
Header value.
int GNUNET_snprintf(char *buf, size_t size, const char *format,...)
Like snprintf, just aborts if the buffer is of insufficient size.
struct HttpResponseHeader * prev
DLL.
library to make it easy to download JSON replies over HTTP
Context information passed to each scheduler task.
uint8_t addr_type
Address type, an enum Socks5AddressType.
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_shutdown(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run on shutdown, that is when a CTRL-C signal is received, or when GNUNET_SCHEDULER_shutdown() is being invoked.
Definition: scheduler.c:1293
void * GNUNET_GNS_lookup_with_tld_cancel(struct GNUNET_GNS_LookupWithTldRequest *ltr)
Cancel pending lookup request.
Definition: gns_tld_api.c:330
struct GNUNET_SCHEDULER_Task * wtask
Client socket write task.
ssize_t GNUNET_NETWORK_socket_recv(const struct GNUNET_NETWORK_Handle *desc, void *buffer, size_t length)
Read data from a connected socket (always non-blocking).
Definition: network.c:894
struct MHD_Connection * con
MHD connection for this request.
int GNUNET_STRINGS_get_utf8_args(int argc, char *const *argv, int *u8argc, char *const **u8argv)
Returns utf-8 encoded arguments.
Definition: strings.c:1521
#define HTTPS_PORT
Port for HTTPS.
static void do_write(void *cls)
Write data from buffer to socks5 client, then continue with state machine.
static size_t curl_check_hdr(void *buffer, size_t size, size_t nmemb, void *cls)
We&#39;re getting an HTTP response header from cURL.
static int create_mhd_response_from_s5r(struct Socks5Request *s5r)
Create an MHD response object in s5r matching the information we got from curl.
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
char * dane_data[32+1]
Payload of the DANE records encountered.
static struct MHD_Response * curl_failure_response
Response we return on cURL failures.
char rbuf[(256+32)]
Read buffer.
int curl_paused
Did we pause CURL processing?
static struct GNUNET_SCHEDULER_TaskContext tc
Task context of the current task.
Definition: scheduler.c:417
int dane_data_len[32+1]
Number of bytes in dane_data.
int GNUNET_NETWORK_socket_bind(struct GNUNET_NETWORK_Handle *desc, const struct sockaddr *address, socklen_t address_len)
Bind a socket to a particular address.
Definition: network.c:522
struct GNUNET_GNS_LookupWithTldRequest * GNUNET_GNS_lookup_with_tld(struct GNUNET_GNS_Handle *handle, const char *name, uint32_t type, enum GNUNET_GNS_LocalOptions options, GNUNET_GNS_LookupResultProcessor2 proc, void *proc_cls)
Perform an asynchronous lookup operation on the GNS, determining the zone using the TLD of the given ...
Definition: gns_tld_api.c:242
Server hello in Socks5 protocol.
We&#39;re waiting to get the initial request.
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_write_net(struct GNUNET_TIME_Relative delay, struct GNUNET_NETWORK_Handle *wfd, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay or when the specified file descriptor is ready f...
Definition: scheduler.c:1548
static void kill_httpd_task(void *cls)
Task run whenever HTTP server is idle for too long.
struct GNUNET_NETWORK_Handle * sock
The client socket.
#define MAX_PEM_SIZE
Largest allowed size for a PEM certificate.
#define IO_BUFFERSIZE
Size of the buffer for the data upload / download.
static struct GNUNET_SCHEDULER_Task * ltask6
The listen task ID for IPv6.
#define GNUNET_NO
Definition: gnunet_common.h:81
#define GNUNET_memdup(buf, size)
Allocate and initialize a block of memory.
#define GNUNET_OK
Named constants for return values.
Definition: gnunet_common.h:78
#define HTTP_HANDSHAKE_TIMEOUT
After how long do we clean up Socks5 handles that failed to show any activity with their respective M...
#define MAX_DANES
Maximum number of DANE records we support per domain name (and port and protocol).
size_t data_size
Number of bytes in data.
IPv4 address.
#define GNUNET_free_non_null(ptr)
Free the memory pointed to by ptr if ptr is not NULL.
const struct GNUNET_NETWORK_FDSet * read_ready
Set of file descriptors ready for reading; note that additional bits may be set that were not in the ...
uint8_t version
Should be SOCKS_VERSION_5.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
Definition of a command line option.
Socks5Commands
Commands in Socks5.
static void do_s5r_read(void *cls)
Read data from incoming Socks5 connection.
A structure for CA cert/key.
static in_addr_t address
The address to bind to.
#define GNUNET_GNSRECORD_TYPE_BOX
Record type for a boxed record (see TLSA/SRV handling in GNS).
void GNUNET_SCHEDULER_shutdown(void)
Request the shutdown of a scheduler.
Definition: scheduler.c:524
static int ret
Final status code.
Definition: gnunet-arm.c:89
We&#39;ve started receiving upload data from MHD.
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
uint8_t reply
Status code, an enum Socks5StatusCode
We&#39;ve finished receiving upload data from MHD.
#define SOCKS_AUTH_NONE
Flag to set for &#39;no authentication&#39;.
SocksPhase
The socks phases.
static struct ProxyCA proxy_ca
The CA for X.509 certificate generation.
IPC messages between GNS API and GNS service.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur...
struct HttpResponseHeader * next
DLL.
struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_OPTION_END
Definition: 002.c:13
static struct GNUNET_NETWORK_Handle * lsock6
The listen socket of the proxy for IPv6.
static struct GNUNET_DNSSTUB_Context * ctx
Context for DNS resolution.
void GNUNET_NETWORK_fdset_copy_native(struct GNUNET_NETWORK_FDSet *to, const fd_set *from, int nfds)
Copy a native fd set into the GNUnet representation.
Definition: network.c:1308
#define GNUNET_GNSRECORD_TYPE_VPN
Record type for VPN resolution.
char * type
Header type.
size_t io_len
Number of bytes already in the IO buffer.
struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_string(char shortName, const char *name, const char *argumentHelp, const char *description, char **str)
Allow user to specify a string.
Socks5StatusCode
Status codes in Socks5 response.
size_t rbuf_len
Number of bytes already in read buffer.
#define _(String)
GNU gettext support macro.
Definition: platform.h:208
static struct GNUNET_NETWORK_Handle * lsock4
The listen socket of the proxy for IPv4.
void GNUNET_NETWORK_socket_free_memory_only_(struct GNUNET_NETWORK_Handle *desc)
Only free memory of a socket, keep the file descriptor untouched.
Definition: network.c:680
struct GNUNET_NETWORK_Handle * GNUNET_NETWORK_socket_accept(const struct GNUNET_NETWORK_Handle *desc, struct sockaddr *address, socklen_t *address_len)
Accept a new connection on a socket.
Definition: network.c:468
static char * cafile_opt
The CA file (pem) to use for the proxy CA.
static void mhd_completed_cb(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe)
Function called when MHD decides that we are done with a request.
int main(int argc, char *const *argv)
The main function for gnunet-gns-proxy.
void GNUNET_NETWORK_fdset_destroy(struct GNUNET_NETWORK_FDSet *fds)
Releases the associated memory of an fd set.
Definition: network.c:1554
static uint16_t port
The port the proxy is running on (default 7777)
#define GNUNET_log_strerror(level, cmd)
Log an error message at log-level &#39;level&#39; that indicates a failure of the command &#39;cmd&#39; with the mess...
int is_ssl
is this an ssl daemon?
int GNUNET_asprintf(char **buf, const char *format,...)
Like asprintf, just portable.
static void do_shutdown(void *cls)
Task run on shutdown.
A header list.
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_delayed(struct GNUNET_TIME_Relative delay, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay.
Definition: scheduler.c:1246
#define GNUNET_DNSPARSER_MAX_NAME_LENGTH
Maximum length of a name in DNS.
struct GNUNET_NETWORK_FDSet * GNUNET_NETWORK_fdset_create(void)
Creates an fd set.
Definition: network.c:1538
static void signal_socks_failure(struct Socks5Request *s5r, enum Socks5StatusCode sc)
Return a server response message indicating a failure to the client.
struct HttpResponseHeader * header_tail
Headers from response.
char wbuf[(256+32)]
Write buffer.
int ssl_checked
X.509 Certificate status.
Establish TCP/IP stream.
static void setup_data_transfer(struct Socks5Request *s5r)
We&#39;re done with the Socks5 protocol, now we need to pass the connection data through to the final des...
static int verify
Verify mode.
static int check_ssl_certificate(struct Socks5Request *s5r)
Check that the website has presented us with a valid X.509 certificate.
enum State state
current state of profiling
static struct GNUNET_TIME_Relative timeout
User defined timestamp for completing operations.
Definition: gnunet-arm.c:114
#define GNUNET_memcpy(dst, src, n)
const void * data
Binary value stored in the DNS record.
int suspended
Did we suspend MHD processing?
unsigned int num_danes
Number of entries used in dane_data_len and dane_data.
static struct GNUNET_GNS_Handle * gns_handle
Handle to the GNS service.
void GNUNET_log_config_missing(enum GNUNET_ErrorType kind, const char *section, const char *option)
Log error message about missing configuration option.
static struct MhdHttpList * httpd
Daemon for HTTP (we have one per X.509 certificate, and then one for all HTTP connections; this is th...
static char * value
Value of the record to add/remove.
Client hello in Socks5 protocol.
struct GNUNET_GNS_LookupWithTldRequest * gns_lookup
Handle to GNS lookup, during SOCKS5_RESOLVING phase.
#define GNUNET_break_op(cond)
Use this for assertion violations caused by other peers (i.e.
struct curl_slist * headers
HTTP request headers for the curl request.
static void do_accept(void *cls)
Accept new incoming connections.
enum SocksPhase state
The socks state.
struct MhdHttpList * hd
MHD HTTP instance handling this request, NULL for none.
static struct GNUNET_NETWORK_Handle * bind_v6()
Create an IPv6 listen socket bound to our port.
uint8_t reserved
Always zero.
static void * load_file(const char *filename, unsigned int *size)
Read file in filename.
uint8_t auth_method
Chosen authentication method, for us always SOCKS_AUTH_NONE, which skips the authentication step...
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_now(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run as soon as possible.
Definition: scheduler.c:1273
void GNUNET_GNS_disconnect(struct GNUNET_GNS_Handle *handle)
Shutdown connection with the GNS service.
Definition: gns_api.c:285
#define SOCKS_BUFFERSIZE
Size of the read/write buffers for Socks.
#define GNUNET_MIN(a, b)
Definition: gnunet_common.h:83
#define GNUNET_GNS_PROXY_PORT
Default Socks5 listen port.
char * domain
the domain name to server (only important for TLS)
collection of IO descriptors
struct MHD_Response * response
MHD response object for this request.
static char buf[2048]
static char * filename
static void schedule_httpd(struct MhdHttpList *hd)
Schedule MHD.
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_select(enum GNUNET_SCHEDULER_Priority prio, struct GNUNET_TIME_Relative delay, const struct GNUNET_NETWORK_FDSet *rs, const struct GNUNET_NETWORK_FDSet *ws, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay or when any of the specified file descriptor set...
Definition: scheduler.c:1829
static void cleanup(void *cls)
Function scheduled as very last function, cleans up after us.
uint8_t resvd
Reserved, always zero.
struct GNUNET_TIME_Relative GNUNET_TIME_relative_multiply(struct GNUNET_TIME_Relative rel, unsigned long long factor)
Multiply relative time by a given factor.
Definition: time.c:439
#define GNUNET_TIME_UNIT_FOREVER_REL
Constant used to specify "forever".
struct GNUNET_SCHEDULER_Task * rtask
Client socket read task.
uint16_t protocol
Protocol of the boxed record (6 = TCP, 17 = UDP, etc.).
struct GNUNET_GNS_Handle * GNUNET_GNS_connect(const struct GNUNET_CONFIGURATION_Handle *cfg)
Initialize the connection with the GNS service.
Definition: gns_api.c:263
static int load_cert_from_file(gnutls_x509_crt_t crt, const char *certfile)
Load cert from file.
#define GNUNET_TIME_UNIT_MILLISECONDS
One millisecond.
struct Socks5Request * next
DLL.
static struct Socks5Request * s5r_head
DLL of active socks requests.
struct MHD_Daemon * daemon
The daemon handle.
int GNUNET_CONFIGURATION_get_value_string(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, char **value)
Get a configuration value that should be a string.
static struct MhdHttpList * lookup_ssl_httpd(const char *domain)
Lookup (or create) an TLS MHD instance for a particular domain.
static struct in6_addr address6
The IPv6 address to bind to.
uint16_t port
Desired destination port.
struct GNUNET_SCHEDULER_Task * timeout_task
Timeout task.
Establish TCP port binding.
static uint64_t etime
Expiration string converted to numeric value.
struct GNUNET_HashCode key
The key used in the DHT.
#define GNUNET_SYSERR
Definition: gnunet_common.h:79
Finish writing the write buffer, then clean up.
static void cleanup_s5r(struct Socks5Request *s5r)
Clean up s5r handles.
static unsigned int size
Size of the "table".
Definition: peer.c:67
static struct GNUNET_SCHEDULER_Task * curl_download_task
The cURL download task (curl multi API).
static const struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
static ssize_t mhd_content_cb(void *cls, uint64_t pos, char *buf, size_t max)
Callback for MHD response generation.
struct sockaddr_storage destination_address
Once known, what&#39;s the target address for the connection?
uint8_t version
Should be SOCKS_VERSION_5.
const char * name
uint8_t version
Should be SOCKS_VERSION_5.
struct curl_slist * hosts
DNS->IP mappings resolved through GNS.
static struct GNUNET_FS_SearchContext * sc
Definition: gnunet-search.c:37
char key[(10 *1024)]
The private key as PEM.
static void run(void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *c)
Main function that will be run.
char cert[(10 *1024)]
The certificate as PEM.
Client socks request in Socks5 protocol.
static int con_val_iter(void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
Read HTTP request header field from the request.
uint32_t record_type
GNS record type of the boxed record.
static struct GNUNET_NETWORK_Handle * ls
Listen socket for STUN processing.
Definition: gnunet-nat.c:85
Server response to client requests in Socks5 protocol.
int GNUNET_DISK_file_size(const char *filename, uint64_t *size, int include_symbolic_links, int single_file_mode)
Get the size of the file (or directory) of the given file (in bytes).
Definition: disk.c:289
#define GNUNET_DNSPARSER_TYPE_TLSA
static size_t curl_download_cb(void *ptr, size_t size, size_t nmemb, void *ctx)
Handle response payload data from cURL.
A structure for all running Httpds.
static void kill_httpd(struct MhdHttpList *hd)
Kill the given MHD daemon.
We&#39;ve finished uploading data via CURL and can now download.
gnutls_x509_privkey_t key
The private key.
IPv6 address.
#define GNUNET_GNSRECORD_TYPE_LEHO
Record type for GNS legacy hostnames ("LEHO").
#define GNUNET_strndup(a, length)
Wrapper around GNUNET_xstrndup_.
static struct MhdHttpList * mhd_httpd_head
DLL for http/https daemons.
struct Socks5Request * prev
DLL.
int is_gns
Was the hostname resolved via GNS?
uint32_t record_type
Type of the GNS/DNS record.
char * leho
DNS Legacy Host Name as given by GNS, NULL if not given.
#define GNUNET_DNSPARSER_TYPE_AAAA
configuration data
Definition: configuration.c:85
static void curl_download_prepare()
Ask cURL for the select() sets and schedule cURL operations.
static struct GNUNET_NETWORK_Handle * bind_v4()
Create an IPv4 listen socket bound to our port.
We&#39;ve finished receiving download data from cURL.
struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_flag(char shortName, const char *name, const char *description, int *val)
Allow user to specify a flag (which internally means setting an integer to 1/GNUNET_YES/GNUNET_OK.
struct ProxyGNSCertificate * proxy_cert
Optional proxy certificate used.
static int inet_pton(int af, const char *cp, struct in_addr *buf)
Convert IPv4 address from text to binary form.
#define GNUNET_log(kind,...)
Entry in list of pending tasks.
Definition: scheduler.c:134
static void do_httpd(void *cls)
Task run whenever HTTP server operations are pending.
int GNUNET_CONFIGURATION_get_value_filename(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, char **value)
Get a configuration value that should be the name of a file or directory.
char io_buf[CURL_MAX_WRITE_SIZE]
Buffer we use for moving data between MHD and curl (in both directions).
handle to a socket
Definition: network.c:46
int is_tls
This is (probably) a TLS connection.
int GNUNET_PROGRAM_run(int argc, char *const *argv, const char *binaryName, const char *binaryHelp, const struct GNUNET_GETOPT_CommandLineOption *options, GNUNET_PROGRAM_Main task, void *task_cls)
Run a standard GNUnet command startup sequence (initialize loggers and configuration, parse options).
Definition: program.c:361
static void curl_task_download(void *cls)
Task that is run when we are ready to receive more data from curl.
static int load_key_from_file(gnutls_x509_privkey_t key, const char *keyfile)
Load PEM key from file.
static void signal_socks_success(struct Socks5Request *s5r)
Return a server response message indicating success.
static void timeout_s5r_handshake(void *cls)
Task run when a Socks5Request somehow fails to be associated with an MHD connection (i...
#define GNUNET_YES
Definition: gnunet_common.h:80
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_read_net(struct GNUNET_TIME_Relative delay, struct GNUNET_NETWORK_Handle *rfd, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay or when the specified file descriptor is ready f...
Definition: scheduler.c:1478
static void clear_from_s5r_rbuf(struct Socks5Request *s5r, size_t len)
Remove the first len bytes from the beginning of the read buffer.
Socks5AddressType
Address types in Socks5.
static struct ProxyGNSCertificate * generate_gns_certificate(const char *name)
Generate new certificate for specific name.
static void handle_gns_result(void *cls, int tld, uint32_t rd_count, const struct GNUNET_GNSRECORD_Data *rd)
Process GNS results for target domain.
We are currently resolving the destination.
Establish UDP port binding.
static int create_response(void *cls, struct MHD_Connection *con, const char *url, const char *meth, const char *ver, const char *upload_data, size_t *upload_data_size, void **con_cls)
Main MHD callback for handling requests.
gnutls_x509_crt_t cert
The certificate.
char * url
The URL to fetch.
Run with the default priority (normal P2P operations).
int GNUNET_log_setup(const char *comp, const char *loglevel, const char *logfile)
Setup logging.
We&#39;re in transfer mode.
int GNUNET_NETWORK_fdset_isset(const struct GNUNET_NETWORK_FDSet *fds, const struct GNUNET_NETWORK_Handle *desc)
Check whether a socket is part of the fd set.
Definition: network.c:1181
A structure for socks requests.
Handle to a lookup request.
Definition: gns_tld_api.c:44
struct MhdHttpList * prev
DLL for httpds.
static CURLM * curl_multi
The cURL multi handle.
#define SOCKS_VERSION_5
Which SOCKS version do we speak?
#define GNUNET_DNSPARSER_TYPE_A
uint8_t command
Command code, we only uspport SOCKS5_CMD_TCP_STREAM.
static void run_mhd_now(struct MhdHttpList *hd)
Run MHD now, we have extra data ready for the callback.
int GNUNET_NETWORK_socket_close(struct GNUNET_NETWORK_Handle *desc)
Close a socket.
Definition: network.c:604
unsigned int response_code
HTTP response code to give to MHD for the response.
static size_t curl_upload_cb(void *buf, size_t size, size_t nmemb, void *cls)
cURL callback for uploaded (PUT/POST) data.
Structure for GNS certificates.
#define GNUNET_malloc(size)
Wrapper around malloc.
ssize_t GNUNET_DISK_fn_read(const char *fn, void *result, size_t len)
Read the contents of a binary file into a buffer.
Definition: disk.c:1019
int GNUNET_NETWORK_test_pf(int pf)
Test if the given protocol family is supported by this system.
Definition: network.c:84
#define GNUNET_free(ptr)
Wrapper around free.
struct GNUNET_SCHEDULER_Task * httpd_task
The task ID.
static int disable_v6
Disable IPv6.
CURL * curl
Handle to cURL.
Time for relative time used by GNUnet, in microseconds.
size_t wbuf_len
Number of bytes already in write buffer.
struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_uint16(char shortName, const char *name, const char *argumentHelp, const char *description, uint16_t *val)
Allow user to specify an uint16_t.
uint16_t len
length of data (which is always a uint32_t, but presumably this can be used to specify that fewer byt...
#define gettext_noop(String)
Definition: gettext.h:69
struct GNUNET_NETWORK_Handle * GNUNET_NETWORK_socket_create(int domain, int type, int protocol)
Create a new socket.
Definition: network.c:1037
uint8_t addr_type
Address type, an enum Socks5AddressType.
void * GNUNET_SCHEDULER_cancel(struct GNUNET_SCHEDULER_Task *task)
Cancel the task with the specified identifier.
Definition: scheduler.c:965