fix another problem in the MCI state machine
[unix-history] / usr / src / usr.sbin / sendmail / src / daemon.c
index 810736f..0a193ac 100644 (file)
@@ -1,17 +1,30 @@
-# include <errno.h>
-# include "sendmail.h"
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ */
+
+#include <errno.h>
+#include "sendmail.h"
 # include <sys/mx.h>
 
 # include <sys/mx.h>
 
-#ifndef DAEMON
-SCCSID(@(#)daemon.c    4.2             %G%     (w/o daemon mode));
+#ifndef lint
+#ifdef DAEMON
+static char sccsid[] = "@(#)daemon.c   6.5 (Berkeley) %G% (with daemon mode)";
 #else
 #else
+static char sccsid[] = "@(#)daemon.c   6.5 (Berkeley) %G% (without daemon mode)";
+#endif
+#endif /* not lint */
 
 
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <sys/wait.h>
+#ifdef DAEMON
 
 
-SCCSID(@(#)daemon.c    4.2             %G%     (with daemon mode));
+# include <netdb.h>
+# include <sys/signal.h>
+# include <sys/wait.h>
+# include <sys/time.h>
+# include <sys/resource.h>
 
 /*
 **  DAEMON.C -- routines to use when running as a daemon.
 
 /*
 **  DAEMON.C -- routines to use when running as a daemon.
@@ -33,14 +46,14 @@ SCCSID(@(#)daemon.c 4.2             %G%     (with daemon mode));
 **             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.
-**
-**     The semantics of both of these should be clean.
+**     maphostname(map, hbuf, hbufsiz, avp)
+**             Convert the entry in hbuf into a canonical form.
 */
 
 static FILE    *MailPort;      /* port that mail comes in on */
 */
 
 static FILE    *MailPort;      /* port that mail comes in on */
@@ -62,14 +75,16 @@ static FILE *MailPort;      /* port that mail comes in on */
 **             to the communication channel.
 */
 
 **             to the communication channel.
 */
 
-struct sockaddr_in     SendmailAddress;/* internet address of sendmail */
-int    DaemonSocket = -1;              /* fd describing socket */
+int    DaemonSocket    = -1;           /* fd describing socket */
 
 getrequests()
 {
        int t;
 
 getrequests()
 {
        int t;
-       union wait status;
        register struct servent *sp;
        register struct servent *sp;
+       int on = 1;
+       bool refusingconnections = TRUE;
+       struct sockaddr_in srvraddr;
+       extern void reapchild();
 
        /*
        **  Set up the address for the mailer.
 
        /*
        **  Set up the address for the mailer.
@@ -81,21 +96,19 @@ getrequests()
                syserr("server \"smtp\" unknown");
                goto severe;
        }
                syserr("server \"smtp\" unknown");
                goto severe;
        }
-       SendmailAddress.sin_family = AF_INET;
-       SendmailAddress.sin_addr.s_addr = INADDR_ANY;
-       SendmailAddress.sin_port = sp->s_port;
+       srvraddr.sin_family = AF_INET;
+       srvraddr.sin_addr.s_addr = INADDR_ANY;
+       srvraddr.sin_port = sp->s_port;
 
        /*
        **  Try to actually open the connection.
        */
 
 
        /*
        **  Try to actually open the connection.
        */
 
-# ifdef DEBUG
        if (tTd(15, 1))
        if (tTd(15, 1))
-               printf("getrequests: port 0x%x\n", SendmailAddress.sin_port);
-# endif DEBUG
+               printf("getrequests: port 0x%x\n", srvraddr.sin_port);
 
        /* get a socket for the SMTP connection */
 
        /* get a socket for the SMTP connection */
-       DaemonSocket = socket(AF_INET, SOCK_STREAM, 0, 0);
+       DaemonSocket = socket(AF_INET, SOCK_STREAM, 0);
        if (DaemonSocket < 0)
        {
                /* probably another daemon already */
        if (DaemonSocket < 0)
        {
                /* probably another daemon already */
@@ -103,29 +116,28 @@ getrequests()
          severe:
 # ifdef LOG
                if (LogLevel > 0)
          severe:
 # ifdef LOG
                if (LogLevel > 0)
-                       syslog(LOG_SALERT, "cannot get connection");
-# endif LOG
+# endif /* LOG */
                finis();
        }
 
                finis();
        }
 
-#ifdef DEBUG
        /* turn on network debugging? */
        /* turn on network debugging? */
-       if (tTd(15, 15))
-               (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, 0, 0);
-#endif DEBUG
+       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);
 
 
-       if (bind(DaemonSocket, &SendmailAddress, sizeof SendmailAddress, 0) < 0)
+       if (bind(DaemonSocket, (struct sockaddr *)&srvraddr, sizeof srvraddr) < 0)
        {
                syserr("getrequests: cannot bind");
                (void) close(DaemonSocket);
                goto severe;
        }
        {
                syserr("getrequests: cannot bind");
                (void) close(DaemonSocket);
                goto severe;
        }
-       listen(DaemonSocket, 10);
 
 
-# ifdef DEBUG
+       (void) signal(SIGCHLD, reapchild);
+
        if (tTd(15, 1))
                printf("getrequests: %d\n", DaemonSocket);
        if (tTd(15, 1))
                printf("getrequests: %d\n", DaemonSocket);
-# endif DEBUG
 
        struct wh wbuf;
 
 
        struct wh wbuf;
 
@@ -141,9 +153,10 @@ getrequests()
 **     Parameters:
 **             host -- the name of the host.
 **             port -- the port number to connect to.
 **     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
@@ -153,23 +166,36 @@ getrequests()
 **             none.
 */
 
 **             none.
 */
 
-makeconnection(host, port, outfile, infile)
+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 s;
+       register int i, s;
+       register struct hostent *hp = (struct hostent *)NULL;
+       struct sockaddr_in addr;
+       int sav_errno;
+       extern char *inet_ntoa();
+#ifdef NAMED_BIND
+       extern int h_errno;
+#endif
 
        /*
        **  Set up the address for the mailer.
        **      Accept "[a.b.c.d]" syntax for host name.
        */
 
 
        /*
        **  Set up the address for the mailer.
        **      Accept "[a.b.c.d]" syntax for host name.
        */
 
+#ifdef NAMED_BIND
+       h_errno = 0;
+#endif
+       errno = 0;
+
        if (host[0] == '[')
        {
                long hid;
        if (host[0] == '[')
        {
                long hid;
-               register char *p = index(host, ']');
+               register char *p = strchr(host, ']');
 
                if (p != NULL)
                {
 
                if (p != NULL)
                {
@@ -182,15 +208,31 @@ makeconnection(host, port, outfile, infile)
                        usrerr("Invalid numeric domain spec \"%s\"", host);
                        return (EX_NOHOST);
                }
                        usrerr("Invalid numeric domain spec \"%s\"", host);
                        return (EX_NOHOST);
                }
-               SendmailAddress.sin_addr.s_addr = hid;
+               addr.sin_addr.s_addr = hid;
        }
        else
        {
        }
        else
        {
-               register struct hostent *hp = gethostbyname(host);
-
+               hp = gethostbyname(host);
                if (hp == NULL)
                if (hp == NULL)
+               {
+#ifdef NAMED_BIND
+                       if (errno == ETIMEDOUT || h_errno == TRY_AGAIN)
+                               return (EX_TEMPFAIL);
+
+                       /* if name server is specified, assume temp fail */
+                       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);
-               bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
+               }
+               bcopy(hp->h_addr, (char *) &addr.sin_addr, hp->h_length);
+               i = 1;
        }
 
        /*
        }
 
        /*
@@ -198,7 +240,7 @@ makeconnection(host, port, outfile, infile)
        */
 
        if (port != 0)
        */
 
        if (port != 0)
-               SendmailAddress.sin_port = htons(port);
+               addr.sin_port = htons(port);
        else
        {
                register struct servent *sp = getservbyname("smtp", "tcp");
        else
        {
                register struct servent *sp = getservbyname("smtp", "tcp");
@@ -206,51 +248,70 @@ makeconnection(host, port, outfile, infile)
                if (sp == NULL)
                {
                        syserr("makeconnection: server \"smtp\" unknown");
                if (sp == NULL)
                {
                        syserr("makeconnection: server \"smtp\" unknown");
-                       return (EX_OSFILE);
+                       return (EX_OSERR);
                }
                }
-               SendmailAddress.sin_port = sp->s_port;
+               addr.sin_port = sp->s_port;
        }
 
        /*
        **  Try to actually open the connection.
        */
 
        }
 
        /*
        **  Try to actually open the connection.
        */
 
-# ifdef DEBUG
-       if (tTd(16, 1))
-               printf("makeconnection (%s)\n", host);
-# endif DEBUG
-
-#ifdef NVMUNIX
-       s = socket(AF_INET, SOCK_STREAM, 0, 0);
-#else NVMUNIX
-       s = socket(AF_INET, SOCK_STREAM, 0, 0);
-#endif NVMUNIX
-       if (s < 0)
+       for (;;)
        {
        {
-               syserr("makeconnection: no socket");
-               goto failure;
-       }
+               if (tTd(16, 1))
+                       printf("makeconnection (%s [%s])\n", host,
+                           inet_ntoa(addr.sin_addr));
 
 
-# ifdef DEBUG
-       if (tTd(16, 1))
-               printf("makeconnection: %d\n", s);
+               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 */
+               addr.sin_family = AF_INET;
+               if (connect(s, (struct sockaddr *) &addr, sizeof addr) >= 0)
+                       break;
+
+               /* couldn't connect.... figure out why */
+               sav_errno = errno;
+               (void) close(s);
+               if (hp && hp->h_addr_list[i])
+               {
+                       if (tTd(16, 1))
+                               printf("Connect failed; trying new address....\n");
+                       bcopy(hp->h_addr_list[i++], (char *) &addr.sin_addr,
+                                       hp->h_length);
+                       continue;
+               }
 
 
-       /* turn on network debugging? */
-       if (tTd(16, 14))
-               (void) setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0);
-# endif DEBUG
-       (void) fflush(CurEnv->e_xfp);                   /* for debugging */
-#ifdef NVMUNIX
-       bind(s, &SendmailAddress, sizeof SendmailAddress, 0);
-       if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0)
-#else NVMUNIX
-       SendmailAddress.sin_family = AF_INET;
-       if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0)
-#endif NVMUNIX
-       {
                /* failure, decide if temporary or not */
        failure:
                /* failure, decide if temporary or not */
        failure:
-               switch (errno)
+               switch (sav_errno)
                {
                  case EISCONN:
                  case ETIMEDOUT:
                {
                  case EISCONN:
                  case ETIMEDOUT:
@@ -265,23 +326,31 @@ makeconnection(host, port, outfile, infile)
                  case ECONNRESET:
                  case EHOSTUNREACH:
                  case ENETUNREACH:
                  case ECONNRESET:
                  case EHOSTUNREACH:
                  case ENETUNREACH:
+#ifdef ENOSR
+                 case ENOSR:
+#endif
                        /* there are others, I'm sure..... */
                        return (EX_TEMPFAIL);
 
                  case EPERM:
                        /* why is this happening? */
                        syserr("makeconnection: funny failure, addr=%lx, port=%x",
                        /* 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);
-                       /* explicit fall-through */
+                               addr.sin_addr.s_addr, addr.sin_port);
+                       return (EX_TEMPFAIL);
 
                  default:
 
                  default:
-                       return (EX_UNAVAILABLE);
+                       {
+                               extern char *errstring();
+
+                               message(Arpa_Info, "%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(s, "r");
+       mci->mci_out = fdopen(s, "w");
+       mci->mci_in = fdopen(dup(s), "r");
 
        return (EX_OK);
 }
 
        return (EX_OK);
 }
@@ -306,17 +375,88 @@ myhostname(hostbuf, size)
 {
        extern struct hostent *gethostbyname();
        struct hostent *hp;
 {
        extern struct hostent *gethostbyname();
        struct hostent *hp;
-       auto int i = size;
 
 
-       gethostname(hostbuf, &i);
+       if (gethostname(hostbuf, size) < 0)
+       {
+               (void) strcpy(hostbuf, "localhost");
+       }
        hp = gethostbyname(hostbuf);
        if (hp != NULL)
        hp = gethostbyname(hostbuf);
        if (hp != NULL)
+       {
+               (void) strcpy(hostbuf, hp->h_name);
                return (hp->h_aliases);
                return (hp->h_aliases);
+       }
        else
                return (NULL);
 }
        else
                return (NULL);
 }
+\f/*
+**  MAPHOSTNAME -- turn a hostname into canonical form
+**
+**     Parameters:
+**             map -- a pointer to this map (unused).
+**             hbuf -- a buffer containing a hostname.
+**             hbsize -- the size of hbuf.
+**             avp -- unused -- for compatibility with other mapping
+**                     functions.
+**
+**     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 *
+maphostname(map, hbuf, hbsize, avp)
+       MAP *map;
+       char *hbuf;
+       int hbsize;
+       char **avp;
+{
+       register struct hostent *hp;
+       u_long in_addr;
+       char *cp;
+       struct hostent *gethostbyaddr();
+
+       /* allow room for null */
+       hbsize--;
+
+       /*
+        * 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 != '[')
+       {
+               extern bool getcanonname();
+
+               if (getcanonname(hbuf, hbsize))
+                       return hbuf;
+               else
+                       return NULL;
+       }
+       if ((cp = strchr(hbuf, ']')) == NULL)
+               return (NULL);
+       *cp = '\0';
+       in_addr = inet_addr(&hbuf[1]);
+       hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET);
+       if (hp == NULL)
+               return (NULL);
+
+       /* found a match -- copy and dot terminate */
+       if (strlen(hp->h_name) > hbsize)
+               hp->h_name[hbsize] = '\0';
+       (void) strcpy(hbuf, hp->h_name);
+       return hbuf;
+}
 
 
-# else DAEMON
+# else /* DAEMON */
+/* code for systems without sophisticated networking */
 
 /*
 **  MYHOSTNAME -- stub version for case of no daemon code.
 
 /*
 **  MYHOSTNAME -- stub version for case of no daemon code.
@@ -343,5 +483,34 @@ myhostname(hostbuf, size)
        }
        return (NULL);
 }
        }
        return (NULL);
 }
+\f/*
+**  MAPHOSTNAME -- turn a hostname into canonical form
+**
+**     Parameters:
+**             map -- a pointer to the database map.
+**             hbuf -- a buffer containing a hostname.
+**             avp -- a pointer to a (cf file defined) argument vector.
+**
+**     Returns:
+**             mapped host name
+**             FALSE otherwise.
+**
+**     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.
+*/
+
+/*ARGSUSED*/
+char *
+maphostname(map, hbuf, hbsize, avp)
+       MAP *map;
+       char *hbuf;
+       int hbsize;
+       char **avp;
+{
+       return NULL;
+}
 
 
-#endif DAEMON
+#endif /* DAEMON */