Check that you are not present in trail twice
[oweals/gnunet.git] / src / transport / test_plugin_transport.c
1 /*
2  This file is part of GNUnet.
3  (C) 2009 Christian Grothoff (and other contributing authors)
4
5  GNUnet is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published
7  by the Free Software Foundation; either version 3, or (at your
8  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  General Public License for more details.
14
15  You should have received a copy of the GNU General Public License
16  along with GNUnet; see the file COPYING.  If not, write to the
17  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  Boston, MA 02111-1307, USA.
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 GNUNET_SCHEDULER_TaskIdentifier timeout_endbadly;
93
94 /**
95  * Timeout task
96  */
97 static GNUNET_SCHEDULER_TaskIdentifier 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 {
125   struct AddressWrapper *next;
126
127   struct AddressWrapper *prev;
128
129   struct GNUNET_HELLO_Address *address;
130
131   char *addrstring;
132 };
133
134 static void
135 end ()
136 {
137   struct AddressWrapper *w;
138   int c = 0;
139   ok = 0;
140
141   if (GNUNET_SCHEDULER_NO_TASK != timeout_endbadly)
142   {
143     GNUNET_SCHEDULER_cancel (timeout_endbadly);
144     timeout_endbadly = GNUNET_SCHEDULER_NO_TASK;
145   }
146   if (NULL != api)
147     GNUNET_PLUGIN_unload (libname, api);
148
149   while (NULL != head)
150   {
151     w = head;
152     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "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 static void
181 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
182 {
183   struct AddressWrapper *w;
184   int c = 0;
185
186   timeout_endbadly = GNUNET_SCHEDULER_NO_TASK;
187   if (GNUNET_SCHEDULER_NO_TASK != timeout_wait)
188   {
189     GNUNET_SCHEDULER_cancel (timeout_wait);
190     timeout_wait = GNUNET_SCHEDULER_NO_TASK;
191   }
192
193   if (pretty_printers_running > 0)
194   {
195     timeout_endbadly = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
196         &end_badly, &ok);
197     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
198         "Have pending calls to pretty_printer ... deferring shutdown\n");
199     return;
200   }
201
202   if (NULL != cls)
203   {
204     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
205         "Test took too long to execute, timeout .... \n");
206   }
207
208   if (NULL != libname)
209   {
210     if (NULL != api)
211       GNUNET_PLUGIN_unload (libname, api);
212     GNUNET_free(libname);
213     libname = NULL;
214   }
215
216   while (NULL != head)
217   {
218     w = head;
219     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Plugin did not remove address `%s'\n",
220         w->addrstring);
221     GNUNET_CONTAINER_DLL_remove(head, tail, w);
222     c++;
223     GNUNET_HELLO_address_free(w->address);
224     GNUNET_free(w->addrstring);
225     GNUNET_free(w);
226   }
227   if (c > 0)
228   {
229     GNUNET_break(0);
230     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Plugin did not remove %u addresses\n",
231         c);
232   }
233
234   if (NULL != stats)
235   {
236     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
237     stats = NULL;
238   }
239
240   if (NULL != suid_helper)
241   {
242     GNUNET_HELPER_stop (suid_helper, GNUNET_NO);
243     suid_helper = NULL;
244   }
245
246   ok = 1;
247 }
248
249 static void
250 wait_end (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
251 {
252   timeout_wait = GNUNET_SCHEDULER_NO_TASK;
253   if (0 == addresses_reported)
254     GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
255         "Plugin did not report any addresses, could not check address conversion functions\n");
256   end ();
257 }
258
259
260 static void
261 end_badly_now ()
262 {
263   if (GNUNET_SCHEDULER_NO_TASK != timeout_wait)
264   {
265     GNUNET_SCHEDULER_cancel (timeout_wait);
266     timeout_wait = GNUNET_SCHEDULER_NO_TASK;
267   }
268   if (GNUNET_SCHEDULER_NO_TASK != timeout_endbadly)
269   {
270     GNUNET_SCHEDULER_cancel (timeout_endbadly);
271     timeout_endbadly = GNUNET_SCHEDULER_NO_TASK;
272   }
273   timeout_endbadly = GNUNET_SCHEDULER_add_now (&end_badly, NULL );
274 }
275
276
277 static struct GNUNET_TIME_Relative
278 env_receive (void *cls,
279              const struct GNUNET_HELLO_Address *address,
280              struct Session *session,
281              const struct GNUNET_MessageHeader *message)
282 {
283   /* do nothing */
284   return GNUNET_TIME_relative_get_zero_ ();
285 }
286
287 static int got_reply;
288
289
290 /**
291  * Take the given address and append it to the set of results sent back to
292  * the client.
293  *
294  * @param cls closure
295  * @param address the address to print
296  * @param res result code
297  */
298 static void
299 address_pretty_printer_cb (void *cls, const char *address, int res)
300 {
301   if (NULL != address)
302   {
303     got_reply = GNUNET_YES;
304     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Pretty address : `%s'\n", address);
305     pretty_printers_running--;
306   }
307   else
308   {
309     if (GNUNET_NO == got_reply)
310     {
311       pretty_printers_running--;
312       GNUNET_break(0);
313       end_badly_now ();
314     }
315   }
316 }
317
318
319 static void
320 env_notify_address (void *cls,
321                     int add_remove,
322                     const struct GNUNET_HELLO_Address *address)
323 {
324   struct AddressWrapper *w;
325   struct AddressWrapper *wtmp;
326   void *s2a;
327   size_t s2a_len;
328
329   if (GNUNET_YES == add_remove)
330   {
331     addresses_reported++;
332     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Adding address of length %u\n",
333         address->address_length);
334
335     for (wtmp = head; NULL != wtmp; wtmp = wtmp->next)
336     {
337       if ((address->address_length == wtmp->address->address_length) &&
338           (0 == memcmp (address->address, wtmp->address->address, address->address_length)))
339       {
340         GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
341             "Duplicate address notification .... \n");
342         return;
343       }
344     }
345
346     w = GNUNET_new (struct AddressWrapper);
347     w->address = GNUNET_HELLO_address_copy (address);
348     GNUNET_CONTAINER_DLL_insert(head, tail, w);
349     got_reply = GNUNET_NO;
350     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Testing: address_to_string \n");
351     w->addrstring = strdup (
352         api->address_to_string (api, w->address->address,
353             w->address->address_length));
354     if (NULL == w->addrstring)
355     {
356       GNUNET_break(0);
357       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
358           "Plugin cannot convert address to string!\n");
359       end_badly_now ();
360       return;
361     }
362     else
363     {
364       GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Plugin added address `%s'\n",
365           w->addrstring);
366       GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Testing address_to_string: OK\n");
367     }
368
369     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Testing: string_to_address \n");
370     s2a = NULL;
371     s2a_len = 0;
372     if ((GNUNET_OK
373         != api->string_to_address (api, w->addrstring,
374             strlen (w->addrstring) + 1, &s2a, &s2a_len)) || (NULL == s2a))
375     {
376       GNUNET_break(0);
377       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
378           "Plugin cannot convert string to address!\n");
379       end_badly_now ();
380       return;
381     }
382
383     /*
384      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
385      "Plugin creates `%s' %u\n",api->address_to_string (api, s2a, s2a_len), s2a_len);
386
387      int c1;
388      for (c1 = 0; c1 < s2a_len; c1++ )
389      fprintf (stderr, "%u == %u\n", ((char *) s2a)[c1], ((char *) w->addr)[c1]);
390      */
391     if (s2a_len != w->address->address_length)
392     {
393       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
394           "Plugin creates different address length when converting address->string->address: %u != %u\n",
395           w->address->address_length, s2a_len);
396     }
397     else if (0 != memcmp (s2a, w->address->address, s2a_len))
398     {
399       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
400           "Plugin creates different address length when converting back and forth %i!\n",
401           memcmp (s2a, w->address->address, s2a_len));
402     }
403     else
404     {
405       GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Testing string_to_address: OK\n");
406     }
407     GNUNET_free(s2a);
408
409     pretty_printers_running++;
410     api->address_pretty_printer (api->cls, address->transport_name,
411         address->address, address->address_length, GNUNET_YES,
412         GNUNET_TIME_UNIT_MINUTES, &address_pretty_printer_cb, w);
413
414     if (GNUNET_OK != api->check_address (api->cls, w->address->address, w->address->address_length))
415     {
416       GNUNET_break(0);
417       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Plugin refuses added address!\n");
418       end_badly_now ();
419       return;
420     }
421     if (GNUNET_SCHEDULER_NO_TASK != timeout_wait)
422     {
423       GNUNET_SCHEDULER_cancel (timeout_wait);
424       timeout_wait = GNUNET_SCHEDULER_NO_TASK;
425     }
426
427     timeout_wait = GNUNET_SCHEDULER_add_delayed (WAIT, &wait_end, NULL );
428
429   }
430   else if (GNUNET_NO == add_remove)
431   {
432     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Removing address of length %u\n",
433         address->address_length);
434
435     w = head;
436     while (NULL != w)
437     {
438       if ((address->address_length == w->address->address_length) &&
439           (0 == memcmp (w->address->address, address->address, address->address_length)))
440       {
441         break;
442       }
443       w = w->next;
444     }
445
446     if (w == NULL )
447     {
448       GNUNET_break(0);
449       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
450           "Plugin removes address never added!\n");
451       end_badly_now ();
452       return;
453     }
454
455     GNUNET_CONTAINER_DLL_remove(head, tail, w);
456     GNUNET_HELLO_address_free (w->address);
457     GNUNET_free(w->addrstring);
458     GNUNET_free(w);
459   }
460   else
461   {
462     GNUNET_break(0);
463     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Invalid operation: %u\n", add_remove);
464     end_badly_now ();
465     return;
466   }
467 }
468
469
470 static struct GNUNET_ATS_Information
471 env_get_address_type (void *cls, const struct sockaddr *addr, size_t addrlen)
472 {
473   struct GNUNET_ATS_Information ats;
474   ats.type = htonl (GNUNET_ATS_NETWORK_TYPE);
475   ats.value = htonl (GNUNET_ATS_NET_LOOPBACK);
476   return ats;
477 }
478
479
480 static const struct GNUNET_MessageHeader *
481 env_get_our_hello ()
482 {
483   return (const struct GNUNET_MessageHeader *) hello;
484 }
485
486
487 static void
488 env_session_end (void *cls,
489                  const struct GNUNET_HELLO_Address *address,
490                  struct Session *session)
491 {
492
493 }
494
495
496 static void
497 env_update_metrics (void *cls,
498                     const struct GNUNET_HELLO_Address *address,
499                     struct Session *session,
500                     const struct GNUNET_ATS_Information *ats,
501                     uint32_t ats_count)
502 {
503 }
504
505
506 static void
507 setup_plugin_environment ()
508 {
509   env.cfg = cfg;
510   env.cls = &env;
511   env.my_identity = &my_identity;
512   env.max_connections = max_connect_per_transport;
513   env.stats = stats;
514   env.receive = &env_receive;
515   env.notify_address = &env_notify_address;
516   env.get_address_type = &env_get_address_type;
517   env.update_address_metrics = &env_update_metrics;
518   env.get_our_hello = &env_get_our_hello;
519   env.session_end = &env_session_end;
520 }
521
522
523 static int
524 handle_helper_message (void *cls, void *client,
525                        const struct GNUNET_MessageHeader *hdr)
526 {
527   return GNUNET_OK;
528 }
529
530
531 /**
532  * Runs the test.
533  *
534  * @param cls closure
535  * @param c configuration to use
536  */
537 static void
538 run (void *cls, char * const *args, const char *cfgfile,
539     const struct GNUNET_CONFIGURATION_Handle *c)
540 {
541   char * const *argv = cls;
542   unsigned long long tneigh;
543   char *keyfile;
544   char *plugin;
545   char *sep;
546
547   timeout_endbadly = GNUNET_SCHEDULER_add_delayed (TIMEOUT, end_badly, &ok);
548
549   cfg = c;
550   /* parse configuration */
551   if ((GNUNET_OK
552       != GNUNET_CONFIGURATION_get_value_number (c, "TRANSPORT",
553           "NEIGHBOUR_LIMIT", &tneigh))
554       || (GNUNET_OK
555           != GNUNET_CONFIGURATION_get_value_filename (c, "PEER", "PRIVATE_KEY",
556               &keyfile)))
557   {
558     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
559         "Transport service is lacking key configuration settings.  Exiting.\n");
560     return;
561   }
562
563   if (NULL == (stats = GNUNET_STATISTICS_create ("transport", cfg)))
564   {
565     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
566         "Could not create statistics.  Exiting.\n");
567     GNUNET_free(keyfile);
568     end_badly_now ();
569     return;
570   }
571
572   if (GNUNET_OK != GNUNET_DISK_file_test (HOSTKEY_FILE))
573   {
574     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Hostkey `%s' missing.  Exiting.\n",
575         HOSTKEY_FILE);
576     GNUNET_free(keyfile);
577     end_badly_now ();
578     return;
579   }
580
581   if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (keyfile))
582   {
583     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
584         "Could not create a directory for hostkey `%s'.  Exiting.\n", keyfile);
585     GNUNET_free(keyfile);
586     end_badly_now ();
587     return;
588   }
589
590   if (GNUNET_OK != GNUNET_DISK_file_copy (HOSTKEY_FILE, keyfile))
591   {
592     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
593         "Could not copy hostkey `%s' to destination `%s'.  Exiting.\n",
594         HOSTKEY_FILE, keyfile);
595     GNUNET_free(keyfile);
596     end_badly_now ();
597     return;
598   }
599
600   max_connect_per_transport = (uint32_t) tneigh;
601   my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile);
602   GNUNET_free(keyfile);
603   if (NULL == my_private_key)
604   {
605     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
606         "Could not access hostkey.  Exiting.\n");
607     end_badly_now ();
608     return;
609   }
610   GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key);
611
612   hello = GNUNET_HELLO_create (&my_identity.public_key, NULL, NULL, GNUNET_NO);
613
614   /* load plugins... */
615   setup_plugin_environment ();
616
617   GNUNET_assert(strlen (argv[0]) > strlen ("test_plugin_"));
618   plugin = strstr (argv[0], "test_plugin_");
619   sep = strrchr (argv[0], '.');
620   if (NULL == plugin)
621   {
622     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Not a valid test name\n");
623     end_badly_now ();
624     return;
625   }
626   plugin += strlen ("test_plugin_");
627   if (NULL != sep)
628     sep[0] = '\0';
629
630   /* Hack for WLAN: start a second helper */
631   if (0 == strcmp (plugin, "wlan"))
632   {
633     char * helper_argv[3];
634     helper_argv[0] = (char *) "gnunet-helper-transport-wlan-dummy";
635     helper_argv[1] = (char *) "2";
636     helper_argv[2] = NULL;
637     suid_helper = GNUNET_HELPER_start (GNUNET_NO,
638         "gnunet-helper-transport-wlan-dummy", helper_argv,
639         &handle_helper_message, NULL, NULL );
640   }
641
642   /* Loading plugin */
643   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Loading transport plugin %s\n", plugin);
644   GNUNET_asprintf (&libname, "libgnunet_plugin_transport_%s", plugin);
645   api = GNUNET_PLUGIN_load (libname, &env);
646   if (api == NULL )
647   {
648     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
649         "Failed to load transport plugin for %s\n", plugin);
650     end_badly_now ();
651     return;
652   }
653
654   timeout_wait = GNUNET_SCHEDULER_add_delayed (WAIT, &wait_end, NULL );
655
656   /* Check if all functions are implemented */
657   if (NULL == api->address_pretty_printer)
658   {
659     GNUNET_break(0);
660     end_badly_now ();
661     return;
662   }
663   if (NULL == api->address_to_string)
664   {
665     GNUNET_break(0);
666     end_badly_now ();
667     return;
668   }
669   GNUNET_assert(NULL != api->check_address);
670   if (NULL == api->check_address)
671   {
672     GNUNET_break(0);
673     end_badly_now ();
674     return;
675   }
676   GNUNET_assert(NULL != api->disconnect_peer);
677   if (NULL == api->disconnect_peer)
678   {
679     GNUNET_break(0);
680     end_badly_now ();
681     return;
682   }
683   GNUNET_assert(NULL != api->get_session);
684   if (NULL == api->get_session)
685   {
686     GNUNET_break(0);
687     end_badly_now ();
688     return;
689   }
690   if (NULL == api->address_pretty_printer)
691   {
692     GNUNET_break(0);
693     end_badly_now ();
694     return;
695   }
696   if (NULL == api->string_to_address)
697   {
698     GNUNET_break(0);
699     end_badly_now ();
700     return;
701   }
702
703 }
704
705 /**
706  * The main function for the test
707  *
708  * @param argc number of arguments from the command line
709  * @param argv command line arguments
710  * @return 0 ok, 1 on error
711  */
712 int
713 main (int argc, char * const *argv)
714 {
715   static struct GNUNET_GETOPT_CommandLineOption options[] = {
716       GNUNET_GETOPT_OPTION_END };
717   int ret;
718
719   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-plugin-transport");
720
721   char * const argv_prog[] = { "test_plugin_transport", "-c",
722       "test_plugin_transport_data.conf", NULL };
723   GNUNET_log_setup ("test-plugin-transport", "WARNING", NULL );
724   ok = 1; /* set to fail */
725   ret =
726       (GNUNET_OK
727           == GNUNET_PROGRAM_run (3, argv_prog, "test-plugin-transport",
728               "testcase", options, &run, (void *) argv)) ? ok : 1;
729   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-plugin-transport");
730   return ret;
731 }
732
733 /* end of test_plugin_transport.c */