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