implementing 0003268 to inbound information in HELLO addresses
[oweals/gnunet.git] / src / transport / transport_api_monitoring.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_monitoring.c
23  * @brief montoring api for transport peer status and validation entries
24  *
25  * This api provides the ability to query the transport service about
26  * the status of a specific or all peers as well as address validation entries.
27  *
28  * Calls back with information about peer(s) including address used, state and
29  * state timeout for peer requests and address, address lifetime and next revalidation
30  * for validation entries.
31  */
32 #include "platform.h"
33 #include "gnunet_util_lib.h"
34 #include "gnunet_arm_service.h"
35 #include "gnunet_hello_lib.h"
36 #include "gnunet_protocols.h"
37 #include "gnunet_transport_service.h"
38 #include "transport.h"
39
40 /**
41  * Context for iterating validation entries.
42  */
43 struct GNUNET_TRANSPORT_PeerMonitoringContext
44 {
45   /**
46    * Function to call with the binary address.
47    */
48   GNUNET_TRANSPORT_PeerIterateCallback cb;
49
50   /**
51    * Closure for cb.
52    */
53   void *cb_cls;
54
55   /**
56    * Connection to the service.
57    */
58   struct GNUNET_CLIENT_Connection *client;
59
60   /**
61    * Configuration we use.
62    */
63   const struct GNUNET_CONFIGURATION_Handle *cfg;
64
65   /**
66    * When should this operation time out?
67    */
68   struct GNUNET_TIME_Absolute timeout;
69
70   /**
71    * Backoff for reconnect.
72    */
73   struct GNUNET_TIME_Relative backoff;
74
75   /**
76    * Task ID for reconnect.
77    */
78   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
79
80   /**
81    * Identity of the peer to monitor.
82    */
83   struct GNUNET_PeerIdentity peer;
84
85   /**
86    * Was this a one-shot request?
87    */
88   int one_shot;
89 };
90
91
92 /**
93  * Context for the address lookup.
94  */
95 struct GNUNET_TRANSPORT_ValidationMonitoringContext
96 {
97   /**
98    * Function to call with the binary address.
99    */
100   GNUNET_TRANSPORT_ValidationIterateCallback cb;
101
102   /**
103    * Closure for cb.
104    */
105   void *cb_cls;
106
107   /**
108    * Connection to the service.
109    */
110   struct GNUNET_CLIENT_Connection *client;
111
112   /**
113    * Configuration we use.
114    */
115   const struct GNUNET_CONFIGURATION_Handle *cfg;
116
117   /**
118    * When should this operation time out?
119    */
120   struct GNUNET_TIME_Absolute timeout;
121
122   /**
123    * Backoff for reconnect.
124    */
125   struct GNUNET_TIME_Relative backoff;
126
127   /**
128    * Task ID for reconnect.
129    */
130   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
131
132   /**
133    * Identity of the peer to monitor.
134    */
135   struct GNUNET_PeerIdentity peer;
136
137   /**
138    * Was this a one-shot request?
139    */
140   int one_shot;
141 };
142
143 /**
144  * Check if a state is defined as connected
145  *
146  * @param state the state value
147  * @return GNUNET_YES or GNUNET_NO
148  */
149 int
150 GNUNET_TRANSPORT_is_connected (enum GNUNET_TRANSPORT_PeerState state)
151 {
152   switch (state)
153   {
154   case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
155   case GNUNET_TRANSPORT_PS_INIT_ATS:
156   case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
157   case GNUNET_TRANSPORT_PS_CONNECT_SENT:
158   case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
159   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
160   case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
161   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
162     return GNUNET_NO;
163   case GNUNET_TRANSPORT_PS_CONNECTED:
164   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
165   case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
166   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
167   case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
168   case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
169     return GNUNET_YES;
170   case GNUNET_TRANSPORT_PS_DISCONNECT:
171   case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
172     return GNUNET_NO;
173   default:
174     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
175                 "Unhandled state `%s' \n",
176                 GNUNET_TRANSPORT_p2s (state));
177     GNUNET_break (0);
178     break;
179   }
180   return GNUNET_SYSERR;
181 }
182
183 /**
184  * Convert state to human-readable string.
185  *
186  * @param state the state value
187  * @return corresponding string
188  */
189 const char *
190 GNUNET_TRANSPORT_p2s (enum GNUNET_TRANSPORT_PeerState state)
191 {
192   switch (state)
193   {
194   case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
195     return "S_NOT_CONNECTED";
196   case GNUNET_TRANSPORT_PS_INIT_ATS:
197     return "S_INIT_ATS";
198   case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
199     return "S_INIT_BLACKLIST";
200   case GNUNET_TRANSPORT_PS_CONNECT_SENT:
201     return "S_CONNECT_SENT";
202   case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
203     return "S_CONNECT_RECV_BLACKLIST_INBOUND";
204   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
205     return "S_CONNECT_RECV_ATS";
206   case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
207     return "S_CONNECT_RECV_BLACKLIST";
208   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
209     return "S_CONNECT_RECV_ACK";
210   case GNUNET_TRANSPORT_PS_CONNECTED:
211     return "S_CONNECTED";
212   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
213     return "S_RECONNECT_ATS";
214   case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
215     return "S_RECONNECT_BLACKLIST";
216   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
217     return "S_RECONNECT_SENT";
218   case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
219     return "S_CONNECTED_SWITCHING_BLACKLIST";
220   case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
221     return "S_CONNECTED_SWITCHING_CONNECT_SENT";
222   case GNUNET_TRANSPORT_PS_DISCONNECT:
223     return "S_DISCONNECT";
224   case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
225     return "S_DISCONNECT_FINISHED";
226   default:
227     GNUNET_break (0);
228     return "UNDEFINED";
229   }
230 }
231
232
233 /**
234  * Function called with responses from the service.
235  *
236  * @param cls our 'struct GNUNET_TRANSPORT_PeerAddressLookupContext*'
237  * @param msg NULL on timeout or error, otherwise presumably a
238  *        message with the human-readable address
239  */
240 static void
241 peer_response_processor (void *cls,
242                                  const struct GNUNET_MessageHeader *msg);
243
244
245 /**
246  * Send our subscription request to the service.
247  *
248  * @param pal_ctx our context
249  */
250 static void
251 send_request (struct GNUNET_TRANSPORT_PeerMonitoringContext *pal_ctx)
252 {
253   struct PeerMonitorMessage msg;
254
255   msg.header.size = htons (sizeof (struct PeerMonitorMessage));
256   msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_PEER_REQUEST);
257   msg.one_shot = htonl (pal_ctx->one_shot);
258   msg.timeout = GNUNET_TIME_absolute_hton (pal_ctx->timeout);
259   msg.peer = pal_ctx->peer;
260   GNUNET_assert (GNUNET_OK ==
261                  GNUNET_CLIENT_transmit_and_get_response (pal_ctx->client,
262                                                           &msg.header,
263                                                           GNUNET_TIME_absolute_get_remaining (pal_ctx->timeout),
264                                                           GNUNET_YES,
265                                                           &peer_response_processor,
266                                                           pal_ctx));
267 }
268
269 /**
270  * Task run to re-establish the connection.
271  *
272  * @param cls our 'struct GNUNET_TRANSPORT_PeerAddressLookupContext*'
273  * @param tc scheduler context, unused
274  */
275 static void
276 do_connect (void *cls,
277             const struct GNUNET_SCHEDULER_TaskContext *tc)
278 {
279   struct GNUNET_TRANSPORT_PeerMonitoringContext *pal_ctx = cls;
280
281   pal_ctx->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
282   pal_ctx->client = GNUNET_CLIENT_connect ("transport", pal_ctx->cfg);
283   GNUNET_assert (NULL != pal_ctx->client);
284   send_request (pal_ctx);
285 }
286
287
288 /**
289  * Cut the existing connection and reconnect.
290  *
291  * @param pal_ctx our context
292  */
293 static void
294 reconnect (struct GNUNET_TRANSPORT_PeerMonitoringContext *pal_ctx)
295 {
296   GNUNET_assert (GNUNET_NO == pal_ctx->one_shot);
297   GNUNET_CLIENT_disconnect (pal_ctx->client);
298   pal_ctx->client = NULL;
299   pal_ctx->backoff = GNUNET_TIME_STD_BACKOFF (pal_ctx->backoff);
300   pal_ctx->reconnect_task = GNUNET_SCHEDULER_add_delayed (pal_ctx->backoff,
301                                                           &do_connect,
302                                                           pal_ctx);
303 }
304
305
306 /**
307  * Function called with responses from the service.
308  *
309  * @param cls our 'struct GNUNET_TRANSPORT_PeerMonitoringContext*'
310  * @param msg NULL on timeout or error, otherwise presumably a
311  *        message with the human-readable address
312  */
313 static void
314 peer_response_processor (void *cls, const struct GNUNET_MessageHeader *msg)
315 {
316   struct GNUNET_TRANSPORT_PeerMonitoringContext *pal_ctx = cls;
317   struct PeerIterateResponseMessage *pir_msg;
318   struct GNUNET_HELLO_Address *address;
319   const char *addr;
320   const char *transport_name;
321   uint16_t size;
322   size_t alen;
323   size_t tlen;
324
325   if (msg == NULL)
326   {
327     if (pal_ctx->one_shot)
328     {
329       /* Disconnect */
330       pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL,
331           GNUNET_TRANSPORT_PS_NOT_CONNECTED, GNUNET_TIME_UNIT_ZERO_ABS);
332       GNUNET_TRANSPORT_monitor_peers_cancel (pal_ctx);
333     }
334     else
335     {
336       reconnect (pal_ctx);
337     }
338     return;
339   }
340   size = ntohs (msg->size);
341   GNUNET_break (ntohs (msg->type) ==
342       GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_PEER_RESPONSE);
343   if (size == sizeof (struct GNUNET_MessageHeader))
344   {
345     /* Done! */
346     if (pal_ctx->one_shot)
347     {
348       pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL,
349           GNUNET_TRANSPORT_PS_NOT_CONNECTED, GNUNET_TIME_UNIT_ZERO_ABS);
350       GNUNET_TRANSPORT_monitor_peers_cancel (pal_ctx);
351     }
352     else
353     {
354       reconnect (pal_ctx);
355     }
356     return;
357   }
358
359   if ((size < sizeof (struct PeerIterateResponseMessage)) ||
360       (ntohs (msg->type) !=
361           GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_PEER_RESPONSE))
362   {
363     GNUNET_break (0);
364     if (pal_ctx->one_shot)
365     {
366       pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL,
367           GNUNET_TRANSPORT_PS_NOT_CONNECTED, GNUNET_TIME_UNIT_ZERO_ABS);
368       GNUNET_TRANSPORT_monitor_peers_cancel (pal_ctx);
369     }
370     else
371     {
372       reconnect (pal_ctx);
373     }
374     return;
375   }
376
377   pir_msg = (struct PeerIterateResponseMessage *) msg;
378   tlen = ntohl (pir_msg->pluginlen);
379   alen = ntohl (pir_msg->addrlen);
380
381   if (size != sizeof (struct PeerIterateResponseMessage) + tlen + alen)
382   {
383     GNUNET_break (0);
384     if (pal_ctx->one_shot)
385     {
386       pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL,
387           GNUNET_TRANSPORT_PS_NOT_CONNECTED, GNUNET_TIME_UNIT_ZERO_ABS);
388       GNUNET_TRANSPORT_monitor_peers_cancel (pal_ctx);
389     }
390     else
391     {
392       reconnect (pal_ctx);
393     }
394     return;
395   }
396
397
398   if ( (0 == tlen) && (0 == alen) )
399   {
400     /* No address available */
401     pal_ctx->cb (pal_ctx->cb_cls, &pir_msg->peer, NULL,
402         ntohl(pir_msg->state),
403         GNUNET_TIME_absolute_ntoh (pir_msg->state_timeout));
404   }
405   else
406   {
407     if (0 == tlen)
408     {
409       GNUNET_break (0); /* This must not happen: address without plugin */
410       return;
411     }
412     addr = (const char *) &pir_msg[1];
413     transport_name = &addr[alen];
414
415     if (transport_name[tlen - 1] != '\0')
416     {
417       /* Corrupt plugin name */
418       GNUNET_break (0);
419       if (pal_ctx->one_shot)
420       {
421         pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL,
422             GNUNET_TRANSPORT_PS_NOT_CONNECTED, GNUNET_TIME_UNIT_ZERO_ABS);
423         GNUNET_TRANSPORT_monitor_peers_cancel (pal_ctx);
424       }
425       else
426       {
427         reconnect (pal_ctx);
428       }
429       return;
430     }
431
432     /* notify client */
433     address = GNUNET_HELLO_address_allocate (&pir_msg->peer,
434         transport_name, addr, alen, ntohl(pir_msg->local_address_info));
435     pal_ctx->cb (pal_ctx->cb_cls, &pir_msg->peer, address,
436         ntohl(pir_msg->state),
437         GNUNET_TIME_absolute_ntoh (pir_msg->state_timeout));
438     GNUNET_HELLO_address_free (address);
439
440   }
441
442   /* expect more replies */
443   GNUNET_CLIENT_receive (pal_ctx->client, &peer_response_processor,
444                          pal_ctx,
445                          GNUNET_TIME_absolute_get_remaining (pal_ctx->timeout));
446 }
447
448
449 /**
450  * Return information about a specific peer or all peers currently known to
451  * transport service once or in monitoring mode. To obtain information about
452  * a specific peer, a peer identity can be passed. To obtain information about
453  * all peers currently known to transport service, NULL can be passed as peer
454  * identity.
455  *
456  * For each peer, the callback is called with information about the address used
457  * to communicate with this peer, the state this peer is currently in and the
458  * the current timeout for this state.
459  *
460  * Upon completion, the 'GNUNET_TRANSPORT_PeerIterateCallback' is called one
461  * more time with 'NULL'. After this, the operation must no longer be
462  * explicitly canceled.
463  *
464  * The #GNUNET_TRANSPORT_monitor_peers_cancel call MUST not be called in the
465  * the peer_callback!
466  *
467  * @param cfg configuration to use
468  * @param peer a specific peer identity to obtain information for,
469  *      NULL for all peers
470  * @param one_shot GNUNET_YES to return the current state and then end (with NULL+NULL),
471  *                 GNUNET_NO to monitor peers continuously
472  * @param timeout how long is the lookup allowed to take at most
473  * @param peer_callback function to call with the results
474  * @param peer_callback_cls closure for peer_address_callback
475  */
476 struct GNUNET_TRANSPORT_PeerMonitoringContext *
477 GNUNET_TRANSPORT_monitor_peers (const struct GNUNET_CONFIGURATION_Handle *cfg,
478     const struct GNUNET_PeerIdentity *peer,
479     int one_shot,
480     struct GNUNET_TIME_Relative timeout,
481     GNUNET_TRANSPORT_PeerIterateCallback peer_callback,
482     void *peer_callback_cls)
483 {
484   struct GNUNET_TRANSPORT_PeerMonitoringContext *pal_ctx;
485   struct GNUNET_CLIENT_Connection *client;
486
487   client = GNUNET_CLIENT_connect ("transport", cfg);
488   if (client == NULL)
489     return NULL;
490   if (GNUNET_YES != one_shot)
491     timeout = GNUNET_TIME_UNIT_FOREVER_REL;
492   pal_ctx = GNUNET_new (struct GNUNET_TRANSPORT_PeerMonitoringContext);
493   pal_ctx->cb = peer_callback;
494   pal_ctx->cb_cls = peer_callback_cls;
495   pal_ctx->cfg = cfg;
496   pal_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
497   if (NULL != peer)
498     pal_ctx->peer = *peer;
499   pal_ctx->one_shot = one_shot;
500   pal_ctx->client = client;
501   send_request (pal_ctx);
502
503   return pal_ctx;
504 }
505
506
507 /**
508  * Cancel request to monitor peers
509  *
510  * @param pic handle for the request to cancel
511  */
512 void
513 GNUNET_TRANSPORT_monitor_peers_cancel (struct GNUNET_TRANSPORT_PeerMonitoringContext *pic)
514 {
515   if (NULL != pic->client)
516   {
517     GNUNET_CLIENT_disconnect (pic->client);
518     pic->client = NULL;
519   }
520   if (GNUNET_SCHEDULER_NO_TASK != pic->reconnect_task)
521   {
522     GNUNET_SCHEDULER_cancel (pic->reconnect_task);
523     pic->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
524   }
525   GNUNET_free (pic);
526 }
527
528
529 /**
530  * Return information about pending address validation operations for a specific
531  * or all peers
532  *
533  * @param cfg configuration to use
534  * @param peer a specific peer identity to obtain validation entries for,
535  *      NULL for all peers
536  * @param one_shot GNUNET_YES to return all entries and then end (with NULL+NULL),
537  *                 GNUNET_NO to monitor validation entries continuously
538  * @param timeout how long is the lookup allowed to take at most
539  * @param validation_callback function to call with the results
540  * @param validation_callback_cls closure for peer_address_callback
541  */
542 struct GNUNET_TRANSPORT_ValidationMonitoringContext *
543 GNUNET_TRANSPORT_monitor_validation_entries (const struct
544                                 GNUNET_CONFIGURATION_Handle *cfg,
545                                 const struct GNUNET_PeerIdentity *peer,
546                                 int one_shot,
547                                 struct GNUNET_TIME_Relative timeout,
548                                 GNUNET_TRANSPORT_ValidationIterateCallback validation_callback,
549                                 void *validation_callback_cls)
550 {
551   /* Not implemented */
552   return NULL;
553 }
554
555
556 /**
557  * Return information about all current pending validation operations
558  *
559  * @param vic handle for the request to cancel
560  */
561 void
562 GNUNET_TRANSPORT_monitor_validation_entries_cancel (struct GNUNET_TRANSPORT_ValidationMonitoringContext *vic)
563 {
564   /* Not implemented */
565 }
566
567
568 /* end of transport_api_monitoring.c */