* Copyright (c) 1986, 1995 Eric P. Allman
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)domain.c 8.39 (Berkeley) %G% (with name server)";
static char sccsid
[] = "@(#)domain.c 8.39 (Berkeley) %G% (without name server)";
static char MXHostBuf
[MAXMXHOSTS
*PACKETSZ
];
#define MAXDNSRCH 6 /* number of possible domains to search */
#define MAX(a, b) ((a) > (b) ? (a) : (b))
# define NO_DATA NO_ADDRESS
# define HFIXEDSZ 12 /* sizeof(HEADER) */
#define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */
#if defined(__RES) && (__RES >= 19940415)
# define RES_UNC_T char *
# define RES_UNC_T u_char *
** GETMXRR -- get MX resource records for a domain
** host -- the name of the host to MX.
** mxhosts -- a pointer to a return buffer of MX records.
** droplocalhost -- If TRUE, all MX records less preferred
** than the local host (as determined by $=w) will
** rcode -- a pointer to an EX_ status code.
** The number of MX records found.
** -1 if there is an internal failure.
** If no MX records are found, mxhosts[0] is set to host
getmxrr(host
, mxhosts
, droplocalhost
, rcode
)
register u_char
*eom
, *cp
;
int ancount
, qdcount
, buflen
;
char *fallbackMX
= FallBackMX
;
static bool firsttime
= TRUE
;
u_short prefer
[MAXMXHOSTS
];
extern bool getcanonname();
printf("getmxrr(%s, droplocalhost=%d)\n", host
, droplocalhost
);
res_query(FallBackMX
, C_IN
, T_A
,
(u_char
*) &answer
, sizeof answer
) < 0)
/* this entry is bogus */
fallbackMX
= FallBackMX
= NULL
;
else if (droplocalhost
&& wordinclass(fallbackMX
, 'w'))
/* don't use fallback for this pass */
/* efficiency hack -- numeric or non-MX lookups */
** If we don't have MX records in our host switch, don't
** try for MX records. Note that this really isn't "right",
** since we might be set up to try NIS first and then DNS;
** if the host is found in NIS we really shouldn't be doing
** MX lookups. However, that should be a degenerate case.
n
= res_search(host
, C_IN
, T_MX
, (u_char
*) &answer
, sizeof(answer
));
printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n",
(host
== NULL
) ? "<NULL>" : host
, errno
, h_errno
);
/* no MX data on this host */
case 0: /* Ultrix resolver retns failure w/ h_errno=0 */
/* host doesn't exist in DNS; might be in /etc/hosts */
/* couldn't connect to the name server */
/* it might come up later; better queue it up */
syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n",
/* irreconcilable differences */
/* find first satisfactory answer */
cp
= (u_char
*)&answer
+ HFIXEDSZ
;
eom
= (u_char
*)&answer
+ n
;
for (qdcount
= ntohs(hp
->qdcount
); qdcount
--; cp
+= n
+ QFIXEDSZ
)
if ((n
= dn_skipname(cp
, eom
)) < 0)
buflen
= sizeof(MXHostBuf
) - 1;
ancount
= ntohs(hp
->ancount
);
while (--ancount
>= 0 && cp
< eom
&& nmx
< MAXMXHOSTS
- 1)
if ((n
= dn_expand((u_char
*)&answer
,
eom
, cp
, (RES_UNC_T
) bp
, buflen
)) < 0)
if (tTd(8, 8) || _res
.options
& RES_DEBUG
)
printf("unexpected answer type %d, size %d\n",
if ((n
= dn_expand((u_char
*)&answer
, eom
, cp
,
(RES_UNC_T
) bp
, buflen
)) < 0)
if (droplocalhost
&& wordinclass(bp
, 'w'))
printf("found localhost (%s) in MX list, pref=%d\n",
if (!seenlocal
|| pref
< localpref
)
if (fallbackMX
!= NULL
&& strcasecmp(bp
, fallbackMX
) == 0)
weight
[nmx
] = mxrand(bp
);
for (i
= 0; i
< nmx
; i
++)
for (j
= i
+ 1; j
< nmx
; j
++)
if (prefer
[i
] > prefer
[j
] ||
(prefer
[i
] == prefer
[j
] && weight
[i
] > weight
[j
]))
if (seenlocal
&& prefer
[i
] >= localpref
)
/* truncate higher preference part of list */
(!TryNullMXList
|| sm_gethostbyname(host
) == NULL
))
** If we have deleted all MX entries, this is
** an error -- we should NEVER send to a host that
** has an MX, and this should have been caught
** earlier in the config file.
** Some sites prefer to go ahead and try the
** A record anyway; that case is handled by
** setting TryNullMXList. I believe this is a
** bad idea, but it's up to you....
syserr("MX list for %s points back to %s",
/* this may be an MX suppression-style address */
p
= strchr(MXHostBuf
, ']');
if (inet_addr(&MXHostBuf
[1]) != -1)
buflen
= sizeof MXHostBuf
- n
- 1;
getcanonname(mxhosts
[0], sizeof MXHostBuf
- 2, FALSE
))
/* if we have a default lowest preference, include that */
if (fallbackMX
!= NULL
&& !seenlocal
&& strlen(fallbackMX
) < buflen
)
** MXRAND -- create a randomizer for equal MX preferences
** If two MX hosts have equal preferences we want to randomize
** the selection. But in order for signatures to be the same,
** we need to randomize the same way each time. This function
** computes a pseudo-random hash function from the host name.
** host -- the name of the host.
** A random but repeatable value based on the host name.
static unsigned int seed
;
seed
= (int) curtime() & 0xffff;
printf("mxrand(%s)", host
);
if (isascii(c
) && isupper(c
))
hfunc
= ((hfunc
<< 1) ^ c
) % 2003;
printf(" = %d\n", hfunc
);
** DNS_GETCANONNAME -- get the canonical name for named host using DNS
** This algorithm tries to be smart about wildcard MX records.
** This is hard to do because DNS doesn't tell is if we matched
** against a wildcard or a specific MX.
** We always prefer A & CNAME records, since these are presumed
** If we match an MX in one pass and lose it in the next, we use
** the old one. For example, consider an MX matching *.FOO.BAR.COM.
** A hostname bletch.foo.bar.com will match against this MX, but
** will stop matching when we try bletch.bar.com -- so we know
** that bletch.foo.bar.com must have been right. This fails if
** there was also an MX record matching *.BAR.COM, but there are
** some things that just can't be fixed.
** host -- a buffer containing the name of the host.
** This is a value-result parameter.
** hbsize -- the size of the host buffer.
** trymx -- if set, try MX records as well as A and CNAME.
** statp -- pointer to place to store status.
** TRUE -- if the host matched.
dns_getcanonname(host
, hbsize
, trymx
, statp
)
register u_char
*eom
, *ap
;
char nbuf
[MAX(PACKETSZ
, MAXDNAME
*2+2)];
char *searchlist
[MAXDNSRCH
+2];
extern char *gethostalias();
printf("getcanonname(%s)\n", host
);
if ((_res
.options
& RES_INIT
) == 0 && res_init() == -1)
** Initialize domain search list. If there is at least one
** dot in the name, search the unmodified name first so we
** find "vse.CS" in Czechoslovakia instead of in the local
** domain (e.g., vse.CS.Berkeley.EDU).
** Older versions of the resolver could create this
** list by tearing apart the host name.
for (cp
= host
, n
= 0; *cp
; cp
++)
if (n
== 0 && (xp
= gethostalias(host
)) != NULL
)
if (loopcnt
++ > MAXCNAMEDEPTH
)
syserr("loop in ${HOSTALIASES} file");
strncpy(host
, xp
, hbsize
);
if (n
>= 0 && *--cp
!= '.' && bitset(RES_DNSRCH
, _res
.options
))
for (domain
= _res
.dnsrch
; *domain
!= NULL
; )
else if (n
== 0 && bitset(RES_DEFNAMES
, _res
.options
))
** Now run through the search list for the name in question.
for (dp
= searchlist
; *dp
!= NULL
; )
printf("getcanonname: trying %s.%s (%s)\n", host
, *dp
,
qtype
== T_ANY
? "ANY" : qtype
== T_A
? "A" :
qtype
== T_MX
? "MX" : "???");
ret
= res_querydomain(host
, *dp
, C_IN
, qtype
,
answer
.qb2
, sizeof(answer
.qb2
));
printf("\tNO: errno=%d, h_errno=%d\n",
if (errno
== ECONNREFUSED
|| h_errno
== TRY_AGAIN
)
/* the name server seems to be down */
if (h_errno
!= HOST_NOT_FOUND
)
/* might have another type of interest */
else if (qtype
== T_A
&& !gotmx
&& trymx
)
/* we matched before -- use that one */
/* otherwise, try the next name */
** This might be a bogus match. Search for A or
** CNAME records. If we don't have a matching
** wild card MX record, we will accept MX as well.
ap
= (u_char
*) &answer
+ HFIXEDSZ
;
eom
= (u_char
*) &answer
+ ret
;
/* skip question part of response -- we know what we asked */
for (qdcount
= ntohs(hp
->qdcount
); qdcount
--; ap
+= ret
+ QFIXEDSZ
)
if ((ret
= dn_skipname(ap
, eom
)) < 0)
printf("qdcount failure (%d)\n",
return FALSE
; /* ???XXX??? */
for (ancount
= ntohs(hp
->ancount
); --ancount
>= 0 && ap
< eom
; ap
+= n
)
n
= dn_expand((u_char
*) &answer
, eom
, ap
,
(RES_UNC_T
) nbuf
, sizeof nbuf
);
/* got a match -- save that info */
if (trymx
&& mxmatch
== NULL
)
/* exact MX matches are as good as an A match */
/* continue in case a CNAME also exists */
if (loopcnt
++ > MAXCNAMEDEPTH
)
/*XXX should notify postmaster XXX*/
message("DNS failure: CNAME loop for %s",
if (CurEnv
->e_message
== NULL
)
sprintf(ebuf
, "Deferred: DNS failure: CNAME loop for %s",
CurEnv
->e_message
= newstr(ebuf
);
/* value points at name */
if ((ret
= dn_expand((u_char
*)&answer
,
eom
, ap
, (RES_UNC_T
) nbuf
, sizeof(nbuf
))) < 0)
(void)strncpy(host
, nbuf
, hbsize
); /* XXX */
** RFC 1034 section 3.6 specifies that CNAME
** should point at the canonical name -- but
** urges software to try again anyway.
/* not a record of interest */
/* got an A record and no CNAME */
** If this was a T_ANY query, we may have the info but
** need an explicit query. Try T_A, then T_MX.
else if (qtype
== T_A
&& !gotmx
&& trymx
)
/* really nothing in this domain; try the next */
/* create matching name and return */
(void) sprintf(nbuf
, "%.*s%s%.*s", MAXDNAME
, host
,
*mxmatch
== '\0' ? "" : ".",
strncpy(host
, nbuf
, hbsize
);
static char hbuf
[MAXDNAME
];
fname
= getenv("HOSTALIASES");
(fp
= safefopen(fname
, O_RDONLY
, 0, SFF_REGONLY
)) == NULL
)
while (fgets(buf
, sizeof buf
, fp
) != NULL
)
for (p
= buf
; p
!= '\0' && !(isascii(*p
) && isspace(*p
)); p
++)
if (strcasecmp(buf
, host
) == 0)
/* got a match; extract the equivalent name */
while (*p
!= '\0' && isascii(*p
) && isspace(*p
))
while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
strncpy(hbuf
, host
, sizeof hbuf
- 1);
hbuf
[sizeof hbuf
- 1] = '\0';
** MAILB_LOOKUP -- do DNS mailbox lookup
** Convert addr to DNS form (user.host).
/* figure out how much space it needs */
atp
= &addr(strlen(addr
));
for (p
= addr
; (p
= strchr(p
, '.')) != NULL
; p
++)
for (p
= addr
, q
= bufp
; (c
= *p
++) != '\0'; )
** Now do a MAILB lookup.
if (res_query(bufp
, C_IN
, T_MAILB
, (char *) &answer
, sizeof answer
< 0)
/* no match -- just continue as usual */
/* find first satisfactory answer */
ap
= (u_char
*)&answer
+ sizeof(HEADER
);
eom
= (u_char
*)&answer
+ n
;
for (qdcount
= ntohs(hp
->qdcount
); qdcount
--; ap
+= n
+ QFIXEDSZ
)
if ((n
= dn_skipname(ap
, eom
)) < 0)
for (ancount
= ntohs(hp
->ancount
); --ancount
>= 0 && ap
< eom
; ap
+= n
)
n
= dn_expand((u_char
*)&answer
, eom
, ap
, (u_char
*)bp
, buflen
);
ap
+= SHORTSIZE
+ LONGSIZE
;
i
= dn_expand((u_char
*) &answer
, eom
, ap
,
(u_char
) abuf
, sizeof abuf
);
i
= dn_expand((u_char
*) &answer
, eom
, ap
,
(u_char
) hbuf
, sizeof hbuf
);
/* hbuf now has the host to deliver to */
i
= dn_expand((u_char
*) &answer
, eom
, ap
,
(u_char
) gbuf
, sizeof gbuf
);
naddrs
+= sendtolist(ubuf
, a
, sendq
, e
);
if (tTd(8, 8) || _res
.options
& RES_DEBUG
)
printf("unexpected answer type %d, size %d\n",
if ((n
= dn_expand((u_char
*)&answer
, eom
, cp
,
(u_char
*)bp
, buflen
)) < 0)