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