Domains and Sendmail

Craig Partridge craig at LOKI.ARPA
Thu Oct 24 03:50:06 AEST 1985


    This is a simple change to (4.2) sendmail that will make it work
with the domain server's mail database.  As many of you are probably
aware, the domain servers are now storing information about where
mail addressed to a given host should actually be delivered.

    Attached is shar file which contains a change to sendmail to
make sendmail query the domain server for Mail information about
the host it is about to send to.  It then routes the mail based
on the information it receives (preferring Mail Destination records
over  Mail Forwarders, and if it can't find either, sends to
the originally indicated host).

    I've tested this code as far as I can -- which is less than
I'd like.  We no longer run sendmail here, so I had to bring
it up specially (and briefly) on a machine here and then pepper
it with messages (after diddling our local domain server to
cause some special failures).  People would probably do well
to treat this code as somewhat experimental.  You will also
need to have applied the diff to sendmail that keeps it from
appending .ARPA to every host name (I think GaTech sent this
out with their sendmail stuff).

    Also included in this message is a new library routine to add
to the resolver library and a new include file <maildb.h>.
This is the code which actually queries the server.
A man page is also included -- it mentions a routine (getmailboxname())
which is not included, in part because (1) I'm not sure people
need it and (2) that I fully understand the problems involved
with putting mailing lists in the databases, much less believe
that the routine described is the proper interface.

    I hope people find this useful.

Craig Partridge
CSNET Technical Staff

craig at csnet-sh (CSNET)
craig at loki.arpa or craig at loki.bbn.com (ARPA)
{decvax,ihnp4,wjh12}!bbncca!craig (USENET)

------------------------cut here--------------------------------
#!/bin/sh

sed  's/^# //' << '!FUNKY_STUFF!' > sendmail.diff
# *** daemon.c.old	Tue Oct 22 16:39:28 1985
# --- daemon.c	Tue Oct 22 16:36:33 1985
# ***************
# *** 1,5
#   /* 
# !  * $Header: daemon.c,v 1.1 85/10/21 15:43:43 craig Exp $ 
#    * 
#    * $Log:	daemon.c,v $
#    * Revision 1.1  85/10/21  15:43:43  craig
# 
# --- 1,5 -----
#   /* 
# !  * $Header: daemon.c,v 1.2 85/10/22 16:36:18 craig Exp $ 
#    * 
#    * $Log:	daemon.c,v $
#    * Revision 1.2  85/10/22  16:36:18  craig
# ***************
# *** 2,7
#    * $Header: daemon.c,v 1.1 85/10/21 15:43:43 craig Exp $ 
#    * 
#    * $Log:	daemon.c,v $
#    * Revision 1.1  85/10/21  15:43:43  craig
#    * Initial revision
#    *  
# 
# --- 2,10 -----
#    * $Header: daemon.c,v 1.2 85/10/22 16:36:18 craig Exp $ 
#    * 
#    * $Log:	daemon.c,v $
# +  * Revision 1.2  85/10/22  16:36:18  craig
# +  * update to work with domains
# +  * 
#    * Revision 1.1  85/10/21  15:43:43  craig
#    * Initial revision
#    *  
# ***************
# *** 9,14
#   */
#   # include <errno.h>
#   # include "sendmail.h"
#   
# 
# --- 12,18 -----
#   */
#   # include <errno.h>
#   # include "sendmail.h"
# + # include <maildb.h>
#   
# ***************
# *** 303,309
#   	}
#   	else
#   	{
# ! 		register struct hostent *hp = gethostbyname(host);
#   
#   		if (hp == NULL)
#   			return (EX_NOHOST);
# 
# --- 307,316 -----
#   	}
#   	else
#   	{
# ! 		register struct hostent *hp;
# ! #ifdef DOMAINS
# ! 		register struct mailent *me;
# ! 		extern struct mailent *getmailhostbyname();
#   
#   		me = getmailhostbyname(host);
#   
# ***************
# *** 305,310
#   	{
#   		register struct hostent *hp = gethostbyname(host);
#   
#   		if (hp == NULL)
#   			return (EX_NOHOST);
#   		bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
# 
# --- 312,340 -----
#   		register struct mailent *me;
#   		extern struct mailent *getmailhostbyname();
#   
# + 		me = getmailhostbyname(host);
# + 
# + 		/* bad ? */
# + 		if (me == NULL)
# + 		    return (EX_NOHOST);
# + 
# + 		/* couldn't resolve? */
# + 		if (me->mailname == NULL)
# + 		    return(EX_TEMPFAIL);
# + 
# + 		if (me->maildest != NULL)
# + 		    hp = gethostbyname(me->maildest);
# + 		else if (me->mailforw != NULL)
# + 		    hp = gethostbyname(*(me->mailforw));
# + 		else
# + 		    hp = gethostbyname(me->mailname);
# + 
# + 		/*
# + 		 * treat failure to get address as transient.  We
# + 		 * know the host exists, so something must be
# + 		 * wrong in the database.  Hopefully it will
# + 		 * be fixed before we return message to sender.
# + 		 */
#   		if (hp == NULL)
#   		    return(EX_TEMPFAIL);
#   
# ***************
# *** 306,312
#   		register struct hostent *hp = gethostbyname(host);
#   
#   		if (hp == NULL)
# ! 			return (EX_NOHOST);
#   		bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
#   	}
#   
# 
# --- 336,351 -----
#  		 * be fixed before we return message to sender.
#   		 */
#   		if (hp == NULL)
# ! 		    return(EX_TEMPFAIL);
# ! 
# ! 		syslog(LOG_INFO,"sending to host (%s) via (%s)",host,hp->h_name);
# ! #else /* not DOMAINS */
# ! 		hp = gethostbyname(host);
# ! 
# ! 		if (hp == NULL)
# ! 		    return(EX_NOHOST);
# ! #endif /* DOMAINS */
# ! 
#   		bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
#   	}
#   
!FUNKY_STUFF!
sed  's/^# //' << '!FUNKY_STUFF!' > mailhost.c
# /**************************************************************************/
# /*                                                                        */
# /*  routine to determine the appropriate mail destination/forwarder for   */
# /*  a given host.                                                         */
# /*                                                                        */
# /*  written by Craig Partridge, BBN Labs, 1985.                           */
# /**************************************************************************/
# 
# #include <sys/types.h>
# #include <sys/socket.h>
# #include <netinet/in.h>
# #include <arpa/nameser.h>
# #include <arpa/resolv.h>
# 
# #include <maildb.h>
# 
# #define MAXFORW 5
# 
# static struct mailent mailhost;
# static char truename[MAXDNAME];
# static char *forw[MAXFORW+1];
# static char buf[1024];
# 
# extern char *strcpy(), *strncpy();
# 
# struct mailent *
# getmailhostbyname(name)
# char *name;
# {
#     HEADER *hp;
#     int sent, recvd, acount, buflen, len, newname, dlen;
#     u_short type, class, qcnt;
#     char *cp, *bp, *eom, **fp;
#     union {
# 	HEADER a_hp;	/* want to force proper alignment */
# 	char a_ans[PACKETSZ];
#     } ans;
# 
#     (void) strncpy(truename,name,sizeof(truename)-1);
#     newname = 0;
# 
#     /* redo query if we find cname */
# restart:
# 
#     sent = res_mkquery(QUERY,truename,C_ANY,T_MAILA,(char *)0,0,
# 	(struct rrec *)0, buf,sizeof(buf));
# 
#     /* something is probably wrong with the domain name */
#     if (sent < 0)
# 	return(0);
# 
#     /* use all 0's in structure to indicate we can't resolve */
#     mailhost.maildest = 0;
#     mailhost.mailforw = 0;
#     mailhost.mailname = 0;
# 
#     /* if res_send dies it is send() problem -- transient error */
#     if ((recvd = res_send(buf,sent,(char *)&ans.a_hp,PACKETSZ)) < 0)
# 	return(&mailhost);
# 
#     hp = &ans.a_hp;
#     acount = ntohs(hp->ancount);
# 
#     /* not valid name */
#     if (hp->rcode != NOERROR)
# 	return(0);
# 
#     /* got something valid so set name field */
#     mailhost.mailname = truename;
# 
#     /* valid name but no dest or forw */
#     if ((hp->rcode == NOERROR) && (acount == 0))
# 	return(&mailhost);
# 
#     bp = buf;
#     buflen = sizeof(buf);
# 
#     cp = ((char *)hp) + sizeof(*hp);
#     eom = ((char *)hp) + recvd;
# 
#     fp = forw;
# 
#     qcnt = ntohs(hp->qdcount);
#     while (qcnt--)
# 	cp += dn_skip(cp) + QFIXEDSZ;
# 
# 
#     while ((--acount >= 0) && (cp < eom) && (buflen>0))
#     {
# 	/* expand though we then discard */
# 	if ((len = dn_expand((char *)&ans.a_hp,cp,bp,buflen)) < 0)
# 	    break;
# 
# 	cp += len;
# 
# 	type = getshort(cp);
# 	cp += sizeof(type);
# 
# 	class = getshort(cp);
# 	cp += sizeof(class) + sizeof(u_long) + sizeof(u_short);
# 
# 	if ((dlen = dn_expand(ans.a_ans,cp,bp,buflen)) < 0)
# 	    break;
# 
# 	cp += dlen;
# 
# 	/* cname? */
# 	if (type == T_CNAME)
# 	{
# 	    (void) strcpy(truename,bp);
# 	    if (newname++ > 0)
# 		return(0);	/* cname loop */
# 	    goto restart;
# 	}
# 
# 	/* interesting stuff? */
# 	if ((class != C_IN) || ((type != T_MD) && (type != T_MF)))
# 	    continue;
# 
# 	len = strlen(bp) + 1;
# 
# 	/* couldn't store it if we wanted to */
# 	if (len > buflen)
# 	    continue;
# 
# 	/* we should only see one */
# 	if (type == T_MD)
# 	{
# 	    if (mailhost.maildest != 0)
# 	    {
# 		if (_res.options & RES_DEBUG)
# 		    printf("%s has more than one mail destination?\n",truename);
# 		continue;
# 	    }
# 	    mailhost.maildest = bp;
# 	    bp += len;
# 	    buflen -= len;
# 	}
# 	else if ((type == T_MF) && ((fp - forw) < MAXFORW))
# 	{
# 	    if (mailhost.mailforw == 0)
# 		mailhost.mailforw = forw;
# 
# 	    *fp++ = bp;
# 	    *fp = 0;
# 	    bp += len;
# 	    buflen -= len;
# 	}
#     } /* end of while */
# 
#     return(&mailhost);
# }
!FUNKY_STUFF!
sed  's/^# //' << '!FUNKY_STUFF!' > maildb.h
# /**************************************************************************/
# /*                                                                        */
# /*              structures used by mail database routines                 */
# /*                                                                        */
# /**************************************************************************/
# 
# struct mailent {		/* returned when doing mail agent queries */
#     char *mailname;		/* true domain name */
#     char *maildest;		/* dest == 0 if no dest given by server */
#     char **mailforw;		/* forw == 0 if no forwarders */
# };
# 
# struct mboxent {		
#     char *mailbox;		/* resolved mailbox name */
#     char *mailhost;		/* mailhost to send to */
#     char **mailgroup;		/* list of mailbox names in group */
# };
# 
!FUNKY_STUFF!
sed  's/^# //' << '!FUNKY_STUFF!' > maildb.3n
# .TH MAILDB 3 "Oct 1985"
# .UC 4
# .SH NAME
# maildb \- routines for querying the nameserver about mailboxes
# .SH SYNOPSIS
# .PP
# .B "#include <maildb.h>
# .PP
# .B "struct mailent *getmailhostbyname(name)
# .br
# .B "char *name;
# .PP
# .B "struct mboxent *getmboxbyname(name)
# .br
# .B "char *name;
# .SH DESCRIPTION
# .PP
# .I Getmailhostbyname
# and
# .I getmboxbyname
# attempt to locate mail related information about a host or mailbox.
# .PP
# .I Getmailhostbyname
# attempts to determine where mail addressed to host
# .I name
# should actually be delivered.  It returns a pointer to a struct\ mailent
# (shown below) or the zero pointer if the
# domain name
# proves invalid.  The nameserver can return two types of information:
# the name of a mail \fIdestination\fP, which is where it believes mailboxes
# listed at host \fIname\fP actually reside, and/or the names of mail
# \fIforwarders\fP which are simply hosts which agree to relay mail
# to \fIname\fP.  The \fImailname\fP entry is filled in with the
# proper domain name of the host being queried about.
# .PP
# It is possible that
# .I getmailhostbyname
# may be unable to reach a domain server to service its query.  In
# this (usually rare) case, it returns a structure with all 0
# pointers to indicate it couldn't answer the question.
# .RS
# .PP
# .nf
# struct	mailent {
#     char *mailname;		/* corrected domain name */
#     char *maildest;		/* dest == 0 if no dest given by server */
#     char **mailforw;		/* forw == 0 if no forwarders */
# };
# .ft R
# .ad
# .fi
# .RE
# .PP
# .I Getmboxbyname
# attempts to locate where to deliver mail addressed to
# .I name,
# where
# .I name
# is in domain-name format.  It returns a struct\ mboxent (shown below),
# or the zero pointer on error or if the name is invalid.  If
# .I name
# is an alias for another mailbox name, the
# .I mailbox
# field will have been rewritten to contain the true name of the mailbox,
# otherwise it is simply a copy of the name passed.
# The
# .I mailhost
# field contains the name of the host to which mail addressed to the
# given mailbox should actually be delivered, unless the
# .I mailgroup
# field is non-null, in which case
# .I mailhost
# is the address of a host agreeing to expand the list if you would
# prefer not to.
# The
# .I mailgroup
# field if non-null, indicates that the mailbox was actually a mailing
# list, and contains a null terminated list of the members of the mailing
# group.  Note that if the mailbox expands to a mailing list which is
# too large to fit into the static space used by
# .I getmboxbyname
# then the routine will do one of two things: (1) if a
# .I mailhost
# willing to expand the names exist, the
# .I mailhost
# name will be returned but the
# .I mailgroup
# field will be null.  If no mailhost was specified, all fields,
# including
# .I mailbox
# are set to null to indicate that the name was valid, but that the
# library routine ran out of space to expand the answer.
# .RS
# .PP
# .nf
# struct	mboxent {
#     char *mailbox;		/* resolved mailbox name */
#     char *mailhost;		/* mailhost to send to */
#     char **mailgroup;		/* list of mailbox names in group */
# };
# .ft R
# .ad
# .fi
# .RE
# .SH SEE ALSO
# named(8), resolver(3), gethostbyname(3)
# .SH BUGS
# The routines return structures which are overwritten on every call.
!FUNKY_STUFF!



More information about the Comp.unix.wizards mailing list