summaryrefslogtreecommitdiffstats
path: root/usr.sbin/sendmail/src/daemon.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/sendmail/src/daemon.c')
-rw-r--r--usr.sbin/sendmail/src/daemon.c1547
1 files changed, 1547 insertions, 0 deletions
diff --git a/usr.sbin/sendmail/src/daemon.c b/usr.sbin/sendmail/src/daemon.c
new file mode 100644
index 0000000..293438b
--- /dev/null
+++ b/usr.sbin/sendmail/src/daemon.c
@@ -0,0 +1,1547 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include "sendmail.h"
+
+#ifndef lint
+#ifdef DAEMON
+static char sccsid[] = "@(#)daemon.c 8.48 (Berkeley) 4/18/94 (with daemon mode)";
+#else
+static char sccsid[] = "@(#)daemon.c 8.48 (Berkeley) 4/18/94 (without daemon mode)";
+#endif
+#endif /* not lint */
+
+#ifdef DAEMON
+
+# include <netdb.h>
+# include <arpa/inet.h>
+
+#if NAMED_BIND
+# include <arpa/nameser.h>
+# include <resolv.h>
+#endif
+
+/*
+** DAEMON.C -- routines to use when running as a daemon.
+**
+** This entire file is highly dependent on the 4.2 BSD
+** interprocess communication primitives. No attempt has
+** been made to make this file portable to Version 7,
+** Version 6, MPX files, etc. If you should try such a
+** thing yourself, I recommend chucking the entire file
+** and starting from scratch. Basic semantics are:
+**
+** getrequests()
+** Opens a port and initiates a connection.
+** Returns in a child. Must set InChannel and
+** OutChannel appropriately.
+** clrdaemon()
+** Close any open files associated with getting
+** the connection; this is used when running the queue,
+** 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, 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.
+** host_map_lookup(map, hbuf, avp, pstat)
+** Convert the entry in hbuf into a canonical form.
+*/
+ /*
+** GETREQUESTS -- open mail IPC port and get requests.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Waits until some interesting activity occurs. When
+** it does, a child is created to process it, and the
+** parent waits for completion. Return from this
+** routine is always in the child. The file pointers
+** "InChannel" and "OutChannel" should be set to point
+** to the communication channel.
+*/
+
+int DaemonSocket = -1; /* fd describing socket */
+SOCKADDR DaemonAddr; /* socket for incoming */
+int ListenQueueSize = 10; /* size of listen queue */
+int TcpRcvBufferSize = 0; /* size of TCP receive buffer */
+int TcpSndBufferSize = 0; /* size of TCP send buffer */
+
+getrequests()
+{
+ int t;
+ bool refusingconnections = TRUE;
+ FILE *pidf;
+ int socksize;
+#ifdef XDEBUG
+ bool j_has_dot;
+#endif
+ extern void reapchild();
+
+ /*
+ ** Set up the address for the mailer.
+ */
+
+ 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)
+ {
+ register struct servent *sp;
+
+ sp = getservbyname("smtp", "tcp");
+ if (sp == NULL)
+ {
+ syserr("554 service \"smtp\" unknown");
+ DaemonAddr.sin.sin_port = htons(25);
+ }
+ else
+ DaemonAddr.sin.sin_port = sp->s_port;
+ }
+
+ /*
+ ** Try to actually open the connection.
+ */
+
+ if (tTd(15, 1))
+ printf("getrequests: port 0x%x\n", DaemonAddr.sin.sin_port);
+
+ /* get a socket for the SMTP connection */
+ socksize = opendaemonsocket(TRUE);
+
+ (void) setsignal(SIGCHLD, reapchild);
+
+ /* write the pid to the log file for posterity */
+ pidf = fopen(PidFile, "w");
+ if (pidf != NULL)
+ {
+ extern char *CommandLineArgs;
+
+ /* write the process id on line 1 */
+ fprintf(pidf, "%d\n", getpid());
+
+ /* line 2 contains all command line flags */
+ fprintf(pidf, "%s\n", CommandLineArgs);
+
+ /* flush and close */
+ fclose(pidf);
+ }
+
+#ifdef XDEBUG
+ {
+ char jbuf[MAXHOSTNAMELEN];
+
+ expand("\201j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
+ j_has_dot = strchr(jbuf, '.') != NULL;
+ }
+#endif
+
+ if (tTd(15, 1))
+ printf("getrequests: %d\n", DaemonSocket);
+
+ for (;;)
+ {
+ register int pid;
+ auto int lotherend;
+ extern bool refuseconnections();
+
+ /* see if we are rejecting connections */
+ CurrentLA = getla();
+ if (refuseconnections())
+ {
+ if (DaemonSocket >= 0)
+ {
+ /* close socket so peer will fail quickly */
+ (void) close(DaemonSocket);
+ DaemonSocket = -1;
+ }
+ refusingconnections = TRUE;
+ setproctitle("rejecting connections: load average: %d",
+ CurrentLA);
+ sleep(15);
+ continue;
+ }
+
+ if (refusingconnections)
+ {
+ /* start listening again */
+ (void) opendaemonsocket(FALSE);
+ setproctitle("accepting connections");
+ refusingconnections = FALSE;
+ }
+
+#ifdef XDEBUG
+ /* check for disaster */
+ {
+ register STAB *s;
+ char jbuf[MAXHOSTNAMELEN];
+
+ expand("\201j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
+ if ((s = stab(jbuf, ST_CLASS, ST_FIND)) == NULL ||
+ !bitnset('w', s->s_class))
+ {
+ dumpstate("daemon lost $j");
+ syslog(LOG_ALERT, "daemon process doesn't have $j in $=w; see syslog");
+ abort();
+ }
+ else if (j_has_dot && strchr(jbuf, '.') == NULL)
+ {
+ dumpstate("daemon $j lost dot");
+ syslog(LOG_ALERT, "daemon process $j lost dot; see syslog");
+ abort();
+ }
+ }
+#endif
+
+ /* wait for a connection */
+ do
+ {
+ errno = 0;
+ lotherend = socksize;
+ t = accept(DaemonSocket,
+ (struct sockaddr *)&RealHostAddr, &lotherend);
+ } while (t < 0 && errno == EINTR);
+ if (t < 0)
+ {
+ syserr("getrequests: accept");
+ sleep(5);
+ continue;
+ }
+
+ /*
+ ** Create a subprocess to process the mail.
+ */
+
+ if (tTd(15, 2))
+ printf("getrequests: forking (fd = %d)\n", t);
+
+ pid = fork();
+ if (pid < 0)
+ {
+ syserr("daemon: cannot fork");
+ sleep(10);
+ (void) close(t);
+ continue;
+ }
+
+ if (pid == 0)
+ {
+ char *p;
+ extern char *hostnamebyanyaddr();
+
+ /*
+ ** CHILD -- return to caller.
+ ** Collect verified idea of sending host.
+ ** Verify calling user id if possible here.
+ */
+
+ (void) setsignal(SIGCHLD, SIG_DFL);
+ DisConnected = FALSE;
+
+ setproctitle("startup with %s",
+ anynet_ntoa(&RealHostAddr));
+
+ /* determine host name */
+ p = hostnamebyanyaddr(&RealHostAddr);
+ RealHostName = newstr(p);
+ setproctitle("startup with %s", p);
+
+#ifdef LOG
+ if (LogLevel > 11)
+ {
+ /* log connection information */
+ syslog(LOG_INFO, "connect from %s (%s)",
+ RealHostName, anynet_ntoa(&RealHostAddr));
+ }
+#endif
+
+ (void) close(DaemonSocket);
+ if ((InChannel = fdopen(t, "r")) == NULL ||
+ (t = dup(t)) < 0 ||
+ (OutChannel = fdopen(t, "w")) == NULL)
+ {
+ syserr("cannot open SMTP server channel, fd=%d", t);
+ exit(0);
+ }
+
+ /* 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");
+ return;
+ }
+
+ /* close the port so that others will hang (for a while) */
+ (void) close(t);
+ }
+ /*NOTREACHED*/
+}
+ /*
+** OPENDAEMONSOCKET -- open the SMTP socket
+**
+** Deals with setting all appropriate options. DaemonAddr must
+** be set up in advance.
+**
+** Parameters:
+** firsttime -- set if this is the initial open.
+**
+** Returns:
+** Size in bytes of the daemon socket addr.
+**
+** Side Effects:
+** Leaves DaemonSocket set to the open socket.
+** Exits if the socket cannot be created.
+*/
+
+#define MAXOPENTRIES 10 /* maximum number of tries to open connection */
+
+int
+opendaemonsocket(firsttime)
+ bool firsttime;
+{
+ int on = 1;
+ int socksize;
+ int ntries = 0;
+ int saveerrno;
+
+ if (tTd(15, 2))
+ printf("opendaemonsocket()\n");
+
+ do
+ {
+ if (ntries > 0)
+ sleep(5);
+ if (firsttime || DaemonSocket < 0)
+ {
+ DaemonSocket = socket(DaemonAddr.sa.sa_family, SOCK_STREAM, 0);
+ if (DaemonSocket < 0)
+ {
+ /* probably another daemon already */
+ saveerrno = errno;
+ syserr("opendaemonsocket: can't create server SMTP socket");
+ severe:
+# ifdef LOG
+ if (LogLevel > 0)
+ syslog(LOG_ALERT, "problem creating SMTP socket");
+# endif /* LOG */
+ DaemonSocket = -1;
+ continue;
+ }
+
+ /* turn on network debugging? */
+ 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);
+
+#ifdef SO_RCVBUF
+ if (TcpRcvBufferSize > 0)
+ {
+ if (setsockopt(DaemonSocket, SOL_SOCKET,
+ SO_RCVBUF,
+ (char *) &TcpRcvBufferSize,
+ sizeof(TcpRcvBufferSize)) < 0)
+ syserr("getrequests: setsockopt(SO_RCVBUF)");
+ }
+#endif
+
+ switch (DaemonAddr.sa.sa_family)
+ {
+# ifdef NETINET
+ case AF_INET:
+ socksize = sizeof DaemonAddr.sin;
+ break;
+# endif
+
+# ifdef NETISO
+ case AF_ISO:
+ socksize = sizeof DaemonAddr.siso;
+ break;
+# endif
+
+ default:
+ socksize = sizeof DaemonAddr;
+ break;
+ }
+
+ if (bind(DaemonSocket, &DaemonAddr.sa, socksize) < 0)
+ {
+ saveerrno = errno;
+ syserr("getrequests: cannot bind");
+ (void) close(DaemonSocket);
+ goto severe;
+ }
+ }
+ if (!firsttime && listen(DaemonSocket, ListenQueueSize) < 0)
+ {
+ saveerrno = errno;
+ syserr("getrequests: cannot listen");
+ (void) close(DaemonSocket);
+ goto severe;
+ }
+ return socksize;
+ } while (ntries++ < MAXOPENTRIES && transienterror(saveerrno));
+ finis();
+}
+ /*
+** CLRDAEMON -- reset the daemon connection
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** releases any resources used by the passive daemon.
+*/
+
+clrdaemon()
+{
+ if (DaemonSocket >= 0)
+ (void) close(DaemonSocket);
+ DaemonSocket = -1;
+}
+ /*
+** 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 = htons(atoi(v));
+ else
+ {
+ register struct servent *sp;
+
+ sp = getservbyname(v, "tcp");
+ if (sp == NULL)
+ syserr("554 service \"%s\" unknown", v);
+ else
+ DaemonAddr.sin.sin_port = sp->s_port;
+ }
+ break;
+#endif
+
+#ifdef NETISO
+ case AF_ISO:
+ /* assume two byte transport selector */
+ if (isascii(*v) && isdigit(*v))
+ port = htons(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;
+
+ case 'S': /* send buffer size */
+ TcpSndBufferSize = atoi(v);
+ break;
+
+ case 'R': /* receive buffer size */
+ TcpRcvBufferSize = atoi(v);
+ break;
+ }
+ }
+}
+ /*
+** 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.
+** 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
+** made and if not why not.
+**
+** Side Effects:
+** none.
+*/
+
+SOCKADDR CurHostAddr; /* address of current host */
+
+int
+makeconnection(host, port, mci, usesecureport)
+ char *host;
+ u_short port;
+ register MCI *mci;
+ bool usesecureport;
+{
+ register int i, s;
+ register struct hostent *hp = (struct hostent *)NULL;
+ SOCKADDR addr;
+ int sav_errno;
+ int addrlen;
+#if NAMED_BIND
+ extern int h_errno;
+#endif
+
+ /*
+ ** Set up the address for the mailer.
+ ** Accept "[a.b.c.d]" syntax for host name.
+ */
+
+#if NAMED_BIND
+ h_errno = 0;
+#endif
+ errno = 0;
+ bzero(&CurHostAddr, sizeof CurHostAddr);
+ SmtpPhase = mci->mci_phase = "initial connection";
+ CurHostName = host;
+
+ if (host[0] == '[')
+ {
+ long hid;
+ register char *p = strchr(host, ']');
+
+ if (p != NULL)
+ {
+ *p = '\0';
+#ifdef NETINET
+ hid = inet_addr(&host[1]);
+ if (hid == -1)
+#endif
+ {
+ /* try it as a host name (avoid MX lookup) */
+ hp = gethostbyname(&host[1]);
+ if (hp == NULL && p[-1] == '.')
+ {
+ p[-1] = '\0';
+ hp = gethostbyname(&host[1]);
+ p[-1] = '.';
+ }
+ *p = ']';
+ goto gothostent;
+ }
+ *p = ']';
+ }
+ if (p == NULL)
+ {
+ usrerr("553 Invalid numeric domain spec \"%s\"", host);
+ return (EX_NOHOST);
+ }
+#ifdef NETINET
+ addr.sin.sin_family = AF_INET; /*XXX*/
+ addr.sin.sin_addr.s_addr = hid;
+#endif
+ }
+ else
+ {
+ register char *p = &host[strlen(host) - 1];
+
+ hp = gethostbyname(host);
+ if (hp == NULL && *p == '.')
+ {
+ *p = '\0';
+ hp = gethostbyname(host);
+ *p = '.';
+ }
+gothostent:
+ if (hp == NULL)
+ {
+#if 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
+ return (EX_NOHOST);
+ }
+ addr.sa.sa_family = hp->h_addrtype;
+ switch (hp->h_addrtype)
+ {
+#ifdef NETINET
+ case AF_INET:
+ bcopy(hp->h_addr,
+ &addr.sin.sin_addr,
+ sizeof addr.sin.sin_addr);
+ break;
+#endif
+
+ default:
+ bcopy(hp->h_addr,
+ addr.sa.sa_data,
+ hp->h_length);
+ break;
+ }
+ i = 1;
+ }
+
+ /*
+ ** Determine the port number.
+ */
+
+ if (port != 0)
+ port = htons(port);
+ else
+ {
+ register struct servent *sp = getservbyname("smtp", "tcp");
+
+ if (sp == NULL)
+ {
+ syserr("554 makeconnection: service \"smtp\" unknown");
+ port = htons(25);
+ }
+ else
+ port = sp->s_port;
+ }
+
+ 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.
+ */
+
+#ifdef XLA
+ /* if too many connections, don't bother trying */
+ if (!xla_noqueue_ok(host))
+ return EX_TEMPFAIL;
+#endif
+
+ for (;;)
+ {
+ if (tTd(16, 1))
+ printf("makeconnection (%s [%s])\n",
+ host, anynet_ntoa(&addr));
+
+ /* save for logging */
+ CurHostAddr = addr;
+
+ 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;
+ }
+
+#ifdef SO_SNDBUF
+ if (TcpSndBufferSize > 0)
+ {
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
+ (char *) &TcpSndBufferSize,
+ sizeof(TcpSndBufferSize)) < 0)
+ syserr("makeconnection: setsockopt(SO_SNDBUF)");
+ }
+#endif
+
+ if (tTd(16, 1))
+ printf("makeconnection: fd=%d\n", s);
+
+ /* turn on network debugging? */
+ if (tTd(16, 101))
+ {
+ int on = 1;
+ (void) setsockopt(s, 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])
+ {
+ 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,
+ sizeof addr.sin.sin_addr);
+ 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:
+#ifdef XLA
+ xla_host_end(host);
+#endif
+ if (transienterror(sav_errno))
+ return EX_TEMPFAIL;
+ else
+ {
+ message("%s", errstring(sav_errno));
+ return (EX_UNAVAILABLE);
+ }
+ }
+
+ /* connection ok, put it into canonical form */
+ if ((mci->mci_out = fdopen(s, "w")) == NULL ||
+ (s = dup(s)) < 0 ||
+ (mci->mci_in = fdopen(s, "r")) == NULL)
+ {
+ syserr("cannot open SMTP client channel, fd=%d", s);
+ return EX_TEMPFAIL;
+ }
+
+ return (EX_OK);
+}
+ /*
+** MYHOSTNAME -- return the name of this host.
+**
+** Parameters:
+** hostbuf -- a place to return the name of this host.
+** size -- the size of hostbuf.
+**
+** Returns:
+** A list of aliases for this host.
+**
+** Side Effects:
+** Adds numeric codes to $=w.
+*/
+
+char **
+myhostname(hostbuf, size)
+ char hostbuf[];
+ int size;
+{
+ register struct hostent *hp;
+ extern struct hostent *gethostbyname();
+
+ if (gethostname(hostbuf, size) < 0)
+ {
+ (void) strcpy(hostbuf, "localhost");
+ }
+ hp = gethostbyname(hostbuf);
+ if (hp == NULL)
+ {
+ syserr("!My host name (%s) does not seem to exist!", hostbuf);
+ }
+ (void) strncpy(hostbuf, hp->h_name, size - 1);
+ hostbuf[size - 1] = '\0';
+
+#if NAMED_BIND
+ /* if still no dot, try DNS directly (i.e., avoid NIS problems) */
+ if (strchr(hostbuf, '.') == NULL)
+ {
+ extern bool getcanonname();
+ extern int h_errno;
+
+ /* try twice in case name server not yet started up */
+ if (!getcanonname(hostbuf, size, TRUE) &&
+ UseNameServer &&
+ (h_errno != TRY_AGAIN ||
+ (sleep(30), !getcanonname(hostbuf, size, TRUE))))
+ {
+ errno = h_errno + E_DNSBASE;
+ syserr("!My host name (%s) not known to DNS",
+ hostbuf);
+ }
+ }
+#endif
+
+ if (hp->h_addrtype == AF_INET && hp->h_length == 4)
+ {
+ register int i;
+
+ for (i = 0; hp->h_addr_list[i] != NULL; i++)
+ {
+ char ipbuf[100];
+
+ sprintf(ipbuf, "[%s]",
+ inet_ntoa(*((struct in_addr *) hp->h_addr_list[i])));
+ setclass('w', ipbuf);
+ }
+ }
+
+ return (hp->h_aliases);
+}
+ /*
+** 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.
+*/
+
+#if IDENTPROTO
+
+static jmp_buf CtxAuthTimeout;
+
+static
+authtimeout()
+{
+ longjmp(CtxAuthTimeout, 1);
+}
+
+#endif
+
+char *
+getauthinfo(fd)
+ int fd;
+{
+ int falen;
+ register char *p;
+#if 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 RealHostAddr;
+ if (getpeername(fd, &RealHostAddr.sa, &falen) < 0 || falen <= 0 ||
+ RealHostAddr.sa.sa_family == 0)
+ {
+ (void) sprintf(hbuf, "%s@localhost", RealUserName);
+ if (tTd(9, 1))
+ printf("getauthinfo: %s\n", hbuf);
+ return hbuf;
+ }
+
+ if (RealHostName == NULL)
+ {
+ /* translate that to a host name */
+ RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr));
+ }
+
+#if IDENTPROTO
+ if (TimeOuts.to_ident == 0)
+ goto noident;
+
+ lalen = sizeof la;
+ if (RealHostAddr.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(RealHostAddr.sin.sin_port), ntohs(la.sin.sin_port));
+
+ /* create local address */
+ la.sin.sin_port = 0;
+
+ /* create foreign address */
+ sp = getservbyname("auth", "tcp");
+ if (sp != NULL)
+ RealHostAddr.sin.sin_port = sp->s_port;
+ else
+ RealHostAddr.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(TimeOuts.to_ident, authtimeout, 0);
+
+ /* connect to foreign IDENT server using same address as SMTP socket */
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ {
+ clrevent(ev);
+ goto noident;
+ }
+ if (bind(s, &la.sa, sizeof la.sin) < 0 ||
+ connect(s, &RealHostAddr.sa, sizeof RealHostAddr.sin) < 0)
+ {
+ goto closeident;
+ }
+
+ 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 == NULL ? "localhost" : RealHostName);
+ goto finish;
+
+closeident:
+ (void) close(s);
+ clrevent(ev);
+
+#endif /* IDENTPROTO */
+
+noident:
+ if (RealHostName == NULL)
+ {
+ if (tTd(9, 1))
+ printf("getauthinfo: NULL\n");
+ return NULL;
+ }
+ (void) strcpy(hbuf, RealHostName);
+
+finish:
+ if (RealHostName != NULL && RealHostName[0] != '[')
+ {
+ p = &hbuf[strlen(hbuf)];
+ (void) sprintf(p, " [%s]", anynet_ntoa(&RealHostAddr));
+ }
+ if (tTd(9, 1))
+ printf("getauthinfo: %s\n", hbuf);
+ return hbuf;
+}
+ /*
+** 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;
+ char *cp;
+ int i;
+ register STAB *s;
+ char hbuf[MAXNAME];
+ extern struct hostent *gethostbyaddr();
+#if NAMED_BIND
+ extern int h_errno;
+#endif
+
+ /*
+ ** 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;
+#if NAMED_BIND
+ h_errno = s->s_namecanon.nc_herrno;
+#endif
+ *statp = s->s_namecanon.nc_stat;
+ if (CurEnv->e_message == NULL && *statp == EX_TEMPFAIL)
+ {
+ sprintf(hbuf, "%s: Name server timeout",
+ shortenstring(name, 33));
+ CurEnv->e_message = newstr(hbuf);
+ }
+ 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 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, TRUE))
+ {
+ 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;
+
+ s->s_namecanon.nc_errno = errno;
+#if NAMED_BIND
+ s->s_namecanon.nc_herrno = h_errno;
+ if (tTd(9, 1))
+ printf("FAIL (%d)\n", h_errno);
+ switch (h_errno)
+ {
+ case TRY_AGAIN:
+ if (UseNameServer)
+ {
+ sprintf(hbuf, "%s: Name server timeout",
+ shortenstring(name, 33));
+ message("%s", hbuf);
+ if (CurEnv->e_message == NULL)
+ CurEnv->e_message = newstr(hbuf);
+ }
+ *statp = EX_TEMPFAIL;
+ break;
+
+ case HOST_NOT_FOUND:
+ *statp = EX_NOHOST;
+ break;
+
+ case NO_RECOVERY:
+ *statp = EX_SOFTWARE;
+ break;
+
+ default:
+ *statp = EX_UNAVAILABLE;
+ break;
+ }
+#else
+ if (tTd(9, 1))
+ printf("FAIL\n");
+ *statp = EX_NOHOST;
+#endif
+ 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';
+ in_addr = inet_addr(&name[1]);
+
+ /* nope -- ask the name server */
+ hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET);
+ s->s_namecanon.nc_errno = errno;
+#if NAMED_BIND
+ s->s_namecanon.nc_herrno = h_errno;
+#endif
+ s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */
+ if (hp == NULL)
+ {
+ 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;
+}
+ /*
+** 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[100];
+
+ /* check for null/zero family */
+ if (sap == NULL)
+ return "NULLADDR";
+ if (sap->sa.sa_family == 0)
+ return "0";
+
+ switch (sap->sa.sa_family)
+ {
+#ifdef MAYBENEXTRELEASE /*** UNTESTED *** UNTESTED *** UNTESTED ***/
+#ifdef NETUNIX
+ case AF_UNIX:
+ if (sap->sunix.sun_path[0] != '\0')
+ sprintf(buf, "[UNIX: %.64s]", sap->sunix.sun_path);
+ else
+ sprintf(buf, "[UNIX: localhost]");
+ return buf;
+#endif
+#endif
+
+#ifdef NETINET
+ case AF_INET:
+ return inet_ntoa(((struct sockaddr_in *) sap)->sin_addr);
+#endif
+
+ default:
+ /* this case is only to ensure syntactic correctness */
+ break;
+ }
+
+ /* 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;
+}
+ /*
+** 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;
+ int saveretry;
+
+#if NAMED_BIND
+ /* 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
+
+#ifdef MAYBENEXTRELEASE /*** UNTESTED *** UNTESTED *** UNTESTED ***/
+ case AF_UNIX:
+ hp = NULL;
+ break;
+#endif
+
+ default:
+ hp = gethostbyaddr(sap->sa.sa_data,
+ sizeof sap->sa.sa_data,
+ sap->sa.sa_family);
+ break;
+ }
+
+#if 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 */
+/* code for systems without sophisticated networking */
+
+/*
+** MYHOSTNAME -- stub version for case of no daemon code.
+**
+** Can't convert to upper case here because might be a UUCP name.
+**
+** Mark, you can change this to be anything you want......
+*/
+
+char **
+myhostname(hostbuf, size)
+ char hostbuf[];
+ int size;
+{
+ register FILE *f;
+
+ hostbuf[0] = '\0';
+ f = fopen("/usr/include/whoami", "r");
+ if (f != NULL)
+ {
+ (void) fgets(hostbuf, size, f);
+ fixcrlf(hostbuf, TRUE);
+ (void) fclose(f);
+ }
+ return (NULL);
+}
+ /*
+** 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;
+}
+ /*
+** MAPHOSTNAME -- turn a hostname into canonical form
+**
+** Parameters:
+** 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:
+** mapped host name
+** FALSE otherwise.
+**
+** Side Effects:
+** 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*/
+char *
+host_map_lookup(map, name, avp, statp)
+ MAP *map;
+ char *name;
+ char **avp;
+ char *statp;
+{
+ register struct hostent *hp;
+
+ hp = gethostbyname(name);
+ if (hp != NULL)
+ return hp->h_name;
+ *statp = EX_NOHOST;
+ return NULL;
+}
+
+#endif /* DAEMON */
OpenPOWER on IntegriCloud