#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
}
}
-
-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);
}