]> sjero.net Git - wget/blob - src/host.c
[svn] Declare h_errno in host.c, but not elsewhere.
[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[] = { tmpstore, NULL };
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       return address_list_new (lst);
242     }
243
244   /* By now we know that the host name we got is not of the form
245      d.d.d.d.  Try to find it in our cache of host names.  */
246   if (host_name_addresses_map)
247     al = hash_table_get (host_name_addresses_map, host);
248
249   if (al)
250     {
251       DEBUGP (("Found %s in host_name_addresses_map (%p)\n", host, al));
252       ++al->refcount;
253       return al;
254     }
255
256   if (!silent)
257     logprintf (LOG_VERBOSE, _("Resolving %s... "), host);
258
259   /* Look up the host using gethostbyname().  */
260   hptr = gethostbyname (host);
261   if (!hptr)
262     {
263       if (!silent)
264         logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno));
265       return NULL;
266     }
267
268   if (!silent)
269     logprintf (LOG_VERBOSE, _("done.\n"));
270
271   al = address_list_new (hptr->h_addr_list);
272
273   /* Cache the lookup information. */
274   cache_host_lookup (host, al);
275
276   return al;
277 }
278 \f
279 /* Determine whether a URL is acceptable to be followed, according to
280    a list of domains to accept.  */
281 int
282 accept_domain (struct url *u)
283 {
284   assert (u->host != NULL);
285   if (opt.domains)
286     {
287       if (!sufmatch ((const char **)opt.domains, u->host))
288         return 0;
289     }
290   if (opt.exclude_domains)
291     {
292       if (sufmatch ((const char **)opt.exclude_domains, u->host))
293         return 0;
294     }
295   return 1;
296 }
297
298 /* Check whether WHAT is matched in LIST, each element of LIST being a
299    pattern to match WHAT against, using backward matching (see
300    match_backwards() in utils.c).
301
302    If an element of LIST matched, 1 is returned, 0 otherwise.  */
303 int
304 sufmatch (const char **list, const char *what)
305 {
306   int i, j, k, lw;
307
308   lw = strlen (what);
309   for (i = 0; list[i]; i++)
310     {
311       for (j = strlen (list[i]), k = lw; j >= 0 && k >= 0; j--, k--)
312         if (TOLOWER (list[i][j]) != TOLOWER (what[k]))
313           break;
314       /* The domain must be first to reach to beginning.  */
315       if (j == -1)
316         return 1;
317     }
318   return 0;
319 }
320
321 /* Print error messages for host errors.  */
322 char *
323 herrmsg (int error)
324 {
325   /* Can't use switch since some constants are equal (at least on my
326      system), and the compiler signals "duplicate case value".  */
327   if (error == HOST_NOT_FOUND
328       || error == NO_RECOVERY
329       || error == NO_DATA
330       || error == NO_ADDRESS
331       || error == TRY_AGAIN)
332     return _("Host not found");
333   else
334     return _("Unknown error");
335 }
336
337 static int
338 host_cleanup_mapper (void *key, void *value, void *arg_ignored)
339 {
340   struct address_list *al;
341
342   xfree (key);                  /* host */
343
344   al = (struct address_list *)value;
345   assert (al->refcount == 1);
346   address_list_delete (al);
347
348   return 0;
349 }
350
351 void
352 host_cleanup (void)
353 {
354   if (host_name_addresses_map)
355     {
356       hash_table_map (host_name_addresses_map, host_cleanup_mapper, NULL);
357       hash_table_destroy (host_name_addresses_map);
358       host_name_addresses_map = NULL;
359     }
360 }