This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / usr.sbin / sendmail / src / daemon.c
index dd1b4bf..cfbfec6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 1983 Eric P. Allman
 /*
  * Copyright (c) 1983 Eric P. Allman
- * Copyright (c) 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  */
 
 #include <errno.h>
  */
 
 #include <errno.h>
+#include <signal.h>
 #include "sendmail.h"
 
 #ifndef lint
 #ifdef DAEMON
 #include "sendmail.h"
 
 #ifndef lint
 #ifdef DAEMON
-static char sccsid[] = "@(#)daemon.c   5.37 (Berkeley) 3/2/91 (with daemon mode)";
+static char sccsid[] = "@(#)daemon.c   8.2 (Berkeley) 7/13/93 (with daemon mode)";
 #else
 #else
-static char sccsid[] = "@(#)daemon.c   5.37 (Berkeley) 3/2/91 (without daemon mode)";
+static char sccsid[] = "@(#)daemon.c   8.2 (Berkeley) 7/13/93 (without daemon mode)";
 #endif
 #endif /* not lint */
 
 #endif
 #endif /* not lint */
 
-int la;        /* load average */
-
 #ifdef DAEMON
 
 # include <netdb.h>
 #ifdef DAEMON
 
 # include <netdb.h>
-# include <sys/signal.h>
 # include <sys/wait.h>
 # include <sys/time.h>
 # include <sys/wait.h>
 # include <sys/time.h>
-# include <sys/resource.h>
+
+#ifdef NAMED_BIND
+# include <arpa/nameser.h>
+# include <resolv.h>
+#endif
 
 /*
 **  DAEMON.C -- routines to use when running as a daemon.
 
 /*
 **  DAEMON.C -- routines to use when running as a daemon.
@@ -73,15 +75,14 @@ int la;     /* load average */
 **             etc., to avoid having extra file descriptors during
 **             the queue run and to avoid confusing the network
 **             code (if it cares).
 **             etc., to avoid having extra file descriptors during
 **             the queue run and to avoid confusing the network
 **             code (if it cares).
-**     makeconnection(host, port, outfile, infile)
+**     makeconnection(host, port, outfile, infile, usesecureport)
 **             Make a connection to the named host on the given
 **             port.  Set *outfile and *infile to the files
 **             appropriate for communication.  Returns zero on
 **             success, else an exit status describing the
 **             error.
 **             Make a connection to the named host on the given
 **             port.  Set *outfile and *infile to the files
 **             appropriate for communication.  Returns zero on
 **             success, else an exit status describing the
 **             error.
-**     maphostname(hbuf, hbufsize)
-**             Convert the entry in hbuf into a canonical form.  It
-**             may not be larger than hbufsize.
+**     host_map_lookup(map, hbuf, avp, pstat)
+**             Convert the entry in hbuf into a canonical form.
 */
 \f/*
 **  GETREQUESTS -- open mail IPC port and get requests.
 */
 \f/*
 **  GETREQUESTS -- open mail IPC port and get requests.
@@ -101,41 +102,51 @@ int la;   /* load average */
 **             to the communication channel.
 */
 
 **             to the communication channel.
 */
 
-struct sockaddr_in     SendmailAddress;/* internet address of sendmail */
-
-int    DaemonSocket    = -1;           /* fd describing socket */
-char   *NetName;                       /* name of home (local?) network */
+int            DaemonSocket    = -1;           /* fd describing socket */
+SOCKADDR       DaemonAddr;                     /* socket for incoming */
+int            ListenQueueSize = 10;           /* size of listen queue */
 
 getrequests()
 {
        int t;
        register struct servent *sp;
        int on = 1;
 
 getrequests()
 {
        int t;
        register struct servent *sp;
        int on = 1;
+       bool refusingconnections = TRUE;
+       FILE *pidf;
        extern void reapchild();
 
        /*
        **  Set up the address for the mailer.
        */
 
        extern void reapchild();
 
        /*
        **  Set up the address for the mailer.
        */
 
-       sp = getservbyname("smtp", "tcp");
-       if (sp == NULL)
+       if (DaemonAddr.sin.sin_family == 0)
+               DaemonAddr.sin.sin_family = AF_INET;
+       if (DaemonAddr.sin.sin_addr.s_addr == 0)
+               DaemonAddr.sin.sin_addr.s_addr = INADDR_ANY;
+       if (DaemonAddr.sin.sin_port == 0)
        {
        {
-               syserr("server \"smtp\" unknown");
-               goto severe;
+               sp = getservbyname("smtp", "tcp");
+               if (sp == NULL)
+               {
+                       syserr("554 service \"smtp\" unknown");
+                       goto severe;
+               }
+#ifdef _SCO_unix_
+               DaemonAddr.sin.sin_port = htons(sp->s_port);
+#else
+               DaemonAddr.sin.sin_port = sp->s_port;
+#endif
        }
        }
-       SendmailAddress.sin_family = AF_INET;
-       SendmailAddress.sin_addr.s_addr = INADDR_ANY;
-       SendmailAddress.sin_port = sp->s_port;
 
        /*
        **  Try to actually open the connection.
        */
 
        if (tTd(15, 1))
 
        /*
        **  Try to actually open the connection.
        */
 
        if (tTd(15, 1))
-               printf("getrequests: port 0x%x\n", SendmailAddress.sin_port);
+               printf("getrequests: port 0x%x\n", DaemonAddr.sin.sin_port);
 
        /* get a socket for the SMTP connection */
 
        /* get a socket for the SMTP connection */
-       DaemonSocket = socket(AF_INET, SOCK_STREAM, 0);
+       DaemonSocket = socket(DaemonAddr.sa.sa_family, SOCK_STREAM, 0);
        if (DaemonSocket < 0)
        {
                /* probably another daemon already */
        if (DaemonSocket < 0)
        {
                /* probably another daemon already */
@@ -143,34 +154,55 @@ getrequests()
          severe:
 # ifdef LOG
                if (LogLevel > 0)
          severe:
 # ifdef LOG
                if (LogLevel > 0)
-                       syslog(LOG_ALERT, "cannot get connection");
-# endif LOG
+                       syslog(LOG_ALERT, "problem creating SMTP socket");
+# endif /* LOG */
                finis();
        }
 
        /* turn on network debugging? */
                finis();
        }
 
        /* turn on network debugging? */
-       if (tTd(15, 15))
+       if (tTd(15, 101))
                (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
 
        (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on);
        (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on);
 
                (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
 
        (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on);
        (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on);
 
-       if (bind(DaemonSocket,
-           (struct sockaddr *)&SendmailAddress, sizeof SendmailAddress) < 0)
+       switch (DaemonAddr.sa.sa_family)
        {
        {
-               syserr("getrequests: cannot bind");
-               (void) close(DaemonSocket);
-               goto severe;
+# ifdef NETINET
+         case AF_INET:
+               t = sizeof DaemonAddr.sin;
+               break;
+# endif
+
+# ifdef NETISO
+         case AF_ISO:
+               t = sizeof DaemonAddr.siso;
+               break;
+# endif
+
+         default:
+               t = sizeof DaemonAddr;
+               break;
        }
        }
-       if (listen(DaemonSocket, 10) < 0)
+
+       if (bind(DaemonSocket, &DaemonAddr.sa, t) < 0)
        {
        {
-               syserr("getrequests: cannot listen");
+               syserr("getrequests: cannot bind");
                (void) close(DaemonSocket);
                goto severe;
        }
 
        (void) signal(SIGCHLD, reapchild);
 
                (void) close(DaemonSocket);
                goto severe;
        }
 
        (void) signal(SIGCHLD, reapchild);
 
+       /* write the pid to the log file for posterity */
+       pidf = fopen(PidFile, "w");
+       if (pidf != NULL)
+       {
+               fprintf(pidf, "%d\n", getpid());
+               fclose(pidf);
+       }
+
+
        if (tTd(15, 1))
                printf("getrequests: %d\n", DaemonSocket);
 
        if (tTd(15, 1))
                printf("getrequests: %d\n", DaemonSocket);
 
@@ -178,17 +210,38 @@ getrequests()
        {
                register int pid;
                auto int lotherend;
        {
                register int pid;
                auto int lotherend;
-               extern int RefuseLA;
+               extern bool refuseconnections();
 
                /* see if we are rejecting connections */
 
                /* see if we are rejecting connections */
-               while ((la = getla()) > RefuseLA)
+               CurrentLA = getla();
+               if (refuseconnections())
                {
                {
-                       setproctitle("rejecting connections: load average: %.2f", (double)la);
+                       if (!refusingconnections)
+                       {
+                               /* don't queue so peer will fail quickly */
+                               (void) listen(DaemonSocket, 0);
+                               refusingconnections = TRUE;
+                       }
+                       setproctitle("rejecting connections: load average: %d",
+                               CurrentLA);
                        sleep(5);
                        sleep(5);
+                       continue;
+               }
+
+               if (refusingconnections)
+               {
+                       /* start listening again */
+                       if (listen(DaemonSocket, ListenQueueSize) < 0)
+                       {
+                               syserr("getrequests: cannot listen");
+                               (void) close(DaemonSocket);
+                               goto severe;
+                       }
+                       setproctitle("accepting connections");
+                       refusingconnections = FALSE;
                }
 
                /* wait for a connection */
                }
 
                /* wait for a connection */
-               setproctitle("accepting connections");
                do
                {
                        errno = 0;
                do
                {
                        errno = 0;
@@ -221,9 +274,7 @@ getrequests()
 
                if (pid == 0)
                {
 
                if (pid == 0)
                {
-                       extern struct hostent *gethostbyaddr();
-                       register struct hostent *hp;
-                       char buf[MAXNAME];
+                       extern char *hostnamebyanyaddr();
 
                        /*
                        **  CHILD -- return to caller.
 
                        /*
                        **  CHILD -- return to caller.
@@ -232,33 +283,35 @@ getrequests()
                        */
 
                        (void) signal(SIGCHLD, SIG_DFL);
                        */
 
                        (void) signal(SIGCHLD, SIG_DFL);
+                       OpMode = MD_SMTP;
 
                        /* determine host name */
 
                        /* determine host name */
-                       hp = gethostbyaddr((char *) &RealHostAddr.sin_addr, sizeof RealHostAddr.sin_addr, AF_INET);
-                       if (hp != NULL)
-                               (void) strcpy(buf, hp->h_name);
-                       else
-                       {
-                               extern char *inet_ntoa();
+                       RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr));
 
 
-                               /* produce a dotted quad */
-                               (void) sprintf(buf, "[%s]",
-                                       inet_ntoa(RealHostAddr.sin_addr));
+#ifdef LOG
+                       if (LogLevel > 10)
+                       {
+                               /* log connection information */
+                               syslog(LOG_INFO, "connect from %s (%s)",
+                                       RealHostName, anynet_ntoa(&RealHostAddr));
                        }
                        }
-
-                       /* should we check for illegal connection here? XXX */
-
-                       RealHostName = newstr(buf);
+#endif
 
                        (void) close(DaemonSocket);
                        InChannel = fdopen(t, "r");
                        OutChannel = fdopen(dup(t), "w");
 
                        (void) close(DaemonSocket);
                        InChannel = fdopen(t, "r");
                        OutChannel = fdopen(dup(t), "w");
+
+                       /* should we check for illegal connection here? XXX */
+#ifdef XLA
+                       if (!xla_host_ok(RealHostName))
+                       {
+                               message("421 Too many SMTP sessions for this host");
+                               exit(0);
+                       }
+#endif
+
                        if (tTd(15, 2))
                                printf("getreq: returning\n");
                        if (tTd(15, 2))
                                printf("getreq: returning\n");
-# ifdef LOG
-                       if (LogLevel > 11)
-                               syslog(LOG_DEBUG, "connected, pid=%d", getpid());
-# endif LOG
                        return;
                }
 
                        return;
                }
 
@@ -287,14 +340,162 @@ clrdaemon()
        DaemonSocket = -1;
 }
 \f/*
        DaemonSocket = -1;
 }
 \f/*
+**  SETDAEMONOPTIONS -- set options for running the daemon
+**
+**     Parameters:
+**             p -- the options line.
+**
+**     Returns:
+**             none.
+*/
+
+setdaemonoptions(p)
+       register char *p;
+{
+       if (DaemonAddr.sa.sa_family == AF_UNSPEC)
+               DaemonAddr.sa.sa_family = AF_INET;
+
+       while (p != NULL)
+       {
+               register char *f;
+               register char *v;
+
+               while (isascii(*p) && isspace(*p))
+                       p++;
+               if (*p == '\0')
+                       break;
+               f = p;
+               p = strchr(p, ',');
+               if (p != NULL)
+                       *p++ = '\0';
+               v = strchr(f, '=');
+               if (v == NULL)
+                       continue;
+               while (isascii(*++v) && isspace(*v))
+                       continue;
+
+               switch (*f)
+               {
+                 case 'F':             /* address family */
+                       if (isascii(*v) && isdigit(*v))
+                               DaemonAddr.sa.sa_family = atoi(v);
+#ifdef NETINET
+                       else if (strcasecmp(v, "inet") == 0)
+                               DaemonAddr.sa.sa_family = AF_INET;
+#endif
+#ifdef NETISO
+                       else if (strcasecmp(v, "iso") == 0)
+                               DaemonAddr.sa.sa_family = AF_ISO;
+#endif
+#ifdef NETNS
+                       else if (strcasecmp(v, "ns") == 0)
+                               DaemonAddr.sa.sa_family = AF_NS;
+#endif
+#ifdef NETX25
+                       else if (strcasecmp(v, "x.25") == 0)
+                               DaemonAddr.sa.sa_family = AF_CCITT;
+#endif
+                       else
+                               syserr("554 Unknown address family %s in Family=option", v);
+                       break;
+
+                 case 'A':             /* address */
+                       switch (DaemonAddr.sa.sa_family)
+                       {
+#ifdef NETINET
+                         case AF_INET:
+                               if (isascii(*v) && isdigit(*v))
+                                       DaemonAddr.sin.sin_addr.s_addr = inet_network(v);
+                               else
+                               {
+                                       register struct netent *np;
+
+                                       np = getnetbyname(v);
+                                       if (np == NULL)
+                                               syserr("554 network \"%s\" unknown", v);
+                                       else
+                                               DaemonAddr.sin.sin_addr.s_addr = np->n_net;
+                               }
+                               break;
+#endif
+
+                         default:
+                               syserr("554 Address= option unsupported for family %d",
+                                       DaemonAddr.sa.sa_family);
+                               break;
+                       }
+                       break;
+
+                 case 'P':             /* port */
+                       switch (DaemonAddr.sa.sa_family)
+                       {
+                               short port;
+
+#ifdef NETINET
+                         case AF_INET:
+                               if (isascii(*v) && isdigit(*v))
+                                       DaemonAddr.sin.sin_port = atoi(v);
+                               else
+                               {
+                                       register struct servent *sp;
+
+                                       sp = getservbyname(v, "tcp");
+                                       if (sp == NULL)
+                                               syserr("554 service \"%s\" unknown", v);
+                                       else
+                                       {
+#ifdef _SCO_unix_
+                                               DaemonAddr.sin.sin_port = htons(sp->s_port);
+#else
+                                               DaemonAddr.sin.sin_port = sp->s_port;
+#endif
+                                       }
+                               }
+                               break;
+#endif
+
+#ifdef NETISO
+                         case AF_ISO:
+                               /* assume two byte transport selector */
+                               if (isascii(*v) && isdigit(*v))
+                                       port = atoi(v);
+                               else
+                               {
+                                       register struct servent *sp;
+
+                                       sp = getservbyname(v, "tcp");
+                                       if (sp == NULL)
+                                               syserr("554 service \"%s\" unknown", v);
+                                       else
+                                               port = sp->s_port;
+                               }
+                               bcopy((char *) &port, TSEL(&DaemonAddr.siso), 2);
+                               break;
+#endif
+
+                         default:
+                               syserr("554 Port= option unsupported for family %d",
+                                       DaemonAddr.sa.sa_family);
+                               break;
+                       }
+                       break;
+
+                 case 'L':             /* listen queue size */
+                       ListenQueueSize = atoi(v);
+                       break;
+               }
+       }
+}
+\f/*
 **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
 **
 **     Parameters:
 **             host -- the name of the host.
 **             port -- the port number to connect to.
 **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
 **
 **     Parameters:
 **             host -- the name of the host.
 **             port -- the port number to connect to.
-**             outfile -- a pointer to a place to put the outfile
-**                     descriptor.
-**             infile -- ditto for infile.
+**             mci -- a pointer to the mail connection information
+**                     structure to be filled in.
+**             usesecureport -- if set, use a low numbered (reserved)
+**                     port to provide some rudimentary authentication.
 **
 **     Returns:
 **             An exit code telling whether the connection could be
 **
 **     Returns:
 **             An exit code telling whether the connection could be
@@ -304,16 +505,20 @@ clrdaemon()
 **             none.
 */
 
 **             none.
 */
 
-makeconnection(host, port, outfile, infile)
+SOCKADDR       CurHostAddr;            /* address of current host */
+
+int
+makeconnection(host, port, mci, usesecureport)
        char *host;
        u_short port;
        char *host;
        u_short port;
-       FILE **outfile;
-       FILE **infile;
+       register MCI *mci;
+       bool usesecureport;
 {
        register int i, s;
        register struct hostent *hp = (struct hostent *)NULL;
 {
        register int i, s;
        register struct hostent *hp = (struct hostent *)NULL;
-       extern char *inet_ntoa();
+       SOCKADDR addr;
        int sav_errno;
        int sav_errno;
+       int addrlen;
 #ifdef NAMED_BIND
        extern int h_errno;
 #endif
 #ifdef NAMED_BIND
        extern int h_errno;
 #endif
@@ -327,28 +532,43 @@ makeconnection(host, port, outfile, infile)
        h_errno = 0;
 #endif
        errno = 0;
        h_errno = 0;
 #endif
        errno = 0;
+       bzero(&CurHostAddr, sizeof CurHostAddr);
+       CurHostName = host;
 
        if (host[0] == '[')
        {
                long hid;
 
        if (host[0] == '[')
        {
                long hid;
-               register char *p = index(host, ']');
+               register char *p = strchr(host, ']');
 
                if (p != NULL)
                {
                        *p = '\0';
 
                if (p != NULL)
                {
                        *p = '\0';
+#ifdef NETINET
                        hid = inet_addr(&host[1]);
                        hid = inet_addr(&host[1]);
+                       if (hid == -1)
+#endif
+                       {
+                               /* try it as a host name (avoid MX lookup) */
+                               hp = gethostbyname(&host[1]);
+                               *p = ']';
+                               goto gothostent;
+                       }
                        *p = ']';
                }
                        *p = ']';
                }
-               if (p == NULL || hid == -1)
+               if (p == NULL)
                {
                {
-                       usrerr("Invalid numeric domain spec \"%s\"", host);
+                       usrerr("553 Invalid numeric domain spec \"%s\"", host);
                        return (EX_NOHOST);
                }
                        return (EX_NOHOST);
                }
-               SendmailAddress.sin_addr.s_addr = hid;
+#ifdef NETINET
+               addr.sin.sin_family = AF_INET;          /*XXX*/
+               addr.sin.sin_addr.s_addr = hid;
+#endif
        }
        else
        {
                hp = gethostbyname(host);
        }
        else
        {
                hp = gethostbyname(host);
+gothostent:
                if (hp == NULL)
                {
 #ifdef NAMED_BIND
                if (hp == NULL)
                {
 #ifdef NAMED_BIND
@@ -359,15 +579,25 @@ makeconnection(host, port, outfile, infile)
                        if (errno == ECONNREFUSED && UseNameServer)
                                return (EX_TEMPFAIL);
 #endif
                        if (errno == ECONNREFUSED && UseNameServer)
                                return (EX_TEMPFAIL);
 #endif
-
-                       /*
-                       **  XXX Should look for mail forwarder record here
-                       **  XXX if (h_errno == NO_ADDRESS).
-                       */
-
                        return (EX_NOHOST);
                }
                        return (EX_NOHOST);
                }
-               bcopy(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
+               addr.sa.sa_family = hp->h_addrtype;
+               switch (hp->h_addrtype)
+               {
+#ifdef NETINET
+                 case AF_INET:
+                       bcopy(hp->h_addr,
+                               &addr.sin.sin_addr,
+                               hp->h_length);
+                       break;
+#endif
+
+                 default:
+                       bcopy(hp->h_addr,
+                               addr.sa.sa_data,
+                               hp->h_length);
+                       break;
+               }
                i = 1;
        }
 
                i = 1;
        }
 
@@ -376,100 +606,141 @@ makeconnection(host, port, outfile, infile)
        */
 
        if (port != 0)
        */
 
        if (port != 0)
-               SendmailAddress.sin_port = htons(port);
+               port = htons(port);
        else
        {
                register struct servent *sp = getservbyname("smtp", "tcp");
 
                if (sp == NULL)
                {
        else
        {
                register struct servent *sp = getservbyname("smtp", "tcp");
 
                if (sp == NULL)
                {
-                       syserr("makeconnection: server \"smtp\" unknown");
-                       return (EX_OSFILE);
+                       syserr("554 makeconnection: service \"smtp\" unknown");
+                       return (EX_OSERR);
                }
                }
-               SendmailAddress.sin_port = sp->s_port;
+#ifdef _SCO_unix_
+               port = htons(sp->s_port);
+#else
+               port = sp->s_port;
+#endif
+       }
+
+       switch (addr.sa.sa_family)
+       {
+#ifdef NETINET
+         case AF_INET:
+               addr.sin.sin_port = port;
+               addrlen = sizeof (struct sockaddr_in);
+               break;
+#endif
+
+#ifdef NETISO
+         case AF_ISO:
+               /* assume two byte transport selector */
+               bcopy((char *) &port, TSEL((struct sockaddr_iso *) &addr), 2);
+               addrlen = sizeof (struct sockaddr_iso);
+               break;
+#endif
+
+         default:
+               syserr("Can't connect to address family %d", addr.sa.sa_family);
+               return (EX_NOHOST);
        }
 
        /*
        **  Try to actually open the connection.
        */
 
        }
 
        /*
        **  Try to actually open the connection.
        */
 
-again:
-       if (tTd(16, 1))
-               printf("makeconnection (%s [%s])\n", host,
-                   inet_ntoa(SendmailAddress.sin_addr.s_addr));
+#ifdef XLA
+       /* if too many connections, don't bother trying */
+       if (!xla_noqueue_ok(host))
+               return EX_TEMPFAIL;
+#endif
 
 
-       s = socket(AF_INET, SOCK_STREAM, 0);
-       if (s < 0)
+       for (;;)
        {
        {
-               syserr("makeconnection: no socket");
-               sav_errno = errno;
-               goto failure;
-       }
+               if (tTd(16, 1))
+                       printf("makeconnection (%s [%s])\n",
+                               host, anynet_ntoa(&addr));
 
 
-       if (tTd(16, 1))
-               printf("makeconnection: %d\n", s);
+               /* save for logging */
+               CurHostAddr = addr;
 
 
-       /* turn on network debugging? */
-       if (tTd(16, 14))
-       {
-               int on = 1;
-               (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
-       }
-       if (CurEnv->e_xfp != NULL)
-               (void) fflush(CurEnv->e_xfp);           /* for debugging */
-       errno = 0;                                      /* for debugging */
-       SendmailAddress.sin_family = AF_INET;
-       if (connect(s,
-           (struct sockaddr *)&SendmailAddress, sizeof SendmailAddress) < 0)
-       {
+               if (usesecureport)
+               {
+                       int rport = IPPORT_RESERVED - 1;
+
+                       s = rresvport(&rport);
+               }
+               else
+               {
+                       s = socket(AF_INET, SOCK_STREAM, 0);
+               }
+               if (s < 0)
+               {
+                       sav_errno = errno;
+                       syserr("makeconnection: no socket");
+                       goto failure;
+               }
+
+               if (tTd(16, 1))
+                       printf("makeconnection: fd=%d\n", s);
+
+               /* turn on network debugging? */
+               if (tTd(16, 101))
+               {
+                       int on = 1;
+                       (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG,
+                                         (char *)&on, sizeof on);
+               }
+               if (CurEnv->e_xfp != NULL)
+                       (void) fflush(CurEnv->e_xfp);           /* for debugging */
+               errno = 0;                                      /* for debugging */
+               if (connect(s, (struct sockaddr *) &addr, addrlen) >= 0)
+                       break;
+
+               /* couldn't connect.... figure out why */
                sav_errno = errno;
                (void) close(s);
                if (hp && hp->h_addr_list[i])
                {
                sav_errno = errno;
                (void) close(s);
                if (hp && hp->h_addr_list[i])
                {
-                       bcopy(hp->h_addr_list[i++],
-                           (char *)&SendmailAddress.sin_addr, hp->h_length);
-                       goto again;
+                       if (tTd(16, 1))
+                               printf("Connect failed (%s); trying new address....\n",
+                                       errstring(sav_errno));
+                       switch (addr.sa.sa_family)
+                       {
+#ifdef NETINET
+                         case AF_INET:
+                               bcopy(hp->h_addr_list[i++],
+                                     &addr.sin.sin_addr,
+                                     hp->h_length);
+                               break;
+#endif
+
+                         default:
+                               bcopy(hp->h_addr_list[i++],
+                                       addr.sa.sa_data,
+                                       hp->h_length);
+                               break;
+                       }
+                       continue;
                }
 
                /* failure, decide if temporary or not */
        failure:
                }
 
                /* failure, decide if temporary or not */
        failure:
-               switch (sav_errno)
+#ifdef XLA
+               xla_host_end(host);
+#endif
+               if (transienterror(sav_errno))
+                       return EX_TEMPFAIL;
+               else
                {
                {
-                 case EISCONN:
-                 case ETIMEDOUT:
-                 case EINPROGRESS:
-                 case EALREADY:
-                 case EADDRINUSE:
-                 case EHOSTDOWN:
-                 case ENETDOWN:
-                 case ENETRESET:
-                 case ENOBUFS:
-                 case ECONNREFUSED:
-                 case ECONNRESET:
-                 case EHOSTUNREACH:
-                 case ENETUNREACH:
-                       /* there are others, I'm sure..... */
-                       return (EX_TEMPFAIL);
-
-                 case EPERM:
-                       /* why is this happening? */
-                       syserr("makeconnection: funny failure, addr=%lx, port=%x",
-                               SendmailAddress.sin_addr.s_addr, SendmailAddress.sin_port);
-                       return (EX_TEMPFAIL);
-
-                 default:
-                       {
-                               extern char *errstring();
-
-                               message(Arpa_Info, "%s", errstring(sav_errno));
-                               return (EX_UNAVAILABLE);
-                       }
+                       message("%s", errstring(sav_errno));
+                       return (EX_UNAVAILABLE);
                }
        }
 
        /* connection ok, put it into canonical form */
                }
        }
 
        /* connection ok, put it into canonical form */
-       *outfile = fdopen(s, "w");
-       *infile = fdopen(dup(s), "r");
+       mci->mci_out = fdopen(s, "w");
+       mci->mci_in = fdopen(dup(s), "r");
 
        return (EX_OK);
 }
 
        return (EX_OK);
 }
@@ -484,16 +755,18 @@ again:
 **             A list of aliases for this host.
 **
 **     Side Effects:
 **             A list of aliases for this host.
 **
 **     Side Effects:
-**             none.
+**             Sets the MyIpAddrs buffer to a list of my IP addresses.
 */
 
 */
 
+struct in_addr MyIpAddrs[MAXIPADDR + 1];
+
 char **
 myhostname(hostbuf, size)
        char hostbuf[];
        int size;
 {
 char **
 myhostname(hostbuf, size)
        char hostbuf[];
        int size;
 {
+       register struct hostent *hp;
        extern struct hostent *gethostbyname();
        extern struct hostent *gethostbyname();
-       struct hostent *hp;
 
        if (gethostname(hostbuf, size) < 0)
        {
 
        if (gethostname(hostbuf, size) < 0)
        {
@@ -502,61 +775,495 @@ myhostname(hostbuf, size)
        hp = gethostbyname(hostbuf);
        if (hp != NULL)
        {
        hp = gethostbyname(hostbuf);
        if (hp != NULL)
        {
-               (void) strcpy(hostbuf, hp->h_name);
+               (void) strncpy(hostbuf, hp->h_name, size - 1);
+               hostbuf[size - 1] = '\0';
+
+               if (hp->h_addrtype == AF_INET && hp->h_length == 4)
+               {
+                       register int i;
+
+                       for (i = 0; i < MAXIPADDR; i++)
+                       {
+                               if (hp->h_addr_list[i] == NULL)
+                                       break;
+                               MyIpAddrs[i].s_addr = *(u_long *) hp->h_addr_list[i];
+                       }
+                       MyIpAddrs[i].s_addr = 0;
+               }
+
                return (hp->h_aliases);
        }
        else
                return (NULL);
 }
                return (hp->h_aliases);
        }
        else
                return (NULL);
 }
+\f/*
+**  GETAUTHINFO -- get the real host name asociated with a file descriptor
+**
+**     Uses RFC1413 protocol to try to get info from the other end.
+**
+**     Parameters:
+**             fd -- the descriptor
+**
+**     Returns:
+**             The user@host information associated with this descriptor.
+**
+**     Side Effects:
+**             Sets RealHostName to the name of the host at the other end.
+*/
 
 
-/*
- *  MAPHOSTNAME -- turn a hostname into canonical form
- *
- *     Parameters:
- *             hbuf -- a buffer containing a hostname.
- *             hbsize -- the size of hbuf.
- *
- *     Returns:
- *             none.
- *
- *     Side Effects:
- *             Looks up the host specified in hbuf.  If it is not
- *             the canonical name for that host, replace it with
- *             the canonical name.  If the name is unknown, or it
- *             is already the canonical name, leave it unchanged.
- */
-maphostname(hbuf, hbsize)
-       char *hbuf;
-       int hbsize;
+#ifdef IDENTPROTO
+
+static jmp_buf CtxAuthTimeout;
+
+static
+authtimeout()
+{
+       longjmp(CtxAuthTimeout, 1);
+}
+
+#endif
+
+char *
+getauthinfo(fd)
+       int fd;
+{
+       SOCKADDR fa;
+       int falen;
+       register char *p;
+#ifdef IDENTPROTO
+       SOCKADDR la;
+       int lalen;
+       register struct servent *sp;
+       int s;
+       int i;
+       EVENT *ev;
+#endif
+       static char hbuf[MAXNAME * 2 + 2];
+       extern char *hostnamebyanyaddr();
+       extern char RealUserName[];                     /* main.c */
+
+       falen = sizeof fa;
+       if (getpeername(fd, &fa.sa, &falen) < 0 || falen <= 0)
+       {
+               RealHostName = "localhost";
+               (void) sprintf(hbuf, "%s@localhost", RealUserName);
+               if (tTd(9, 1))
+                       printf("getauthinfo: %s\n", hbuf);
+               return hbuf;
+       }
+
+       RealHostName = newstr(hostnamebyanyaddr(&fa));
+       RealHostAddr = fa;
+
+#ifdef IDENTPROTO
+       lalen = sizeof la;
+       if (fa.sa.sa_family != AF_INET ||
+           getsockname(fd, &la.sa, &lalen) < 0 || lalen <= 0 ||
+           la.sa.sa_family != AF_INET)
+       {
+               /* no ident info */
+               goto noident;
+       }
+
+       /* create ident query */
+       (void) sprintf(hbuf, "%d,%d\r\n",
+               ntohs(fa.sin.sin_port), ntohs(la.sin.sin_port));
+
+       /* create local address */
+       bzero(&la, sizeof la);
+
+       /* create foreign address */
+       sp = getservbyname("auth", "tcp");
+       if (sp != NULL)
+       {
+#ifdef _SCO_unix_
+               fa.sin.sin_port = htons(sp->s_port);
+#else
+               fa.sin.sin_port = sp->s_port;
+#endif
+       }
+       else
+               fa.sin.sin_port = htons(113);
+
+       s = -1;
+       if (setjmp(CtxAuthTimeout) != 0)
+       {
+               if (s >= 0)
+                       (void) close(s);
+               goto noident;
+       }
+
+       /* put a timeout around the whole thing */
+       ev = setevent((time_t) 30, authtimeout, 0);
+
+       /* connect to foreign IDENT server */
+       s = socket(AF_INET, SOCK_STREAM, 0);
+       if (s < 0)
+       {
+               clrevent(ev);
+               goto noident;
+       }
+       if (connect(s, &fa.sa, sizeof fa.sin) < 0)
+       {
+closeident:
+               (void) close(s);
+               clrevent(ev);
+               goto noident;
+       }
+
+       if (tTd(9, 10))
+               printf("getauthinfo: sent %s", hbuf);
+
+       /* send query */
+       if (write(s, hbuf, strlen(hbuf)) < 0)
+               goto closeident;
+
+       /* get result */
+       i = read(s, hbuf, sizeof hbuf);
+       (void) close(s);
+       clrevent(ev);
+       if (i <= 0)
+               goto noident;
+       if (hbuf[--i] == '\n' && hbuf[--i] == '\r')
+               i--;
+       hbuf[++i] = '\0';
+
+       if (tTd(9, 3))
+               printf("getauthinfo:  got %s\n", hbuf);
+
+       /* parse result */
+       p = strchr(hbuf, ':');
+       if (p == NULL)
+       {
+               /* malformed response */
+               goto noident;
+       }
+       while (isascii(*++p) && isspace(*p))
+               continue;
+       if (strncasecmp(p, "userid", 6) != 0)
+       {
+               /* presumably an error string */
+               goto noident;
+       }
+       p += 6;
+       while (isascii(*p) && isspace(*p))
+               p++;
+       if (*p++ != ':')
+       {
+               /* either useridxx or malformed response */
+               goto noident;
+       }
+
+       /* p now points to the OSTYPE field */
+       p = strchr(p, ':');
+       if (p == NULL)
+       {
+               /* malformed response */
+               goto noident;
+       }
+
+       /* 1413 says don't do this -- but it's broken otherwise */
+       while (isascii(*++p) && isspace(*p))
+               continue;
+
+       /* p now points to the authenticated name */
+       (void) sprintf(hbuf, "%s@%s", p, RealHostName);
+       goto finish;
+
+#endif /* IDENTPROTO */
+
+noident:
+       (void) strcpy(hbuf, RealHostName);
+
+finish:
+       if (RealHostName[0] != '[')
+       {
+               p = &hbuf[strlen(hbuf)];
+               (void) sprintf(p, " [%s]", anynet_ntoa(&RealHostAddr));
+       }
+       if (tTd(9, 1))
+               printf("getauthinfo: %s\n", hbuf);
+       return hbuf;
+}
+\f/*
+**  HOST_MAP_LOOKUP -- turn a hostname into canonical form
+**
+**     Parameters:
+**             map -- a pointer to this map (unused).
+**             name -- the (presumably unqualified) hostname.
+**             av -- unused -- for compatibility with other mapping
+**                     functions.
+**             statp -- an exit status (out parameter) -- set to
+**                     EX_TEMPFAIL if the name server is unavailable.
+**
+**     Returns:
+**             The mapping, if found.
+**             NULL if no mapping found.
+**
+**     Side Effects:
+**             Looks up the host specified in hbuf.  If it is not
+**             the canonical name for that host, return the canonical
+**             name.
+*/
+
+char *
+host_map_lookup(map, name, av, statp)
+       MAP *map;
+       char *name;
+       char **av;
+       int *statp;
 {
        register struct hostent *hp;
        u_long in_addr;
 {
        register struct hostent *hp;
        u_long in_addr;
-       char ptr[256], *cp;
-       struct hostent *gethostbyaddr();
+       char *cp;
+       int i;
+       register STAB *s;
+       char hbuf[MAXNAME];
+       extern struct hostent *gethostbyaddr();
+       extern int h_errno;
+
+       /*
+       **  See if we have already looked up this name.  If so, just
+       **  return it.
+       */
+
+       s = stab(name, ST_NAMECANON, ST_ENTER);
+       if (bitset(NCF_VALID, s->s_namecanon.nc_flags))
+       {
+               if (tTd(9, 1))
+                       printf("host_map_lookup(%s) => CACHE %s\n",
+                               name, s->s_namecanon.nc_cname);
+               errno = s->s_namecanon.nc_errno;
+               h_errno = s->s_namecanon.nc_herrno;
+               *statp = s->s_namecanon.nc_stat;
+               return s->s_namecanon.nc_cname;
+       }
 
        /*
 
        /*
-        * If first character is a bracket, then it is an address
-        * lookup.  Address is copied into a temporary buffer to
-        * strip the brackets and to preserve hbuf if address is
-        * unknown.
-        */
-       if (*hbuf != '[') {
-               getcanonname(hbuf, hbsize);
-               return;
-       }
-       if ((cp = index(strcpy(ptr, hbuf), ']')) == NULL)
-               return;
+       **  If first character is a bracket, then it is an address
+       **  lookup.  Address is copied into a temporary buffer to
+       **  strip the brackets and to preserve name if address is
+       **  unknown.
+       */
+
+       if (*name != '[')
+       {
+               extern bool getcanonname();
+
+               if (tTd(9, 1))
+                       printf("host_map_lookup(%s) => ", name);
+               s->s_namecanon.nc_flags |= NCF_VALID;           /* will be soon */
+               (void) strcpy(hbuf, name);
+               if (getcanonname(hbuf, sizeof hbuf - 1))
+               {
+                       if (tTd(9, 1))
+                               printf("%s\n", hbuf);
+                       cp = map_rewrite(map, hbuf, strlen(hbuf), av);
+                       s->s_namecanon.nc_cname = newstr(cp);
+                       return cp;
+               }
+               else
+               {
+                       register struct hostent *hp;
+
+                       if (tTd(9, 1))
+                               printf("FAIL (%d)\n", h_errno);
+                       s->s_namecanon.nc_errno = errno;
+                       s->s_namecanon.nc_herrno = h_errno;
+                       switch (h_errno)
+                       {
+                         case TRY_AGAIN:
+                               if (UseNameServer)
+                               {
+                                       char *msg = "Recipient domain nameserver timed out";
+
+                                       message(msg);
+                                       if (CurEnv->e_message == NULL)
+                                               CurEnv->e_message = newstr(msg);
+                               }
+                               *statp = EX_TEMPFAIL;
+                               break;
+
+                         case HOST_NOT_FOUND:
+                               *statp = EX_NOHOST;
+                               break;
+
+                         case NO_RECOVERY:
+                               *statp = EX_SOFTWARE;
+                               break;
+
+                         default:
+                               *statp = EX_UNAVAILABLE;
+                               break;
+                       }
+                       s->s_namecanon.nc_stat = *statp;
+                       if (*statp != EX_TEMPFAIL || UseNameServer)
+                               return NULL;
+
+                       /*
+                       **  Try to look it up in /etc/hosts
+                       */
+
+                       hp = gethostbyname(name);
+                       if (hp == NULL)
+                       {
+                               /* no dice there either */
+                               s->s_namecanon.nc_stat = *statp = EX_NOHOST;
+                               return NULL;
+                       }
+
+                       s->s_namecanon.nc_stat = *statp = EX_OK;
+                       cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), av);
+                       s->s_namecanon.nc_cname = newstr(cp);
+                       return cp;
+               }
+       }
+       if ((cp = strchr(name, ']')) == NULL)
+               return (NULL);
        *cp = '\0';
        *cp = '\0';
-       in_addr = inet_addr(&ptr[1]);
+       in_addr = inet_addr(&name[1]);
+
+       /* check to see if this is one of our addresses */
+       for (i = 0; MyIpAddrs[i].s_addr != 0; i++)
+       {
+               if (MyIpAddrs[i].s_addr == in_addr)
+               {
+                       return map_rewrite(map, MyHostName, strlen(MyHostName), av);
+               }
+       }
+
+       /* nope -- ask the name server */
        hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET);
        hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET);
+       s->s_namecanon.nc_errno = errno;
+       s->s_namecanon.nc_herrno = h_errno;
+       s->s_namecanon.nc_flags |= NCF_VALID;           /* will be soon */
        if (hp == NULL)
        if (hp == NULL)
-               return;
-       if (strlen(hp->h_name) >= hbsize)
-               hp->h_name[hbsize - 1] = '\0';
-       (void)strcpy(hbuf, hp->h_name);
+       {
+               s->s_namecanon.nc_stat = *statp = EX_NOHOST;
+               return (NULL);
+       }
+
+       /* found a match -- copy out */
+       cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), av);
+       s->s_namecanon.nc_stat = *statp = EX_OK;
+       s->s_namecanon.nc_cname = newstr(cp);
+       return cp;
+}
+\f/*
+**  ANYNET_NTOA -- convert a network address to printable form.
+**
+**     Parameters:
+**             sap -- a pointer to a sockaddr structure.
+**
+**     Returns:
+**             A printable version of that sockaddr.
+*/
+
+char *
+anynet_ntoa(sap)
+       register SOCKADDR *sap;
+{
+       register char *bp;
+       register char *ap;
+       int l;
+       static char buf[80];
+
+       /* check for null/zero family */
+       if (sap == NULL)
+               return "NULLADDR";
+       if (sap->sa.sa_family == 0)
+               return "0";
+
+#ifdef NETINET
+       if (sap->sa.sa_family == AF_INET)
+       {
+               extern char *inet_ntoa();
+
+               return inet_ntoa(((struct sockaddr_in *) sap)->sin_addr);
+       }
+#endif
+
+       /* unknown family -- just dump bytes */
+       (void) sprintf(buf, "Family %d: ", sap->sa.sa_family);
+       bp = &buf[strlen(buf)];
+       ap = sap->sa.sa_data;
+       for (l = sizeof sap->sa.sa_data; --l >= 0; )
+       {
+               (void) sprintf(bp, "%02x:", *ap++ & 0377);
+               bp += 3;
+       }
+       *--bp = '\0';
+       return buf;
+}
+\f/*
+**  HOSTNAMEBYANYADDR -- return name of host based on address
+**
+**     Parameters:
+**             sap -- SOCKADDR pointer
+**
+**     Returns:
+**             text representation of host name.
+**
+**     Side Effects:
+**             none.
+*/
+
+char *
+hostnamebyanyaddr(sap)
+       register SOCKADDR *sap;
+{
+       register struct hostent *hp;
+
+#ifdef NAMED_BIND
+       int saveretry;
+
+       /* shorten name server timeout to avoid higher level timeouts */
+       saveretry = _res.retry;
+       _res.retry = 3;
+#endif /* NAMED_BIND */
+
+       switch (sap->sa.sa_family)
+       {
+#ifdef NETINET
+         case AF_INET:
+               hp = gethostbyaddr((char *) &sap->sin.sin_addr,
+                       sizeof sap->sin.sin_addr,
+                       AF_INET);
+               break;
+#endif
+
+#ifdef NETISO
+         case AF_ISO:
+               hp = gethostbyaddr((char *) &sap->siso.siso_addr,
+                       sizeof sap->siso.siso_addr,
+                       AF_ISO);
+               break;
+#endif
+
+         default:
+               hp = gethostbyaddr(sap->sa.sa_data,
+                          sizeof sap->sa.sa_data,
+                          sap->sa.sa_family);
+               break;
+       }
+
+#ifdef NAMED_BIND
+       _res.retry = saveretry;
+#endif /* NAMED_BIND */
+
+       if (hp != NULL)
+               return hp->h_name;
+       else
+       {
+               /* produce a dotted quad */
+               static char buf[512];
+
+               (void) sprintf(buf, "[%s]", anynet_ntoa(sap));
+               return buf;
+       }
 }
 
 }
 
-# else DAEMON
+# else /* DAEMON */
 /* code for systems without sophisticated networking */
 
 /*
 /* code for systems without sophisticated networking */
 
 /*
@@ -585,28 +1292,61 @@ myhostname(hostbuf, size)
        return (NULL);
 }
 \f/*
        return (NULL);
 }
 \f/*
+**  GETAUTHINFO -- get the real host name asociated with a file descriptor
+**
+**     Parameters:
+**             fd -- the descriptor
+**
+**     Returns:
+**             The host name associated with this descriptor, if it can
+**                     be determined.
+**             NULL otherwise.
+**
+**     Side Effects:
+**             none
+*/
+
+char *
+getauthinfo(fd)
+       int fd;
+{
+       return NULL;
+}
+\f/*
 **  MAPHOSTNAME -- turn a hostname into canonical form
 **
 **     Parameters:
 **  MAPHOSTNAME -- turn a hostname into canonical form
 **
 **     Parameters:
-**             hbuf -- a buffer containing a hostname.
-**             hbsize -- the size of hbuf.
+**             map -- a pointer to the database map.
+**             name -- a buffer containing a hostname.
+**             avp -- a pointer to a (cf file defined) argument vector.
+**             statp -- an exit status (out parameter).
 **
 **     Returns:
 **
 **     Returns:
-**             none.
+**             mapped host name
+**             FALSE otherwise.
 **
 **     Side Effects:
 **
 **     Side Effects:
-**             Looks up the host specified in hbuf.  If it is not
+**             Looks up the host specified in name.  If it is not
 **             the canonical name for that host, replace it with
 **             the canonical name.  If the name is unknown, or it
 **             is already the canonical name, leave it unchanged.
 */
 
 /*ARGSUSED*/
 **             the canonical name for that host, replace it with
 **             the canonical name.  If the name is unknown, or it
 **             is already the canonical name, leave it unchanged.
 */
 
 /*ARGSUSED*/
-maphostname(hbuf, hbsize)
-       char *hbuf;
-       int hbsize;
+char *
+host_map_lookup(map, name, avp, statp)
+       MAP *map;
+       char *name;
+       char **avp;
+       char *statp;
 {
 {
-       return;
+       register struct hostent *hp;
+
+       hp = gethostbyname(name);
+       if (hp != NULL)
+               return hp->h_name;
+       *statp = EX_NOHOST;
+       return NULL;
 }
 
 }
 
-#endif DAEMON
+#endif /* DAEMON */