-remove debug message
[oweals/gnunet.git] / src / gns / test_gns_proxy.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2007, 2009, 2011, 2012 Christian Grothoff
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  */
20
21 /**
22  * @file test_gns_proxy.c
23  * @brief testcase for accessing SOCKS5 GNS proxy
24  * @author Martin Schanzenbach
25  */
26 #include "platform.h"
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"
32
33 /**
34  * Largest allowed size for a PEM certificate.
35  */
36 #define MAX_PEM_SIZE (10 * 1024)
37
38 #define TEST_DOMAIN "www.test"
39
40 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
41
42 /**
43  * Return value for 'main'.
44  */
45 static int global_ret;
46
47
48 static struct MHD_Daemon *mhd;
49
50 static struct GNUNET_SCHEDULER_Task *mhd_task_id;
51
52 static struct GNUNET_SCHEDULER_Task *curl_task_id;
53
54 static CURL *curl;
55
56 static CURLM *multi;
57
58 static char *url;
59
60 static struct GNUNET_OS_Process *proxy_proc;
61
62 static char*cafile_opt;
63
64 static char*cafile_srv;
65
66 static uint16_t port;
67
68 static gnutls_x509_crt_t proxy_cert;
69
70 static gnutls_x509_privkey_t proxy_key;
71
72 struct CBC
73 {
74   char buf[1024];
75   size_t pos;
76 };
77
78 static struct CBC cbc;
79
80 /**
81  * Read file in filename
82  *
83  * @param filename file to read
84  * @param size pointer where filesize is stored
85  * @return NULL on error
86  */
87 static void*
88 load_file (const char*filename,
89            unsigned int*size)
90 {
91   void *buffer;
92   uint64_t fsize;
93
94   if (GNUNET_OK !=
95       GNUNET_DISK_file_size (filename,
96                              &fsize,
97                              GNUNET_YES,
98                              GNUNET_YES))
99     return NULL;
100   if (fsize > MAX_PEM_SIZE)
101     return NULL;
102   *size = (unsigned int) fsize;
103   buffer = GNUNET_malloc (*size);
104   if (fsize !=
105       GNUNET_DISK_fn_read (filename,
106                            buffer,
107                            (size_t) fsize))
108   {
109     GNUNET_free (buffer);
110     return NULL;
111   }
112   return buffer;
113 }
114
115
116 /**
117  * Load PEM key from file
118  *
119  * @param key where to store the data
120  * @param keyfile path to the PEM file
121  * @return #GNUNET_OK on success
122  */
123 static int
124 load_key_from_file (gnutls_x509_privkey_t key,
125                     const char*keyfile)
126 {
127   gnutls_datum_t key_data;
128   int ret;
129
130   key_data.data = load_file (keyfile,
131                              &key_data.size);
132   if (NULL == key_data.data)
133     return GNUNET_SYSERR;
134   ret = gnutls_x509_privkey_import (key, &key_data,
135                                     GNUTLS_X509_FMT_PEM);
136   if (GNUTLS_E_SUCCESS != ret)
137   {
138     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
139                 _ ("Unable to import private key from file `%s'\n"),
140                 keyfile);
141   }
142   GNUNET_free_non_null (key_data.data);
143   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
144 }
145
146
147 /**
148  * Load cert from file
149  *
150  * @param crt struct to store data in
151  * @param certfile path to pem file
152  * @return #GNUNET_OK on success
153  */
154 static int
155 load_cert_from_file (gnutls_x509_crt_t crt,
156                      const char*certfile)
157 {
158   gnutls_datum_t cert_data;
159   int ret;
160
161   cert_data.data = load_file (certfile,
162                               &cert_data.size);
163   if (NULL == cert_data.data)
164     return GNUNET_SYSERR;
165   ret = gnutls_x509_crt_import (crt,
166                                 &cert_data,
167                                 GNUTLS_X509_FMT_PEM);
168   if (GNUTLS_E_SUCCESS != ret)
169   {
170     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
171                 _ ("Unable to import certificate from `%s'\n"),
172                 certfile);
173   }
174   GNUNET_free_non_null (cert_data.data);
175   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
176 }
177
178
179 static size_t
180 copy_buffer (void *ptr, size_t size, size_t nmemb, void *ctx)
181 {
182   struct CBC *cbc = ctx;
183
184   if (cbc->pos + size * nmemb > sizeof(cbc->buf))
185     return 0;                   /* overflow */
186   GNUNET_memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
187   cbc->pos += size * nmemb;
188   return size * nmemb;
189 }
190
191
192 static int
193 mhd_ahc (void *cls,
194          struct MHD_Connection *connection,
195          const char *url,
196          const char *method,
197          const char *version,
198          const char *upload_data, size_t *upload_data_size,
199          void **unused)
200 {
201   static int ptr;
202   struct MHD_Response *response;
203   int ret;
204
205   if (0 != strcmp ("GET", method))
206     return MHD_NO;              /* unexpected method */
207   if (&ptr != *unused)
208   {
209     *unused = &ptr;
210     return MHD_YES;
211   }
212   *unused = NULL;
213   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
214               "MHD sends respose for request to URL `%s'\n", url);
215   response = MHD_create_response_from_buffer (strlen (url),
216                                               (void *) url,
217                                               MHD_RESPMEM_MUST_COPY);
218   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
219   MHD_destroy_response (response);
220   if (ret == MHD_NO)
221   {
222     global_ret = 1;
223     abort ();
224   }
225   global_ret = 0;
226   return ret;
227 }
228
229
230 static void
231 do_shutdown ()
232 {
233   if (mhd_task_id != NULL)
234   {
235     GNUNET_SCHEDULER_cancel (mhd_task_id);
236     mhd_task_id = NULL;
237   }
238   if (curl_task_id != NULL)
239   {
240     GNUNET_SCHEDULER_cancel (curl_task_id);
241     curl_task_id = NULL;
242   }
243   if (NULL != mhd)
244   {
245     MHD_stop_daemon (mhd);
246     mhd = NULL;
247   }
248   GNUNET_free_non_null (url);
249
250   if (NULL != proxy_proc)
251   {
252     (void) GNUNET_OS_process_kill (proxy_proc, SIGKILL);
253     GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (proxy_proc));
254     GNUNET_OS_process_destroy (proxy_proc);
255     proxy_proc = NULL;
256   }
257   url = NULL;
258   GNUNET_SCHEDULER_shutdown ();
259 }
260
261
262 /**
263  * Function to run the HTTP client.
264  */
265 static void
266 curl_main (void);
267
268
269 static void
270 curl_task (void *cls)
271 {
272   curl_task_id = NULL;
273   curl_main ();
274 }
275
276
277 static void
278 curl_main ()
279 {
280   fd_set rs;
281   fd_set ws;
282   fd_set es;
283   int max;
284   struct GNUNET_NETWORK_FDSet nrs;
285   struct GNUNET_NETWORK_FDSet nws;
286   struct GNUNET_TIME_Relative delay;
287   long timeout;
288   int running;
289   struct CURLMsg *msg;
290
291   max = 0;
292   FD_ZERO (&rs);
293   FD_ZERO (&ws);
294   FD_ZERO (&es);
295   curl_multi_perform (multi, &running);
296   if (running == 0)
297   {
298     GNUNET_assert (NULL != (msg = curl_multi_info_read (multi, &running)));
299     if (msg->msg == CURLMSG_DONE)
300     {
301       if (msg->data.result != CURLE_OK)
302       {
303         fprintf (stderr,
304                  "%s failed at %s:%d: `%s'\n",
305                  "curl_multi_perform",
306                  __FILE__,
307                  __LINE__, curl_easy_strerror (msg->data.result));
308         global_ret = 1;
309       }
310     }
311     curl_multi_remove_handle (multi, curl);
312     curl_multi_cleanup (multi);
313     curl_easy_cleanup (curl);
314     curl = NULL;
315     multi = NULL;
316     if (cbc.pos != strlen ("/hello_world"))
317     {
318       GNUNET_break (0);
319       global_ret = 2;
320     }
321     if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
322     {
323       GNUNET_break (0);
324       global_ret = 3;
325     }
326     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download complete, shutting down!\n");
327     do_shutdown ();
328     return;
329   }
330   GNUNET_assert (CURLM_OK == curl_multi_fdset (multi, &rs, &ws, &es, &max));
331   if ((CURLM_OK != curl_multi_timeout (multi, &timeout)) ||
332       (-1 == timeout))
333     delay = GNUNET_TIME_UNIT_SECONDS;
334   else
335     delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
336                                            (unsigned int) timeout);
337   GNUNET_NETWORK_fdset_copy_native (&nrs,
338                                     &rs,
339                                     max + 1);
340   GNUNET_NETWORK_fdset_copy_native (&nws,
341                                     &ws,
342                                     max + 1);
343   curl_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
344                                               delay,
345                                               &nrs,
346                                               &nws,
347                                               &curl_task,
348                                               NULL);
349 }
350
351
352 static void
353 start_curl (void *cls)
354 {
355   curl_task_id = NULL;
356   GNUNET_asprintf (&url,
357                    "https://%s:%d/hello_world",
358                    TEST_DOMAIN, port);
359   curl = curl_easy_init ();
360   curl_easy_setopt (curl, CURLOPT_URL, url);
361   // curl_easy_setopt (curl, CURLOPT_URL, "https://127.0.0.1:8443/hello_world");
362   curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, &copy_buffer);
363   curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbc);
364   curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
365   curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L);
366   curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 15L);
367   curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
368   curl_easy_setopt (curl, CURLOPT_CAINFO, cafile_opt);
369   // curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0L);
370   // curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L);
371   curl_easy_setopt (curl, CURLOPT_PROXY, "socks5h://127.0.0.1:7777");
372
373   multi = curl_multi_init ();
374   GNUNET_assert (multi != NULL);
375   GNUNET_assert (CURLM_OK == curl_multi_add_handle (multi, curl));
376   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
377               "Beginning HTTP download from `%s'\n",
378               url);
379   curl_main ();
380 }
381
382
383 /**
384  * Callback invoked from the namestore service once record is
385  * created.
386  *
387  * @param cls closure
388  * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
389  *                will match 'result_af' from the request
390  * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
391  *                that the VPN allocated for the redirection;
392  *                traffic to this IP will now be redirected to the
393  *                specified target peer; NULL on error
394  */
395 static void
396 commence_testing (void *cls)
397 {
398   curl_task_id =
399     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
400                                   &start_curl,
401                                   NULL);
402 }
403
404
405 /**
406  * Function to keep the HTTP server running.
407  */
408 static void
409 mhd_main (void);
410
411
412 static void
413 mhd_task (void *cls)
414 {
415   mhd_task_id = NULL;
416   MHD_run (mhd);
417   mhd_main ();
418 }
419
420
421 static void
422 mhd_main ()
423 {
424   struct GNUNET_NETWORK_FDSet nrs;
425   struct GNUNET_NETWORK_FDSet nws;
426   fd_set rs;
427   fd_set ws;
428   fd_set es;
429   int max_fd;
430   unsigned MHD_LONG_LONG timeout;
431   struct GNUNET_TIME_Relative delay;
432
433   GNUNET_assert (NULL == mhd_task_id);
434   FD_ZERO (&rs);
435   FD_ZERO (&ws);
436   FD_ZERO (&es);
437   max_fd = -1;
438   GNUNET_assert (MHD_YES ==
439                  MHD_get_fdset (mhd, &rs, &ws, &es, &max_fd));
440   if (MHD_YES == MHD_get_timeout (mhd, &timeout))
441     delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
442                                            (unsigned int) timeout);
443   else
444     delay = GNUNET_TIME_UNIT_FOREVER_REL;
445   GNUNET_NETWORK_fdset_copy_native (&nrs,
446                                     &rs,
447                                     max_fd + 1);
448   GNUNET_NETWORK_fdset_copy_native (&nws,
449                                     &ws,
450                                     max_fd + 1);
451   mhd_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
452                                              delay,
453                                              &nrs,
454                                              &nws,
455                                              &mhd_task,
456                                              NULL);
457 }
458
459
460 /**
461  * Main function that will be run
462  *
463  * @param cls closure
464  * @param args remaining command-line arguments
465  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
466  * @param c configuration
467  */
468 static void
469 run (void *cls,
470      char *const *args,
471      const char *cfgfile,
472      const struct GNUNET_CONFIGURATION_Handle *c)
473 {
474   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
475               "Using `%s' as CA\n",
476               cafile_srv);
477   char cert[MAX_PEM_SIZE];
478   char key[MAX_PEM_SIZE];
479   size_t key_buf_size;
480   size_t cert_buf_size;
481
482   gnutls_global_init ();
483   gnutls_x509_crt_init (&proxy_cert);
484   gnutls_x509_privkey_init (&proxy_key);
485
486   if ((GNUNET_OK !=
487        load_cert_from_file (proxy_cert,
488                             cafile_srv)) ||
489       (GNUNET_OK !=
490        load_key_from_file (proxy_key,
491                            cafile_srv)))
492   {
493     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
494                 _ ("Failed to load X.509 key and certificate from `%s'\n"),
495                 cafile_srv);
496     gnutls_x509_crt_deinit (proxy_cert);
497     gnutls_x509_privkey_deinit (proxy_key);
498     gnutls_global_deinit ();
499     return;
500   }
501   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
502                                  NULL);
503   key_buf_size = sizeof(key);
504   cert_buf_size = sizeof(cert);
505   gnutls_x509_crt_export (proxy_cert,
506                           GNUTLS_X509_FMT_PEM,
507                           cert,
508                           &cert_buf_size);
509   gnutls_x509_privkey_export (proxy_key,
510                               GNUTLS_X509_FMT_PEM,
511                               key,
512                               &key_buf_size);
513   mhd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL
514                           | MHD_ALLOW_SUSPEND_RESUME, port,
515                           NULL, NULL,
516                           &mhd_ahc, NULL,
517                           MHD_OPTION_HTTPS_MEM_KEY, key,
518                           MHD_OPTION_HTTPS_MEM_CERT, cert,
519                           MHD_OPTION_END);
520   GNUNET_assert (NULL != mhd);
521   mhd_main ();
522
523   GNUNET_SCHEDULER_add_now (&commence_testing,
524                             NULL);
525 }
526
527
528 int
529 main (int argc, char *const *argv)
530 {
531   struct GNUNET_GETOPT_CommandLineOption options[] = {
532     GNUNET_GETOPT_option_uint16 ('p',
533                                  "port",
534                                  NULL,
535                                  gettext_noop (
536                                    "listen on specified port (default: 7777)"),
537                                  &port),
538     GNUNET_GETOPT_option_string ('A',
539                                  "curlcert",
540                                  NULL,
541                                  gettext_noop ("pem file to use as CA"),
542                                  &cafile_opt),
543     GNUNET_GETOPT_option_string ('S',
544                                  "servercert",
545                                  NULL,
546                                  gettext_noop (
547                                    "pem file to use for the server"),
548                                  &cafile_srv),
549
550     GNUNET_GETOPT_OPTION_END
551   };
552
553   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
554   {
555     fprintf (stderr, "failed to initialize curl\n");
556     return 2;
557   }
558   if (GNUNET_OK !=
559       GNUNET_STRINGS_get_utf8_args (argc, argv,
560                                     &argc, &argv))
561     return 2;
562   GNUNET_log_setup ("gnunet-gns-proxy-test",
563                     "WARNING",
564                     NULL);
565   if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv,
566                                        "gnunet-gns-proxy-test",
567                                        _ ("GNUnet GNS proxy test"),
568                                        options,
569                                        &run, NULL))
570     return 1;
571   GNUNET_free_non_null ((char *) argv);
572   return global_ret;
573 }
574
575
576 /* end of test_gns_proxy.c */