replace printf() with GNUNET_log()
[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, ...) \
33   GNUNET_log_from (kind, "transport-application-api", __VA_ARGS__)
34
35
36 /**
37  * Handle for TRANSPORT address suggestion requests.
38  */
39 struct GNUNET_TRANSPORT_ApplicationSuggestHandle
40 {
41   /**
42    * ID of the peer for which address suggestion was requested.
43    */
44   struct GNUNET_PeerIdentity id;
45
46   /**
47    * Connecitivity handle this suggestion handle belongs to.
48    */
49   struct GNUNET_TRANSPORT_ApplicationHandle *ch;
50
51   /**
52    * What preference is being expressed?
53    */
54   enum GNUNET_MQ_PriorityPreferences pk;
55
56   /**
57    * How much bandwidth does the client expect?
58    */
59   struct GNUNET_BANDWIDTH_Value32NBO bw;
60 };
61
62
63 /**
64  * Handle to the TRANSPORT subsystem for application management.
65  */
66 struct GNUNET_TRANSPORT_ApplicationHandle
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, &reconnect_task, ch);
136 }
137
138
139 /**
140  * We encountered an error handling the MQ to the
141  * TRANSPORT service.  Reconnect.
142  *
143  * @param cls the `struct GNUNET_TRANSPORT_ApplicationHandle`
144  * @param error details about the error
145  */
146 static void
147 error_handler (void *cls, enum GNUNET_MQ_Error error)
148 {
149   struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls;
150
151   LOG (GNUNET_ERROR_TYPE_DEBUG,
152        "TRANSPORT connection died (code %d), reconnecting\n",
153        (int) error);
154   force_reconnect (ch);
155 }
156
157
158 /**
159  * Transmit request for an address suggestion.
160  *
161  * @param cls the `struct GNUNET_TRANSPORT_ApplicationHandle`
162  * @param peer peer to ask for an address suggestion for
163  * @param value the `struct GNUNET_TRANSPORT_SuggestHandle`
164  * @return #GNUNET_OK (continue to iterate), #GNUNET_SYSERR on
165  *         failure (message queue no longer exists)
166  */
167 static int
168 transmit_suggestion (void *cls,
169                      const struct GNUNET_PeerIdentity *peer,
170                      void *value)
171 {
172   struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls;
173   struct GNUNET_TRANSPORT_ApplicationSuggestHandle *sh = value;
174   struct GNUNET_MQ_Envelope *ev;
175   struct ExpressPreferenceMessage *m;
176
177   if (NULL == ch->mq)
178     return GNUNET_SYSERR;
179   ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST);
180   m->pk = htonl ((uint32_t) sh->pk);
181   m->bw = sh->bw;
182   m->peer = *peer;
183   GNUNET_MQ_send (ch->mq, ev);
184   return GNUNET_OK;
185 }
186
187
188 /**
189  * Re-establish the connection to the TRANSPORT service.
190  *
191  * @param ch handle to use to re-connect.
192  */
193 static void
194 reconnect (struct GNUNET_TRANSPORT_ApplicationHandle *ch)
195 {
196   static const struct GNUNET_MQ_MessageHandler handlers[] = { { NULL, 0, 0 } };
197
198   GNUNET_assert (NULL == ch->mq);
199   ch->mq =
200     GNUNET_CLIENT_connect (ch->cfg, "transport", handlers, &error_handler, ch);
201   if (NULL == ch->mq)
202   {
203     force_reconnect (ch);
204     return;
205   }
206   GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
207                                          &transmit_suggestion,
208                                          ch);
209 }
210
211
212 /**
213  * Initialize the TRANSPORT application suggestion client handle.
214  *
215  * @param cfg configuration to use
216  * @return transport application handle, NULL on error
217  */
218 struct GNUNET_TRANSPORT_ApplicationHandle *
219 GNUNET_TRANSPORT_application_init (
220   const struct GNUNET_CONFIGURATION_Handle *cfg)
221 {
222   struct GNUNET_TRANSPORT_ApplicationHandle *ch;
223
224   ch = GNUNET_new (struct GNUNET_TRANSPORT_ApplicationHandle);
225   ch->cfg = cfg;
226   ch->sug_requests = GNUNET_CONTAINER_multipeermap_create (32, GNUNET_YES);
227   reconnect (ch);
228   return ch;
229 }
230
231
232 /**
233  * Function called to free all `struct GNUNET_TRANSPORT_ApplicationSuggestHandle`s
234  * in the map.
235  *
236  * @param cls NULL
237  * @param key the key
238  * @param value the value to free
239  * @return #GNUNET_OK (continue to iterate)
240  */
241 static int
242 free_sug_handle (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
243 {
244   struct GNUNET_TRANSPORT_ApplicationSuggestHandle *cur = value;
245
246   GNUNET_free (cur);
247   return GNUNET_OK;
248 }
249
250
251 /**
252  * Client is done with TRANSPORT application management, release resources.
253  *
254  * @param ch handle to release
255  */
256 void
257 GNUNET_TRANSPORT_application_done (
258   struct GNUNET_TRANSPORT_ApplicationHandle *ch)
259 {
260   if (NULL != ch->mq)
261   {
262     GNUNET_MQ_destroy (ch->mq);
263     ch->mq = NULL;
264   }
265   if (NULL != ch->task)
266   {
267     GNUNET_SCHEDULER_cancel (ch->task);
268     ch->task = NULL;
269   }
270   GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
271                                          &free_sug_handle,
272                                          NULL);
273   GNUNET_CONTAINER_multipeermap_destroy (ch->sug_requests);
274   GNUNET_free (ch);
275 }
276
277
278 /**
279  * An application would like TRANSPORT to connect to a peer.
280  *
281  * @param ch handle
282  * @param peer identity of the peer we need an address for
283  * @param pk what kind of application will the application require (can be
284  *         #GNUNET_MQ_PRIO_BACKGROUND, we will still try to connect)
285  * @param bw desired bandwith, can be zero (we will still try to connect)
286  * @return suggest handle, NULL if a request is already pending
287  */
288 struct GNUNET_TRANSPORT_ApplicationSuggestHandle *
289 GNUNET_TRANSPORT_application_suggest (
290   struct GNUNET_TRANSPORT_ApplicationHandle *ch,
291   const struct GNUNET_PeerIdentity *peer,
292   enum GNUNET_MQ_PriorityPreferences pk,
293   struct GNUNET_BANDWIDTH_Value32NBO bw)
294 {
295   struct GNUNET_TRANSPORT_ApplicationSuggestHandle *s;
296
297   s = GNUNET_new (struct GNUNET_TRANSPORT_ApplicationSuggestHandle);
298   s->ch = ch;
299   s->id = *peer;
300   s->pk = pk;
301   s->bw = bw;
302   (void) GNUNET_CONTAINER_multipeermap_put (
303     ch->sug_requests,
304     &s->id,
305     s,
306     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
307   LOG (GNUNET_ERROR_TYPE_DEBUG,
308        "Requesting TRANSPORT to suggest address for `%s'\n",
309        GNUNET_i2s (peer));
310   if (NULL == ch->mq)
311     return s;
312   GNUNET_assert (GNUNET_OK == transmit_suggestion (ch, &s->id, s));
313   return s;
314 }
315
316
317 /**
318  * We no longer care about being connected to a peer.
319  *
320  * @param sh handle to stop
321  */
322 void
323 GNUNET_TRANSPORT_application_suggest_cancel (
324   struct GNUNET_TRANSPORT_ApplicationSuggestHandle *sh)
325 {
326   struct GNUNET_TRANSPORT_ApplicationHandle *ch = sh->ch;
327   struct GNUNET_MQ_Envelope *ev;
328   struct ExpressPreferenceMessage *m;
329
330   LOG (GNUNET_ERROR_TYPE_DEBUG,
331        "Telling TRANSPORT we no longer care for an address for `%s'\n",
332        GNUNET_i2s (&sh->id));
333   GNUNET_assert (
334     GNUNET_OK ==
335     GNUNET_CONTAINER_multipeermap_remove (ch->sug_requests, &sh->id, sh));
336   if (NULL == ch->mq)
337   {
338     GNUNET_free (sh);
339     return;
340   }
341   ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST_CANCEL);
342   m->pk = htonl ((uint32_t) sh->pk);
343   m->bw = sh->bw;
344   m->peer = sh->id;
345   GNUNET_MQ_send (ch->mq, ev);
346   GNUNET_free (sh);
347 }
348
349
350 /**
351  * An application (or a communicator) has received a HELLO (or other address
352  * data of another peer) and wants TRANSPORT to validate that the address is
353  * correct.  The result is NOT returned, in fact TRANSPORT may do nothing
354  * (i.e. if it has too many active validations or recently tried this one
355  * already).  If the @a addr validates, TRANSPORT will persist the address
356  * with PEERSTORE.
357  *
358  * @param ch handle
359  * @param peer identity of the peer we have an address for
360  * @param nt network type of @a addr (as claimed by the other peer);
361  *        used by TRANSPORT to avoid trying @a addr's that really cannot work
362  *        due to network type missmatches
363  * @param addr address to validate
364  */
365 void
366 GNUNET_TRANSPORT_application_validate (
367   struct GNUNET_TRANSPORT_ApplicationHandle *ch,
368   const struct GNUNET_PeerIdentity *peer,
369   enum GNUNET_NetworkType nt,
370   const char *addr)
371 {
372   struct GNUNET_MQ_Envelope *ev;
373   struct RequestHelloValidationMessage *m;
374   size_t alen;
375
376   if (NULL == ch->mq)
377   {
378     GNUNET_log (
379       GNUNET_ERROR_TYPE_WARNING,
380       "Address validation for %s:%s skipped as transport is not connected\n",
381       GNUNET_i2s (peer),
382       addr);
383     return;
384   }
385   alen = strlen (addr) + 1;
386   ev =
387     GNUNET_MQ_msg_extra (m,
388                          alen,
389                          GNUNET_MESSAGE_TYPE_TRANSPORT_REQUEST_HELLO_VALIDATION);
390   m->peer = *peer;
391   m->nt = htonl ((uint32_t) nt);
392   memcpy (&m[1], addr, alen);
393   GNUNET_MQ_send (ch->mq, ev);
394 }
395
396
397 /* end of transport_api2_application.c */