uncrustify as demanded.
[oweals/gnunet.git] / src / transport / test_plugin_transport.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2009, 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  */
20 /**
21  * @file transport/test_plugin_transport.c
22  * @brief testcase for transport_api.c
23  * @author Sailor Siraj
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_hello_lib.h"
30 #include "gnunet_peerinfo_service.h"
31 #include "gnunet_statistics_service.h"
32 #include "gnunet_protocols.h"
33 #include "gnunet_transport_plugin.h"
34 #include "transport.h"
35
36 /**
37  * How long until we give up on transmitting the message?
38  */
39 #define WAIT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 5)
40 #define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30)
41
42 #define HOSTKEY_FILE "test_plugin_hostkey.ecc"
43
44 /**
45  * Our public key.
46  */
47 static struct GNUNET_PeerIdentity my_identity;
48
49 /**
50  * Our private key.
51  */
52 static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
53
54 /**
55  * Our configuration.
56  */
57 const struct GNUNET_CONFIGURATION_Handle *cfg;
58
59 /**
60  * Our configuration.
61  */
62 struct GNUNET_STATISTICS_Handle *stats;
63
64 /**
65  * Our HELLO
66  */
67 struct GNUNET_HELLO_Message *hello;
68
69 /**
70  * Number of neighbours we'd like to have.
71  */
72 static uint32_t max_connect_per_transport;
73
74 /**
75  * Environment for this plugin.
76  */
77 struct GNUNET_TRANSPORT_PluginEnvironment env;
78
79 /**
80  * handle for the api provided by this plugin
81  */
82 struct GNUNET_TRANSPORT_PluginFunctions *api;
83
84 /**
85  * Helper handler
86  */
87 struct GNUNET_HELPER_Handle *suid_helper;
88
89 /**
90  * Timeout task
91  */
92 static struct GNUNET_SCHEDULER_Task * timeout_endbadly;
93
94 /**
95  * Timeout task
96  */
97 static struct GNUNET_SCHEDULER_Task * timeout_wait;
98
99 /**
100  * Library name
101  */
102 static char *libname;
103
104 /**
105  * Plugin addresses head
106  */
107 struct AddressWrapper *head;
108
109 /**
110  * Plugin addresses tail
111  */
112 struct AddressWrapper *tail;
113
114 unsigned int addresses_reported;
115
116 unsigned int pretty_printers_running;
117
118 /**
119  * Did the test pass or fail?
120  */
121 static int ok;
122
123 struct AddressWrapper {
124   struct AddressWrapper *next;
125
126   struct AddressWrapper *prev;
127
128   struct GNUNET_HELLO_Address *address;
129
130   char *addrstring;
131
132   struct GNUNET_SCHEDULER_Task *test_task;
133 };
134
135
136 static void
137 end()
138 {
139   struct AddressWrapper *w;
140   int c = 0;
141
142   ok = 0;
143
144   if (NULL != timeout_endbadly)
145     {
146       GNUNET_SCHEDULER_cancel(timeout_endbadly);
147       timeout_endbadly = NULL;
148     }
149   if (NULL != api)
150     GNUNET_PLUGIN_unload(libname, api);
151
152   while (NULL != head)
153     {
154       w = head;
155       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
156                  "Plugin did not remove address `%s'\n",
157                  w->addrstring);
158       GNUNET_CONTAINER_DLL_remove(head, tail, w);
159       c++;
160       GNUNET_HELLO_address_free(w->address);
161       GNUNET_free(w->addrstring);
162       GNUNET_free(w);
163     }
164   if (c > 0)
165     {
166       GNUNET_break(0);
167       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Plugin did not remove %u addresses \n",
168                  c);
169       ok = 1;
170     }
171
172   GNUNET_free(libname);
173   libname = NULL;
174   GNUNET_STATISTICS_destroy(stats, GNUNET_NO);
175   stats = NULL;
176
177   if (NULL != suid_helper)
178     {
179       GNUNET_HELPER_stop(suid_helper, GNUNET_NO);
180       suid_helper = NULL;
181     }
182 }
183
184
185 static void
186 end_badly(void *cls)
187 {
188   struct AddressWrapper *w;
189   int c = 0;
190
191   timeout_endbadly = NULL;
192   if (NULL != timeout_wait)
193     {
194       GNUNET_SCHEDULER_cancel(timeout_wait);
195       timeout_wait = NULL;
196     }
197
198   if (pretty_printers_running > 0)
199     {
200       timeout_endbadly = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_SECONDS,
201                                                       &end_badly, &ok);
202       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
203                  "Have pending calls to pretty_printer ... deferring shutdown\n");
204       return;
205     }
206
207   if (NULL != cls)
208     {
209       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
210                  "Test took too long to execute, timeout .... \n");
211     }
212
213   if (NULL != libname)
214     {
215       if (NULL != api)
216         GNUNET_PLUGIN_unload(libname, api);
217       GNUNET_free(libname);
218       libname = NULL;
219     }
220
221   while (NULL != head)
222     {
223       w = head;
224       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Plugin did not remove address `%s'\n",
225                  w->addrstring);
226       GNUNET_CONTAINER_DLL_remove(head, tail, w);
227       c++;
228       GNUNET_HELLO_address_free(w->address);
229       if (NULL != w->test_task)
230         GNUNET_SCHEDULER_cancel(w->test_task);
231       GNUNET_free(w->addrstring);
232       GNUNET_free(w);
233     }
234   if (c > 0)
235     {
236       GNUNET_break(0);
237       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Plugin did not remove %u addresses\n",
238                  c);
239     }
240
241   if (NULL != stats)
242     {
243       GNUNET_STATISTICS_destroy(stats, GNUNET_NO);
244       stats = NULL;
245     }
246
247   if (NULL != suid_helper)
248     {
249       GNUNET_HELPER_stop(suid_helper, GNUNET_NO);
250       suid_helper = NULL;
251     }
252
253   ok = 1;
254 }
255
256 static void
257 wait_end(void *cls)
258 {
259   timeout_wait = NULL;
260   if (0 == addresses_reported)
261     GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
262                "Plugin did not report any addresses, could not check address conversion functions\n");
263   end();
264 }
265
266
267 static void
268 end_badly_now()
269 {
270   if (NULL != timeout_wait)
271     {
272       GNUNET_SCHEDULER_cancel(timeout_wait);
273       timeout_wait = NULL;
274     }
275   if (NULL != timeout_endbadly)
276     {
277       GNUNET_SCHEDULER_cancel(timeout_endbadly);
278       timeout_endbadly = NULL;
279     }
280   timeout_endbadly = GNUNET_SCHEDULER_add_now(&end_badly, NULL);
281 }
282
283
284 static struct GNUNET_TIME_Relative
285 env_receive(void *cls,
286             const struct GNUNET_HELLO_Address *address,
287             struct GNUNET_ATS_Session *session,
288             const struct GNUNET_MessageHeader *message)
289 {
290   /* do nothing */
291   return GNUNET_TIME_relative_get_zero_();
292 }
293
294 static int got_reply;
295
296
297 /**
298  * Take the given address and append it to the set of results sent back to
299  * the client.
300  *
301  * @param cls closure
302  * @param address the address to print
303  * @param res result code
304  */
305 static void
306 address_pretty_printer_cb(void *cls, const char *address, int res)
307 {
308   if (NULL != address)
309     {
310       got_reply = GNUNET_YES;
311       GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Pretty address : `%s'\n", address);
312       pretty_printers_running--;
313     }
314   else
315     {
316       if (GNUNET_NO == got_reply)
317         {
318           pretty_printers_running--;
319           GNUNET_break(0);
320           end_badly_now();
321         }
322     }
323 }
324
325
326 static void
327 test_addr_string(void *cls)
328 {
329   struct AddressWrapper *w = cls;
330   void *s2a;
331   size_t s2a_len;
332
333   w->test_task = NULL;
334
335   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
336              "Testing: address_to_string \n");
337   w->addrstring = GNUNET_strdup(api->address_to_string(api,
338                                                        w->address->address,
339                                                        w->address->address_length));
340   if (NULL == w->addrstring)
341     {
342       GNUNET_break(0);
343       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
344                  "Plugin cannot convert address to string!\n");
345       end_badly_now();
346       return;
347     }
348   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
349              "Plugin added address `%s'\n",
350              w->addrstring);
351   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
352              "Testing address_to_string: OK\n");
353   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
354              "Testing: string_to_address \n");
355   s2a = NULL;
356   s2a_len = 0;
357   if ((GNUNET_OK !=
358        api->string_to_address(api, w->addrstring,
359                               strlen(w->addrstring) + 1,
360                               &s2a, &s2a_len)) ||
361       (NULL == s2a))
362     {
363       GNUNET_break(0);
364       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
365                  "Plugin cannot convert string to address!\n");
366       end_badly_now();
367       return;
368     }
369
370   /*
371      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
372      "Plugin creates `%s' %u\n",api->address_to_string (api, s2a, s2a_len), s2a_len);
373
374      int c1;
375      for (c1 = 0; c1 < s2a_len; c1++ )
376      fprintf (stderr, "%u == %u\n", ((char *) s2a)[c1], ((char *) w->addr)[c1]);
377    */
378   if (s2a_len != w->address->address_length)
379     {
380       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
381                  "Plugin creates different address length when converting address->string->address: %u != %u\n",
382                  (unsigned int)w->address->address_length,
383                  (unsigned int)s2a_len);
384     }
385   else if (0 != memcmp(s2a, w->address->address, s2a_len))
386     {
387       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
388                  "Plugin creates different address length when converting back and forth %i!\n",
389                  memcmp(s2a,
390                         w->address->address,
391                         s2a_len));
392     }
393   else
394     {
395       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
396                  "Testing string_to_address: OK\n");
397     }
398   GNUNET_free(s2a);
399
400   pretty_printers_running++;
401   api->address_pretty_printer(api->cls,
402                               w->address->transport_name,
403                               w->address->address,
404                               w->address->address_length,
405                               GNUNET_YES,
406                               GNUNET_TIME_UNIT_MINUTES,
407                               &address_pretty_printer_cb, w);
408
409   if (GNUNET_OK !=
410       api->check_address(api->cls,
411                          w->address->address,
412                          w->address->address_length))
413     {
414       GNUNET_break(0);
415       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
416                  "Plugin refuses added address!\n");
417       end_badly_now();
418       return;
419     }
420   if (NULL != timeout_wait)
421     {
422       GNUNET_SCHEDULER_cancel(timeout_wait);
423       timeout_wait = NULL;
424     }
425   timeout_wait = GNUNET_SCHEDULER_add_delayed(WAIT, &wait_end, NULL);
426 }
427
428
429 static void
430 env_notify_address(void *cls,
431                    int add_remove,
432                    const struct GNUNET_HELLO_Address *address)
433 {
434   struct AddressWrapper *w;
435   struct AddressWrapper *wtmp;
436
437   if (GNUNET_YES == add_remove)
438     {
439       addresses_reported++;
440       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
441                  "Adding address of length %u\n",
442                  (unsigned int)address->address_length);
443
444       for (wtmp = head; NULL != wtmp; wtmp = wtmp->next)
445         {
446           if ((address->address_length == wtmp->address->address_length) &&
447               (0 == memcmp(address->address, wtmp->address->address, address->address_length)))
448             {
449               GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
450                          "Duplicate address notification .... \n");
451               return;
452             }
453         }
454
455       w = GNUNET_new(struct AddressWrapper);
456       w->address = GNUNET_HELLO_address_copy(address);
457       GNUNET_CONTAINER_DLL_insert(head, tail, w);
458       got_reply = GNUNET_NO;
459       w->test_task = GNUNET_SCHEDULER_add_now(&test_addr_string,
460                                               w);
461       return;
462     }
463
464   if (GNUNET_NO == add_remove)
465     {
466       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
467                  "Removing address of length %u\n",
468                  (unsigned int)address->address_length);
469       w = head;
470       while (NULL != w)
471         {
472           if ((address->address_length == w->address->address_length) &&
473               (0 == memcmp(w->address->address, address->address, address->address_length)))
474             {
475               break;
476             }
477           w = w->next;
478         }
479
480       if (w == NULL)
481         {
482           GNUNET_break(0);
483           GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
484                      "Plugin removes address never added!\n");
485           end_badly_now();
486           return;
487         }
488
489       GNUNET_CONTAINER_DLL_remove(head, tail, w);
490       GNUNET_HELLO_address_free(w->address);
491       GNUNET_free(w->addrstring);
492       GNUNET_free(w);
493       return;
494     }
495   GNUNET_break(0);
496   GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
497              "Invalid operation: %u\n",
498              add_remove);
499   end_badly_now();
500 }
501
502
503 static enum GNUNET_NetworkType
504 env_get_address_type(void *cls,
505                      const struct sockaddr *addr,
506                      size_t addrlen)
507 {
508   return GNUNET_NT_LOOPBACK;
509 }
510
511
512 static const struct GNUNET_MessageHeader *
513 env_get_our_hello()
514 {
515   return (const struct GNUNET_MessageHeader *)hello;
516 }
517
518
519 static void
520 env_session_end(void *cls,
521                 const struct GNUNET_HELLO_Address *address,
522                 struct GNUNET_ATS_Session *session)
523 {
524 }
525
526
527 static void
528 env_update_distance(void *cls,
529                     const struct GNUNET_HELLO_Address *address,
530                     uint32_t distance)
531 {
532 }
533
534
535 static void
536 setup_plugin_environment()
537 {
538   env.cfg = cfg;
539   env.cls = &env;
540   env.my_identity = &my_identity;
541   env.max_connections = max_connect_per_transport;
542   env.stats = stats;
543   env.receive = &env_receive;
544   env.notify_address = &env_notify_address;
545   env.get_address_type = &env_get_address_type;
546   env.update_address_distance = &env_update_distance;
547   env.get_our_hello = &env_get_our_hello;
548   env.session_end = &env_session_end;
549 }
550
551
552 static int
553 handle_helper_message(void *cls,
554                       const struct GNUNET_MessageHeader *hdr)
555 {
556   return GNUNET_OK;
557 }
558
559
560 /**
561  * Runs the test.
562  *
563  * @param cls closure
564  * @param c configuration to use
565  */
566 static void
567 run(void *cls,
568     char * const *args,
569     const char *cfgfile,
570     const struct GNUNET_CONFIGURATION_Handle *c)
571 {
572   char * const *argv = cls;
573   unsigned long long tneigh;
574   char *keyfile;
575   char *plugin;
576   char *sep;
577
578   timeout_endbadly = GNUNET_SCHEDULER_add_delayed(TIMEOUT,
579                                                   &end_badly,
580                                                   &ok);
581   cfg = c;
582   /* parse configuration */
583   if ((GNUNET_OK !=
584        GNUNET_CONFIGURATION_get_value_number(c,
585                                              "TRANSPORT",
586                                              "NEIGHBOUR_LIMIT",
587                                              &tneigh)) ||
588       (GNUNET_OK !=
589        GNUNET_CONFIGURATION_get_value_filename(c,
590                                                "PEER",
591                                                "PRIVATE_KEY",
592                                                &keyfile)))
593     {
594       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
595                  "Transport service is lacking key configuration settings.  Exiting.\n");
596       return;
597     }
598
599   if (NULL == (stats = GNUNET_STATISTICS_create("transport",
600                                                 cfg)))
601     {
602       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
603                  "Could not create statistics.  Exiting.\n");
604       GNUNET_free(keyfile);
605       end_badly_now();
606       return;
607     }
608
609   if (GNUNET_OK != GNUNET_DISK_file_test(HOSTKEY_FILE))
610     {
611       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
612                  "Hostkey `%s' missing.  Exiting.\n",
613                  HOSTKEY_FILE);
614       GNUNET_free(keyfile);
615       end_badly_now();
616       return;
617     }
618
619   if (GNUNET_OK !=
620       GNUNET_DISK_directory_create_for_file(keyfile))
621     {
622       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
623                  "Could not create a directory for hostkey `%s'.  Exiting.\n",
624                  keyfile);
625       GNUNET_free(keyfile);
626       end_badly_now();
627       return;
628     }
629
630   if (GNUNET_OK !=
631       GNUNET_DISK_file_copy(HOSTKEY_FILE,
632                             keyfile))
633     {
634       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
635                  "Could not copy hostkey `%s' to destination `%s'.  Exiting.\n",
636                  HOSTKEY_FILE,
637                  keyfile);
638       GNUNET_free(keyfile);
639       end_badly_now();
640       return;
641     }
642
643   max_connect_per_transport = (uint32_t)tneigh;
644   my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_file(keyfile);
645   GNUNET_free(keyfile);
646   if (NULL == my_private_key)
647     {
648       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
649                  "Could not access hostkey.  Exiting.\n");
650       end_badly_now();
651       return;
652     }
653   GNUNET_CRYPTO_eddsa_key_get_public(my_private_key, &my_identity.public_key);
654
655   hello = GNUNET_HELLO_create(&my_identity.public_key, NULL, NULL, GNUNET_NO);
656
657   /* load plugins... */
658   setup_plugin_environment();
659
660   GNUNET_assert(strlen(argv[0]) > strlen("test_plugin_"));
661   plugin = strstr(argv[0], "test_plugin_");
662   sep = strrchr(argv[0], '.');
663   if (NULL == plugin)
664     {
665       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Not a valid test name\n");
666       end_badly_now();
667       return;
668     }
669   plugin += strlen("test_plugin_");
670   if (NULL != sep)
671     sep[0] = '\0';
672
673   /* Hack for WLAN: start a second helper */
674   if (0 == strcmp(plugin, "wlan"))
675     {
676       char * helper_argv[3];
677       helper_argv[0] = (char *)"gnunet-helper-transport-wlan-dummy";
678       helper_argv[1] = (char *)"2";
679       helper_argv[2] = NULL;
680       suid_helper = GNUNET_HELPER_start(GNUNET_NO,
681                                         "gnunet-helper-transport-wlan-dummy", helper_argv,
682                                         &handle_helper_message, NULL, NULL);
683     }
684
685   /* Loading plugin */
686   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Loading transport plugin %s\n", plugin);
687   GNUNET_asprintf(&libname, "libgnunet_plugin_transport_%s", plugin);
688   api = GNUNET_PLUGIN_load(libname, &env);
689   if (NULL == api)
690     {
691       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
692                  "Failed to load transport plugin for %s\n", plugin);
693       end_badly_now();
694       return;
695     }
696
697   timeout_wait = GNUNET_SCHEDULER_add_delayed(WAIT, &wait_end, NULL);
698
699   /* Check if all functions are implemented */
700   if (NULL == api->address_pretty_printer)
701     {
702       GNUNET_break(0);
703       end_badly_now();
704       return;
705     }
706   if (NULL == api->address_to_string)
707     {
708       GNUNET_break(0);
709       end_badly_now();
710       return;
711     }
712   GNUNET_assert(NULL != api->check_address);
713   if (NULL == api->check_address)
714     {
715       GNUNET_break(0);
716       end_badly_now();
717       return;
718     }
719   GNUNET_assert(NULL != api->disconnect_peer);
720   if (NULL == api->disconnect_peer)
721     {
722       GNUNET_break(0);
723       end_badly_now();
724       return;
725     }
726   GNUNET_assert(NULL != api->get_session);
727   if (NULL == api->get_session)
728     {
729       GNUNET_break(0);
730       end_badly_now();
731       return;
732     }
733   if (NULL == api->address_pretty_printer)
734     {
735       GNUNET_break(0);
736       end_badly_now();
737       return;
738     }
739   if (NULL == api->string_to_address)
740     {
741       GNUNET_break(0);
742       end_badly_now();
743       return;
744     }
745 }
746
747
748 /**
749  * The main function for the test
750  *
751  * @param argc number of arguments from the command line
752  * @param argv command line arguments
753  * @return 0 ok, 1 on error
754  */
755 int
756 main(int argc,
757      char * const *argv)
758 {
759   static struct GNUNET_GETOPT_CommandLineOption options[] = {
760     GNUNET_GETOPT_OPTION_END
761   };
762   int ret;
763   char * const argv_prog[] = {
764     "test_plugin_transport",
765     "-c",
766     "test_plugin_transport_data.conf",
767     NULL
768   };
769
770   GNUNET_log_setup("test-plugin-transport",
771                    "WARNING",
772                    NULL);
773   GNUNET_DISK_purge_cfg_dir("test_plugin_transport_data.conf",
774                             "GNUNET_TEST_HOME");
775   ok = 1; /* set to fail */
776   ret =
777     (GNUNET_OK
778      == GNUNET_PROGRAM_run(3, argv_prog, "test-plugin-transport",
779                            "testcase", options, &run, (void *)argv)) ? ok : 1;
780   GNUNET_DISK_purge_cfg_dir("test_plugin_transport_data.conf",
781                             "GNUNET_TEST_HOME");
782   return ret;
783 }
784
785 /* end of test_plugin_transport.c */