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