61c392d77c851cb1bbcd9173d1a172b3c9cfd2d2
[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, const struct GNUNET_MessageHeader *message);
46 static void
47 exchange_hello (void *cb_cls, const struct GNUNET_MessageHeader *message);
48
49 static void
50 notify_connect_internal (void *cls, const struct GNUNET_PeerIdentity *peer,
51                          const struct GNUNET_TRANSPORT_ATS_Information *ats,
52                          uint32_t ats_count)
53 {
54   struct ConnectingContext *cc = cls;
55
56   GNUNET_assert (cc != NULL);
57
58   if (0 ==
59       memcmp (&(*peer).hashPubKey, &cc->p1->id.hashPubKey,
60               sizeof (GNUNET_HashCode)))
61   {
62     if (cc->p1_c == GNUNET_NO)
63       cc->p1_c = GNUNET_YES;
64   }
65   if (0 ==
66       memcmp (&(*peer).hashPubKey, &cc->p2->id.hashPubKey,
67               sizeof (GNUNET_HashCode)))
68   {
69     if (cc->p2_c == GNUNET_NO)
70       cc->p2_c = GNUNET_YES;
71   }
72
73   if ((cc->p2_c == GNUNET_YES) && (cc->p2_c == GNUNET_YES))
74   {
75     /* clean up */
76     GNUNET_TRANSPORT_get_hello_cancel (cc->th_p2, &exchange_hello_last, cc);
77     GNUNET_TRANSPORT_get_hello_cancel (cc->th_p1, &exchange_hello, cc);
78
79     if (cc->tct != GNUNET_SCHEDULER_NO_TASK)
80       GNUNET_SCHEDULER_cancel (cc->tct);
81
82     cc->tct = GNUNET_SCHEDULER_NO_TASK;
83
84     GNUNET_TRANSPORT_disconnect (cc->th_p1);
85     GNUNET_TRANSPORT_disconnect (cc->th_p2);
86
87     if (cc->cb != NULL)
88       cc->cb (cc->p1, cc->p2, cc->cb_cls);
89
90     GNUNET_free (cc);
91   }
92 }
93
94 static void
95 notify_connect (void *cls, 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
101   if (p == NULL)
102     return;
103   if (p->nc != NULL)
104     p->nc (p->cb_cls, peer, ats, ats_count);
105 }
106
107 static void
108 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
109 {
110   struct PeerContext *p = cls;
111
112   if (p == NULL)
113     return;
114   if (p->nd != NULL)
115     p->nd (p->cb_cls, peer);
116 }
117
118 static void
119 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
120                 const struct GNUNET_MessageHeader *message,
121                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
122                 uint32_t ats_count)
123 {
124   struct PeerContext *p = cls;
125
126   if (p == NULL)
127     return;
128   if (p->rec != NULL)
129     p->rec (p->cb_cls, peer, message, ats, ats_count);
130 }
131
132
133 static void
134 exchange_hello_last (void *cb_cls, const struct GNUNET_MessageHeader *message)
135 {
136   struct ConnectingContext *cc = cb_cls;
137   struct PeerContext *me = cc->p2;
138
139   //struct PeerContext *p1 = cc->p1;
140
141   GNUNET_assert (message != NULL);
142   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
143               "Exchanging HELLO of size %d with peer (%s)!\n",
144               (int) GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message *)
145                                        message), GNUNET_i2s (&me->id));
146   GNUNET_assert (GNUNET_OK ==
147                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
148                                       message, &me->id));
149   GNUNET_TRANSPORT_offer_hello (cc->th_p1, message, NULL, NULL);
150 }
151
152
153 static void
154 exchange_hello (void *cb_cls, const struct GNUNET_MessageHeader *message)
155 {
156   struct ConnectingContext *cc = cb_cls;
157   struct PeerContext *me = cc->p1;
158
159   //struct PeerContext *p2 = cc->p2;
160
161   GNUNET_assert (message != NULL);
162   GNUNET_assert (GNUNET_OK ==
163                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
164                                       message, &me->id));
165   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
166               "Exchanging HELLO of size %d from peer %s!\n",
167               (int) GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message *)
168                                        message), GNUNET_i2s (&me->id));
169   GNUNET_TRANSPORT_offer_hello (cc->th_p2, message, NULL, NULL);
170 }
171
172 static void
173 try_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
174 {
175   struct ConnectingContext *cc = cls;
176   struct PeerContext *p1 = cc->p1;
177   struct PeerContext *p2 = cc->p2;
178
179   cc->tct = GNUNET_SCHEDULER_NO_TASK;
180   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
181     return;
182
183   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asking peers to connect...\n");
184   /* FIXME: 'pX.id' may still be all-zeros here... */
185   GNUNET_TRANSPORT_try_connect (cc->th_p1, &p2->id);
186   GNUNET_TRANSPORT_try_connect (cc->th_p2, &p1->id);
187
188   cc->tct =
189       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &try_connect, cc);
190 }
191
192
193 /**
194  * Start a peer with the given configuration
195  * @param rec receive callback
196  * @param nc connect callback
197  * @param nd disconnect callback
198  * @param cb_cls closure for callback
199  * @return the peer context
200  */
201 struct PeerContext *
202 GNUNET_TRANSPORT_TESTING_start_peer (const char *cfgname,
203                                      GNUNET_TRANSPORT_ReceiveCallback rec,
204                                      GNUNET_TRANSPORT_NotifyConnect nc,
205                                      GNUNET_TRANSPORT_NotifyDisconnect nd,
206                                      void *cb_cls)
207 {
208   struct PeerContext *p = GNUNET_malloc (sizeof (struct PeerContext));
209
210   p->cfg = GNUNET_CONFIGURATION_create ();
211
212   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
213   if (GNUNET_CONFIGURATION_have_value (p->cfg, "PATHS", "SERVICEHOME"))
214     GNUNET_CONFIGURATION_get_value_string (p->cfg, "PATHS", "SERVICEHOME",
215                                            &p->servicehome);
216   if (NULL != p->servicehome)
217     GNUNET_DISK_directory_remove (p->servicehome);
218   p->arm_proc =
219       GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
220                                "gnunet-service-arm", "-c", cfgname,
221 #if VERBOSE_PEERS
222                                "-L", "DEBUG",
223 #else
224                                "-L", "ERROR",
225 #endif
226                                NULL);
227   p->nc = nc;
228   p->nd = nd;
229   p->rec = rec;
230   if (cb_cls != NULL)
231     p->cb_cls = cb_cls;
232   else
233     p->cb_cls = p;
234
235   p->th =
236       GNUNET_TRANSPORT_connect (p->cfg, NULL, p, &notify_receive,
237                                 &notify_connect, &notify_disconnect);
238   GNUNET_assert (p->th != NULL);
239   return p;
240 }
241
242 /**
243  * shutdown the given peer
244  * @param p the peer
245  */
246 void
247 GNUNET_TRANSPORT_TESTING_stop_peer (struct PeerContext *p)
248 {
249   GNUNET_assert (p != NULL);
250   if (p->th != NULL)
251     GNUNET_TRANSPORT_disconnect (p->th);
252
253   if (NULL != p->arm_proc)
254   {
255     if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
256       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
257     GNUNET_OS_process_wait (p->arm_proc);
258     GNUNET_OS_process_close (p->arm_proc);
259     p->arm_proc = NULL;
260   }
261   if (p->servicehome != NULL)
262   {
263     GNUNET_DISK_directory_remove (p->servicehome);
264     GNUNET_free (p->servicehome);
265   }
266
267   if (p->cfg != NULL)
268     GNUNET_CONFIGURATION_destroy (p->cfg);
269   GNUNET_free (p);
270   p = NULL;
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  * @return connect context
282  */
283 GNUNET_TRANSPORT_TESTING_ConnectRequest
284 GNUNET_TRANSPORT_TESTING_connect_peers (struct PeerContext *p1,
285                                         struct PeerContext *p2,
286                                         GNUNET_TRANSPORT_TESTING_connect_cb cb,
287                                         void *cb_cls)
288 {
289   struct ConnectingContext *cc =
290       GNUNET_malloc (sizeof (struct ConnectingContext));
291
292   GNUNET_assert (p1 != NULL);
293   GNUNET_assert (p2 != NULL);
294
295   cc->p1 = p1;
296   cc->p2 = p2;
297
298   cc->cb = cb;
299   cc->cb_cls = cb_cls;
300
301   cc->th_p1 =
302       GNUNET_TRANSPORT_connect (cc->p1->cfg, NULL, cc, NULL,
303                                 &notify_connect_internal, NULL);
304
305   cc->th_p2 =
306       GNUNET_TRANSPORT_connect (cc->p2->cfg, NULL, cc, NULL,
307                                 &notify_connect_internal, NULL);
308
309   GNUNET_assert (cc->th_p1 != NULL);
310   GNUNET_assert (cc->th_p2 != NULL);
311
312   GNUNET_TRANSPORT_get_hello (cc->th_p1, &exchange_hello, cc);
313   GNUNET_TRANSPORT_get_hello (cc->th_p2, &exchange_hello_last, cc);
314
315   cc->tct = GNUNET_SCHEDULER_add_now (&try_connect, cc);
316   return cc;
317 }
318
319 /**
320  * Cancel the request to connect two peers
321  * Tou MUST cancel the request if you stop the peers before the peers connected succesfully
322  * @param cc a connect request handle
323  */
324 void
325 GNUNET_TRANSPORT_TESTING_connect_peers_cancel (GNUNET_TRANSPORT_TESTING_ConnectRequest ccr)
326 {
327   struct ConnectingContext *cc = ccr;
328   /* clean up */
329   GNUNET_TRANSPORT_get_hello_cancel (cc->th_p2, &exchange_hello_last, cc);
330   GNUNET_TRANSPORT_get_hello_cancel (cc->th_p1, &exchange_hello, cc);
331
332   if (cc->tct != GNUNET_SCHEDULER_NO_TASK)
333     GNUNET_SCHEDULER_cancel (cc->tct);
334
335   cc->tct = GNUNET_SCHEDULER_NO_TASK;
336
337   GNUNET_TRANSPORT_disconnect (cc->th_p1);
338   GNUNET_TRANSPORT_disconnect (cc->th_p2);
339
340   GNUNET_free (cc);
341 }
342
343
344 /* end of transport_testing.h */