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