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