(no commit message)
[oweals/gnunet.git] / src / transport / test_plugin_transport_http.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010 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 2, 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_http.c
22  * @brief testcase for plugin_transport_http.c
23  * @author Matthias Wachs
24  */
25
26 #include "platform.h"
27 #include "gnunet_constants.h"
28 #include "gnunet_getopt_lib.h"
29 #include "gnunet_hello_lib.h"
30 #include "gnunet_os_lib.h"
31 #include "gnunet_peerinfo_service.h"
32 #include "gnunet_plugin_lib.h"
33 #include "gnunet_protocols.h"
34 #include "gnunet_program_lib.h"
35 #include "gnunet_signatures.h"
36 #include "gnunet_service_lib.h"
37 #include "plugin_transport.h"
38 #include "gnunet_statistics_service.h"
39 #include "transport.h"
40 #include <curl/curl.h>
41
42 #define VERBOSE GNUNET_YES
43 #define DEBUG GNUNET_YES
44
45 #define PLUGIN libgnunet_plugin_transport_template
46
47 /**
48  * How long until we give up on transmitting the message?
49  */
50 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 90)
51
52 /**
53  * How long until we give up on transmitting the message?
54  */
55 #define TEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20)
56
57 /**
58  * How long between recieve and send?
59  */
60 #define WAIT_INTERVALL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
61
62 /**
63  * Our public key.
64  */
65 /* static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; */
66
67 /**
68  * Our public key.
69  */
70 static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
71
72 /**
73  * Our identity.
74  */
75 static struct GNUNET_PeerIdentity my_identity;
76
77 /**
78  * Our private key.
79  */
80 static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
81
82
83 /**
84  * Our scheduler.
85  */
86 struct GNUNET_SCHEDULER_Handle *sched;
87
88 /**
89  * Our statistics handle.
90  */
91 struct GNUNET_STATISTICS_Handle *stats;
92
93
94 /**
95  * Our configuration.
96  */
97 const struct GNUNET_CONFIGURATION_Handle *cfg;
98
99 /**
100  * Number of neighbours we'd like to have.
101  */
102 static uint32_t max_connect_per_transport;
103
104 /**
105  * Environment for this plugin.
106  */
107 static struct GNUNET_TRANSPORT_PluginEnvironment env;
108
109 /**
110  *handle for the api provided by this plugin
111  */
112 static struct GNUNET_TRANSPORT_PluginFunctions *api;
113
114 /**
115  * ID of the task controlling the testcase timeout
116  */
117 static GNUNET_SCHEDULER_TaskIdentifier ti_timeout;
118
119 static GNUNET_SCHEDULER_TaskIdentifier ti_send;
120
121 const struct GNUNET_PeerIdentity * p;
122
123 /**
124  *  Struct for plugin addresses
125  */
126 struct Plugin_Address
127 {
128   /**
129    * Next field for linked list
130    */
131   struct Plugin_Address * next;
132
133   /**
134    * buffer containing data to send
135    */
136   void * addr;
137
138   /**
139    * amount of data to sent
140    */
141   size_t addrlen;
142 };
143
144 struct Plugin_Address * addr_head;
145
146 /**
147  * Did the test pass or fail?
148  */
149 static int fail_notify_address;
150 /**
151  * Did the test pass or fail?
152  */
153 static int fail_notify_address_count;
154
155 /**
156  * Did the test pass or fail?
157  */
158 static int fail_pretty_printer;
159
160 /**
161  * Did the test pass or fail?
162  */
163 static int fail_pretty_printer_count;
164
165 /**
166  * Did the test pass or fail?
167  */
168 static int fail_addr_to_str;
169
170 /**
171  * Did the test pass or fail?
172  */
173 static int fail;
174
175 /**
176  * Recieved message already returned to sender?
177  */
178 static int sent;
179
180 /**
181  * Shutdown testcase
182  */
183 static void
184 shutdown_clean ()
185 {
186   if (ti_send != GNUNET_SCHEDULER_NO_TASK)
187   {
188     GNUNET_SCHEDULER_cancel(sched,ti_send);
189     ti_send = GNUNET_SCHEDULER_NO_TASK;
190   }
191
192   if (ti_timeout != GNUNET_SCHEDULER_NO_TASK)
193   {
194     GNUNET_SCHEDULER_cancel(sched,ti_timeout);
195     ti_timeout = GNUNET_SCHEDULER_NO_TASK;
196   }
197   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Unloading http plugin\n");
198   GNUNET_assert (NULL == GNUNET_PLUGIN_unload ("libgnunet_plugin_transport_http", api));
199
200   GNUNET_SCHEDULER_shutdown(sched);
201   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exiting testcase\n");
202   exit(fail);
203   return;
204 }
205
206 /**
207  * Continuation called after plugin send message
208  * @cls closure
209  * @target target
210  * @result GNUNET_OK or GNUNET_SYSERR
211  */
212 static void task_send_cont (void *cls,
213                             const struct GNUNET_PeerIdentity * target,
214                             int result)
215 {
216   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Message was sent!\n");
217   fail = GNUNET_NO;
218   shutdown_clean();
219 }
220
221 /**
222  * Task sending recieved message back to peer
223  * @cls closure
224  * @tc task context
225  */
226 static void
227 task_send (void *cls,
228             const struct GNUNET_SCHEDULER_TaskContext *tc)
229 {
230   ti_timeout = GNUNET_SCHEDULER_NO_TASK;
231   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
232     return;
233
234   if (GNUNET_YES==sent)
235     return;
236
237   struct GNUNET_MessageHeader * msg = cls;
238   unsigned int len = ntohs(msg->size);
239   const char * msgc = (const char *) msg;
240
241   api->send(api->cls, p, msgc, len, 0, TIMEOUT, NULL,NULL, 0, GNUNET_NO, &task_send_cont, NULL);
242   sent = GNUNET_YES;
243
244 }
245
246 /**
247  * Recieves messages from plugin, in real world transport
248  */
249 static struct GNUNET_TIME_Relative
250 receive (void *cls,
251          const struct GNUNET_PeerIdentity * peer,
252          const struct GNUNET_MessageHeader * message,
253          uint32_t distance,
254          struct Session *session,
255          const char *sender_address,
256          uint16_t sender_address_len)
257 {
258   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testcase recieved new message from peer `%s' with type %u and length %u\n",  GNUNET_i2s(peer),ntohs(message->type),ntohs(message->size));
259
260   /* take recieved message and send it back to peer */
261   p = peer;
262   void * c = (void *) message;
263   ti_send =GNUNET_SCHEDULER_add_delayed (sched, WAIT_INTERVALL, &task_send, c);
264
265   return GNUNET_TIME_UNIT_ZERO;
266 }
267
268
269 /**
270  * Network format for IPv4 addresses.
271  */
272 struct IPv4HttpAddress
273 {
274   /**
275    * IPv4 address, in network byte order.
276    */
277   uint32_t ipv4_addr;
278
279   /**
280    * Port number, in network byte order.
281    */
282   uint16_t u_port;
283
284 };
285
286
287 /**
288  * Network format for IPv6 addresses.
289  */
290 struct IPv6HttpAddress
291 {
292   /**
293    * IPv6 address.
294    */
295   struct in6_addr ipv6_addr;
296
297   /**
298    * Port number, in network byte order.
299    */
300   uint16_t u6_port;
301
302 };
303
304 /**
305  * Plugin notifies transport (aka testcase) about its addresses
306  */
307 void
308 notify_address (void *cls,
309                 const char *name,
310                 const void *addr,
311                 uint16_t addrlen,
312                 struct GNUNET_TIME_Relative expires)
313 {
314   char * address = NULL;
315   unsigned int port;
316   struct Plugin_Address * pl_addr;
317   struct Plugin_Address * cur;
318
319   if (addrlen == (sizeof (struct IPv4HttpAddress)))
320   {
321     address = GNUNET_malloc (INET_ADDRSTRLEN);
322     inet_ntop(AF_INET, (struct in_addr *) addr,address,INET_ADDRSTRLEN);
323     port = ntohs(((struct IPv4HttpAddress *) addr)->u_port);
324   }
325
326   if (addrlen == (sizeof (struct IPv6HttpAddress)))
327   {
328     address = GNUNET_malloc (INET6_ADDRSTRLEN);
329     inet_ntop(AF_INET6, (struct in6_addr *) addr,address,INET6_ADDRSTRLEN);
330     port = ntohs(((struct IPv6HttpAddress *) addr)->u6_port);
331   }
332   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Transport plugin notification for address: `%s':%u\n"),address,port);
333
334   pl_addr = GNUNET_malloc (sizeof (struct Plugin_Address) );
335   pl_addr->addrlen = addrlen;
336   pl_addr->addr = GNUNET_malloc(addrlen);
337   memcpy(pl_addr->addr,addr,addrlen);
338   pl_addr->next = NULL;
339
340   if ( NULL == addr_head)
341   {
342     addr_head = pl_addr;
343   }
344   else
345   {
346     cur = addr_head;
347     while (NULL != cur->next)
348       {
349         cur = cur->next;
350       }
351     cur->next = pl_addr;
352   }
353
354   fail_notify_address_count++;
355   fail_notify_address = GNUNET_NO;
356 }
357
358 /**
359  * Setup plugin environment
360  */
361 static void
362 setup_plugin_environment ()
363 {
364   env.cfg = cfg;
365   env.sched = sched;
366   env.stats = stats;
367   env.my_identity = &my_identity;
368   env.cls = &env;
369   env.receive = &receive;
370   env.notify_address = &notify_address;
371   env.max_connections = max_connect_per_transport;
372 }
373
374
375 /**
376  * Task shutting down testcase if it a timeout occurs
377  */
378 static void
379 task_timeout (void *cls,
380             const struct GNUNET_SCHEDULER_TaskContext *tc)
381 {
382   ti_timeout = GNUNET_SCHEDULER_NO_TASK;
383   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
384     return;
385
386   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testcase timeout\n");
387   fail = GNUNET_YES;
388   shutdown_clean();
389   return;
390 }
391
392 static void pretty_printer_cb (void *cls,
393                                const char *address)
394 {
395   if (NULL==address)
396     return;
397   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Plugin returned: `%s'\n",address);
398   fail_pretty_printer_count++;
399 }
400
401
402 /**
403  * Runs the test.
404  *
405  * @param cls closure
406  * @param s scheduler to use
407  * @param c configuration to use
408  */
409 static void
410 run (void *cls,
411      struct GNUNET_SCHEDULER_Handle *s,
412      char *const *args,
413      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *c)
414 {
415   char * libname;
416   sched = s;
417   cfg = c;
418   char *keyfile;
419   unsigned long long tneigh;
420   struct Plugin_Address * cur;
421   struct Plugin_Address * tmp;
422   const char * addr_str;
423   unsigned int count_str_addr;
424
425   fail_pretty_printer = GNUNET_YES;
426   fail_notify_address = GNUNET_YES;
427   fail_addr_to_str = GNUNET_YES;
428   addr_head = NULL;
429   count_str_addr = 0;
430   /* parse configuration */
431   if ((GNUNET_OK !=
432        GNUNET_CONFIGURATION_get_value_number (c,
433                                               "TRANSPORT",
434                                               "NEIGHBOUR_LIMIT",
435                                               &tneigh)) ||
436       (GNUNET_OK !=
437        GNUNET_CONFIGURATION_get_value_filename (c,
438                                                 "GNUNETD",
439                                                 "HOSTKEY", &keyfile)))
440     {
441       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
442                   _
443                   ("Transport service is lacking key configuration settings.  Exiting.\n"));
444       GNUNET_SCHEDULER_shutdown (s);
445       fail = 1;
446       return;
447     }
448   max_connect_per_transport = (uint32_t) tneigh;
449   my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
450   GNUNET_free (keyfile);
451   if (my_private_key == NULL)
452     {
453       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
454                   _("Transport service could not access hostkey.  Exiting.\n"));
455       GNUNET_SCHEDULER_shutdown (s);
456       fail = 1;
457       return;
458     }
459   GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
460   GNUNET_CRYPTO_hash (&my_public_key,
461                       sizeof (my_public_key), &my_identity.hashPubKey);
462
463   /* load plugins... */
464   setup_plugin_environment ();
465   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading HTTP transport plugin `%s'\n"),"libgnunet_plugin_transport_http");
466   GNUNET_asprintf (&libname, "libgnunet_plugin_transport_http");
467   api = GNUNET_PLUGIN_load (libname, &env);
468   GNUNET_free (libname);
469   if (api == NULL)
470   {
471     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
472                 _("Failed to load transport plugin for http\n"));
473     fail = 1;
474     return;
475   }
476
477   ti_timeout = GNUNET_SCHEDULER_add_delayed (sched, TEST_TIMEOUT, &task_timeout, NULL);
478
479   /* testing plugin functionality */
480   GNUNET_assert (0!=fail_notify_address_count);
481   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transport plugin returned %u addresses to connect to\n"),  fail_notify_address_count);
482
483   /* testing pretty printer with all addresses obtained from the plugin*/
484   while (addr_head != NULL)
485   {
486     cur = addr_head;
487
488     api->address_pretty_printer(NULL,"http",cur->addr,cur->addrlen,GNUNET_NO,TEST_TIMEOUT,&pretty_printer_cb,NULL);
489     addr_str = api->address_to_string(NULL,cur->addr,cur->addrlen);
490     GNUNET_assert (NULL != addr_str);
491     count_str_addr++;
492
493     tmp = addr_head->next;
494     GNUNET_free (addr_head->addr);
495     GNUNET_free (addr_head);
496     GNUNET_free ((char *) addr_str);
497     addr_head=tmp;
498   }
499   GNUNET_assert (fail_pretty_printer_count==fail_notify_address_count);
500   GNUNET_assert (fail_pretty_printer_count==count_str_addr);
501   fail_pretty_printer=GNUNET_NO;
502   fail_addr_to_str=GNUNET_NO;
503
504   /* testing finished, shutting down */
505   if ((fail_notify_address == GNUNET_NO) && (fail_pretty_printer == GNUNET_NO) && (fail_addr_to_str == GNUNET_NO) )
506     fail = 0;
507   else
508     fail = 1;
509   shutdown_clean();
510   return;
511 }
512
513
514 /**
515  * The main function for the transport service.
516  *
517  * @param argc number of arguments from the command line
518  * @param argv command line arguments
519  * @return 0 ok, 1 on error
520  */
521 int
522 main (int argc, char *const *argv)
523 {
524
525   static struct GNUNET_GETOPT_CommandLineOption options[] = {
526     GNUNET_GETOPT_OPTION_END
527   };
528   int ret;
529   char *const argv_prog[] = {
530     "test_plugin_transport_http",
531     "-c",
532     "test_plugin_transport_data_http.conf",
533     "-L",
534 #if VERBOSE
535     "DEBUG",
536 #else
537     "WARNING",
538 #endif
539     NULL
540   };
541   GNUNET_log_setup ("test_plugin_transport_http",
542 #if VERBOSE
543                     "DEBUG",
544 #else
545                     "WARNING",
546 #endif
547                     NULL);
548
549   ret = (GNUNET_OK ==
550          GNUNET_PROGRAM_run (5,
551                              argv_prog,
552                              "test_plugin_transport_http",
553                              "testcase", options, &run, NULL)) ? GNUNET_NO : GNUNET_YES;
554
555     GNUNET_DISK_directory_remove ("/tmp/test_plugin_transport_http");
556
557   return fail;
558 }
559
560 /* end of test_plugin_transport_http.c */