]> sjero.net Git - wget/blob - src/host.c
[svn] Committed host.c patch from <dpk8i3za97.fsf_-_@mraz.iskon.hr>.
[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;
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 = search_address (hlist, inet_s);
298   if (l) /* Found in the list, as realname.  */
299     {
300       /* Set the default, 0 quality.  */
301       hlist = add_hlist (hlist, host, inet_s, 0);
302       return xstrdup (l->hostname);
303     }
304   /* Since this is really the first time this host is encountered,
305      set quality to 1.  */
306   hlist = add_hlist (hlist, host, inet_s, 1);
307   return xstrdup (host);
308 }
309
310 /* Compare two hostnames (out of URL-s if the arguments are URL-s),
311    taking care of aliases.  It uses realhost() to determine a unique
312    hostname for each of two hosts.  If simple_check is non-zero, only
313    strcmp() is used for comparison.  */
314 int
315 same_host (const char *u1, const char *u2)
316 {
317   const char *s;
318   char *p1, *p2;
319   char *real1, *real2;
320
321   /* Skip protocol, if present.  */
322   u1 += skip_url (u1);
323   u2 += skip_url (u2);
324   u1 += skip_proto (u1);
325   u2 += skip_proto (u2);
326
327   /* Skip username ans password, if present.  */
328   u1 += skip_uname (u1);
329   u2 += skip_uname (u2);
330
331   for (s = u1; *u1 && *u1 != '/' && *u1 != ':'; u1++);
332   p1 = strdupdelim (s, u1);
333   for (s = u2; *u2 && *u2 != '/' && *u2 != ':'; u2++);
334   p2 = strdupdelim (s, u2);
335   DEBUGP (("Comparing hosts %s and %s...\n", p1, p2));
336   if (strcasecmp (p1, p2) == 0)
337     {
338       free (p1);
339       free (p2);
340       DEBUGP (("They are quite alike.\n"));
341       return 1;
342     }
343   else if (opt.simple_check)
344     {
345       free (p1);
346       free (p2);
347       DEBUGP (("Since checking is simple, I'd say they are not the same.\n"));
348       return 0;
349     }
350   real1 = realhost (p1);
351   real2 = realhost (p2);
352   free (p1);
353   free (p2);
354   if (strcasecmp (real1, real2) == 0)
355     {
356       DEBUGP (("They are alike, after realhost()->%s.\n", real1));
357       free (real1);
358       free (real2);
359       return 1;
360     }
361   else
362     {
363       DEBUGP (("They are not the same (%s, %s).\n", real1, real2));
364       free (real1);
365       free (real2);
366       return 0;
367     }
368 }
369
370 /* Determine whether a URL is acceptable to be followed, according to
371    a list of domains to accept.  */
372 int
373 accept_domain (struct urlinfo *u)
374 {
375   assert (u->host != NULL);
376   if (opt.domains)
377     {
378       if (!sufmatch ((const char **)opt.domains, u->host))
379         return 0;
380     }
381   if (opt.exclude_domains)
382     {
383       if (sufmatch ((const char **)opt.exclude_domains, u->host))
384         return 0;
385     }
386   return 1;
387 }
388
389 /* Check whether WHAT is matched in LIST, each element of LIST being a
390    pattern to match WHAT against, using backward matching (see
391    match_backwards() in utils.c).
392
393    If an element of LIST matched, 1 is returned, 0 otherwise.  */
394 int
395 sufmatch (const char **list, const char *what)
396 {
397   int i, j, k, lw;
398
399   lw = strlen (what);
400   for (i = 0; list[i]; i++)
401     {
402       for (j = strlen (list[i]), k = lw; j >= 0 && k >= 0; j--, k--)
403         if (TOLOWER (list[i][j]) != TOLOWER (what[k]))
404           break;
405       /* The domain must be first to reach to beginning.  */
406       if (j == -1)
407         return 1;
408     }
409   return 0;
410 }
411
412 /* Return email address of the form username@FQDN suitable for
413    anonymous FTP passwords.  This process is error-prone, and the
414    escape hatch is the MY_HOST preprocessor constant, which can be
415    used to hard-code either your hostname or FQDN at compile-time.
416
417    If the FQDN cannot be determined, a warning is printed, and the
418    function returns a short `username@' form, accepted by most
419    anonymous servers.
420
421    If not even the username cannot be divined, it means things are
422    seriously fucked up, and Wget exits.  */
423 char *
424 ftp_getaddress (void)
425 {
426   static char *address;
427
428   /* Do the drill only the first time, as it won't change.  */
429   if (!address)
430     {
431       char userid[32];          /* 9 should be enough for Unix, but
432                                    I'd rather be on the safe side.  */
433       char *host, *fqdn;
434
435       if (!pwd_cuserid (userid))
436         {
437           logprintf (LOG_ALWAYS, _("%s: Cannot determine user-id.\n"),
438                      exec_name);
439           exit (1);
440         }
441 #ifdef MY_HOST
442       STRDUP_ALLOCA (host, MY_HOST);
443 #else /* not MY_HOST */
444 #ifdef HAVE_UNAME
445       {
446         struct utsname ubuf;
447         if (uname (&ubuf) < 0)
448           {
449             logprintf (LOG_ALWAYS, _("%s: Warning: uname failed: %s\n"),
450                        exec_name, strerror (errno));
451             fqdn = "";
452             goto giveup;
453           }
454         STRDUP_ALLOCA (host, ubuf.nodename);
455       }
456 #else /* not HAVE_UNAME */
457 #ifdef HAVE_GETHOSTNAME
458       host = alloca (256);
459       if (gethostname (host, 256) < 0)
460         {
461           logprintf (LOG_ALWAYS, _("%s: Warning: gethostname failed\n"),
462                      exec_name);
463           fqdn = "";
464           goto giveup;
465         }
466 #else /* not HAVE_GETHOSTNAME */
467  #error Cannot determine host name.
468 #endif /* not HAVE_GETHOSTNAME */
469 #endif /* not HAVE_UNAME */
470 #endif /* not MY_HOST */
471       /* If the address we got so far contains a period, don't bother
472          anymore.  */
473       if (strchr (host, '.'))
474         fqdn = host;
475       else
476         {
477           /* #### I've seen the following scheme fail on at least one
478              system!  Do we care?  */
479           char *tmpstore;
480           /* According to Richard Stevens, the correct way to find the
481              FQDN is to (1) find the host name, (2) find its IP
482              address using gethostbyname(), and (3) get the FQDN using
483              gethostbyaddr().  So that's what we'll do.  Step one has
484              been done above.  */
485           /* (2) */
486           struct hostent *hp = gethostbyname (host);
487           if (!hp || !hp->h_addr_list)
488             {
489               logprintf (LOG_ALWAYS, _("\
490 %s: Warning: cannot determine local IP address.\n"),
491                          exec_name);
492               fqdn = "";
493               goto giveup;
494             }
495           /* Copy the argument, so the call to gethostbyaddr doesn't
496              clobber it -- just in case.  */
497           tmpstore = (char *)alloca (hp->h_length);
498           memcpy (tmpstore, *hp->h_addr_list, hp->h_length);
499           /* (3) */
500           hp = gethostbyaddr (tmpstore, hp->h_length, hp->h_addrtype);
501           if (!hp || !hp->h_name)
502             {
503               logprintf (LOG_ALWAYS, _("\
504 %s: Warning: cannot reverse-lookup local IP address.\n"),
505                          exec_name);
506               fqdn = "";
507               goto giveup;
508             }
509           if (!strchr (hp->h_name, '.'))
510             {
511 #if 0
512               /* This gets ticked pretty often.  Karl Berry reports
513                  that there can be valid reasons for the local host
514                  name not to be an FQDN, so I've decided to remove the
515                  annoying warning.  */
516               logprintf (LOG_ALWAYS, _("\
517 %s: Warning: reverse-lookup of local address did not yield FQDN!\n"),
518                        exec_name);
519 #endif
520               fqdn = "";
521               goto giveup;
522             }
523           /* Once we're here, hp->h_name contains the correct FQDN.  */
524           STRDUP_ALLOCA (fqdn, hp->h_name);
525         }
526     giveup:
527       address = (char *)xmalloc (strlen (userid) + 1 + strlen (fqdn) + 1);
528       sprintf (address, "%s@%s", userid, fqdn);
529     }
530   return address;
531 }
532
533 /* Print error messages for host errors.  */
534 char *
535 herrmsg (int error)
536 {
537   /* Can't use switch since some constants are equal (at least on my
538      system), and the compiler signals "duplicate case value".  */
539   if (error == HOST_NOT_FOUND
540       || error == NO_RECOVERY
541       || error == NO_DATA
542       || error == NO_ADDRESS
543       || error == TRY_AGAIN)
544     return _("Host not found");
545   else
546     return _("Unknown error");
547 }
548
549 /* Clean the host list.  This is a separate function, so we needn't
550    export HLIST and its implementation.  Ha!  */
551 void
552 clean_hosts (void)
553 {
554   struct host *l = hlist;
555
556   while (l)
557     {
558       struct host *p = l->next;
559       free (l->hostname);
560       free (l->realname);
561       free (l);
562       l = p;
563     }
564   hlist = NULL;
565 }