- implementation for mantis 0002485
[oweals/gnunet.git] / src / transport / transport_api_address_lookup.c
index cd2e735843e9a6347277acf10fff0cd91607a0b2..655be833f239c83ff89038cf3466ea0296aadcbf 100644 (file)
@@ -4,7 +4,7 @@
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      Boston, MA 02111-1307, USA.
 */
      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      Boston, MA 02111-1307, USA.
 */
+
+/**
+ * @file transport/transport_api_address_lookup.c
+ * @brief given a peer id, get all known addresses from transport service
+ *
+ * This api provides the ability to query the transport service about
+ * the status of connections to a specific peer.  Calls back with a
+ * pretty printed string of the address, as formatted by the appropriate
+ * transport plugin, and whether or not the address given is currently
+ * in the 'connected' state (according to the transport service).
+ */
+
 #include "platform.h"
 #include "gnunet_client_lib.h"
 #include "gnunet_arm_service.h"
 #include "platform.h"
 #include "gnunet_client_lib.h"
 #include "gnunet_arm_service.h"
 
 /**
  * Context for the address lookup.
 
 /**
  * Context for the address lookup.
- */ 
-struct AddressLookupCtx
+ */
+struct GNUNET_TRANSPORT_PeerIterateContext
 {
   /**
 {
   /**
-   * Function to call with the human-readable address.
+   * Function to call with the binary address.
    */
    */
-  GNUNET_TRANSPORT_AddressLookUpCallback cb;
+  GNUNET_TRANSPORT_PeerIterateCallback cb;
 
   /**
    * Closure for cb.
 
   /**
    * Closure for cb.
@@ -47,133 +59,311 @@ struct AddressLookupCtx
    */
   struct GNUNET_CLIENT_Connection *client;
 
    */
   struct GNUNET_CLIENT_Connection *client;
 
+  /**
+   * Configuration we use.
+   */
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
   /**
    * When should this operation time out?
    */
   struct GNUNET_TIME_Absolute timeout;
   /**
    * When should this operation time out?
    */
   struct GNUNET_TIME_Absolute timeout;
+
+  /**
+   * Backoff for reconnect.
+   */
+  struct GNUNET_TIME_Relative backoff;
+  
+  /**
+   * Task ID for reconnect.
+   */
+  GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
+
+  /**
+   * Identity of the peer to monitor.
+   */
+  struct GNUNET_PeerIdentity peer;
+
+  /**
+   * Was this a one-shot request?
+   */
+  int one_shot;
 };
 
 
 /**
  * Function called with responses from the service.
  *
 };
 
 
 /**
  * Function called with responses from the service.
  *
- * @param cls our 'struct AddressLookupCtx*'
+ * @param cls our 'struct GNUNET_TRANSPORT_PeerAddressLookupContext*'
  * @param msg NULL on timeout or error, otherwise presumably a
  *        message with the human-readable address
  */
 static void
  * @param msg NULL on timeout or error, otherwise presumably a
  *        message with the human-readable address
  */
 static void
-address_response_processor (void *cls, 
-                           const struct GNUNET_MessageHeader *msg)
+peer_address_response_processor (void *cls,
+                                 const struct GNUNET_MessageHeader *msg);
+
+
+/**
+ * Send our subscription request to the service.
+ *
+ * @param pal_ctx our context
+ */
+static void
+send_request (struct GNUNET_TRANSPORT_PeerIterateContext *pal_ctx)
+{
+  struct AddressIterateMessage msg;
+
+  msg.header.size = htons (sizeof (struct AddressIterateMessage));
+  msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_ITERATE);
+  msg.one_shot = htonl (pal_ctx->one_shot);
+  msg.timeout = GNUNET_TIME_absolute_hton (pal_ctx->timeout);
+  msg.peer = pal_ctx->peer;
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CLIENT_transmit_and_get_response (pal_ctx->client, 
+                                                         &msg.header,
+                                                          GNUNET_TIME_absolute_get_remaining (pal_ctx->timeout),
+                                                         GNUNET_YES,
+                                                          &peer_address_response_processor,
+                                                          pal_ctx));
+}
+
+/**
+ * Task run to re-establish the connection.
+ * 
+ * @param cls our 'struct GNUNET_TRANSPORT_PeerAddressLookupContext*'
+ * @param tc scheduler context, unused
+ */
+static void
+do_connect (void *cls,
+           const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
 {
-  struct AddressLookupCtx *alucb = cls;
-  const char *address;
+  struct GNUNET_TRANSPORT_PeerIterateContext *pal_ctx = cls;
+
+  pal_ctx->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
+  pal_ctx->client = GNUNET_CLIENT_connect ("transport", pal_ctx->cfg);
+  GNUNET_assert (NULL != pal_ctx->client);
+  send_request (pal_ctx);
+}
+
+
+/**
+ * Cut the existing connection and reconnect.
+ *
+ * @param pal_ctx our context
+ */
+static void
+reconnect (struct GNUNET_TRANSPORT_PeerIterateContext *pal_ctx)
+{
+  GNUNET_assert (GNUNET_NO == pal_ctx->one_shot);
+  GNUNET_CLIENT_disconnect (pal_ctx->client);
+  pal_ctx->client = NULL;
+  pal_ctx->backoff = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MILLISECONDS,
+                                              GNUNET_TIME_relative_min (GNUNET_TIME_relative_multiply (pal_ctx->backoff, 2),
+                                                                        GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)));
+  pal_ctx->reconnect_task = GNUNET_SCHEDULER_add_delayed (pal_ctx->backoff,
+                                                         &do_connect,
+                                                         pal_ctx);
+}
+
+
+/**
+ * Function called with responses from the service.
+ *
+ * @param cls our 'struct GNUNET_TRANSPORT_PeerAddressLookupContext*'
+ * @param msg NULL on timeout or error, otherwise presumably a
+ *        message with the human-readable address
+ */
+static void
+peer_address_response_processor (void *cls,
+                                 const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_TRANSPORT_PeerIterateContext *pal_ctx = cls;
+  struct AddressIterateResponseMessage *air_msg;
+  struct GNUNET_HELLO_Address *address;
+  const char *addr;
+  const char *transport_name;
   uint16_t size;
   uint16_t size;
+  size_t alen;
+  size_t tlen;
 
   if (msg == NULL)
 
   if (msg == NULL)
+  {
+    if (pal_ctx->one_shot)
     {
     {
-      alucb->cb (alucb->cb_cls, NULL);
-      GNUNET_CLIENT_disconnect (alucb->client, GNUNET_NO);
-      GNUNET_free (alucb);
-      return;
+      pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL);
+      GNUNET_TRANSPORT_peer_get_active_addresses_cancel (pal_ctx);
+    }
+    else
+    {
+      reconnect (pal_ctx);
     }
     }
-  GNUNET_break (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_REPLY);
+    return;
+  }
   size = ntohs (msg->size);
   size = ntohs (msg->size);
+  GNUNET_break (ntohs (msg->type) ==
+                GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_ITERATE_RESPONSE);
   if (size == sizeof (struct GNUNET_MessageHeader))
   if (size == sizeof (struct GNUNET_MessageHeader))
+  {
+    /* done! */
+    if (pal_ctx->one_shot)
     {
     {
-      /* done! */
-      alucb->cb (alucb->cb_cls, NULL);
-      GNUNET_CLIENT_disconnect (alucb->client, GNUNET_NO);
-      GNUNET_free (alucb);
-      return;
+      pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL);
+      GNUNET_TRANSPORT_peer_get_active_addresses_cancel (pal_ctx);
+    }
+    else
+    {
+      reconnect (pal_ctx);
+    }
+    return;
+  }
+
+  if ((size < sizeof (struct AddressIterateResponseMessage)) ||
+      (ntohs (msg->type) !=
+       GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_ITERATE_RESPONSE))
+  {
+    GNUNET_break (0);
+    if (pal_ctx->one_shot)
+    {
+      pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL);
+      GNUNET_TRANSPORT_peer_get_active_addresses_cancel (pal_ctx);
+    }
+    else
+    {
+      reconnect (pal_ctx);
+    }
+    return;
+  }
+
+  air_msg = (struct AddressIterateResponseMessage *) msg;
+  tlen = ntohl (air_msg->pluginlen);
+  alen = ntohl (air_msg->addrlen);
+
+  if (size != sizeof (struct AddressIterateResponseMessage) + tlen + alen)
+  {
+    GNUNET_break (0);
+    if (pal_ctx->one_shot)
+    {
+      pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL);
+      GNUNET_TRANSPORT_peer_get_active_addresses_cancel (pal_ctx);
+    }
+    else
+    {
+      reconnect (pal_ctx);
     }
     }
-  address = (const char *) &msg[1];
-  if (address[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
+    return;
+  }
+
+  if (alen == 0 && tlen == 0)
+  {
+    pal_ctx->cb (pal_ctx->cb_cls, &air_msg->peer, NULL);
+  }
+  else
+  {
+    addr = (const char *) &air_msg[1];
+    transport_name = &addr[alen];
+
+    if (transport_name[tlen - 1] != '\0')
     {
     {
-      /* invalid reply */
       GNUNET_break (0);
       GNUNET_break (0);
-      alucb->cb (alucb->cb_cls, NULL);
-      GNUNET_CLIENT_disconnect (alucb->client, GNUNET_NO);
-      GNUNET_free (alucb);
+      if (pal_ctx->one_shot)   
+      {
+       pal_ctx->cb (pal_ctx->cb_cls, NULL, NULL);
+       GNUNET_TRANSPORT_peer_get_active_addresses_cancel (pal_ctx);
+      }
+      else
+      {
+       reconnect (pal_ctx);
+      }
       return;
     }
       return;
     }
+
+    /* notify client */
+    address =
+        GNUNET_HELLO_address_allocate (&air_msg->peer, transport_name, addr,
+                                       alen);
+    pal_ctx->cb (pal_ctx->cb_cls, &air_msg->peer, address);
+    GNUNET_HELLO_address_free (address);
+  }
+
   /* expect more replies */
   /* expect more replies */
-  GNUNET_CLIENT_receive (alucb->client,
-                        &address_response_processor, alucb,
-                        GNUNET_TIME_absolute_get_remaining
-                        (alucb->timeout));    
-  alucb->cb (alucb->cb_cls, address);
+  GNUNET_CLIENT_receive (pal_ctx->client, &peer_address_response_processor,
+                         pal_ctx,
+                         GNUNET_TIME_absolute_get_remaining (pal_ctx->timeout));
 }
 
 
 /**
 }
 
 
 /**
- * Convert a binary address into a human readable address.
+ * Return all the known addresses for a specific peer or all peers.
+ * Returns continuously all address if one_shot is set to GNUNET_NO
+ *
+ * CHANGE: Returns the address(es) that we are currently using for this
+ * peer.  Upon completion, the 'AddressLookUpCallback' is called one more
+ * time with 'NULL' for the address and the peer.  After this, the operation must no
+ * longer be explicitly canceled.
  *
  *
- * @param sched scheduler to use
  * @param cfg configuration to use
  * @param cfg configuration to use
- * @param address address to convert (binary format)
- * @param addressLen number of bytes in address
- * @param numeric should (IP) addresses be displayed in numeric form 
- *                (otherwise do reverse DNS lookup)
- * @param nameTrans name of the transport to which the address belongs
- * @param timeout how long is the lookup allowed to take at most
- * @param aluc function to call with the results
- * @param aluc_cls closure for aluc
+ * @param peer peer identity to look up the addresses of, CHANGE: allow NULL for all (connected) peers
+ * @param one_shot GNUNET_YES to return the current state and then end (with NULL+NULL),
+ *                 GNUNET_NO to monitor the set of addresses used (continuously, must be explicitly canceled)
+ * @param timeout how long is the lookup allowed to take at most (irrelevant if one_shot is set to GNUNET_NO)
+ * @param peer_address_callback function to call with the results
+ * @param peer_address_callback_cls closure for peer_address_callback
  */
  */
-void
-GNUNET_TRANSPORT_address_lookup (struct GNUNET_SCHEDULER_Handle *sched,
-                                 const struct GNUNET_CONFIGURATION_Handle  *cfg, 
-                                const char *address, 
-                                size_t addressLen,
-                                int numeric,
-                                 const char *nameTrans,
-                                 struct GNUNET_TIME_Relative timeout,
-                                 GNUNET_TRANSPORT_AddressLookUpCallback aluc,
-                                 void *aluc_cls)
+struct GNUNET_TRANSPORT_PeerIterateContext *
+GNUNET_TRANSPORT_peer_get_active_addresses (const struct
+                                            GNUNET_CONFIGURATION_Handle *cfg,
+                                            const struct GNUNET_PeerIdentity
+                                            *peer, int one_shot,
+                                            struct GNUNET_TIME_Relative timeout,
+                                            GNUNET_TRANSPORT_PeerIterateCallback
+                                            peer_address_callback,
+                                            void *peer_address_callback_cls)
 {
 {
-  size_t slen;
-  size_t len;
-  struct AddressLookupMessage *msg;
-  struct GNUNET_TIME_Absolute abs_timeout;
-  struct AddressLookupCtx *aluCB;
+  struct GNUNET_TRANSPORT_PeerIterateContext *pal_ctx;
   struct GNUNET_CLIENT_Connection *client;
   struct GNUNET_CLIENT_Connection *client;
-  char *addrbuf;
 
 
-  slen = strlen (nameTrans) + 1;
-  len = sizeof (struct AddressLookupMessage) + addressLen + slen;
-  if (len >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
-    {
-      GNUNET_break (0);
-      aluc (aluc_cls, NULL);
-      return;
-    }
-  client = GNUNET_CLIENT_connect (sched, "transport", cfg);
+  client = GNUNET_CLIENT_connect ("transport", cfg);
   if (client == NULL)
   if (client == NULL)
-    {
-      aluc (aluc_cls, NULL);
-      return;
-    }
-  abs_timeout = GNUNET_TIME_relative_to_absolute (timeout);
-  msg = GNUNET_malloc (len);
-  msg->header.size = htons (len);
-  msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_LOOKUP);
-  msg->numeric_only = htonl (numeric);
-  msg->timeout = GNUNET_TIME_absolute_hton (abs_timeout);
-  msg->addrlen = htonl (addressLen);
-  addrbuf = (char *) &msg[1];
-  memcpy (addrbuf, address, addressLen);
-  memcpy (&addrbuf[addressLen], nameTrans, slen);
-  aluCB = GNUNET_malloc (sizeof (struct AddressLookupCtx));
-  aluCB->cb = aluc;
-  aluCB->cb_cls = aluc_cls;
-  aluCB->timeout = abs_timeout;
-  aluCB->client = client;
-  GNUNET_CLIENT_transmit_and_get_response (client, 
-                                          &msg->header, 
-                                          timeout,
-                                           GNUNET_YES,
-                                           &address_response_processor,
-                                           aluCB);
-  GNUNET_free (msg);
+    return NULL;
+  if (GNUNET_YES != one_shot)
+    timeout = GNUNET_TIME_UNIT_FOREVER_REL;
+  pal_ctx = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PeerIterateContext));
+  pal_ctx->cb = peer_address_callback;
+  pal_ctx->cb_cls = peer_address_callback_cls;
+  pal_ctx->cfg = cfg;
+  pal_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+  if (NULL != peer)
+    pal_ctx->peer = *peer;
+  pal_ctx->one_shot = one_shot;  
+  pal_ctx->client = client;
+  send_request (pal_ctx);
+
+  return pal_ctx;
 }
 
 }
 
-/* end of transport_api_address_lookup.c */
+
+/**
+ * Cancel request for address conversion.
+ *
+ * @param alc handle for the request to cancel
+ */
+void
+GNUNET_TRANSPORT_peer_get_active_addresses_cancel (struct
+                                                   GNUNET_TRANSPORT_PeerIterateContext
+                                                   *alc)
+{
+  if (NULL != alc->client)
+  {
+    GNUNET_CLIENT_disconnect (alc->client);
+    alc->client = NULL;
+  }
+  if (GNUNET_SCHEDULER_NO_TASK != alc->reconnect_task)
+  {
+    GNUNET_SCHEDULER_cancel (alc->reconnect_task);
+    alc->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  GNUNET_free (alc);
+}
+
+
+/* end of transport_api_peer_address_lookup.c */