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