src: for every AGPL3.0 file, add SPDX identifier.
[oweals/gnunet.git] / src / transport / transport_api2_monitor.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2018 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_api2_monitor.c
23  * @brief implementation of the gnunet_transport_monitor_service.h API
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_transport_monitor_service.h"
30 #include "transport.h"
31
32
33 /**
34  * Opaque handle to the transport service for monitors.
35  */
36 struct GNUNET_TRANSPORT_MonitorContext
37 {
38   /**
39    * Our configuration.
40    */
41   const struct GNUNET_CONFIGURATION_Handle *cfg;
42
43   /**
44    * Queue to talk to the transport service.
45    */
46   struct GNUNET_MQ_Handle *mq;
47
48   /**
49    * Peer we monitor, all zeros for "all"
50    */
51   struct GNUNET_PeerIdentity peer;
52
53   /**
54    * #GNUNET_YES to return the current state and then end.
55    */
56   int one_shot;
57
58   /**
59    * Function to call with monitor data.
60    */
61   GNUNET_TRANSPORT_MonitorCallback cb;
62
63   /**
64    * Closure for @e cb.
65    */
66   void *cb_cls;
67
68 };
69
70
71 /**
72  * (re)connect our monitor to the transport service
73  *
74  * @param mc handle to reconnect
75  */
76 static void
77 reconnect (struct GNUNET_TRANSPORT_MonitorContext *mc);
78
79
80 /**
81  * Send message to the transport service about our montoring
82  * desire.
83  *
84  * @param ai address to delete
85  */
86 static void
87 send_start_monitor (struct GNUNET_TRANSPORT_MonitorContext *mc)
88 {
89   struct GNUNET_MQ_Envelope *env;
90   struct GNUNET_TRANSPORT_MonitorStart *smm;
91
92   if (NULL == mc->mq)
93     return;
94   env = GNUNET_MQ_msg (smm,
95                        GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_START);
96   smm->one_shot = htonl ((uint32_t) mc->one_shot);
97   smm->peer = mc->peer;
98   GNUNET_MQ_send (mc->mq,
99                   env);
100 }
101
102
103 /**
104  * Disconnect from the transport service.
105  *
106  * @param mc service to disconnect from
107  */
108 static void
109 disconnect (struct GNUNET_TRANSPORT_MonitorContext *mc)
110 {
111   if (NULL == mc->mq)
112     return;
113   GNUNET_MQ_destroy (mc->mq);
114   mc->mq = NULL;
115 }
116
117
118 /**
119  * Function called on MQ errors. Reconnects to the service.
120  *
121  * @param cls our `struct GNUNET_TRANSPORT_MonitorContext *`
122  * @param error what error happened?
123  */
124 static void
125 error_handler (void *cls,
126                enum GNUNET_MQ_Error error)
127 {
128   struct GNUNET_TRANSPORT_MonitorContext *mc = cls;
129
130   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
131               "MQ failure %d, reconnecting to transport service.\n",
132               error);
133   disconnect (mc);
134   /* TODO: maybe do this with exponential backoff/delay */
135   reconnect (mc);
136 }
137
138
139 /**
140  * Transport service sends us information about what is going on.
141  * Check if @a md is well-formed.
142  *
143  * @param cls our `struct GNUNET_TRANSPORT_MonitorContext *`
144  * @param md the monitor data we got
145  * @return #GNUNET_OK if @a smt is well-formed
146  */
147 static int
148 check_monitor_data (void *cls,
149                     const struct GNUNET_TRANSPORT_MonitorData *md)
150 {
151   uint16_t len = ntohs (md->header.size) - sizeof (*md);
152   const char *addr = (const char *) &md[1];
153
154   (void) cls;
155   if ( (0 == len) ||
156        ('\0' != addr[len-1]) )
157   {
158     GNUNET_break (0);
159     return GNUNET_SYSERR;
160   }
161   return GNUNET_OK;
162 }
163
164
165 /**
166  * Transport service sends us information about what is going on.
167  *
168  * @param cls our `struct GNUNET_TRANSPORT_MonitorContext *`
169  * @param md monitor data
170  */
171 static void
172 handle_monitor_data (void *cls,
173                      const struct GNUNET_TRANSPORT_MonitorData *md)
174 {
175   struct GNUNET_TRANSPORT_MonitorContext *mc = cls;
176   struct GNUNET_TRANSPORT_MonitorInformation mi;
177
178   mi.address = (const char *) &md[1];
179   mi.nt = (enum GNUNET_NetworkType) ntohl (md->nt);
180   mi.cs = (enum GNUNET_TRANSPORT_ConnectionStatus) ntohl (md->cs);
181   mi.num_msg_pending = ntohl (md->num_msg_pending);
182   mi.num_bytes_pending = ntohl (md->num_bytes_pending);
183   mi.last_validation = GNUNET_TIME_absolute_ntoh (md->last_validation);
184   mi.valid_until = GNUNET_TIME_absolute_ntoh (md->valid_until);
185   mi.next_validation = GNUNET_TIME_absolute_ntoh (md->next_validation);
186   mi.rtt = GNUNET_TIME_relative_ntoh (md->rtt);
187   mc->cb (mc->cb_cls,
188           &md->peer,
189           &mi);
190 }
191
192
193 /**
194  * One shot was requested, and transport service is done.
195  *
196  * @param cls our `struct GNUNET_TRANSPORT_MonitorContext *`
197  * @param me end message
198  */
199 static void
200 handle_monitor_end (void *cls,
201                     const struct GNUNET_MessageHeader *me)
202 {
203   struct GNUNET_TRANSPORT_MonitorContext *mc = cls;
204
205   if (GNUNET_YES != mc->one_shot)
206   {
207     GNUNET_break (0);
208     disconnect (mc);
209     reconnect (mc);
210     return;
211   }
212   mc->cb (mc->cb_cls,
213           NULL,
214           NULL);
215   GNUNET_TRANSPORT_monitor_cancel (mc);
216 }
217
218
219 /**
220  * (re)connect our monitor to the transport service
221  *
222  * @param mc handle to reconnect
223  */
224 static void
225 reconnect (struct GNUNET_TRANSPORT_MonitorContext *mc)
226 {
227   struct GNUNET_MQ_MessageHandler handlers[] = {
228     GNUNET_MQ_hd_var_size (monitor_data,
229                            GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_DATA,
230                            struct GNUNET_TRANSPORT_MonitorData,
231                            mc),
232     GNUNET_MQ_hd_fixed_size (monitor_end,
233                              GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_END,
234                              struct GNUNET_MessageHeader,
235                              mc),
236     GNUNET_MQ_handler_end()
237   };
238
239   mc->mq = GNUNET_CLIENT_connect (mc->cfg,
240                                   "transport",
241                                   handlers,
242                                   &error_handler,
243                                   mc);
244   if (NULL == mc->mq)
245     return;
246   send_start_monitor (mc);
247 }
248
249
250 /**
251  * Return information about a specific peer or all peers currently known to
252  * transport service once or in monitoring mode. To obtain information about
253  * a specific peer, a peer identity can be passed. To obtain information about
254  * all peers currently known to transport service, NULL can be passed as peer
255  * identity.
256  *
257  * For each peer, the callback is called with information about the address used
258  * to communicate with this peer, the state this peer is currently in and the
259  * the current timeout for this state.
260  *
261  * Upon completion, the #GNUNET_TRANSPORT_PeerIterateCallback is called one
262  * more time with `NULL`. After this, the operation must no longer be
263  * explicitly canceled.
264  *
265  * The #GNUNET_TRANSPORT_monitor_peers_cancel call MUST not be called in the
266  * the peer_callback!
267  *
268  * @param cfg configuration to use
269  * @param peer a specific peer identity to obtain information for,
270  *      NULL for all peers
271  * @param one_shot #GNUNET_YES to return the current state and then end (with NULL+NULL),
272  *                 #GNUNET_NO to monitor peers continuously
273  * @param cb function to call with the results
274  * @param cb_cls closure for @a mc
275  */
276 struct GNUNET_TRANSPORT_MonitorContext *
277 GNUNET_TRANSPORT_monitor (const struct GNUNET_CONFIGURATION_Handle *cfg,
278                           const struct GNUNET_PeerIdentity *peer,
279                           int one_shot,
280                           GNUNET_TRANSPORT_MonitorCallback cb,
281                           void *cb_cls)
282 {
283   struct GNUNET_TRANSPORT_MonitorContext *mc;
284
285   mc = GNUNET_new (struct GNUNET_TRANSPORT_MonitorContext);
286   mc->cfg = cfg;
287   if (NULL != peer)
288     mc->peer = *peer;
289   mc->one_shot = one_shot;
290   mc->cb = cb;
291   mc->cb_cls = cb_cls;
292   reconnect (mc);
293   if (NULL == mc->mq)
294   {
295     GNUNET_free (mc);
296     return NULL;
297   }
298   return mc;
299 }
300
301
302
303 /**
304  * Cancel request to monitor peers
305  *
306  * @param pmc handle for the request to cancel
307  */
308 void
309 GNUNET_TRANSPORT_monitor_cancel (struct GNUNET_TRANSPORT_MonitorContext *mc)
310 {
311   disconnect (mc);
312   GNUNET_free (mc);
313 }
314
315 /* end of transport_api2_monitor.c */