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