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