Check that you are not present in trail twice
[oweals/gnunet.git] / src / transport / transport_api_monitor_validation.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 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 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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file transport/transport_api_monitor_validation.c
23  * @brief montoring api for validation status
24  *
25  * This api provides the ability to query the transport service about
26  * the status of address validation.
27  */
28 #include "platform.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_arm_service.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_protocols.h"
33 #include "gnunet_transport_service.h"
34 #include "transport.h"
35
36
37 /**
38  * Context for the address lookup.
39  */
40 struct GNUNET_TRANSPORT_ValidationMonitoringContext
41 {
42   /**
43    * Function to call with the binary address.
44    */
45   GNUNET_TRANSPORT_ValidationIterateCallback cb;
46
47   /**
48    * Closure for @e cb.
49    */
50   void *cb_cls;
51
52   /**
53    * Connection to the service.
54    */
55   struct GNUNET_CLIENT_Connection *client;
56
57   /**
58    * Configuration we use.
59    */
60   const struct GNUNET_CONFIGURATION_Handle *cfg;
61
62   /**
63    * When should this operation time out?
64    */
65   struct GNUNET_TIME_Absolute timeout;
66
67   /**
68    * Backoff for reconnect.
69    */
70   struct GNUNET_TIME_Relative backoff;
71
72   /**
73    * Task ID for reconnect.
74    */
75   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
76
77   /**
78    * Identity of the peer to monitor.
79    */
80   struct GNUNET_PeerIdentity peer;
81
82   /**
83    * Was this a one-shot request?
84    */
85   int one_shot;
86 };
87
88
89 /**
90  * Convert validation state to human-readable string.
91  *
92  * @param state the state value
93  * @return corresponding string
94  */
95 const char *
96 GNUNET_TRANSPORT_vs2s (enum GNUNET_TRANSPORT_ValidationState state)
97 {
98   switch (state)
99   {
100   case GNUNET_TRANSPORT_VS_NONE:
101     return "NONE";
102   case GNUNET_TRANSPORT_VS_NEW:
103     return "NEW";
104   case GNUNET_TRANSPORT_VS_REMOVE:
105     return "REMOVE";
106   case GNUNET_TRANSPORT_VS_TIMEOUT:
107     return "TIMEOUT";
108   case GNUNET_TRANSPORT_VS_UPDATE:
109     return "UPDATE";
110   default:
111     GNUNET_break (0);
112     return "UNDEFINED";
113   }
114 }
115
116
117 /**
118  * Function called with responses from the service.
119  *
120  * @param cls our `struct GNUNET_TRANSPORT_ValidationMonitoringContext *`
121  * @param msg NULL on timeout or error, otherwise presumably a
122  *        message with the human-readable address
123  */
124 static void
125 val_response_processor (void *cls,
126                         const struct GNUNET_MessageHeader *msg);
127
128
129 /**
130  * Send our subscription request to the service.
131  *
132  * @param val_ctx our context
133  */
134 static void
135 send_val_mon_request (struct GNUNET_TRANSPORT_ValidationMonitoringContext *val_ctx)
136 {
137   struct ValidationMonitorMessage msg;
138
139   msg.header.size = htons (sizeof (struct ValidationMonitorMessage));
140   msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_VALIDATION_REQUEST);
141   msg.one_shot = htonl (val_ctx->one_shot);
142   msg.peer = val_ctx->peer;
143   GNUNET_assert (GNUNET_OK ==
144                  GNUNET_CLIENT_transmit_and_get_response (val_ctx->client,
145                     &msg.header,
146                     GNUNET_TIME_absolute_get_remaining (val_ctx->timeout),
147                     GNUNET_YES,
148                     &val_response_processor,
149                     val_ctx));
150 }
151
152
153 /**
154  * Task run to re-establish the connection.
155  *
156  * @param cls our `struct GNUNET_TRANSPORT_ValidationMonitoringContext *`
157  * @param tc scheduler context, unused
158  */
159 static void
160 do_val_connect (void *cls,
161                 const struct GNUNET_SCHEDULER_TaskContext *tc)
162 {
163   struct GNUNET_TRANSPORT_ValidationMonitoringContext *val_ctx = cls;
164
165   val_ctx->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
166   val_ctx->client = GNUNET_CLIENT_connect ("transport", val_ctx->cfg);
167   GNUNET_assert (NULL != val_ctx->client);
168   send_val_mon_request (val_ctx);
169 }
170
171
172 /**
173  * Cut the existing connection and reconnect.
174  *
175  * @param val_ctx our context
176  */
177 static void
178 reconnect_val_ctx (struct GNUNET_TRANSPORT_ValidationMonitoringContext *val_ctx)
179 {
180   GNUNET_assert (GNUNET_NO == val_ctx->one_shot);
181   GNUNET_CLIENT_disconnect (val_ctx->client);
182   val_ctx->client = NULL;
183   /* notify clients about (re)connect */
184   val_ctx->cb (val_ctx->cb_cls, NULL, NULL,
185                GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TIME_UNIT_ZERO_ABS,
186                GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TRANSPORT_VS_TIMEOUT);
187   val_ctx->backoff = GNUNET_TIME_STD_BACKOFF (val_ctx->backoff);
188   val_ctx->reconnect_task = GNUNET_SCHEDULER_add_delayed (val_ctx->backoff,
189                                                           &do_val_connect,
190                                                           val_ctx);
191 }
192
193
194 /**
195  * Function called with responses from the service.
196  *
197  * @param cls our `struct GNUNET_TRANSPORT_ValidationMonitoringContext *`
198  * @param msg NULL on timeout or error, otherwise presumably a
199  *        message with the human-readable address
200  */
201 static void
202 val_response_processor (void *cls,
203                         const struct GNUNET_MessageHeader *msg)
204 {
205   struct GNUNET_TRANSPORT_ValidationMonitoringContext *val_ctx = cls;
206   struct ValidationIterateResponseMessage *vr_msg;
207   struct GNUNET_HELLO_Address *address;
208   const char *addr;
209   const char *transport_name;
210   size_t size;
211   size_t tlen;
212   size_t alen;
213
214   if (NULL == msg)
215   {
216     if (val_ctx->one_shot)
217     {
218       /* Disconnect */
219       val_ctx->cb (val_ctx->cb_cls, NULL, NULL,
220           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TIME_UNIT_ZERO_ABS,
221           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TRANSPORT_VS_TIMEOUT);
222       GNUNET_TRANSPORT_monitor_validation_entries_cancel (val_ctx);
223     }
224     else
225     {
226       reconnect_val_ctx (val_ctx);
227     }
228     return;
229   }
230   size = ntohs (msg->size);
231   GNUNET_break (ntohs (msg->type) ==
232       GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_VALIDATION_RESPONSE);
233
234   if (size == sizeof (struct GNUNET_MessageHeader))
235   {
236     /* Done! */
237     if (val_ctx->one_shot)
238     {
239       val_ctx->cb (val_ctx->cb_cls, NULL, NULL,
240           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TIME_UNIT_ZERO_ABS,
241           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TRANSPORT_VS_NONE);
242       GNUNET_TRANSPORT_monitor_validation_entries_cancel (val_ctx);
243     }
244     else
245     {
246       reconnect_val_ctx (val_ctx);
247     }
248     return;
249   }
250
251   if ((size < sizeof (struct ValidationIterateResponseMessage)) ||
252       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_VALIDATION_RESPONSE))
253   {
254     GNUNET_break (0);
255     if (val_ctx->one_shot)
256     {
257       val_ctx->cb (val_ctx->cb_cls, NULL, NULL,
258           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TIME_UNIT_ZERO_ABS,
259           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TRANSPORT_VS_NONE);
260       GNUNET_TRANSPORT_monitor_validation_entries_cancel (val_ctx);
261     }
262     else
263     {
264       reconnect_val_ctx (val_ctx);
265     }
266     return;
267   }
268
269   vr_msg = (struct ValidationIterateResponseMessage *) msg;
270   tlen = ntohl (vr_msg->pluginlen);
271   alen = ntohl (vr_msg->addrlen);
272
273   if (size != sizeof (struct ValidationIterateResponseMessage) + tlen + alen)
274   {
275     GNUNET_break (0);
276     if (val_ctx->one_shot)
277     {
278       val_ctx->cb (val_ctx->cb_cls, NULL, NULL,
279           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TIME_UNIT_ZERO_ABS,
280           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TRANSPORT_VS_NONE);
281       GNUNET_TRANSPORT_monitor_validation_entries_cancel (val_ctx);
282     }
283     else
284     {
285       reconnect_val_ctx (val_ctx);
286     }
287     return;
288   }
289   if ( (0 == tlen) && (0 == alen) )
290   {
291     GNUNET_break (0);
292     if (val_ctx->one_shot)
293     {
294       val_ctx->cb (val_ctx->cb_cls, NULL, NULL,
295           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TIME_UNIT_ZERO_ABS,
296           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TRANSPORT_VS_NONE);
297       GNUNET_TRANSPORT_monitor_validation_entries_cancel (val_ctx);
298     }
299     else
300     {
301       reconnect_val_ctx (val_ctx);
302     }
303     return;
304   }
305   else
306   {
307     if (0 == tlen)
308     {
309       GNUNET_break (0); /* This must not happen: address without plugin */
310       return;
311     }
312     addr = (const char *) &vr_msg[1];
313     transport_name = &addr[alen];
314
315     if (transport_name[tlen - 1] != '\0')
316     {
317       /* Corrupt plugin name */
318       GNUNET_break (0);
319       if (val_ctx->one_shot)
320       {
321         val_ctx->cb (val_ctx->cb_cls,
322                      NULL, NULL,
323                      GNUNET_TIME_UNIT_ZERO_ABS,
324                      GNUNET_TIME_UNIT_ZERO_ABS,
325                      GNUNET_TIME_UNIT_ZERO_ABS,
326                      GNUNET_TRANSPORT_VS_NONE);
327         GNUNET_TRANSPORT_monitor_validation_entries_cancel (val_ctx);
328       }
329       else
330       {
331         reconnect_val_ctx (val_ctx);
332       }
333       return;
334     }
335
336     /* notify client */
337     address = GNUNET_HELLO_address_allocate (&vr_msg->peer,
338                                              transport_name,
339                                              addr, alen,
340                                              ntohl (vr_msg->local_address_info));
341     val_ctx->cb (val_ctx->cb_cls,
342                  &vr_msg->peer,
343                  address,
344                  GNUNET_TIME_absolute_ntoh (vr_msg->last_validation),
345                  GNUNET_TIME_absolute_ntoh (vr_msg->valid_until),
346                  GNUNET_TIME_absolute_ntoh (vr_msg->next_validation),
347                  ntohl(vr_msg->state));
348     GNUNET_HELLO_address_free (address);
349   }
350   /* expect more replies */
351   GNUNET_CLIENT_receive (val_ctx->client,
352                          &val_response_processor,
353                          val_ctx,
354                          GNUNET_TIME_absolute_get_remaining (val_ctx->timeout));
355 }
356
357
358 /**
359  * Return information about pending address validation operations for a specific
360  * or all peers
361  *
362  * @param cfg configuration to use
363  * @param peer a specific peer identity to obtain validation entries for,
364  *      NULL for all peers
365  * @param one_shot #GNUNET_YES to return all entries and then end (with NULL+NULL),
366  *                 #GNUNET_NO to monitor validation entries continuously
367  * @param timeout how long is the lookup allowed to take at most
368  * @param validation_callback function to call with the results
369  * @param validation_callback_cls closure for peer_address_callback
370  */
371 struct GNUNET_TRANSPORT_ValidationMonitoringContext *
372 GNUNET_TRANSPORT_monitor_validation_entries (const struct GNUNET_CONFIGURATION_Handle *cfg,
373                                              const struct GNUNET_PeerIdentity *peer,
374                                              int one_shot,
375                                              struct GNUNET_TIME_Relative timeout,
376                                              GNUNET_TRANSPORT_ValidationIterateCallback validation_callback,
377                                              void *validation_callback_cls)
378 {
379   struct GNUNET_TRANSPORT_ValidationMonitoringContext *val_ctx;
380   struct GNUNET_CLIENT_Connection *client;
381
382   client = GNUNET_CLIENT_connect ("transport", cfg);
383   if (NULL == client)
384     return NULL;
385   if (GNUNET_YES != one_shot)
386     timeout = GNUNET_TIME_UNIT_FOREVER_REL;
387   val_ctx = GNUNET_new (struct GNUNET_TRANSPORT_ValidationMonitoringContext);
388   val_ctx->cb = validation_callback;
389   val_ctx->cb_cls = validation_callback_cls;
390   val_ctx->cfg = cfg;
391   val_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
392   if (NULL != peer)
393     val_ctx->peer = *peer;
394   val_ctx->one_shot = one_shot;
395   val_ctx->client = client;
396   send_val_mon_request (val_ctx);
397
398   return val_ctx;
399 }
400
401
402 /**
403  * Return information about all current pending validation operations
404  *
405  * @param vic handle for the request to cancel
406  */
407 void
408 GNUNET_TRANSPORT_monitor_validation_entries_cancel (struct GNUNET_TRANSPORT_ValidationMonitoringContext *vic)
409 {
410   if (NULL != vic->client)
411   {
412     GNUNET_CLIENT_disconnect (vic->client);
413     vic->client = NULL;
414   }
415   if (GNUNET_SCHEDULER_NO_TASK != vic->reconnect_task)
416   {
417     GNUNET_SCHEDULER_cancel (vic->reconnect_task);
418     vic->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
419   }
420   GNUNET_free (vic);
421 }
422
423
424 /* end of transport_api_monitor_validation.c */