]> Pileus Git - ~andy/fetchmail/blob - checkalias.c
Check aka suffixes even if DNS is on.
[~andy/fetchmail] / checkalias.c
1 /*
2  * checkalias.c -- check to see if two hostnames or IP addresses are equivalent
3  *
4  * Copyright 1997 by Eric S. Raymond
5  * For license terms, see the file COPYING in this directory.
6  */
7 #include "config.h"
8 #ifdef HAVE_GETHOSTBYNAME
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <sys/types.h>
13 #ifdef HAVE_NET_SOCKET_H
14 #include <net/socket.h>
15 #else
16 #include <sys/socket.h>
17 #endif
18 #include <netinet/in.h>
19 #ifdef HAVE_ARPA_INET_H
20 #include <arpa/inet.h>
21 #endif
22 #include <netdb.h>
23 #include "i18n.h"
24 #include "mx.h"
25 #include "fetchmail.h"
26
27 #define MX_RETRIES      3
28
29 static int is_ip_alias(const char *name1,const char *name2)
30 /*
31  * Given two hostnames as arguments, returns TRUE if they
32  * have at least one IP address in common.
33  * No check is done on errors returned by gethostbyname,
34  * the calling function does them.
35  */
36 {
37     typedef unsigned char address_t[sizeof (struct in_addr)]; 
38     typedef struct _address_e
39     {
40         struct _address_e *next;
41         address_t address;
42     } 
43     address_e;
44     address_e *host_a_addr=0, *host_b_addr=0;   /* assignments pacify -Wall */
45     address_e *dummy_addr;
46
47     int i;
48     struct hostent *hp;
49     char **p;
50  
51     hp = gethostbyname(name1);
52  
53     dummy_addr = (address_e *)NULL;
54
55     for (i=0,p = hp->h_addr_list; *p != 0; i++,p++)
56     {
57         struct in_addr in;
58         (void) memcpy(&in.s_addr, *p, sizeof (in.s_addr));
59         xalloca(host_a_addr, address_e *, sizeof (address_e));
60         memset (host_a_addr,0, sizeof (address_e));
61         host_a_addr->next = dummy_addr;
62         (void) memcpy(&host_a_addr->address, *p, sizeof (in.s_addr));
63         dummy_addr = host_a_addr;
64     }
65
66     hp = gethostbyname(name2);
67
68     dummy_addr = (address_e *)NULL;
69     for (i=0,p = hp->h_addr_list; *p != 0; i++,p++)
70     {
71         struct in_addr in;
72         (void) memcpy(&in.s_addr, *p, sizeof (in.s_addr));
73         xalloca(host_b_addr, address_e *, sizeof (address_e));
74         memset (host_b_addr,0, sizeof (address_e));
75         host_b_addr->next = dummy_addr;
76         (void) memcpy(&host_b_addr->address, *p, sizeof (in.s_addr));
77         dummy_addr = host_b_addr;
78     }
79
80     while (host_a_addr)
81     {
82         while (host_b_addr)
83         {
84             if (!memcmp(host_b_addr->address,host_a_addr->address, sizeof (address_t)))
85                 return (TRUE);
86
87             host_b_addr = host_b_addr->next;
88         }
89         host_a_addr = host_a_addr->next;
90     }
91     return (FALSE);
92 }
93
94 int is_host_alias(const char *name, struct query *ctl)
95 /* determine whether name is a DNS alias of the mailserver for this query */
96 {
97     struct hostent      *he,*he_st;
98     struct mxentry      *mxp, *mxrecords;
99     struct idlist       *idl;
100     int                 namelen;
101
102     struct hostdata *lead_server = 
103         ctl->server.lead_server ? ctl->server.lead_server : &ctl->server;
104
105     /*
106      * The first two checks are optimizations that will catch a good
107      * many cases.
108      *
109      * (1) check against the `true name' deduced from the poll label
110      * and the via option (if present) at the beginning of the poll cycle.  
111      * Odds are good this will either be the mailserver's FQDN or a suffix of
112      * it with the mailserver's domain's default host name omitted.
113      *
114      * (2) Then check the rest of the `also known as'
115      * cache accumulated by previous DNS checks.  This cache is primed
116      * by the aka list option.
117      *
118      * Any of these on a mail address is definitive.  Only if the
119      * name doesn't match any is it time to call the bind library.
120      * If this happens odds are good we're looking at an MX name.
121      */
122     if (strcasecmp(lead_server->truename, name) == 0)
123         return(TRUE);
124     else if (str_in_list(&lead_server->akalist, name, TRUE))
125         return(TRUE);
126
127     /*
128      * Now check for a suffix match on the akalist.  The theory here is
129      * that if the user says `aka netaxs.com', we actually want to match
130      * foo.netaxs.com and bar.netaxs.com.
131      */
132     namelen = strlen(name);
133     for (idl = lead_server->akalist; idl; idl = idl->next)
134     {
135         char    *ep;
136
137         /*
138          * Test is >= here because str_in_list() should have caught the
139          * equal-length case above.  Doing it this way guarantees that
140          * ep[-1] is a valid reference.
141          */
142         if (strlen(idl->id) >= namelen)
143             continue;
144         ep = (char *)name + (namelen - strlen(idl->id));
145         /* a suffix led by . must match */
146         if (ep[-1] == '.' && !strcmp(ep, idl->id))
147             return(TRUE);
148     }
149
150     if (!ctl->server.dns)
151         return(FALSE);
152 #ifndef HAVE_RES_SEARCH
153     return(FALSE);
154 #else
155     /*
156      * The only code that calls the BIND library is here and in the
157      * start-of-run probe with gethostbyname(3) under ETRN/Kerberos.
158      *
159      * We know DNS service was up at the beginning of the run.
160      * If it's down, our nameserver has crashed.  We don't want to try
161      * delivering the current message or anything else from the
162      * current server until it's back up.
163      */
164     if ((he = gethostbyname(name)) != (struct hostent *)NULL)
165     {
166         if (strcasecmp(ctl->server.truename, he->h_name) == 0)
167             goto match;
168         else if (((he_st = gethostbyname(ctl->server.truename)) != (struct hostent *)NULL) && ctl->server.checkalias)
169         {
170             if (outlevel >= O_DEBUG)
171                 report(stdout, _("Checking if %s is really the same node as %s\n"),ctl->server.truename,name);
172             if (is_ip_alias(ctl->server.truename,name) == TRUE)
173             {
174                 if (outlevel >= O_DEBUG)
175                     report(stdout, _("Yes, their IP addresses match\n"));
176                 goto match;
177             }
178             if (outlevel >= O_DEBUG)
179                 report(stdout, _("No, their IP addresses don't match\n"));
180             return(FALSE);
181         }
182         else
183             return(FALSE);
184     }
185     else
186         switch (h_errno)
187         {
188         case HOST_NOT_FOUND:    /* specified host is unknown */
189 #ifndef __BEOS__
190         case NO_ADDRESS:        /* valid, but does not have an IP address */
191             break;
192 #endif
193         case NO_RECOVERY:       /* non-recoverable name server error */
194         case TRY_AGAIN:         /* temporary error on authoritative server */
195         default:
196             if (outlevel != O_SILENT)
197                 report_complete(stdout, "\n");  /* terminate the progress message */
198             report(stderr,
199                 _("nameserver failure while looking for `%s' during poll of %s.\n"),
200                 name, ctl->server.pollname);
201             ctl->errcount++;
202             break;
203         }
204
205     /*
206      * We're only here if DNS was OK but the gethostbyname() failed
207      * with a HOST_NOT_FOUND or NO_ADDRESS error.
208      * Search for a name match on MX records pointing to the server.
209      */
210     h_errno = 0;
211     if ((mxrecords = getmxrecords(name)) == (struct mxentry *)NULL)
212     {
213         switch (h_errno)
214         {
215         case HOST_NOT_FOUND:    /* specified host is unknown */
216 #ifndef __BEOS__
217         case NO_ADDRESS:        /* valid, but does not have an IP address */
218             return(FALSE);
219             break;
220 #endif
221         case NO_RECOVERY:       /* non-recoverable name server error */
222         case TRY_AGAIN:         /* temporary error on authoritative server */
223         default:
224             report(stderr,
225                 _("nameserver failure while looking for `%s' during poll of %s.\n"),
226                 name, ctl->server.pollname);
227             ctl->errcount++;
228             break;
229         }
230     }
231     else
232     {
233         for (mxp = mxrecords; mxp->name; mxp++)
234             if (strcasecmp(ctl->server.truename, mxp->name) == 0)
235                 goto match;
236         return(FALSE);
237     match:;
238     }
239
240     /* add this name to relevant server's `also known as' list */
241     save_str(&lead_server->akalist, name, 0);
242     return(TRUE);
243 #endif /* HAVE_RES_SEARCH */
244 }
245 #endif /* HAVE_GETHOSTBYNAME */
246
247 /* checkalias.c ends here */