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