session end function must include address to notify address
[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 static void
260 end_badly_now ()
261 {
262   if (GNUNET_SCHEDULER_NO_TASK != timeout_wait)
263   {
264     GNUNET_SCHEDULER_cancel (timeout_wait);
265     timeout_wait = GNUNET_SCHEDULER_NO_TASK;
266   }
267   if (GNUNET_SCHEDULER_NO_TASK != timeout_endbadly)
268   {
269     GNUNET_SCHEDULER_cancel (timeout_endbadly);
270     timeout_endbadly = GNUNET_SCHEDULER_NO_TASK;
271   }
272   timeout_endbadly = GNUNET_SCHEDULER_add_now (&end_badly, NULL );
273 }
274
275 static struct GNUNET_TIME_Relative
276 env_receive (void *cls,
277     const struct GNUNET_HELLO_Address *address,
278     struct Session *session,
279     const struct GNUNET_MessageHeader *message)
280 {
281   /* do nothing */
282   return GNUNET_TIME_relative_get_zero_ ();
283 }
284
285 static int got_reply;
286
287 /**
288  * Take the given address and append it to the set of results sent back to
289  * the client.
290  *
291  * @param cls the transmission context used ('struct GNUNET_SERVER_TransmitContext*')
292  * @param buf text to transmit
293  */
294 static void
295 address_pretty_printer_cb (void *cls, const char *buf)
296 {
297   if (NULL != buf)
298   {
299     got_reply = GNUNET_YES;
300     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Pretty address : `%s'\n", buf);
301     pretty_printers_running--;
302   }
303   else
304   {
305     if (GNUNET_NO == got_reply)
306     {
307       pretty_printers_running--;
308       GNUNET_break(0);
309       end_badly_now ();
310     }
311   }
312 }
313
314 static void
315 env_notify_address (void *cls, int add_remove,
316     const struct GNUNET_HELLO_Address *address)
317 {
318   struct AddressWrapper *w;
319   struct AddressWrapper *wtmp;
320   void *s2a;
321   size_t s2a_len;
322
323   if (GNUNET_YES == add_remove)
324   {
325     addresses_reported++;
326     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Adding address of length %u\n",
327         address->address_length);
328
329     for (wtmp = head; NULL != wtmp; wtmp = wtmp->next)
330     {
331       if ((address->address_length == wtmp->address->address_length) &&
332           (0 == memcmp (address->address, wtmp->address->address, address->address_length)))
333       {
334         GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
335             "Duplicate address notification .... \n");
336         return;
337       }
338     }
339
340     w = GNUNET_new (struct AddressWrapper);
341     w->address = GNUNET_HELLO_address_copy (address);
342     GNUNET_CONTAINER_DLL_insert(head, tail, w);
343     got_reply = GNUNET_NO;
344     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Testing: address_to_string \n");
345     w->addrstring = strdup (
346         api->address_to_string (api, w->address->address,
347             w->address->address_length));
348     if (NULL == w->addrstring)
349     {
350       GNUNET_break(0);
351       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
352           "Plugin cannot convert address to string!\n");
353       end_badly_now ();
354       return;
355     }
356     else
357     {
358       GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Plugin added address `%s'\n",
359           w->addrstring);
360       GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Testing address_to_string: OK\n");
361     }
362
363     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Testing: string_to_address \n");
364     s2a = NULL;
365     s2a_len = 0;
366     if ((GNUNET_OK
367         != api->string_to_address (api, w->addrstring,
368             strlen (w->addrstring) + 1, &s2a, &s2a_len)) || (NULL == s2a))
369     {
370       GNUNET_break(0);
371       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
372           "Plugin cannot convert string to address!\n");
373       end_badly_now ();
374       return;
375     }
376
377     /*
378      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
379      "Plugin creates `%s' %u\n",api->address_to_string (api, s2a, s2a_len), s2a_len);
380
381      int c1;
382      for (c1 = 0; c1 < s2a_len; c1++ )
383      fprintf (stderr, "%u == %u\n", ((char *) s2a)[c1], ((char *) w->addr)[c1]);
384      */
385     if (s2a_len != w->address->address_length)
386     {
387       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
388           "Plugin creates different address length when converting address->string->address: %u != %u\n",
389           w->address->address_length, s2a_len);
390     }
391     else if (0 != memcmp (s2a, w->address->address, s2a_len))
392     {
393       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
394           "Plugin creates different address length when converting back and forth %i!\n",
395           memcmp (s2a, w->address->address, s2a_len));
396     }
397     else
398     {
399       GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Testing string_to_address: OK\n");
400     }
401     GNUNET_free(s2a);
402
403     pretty_printers_running++;
404     api->address_pretty_printer (api->cls, address->transport_name, address->address, address->address_length, GNUNET_YES,
405         GNUNET_TIME_UNIT_MINUTES, &address_pretty_printer_cb, w);
406
407     if (GNUNET_OK != api->check_address (api->cls, w->address->address, w->address->address_length))
408     {
409       GNUNET_break(0);
410       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Plugin refuses added address!\n");
411       end_badly_now ();
412       return;
413     }
414     if (GNUNET_SCHEDULER_NO_TASK != timeout_wait)
415     {
416       GNUNET_SCHEDULER_cancel (timeout_wait);
417       timeout_wait = GNUNET_SCHEDULER_NO_TASK;
418     }
419
420     timeout_wait = GNUNET_SCHEDULER_add_delayed (WAIT, &wait_end, NULL );
421
422   }
423   else if (GNUNET_NO == add_remove)
424   {
425     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Removing address of length %u\n",
426         address->address_length);
427
428     w = head;
429     while (NULL != w)
430     {
431       if ((address->address_length == w->address->address_length) &&
432           (0 == memcmp (w->address->address, address->address, address->address_length)))
433       {
434         break;
435       }
436       w = w->next;
437     }
438
439     if (w == NULL )
440     {
441       GNUNET_break(0);
442       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
443           "Plugin removes address never added!\n");
444       end_badly_now ();
445       return;
446     }
447
448     GNUNET_CONTAINER_DLL_remove(head, tail, w);
449     GNUNET_HELLO_address_free (w->address);
450     GNUNET_free(w->addrstring);
451     GNUNET_free(w);
452   }
453   else
454   {
455     GNUNET_break(0);
456     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Invalid operation: %u\n", add_remove);
457     end_badly_now ();
458     return;
459   }
460 }
461
462 static struct GNUNET_ATS_Information
463 env_get_address_type (void *cls, const struct sockaddr *addr, size_t addrlen)
464 {
465   struct GNUNET_ATS_Information ats;
466   ats.type = htonl (GNUNET_ATS_NETWORK_TYPE);
467   ats.value = htonl (GNUNET_ATS_NET_LOOPBACK);
468   return ats;
469 }
470
471 static const struct GNUNET_MessageHeader *
472 env_get_our_hello ()
473 {
474   return (const struct GNUNET_MessageHeader *) hello;
475 }
476
477 static void
478 env_session_end (void *cls, const struct GNUNET_PeerIdentity *peer,
479     struct Session * session)
480 {
481 }
482
483 static void
484 env_update_metrics (void *cls,
485     const struct GNUNET_HELLO_Address *address,
486     struct Session *session,
487     const struct GNUNET_ATS_Information *ats,
488     uint32_t ats_count)
489 {
490 }
491
492 static void
493 setup_plugin_environment ()
494 {
495   env.cfg = cfg;
496   env.cls = &env;
497   env.my_identity = &my_identity;
498   env.max_connections = max_connect_per_transport;
499   env.stats = stats;
500   env.receive = &env_receive;
501   env.notify_address = &env_notify_address;
502   env.get_address_type = &env_get_address_type;
503   env.update_address_metrics = &env_update_metrics;
504   env.get_our_hello = &env_get_our_hello;
505   env.session_end = &env_session_end;
506 }
507
508 static int
509 handle_helper_message (void *cls, void *client,
510     const struct GNUNET_MessageHeader *hdr)
511 {
512   return GNUNET_OK;
513 }
514
515 /**
516  * Runs the test.
517  *
518  * @param cls closure
519  * @param c configuration to use
520  */
521 static void
522 run (void *cls, char * const *args, const char *cfgfile,
523     const struct GNUNET_CONFIGURATION_Handle *c)
524 {
525   char * const *argv = cls;
526   unsigned long long tneigh;
527   char *keyfile;
528   char *plugin;
529   char *sep;
530
531   timeout_endbadly = GNUNET_SCHEDULER_add_delayed (TIMEOUT, end_badly, &ok);
532
533   cfg = c;
534   /* parse configuration */
535   if ((GNUNET_OK
536       != GNUNET_CONFIGURATION_get_value_number (c, "TRANSPORT",
537           "NEIGHBOUR_LIMIT", &tneigh))
538       || (GNUNET_OK
539           != GNUNET_CONFIGURATION_get_value_filename (c, "PEER", "PRIVATE_KEY",
540               &keyfile)))
541   {
542     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
543         "Transport service is lacking key configuration settings.  Exiting.\n");
544     return;
545   }
546
547   if (NULL == (stats = GNUNET_STATISTICS_create ("transport", cfg)))
548   {
549     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
550         "Could not create statistics.  Exiting.\n");
551     GNUNET_free(keyfile);
552     end_badly_now ();
553     return;
554   }
555
556   if (GNUNET_OK != GNUNET_DISK_file_test (HOSTKEY_FILE))
557   {
558     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Hostkey `%s' missing.  Exiting.\n",
559         HOSTKEY_FILE);
560     GNUNET_free(keyfile);
561     end_badly_now ();
562     return;
563   }
564
565   if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (keyfile))
566   {
567     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
568         "Could not create a directory for hostkey `%s'.  Exiting.\n", keyfile);
569     GNUNET_free(keyfile);
570     end_badly_now ();
571     return;
572   }
573
574   if (GNUNET_OK != GNUNET_DISK_file_copy (HOSTKEY_FILE, keyfile))
575   {
576     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
577         "Could not copy hostkey `%s' to destination `%s'.  Exiting.\n",
578         HOSTKEY_FILE, keyfile);
579     GNUNET_free(keyfile);
580     end_badly_now ();
581     return;
582   }
583
584   max_connect_per_transport = (uint32_t) tneigh;
585   my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile);
586   GNUNET_free(keyfile);
587   if (NULL == my_private_key)
588   {
589     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
590         "Could not access hostkey.  Exiting.\n");
591     end_badly_now ();
592     return;
593   }
594   GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key);
595
596   hello = GNUNET_HELLO_create (&my_identity.public_key, NULL, NULL, GNUNET_NO);
597
598   /* load plugins... */
599   setup_plugin_environment ();
600
601   GNUNET_assert(strlen (argv[0]) > strlen ("test_plugin_"));
602   plugin = strstr (argv[0], "test_plugin_");
603   sep = strrchr (argv[0], '.');
604   if (NULL == plugin)
605   {
606     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Not a valid test name\n");
607     end_badly_now ();
608     return;
609   }
610   plugin += strlen ("test_plugin_");
611   if (NULL != sep)
612     sep[0] = '\0';
613
614   /* Hack for WLAN: start a second helper */
615   if (0 == strcmp (plugin, "wlan"))
616   {
617     char * helper_argv[3];
618     helper_argv[0] = (char *) "gnunet-helper-transport-wlan-dummy";
619     helper_argv[1] = (char *) "2";
620     helper_argv[2] = NULL;
621     suid_helper = GNUNET_HELPER_start (GNUNET_NO,
622         "gnunet-helper-transport-wlan-dummy", helper_argv,
623         &handle_helper_message, NULL, NULL );
624   }
625
626   /* Loading plugin */
627   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Loading transport plugin %s\n", plugin);
628   GNUNET_asprintf (&libname, "libgnunet_plugin_transport_%s", plugin);
629   api = GNUNET_PLUGIN_load (libname, &env);
630   if (api == NULL )
631   {
632     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
633         "Failed to load transport plugin for %s\n", plugin);
634     end_badly_now ();
635     return;
636   }
637
638   timeout_wait = GNUNET_SCHEDULER_add_delayed (WAIT, &wait_end, NULL );
639
640   /* Check if all functions are implemented */
641   if (NULL == api->address_pretty_printer)
642   {
643     GNUNET_break(0);
644     end_badly_now ();
645     return;
646   }
647   if (NULL == api->address_to_string)
648   {
649     GNUNET_break(0);
650     end_badly_now ();
651     return;
652   }
653   GNUNET_assert(NULL != api->check_address);
654   if (NULL == api->check_address)
655   {
656     GNUNET_break(0);
657     end_badly_now ();
658     return;
659   }
660   GNUNET_assert(NULL != api->disconnect_peer);
661   if (NULL == api->disconnect_peer)
662   {
663     GNUNET_break(0);
664     end_badly_now ();
665     return;
666   }
667   GNUNET_assert(NULL != api->get_session);
668   if (NULL == api->get_session)
669   {
670     GNUNET_break(0);
671     end_badly_now ();
672     return;
673   }
674   if (NULL == api->address_pretty_printer)
675   {
676     GNUNET_break(0);
677     end_badly_now ();
678     return;
679   }
680   if (NULL == api->string_to_address)
681   {
682     GNUNET_break(0);
683     end_badly_now ();
684     return;
685   }
686
687 }
688
689 /**
690  * The main function for the test
691  *
692  * @param argc number of arguments from the command line
693  * @param argv command line arguments
694  * @return 0 ok, 1 on error
695  */
696 int
697 main (int argc, char * const *argv)
698 {
699   static struct GNUNET_GETOPT_CommandLineOption options[] = {
700       GNUNET_GETOPT_OPTION_END };
701   int ret;
702
703   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-plugin-transport");
704
705   char * const argv_prog[] = { "test_plugin_transport", "-c",
706       "test_plugin_transport_data.conf", NULL };
707   GNUNET_log_setup ("test-plugin-transport", "WARNING", NULL );
708   ok = 1; /* set to fail */
709   ret =
710       (GNUNET_OK
711           == GNUNET_PROGRAM_run (3, argv_prog, "test-plugin-transport",
712               "testcase", options, &run, (void *) argv)) ? ok : 1;
713   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-plugin-transport");
714   return ret;
715 }
716
717 /* end of test_plugin_transport.c */