|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T b
Length: 30803 (0x7853)
Types: TextFile
Names: »bind.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
└─⟦2fafebccf⟧ »EurOpenD3/mail/smail3.1.19.tar.Z«
└─⟦bcd2bc73f⟧
└─⟦this⟧ »src/routers/bind.c«
/* @(#)bind.c 1.4 3/15/89 18:34:59 */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
*
* See the file COPYING, distributed with smail, for restriction
* and warranty information.
*/
/*
* bind.c
* routing driver which connects to a Berkeley Internet Name
* Domain (BIND) server for routing mail. At the present time
* this router has only been tested with BIND4.8, which was
* obtained from the archives on uunet.
*
* Specifications for the pathalias routing driver:
*
* associated transports:
* Generally used with an smtp transport.
*
* private attribute data:
* None.
*
* private attribute flags:
* defer_no_connect: if set and we cannot connect to the
* name server, try again later. This is set by default.
* local_mx_okay: if not set, an MX record which points to the
* local host is considered to be an error which will
* will cause mail to be returned to the sender.
* defname: append a default domain to an unqualified hostname,
* using the RES_DEFNAME flag to the resolver library.
* This is set by default.
*
* algorithm:
* For bind routing, given a target use the following
* strategy:
*
* 1. Replace sequences of more than one dot in the target
* with a single dot. Remove any dots from the beginning
* and end of the target.
*
* 2. See if there is an MX record for the target. If so,
* skip to step 6.
*
* 3. If the MX record query returned a CNAME record, then
* treat the canonical name as the target and return to
* step 2.
*
* 4. See if there is an A record for the target. If so,
* get the WKS record for the target. If there is a WKS
* record which includes the SMTP service, then match
* the the target. If the WKS record does not include
* the SMTP service, then reject the message as
* undeliverable. If there is no WKS record, then match
* the target.
*
* 5. Do not match the target.
*
* 6. Issue a request for WKS records for each name
* specified by a returned MX record. Discard all MX
* records for which no WKS record is found.
*
* 7. If the primary name of the local host is listed in an
* MX record, all MX record's with equal or greater
* preference fields are discarded.
*
* 8. If the only MX record remaining references the local
* host, do not match the target.
*
* 9. Choose one of the MX records with the lowest
* preference fields. Match the address with the
* next_host value set to the referenced host.
*
* When a hostname is matched, the next_host field is set to
* the referenced host, for MX records, or the matched host
* for A records. The route field is set to the canonical
* name for the host, if that is different from the original
* target host. The match length is always set to the length
* of the original target host.
*
* NOTE: Use of WKS records is enabled if the USE_WKS_RECORDS macro
* is defined. This can be set from the EDITME file with the
* MISC_H_DEFINES variable. If this macro is not defined, then
* WKS records are not retrieved and are assumed to exist and
* to contain the SMTP service.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <netdb.h>
#include <arpa/resolv.h>
#include "defs.h"
#include "../smail.h"
#include "../smailconf.h"
#include "../parse.h"
#include "../addr.h"
#include "../route.h"
#include "../transport.h"
#include "../lookup.h"
#include "../dys.h"
#include "rtlib.h"
#include "bind.h"
#ifndef DEPEND
# include "../extern.h"
# include "../debug.h"
# include "../error.h"
#endif
#if PACKETSZ > 1024
# define MAXPACKET PACKETSZ
#else
# define MAXPACKET 1024
#endif
#ifndef GETSHORT
/*
* earlier versions of bind don't seem to define these useful macros,
* so roll our own.
*/
# define GETSHORT(i, p) \
((i) = ((unsigned)(*(p)++ & 0xff) << 8), \
(i) |= ((unsigned)(*(p)++ & 0xff)))
# define GETLONG(l, p) \
((l) = ((unsigned long)(*(p)++ & 0xff) << 24), \
(l) |= ((unsigned long)(*(p)++ & 0xff) << 16), \
(l) |= ((unsigned long)(*(p)++ & 0xff) << 8), \
(l) |= ((unsigned long)(*(p)++ & 0xff)))
# define PUTSHORT(i, p) \
((*(p)++ = (unsigned)(i) >> 8), \
(*(p)++ = (unsigned)(i)))
# define PUTLONG(l, p) \
((*(p)++ = (unsigned long)(l) >> 24), \
(*(p)++ = (unsigned long)(l) >> 16), \
(*(p)++ = (unsigned long)(l) >> 8), \
(*(p)++ = (unsigned long)(l)))
#endif
/*
* The standard rrec structure doesn't have a space for the domain
* name, so define our own.
*/
enum rr_sect { SECT_AN, SECT_NS, SECT_AR };
typedef struct rr {
enum rr_sect rr_sect; /* resource record section */
char *rr_dname; /* domain name */
short rr_class; /* class number */
short rr_type; /* type number */
int rr_size; /* size of data area */
char *rr_data; /* pointer to data */
} RR;
/* structure for iterating over RR's with getnextrr() */
struct rr_iterator {
RR rr; /* space for storing RR */
char dname[MAXDNAME]; /* space for storing domain name */
char *dp; /* pointer within packet */
HEADER *hp; /* saved header pointer */
char *eom; /* end of packet */
int ancount; /* count of answer records */
int nscount; /* count of ns records */
int arcount; /* count of additional records */
};
/* functions local to this file */
static int bind_lookup();
static char *strip_dots();
static int get_records();
static RR *getnextrr();
static void rewindrr();
static int find_a_record();
#ifdef USE_WKS_RECORDS
static int find_smtp_service();
static int find_wks_service();
static int decode_mx_rr();
#endif
static struct error *lost_server();
static struct error *server_failure();
static struct error *packet_error();
#ifdef USE_WKS_RECORDS
static struct error *no_smtp_service();
static struct error *unknown_service();
#endif
static struct error *no_valid_mx_records();
static struct error *matched_local_host();
\f
/*
* rtd_bind - route using Berkeley Internet Name Domain server
*/
void
rtd_bind(rp, in, out, defer, fail)
struct router *rp; /* router table entry */
struct addr *in; /* input addr structures */
struct addr **out; /* non-failed addr structures */
struct addr **defer; /* addrs to defer to a later time */
struct addr **fail; /* unresolvable addrs */
{
rtd_standard(rp, in, out, defer, fail, bind_lookup);
}
/*
* rtv_bind - verify that a match exists for a list of addr structures
*/
/*ARGSUSED*/
void
rtv_bind(rp, in, retry, okay, defer, fail)
struct router *rp; /* router entry */
struct addr *in; /* input local-form addrs */
struct addr **retry; /* output list of unmatched addrs */
struct addr **okay; /* output list of verified addrs */
struct addr **defer; /* temporariliy unverifiable addrs */
struct addr **fail; /* unverified addrs */
{
rtv_standard(rp, in, retry, okay, defer, fail, bind_lookup);
}
/*
* rtb_bind - read the configuration file attributes
*/
char *
rtb_bind(rp, attrs)
struct router *rp; /* router entry being defined */
struct attribute *attrs; /* list of per-driver attributes */
{
char *error;
static struct attr_table bind_attributes[] = {
{ "local_mx_okay", t_boolean, NULL, NULL, BIND_LOCAL_MX_OKAY },
{ "defer_no_connect", t_boolean, NULL, NULL, BIND_DEFER_NO_CONN },
{ "defnames", t_boolean, NULL, NULL, BIND_DEFNAMES },
};
static struct attr_table *end_bind_attributes = ENDTABLE(bind_attributes);
static struct bind_private bind_template = {
0, /* dummy value */
};
struct bind_private *priv; /* new bind_private structure */
/* copy the template private data */
priv = (struct bind_private *)xmalloc(sizeof(*priv));
(void) memcpy((char *)priv, (char *)&bind_template, sizeof(*priv));
rp->private = (char *)priv;
rp->flags |= (BIND_DEFNAMES|BIND_DEFER_NO_CONN);
/* fill in the attributes of the private data */
error = fill_attributes((char *)priv,
attrs,
&rp->flags,
bind_attributes,
end_bind_attributes);
if (error) {
return error;
} else {
return NULL;
}
}
\f
/*
* bind_lookup - lookup a host in through the domain system
*
* Use the algorithm described at the top of this source file for
* finding a match for a target.
*
* Return one of the following values:
*
* These return codes apply only to the specific address:
* DB_SUCCEED Matched the target host.
* DB_NOMATCH Did not match the target host.
* DB_FAIL Fail the address with the given error.
* DB_AGAIN Try to route with this address again at a
* later time.
*
* These return codes apply to this router in general:
* FILE_NOMATCH There is no server running on this machine.
* FILE_AGAIN Lost contact with server, or server is
* required to exist. Try again later.
* FILE_FAIL A major error has been caught in router,
* notify postmaster.
*/
/*ARGSUSED*/
static int
bind_lookup(rp, addr, fl, rt_info, error_p)
struct router *rp; /* router table entry */
struct addr *addr; /* addr structure */
int fl; /* flags from rt[dv]_standard */
struct rt_info *rt_info; /* return route info here */
struct error **error_p; /* return lookup error here */
{
char *target; /* target stripped of extra dots */
static char *full_target = NULL; /* target sent by res_send */
HEADER *mx_rrs = NULL; /* response for MX rr's */
int mx_size; /* size of MX rr response packet */
struct rr_iterator mx_it; /* MX rr iterator */
struct rr_iterator a_it; /* A rr iterator */
RR *mx_rr; /* a single MX rr */
RR *a_rr; /* a single A rr */
static int no_server = FALSE; /* TRUE if no server process */
static int server = FALSE; /* TRUE if server process found */
int success; /* value to return */
char *error; /* error text from called function */
int low_precedence; /* lowest precedence MX record */
int local_precedence; /* precedence of MX to local host */
if (no_server) {
return DB_NOMATCH;
}
#ifdef RES_DEFNAMES
if (rp->flags & BIND_DEFNAMES) {
_res.options |= RES_DEFNAMES;
} else {
_res.options &= ~RES_DEFNAMES;
}
#endif
/* Step 1: Strip extra dots */
/* store return values for common case */
rt_info->next_host = target = strip_dots(addr->target);
rt_info->route = NULL;
rt_info->matchlen = strlen(addr->target);
get_target_mx_records:
/* Step 2: See if there are any MX records */
if (mx_rrs == NULL) {
mx_rrs = (HEADER *)xmalloc(MAXPACKET);
}
success = get_records(target, T_MX, mx_rrs, &mx_size, &error);
if (success != DB_SUCCEED) {
switch (success) {
case FILE_NOMATCH:
if ((rp->flags & BIND_DEFER_NO_CONN) && ! server) {
no_server = TRUE;
success = DB_NOMATCH;
break;
}
if (server) {
*error_p = lost_server(rp);
} else {
*error_p = server_failure(rp, error);
}
success = FILE_AGAIN;
break;
case DB_AGAIN:
case DB_FAIL:
case FILE_AGAIN:
*error_p = server_failure(rp, error);
break;
}
xfree((char *)mx_rrs);
return success;
}
/* We have connected to the server at least once */
server = TRUE;
/* See if there are any MX records */
rewindrr(&mx_it, mx_rrs, mx_size);
while ((mx_rr = getnextrr(&mx_it)) && mx_rr->rr_type != T_MX) ;
if (mx_rr == NULL) {
/* Step 3: Look for a CNAME record */
rewindrr(&mx_it, mx_rrs, mx_size);
while ((mx_rr = getnextrr(&mx_it)) && mx_rr->rr_type != T_CNAME) ;
if (mx_rr) {
char nambuf[MAXDNAME];
int dlen;
dlen = dn_expand((char *)mx_it.hp, mx_it.eom, mx_rr->rr_data,
nambuf, MAXDNAME);
if (dlen < 0) {
/* format error in response packet */
*error_p = packet_error(rp, "CNAME", target);
xfree((char *)mx_rrs);
return DB_AGAIN;
}
rt_info->next_host = target = nambuf;
/* Go back and look for MX records again */
goto get_target_mx_records;
}
/* Step 4: Look for an A record */
/* First look through the previously returned packet */
rewindrr(&a_it, mx_rrs, mx_size);
while ((a_rr = getnextrr(&a_it)) && a_rr->rr_type != T_A) ;
/* we don't need the MX record response anymore */
xfree((char *)mx_rrs);
if (a_rr == NULL) {
/* Go out and get an A record */
success = find_a_record(target, &error);
switch (success) {
case DB_NOMATCH:
return DB_NOMATCH;
case FILE_NOMATCH:
success = FILE_AGAIN;
/* FALL THROUGH */
case DB_FAIL:
case DB_AGAIN:
case FILE_AGAIN:
/* Step 5: Do not match the target */
*error_p = server_failure(rp, error);
return success;
}
/* found an A record */
}
#ifdef USE_WKS_RECORDS
/* Step 4 continued: look for WKS records */
success = find_smtp_service(target, &error);
switch(success) {
case DB_NOMATCH:
*error_p = no_smtp_service(rp, target);
break;
case DB_FAIL:
success = DB_SUCCEED;
break;
case FILE_NOMATCH:
success = FILE_AGAIN;
/* FALL THROUGH */
case DB_AGAIN:
case FILE_AGAIN:
*error_p = server_failure(rp, error);
break;
case FILE_FAIL:
*error_p = unknown_service(rp, error);
break;
}
return success;
#else /* not USE_WKS_RECORDS */
return DB_SUCCEED;
#endif /* not USE_WKS_RECORDS */
}
/*
* Steps 6-9: We combine these four steps by going through the MX
* records and keeping track of the lowest precedence number.
*/
rewindrr(&mx_it, mx_rrs, mx_size);
low_precedence = -1;
local_precedence = -1;
/* grab the domain returned in the packet as the real target name */
if (full_target) {
xfree(full_target);
}
if (! EQIC(target, mx_it.dname)) {
full_target = COPY_STRING(mx_it.dname);
} else {
full_target = COPY_STRING(target);
}
while (mx_rr = getnextrr(&mx_it)) {
static char *return_name = NULL;
char *name;
int precedence;
if (mx_rr->rr_type != T_MX) {
continue;
}
success = decode_mx_rr(mx_rr, &mx_it, &name, &precedence);
if (success != DB_SUCCEED) {
*error_p = packet_error(rp, "MX", target);
xfree((char *)mx_rrs);
return DB_AGAIN;
}
#ifdef USE_WKS_RECORDS
success = find_smtp_service(name, &error);
switch (success) {
case DB_FAIL:
case DB_NOMATCH:
/* no SMTP service, ignore entry */
continue;
case FILE_FAIL:
/* Could not determine smtp or tcp protocol */
*error_p = unknown_service(rp, error);
xfree((char *)mx_rrs);
return FILE_FAIL;
case FILE_AGAIN:
case FILE_NOMATCH:
*error_p = server_failure(rp, error);
xfree((char *)mx_rrs);
return FILE_AGAIN;
}
#endif /* USE_WKS_RECORDS */
if (islocalhost(name)) {
if (local_precedence < 0 || precedence < local_precedence) {
local_precedence = precedence;
}
}
if (low_precedence < 0 || precedence < low_precedence) {
low_precedence = precedence;
if (return_name) {
xfree(return_name);
}
rt_info->next_host = return_name = COPY_STRING(name);
}
}
xfree((char *)mx_rrs);
if (low_precedence < 0) {
/* There were no valid MX records, this is an error */
*error_p = no_valid_mx_records(rp, target);
return DB_FAIL;
}
if (local_precedence == low_precedence) {
if ((rp->flags & BIND_LOCAL_MX_OKAY) == 0) {
*error_p = matched_local_host(rp, target);
return DB_FAIL;
}
return DB_NOMATCH;
}
/*
* if the MX record points to a different host than the
* target, make sure the target hostname is passed to that
* host.
*/
if (! EQIC(full_target, rt_info->next_host)) {
rt_info->route = full_target;
}
return DB_SUCCEED;
}
/*
* strip_dots - remove extra dots from a hostname string
*
* Remove all dots from the beginning and end of a string. Also, any
* sequence of more than one dot is replaced by a single dot. For
* example, the string:
*
* .att..com.
*
* will result in the string:
*
* att.com
*
* The operation is non-destructive on the passed string. The
* resulting value points to a region which may be reused on
* subsequent calls to strip_dots().
*/
static char *
strip_dots(s)
register char *s;
{
static struct str new; /* region for building new string */
static int inited = FALSE; /* true if target initialized */
/* initialize or clear the new string */
if (! inited) {
STR_INIT(&new);
} else {
new.i = 0;
}
/*
* copy target, removing extra dots.
*/
while (*s == '.') s++;
do {
if (*s == '.') {
while (*s == '.') s++;
if (*s) --s;
}
STR_NEXT(&new, *s);
} while (*s++);
return new.p;
}
\f
/*
* get_records - query the domain system for resource records of the given
* name.
*
* Send out a query and return the response packet in a passed buffer.
* Resource records are in the bytes following the header for that
* packet. The actual size of the response packet is stored in pack_size.
*
* The passed answer buffer must have space for at least MAXPACKET
* bytes of data.
*
* Return one of the following response codes:
*
* DB_SUCCEED We received an affirmative packet from the
* server.
* DB_NOMATCH We received a negative response from the server
* indicating that the name does not exist.
* DB_FAIL We received a negative response from the
* server indicating some problem with the
* packet.
* DB_AGAIN We received a negative response from the
* server indicating a temporary failure while
* processing the name.
* FILE_NOMATCH We could not connect to the server.
* FILE_AGAIN There was a failure in the server, try again
* later.
*/
static int
get_records(qname, qtype, answer, pack_size, error)
char *qname; /* search for this name */
int qtype; /* and for records of this type */
register HEADER *answer; /* buffer for storing answer */
int *pack_size; /* store answer packet size here */
char **error; /* store error message here */
{
char msgbuf[MAXPACKET];
int msglen;
int anslen;
msglen = res_mkquery(QUERY, qname, C_ANY, qtype, (char *)NULL, 0,
(struct rrec *)NULL, msgbuf, MAXPACKET);
anslen = res_send(msgbuf, msglen, (char *)answer, MAXPACKET);
if (anslen < 0) {
return FILE_NOMATCH;
}
*pack_size = anslen;
answer->qdcount = ntohs(answer->qdcount);
answer->ancount = ntohs(answer->ancount);
answer->nscount = ntohs(answer->nscount);
answer->arcount = ntohs(answer->arcount);
switch (answer->rcode) {
case NOERROR:
return DB_SUCCEED;
case FORMERR:
*error = "Nameserver: Format error in packet";
return DB_FAIL;
case SERVFAIL:
*error = "Nameserver: Server failure";
return FILE_AGAIN;
case NXDOMAIN:
return DB_NOMATCH;
case NOTIMP:
*error = "Nameserver: Unimplemented request";
return DB_FAIL;
case REFUSED:
*error = "Nameserver: Query refused";
return FILE_AGAIN;
default:
*error = "Nameserver: Unknown response code";
return DB_FAIL;
}
}
/*
* getnextrr - get a sequence of resource records from a name server
* response packet.
*
* The first time getnextrr() is called to process a packet, pass it
* the header address of the packet. For subsequent calls pass NULL.
* When no more records remain, getnexrr() returns NULL.
*
* To process a specific response section, pass the section in sect.
*/
static RR *
getnextrr(it)
register struct rr_iterator *it; /* iteration variables */
{
int dnamelen;
register char *dp;
dp = it->dp;
/* return NULL if no rr's remain */
if (it->ancount != 0) {
--it->ancount;
it->rr.rr_sect = SECT_AN;
} else if (it->nscount != 0) {
--it->nscount;
it->rr.rr_sect = SECT_NS;
} else if (it->arcount != 0) {
--it->arcount;
it->rr.rr_sect = SECT_AR;
} else {
return NULL;
}
dnamelen = dn_expand((char *)it->hp, it->eom, dp, it->dname, MAXDNAME);
if (dnamelen < 0) {
return NULL;
}
dp += dnamelen;
GETSHORT(it->rr.rr_type, dp); /* extract type from record */
GETSHORT(it->rr.rr_class, dp); /* extract class */
dp += 4; /* skip time to live */
GETSHORT(it->rr.rr_size, dp); /* extract length of data */
it->rr.rr_data = dp; /* there is the data */
it->dp = dp + it->rr.rr_size; /* skip to next resource record */
return &it->rr;
}
static void
rewindrr(it, hp, packsize)
register struct rr_iterator *it;
HEADER *hp;
int packsize;
{
int dnamelen;
int qdcount;
it->dp = (char *)(hp + 1);
it->hp = hp;
it->eom = (char *)hp + packsize;
it->rr.rr_dname = it->dname;
it->ancount = it->hp->ancount;
it->nscount = it->hp->nscount;
it->arcount = it->hp->arcount;
qdcount = it->hp->qdcount;
/* skip over questions */
while (qdcount > 0) {
dnamelen = dn_expand((char *)it->hp, it->eom, it->dp,
it->dname, MAXDNAME);
if (dnamelen < 0) {
it->ancount = it->nscount = it->arcount = 0;
}
it->dp += dnamelen;
it->dp += 4; /* skip over class and type */
--qdcount;
}
}
\f
/*
* find_a_record - look for an A record for the target
*
* Look for an A record for the target, and return an appropriate
* response code:
*
* DB_SUCCEED An A record was found for the target.
* DB_NOMATCH There was not an A record.
* DB_FAIL There was a server error for this query.
* DB_AGAIN There was a server error for this query, try
* again later.
* FILE_AGAIN Server error, try again later.
* FILE_NOMATCH Could not connect to server.
*
* For response codes other than DB_SUCCEED and DB_NOMATCH, store an
* error message.
*/
static int
find_a_record(target, error)
char *target;
char **error;
{
int success;
HEADER *a_rrs;
int a_size;
struct rr_iterator a_it;
RR *a_rr;
a_rrs = (HEADER *)xmalloc(MAXPACKET);
success = get_records(target, T_A, a_rrs, &a_size, error);
if (success != DB_SUCCEED) {
xfree((char *)a_rrs);
return success;
}
rewindrr(&a_it, a_rrs, a_size);
while ((a_rr = getnextrr(&a_it)) &&a_rr->rr_type != T_A) ;
xfree((char *)a_rrs);
if (a_rr) {
return DB_SUCCEED;
}
return DB_NOMATCH;
}
#ifdef USE_WKS_RECORDS
/*
* find_smtp_service - look for the SMTP service in WKS records for target
*
* get the WKS records for the specific target and search those WKS
* records for the SMTP service. Return the following:
*
* DB_SUCCEED The SMTP service was found.
* DB_FAIL There were no WKS records.
* DB_NOMATCH The SMTP service was not found in the WKS
* records.
* DB_AGAIN There was a server error for this query, try
* again later.
* FILE_AGAIN Server error, try again later.
* FILE_NOMATCH We could not connect to the server.
* FILE_FAIL Could not determine smtp or tcp protocol
* numbers.
*
* For response codes other than DB_SUCCEED, DB_FAIL and DB_NOMATCH,
* store an error message.
*/
static int
find_smtp_service(target, error)
char *target;
char **error;
{
int success;
HEADER *wks_rrs;
int wks_size;
struct rr_iterator wks_it;
RR *wks_rr;
int found_wks = FALSE;
int found_smtp = FALSE;
wks_rrs = (HEADER *)xmalloc(MAXPACKET);
success = get_records(target, T_WKS, wks_rrs, &wks_size, error);
if (success != DB_SUCCEED) {
xfree((char *)wks_rrs);
return success;
}
rewindrr(&wks_it, wks_rrs, wks_size);
while (wks_rr = getnextrr(&wks_it)) {
int proto;
static int tcp_proto;
static int smtp_service = -1;
struct servent *smtp_servent;
struct protoent *tcp_protoent;
if (smtp_service < 0) {
smtp_servent = getservbyname("smtp", "tcp");
if (smtp_servent) {
smtp_service = smtp_servent->s_port;
} else {
*error = "smtp/tcp: Unknown service";
xfree((char *)wks_rrs);
return FILE_FAIL;
}
tcp_protoent = getprotobyname("tcp");
if (tcp_protoent) {
tcp_proto = tcp_protoent->p_proto;
} else {
*error = "tcp: Unknown protocol";
xfree((char *)wks_rrs);
return FILE_FAIL;
}
}
if (wks_rr->rr_type != T_WKS) {
continue;
}
/* proto is in fifth byte of record data */
proto = 0xff & wks_rr->rr_data[4];
if (proto != tcp_proto) {
continue;
}
found_wks = TRUE;
if (find_wks_service(wks_rr, smtp_service)) {
found_smtp = TRUE;
break;
}
}
if (found_smtp) {
success = DB_SUCCEED;
} else if (found_wks) {
success = DB_NOMATCH;
} else {
success = DB_FAIL;
}
xfree((char *)wks_rrs);
return success;
}
/*
* find_wks_service - look for a service in a WKS record
*
* Return TRUE if the service is listed in the record, FALSE
* otherwise.
*/
static int
find_wks_service(rr, service)
RR *rr;
int service;
{
int i = 0;
char *s = rr->rr_data + 5; /* skip over inet address and proto */
while (s < rr->rr_data + rr->rr_size) {
register unsigned int bits = 0xff & *s++;
int bit;
for (bit = 0; bit < 8; bit++) {
if (i == service) {
return (bits & 0x80)? TRUE: FALSE;
}
bits = bits << 1;
i++;
}
}
return FALSE;
}
#endif /* USE_WKS_RECORDS */
static int
decode_mx_rr(rr, it, name, precedence)
RR *rr;
struct rr_iterator *it;
char **name;
int *precedence;
{
static char nambuf[MAXDNAME];
char *s = rr->rr_data;
int dlen;
GETSHORT(*precedence, s);
dlen = dn_expand((char *)it->hp, it->eom, s, nambuf, MAXDNAME);
if (dlen < 0) {
return DB_FAIL;
}
*name = nambuf;
return DB_SUCCEED;
}
\f
/*
* Create error structures for various errors.
*/
static struct error *
lost_server(rp)
struct router *rp;
{
char *error_text;
/*
* ERR_163 - lost connection to BIND server
*
* DESCRIPTION
* Lost connection to the nameserver.
*
* ACTIONS
* Try again later.
*
* RESOLUTION
* Hopefully, a later retry will reconnect.
*/
error_text = xprintf("router %s: Lost connection to BIND server: %s",
rp->name, strerrno());
DEBUG1(DBG_DRIVER_LO, "%s\n", error_text);
return note_error(ERR_163, error_text);
}
static struct error *
server_failure(rp, error)
struct router *rp;
char *error; /* additional error text */
{
char *error_text;
/*
* ERR_164 - failure talking to BIND server
*
* DESCRIPTION
* An error occured when sending or receiving packets from
* the BIND server, or the server registered an error in the
* response packet.
*
* ACTIONS
* Actions depend upon the specific error. Usually, a retry
* is attempted later.
*
* RESOLUTION
* Resolution depends upon the specific error.
*/
error_text = xprintf("router %s: BIND server failure: %s: %s",
rp->name, error, strerrno());
DEBUG1(DBG_DRIVER_LO, "%s\n", error_text);
return note_error(ERR_NPOSTMAST | ERR_164, error_text);
}
static struct error *
packet_error(rp, type, host)
struct router *rp;
char *type; /* type name of packet */
char *host; /* target host */
{
char *error_text;
/*
* ERR_165 - response packet format error
*
* DESCRIPTION
* A format error was found in a packet returned by the BIND
* name server.
*
* ACTIONS
* Retry again later, in the hope that the problem will go
* away.
*
* RESOLUTION
* This is probably a bug in the nameserver.
*/
error_text =
xprintf("router %s: BIND server format error in %s packet for %s",
rp->name, type, host);
DEBUG1(DBG_DRIVER_LO, "%s\n", error_text);
return note_error(ERR_165, error_text);
}
#ifdef USE_WKS_RECORDS
static struct error *
no_smtp_service(rp, host)
struct router *rp;
char *host;
{
char *error_text;
/*
* ERR_166 - no SMTP service for host
*
* DESCRIPTION
* There were WKS records for the target host, though none of
* them listed the SMTP service. This means that the target
* does not accept SMTP connections, and is not likely to be
* something that wants to receive mail. This error is only
* returned for targets that had no MX records.
*
* ACTIONS
* Fail the message.
*
* RESOLUTION
* Use a different target.
*/
error_text = xprintf("router %s: no SMTP service for: %s", rp->name, host);
DEBUG1(DBG_DRIVER_LO, "%s\n", error_text);
return note_error(ERR_NSOWNER | ERR_166, error_text);
}
static struct error *
unknown_service(rp, error)
struct router *rp;
char *error; /* additional error text */
{
char *error_text;
/*
* ERR_167 - unknown service or protocol
*
* DESCRIPTION
* The SMTP service or the TCP protocol numbers could not be
* found. This is a configuration error.
*
* ACTIONS
* Defer the message as a configuration error.
*
* RESOLUTION
* The site administrator should check to make sure that the
* getservbyname() and getprotobyname() functions can find
* the SMTP service and TCP protocol numbers.
*/
error_text = xprintf("router %s: %s", rp->name, error);
DEBUG1(DBG_DRIVER_LO, "%s\n", error_text);
return note_error(ERR_CONFERR | ERR_167, error_text);
}
#endif /* USE_WKS_RECORDS */
static struct error *
no_valid_mx_records(rp, host)
struct router *rp;
char *host; /* target hostname */
{
char *error_text;
/*
* ERR_168 - no valid MX records for host
*
* DESCRIPTION
* There were MX records for the target host, though all of
* them were rejected.
*
* ACTIONS
* Delivery to the target fails.
*
* RESOLUTION
* The postmaster should look into the problem.
*/
error_text = xprintf("router %s: no valid MX records for %s",
rp->name, host);
DEBUG1(DBG_DRIVER_LO, "%s\n", error_text);
return note_error(ERR_NSOWNER | ERR_168, error_text);
}
static struct error *
matched_local_host(rp, host)
struct router *rp;
char *host; /* target hostname */
{
char *error_text;
/*
* ERR_169 - MX record points to local host
*
* DESCRIPTION
* The MX record for the target host points to the local
* host, but the local host is not prepared to handle this
* case.
*
* ACTIONS
* The domain database should probably be looked at.
*
* RESOLUTION
* The postmaster should probably look into the problem.
*/
error_text = xprintf("router %s: MX record for %s points to local host",
rp->name, host);
DEBUG1(DBG_DRIVER_LO, "%s\n", error_text);
return note_error(ERR_NSOWNER | ERR_169, error_text);
}