]> sjero.net Git - wget/blob - src/host.c
[svn] Committed memory debugging stuff.
[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 #include <ctype.h>
25 #ifdef HAVE_STRING_H
26 # include <string.h>
27 #else
28 # include <strings.h>
29 #endif
30 #include <assert.h>
31 #include <sys/types.h>
32
33 #ifdef WINDOWS
34 # include <winsock.h>
35 #else
36 # include <sys/socket.h>
37 # include <netinet/in.h>
38 # include <arpa/inet.h>
39 # include <netdb.h>
40 #endif /* WINDOWS */
41
42 #ifdef HAVE_SYS_UTSNAME_H
43 # include <sys/utsname.h>
44 #endif
45 #include <errno.h>
46
47 #include "wget.h"
48 #include "utils.h"
49 #include "host.h"
50 #include "url.h"
51 #include "hash.h"
52
53 #ifndef errno
54 extern int errno;
55 #endif
56
57 /* Mapping between all known hosts to their addresses (n.n.n.n). */
58 struct hash_table *host_name_address_map;
59
60 /* Mapping between all known addresses (n.n.n.n) to their hosts.  This
61    is the inverse of host_name_address_map.  These two tables share
62    the strdup'ed strings. */
63 struct hash_table *host_address_name_map;
64
65 /* Mapping between auxilliary (slave) and master host names. */
66 struct hash_table *host_slave_master_map;
67
68 /* Utility function: like xstrdup(), but also lowercases S.  */
69
70 static char *
71 xstrdup_lower (const char *s)
72 {
73   char *copy = xstrdup (s);
74   char *p = copy;
75   for (; *p; p++)
76     *p = TOLOWER (*p);
77   return copy;
78 }
79
80 /* The same as gethostbyname, but supports internet addresses of the
81    form `N.N.N.N'.  On some systems gethostbyname() knows how to do
82    this automatically.  */
83 struct hostent *
84 ngethostbyname (const char *name)
85 {
86   struct hostent *hp;
87   unsigned long addr;
88
89   addr = (unsigned long)inet_addr (name);
90   if ((int)addr != -1)
91     hp = gethostbyaddr ((char *)&addr, sizeof (addr), AF_INET);
92   else
93     hp = gethostbyname (name);
94   return hp;
95 }
96
97 /* Add host name HOST with the address ADDR_TEXT to the cache.
98    Normally this means that the (HOST, ADDR_TEXT) pair will be to
99    host_name_address_map and to host_address_name_map.  (It is the
100    caller's responsibility to make sure that HOST is not already in
101    host_name_address_map.)
102
103    If the ADDR_TEXT has already been seen and belongs to another host,
104    HOST will be added to host_slave_master_map instead.  */
105
106 static void
107 add_host_to_cache (const char *host, const char *addr_text)
108 {
109   char *canonical_name = hash_table_get (host_address_name_map, addr_text);
110   if (canonical_name)
111     {
112       DEBUGP (("Mapping %s to %s in host_slave_master_map.\n",
113                host, canonical_name));
114       /* We've already dealt with that host under another name. */
115       hash_table_put (host_slave_master_map,
116                       xstrdup_lower (host),
117                       xstrdup_lower (canonical_name));
118     }
119   else
120     {
121       /* This is really the first time we're dealing with that host.  */
122       char *h_copy = xstrdup_lower (host);
123       char *a_copy = xstrdup (addr_text);
124       DEBUGP (("Caching %s <-> %s\n", h_copy, a_copy));
125       hash_table_put (host_name_address_map, h_copy, a_copy);
126       hash_table_put (host_address_name_map, a_copy, h_copy);
127     }
128 }
129
130 /* Store the address of HOSTNAME, internet-style (four octets in
131    network order), to WHERE.  First try to get the address from the
132    cache; if it is not available, call the DNS functions and update
133    the cache.
134
135    Return 1 on successful finding of the hostname, 0 otherwise.  */
136 int
137 store_hostaddress (unsigned char *where, const char *hostname)
138 {
139   unsigned long addr;
140   char *addr_text;
141   char *canonical_name;
142   struct hostent *hptr;
143   struct in_addr in;
144   char *inet_s;
145
146   /* If the address is of the form d.d.d.d, there will be no trouble
147      with it.  */
148   addr = (unsigned long)inet_addr (hostname);
149   /* If we have the numeric address, just store it.  */
150   if ((int)addr != -1)
151     {
152       /* ADDR is defined to be in network byte order, meaning the code
153          works on little and big endian 32-bit architectures without
154          change.  On big endian 64-bit architectures we need to be
155          careful to copy the correct four bytes.  */
156       int offset;
157     have_addr:
158 #ifdef WORDS_BIGENDIAN
159       offset = sizeof (unsigned long) - 4;
160 #else
161       offset = 0;
162 #endif
163       memcpy (where, (char *)&addr + offset, 4);
164       return 1;
165     }
166
167   /* By now we know that the address is not of the form d.d.d.d.  Try
168      to find it in our cache of host addresses.  */
169   addr_text = hash_table_get (host_name_address_map, hostname);
170   if (addr_text)
171     {
172       DEBUGP (("Found %s in host_name_address_map: %s\n",
173                hostname, addr_text));
174       addr = (unsigned long)inet_addr (addr_text);
175       goto have_addr;
176     }
177
178   /* Maybe this host is known to us under another name.  If so, we'll
179      find it in host_slave_master_map, and use the master name to find
180      its address in host_name_address_map. */
181   canonical_name = hash_table_get (host_slave_master_map, hostname);
182   if (canonical_name)
183     {
184       addr_text = hash_table_get (host_name_address_map, canonical_name);
185       assert (addr_text != NULL);
186       DEBUGP (("Found %s as slave of %s -> %s\n",
187                hostname, canonical_name, addr_text));
188       addr = (unsigned long)inet_addr (addr_text);
189       goto have_addr;
190     }
191
192   /* Since all else has failed, let's try gethostbyname().  Note that
193      we use gethostbyname() rather than ngethostbyname(), because we
194      already know that the address is not numerical.  */
195   hptr = gethostbyname (hostname);
196   if (!hptr)
197     return 0;
198   /* Copy the address of the host to socket description.  */
199   memcpy (where, hptr->h_addr_list[0], hptr->h_length);
200   assert (hptr->h_length == 4);
201
202   /* Now that we've gone through the truoble of calling
203      gethostbyname(), we can store this valuable information to the
204      cache.  First, we have to look for it by address to know if it's
205      already in the cache by another name.  */
206   /* Originally, we copied to in.s_addr, but it appears to be missing
207      on some systems.  */
208   memcpy (&in, *hptr->h_addr_list, sizeof (in));
209   inet_s = inet_ntoa (in);
210   add_host_to_cache (hostname, inet_s);
211   return 1;
212 }
213
214 /* Determine the "real" name of HOST, as perceived by Wget.  If HOST
215    is referenced by more than one name, "real" name is considered to
216    be the first one encountered in the past.  */
217 char *
218 realhost (const char *host)
219 {
220   struct in_addr in;
221   struct hostent *hptr;
222   char *master_name;
223
224   DEBUGP (("Checking for %s in host_name_address_map.\n", host));
225   if (hash_table_exists (host_name_address_map, host))
226     {
227       DEBUGP (("Found; %s was already used, by that name.\n", host));
228       return xstrdup_lower (host);
229     }
230
231   DEBUGP (("Checking for %s in host_slave_master_map.\n", host));
232   master_name = hash_table_get (host_slave_master_map, host);
233   if (master_name)
234     {
235     has_master:
236       DEBUGP (("Found; %s was already used, by the name %s.\n",
237                host, master_name));
238       return xstrdup (master_name);
239     }
240
241   DEBUGP (("First time I hear about %s by that name; looking it up.\n",
242            host));
243   hptr = ngethostbyname (host);
244   if (hptr)
245     {
246       char *inet_s;
247       /* Originally, we copied to in.s_addr, but it appears to be
248          missing on some systems.  */
249       memcpy (&in, *hptr->h_addr_list, sizeof (in));
250       inet_s = inet_ntoa (in);
251
252       add_host_to_cache (host, inet_s);
253
254       /* add_host_to_cache() can establish a slave-master mapping. */
255       DEBUGP (("Checking again for %s in host_slave_master_map.\n", host));
256       master_name = hash_table_get (host_slave_master_map, host);
257       if (master_name)
258         goto has_master;
259     }
260
261   return xstrdup_lower (host);
262 }
263
264 /* Compare two hostnames (out of URL-s if the arguments are URL-s),
265    taking care of aliases.  It uses realhost() to determine a unique
266    hostname for each of two hosts.  If simple_check is non-zero, only
267    strcmp() is used for comparison.  */
268 int
269 same_host (const char *u1, const char *u2)
270 {
271   const char *s;
272   char *p1, *p2;
273   char *real1, *real2;
274
275   /* Skip protocol, if present.  */
276   u1 += skip_url (u1);
277   u2 += skip_url (u2);
278   u1 += skip_proto (u1);
279   u2 += skip_proto (u2);
280
281   /* Skip username ans password, if present.  */
282   u1 += skip_uname (u1);
283   u2 += skip_uname (u2);
284
285   for (s = u1; *u1 && *u1 != '/' && *u1 != ':'; u1++);
286   p1 = strdupdelim (s, u1);
287   for (s = u2; *u2 && *u2 != '/' && *u2 != ':'; u2++);
288   p2 = strdupdelim (s, u2);
289   DEBUGP (("Comparing hosts %s and %s...\n", p1, p2));
290   if (strcasecmp (p1, p2) == 0)
291     {
292       xfree (p1);
293       xfree (p2);
294       DEBUGP (("They are quite alike.\n"));
295       return 1;
296     }
297   else if (opt.simple_check)
298     {
299       xfree (p1);
300       xfree (p2);
301       DEBUGP (("Since checking is simple, I'd say they are not the same.\n"));
302       return 0;
303     }
304   real1 = realhost (p1);
305   real2 = realhost (p2);
306   xfree (p1);
307   xfree (p2);
308   if (strcasecmp (real1, real2) == 0)
309     {
310       DEBUGP (("They are alike, after realhost()->%s.\n", real1));
311       xfree (real1);
312       xfree (real2);
313       return 1;
314     }
315   else
316     {
317       DEBUGP (("They are not the same (%s, %s).\n", real1, real2));
318       xfree (real1);
319       xfree (real2);
320       return 0;
321     }
322 }
323
324 /* Determine whether a URL is acceptable to be followed, according to
325    a list of domains to accept.  */
326 int
327 accept_domain (struct urlinfo *u)
328 {
329   assert (u->host != NULL);
330   if (opt.domains)
331     {
332       if (!sufmatch ((const char **)opt.domains, u->host))
333         return 0;
334     }
335   if (opt.exclude_domains)
336     {
337       if (sufmatch ((const char **)opt.exclude_domains, u->host))
338         return 0;
339     }
340   return 1;
341 }
342
343 /* Check whether WHAT is matched in LIST, each element of LIST being a
344    pattern to match WHAT against, using backward matching (see
345    match_backwards() in utils.c).
346
347    If an element of LIST matched, 1 is returned, 0 otherwise.  */
348 int
349 sufmatch (const char **list, const char *what)
350 {
351   int i, j, k, lw;
352
353   lw = strlen (what);
354   for (i = 0; list[i]; i++)
355     {
356       for (j = strlen (list[i]), k = lw; j >= 0 && k >= 0; j--, k--)
357         if (TOLOWER (list[i][j]) != TOLOWER (what[k]))
358           break;
359       /* The domain must be first to reach to beginning.  */
360       if (j == -1)
361         return 1;
362     }
363   return 0;
364 }
365
366 /* Return email address of the form username@FQDN suitable for
367    anonymous FTP passwords.  This process is error-prone, and the
368    escape hatch is the MY_HOST preprocessor constant, which can be
369    used to hard-code either your hostname or FQDN at compile-time.
370
371    If the FQDN cannot be determined, a warning is printed, and the
372    function returns a short `username@' form, accepted by most
373    anonymous servers.
374
375    The returned string is generated by malloc() and should be freed
376    using free().
377
378    If not even the username cannot be divined, it means things are
379    seriously fucked up, and Wget exits.  */
380 char *
381 ftp_getaddress (void)
382 {
383   static char *address;
384
385   /* Do the drill only the first time, as it won't change.  */
386   if (!address)
387     {
388       char userid[32];          /* 9 should be enough for Unix, but
389                                    I'd rather be on the safe side.  */
390       char *host, *fqdn;
391
392       if (!pwd_cuserid (userid))
393         {
394           logprintf (LOG_ALWAYS, _("%s: Cannot determine user-id.\n"),
395                      exec_name);
396           exit (1);
397         }
398 #ifdef MY_HOST
399       STRDUP_ALLOCA (host, MY_HOST);
400 #else /* not MY_HOST */
401 #ifdef HAVE_UNAME
402       {
403         struct utsname ubuf;
404         if (uname (&ubuf) < 0)
405           {
406             logprintf (LOG_ALWAYS, _("%s: Warning: uname failed: %s\n"),
407                        exec_name, strerror (errno));
408             fqdn = "";
409             goto giveup;
410           }
411         STRDUP_ALLOCA (host, ubuf.nodename);
412       }
413 #else /* not HAVE_UNAME */
414 #ifdef HAVE_GETHOSTNAME
415       host = alloca (256);
416       if (gethostname (host, 256) < 0)
417         {
418           logprintf (LOG_ALWAYS, _("%s: Warning: gethostname failed\n"),
419                      exec_name);
420           fqdn = "";
421           goto giveup;
422         }
423 #else /* not HAVE_GETHOSTNAME */
424  #error Cannot determine host name.
425 #endif /* not HAVE_GETHOSTNAME */
426 #endif /* not HAVE_UNAME */
427 #endif /* not MY_HOST */
428       /* If the address we got so far contains a period, don't bother
429          anymore.  */
430       if (strchr (host, '.'))
431         fqdn = host;
432       else
433         {
434           /* #### I've seen the following scheme fail on at least one
435              system!  Do we care?  */
436           char *tmpstore;
437           /* According to Richard Stevens, the correct way to find the
438              FQDN is to (1) find the host name, (2) find its IP
439              address using gethostbyname(), and (3) get the FQDN using
440              gethostbyaddr().  So that's what we'll do.  Step one has
441              been done above.  */
442           /* (2) */
443           struct hostent *hp = gethostbyname (host);
444           if (!hp || !hp->h_addr_list)
445             {
446               logprintf (LOG_ALWAYS, _("\
447 %s: Warning: cannot determine local IP address.\n"),
448                          exec_name);
449               fqdn = "";
450               goto giveup;
451             }
452           /* Copy the argument, so the call to gethostbyaddr doesn't
453              clobber it -- just in case.  */
454           tmpstore = (char *)alloca (hp->h_length);
455           memcpy (tmpstore, *hp->h_addr_list, hp->h_length);
456           /* (3) */
457           hp = gethostbyaddr (tmpstore, hp->h_length, hp->h_addrtype);
458           if (!hp || !hp->h_name)
459             {
460               logprintf (LOG_ALWAYS, _("\
461 %s: Warning: cannot reverse-lookup local IP address.\n"),
462                          exec_name);
463               fqdn = "";
464               goto giveup;
465             }
466           if (!strchr (hp->h_name, '.'))
467             {
468 #if 0
469               /* This gets ticked pretty often.  Karl Berry reports
470                  that there can be valid reasons for the local host
471                  name not to be an FQDN, so I've decided to remove the
472                  annoying warning.  */
473               logprintf (LOG_ALWAYS, _("\
474 %s: Warning: reverse-lookup of local address did not yield FQDN!\n"),
475                        exec_name);
476 #endif
477               fqdn = "";
478               goto giveup;
479             }
480           /* Once we're here, hp->h_name contains the correct FQDN.  */
481           STRDUP_ALLOCA (fqdn, hp->h_name);
482         }
483     giveup:
484       address = (char *)xmalloc (strlen (userid) + 1 + strlen (fqdn) + 1);
485       sprintf (address, "%s@%s", userid, fqdn);
486     }
487   return address;
488 }
489
490 /* Print error messages for host errors.  */
491 char *
492 herrmsg (int error)
493 {
494   /* Can't use switch since some constants are equal (at least on my
495      system), and the compiler signals "duplicate case value".  */
496   if (error == HOST_NOT_FOUND
497       || error == NO_RECOVERY
498       || error == NO_DATA
499       || error == NO_ADDRESS
500       || error == TRY_AGAIN)
501     return _("Host not found");
502   else
503     return _("Unknown error");
504 }
505
506 void
507 clean_hosts (void)
508 {
509   /* host_name_address_map and host_address_name_map share the
510      strings.  Because of that, calling free_keys_and_values once
511      suffices for both.  */
512   free_keys_and_values (host_name_address_map);
513   hash_table_destroy (host_name_address_map);
514   hash_table_destroy (host_address_name_map);
515   free_keys_and_values (host_slave_master_map);
516   hash_table_destroy (host_slave_master_map);
517 }
518
519 void
520 host_init (void)
521 {
522   host_name_address_map = make_string_hash_table (0);
523   host_address_name_map = make_string_hash_table (0);
524   host_slave_master_map = make_string_hash_table (0);
525 }