fix div by zero
[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 /**
19  * @file transport/test_plugin_transport.c
20  * @brief testcase for transport_api.c
21  * @author Sailor Siraj
22  * @author Christian Grothoff
23  */
24
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "gnunet_hello_lib.h"
28 #include "gnunet_peerinfo_service.h"
29 #include "gnunet_statistics_service.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_transport_plugin.h"
32 #include "transport.h"
33
34 /**
35  * How long until we give up on transmitting the message?
36  */
37 #define WAIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
38 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
39
40 #define HOSTKEY_FILE "test_plugin_hostkey.ecc"
41
42 /**
43  * Our public key.
44  */
45 static struct GNUNET_PeerIdentity my_identity;
46
47 /**
48  * Our private key.
49  */
50 static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
51
52 /**
53  * Our configuration.
54  */
55 const struct GNUNET_CONFIGURATION_Handle *cfg;
56
57 /**
58  * Our configuration.
59  */
60 struct GNUNET_STATISTICS_Handle *stats;
61
62 /**
63  * Our HELLO
64  */
65 struct GNUNET_HELLO_Message *hello;
66
67 /**
68  * Number of neighbours we'd like to have.
69  */
70 static uint32_t max_connect_per_transport;
71
72 /**
73  * Environment for this plugin.
74  */
75 struct GNUNET_TRANSPORT_PluginEnvironment env;
76
77 /**
78  *handle for the api provided by this plugin
79  */
80 struct GNUNET_TRANSPORT_PluginFunctions *api;
81
82 /**
83  * Helper handler
84  */
85 struct GNUNET_HELPER_Handle *suid_helper;
86
87 /**
88  * Timeout task
89  */
90 static struct GNUNET_SCHEDULER_Task * timeout_endbadly;
91
92 /**
93  * Timeout task
94  */
95 static struct GNUNET_SCHEDULER_Task * timeout_wait;
96
97 /**
98  * Library name
99  */
100 static char *libname;
101
102 /**
103  * Plugin addresses head
104  */
105 struct AddressWrapper *head;
106
107 /**
108  * Plugin addresses tail
109  */
110 struct AddressWrapper *tail;
111
112 unsigned int addresses_reported;
113
114 unsigned int pretty_printers_running;
115
116 /**
117  * Did the test pass or fail?
118  */
119 static int ok;
120
121 struct AddressWrapper
122 {
123   struct AddressWrapper *next;
124
125   struct AddressWrapper *prev;
126
127   struct GNUNET_HELLO_Address *address;
128
129   char *addrstring;
130
131   struct GNUNET_SCHEDULER_Task *test_task;
132
133 };
134
135
136 static void
137 end ()
138 {
139   struct AddressWrapper *w;
140   int c = 0;
141   ok = 0;
142
143   if (NULL != timeout_endbadly)
144   {
145     GNUNET_SCHEDULER_cancel (timeout_endbadly);
146     timeout_endbadly = NULL;
147   }
148   if (NULL != api)
149     GNUNET_PLUGIN_unload (libname, api);
150
151   while (NULL != head)
152   {
153     w = head;
154     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
155                 "Plugin did not remove address `%s'\n",
156                 w->addrstring);
157     GNUNET_CONTAINER_DLL_remove(head, tail, w);
158     c++;
159     GNUNET_HELLO_address_free(w->address);
160     GNUNET_free(w->addrstring);
161     GNUNET_free(w);
162   }
163   if (c > 0)
164   {
165     GNUNET_break(0);
166     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Plugin did not remove %u addresses \n",
167         c);
168     ok = 1;
169   }
170
171   GNUNET_free(libname);
172   libname = NULL;
173   GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
174   stats = NULL;
175
176   if (NULL != suid_helper)
177   {
178     GNUNET_HELPER_stop (suid_helper, GNUNET_NO);
179     suid_helper = NULL;
180   }
181 }
182
183
184 static void
185 end_badly (void *cls)
186 {
187   struct AddressWrapper *w;
188   int c = 0;
189
190   timeout_endbadly = NULL;
191   if (NULL != timeout_wait)
192   {
193     GNUNET_SCHEDULER_cancel (timeout_wait);
194     timeout_wait = NULL;
195   }
196
197   if (pretty_printers_running > 0)
198   {
199     timeout_endbadly = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
200         &end_badly, &ok);
201     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
202         "Have pending calls to pretty_printer ... deferring shutdown\n");
203     return;
204   }
205
206   if (NULL != cls)
207   {
208     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
209         "Test took too long to execute, timeout .... \n");
210   }
211
212   if (NULL != libname)
213   {
214     if (NULL != api)
215       GNUNET_PLUGIN_unload (libname, api);
216     GNUNET_free(libname);
217     libname = NULL;
218   }
219
220   while (NULL != head)
221   {
222     w = head;
223     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Plugin did not remove address `%s'\n",
224         w->addrstring);
225     GNUNET_CONTAINER_DLL_remove(head, tail, w);
226     c++;
227     GNUNET_HELLO_address_free(w->address);
228     if (NULL != w->test_task)
229       GNUNET_SCHEDULER_cancel (w->test_task);
230     GNUNET_free(w->addrstring);
231     GNUNET_free(w);
232   }
233   if (c > 0)
234   {
235     GNUNET_break(0);
236     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Plugin did not remove %u addresses\n",
237         c);
238   }
239
240   if (NULL != stats)
241   {
242     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
243     stats = NULL;
244   }
245
246   if (NULL != suid_helper)
247   {
248     GNUNET_HELPER_stop (suid_helper, GNUNET_NO);
249     suid_helper = NULL;
250   }
251
252   ok = 1;
253 }
254
255 static void
256 wait_end (void *cls)
257 {
258   timeout_wait = NULL;
259   if (0 == addresses_reported)
260     GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
261         "Plugin did not report any addresses, could not check address conversion functions\n");
262   end ();
263 }
264
265
266 static void
267 end_badly_now ()
268 {
269   if (NULL != timeout_wait)
270   {
271     GNUNET_SCHEDULER_cancel (timeout_wait);
272     timeout_wait = NULL;
273   }
274   if (NULL != timeout_endbadly)
275   {
276     GNUNET_SCHEDULER_cancel (timeout_endbadly);
277     timeout_endbadly = NULL;
278   }
279   timeout_endbadly = GNUNET_SCHEDULER_add_now (&end_badly, NULL );
280 }
281
282
283 static struct GNUNET_TIME_Relative
284 env_receive (void *cls,
285              const struct GNUNET_HELLO_Address *address,
286              struct GNUNET_ATS_Session *session,
287              const struct GNUNET_MessageHeader *message)
288 {
289   /* do nothing */
290   return GNUNET_TIME_relative_get_zero_ ();
291 }
292
293 static int got_reply;
294
295
296 /**
297  * Take the given address and append it to the set of results sent back to
298  * the client.
299  *
300  * @param cls closure
301  * @param address the address to print
302  * @param res result code
303  */
304 static void
305 address_pretty_printer_cb (void *cls, const char *address, int res)
306 {
307   if (NULL != address)
308   {
309     got_reply = GNUNET_YES;
310     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Pretty address : `%s'\n", address);
311     pretty_printers_running--;
312   }
313   else
314   {
315     if (GNUNET_NO == got_reply)
316     {
317       pretty_printers_running--;
318       GNUNET_break(0);
319       end_badly_now ();
320     }
321   }
322 }
323
324
325 static void
326 test_addr_string (void *cls)
327 {
328   struct AddressWrapper *w = cls;
329   void *s2a;
330   size_t s2a_len;
331
332   w->test_task = NULL;
333
334   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
335               "Testing: address_to_string \n");
336   w->addrstring = GNUNET_strdup (api->address_to_string (api,
337                                                          w->address->address,
338                                                          w->address->address_length));
339   if (NULL == w->addrstring)
340   {
341     GNUNET_break(0);
342     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
343                 "Plugin cannot convert address to string!\n");
344     end_badly_now ();
345     return;
346   }
347   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
348               "Plugin added address `%s'\n",
349               w->addrstring);
350   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
351               "Testing address_to_string: OK\n");
352   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
353               "Testing: string_to_address \n");
354   s2a = NULL;
355   s2a_len = 0;
356   if ((GNUNET_OK !=
357        api->string_to_address (api, w->addrstring,
358                                strlen (w->addrstring) + 1,
359                                &s2a, &s2a_len)) ||
360       (NULL == s2a))
361   {
362     GNUNET_break(0);
363     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
364                "Plugin cannot convert string to address!\n");
365     end_badly_now ();
366     return;
367   }
368
369   /*
370     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
371     "Plugin creates `%s' %u\n",api->address_to_string (api, s2a, s2a_len), s2a_len);
372
373     int c1;
374     for (c1 = 0; c1 < s2a_len; c1++ )
375     fprintf (stderr, "%u == %u\n", ((char *) s2a)[c1], ((char *) w->addr)[c1]);
376   */
377   if (s2a_len != w->address->address_length)
378   {
379     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
380                "Plugin creates different address length when converting address->string->address: %u != %u\n",
381                (unsigned int) w->address->address_length,
382                (unsigned int) s2a_len);
383   }
384   else if (0 != memcmp (s2a, w->address->address, s2a_len))
385   {
386     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
387                 "Plugin creates different address length when converting back and forth %i!\n",
388                 memcmp (s2a,
389                         w->address->address,
390                         s2a_len));
391   }
392   else
393   {
394     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
395                 "Testing string_to_address: OK\n");
396   }
397   GNUNET_free(s2a);
398
399   pretty_printers_running++;
400   api->address_pretty_printer (api->cls,
401                                w->address->transport_name,
402                                w->address->address,
403                                w->address->address_length,
404                                GNUNET_YES,
405                                GNUNET_TIME_UNIT_MINUTES,
406                                &address_pretty_printer_cb, w);
407
408   if (GNUNET_OK !=
409       api->check_address (api->cls,
410                           w->address->address,
411                           w->address->address_length))
412   {
413     GNUNET_break (0);
414     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
415                 "Plugin refuses added address!\n");
416     end_badly_now ();
417     return;
418   }
419   if (NULL != timeout_wait)
420   {
421     GNUNET_SCHEDULER_cancel (timeout_wait);
422     timeout_wait = NULL;
423   }
424   timeout_wait = GNUNET_SCHEDULER_add_delayed (WAIT, &wait_end, NULL);
425 }
426
427
428 static void
429 env_notify_address (void *cls,
430                     int add_remove,
431                     const struct GNUNET_HELLO_Address *address)
432 {
433   struct AddressWrapper *w;
434   struct AddressWrapper *wtmp;
435
436   if (GNUNET_YES == add_remove)
437   {
438     addresses_reported++;
439     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
440                "Adding address of length %u\n",
441                 (unsigned int) address->address_length);
442
443     for (wtmp = head; NULL != wtmp; wtmp = wtmp->next)
444     {
445       if ((address->address_length == wtmp->address->address_length) &&
446           (0 == memcmp (address->address, wtmp->address->address, address->address_length)))
447       {
448         GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
449                    "Duplicate address notification .... \n");
450         return;
451       }
452     }
453
454     w = GNUNET_new (struct AddressWrapper);
455     w->address = GNUNET_HELLO_address_copy (address);
456     GNUNET_CONTAINER_DLL_insert (head, tail, w);
457     got_reply = GNUNET_NO;
458     w->test_task = GNUNET_SCHEDULER_add_now (&test_addr_string,
459                                              w);
460     return;
461   }
462
463   if (GNUNET_NO == add_remove)
464   {
465     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
466                 "Removing address of length %u\n",
467                 (unsigned int) address->address_length);
468     w = head;
469     while (NULL != w)
470     {
471       if ((address->address_length == w->address->address_length) &&
472           (0 == memcmp (w->address->address, address->address, address->address_length)))
473       {
474         break;
475       }
476       w = w->next;
477     }
478
479     if (w == NULL)
480     {
481       GNUNET_break(0);
482       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
483           "Plugin removes address never added!\n");
484       end_badly_now ();
485       return;
486     }
487
488     GNUNET_CONTAINER_DLL_remove(head, tail, w);
489     GNUNET_HELLO_address_free (w->address);
490     GNUNET_free(w->addrstring);
491     GNUNET_free(w);
492     return;
493   }
494   GNUNET_break(0);
495   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
496               "Invalid operation: %u\n",
497               add_remove);
498   end_badly_now ();
499 }
500
501
502 static enum GNUNET_ATS_Network_Type
503 env_get_address_type (void *cls,
504                       const struct sockaddr *addr,
505                       size_t addrlen)
506 {
507   return GNUNET_ATS_NET_LOOPBACK;
508 }
509
510
511 static const struct GNUNET_MessageHeader *
512 env_get_our_hello ()
513 {
514   return (const struct GNUNET_MessageHeader *) hello;
515 }
516
517
518 static void
519 env_session_end (void *cls,
520                  const struct GNUNET_HELLO_Address *address,
521                  struct GNUNET_ATS_Session *session)
522 {
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 */