]> sjero.net Git - wget/blob - src/host.c
ddc04445e0fe2d17909769a33b280a552802889b
[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 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #ifdef HAVE_STRING_H
25 # include <string.h>
26 #else
27 # include <strings.h>
28 #endif
29 #include <assert.h>
30 #include <sys/types.h>
31
32 #ifdef WINDOWS
33 # include <winsock.h>
34 #else
35 # include <sys/socket.h>
36 # include <netinet/in.h>
37 # ifndef __BEOS__
38 #  include <arpa/inet.h>
39 # endif
40 # include <netdb.h>
41 #endif /* WINDOWS */
42
43 #ifndef NO_ADDRESS
44 #define NO_ADDRESS NO_DATA
45 #endif
46
47 #ifdef HAVE_SYS_UTSNAME_H
48 # include <sys/utsname.h>
49 #endif
50 #include <errno.h>
51
52 #include "wget.h"
53 #include "utils.h"
54 #include "host.h"
55 #include "url.h"
56 #include "hash.h"
57
58 #ifndef errno
59 extern int errno;
60 #endif
61
62 #ifndef h_errno
63 # ifndef __CYGWIN__
64 extern int h_errno;
65 # endif
66 #endif
67
68 /* An IPv4 address is simply a 4-byte quantity. */
69 typedef unsigned char ipv4_address[4];
70
71 /* Mapping between known hosts and to lists of their addresses. */
72
73 static struct hash_table *host_name_addresses_map;
74 \f
75 /* Lists of addresses.  This should eventually be extended to handle
76    IPv6.  */
77
78 struct address_list {
79   int count;                    /* number of adrresses */
80   ipv4_address *addresses;      /* pointer to the string of addresses */
81
82   int faulty;                   /* number of addresses known not to
83                                    work. */
84   int refcount;                 /* so we know whether to free it or
85                                    not. */
86 };
87
88 /* Get the bounds of the address list.  */
89
90 void
91 address_list_get_bounds (struct address_list *al, int *start, int *end)
92 {
93   *start = al->faulty;
94   *end   = al->count;
95 }
96
97 /* Copy address number INDEX to IP_STORE.  */
98
99 void
100 address_list_copy_one (struct address_list *al, int index,
101                        unsigned char *ip_store)
102 {
103   assert (index >= al->faulty && index < al->count);
104   memcpy (ip_store, al->addresses + index, sizeof (ipv4_address));
105 }
106
107 /* Check whether two address lists have all their IPs in common.  */
108
109 int
110 address_list_match_all (struct address_list *al1, struct address_list *al2)
111 {
112   if (al1 == al2)
113     return 1;
114   if (al1->count != al2->count)
115     return 0;
116   return 0 == memcmp (al1->addresses, al2->addresses,
117                       al1->count * sizeof (ipv4_address));
118 }
119
120 /* Mark the INDEXth element of AL as faulty, so that the next time
121    this address list is used, the faulty element will be skipped.  */
122
123 void
124 address_list_set_faulty (struct address_list *al, int index)
125 {
126   /* We assume that the address list is traversed in order, so that a
127      "faulty" attempt is always preceded with all-faulty addresses,
128      and this is how Wget uses it.  */
129   assert (index == al->faulty);
130
131   ++al->faulty;
132   if (al->faulty >= al->count)
133     /* All addresses have been proven faulty.  Since there's not much
134        sense in returning the user an empty address list the next
135        time, we'll rather make them all clean, so that they can be
136        retried anew.  */
137     al->faulty = 0;
138 }
139
140 /* Create an address_list out of a NULL-terminated list of addresses,
141    as returned by gethostbyname.  */
142
143 static struct address_list *
144 address_list_new (char **h_addr_list)
145 {
146   int count = 0, i;
147
148   struct address_list *al = xmalloc (sizeof (struct address_list));
149
150   while (h_addr_list[count])
151     ++count;
152   assert (count > 0);
153   al->count     = count;
154   al->faulty    = 0;
155   al->addresses = xmalloc (count * sizeof (ipv4_address));
156   al->refcount  = 1;
157
158   for (i = 0; i < count; i++)
159     memcpy (al->addresses + i, h_addr_list[i], sizeof (ipv4_address));
160
161   return al;
162 }
163
164 /* Like address_list_new, but initialized with only one address. */
165
166 static struct address_list *
167 address_list_new_one (const char *addr)
168 {
169   struct address_list *al = xmalloc (sizeof (struct address_list));
170   al->count     = 1;
171   al->faulty    = 0;
172   al->addresses = xmalloc (sizeof (ipv4_address));
173   al->refcount  = 1;
174   memcpy (al->addresses, addr, sizeof (ipv4_address));
175
176   return al;
177 }
178
179 static void
180 address_list_delete (struct address_list *al)
181 {
182   xfree (al->addresses);
183   xfree (al);
184 }
185
186 void
187 address_list_release (struct address_list *al)
188 {
189   --al->refcount;
190   DEBUGP (("Releasing %p (new refcount %d).\n", al, al->refcount));
191   if (al->refcount <= 0)
192     {
193       DEBUGP (("Deleting unused %p.\n", al));
194       address_list_delete (al);
195     }
196 }
197 \f
198 /* The same as inet_ntoa, but without the need for a cast, or for
199    #including the netinet stuff.  */
200
201 char *
202 pretty_print_address (const void *addr)
203 {
204   return inet_ntoa (*(struct in_addr *)addr);
205 }
206
207 /* Add host name HOST with the address ADDR_TEXT to the cache.
208    ADDR_LIST is a NULL-terminated list of addresses, as in struct
209    hostent.  */
210
211 static void
212 cache_host_lookup (const char *host, struct address_list *al)
213 {
214   if (!host_name_addresses_map)
215     host_name_addresses_map = make_nocase_string_hash_table (0);
216
217   ++al->refcount;
218   hash_table_put (host_name_addresses_map, xstrdup_lower (host), al);
219
220 #ifdef DEBUG
221   if (opt.debug)
222     {
223       int i;
224       debug_logprintf ("Caching %s =>", host);
225       for (i = 0; i < al->count; i++)
226         debug_logprintf (" %s", pretty_print_address (al->addresses + i));
227       debug_logprintf ("\n");
228     }
229 #endif
230 }
231
232 struct address_list *
233 lookup_host (const char *host, int silent)
234 {
235   struct address_list *al = NULL;
236   unsigned long addr;
237   struct hostent *hptr;
238
239   /* If the address is of the form d.d.d.d, no further lookup is
240      needed.  */
241   addr = (unsigned long)inet_addr (host);
242   if ((int)addr != -1)
243     {
244       /* ADDR is defined to be in network byte order, which is what
245          this returns, so we can just copy it to STORE_IP.  However,
246          on big endian 64-bit architectures the value will be stored
247          in the *last*, not first four bytes.  OFFSET makes sure that
248          we copy the correct four bytes.  */
249       int offset;
250 #ifdef WORDS_BIGENDIAN
251       offset = sizeof (unsigned long) - sizeof (ipv4_address);
252 #else
253       offset = 0;
254 #endif
255       return address_list_new_one ((char *)&addr + offset);
256     }
257
258   /* By now we know that the host name we got is not of the form
259      d.d.d.d.  Try to find it in our cache of host names.  */
260   if (host_name_addresses_map)
261     al = hash_table_get (host_name_addresses_map, host);
262
263   if (al)
264     {
265       DEBUGP (("Found %s in host_name_addresses_map (%p)\n", host, al));
266       ++al->refcount;
267       return al;
268     }
269
270   if (!silent)
271     logprintf (LOG_VERBOSE, _("Resolving %s... "), host);
272
273   /* Look up the host using gethostbyname().  */
274   hptr = gethostbyname (host);
275   if (!hptr)
276     {
277       if (!silent)
278         logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno));
279       return NULL;
280     }
281
282   if (!silent)
283     logprintf (LOG_VERBOSE, _("done.\n"));
284
285   /* Do all systems have h_addr_list, or is it a newer thing?  If the
286      latter, use address_list_new_one.  */
287   al = address_list_new (hptr->h_addr_list);
288
289   /* Cache the lookup information. */
290   cache_host_lookup (host, al);
291
292   return al;
293 }
294 \f
295 /* Determine whether a URL is acceptable to be followed, according to
296    a list of domains to accept.  */
297 int
298 accept_domain (struct url *u)
299 {
300   assert (u->host != NULL);
301   if (opt.domains)
302     {
303       if (!sufmatch ((const char **)opt.domains, u->host))
304         return 0;
305     }
306   if (opt.exclude_domains)
307     {
308       if (sufmatch ((const char **)opt.exclude_domains, u->host))
309         return 0;
310     }
311   return 1;
312 }
313
314 /* Check whether WHAT is matched in LIST, each element of LIST being a
315    pattern to match WHAT against, using backward matching (see
316    match_backwards() in utils.c).
317
318    If an element of LIST matched, 1 is returned, 0 otherwise.  */
319 int
320 sufmatch (const char **list, const char *what)
321 {
322   int i, j, k, lw;
323
324   lw = strlen (what);
325   for (i = 0; list[i]; i++)
326     {
327       for (j = strlen (list[i]), k = lw; j >= 0 && k >= 0; j--, k--)
328         if (TOLOWER (list[i][j]) != TOLOWER (what[k]))
329           break;
330       /* The domain must be first to reach to beginning.  */
331       if (j == -1)
332         return 1;
333     }
334   return 0;
335 }
336
337 /* Print error messages for host errors.  */
338 char *
339 herrmsg (int error)
340 {
341   /* Can't use switch since some constants are equal (at least on my
342      system), and the compiler signals "duplicate case value".  */
343   if (error == HOST_NOT_FOUND
344       || error == NO_RECOVERY
345       || error == NO_DATA
346       || error == NO_ADDRESS
347       || error == TRY_AGAIN)
348     return _("Host not found");
349   else
350     return _("Unknown error");
351 }
352
353 static int
354 host_cleanup_mapper (void *key, void *value, void *arg_ignored)
355 {
356   struct address_list *al;
357
358   xfree (key);                  /* host */
359
360   al = (struct address_list *)value;
361   assert (al->refcount == 1);
362   address_list_delete (al);
363
364   return 0;
365 }
366
367 void
368 host_cleanup (void)
369 {
370   if (host_name_addresses_map)
371     {
372       hash_table_map (host_name_addresses_map, host_cleanup_mapper, NULL);
373       hash_table_destroy (host_name_addresses_map);
374       host_name_addresses_map = NULL;
375     }
376 }