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