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