-ensure stats queues do not grow too big
[oweals/gnunet.git] / src / transport / transport_api_monitor_validation.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2010 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 /**
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   struct GNUNET_SCHEDULER_Task * 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  */
158 static void
159 do_val_connect (void *cls)
160 {
161   struct GNUNET_TRANSPORT_ValidationMonitoringContext *val_ctx = cls;
162
163   val_ctx->reconnect_task = NULL;
164   val_ctx->client = GNUNET_CLIENT_connect ("transport", val_ctx->cfg);
165   GNUNET_assert (NULL != val_ctx->client);
166   send_val_mon_request (val_ctx);
167 }
168
169
170 /**
171  * Cut the existing connection and reconnect.
172  *
173  * @param val_ctx our context
174  */
175 static void
176 reconnect_val_ctx (struct GNUNET_TRANSPORT_ValidationMonitoringContext *val_ctx)
177 {
178   GNUNET_assert (GNUNET_NO == val_ctx->one_shot);
179   GNUNET_CLIENT_disconnect (val_ctx->client);
180   val_ctx->client = NULL;
181   /* notify clients about (re)connect */
182   val_ctx->cb (val_ctx->cb_cls, NULL,
183                GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TIME_UNIT_ZERO_ABS,
184                GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TRANSPORT_VS_TIMEOUT);
185   val_ctx->backoff = GNUNET_TIME_STD_BACKOFF (val_ctx->backoff);
186   val_ctx->reconnect_task = GNUNET_SCHEDULER_add_delayed (val_ctx->backoff,
187                                                           &do_val_connect,
188                                                           val_ctx);
189 }
190
191
192 /**
193  * Function called with responses from the service.
194  *
195  * @param cls our `struct GNUNET_TRANSPORT_ValidationMonitoringContext *`
196  * @param msg NULL on timeout or error, otherwise presumably a
197  *        message with the human-readable address
198  */
199 static void
200 val_response_processor (void *cls,
201                         const struct GNUNET_MessageHeader *msg)
202 {
203   struct GNUNET_TRANSPORT_ValidationMonitoringContext *val_ctx = cls;
204   struct ValidationIterateResponseMessage *vr_msg;
205   struct GNUNET_HELLO_Address *address;
206   const char *addr;
207   const char *transport_name;
208   size_t size;
209   size_t tlen;
210   size_t alen;
211
212   if (NULL == msg)
213   {
214     if (val_ctx->one_shot)
215     {
216       /* Disconnect */
217       val_ctx->cb (val_ctx->cb_cls, NULL,
218           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TIME_UNIT_ZERO_ABS,
219           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TRANSPORT_VS_TIMEOUT);
220       GNUNET_TRANSPORT_monitor_validation_entries_cancel (val_ctx);
221     }
222     else
223     {
224       reconnect_val_ctx (val_ctx);
225     }
226     return;
227   }
228   size = ntohs (msg->size);
229   GNUNET_break (ntohs (msg->type) ==
230       GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_VALIDATION_RESPONSE);
231
232   if (size == sizeof (struct GNUNET_MessageHeader))
233   {
234     /* Done! */
235     if (val_ctx->one_shot)
236     {
237       val_ctx->cb (val_ctx->cb_cls, NULL,
238           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TIME_UNIT_ZERO_ABS,
239           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TRANSPORT_VS_NONE);
240       GNUNET_TRANSPORT_monitor_validation_entries_cancel (val_ctx);
241     }
242     else
243     {
244       reconnect_val_ctx (val_ctx);
245     }
246     return;
247   }
248
249   if ((size < sizeof (struct ValidationIterateResponseMessage)) ||
250       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_VALIDATION_RESPONSE))
251   {
252     GNUNET_break (0);
253     if (val_ctx->one_shot)
254     {
255       val_ctx->cb (val_ctx->cb_cls, NULL,
256           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TIME_UNIT_ZERO_ABS,
257           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TRANSPORT_VS_NONE);
258       GNUNET_TRANSPORT_monitor_validation_entries_cancel (val_ctx);
259     }
260     else
261     {
262       reconnect_val_ctx (val_ctx);
263     }
264     return;
265   }
266
267   vr_msg = (struct ValidationIterateResponseMessage *) msg;
268   tlen = ntohl (vr_msg->pluginlen);
269   alen = ntohl (vr_msg->addrlen);
270
271   if (size != sizeof (struct ValidationIterateResponseMessage) + tlen + alen)
272   {
273     GNUNET_break (0);
274     if (val_ctx->one_shot)
275     {
276       val_ctx->cb (val_ctx->cb_cls, NULL,
277           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TIME_UNIT_ZERO_ABS,
278           GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TRANSPORT_VS_NONE);
279       GNUNET_TRANSPORT_monitor_validation_entries_cancel (val_ctx);
280     }
281     else
282     {
283       reconnect_val_ctx (val_ctx);
284     }
285     return;
286   }
287   if (0 == tlen)
288   {
289     GNUNET_break (0); /* This must not happen: address without plugin */
290     return;
291   }
292   addr = (const char *) &vr_msg[1];
293   transport_name = &addr[alen];
294
295   if (transport_name[tlen - 1] != '\0')
296   {
297     /* Corrupt plugin name */
298     GNUNET_break (0);
299     if (val_ctx->one_shot)
300     {
301       val_ctx->cb (val_ctx->cb_cls,
302                    NULL,
303                    GNUNET_TIME_UNIT_ZERO_ABS,
304                    GNUNET_TIME_UNIT_ZERO_ABS,
305                    GNUNET_TIME_UNIT_ZERO_ABS,
306                    GNUNET_TRANSPORT_VS_NONE);
307       GNUNET_TRANSPORT_monitor_validation_entries_cancel (val_ctx);
308     }
309     else
310     {
311       reconnect_val_ctx (val_ctx);
312     }
313     return;
314   }
315
316   /* notify client */
317   address = GNUNET_HELLO_address_allocate (&vr_msg->peer,
318                                            transport_name,
319                                            addr, alen,
320                                            ntohl (vr_msg->local_address_info));
321   val_ctx->cb (val_ctx->cb_cls,
322                address,
323                GNUNET_TIME_absolute_ntoh (vr_msg->last_validation),
324                GNUNET_TIME_absolute_ntoh (vr_msg->valid_until),
325                GNUNET_TIME_absolute_ntoh (vr_msg->next_validation),
326                ntohl(vr_msg->state));
327   GNUNET_HELLO_address_free (address);
328   /* expect more replies */
329   GNUNET_CLIENT_receive (val_ctx->client,
330                          &val_response_processor,
331                          val_ctx,
332                          GNUNET_TIME_absolute_get_remaining (val_ctx->timeout));
333 }
334
335
336 /**
337  * Return information about pending address validation operations for a specific
338  * or all peers
339  *
340  * @param cfg configuration to use
341  * @param peer a specific peer identity to obtain validation entries for,
342  *      NULL for all peers
343  * @param one_shot #GNUNET_YES to return all entries and then end (with NULL+NULL),
344  *                 #GNUNET_NO to monitor validation entries continuously
345  * @param timeout how long is the lookup allowed to take at most
346  * @param validation_callback function to call with the results
347  * @param validation_callback_cls closure for peer_address_callback
348  */
349 struct GNUNET_TRANSPORT_ValidationMonitoringContext *
350 GNUNET_TRANSPORT_monitor_validation_entries (const struct GNUNET_CONFIGURATION_Handle *cfg,
351                                              const struct GNUNET_PeerIdentity *peer,
352                                              int one_shot,
353                                              struct GNUNET_TIME_Relative timeout,
354                                              GNUNET_TRANSPORT_ValidationIterateCallback validation_callback,
355                                              void *validation_callback_cls)
356 {
357   struct GNUNET_TRANSPORT_ValidationMonitoringContext *val_ctx;
358   struct GNUNET_CLIENT_Connection *client;
359
360   client = GNUNET_CLIENT_connect ("transport", cfg);
361   if (NULL == client)
362     return NULL;
363   if (GNUNET_YES != one_shot)
364     timeout = GNUNET_TIME_UNIT_FOREVER_REL;
365   val_ctx = GNUNET_new (struct GNUNET_TRANSPORT_ValidationMonitoringContext);
366   val_ctx->cb = validation_callback;
367   val_ctx->cb_cls = validation_callback_cls;
368   val_ctx->cfg = cfg;
369   val_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
370   if (NULL != peer)
371     val_ctx->peer = *peer;
372   val_ctx->one_shot = one_shot;
373   val_ctx->client = client;
374   send_val_mon_request (val_ctx);
375
376   return val_ctx;
377 }
378
379
380 /**
381  * Return information about all current pending validation operations
382  *
383  * @param vic handle for the request to cancel
384  */
385 void
386 GNUNET_TRANSPORT_monitor_validation_entries_cancel (struct GNUNET_TRANSPORT_ValidationMonitoringContext *vic)
387 {
388   if (NULL != vic->client)
389   {
390     GNUNET_CLIENT_disconnect (vic->client);
391     vic->client = NULL;
392   }
393   if (NULL != vic->reconnect_task)
394   {
395     GNUNET_SCHEDULER_cancel (vic->reconnect_task);
396     vic->reconnect_task = NULL;
397   }
398   GNUNET_free (vic);
399 }
400
401
402 /* end of transport_api_monitor_validation.c */