psycstore
[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_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 (p2 != NULL)
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   p->start_cb_cls = start_cb_cls;
417   GNUNET_CONTAINER_DLL_insert (tth->p_head,
418                                tth->p_tail,
419                                p);
420
421   /* Create configuration and call testing lib to modify it */
422   p->cfg = GNUNET_CONFIGURATION_create ();
423   GNUNET_assert (GNUNET_OK ==
424                  GNUNET_CONFIGURATION_load (p->cfg, cfgname));
425   if (GNUNET_SYSERR ==
426       GNUNET_TESTING_configuration_create (tth->tl_system,
427                                            p->cfg))
428   {
429     LOG (GNUNET_ERROR_TYPE_ERROR,
430          "Testing library failed to create unique configuration based on `%s'\n",
431          cfgname);
432     GNUNET_CONFIGURATION_destroy (p->cfg);
433     GNUNET_free (p);
434     return NULL;
435   }
436
437   p->no = peer_id;
438   /* Configure peer with configuration */
439   p->peer = GNUNET_TESTING_peer_configure (tth->tl_system,
440                                            p->cfg,
441                                            p->no,
442                                            NULL,
443                                            &emsg);
444   if (NULL == p->peer)
445   {
446     LOG (GNUNET_ERROR_TYPE_ERROR,
447          "Testing library failed to create unique configuration based on `%s': `%s'\n",
448          cfgname,
449          emsg);
450     GNUNET_TRANSPORT_TESTING_stop_peer (p);
451     GNUNET_free_non_null (emsg);
452     return NULL;
453   }
454   GNUNET_free_non_null (emsg);
455   if (GNUNET_OK != GNUNET_TESTING_peer_start (p->peer))
456   {
457     LOG (GNUNET_ERROR_TYPE_ERROR,
458          "Testing library failed to create unique configuration based on `%s'\n",
459          cfgname);
460     GNUNET_TRANSPORT_TESTING_stop_peer (p);
461     return NULL;
462   }
463
464   memset (&dummy,
465           '\0',
466           sizeof (dummy));
467   GNUNET_TESTING_peer_get_identity (p->peer,
468                                     &p->id);
469   if (0 == memcmp (&dummy,
470                    &p->id,
471                    sizeof (struct GNUNET_PeerIdentity)))
472   {
473     LOG (GNUNET_ERROR_TYPE_ERROR,
474          "Testing library failed to obtain peer identity for peer %u\n",
475          p->no);
476     GNUNET_TRANSPORT_TESTING_stop_peer (p);
477     return NULL;
478   }
479   LOG (GNUNET_ERROR_TYPE_DEBUG,
480        "Peer %u configured with identity `%s'\n",
481        p->no,
482        GNUNET_i2s_full (&p->id));
483   p->tmh = GNUNET_TRANSPORT_manipulation_connect (p->cfg);
484   p->th = GNUNET_TRANSPORT_core_connect (p->cfg,
485                                          NULL,
486                                          handlers,
487                                          p,
488                                          &notify_connect,
489                                          &notify_disconnect,
490                                          NULL);
491   if ( (NULL == p->th) ||
492        (NULL == p->tmh) )
493   {
494     LOG (GNUNET_ERROR_TYPE_ERROR,
495          "Failed to connect to transport service for peer `%s': `%s'\n",
496          cfgname,
497          emsg);
498     GNUNET_TRANSPORT_TESTING_stop_peer (p);
499     return NULL;
500   }
501   p->ats = GNUNET_ATS_connectivity_init (p->cfg);
502   if (NULL == p->ats)
503   {
504     LOG (GNUNET_ERROR_TYPE_ERROR,
505          "Failed to connect to ATS service for peer `%s': `%s'\n",
506          cfgname,
507          emsg);
508     GNUNET_TRANSPORT_TESTING_stop_peer (p);
509     return NULL;
510   }
511   p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg,
512                                        GNUNET_TRANSPORT_AC_ANY,
513                                        &get_hello,
514                                        p);
515   GNUNET_assert (NULL != p->ghh);
516   return p;
517 }
518
519
520 /**
521  * Stops and restarts the given peer, sleeping (!) for 5s in between.
522  *
523  * @param p the peer
524  * @param restart_cb callback to call when restarted
525  * @param restart_cb_cls callback closure
526  * @return #GNUNET_OK in success otherwise #GNUNET_SYSERR
527  */
528 int
529 GNUNET_TRANSPORT_TESTING_restart_peer (struct GNUNET_TRANSPORT_TESTING_PeerContext *p,
530                                        GNUNET_SCHEDULER_TaskCallback restart_cb,
531                                        void *restart_cb_cls)
532 {
533   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
534   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn;
535  
536   /* shutdown */
537   LOG (GNUNET_ERROR_TYPE_DEBUG,
538        "Stopping peer %u (`%s')\n",
539        p->no,
540        GNUNET_i2s (&p->id));
541   if (NULL != p->ghh)
542   {
543     GNUNET_TRANSPORT_hello_get_cancel (p->ghh);
544     p->ghh = NULL;
545   }
546   if (NULL != p->th)
547   {
548     GNUNET_TRANSPORT_core_disconnect (p->th);
549     p->th = NULL;
550   }
551   if (NULL != p->tmh)
552   {
553     GNUNET_TRANSPORT_manipulation_disconnect (p->tmh);
554     p->tmh = NULL;
555   }
556   for (cc = p->tth->cc_head; NULL != cc; cc = ccn)
557   {
558     ccn = cc->next;
559     if ( (cc->p1 == p) ||
560          (cc->p2 == p) )
561       GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc);
562   }
563   if (NULL != p->ats)
564   {
565     GNUNET_ATS_connectivity_done (p->ats);
566     p->ats = NULL;
567   }
568   if (GNUNET_SYSERR ==
569       GNUNET_TESTING_peer_stop (p->peer))
570   {
571     LOG (GNUNET_ERROR_TYPE_ERROR,
572          "Failed to stop peer %u (`%s')\n",
573          p->no,
574          GNUNET_i2s (&p->id));
575     return GNUNET_SYSERR;
576   }
577
578   sleep (5); // YUCK!
579
580   LOG (GNUNET_ERROR_TYPE_DEBUG,
581        "Restarting peer %u (`%s')\n",
582        p->no,
583        GNUNET_i2s (&p->id));
584   /* restart */
585   if (GNUNET_SYSERR == GNUNET_TESTING_peer_start (p->peer))
586   {
587     LOG (GNUNET_ERROR_TYPE_ERROR,
588          "Failed to restart peer %u (`%s')\n",
589          p->no,
590          GNUNET_i2s (&p->id));
591     return GNUNET_SYSERR;
592   }
593
594   GNUNET_assert (NULL == p->start_cb);
595   p->start_cb = restart_cb;
596   p->start_cb_cls = restart_cb_cls;
597
598   p->th = GNUNET_TRANSPORT_core_connect (p->cfg,
599                                          NULL,
600                                          p->handlers,
601                                          p,
602                                          &notify_connect,
603                                          &notify_disconnect,
604                                          NULL);
605   GNUNET_assert (NULL != p->th);
606   p->ats = GNUNET_ATS_connectivity_init (p->cfg);
607   p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg,
608                                        GNUNET_TRANSPORT_AC_ANY,
609                                        &get_hello,
610                                        p);
611   GNUNET_assert (NULL != p->ghh);
612   return GNUNET_OK;
613 }
614
615
616 /**
617  * Shutdown the given peer
618  *
619  * @param p the peer
620  */
621 void
622 GNUNET_TRANSPORT_TESTING_stop_peer (struct GNUNET_TRANSPORT_TESTING_PeerContext *p)
623 {
624   struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth;
625   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
626   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn;
627
628   for (cc = tth->cc_head; NULL != cc; cc = ccn)
629   {
630     ccn = cc->next;
631     if ( (cc->p1 == p) ||
632          (cc->p2 == p) )
633       GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc);
634   }
635   if (NULL != p->ghh)
636   {
637     GNUNET_TRANSPORT_hello_get_cancel (p->ghh);
638     p->ghh = NULL;
639   }
640   if (NULL != p->tmh)
641   {
642     GNUNET_TRANSPORT_manipulation_disconnect (p->tmh);
643     p->tmh = NULL;
644   }
645   if (NULL != p->th)
646   {
647     GNUNET_TRANSPORT_core_disconnect (p->th);
648     p->th = NULL;
649   }
650   if (NULL != p->peer)
651   {
652     if (GNUNET_OK !=
653         GNUNET_TESTING_peer_stop (p->peer))
654     {
655       LOG (GNUNET_ERROR_TYPE_DEBUG,
656            "Testing lib failed to stop peer %u (`%s')\n",
657            p->no,
658            GNUNET_i2s (&p->id));
659     }
660     GNUNET_TESTING_peer_destroy (p->peer);
661     p->peer = NULL;
662   }
663   if (NULL != p->ats)
664   {
665     GNUNET_ATS_connectivity_done (p->ats);
666     p->ats = NULL;
667   }
668   if (NULL != p->hello)
669   {
670     GNUNET_free (p->hello);
671     p->hello = NULL;
672   }
673   if (NULL != p->cfg)
674   {
675     GNUNET_CONFIGURATION_destroy (p->cfg);
676     p->cfg = NULL;
677   }
678   GNUNET_CONTAINER_DLL_remove (tth->p_head,
679                                tth->p_tail,
680                                p);
681   LOG (GNUNET_ERROR_TYPE_DEBUG,
682        "Peer %u (`%s') stopped\n",
683        p->no,
684        GNUNET_i2s (&p->id));
685   GNUNET_free (p);
686 }
687
688
689 /**
690  * Function called after the HELLO was passed to the
691  * transport service.
692  */
693 static void
694 hello_offered (void *cls)
695 {
696   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc = cls;
697
698   cc->oh = NULL;
699   cc->tct = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
700                                           &offer_hello,
701                                           cc);
702 }
703
704
705 /**
706  * Offer the current HELLO of P2 to P1.
707  *
708  * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectRequest`
709  */
710 static void
711 offer_hello (void *cls)
712 {
713   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc = cls;
714   struct GNUNET_TRANSPORT_TESTING_PeerContext *p1 = cc->p1;
715   struct GNUNET_TRANSPORT_TESTING_PeerContext *p2 = cc->p2;
716
717   cc->tct = NULL;
718   {
719     char *p2_s = GNUNET_strdup (GNUNET_i2s (&p2->id));
720
721     LOG (GNUNET_ERROR_TYPE_DEBUG,
722          "Asking peer %u (`%s') to connect peer %u (`%s'), providing HELLO with %u bytes\n",
723          p1->no,
724          GNUNET_i2s (&p1->id),
725          p2->no,
726          p2_s,
727          GNUNET_HELLO_size (cc->p2->hello));
728     GNUNET_free (p2_s);
729   }
730
731   if (NULL != cc->oh)
732     GNUNET_TRANSPORT_offer_hello_cancel (cc->oh);
733   cc->oh =
734     GNUNET_TRANSPORT_offer_hello (cc->p1->cfg,
735                                   (const struct GNUNET_MessageHeader *) cc->p2->hello,
736                                   &hello_offered,
737                                   cc);
738 }
739
740
741 /**
742  * Initiate a connection from p1 to p2 by offering p1 p2's HELLO message
743  *
744  * Remarks: start_peer's notify_connect callback can be called before.
745  *
746  * @param tth transport testing handle
747  * @param p1 peer 1
748  * @param p2 peer 2
749  * @param cb the callback to call when both peers notified that they are connected
750  * @param cls callback cls
751  * @return a connect request handle
752  */
753 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *
754 GNUNET_TRANSPORT_TESTING_connect_peers (struct GNUNET_TRANSPORT_TESTING_PeerContext *p1,
755                                         struct GNUNET_TRANSPORT_TESTING_PeerContext *p2,
756                                         GNUNET_SCHEDULER_TaskCallback cb,
757                                         void *cls)
758 {
759   struct GNUNET_TRANSPORT_TESTING_Handle *tth = p1->tth;
760   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
761   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn;
762
763   ccn = NULL;
764   for (cc = tth->cc_head; NULL != cc; cc = cc->next)
765   {
766     if ( (cc->p1 == p1) &&
767          (cc->p2 == p2) )
768     {
769       ccn = cc;
770       break;
771     }
772   }
773           
774   cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest);
775   cc->p1 = p1;
776   cc->p2 = p2;
777   cc->cb = cb;
778   if (NULL != cls)
779     cc->cb_cls = cls;
780   else
781     cc->cb_cls = cc;
782   if (NULL != ccn)
783   {
784     cc->p1_c = ccn->p1_c;
785     cc->p2_c = ccn->p2_c;
786     cc->connected = ccn->connected;
787   }
788   GNUNET_CONTAINER_DLL_insert (tth->cc_head,
789                                tth->cc_tail,
790                                cc);
791   cc->tct = GNUNET_SCHEDULER_add_now (&offer_hello,
792                                       cc);
793   cc->ats_sh = GNUNET_ATS_connectivity_suggest (cc->p1->ats,
794                                                 &p2->id,
795                                                 1);
796   LOG (GNUNET_ERROR_TYPE_DEBUG,
797        "New connect request %p\n",
798        cc);
799   return cc;
800 }
801
802
803 /**
804  * Cancel the request to connect two peers
805  * Tou MUST cancel the request if you stop the peers before the peers connected succesfully
806  *
807  * @param tth transport testing handle
808  * @param cc a connect request handle
809  */
810 void
811 GNUNET_TRANSPORT_TESTING_connect_peers_cancel (struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc)
812 {
813   struct GNUNET_TRANSPORT_TESTING_Handle *tth = cc->p1->tth;
814
815   LOG (GNUNET_ERROR_TYPE_DEBUG,
816        "Canceling connect request!\n");
817   if (NULL != cc->tct)
818   {
819     GNUNET_SCHEDULER_cancel (cc->tct);
820     cc->tct = NULL;
821   }
822   if (NULL != cc->oh)
823   {
824     GNUNET_TRANSPORT_offer_hello_cancel (cc->oh);
825     cc->oh = NULL;
826   }
827   if (NULL != cc->ats_sh)
828   {
829     GNUNET_ATS_connectivity_suggest_cancel (cc->ats_sh);
830     cc->ats_sh = NULL;
831   }
832   GNUNET_CONTAINER_DLL_remove (tth->cc_head,
833                                tth->cc_tail,
834                                cc);
835   GNUNET_free (cc);
836 }
837
838
839 /**
840  * Clean up the transport testing
841  *
842  * @param tth transport testing handle
843  */
844 void
845 GNUNET_TRANSPORT_TESTING_done (struct GNUNET_TRANSPORT_TESTING_Handle *tth)
846 {
847   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
848   struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ct;
849   struct GNUNET_TRANSPORT_TESTING_PeerContext *p;
850   struct GNUNET_TRANSPORT_TESTING_PeerContext *t;
851
852   if (NULL == tth)
853     return;
854   cc = tth->cc_head;
855   while (NULL != cc)
856   {
857     ct = cc->next;
858     LOG (GNUNET_ERROR_TYPE_ERROR,
859          "Developer forgot to cancel connect request!\n");
860     GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc);
861     cc = ct;
862   }
863   p = tth->p_head;
864   while (NULL != p)
865   {
866     t = p->next;
867     LOG (GNUNET_ERROR_TYPE_ERROR,
868          "Developer forgot to stop peer!\n");
869     GNUNET_TRANSPORT_TESTING_stop_peer (p);
870     p = t;
871   }
872   GNUNET_TESTING_system_destroy (tth->tl_system,
873                                  GNUNET_YES);
874
875   GNUNET_free (tth);
876 }
877
878
879 /**
880  * Initialize the transport testing
881  *
882  * @return transport testing handle
883  */
884 struct GNUNET_TRANSPORT_TESTING_Handle *
885 GNUNET_TRANSPORT_TESTING_init ()
886 {
887   struct GNUNET_TRANSPORT_TESTING_Handle *tth;
888
889   tth = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_Handle);
890   tth->tl_system = GNUNET_TESTING_system_create ("transport-testing",
891                                                  NULL,
892                                                  NULL,
893                                                  NULL);
894   if (NULL == tth->tl_system)
895   {
896     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
897                 "Failed to initialize testing library!\n");
898     GNUNET_free (tth);
899     return NULL;
900   }
901   return tth;
902 }
903
904 /* end of transport-testing.c */