01419ce4423e2e8e71a341a24559033726963d2f
[oweals/gnunet.git] / src / transport / transport-testing.c
1 /*
2      This file is part of GNUnet.
3      (C) 2006, 2009 Christian Grothoff (and other contributing authors)
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 2, 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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file transport_testing.c
23  * @brief testing lib for transport service
24  *
25  * @author Matthias Wachs
26  */
27
28 #include "transport-testing.h"
29
30 struct ConnectingContext
31 {
32   struct PeerContext * p1;
33   struct PeerContext * p2;
34   GNUNET_SCHEDULER_TaskIdentifier tct;
35   GNUNET_TRANSPORT_TESTING_connect_cb cb;
36   void * cb_cls;
37
38   struct GNUNET_TRANSPORT_Handle *th_p1;
39   struct GNUNET_TRANSPORT_Handle *th_p2;
40   int p1_c;
41   int p2_c;
42 };
43
44 static void
45 exchange_hello_last (void *cb_cls,
46                      const struct GNUNET_MessageHeader *message);
47 static void
48 exchange_hello (void *cb_cls,
49                      const struct GNUNET_MessageHeader *message);
50
51 static void
52 notify_connect_internal (void *cls,
53                 const struct GNUNET_PeerIdentity *peer,
54                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
55                 uint32_t ats_count)
56 {
57   struct ConnectingContext * cc = cls;
58
59   GNUNET_assert(cc != NULL);
60
61   if (0 == memcmp (&(*peer).hashPubKey, &cc->p1->id.hashPubKey, sizeof (GNUNET_HashCode)))
62     {
63     if (cc->p1_c == GNUNET_NO)
64       cc->p1_c = GNUNET_YES;
65     }
66   if (0 == memcmp (&(*peer).hashPubKey, &cc->p2->id.hashPubKey, sizeof (GNUNET_HashCode)))
67     {
68     if (cc->p2_c == GNUNET_NO)
69       cc->p2_c = GNUNET_YES;
70     }
71
72   if ((cc->p2_c == GNUNET_YES) && (cc->p2_c == GNUNET_YES))
73   {
74      /* clean up */
75     GNUNET_TRANSPORT_get_hello_cancel (cc->th_p2, &exchange_hello_last, cc);
76     GNUNET_TRANSPORT_get_hello_cancel (cc->th_p1, &exchange_hello, cc);
77
78     if (cc->tct != GNUNET_SCHEDULER_NO_TASK)
79       GNUNET_SCHEDULER_cancel(cc->tct);
80
81     cc->tct = GNUNET_SCHEDULER_NO_TASK;
82
83     GNUNET_TRANSPORT_disconnect (cc->th_p1);
84     GNUNET_TRANSPORT_disconnect (cc->th_p2);
85
86     if (cc->cb != NULL)
87       cc->cb (cc->p1, cc->p2, cc->cb_cls);
88
89     GNUNET_free(cc);
90   }
91 }
92
93 static void
94 notify_connect (void *cls,
95                 const struct GNUNET_PeerIdentity *peer,
96                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
97                 uint32_t ats_count)
98 {
99   struct PeerContext * p = cls;
100   if (p == NULL)
101     return;
102   if (p->nc != NULL)
103     p->nc (p->cb_cls, peer, ats, ats_count);
104 }
105
106 static void
107 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
108 {
109   struct PeerContext * p = cls;
110   if (p == NULL)
111     return;
112   if (p->nd != NULL)
113     p->nd (p->cb_cls, peer);
114 }
115
116 static void
117 notify_receive (void *cls,
118                 const struct GNUNET_PeerIdentity *peer,
119                 const struct GNUNET_MessageHeader *message,
120                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
121                 uint32_t ats_count)
122 {
123   struct PeerContext * p = cls;
124   if (p == NULL)
125     return;
126   if (p->rec != NULL)
127     p->rec (p->cb_cls, peer, message, ats, ats_count);
128 }
129
130
131 static void
132 exchange_hello_last (void *cb_cls,
133                      const struct GNUNET_MessageHeader *message)
134 {
135   struct ConnectingContext * cc = cb_cls;
136   struct PeerContext *me = cc->p2;
137   //struct PeerContext *p1 = cc->p1;
138
139   GNUNET_assert (message != NULL);
140   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
141               "Exchanging HELLO of size %d with peer (%s)!\n",
142               (int) GNUNET_HELLO_size((const struct GNUNET_HELLO_Message *)message),
143               GNUNET_i2s (&me->id));
144   GNUNET_assert (GNUNET_OK ==
145                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
146                                       message, &me->id));
147   GNUNET_TRANSPORT_offer_hello (cc->th_p1, message, NULL, NULL);
148 }
149
150
151 static void
152 exchange_hello (void *cb_cls,
153                 const struct GNUNET_MessageHeader *message)
154 {
155   struct ConnectingContext * cc = cb_cls;
156   struct PeerContext *me = cc->p1;
157   //struct PeerContext *p2 = cc->p2;
158
159   GNUNET_assert (message != NULL);
160   GNUNET_assert (GNUNET_OK ==
161                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
162                                       message, &me->id));
163   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
164               "Exchanging HELLO of size %d from peer %s!\n",
165               (int) GNUNET_HELLO_size((const struct GNUNET_HELLO_Message *)message),
166               GNUNET_i2s (&me->id));
167   GNUNET_TRANSPORT_offer_hello (cc->th_p2, message, NULL, NULL);
168 }
169
170 static void
171 try_connect (void *cls,
172              const struct GNUNET_SCHEDULER_TaskContext *tc)
173 {
174   struct ConnectingContext * cc = cls;
175   struct PeerContext *p1 = cc->p1;
176   struct PeerContext *p2 = cc->p2;
177
178   cc->tct = GNUNET_SCHEDULER_NO_TASK;
179   if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
180     return;
181
182   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
183               "Asking peers to connect...\n");
184   /* FIXME: 'pX.id' may still be all-zeros here... */
185   GNUNET_TRANSPORT_try_connect (cc->th_p1,
186                                 &p2->id);
187   GNUNET_TRANSPORT_try_connect (cc->th_p2,
188                                 &p1->id);
189
190   cc->tct = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
191                                       &try_connect,
192                                       cc);
193 }
194
195
196 /**
197  * Start a peer with the given configuration
198  * @param rec receive callback
199  * @param nc connect callback
200  * @param nd disconnect callback
201  * @param cb_cls closure for callback
202  * @return the peer context
203  */
204 struct PeerContext *
205 GNUNET_TRANSPORT_TESTING_start_peer (const char * cfgname,
206     GNUNET_TRANSPORT_ReceiveCallback rec,
207     GNUNET_TRANSPORT_NotifyConnect nc,
208     GNUNET_TRANSPORT_NotifyDisconnect nd,
209     void * cb_cls)
210 {
211   struct PeerContext * p = GNUNET_malloc (sizeof (struct PeerContext));
212
213   p->cfg = GNUNET_CONFIGURATION_create ();
214
215   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
216   if (GNUNET_CONFIGURATION_have_value (p->cfg,"PATHS", "SERVICEHOME"))
217       GNUNET_CONFIGURATION_get_value_string (p->cfg, "PATHS", "SERVICEHOME", &p->servicehome);
218   if (NULL != p->servicehome)
219     GNUNET_DISK_directory_remove (p->servicehome);
220   p->arm_proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
221                                         "gnunet-service-arm",
222                                         "-c", cfgname,
223 #if VERBOSE_PEERS
224                                         "-L", "DEBUG",
225 #else
226                                         "-L", "ERROR",
227 #endif
228                                         NULL);
229   p->nc = nc;
230   p->nd = nd;
231   p->rec = rec;
232   if (cb_cls != NULL)
233     p->cb_cls = cb_cls;
234   else
235     p->cb_cls = p;
236
237   p->th = GNUNET_TRANSPORT_connect(p->cfg, NULL,
238                             p,
239                             &notify_receive,
240                             &notify_connect,
241                             &notify_disconnect);
242   GNUNET_assert (p->th != NULL);
243   return p;
244 }
245
246 /**
247  * shutdown the given peer
248  * @param p the peer
249  */
250 void
251 GNUNET_TRANSPORT_TESTING_stop_peer (struct PeerContext * p)
252 {
253   if (p->th != NULL)
254     GNUNET_TRANSPORT_disconnect(p->th);
255
256   if (NULL != p->arm_proc)
257     {
258       if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
259         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
260       GNUNET_OS_process_wait (p->arm_proc);
261       GNUNET_OS_process_close (p->arm_proc);
262       p->arm_proc = NULL;
263     }
264   GNUNET_CONFIGURATION_destroy (p->cfg);
265   if (p->servicehome != NULL)
266     {
267     GNUNET_DISK_directory_remove (p->servicehome);
268     GNUNET_free(p->servicehome);
269     }
270   GNUNET_free (p);
271 }
272
273 /**
274  * Connect the two given peers and call the callback when both peers report the
275  * inbound connect. Remarks: start_peer's notify_connect callback can be called
276  * before.
277  * @param p1 peer 1
278  * @param p2 peer 2
279  * @param cb the callback to call
280  * @param cb_cls callback cls
281  */
282 void
283 GNUNET_TRANSPORT_TESTING_connect_peers (struct PeerContext * p1,
284                                         struct PeerContext * p2,
285                                         GNUNET_TRANSPORT_TESTING_connect_cb cb,
286                                         void * cb_cls)
287 {
288   struct ConnectingContext * cc = GNUNET_malloc (sizeof (struct ConnectingContext));
289
290   GNUNET_assert (p1 != NULL);
291   GNUNET_assert (p2 != NULL);
292
293   cc->p1 = p1;
294   cc->p2 = p2;
295
296   cc->cb = cb;
297   cc->cb_cls = cb_cls;
298
299   cc->th_p1 = GNUNET_TRANSPORT_connect(cc->p1->cfg, NULL,
300                             cc,
301                             NULL,
302                             &notify_connect_internal,
303                             NULL);
304
305   cc->th_p2 = GNUNET_TRANSPORT_connect(cc->p2->cfg, NULL,
306                             cc,
307                             NULL,
308                             &notify_connect_internal,
309                             NULL);
310
311   GNUNET_assert (cc->th_p1 != NULL);
312   GNUNET_assert (cc->th_p2 != NULL);
313
314   GNUNET_TRANSPORT_get_hello (cc->th_p1, &exchange_hello, cc);
315   GNUNET_TRANSPORT_get_hello (cc->th_p2, &exchange_hello_last, cc);
316
317   cc->tct = GNUNET_SCHEDULER_add_now (&try_connect, cc);
318 }
319
320
321
322 /* end of transport_testing.h */