]> sjero.net Git - wget/blob - src/host.c
[svn] Imported Mauro's IPv6 changes.
[wget] / src / host.c
1 /* Host name resolution and matching.
2    Copyright (C) 1995, 1996, 1997, 2000, 2001 Free Software Foundation, Inc.
3
4 This file is part of GNU Wget.
5
6 GNU Wget is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 GNU Wget is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Wget; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 In addition, as a special exception, the Free Software Foundation
21 gives permission to link the code of its release of Wget with the
22 OpenSSL project's "OpenSSL" library (or with modified versions of it
23 that use the same license as the "OpenSSL" library), and distribute
24 the linked executables.  You must obey the GNU General Public License
25 in all respects for all of the code used other than "OpenSSL".  If you
26 modify this file, you may extend this exception to your version of the
27 file, but you are not obligated to do so.  If you do not wish to do
28 so, delete this exception statement from your version.  */
29
30 #include <config.h>
31
32 #ifndef WINDOWS
33 #include <netdb.h>
34 #endif
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #ifdef HAVE_STRING_H
39 # include <string.h>
40 #else
41 # include <strings.h>
42 #endif
43 #include <assert.h>
44 #include <sys/types.h>
45
46 #ifdef WINDOWS
47 # include <winsock.h>
48 # define SET_H_ERRNO(err) WSASetLastError (err)
49 #else
50 # include <sys/socket.h>
51 # include <netinet/in.h>
52 # ifndef __BEOS__
53 #  include <arpa/inet.h>
54 # endif
55 # include <netdb.h>
56 # define SET_H_ERRNO(err) ((void)(h_errno = (err)))
57 #endif /* WINDOWS */
58
59 #ifndef NO_ADDRESS
60 #define NO_ADDRESS NO_DATA
61 #endif
62
63 #ifdef HAVE_SYS_UTSNAME_H
64 # include <sys/utsname.h>
65 #endif
66 #include <errno.h>
67
68 #include "wget.h"
69 #include "utils.h"
70 #include "host.h"
71 #include "url.h"
72 #include "hash.h"
73
74 #ifndef errno
75 extern int errno;
76 #endif
77
78 #ifndef h_errno
79 # ifndef __CYGWIN__
80 extern int h_errno;
81 # endif
82 #endif
83
84 #ifdef ENABLE_IPV6
85 int ip_default_family = AF_UNSPEC;
86 #else
87 int ip_default_family = AF_INET;
88 #endif
89
90 /* Mapping between known hosts and to lists of their addresses. */
91
92 static struct hash_table *host_name_addresses_map;
93 \f
94 /* Lists of addresses.  This should eventually be extended to handle
95    IPv6.  */
96
97 struct address_list {
98   int count;                    /* number of adrresses */
99   ip_address *addresses;        /* pointer to the string of addresses */
100
101   int faulty;                   /* number of addresses known not to work. */
102   int refcount;                 /* so we know whether to free it or not. */
103 };
104
105 /* Get the bounds of the address list.  */
106
107 void
108 address_list_get_bounds (const struct address_list *al, int *start, int *end)
109 {
110   *start = al->faulty;
111   *end   = al->count;
112 }
113
114 /* Copy address number INDEX to IP_STORE.  */
115
116 void
117 address_list_copy_one (const struct address_list *al, int index, ip_address *ip_store)
118 {
119   assert (index >= al->faulty && index < al->count);
120   memcpy (ip_store, al->addresses + index, sizeof (ip_address));
121 }
122
123 /* Check whether two address lists have all their IPs in common.  */
124
125 int
126 address_list_match_all (const struct address_list *al1, const struct address_list *al2)
127 {
128   int i;
129   if (al1 == al2)
130     return 1;
131   if (al1->count != al2->count)
132     return 0;
133   for (i = 0; i < al1->count; ++i) 
134     {
135 #ifdef ENABLE_IPv6
136       if (al1->addresses[i].type != al2->addresses[i].type) 
137         return 0;
138       if (al1->addresses[i].type == IPv6_ADDRESS)
139         {
140           const struct in6_addr *addr1 = &al1->addresses[i].addr.ipv6.addr;
141           const struct in6_addr *addr2 = &al2->addresses[i].addr.ipv6.addr;
142
143 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
144           if ((al1->addresses[i].address.scope_id
145                != al2->addresses[i].address.scope_id)
146               || !IN6_ARE_ADDR_EQUAL (addr1, addr2))
147 #else
148           if (!IN6_ARE_ADDR_EQUAL (addr1, addr2))
149 #endif
150             return 0;
151         }
152       else
153 #endif
154         {
155           const struct in_addr *addr1 = (const struct in_addr *)&al1->addresses[i].addr.ipv4.addr;
156           const struct in_addr *addr2 = (const struct in_addr *)&al2->addresses[i].addr.ipv4.addr;
157           
158           if (addr1->s_addr != addr2->s_addr)
159             return 0;
160         }
161     }
162   return 1;
163 }
164
165 /* Mark the INDEXth element of AL as faulty, so that the next time
166    this address list is used, the faulty element will be skipped.  */
167
168 void
169 address_list_set_faulty (struct address_list *al, int index)
170 {
171   /* We assume that the address list is traversed in order, so that a
172      "faulty" attempt is always preceded with all-faulty addresses,
173      and this is how Wget uses it.  */
174   assert (index == al->faulty);
175
176   ++al->faulty;
177   if (al->faulty >= al->count)
178     /* All addresses have been proven faulty.  Since there's not much
179        sense in returning the user an empty address list the next
180        time, we'll rather make them all clean, so that they can be
181        retried anew.  */
182     al->faulty = 0;
183 }
184
185 #ifdef ENABLE_IPV6
186 /**
187   * address_list_from_addrinfo
188   *
189   * This function transform an addrinfo links list in and address_list.
190   *
191   * Input:
192   * addrinfo*           Linkt list of addrinfo
193   *
194   * Output:
195   * address_list*       New allocated address_list
196   */
197 static struct address_list *
198 address_list_from_addrinfo (const struct addrinfo *ai)
199 {
200   struct address_list *al;
201   const struct addrinfo *ptr;
202   int cnt = 0;
203   int i;
204
205   for (ptr = ai; ptr != NULL ; ptr = ptr->ai_next)
206     if (ptr->ai_family == AF_INET || ptr->ai_family == AF_INET6)
207       ++cnt;
208   if (cnt == 0)
209     return NULL;
210
211   al = xmalloc (sizeof (struct address_list));
212   al->addresses = xmalloc (cnt * sizeof (ip_address));
213   al->count     = cnt;
214   al->faulty    = 0;
215   al->refcount  = 1;
216
217   for (i = 0, ptr = ai; ptr != NULL; ptr = ptr->ai_next)
218     if (ptr->ai_family == AF_INET6) 
219       {
220         const struct sockaddr_in6 *sin6 =
221           (const struct sockaddr_in6 *)ptr->ai_addr;
222         al->addresses[i].addr.ipv6.addr = sin6->sin6_addr;
223         al->addresses[i].type = IPv6_ADDRESS;
224 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
225         al->addresses[i].addr.ipv6.scope_id = sin6->sin6_scope_id;
226 #endif
227         ++i;
228       } 
229     else if (ptr->ai_family == AF_INET)
230       {
231         const struct sockaddr_in *sin =
232           (const struct sockaddr_in *)ptr->ai_addr;
233         al->addresses[i].addr.ipv4.addr = sin->sin_addr;
234         al->addresses[i].type = IPv4_ADDRESS;
235         ++i;
236       }
237   assert (i == cnt);
238   return al;
239 }
240 #else
241 /* Create an address_list out of a NULL-terminated vector of
242    addresses, as returned by gethostbyname.  */
243 static struct address_list *
244 address_list_from_vector (char **h_addr_list)
245 {
246   int count = 0, i;
247
248   struct address_list *al = xmalloc (sizeof (struct address_list));
249
250   while (h_addr_list[count])
251     ++count;
252   assert (count > 0);
253   al->count     = count;
254   al->faulty    = 0;
255   al->addresses = xmalloc (count * sizeof (ip_address));
256   al->refcount  = 1;
257
258   for (i = 0; i < count; i++) {
259     /* Mauro Tortonesi: is this safe? */
260     memcpy (&((al->addresses + i)->addr.ipv4.addr.s_addr), h_addr_list[i], 4);
261     (al->addresses + i)->type = IPv4_ADDRESS;
262   }
263
264   return al;
265 }
266
267 /* Like address_list_from_vector, but initialized with a single
268    address. */
269
270 static struct address_list *
271 address_list_from_single (const ip_address *addr)
272 {
273   struct address_list *al = xmalloc (sizeof (struct address_list));
274   al->count     = 1;
275   al->faulty    = 0;
276   al->addresses = xmalloc (sizeof (ip_address));
277   al->refcount  = 1;
278   memcpy (al->addresses, addr, sizeof (ip_address));
279
280   return al;
281 }
282 #endif
283
284 static void
285 address_list_delete (struct address_list *al)
286 {
287   xfree (al->addresses);
288   xfree (al);
289 }
290
291 void
292 address_list_release (struct address_list *al)
293 {
294   --al->refcount;
295   DEBUGP (("Releasing %p (new refcount %d).\n", al, al->refcount));
296   if (al->refcount <= 0)
297     {
298       DEBUGP (("Deleting unused %p.\n", al));
299       address_list_delete (al);
300     }
301 }
302 \f
303 /**
304   * sockaddr_set_address
305   *
306   * This function takes a sockaddr struct and fills in the protocol type,
307   * the port number and the address. If ENABLE_IPV6 is defined, the sa
308   * parameter should point to a sockaddr_storage structure; if not, it 
309   * should point to a sockaddr_in structure.
310   * If the address parameter is NULL, the function will use the unspecified 
311   * address (0.0.0.0 for IPv4 and :: for IPv6). 
312   * Unsupported address family will abort the whole programm.
313   *
314   * Input:
315   * struct sockaddr*    The space to be filled
316   * unsigned short      The port
317   * const ip_address    The IP address
318   *
319   * Return:
320   * -                   Only modifies 1st parameter.
321   */
322 void
323 sockaddr_set_address (struct sockaddr *sa, unsigned short port, 
324                       const ip_address *addr)
325 {
326   if (addr->type == IPv4_ADDRESS)
327     {
328       struct sockaddr_in *sin = (struct sockaddr_in *)sa;
329
330       sin->sin_family = AF_INET;
331       sin->sin_port = htons (port);
332       if (addr == NULL) 
333         sin->sin_addr.s_addr = INADDR_ANY;
334       else
335         sin->sin_addr = addr->addr.ipv4.addr;
336     }
337 #ifdef ENABLE_IPV6
338   else if (addr->type == IPv6_ADDRESS) 
339     {
340       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
341
342       sin6->sin6_family = AF_INET6;
343       sin6->sin6_port = htons (port);
344       if (addr == NULL) 
345         sin6->sin6_addr = in6addr_any;
346       else
347         sin6->sin6_addr = addr->addr.ipv6.addr;
348 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
349       sin6->sin6_scope_id = addr->addr.ipv6.scope_id;
350 #endif /* HAVE_SOCKADDR_IN6_SCOPE_ID */
351     }
352 #endif /* ENABLE_IPV6 */
353   else
354     abort ();
355 }
356
357 void
358 sockaddr_get_address (const struct sockaddr *sa, unsigned short *port, 
359                       ip_address *addr)
360 {
361   if (sa->sa_family == AF_INET)
362     {
363       struct sockaddr_in *sin = (struct sockaddr_in *)sa;
364
365       addr->type = IPv4_ADDRESS;
366       addr->addr.ipv4.addr = sin->sin_addr;
367       if (port != NULL) 
368         *port = ntohs (sin->sin_port);
369     }
370 #ifdef ENABLE_IPV6
371   else if (sa->sa_family == AF_INET6) 
372     {
373       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
374
375       addr->type = IPv6_ADDRESS;
376       addr->addr.ipv6.addr = sin6->sin6_addr;
377 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
378       addr->addr.ipv6.scope_id = sin6->sin6_scope_id;
379 #endif  
380       if (port != NULL) 
381         *port = ntohs (sin6->sin6_port);
382     }
383 #endif  
384   else
385     abort ();
386 }
387
388 #if 0                           /* currently unused */
389 /**
390   * sockaddr_set_port
391   *
392   * This funtion only fill the port of the socket information.
393   * If the protocol is not supported nothing is done.
394   * Unsuported adress family will abort the whole programm.
395   * 
396   * Require:
397   * that the IP-Protocol already is set.
398   *
399   * Input:
400   * wget_sockaddr*      The space there port should be entered
401   * unsigned int        The port that should be entered in host order
402   *
403   * Return:
404   * -                   Only modify 1. param
405   */
406 void 
407 sockaddr_set_port (struct sockaddr *sa, unsigned short port)
408 {
409   if (sa->sa_family == AF_INET)
410     {
411       struct sockaddr_in *sin = (struct sockaddr_in *)sa;
412       sin->sin_port = htons (port);
413     }  
414 #ifdef ENABLE_IPV6
415   else if (sa->sa_family == AF_INET6)
416     {
417       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
418       sin6->sin6_port = htons (port);
419     }
420 #endif
421   else
422     abort ();
423 }
424 #endif
425
426 /**
427   * sockaddr_get_port
428   *
429   * This function only return the port from the input structure
430   * Unsuported adress family will abort the whole programm.
431   * 
432   * Require:
433   * that the IP-Protocol already is set.
434   *
435   * Input:
436   * wget_sockaddr*      Information where to get the port
437   *
438   * Output:
439   * unsigned short      Port Number in host order.
440   */
441 unsigned short 
442 sockaddr_get_port (const struct sockaddr *sa)
443 {
444   if (sa->sa_family == AF_INET) {
445       const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
446       return htons (sin->sin_port);
447 #ifdef ENABLE_IPV6
448   } else if (sa->sa_family == AF_INET6) {
449       const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
450       return htons (sin6->sin6_port);
451 #endif
452   } else
453     abort ();
454   /* do not complain about return nothing */
455   return -1;
456 }
457
458 /**
459   * sockaddr_len
460   *
461   * This function return the length of the sockaddr corresponding to 
462   * the acutall prefered protocol for (bind, connect etc...)
463   * Unsuported adress family will abort the whole programm.
464   * 
465   * Require:
466   * that the IP-Protocol already is set.
467   *
468   * Input:
469   * -           Public IP-Family Information
470   *
471   * Output:
472   * int         structure length for socket options
473   */
474 socklen_t
475 sockaddr_len (const struct sockaddr *sa)
476 {
477   if (sa->sa_family == AF_INET)
478     {
479       return sizeof (struct sockaddr_in);
480     }  
481 #ifdef ENABLE_IPV6
482   else if (sa->sa_family == AF_INET6)
483     {
484       return sizeof (struct sockaddr_in6);
485     }
486 #endif
487   else
488     abort ();
489   /* do not complain about return nothing */
490   return 0;
491 }
492 \f
493 /* Versions of gethostbyname and getaddrinfo that support timeout. */
494
495 #ifndef ENABLE_IPV6
496
497 struct ghbnwt_context {
498   const char *host_name;
499   struct hostent *hptr;
500 };
501
502 static void
503 gethostbyname_with_timeout_callback (void *arg)
504 {
505   struct ghbnwt_context *ctx = (struct ghbnwt_context *)arg;
506   ctx->hptr = gethostbyname (ctx->host_name);
507 }
508
509 /* Just like gethostbyname, except it times out after TIMEOUT seconds.
510    In case of timeout, NULL is returned and errno is set to ETIMEDOUT.
511    The function makes sure that when NULL is returned for reasons
512    other than timeout, errno is reset.  */
513
514 static struct hostent *
515 gethostbyname_with_timeout (const char *host_name, double timeout)
516 {
517   struct ghbnwt_context ctx;
518   ctx.host_name = host_name;
519   if (run_with_timeout (timeout, gethostbyname_with_timeout_callback, &ctx))
520     {
521       SET_H_ERRNO (HOST_NOT_FOUND);
522       errno = ETIMEDOUT;
523       return NULL;
524     }
525   if (!ctx.hptr)
526     errno = 0;
527   return ctx.hptr;
528 }
529
530 #else  /* ENABLE_IPV6 */
531
532 struct gaiwt_context {
533   const char *node;
534   const char *service;
535   const struct addrinfo *hints;
536   struct addrinfo **res;
537   int exit_code;
538 };
539
540 static void
541 getaddrinfo_with_timeout_callback (void *arg)
542 {
543   struct gaiwt_context *ctx = (struct gaiwt_context *)arg;
544   ctx->exit_code = getaddrinfo (ctx->node, ctx->service, ctx->hints, ctx->res);
545 }
546
547 /* Just like getaddrinfo, except it times out after TIMEOUT seconds.
548    In case of timeout, the EAI_SYSTEM error code is returned and errno
549    is set to ETIMEDOUT.  */
550
551 static int
552 getaddrinfo_with_timeout (const char *node, const char *service,
553                           const struct addrinfo *hints, struct addrinfo **res,
554                           double timeout)
555 {
556   struct gaiwt_context ctx;
557   ctx.node = node;
558   ctx.service = service;
559   ctx.hints = hints;
560   ctx.res = res;
561
562   if (run_with_timeout (timeout, getaddrinfo_with_timeout_callback, &ctx))
563     {
564       errno = ETIMEDOUT;
565       return EAI_SYSTEM;
566     }
567   return ctx.exit_code;
568 }
569
570 #endif /* ENABLE_IPV6 */
571 \f
572 /* Pretty-print ADDR.  When compiled without IPv6, this is the same as
573    inet_ntoa.  With IPv6, it either prints an IPv6 address or an IPv4
574    address.  */
575
576 const char *
577 pretty_print_address (const ip_address *addr)
578 {
579   switch (addr->type) 
580     {
581     case IPv4_ADDRESS:
582       return inet_ntoa (addr->addr.ipv4.addr);
583 #ifdef ENABLE_IPV6
584     case IPv6_ADDRESS:
585       {
586         int len;
587         static char buf[128];
588         inet_ntop (AF_INET6, &addr->addr.ipv6.addr, buf, sizeof (buf));
589 #if 0
590 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
591         /* print also scope_id for all ?non-global? addresses */
592         snprintf (buf + len, sizeof (buf) - len, "%%%d", addr->addr.ipv6.scope_id);
593 #endif
594 #endif
595         len = strlen (buf);
596         buf[sizeof (buf) - 1] = '\0';
597         return buf;
598       }
599 #endif
600     }
601   abort ();
602   return NULL;
603 }
604
605 /* Add host name HOST with the address ADDR_TEXT to the cache.
606    ADDR_LIST is a NULL-terminated list of addresses, as in struct
607    hostent.  */
608
609 static void
610 cache_host_lookup (const char *host, struct address_list *al)
611 {
612   if (!host_name_addresses_map)
613     host_name_addresses_map = make_nocase_string_hash_table (0);
614
615   ++al->refcount;
616   hash_table_put (host_name_addresses_map, xstrdup_lower (host), al);
617
618 #ifdef ENABLE_DEBUG
619   if (opt.debug)
620     {
621       int i;
622       debug_logprintf ("Caching %s =>", host);
623       for (i = 0; i < al->count; i++)
624         debug_logprintf (" %s", pretty_print_address (al->addresses + i));
625       debug_logprintf ("\n");
626     }
627 #endif
628 }
629
630 struct address_list *
631 lookup_host (const char *host, int flags)
632 {
633   struct address_list *al = NULL;
634
635 #ifdef ENABLE_IPV6
636   int err, family;
637   struct addrinfo hints, *res;
638
639   /* This ip_default_family+flags business looks like bad design to
640      me.  This function should rather accept a FAMILY argument.  */
641   if (flags & LH_IPv4_ONLY)
642     family = AF_INET;
643   else if (flags & LH_IPv6_ONLY)
644     family = AF_INET6;
645   else
646     family = ip_default_family;
647 #endif
648           
649   /* First, try to check whether the address is already a numeric
650      address.  Where getaddrinfo is available, we do it using the
651      AI_NUMERICHOST flag.  Without IPv6, we check whether inet_addr
652      succeeds.  */
653
654 #ifdef ENABLE_IPV6
655   memset (&hints, 0, sizeof (hints));
656   hints.ai_family   = family;
657   hints.ai_socktype = SOCK_STREAM;
658   hints.ai_flags    = AI_NUMERICHOST;
659   if (flags & LH_PASSIVE)
660     hints.ai_flags = AI_PASSIVE;
661
662   /* no need to call getaddrinfo_with_timeout here, as we're not
663    * relying on the DNS, but we're only doing an address translation
664    * from presentation (ASCII) to network format */
665   err = getaddrinfo (host, NULL, &hints, &res);
666   if (err == 0 && res != NULL)
667     {
668       al = address_list_from_addrinfo (res);
669       freeaddrinfo (res);
670       return al;
671     }
672 #else
673   {
674     uint32_t addr_ipv4 = (uint32_t)inet_addr (host);
675     if (addr_ipv4 != (uint32_t) -1)
676       {
677         /* The return value of inet_addr is in network byte order, so
678            we can just copy it to ADDR.  */
679         ip_address addr;
680         /* This has a huge number of dereferences because C doesn't
681            support anonymous unions and because struct in_addr adds a
682            cast.  */
683         addr.addr.ipv4.addr.s_addr = addr_ipv4;
684         addr.type = IPv4_ADDRESS;
685         return address_list_from_single (&addr);
686       }
687   }
688 #endif
689
690   /* Then, try to find the host in the cache. */
691
692   if (host_name_addresses_map)
693     {
694       al = hash_table_get (host_name_addresses_map, host);
695       if (al)
696         {
697           DEBUGP (("Found %s in host_name_addresses_map (%p)\n", host, al));
698           ++al->refcount;
699           return al;
700         }
701     }
702
703   if (!(flags & LH_SILENT))
704     logprintf (LOG_VERBOSE, _("Resolving %s... "), host);
705
706   /* Host name lookup goes on below. */
707
708 #ifdef ENABLE_IPV6
709   {
710     memset (&hints, 0, sizeof (hints));
711     hints.ai_family   = family;
712     hints.ai_socktype = SOCK_STREAM;
713     if (flags & LH_PASSIVE) 
714       hints.ai_flags = AI_PASSIVE;
715
716     err = getaddrinfo_with_timeout (host, NULL, &hints, &res, opt.dns_timeout);
717
718     if (err != 0 || res == NULL)
719       {
720         if (!(flags & LH_SILENT))
721           logprintf (LOG_VERBOSE, _("failed: %s.\n"),
722                      err != EAI_SYSTEM ? gai_strerror (err) : strerror (errno));
723         return NULL;
724       }
725     al = address_list_from_addrinfo (res);
726     freeaddrinfo (res);
727   }
728 #else
729   {
730     struct hostent *hptr = gethostbyname_with_timeout (host, opt.dns_timeout);
731     if (!hptr)
732       {
733         if (!(flags & LH_SILENT))
734           {
735             if (errno != ETIMEDOUT)
736               logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno));
737             else
738               logputs (LOG_VERBOSE, _("failed: timed out.\n"));
739           }
740         return NULL;
741       }
742     assert (hptr->h_length == 4);
743     /* Do all systems have h_addr_list, or is it a newer thing?  If
744        the latter, use address_list_from_single.  */
745     al = address_list_from_vector (hptr->h_addr_list);
746   }
747 #endif
748
749   /* Print the addresses determined by DNS lookup, but no more than
750      three.  */
751   if (!(flags & LH_SILENT))
752     {
753       int i;
754       int printmax = al->count <= 3 ? al->count : 3;
755       for (i = 0; i < printmax; i++)
756         {
757           logprintf (LOG_VERBOSE, "%s",
758                      pretty_print_address (al->addresses + i));
759           if (i < printmax - 1)
760             logputs (LOG_VERBOSE, ", ");
761         }
762       if (printmax != al->count)
763         logputs (LOG_VERBOSE, ", ...");
764       logputs (LOG_VERBOSE, "\n");
765     }
766
767   /* Cache the lookup information. */
768   if (opt.dns_cache)
769     cache_host_lookup (host, al);
770
771   return al;
772 }
773 \f
774 /* Determine whether a URL is acceptable to be followed, according to
775    a list of domains to accept.  */
776 int
777 accept_domain (struct url *u)
778 {
779   assert (u->host != NULL);
780   if (opt.domains)
781     {
782       if (!sufmatch ((const char **)opt.domains, u->host))
783         return 0;
784     }
785   if (opt.exclude_domains)
786     {
787       if (sufmatch ((const char **)opt.exclude_domains, u->host))
788         return 0;
789     }
790   return 1;
791 }
792
793 /* Check whether WHAT is matched in LIST, each element of LIST being a
794    pattern to match WHAT against, using backward matching (see
795    match_backwards() in utils.c).
796
797    If an element of LIST matched, 1 is returned, 0 otherwise.  */
798 int
799 sufmatch (const char **list, const char *what)
800 {
801   int i, j, k, lw;
802
803   lw = strlen (what);
804   for (i = 0; list[i]; i++)
805     {
806       for (j = strlen (list[i]), k = lw; j >= 0 && k >= 0; j--, k--)
807         if (TOLOWER (list[i][j]) != TOLOWER (what[k]))
808           break;
809       /* The domain must be first to reach to beginning.  */
810       if (j == -1)
811         return 1;
812     }
813   return 0;
814 }
815
816 /* Print error messages for host errors.  */
817 char *
818 herrmsg (int error)
819 {
820   /* Can't use switch since some constants are equal (at least on my
821      system), and the compiler signals "duplicate case value".  */
822   if (error == HOST_NOT_FOUND
823       || error == NO_RECOVERY
824       || error == NO_DATA
825       || error == NO_ADDRESS
826       || error == TRY_AGAIN)
827     return _("Host not found");
828   else
829     return _("Unknown error");
830 }
831
832 static int
833 host_cleanup_mapper (void *key, void *value, void *arg_ignored)
834 {
835   struct address_list *al;
836
837   xfree (key);                  /* host */
838
839   al = (struct address_list *)value;
840   assert (al->refcount == 1);
841   address_list_delete (al);
842
843   return 0;
844 }
845
846 void
847 host_cleanup (void)
848 {
849   if (host_name_addresses_map)
850     {
851       hash_table_map (host_name_addresses_map, host_cleanup_mapper, NULL);
852       hash_table_destroy (host_name_addresses_map);
853       host_name_addresses_map = NULL;
854     }
855 }