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