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