fix div by zero
[oweals/gnunet.git] / src / transport / transport-testing.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2006, 2009, 2015, 2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 /**
19  * @file transport-testing.c
20  * @brief testing lib for transport service
21  * @author Matthias Wachs
22  * @author Christian Grothoff
23  */
24 #include "transport-testing.h"
25
26
27 #define LOG(kind,...) GNUNET_log_from(kind, "transport-testing", __VA_ARGS__)
28
29
30 static struct GNUNET_TRANSPORT_TESTING_PeerContext *
31 find_peer_context (struct GNUNET_TRANSPORT_TESTING_Handle *tth,
32                    const struct GNUNET_PeerIdentity *peer)
33 {
34   struct GNUNET_TRANSPORT_TESTING_PeerContext *t;
35
36   for (t = tth->p_head; NULL != t; t = t->next)
37     if (0 == memcmp (&t->id,
38                      peer,
39                      sizeof (struct GNUNET_PeerIdentity)))
40       return t;
41   return NULL;
42 }
43
44
45 /**
46  * Find any connecting context matching the given pair of peers.
47  *
48  * @param p1 first peer
49  * @param p2 second peer
50  * @param cb function to call
51  * @param cb_cls closure for @a cb
52  */
53 void
54 GNUNET_TRANSPORT_TESTING_find_connecting_context (struct GNUNET_TRANSPORT_TESTING_PeerContext *p1,
55                                                   struct GNUNET_TRANSPORT_TESTING_PeerContext *p2,
56                                                   GNUNET_TRANSPORT_TESTING_ConnectContextCallback cb,
57                                                   void *cb_cls)
58 {
59   struct GNUNET_TRANSPORT_TESTING_Handle *tth = p1->tth;
60   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
61   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn;
62
63   for (cc = tth->cc_head; NULL != cc; cc = ccn)
64   {
65     ccn = cc->next;
66     if ( (cc->p1 == p1) &&
67          (cc->p2 == p2) )
68       cb (cb_cls,
69           cc);
70   }
71 }
72
73
74 static void
75 set_p1c (void *cls,
76          struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx)
77 {
78   int *found = cls;
79
80   if (NULL != found)
81     *found = GNUNET_YES;
82   cx->p1_c = GNUNET_YES;
83 }
84
85
86 static void
87 set_mq (void *cls,
88         struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx)
89 {
90   struct GNUNET_MQ_Handle *mq = cls;
91
92   cx->mq = mq;
93 }
94
95
96 static void
97 set_p2c (void *cls,
98          struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx)
99 {
100   int *found = cls;
101
102   if (NULL != found)
103     *found = GNUNET_YES;
104   cx->p2_c = GNUNET_YES;
105 }
106
107
108 static void
109 clear_p1c (void *cls,
110            struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx)
111 {
112   int *found = cls;
113
114   if (NULL != found)
115     *found = GNUNET_YES;
116   cx->p1_c = GNUNET_NO;
117 }
118
119
120 static void
121 clear_p2c (void *cls,
122          struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx)
123 {
124   int *found = cls;
125
126   if (NULL != found)
127     *found = GNUNET_YES;
128   cx->p2_c = GNUNET_NO;
129 }
130
131
132 static void *
133 notify_connect (void *cls,
134                 const struct GNUNET_PeerIdentity *peer,
135                 struct GNUNET_MQ_Handle *mq)
136 {
137   struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cls;
138   struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth;
139   char *p2_s;
140   struct GNUNET_TRANSPORT_TESTING_PeerContext *p2;
141   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
142   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn;
143   int found;
144   void *ret;
145
146   p2 = find_peer_context (p->tth,
147                           peer);
148   if (NULL != p->nc)
149     ret = p->nc (p->cb_cls,
150                  peer,
151                  mq);
152   else
153     ret = NULL;
154
155   if (NULL != p2)
156     GNUNET_asprintf (&p2_s,
157                      "%u (`%s')",
158                      p2->no,
159                      GNUNET_i2s (&p2->id));
160   else
161     GNUNET_asprintf (&p2_s,
162                      "`%s'",
163                      GNUNET_i2s (peer));
164   LOG (GNUNET_ERROR_TYPE_DEBUG,
165        "Peers %s connected to peer %u (`%s')\n",
166        p2_s,
167        p->no,
168        GNUNET_i2s (&p->id));
169   GNUNET_free (p2_s);
170   /* update flags in connecting contexts */
171   found = GNUNET_NO;
172   GNUNET_TRANSPORT_TESTING_find_connecting_context (p,
173                                                     p2,
174                                                     &set_p1c,
175                                                     &found);
176   if (GNUNET_NO == found)
177   {
178     cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest);
179     cc->p1 = p;
180     cc->p2 = p2;
181     cc->p1_c = GNUNET_YES;
182     GNUNET_CONTAINER_DLL_insert (tth->cc_head,
183                                  tth->cc_tail,
184                                  cc);
185   }
186   found = GNUNET_NO;
187   GNUNET_TRANSPORT_TESTING_find_connecting_context (p2,
188                                                     p,
189                                                     &set_p2c,
190                                                     &found);
191   if (GNUNET_NO == found)
192   {
193     cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest);
194     cc->p1 = p2;
195     cc->p2 = p;
196     cc->p1_c = GNUNET_YES;
197     GNUNET_CONTAINER_DLL_insert (tth->cc_head,
198                                  tth->cc_tail,
199                                  cc);
200   }
201   GNUNET_TRANSPORT_TESTING_find_connecting_context (p,
202                                                     p2,
203                                                     &set_mq,
204                                                     mq);
205   /* update set connected flag for all requests */
206   for (cc = tth->cc_head; NULL != cc; cc = cc->next)
207   {
208     if (GNUNET_YES == cc->connected)
209       continue;
210     if ( (GNUNET_YES == cc->p1_c) &&
211          (GNUNET_YES == cc->p2_c) )
212     {
213       cc->connected = GNUNET_YES;
214       /* stop trying to connect */
215       if (NULL != cc->tct)
216       {
217         GNUNET_SCHEDULER_cancel (cc->tct);
218         cc->tct = NULL;
219       }
220       if (NULL != cc->oh)
221       {
222         GNUNET_TRANSPORT_offer_hello_cancel (cc->oh);
223         cc->oh = NULL;
224       }
225       if (NULL != cc->ats_sh)
226       {
227         GNUNET_ATS_connectivity_suggest_cancel (cc->ats_sh);
228         cc->ats_sh = NULL;
229       }
230     }
231   }
232   /* then notify application */
233   for (cc = tth->cc_head; NULL != cc; cc = ccn)
234   {
235     ccn = cc->next;
236     if ( (GNUNET_YES == cc->connected) &&
237          (NULL != cc->cb) )
238     {
239       cc->cb (cc->cb_cls);
240       cc->cb = NULL; /* only notify once! */
241     }
242   }
243   return ret;
244 }
245
246
247 /**
248  * Offer the current HELLO of P2 to P1.
249  *
250  * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectRequest`
251  */
252 static void
253 offer_hello (void *cls);
254
255
256 static void
257 notify_disconnect (void *cls,
258                    const struct GNUNET_PeerIdentity *peer,
259                    void *handler_cls)
260 {
261   struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cls;
262   struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth;
263   char *p2_s;
264   /* Find PeerContext */
265   int no = 0;
266   struct GNUNET_TRANSPORT_TESTING_PeerContext *p2 = NULL;
267   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
268
269   p2 = find_peer_context (p->tth,
270                           peer);
271   no = p->no;
272   if (NULL != p2)
273     GNUNET_asprintf (&p2_s,
274                      "%u (`%s')",
275                      p2->no,
276                      GNUNET_i2s (&p2->id));
277   else
278     GNUNET_asprintf (&p2_s,
279                      "`%s'",
280                      GNUNET_i2s (peer));
281   LOG (GNUNET_ERROR_TYPE_DEBUG,
282        "Peers %s disconnected from peer %u (`%s')\n",
283        p2_s,
284        no,
285        GNUNET_i2s (&p->id));
286   GNUNET_free (p2_s);
287   /* notify about disconnect */
288   if (NULL != p->nd)
289     p->nd (p->cb_cls,
290            peer,
291            handler_cls);
292   if (NULL == p2)
293     return;
294   /* clear MQ, it is now invalid */
295   GNUNET_TRANSPORT_TESTING_find_connecting_context (p,
296                                                     p2,
297                                                     &set_mq,
298                                                     NULL);
299   /* update set connected flags for all requests */
300   GNUNET_TRANSPORT_TESTING_find_connecting_context (p,
301                                                     p2,
302                                                     &clear_p1c,
303                                                     NULL);
304   GNUNET_TRANSPORT_TESTING_find_connecting_context (p2,
305                                                     p,
306                                                     &clear_p2c,
307                                                     NULL);
308   /* resume connectivity requests as necessary */
309   for (cc = tth->cc_head; NULL != cc; cc = cc->next)
310   {
311     if (GNUNET_NO == cc->connected)
312       continue;
313     if ( (GNUNET_YES != cc->p1_c) ||
314          (GNUNET_YES != cc->p2_c) )
315     {
316       cc->connected = GNUNET_NO;
317       /* start trying to connect */
318       if ( (NULL == cc->tct) &&
319            (NULL == cc->oh) )
320         cc->tct = GNUNET_SCHEDULER_add_now (&offer_hello,
321                                             cc);
322       if (NULL == cc->ats_sh)
323         cc->ats_sh = GNUNET_ATS_connectivity_suggest (cc->p1->ats,
324                                                       &p2->id,
325                                                       1);
326     }
327   }
328 }
329
330
331 static void
332 get_hello (void *cb_cls,
333            const struct GNUNET_MessageHeader *message)
334 {
335   struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cb_cls;
336   struct GNUNET_PeerIdentity hello_id;
337
338   GNUNET_assert (GNUNET_OK ==
339                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *) message,
340                                       &hello_id));
341   GNUNET_assert (0 == memcmp (&hello_id,
342                               &p->id,
343                               sizeof (hello_id)));
344   GNUNET_free_non_null (p->hello);
345   p->hello = (struct GNUNET_HELLO_Message *) GNUNET_copy_message (message);
346
347   if (NULL != p->start_cb)
348   {
349     LOG (GNUNET_ERROR_TYPE_DEBUG,
350          "Peer %u (`%s') successfully started\n",
351          p->no,
352          GNUNET_i2s (&p->id));
353     p->start_cb (p->start_cb_cls);
354     p->start_cb = NULL;
355   }
356 }
357
358
359 /**
360  * Start a peer with the given configuration
361  * @param tth the testing handle
362  * @param cfgname configuration file
363  * @param peer_id a unique number to identify the peer
364  * @param handlers functions for receiving messages
365  * @param nc connect callback
366  * @param nd disconnect callback
367  * @param cb_cls closure for callback
368  * @param start_cb start callback
369  * @param start_cb_cls closure for callback
370  * @return the peer context
371  */
372 struct GNUNET_TRANSPORT_TESTING_PeerContext *
373 GNUNET_TRANSPORT_TESTING_start_peer (struct GNUNET_TRANSPORT_TESTING_Handle *tth,
374                                      const char *cfgname,
375                                      int peer_id,
376                                      const struct GNUNET_MQ_MessageHandler *handlers,
377                                      GNUNET_TRANSPORT_NotifyConnecT nc,
378                                      GNUNET_TRANSPORT_NotifyDisconnecT nd,
379                                      void *cb_cls,
380                                      GNUNET_SCHEDULER_TaskCallback start_cb,
381                                      void *start_cb_cls)
382 {
383   char *emsg = NULL;
384   struct GNUNET_TRANSPORT_TESTING_PeerContext *p;
385   struct GNUNET_PeerIdentity dummy;
386   unsigned int i;
387
388   if (GNUNET_NO == GNUNET_DISK_file_test (cfgname))
389   {
390     LOG (GNUNET_ERROR_TYPE_ERROR,
391          "File not found: `%s'\n",
392          cfgname);
393     return NULL;
394   }
395
396   p = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_PeerContext);
397   p->tth = tth;
398   p->nc = nc;
399   p->nd = nd;
400   if (NULL != handlers)
401   {
402     for (i=0;NULL != handlers[i].cb;i++) ;
403     p->handlers = GNUNET_new_array (i + 1,
404                                     struct GNUNET_MQ_MessageHandler);
405     GNUNET_memcpy (p->handlers,
406                    handlers,
407                    i * sizeof (struct GNUNET_MQ_MessageHandler));
408   }
409   if (NULL != cb_cls)
410     p->cb_cls = cb_cls;
411   else
412     p->cb_cls = p;
413   p->start_cb = start_cb;
414   if (NULL != start_cb_cls)
415     p->start_cb_cls = start_cb_cls;
416   else
417     p->start_cb_cls = p;
418   GNUNET_CONTAINER_DLL_insert (tth->p_head,
419                                tth->p_tail,
420                                p);
421
422   /* Create configuration and call testing lib to modify it */
423   p->cfg = GNUNET_CONFIGURATION_create ();
424   GNUNET_assert (GNUNET_OK ==
425                  GNUNET_CONFIGURATION_load (p->cfg, cfgname));
426   if (GNUNET_SYSERR ==
427       GNUNET_TESTING_configuration_create (tth->tl_system,
428                                            p->cfg))
429   {
430     LOG (GNUNET_ERROR_TYPE_ERROR,
431          "Testing library failed to create unique configuration based on `%s'\n",
432          cfgname);
433     GNUNET_CONFIGURATION_destroy (p->cfg);
434     GNUNET_free (p);
435     return NULL;
436   }
437
438   p->no = peer_id;
439   /* Configure peer with configuration */
440   p->peer = GNUNET_TESTING_peer_configure (tth->tl_system,
441                                            p->cfg,
442                                            p->no,
443                                            NULL,
444                                            &emsg);
445   if (NULL == p->peer)
446   {
447     LOG (GNUNET_ERROR_TYPE_ERROR,
448          "Testing library failed to create unique configuration based on `%s': `%s'\n",
449          cfgname,
450          emsg);
451     GNUNET_TRANSPORT_TESTING_stop_peer (p);
452     GNUNET_free_non_null (emsg);
453     return NULL;
454   }
455   GNUNET_free_non_null (emsg);
456   if (GNUNET_OK != GNUNET_TESTING_peer_start (p->peer))
457   {
458     LOG (GNUNET_ERROR_TYPE_ERROR,
459          "Testing library failed to create unique configuration based on `%s'\n",
460          cfgname);
461     GNUNET_TRANSPORT_TESTING_stop_peer (p);
462     return NULL;
463   }
464
465   memset (&dummy,
466           '\0',
467           sizeof (dummy));
468   GNUNET_TESTING_peer_get_identity (p->peer,
469                                     &p->id);
470   if (0 == memcmp (&dummy,
471                    &p->id,
472                    sizeof (struct GNUNET_PeerIdentity)))
473   {
474     LOG (GNUNET_ERROR_TYPE_ERROR,
475          "Testing library failed to obtain peer identity for peer %u\n",
476          p->no);
477     GNUNET_TRANSPORT_TESTING_stop_peer (p);
478     return NULL;
479   }
480   LOG (GNUNET_ERROR_TYPE_DEBUG,
481        "Peer %u configured with identity `%s'\n",
482        p->no,
483        GNUNET_i2s_full (&p->id));
484   p->tmh = GNUNET_TRANSPORT_manipulation_connect (p->cfg);
485   p->th = GNUNET_TRANSPORT_core_connect (p->cfg,
486                                          NULL,
487                                          handlers,
488                                          p,
489                                          &notify_connect,
490                                          &notify_disconnect,
491                                          NULL);
492   if ( (NULL == p->th) ||
493        (NULL == p->tmh) )
494   {
495     LOG (GNUNET_ERROR_TYPE_ERROR,
496          "Failed to connect to transport service for peer `%s': `%s'\n",
497          cfgname,
498          emsg);
499     GNUNET_TRANSPORT_TESTING_stop_peer (p);
500     return NULL;
501   }
502   p->ats = GNUNET_ATS_connectivity_init (p->cfg);
503   if (NULL == p->ats)
504   {
505     LOG (GNUNET_ERROR_TYPE_ERROR,
506          "Failed to connect to ATS service for peer `%s': `%s'\n",
507          cfgname,
508          emsg);
509     GNUNET_TRANSPORT_TESTING_stop_peer (p);
510     return NULL;
511   }
512   p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg,
513                                        GNUNET_TRANSPORT_AC_ANY,
514                                        &get_hello,
515                                        p);
516   GNUNET_assert (NULL != p->ghh);
517   return p;
518 }
519
520
521 /**
522  * Stops and restarts the given peer, sleeping (!) for 5s in between.
523  *
524  * @param p the peer
525  * @param restart_cb callback to call when restarted
526  * @param restart_cb_cls callback closure
527  * @return #GNUNET_OK in success otherwise #GNUNET_SYSERR
528  */
529 int
530 GNUNET_TRANSPORT_TESTING_restart_peer (struct GNUNET_TRANSPORT_TESTING_PeerContext *p,
531                                        GNUNET_SCHEDULER_TaskCallback restart_cb,
532                                        void *restart_cb_cls)
533 {
534   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
535   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn;
536
537   /* shutdown */
538   LOG (GNUNET_ERROR_TYPE_DEBUG,
539        "Stopping peer %u (`%s')\n",
540        p->no,
541        GNUNET_i2s (&p->id));
542   if (NULL != p->ghh)
543   {
544     GNUNET_TRANSPORT_hello_get_cancel (p->ghh);
545     p->ghh = NULL;
546   }
547   if (NULL != p->th)
548   {
549     GNUNET_TRANSPORT_core_disconnect (p->th);
550     p->th = NULL;
551   }
552   if (NULL != p->tmh)
553   {
554     GNUNET_TRANSPORT_manipulation_disconnect (p->tmh);
555     p->tmh = NULL;
556   }
557   for (cc = p->tth->cc_head; NULL != cc; cc = ccn)
558   {
559     ccn = cc->next;
560     if ( (cc->p1 == p) ||
561          (cc->p2 == p) )
562       GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc);
563   }
564   if (NULL != p->ats)
565   {
566     GNUNET_ATS_connectivity_done (p->ats);
567     p->ats = NULL;
568   }
569   if (GNUNET_SYSERR ==
570       GNUNET_TESTING_peer_stop (p->peer))
571   {
572     LOG (GNUNET_ERROR_TYPE_ERROR,
573          "Failed to stop peer %u (`%s')\n",
574          p->no,
575          GNUNET_i2s (&p->id));
576     return GNUNET_SYSERR;
577   }
578
579   sleep (5); // YUCK!
580
581   LOG (GNUNET_ERROR_TYPE_DEBUG,
582        "Restarting peer %u (`%s')\n",
583        p->no,
584        GNUNET_i2s (&p->id));
585   /* restart */
586   if (GNUNET_SYSERR == GNUNET_TESTING_peer_start (p->peer))
587   {
588     LOG (GNUNET_ERROR_TYPE_ERROR,
589          "Failed to restart peer %u (`%s')\n",
590          p->no,
591          GNUNET_i2s (&p->id));
592     return GNUNET_SYSERR;
593   }
594
595   GNUNET_assert (NULL == p->start_cb);
596   p->start_cb = restart_cb;
597   p->start_cb_cls = restart_cb_cls;
598
599   p->th = GNUNET_TRANSPORT_core_connect (p->cfg,
600                                          NULL,
601                                          p->handlers,
602                                          p,
603                                          &notify_connect,
604                                          &notify_disconnect,
605                                          NULL);
606   GNUNET_assert (NULL != p->th);
607   p->ats = GNUNET_ATS_connectivity_init (p->cfg);
608   p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg,
609                                        GNUNET_TRANSPORT_AC_ANY,
610                                        &get_hello,
611                                        p);
612   GNUNET_assert (NULL != p->ghh);
613   return GNUNET_OK;
614 }
615
616
617 /**
618  * Shutdown the given peer
619  *
620  * @param p the peer
621  */
622 void
623 GNUNET_TRANSPORT_TESTING_stop_peer (struct GNUNET_TRANSPORT_TESTING_PeerContext *p)
624 {
625   struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth;
626   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
627   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn;
628
629   for (cc = tth->cc_head; NULL != cc; cc = ccn)
630   {
631     ccn = cc->next;
632     if ( (cc->p1 == p) ||
633          (cc->p2 == p) )
634       GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc);
635   }
636   if (NULL != p->ghh)
637   {
638     GNUNET_TRANSPORT_hello_get_cancel (p->ghh);
639     p->ghh = NULL;
640   }
641   if (NULL != p->tmh)
642   {
643     GNUNET_TRANSPORT_manipulation_disconnect (p->tmh);
644     p->tmh = NULL;
645   }
646   if (NULL != p->th)
647   {
648     GNUNET_TRANSPORT_core_disconnect (p->th);
649     p->th = NULL;
650   }
651   if (NULL != p->peer)
652   {
653     if (GNUNET_OK !=
654         GNUNET_TESTING_peer_stop (p->peer))
655     {
656       LOG (GNUNET_ERROR_TYPE_DEBUG,
657            "Testing lib failed to stop peer %u (`%s')\n",
658            p->no,
659            GNUNET_i2s (&p->id));
660     }
661     GNUNET_TESTING_peer_destroy (p->peer);
662     p->peer = NULL;
663   }
664   if (NULL != p->ats)
665   {
666     GNUNET_ATS_connectivity_done (p->ats);
667     p->ats = NULL;
668   }
669   if (NULL != p->hello)
670   {
671     GNUNET_free (p->hello);
672     p->hello = NULL;
673   }
674   if (NULL != p->cfg)
675   {
676     GNUNET_CONFIGURATION_destroy (p->cfg);
677     p->cfg = NULL;
678   }
679   if (NULL != p->handlers)
680   {
681     GNUNET_free (p->handlers);
682     p->handlers = NULL;
683   }
684   GNUNET_CONTAINER_DLL_remove (tth->p_head,
685                                tth->p_tail,
686                                p);
687   LOG (GNUNET_ERROR_TYPE_DEBUG,
688        "Peer %u (`%s') stopped\n",
689        p->no,
690        GNUNET_i2s (&p->id));
691   GNUNET_free (p);
692 }
693
694
695 /**
696  * Function called after the HELLO was passed to the
697  * transport service.
698  */
699 static void
700 hello_offered (void *cls)
701 {
702   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc = cls;
703
704   cc->oh = NULL;
705   cc->tct = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
706                                           &offer_hello,
707                                           cc);
708 }
709
710
711 /**
712  * Offer the current HELLO of P2 to P1.
713  *
714  * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectRequest`
715  */
716 static void
717 offer_hello (void *cls)
718 {
719   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc = cls;
720   struct GNUNET_TRANSPORT_TESTING_PeerContext *p1 = cc->p1;
721   struct GNUNET_TRANSPORT_TESTING_PeerContext *p2 = cc->p2;
722
723   cc->tct = NULL;
724   {
725     char *p2_s = GNUNET_strdup (GNUNET_i2s (&p2->id));
726
727     LOG (GNUNET_ERROR_TYPE_DEBUG,
728          "Asking peer %u (`%s') to connect peer %u (`%s'), providing HELLO with %u bytes\n",
729          p1->no,
730          GNUNET_i2s (&p1->id),
731          p2->no,
732          p2_s,
733          GNUNET_HELLO_size (cc->p2->hello));
734     GNUNET_free (p2_s);
735   }
736
737   if (NULL != cc->oh)
738     GNUNET_TRANSPORT_offer_hello_cancel (cc->oh);
739   cc->oh =
740     GNUNET_TRANSPORT_offer_hello (cc->p1->cfg,
741                                   (const struct GNUNET_MessageHeader *) cc->p2->hello,
742                                   &hello_offered,
743                                   cc);
744 }
745
746
747 /**
748  * Initiate a connection from p1 to p2 by offering p1 p2's HELLO message
749  *
750  * Remarks: start_peer's notify_connect callback can be called before.
751  *
752  * @param tth transport testing handle
753  * @param p1 peer 1
754  * @param p2 peer 2
755  * @param cb the callback to call when both peers notified that they are connected
756  * @param cls callback cls
757  * @return a connect request handle
758  */
759 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *
760 GNUNET_TRANSPORT_TESTING_connect_peers (struct GNUNET_TRANSPORT_TESTING_PeerContext *p1,
761                                         struct GNUNET_TRANSPORT_TESTING_PeerContext *p2,
762                                         GNUNET_SCHEDULER_TaskCallback cb,
763                                         void *cls)
764 {
765   struct GNUNET_TRANSPORT_TESTING_Handle *tth = p1->tth;
766   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
767   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn;
768
769   ccn = NULL;
770   for (cc = tth->cc_head; NULL != cc; cc = cc->next)
771   {
772     if ( (cc->p1 == p1) &&
773          (cc->p2 == p2) )
774     {
775       ccn = cc;
776       break;
777     }
778   }
779
780   cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest);
781   cc->p1 = p1;
782   cc->p2 = p2;
783   cc->cb = cb;
784   if (NULL != cls)
785     cc->cb_cls = cls;
786   else
787     cc->cb_cls = cc;
788   if (NULL != ccn)
789   {
790     cc->p1_c = ccn->p1_c;
791     cc->p2_c = ccn->p2_c;
792     cc->connected = ccn->connected;
793   }
794   GNUNET_CONTAINER_DLL_insert (tth->cc_head,
795                                tth->cc_tail,
796                                cc);
797   cc->tct = GNUNET_SCHEDULER_add_now (&offer_hello,
798                                       cc);
799   cc->ats_sh = GNUNET_ATS_connectivity_suggest (cc->p1->ats,
800                                                 &p2->id,
801                                                 1);
802   LOG (GNUNET_ERROR_TYPE_DEBUG,
803        "New connect request %p\n",
804        cc);
805   return cc;
806 }
807
808
809 /**
810  * Cancel the request to connect two peers
811  * Tou MUST cancel the request if you stop the peers before the peers connected succesfully
812  *
813  * @param tth transport testing handle
814  * @param cc a connect request handle
815  */
816 void
817 GNUNET_TRANSPORT_TESTING_connect_peers_cancel (struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc)
818 {
819   struct GNUNET_TRANSPORT_TESTING_Handle *tth = cc->p1->tth;
820
821   LOG (GNUNET_ERROR_TYPE_DEBUG,
822        "Canceling connect request!\n");
823   if (NULL != cc->tct)
824   {
825     GNUNET_SCHEDULER_cancel (cc->tct);
826     cc->tct = NULL;
827   }
828   if (NULL != cc->oh)
829   {
830     GNUNET_TRANSPORT_offer_hello_cancel (cc->oh);
831     cc->oh = NULL;
832   }
833   if (NULL != cc->ats_sh)
834   {
835     GNUNET_ATS_connectivity_suggest_cancel (cc->ats_sh);
836     cc->ats_sh = NULL;
837   }
838   GNUNET_CONTAINER_DLL_remove (tth->cc_head,
839                                tth->cc_tail,
840                                cc);
841   GNUNET_free (cc);
842 }
843
844
845 /**
846  * Clean up the transport testing
847  *
848  * @param tth transport testing handle
849  */
850 void
851 GNUNET_TRANSPORT_TESTING_done (struct GNUNET_TRANSPORT_TESTING_Handle *tth)
852 {
853   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
854   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ct;
855   struct GNUNET_TRANSPORT_TESTING_PeerContext *p;
856   struct GNUNET_TRANSPORT_TESTING_PeerContext *t;
857
858   if (NULL == tth)
859     return;
860   cc = tth->cc_head;
861   while (NULL != cc)
862   {
863     ct = cc->next;
864     LOG (GNUNET_ERROR_TYPE_ERROR,
865          "Developer forgot to cancel connect request!\n");
866     GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc);
867     cc = ct;
868   }
869   p = tth->p_head;
870   while (NULL != p)
871   {
872     t = p->next;
873     LOG (GNUNET_ERROR_TYPE_ERROR,
874          "Developer forgot to stop peer!\n");
875     GNUNET_TRANSPORT_TESTING_stop_peer (p);
876     p = t;
877   }
878   GNUNET_TESTING_system_destroy (tth->tl_system,
879                                  GNUNET_YES);
880
881   GNUNET_free (tth);
882 }
883
884
885 /**
886  * Initialize the transport testing
887  *
888  * @return transport testing handle
889  */
890 struct GNUNET_TRANSPORT_TESTING_Handle *
891 GNUNET_TRANSPORT_TESTING_init ()
892 {
893   struct GNUNET_TRANSPORT_TESTING_Handle *tth;
894
895   tth = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_Handle);
896   tth->tl_system = GNUNET_TESTING_system_create ("transport-testing",
897                                                  NULL,
898                                                  NULL,
899                                                  NULL);
900   if (NULL == tth->tl_system)
901   {
902     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
903                 "Failed to initialize testing library!\n");
904     GNUNET_free (tth);
905     return NULL;
906   }
907   return tth;
908 }
909
910 /* end of transport-testing.c */