]> sjero.net Git - wget/blob - src/host.c
[svn] Updated IPv6 code.
[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,
118                        ip_address *ip_store)
119 {
120   assert (index >= al->faulty && index < al->count);
121   memcpy (ip_store, al->addresses + index, sizeof (ip_address));
122 }
123
124 /* Check whether two address lists have all their IPs in common.  */
125
126 int
127 address_list_match_all (const struct address_list *al1,
128                         const struct address_list *al2)
129 {
130 #ifdef ENABLE_IPV6
131   int i;
132 #endif
133   if (al1 == al2)
134     return 1;
135   if (al1->count != al2->count)
136     return 0;
137
138   /* For the comparison to be complete, we'd need to sort the IP
139      addresses first.  But that's not necessary because this is only
140      used as an optimization.  */
141
142 #ifndef ENABLE_IPV6
143   /* In the non-IPv6 case, there is only one address type, so we can
144      compare the whole array with memcmp.  */
145   return 0 == memcmp (al1->addresses, al2->addresses,
146                       al1->count * sizeof (ip_address));
147 #else  /* ENABLE_IPV6 */
148   for (i = 0; i < al1->count; ++i) 
149     {
150       const ip_address *ip1 = &al1->addresses[i];
151       const ip_address *ip2 = &al2->addresses[i];
152
153       if (ip1->type != ip2->type)
154         return 0;
155
156       switch (ip1->type)
157         {
158         case IPV4_ADDRESS:
159           if (ADDRESS_IPV4_IN_ADDR (ip1).s_addr
160               != ADDRESS_IPV4_IN_ADDR (ip2).s_addr)
161             return 0;
162           break;
163         case IPV6_ADDRESS:
164 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
165           if (ADDRESS_IPV6_SCOPE (ip1) != ADDRESS_IPV6_SCOPE (ip2))
166             return 0;
167 #endif /* HAVE_SOCKADDR_IN6_SCOPE_ID */
168           if (!IN6_ARE_ADDR_EQUAL (&ADDRESS_IPV6_IN6_ADDR (ip1),
169                                    &ADDRESS_IPV6_IN6_ADDR (ip2)))
170             return 0;
171           break;
172         default:
173           abort ();
174         }
175     }
176   return 1;
177 #endif /* ENABLE_IPV6 */
178 }
179
180 /* Mark the INDEXth element of AL as faulty, so that the next time
181    this address list is used, the faulty element will be skipped.  */
182
183 void
184 address_list_set_faulty (struct address_list *al, int index)
185 {
186   /* We assume that the address list is traversed in order, so that a
187      "faulty" attempt is always preceded with all-faulty addresses,
188      and this is how Wget uses it.  */
189   assert (index == al->faulty);
190
191   ++al->faulty;
192   if (al->faulty >= al->count)
193     /* All addresses have been proven faulty.  Since there's not much
194        sense in returning the user an empty address list the next
195        time, we'll rather make them all clean, so that they can be
196        retried anew.  */
197     al->faulty = 0;
198 }
199
200 #ifdef ENABLE_IPV6
201 /**
202   * address_list_from_addrinfo
203   *
204   * This function transform an addrinfo links list in and address_list.
205   *
206   * Input:
207   * addrinfo*           Linkt list of addrinfo
208   *
209   * Output:
210   * address_list*       New allocated address_list
211   */
212 static struct address_list *
213 address_list_from_addrinfo (const struct addrinfo *ai)
214 {
215   struct address_list *al;
216   const struct addrinfo *ptr;
217   int cnt;
218   ip_address *ip;
219
220   cnt = 0;
221   for (ptr = ai; ptr != NULL ; ptr = ptr->ai_next)
222     if (ptr->ai_family == AF_INET || ptr->ai_family == AF_INET6)
223       ++cnt;
224   if (cnt == 0)
225     return NULL;
226
227   al = xmalloc (sizeof (struct address_list));
228   al->addresses = xmalloc (cnt * sizeof (ip_address));
229   al->count     = cnt;
230   al->faulty    = 0;
231   al->refcount  = 1;
232
233   ip = al->addresses;
234   for (ptr = ai; ptr != NULL; ptr = ptr->ai_next)
235     if (ptr->ai_family == AF_INET6) 
236       {
237         const struct sockaddr_in6 *sin6 =
238           (const struct sockaddr_in6 *)ptr->ai_addr;
239         ip->type = IPV6_ADDRESS;
240         ADDRESS_IPV6_IN6_ADDR (ip) = sin6->sin6_addr;
241 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
242         ADDRESS_IPV6_SCOPE (ip) = sin6->sin6_scope_id;
243 #endif
244         ++ip;
245       } 
246     else if (ptr->ai_family == AF_INET)
247       {
248         const struct sockaddr_in *sin =
249           (const struct sockaddr_in *)ptr->ai_addr;
250         ip->type = IPV4_ADDRESS;
251         ADDRESS_IPV4_IN_ADDR (ip) = sin->sin_addr;
252         ++ip;
253       }
254   assert (ip - al->addresses == cnt);
255   return al;
256 }
257 #else
258 /* Create an address_list out of a NULL-terminated vector of
259    addresses, as returned by gethostbyname.  */
260 static struct address_list *
261 address_list_from_vector (char **h_addr_list)
262 {
263   int count, i;
264   struct address_list *al = xmalloc (sizeof (struct address_list));
265
266   count = 0;
267   while (h_addr_list[count])
268     ++count;
269   assert (count > 0);
270
271   al->count     = count;
272   al->faulty    = 0;
273   al->addresses = xmalloc (count * sizeof (ip_address));
274   al->refcount  = 1;
275
276   for (i = 0; i < count; i++)
277     {
278       ip_address *ip = &al->addresses[i];
279       ip->type = IPV4_ADDRESS;
280       memcpy (ADDRESS_IPV4_DATA (ip), h_addr_list[i], 4);
281     }
282
283   return al;
284 }
285
286 /* Like address_list_from_vector, but initialized with a single
287    address. */
288
289 static struct address_list *
290 address_list_from_single (const ip_address *addr)
291 {
292   struct address_list *al = xmalloc (sizeof (struct address_list));
293   al->count     = 1;
294   al->faulty    = 0;
295   al->addresses = xmalloc (sizeof (ip_address));
296   al->refcount  = 1;
297   memcpy (al->addresses, addr, sizeof (ip_address));
298
299   return al;
300 }
301 #endif
302
303 static void
304 address_list_delete (struct address_list *al)
305 {
306   xfree (al->addresses);
307   xfree (al);
308 }
309
310 void
311 address_list_release (struct address_list *al)
312 {
313   --al->refcount;
314   DEBUGP (("Releasing %p (new refcount %d).\n", al, al->refcount));
315   if (al->refcount <= 0)
316     {
317       DEBUGP (("Deleting unused %p.\n", al));
318       address_list_delete (al);
319     }
320 }
321 \f
322 /**
323   * sockaddr_set_address
324   *
325   * This function takes a sockaddr struct and fills in the protocol type,
326   * the port number and the address. If ENABLE_IPV6 is defined, the sa
327   * parameter should point to a sockaddr_storage structure; if not, it 
328   * should point to a sockaddr_in structure.
329   * If the address parameter is NULL, the function will use the unspecified 
330   * address (0.0.0.0 for IPv4 and :: for IPv6). 
331   * Unsupported address family will abort the whole programm.
332   *
333   * Input:
334   * struct sockaddr*    The space to be filled
335   * unsigned short      The port
336   * const ip_address    The IP address
337   *
338   * Return:
339   * -                   Only modifies 1st parameter.
340   */
341 void
342 sockaddr_set_address (struct sockaddr *sa, unsigned short port, 
343                       const ip_address *addr)
344 {
345   if (addr->type == IPV4_ADDRESS)
346     {
347       struct sockaddr_in *sin = (struct sockaddr_in *)sa;
348
349       sin->sin_family = AF_INET;
350       sin->sin_port = htons (port);
351       if (addr == NULL)
352         sin->sin_addr.s_addr = INADDR_ANY;
353       else
354         sin->sin_addr = ADDRESS_IPV4_IN_ADDR (addr);
355     }
356 #ifdef ENABLE_IPV6
357   else if (addr->type == IPV6_ADDRESS) 
358     {
359       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
360
361       sin6->sin6_family = AF_INET6;
362       sin6->sin6_port = htons (port);
363       /* #### How can ADDR be NULL?  We have dereferenced it above by
364          accessing addr->type!  */
365       if (addr == NULL)
366         {
367           sin6->sin6_addr = in6addr_any;
368           /* #### Should we set the scope_id here? */
369         }
370       else
371         {
372           sin6->sin6_addr = ADDRESS_IPV6_IN6_ADDR (addr);
373 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
374           sin6->sin6_scope_id = ADDRESS_IPV6_SCOPE (addr);
375 #endif
376         }
377     }
378 #endif /* ENABLE_IPV6 */
379   else
380     abort ();
381 }
382
383 void
384 sockaddr_get_address (const struct sockaddr *sa, unsigned short *port, 
385                       ip_address *addr)
386 {
387   if (sa->sa_family == AF_INET)
388     {
389       struct sockaddr_in *sin = (struct sockaddr_in *)sa;
390
391       addr->type = IPV4_ADDRESS;
392       ADDRESS_IPV4_IN_ADDR (addr) = sin->sin_addr;
393       if (port != NULL) 
394         *port = ntohs (sin->sin_port);
395     }
396 #ifdef ENABLE_IPV6
397   else if (sa->sa_family == AF_INET6) 
398     {
399       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
400
401       addr->type = IPV6_ADDRESS;
402       ADDRESS_IPV6_IN6_ADDR (addr) = sin6->sin6_addr;
403 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
404       ADDRESS_IPV6_SCOPE (addr) = sin6->sin6_scope_id;
405 #endif  
406       if (port != NULL) 
407         *port = ntohs (sin6->sin6_port);
408     }
409 #endif  
410   else
411     abort ();
412 }
413
414 #if 0                           /* currently unused */
415 /**
416   * sockaddr_set_port
417   *
418   * This funtion only fill the port of the socket information.
419   * If the protocol is not supported nothing is done.
420   * Unsuported adress family will abort the whole programm.
421   * 
422   * Require:
423   * that the IP-Protocol already is set.
424   *
425   * Input:
426   * wget_sockaddr*      The space there port should be entered
427   * unsigned int        The port that should be entered in host order
428   *
429   * Return:
430   * -                   Only modify 1. param
431   */
432 void 
433 sockaddr_set_port (struct sockaddr *sa, unsigned short port)
434 {
435   if (sa->sa_family == AF_INET)
436     {
437       struct sockaddr_in *sin = (struct sockaddr_in *)sa;
438       sin->sin_port = htons (port);
439     }  
440 #ifdef ENABLE_IPV6
441   else if (sa->sa_family == AF_INET6)
442     {
443       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
444       sin6->sin6_port = htons (port);
445     }
446 #endif
447   else
448     abort ();
449 }
450 #endif
451
452 /**
453   * sockaddr_get_port
454   *
455   * This function only return the port from the input structure
456   * Unsuported adress family will abort the whole programm.
457   * 
458   * Require:
459   * that the IP-Protocol already is set.
460   *
461   * Input:
462   * wget_sockaddr*      Information where to get the port
463   *
464   * Output:
465   * unsigned short      Port Number in host order.
466   */
467 unsigned short 
468 sockaddr_get_port (const struct sockaddr *sa)
469 {
470   if (sa->sa_family == AF_INET) {
471       const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
472       return htons (sin->sin_port);
473 #ifdef ENABLE_IPV6
474   } else if (sa->sa_family == AF_INET6) {
475       const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
476       return htons (sin6->sin6_port);
477 #endif
478   } else
479     abort ();
480   /* do not complain about return nothing */
481   return -1;
482 }
483
484 /**
485   * sockaddr_len
486   *
487   * This function return the length of the sockaddr corresponding to 
488   * the acutall prefered protocol for (bind, connect etc...)
489   * Unsuported adress family will abort the whole programm.
490   * 
491   * Require:
492   * that the IP-Protocol already is set.
493   *
494   * Input:
495   * -           Public IP-Family Information
496   *
497   * Output:
498   * int         structure length for socket options
499   */
500 socklen_t
501 sockaddr_len (const struct sockaddr *sa)
502 {
503   if (sa->sa_family == AF_INET)
504     {
505       return sizeof (struct sockaddr_in);
506     }  
507 #ifdef ENABLE_IPV6
508   else if (sa->sa_family == AF_INET6)
509     {
510       return sizeof (struct sockaddr_in6);
511     }
512 #endif
513   else
514     abort ();
515   /* do not complain about return nothing */
516   return 0;
517 }
518 \f
519 /* Versions of gethostbyname and getaddrinfo that support timeout. */
520
521 #ifndef ENABLE_IPV6
522
523 struct ghbnwt_context {
524   const char *host_name;
525   struct hostent *hptr;
526 };
527
528 static void
529 gethostbyname_with_timeout_callback (void *arg)
530 {
531   struct ghbnwt_context *ctx = (struct ghbnwt_context *)arg;
532   ctx->hptr = gethostbyname (ctx->host_name);
533 }
534
535 /* Just like gethostbyname, except it times out after TIMEOUT seconds.
536    In case of timeout, NULL is returned and errno is set to ETIMEDOUT.
537    The function makes sure that when NULL is returned for reasons
538    other than timeout, errno is reset.  */
539
540 static struct hostent *
541 gethostbyname_with_timeout (const char *host_name, double timeout)
542 {
543   struct ghbnwt_context ctx;
544   ctx.host_name = host_name;
545   if (run_with_timeout (timeout, gethostbyname_with_timeout_callback, &ctx))
546     {
547       SET_H_ERRNO (HOST_NOT_FOUND);
548       errno = ETIMEDOUT;
549       return NULL;
550     }
551   if (!ctx.hptr)
552     errno = 0;
553   return ctx.hptr;
554 }
555
556 #else  /* ENABLE_IPV6 */
557
558 struct gaiwt_context {
559   const char *node;
560   const char *service;
561   const struct addrinfo *hints;
562   struct addrinfo **res;
563   int exit_code;
564 };
565
566 static void
567 getaddrinfo_with_timeout_callback (void *arg)
568 {
569   struct gaiwt_context *ctx = (struct gaiwt_context *)arg;
570   ctx->exit_code = getaddrinfo (ctx->node, ctx->service, ctx->hints, ctx->res);
571 }
572
573 /* Just like getaddrinfo, except it times out after TIMEOUT seconds.
574    In case of timeout, the EAI_SYSTEM error code is returned and errno
575    is set to ETIMEDOUT.  */
576
577 static int
578 getaddrinfo_with_timeout (const char *node, const char *service,
579                           const struct addrinfo *hints, struct addrinfo **res,
580                           double timeout)
581 {
582   struct gaiwt_context ctx;
583   ctx.node = node;
584   ctx.service = service;
585   ctx.hints = hints;
586   ctx.res = res;
587
588   if (run_with_timeout (timeout, getaddrinfo_with_timeout_callback, &ctx))
589     {
590       errno = ETIMEDOUT;
591       return EAI_SYSTEM;
592     }
593   return ctx.exit_code;
594 }
595
596 #endif /* ENABLE_IPV6 */
597 \f
598 /* Pretty-print ADDR.  When compiled without IPv6, this is the same as
599    inet_ntoa.  With IPv6, it either prints an IPv6 address or an IPv4
600    address.  */
601
602 const char *
603 pretty_print_address (const ip_address *addr)
604 {
605   switch (addr->type) 
606     {
607     case IPV4_ADDRESS:
608       return inet_ntoa (ADDRESS_IPV4_IN_ADDR (addr));
609 #ifdef ENABLE_IPV6
610     case IPV6_ADDRESS:
611       {
612         static char buf[128];
613         inet_ntop (AF_INET6, &ADDRESS_IPV6_IN6_ADDR (addr), buf, sizeof (buf));
614 #if 0
615 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
616         {
617           /* append "%SCOPE_ID" for all ?non-global? addresses */
618           char *p = buf + strlen (buf);
619           *p++ = '%';
620           number_to_string (p, ADDRESS_IPV6_SCOPE (addr));
621         }
622 #endif
623 #endif
624         buf[sizeof (buf) - 1] = '\0';
625         return buf;
626       }
627 #endif
628     }
629   abort ();
630   return NULL;
631 }
632
633 /* Add host name HOST with the address ADDR_TEXT to the cache.
634    ADDR_LIST is a NULL-terminated list of addresses, as in struct
635    hostent.  */
636
637 static void
638 cache_host_lookup (const char *host, struct address_list *al)
639 {
640   if (!host_name_addresses_map)
641     host_name_addresses_map = make_nocase_string_hash_table (0);
642
643   ++al->refcount;
644   hash_table_put (host_name_addresses_map, xstrdup_lower (host), al);
645
646 #ifdef ENABLE_DEBUG
647   if (opt.debug)
648     {
649       int i;
650       debug_logprintf ("Caching %s =>", host);
651       for (i = 0; i < al->count; i++)
652         debug_logprintf (" %s", pretty_print_address (al->addresses + i));
653       debug_logprintf ("\n");
654     }
655 #endif
656 }
657
658 struct address_list *
659 lookup_host (const char *host, int flags)
660 {
661   struct address_list *al = NULL;
662
663 #ifdef ENABLE_IPV6
664   int err, family;
665   struct addrinfo hints, *res;
666
667   /* Is this necessary?  Should this function be changed to accept a
668      FAMILY argument?  */
669   if (flags & LH_IPV4_ONLY)
670     family = AF_INET;
671   else if (flags & LH_IPV6_ONLY)
672     family = AF_INET6;
673   else
674     family = ip_default_family;
675 #endif
676           
677   /* First, try to check whether the address is already a numeric
678      address, in which case we don't need to cache it or bother with
679      setting up timeouts.  Plus, if memory serves me right, Ultrix's
680      gethostbyname can't handle numeric addresses (!).
681
682      Where getaddrinfo is available, we do it using the AI_NUMERICHOST
683      flag.  Without IPv6, we use inet_addr succeeds.  */
684
685 #ifdef ENABLE_IPV6
686   memset (&hints, 0, sizeof (hints));
687   hints.ai_family   = family;
688   hints.ai_socktype = SOCK_STREAM;
689   hints.ai_flags    = AI_NUMERICHOST;
690   if (flags & LH_PASSIVE)
691     hints.ai_flags = AI_PASSIVE;
692
693   /* no need to call getaddrinfo_with_timeout here, as we're not
694    * relying on the DNS, but we're only doing an address translation
695    * from presentation (ASCII) to network format */
696   err = getaddrinfo (host, NULL, &hints, &res);
697   if (err == 0 && res != NULL)
698     {
699       al = address_list_from_addrinfo (res);
700       freeaddrinfo (res);
701       return al;
702     }
703 #else
704   {
705     uint32_t addr_ipv4 = (uint32_t)inet_addr (host);
706     if (addr_ipv4 != (uint32_t) -1)
707       {
708         /* The return value of inet_addr is in network byte order, so
709            we can just copy it to IP.  */
710         ip_address ip;
711         ip.type = IPV4_ADDRESS;
712         memcpy (ADDRESS_IPV4_DATA (&ip), &addr_ipv4, 4);
713         return address_list_from_single (&ip);
714       }
715   }
716 #endif
717
718   /* Then, try to find the host in the cache. */
719
720   if (host_name_addresses_map)
721     {
722       al = hash_table_get (host_name_addresses_map, host);
723       if (al)
724         {
725           DEBUGP (("Found %s in host_name_addresses_map (%p)\n", host, al));
726           ++al->refcount;
727           return al;
728         }
729     }
730
731   if (!(flags & LH_SILENT))
732     logprintf (LOG_VERBOSE, _("Resolving %s... "), host);
733
734   /* Host name lookup goes on below. */
735
736 #ifdef ENABLE_IPV6
737   {
738     memset (&hints, 0, sizeof (hints));
739     hints.ai_family   = family;
740     hints.ai_socktype = SOCK_STREAM;
741     if (flags & LH_PASSIVE) 
742       hints.ai_flags = AI_PASSIVE;
743
744     err = getaddrinfo_with_timeout (host, NULL, &hints, &res, opt.dns_timeout);
745
746     if (err != 0 || res == NULL)
747       {
748         if (!(flags & LH_SILENT))
749           logprintf (LOG_VERBOSE, _("failed: %s.\n"),
750                      err != EAI_SYSTEM ? gai_strerror (err) : strerror (errno));
751         return NULL;
752       }
753     al = address_list_from_addrinfo (res);
754     freeaddrinfo (res);
755   }
756 #else
757   {
758     struct hostent *hptr = gethostbyname_with_timeout (host, opt.dns_timeout);
759     if (!hptr)
760       {
761         if (!(flags & LH_SILENT))
762           {
763             if (errno != ETIMEDOUT)
764               logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno));
765             else
766               logputs (LOG_VERBOSE, _("failed: timed out.\n"));
767           }
768         return NULL;
769       }
770     assert (hptr->h_length == 4);
771     /* Do all systems have h_addr_list, or is it a newer thing?  If
772        the latter, use address_list_from_single.  */
773     al = address_list_from_vector (hptr->h_addr_list);
774   }
775 #endif
776
777   /* Print the addresses determined by DNS lookup, but no more than
778      three.  */
779   if (!(flags & LH_SILENT))
780     {
781       int i;
782       int printmax = al->count <= 3 ? al->count : 3;
783       for (i = 0; i < printmax; i++)
784         {
785           logprintf (LOG_VERBOSE, "%s",
786                      pretty_print_address (al->addresses + i));
787           if (i < printmax - 1)
788             logputs (LOG_VERBOSE, ", ");
789         }
790       if (printmax != al->count)
791         logputs (LOG_VERBOSE, ", ...");
792       logputs (LOG_VERBOSE, "\n");
793     }
794
795   /* Cache the lookup information. */
796   if (opt.dns_cache)
797     cache_host_lookup (host, al);
798
799   return al;
800 }
801 \f
802 /* Determine whether a URL is acceptable to be followed, according to
803    a list of domains to accept.  */
804 int
805 accept_domain (struct url *u)
806 {
807   assert (u->host != NULL);
808   if (opt.domains)
809     {
810       if (!sufmatch ((const char **)opt.domains, u->host))
811         return 0;
812     }
813   if (opt.exclude_domains)
814     {
815       if (sufmatch ((const char **)opt.exclude_domains, u->host))
816         return 0;
817     }
818   return 1;
819 }
820
821 /* Check whether WHAT is matched in LIST, each element of LIST being a
822    pattern to match WHAT against, using backward matching (see
823    match_backwards() in utils.c).
824
825    If an element of LIST matched, 1 is returned, 0 otherwise.  */
826 int
827 sufmatch (const char **list, const char *what)
828 {
829   int i, j, k, lw;
830
831   lw = strlen (what);
832   for (i = 0; list[i]; i++)
833     {
834       for (j = strlen (list[i]), k = lw; j >= 0 && k >= 0; j--, k--)
835         if (TOLOWER (list[i][j]) != TOLOWER (what[k]))
836           break;
837       /* The domain must be first to reach to beginning.  */
838       if (j == -1)
839         return 1;
840     }
841   return 0;
842 }
843
844 /* Print error messages for host errors.  */
845 char *
846 herrmsg (int error)
847 {
848   /* Can't use switch since some constants are equal (at least on my
849      system), and the compiler signals "duplicate case value".  */
850   if (error == HOST_NOT_FOUND
851       || error == NO_RECOVERY
852       || error == NO_DATA
853       || error == NO_ADDRESS
854       || error == TRY_AGAIN)
855     return _("Host not found");
856   else
857     return _("Unknown error");
858 }
859
860 static int
861 host_cleanup_mapper (void *key, void *value, void *arg_ignored)
862 {
863   struct address_list *al;
864
865   xfree (key);                  /* host */
866
867   al = (struct address_list *)value;
868   assert (al->refcount == 1);
869   address_list_delete (al);
870
871   return 0;
872 }
873
874 void
875 host_cleanup (void)
876 {
877   if (host_name_addresses_map)
878     {
879       hash_table_map (host_name_addresses_map, host_cleanup_mapper, NULL);
880       hash_table_destroy (host_name_addresses_map);
881       host_name_addresses_map = NULL;
882     }
883 }