-fixing
[oweals/gnunet.git] / src / mesh / test_mesh_regex.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 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 mesh/test_mesh_regex.c
22  *
23  * @brief Test for regex announce / by_string connect.
24  * based on the 2dtorus testcase
25  */
26 #include "platform.h"
27 #include "gnunet_testing_lib.h"
28 #include "gnunet_mesh_service.h"
29
30 #define REMOVE_DIR GNUNET_YES
31 #define MESH_REGEX_PEERS 4
32
33 /**
34  * How long until we give up on connecting the peers?
35  */
36 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
37
38 /**
39  * Time to wait for stuff that should be rather fast
40  */
41 #define SHORT_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
42
43 /**
44  * Which strings have been found & connected.
45  */
46 static int ok[MESH_REGEX_PEERS];
47
48 /**
49  * How many connects have happened.
50  */
51 static int regex_peers;
52
53 /**
54  * Be verbose
55  */
56 static int verbose;
57
58 /**
59  * Total number of peers in the test.
60  */
61 static unsigned long long num_peers;
62
63 /**
64  * Global configuration file
65  */
66 static struct GNUNET_CONFIGURATION_Handle *testing_cfg;
67
68 /**
69  * Total number of currently running peers.
70  */
71 static unsigned long long peers_running;
72
73 /**
74  * Total number of successful connections in the whole network.
75  */
76 static unsigned int total_connections;
77
78 /**
79  * Total number of failed connections in the whole network.
80  */
81 static unsigned int failed_connections;
82
83 /**
84  * The currently running peer group.
85  */
86 static struct GNUNET_TESTING_PeerGroup *pg;
87
88 /**
89  * Task called to disconnect peers
90  */
91 static GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
92
93 /**
94  * Task called to shutdown test.
95  */
96 static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle;
97
98 /**
99  * Mesh handle for connecting peer.
100  */
101 static struct GNUNET_MESH_Handle *h1;
102
103 /**
104  * Mesh handle for announcing peers.
105  */
106 static struct GNUNET_MESH_Handle *h2[MESH_REGEX_PEERS];
107
108 /**
109  * Tunnel handles for announcing peer.
110  */
111 static struct GNUNET_MESH_Tunnel *t[MESH_REGEX_PEERS];
112
113 /**
114  * Incoming tunnels for announcing peers.
115  */
116 static struct GNUNET_MESH_Tunnel *incoming_t[MESH_REGEX_PEERS];
117
118 /**
119  * Regular expressions for the announces.
120  */
121 static char *regexes[MESH_REGEX_PEERS] = {"(0|1)"
122                                           "(0|1)"
123                                           "23456789ABC",
124
125                                           "0123456789A*BC",
126
127                                           "0*123456789ABC*",
128
129                                           "GNUNETVPN0001000IPEX401110011101100100000111(0|1)*"};
130
131
132 /**
133  * Service strings to look for.
134  */
135 static char *strings[MESH_REGEX_PEERS] = {"1123456789ABC",
136
137                                           "0123456789AABC",
138
139                                           "00123456789ABCCCC",
140
141                                           "GNUNETVPN0001000IPEX401110011101100100000111"};
142
143 /**
144  * Check whether peers successfully shut down.
145  *
146  * @param cls Closure (unused).
147  * @param emsg Error message, NULL on success.
148  */
149 static void
150 shutdown_callback (void *cls, const char *emsg)
151 {
152   unsigned int i;
153
154   if (emsg != NULL)
155   {
156     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
157                 "test: Shutdown of peers failed! (%s)\n", emsg);
158     for (i = 0; i < MESH_REGEX_PEERS; i++)
159       ok[i] = GNUNET_NO;
160   }
161   else
162   {
163     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
164                 "test: All peers successfully shut down!\n");
165   }
166   GNUNET_CONFIGURATION_destroy (testing_cfg);
167 }
168
169
170 /**
171  * Task to run for shutdown: stops peers, ends test.
172  *
173  * @param cls Closure (not used).
174  * @param tc TaskContext.
175  *
176  */
177 static void
178 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
179 {
180   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Ending test.\n");
181   shutdown_handle = GNUNET_SCHEDULER_NO_TASK;
182   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
183 }
184
185
186 /**
187  * Ends test: Disconnects peers and calls shutdown.
188  * @param cls Closure (not used).
189  * @param tc TaskContext.
190  */
191 static void
192 disconnect_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
193 {
194   unsigned int i;
195
196   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: disconnecting peers\n");
197
198   for (i = 0; i < MESH_REGEX_PEERS; i++)
199   {
200     GNUNET_MESH_tunnel_destroy (t[i]);
201     GNUNET_MESH_disconnect (h2[i]);
202   }
203   GNUNET_MESH_disconnect (h1);
204   if (GNUNET_SCHEDULER_NO_TASK != shutdown_handle)
205   {
206     GNUNET_SCHEDULER_cancel (shutdown_handle);
207   }
208   shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
209 }
210
211 /**
212  * Function called whenever an inbound tunnel is destroyed.  Should clean up
213  * any associated state.
214  *
215  * @param cls closure (set from GNUNET_MESH_connect)
216  * @param tunnel connection to the other end (henceforth invalid)
217  * @param tunnel_ctx place where local state associated
218  *                   with the tunnel is stored
219  */
220 static void
221 tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
222                 void *tunnel_ctx)
223 {
224   long i = (long) cls;
225
226   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
227               "Incoming tunnel disconnected at peer %d\n",
228               i);
229   return;
230 }
231
232
233 /**
234  * Method called whenever a tunnel falls apart.
235  *
236  * @param cls closure
237  * @param peer peer identity the tunnel stopped working with
238  */
239 static void
240 dh (void *cls, const struct GNUNET_PeerIdentity *peer)
241 {
242   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
243               "peer %s disconnected\n",
244               GNUNET_i2s (peer));
245   return;
246 }
247
248 /**
249  * Function called to notify a client about the connection
250  * begin ready to queue more data.  "buf" will be
251  * NULL and "size" zero if the connection was closed for
252  * writing in the meantime.
253  *
254  * @param cls closure
255  * @param size number of bytes available in buf
256  * @param buf where the callee should write the message
257  * @return number of bytes written to buf
258  */
259 static size_t
260 data_ready (void *cls, size_t size, void *buf)
261 {
262   struct GNUNET_MessageHeader *m = buf;
263
264   if (NULL == buf || size < sizeof(struct GNUNET_MessageHeader))
265     return 0;
266   m->type = htons (1);
267   m->size = htons (sizeof(struct GNUNET_MessageHeader));
268   return sizeof(struct GNUNET_MessageHeader);
269 }
270
271 /**
272  * Method called whenever a peer connects to a tunnel.
273  *
274  * @param cls closure
275  * @param peer peer identity the tunnel was created to, NULL on timeout
276  * @param atsi performance data for the connection
277  */
278 static void
279 ch (void *cls, const struct GNUNET_PeerIdentity *peer,
280     const struct GNUNET_ATS_Information *atsi)
281 {
282   long i = (long) cls;
283
284   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
285               "Peer connected: %s\n",
286               GNUNET_i2s (peer));
287   regex_peers++;
288
289   GNUNET_MESH_notify_transmit_ready(t[i], GNUNET_NO,
290                                     GNUNET_TIME_UNIT_FOREVER_REL,
291                                     peer,
292                                     sizeof(struct GNUNET_MessageHeader),
293                                     &data_ready, NULL);
294 }
295
296 /**
297  * Method called whenever another peer has added us to a tunnel
298  * the other peer initiated.
299  *
300  * @param cls closure
301  * @param tunnel new handle to the tunnel
302  * @param initiator peer that started the tunnel
303  * @param atsi performance information for the tunnel
304  * @return initial tunnel context for the tunnel
305  *         (can be NULL -- that's not an error)
306  */
307 static void *
308 incoming_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
309                  const struct GNUNET_PeerIdentity *initiator,
310                  const struct GNUNET_ATS_Information *atsi)
311 {
312   long i = (long) cls;
313   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
314               "Incoming tunnel from %s to peer %d\n",
315               GNUNET_i2s (initiator), (long) cls);
316   if (i > 1L && i <= 1L + MESH_REGEX_PEERS)
317   {
318     incoming_t[i - 2] = tunnel;
319     ok[i - 2] = GNUNET_OK;
320   }
321   else
322   {
323     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
324                 "Incoming tunnel for unknown client %lu\n", (long) cls);
325   }
326   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
327   {
328     GNUNET_SCHEDULER_cancel (disconnect_task);
329     disconnect_task =
330         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_peers, NULL);
331   }
332   return NULL;
333 }
334
335 /**
336  * Function is called whenever a message is received.
337  *
338  * @param cls closure (set from GNUNET_MESH_connect)
339  * @param tunnel connection to the other end
340  * @param tunnel_ctx place to store local state associated with the tunnel
341  * @param sender who sent the message
342  * @param message the actual message
343  * @param atsi performance data for the connection
344  * @return GNUNET_OK to keep the connection open,
345  *         GNUNET_SYSERR to close it (signal serious error)
346  */
347 int
348 data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx,
349                const struct GNUNET_PeerIdentity *sender,
350                const struct GNUNET_MessageHeader *message,
351                const struct GNUNET_ATS_Information *atsi)
352 {
353   int i;
354   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
355               "test: GOT DATA!\n");
356   for (i = 0; i < MESH_REGEX_PEERS; i++)
357   {
358     if (GNUNET_OK != ok[i]) {
359       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360               "test: %u DATA MISSING!\n", i);
361       return GNUNET_OK;
362     }
363   }
364   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
365               "test: EVERYONE GOT DATA, FINISHING!\n");
366   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
367   {
368     GNUNET_SCHEDULER_cancel (disconnect_task);
369     disconnect_task =
370         GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL);
371   }
372   return GNUNET_OK;
373 }
374
375 /**
376  * Handlers, for diverse services
377  */
378 static struct GNUNET_MESH_MessageHandler handlers[] = {
379   {&data_callback, 1, sizeof (struct GNUNET_MessageHeader)},
380   {NULL, 0, 0}
381 };
382
383
384 /**
385  * peergroup_ready: start test when all peers are connected
386  * @param cls closure
387  * @param emsg error message
388  */
389 static void
390 peergroup_ready (void *cls, const char *emsg)
391 {
392   GNUNET_MESH_ApplicationType app;
393   struct GNUNET_TESTING_Daemon *d;
394   unsigned int i;
395
396   if (emsg != NULL)
397   {
398     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
399                 "test: Peergroup callback called with error, aborting test!\n");
400     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test: Error from testing: `%s'\n",
401                 emsg);
402     GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
403     return;
404   }
405   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
406               "Peer Group started successfully with %u connections\n",
407               total_connections);
408   peers_running = GNUNET_TESTING_daemons_running (pg);
409   if (0 < failed_connections)
410   {
411     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: %u connections have FAILED!\n",
412                 failed_connections);
413     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL);
414     return;
415   }
416   disconnect_task =
417     GNUNET_SCHEDULER_add_delayed (TIMEOUT, &disconnect_peers, NULL);
418   d = GNUNET_TESTING_daemon_get (pg, 1);
419
420   app = (GNUNET_MESH_ApplicationType) 0;
421
422   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
423               "Connect to mesh\n");
424   h1 = GNUNET_MESH_connect (d->cfg, (void *) 1L,
425                             NULL,
426                             NULL,
427                             handlers,
428                             &app);
429   regex_peers = 0;
430   for (i = 0; i < MESH_REGEX_PEERS; i++)
431   {
432     ok[i] = GNUNET_NO;
433     d = GNUNET_TESTING_daemon_get (pg, 10 + i);
434     h2[i] = GNUNET_MESH_connect (d->cfg, (void *) (long) (i + 2),
435                                  &incoming_tunnel,
436                                  &tunnel_cleaner,
437                                  handlers,
438                                  &app);
439     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
440                 "Announce REGEX %u: %s\n", i, regexes[i]);
441     GNUNET_MESH_announce_regex (h2[i], regexes[i], 1);
442   }
443
444   for (i = 0; i < MESH_REGEX_PEERS; i++)
445   {
446 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
447                 "Create tunnel\n");
448     t[i] = GNUNET_MESH_tunnel_create (h1, NULL, &ch, &dh, (void *) (long) i);
449     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
450                 "Connect by string %s\n", strings[i]);
451     GNUNET_MESH_peer_request_connect_by_string (t[i], strings[i]);
452   }
453   /* connect handler = success, timeout = error */
454
455 }
456
457
458 /**
459  * Function that will be called whenever two daemons are connected by
460  * the testing library.
461  *
462  * @param cls closure
463  * @param first peer id for first daemon
464  * @param second peer id for the second daemon
465  * @param distance distance between the connected peers
466  * @param first_cfg config for the first daemon
467  * @param second_cfg config for the second daemon
468  * @param first_daemon handle for the first daemon
469  * @param second_daemon handle for the second daemon
470  * @param emsg error message (NULL on success)
471  */
472 static void
473 connect_cb (void *cls, const struct GNUNET_PeerIdentity *first,
474             const struct GNUNET_PeerIdentity *second, uint32_t distance,
475             const struct GNUNET_CONFIGURATION_Handle *first_cfg,
476             const struct GNUNET_CONFIGURATION_Handle *second_cfg,
477             struct GNUNET_TESTING_Daemon *first_daemon,
478             struct GNUNET_TESTING_Daemon *second_daemon, const char *emsg)
479 {
480   if (emsg == NULL)
481   {
482     total_connections++;
483   }
484   else
485   {
486     failed_connections++;
487     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
488                 "test: Problem with new connection (%s)\n", emsg);
489     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test:   (%s)\n", GNUNET_i2s (first));
490     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test:   (%s)\n", GNUNET_i2s (second));
491   }
492 }
493
494
495 /**
496  * run: load configuration options and schedule test to run (start peergroup)
497  * @param cls closure
498  * @param args argv
499  * @param cfgfile configuration file name (can be NULL)
500  * @param cfg configuration handle
501  */
502 static void
503 run (void *cls, char *const *args, const char *cfgfile,
504      const struct GNUNET_CONFIGURATION_Handle *cfg)
505 {
506   struct GNUNET_TESTING_Host *hosts;
507
508   total_connections = 0;
509   failed_connections = 0;
510   testing_cfg = GNUNET_CONFIGURATION_dup (cfg);
511
512   GNUNET_log_setup ("test_mesh_regex",
513                     "WARNING",
514                     NULL);
515   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Starting daemons.\n");
516   if (GNUNET_OK !=
517       GNUNET_CONFIGURATION_get_value_number (testing_cfg, "testing_old",
518                                              "num_peers", &num_peers))
519   {
520     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
521                 "Option TESTING:NUM_PEERS is required!\n");
522     return;
523   }
524
525   hosts = GNUNET_TESTING_hosts_load (testing_cfg);
526
527   pg = GNUNET_TESTING_peergroup_start (testing_cfg, num_peers, TIMEOUT,
528                                        &connect_cb, &peergroup_ready, NULL,
529                                        hosts);
530   GNUNET_assert (pg != NULL);
531   shutdown_handle =
532     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
533                                     &shutdown_task, NULL);
534 }
535
536
537 /**
538  * test_mesh_regex command line options
539  */
540 static struct GNUNET_GETOPT_CommandLineOption options[] = {
541   {'V', "verbose", NULL,
542    gettext_noop ("be verbose (print progress information)"),
543    0, &GNUNET_GETOPT_set_one, &verbose},
544   GNUNET_GETOPT_OPTION_END
545 };
546
547
548 /**
549  * Main: start test
550  */
551 int
552 main (int argc, char *argv[])
553 {
554   char *const argv2[] = {
555     argv[0],
556     "-c",
557     "test_mesh_2dtorus.conf",
558     NULL
559   };
560   int result;
561   unsigned int i;
562
563   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Start\n");
564
565
566   GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2,
567                       "test_mesh_regex",
568                       gettext_noop ("Test mesh regex integration."),
569                       options, &run, NULL);
570 #if REMOVE_DIR
571   GNUNET_DISK_directory_remove ("/tmp/test_mesh_2dtorus");
572 #endif
573   result = GNUNET_OK;
574   for (i = 0; i < MESH_REGEX_PEERS; i++)
575   {
576     if (GNUNET_OK != ok[i])
577     {
578       result = GNUNET_SYSERR;
579       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
580                   "COULD NOT CONNECT TO %u: %s!\n",
581                   i, strings[i]);
582     }
583   }
584   if (GNUNET_OK != result || regex_peers != MESH_REGEX_PEERS)
585   {
586     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
587                 "test: FAILED! %u connected peers\n",
588                 regex_peers);
589     return 1;
590   }
591   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "test: success\n");
592   return 0;
593 }
594
595 /* end of test_mesh_regex.c */