REST: nothing triggers rest
[oweals/gnunet.git] / src / ats / ats_api2_application.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010-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  * @file ats/ats_api2_application.c
22  * @brief enable clients to ask ATS about establishing connections to peers
23  * @author Christian Grothoff
24  * @author Matthias Wachs
25  */
26 #include "platform.h"
27 #include "gnunet_ats_application_service.h"
28 #include "ats2.h"
29
30
31 #define LOG(kind,...) GNUNET_log_from(kind, "ats-application-api", __VA_ARGS__)
32
33
34 /**
35  * Handle for ATS address suggestion requests.
36  */
37 struct GNUNET_ATS_ApplicationSuggestHandle
38 {
39   /**
40    * ID of the peer for which address suggestion was requested.
41    */
42   struct GNUNET_PeerIdentity id;
43
44   /**
45    * Connecitivity handle this suggestion handle belongs to.
46    */
47   struct GNUNET_ATS_ApplicationHandle *ch;
48
49   /**
50    * What preference is being expressed?
51    */
52   enum GNUNET_MQ_PreferenceKind pk;
53
54   /**
55    * How much bandwidth does the client expect?
56    */
57   struct GNUNET_BANDWIDTH_Value32NBO bw;
58 };
59
60
61 /**
62  * Handle to the ATS subsystem for application management.
63  */
64 struct GNUNET_ATS_ApplicationHandle
65 {
66
67   /**
68    * Our configuration.
69    */
70   const struct GNUNET_CONFIGURATION_Handle *cfg;
71
72   /**
73    * Map with the identities of all the peers for which we would
74    * like to have address suggestions.  The key is the PID, the
75    * value is currently the `struct GNUNET_ATS_ApplicationSuggestHandle`
76    */
77   struct GNUNET_CONTAINER_MultiPeerMap *sug_requests;
78
79   /**
80    * Message queue for sending requests to the ATS service.
81    */
82   struct GNUNET_MQ_Handle *mq;
83
84   /**
85    * Task to trigger reconnect.
86    */
87   struct GNUNET_SCHEDULER_Task *task;
88
89   /**
90    * Reconnect backoff delay.
91    */
92   struct GNUNET_TIME_Relative backoff;
93 };
94
95
96 /**
97  * Re-establish the connection to the ATS service.
98  *
99  * @param ch handle to use to re-connect.
100  */
101 static void
102 reconnect (struct GNUNET_ATS_ApplicationHandle *ch);
103
104
105 /**
106  * Re-establish the connection to the ATS service.
107  *
108  * @param cls handle to use to re-connect.
109  */
110 static void
111 reconnect_task (void *cls)
112 {
113   struct GNUNET_ATS_ApplicationHandle *ch = cls;
114
115   ch->task = NULL;
116   reconnect (ch);
117 }
118
119
120 /**
121  * Disconnect from ATS and then reconnect.
122  *
123  * @param ch our handle
124  */
125 static void
126 force_reconnect (struct GNUNET_ATS_ApplicationHandle *ch)
127 {
128   if (NULL != ch->mq)
129   {
130     GNUNET_MQ_destroy (ch->mq);
131     ch->mq = NULL;
132   }
133   ch->backoff = GNUNET_TIME_STD_BACKOFF (ch->backoff);
134   ch->task = GNUNET_SCHEDULER_add_delayed (ch->backoff,
135                                            &reconnect_task,
136                                            ch);
137 }
138
139
140 /**
141  * We encountered an error handling the MQ to the
142  * ATS service.  Reconnect.
143  *
144  * @param cls the `struct GNUNET_ATS_ApplicationHandle`
145  * @param error details about the error
146  */
147 static void
148 error_handler (void *cls,
149                enum GNUNET_MQ_Error error)
150 {
151   struct GNUNET_ATS_ApplicationHandle *ch = cls;
152
153   LOG (GNUNET_ERROR_TYPE_DEBUG,
154        "ATS connection died (code %d), reconnecting\n",
155        (int) error);
156   force_reconnect (ch);
157 }
158
159
160 /**
161  * Transmit request for an address suggestion.
162  *
163  * @param cls the `struct GNUNET_ATS_ApplicationHandle`
164  * @param peer peer to ask for an address suggestion for
165  * @param value the `struct GNUNET_ATS_SuggestHandle`
166  * @return #GNUNET_OK (continue to iterate), #GNUNET_SYSERR on
167  *         failure (message queue no longer exists)
168  */
169 static int
170 transmit_suggestion (void *cls,
171                      const struct GNUNET_PeerIdentity *peer,
172                      void *value)
173 {
174   struct GNUNET_ATS_ApplicationHandle *ch = cls;
175   struct GNUNET_ATS_ApplicationSuggestHandle *sh = value;
176   struct GNUNET_MQ_Envelope *ev;
177   struct ExpressPreferenceMessage *m;
178
179   if (NULL == ch->mq)
180     return GNUNET_SYSERR;
181   ev = GNUNET_MQ_msg (m,
182                       GNUNET_MESSAGE_TYPE_ATS_SUGGEST);
183   m->pk = htonl ((uint32_t) sh->pk);
184   m->bw = sh->bw;
185   m->peer = *peer;
186   GNUNET_MQ_send (ch->mq, ev);
187   return GNUNET_OK;
188 }
189
190
191 /**
192  * Re-establish the connection to the ATS service.
193  *
194  * @param ch handle to use to re-connect.
195  */
196 static void
197 reconnect (struct GNUNET_ATS_ApplicationHandle *ch)
198 {
199   static const struct GNUNET_MQ_MessageHandler handlers[] = {
200     { NULL, 0, 0 }
201   };
202
203   GNUNET_assert (NULL == ch->mq);
204   ch->mq = GNUNET_CLIENT_connect (ch->cfg,
205                                   "ats",
206                                   handlers,
207                                   &error_handler,
208                                   ch);
209   if (NULL == ch->mq)
210   {
211     force_reconnect (ch);
212     return;
213   }
214   GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
215                                          &transmit_suggestion,
216                                          ch);
217 }
218
219
220 /**
221  * Initialize the ATS application suggestion client handle.
222  *
223  * @param cfg configuration to use
224  * @return ats application handle, NULL on error
225  */
226 struct GNUNET_ATS_ApplicationHandle *
227 GNUNET_ATS_application_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
228 {
229   struct GNUNET_ATS_ApplicationHandle *ch;
230
231   ch = GNUNET_new (struct GNUNET_ATS_ApplicationHandle);
232   ch->cfg = cfg;
233   ch->sug_requests = GNUNET_CONTAINER_multipeermap_create (32,
234                                                            GNUNET_YES);
235   reconnect (ch);
236   return ch;
237 }
238
239
240 /**
241  * Function called to free all `struct GNUNET_ATS_ApplicationSuggestHandle`s
242  * in the map.
243  *
244  * @param cls NULL
245  * @param key the key
246  * @param value the value to free
247  * @return #GNUNET_OK (continue to iterate)
248  */
249 static int
250 free_sug_handle (void *cls,
251                  const struct GNUNET_PeerIdentity *key,
252                  void *value)
253 {
254   struct GNUNET_ATS_ApplicationSuggestHandle *cur = value;
255
256   GNUNET_free (cur);
257   return GNUNET_OK;
258 }
259
260
261 /**
262  * Client is done with ATS application management, release resources.
263  *
264  * @param ch handle to release
265  */
266 void
267 GNUNET_ATS_application_done (struct GNUNET_ATS_ApplicationHandle *ch)
268 {
269   if (NULL != ch->mq)
270   {
271     GNUNET_MQ_destroy (ch->mq);
272     ch->mq = NULL;
273   }
274   if (NULL != ch->task)
275   {
276     GNUNET_SCHEDULER_cancel (ch->task);
277     ch->task = NULL;
278   }
279   GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
280                                          &free_sug_handle,
281                                          NULL);
282   GNUNET_CONTAINER_multipeermap_destroy (ch->sug_requests);
283   GNUNET_free (ch);
284 }
285
286
287 /**
288  * We would like to receive address suggestions for a peer. ATS will
289  * respond with a call to the continuation immediately containing an address or
290  * no address if none is available. ATS can suggest more addresses until we call
291  * #GNUNET_ATS_application_suggest_cancel().
292  *
293  * @param ch handle
294  * @param peer identity of the peer we need an address for
295  * @param pk what kind of application will the application require (can be
296  *         #GNUNET_MQ_PREFERENCE_NONE, we will still try to connect)
297  * @param bw desired bandwith, can be zero (we will still try to connect)
298  * @return suggest handle, NULL if a request is already pending
299  */
300 struct GNUNET_ATS_ApplicationSuggestHandle *
301 GNUNET_ATS_application_suggest (struct GNUNET_ATS_ApplicationHandle *ch,
302                                 const struct GNUNET_PeerIdentity *peer,
303                                 enum GNUNET_MQ_PreferenceKind pk,
304                                 struct GNUNET_BANDWIDTH_Value32NBO bw)
305 {
306   struct GNUNET_ATS_ApplicationSuggestHandle *s;
307
308   s = GNUNET_new (struct GNUNET_ATS_ApplicationSuggestHandle);
309   s->ch = ch;
310   s->id = *peer;
311   s->pk = pk;
312   s->bw = bw;
313   (void) GNUNET_CONTAINER_multipeermap_put (ch->sug_requests,
314                                             &s->id,
315                                             s,
316                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
317   LOG (GNUNET_ERROR_TYPE_DEBUG,
318        "Requesting ATS to suggest address for `%s'\n",
319        GNUNET_i2s (peer));
320   if (NULL == ch->mq)
321     return s;
322   GNUNET_assert (GNUNET_OK ==
323                  transmit_suggestion (ch,
324                                       &s->id,
325                                       s));
326   return s;
327 }
328
329
330 /**
331  * We no longer care about being connected to a peer.
332  *
333  * @param sh handle to stop
334  */
335 void
336 GNUNET_ATS_application_suggest_cancel (struct GNUNET_ATS_ApplicationSuggestHandle *sh)
337 {
338   struct GNUNET_ATS_ApplicationHandle *ch = sh->ch;
339   struct GNUNET_MQ_Envelope *ev;
340   struct ExpressPreferenceMessage *m;
341
342   LOG (GNUNET_ERROR_TYPE_DEBUG,
343        "Telling ATS we no longer care for an address for `%s'\n",
344        GNUNET_i2s (&sh->id));
345   GNUNET_assert (GNUNET_OK ==
346                  GNUNET_CONTAINER_multipeermap_remove (ch->sug_requests,
347                                                        &sh->id,
348                                                        sh));
349   if (NULL == ch->mq)
350   {
351     GNUNET_free (sh);
352     return;
353   }
354   ev = GNUNET_MQ_msg (m,
355                       GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL);
356   m->pk = htonl ((uint32_t) sh->pk);
357   m->bw = sh->bw;
358   m->peer = sh->id;
359   GNUNET_MQ_send (ch->mq,
360                   ev);
361   GNUNET_free (sh);
362 }
363
364
365 /* end of ats_api2_application.c */