fix
[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_common.h"
29 #include "gnunet_constants.h"
30 #include "gnunet_util_lib.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_peerinfo_service.h"
33 #include "gnunet_statistics_service.h"
34 #include "gnunet_protocols.h"
35 #include "gnunet_signatures.h"
36 #include "gnunet_transport_plugin.h"
37
38 #include "transport.h"
39
40 /**
41  * How long until we give up on transmitting the message?
42  */
43 #define WAIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
44 #define TIMEOUT GNUNET_TIME_relative_multiply (WAIT, 3)
45
46 /**
47  * Our public key.
48  */
49 static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
50
51 /**
52  * Our identity.
53  */
54 static struct GNUNET_PeerIdentity my_identity;
55
56 /**
57  * Our private key.
58  */
59 static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
60
61 /**
62  * Our configuration.
63  */
64 const struct GNUNET_CONFIGURATION_Handle *cfg;
65
66 /**
67  * Our configuration.
68  */
69 struct GNUNET_STATISTICS_Handle *stats;
70
71 /**
72  * Our HELLO
73  */
74 struct GNUNET_HELLO_Message *hello;
75
76 /**
77  * Number of neighbours we'd like to have.
78  */
79 static uint32_t max_connect_per_transport;
80
81 /**
82  * Environment for this plugin.
83  */
84 struct GNUNET_TRANSPORT_PluginEnvironment env;
85
86 /**
87  *handle for the api provided by this plugin
88  */
89 struct GNUNET_TRANSPORT_PluginFunctions *api;
90
91 /**
92  * Helper handler
93  */
94 struct GNUNET_HELPER_Handle *suid_helper;
95
96 /**
97  * Timeout task
98  */
99 static GNUNET_SCHEDULER_TaskIdentifier timeout_task;
100
101 /**
102  * Timeout task
103  */
104 static GNUNET_SCHEDULER_TaskIdentifier timeout_wait;
105
106 /**
107  * Library name
108  */
109 static char *libname;
110
111 /**
112  * Plugin addresses head
113  */
114 struct AddressWrapper *head;
115
116 /**
117  * Plugin addresses tail
118  */
119 struct AddressWrapper *tail;
120
121 unsigned int addresses_reported;
122
123 /**
124  * Did the test pass or fail?
125  */
126 static int ok;
127
128
129 struct AddressWrapper
130 {
131   struct AddressWrapper *next;
132
133   struct AddressWrapper *prev;
134
135   void *addr;
136
137   size_t addrlen;
138 };
139
140 static void
141 end ()
142 {
143   if (GNUNET_SCHEDULER_NO_TASK != timeout_task)
144   {
145       GNUNET_SCHEDULER_cancel (timeout_task);
146       timeout_task = GNUNET_SCHEDULER_NO_TASK;
147   }
148   if (NULL != api)
149       GNUNET_PLUGIN_unload (libname, api);
150   GNUNET_free (libname);
151   libname = NULL;
152   GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
153   stats = NULL;
154
155   if (NULL != suid_helper)
156   {
157     GNUNET_HELPER_stop (suid_helper);
158     suid_helper = NULL;
159   }
160
161   ok = 0;
162 }
163
164 static void
165 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
166 {
167   struct AddressWrapper *w;
168   int c = 0;
169   timeout_task = GNUNET_SCHEDULER_NO_TASK;
170   if (GNUNET_SCHEDULER_NO_TASK != timeout_wait)
171   {
172       GNUNET_SCHEDULER_cancel (timeout_wait);
173       timeout_wait = GNUNET_SCHEDULER_NO_TASK;
174   }
175   if (NULL != libname)
176   {
177     if (NULL != api)
178       GNUNET_PLUGIN_unload (libname, api);
179     GNUNET_free (libname);
180     libname = NULL;
181   }
182
183   w = head;
184   while (NULL != head)
185   {
186       GNUNET_CONTAINER_DLL_remove (head, tail, w);
187       c ++;
188       GNUNET_free (w->addr);
189       GNUNET_free (w);
190   }
191   if (c > 0)
192   {
193     GNUNET_break (0);
194     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
195               _("Plugin did not remove %u addresses \n"), c);
196   }
197
198   if (NULL != stats)
199   {
200     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
201     stats = NULL;
202   }
203
204   if (NULL != suid_helper)
205   {
206     GNUNET_HELPER_stop (suid_helper);
207     suid_helper = NULL;
208   }
209
210   ok = 1;
211 }
212
213 static void
214 wait_end (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
215 {
216   timeout_wait = GNUNET_SCHEDULER_NO_TASK;
217   if (0 == addresses_reported)
218     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
219               _("Plugin did not report any addresses, could not check address conversion functions\n"));
220   end ();
221 }
222
223
224 static void
225 end_badly_now ()
226 {
227   if (GNUNET_SCHEDULER_NO_TASK != timeout_wait)
228   {
229       GNUNET_SCHEDULER_cancel (timeout_wait);
230       timeout_wait = GNUNET_SCHEDULER_NO_TASK;
231   }
232   if (GNUNET_SCHEDULER_NO_TASK != timeout_task)
233   {
234       GNUNET_SCHEDULER_cancel (timeout_task);
235       timeout_task = GNUNET_SCHEDULER_NO_TASK;
236   }
237   timeout_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
238 }
239
240
241 static struct GNUNET_TIME_Relative
242 env_receive (void *cls,
243             const struct GNUNET_PeerIdentity *peer,
244             const struct GNUNET_MessageHeader *message,
245             const struct GNUNET_ATS_Information *ats,
246             uint32_t ats_count,
247             struct Session * session,
248             const char *sender_address,
249             uint16_t sender_address_len)
250 {
251   /* do nothing */
252   return GNUNET_TIME_relative_get_zero_();
253 }
254
255
256 static void
257 env_notify_address (void *cls,
258                     int add_remove,
259                     const void *addr,
260                     size_t addrlen,
261                     const char *plugin)
262 {
263   struct AddressWrapper *w;
264   char *a2s;
265   void *s2a;
266   size_t s2a_len;
267
268   if (GNUNET_YES == add_remove)
269   {
270       addresses_reported ++;
271       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
272                   _("Adding address of length %u\n"), addrlen);
273
274       w = GNUNET_malloc (sizeof (struct AddressWrapper));
275       w->addr = GNUNET_malloc (addrlen);
276       w->addrlen = addrlen;
277       memcpy (w->addr, addr, addrlen);
278       GNUNET_CONTAINER_DLL_insert(head, tail, w);
279
280       a2s = strdup (api->address_to_string (api, w->addr, w->addrlen));
281       if (NULL == a2s)
282       {
283           GNUNET_break (0);
284           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
285                       _("Plugin cannot convert address to string!\n"));
286           end_badly_now();
287           return;
288       }
289
290       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
291                   _("Plugin added address `%s'\n"), a2s);
292
293       if ((GNUNET_OK != api->string_to_address (api, a2s, strlen (a2s)+1, &s2a, &s2a_len)) || (NULL == s2a))
294       {
295           GNUNET_break (0);
296           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
297                       _("Plugin cannot convert string to address!\n"));
298           end_badly_now();
299           return;
300       }
301
302       if (s2a_len != w->addrlen)
303       {
304           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
305                       _("Plugin creates different address length when converting address->string->address: %u != %u\n"), w->addrlen, s2a_len);
306       }
307       else
308       {
309           if (0 != memcmp (s2a, w->addr, s2a_len))
310             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
311                         _("Plugin creates different address length when connecting back and forth!\n"));
312       }
313       GNUNET_free (s2a);
314       GNUNET_free (a2s);
315       if (GNUNET_OK != api->check_address (api->cls, w->addr, w->addrlen))
316       {
317           GNUNET_break (0);
318           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
319                       _("Plugin refuses added address!\n"));
320           end_badly_now();
321           return;
322       }
323       if (GNUNET_SCHEDULER_NO_TASK != timeout_wait)
324       {
325           GNUNET_SCHEDULER_cancel (timeout_wait);
326           timeout_wait = GNUNET_SCHEDULER_NO_TASK;
327       }
328       timeout_wait = GNUNET_SCHEDULER_add_delayed (WAIT, &wait_end, NULL);
329   }
330   else if (GNUNET_NO == add_remove)
331   {
332       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
333                   _("Removing address of length %u\n"), addrlen);
334
335       w = head;
336       while (NULL != w)
337       {
338           if ((addrlen == w->addrlen) && (0 == memcmp (w->addr, addr, addrlen)))
339           {
340             break;
341           }
342           w = w->next;
343       }
344
345       if (w == NULL)
346       {
347           GNUNET_break (0);
348           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
349                       _("Plugin removes address never added!\n"));
350           end_badly_now();
351           return;
352       }
353
354       GNUNET_CONTAINER_DLL_remove (head, tail, w);
355       GNUNET_free (w->addr);
356       GNUNET_free (w);
357   }
358   else
359   {
360       GNUNET_break (0);
361       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
362                   _("Invalid operation: %u \n"), add_remove);
363       end_badly_now ();
364       return;
365   }
366 }
367
368 struct GNUNET_ATS_Information
369 env_get_address_type (void *cls,
370                      const struct sockaddr *addr,
371                      size_t addrlen)
372 {
373   struct GNUNET_ATS_Information ats;
374   ats.type = htonl (GNUNET_ATS_NETWORK_TYPE);
375   ats.value = htonl (GNUNET_ATS_NET_LOOPBACK);
376   return ats;
377 }
378
379 const struct GNUNET_MessageHeader *
380 env_get_our_hello (void)
381 {
382   return (const struct GNUNET_MessageHeader *) hello;
383 }
384
385 void env_session_end (void *cls,
386                       const struct GNUNET_PeerIdentity *peer,
387                       struct Session * session)
388 {
389
390 }
391
392 static void
393 setup_plugin_environment ()
394 {
395   env.cfg = cfg;
396   env.cls = &env;
397   env.my_identity = &my_identity;
398   env.max_connections = max_connect_per_transport;
399   env.stats = stats;
400
401   env.receive = &env_receive;
402   env.notify_address = &env_notify_address;
403   env.get_address_type = &env_get_address_type;
404   env.get_our_hello = &env_get_our_hello;
405   env.session_end = &env_session_end;
406 }
407
408 static int
409 handle_helper_message (void *cls, void *client,
410                        const struct GNUNET_MessageHeader *hdr)
411 {
412   return GNUNET_OK;
413 }
414
415 /**
416  * Runs the test.
417  *
418  * @param cls closure
419  * @param c configuration to use
420  */
421 static void
422 run (void *cls, char *const *args, const char *cfgfile,
423      const struct GNUNET_CONFIGURATION_Handle *c)
424 {
425   char *const *argv = cls;
426   unsigned long long tneigh;
427   char *keyfile;
428   char *plugin;
429   char *sep;
430
431   timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, end_badly, NULL);
432
433   cfg = c;
434   /* parse configuration */
435   if ((GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (c,
436                           "TRANSPORT",
437                           "NEIGHBOUR_LIMIT",
438                           &tneigh)) ||
439       (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (c,
440                           "GNUNETD", "HOSTKEY",
441                           &keyfile)))
442   {
443     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
444                 _("Transport service is lacking key configuration settings.  Exiting.\n"));
445
446     return;
447   }
448
449   stats = GNUNET_STATISTICS_create ("transport", cfg);
450   if (NULL == stats)
451   {
452       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
453                   _("Could not create statistics.  Exiting.\n"));
454       end_badly_now ();
455       return;
456   }
457
458   max_connect_per_transport = (uint32_t) tneigh;
459   my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
460   GNUNET_free (keyfile);
461   if (my_private_key == NULL)
462   {
463     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
464                 _("Transport service could not access hostkey.  Exiting.\n"));
465     end_badly_now ();
466     return;
467   }
468   GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
469   GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key),
470                       &my_identity.hashPubKey);
471
472
473   hello = GNUNET_HELLO_create(&my_public_key, NULL, NULL);
474
475   /* load plugins... */
476   setup_plugin_environment ();
477
478   GNUNET_assert (strlen (argv[0]) > strlen ("test_plugin_"));
479   plugin = strstr(argv[0],"test_plugin_");
480   sep = strrchr(argv[0],'.');
481   if (NULL == plugin)
482   {
483       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Not a valid test name\n"));
484       end_badly_now ();
485       return;
486   }
487   plugin += strlen ("test_plugin_");
488   if (NULL != sep)
489       sep[0] = '\0';
490
491   /* Hack for WLAN: start a second helper */
492   if (0 == strcmp (plugin, "wlan"))
493   {
494     char * helper_argv[3];
495     helper_argv[0] = (char *) "gnunet-helper-transport-wlan-dummy";
496     helper_argv[1] = (char *) "2";
497     helper_argv[2] = NULL;
498     suid_helper = GNUNET_HELPER_start ("gnunet-helper-transport-wlan-dummy",
499                                        helper_argv,
500                                        &handle_helper_message,
501                                        NULL,
502                                        NULL);
503   }
504
505
506   /* Loading plugin */
507   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading transport plugin %s\n"), plugin);
508   GNUNET_asprintf (&libname, "libgnunet_plugin_transport_%s", plugin);
509   api = GNUNET_PLUGIN_load (libname, &env);
510   if (api == NULL)
511   {
512     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
513                 _("Failed to load transport plugin for %s\n"), plugin);
514     end_badly_now ();
515     return;
516   }
517
518   timeout_wait = GNUNET_SCHEDULER_add_delayed (WAIT, &wait_end, NULL);
519
520   /* Check if all functions are implemented */
521   if (NULL == api->address_pretty_printer)
522   {
523       GNUNET_break (0);
524       end_badly_now ();
525       return;
526   }
527   if (NULL == api->address_to_string)
528   {
529       GNUNET_break (0);
530       end_badly_now ();
531       return;
532   }
533   GNUNET_assert (NULL != api->check_address);
534   if (NULL == api->check_address)
535   {
536       GNUNET_break (0);
537       end_badly_now ();
538       return;
539   }
540   GNUNET_assert (NULL != api->disconnect);
541   if (NULL == api->disconnect)
542   {
543       GNUNET_break (0);
544       end_badly_now ();
545       return;
546   }
547   GNUNET_assert (NULL != api->get_session);
548   if (NULL == api->get_session)
549   {
550       GNUNET_break (0);
551       end_badly_now ();
552       return;
553   }
554   if (NULL == api->address_pretty_printer)
555   {
556       GNUNET_break (0);
557       end_badly_now ();
558       return;
559   }
560   if (NULL == api->string_to_address)
561   {
562       GNUNET_break (0);
563       end_badly_now ();
564       return;
565   }
566 }
567
568
569 /**
570  * The main function for the transport service.
571  *
572  * @param argc number of arguments from the command line
573  * @param argv command line arguments
574  * @return 0 ok, 1 on error
575  */
576 int
577 main (int argc, char *const *argv)
578 {
579   static struct GNUNET_GETOPT_CommandLineOption options[] = {
580     GNUNET_GETOPT_OPTION_END
581   };
582   int ret;
583
584   char *const argv_prog[] = {
585     "test_plugin_transport",
586     "-c",
587     "test_plugin_transport_data.conf",
588     "-L", "WARNING",
589     NULL
590   };
591   GNUNET_log_setup ("test-plugin-transport",
592                     "WARNING",
593                     NULL);
594   ok = 1;                       /* set to fail */
595   ret = (GNUNET_OK == GNUNET_PROGRAM_run (5,
596                            argv_prog,
597                            "test-plugin-transport",
598                            "testcase",
599                            options,
600                            &run,
601                            (void *) argv)) ? ok : 1;
602   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-plugin-transport");
603   return ret;
604 }
605
606 /* end of test_plugin_transport.c */