tolerate additional IPv4 address now available for gnunet.org
[oweals/gnunet.git] / src / transport / transport_api2_application.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010--2019 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 transport/transport_api2_application.c
22  * @brief enable clients to ask TRANSPORT about establishing connections to peers
23  * @author Christian Grothoff
24  * @author Matthias Wachs
25  */
26 #include "platform.h"
27 #include "gnunet_transport_application_service.h"
28 #include "gnunet_transport_core_service.h"
29 #include "transport.h"
30
31
32 #define LOG(kind,...) GNUNET_log_from(kind, "transport-application-api", __VA_ARGS__)
33
34
35 /**
36  * Handle for TRANSPORT address suggestion requests.
37  */
38 struct GNUNET_TRANSPORT_ApplicationSuggestHandle
39 {
40   /**
41    * ID of the peer for which address suggestion was requested.
42    */
43   struct GNUNET_PeerIdentity id;
44
45   /**
46    * Connecitivity handle this suggestion handle belongs to.
47    */
48   struct GNUNET_TRANSPORT_ApplicationHandle *ch;
49
50   /**
51    * What preference is being expressed?
52    */
53   enum GNUNET_MQ_PreferenceKind pk;
54
55   /**
56    * How much bandwidth does the client expect?
57    */
58   struct GNUNET_BANDWIDTH_Value32NBO bw;
59 };
60
61
62 /**
63  * Handle to the TRANSPORT subsystem for application management.
64  */
65 struct GNUNET_TRANSPORT_ApplicationHandle
66 {
67
68   /**
69    * Our configuration.
70    */
71   const struct GNUNET_CONFIGURATION_Handle *cfg;
72
73   /**
74    * Map with the identities of all the peers for which we would
75    * like to have address suggestions.  The key is the PID, the
76    * value is currently the `struct GNUNET_TRANSPORT_ApplicationSuggestHandle`
77    */
78   struct GNUNET_CONTAINER_MultiPeerMap *sug_requests;
79
80   /**
81    * Message queue for sending requests to the TRANSPORT service.
82    */
83   struct GNUNET_MQ_Handle *mq;
84
85   /**
86    * Task to trigger reconnect.
87    */
88   struct GNUNET_SCHEDULER_Task *task;
89
90   /**
91    * Reconnect backoff delay.
92    */
93   struct GNUNET_TIME_Relative backoff;
94 };
95
96
97 /**
98  * Re-establish the connection to the TRANSPORT service.
99  *
100  * @param ch handle to use to re-connect.
101  */
102 static void
103 reconnect (struct GNUNET_TRANSPORT_ApplicationHandle *ch);
104
105
106 /**
107  * Re-establish the connection to the TRANSPORT service.
108  *
109  * @param cls handle to use to re-connect.
110  */
111 static void
112 reconnect_task (void *cls)
113 {
114   struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls;
115
116   ch->task = NULL;
117   reconnect (ch);
118 }
119
120
121 /**
122  * Disconnect from TRANSPORT and then reconnect.
123  *
124  * @param ch our handle
125  */
126 static void
127 force_reconnect (struct GNUNET_TRANSPORT_ApplicationHandle *ch)
128 {
129   if (NULL != ch->mq)
130   {
131     GNUNET_MQ_destroy (ch->mq);
132     ch->mq = NULL;
133   }
134   ch->backoff = GNUNET_TIME_STD_BACKOFF (ch->backoff);
135   ch->task = GNUNET_SCHEDULER_add_delayed (ch->backoff,
136                                            &reconnect_task,
137                                            ch);
138 }
139
140
141 /**
142  * We encountered an error handling the MQ to the
143  * TRANSPORT service.  Reconnect.
144  *
145  * @param cls the `struct GNUNET_TRANSPORT_ApplicationHandle`
146  * @param error details about the error
147  */
148 static void
149 error_handler (void *cls,
150                enum GNUNET_MQ_Error error)
151 {
152   struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls;
153
154   LOG (GNUNET_ERROR_TYPE_DEBUG,
155        "TRANSPORT connection died (code %d), reconnecting\n",
156        (int) error);
157   force_reconnect (ch);
158 }
159
160
161 /**
162  * Transmit request for an address suggestion.
163  *
164  * @param cls the `struct GNUNET_TRANSPORT_ApplicationHandle`
165  * @param peer peer to ask for an address suggestion for
166  * @param value the `struct GNUNET_TRANSPORT_SuggestHandle`
167  * @return #GNUNET_OK (continue to iterate), #GNUNET_SYSERR on
168  *         failure (message queue no longer exists)
169  */
170 static int
171 transmit_suggestion (void *cls,
172                      const struct GNUNET_PeerIdentity *peer,
173                      void *value)
174 {
175   struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls;
176   struct GNUNET_TRANSPORT_ApplicationSuggestHandle *sh = value;
177   struct GNUNET_MQ_Envelope *ev;
178   struct ExpressPreferenceMessage *m;
179
180   if (NULL == ch->mq)
181     return GNUNET_SYSERR;
182   ev = GNUNET_MQ_msg (m,
183                       GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST);
184   m->pk = htonl ((uint32_t) sh->pk);
185   m->bw = sh->bw;
186   m->peer = *peer;
187   GNUNET_MQ_send (ch->mq, ev);
188   return GNUNET_OK;
189 }
190
191
192 /**
193  * Re-establish the connection to the TRANSPORT service.
194  *
195  * @param ch handle to use to re-connect.
196  */
197 static void
198 reconnect (struct GNUNET_TRANSPORT_ApplicationHandle *ch)
199 {
200   static const struct GNUNET_MQ_MessageHandler handlers[] = {
201     { NULL, 0, 0 }
202   };
203
204   GNUNET_assert (NULL == ch->mq);
205   ch->mq = GNUNET_CLIENT_connect (ch->cfg,
206                                   "transport",
207                                   handlers,
208                                   &error_handler,
209                                   ch);
210   if (NULL == ch->mq)
211   {
212     force_reconnect (ch);
213     return;
214   }
215   GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
216                                          &transmit_suggestion,
217                                          ch);
218 }
219
220
221 /**
222  * Initialize the TRANSPORT application suggestion client handle.
223  *
224  * @param cfg configuration to use
225  * @return transport application handle, NULL on error
226  */
227 struct GNUNET_TRANSPORT_ApplicationHandle *
228 GNUNET_TRANSPORT_application_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
229 {
230   struct GNUNET_TRANSPORT_ApplicationHandle *ch;
231
232   ch = GNUNET_new (struct GNUNET_TRANSPORT_ApplicationHandle);
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_TRANSPORT_ApplicationSuggestHandle`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_TRANSPORT_ApplicationSuggestHandle *cur = value;
256
257   GNUNET_free (cur);
258   return GNUNET_OK;
259 }
260
261
262 /**
263  * Client is done with TRANSPORT application management, release resources.
264  *
265  * @param ch handle to release
266  */
267 void
268 GNUNET_TRANSPORT_application_done (struct GNUNET_TRANSPORT_ApplicationHandle *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. TRANSPORT will
290  * respond with a call to the continuation immediately containing an address or
291  * no address if none is available. TRANSPORT can suggest more addresses until we call
292  * #GNUNET_TRANSPORT_application_suggest_cancel().
293  *
294  * @param ch handle
295  * @param peer identity of the peer we need an address for
296  * @param pk what kind of application will the application require (can be
297  *         #GNUNET_MQ_PREFERENCE_NONE, we will still try to connect)
298  * @param bw desired bandwith, can be zero (we will still try to connect)
299  * @return suggest handle, NULL if a request is already pending
300  */
301 struct GNUNET_TRANSPORT_ApplicationSuggestHandle *
302 GNUNET_TRANSPORT_application_suggest (struct GNUNET_TRANSPORT_ApplicationHandle *ch,
303                                       const struct GNUNET_PeerIdentity *peer,
304                                       enum GNUNET_MQ_PreferenceKind pk,
305                                       struct GNUNET_BANDWIDTH_Value32NBO bw)
306 {
307   struct GNUNET_TRANSPORT_ApplicationSuggestHandle *s;
308
309   s = GNUNET_new (struct GNUNET_TRANSPORT_ApplicationSuggestHandle);
310   s->ch = ch;
311   s->id = *peer;
312   s->pk = pk;
313   s->bw = bw;
314   (void) GNUNET_CONTAINER_multipeermap_put (ch->sug_requests,
315                                             &s->id,
316                                             s,
317                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
318   LOG (GNUNET_ERROR_TYPE_DEBUG,
319        "Requesting TRANSPORT to suggest address for `%s'\n",
320        GNUNET_i2s (peer));
321   if (NULL == ch->mq)
322     return s;
323   GNUNET_assert (GNUNET_OK ==
324                  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_TRANSPORT_application_suggest_cancel (struct GNUNET_TRANSPORT_ApplicationSuggestHandle *sh)
338 {
339   struct GNUNET_TRANSPORT_ApplicationHandle *ch = sh->ch;
340   struct GNUNET_MQ_Envelope *ev;
341   struct ExpressPreferenceMessage *m;
342
343   LOG (GNUNET_ERROR_TYPE_DEBUG,
344        "Telling TRANSPORT 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,
356                       GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST_CANCEL);
357   m->pk = htonl ((uint32_t) sh->pk);
358   m->bw = sh->bw;
359   m->peer = sh->id;
360   GNUNET_MQ_send (ch->mq,
361                   ev);
362   GNUNET_free (sh);
363 }
364
365
366
367 /**
368  * An application (or a communicator) has received a HELLO (or other address
369  * data of another peer) and wants TRANSPORT to validate that the address is
370  * correct.  The result is NOT returned, in fact TRANSPORT may do nothing
371  * (i.e. if it has too many active validations or recently tried this one
372  * already).  If the @a addr validates, TRANSPORT will persist the address
373  * with PEERSTORE.
374  *
375  * @param ch handle
376  * @param peer identity of the peer we have an address for
377  * @param expiration when does @a addr expire; used by TRANSPORT to know when
378  *        to definitively give up attempting to validate
379  * @param nt network type of @a addr (as claimed by the other peer);
380  *        used by TRANSPORT to avoid trying @a addr's that really cannot work
381  *        due to network type missmatches
382  * @param addr address to validate
383  */
384 void
385 GNUNET_TRANSPORT_application_validate (struct GNUNET_TRANSPORT_ApplicationHandle *ch,
386                                        const struct GNUNET_PeerIdentity *peer,
387                                        struct GNUNET_TIME_Absolute expiration,
388                                        enum GNUNET_NetworkType nt,
389                                        const char *addr)
390 {
391   struct GNUNET_MQ_Envelope *ev;
392   struct RequestHelloValidationMessage *m;
393   size_t alen;
394
395   if (NULL == ch->mq)
396   {
397     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
398                 "Address validation for %s:%s skipped as transport is not connected\n",
399                 GNUNET_i2s (peer),
400                 addr);
401     return;
402   }
403   alen = strlen (addr) + 1;
404   ev = GNUNET_MQ_msg_extra (m,
405                             alen,
406                             GNUNET_MESSAGE_TYPE_TRANSPORT_REQUEST_HELLO_VALIDATION);
407   m->peer = *peer;
408   m->expiration = GNUNET_TIME_absolute_hton (expiration);
409   m->nt = htonl ((uint32_t) nt);
410   memcpy (&m[1],
411           addr,
412           alen);
413   GNUNET_MQ_send (ch->mq,
414                   ev);
415 }
416
417
418 /* end of transport_api2_application.c */