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