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