- use proper signedness
[oweals/gnunet.git] / src / transport / transport_api_address_lookup.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file transport/transport_api_address_lookup.c
23  * @brief given a peer id, get all known addresses from transport service
24  *
25  * This api provides the ability to query the transport service about
26  * the status of connections to a specific peer.  Calls back with a
27  * pretty printed string of the address, as formatted by the appropriate
28  * transport plugin, and whether or not the address given is currently
29  * in the 'connected' state (according to the transport service).
30  */
31 #include "platform.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_arm_service.h"
34 #include "gnunet_hello_lib.h"
35 #include "gnunet_protocols.h"
36 #include "gnunet_transport_service.h"
37 #include "transport.h"
38
39 /**
40  * Context for the address lookup.
41  */
42 struct GNUNET_TRANSPORT_PeerIterateContext
43 {
44   /**
45    * Function to call with the binary address.
46    */
47   GNUNET_TRANSPORT_PeerIterateCallback cb;
48
49   /**
50    * Closure for cb.
51    */
52   void *cb_cls;
53
54   /**
55    * Connection to the service.
56    */
57   struct GNUNET_CLIENT_Connection *client;
58
59   /**
60    * Configuration we use.
61    */
62   const struct GNUNET_CONFIGURATION_Handle *cfg;
63
64   /**
65    * When should this operation time out?
66    */
67   struct GNUNET_TIME_Absolute timeout;
68
69   /**
70    * Backoff for reconnect.
71    */
72   struct GNUNET_TIME_Relative backoff;
73
74   /**
75    * Task ID for reconnect.
76    */
77   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
78
79   /**
80    * Identity of the peer to monitor.
81    */
82   struct GNUNET_PeerIdentity peer;
83
84   /**
85    * Was this a one-shot request?
86    */
87   int one_shot;
88 };
89
90
91 /**
92  * Function called with responses from the service.
93  *
94  * @param cls our 'struct GNUNET_TRANSPORT_PeerAddressLookupContext*'
95  * @param msg NULL on timeout or error, otherwise presumably a
96  *        message with the human-readable address
97  */
98 static void
99 peer_address_response_processor (void *cls,
100                                  const struct GNUNET_MessageHeader *msg);
101
102
103 /**
104  * Send our subscription request to the service.
105  *
106  * @param pal_ctx our context
107  */
108 static void
109 send_request (struct GNUNET_TRANSPORT_PeerIterateContext *pal_ctx)
110 {
111   struct AddressIterateMessage msg;
112
113   msg.header.size = htons (sizeof (struct AddressIterateMessage));
114   msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_ITERATE);
115   msg.one_shot = htonl (pal_ctx->one_shot);
116   msg.timeout = GNUNET_TIME_absolute_hton (pal_ctx->timeout);
117   msg.peer = pal_ctx->peer;
118   GNUNET_assert (GNUNET_OK ==
119                  GNUNET_CLIENT_transmit_and_get_response (pal_ctx->client,
120                                                           &msg.header,
121                                                           GNUNET_TIME_absolute_get_remaining (pal_ctx->timeout),
122                                                           GNUNET_YES,
123                                                           &peer_address_response_processor,
124                                                           pal_ctx));
125 }
126
127 /**
128  * Task run to re-establish the connection.
129  *
130  * @param cls our 'struct GNUNET_TRANSPORT_PeerAddressLookupContext*'
131  * @param tc scheduler context, unused
132  */
133 static void
134 do_connect (void *cls,
135             const struct GNUNET_SCHEDULER_TaskContext *tc)
136 {
137   struct GNUNET_TRANSPORT_PeerIterateContext *pal_ctx = cls;
138
139   pal_ctx->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
140   pal_ctx->client = GNUNET_CLIENT_connect ("transport", pal_ctx->cfg);
141   GNUNET_assert (NULL != pal_ctx->client);
142   send_request (pal_ctx);
143 }
144
145
146 /**
147  * Cut the existing connection and reconnect.
148  *
149  * @param pal_ctx our context
150  */
151 static void
152 reconnect (struct GNUNET_TRANSPORT_PeerIterateContext *pal_ctx)
153 {
154   GNUNET_assert (GNUNET_NO == pal_ctx->one_shot);
155   GNUNET_CLIENT_disconnect (pal_ctx->client);
156   pal_ctx->client = NULL;
157   pal_ctx->backoff = GNUNET_TIME_STD_BACKOFF (pal_ctx->backoff);
158   pal_ctx->reconnect_task = GNUNET_SCHEDULER_add_delayed (pal_ctx->backoff,
159                                                           &do_connect,
160                                                           pal_ctx);
161 }
162
163
164 /**
165  * Function called with responses from the service.
166  *
167  * @param cls our 'struct GNUNET_TRANSPORT_PeerAddressLookupContext*'
168  * @param msg NULL on timeout or error, otherwise presumably a
169  *        message with the human-readable address
170  */
171 static void
172 peer_address_response_processor (void *cls,
173                                  const struct GNUNET_MessageHeader *msg)
174 {
175   struct GNUNET_TRANSPORT_PeerIterateContext *pal_ctx = cls;
176   struct AddressIterateResponseMessage *air_msg;
177   struct GNUNET_HELLO_Address *address;
178   const char *addr;
179   const char *transport_name;
180   uint16_t size;
181   size_t alen;
182   size_t tlen;
183
184   if (msg == NULL)
185   {
186     if (pal_ctx->one_shot)
187     {
188       pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL);
189       GNUNET_TRANSPORT_peer_get_active_addresses_cancel (pal_ctx);
190     }
191     else
192     {
193       reconnect (pal_ctx);
194     }
195     return;
196   }
197   size = ntohs (msg->size);
198   GNUNET_break (ntohs (msg->type) ==
199                 GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_ITERATE_RESPONSE);
200   if (size == sizeof (struct GNUNET_MessageHeader))
201   {
202     /* done! */
203     if (pal_ctx->one_shot)
204     {
205       pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL);
206       GNUNET_TRANSPORT_peer_get_active_addresses_cancel (pal_ctx);
207     }
208     else
209     {
210       reconnect (pal_ctx);
211     }
212     return;
213   }
214
215   if ((size < sizeof (struct AddressIterateResponseMessage)) ||
216       (ntohs (msg->type) !=
217        GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_ITERATE_RESPONSE))
218   {
219     GNUNET_break (0);
220     if (pal_ctx->one_shot)
221     {
222       pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL);
223       GNUNET_TRANSPORT_peer_get_active_addresses_cancel (pal_ctx);
224     }
225     else
226     {
227       reconnect (pal_ctx);
228     }
229     return;
230   }
231
232   air_msg = (struct AddressIterateResponseMessage *) msg;
233   tlen = ntohl (air_msg->pluginlen);
234   alen = ntohl (air_msg->addrlen);
235
236   if (size != sizeof (struct AddressIterateResponseMessage) + tlen + alen)
237   {
238     GNUNET_break (0);
239     if (pal_ctx->one_shot)
240     {
241       pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL);
242       GNUNET_TRANSPORT_peer_get_active_addresses_cancel (pal_ctx);
243     }
244     else
245     {
246       reconnect (pal_ctx);
247     }
248     return;
249   }
250
251   if (alen == 0 && tlen == 0)
252   {
253     pal_ctx->cb (pal_ctx->cb_cls, &air_msg->peer, NULL);
254   }
255   else
256   {
257     addr = (const char *) &air_msg[1];
258     transport_name = &addr[alen];
259
260     if (transport_name[tlen - 1] != '\0')
261     {
262       GNUNET_break (0);
263       if (pal_ctx->one_shot)    
264       {
265         pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL);
266         GNUNET_TRANSPORT_peer_get_active_addresses_cancel (pal_ctx);
267       }
268       else
269       {
270         reconnect (pal_ctx);
271       }
272       return;
273     }
274
275     /* notify client */
276     address =
277         GNUNET_HELLO_address_allocate (&air_msg->peer, transport_name, addr,
278                                        alen);
279     pal_ctx->cb (pal_ctx->cb_cls, &air_msg->peer, address);
280     GNUNET_HELLO_address_free (address);
281   }
282
283   /* expect more replies */
284   GNUNET_CLIENT_receive (pal_ctx->client, &peer_address_response_processor,
285                          pal_ctx,
286                          GNUNET_TIME_absolute_get_remaining (pal_ctx->timeout));
287 }
288
289
290 /**
291  * Return all the known addresses for a specific peer or all peers.
292  * Returns continuously all address if one_shot is set to GNUNET_NO
293  *
294  * CHANGE: Returns the address(es) that we are currently using for this
295  * peer.  Upon completion, the 'AddressLookUpCallback' is called one more
296  * time with 'NULL' for the address and the peer.  After this, the operation must no
297  * longer be explicitly canceled.
298  *
299  * @param cfg configuration to use
300  * @param peer peer identity to look up the addresses of, CHANGE: allow NULL for all (connected) peers
301  * @param one_shot GNUNET_YES to return the current state and then end (with NULL+NULL),
302  *                 GNUNET_NO to monitor the set of addresses used (continuously, must be explicitly canceled)
303  * @param timeout how long is the lookup allowed to take at most (irrelevant if one_shot is set to GNUNET_NO)
304  * @param peer_address_callback function to call with the results
305  * @param peer_address_callback_cls closure for peer_address_callback
306  */
307 struct GNUNET_TRANSPORT_PeerIterateContext *
308 GNUNET_TRANSPORT_peer_get_active_addresses (const struct
309                                             GNUNET_CONFIGURATION_Handle *cfg,
310                                             const struct GNUNET_PeerIdentity
311                                             *peer, int one_shot,
312                                             struct GNUNET_TIME_Relative timeout,
313                                             GNUNET_TRANSPORT_PeerIterateCallback
314                                             peer_address_callback,
315                                             void *peer_address_callback_cls)
316 {
317   struct GNUNET_TRANSPORT_PeerIterateContext *pal_ctx;
318   struct GNUNET_CLIENT_Connection *client;
319
320   client = GNUNET_CLIENT_connect ("transport", cfg);
321   if (client == NULL)
322     return NULL;
323   if (GNUNET_YES != one_shot)
324     timeout = GNUNET_TIME_UNIT_FOREVER_REL;
325   pal_ctx = GNUNET_new (struct GNUNET_TRANSPORT_PeerIterateContext);
326   pal_ctx->cb = peer_address_callback;
327   pal_ctx->cb_cls = peer_address_callback_cls;
328   pal_ctx->cfg = cfg;
329   pal_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
330   if (NULL != peer)
331     pal_ctx->peer = *peer;
332   pal_ctx->one_shot = one_shot;
333   pal_ctx->client = client;
334   send_request (pal_ctx);
335
336   return pal_ctx;
337 }
338
339
340 /**
341  * Cancel request for address conversion.
342  *
343  * @param alc handle for the request to cancel
344  */
345 void
346 GNUNET_TRANSPORT_peer_get_active_addresses_cancel (struct
347                                                    GNUNET_TRANSPORT_PeerIterateContext
348                                                    *alc)
349 {
350   if (NULL != alc->client)
351   {
352     GNUNET_CLIENT_disconnect (alc->client);
353     alc->client = NULL;
354   }
355   if (GNUNET_SCHEDULER_NO_TASK != alc->reconnect_task)
356   {
357     GNUNET_SCHEDULER_cancel (alc->reconnect_task);
358     alc->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
359   }
360   GNUNET_free (alc);
361 }
362
363
364 /* end of transport_api_peer_address_lookup.c */