]> sjero.net Git - wget/blob - src/host.c
3fa1bb84de03ec28f21595494da695d0e8468e28
[wget] / src / host.c
1 /* Dealing with host names.
2    Copyright (C) 1995, 1996, 1997 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
52 #ifndef errno
53 extern int errno;
54 #endif
55
56 /* Host list entry */
57 struct host
58 {
59   /* Host's symbolical name, as encountered at the time of first
60      inclusion, e.g. "fly.cc.fer.hr".  */
61   char *hostname;
62   /* Host's "real" name, i.e. its IP address, written out in ASCII
63      form of N.N.N.N, e.g. "161.53.70.130".  */
64   char *realname;
65   /* More than one HOSTNAME can correspond to the same REALNAME.  For
66      our purposes, the canonical name of the host is its HOSTNAME when
67      it was first encountered.  This entry is said to have QUALITY.  */
68   int quality;
69   /* Next entry in the list.  */
70   struct host *next;
71 };
72
73 static struct host *hlist;
74
75 static struct host *add_hlist PARAMS ((struct host *, const char *,
76                                        const char *, int));
77
78 /* The same as gethostbyname, but supports internet addresses of the
79    form `N.N.N.N'.  */
80 struct hostent *
81 ngethostbyname (const char *name)
82 {
83   struct hostent *hp;
84   unsigned long addr;
85
86   addr = (unsigned long)inet_addr (name);
87   if ((int)addr != -1)
88     hp = gethostbyaddr ((char *)&addr, sizeof (addr), AF_INET);
89   else
90     hp = gethostbyname (name);
91   return hp;
92 }
93
94 /* Search for HOST in the linked list L, by hostname.  Return the
95    entry, if found, or NULL.  The search is case-insensitive.  */
96 static struct host *
97 search_host (struct host *l, const char *host)
98 {
99   for (; l; l = l->next)
100     if (strcasecmp (l->hostname, host) == 0)
101       return l;
102   return NULL;
103 }
104
105 /* Like search_host, but searches by address.  */
106 static struct host *
107 search_address (struct host *l, const char *address)
108 {
109   for (; l; l = l->next)
110     {
111       int cmp = strcmp (l->realname, address);
112       if (cmp == 0)
113         return l;
114       else if (cmp > 0)
115         return NULL;
116     }
117   return NULL;
118 }
119
120 /* Store the address of HOSTNAME, internet-style, to WHERE.  First
121    check for it in the host list, and (if not found), use
122    ngethostbyname to get it.
123
124    Return 1 on successful finding of the hostname, 0 otherwise.  */
125 int
126 store_hostaddress (unsigned char *where, const char *hostname)
127 {
128   struct host *t;
129   unsigned long addr;
130   struct hostent *hptr;
131   struct in_addr in;
132   char *inet_s;
133
134   /* If the address is of the form d.d.d.d, there will be no trouble
135      with it.  */
136   addr = (unsigned long)inet_addr (hostname);
137   if ((int)addr == -1)
138     {
139       /* If it is not of that form, try to find it in the cache.  */
140       t = search_host (hlist, hostname);
141       if (t)
142         addr = (unsigned long)inet_addr (t->realname);
143     }
144   /* If we have the numeric address, just store it.  */
145   if ((int)addr != -1)
146     {
147       /* ADDR is in network byte order, meaning the code works on
148          little and big endian 32-bit architectures without change.
149          On big endian 64-bit architectures we need to be careful to
150          copy the correct four bytes.  */
151       int offset = 0;
152 #ifdef WORDS_BIGENDIAN
153       offset = sizeof (unsigned long) - 4;
154 #endif
155       memcpy (where, (char *)&addr + offset, 4);
156       return 1;
157     }
158   /* Since all else has failed, let's try gethostbyname().  Note that
159      we use gethostbyname() rather than ngethostbyname(), because we
160      *know* the address is not numerical.  */
161   hptr = gethostbyname (hostname);
162   if (!hptr)
163     return 0;
164   /* Copy the address of the host to socket description.  */
165   memcpy (where, hptr->h_addr_list[0], hptr->h_length);
166   /* Now that we're here, we could as well cache the hostname for
167      future use, as in realhost().  First, we have to look for it by
168      address to know if it's already in the cache by another name.  */
169
170   /* Originally, we copied to in.s_addr, but it appears to be missing
171      on some systems.  */
172   memcpy (&in, *hptr->h_addr_list, sizeof (in));
173   STRDUP_ALLOCA (inet_s, inet_ntoa (in));
174   t = search_address (hlist, inet_s);
175   if (t) /* Found in the list, as realname.  */
176     {
177       /* Set the default, 0 quality.  */
178       hlist = add_hlist (hlist, hostname, inet_s, 0);
179       return 1;
180     }
181   /* Since this is really the first time this host is encountered,
182      set quality to 1.  */
183   hlist = add_hlist (hlist, hostname, inet_s, 1);
184   return 1;
185 }
186
187 /* Add a host to the host list.  The list is sorted by addresses.  For
188    equal addresses, the entries with quality should bubble towards the
189    beginning of the list.  */
190 static struct host *
191 add_hlist (struct host *l, const char *nhost, const char *nreal, int quality)
192 {
193   struct host *t, *old, *beg;
194
195   /* The entry goes to the beginning of the list if the list is empty
196      or the order requires it.  */
197   if (!l || (strcmp (nreal, l->realname) < 0))
198     {
199       t = (struct host *)xmalloc (sizeof (struct host));
200       t->hostname = xstrdup (nhost);
201       t->realname = xstrdup (nreal);
202       t->quality = quality;
203       t->next = l;
204       return t;
205     }
206
207   beg = l;
208   /* Second two one-before-the-last element.  */
209   while (l->next)
210     {
211       int cmp;
212       old = l;
213       l = l->next;
214       cmp = strcmp (nreal, l->realname);
215       if (cmp >= 0)
216         continue;
217       /* If the next list element is greater than s, put s between the
218          current and the next list element.  */
219       t = (struct host *)xmalloc (sizeof (struct host));
220       old->next = t;
221       t->next = l;
222       t->hostname = xstrdup (nhost);
223       t->realname = xstrdup (nreal);
224       t->quality = quality;
225       return beg;
226     }
227   t = (struct host *)xmalloc (sizeof (struct host));
228   t->hostname = xstrdup (nhost);
229   t->realname = xstrdup (nreal);
230   t->quality = quality;
231   /* Insert the new element after the last element.  */
232   l->next = t;
233   t->next = NULL;
234   return beg;
235 }
236
237 /* Determine the "real" name of HOST, as perceived by Wget.  If HOST
238    is referenced by more than one name, "real" name is considered to
239    be the first one encountered in the past.
240
241    If the host cannot be found in the list of already dealt-with
242    hosts, try with its INET address.  If this fails too, add it to the
243    list.  The routine does not call gethostbyname twice for the same
244    host if it can possibly avoid it.  */
245 char *
246 realhost (const char *host)
247 {
248   struct host *l, *l_real;
249   struct in_addr in;
250   struct hostent *hptr;
251   char *inet_s;
252
253   DEBUGP (("Checking for %s.\n", host));
254   /* Look for the host, looking by the host name.  */
255   l = search_host (hlist, host);
256   if (l && l->quality)          /* Found it with quality */
257     {
258       DEBUGP (("%s was already used, by that name.\n", host));
259       /* Here we return l->hostname, not host, because of the possible
260          case differences (e.g. jaGOR.srce.hr and jagor.srce.hr are
261          the same, but we want the one that was first.  */
262       return xstrdup (l->hostname);
263     }
264   else if (!l)                  /* Not found, with or without quality */
265     {
266       /* The fact that gethostbyname will get called makes it
267          necessary to store it to the list, to ensure that
268          gethostbyname will not be called twice for the same string.
269          However, the quality argument must be set appropriately.
270
271          Note that add_hlist must be called *after* the realname
272          search, or the quality would be always set to 0 */
273       DEBUGP (("This is the first time I hear about host %s by that name.\n",
274                host));
275       hptr = ngethostbyname (host);
276       if (!hptr)
277         return xstrdup (host);
278       /* Originally, we copied to in.s_addr, but it appears to be
279          missing on some systems.  */
280       memcpy (&in, *hptr->h_addr_list, sizeof (in));
281       STRDUP_ALLOCA (inet_s, inet_ntoa (in));
282     }
283   else                          /* Found, without quality */
284     {
285       /* This case happens when host is on the list,
286          but not as first entry (the one with quality).
287          Then we just get its INET address and pick
288          up the first entry with quality.  */
289       DEBUGP (("We've dealt with host %s, but under the name %s.\n",
290                host, l->realname));
291       STRDUP_ALLOCA (inet_s, l->realname);
292     }
293
294   /* Now we certainly have the INET address.  The following loop is
295      guaranteed to pick either an entry with quality (because it is
296      the first one), or none at all.  */
297   l_real = search_address (hlist, inet_s);
298   if (l_real)                   /* Found in the list, as realname.  */
299     {
300       if (!l)
301         /* Set the default, 0 quality.  */
302         hlist = add_hlist (hlist, host, inet_s, 0);
303       return xstrdup (l_real->hostname);
304     }
305   /* Since this is really the first time this host is encountered,
306      set quality to 1.  */
307   hlist = add_hlist (hlist, host, inet_s, 1);
308   return xstrdup (host);
309 }
310
311 /* Compare two hostnames (out of URL-s if the arguments are URL-s),
312    taking care of aliases.  It uses realhost() to determine a unique
313    hostname for each of two hosts.  If simple_check is non-zero, only
314    strcmp() is used for comparison.  */
315 int
316 same_host (const char *u1, const char *u2)
317 {
318   const char *s;
319   char *p1, *p2;
320   char *real1, *real2;
321
322   /* Skip protocol, if present.  */
323   u1 += skip_url (u1);
324   u2 += skip_url (u2);
325   u1 += skip_proto (u1);
326   u2 += skip_proto (u2);
327
328   /* Skip username ans password, if present.  */
329   u1 += skip_uname (u1);
330   u2 += skip_uname (u2);
331
332   for (s = u1; *u1 && *u1 != '/' && *u1 != ':'; u1++);
333   p1 = strdupdelim (s, u1);
334   for (s = u2; *u2 && *u2 != '/' && *u2 != ':'; u2++);
335   p2 = strdupdelim (s, u2);
336   DEBUGP (("Comparing hosts %s and %s...\n", p1, p2));
337   if (strcasecmp (p1, p2) == 0)
338     {
339       free (p1);
340       free (p2);
341       DEBUGP (("They are quite alike.\n"));
342       return 1;
343     }
344   else if (opt.simple_check)
345     {
346       free (p1);
347       free (p2);
348       DEBUGP (("Since checking is simple, I'd say they are not the same.\n"));
349       return 0;
350     }
351   real1 = realhost (p1);
352   real2 = realhost (p2);
353   free (p1);
354   free (p2);
355   if (strcasecmp (real1, real2) == 0)
356     {
357       DEBUGP (("They are alike, after realhost()->%s.\n", real1));
358       free (real1);
359       free (real2);
360       return 1;
361     }
362   else
363     {
364       DEBUGP (("They are not the same (%s, %s).\n", real1, real2));
365       free (real1);
366       free (real2);
367       return 0;
368     }
369 }
370
371 /* Determine whether a URL is acceptable to be followed, according to
372    a list of domains to accept.  */
373 int
374 accept_domain (struct urlinfo *u)
375 {
376   assert (u->host != NULL);
377   if (opt.domains)
378     {
379       if (!sufmatch ((const char **)opt.domains, u->host))
380         return 0;
381     }
382   if (opt.exclude_domains)
383     {
384       if (sufmatch ((const char **)opt.exclude_domains, u->host))
385         return 0;
386     }
387   return 1;
388 }
389
390 /* Check whether WHAT is matched in LIST, each element of LIST being a
391    pattern to match WHAT against, using backward matching (see
392    match_backwards() in utils.c).
393
394    If an element of LIST matched, 1 is returned, 0 otherwise.  */
395 int
396 sufmatch (const char **list, const char *what)
397 {
398   int i, j, k, lw;
399
400   lw = strlen (what);
401   for (i = 0; list[i]; i++)
402     {
403       for (j = strlen (list[i]), k = lw; j >= 0 && k >= 0; j--, k--)
404         if (TOLOWER (list[i][j]) != TOLOWER (what[k]))
405           break;
406       /* The domain must be first to reach to beginning.  */
407       if (j == -1)
408         return 1;
409     }
410   return 0;
411 }
412
413 /* Return email address of the form username@FQDN suitable for
414    anonymous FTP passwords.  This process is error-prone, and the
415    escape hatch is the MY_HOST preprocessor constant, which can be
416    used to hard-code either your hostname or FQDN at compile-time.
417
418    If the FQDN cannot be determined, a warning is printed, and the
419    function returns a short `username@' form, accepted by most
420    anonymous servers.
421
422    If not even the username cannot be divined, it means things are
423    seriously fucked up, and Wget exits.  */
424 char *
425 ftp_getaddress (void)
426 {
427   static char *address;
428
429   /* Do the drill only the first time, as it won't change.  */
430   if (!address)
431     {
432       char userid[32];          /* 9 should be enough for Unix, but
433                                    I'd rather be on the safe side.  */
434       char *host, *fqdn;
435
436       if (!pwd_cuserid (userid))
437         {
438           logprintf (LOG_ALWAYS, _("%s: Cannot determine user-id.\n"),
439                      exec_name);
440           exit (1);
441         }
442 #ifdef MY_HOST
443       STRDUP_ALLOCA (host, MY_HOST);
444 #else /* not MY_HOST */
445 #ifdef HAVE_UNAME
446       {
447         struct utsname ubuf;
448         if (uname (&ubuf) < 0)
449           {
450             logprintf (LOG_ALWAYS, _("%s: Warning: uname failed: %s\n"),
451                        exec_name, strerror (errno));
452             fqdn = "";
453             goto giveup;
454           }
455         STRDUP_ALLOCA (host, ubuf.nodename);
456       }
457 #else /* not HAVE_UNAME */
458 #ifdef HAVE_GETHOSTNAME
459       host = alloca (256);
460       if (gethostname (host, 256) < 0)
461         {
462           logprintf (LOG_ALWAYS, _("%s: Warning: gethostname failed\n"),
463                      exec_name);
464           fqdn = "";
465           goto giveup;
466         }
467 #else /* not HAVE_GETHOSTNAME */
468  #error Cannot determine host name.
469 #endif /* not HAVE_GETHOSTNAME */
470 #endif /* not HAVE_UNAME */
471 #endif /* not MY_HOST */
472       /* If the address we got so far contains a period, don't bother
473          anymore.  */
474       if (strchr (host, '.'))
475         fqdn = host;
476       else
477         {
478           /* #### I've seen the following scheme fail on at least one
479              system!  Do we care?  */
480           char *tmpstore;
481           /* According to Richard Stevens, the correct way to find the
482              FQDN is to (1) find the host name, (2) find its IP
483              address using gethostbyname(), and (3) get the FQDN using
484              gethostbyaddr().  So that's what we'll do.  Step one has
485              been done above.  */
486           /* (2) */
487           struct hostent *hp = gethostbyname (host);
488           if (!hp || !hp->h_addr_list)
489             {
490               logprintf (LOG_ALWAYS, _("\
491 %s: Warning: cannot determine local IP address.\n"),
492                          exec_name);
493               fqdn = "";
494               goto giveup;
495             }
496           /* Copy the argument, so the call to gethostbyaddr doesn't
497              clobber it -- just in case.  */
498           tmpstore = (char *)alloca (hp->h_length);
499           memcpy (tmpstore, *hp->h_addr_list, hp->h_length);
500           /* (3) */
501           hp = gethostbyaddr (tmpstore, hp->h_length, hp->h_addrtype);
502           if (!hp || !hp->h_name)
503             {
504               logprintf (LOG_ALWAYS, _("\
505 %s: Warning: cannot reverse-lookup local IP address.\n"),
506                          exec_name);
507               fqdn = "";
508               goto giveup;
509             }
510           if (!strchr (hp->h_name, '.'))
511             {
512 #if 0
513               /* This gets ticked pretty often.  Karl Berry reports
514                  that there can be valid reasons for the local host
515                  name not to be an FQDN, so I've decided to remove the
516                  annoying warning.  */
517               logprintf (LOG_ALWAYS, _("\
518 %s: Warning: reverse-lookup of local address did not yield FQDN!\n"),
519                        exec_name);
520 #endif
521               fqdn = "";
522               goto giveup;
523             }
524           /* Once we're here, hp->h_name contains the correct FQDN.  */
525           STRDUP_ALLOCA (fqdn, hp->h_name);
526         }
527     giveup:
528       address = (char *)xmalloc (strlen (userid) + 1 + strlen (fqdn) + 1);
529       sprintf (address, "%s@%s", userid, fqdn);
530     }
531   return address;
532 }
533
534 /* Print error messages for host errors.  */
535 char *
536 herrmsg (int error)
537 {
538   /* Can't use switch since some constants are equal (at least on my
539      system), and the compiler signals "duplicate case value".  */
540   if (error == HOST_NOT_FOUND
541       || error == NO_RECOVERY
542       || error == NO_DATA
543       || error == NO_ADDRESS
544       || error == TRY_AGAIN)
545     return _("Host not found");
546   else
547     return _("Unknown error");
548 }
549
550 /* Clean the host list.  This is a separate function, so we needn't
551    export HLIST and its implementation.  Ha!  */
552 void
553 clean_hosts (void)
554 {
555   struct host *l = hlist;
556
557   while (l)
558     {
559       struct host *p = l->next;
560       free (l->hostname);
561       free (l->realname);
562       free (l);
563       l = p;
564     }
565   hlist = NULL;
566 }