]> sjero.net Git - wget/blob - src/host.c
[svn] Don't reuse IP addresses proven to be faulty, unless only such ones remain.
[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->buffer   = xmalloc (count * IP4_ADDRESS_LENGTH);
145   al->refcount = 1;
146
147   for (i = 0; i < count; i++)
148     memcpy (ADDR_LOCATION (al, i), h_addr_list[i], IP4_ADDRESS_LENGTH);
149
150   return al;
151 }
152
153 static void
154 address_list_delete (struct address_list *al)
155 {
156   xfree (al->buffer);
157   xfree (al);
158 }
159
160 void
161 address_list_release (struct address_list *al)
162 {
163   --al->refcount;
164   DEBUGP (("Releasing %p (new refcount %d).\n", al, al->refcount));
165   if (al->refcount <= 0)
166     {
167       DEBUGP (("Deleting unused %p.\n", al));
168       address_list_delete (al);
169     }
170 }
171 \f
172 /* The same as inet_ntoa, but without the need for a cast, or for
173    #including the netinet stuff.  */
174
175 char *
176 pretty_print_address (const unsigned char *addr)
177 {
178   return inet_ntoa (*(struct in_addr *)addr);
179 }
180
181 /* Add host name HOST with the address ADDR_TEXT to the cache.
182    ADDR_LIST is a NULL-terminated list of addresses, as in struct
183    hostent.  */
184
185 static void
186 cache_host_lookup (const char *host, struct address_list *al)
187 {
188   if (!host_name_addresses_map)
189     host_name_addresses_map = make_nocase_string_hash_table (0);
190
191   ++al->refcount;
192   hash_table_put (host_name_addresses_map, xstrdup_lower (host), al);
193
194 #ifdef DEBUG
195   if (opt.debug)
196     {
197       int i;
198       debug_logprintf ("Caching %s =>", host);
199       for (i = 0; i < al->count; i++)
200         debug_logprintf (" %s",
201                          pretty_print_address (ADDR_LOCATION (al, i)));
202       debug_logprintf ("\n");
203     }
204 #endif
205 }
206
207 struct address_list *
208 lookup_host (const char *host, int silent)
209 {
210   struct address_list *al = NULL;
211   unsigned long addr;
212   struct hostent *hptr;
213
214   /* If the address is of the form d.d.d.d, no further lookup is
215      needed.  */
216   addr = (unsigned long)inet_addr (host);
217   if ((int)addr != -1)
218     {
219       unsigned char tmpstore[IP4_ADDRESS_LENGTH];
220       char *lst[] = { tmpstore, NULL };
221
222       /* ADDR is defined to be in network byte order, which is what
223          this returns, so we can just copy it to STORE_IP.  However,
224          on big endian 64-bit architectures the value will be stored
225          in the *last*, not first four bytes.  OFFSET makes sure that
226          we copy the correct four bytes.  */
227       int offset;
228 #ifdef WORDS_BIGENDIAN
229       offset = sizeof (unsigned long) - IP4_ADDRESS_LENGTH;
230 #else
231       offset = 0;
232 #endif
233       memcpy (tmpstore, (char *)&addr + offset, IP4_ADDRESS_LENGTH);
234       return address_list_new (lst);
235     }
236
237   /* By now we know that the host name we got is not of the form
238      d.d.d.d.  Try to find it in our cache of host names.  */
239   if (host_name_addresses_map)
240     al = hash_table_get (host_name_addresses_map, host);
241
242   if (al)
243     {
244       DEBUGP (("Found %s in host_name_addresses_map (%p)\n", host, al));
245       ++al->refcount;
246       return al;
247     }
248
249   if (!silent)
250     logprintf (LOG_VERBOSE, _("Resolving %s... "), host);
251
252   /* Look up the host using gethostbyname().  Note that we use
253      gethostbyname() rather than ngethostbyname(), because we already
254      know that the address is not numerical.  */
255   hptr = gethostbyname (host);
256   if (!hptr)
257     {
258       if (!silent)
259         logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno));
260       return NULL;
261     }
262
263   if (!silent)
264     logprintf (LOG_VERBOSE, _("done.\n"));
265
266   al = address_list_new (hptr->h_addr_list);
267
268   /* Cache the lookup information. */
269   cache_host_lookup (host, al);
270
271   return al;
272 }
273 \f
274 /* Determine whether a URL is acceptable to be followed, according to
275    a list of domains to accept.  */
276 int
277 accept_domain (struct url *u)
278 {
279   assert (u->host != NULL);
280   if (opt.domains)
281     {
282       if (!sufmatch ((const char **)opt.domains, u->host))
283         return 0;
284     }
285   if (opt.exclude_domains)
286     {
287       if (sufmatch ((const char **)opt.exclude_domains, u->host))
288         return 0;
289     }
290   return 1;
291 }
292
293 /* Check whether WHAT is matched in LIST, each element of LIST being a
294    pattern to match WHAT against, using backward matching (see
295    match_backwards() in utils.c).
296
297    If an element of LIST matched, 1 is returned, 0 otherwise.  */
298 int
299 sufmatch (const char **list, const char *what)
300 {
301   int i, j, k, lw;
302
303   lw = strlen (what);
304   for (i = 0; list[i]; i++)
305     {
306       for (j = strlen (list[i]), k = lw; j >= 0 && k >= 0; j--, k--)
307         if (TOLOWER (list[i][j]) != TOLOWER (what[k]))
308           break;
309       /* The domain must be first to reach to beginning.  */
310       if (j == -1)
311         return 1;
312     }
313   return 0;
314 }
315
316 /* Print error messages for host errors.  */
317 char *
318 herrmsg (int error)
319 {
320   /* Can't use switch since some constants are equal (at least on my
321      system), and the compiler signals "duplicate case value".  */
322   if (error == HOST_NOT_FOUND
323       || error == NO_RECOVERY
324       || error == NO_DATA
325       || error == NO_ADDRESS
326       || error == TRY_AGAIN)
327     return _("Host not found");
328   else
329     return _("Unknown error");
330 }
331
332 static int
333 host_cleanup_mapper (void *key, void *value, void *arg_ignored)
334 {
335   struct address_list *al;
336
337   xfree (key);                  /* host */
338
339   al = (struct address_list *)value;
340   assert (al->refcount == 1);
341   address_list_delete (al);
342
343   return 0;
344 }
345
346 void
347 host_cleanup (void)
348 {
349   if (host_name_addresses_map)
350     {
351       hash_table_map (host_name_addresses_map, host_cleanup_mapper, NULL);
352       hash_table_destroy (host_name_addresses_map);
353       host_name_addresses_map = NULL;
354     }
355 }