]> sjero.net Git - wget/blob - src/host.c
[svn] Minor -Wall-induced fixes. Also, skip_url is removed.
[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_exists (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 /* Return email address of the form username@FQDN suitable for
364    anonymous FTP passwords.  This process is error-prone, and the
365    escape hatch is the MY_HOST preprocessor constant, which can be
366    used to hard-code either your hostname or FQDN at compile-time.
367
368    If the FQDN cannot be determined, a warning is printed, and the
369    function returns a short `username@' form, accepted by most
370    anonymous servers.
371
372    The returned string is generated by malloc() and should be freed
373    using free().
374
375    If not even the username cannot be divined, it means things are
376    seriously fucked up, and Wget exits.  */
377 char *
378 ftp_getaddress (void)
379 {
380   static char *address;
381
382   /* Do the drill only the first time, as it won't change.  */
383   if (!address)
384     {
385       char userid[32];          /* 9 should be enough for Unix, but
386                                    I'd rather be on the safe side.  */
387       char *host, *fqdn;
388
389       if (!pwd_cuserid (userid))
390         {
391           logprintf (LOG_ALWAYS, _("%s: Cannot determine user-id.\n"),
392                      exec_name);
393           exit (1);
394         }
395 #ifdef MY_HOST
396       STRDUP_ALLOCA (host, MY_HOST);
397 #else /* not MY_HOST */
398 #ifdef HAVE_UNAME
399       {
400         struct utsname ubuf;
401         if (uname (&ubuf) < 0)
402           {
403             logprintf (LOG_ALWAYS, _("%s: Warning: uname failed: %s\n"),
404                        exec_name, strerror (errno));
405             fqdn = "";
406             goto giveup;
407           }
408         STRDUP_ALLOCA (host, ubuf.nodename);
409       }
410 #else /* not HAVE_UNAME */
411 #ifdef HAVE_GETHOSTNAME
412       host = alloca (256);
413       if (gethostname (host, 256) < 0)
414         {
415           logprintf (LOG_ALWAYS, _("%s: Warning: gethostname failed\n"),
416                      exec_name);
417           fqdn = "";
418           goto giveup;
419         }
420 #else /* not HAVE_GETHOSTNAME */
421  #error Cannot determine host name.
422 #endif /* not HAVE_GETHOSTNAME */
423 #endif /* not HAVE_UNAME */
424 #endif /* not MY_HOST */
425       /* If the address we got so far contains a period, don't bother
426          anymore.  */
427       if (strchr (host, '.'))
428         fqdn = host;
429       else
430         {
431           /* #### I've seen the following scheme fail on at least one
432              system!  Do we care?  */
433           char *tmpstore;
434           /* According to Richard Stevens, the correct way to find the
435              FQDN is to (1) find the host name, (2) find its IP
436              address using gethostbyname(), and (3) get the FQDN using
437              gethostbyaddr().  So that's what we'll do.  Step one has
438              been done above.  */
439           /* (2) */
440           struct hostent *hp = gethostbyname (host);
441           if (!hp || !hp->h_addr_list)
442             {
443               logprintf (LOG_ALWAYS, _("\
444 %s: Warning: cannot determine local IP address.\n"),
445                          exec_name);
446               fqdn = "";
447               goto giveup;
448             }
449           /* Copy the argument, so the call to gethostbyaddr doesn't
450              clobber it -- just in case.  */
451           tmpstore = (char *)alloca (hp->h_length);
452           memcpy (tmpstore, *hp->h_addr_list, hp->h_length);
453           /* (3) */
454           hp = gethostbyaddr (tmpstore, hp->h_length, hp->h_addrtype);
455           if (!hp || !hp->h_name)
456             {
457               logprintf (LOG_ALWAYS, _("\
458 %s: Warning: cannot reverse-lookup local IP address.\n"),
459                          exec_name);
460               fqdn = "";
461               goto giveup;
462             }
463           if (!strchr (hp->h_name, '.'))
464             {
465 #if 0
466               /* This gets ticked pretty often.  Karl Berry reports
467                  that there can be valid reasons for the local host
468                  name not to be an FQDN, so I've decided to remove the
469                  annoying warning.  */
470               logprintf (LOG_ALWAYS, _("\
471 %s: Warning: reverse-lookup of local address did not yield FQDN!\n"),
472                        exec_name);
473 #endif
474               fqdn = "";
475               goto giveup;
476             }
477           /* Once we're here, hp->h_name contains the correct FQDN.  */
478           STRDUP_ALLOCA (fqdn, hp->h_name);
479         }
480     giveup:
481       address = (char *)xmalloc (strlen (userid) + 1 + strlen (fqdn) + 1);
482       sprintf (address, "%s@%s", userid, fqdn);
483     }
484   return address;
485 }
486
487 /* Print error messages for host errors.  */
488 char *
489 herrmsg (int error)
490 {
491   /* Can't use switch since some constants are equal (at least on my
492      system), and the compiler signals "duplicate case value".  */
493   if (error == HOST_NOT_FOUND
494       || error == NO_RECOVERY
495       || error == NO_DATA
496       || error == NO_ADDRESS
497       || error == TRY_AGAIN)
498     return _("Host not found");
499   else
500     return _("Unknown error");
501 }
502
503 void
504 clean_hosts (void)
505 {
506   /* host_name_address_map and host_address_name_map share the
507      strings.  Because of that, calling free_keys_and_values once
508      suffices for both.  */
509   free_keys_and_values (host_name_address_map);
510   hash_table_destroy (host_name_address_map);
511   hash_table_destroy (host_address_name_map);
512   free_keys_and_values (host_slave_master_map);
513   hash_table_destroy (host_slave_master_map);
514 }
515
516 void
517 host_init (void)
518 {
519   host_name_address_map = make_string_hash_table (0);
520   host_address_name_map = make_string_hash_table (0);
521   host_slave_master_map = make_string_hash_table (0);
522 }