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