]> sjero.net Git - wget/blob - src/host.c
[svn] Commit several minor changes:
[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 Wget.
5
6 This program 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 This program 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 this program; 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 # include <arpa/inet.h>
38 # include <netdb.h>
39 #endif /* WINDOWS */
40
41 #ifdef HAVE_SYS_UTSNAME_H
42 # include <sys/utsname.h>
43 #endif
44 #include <errno.h>
45
46 #include "wget.h"
47 #include "utils.h"
48 #include "host.h"
49 #include "url.h"
50 #include "hash.h"
51
52 #ifndef errno
53 extern int errno;
54 #endif
55
56 /* Mapping between all known hosts to their addresses (n.n.n.n). */
57 struct hash_table *host_name_address_map;
58
59 /* Mapping between all known addresses (n.n.n.n) to their hosts.  This
60    is the inverse of host_name_address_map.  These two tables share
61    the strdup'ed strings. */
62 struct hash_table *host_address_name_map;
63
64 /* Mapping between auxilliary (slave) and master host names. */
65 struct hash_table *host_slave_master_map;
66
67 /* Utility function: like xstrdup(), but also lowercases S.  */
68
69 static char *
70 xstrdup_lower (const char *s)
71 {
72   char *copy = xstrdup (s);
73   char *p = copy;
74   for (; *p; p++)
75     *p = TOLOWER (*p);
76   return copy;
77 }
78
79 /* The same as gethostbyname, but supports internet addresses of the
80    form `N.N.N.N'.  On some systems gethostbyname() knows how to do
81    this automatically.  */
82 struct hostent *
83 ngethostbyname (const char *name)
84 {
85   struct hostent *hp;
86   unsigned long addr;
87
88   addr = (unsigned long)inet_addr (name);
89   if ((int)addr != -1)
90     hp = gethostbyaddr ((char *)&addr, sizeof (addr), AF_INET);
91   else
92     hp = gethostbyname (name);
93   return hp;
94 }
95
96 /* Add host name HOST with the address ADDR_TEXT to the cache.
97    Normally this means that the (HOST, ADDR_TEXT) pair will be to
98    host_name_address_map and to host_address_name_map.  (It is the
99    caller's responsibility to make sure that HOST is not already in
100    host_name_address_map.)
101
102    If the ADDR_TEXT has already been seen and belongs to another host,
103    HOST will be added to host_slave_master_map instead.  */
104
105 static void
106 add_host_to_cache (const char *host, const char *addr_text)
107 {
108   char *canonical_name = hash_table_get (host_address_name_map, addr_text);
109   if (canonical_name)
110     {
111       DEBUGP (("Mapping %s to %s in host_slave_master_map.\n",
112                host, canonical_name));
113       /* We've already dealt with that host under another name. */
114       hash_table_put (host_slave_master_map,
115                       xstrdup_lower (host),
116                       xstrdup_lower (canonical_name));
117     }
118   else
119     {
120       /* This is really the first time we're dealing with that host.  */
121       char *h_copy = xstrdup_lower (host);
122       char *a_copy = xstrdup (addr_text);
123       DEBUGP (("Caching %s <-> %s\n", h_copy, a_copy));
124       hash_table_put (host_name_address_map, h_copy, a_copy);
125       hash_table_put (host_address_name_map, a_copy, h_copy);
126     }
127 }
128
129 /* Store the address of HOSTNAME, internet-style (four octets in
130    network order), to WHERE.  First try to get the address from the
131    cache; if it is not available, call the DNS functions and update
132    the cache.
133
134    Return 1 on successful finding of the hostname, 0 otherwise.  */
135 int
136 store_hostaddress (unsigned char *where, const char *hostname)
137 {
138   unsigned long addr;
139   char *addr_text;
140   char *canonical_name;
141   struct hostent *hptr;
142   struct in_addr in;
143   char *inet_s;
144
145   /* If the address is of the form d.d.d.d, there will be no trouble
146      with it.  */
147   addr = (unsigned long)inet_addr (hostname);
148   /* If we have the numeric address, just store it.  */
149   if ((int)addr != -1)
150     {
151       /* ADDR is defined to be in network byte order, meaning the code
152          works on little and big endian 32-bit architectures without
153          change.  On big endian 64-bit architectures we need to be
154          careful to copy the correct four bytes.  */
155       int offset;
156     have_addr:
157 #ifdef WORDS_BIGENDIAN
158       offset = sizeof (unsigned long) - 4;
159 #else
160       offset = 0;
161 #endif
162       memcpy (where, (char *)&addr + offset, 4);
163       return 1;
164     }
165
166   /* By now we know that the address is not of the form d.d.d.d.  Try
167      to find it in our cache of host addresses.  */
168   addr_text = hash_table_get (host_name_address_map, hostname);
169   if (addr_text)
170     {
171       DEBUGP (("Found %s in host_name_address_map: %s\n",
172                hostname, addr_text));
173       addr = (unsigned long)inet_addr (addr_text);
174       goto have_addr;
175     }
176
177   /* Maybe this host is known to us under another name.  If so, we'll
178      find it in host_slave_master_map, and use the master name to find
179      its address in host_name_address_map. */
180   canonical_name = hash_table_get (host_slave_master_map, hostname);
181   if (canonical_name)
182     {
183       addr_text = hash_table_get (host_name_address_map, canonical_name);
184       assert (addr_text != NULL);
185       DEBUGP (("Found %s as slave of %s -> %s\n",
186                hostname, canonical_name, addr_text));
187       addr = (unsigned long)inet_addr (addr_text);
188       goto have_addr;
189     }
190
191   /* Since all else has failed, let's try gethostbyname().  Note that
192      we use gethostbyname() rather than ngethostbyname(), because we
193      already know that the address is not numerical.  */
194   hptr = gethostbyname (hostname);
195   if (!hptr)
196     return 0;
197   /* Copy the address of the host to socket description.  */
198   memcpy (where, hptr->h_addr_list[0], hptr->h_length);
199   assert (hptr->h_length == 4);
200
201   /* Now that we've gone through the truoble of calling
202      gethostbyname(), we can store this valuable information to the
203      cache.  First, we have to look for it by address to know if it's
204      already in the cache by another name.  */
205   /* Originally, we copied to in.s_addr, but it appears to be missing
206      on some systems.  */
207   memcpy (&in, *hptr->h_addr_list, sizeof (in));
208   inet_s = inet_ntoa (in);
209   add_host_to_cache (hostname, inet_s);
210   return 1;
211 }
212
213 /* Determine the "real" name of HOST, as perceived by Wget.  If HOST
214    is referenced by more than one name, "real" name is considered to
215    be the first one encountered in the past.  */
216 char *
217 realhost (const char *host)
218 {
219   struct in_addr in;
220   struct hostent *hptr;
221   char *master_name;
222
223   DEBUGP (("Checking for %s in host_name_address_map.\n", host));
224   if (hash_table_contains (host_name_address_map, host))
225     {
226       DEBUGP (("Found; %s was already used, by that name.\n", host));
227       return xstrdup_lower (host);
228     }
229
230   DEBUGP (("Checking for %s in host_slave_master_map.\n", host));
231   master_name = hash_table_get (host_slave_master_map, host);
232   if (master_name)
233     {
234     has_master:
235       DEBUGP (("Found; %s was already used, by the name %s.\n",
236                host, master_name));
237       return xstrdup (master_name);
238     }
239
240   DEBUGP (("First time I hear about %s by that name; looking it up.\n",
241            host));
242   hptr = ngethostbyname (host);
243   if (hptr)
244     {
245       char *inet_s;
246       /* Originally, we copied to in.s_addr, but it appears to be
247          missing on some systems.  */
248       memcpy (&in, *hptr->h_addr_list, sizeof (in));
249       inet_s = inet_ntoa (in);
250
251       add_host_to_cache (host, inet_s);
252
253       /* add_host_to_cache() can establish a slave-master mapping. */
254       DEBUGP (("Checking again for %s in host_slave_master_map.\n", host));
255       master_name = hash_table_get (host_slave_master_map, host);
256       if (master_name)
257         goto has_master;
258     }
259
260   return xstrdup_lower (host);
261 }
262
263 /* Compare two hostnames (out of URL-s if the arguments are URL-s),
264    taking care of aliases.  It uses realhost() to determine a unique
265    hostname for each of two hosts.  If simple_check is non-zero, only
266    strcmp() is used for comparison.  */
267 int
268 same_host (const char *u1, const char *u2)
269 {
270   const char *s;
271   char *p1, *p2;
272   char *real1, *real2;
273
274   /* Skip protocol, if present.  */
275   u1 += skip_proto (u1);
276   u2 += skip_proto (u2);
277
278   /* Skip username ans password, if present.  */
279   u1 += skip_uname (u1);
280   u2 += skip_uname (u2);
281
282   for (s = u1; *u1 && *u1 != '/' && *u1 != ':'; u1++);
283   p1 = strdupdelim (s, u1);
284   for (s = u2; *u2 && *u2 != '/' && *u2 != ':'; u2++);
285   p2 = strdupdelim (s, u2);
286   DEBUGP (("Comparing hosts %s and %s...\n", p1, p2));
287   if (strcasecmp (p1, p2) == 0)
288     {
289       xfree (p1);
290       xfree (p2);
291       DEBUGP (("They are quite alike.\n"));
292       return 1;
293     }
294   else if (opt.simple_check)
295     {
296       xfree (p1);
297       xfree (p2);
298       DEBUGP (("Since checking is simple, I'd say they are not the same.\n"));
299       return 0;
300     }
301   real1 = realhost (p1);
302   real2 = realhost (p2);
303   xfree (p1);
304   xfree (p2);
305   if (strcasecmp (real1, real2) == 0)
306     {
307       DEBUGP (("They are alike, after realhost()->%s.\n", real1));
308       xfree (real1);
309       xfree (real2);
310       return 1;
311     }
312   else
313     {
314       DEBUGP (("They are not the same (%s, %s).\n", real1, real2));
315       xfree (real1);
316       xfree (real2);
317       return 0;
318     }
319 }
320
321 /* Determine whether a URL is acceptable to be followed, according to
322    a list of domains to accept.  */
323 int
324 accept_domain (struct urlinfo *u)
325 {
326   assert (u->host != NULL);
327   if (opt.domains)
328     {
329       if (!sufmatch ((const char **)opt.domains, u->host))
330         return 0;
331     }
332   if (opt.exclude_domains)
333     {
334       if (sufmatch ((const char **)opt.exclude_domains, u->host))
335         return 0;
336     }
337   return 1;
338 }
339
340 /* Check whether WHAT is matched in LIST, each element of LIST being a
341    pattern to match WHAT against, using backward matching (see
342    match_backwards() in utils.c).
343
344    If an element of LIST matched, 1 is returned, 0 otherwise.  */
345 int
346 sufmatch (const char **list, const char *what)
347 {
348   int i, j, k, lw;
349
350   lw = strlen (what);
351   for (i = 0; list[i]; i++)
352     {
353       for (j = strlen (list[i]), k = lw; j >= 0 && k >= 0; j--, k--)
354         if (TOLOWER (list[i][j]) != TOLOWER (what[k]))
355           break;
356       /* The domain must be first to reach to beginning.  */
357       if (j == -1)
358         return 1;
359     }
360   return 0;
361 }
362
363 /* Print error messages for host errors.  */
364 char *
365 herrmsg (int error)
366 {
367   /* Can't use switch since some constants are equal (at least on my
368      system), and the compiler signals "duplicate case value".  */
369   if (error == HOST_NOT_FOUND
370       || error == NO_RECOVERY
371       || error == NO_DATA
372       || error == NO_ADDRESS
373       || error == TRY_AGAIN)
374     return _("Host not found");
375   else
376     return _("Unknown error");
377 }
378
379 void
380 clean_hosts (void)
381 {
382   /* host_name_address_map and host_address_name_map share the
383      strings.  Because of that, calling free_keys_and_values once
384      suffices for both.  */
385   free_keys_and_values (host_name_address_map);
386   hash_table_destroy (host_name_address_map);
387   hash_table_destroy (host_address_name_map);
388   free_keys_and_values (host_slave_master_map);
389   hash_table_destroy (host_slave_master_map);
390 }
391
392 void
393 host_init (void)
394 {
395   host_name_address_map = make_string_hash_table (0);
396   host_address_name_map = make_string_hash_table (0);
397   host_slave_master_map = make_string_hash_table (0);
398 }