2 This file is part of GNUnet
3 Copyright (C) 2007, 2009, 2011, 2012 Christian Grothoff
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file test_gns_proxy.c
23 * @brief testcase for accessing SOCKS5 GNS proxy
24 * @author Martin Schanzenbach
27 /* Just included for the right curl.h */
28 #include "gnunet_curl_lib.h"
29 #include <microhttpd.h>
30 #include "gnunet_util_lib.h"
31 #include "gnutls/x509.h"
34 * Largest allowed size for a PEM certificate.
36 #define MAX_PEM_SIZE (10 * 1024)
38 #define TEST_DOMAIN "www.test"
40 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
43 * Return value for 'main'.
45 static int global_ret;
48 static struct MHD_Daemon *mhd;
50 static struct GNUNET_SCHEDULER_Task *mhd_task_id;
52 static struct GNUNET_SCHEDULER_Task *curl_task_id;
60 static struct GNUNET_OS_Process *proxy_proc;
62 static char* cafile_opt;
64 static char* cafile_srv;
68 static gnutls_x509_crt_t proxy_cert;
70 static gnutls_x509_privkey_t proxy_key;
78 static struct CBC cbc;
81 * Read file in filename
83 * @param filename file to read
84 * @param size pointer where filesize is stored
85 * @return NULL on error
88 load_file (const char* filename,
95 GNUNET_DISK_file_size (filename,
100 if (fsize > MAX_PEM_SIZE)
102 *size = (unsigned int) fsize;
103 buffer = GNUNET_malloc (*size);
105 GNUNET_DISK_fn_read (filename,
109 GNUNET_free (buffer);
116 * Load PEM key from file
118 * @param key where to store the data
119 * @param keyfile path to the PEM file
120 * @return #GNUNET_OK on success
123 load_key_from_file (gnutls_x509_privkey_t key,
126 gnutls_datum_t key_data;
129 key_data.data = load_file (keyfile,
131 if (NULL == key_data.data)
132 return GNUNET_SYSERR;
133 ret = gnutls_x509_privkey_import (key, &key_data,
134 GNUTLS_X509_FMT_PEM);
135 if (GNUTLS_E_SUCCESS != ret)
137 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
138 _("Unable to import private key from file `%s'\n"),
141 GNUNET_free_non_null (key_data.data);
142 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
146 * Load cert from file
148 * @param crt struct to store data in
149 * @param certfile path to pem file
150 * @return #GNUNET_OK on success
153 load_cert_from_file (gnutls_x509_crt_t crt,
154 const char* certfile)
156 gnutls_datum_t cert_data;
159 cert_data.data = load_file (certfile,
161 if (NULL == cert_data.data)
162 return GNUNET_SYSERR;
163 ret = gnutls_x509_crt_import (crt,
165 GNUTLS_X509_FMT_PEM);
166 if (GNUTLS_E_SUCCESS != ret)
168 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
169 _("Unable to import certificate from `%s'\n"),
172 GNUNET_free_non_null (cert_data.data);
173 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
177 copy_buffer (void *ptr, size_t size, size_t nmemb, void *ctx)
179 struct CBC *cbc = ctx;
181 if (cbc->pos + size * nmemb > sizeof(cbc->buf))
182 return 0; /* overflow */
183 GNUNET_memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
184 cbc->pos += size * nmemb;
191 struct MHD_Connection *connection,
195 const char *upload_data, size_t *upload_data_size,
199 struct MHD_Response *response;
202 if (0 != strcmp ("GET", method))
203 return MHD_NO; /* unexpected method */
210 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MHD sends respose for request to URL `%s'\n", url);
211 response = MHD_create_response_from_buffer (strlen (url),
213 MHD_RESPMEM_MUST_COPY);
214 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
215 MHD_destroy_response (response);
228 if (mhd_task_id != NULL)
230 GNUNET_SCHEDULER_cancel (mhd_task_id);
233 if (curl_task_id != NULL)
235 GNUNET_SCHEDULER_cancel (curl_task_id);
240 MHD_stop_daemon (mhd);
243 GNUNET_free_non_null (url);
245 if (NULL != proxy_proc)
247 (void) GNUNET_OS_process_kill (proxy_proc, SIGKILL);
248 GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (proxy_proc));
249 GNUNET_OS_process_destroy (proxy_proc);
253 GNUNET_SCHEDULER_shutdown ();
258 * Function to run the HTTP client.
265 curl_task (void *cls)
279 struct GNUNET_NETWORK_FDSet nrs;
280 struct GNUNET_NETWORK_FDSet nws;
281 struct GNUNET_TIME_Relative delay;
290 curl_multi_perform (multi, &running);
293 GNUNET_assert (NULL != (msg = curl_multi_info_read (multi, &running)));
294 if (msg->msg == CURLMSG_DONE)
296 if (msg->data.result != CURLE_OK)
299 "%s failed at %s:%d: `%s'\n",
300 "curl_multi_perform",
302 __LINE__, curl_easy_strerror (msg->data.result));
306 curl_multi_remove_handle (multi, curl);
307 curl_multi_cleanup (multi);
308 curl_easy_cleanup (curl);
311 if (cbc.pos != strlen ("/hello_world"))
316 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download complete, shutting down!\n");
325 GNUNET_assert (CURLM_OK == curl_multi_fdset (multi, &rs, &ws, &es, &max));
326 if ( (CURLM_OK != curl_multi_timeout (multi, &timeout)) ||
328 delay = GNUNET_TIME_UNIT_SECONDS;
330 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, (unsigned int) timeout);
331 GNUNET_NETWORK_fdset_copy_native (&nrs,
334 GNUNET_NETWORK_fdset_copy_native (&nws,
337 curl_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
347 start_curl (void *cls)
350 GNUNET_asprintf (&url,
351 "https://%s:%d/hello_world",
353 curl = curl_easy_init ();
354 curl_easy_setopt (curl, CURLOPT_URL, url);
355 //curl_easy_setopt (curl, CURLOPT_URL, "https://127.0.0.1:8443/hello_world");
356 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ©_buffer);
357 curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbc);
358 curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
359 curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L);
360 curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 15L);
361 curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
362 curl_easy_setopt (curl, CURLOPT_CAINFO, cafile_opt);
363 //curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0L);
364 //curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L);
365 curl_easy_setopt (curl, CURLOPT_PROXY, "socks5h://127.0.0.1:7777");
367 multi = curl_multi_init ();
368 GNUNET_assert (multi != NULL);
369 GNUNET_assert (CURLM_OK == curl_multi_add_handle (multi, curl));
370 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
371 "Beginning HTTP download from `%s'\n",
378 * Callback invoked from the namestore service once record is
382 * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
383 * will match 'result_af' from the request
384 * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
385 * that the VPN allocated for the redirection;
386 * traffic to this IP will now be redirected to the
387 * specified target peer; NULL on error
390 commence_testing (void *cls)
393 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
400 * Function to keep the HTTP server running.
418 struct GNUNET_NETWORK_FDSet nrs;
419 struct GNUNET_NETWORK_FDSet nws;
424 unsigned MHD_LONG_LONG timeout;
425 struct GNUNET_TIME_Relative delay;
427 GNUNET_assert (NULL == mhd_task_id);
432 GNUNET_assert (MHD_YES ==
433 MHD_get_fdset (mhd, &rs, &ws, &es, &max_fd));
434 if (MHD_YES == MHD_get_timeout (mhd, &timeout))
435 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
436 (unsigned int) timeout);
438 delay = GNUNET_TIME_UNIT_FOREVER_REL;
439 GNUNET_NETWORK_fdset_copy_native (&nrs,
442 GNUNET_NETWORK_fdset_copy_native (&nws,
445 mhd_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
455 * Main function that will be run
458 * @param args remaining command-line arguments
459 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
460 * @param c configuration
466 const struct GNUNET_CONFIGURATION_Handle *c)
468 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
469 "Using `%s' as CA\n",
471 char cert[MAX_PEM_SIZE];
472 char key[MAX_PEM_SIZE];
474 size_t cert_buf_size;
476 gnutls_global_init ();
477 gnutls_x509_crt_init (&proxy_cert);
478 gnutls_x509_privkey_init (&proxy_key);
481 load_cert_from_file (proxy_cert,
484 load_key_from_file (proxy_key,
487 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
488 _("Failed to load X.509 key and certificate from `%s'\n"),
490 gnutls_x509_crt_deinit (proxy_cert);
491 gnutls_x509_privkey_deinit (proxy_key);
492 gnutls_global_deinit ();
495 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
497 key_buf_size = sizeof (key);
498 cert_buf_size = sizeof (cert);
499 gnutls_x509_crt_export (proxy_cert,
503 gnutls_x509_privkey_export (proxy_key,
507 mhd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_ALLOW_SUSPEND_RESUME, port,
510 MHD_OPTION_HTTPS_MEM_KEY, key,
511 MHD_OPTION_HTTPS_MEM_CERT, cert,
513 GNUNET_assert (NULL != mhd);
516 GNUNET_SCHEDULER_add_now (&commence_testing,
521 main (int argc, char *const *argv)
523 struct GNUNET_GETOPT_CommandLineOption options[] = {
524 GNUNET_GETOPT_option_uint16 ('p',
527 gettext_noop ("listen on specified port (default: 7777)"),
529 GNUNET_GETOPT_option_string ('A',
532 gettext_noop ("pem file to use as CA"),
534 GNUNET_GETOPT_option_string ('S',
537 gettext_noop ("pem file to use for the server"),
540 GNUNET_GETOPT_OPTION_END
543 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
545 fprintf (stderr, "failed to initialize curl\n");
549 GNUNET_STRINGS_get_utf8_args (argc, argv,
552 GNUNET_log_setup ("gnunet-gns-proxy-test",
555 if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv,
556 "gnunet-gns-proxy-test",
557 _("GNUnet GNS proxy test"),
561 GNUNET_free_non_null ((char *) argv);
565 /* end of test_gns_proxy.c */