get_address_latency also does not use session
[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 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 {
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 (NULL != timeout_endbadly)
142   {
143     GNUNET_SCHEDULER_cancel (timeout_endbadly);
144     timeout_endbadly = NULL;
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 = NULL;
187   if (NULL != timeout_wait)
188   {
189     GNUNET_SCHEDULER_cancel (timeout_wait);
190     timeout_wait = NULL;
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 = NULL;
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 (NULL != timeout_wait)
264   {
265     GNUNET_SCHEDULER_cancel (timeout_wait);
266     timeout_wait = NULL;
267   }
268   if (NULL != timeout_endbadly)
269   {
270     GNUNET_SCHEDULER_cancel (timeout_endbadly);
271     timeout_endbadly = NULL;
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 = GNUNET_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 (NULL != timeout_wait)
422     {
423       GNUNET_SCHEDULER_cancel (timeout_wait);
424       timeout_wait = NULL;
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 enum GNUNET_ATS_Network_Type
471 env_get_address_type (void *cls,
472                       const struct sockaddr *addr,
473                       size_t addrlen)
474 {
475   return GNUNET_ATS_NET_LOOPBACK;
476 }
477
478
479 static const struct GNUNET_MessageHeader *
480 env_get_our_hello ()
481 {
482   return (const struct GNUNET_MessageHeader *) hello;
483 }
484
485
486 static void
487 env_session_end (void *cls,
488                  const struct GNUNET_HELLO_Address *address,
489                  struct Session *session)
490 {
491
492 }
493
494
495 static void
496 env_update_metrics (void *cls,
497                     const struct GNUNET_HELLO_Address *address,
498                     struct Session *session,
499                     const struct GNUNET_ATS_Information *ats,
500                     uint32_t ats_count)
501 {
502 }
503
504
505 static void
506 setup_plugin_environment ()
507 {
508   env.cfg = cfg;
509   env.cls = &env;
510   env.my_identity = &my_identity;
511   env.max_connections = max_connect_per_transport;
512   env.stats = stats;
513   env.receive = &env_receive;
514   env.notify_address = &env_notify_address;
515   env.get_address_type = &env_get_address_type;
516   env.update_address_metrics = &env_update_metrics;
517   env.get_our_hello = &env_get_our_hello;
518   env.session_end = &env_session_end;
519 }
520
521
522 static int
523 handle_helper_message (void *cls, void *client,
524                        const struct GNUNET_MessageHeader *hdr)
525 {
526   return GNUNET_OK;
527 }
528
529
530 /**
531  * Runs the test.
532  *
533  * @param cls closure
534  * @param c configuration to use
535  */
536 static void
537 run (void *cls, char * const *args, const char *cfgfile,
538     const struct GNUNET_CONFIGURATION_Handle *c)
539 {
540   char * const *argv = cls;
541   unsigned long long tneigh;
542   char *keyfile;
543   char *plugin;
544   char *sep;
545
546   timeout_endbadly = GNUNET_SCHEDULER_add_delayed (TIMEOUT, end_badly, &ok);
547
548   cfg = c;
549   /* parse configuration */
550   if ((GNUNET_OK
551       != GNUNET_CONFIGURATION_get_value_number (c, "TRANSPORT",
552           "NEIGHBOUR_LIMIT", &tneigh))
553       || (GNUNET_OK
554           != GNUNET_CONFIGURATION_get_value_filename (c, "PEER", "PRIVATE_KEY",
555               &keyfile)))
556   {
557     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
558         "Transport service is lacking key configuration settings.  Exiting.\n");
559     return;
560   }
561
562   if (NULL == (stats = GNUNET_STATISTICS_create ("transport", cfg)))
563   {
564     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
565         "Could not create statistics.  Exiting.\n");
566     GNUNET_free(keyfile);
567     end_badly_now ();
568     return;
569   }
570
571   if (GNUNET_OK != GNUNET_DISK_file_test (HOSTKEY_FILE))
572   {
573     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Hostkey `%s' missing.  Exiting.\n",
574         HOSTKEY_FILE);
575     GNUNET_free(keyfile);
576     end_badly_now ();
577     return;
578   }
579
580   if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (keyfile))
581   {
582     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
583         "Could not create a directory for hostkey `%s'.  Exiting.\n", keyfile);
584     GNUNET_free(keyfile);
585     end_badly_now ();
586     return;
587   }
588
589   if (GNUNET_OK != GNUNET_DISK_file_copy (HOSTKEY_FILE, keyfile))
590   {
591     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
592         "Could not copy hostkey `%s' to destination `%s'.  Exiting.\n",
593         HOSTKEY_FILE, keyfile);
594     GNUNET_free(keyfile);
595     end_badly_now ();
596     return;
597   }
598
599   max_connect_per_transport = (uint32_t) tneigh;
600   my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile);
601   GNUNET_free(keyfile);
602   if (NULL == my_private_key)
603   {
604     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
605         "Could not access hostkey.  Exiting.\n");
606     end_badly_now ();
607     return;
608   }
609   GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key);
610
611   hello = GNUNET_HELLO_create (&my_identity.public_key, NULL, NULL, GNUNET_NO);
612
613   /* load plugins... */
614   setup_plugin_environment ();
615
616   GNUNET_assert(strlen (argv[0]) > strlen ("test_plugin_"));
617   plugin = strstr (argv[0], "test_plugin_");
618   sep = strrchr (argv[0], '.');
619   if (NULL == plugin)
620   {
621     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Not a valid test name\n");
622     end_badly_now ();
623     return;
624   }
625   plugin += strlen ("test_plugin_");
626   if (NULL != sep)
627     sep[0] = '\0';
628
629   /* Hack for WLAN: start a second helper */
630   if (0 == strcmp (plugin, "wlan"))
631   {
632     char * helper_argv[3];
633     helper_argv[0] = (char *) "gnunet-helper-transport-wlan-dummy";
634     helper_argv[1] = (char *) "2";
635     helper_argv[2] = NULL;
636     suid_helper = GNUNET_HELPER_start (GNUNET_NO,
637         "gnunet-helper-transport-wlan-dummy", helper_argv,
638         &handle_helper_message, NULL, NULL );
639   }
640
641   /* Loading plugin */
642   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Loading transport plugin %s\n", plugin);
643   GNUNET_asprintf (&libname, "libgnunet_plugin_transport_%s", plugin);
644   api = GNUNET_PLUGIN_load (libname, &env);
645   if (api == NULL )
646   {
647     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
648         "Failed to load transport plugin for %s\n", plugin);
649     end_badly_now ();
650     return;
651   }
652
653   timeout_wait = GNUNET_SCHEDULER_add_delayed (WAIT, &wait_end, NULL );
654
655   /* Check if all functions are implemented */
656   if (NULL == api->address_pretty_printer)
657   {
658     GNUNET_break(0);
659     end_badly_now ();
660     return;
661   }
662   if (NULL == api->address_to_string)
663   {
664     GNUNET_break(0);
665     end_badly_now ();
666     return;
667   }
668   GNUNET_assert(NULL != api->check_address);
669   if (NULL == api->check_address)
670   {
671     GNUNET_break(0);
672     end_badly_now ();
673     return;
674   }
675   GNUNET_assert(NULL != api->disconnect_peer);
676   if (NULL == api->disconnect_peer)
677   {
678     GNUNET_break(0);
679     end_badly_now ();
680     return;
681   }
682   GNUNET_assert(NULL != api->get_session);
683   if (NULL == api->get_session)
684   {
685     GNUNET_break(0);
686     end_badly_now ();
687     return;
688   }
689   if (NULL == api->address_pretty_printer)
690   {
691     GNUNET_break(0);
692     end_badly_now ();
693     return;
694   }
695   if (NULL == api->string_to_address)
696   {
697     GNUNET_break(0);
698     end_badly_now ();
699     return;
700   }
701
702 }
703
704 /**
705  * The main function for the test
706  *
707  * @param argc number of arguments from the command line
708  * @param argv command line arguments
709  * @return 0 ok, 1 on error
710  */
711 int
712 main (int argc, char * const *argv)
713 {
714   static struct GNUNET_GETOPT_CommandLineOption options[] = {
715       GNUNET_GETOPT_OPTION_END };
716   int ret;
717
718   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-plugin-transport");
719
720   char * const argv_prog[] = { "test_plugin_transport", "-c",
721       "test_plugin_transport_data.conf", NULL };
722   GNUNET_log_setup ("test-plugin-transport", "WARNING", NULL );
723   ok = 1; /* set to fail */
724   ret =
725       (GNUNET_OK
726           == GNUNET_PROGRAM_run (3, argv_prog, "test-plugin-transport",
727               "testcase", options, &run, (void *) argv)) ? ok : 1;
728   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-plugin-transport");
729   return ret;
730 }
731
732 /* end of test_plugin_transport.c */