convert conversation_api_call.c
[oweals/gnunet.git] / src / ats / ats_api_connectivity.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010-2015 GNUnet e.V.
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  */
110 static void
111 reconnect_task (void *cls)
112 {
113   struct GNUNET_ATS_ConnectivityHandle *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_ConnectivityHandle *ch)
127 {
128   if (NULL != ch->mq)
129   {
130     GNUNET_MQ_destroy (ch->mq);
131     ch->mq = NULL;
132   }
133   if (NULL != ch->client)
134   {
135     GNUNET_CLIENT_disconnect (ch->client);
136     ch->client = NULL;
137   }
138   ch->backoff = GNUNET_TIME_STD_BACKOFF (ch->backoff);
139   ch->task = GNUNET_SCHEDULER_add_delayed (ch->backoff,
140                                            &reconnect_task,
141                                            ch);
142 }
143
144
145 /**
146  * We encountered an error handling the MQ to the
147  * ATS service.  Reconnect.
148  *
149  * @param cls the `struct GNUNET_ATS_ConnectivityHandle`
150  * @param error details about the error
151  */
152 static void
153 error_handler (void *cls,
154                enum GNUNET_MQ_Error error)
155 {
156   struct GNUNET_ATS_ConnectivityHandle *ch = cls;
157
158   LOG (GNUNET_ERROR_TYPE_DEBUG,
159        "ATS connection died (code %d), reconnecting\n",
160        (int) error);
161   force_reconnect (ch);
162 }
163
164
165 /**
166  * Transmit request for an address suggestion.
167  *
168  * @param cls the `struct GNUNET_ATS_ConnectivityHandle`
169  * @param peer peer to ask for an address suggestion for
170  * @param value the `struct GNUNET_ATS_SuggestHandle`
171  * @return #GNUNET_OK (continue to iterate), #GNUNET_SYSERR on
172  *         failure (message queue no longer exists)
173  */
174 static int
175 transmit_suggestion (void *cls,
176                      const struct GNUNET_PeerIdentity *peer,
177                      void *value)
178 {
179   struct GNUNET_ATS_ConnectivityHandle *ch = cls;
180   struct GNUNET_ATS_ConnectivitySuggestHandle *sh = value;
181   struct GNUNET_MQ_Envelope *ev;
182   struct RequestAddressMessage *m;
183
184   if (NULL == ch->mq)
185     return GNUNET_SYSERR;
186   ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
187   m->strength = htonl (sh->strength);
188   m->peer = *peer;
189   GNUNET_MQ_send (ch->mq, ev);
190   return GNUNET_OK;
191 }
192
193
194 /**
195  * Re-establish the connection to the ATS service.
196  *
197  * @param ch handle to use to re-connect.
198  */
199 static void
200 reconnect (struct GNUNET_ATS_ConnectivityHandle *ch)
201 {
202   static const struct GNUNET_MQ_MessageHandler handlers[] =
203     { { NULL, 0, 0 } };
204   struct GNUNET_MQ_Envelope *ev;
205   struct ClientStartMessage *init;
206
207   GNUNET_assert (NULL == ch->client);
208   ch->client = GNUNET_CLIENT_connect ("ats", ch->cfg);
209   if (NULL == ch->client)
210   {
211     force_reconnect (ch);
212     return;
213   }
214   ch->mq = GNUNET_MQ_queue_for_connection_client (ch->client,
215                                                   handlers,
216                                                   &error_handler,
217                                                   ch);
218   ev = GNUNET_MQ_msg (init,
219                       GNUNET_MESSAGE_TYPE_ATS_START);
220   init->start_flag = htonl (START_FLAG_CONNECTION_SUGGESTION);
221   GNUNET_MQ_send (ch->mq, ev);
222   if (NULL == ch->mq)
223     return;
224   GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
225                                          &transmit_suggestion,
226                                          ch);
227 }
228
229
230 /**
231  * Initialize the ATS connectivity suggestion client handle.
232  *
233  * @param cfg configuration to use
234  * @return ats connectivity handle, NULL on error
235  */
236 struct GNUNET_ATS_ConnectivityHandle *
237 GNUNET_ATS_connectivity_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
238 {
239   struct GNUNET_ATS_ConnectivityHandle *ch;
240
241   ch = GNUNET_new (struct GNUNET_ATS_ConnectivityHandle);
242   ch->cfg = cfg;
243   ch->sug_requests = GNUNET_CONTAINER_multipeermap_create (32,
244                                                            GNUNET_YES);
245   reconnect (ch);
246   return ch;
247 }
248
249
250 /**
251  * Function called to free all `struct GNUNET_ATS_ConnectivitySuggestHandle`s
252  * in the map.
253  *
254  * @param cls NULL
255  * @param key the key
256  * @param value the value to free
257  * @return #GNUNET_OK (continue to iterate)
258  */
259 static int
260 free_sug_handle (void *cls,
261                  const struct GNUNET_PeerIdentity *key,
262                  void *value)
263 {
264   struct GNUNET_ATS_ConnectivitySuggestHandle *cur = value;
265
266   GNUNET_free (cur);
267   return GNUNET_OK;
268 }
269
270
271 /**
272  * Client is done with ATS connectivity management, release resources.
273  *
274  * @param ch handle to release
275  */
276 void
277 GNUNET_ATS_connectivity_done (struct GNUNET_ATS_ConnectivityHandle *ch)
278 {
279   if (NULL != ch->mq)
280   {
281     GNUNET_MQ_destroy (ch->mq);
282     ch->mq = NULL;
283   }
284   if (NULL != ch->client)
285   {
286     GNUNET_CLIENT_disconnect (ch->client);
287     ch->client = NULL;
288   }
289   if (NULL != ch->task)
290   {
291     GNUNET_SCHEDULER_cancel (ch->task);
292     ch->task = NULL;
293   }
294   GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
295                                          &free_sug_handle,
296                                          NULL);
297   GNUNET_CONTAINER_multipeermap_destroy (ch->sug_requests);
298   GNUNET_free (ch);
299 }
300
301
302 /**
303  * We would like to receive address suggestions for a peer. ATS will
304  * respond with a call to the continuation immediately containing an address or
305  * no address if none is available. ATS can suggest more addresses until we call
306  * #GNUNET_ATS_connectivity_suggest_cancel().
307  *
308  * @param ch handle
309  * @param peer identity of the peer we need an address for
310  * @param strength how urgent is the need for such a suggestion
311  * @return suggest handle, NULL if a request is already pending
312  */
313 struct GNUNET_ATS_ConnectivitySuggestHandle *
314 GNUNET_ATS_connectivity_suggest (struct GNUNET_ATS_ConnectivityHandle *ch,
315                                  const struct GNUNET_PeerIdentity *peer,
316                                  uint32_t strength)
317 {
318   struct GNUNET_ATS_ConnectivitySuggestHandle *s;
319
320   LOG (GNUNET_ERROR_TYPE_DEBUG,
321        "Requesting ATS to suggest address for `%s'\n",
322        GNUNET_i2s (peer));
323   s = GNUNET_new (struct GNUNET_ATS_ConnectivitySuggestHandle);
324   s->ch = ch;
325   s->id = *peer;
326   s->strength = strength;
327   if (GNUNET_OK !=
328       GNUNET_CONTAINER_multipeermap_put (ch->sug_requests,
329                                          &s->id,
330                                          s,
331                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
332   {
333     GNUNET_break (0);
334     return NULL;
335   }
336   if (NULL == ch->mq)
337     return s;
338   (void) transmit_suggestion (ch,
339                               &s->id,
340                               s);
341   return s;
342 }
343
344
345 /**
346  * We no longer care about being connected to a peer.
347  *
348  * @param sh handle to stop
349  */
350 void
351 GNUNET_ATS_connectivity_suggest_cancel (struct GNUNET_ATS_ConnectivitySuggestHandle *sh)
352 {
353   struct GNUNET_ATS_ConnectivityHandle *ch = sh->ch;
354   struct GNUNET_MQ_Envelope *ev;
355   struct RequestAddressMessage *m;
356
357   LOG (GNUNET_ERROR_TYPE_DEBUG,
358        "Telling ATS we no longer care for an address for `%s'\n",
359        GNUNET_i2s (&sh->id));
360   GNUNET_assert (GNUNET_OK ==
361                  GNUNET_CONTAINER_multipeermap_remove (ch->sug_requests,
362                                                        &sh->id,
363                                                        sh));
364   if (NULL == ch->mq)
365   {
366     GNUNET_free (sh);
367     return;
368   }
369   ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
370   m->strength = htonl (0);
371   m->peer = sh->id;
372   GNUNET_MQ_send (ch->mq, ev);
373   GNUNET_free (sh);
374 }
375
376
377 /* end of ats_api_connectivity.c */