Remove the various gethostbyname_r checks and just check for
authorDan Winship <danw@src.gnome.org>
Tue, 12 Apr 2005 19:18:46 +0000 (19:18 +0000)
committerDan Winship <danw@src.gnome.org>
Tue, 12 Apr 2005 19:18:46 +0000 (19:18 +0000)
* configure.in: Remove the various gethostbyname_r checks and just
check for getnameinfo/getaddrinfo.

* libsoup/soup-dns.c: de-nastify. Make this use threads instead of
forking. Change the API around a bunch in the process.

* libsoup/soup-address.c: Update for soup-dns changes

* tests/dns.c: take multiple hostnames on the command line and
resolve them all at once (patch from tml)

ChangeLog
configure.in
libsoup/soup-address.c
libsoup/soup-dns.c
libsoup/soup-dns.h
tests/dns.c

index 575fc58..845984c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2005-04-12  Dan Winship  <danw@novell.com>
+
+       * configure.in: Remove the various gethostbyname_r checks and just
+       check for getnameinfo/getaddrinfo.
+
+       * libsoup/soup-dns.c: de-nastify. Make this use threads instead of
+       forking. Change the API around a bunch in the process.
+
+       * libsoup/soup-address.c: Update for soup-dns changes
+
+       * tests/dns.c: take multiple hostnames on the command line and
+       resolve them all at once (patch from tml)
+
 2005-04-11  Dan Winship  <danw@novell.com>
 
        * configure.in: require glib-2.0 >= 2.4.0
index ac03ab8..ffb5198 100644 (file)
@@ -85,88 +85,7 @@ dnl *********************************
 AC_CHECK_FUNC(socket, , AC_CHECK_LIB(socket, socket))
 AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, gethostbyname))
 
-AC_CHECK_FUNCS(inet_pton inet_aton)
-
-### Check if we have gethostbyname_r (if so, assume gethostbyaddr_r).
-AC_CHECK_FUNC(gethostbyname_r,
-  [
-  dnl  First check for the glibc variant of gethostbyname_r
-
-  AC_MSG_CHECKING(for glibc gethostbyname_r)
-  AC_TRY_LINK([        #include <netdb.h>],[
-         struct hostent result_buf;
-         char buf[1024];
-         struct hostent* result;
-         int h_errnop;
-
-         gethostbyname_r("localhost", &result_buf, buf, sizeof(buf),
-                         &result, &h_errnop);
-       ], [
-
-         dnl Have glibc gethostbyname_r
-
-         AC_MSG_RESULT(yes)
-         AC_DEFINE(HAVE_GETHOSTBYNAME_R_GLIBC, 1,
-                   [Define if you have a glibc-style gethostbyname_r()])
-         HAVE_GETHOSTBYNAME_R=yes
-
-        ], [
-
-  dnl  If we don't have glibc gethostbyname_r, check
-  dnl  for Solaris/Irix gethostbyname_r
-
-  AC_MSG_RESULT(no)
-  AC_MSG_CHECKING(for Solaris/Irix gethostbyname_r)
-  AC_TRY_LINK([ #include <netdb.h>],[
-         struct hostent result;
-         char buf[1024];
-         int h_errnop;
-
-         gethostbyname_r("localhost", &result, buf, sizeof(buf), &h_errnop);
-
-       ], [
-
-         dnl Have Solaris/Irix gethostbyname_r
-
-         AC_MSG_RESULT(yes)
-         AC_DEFINE(HAVE_GETHOSTBYNAME_R_SOLARIS, 1,
-                   [Define if you have a Solaris-style gethostbyname_r()])
-         HAVE_GETHOSTBYNAME_R=yes
-
-       ], [
-  dnl  If don't have Solaris/Irix gethostbyname_r, check
-  dnl  for HP-UX gethostbyname_r
-
-  AC_MSG_RESULT(no)
-  AC_MSG_CHECKING(for HP-UX gethostbyname_r)
-  AC_TRY_LINK([ #include <netdb.h>],[
-         struct hostent result;
-         char buf[1024];
-          gethostbyname_r("localhost", &result, buf);
-        ], [
-        
-          dnl Have HP-UX gethostbyname_r
-
-          AC_MSG_RESULT(yes)
-         AC_DEFINE(HAVE_GETHOSTBYNAME_R_HPUX, 1,
-                   [Define if you have an HP-UX-style gethostbyname_r()])
-         HAVE_GETHOSTBYNAME_R=yes
-
-       ]
-     )]
-  )]
-)])
-
-# If we don't have gethostbyname_r, we'll use Glib mutexes, but give a warning
-if test -z "$HAVE_GETHOSTBYNAME_R"; then
-       AC_DEFINE(HAVE_GETHOSTBYNAME_R_GLIB_MUTEX, 1,
-                 [Define if you have no gethostbyname_r()])
-       AC_MSG_WARN([You have neither Glib threads nor the function
-                   gethostbyname_r.  This means that calls to
-                   gethostbyname (called by the Soup address
-                   functions) will not be thread safe so could
-                   malfunction in programs that use threads.])
-fi
+AC_CHECK_FUNCS(inet_pton inet_aton getaddrinfo getnameinfo)
 
 AC_CACHE_CHECK(IPv6 support, soup_cv_ipv6, [
        AC_EGREP_HEADER(sockaddr_in6, netinet/in.h, soup_cv_ipv6=yes, soup_cv_ipv6=no)
index 4d00678..cd61df5 100644 (file)
@@ -42,7 +42,7 @@ typedef struct {
        char *name, *physical;
        guint port;
 
-       SoupDNSEntry *lookup;
+       SoupDNSLookup *lookup;
        guint timeout_id;
 } SoupAddressPrivate;
 #define SOUP_ADDRESS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_ADDRESS, SoupAddressPrivate))
@@ -132,7 +132,7 @@ finalize (GObject *object)
                g_free (priv->physical);
 
        if (priv->lookup)
-               soup_dns_entry_cancel_lookup (priv->lookup);
+               soup_dns_lookup_free (priv->lookup);
        if (priv->timeout_id)
                g_source_remove (priv->timeout_id);
 
@@ -144,6 +144,8 @@ soup_address_class_init (SoupAddressClass *address_class)
 {
        GObjectClass *object_class = G_OBJECT_CLASS (address_class);
 
+       soup_dns_init ();
+
        g_type_class_add_private (address_class, sizeof (SoupAddressPrivate));
 
        /* virtual method override */
@@ -187,6 +189,7 @@ soup_address_new (const char *name, guint port)
        priv = SOUP_ADDRESS_GET_PRIVATE (addr);
        priv->name = g_strdup (name);
        priv->port = port;
+       priv->lookup = soup_dns_lookup_name (priv->name);
 
        return addr;
 }
@@ -215,6 +218,8 @@ soup_address_new_from_sockaddr (struct sockaddr *sa, int len)
        priv = SOUP_ADDRESS_GET_PRIVATE (addr);
        priv->sockaddr = g_memdup (sa, len);
        priv->port = ntohs (SOUP_ADDRESS_GET_PORT (priv));
+       priv->lookup = soup_dns_lookup_address (priv->sockaddr);
+
        return addr;
 }
 
@@ -245,6 +250,7 @@ soup_address_new_any (SoupAddressFamily family, guint port)
        priv->sockaddr = g_malloc0 (SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (family));
        SOUP_ADDRESS_SET_FAMILY (priv, family);
        SOUP_ADDRESS_SET_PORT (priv, htons (port));
+       priv->lookup = soup_dns_lookup_address (priv->sockaddr);
 
        return addr;
 }
@@ -309,11 +315,8 @@ soup_address_get_physical (SoupAddress *addr)
        if (!priv->sockaddr)
                return NULL;
 
-       if (!priv->physical) {
-               priv->physical =
-                       soup_dns_ntop (SOUP_ADDRESS_GET_DATA (priv),
-                                      SOUP_ADDRESS_GET_FAMILY (priv));
-       }
+       if (!priv->physical)
+               priv->physical = soup_dns_ntop (priv->sockaddr);
 
        return priv->physical;
 }
@@ -335,58 +338,23 @@ soup_address_get_port (SoupAddress *addr)
 }
 
 
-static guint
-update_address_from_entry (SoupAddress *addr, SoupDNSEntry *entry)
-{
-       SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
-       struct hostent *h;
-
-       h = soup_dns_entry_get_hostent (entry);
-       if (!h)
-               return SOUP_STATUS_CANT_RESOLVE;
-
-       if (!priv->name)
-               priv->name = g_strdup (h->h_name);
-
-       if (!priv->sockaddr &&
-           SOUP_ADDRESS_FAMILY_IS_VALID (h->h_addrtype) &&
-           SOUP_ADDRESS_FAMILY_DATA_SIZE (h->h_addrtype) == h->h_length) {
-               priv->sockaddr = g_malloc0 (SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (h->h_addrtype));
-               SOUP_ADDRESS_SET_FAMILY (priv, h->h_addrtype);
-               SOUP_ADDRESS_SET_PORT (priv, htons (priv->port));
-               SOUP_ADDRESS_SET_DATA (priv, h->h_addr, h->h_length);
-       }
-
-       soup_dns_free_hostent (h);
-
-       if (priv->name && priv->sockaddr)
-               return SOUP_STATUS_OK;
-       else
-               return SOUP_STATUS_CANT_RESOLVE;
-}
-
-static gboolean
-timeout_check_lookup (gpointer user_data)
+static void
+update_address (SoupDNSLookup *lookup, gboolean success, gpointer user_data)
 {
        SoupAddress *addr = user_data;
        SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
-       guint status;
 
-       if (priv->name && priv->sockaddr) {
-               priv->timeout_id = 0;
-               g_signal_emit (addr, signals[DNS_RESULT], 0, SOUP_STATUS_OK);
-               return FALSE;
-       }
-
-       if (!soup_dns_entry_check_lookup (priv->lookup))
-               return TRUE;
+       if (success) {
+               if (!priv->name)
+                       priv->name = soup_dns_lookup_get_hostname (lookup);
 
-       status = update_address_from_entry (addr, priv->lookup);
-       priv->lookup = NULL;
-       priv->timeout_id = 0;
+               if (!priv->sockaddr) {
+                       priv->sockaddr = soup_dns_lookup_get_address (lookup);
+                       SOUP_ADDRESS_SET_PORT (priv, htons (priv->port));
+               }
+       }
 
-       g_signal_emit (addr, signals[DNS_RESULT], 0, status);
-       return FALSE;
+       g_signal_emit (addr, signals[DNS_RESULT], 0, success ? SOUP_STATUS_OK : SOUP_STATUS_CANT_RESOLVE);
 }
 
 /**
@@ -416,17 +384,7 @@ soup_address_resolve_async (SoupAddress *addr,
                                          G_CALLBACK (callback), user_data);
        }
 
-       if (priv->timeout_id)
-               return;
-
-       if (!priv->sockaddr) {
-               priv->lookup = soup_dns_entry_from_name (priv->name);
-       } else if (!priv->name) {
-               priv->lookup = soup_dns_entry_from_addr (SOUP_ADDRESS_GET_DATA (priv),
-                                                        SOUP_ADDRESS_GET_FAMILY (priv));
-       }
-
-       priv->timeout_id = g_timeout_add (100, timeout_check_lookup, addr);
+       soup_dns_lookup_resolve_async (priv->lookup, update_address, addr);
 }
 
 /**
@@ -441,18 +399,13 @@ soup_address_resolve_async (SoupAddress *addr,
 guint
 soup_address_resolve_sync (SoupAddress *addr)
 {
-       SoupDNSEntry *entry;
        SoupAddressPrivate *priv;
+       gboolean success;
 
        g_return_val_if_fail (SOUP_IS_ADDRESS (addr), SOUP_STATUS_MALFORMED);
        priv = SOUP_ADDRESS_GET_PRIVATE (addr);
 
-       if (priv->name)
-               entry = soup_dns_entry_from_name (priv->name);
-       else {
-               entry = soup_dns_entry_from_addr (SOUP_ADDRESS_GET_DATA (priv),
-                                                 SOUP_ADDRESS_GET_FAMILY (priv));
-       }
-
-       return update_address_from_entry (addr, entry);
+       success = soup_dns_lookup_resolve (priv->lookup);
+       update_address (priv->lookup, success, addr);
+       return success ? SOUP_STATUS_OK : SOUP_STATUS_CANT_RESOLVE;
 }
index 050327d..3a98c0f 100644 (file)
 #define INADDR_NONE -1
 #endif
 
-static struct hostent *
-new_hostent (const char *name, int type, int length, gpointer addr)
-{
-       struct hostent *h;
+#ifdef HAVE_IPV6
+#define SOUP_DNS_SOCKADDR_LEN(sa) \
+       (sa->sa_family == AF_INET ? sizeof (struct sockaddr_in) : \
+                                   sizeof (struct sockaddr_in6))
+#else
+#define SOUP_DNS_SOCKADDR_LEN(sa) sizeof (struct sockaddr_in)
+#endif
 
-       h = g_new0 (struct hostent, 1);
-       h->h_name = g_strdup (name);
-       h->h_aliases = NULL;
-       h->h_addrtype = type;
-       h->h_length = length;
-       h->h_addr_list = g_new (char *, 2);
-       h->h_addr_list[0] = g_memdup (addr, length);
-       h->h_addr_list[1] = NULL;
+typedef struct {
+       char *entry_name;
+       guint ref_count;
+       time_t expires;
 
-       return h;
-}
+       char *hostname;
+       struct sockaddr *sockaddr;
 
-static struct hostent *
-copy_hostent (struct hostent *h)
-{
-       return new_hostent (h->h_name, h->h_addrtype,
-                           h->h_length, h->h_addr_list[0]);
-}
+       gboolean resolved;
+       GThread *resolver_thread;
+       GSList *lookups;
+} SoupDNSCacheEntry;
+
+static GHashTable *soup_dns_cache;
+#define SOUP_DNS_CACHE_MAX 20
+
+struct SoupDNSLookup {
+       SoupDNSCacheEntry *entry;
+
+       SoupDNSCallback callback;
+       gpointer user_data;
+       gboolean running;
+};
+
+static GMutex *soup_dns_lock;
+static GCond *soup_dns_cond;
+
+#if !defined (HAVE_GETADDRINFO) || !defined (HAVE_GETNAMEINFO)
+static GMutex *soup_gethost_lock;
+#endif
 
-/**
- * soup_dns_free_hostent:
- * @h: a #hostent
- *
- * Frees @h. Use this to free the return value from
- * soup_dns_entry_get_hostent().
- **/
 void
-soup_dns_free_hostent (struct hostent *h)
+soup_dns_init (void)
 {
-       g_free (h->h_name);
-       g_free (h->h_addr_list[0]);
-       g_free (h->h_addr_list);
-       g_free (h);
+       if (soup_dns_cache == NULL) {
+               soup_dns_cache = g_hash_table_new (soup_str_case_hash, soup_str_case_equal);
+               soup_dns_lock = g_mutex_new ();
+               soup_dns_cond = g_cond_new ();
+#if !defined (HAVE_GETADDRINFO) || !defined (HAVE_GETNAMEINFO)
+               soup_gethost_lock = g_mutex_new ();
+#endif
+       }
 }
 
 static void
-write_hostent (struct hostent *h, int fd)
+prune_cache_cb (gpointer key, gpointer value, gpointer data)
 {
-       guchar namelen = strlen (h->h_name) + 1;
-       guchar addrlen = h->h_length;
-       guchar addrtype = h->h_addrtype;
-       struct iovec iov[5];
-
-       iov[0].iov_base = &namelen;
-       iov[0].iov_len = 1;
-       iov[1].iov_base = h->h_name;
-       iov[1].iov_len = namelen;
-       iov[2].iov_base = &addrtype;
-       iov[2].iov_len = 1;
-       iov[3].iov_base = &addrlen;
-       iov[3].iov_len = 1;
-       iov[4].iov_base = h->h_addr_list[0];
-       iov[4].iov_len = addrlen;
-
-       if (writev (fd, iov, 5) == -1)
-               g_warning ("Problem writing to pipe");
+       SoupDNSCacheEntry *entry = value, **prune_entry = data; 
+
+       if (!*prune_entry || (*prune_entry)->expires > entry->expires)
+               *prune_entry = entry;
 }
 
-static struct hostent *
-new_hostent_from_phys (const char *addr)
+static void
+soup_dns_cache_entry_set_from_phys (SoupDNSCacheEntry *entry)
 {
-       struct in_addr inaddr;
+       struct sockaddr_in sin;
 #ifdef HAVE_IPV6
-       struct in6_addr inaddr6;
+       struct sockaddr_in6 sin6;
 #endif
 
-#if defined(HAVE_INET_PTON)
 #ifdef HAVE_IPV6
-       if (inet_pton (AF_INET6, addr, &inaddr6) != 0)
-               return new_hostent (addr, AF_INET6, sizeof (inaddr6), &inaddr6);
-       else
-#endif
-       if (inet_pton (AF_INET, addr, &inaddr) != 0)
-               return new_hostent (addr, AF_INET, sizeof (inaddr), &inaddr);
+       memset (&sin6, 0, sizeof (struct sockaddr_in6));
+       if (inet_pton (AF_INET6, entry->entry_name, &sin6.sin6_addr) != 0) {
+               entry->sockaddr = g_memdup (&sin6, sizeof (struct sockaddr_in6));
+               entry->sockaddr->sa_family = AF_INET6;
+               return;
+       }
+#endif /* HAVE_IPV6 */
+
+       memset (&sin, 0, sizeof (struct sockaddr_in));
+       if (
+#if defined(HAVE_INET_PTON)
+               inet_pton (AF_INET, entry->entry_name, &sin.sin_addr) != 0
 #elif defined(HAVE_INET_ATON)
-       if (inet_aton (addr, &inaddr) != 0)
-               return new_hostent (addr, AF_INET, sizeof (inaddr), &inaddr);
+               inet_aton (entry->entry_name, &sin.sin_addr) != 0
 #else
-       inaddr.s_addr = inet_addr (addr);
-       if (inaddr.s_addr != INADDR_NONE)
-               return new_hostent (addr, AF_INET, sizeof (inaddr), &inaddr);
+               (sin.sin_addr.s_addr = inet_addr (entry->entry_name)) &&
+               (sin.sin_addr.s_addr != INADDR_NONE)
 #endif
+               ) {
+               entry->sockaddr = g_memdup (&sin, sizeof (struct sockaddr_in));
+               entry->sockaddr->sa_family = AF_INET;
+               return;
+       }
+}
 
-       return NULL;
+static void
+soup_dns_cache_entry_ref (SoupDNSCacheEntry *entry)
+{
+       entry->ref_count++;
+}
+
+static void
+soup_dns_cache_entry_unref (SoupDNSCacheEntry *entry)
+{
+       if (--entry->ref_count == 0) {
+               g_free (entry->entry_name);
+               g_free (entry->hostname);
+               g_free (entry->sockaddr);
+
+               /* If there were lookups pending, ref_count couldn't
+                * have reached zero. So no cleanup needed there.
+                */
+
+               g_free (entry);
+       }
+}
+
+static SoupDNSCacheEntry *
+soup_dns_cache_entry_new (const char *name)
+{
+       SoupDNSCacheEntry *entry;
+
+       entry = g_new0 (SoupDNSCacheEntry, 1);
+       entry->entry_name = g_strdup (name);
+       entry->ref_count = 2; /* One for the caller, one for the cache */
+       soup_dns_cache_entry_set_from_phys (entry);
+
+       if (g_hash_table_size (soup_dns_cache) == SOUP_DNS_CACHE_MAX) {
+               SoupDNSCacheEntry *prune_entry = NULL;
+
+               g_hash_table_foreach (soup_dns_cache, prune_cache_cb, &prune_entry);
+               if (prune_entry) {
+                       g_hash_table_remove (soup_dns_cache, prune_entry->entry_name);
+                       soup_dns_cache_entry_unref (prune_entry);
+               }
+       }
+
+       entry->expires = time (0) + 60 * 60;
+       g_hash_table_insert (soup_dns_cache, entry->entry_name, entry);
+
+       return entry;
 }
 
 /**
  * soup_dns_ntop:
- * @addr: pointer to address data (eg, an #in_addr_t)
- * @family: address family of @addr 
+ * @sa: pointer to a #sockaddr
  *
- * Converts @addr into textual form (eg, "141.213.8.59"), like the
- * standard library function inet_ntop(), except that the returned
+ * Converts @sa's address into textual form (eg, "141.213.8.59"), like
+ * the standard library function inet_ntop(), except that the returned
  * string must be freed.
  *
- * Return value: the text form or @addr, which must be freed.
+ * Return value: the text form or @sa, which must be freed.
  **/
 char *
-soup_dns_ntop (gconstpointer addr, int family)
+soup_dns_ntop (struct sockaddr *sa)
 {
-       switch (family) {
+       switch (sa->sa_family) {
        case AF_INET:
        {
+               struct sockaddr_in *sin = (struct sockaddr_in *)sa;
 #ifdef HAVE_INET_NTOP
                char buffer[INET_ADDRSTRLEN];
 
-               inet_ntop (family, addr, buffer, sizeof (buffer));
+               inet_ntop (family, &sin->sin_addr, buffer, sizeof (buffer));
                return g_strdup (buffer);
 #else
-               return g_strdup (inet_ntoa (*(struct in_addr *)addr));
+               return g_strdup (inet_ntoa (sin->sin_addr));
 #endif
        }
 
 #ifdef HAVE_IPV6
        case AF_INET6:
        {
+               struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
                char buffer[INET6_ADDRSTRLEN];
 
-               inet_ntop (family, addr, buffer, sizeof (buffer));
+               inet_ntop (AF_INET6, &sin6->sin6_addr, buffer, sizeof (buffer));
                return g_strdup (buffer);
        }
 #endif
@@ -168,569 +221,350 @@ soup_dns_ntop (gconstpointer addr, int family)
        }
 }
 
-
-static struct hostent *
-soup_gethostbyname_internal (const char *hostname)
+static void
+resolve_address (SoupDNSCacheEntry *entry)
 {
-       struct hostent result_buf, *result = &result_buf, *out;
-       char *buf = NULL;
-
-#if defined(HAVE_GETHOSTBYNAME_R_GLIBC)
-       {
-               size_t len;
-               int herr, res;
-
-               len = 1024;
-               buf = g_new (char, len);
-
-               while ((res = gethostbyname_r (hostname,
-                                              &result_buf,
-                                              buf,
-                                              len,
-                                              &result,
-                                              &herr)) == ERANGE) {
-                       len *= 2;
-                       buf = g_renew (char, buf, len);
-               }
-
-               if (res || result == NULL || result->h_addr_list [0] == NULL)
-                       result = NULL;
-       }
-#elif defined(HAVE_GETHOSTBYNAME_R_SOLARIS)
-       {
-               size_t len;
-               int herr, res;
-
-               len = 1024;
-               buf = g_new (char, len);
-
-               while ((res = gethostbyname_r (hostname,
-                                              &result_buf,
-                                              buf,
-                                              len,
-                                              &herr)) == ERANGE) {
-                       len *= 2;
-                       buf = g_renew (char, buf, len);
-               }
-
-               if (res)
-                       result = NULL;
-       }
-#elif defined(HAVE_GETHOSTBYNAME_R_HPUX)
-       {
-               struct hostent_data hdbuf;
-
-               if (!gethostbyname_r (hostname, &result_buf, &hdbuf))
-                       result = NULL;
-       }
-#else
-       {
-               result = gethostbyname (hostname);
+#if defined (HAVE_GETADDRINFO)
+
+       struct addrinfo hints, *res;
+       int retval;
+
+       memset (&hints, 0, sizeof (struct addrinfo));
+#  ifdef HAVE_AI_ADDRCONFIG
+       hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
+#  else
+       hints.ai_flags = AI_CANONNAME;
+#  endif
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_protocol = IPPROTO_TCP;
+
+       retval = getaddrinfo (entry->hostname, NULL, &hints, &res);
+       if (retval == 0) {
+               entry->sockaddr = g_memdup (res->ai_addr, res->ai_addrlen);
+               freeaddrinfo (res);
        }
-#endif
-
-       if (result)
-               out = copy_hostent (result);
-       else
-               out = NULL;
-
-       if (buf)
-               g_free (buf);
 
-       return out;
-}
-
-static struct hostent *
-soup_gethostbyaddr_internal (gconstpointer addr, int family)
-{
-       struct hostent result_buf, *result = &result_buf, *out;
-       char *buf = NULL;
-       int length;
+#else /* !HAVE_GETADDRINFO */
 
-       switch (family) {
-       case AF_INET:
-               length = sizeof (struct in_addr);
-               break;
-#ifdef HAVE_IPV6
-       case AF_INET6:
-               length = sizeof (struct in6_addr);
-               break;
-#endif
-       default:
-               return NULL;
-       }
+       struct hostent *h;
 
-#if defined(HAVE_GETHOSTBYNAME_R_GLIBC)
-       {
-               size_t len;
-               int herr, res;
-
-               len = 1024;
-               buf = g_new (char, len);
-
-               while ((res = gethostbyaddr_r (addr,
-                                              length,
-                                              family,
-                                              &result_buf,
-                                              buf,
-                                              len,
-                                              &result,
-                                              &herr)) == ERANGE) {
-                       len *= 2;
-                       buf = g_renew (char, buf, len);
-               }
+       g_mutex_lock (soup_gethost_lock);
 
-               if (res || result == NULL || result->h_name == NULL)
-                       result = NULL;
+       h = gethostbyname (entry->hostname);
+       if (h && h->h_addrtype == AF_INET) {
+               struct sockaddr_in sin;
+               memset (&sin, 0, sizeof (struct sockaddr_in));
+               sin.sin_family = AF_INET;
+               memcpy (&sin.sin_addr, h->h_addr_list[0], sizeof (struct in_addr));
+               entry->sockaddr = g_memdup (&sin, sizeof (struct sockaddr_in));
        }
-#elif defined(HAVE_GETHOSTBYNAME_R_SOLARIS)
-       {
-               size_t len;
-               int herr, res;
-
-               len = 1024;
-               buf = g_new (char, len);
-
-               while ((res = gethostbyaddr_r (addr,
-                                              length,
-                                              family,
-                                              &result_buf,
-                                              buf,
-                                              len,
-                                              &herr)) == ERANGE) {
-                       len *= 2;
-                       buf = g_renew (char, buf, len);
-               }
 
-               if (res)
-                       result = NULL;
-       }
-#elif defined(HAVE_GETHOSTBYNAME_R_HPUX)
-       {
-               struct hostent_data hdbuf;
+       g_mutex_unlock (soup_gethost_lock);
 
-               if (!gethostbyaddr_r (addr, length, family, &result_buf, &hdbuf))
-                       result = NULL;
-       }
-#else
-       {
-               result = gethostbyaddr (addr, length, family);
-       }
 #endif
-
-       if (result)
-               out = copy_hostent (result);
-       else
-               out = NULL;
-
-       if (buf)
-               g_free (buf);
-
-       return out;
-}
-
-
-/* Cache */
-
-struct SoupDNSEntry {
-       char           *name;
-       struct hostent *h;
-       gboolean        resolved;
-
-       time_t          expires;
-       guint           ref_count;
-
-       pid_t           lookup_pid;
-       int             fd;
-};
-
-static GHashTable *soup_dns_entries;
-
-#define SOUP_DNS_ENTRIES_MAX 20
-
-static GStaticMutex soup_dns_mutex = G_STATIC_MUTEX_INIT;
-#define soup_dns_lock() g_static_mutex_lock (&soup_dns_mutex)
-#define soup_dns_unlock() g_static_mutex_unlock (&soup_dns_mutex)
-
-static void
-soup_dns_entry_ref (SoupDNSEntry *entry)
-{
-       entry->ref_count++;
-}
-
-static void
-soup_dns_entry_unref (SoupDNSEntry *entry)
-{
-       if (!--entry->ref_count) {
-               g_free (entry->name);
-
-               if (entry->h)
-                       soup_dns_free_hostent (entry->h);
-
-               if (entry->fd)
-                       close (entry->fd);
-               if (entry->lookup_pid) {
-                       kill (entry->lookup_pid, SIGKILL);
-                       waitpid (entry->lookup_pid, NULL, 0);
-               }
-
-               g_free (entry);
-       }
-}
-
-static void
-uncache_entry (SoupDNSEntry *entry)
-{
-       g_hash_table_remove (soup_dns_entries, entry->name);
-       soup_dns_entry_unref (entry);
 }
 
 static void
-prune_cache_cb (gpointer key, gpointer value, gpointer data)
+resolve_name (SoupDNSCacheEntry *entry)
 {
-       SoupDNSEntry *entry = value, **prune_entry = data; 
+#ifdef HAVE_GETNAMEINFO
+       int retval, len = 128;
+       char *name = NULL;
 
-       if (!*prune_entry || (*prune_entry)->expires > entry->expires)
-               *prune_entry = entry;
-}
+       do {
+               name = g_realloc (name, len);
+               retval = getnameinfo (entry->sockaddr, SOUP_DNS_SOCKADDR_LEN (entry->sockaddr),
+                                     name, len, NULL, 0, NI_NAMEREQD);
+               len += 128;
+       } while (retval == EAI_OVERFLOW);
+
+       if (retval == 0)
+               entry->hostname = name;
+       else
+               g_free (name);
 
-static SoupDNSEntry *
-soup_dns_entry_new (const char *name)
-{
-       SoupDNSEntry *entry;
+#else /* !HAVE_GETNAMEINFO */
 
-       entry = g_new0 (SoupDNSEntry, 1);
-       entry->name = g_strdup (name);
-       entry->ref_count = 2; /* One for the caller, one for the cache */
+       struct sockaddr_in *sin = (struct sockaddr_in *)entry->sockaddr;
+       struct hostent *h;
 
-       if (!soup_dns_entries) {
-               soup_dns_entries = g_hash_table_new (soup_str_case_hash,
-                                                    soup_str_case_equal);
-       } else if (g_hash_table_size (soup_dns_entries) == SOUP_DNS_ENTRIES_MAX) {
-               SoupDNSEntry *prune_entry = NULL;
+       g_mutex_lock (soup_gethost_lock);
 
-               g_hash_table_foreach (soup_dns_entries, prune_cache_cb,
-                                     &prune_entry);
-               if (prune_entry)
-                       uncache_entry (prune_entry);
+       if (sin->sin_family == AF_INET) {
+               h = gethostbyaddr (&sin->sin_addr, sizeof (sin->sin_addr), AF_INET);
+               if (h)
+                       entry->hostname = g_strdup (h->h_name);
        }
 
-       entry->expires = time (0) + 60 * 60;
-       g_hash_table_insert (soup_dns_entries, entry->name, entry);
+       g_mutex_unlock (soup_gethost_lock);
 
-       return entry;
+#endif /* HAVE_GETNAMEINFO */
 }
 
-static SoupDNSEntry *
-soup_dns_lookup_entry (const char *name)
+/* Assumes soup_dns_lock is held */
+static SoupDNSCacheEntry *
+soup_dns_cache_entry_lookup (const char *name)
 {
-       SoupDNSEntry *entry;
-
-       if (!soup_dns_entries)
-               return NULL;
+       SoupDNSCacheEntry *entry;
 
-       entry = g_hash_table_lookup (soup_dns_entries, name);
+       entry = g_hash_table_lookup (soup_dns_cache, name);
        if (entry)
-               soup_dns_entry_ref (entry);
+               soup_dns_cache_entry_ref (entry);
        return entry;
 }
 
 /**
- * soup_dns_entry_from_name:
- * @name: a nice name (eg, mofo.eecs.umich.edu) or a dotted decimal name
- *   (eg, 141.213.8.59).
- *
- * Begins asynchronous resolution of @name. The caller should
- * periodically call soup_dns_entry_check_lookup() to see if it is
- * done, and call soup_dns_entry_get_hostent() when
- * soup_dns_entry_check_lookup() returns %TRUE.
+ * soup_dns_lookup_name:
+ * @name: a hostname (eg, "www.gnome.org") or physical address
+ * (eg, "12.107.209.247").
  *
- * Currently, this routine forks and does the lookup, which can cause
- * some problems. In general, this will work ok for most programs most
- * of the time. It will be slow or even fail when using operating
- * systems that copy the entire process when forking.
+ * Creates a #SoupDNSLookup for @name. This should be passed to
+ * soup_dns_lookup_resolve() or soup_dns_lookup_resolve_async().
  *
- * Returns: a #SoupDNSEntry, which will be freed when you call
- * soup_dns_entry_get_hostent() or soup_dns_entry_cancel_lookup().
+ * Returns: a #SoupDNSLookup, which should eventually be freed with
+ * soup_dns_lookup_free().
  **/
-SoupDNSEntry *
-soup_dns_entry_from_name (const char *name)
+SoupDNSLookup *
+soup_dns_lookup_name (const char *name)
 {
-       SoupDNSEntry *entry;
-       int pipes[2];
+       SoupDNSCacheEntry *entry;
+       SoupDNSLookup *lookup;
 
-       soup_dns_lock ();
+       g_mutex_lock (soup_dns_lock);
 
-       /* Try the cache */
-       entry = soup_dns_lookup_entry (name);
-       if (entry) {
-               soup_dns_unlock ();
-               return entry;
+       entry = soup_dns_cache_entry_lookup (name);
+       if (!entry) {
+               entry = soup_dns_cache_entry_new (name);
+               entry->hostname = g_strdup (name);
+               if (entry->sockaddr)
+                       entry->resolved = TRUE;
        }
 
-       entry = soup_dns_entry_new (name);
+       lookup = g_new0 (SoupDNSLookup, 1);
+       lookup->entry = entry;
+       g_mutex_unlock (soup_dns_lock);
 
-       /* Try to read the name as if it were dotted decimal */
-       entry->h = new_hostent_from_phys (name);
-       if (entry->h) {
-               entry->resolved = TRUE;
-               soup_dns_unlock ();
-               return entry;
-       }
-
-       /* Check to see if we are doing synchronous DNS lookups */
-       if (getenv ("SOUP_SYNC_DNS")) {
-               entry->h = soup_gethostbyname_internal (name);
-               entry->resolved = TRUE;
-               soup_dns_unlock ();
-               return entry;
-       }
-
-       /* Ok, we need to start a new lookup */
-
-       if (pipe (pipes) == -1) {
-               entry->resolved = TRUE;
-               soup_dns_unlock ();
-               return entry;
-       }
-
-       entry->lookup_pid = fork ();
-       switch (entry->lookup_pid) {
-       case -1:
-               g_warning ("Fork error: %s (%d)\n", g_strerror (errno), errno);
-               close (pipes[0]);
-               close (pipes[1]);
-
-               entry->resolved = TRUE;
-               soup_dns_unlock ();
-               return entry;
-
-       case 0:
-               /* Child */
-               close (pipes[0]);
-
-               entry->h = soup_gethostbyname_internal (name);
-               if (entry->h)
-                       write_hostent (entry->h, pipes[1]);
-
-               /* Close the socket */
-               close (pipes[1]);
-
-               /* Exit (we don't want atexit called, so do _exit instead) */
-               _exit (EXIT_SUCCESS);
-
-       default:
-               /* Parent */
-               close (pipes[1]);
-
-               entry->fd = pipes[0];
-               soup_dns_unlock ();
-               return entry;
-       }
+       return lookup;
 }
 
 /**
- * soup_dns_entry_from_addr:
- * @addr: pointer to address data (eg, an #in_addr_t)
- * @family: address family of @addr
+ * soup_dns_lookup_address:
+ * @sockaddr: pointer to a #sockaddr
  *
- * Begins asynchronous resolution of @addr. The caller should
- * periodically call soup_dns_entry_check_lookup() to see if it is
- * done, and call soup_dns_entry_get_hostent() when
- * soup_dns_entry_check_lookup() returns %TRUE.
+ * Creates a #SoupDNSLookup for @sockaddr. This should be passed to
+ * soup_dns_lookup_resolve() or soup_dns_lookup_resolve_async().
  *
- * Currently, this routine forks and does the lookup, which can cause
- * some problems. In general, this will work ok for most programs most
- * of the time. It will be slow or even fail when using operating
- * systems that copy the entire process when forking.
- *
- * Returns: a #SoupDNSEntry, which will be freed when you call
- * soup_dns_entry_get_hostent() or soup_dns_entry_cancel_lookup().
+ * Returns: a #SoupDNSLookup, which should eventually be freed with
+ * soup_dns_lookup_free()
  **/
-SoupDNSEntry *
-soup_dns_entry_from_addr (gconstpointer addr, int family)
+SoupDNSLookup *
+soup_dns_lookup_address (struct sockaddr *sockaddr)
 {
-       SoupDNSEntry *entry;
-       int pipes[2];
+       SoupDNSCacheEntry *entry;
+       SoupDNSLookup *lookup;
        char *name;
 
-       name = soup_dns_ntop (addr, family);
+       name = soup_dns_ntop (sockaddr);
        g_return_val_if_fail (name != NULL, NULL);
 
-       soup_dns_lock ();
+       g_mutex_lock (soup_dns_lock);
 
-       /* Try the cache */
-       entry = soup_dns_lookup_entry (name);
-       if (entry) {
-               g_free (name);
-               soup_dns_unlock ();
-               return entry;
-       }
+       entry = soup_dns_cache_entry_lookup (name);
+       if (!entry)
+               entry = soup_dns_cache_entry_new (name); // FIXME
+       g_free (name);
 
-       entry = soup_dns_entry_new (name);
+       lookup = g_new0 (SoupDNSLookup, 1);
+       lookup->entry = entry;
+       g_mutex_unlock (soup_dns_lock);
 
-       /* Check to see if we are doing synchronous DNS lookups */
-       if (getenv ("SOUP_SYNC_DNS")) {
-               entry->h = soup_gethostbyaddr_internal (addr, family);
-               entry->resolved = TRUE;
-               soup_dns_unlock ();
-               return entry;
-       }
+       return lookup;
+}
 
-       if (pipe (pipes) != 0) {
-               entry->resolved = TRUE;
-               soup_dns_unlock ();
-               return entry;
+static gboolean
+do_async_callbacks (gpointer user_data)
+{
+       SoupDNSCacheEntry *entry = user_data;
+       GSList *lookups;
+       SoupDNSLookup *lookup;
+       gboolean success = (entry->hostname != NULL && entry->sockaddr != NULL);
+
+       g_mutex_lock (soup_dns_lock);
+       lookups = entry->lookups;
+       entry->lookups = NULL;
+       g_mutex_unlock (soup_dns_lock);
+
+       while (lookups) {
+               lookup = lookups->data;
+               lookups = g_slist_remove (lookups, lookup);
+               if (lookup->running) {
+                       lookup->running = FALSE;
+                       lookup->callback (lookup, success, lookup->user_data);
+               }
        }
 
-       entry->lookup_pid = fork ();
-       switch (entry->lookup_pid) {
-       case -1:
-               close (pipes[0]);
-               close (pipes[1]);
-
-               g_warning ("Fork error: %s (%d)\n", g_strerror(errno), errno);
-               entry->resolved = TRUE;
-               soup_dns_unlock ();
-               return entry;
-
-       case 0:
-               /* Child */
-               close (pipes[0]);
+       soup_dns_cache_entry_unref (entry);
+       return FALSE;
+}
 
-               entry->h = soup_gethostbyaddr_internal (addr, family);
-               if (entry->h)
-                       write_hostent (entry->h, pipes[1]);
+static gpointer
+resolver_thread (gpointer user_data)
+{
+       SoupDNSCacheEntry *entry = user_data;
 
-               /* Close the socket */
-               close (pipes[1]);
+       if (entry->hostname == NULL)
+               resolve_name (entry);
+       if (entry->sockaddr == NULL)
+               resolve_address (entry);
 
-               /* Exit (we don't want atexit called, so do _exit instead) */
-               _exit (EXIT_SUCCESS);
+       entry->resolved = TRUE;
+       entry->resolver_thread = NULL;
+       g_cond_broadcast (soup_dns_cond);
 
-       default:
-               /* Parent */
-               close (pipes[1]);
+       if (entry->lookups)
+               g_idle_add (do_async_callbacks, entry);
+       else
+               soup_dns_cache_entry_unref (entry);
 
-               entry->fd = pipes[0];
-               soup_dns_unlock ();
-               return entry;
-       }
+       return NULL;
 }
 
-static void
-check_hostent (SoupDNSEntry *entry, gboolean block)
+/**
+ * soup_dns_lookup_resolve:
+ * @lookup: a #SoupDNSLookup
+ *
+ * Synchronously resolves @lookup. You can cancel a pending resolution
+ * using soup_dns_lookup_cancel().
+ *
+ * Return value: success or failure.
+ **/
+gboolean
+soup_dns_lookup_resolve (SoupDNSLookup *lookup)
 {
-       char buf[256], *namelenp, *name, *typep, *addrlenp, *addr;
-       int bytes_read, nread, status;
-       fd_set readfds;
-       struct timeval tv = { 0, 0 }, *tvp;
+       SoupDNSCacheEntry *entry = lookup->entry;
 
-       soup_dns_lock ();
+       g_mutex_lock (soup_dns_lock);
 
-       if (entry->resolved) {
-               soup_dns_unlock ();
-               return;
+       lookup->running = TRUE;
+
+       if (!entry->resolved && !entry->resolver_thread) {
+               soup_dns_cache_entry_ref (entry);
+               entry->resolver_thread =
+                       g_thread_create (resolver_thread, entry, FALSE, NULL);
        }
 
-       if (block)
-               tvp = NULL;
-       else
-               tvp = &tv;
+       while (!entry->resolved && lookup->running)
+               g_cond_wait (soup_dns_cond, soup_dns_lock);
 
-       do {
-               FD_ZERO (&readfds);
-               FD_SET (entry->fd, &readfds);
-               status = select (entry->fd + 1, &readfds, NULL, NULL, tvp);
-       } while (status == -1 && errno == EINTR);
+       lookup->running = FALSE;
 
-       if (status == 0) {
-               soup_dns_unlock ();
-               return;
-       }
-       
-       nread = 0;
-       do {
-               bytes_read = read (entry->fd, buf + nread, 
-                                  sizeof (buf) - nread);
-
-               if (bytes_read > 0)
-                       nread += bytes_read;
-       } while (bytes_read > 0 || (bytes_read == -1 && errno == EINTR));
-
-       close (entry->fd);
-       entry->fd = -1;
-       kill (entry->lookup_pid, SIGKILL);
-       waitpid (entry->lookup_pid, NULL, 0);
-       entry->lookup_pid = 0;
-       entry->resolved = TRUE;
+       g_mutex_unlock (soup_dns_lock);
+       return entry->hostname != NULL && entry->sockaddr != NULL;
+}
 
-       if (nread < 1) {
-               soup_dns_unlock ();
-               return;
+/**
+ * soup_dns_lookup_resolve_async:
+ * @lookup: a #SoupDNSLookup
+ * @callback: callback to call when @lookup is resolved
+ * @user_data: data to pass to @callback;
+ *
+ * Tries to asynchronously resolve @lookup. Invokes @callback when it
+ * has succeeded or failed. You can cancel a pending resolution using
+ * soup_dns_lookup_cancel().
+ **/
+void
+soup_dns_lookup_resolve_async (SoupDNSLookup *lookup,
+                              SoupDNSCallback callback, gpointer user_data)
+{
+       SoupDNSCacheEntry *entry = lookup->entry;
+
+       g_mutex_lock (soup_dns_lock);
+
+       lookup->callback = callback;
+       lookup->user_data = user_data;
+       lookup->running = TRUE;
+       entry->lookups = g_slist_prepend (entry->lookups, lookup);
+
+       if (!entry->resolved) {
+               if (!entry->resolver_thread) {
+                       soup_dns_cache_entry_ref (entry);
+                       entry->resolver_thread =
+                               g_thread_create (resolver_thread, entry, FALSE, NULL);
+               }
+       } else {
+               soup_dns_cache_entry_ref (entry);
+               g_idle_add (do_async_callbacks, entry);
        }
 
-       namelenp = buf;
-       name = namelenp + 1;
-       typep = name + *namelenp;
-       addrlenp = typep + 1;
-       addr = addrlenp + 1;
+       g_mutex_unlock (soup_dns_lock);
+}
 
-       if (addrlenp < buf + nread && (addr + *addrlenp) == buf + nread)
-               entry->h = new_hostent (name, *typep, *addrlenp, addr);
-       soup_dns_unlock ();
+/**
+ * soup_dns_lookup_cancel:
+ * @lookup: a #SoupDNSLookup
+ *
+ * Cancels @lookup. If @lookup was running synchronously in another
+ * thread, it will immediately return %FALSE. If @lookup was running
+ * asynchronously, its callback function will not be called.
+ **/
+void
+soup_dns_lookup_cancel (SoupDNSLookup *lookup)
+{
+       /* We never really cancel the DNS lookup itself (since GThread
+        * doesn't have a kill function, and it might mess up
+        * underlying resolver data anyway). But clearing lookup->running
+        * and broadcasting on soup_dns_cond will immediately stop any
+        * blocking synchronous lookups, and clearing lookup->running
+        * will also make sure that its async callback is never invoked.
+        */
+       lookup->running = FALSE;
+       g_cond_broadcast (soup_dns_cond);
 }
 
 /**
- * soup_dns_entry_check_lookup:
- * @entry: a #SoupDNSEntry
+ * soup_dns_lookup_get_hostname:
+ * @lookup: a #SoupDNSLookup
  *
- * Checks if @entry has finished resolving
+ * Gets the hostname of @lookup.
  *
- * Return value: %TRUE if @entry has finished resolving (either
- * successfully or not)
+ * Return value: the hostname, which the caller owns and must free, or
+ * %NULL if @lookup has not been completely resolved.
  **/
-gboolean
-soup_dns_entry_check_lookup (SoupDNSEntry *entry)
+char *
+soup_dns_lookup_get_hostname (SoupDNSLookup *lookup)
 {
-       check_hostent (entry, FALSE);
-
-       if (entry->resolved && entry->h == NULL)
-               uncache_entry (entry);
-
-       return entry->resolved;
+       return g_strdup (lookup->entry->hostname);
 }
 
 /**
- * soup_dns_entry_get_hostent:
- * @entry: a #SoupDNSEntry
+ * soup_dns_lookup_get_address:
+ * @lookup: a #SoupDNSLookup
  *
- * Extracts a #hostent from @entry. If @entry had not already finished
- * resolving, soup_dns_entry_get_hostent() will block until it does.
+ * Gets the address of @lookup.
  *
- * Return value: the #hostent (which must be freed with
- * soup_dns_free_hostent()), or %NULL if it couldn't be resolved.
+ * Return value: the address, which the caller owns and must free, or
+ * %NULL if @lookup has not been completely resolved.
  **/
-struct hostent *
-soup_dns_entry_get_hostent (SoupDNSEntry *entry)
+struct sockaddr *
+soup_dns_lookup_get_address (SoupDNSLookup *lookup)
 {
-       struct hostent *h;
-
-       check_hostent (entry, TRUE);
-       h = entry->h ? copy_hostent (entry->h) : NULL;
-       soup_dns_entry_unref (entry);
-
-       return h;
+       return g_memdup (lookup->entry->sockaddr,
+                        SOUP_DNS_SOCKADDR_LEN (lookup->entry->sockaddr));
 }
 
 /**
- * soup_dns_entry_cancel_lookup:
- * @entry: a #SoupDNSEntry
+ * soup_dns_lookup_free:
+ * @lookup: a #SoupDNSLookup
  *
- * Cancels the lookup for @entry.
+ * Frees @lookup. If @lookup is still running, it will be canceled
+ * first.
  **/
 void
-soup_dns_entry_cancel_lookup (SoupDNSEntry *entry)
+soup_dns_lookup_free (SoupDNSLookup *lookup)
 {
-       soup_dns_entry_unref (entry);
+       if (lookup->running)
+               soup_dns_lookup_cancel (lookup);
+       soup_dns_cache_entry_unref (lookup->entry);
+       g_free (lookup);
 }
index 2162e65..d3a51a1 100644 (file)
 #include <sys/socket.h>
 #include <netdb.h>
 
-typedef struct SoupDNSEntry SoupDNSEntry;
+void             soup_dns_init                 (void);
 
-SoupDNSEntry   *soup_dns_entry_from_name     (const char     *name);
-SoupDNSEntry   *soup_dns_entry_from_addr     (gconstpointer   addr,
-                                             int             family);
+typedef struct SoupDNSLookup SoupDNSLookup;
 
-gboolean        soup_dns_entry_check_lookup  (SoupDNSEntry   *entry);
-void            soup_dns_entry_cancel_lookup (SoupDNSEntry   *entry);
+SoupDNSLookup   *soup_dns_lookup_name          (const char  *name);
+SoupDNSLookup   *soup_dns_lookup_address       (struct sockaddr *address);
 
-struct hostent *soup_dns_entry_get_hostent   (SoupDNSEntry   *entry);
-void            soup_dns_free_hostent        (struct hostent *h);
+typedef void (*SoupDNSCallback) (SoupDNSLookup *lookup, gboolean success, gpointer user_data);
+
+gboolean         soup_dns_lookup_resolve       (SoupDNSLookup   *lookup);
+void             soup_dns_lookup_resolve_async (SoupDNSLookup   *lookup,
+                                               SoupDNSCallback  callback,
+                                               gpointer         user_data);
+void             soup_dns_lookup_cancel        (SoupDNSLookup   *lookup);
+
+char            *soup_dns_lookup_get_hostname  (SoupDNSLookup   *lookup);
+struct sockaddr *soup_dns_lookup_get_address   (SoupDNSLookup   *lookup);
+
+void             soup_dns_lookup_free          (SoupDNSLookup   *lookup);
+
+
+char            *soup_dns_ntop                 (struct sockaddr *address);
 
-char           *soup_dns_ntop                (gconstpointer   addr,
-                                             int             family);
 
 #endif /* SOUP_DNS_H */
index 496c543..755ff69 100644 (file)
@@ -5,25 +5,30 @@
 
 #include "libsoup/soup-address.h"
 
-GMainLoop *loop;
+static GMainLoop *loop;
+static int nlookups = 0;
 
 static void
 resolve_callback (SoupAddress *addr, guint status, gpointer data)
 {
-       if (status != SOUP_STATUS_OK) {
-               fprintf (stderr, "%s\n", soup_status_get_phrase (status));
-               exit (1);
+       if (status == SOUP_STATUS_OK) {
+               printf ("Name:    %s\n", soup_address_get_name (addr));
+               printf ("Address: %s\n", soup_address_get_physical (addr));
+       } else {
+               printf ("Name:    %s\n", soup_address_get_name (addr));
+               printf ("Error:   %s\n", soup_status_get_phrase (status));
        }
+       printf ("\n");
 
-       printf ("Name:    %s\n", soup_address_get_name (addr));
-       printf ("Address: %s\n", soup_address_get_physical (addr));
-       g_main_loop_quit (loop);
+       nlookups--;
+       if (nlookups == 0)
+               g_main_loop_quit (loop);
 }
 
 static void
 usage (void)
 {
-       fprintf (stderr, "Usage: dns [hostname | -r IP]\n");
+       fprintf (stderr, "Usage: dns hostname ...\n");
        exit (1);
 }
 
@@ -31,20 +36,24 @@ int
 main (int argc, char **argv)
 {
        SoupAddress *addr;
+       int i;
 
-       if (argc != 2)
+       if (argc < 2)
                usage ();
 
        g_type_init ();
        g_thread_init (NULL);
 
-       addr = soup_address_new (argv[1], 0);
-       if (!addr) {
-               fprintf (stderr, "Could not parse address %s\n", argv[1]);
-               exit (1);
-       }
+       for (i = 1; i < argc; i++) {
+               addr = soup_address_new (argv[i], 0);
+               if (!addr) {
+                       fprintf (stderr, "Could not parse address %s\n", argv[1]);
+                       exit (1);
+               }
 
-       soup_address_resolve_async (addr, resolve_callback, NULL);
+               soup_address_resolve_async (addr, resolve_callback, NULL);
+               nlookups++;
+       }
 
        loop = g_main_loop_new (NULL, TRUE);
        g_main_run (loop);