-remove find() forking, we pretty much should not need this anymore, and it confused...
[oweals/gnunet.git] / src / util / resolver_api.c
index d761c075edabd12075fa4e6a81bd438c570ec0b6..46ac52586c752c1bb8baff3f9728d04779a52578 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
 /*
      This file is part of GNUnet.
-     (C) 2009 Christian Grothoff (and other contributing authors)
+     (C) 2009-2013 Christian Grothoff (and other contributing authors)
 
      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
 */
 
 /**
 */
 
 /**
- * @file resolver/resolver_api.c
+ * @file util/resolver_api.c
  * @brief resolver for writing a tool
  * @author Christian Grothoff
  */
 #include "platform.h"
  * @brief resolver for writing a tool
  * @author Christian Grothoff
  */
 #include "platform.h"
-#include "gnunet_getopt_lib.h"
-#include "gnunet_client_lib.h"
+#include "gnunet_util_lib.h"
 #include "gnunet_protocols.h"
 #include "gnunet_resolver_service.h"
 #include "gnunet_protocols.h"
 #include "gnunet_resolver_service.h"
-#include "gnunet_server_lib.h"
 #include "resolver.h"
 
 #include "resolver.h"
 
+#define LOG(kind,...) GNUNET_log_from (kind, "resolver-api", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "resolver-api", syscall)
+
+/**
+ * Maximum supported length for a hostname
+ */
+#define MAX_HOSTNAME 1024
+
+
+/**
+ * Possible hostnames for "loopback".
+ */
+static const char *loopback[] = {
+  "localhost",
+  "ip6-localnet",
+  NULL
+};
+
+
+/**
+ * Configuration.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *resolver_cfg;
+
+/**
+ * Our connection to the resolver service, created on-demand, but then
+ * persists until error or shutdown.
+ */
+static struct GNUNET_CLIENT_Connection *client;
+
+/**
+ * Head of DLL of requests.
+ */
+static struct GNUNET_RESOLVER_RequestHandle *req_head;
 
 /**
 
 /**
- * FIXME.
+ * Tail of DLL of requests.
  */
  */
-struct GetAddressContext
+static struct GNUNET_RESOLVER_RequestHandle *req_tail;
+
+/**
+ * How long should we wait to reconnect?
+ */
+static struct GNUNET_TIME_Relative backoff;
+
+/**
+ * Task for reconnecting.
+ */
+static GNUNET_SCHEDULER_TaskIdentifier r_task;
+
+/**
+ * Task ID of shutdown task; only present while we have a
+ * connection to the resolver service.
+ */
+static GNUNET_SCHEDULER_TaskIdentifier s_task;
+
+
+/**
+ * Handle to a request given to the resolver.  Can be used to cancel
+ * the request prior to the timeout or successful execution.  Also
+ * used to track our internal state for the request.
+ */
+struct GNUNET_RESOLVER_RequestHandle
 {
 
   /**
 {
 
   /**
-   * FIXME.
+   * Next entry in DLL of requests.
    */
    */
-  GNUNET_RESOLVER_AddressCallback callback;
+  struct GNUNET_RESOLVER_RequestHandle *next;
 
   /**
 
   /**
-   * Closure for "callback".
+   * Previous entry in DLL of requests.
    */
    */
-  void *cls;
+  struct GNUNET_RESOLVER_RequestHandle *prev;
 
   /**
 
   /**
-   * FIXME.
+   * Callback if this is an name resolution request,
+   * otherwise NULL.
    */
    */
-  struct GNUNET_RESOLVER_GetMessage *msg;
+  GNUNET_RESOLVER_AddressCallback addr_callback;
+
+  /**
+   * Callback if this is a reverse lookup request,
+   * otherwise NULL.
+   */
+  GNUNET_RESOLVER_HostnameCallback name_callback;
 
   /**
 
   /**
-   * FIXME.
+   * Closure for the respective "callback".
    */
    */
-  struct GNUNET_CLIENT_Connection *client;
+  void *cls;
 
   /**
 
   /**
-   * FIXME.
+   * When should this request time out?
    */
   struct GNUNET_TIME_Absolute timeout;
    */
   struct GNUNET_TIME_Absolute timeout;
-};
 
 
+  /**
+   * Task handle for numeric lookups.
+   */
+  GNUNET_SCHEDULER_TaskIdentifier task;
 
 
-/**
- * Possible hostnames for "loopback".
- */
-static const char *loopback[] = {
-  "localhost",
-  "ip6-localnet",
-  NULL
+  /**
+   * Desired address family.
+   */
+  int af;
+
+  /**
+   * Has this request been transmitted to the service?
+   * GNUNET_YES if transmitted
+   * GNUNET_YES if not transmitted
+   * GNUNET_SYSERR when request was canceled
+   */
+  int was_transmitted;
+
+  /**
+   * Did we add this request to the queue?
+   */
+  int was_queued;
+
+  /**
+   * Desired direction (IP to name or name to IP)
+   */
+  int direction;
+
+  /**
+   * GNUNET_YES if a response was received
+   */
+  int received_response;
+
+  /**
+   * Length of the data that follows this struct.
+   */
+  size_t data_len;
 };
 
 
 };
 
 
@@ -80,540 +172,786 @@ static const char *loopback[] = {
  * (or equivalent).
  */
 static void
  * (or equivalent).
  */
 static void
-check_config (const struct GNUNET_CONFIGURATION_Handle *cfg)
+check_config ()
 {
   char *hostname;
   unsigned int i;
 {
   char *hostname;
   unsigned int i;
-  struct in_addr v4;
-  struct in6_addr v6;
+  struct sockaddr_in v4;
+  struct sockaddr_in6 v6;
 
 
+  memset (&v4, 0, sizeof (v4));
+  v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+  v4.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+  v4.sin_len = sizeof (v4);
+#endif
+  memset (&v6, 0, sizeof (v6));
+  v6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+  v6.sin6_len = sizeof (v6);
+#endif
   if (GNUNET_OK !=
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (cfg,
-                                            "resolver",
-                                            "HOSTNAME",
-                                            &hostname))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                 _("Must specify `%s' for `%s' in configuration!\n"),
-                 "HOSTNAME",
-                 "resolver");
-      GNUNET_assert (0);
-    }
-  if ( (0 == inet_pton (AF_INET,
-                       hostname,
-                       &v4)) ||
-       (0 == inet_pton (AF_INET6,
-                       hostname,
-                       &v6)) )
+      GNUNET_CONFIGURATION_get_value_string (resolver_cfg, "resolver",
+                                             "HOSTNAME", &hostname))
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         _("Must specify `%s' for `%s' in configuration!\n"), "HOSTNAME",
+         "resolver");
+    GNUNET_assert (0);
+  }
+  if ((1 != inet_pton (AF_INET, hostname, &v4)) ||
+      (1 != inet_pton (AF_INET6, hostname, &v6)))
+  {
+    GNUNET_free (hostname);
     return;
     return;
+  }
   i = 0;
   i = 0;
-  while (loopback[i] != NULL)
-    if (0 == strcmp (loopback[i++], hostname))
-      {
-       GNUNET_free (hostname); 
-       return;
-      }
-  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-             _("Must specify `%s' for `%s' in configuration!\n"),
-             "localhost",
-             "resolver");
-  GNUNET_free (hostname); 
-  GNUNET_assert (0); 
+  while (NULL != loopback[i])
+    if (0 == strcasecmp (loopback[i++], hostname))
+    {
+      GNUNET_free (hostname);
+      return;
+    }
+  LOG (GNUNET_ERROR_TYPE_ERROR,
+       _("Must specify `%s' or numeric IP address for `%s' of `%s' in configuration!\n"),
+       "localhost", "HOSTNAME", "resolver");
+  GNUNET_free (hostname);
+  GNUNET_assert (0);
+}
+
+
+/**
+ * Create the connection to the resolver service.
+ *
+ * @param cfg configuration to use
+ */
+void
+GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  GNUNET_assert (NULL != cfg);
+  backoff = GNUNET_TIME_UNIT_MILLISECONDS;
+  resolver_cfg = cfg;
+  check_config ();
+}
+
+
+/**
+ * Destroy the connection to the resolver service.
+ */
+void
+GNUNET_RESOLVER_disconnect ()
+{
+  GNUNET_assert (NULL == req_head);
+  GNUNET_assert (NULL == req_tail);
+  if (NULL != client)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from DNS service\n");
+    GNUNET_CLIENT_disconnect (client);
+    client = NULL;
+  }
+  if (GNUNET_SCHEDULER_NO_TASK != r_task)
+  {
+    GNUNET_SCHEDULER_cancel (r_task);
+    r_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  if (GNUNET_SCHEDULER_NO_TASK != s_task)
+  {
+    GNUNET_SCHEDULER_cancel (s_task);
+    s_task = GNUNET_SCHEDULER_NO_TASK;
+  }
 }
 
 
 /**
  * Convert IP address to string without DNS resolution.
 }
 
 
 /**
  * Convert IP address to string without DNS resolution.
+ *
+ * @param af address family
+ * @param ip the address
+ * @param ip_len number of bytes in ip
+ * @return address as a string, NULL on error
  */
 static char *
  */
 static char *
-no_resolve (const struct sockaddr *sa, socklen_t salen)
+no_resolve (int af,
+           const void *ip, socklen_t ip_len)
 {
 {
-  char *ret;
-  char inet4[INET_ADDRSTRLEN];
-  char inet6[INET6_ADDRSTRLEN];
+  char buf[INET6_ADDRSTRLEN];
 
 
-  if (salen < sizeof (struct sockaddr))
-    return NULL;
-  switch (sa->sa_family)
+  switch (af)
+  {
+  case AF_INET:
+    if (ip_len != sizeof (struct in_addr))
+      return NULL;
+    if (NULL ==
+        inet_ntop (AF_INET, ip, buf, sizeof (buf)))
     {
     {
-    case AF_INET:
-      if (salen != sizeof (struct sockaddr_in))
-        return NULL;
-      inet_ntop (AF_INET,
-                 &((struct sockaddr_in *) sa)->sin_addr,
-                 inet4, INET_ADDRSTRLEN);
-      ret = GNUNET_strdup (inet4);
-      break;
-    case AF_INET6:
-      if (salen != sizeof (struct sockaddr_in6))
-        return NULL;
-      inet_ntop (AF_INET6,
-                 &((struct sockaddr_in6 *) sa)->sin6_addr,
-                 inet6, INET6_ADDRSTRLEN);
-      ret = GNUNET_strdup (inet6);
-      break;
-    default:
-      ret = NULL;
-      break;
+      LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
+      return NULL;
+    }
+    break;
+  case AF_INET6:
+    if (ip_len != sizeof (struct in6_addr))
+      return NULL;
+    if (NULL ==
+        inet_ntop (AF_INET6, ip, buf, sizeof (buf)))
+    {
+      LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
+      return NULL;
     }
     }
-  return ret;
+    break;
+  default:
+    GNUNET_break (0);
+    return NULL;
+  }
+  return GNUNET_strdup (buf);
 }
 
 
 }
 
 
+/**
+ * Adjust exponential back-off and reconnect to the service.
+ */
 static void
 static void
-handle_address_response (void *cls, const struct GNUNET_MessageHeader *msg)
+reconnect ();
+
+
+/**
+ * Process pending requests to the resolver.
+ */
+static void
+process_requests ();
+
+
+/**
+ * Process response with a hostname for a DNS lookup.
+ *
+ * @param cls our "struct GNUNET_RESOLVER_RequestHandle" context
+ * @param msg message with the hostname, NULL on error
+ */
+static void
+handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
 {
 {
-  struct GetAddressContext *gac = cls;
+  struct GNUNET_RESOLVER_RequestHandle *rh = cls;
   uint16_t size;
   uint16_t size;
-  const struct sockaddr *sa;
-  socklen_t salen;
-
 
 
-  if (msg == NULL)
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Receiving response from DNS service\n");
+  if (NULL == msg)
+  {
+    char buf[INET6_ADDRSTRLEN];
+
+    if (NULL != rh->name_callback)
+      LOG (GNUNET_ERROR_TYPE_INFO,
+           _("Timeout trying to resolve IP address `%s'.\n"),
+           inet_ntop (rh->af, (const void *) &rh[1], buf, sizeof(buf)));
+    else
+      LOG (GNUNET_ERROR_TYPE_INFO,
+           _("Timeout trying to resolve hostname `%s'.\n"),
+           (const char *) &rh[1]);
+    /* check if request was canceled */
+    if (GNUNET_SYSERR != rh->was_transmitted)
     {
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  _("Timeout trying to resolve hostname.\n"));
-      gac->callback (gac->cls, NULL, 0);
-      GNUNET_CLIENT_disconnect (gac->client);
-      GNUNET_free (gac);
-      return;
+      if (NULL != rh->name_callback)
+      {
+        /* no reverse lookup was successful, return ip as string */
+        if (rh->received_response == GNUNET_NO)
+        {
+          rh->name_callback (rh->cls, no_resolve (rh->af, &rh[1], rh->data_len));
+          rh->name_callback (rh->cls, NULL);
+        }
+        /* at least one reverse lookup was successful */
+        else
+          rh->name_callback (rh->cls, NULL);
+      }
+      if (NULL != rh->addr_callback)
+        rh->addr_callback (rh->cls, NULL, 0);
     }
     }
+    GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
+    GNUNET_free (rh);
+    GNUNET_CLIENT_disconnect (client);
+    client = NULL;
+    reconnect ();
+    return;
+  }
   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
+  {
+    GNUNET_break (0);
+    GNUNET_CLIENT_disconnect (client);
+    client = NULL;
+    reconnect ();
+    return;
+  }
+  size = ntohs (msg->size);
+  /* message contains not data, just header */
+  if (size == sizeof (struct GNUNET_MessageHeader))
+  {
+    /* check if request was canceled */
+    if (rh->was_transmitted != GNUNET_SYSERR)
+    {
+      if (NULL != rh->name_callback)
+        rh->name_callback (rh->cls, NULL);
+      if (NULL != rh->addr_callback)
+        rh->addr_callback (rh->cls, NULL, 0);
+    }
+    GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
+    GNUNET_free (rh);
+    process_requests ();
+    return;
+  }
+  /* return reverse lookup results to caller */
+  if (NULL != rh->name_callback)
+  {
+    const char *hostname;
+
+    hostname = (const char *) &msg[1];
+    if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
     {
       GNUNET_break (0);
     {
       GNUNET_break (0);
-      gac->callback (gac->cls, NULL, 0);
-      GNUNET_CLIENT_disconnect (gac->client);
-      GNUNET_free (gac);
+      if (rh->was_transmitted != GNUNET_SYSERR)
+        rh->name_callback (rh->cls, NULL);
+      GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
+      GNUNET_free (rh);
+      GNUNET_CLIENT_disconnect (client);
+      client = NULL;
+      reconnect ();
       return;
     }
       return;
     }
-
-  size = ntohs (msg->size);
-  if (size == sizeof (struct GNUNET_MessageHeader))
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Resolver returns `%s' for IP `%s'.\n",
+         hostname, GNUNET_a2s ((const void *) &rh[1], rh->data_len));
+    if (rh->was_transmitted != GNUNET_SYSERR)
+      rh->name_callback (rh->cls, hostname);
+    rh->received_response = GNUNET_YES;
+    GNUNET_CLIENT_receive (client, &handle_response, rh,
+                           GNUNET_TIME_absolute_get_remaining (rh->timeout));
+  }
+  /* return lookup results to caller */
+  if (NULL != rh->addr_callback)
+  {
+    struct sockaddr_in v4;
+    struct sockaddr_in6 v6;
+    const struct sockaddr *sa;
+    socklen_t salen;
+    const void *ip;
+    size_t ip_len;
+
+    ip = &msg[1];
+    ip_len = size - sizeof (struct GNUNET_MessageHeader);
+    if (ip_len == sizeof (struct in_addr))
     {
     {
-#if DEBUG_RESOLVER
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  _("Received end message resolving hostname.\n"));
+      memset (&v4, 0, sizeof (v4));
+      v4.sin_family = AF_INET;
+      v4.sin_addr = *(struct in_addr*) ip;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+      v4.sin_len = sizeof (v4);
 #endif
 #endif
-      gac->callback (gac->cls, NULL, 0);
-      GNUNET_CLIENT_disconnect (gac->client);
-      GNUNET_free (gac);
-      return;
+      salen = sizeof (v4);
+      sa = (const struct sockaddr *) &v4;
     }
     }
-  sa = (const struct sockaddr *) &msg[1];
-  salen = size - sizeof (struct GNUNET_MessageHeader);
-  if (salen < sizeof (struct sockaddr))
+    else if (ip_len == sizeof (struct in6_addr))
+    {
+      memset (&v6, 0, sizeof (v6));
+      v6.sin6_family = AF_INET6;
+      v6.sin6_addr = *(struct in6_addr*) ip;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+      v6.sin6_len = sizeof (v6);
+#endif
+      salen = sizeof (v6);
+      sa = (const struct sockaddr *) &v6;
+    }
+    else
     {
       GNUNET_break (0);
     {
       GNUNET_break (0);
-      gac->callback (gac->cls, NULL, 0);
-      GNUNET_CLIENT_disconnect (gac->client);
-      GNUNET_free (gac);
+      if (rh->was_transmitted != GNUNET_SYSERR)
+        rh->addr_callback (rh->cls, NULL, 0);
+      GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
+      GNUNET_free (rh);
+      GNUNET_CLIENT_disconnect (client);
+      client = NULL;
+      reconnect ();
       return;
     }
       return;
     }
-#if DEBUG_RESOLVER
-  {
-    char *ips = no_resolve (sa, salen);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s'.\n"), ips);
-    GNUNET_free (ips);
+    rh->addr_callback (rh->cls, sa, salen);
+    GNUNET_CLIENT_receive (client, &handle_response, rh,
+                           GNUNET_TIME_absolute_get_remaining (rh->timeout));
   }
   }
-#endif
-  gac->callback (gac->cls, sa, salen);
-  GNUNET_CLIENT_receive (gac->client,
-                         &handle_address_response,
-                         gac,
-                         GNUNET_TIME_absolute_get_remaining (gac->timeout));
 }
 
 
 }
 
 
-static size_t
-transmit_get_ip (void *cls, size_t size, void *buf)
+/**
+ * We've been asked to lookup the address for a hostname and were
+ * given a valid numeric string.  Perform the callbacks for the
+ * numeric addresses.
+ *
+ * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
+ * @param tc unused scheduler context
+ */
+static void
+numeric_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
 {
-  struct GetAddressContext *actx = cls;
-  uint16_t ms;
+  struct GNUNET_RESOLVER_RequestHandle *rh = cls;
+  struct sockaddr_in v4;
+  struct sockaddr_in6 v6;
+  const char *hostname;
 
 
-  if (buf == NULL)
+  memset (&v4, 0, sizeof (v4));
+  v4.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+  v4.sin_len = sizeof (v4);
+#endif
+  memset (&v6, 0, sizeof (v6));
+  v6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+  v6.sin6_len = sizeof (v6);
+#endif
+  hostname = (const char *) &rh[1];
+  if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET)) &&
+      (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
+  {
+    rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
+    if ((rh->af == AF_UNSPEC) &&
+        (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
     {
     {
-      /* timeout / error */
-      GNUNET_free (actx->msg);
-      actx->callback (actx->cls, NULL, 0);
-      GNUNET_CLIENT_disconnect (actx->client);
-      GNUNET_free (actx);
-      return 0;
+      /* this can happen on some systems IF "hostname" is "localhost" */
+      rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
     }
     }
-  ms = ntohs (actx->msg->header.size);
-  GNUNET_assert (size >= ms);
-  memcpy (buf, actx->msg, ms);
-  GNUNET_free (actx->msg);
-  actx->msg = NULL;
-  GNUNET_CLIENT_receive (actx->client,
-                         &handle_address_response,
-                         actx,
-                         GNUNET_TIME_absolute_get_remaining (actx->timeout));
-  return ms;
+    rh->addr_callback (rh->cls, NULL, 0);
+    GNUNET_free (rh);
+    return;
+  }
+  if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET6)) &&
+      (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
+  {
+    rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
+    rh->addr_callback (rh->cls, NULL, 0);
+    GNUNET_free (rh);
+    return;
+  }
+  /* why are we here? this task should not have been scheduled! */
+  GNUNET_assert (0);
+  GNUNET_free (rh);
 }
 
 
 }
 
 
-
 /**
 /**
- * Convert a string to one or more IP addresses.
+ * We've been asked to lookup the address for a hostname and were
+ * given a variant of "loopback".  Perform the callbacks for the
+ * respective loopback numeric addresses.
  *
  *
- * @param sched scheduler to use
- * @param cfg configuration to use
- * @param hostname the hostname to resolve
- * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
- * @param callback function to call with addresses
- * @param callback_cls closure for callback
- * @param timeout how long to try resolving
+ * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
+ * @param tc unused scheduler context
  */
  */
-void
-GNUNET_RESOLVER_ip_get (struct GNUNET_SCHEDULER_Handle *sched,
-                        const struct GNUNET_CONFIGURATION_Handle *cfg,
-                        const char *hostname,
-                        int domain,
-                        struct GNUNET_TIME_Relative timeout,
-                        GNUNET_RESOLVER_AddressCallback callback, 
-                       void *callback_cls)
+static void
+loopback_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
 {
-  struct GNUNET_CLIENT_Connection *client;
-  struct GNUNET_RESOLVER_GetMessage *msg;
-  struct GetAddressContext *actx;
-  size_t slen;
-  unsigned int i;
+  struct GNUNET_RESOLVER_RequestHandle *rh = cls;
   struct sockaddr_in v4;
   struct sockaddr_in6 v6;
 
   struct sockaddr_in v4;
   struct sockaddr_in6 v6;
 
-  memset (&v4, 0, sizeof(v4));
-  v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);  
+  memset (&v4, 0, sizeof (v4));
+  v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
   v4.sin_family = AF_INET;
 #if HAVE_SOCKADDR_IN_SIN_LEN
   v4.sin_family = AF_INET;
 #if HAVE_SOCKADDR_IN_SIN_LEN
-  v4.sin_len = sizeof(v4);
+  v4.sin_len = sizeof (v4);
 #endif
 #endif
-  memset (&v6, 0, sizeof(v6)); 
+  memset (&v6, 0, sizeof (v6));
   v6.sin6_family = AF_INET6;
 #if HAVE_SOCKADDR_IN_SIN_LEN
   v6.sin6_family = AF_INET6;
 #if HAVE_SOCKADDR_IN_SIN_LEN
-  v6.sin6_len = sizeof(v6);
+  v6.sin6_len = sizeof (v6);
 #endif
 #endif
-  /* first, check if this is a numeric address */
-  if ( ( (domain == AF_UNSPEC) ||(domain == AF_INET) ) && 
-       (0 == inet_pton (AF_INET,
-                       hostname,
-                       &v4.sin_addr)) )
-    {
-      callback (callback_cls,
-               (const struct sockaddr*) &v4,
-               sizeof(v4));
-      callback (callback_cls, NULL, 0);
-      return;
-    }
-  if ( ( (domain == AF_UNSPEC) ||(domain == AF_INET) ) && 
-       (0 == inet_pton (AF_INET6,
-                       hostname,
-                       &v6.sin6_addr)) )
-    {
-      callback (callback_cls,
-               (const struct sockaddr*) &v6,
-               sizeof(v6));
-      callback (callback_cls, NULL, 0);
-      return;
-    }
-  check_config (cfg);
-  /* then, check if this is a loopback address */
-  i = 0;
-  while (loopback[i] != NULL)
-    if (0 == strcmp (loopback[i++], hostname))
-      {
-       v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);  
-       v6.sin6_addr = in6addr_loopback;
-       switch (domain)
-         {
-         case AF_INET:
-           callback (callback_cls, 
-                     (const struct sockaddr*) &v4,
-                     sizeof(v4));
-           break;
-         case AF_INET6:
-           callback (callback_cls, 
-                     (const struct sockaddr*) &v6,
-                     sizeof(v6));
-           break;
-         case AF_UNSPEC:
-           callback (callback_cls, 
-                     (const struct sockaddr*) &v4,
-                     sizeof(v4));
-           callback (callback_cls, 
-                     (const struct sockaddr*) &v6,
-                     sizeof(v6));
-           break;
-         }
-       callback (callback_cls, NULL, 0);
-       return;
-      }
-  slen = strlen (hostname) + 1;
-  if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
-      GNUNET_SERVER_MAX_MESSAGE_SIZE)
-    {
-      GNUNET_break (0);
-      callback (callback_cls, NULL, 0);
-      return;
-    }
-  client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
-  if (client == NULL)
-    {
-      callback (callback_cls, NULL, 0);
-      return;
-    }
-  msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen);
+  v6.sin6_addr = in6addr_loopback;
+  switch (rh->af)
+  {
+  case AF_INET:
+    rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
+    break;
+  case AF_INET6:
+    rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
+    break;
+  case AF_UNSPEC:
+    rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
+    rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
+    break;
+  default:
+    GNUNET_break (0);
+    break;
+  }
+  rh->addr_callback (rh->cls, NULL, 0);
+  GNUNET_free (rh);
+}
+
+
+/**
+ * Task executed on system shutdown.
+ */
+static void
+shutdown_task (void *cls,
+              const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  s_task = GNUNET_SCHEDULER_NO_TASK;
+  GNUNET_RESOLVER_disconnect ();
+  backoff = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+/**
+ * Process pending requests to the resolver.
+ */
+static void
+process_requests ()
+{
+  struct GNUNET_RESOLVER_GetMessage *msg;
+  char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
+  struct GNUNET_RESOLVER_RequestHandle *rh;
+
+  if (NULL == client)
+  {
+    reconnect ();
+    return;
+  }
+  rh = req_head;
+  if (NULL == rh)
+  {
+    /* nothing to do, release socket really soon if there is nothing
+     * else happening... */
+    s_task =
+        GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
+                                      &shutdown_task, NULL);
+    return;
+  }
+  if (GNUNET_YES == rh->was_transmitted)
+    return;                     /* waiting for reply */
+  msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
   msg->header.size =
   msg->header.size =
-    htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen);
+      htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + rh->data_len);
   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
-  msg->direction = htonl (GNUNET_NO);
-  msg->domain = htonl (domain);
-  memcpy (&msg[1], hostname, slen);
-  actx = GNUNET_malloc (sizeof (struct GetAddressContext));
-  actx->callback = callback;
-  actx->cls = callback_cls;
-  actx->client = client;
-  actx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
-  actx->msg = msg;
-
-#if DEBUG_RESOLVER
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              _("Resolver requests DNS resolution of hostname `%s'.\n"),
-              hostname);
-#endif
-  if (NULL ==
-      GNUNET_CLIENT_notify_transmit_ready (client,
-                                           slen +
-                                           sizeof (struct
-                                                   GNUNET_RESOLVER_GetMessage),
-                                           timeout, &transmit_get_ip, actx))
-    {
-      GNUNET_free (msg);
-      GNUNET_free (actx);
-      callback (callback_cls, NULL, 0);
-      GNUNET_CLIENT_disconnect (client);
-      return;
-    }
+  msg->direction = htonl (rh->direction);
+  msg->af = htonl (rh->af);
+  memcpy (&msg[1], &rh[1], rh->data_len);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Transmitting DNS resolution request to DNS service\n");
+  if (GNUNET_OK !=
+      GNUNET_CLIENT_transmit_and_get_response (client, &msg->header,
+                                               GNUNET_TIME_absolute_get_remaining
+                                               (rh->timeout), GNUNET_YES,
+                                               &handle_response, rh))
+  {
+    GNUNET_CLIENT_disconnect (client);
+    client = NULL;
+    GNUNET_break (0);
+    reconnect ();
+    return;
+  }
+  rh->was_transmitted = GNUNET_YES;
 }
 
 
 }
 
 
-struct GetHostnameContext
+/**
+ * Now try to reconnect to the resolver service.
+ *
+ * @param cls NULL
+ * @param tc scheduler context
+ */
+static void
+reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
 {
-  GNUNET_RESOLVER_HostnameCallback callback;
-  void *cls;
-  struct GNUNET_RESOLVER_GetMessage *msg;
-  struct GNUNET_CLIENT_Connection *client;
-  struct GNUNET_TIME_Absolute timeout;
-};
+  r_task = GNUNET_SCHEDULER_NO_TASK;
+  if (NULL == req_head)
+    return;                     /* no work pending */
+  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+    return;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Trying to connect to DNS service\n");
+  client = GNUNET_CLIENT_connect ("resolver", resolver_cfg);
+  if (NULL == client)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "Failed to connect, will try again later\n");
+    reconnect ();
+    return;
+  }
+  process_requests ();
+}
 
 
 
 
+/**
+ * Adjust exponential back-off and reconnect to the service.
+ */
 static void
 static void
-handle_hostname_response (void *cls, const struct GNUNET_MessageHeader *msg)
+reconnect ()
 {
 {
-  struct GetHostnameContext *ghc = cls;
-  uint16_t size;
-  const char *hostname;
+  struct GNUNET_RESOLVER_RequestHandle *rh;
 
 
-  if (msg == NULL)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  _("Timeout trying to resolve IP address.\n"));
-      ghc->callback (ghc->cls, NULL);
-      GNUNET_CLIENT_disconnect (ghc->client);
-      GNUNET_free (ghc);
-      return;
-    }
-  size = ntohs (msg->size);
-  if (size == sizeof (struct GNUNET_MessageHeader))
-    {
-#if DEBUG_RESOLVER
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  _("Received end message resolving IP address.\n"));
-#endif
-      ghc->callback (ghc->cls, NULL);
-      GNUNET_CLIENT_disconnect (ghc->client);
-      GNUNET_free (ghc);
-      return;
-    }
-  hostname = (const char *) &msg[1];
-  if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
+  if (GNUNET_SCHEDULER_NO_TASK != r_task)
+    return;
+  GNUNET_assert (NULL == client);
+  if (NULL != (rh = req_head))
+  {
+    switch (rh->was_transmitted)
     {
     {
-      GNUNET_break (0);
-      ghc->callback (ghc->cls, NULL);
-      GNUNET_CLIENT_disconnect (ghc->client);
-      GNUNET_free (ghc);
-      return;
+    case GNUNET_NO:
+      /* nothing more to do */
+      break;
+    case GNUNET_YES:
+      /* disconnected, transmit again! */
+      rh->was_transmitted = GNUNET_NO;
+      break;
+    case GNUNET_SYSERR:
+      /* request was cancelled, remove entirely */
+      GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
+      GNUNET_free (rh);
+      break;
+    default:
+      GNUNET_assert (0);
+      break;
     }
     }
-#if DEBUG_RESOLVER
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              _("Resolver returns `%s'.\n"), hostname);
-#endif
-  ghc->callback (ghc->cls, hostname);
-  GNUNET_CLIENT_receive (ghc->client,
-                         &handle_hostname_response,
-                         ghc,
-                         GNUNET_TIME_absolute_get_remaining (ghc->timeout));
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Will try to connect to DNS service in %s\n",
+       GNUNET_STRINGS_relative_time_to_string (backoff, GNUNET_YES));
+  GNUNET_assert (NULL != resolver_cfg);
+  r_task = GNUNET_SCHEDULER_add_delayed (backoff, &reconnect_task, NULL);
+  backoff = GNUNET_TIME_STD_BACKOFF (backoff);
 }
 
 
 }
 
 
-static size_t
-transmit_get_hostname (void *cls, size_t size, void *buf)
+/**
+ * Convert a string to one or more IP addresses.
+ *
+ * @param hostname the hostname to resolve
+ * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
+ * @param callback function to call with addresses
+ * @param callback_cls closure for callback
+ * @param timeout how long to try resolving
+ * @return handle that can be used to cancel the request, NULL on error
+ */
+struct GNUNET_RESOLVER_RequestHandle *
+GNUNET_RESOLVER_ip_get (const char *hostname, int af,
+                        struct GNUNET_TIME_Relative timeout,
+                        GNUNET_RESOLVER_AddressCallback callback,
+                        void *callback_cls)
 {
 {
-  struct GetHostnameContext *hctx = cls;
-  uint16_t msize;
+  struct GNUNET_RESOLVER_RequestHandle *rh;
+  size_t slen;
+  unsigned int i;
+  struct in_addr v4;
+  struct in6_addr v6;
 
 
-  if (buf == NULL)
+  slen = strlen (hostname) + 1;
+  if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >=
+      GNUNET_SERVER_MAX_MESSAGE_SIZE)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
+  rh->af = af;
+  rh->addr_callback = callback;
+  rh->cls = callback_cls;
+  memcpy (&rh[1], hostname, slen);
+  rh->data_len = slen;
+  rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+  rh->direction = GNUNET_NO;
+  /* first, check if this is a numeric address */
+  if (((1 == inet_pton (AF_INET, hostname, &v4)) &&
+       ((af == AF_INET) || (af == AF_UNSPEC))) ||
+      ((1 == inet_pton (AF_INET6, hostname, &v6)) &&
+       ((af == AF_INET6) || (af == AF_UNSPEC))))
+  {
+    rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution, rh);
+    return rh;
+  }
+  /* then, check if this is a loopback address */
+  i = 0;
+  while (NULL != loopback[i])
+    if (0 == strcasecmp (loopback[i++], hostname))
     {
     {
-      GNUNET_free (hctx->msg);
-      hctx->callback (hctx->cls, NULL);
-      GNUNET_CLIENT_disconnect (hctx->client);
-      GNUNET_free (hctx);
-      return 0;
+      rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution, rh);
+      return rh;
     }
     }
-  msize = ntohs (hctx->msg->header.size);
-  GNUNET_assert (size >= msize);
-  memcpy (buf, hctx->msg, msize);
-  GNUNET_free (hctx->msg);
-  hctx->msg = NULL;
-  GNUNET_CLIENT_receive (hctx->client,
-                         &handle_hostname_response,
-                         hctx,
-                         GNUNET_TIME_absolute_get_remaining (hctx->timeout));
-  return msize;
+  GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
+  rh->was_queued = GNUNET_YES;
+  if (s_task != GNUNET_SCHEDULER_NO_TASK)
+  {
+    GNUNET_SCHEDULER_cancel (s_task);
+    s_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  process_requests ();
+  return rh;
 }
 
 
 }
 
 
+/**
+ * We've been asked to convert an address to a string without
+ * a reverse lookup.  Do it.
+ *
+ * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
+ * @param tc unused scheduler context
+ */
+static void
+numeric_reverse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct GNUNET_RESOLVER_RequestHandle *rh = cls;
+  char *result;
+
+  result = no_resolve (rh->af, &rh[1], rh->data_len);
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Resolver returns `%s'.\n", result);
+  if (result != NULL)
+  {
+    rh->name_callback (rh->cls, result);
+    GNUNET_free (result);
+  }
+  rh->name_callback (rh->cls, NULL);
+  GNUNET_free (rh);
+}
 
 
 /**
  * Get an IP address as a string.
  *
 
 
 /**
  * Get an IP address as a string.
  *
- * @param sched scheduler to use
- * @param cfg configuration to use
  * @param sa host address
  * @param salen length of host address
  * @param do_resolve use GNUNET_NO to return numeric hostname
  * @param timeout how long to try resolving
  * @param callback function to call with hostnames
  * @param sa host address
  * @param salen length of host address
  * @param do_resolve use GNUNET_NO to return numeric hostname
  * @param timeout how long to try resolving
  * @param callback function to call with hostnames
+ *        last callback is NULL when finished
  * @param cls closure for callback
  * @param cls closure for callback
+ * @return handle that can be used to cancel the request
  */
  */
-void
-GNUNET_RESOLVER_hostname_get (struct GNUNET_SCHEDULER_Handle *sched,
-                              const struct GNUNET_CONFIGURATION_Handle *cfg,
-                              const struct sockaddr *sa,
-                              socklen_t salen,
+struct GNUNET_RESOLVER_RequestHandle *
+GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa, socklen_t salen,
                               int do_resolve,
                               struct GNUNET_TIME_Relative timeout,
                               GNUNET_RESOLVER_HostnameCallback callback,
                               void *cls)
 {
                               int do_resolve,
                               struct GNUNET_TIME_Relative timeout,
                               GNUNET_RESOLVER_HostnameCallback callback,
                               void *cls)
 {
-  char *result;
-  struct GNUNET_CLIENT_Connection *client;
-  struct GNUNET_RESOLVER_GetMessage *msg;
-  struct GetHostnameContext *hctx;
+  struct GNUNET_RESOLVER_RequestHandle *rh;
+  size_t ip_len;
+  const void *ip;
 
 
-  check_config (cfg);
+  check_config ();
+  switch (sa->sa_family)
+  {
+  case AF_INET:
+    ip_len = sizeof (struct in_addr);
+    ip = &((const struct sockaddr_in*)sa)->sin_addr;
+    break;
+  case AF_INET6:
+    ip_len = sizeof (struct in6_addr);
+    ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
+    break;
+  default:
+    GNUNET_break (0);
+    return NULL;
+  }
+  rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
+  rh->name_callback = callback;
+  rh->cls = cls;
+  rh->af = sa->sa_family;
+  rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+  memcpy (&rh[1], ip, ip_len);
+  rh->data_len = ip_len;
+  rh->direction = GNUNET_YES;
+  rh->received_response = GNUNET_NO;
   if (GNUNET_NO == do_resolve)
   if (GNUNET_NO == do_resolve)
-    {
-      result = no_resolve (sa, salen);
-#if DEBUG_RESOLVER
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  _("Resolver returns `%s'.\n"), result);
-#endif
-      callback (cls, result);
-      if (result != NULL)
-        {
-          GNUNET_free (result);
-          callback (cls, NULL);
-        }
-      return;
-    }
-  if (salen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
-      GNUNET_SERVER_MAX_MESSAGE_SIZE)
-    {
-      GNUNET_break (0);
-      callback (cls, NULL);
-      return;
-    }
-  client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
-  if (client == NULL)
-    {
-      callback (cls, NULL);
-      return;
-    }
-  msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen);
-  msg->header.size =
-    htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen);
-  msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
-  msg->direction = htonl (GNUNET_YES);
-  msg->domain = htonl (sa->sa_family);
-  memcpy (&msg[1], sa, salen);
-#if DEBUG_RESOLVER
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              _("Resolver requests DNS resolution of IP address.\n"));
-#endif
-  hctx = GNUNET_malloc (sizeof (struct GetHostnameContext));
-  hctx->callback = callback;
-  hctx->cls = cls;
-  hctx->client = client;
-  hctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
-  hctx->msg = msg;
-  if (NULL ==
-      GNUNET_CLIENT_notify_transmit_ready (client,
-                                           sizeof (struct
-                                                   GNUNET_RESOLVER_GetMessage)
-                                           + salen, timeout,
-                                           &transmit_get_hostname, hctx))
-    {
-      GNUNET_free (msg);
-      callback (cls, NULL);
-      GNUNET_CLIENT_disconnect (client);
-      GNUNET_free (hctx);
-    }
+  {
+    rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse, rh);
+    return rh;
+  }
+  GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
+  rh->was_queued = GNUNET_YES;
+  if (s_task != GNUNET_SCHEDULER_NO_TASK)
+  {
+    GNUNET_SCHEDULER_cancel (s_task);
+    s_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  process_requests ();
+  return rh;
 }
 
 }
 
+
 /**
 /**
- * Maximum supported length of hostname
+ * Get local fully qualified af name
+ *
+ * @return fqdn
  */
  */
-#define MAX_HOSTNAME 1024
+char *
+GNUNET_RESOLVER_local_fqdn_get ()
+{
+  struct hostent *host;
+  char hostname[GNUNET_OS_get_hostname_max_length () + 1];
+
+  if (0 != gethostname (hostname, sizeof (hostname) - 1))
+  {
+    LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "gethostname");
+    return NULL;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Resolving our FQDN `%s'\n", hostname);
+  host = gethostbyname (hostname);
+  if (NULL == host)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR, _("Could not resolve our FQDN : %s\n"),
+         hstrerror (h_errno));
+    return NULL;
+  }
+  return GNUNET_strdup (host->h_name);
+}
 
 
 /**
 
 
 /**
- * Resolve our hostname to an IP address.
+ * Looking our own hostname.
  *
  *
- * @param sched scheduler to use
- * @param cfg configuration to use
- * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
+ * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
  * @param callback function to call with addresses
  * @param cls closure for callback
  * @param timeout how long to try resolving
  * @param callback function to call with addresses
  * @param cls closure for callback
  * @param timeout how long to try resolving
+ * @return handle that can be used to cancel the request, NULL on error
  */
  */
-void
-GNUNET_RESOLVER_hostname_resolve (struct GNUNET_SCHEDULER_Handle *sched,
-                                  const struct GNUNET_CONFIGURATION_Handle *cfg,
-                                  int domain,
+struct GNUNET_RESOLVER_RequestHandle *
+GNUNET_RESOLVER_hostname_resolve (int af,
                                   struct GNUNET_TIME_Relative timeout,
                                   GNUNET_RESOLVER_AddressCallback callback,
                                   void *cls)
 {
                                   struct GNUNET_TIME_Relative timeout,
                                   GNUNET_RESOLVER_AddressCallback callback,
                                   void *cls)
 {
-  char hostname[MAX_HOSTNAME];
+  char hostname[GNUNET_OS_get_hostname_max_length () + 1];
 
 
-  check_config (cfg);
   if (0 != gethostname (hostname, sizeof (hostname) - 1))
   if (0 != gethostname (hostname, sizeof (hostname) - 1))
-    {
-      GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR |
-                           GNUNET_ERROR_TYPE_BULK, "gethostname");
-      callback (cls, NULL, 0);
-      return;
-    }
-#if DEBUG_RESOLVER
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              _("Resolving our hostname `%s'\n"), hostname);
-#endif
-  GNUNET_RESOLVER_ip_get (sched,
-                          cfg, hostname, domain, timeout, callback, cls);
+  {
+    LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "gethostname");
+    return NULL;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Resolving our hostname `%s'\n",
+       hostname);
+  return GNUNET_RESOLVER_ip_get (hostname, af, timeout, callback, cls);
 }
 
 
 }
 
 
+/**
+ * Cancel a request that is still pending with the resolver.
+ * Note that a client MUST NOT cancel a request that has
+ * been completed (i.e, the callback has been called to
+ * signal timeout or the final result).
+ *
+ * @param rh handle of request to cancel
+ */
+void
+GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
+{
+  if (rh->task != GNUNET_SCHEDULER_NO_TASK)
+  {
+    GNUNET_SCHEDULER_cancel (rh->task);
+    rh->task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  if (rh->was_transmitted == GNUNET_NO)
+  {
+    if (rh->was_queued == GNUNET_YES)
+      GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
+    GNUNET_free (rh);
+    return;
+  }
+  GNUNET_assert (rh->was_transmitted == GNUNET_YES);
+  rh->was_transmitted = GNUNET_SYSERR;  /* mark as cancelled */
+}
 
 
 /* end of resolver_api.c */
 
 
 /* end of resolver_api.c */