more work towards fixing #5385
[oweals/gnunet.git] / src / cadet / cadet_api_get_path.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011, 2017 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  * @file cadet/cadet_api_get_path.c
22  * @brief cadet api: client implementation of cadet service
23  * @author Bartlomiej Polot
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_cadet_service.h"
30 #include "cadet.h"
31 #include "cadet_protocol.h"
32
33
34 /**
35  * Operation handle.
36  */
37 struct GNUNET_CADET_GetPath
38 {
39
40   /**
41    * Monitor callback
42    */
43   GNUNET_CADET_PathCB path_cb;
44
45   /**
46    * Closure for @c path_cb.
47    */
48   void *path_cb_cls;
49
50   /**
51    * Message queue to talk to CADET service.
52    */
53   struct GNUNET_MQ_Handle *mq;
54
55   /**
56    * Configuration we use.
57    */
58   const struct GNUNET_CONFIGURATION_Handle *cfg;
59
60   /**
61    * Task to reconnect.
62    */
63   struct GNUNET_SCHEDULER_Task *reconnect_task;
64
65   /**
66    * Backoff for reconnect attempts.
67    */
68   struct GNUNET_TIME_Relative backoff;
69   
70   /**
71    * Peer we want information about.
72    */
73   struct GNUNET_PeerIdentity id;
74
75 };
76
77
78 /**
79  * Check that message received from CADET service is well-formed.
80  *
81  * @param cls unused
82  * @param message the message we got
83  * @return #GNUNET_OK if the message is well-formed,
84  *         #GNUNET_SYSERR otherwise
85  */
86 static int
87 check_get_path (void *cls,
88                 const struct GNUNET_CADET_LocalInfoPath *message)
89 {
90   size_t msize = sizeof (struct GNUNET_CADET_LocalInfoPath);
91   size_t esize;
92
93   (void) cls;
94   esize = ntohs (message->header.size);
95   if (esize < msize)
96   {
97     GNUNET_break (0);
98     return GNUNET_SYSERR;
99   }
100   if (0 != ((esize - msize) % sizeof (struct GNUNET_PeerIdentity)))
101   {
102     GNUNET_break (0);
103     return GNUNET_SYSERR;
104   }
105   return GNUNET_OK;
106 }
107
108
109 /**
110  * Process a local peer info reply, pass info to the user.
111  *
112  * @param cls Closure 
113  * @param message Message itself.
114  */
115 static void
116 handle_get_path (void *cls,
117                  const struct GNUNET_CADET_LocalInfoPath *message)
118 {
119   struct GNUNET_CADET_GetPath *gp = cls;
120   struct GNUNET_CADET_PeerPathDetail ppd;
121   
122   ppd.peer = gp->id;
123   ppd.path = (const struct GNUNET_PeerIdentity *) &message[1];
124   ppd.path_length = (ntohs (message->header.size) - sizeof (*message))
125     / sizeof (struct GNUNET_PeerIdentity);
126   gp->path_cb (gp->path_cb_cls,
127                &ppd);
128 }
129
130
131 /**
132  * Process a local peer info reply, pass info to the user.
133  *
134  * @param cls Closure 
135  * @param message Message itself.
136  */
137 static void
138 handle_get_path_end (void *cls,
139                      const struct GNUNET_MessageHeader *message)
140 {
141   struct GNUNET_CADET_GetPath *gp = cls;
142
143   (void) message;
144   gp->path_cb (gp->path_cb_cls,
145                NULL);
146   GNUNET_CADET_get_path_cancel (gp);
147 }
148
149
150 /**
151  * Reconnect to the service and try again.
152  *
153  * @param cls a `struct GNUNET_CADET_GetPath` operation
154  */
155 static void
156 reconnect (void *cls);
157
158
159 /**
160  * Function called on connection trouble.  Reconnects.
161  *
162  * @param cls a `struct GNUNET_CADET_GetPath`
163  * @param error error code from MQ
164  */
165 static void
166 error_handler (void *cls,
167                enum GNUNET_MQ_Error error)
168 {
169   struct GNUNET_CADET_GetPath *gp = cls;
170
171   GNUNET_MQ_destroy (gp->mq);
172   gp->mq = NULL;
173   gp->backoff = GNUNET_TIME_randomized_backoff (gp->backoff,
174                                                 GNUNET_TIME_UNIT_MINUTES);
175   gp->reconnect_task = GNUNET_SCHEDULER_add_delayed (gp->backoff,
176                                                      &reconnect,
177                                                      gp);
178 }
179
180   
181 /**
182  * Reconnect to the service and try again.
183  *
184  * @param cls a `struct GNUNET_CADET_GetPath` operation
185  */
186 static void
187 reconnect (void *cls)
188 {
189   struct GNUNET_CADET_GetPath *gp = cls;
190   struct GNUNET_MQ_MessageHandler handlers[] = {
191     GNUNET_MQ_hd_var_size (get_path,
192                            GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PATH,
193                            struct GNUNET_CADET_LocalInfoPath,
194                            gp),
195     GNUNET_MQ_hd_fixed_size (get_path_end,
196                              GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PATH_END,
197                              struct GNUNET_MessageHeader,
198                              gp),
199     GNUNET_MQ_handler_end ()
200   };
201   struct GNUNET_CADET_RequestPathInfoMessage *msg;
202   struct GNUNET_MQ_Envelope *env;
203
204   gp->reconnect_task = NULL;
205   gp->mq = GNUNET_CLIENT_connect (gp->cfg,
206                                   "cadet",
207                                   handlers,
208                                   &error_handler,
209                                   gp);
210   if (NULL == gp->mq)
211     return;
212   env = GNUNET_MQ_msg (msg,
213                        GNUNET_MESSAGE_TYPE_CADET_LOCAL_REQUEST_INFO_PATH);
214   msg->peer = gp->id;
215   GNUNET_MQ_send (gp->mq,
216                   env);
217 }
218
219
220 /**
221  * Request information about paths known to the running cadet peer.
222  *
223  * @param cfg configuration to use
224  * @param id Peer whose paths to examine.
225  * @param callback Function to call with the requested data.
226  * @param callback_cls Closure for @c callback.
227  * @return NULL on error
228  */
229 struct GNUNET_CADET_GetPath *
230 GNUNET_CADET_get_path (const struct GNUNET_CONFIGURATION_Handle *cfg,
231                        const struct GNUNET_PeerIdentity *id,
232                        GNUNET_CADET_PathCB callback,
233                        void *callback_cls)
234 {
235   struct GNUNET_CADET_GetPath *gp;
236
237   if (NULL == callback)
238   {
239     GNUNET_break (0);
240     return NULL;
241   }
242   gp = GNUNET_new (struct GNUNET_CADET_GetPath);
243   gp->path_cb = callback;
244   gp->path_cb_cls = callback_cls;
245   gp->cfg = cfg;
246   gp->id = *id;
247   reconnect (gp);
248   if (NULL == gp->mq)
249   {
250     GNUNET_free (gp);
251     return NULL;
252   }
253   return gp;
254 }
255
256
257 /**
258  * Cancel @a gp operation.
259  *
260  * @param gp operation to cancel
261  * @return closure from #GNUNET_CADET_get_path().
262  */
263 void *
264 GNUNET_CADET_get_path_cancel (struct GNUNET_CADET_GetPath *gp)
265 {
266   void *ret = gp->path_cb_cls;
267
268   if (NULL != gp->mq)
269     GNUNET_MQ_destroy (gp->mq);
270   if (NULL != gp->reconnect_task)
271     GNUNET_SCHEDULER_cancel (gp->reconnect_task);
272   GNUNET_free (gp);
273   return ret;
274 }
275
276
277 /* end of cadet_api_get_path.c */