1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-dns.c: Async DNS code
5 * Copyright (C) 2000-2003, Ximian, Inc.
18 #include <sys/select.h>
19 #include <sys/types.h>
22 #include <arpa/inet.h>
23 #include <netinet/in.h>
26 #include "soup-private.h"
29 # define socklen_t int
32 #ifndef INET_ADDRSTRLEN
33 # define INET_ADDRSTRLEN 16
34 # define INET6_ADDRSTRLEN 46
38 #define INADDR_NONE -1
41 static struct hostent *
42 new_hostent (const char *name, int type, int length, gpointer addr)
46 h = g_new0 (struct hostent, 1);
47 h->h_name = g_strdup (name);
51 h->h_addr_list = g_new (char *, 2);
52 h->h_addr_list[0] = g_memdup (addr, length);
53 h->h_addr_list[1] = NULL;
58 static struct hostent *
59 copy_hostent (struct hostent *h)
61 return new_hostent (h->h_name, h->h_addrtype,
62 h->h_length, h->h_addr_list[0]);
66 soup_dns_free_hostent (struct hostent *h)
69 g_free (h->h_addr_list[0]);
70 g_free (h->h_addr_list);
75 write_hostent (struct hostent *h, int fd)
77 guchar namelen = strlen (h->h_name) + 1;
78 guchar addrlen = h->h_length;
79 guchar addrtype = h->h_addrtype;
82 iov[0].iov_base = &namelen;
84 iov[1].iov_base = h->h_name;
85 iov[1].iov_len = namelen;
86 iov[2].iov_base = &addrtype;
88 iov[3].iov_base = &addrlen;
90 iov[4].iov_base = h->h_addr_list[0];
91 iov[4].iov_len = addrlen;
93 if (writev (fd, iov, 5) == -1)
94 g_warning ("Problem writing to pipe");
97 static struct hostent *
98 new_hostent_from_phys (const char *addr)
100 struct in_addr inaddr;
102 struct in6_addr inaddr6;
105 #if defined(HAVE_INET_PTON)
107 if (inet_pton (AF_INET6, addr, &inaddr6) != 0)
108 return new_hostent (addr, AF_INET6, sizeof (inaddr6), &inaddr6);
111 if (inet_pton (AF_INET, addr, &inaddr) != 0)
112 return new_hostent (addr, AF_INET, sizeof (inaddr), &inaddr);
113 #elif defined(HAVE_INET_ATON)
114 if (inet_aton (addr, &inaddr) != 0)
115 return new_hostent (addr, AF_INET, sizeof (inaddr), &inaddr);
117 inaddr.s_addr = inet_addr (addr);
118 if (inaddr.s_addr != INADDR_NONE)
119 return new_hostent (addr, AF_INET, sizeof (inaddr), &inaddr);
126 soup_dns_ntop (gconstpointer addr, int family)
131 #ifdef HAVE_INET_NTOP
132 char buffer[INET_ADDRSTRLEN];
134 inet_ntop (family, addr, buffer, sizeof (buffer));
135 return g_strdup (buffer);
137 return g_strdup (inet_ntoa (*(struct in_addr *)addr));
144 char buffer[INET6_ADDRSTRLEN];
146 inet_ntop (family, addr, buffer, sizeof (buffer));
147 return g_strdup (buffer);
157 static struct hostent *
158 soup_gethostbyname_internal (const char *hostname)
160 struct hostent result_buf, *result = &result_buf, *out;
163 #if defined(HAVE_GETHOSTBYNAME_R_GLIBC)
169 buf = g_new (char, len);
171 while ((res = gethostbyname_r (hostname,
178 buf = g_renew (char, buf, len);
181 if (res || result == NULL || result->h_addr_list [0] == NULL)
184 #elif defined(HAVE_GETHOSTBYNAME_R_SOLARIS)
190 buf = g_new (char, len);
192 while ((res = gethostbyname_r (hostname,
198 buf = g_renew (char, buf, len);
204 #elif defined(HAVE_GETHOSTBYNAME_R_HPUX)
206 struct hostent_data hdbuf;
208 if (!gethostbyname_r (hostname, &result_buf, &hdbuf))
213 result = gethostbyname (hostname);
218 out = copy_hostent (result);
228 static struct hostent *
229 soup_gethostbyaddr_internal (gconstpointer addr, int family)
231 struct hostent result_buf, *result = &result_buf, *out;
237 length = sizeof (struct in_addr);
241 length = sizeof (struct in6_addr);
248 #if defined(HAVE_GETHOSTBYNAME_R_GLIBC)
254 buf = g_new (char, len);
256 while ((res = gethostbyaddr_r (addr,
265 buf = g_renew (char, buf, len);
268 if (res || result == NULL || result->h_name == NULL)
271 #elif defined(HAVE_GETHOSTBYNAME_R_SOLARIS)
277 buf = g_new (char, len);
279 while ((res = gethostbyaddr_r (addr,
287 buf = g_renew (char, buf, len);
293 #elif defined(HAVE_GETHOSTBYNAME_R_HPUX)
295 struct hostent_data hdbuf;
297 if (!gethostbyaddr_r (addr, length, family, &result_buf, &hdbuf))
302 result = gethostbyaddr (addr, length, family);
307 out = copy_hostent (result);
320 struct SoupDNSEntry {
332 static GHashTable *soup_dns_entries;
334 #define SOUP_DNS_ENTRIES_MAX 20
336 static GStaticMutex soup_dns_mutex = G_STATIC_MUTEX_INIT;
337 #define soup_dns_lock() g_static_mutex_lock (&soup_dns_mutex)
338 #define soup_dns_unlock() g_static_mutex_unlock (&soup_dns_mutex)
341 soup_dns_entry_ref (SoupDNSEntry *entry)
347 soup_dns_entry_unref (SoupDNSEntry *entry)
349 if (!--entry->ref_count) {
350 g_free (entry->name);
351 soup_dns_free_hostent (entry->h);
355 if (entry->lookup_pid) {
356 kill (entry->lookup_pid, SIGKILL);
357 waitpid (entry->lookup_pid, NULL, 0);
365 uncache_entry (SoupDNSEntry *entry)
367 g_hash_table_remove (soup_dns_entries, entry->name);
368 soup_dns_entry_unref (entry);
372 prune_cache_cb (gpointer key, gpointer value, gpointer data)
374 SoupDNSEntry *entry = value, **prune_entry = data;
376 if (!*prune_entry || (*prune_entry)->expires > entry->expires)
377 *prune_entry = entry;
380 static SoupDNSEntry *
381 soup_dns_entry_new (const char *name)
385 entry = g_new0 (SoupDNSEntry, 1);
386 entry->name = g_strdup (name);
387 entry->ref_count = 2; /* One for the caller, one for the cache */
389 if (!soup_dns_entries) {
390 soup_dns_entries = g_hash_table_new (soup_str_case_hash,
391 soup_str_case_equal);
392 } else if (g_hash_table_size (soup_dns_entries) == SOUP_DNS_ENTRIES_MAX) {
393 SoupDNSEntry *prune_entry = NULL;
395 g_hash_table_foreach (soup_dns_entries, prune_cache_cb,
398 uncache_entry (prune_entry);
401 entry->expires = time (0) + 60 * 60;
402 g_hash_table_insert (soup_dns_entries, entry->name, entry);
407 static SoupDNSEntry *
408 soup_dns_lookup_entry (const char *name)
412 if (!soup_dns_entries)
415 entry = g_hash_table_lookup (soup_dns_entries, name);
417 soup_dns_entry_ref (entry);
422 * soup_dns_entry_from_name:
423 * @name: a nice name (eg, mofo.eecs.umich.edu) or a dotted decimal name
424 * (eg, 141.213.8.59).
426 * Begins asynchronous resolution of @name. The caller should
427 * periodically call soup_entry_check_lookup() to see if it is done,
428 * and call soup_entry_get_hostent() when soup_entry_check_lookup()
431 * Currently, this routine forks and does the lookup, which can cause
432 * some problems. In general, this will work ok for most programs most
433 * of the time. It will be slow or even fail when using operating
434 * systems that copy the entire process when forking.
436 * Returns: a #SoupDNSEntry, which will be freed when you call
437 * soup_entry_get_hostent() or soup_entry_cancel_lookup().
440 soup_dns_entry_from_name (const char *name)
448 entry = soup_dns_lookup_entry (name);
454 entry = soup_dns_entry_new (name);
456 /* Try to read the name as if it were dotted decimal */
457 entry->h = new_hostent_from_phys (name);
459 entry->resolved = TRUE;
464 /* Check to see if we are doing synchronous DNS lookups */
465 if (getenv ("SOUP_SYNC_DNS")) {
466 entry->h = soup_gethostbyname_internal (name);
467 entry->resolved = TRUE;
472 /* Ok, we need to start a new lookup */
474 if (pipe (pipes) == -1) {
475 entry->resolved = TRUE;
480 entry->lookup_pid = fork ();
481 switch (entry->lookup_pid) {
483 g_warning ("Fork error: %s (%d)\n", g_strerror (errno), errno);
487 entry->resolved = TRUE;
495 entry->h = soup_gethostbyname_internal (name);
497 write_hostent (entry->h, pipes[1]);
499 /* Close the socket */
502 /* Exit (we don't want atexit called, so do _exit instead) */
503 _exit (EXIT_SUCCESS);
509 entry->fd = pipes[0];
516 * soup_dns_entry_from_addr:
517 * @addr: pointer to address data (eg, an #in_addr_t)
518 * @family: address family of @addr
520 * Begins asynchronous resolution of @addr. The caller should
521 * periodically call soup_entry_check_lookup() to see if it is done,
522 * and call soup_entry_get_hostent() when soup_entry_check_lookup()
525 * Currently, this routine forks and does the lookup, which can cause
526 * some problems. In general, this will work ok for most programs most
527 * of the time. It will be slow or even fail when using operating
528 * systems that copy the entire process when forking.
530 * Returns: a #SoupDNSEntry, which will be freed when you call
531 * soup_entry_get_hostent() or soup_entry_cancel_lookup().
534 soup_dns_entry_from_addr (gconstpointer addr, int family)
540 name = soup_dns_ntop (addr, family);
541 g_return_val_if_fail (name != NULL, NULL);
546 entry = soup_dns_lookup_entry (name);
553 entry = soup_dns_entry_new (name);
555 /* Check to see if we are doing synchronous DNS lookups */
556 if (getenv ("SOUP_SYNC_DNS")) {
557 entry->h = soup_gethostbyaddr_internal (addr, family);
558 entry->resolved = TRUE;
563 if (pipe (pipes) != 0) {
564 entry->resolved = TRUE;
569 entry->lookup_pid = fork ();
570 switch (entry->lookup_pid) {
575 g_warning ("Fork error: %s (%d)\n", g_strerror(errno), errno);
576 entry->resolved = TRUE;
584 entry->h = soup_gethostbyaddr_internal (addr, family);
586 write_hostent (entry->h, pipes[1]);
588 /* Close the socket */
591 /* Exit (we don't want atexit called, so do _exit instead) */
592 _exit (EXIT_SUCCESS);
598 entry->fd = pipes[0];
605 check_hostent (SoupDNSEntry *entry, gboolean block)
607 char buf[256], *namelenp, *name, *typep, *addrlenp, *addr;
610 struct timeval tv = { 0, 0 };
614 if (entry->resolved || !entry->fd) {
620 FD_SET (entry->fd, &readfds);
621 if (select (entry->fd + 1, &readfds, NULL, NULL, &tv) != 0) {
626 nread = read (entry->fd, buf, sizeof (buf));
629 kill (entry->lookup_pid, SIGKILL);
630 waitpid (entry->lookup_pid, NULL, 0);
631 entry->lookup_pid = 0;
632 entry->resolved = TRUE;
641 typep = name + *namelenp;
642 addrlenp = typep + 1;
645 if (addrlenp < buf + nread && (addr + *addrlenp) == buf + nread)
646 entry->h = new_hostent (name, *typep, *addrlenp, addr);
651 soup_dns_entry_check_lookup (SoupDNSEntry *entry)
653 check_hostent (entry, FALSE);
654 return entry->resolved;
658 soup_dns_entry_get_hostent (SoupDNSEntry *entry)
662 check_hostent (entry, TRUE);
663 h = copy_hostent (entry->h);
664 soup_dns_entry_unref (entry);
670 soup_dns_entry_cancel_lookup (SoupDNSEntry *entry)
672 soup_dns_entry_unref (entry);