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