-preparations for replacement of try_connect call
[oweals/gnunet.git] / src / ats / ats_api_connectivity.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010-2015 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file ats/ats_api_connectivity.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_service.h"
28 #include "ats.h"
29
30
31 #define LOG(kind,...) GNUNET_log_from(kind, "ats-connectivity-api", __VA_ARGS__)
32
33
34 /**
35  * Handle for ATS address suggestion requests.
36  */
37 struct GNUNET_ATS_ConnectivitySuggestHandle
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_ConnectivityHandle *ch;
48
49   /**
50    * How urgent is the request.
51    */
52   uint32_t strength;
53 };
54
55
56 /**
57  * Handle to the ATS subsystem for connectivity management.
58  */
59 struct GNUNET_ATS_ConnectivityHandle
60 {
61
62   /**
63    * Our configuration.
64    */
65   const struct GNUNET_CONFIGURATION_Handle *cfg;
66
67   /**
68    * Map with the identities of all the peers for which we would
69    * like to have address suggestions.  The key is the PID, the
70    * value is currently the `struct GNUNET_ATS_ConnectivitySuggestHandle`
71    */
72   struct GNUNET_CONTAINER_MultiPeerMap *sug_requests;
73
74   /**
75    * Connection to ATS service.
76    */
77   struct GNUNET_CLIENT_Connection *client;
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_ConnectivityHandle *ch);
103
104
105 /**
106  * Re-establish the connection to the ATS service.
107  *
108  * @param cls handle to use to re-connect.
109  * @param tc scheduler context
110  */
111 static void
112 reconnect_task (void *cls,
113                 const struct GNUNET_SCHEDULER_TaskContext *tc)
114 {
115   struct GNUNET_ATS_ConnectivityHandle *ch = cls;
116
117   ch->task = NULL;
118   reconnect (ch);
119 }
120
121
122 /**
123  * Disconnect from ATS and then reconnect.
124  *
125  * @param ch our handle
126  */
127 static void
128 force_reconnect (struct GNUNET_ATS_ConnectivityHandle *ch)
129 {
130   if (NULL != ch->mq)
131   {
132     GNUNET_MQ_destroy (ch->mq);
133     ch->mq = NULL;
134   }
135   if (NULL != ch->client)
136   {
137     GNUNET_CLIENT_disconnect (ch->client);
138     ch->client = NULL;
139   }
140   ch->backoff = GNUNET_TIME_STD_BACKOFF (ch->backoff);
141   ch->task = GNUNET_SCHEDULER_add_delayed (ch->backoff,
142                                            &reconnect_task,
143                                            ch);
144 }
145
146
147 /**
148  * We encountered an error handling the MQ to the
149  * ATS service.  Reconnect.
150  *
151  * @param cls the `struct GNUNET_ATS_ConnectivityHandle`
152  * @param error details about the error
153  */
154 static void
155 error_handler (void *cls,
156                enum GNUNET_MQ_Error error)
157 {
158   struct GNUNET_ATS_ConnectivityHandle *ch = cls;
159
160   LOG (GNUNET_ERROR_TYPE_DEBUG,
161        "ATS connection died (code %d), reconnecting\n",
162        (int) error);
163   force_reconnect (ch);
164 }
165
166
167 /**
168  * Transmit request for an address suggestion.
169  *
170  * @param cls the `struct GNUNET_ATS_ConnectivityHandle`
171  * @param peer peer to ask for an address suggestion for
172  * @param value the `struct GNUNET_ATS_SuggestHandle`
173  * @return #GNUNET_OK (continue to iterate), #GNUNET_SYSERR on
174  *         failure (message queue no longer exists)
175  */
176 static int
177 transmit_suggestion (void *cls,
178                      const struct GNUNET_PeerIdentity *peer,
179                      void *value)
180 {
181   struct GNUNET_ATS_ConnectivityHandle *ch = cls;
182   struct GNUNET_ATS_ConnectivitySuggestHandle *sh = value;
183   struct GNUNET_MQ_Envelope *ev;
184   struct RequestAddressMessage *m;
185
186   if (NULL == ch->mq)
187     return GNUNET_SYSERR;
188   ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
189   m->strength = htonl (sh->strength);
190   m->peer = *peer;
191   GNUNET_MQ_send (ch->mq, ev);
192   return GNUNET_OK;
193 }
194
195
196 /**
197  * Re-establish the connection to the ATS service.
198  *
199  * @param ch handle to use to re-connect.
200  */
201 static void
202 reconnect (struct GNUNET_ATS_ConnectivityHandle *ch)
203 {
204   static const struct GNUNET_MQ_MessageHandler handlers[] =
205     { { NULL, 0, 0 } };
206   struct GNUNET_MQ_Envelope *ev;
207   struct ClientStartMessage *init;
208
209   GNUNET_assert (NULL == ch->client);
210   ch->client = GNUNET_CLIENT_connect ("ats", ch->cfg);
211   if (NULL == ch->client)
212   {
213     force_reconnect (ch);
214     return;
215   }
216   ch->mq = GNUNET_MQ_queue_for_connection_client (ch->client,
217                                                   handlers,
218                                                   &error_handler,
219                                                   ch);
220   ev = GNUNET_MQ_msg (init,
221                       GNUNET_MESSAGE_TYPE_ATS_START);
222   init->start_flag = htonl (START_FLAG_CONNECTION_SUGGESTION);
223   GNUNET_MQ_send (ch->mq, ev);
224   if (NULL == ch->mq)
225     return;
226   GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
227                                          &transmit_suggestion,
228                                          ch);
229 }
230
231
232 /**
233  * Initialize the ATS connectivity suggestion client handle.
234  *
235  * @param cfg configuration to use
236  * @return ats connectivity handle, NULL on error
237  */
238 struct GNUNET_ATS_ConnectivityHandle *
239 GNUNET_ATS_connectivity_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
240 {
241   struct GNUNET_ATS_ConnectivityHandle *ch;
242
243   ch = GNUNET_new (struct GNUNET_ATS_ConnectivityHandle);
244   ch->cfg = cfg;
245   ch->sug_requests = GNUNET_CONTAINER_multipeermap_create (32,
246                                                            GNUNET_YES);
247   reconnect (ch);
248   return ch;
249 }
250
251
252 /**
253  * Function called to free all `struct GNUNET_ATS_ConnectivitySuggestHandle`s
254  * in the map.
255  *
256  * @param cls NULL
257  * @param key the key
258  * @param value the value to free
259  * @return #GNUNET_OK (continue to iterate)
260  */
261 static int
262 free_sug_handle (void *cls,
263                  const struct GNUNET_PeerIdentity *key,
264                  void *value)
265 {
266   struct GNUNET_ATS_ConnectivitySuggestHandle *cur = value;
267
268   GNUNET_free (cur);
269   return GNUNET_OK;
270 }
271
272
273 /**
274  * Client is done with ATS connectivity management, release resources.
275  *
276  * @param ch handle to release
277  */
278 void
279 GNUNET_ATS_connectivity_done (struct GNUNET_ATS_ConnectivityHandle *ch)
280 {
281   if (NULL != ch->mq)
282   {
283     GNUNET_MQ_destroy (ch->mq);
284     ch->mq = NULL;
285   }
286   if (NULL != ch->client)
287   {
288     GNUNET_CLIENT_disconnect (ch->client);
289     ch->client = NULL;
290   }
291   if (NULL != ch->task)
292   {
293     GNUNET_SCHEDULER_cancel (ch->task);
294     ch->task = NULL;
295   }
296   GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
297                                          &free_sug_handle,
298                                          NULL);
299   GNUNET_CONTAINER_multipeermap_destroy (ch->sug_requests);
300   GNUNET_free (ch);
301 }
302
303
304 /**
305  * We would like to receive address suggestions for a peer. ATS will
306  * respond with a call to the continuation immediately containing an address or
307  * no address if none is available. ATS can suggest more addresses until we call
308  * #GNUNET_ATS_connectivity_suggest_cancel().
309  *
310  * @param ch handle
311  * @param peer identity of the peer we need an address for
312  * @param strength how urgent is the need for such a suggestion
313  * @return suggest handle, NULL if a request is already pending
314  */
315 struct GNUNET_ATS_ConnectivitySuggestHandle *
316 GNUNET_ATS_connectivity_suggest (struct GNUNET_ATS_ConnectivityHandle *ch,
317                                  const struct GNUNET_PeerIdentity *peer,
318                                  uint32_t strength)
319 {
320   struct GNUNET_ATS_ConnectivitySuggestHandle *s;
321
322   LOG (GNUNET_ERROR_TYPE_DEBUG,
323        "Requesting ATS to suggest address for `%s'\n",
324        GNUNET_i2s (peer));
325   s = GNUNET_new (struct GNUNET_ATS_ConnectivitySuggestHandle);
326   s->ch = ch;
327   s->id = *peer;
328   s->strength = strength;
329   if (GNUNET_OK !=
330       GNUNET_CONTAINER_multipeermap_put (ch->sug_requests,
331                                          &s->id,
332                                          s,
333                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
334   {
335     GNUNET_break (0);
336     return NULL;
337   }
338   if (NULL == ch->mq)
339     return s;
340   (void) transmit_suggestion (ch,
341                               &s->id,
342                               s);
343   return s;
344 }
345
346
347 /**
348  * We no longer care about being connected to a peer.
349  *
350  * @param sh handle to stop
351  */
352 void
353 GNUNET_ATS_connectivity_suggest_cancel (struct GNUNET_ATS_ConnectivitySuggestHandle *sh)
354 {
355   struct GNUNET_ATS_ConnectivityHandle *ch = sh->ch;
356   struct GNUNET_MQ_Envelope *ev;
357   struct RequestAddressMessage *m;
358
359   LOG (GNUNET_ERROR_TYPE_DEBUG,
360        "Telling ATS we no longer care for an address for `%s'\n",
361        GNUNET_i2s (&sh->id));
362   GNUNET_assert (GNUNET_OK ==
363                  GNUNET_CONTAINER_multipeermap_remove (ch->sug_requests,
364                                                        &sh->id,
365                                                        sh));
366   if (NULL == ch->mq)
367   {
368     GNUNET_free (sh);
369     return;
370   }
371   ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
372   m->strength = htonl (0);
373   m->peer = sh->id;
374   GNUNET_MQ_send (ch->mq, ev);
375   GNUNET_free (sh);
376 }
377
378
379 /* end of ats_api_connectivity.c */