diff options
Diffstat (limited to 'contrib/sendmail/src/daemon.c')
-rw-r--r-- | contrib/sendmail/src/daemon.c | 4486 |
1 files changed, 0 insertions, 4486 deletions
diff --git a/contrib/sendmail/src/daemon.c b/contrib/sendmail/src/daemon.c deleted file mode 100644 index 76b5b58..0000000 --- a/contrib/sendmail/src/daemon.c +++ /dev/null @@ -1,4486 +0,0 @@ -/* - * Copyright (c) 1998-2007 Sendmail, Inc. and its suppliers. - * All rights reserved. - * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. - * Copyright (c) 1988, 1993 - * The Regents of the University of California. All rights reserved. - * - * By using this file, you agree to the terms and conditions set - * forth in the LICENSE file which can be found at the top level of - * the sendmail distribution. - * - */ - -#include <sendmail.h> -#include "map.h" - -SM_RCSID("@(#)$Id: daemon.c,v 8.678 2007/03/08 00:33:40 ca Exp $") - -#if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) -# define USE_SOCK_STREAM 1 -#endif /* defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) */ - -#if defined(USE_SOCK_STREAM) -# if NETINET || NETINET6 -# include <arpa/inet.h> -# endif /* NETINET || NETINET6 */ -# if NAMED_BIND -# ifndef NO_DATA -# define NO_DATA NO_ADDRESS -# endif /* ! NO_DATA */ -# endif /* NAMED_BIND */ -#endif /* defined(USE_SOCK_STREAM) */ - -#if STARTTLS -# include <openssl/rand.h> -#endif /* STARTTLS */ - -#include <sm/time.h> - -#if IP_SRCROUTE && NETINET -# include <netinet/in_systm.h> -# include <netinet/ip.h> -# if HAS_IN_H -# include <netinet/in.h> -# ifndef IPOPTION -# define IPOPTION ip_opts -# define IP_LIST ip_opts -# define IP_DST ip_dst -# endif /* ! IPOPTION */ -# else /* HAS_IN_H */ -# include <netinet/ip_var.h> -# ifndef IPOPTION -# define IPOPTION ipoption -# define IP_LIST ipopt_list -# define IP_DST ipopt_dst -# endif /* ! IPOPTION */ -# endif /* HAS_IN_H */ -#endif /* IP_SRCROUTE && NETINET */ - -#include <sm/fdset.h> - -#define DAEMON_C 1 -#include <daemon.h> - -static void connecttimeout __P((int)); -static int opendaemonsocket __P((DAEMON_T *, bool)); -static unsigned short setupdaemon __P((SOCKADDR *)); -static void getrequests_checkdiskspace __P((ENVELOPE *e)); -static void setsockaddroptions __P((char *, DAEMON_T *)); -static void printdaemonflags __P((DAEMON_T *)); -static int addr_family __P((char *)); -static int addrcmp __P((struct hostent *, char *, SOCKADDR *)); -static void authtimeout __P((int)); - -/* -** 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(e) -** 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, mci, e, enough) -** Make a connection to the named host on the given -** port. 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. -*/ - -static int NDaemons = 0; /* actual number of daemons */ - -static time_t NextDiskSpaceCheck = 0; - -/* -** GETREQUESTS -- open mail IPC port and get requests. -** -** Parameters: -** e -- the current envelope. -** -** Returns: -** pointer to flags. -** -** 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. -** May restart persistent queue runners if they have ended -** for some reason. -*/ - -BITMAP256 * -getrequests(e) - ENVELOPE *e; -{ - int t; - int idx, curdaemon = -1; - int i, olddaemon = 0; -#if XDEBUG - bool j_has_dot; -#endif /* XDEBUG */ - char status[MAXLINE]; - SOCKADDR sa; - SOCKADDR_LEN_T len = sizeof(sa); -#if _FFR_QUEUE_RUN_PARANOIA - time_t lastrun; -#endif /* _FFR_QUEUE_RUN_PARANOIA */ -# if NETUNIX - extern int ControlSocket; -# endif /* NETUNIX */ - extern ENVELOPE BlankEnvelope; - - - /* initialize data for function that generates queue ids */ - init_qid_alg(); - for (idx = 0; idx < NDaemons; idx++) - { - Daemons[idx].d_port = setupdaemon(&(Daemons[idx].d_addr)); - Daemons[idx].d_firsttime = true; - Daemons[idx].d_refuse_connections_until = (time_t) 0; - } - - /* - ** Try to actually open the connection. - */ - - if (tTd(15, 1)) - { - for (idx = 0; idx < NDaemons; idx++) - { - sm_dprintf("getrequests: daemon %s: port %d\n", - Daemons[idx].d_name, - ntohs(Daemons[idx].d_port)); - } - } - - /* get a socket for the SMTP connection */ - for (idx = 0; idx < NDaemons; idx++) - Daemons[idx].d_socksize = opendaemonsocket(&Daemons[idx], true); - - if (opencontrolsocket() < 0) - sm_syslog(LOG_WARNING, NOQID, - "daemon could not open control socket %s: %s", - ControlSocketName, sm_errstring(errno)); - - /* If there are any queue runners released reapchild() co-ord's */ - (void) sm_signal(SIGCHLD, reapchild); - - /* write the pid to file, command line args to syslog */ - log_sendmail_pid(e); - -#if XDEBUG - { - char jbuf[MAXHOSTNAMELEN]; - - expand("\201j", jbuf, sizeof(jbuf), e); - j_has_dot = strchr(jbuf, '.') != NULL; - } -#endif /* XDEBUG */ - - /* Add parent process as first item */ - proc_list_add(CurrentPid, "Sendmail daemon", PROC_DAEMON, 0, -1, NULL); - - if (tTd(15, 1)) - { - for (idx = 0; idx < NDaemons; idx++) - sm_dprintf("getrequests: daemon %s: %d\n", - Daemons[idx].d_name, - Daemons[idx].d_socket); - } - - for (;;) - { - register pid_t pid; - auto SOCKADDR_LEN_T lotherend; - bool timedout = false; - bool control = false; - int save_errno; - int pipefd[2]; - time_t now; -#if STARTTLS - long seed; -#endif /* STARTTLS */ - - /* see if we are rejecting connections */ - (void) sm_blocksignal(SIGALRM); - CHECK_RESTART; - - for (idx = 0; idx < NDaemons; idx++) - { - /* - ** XXX do this call outside the loop? - ** no: refuse_connections may sleep(). - */ - - now = curtime(); - if (now < Daemons[idx].d_refuse_connections_until) - continue; - if (bitnset(D_DISABLE, Daemons[idx].d_flags)) - continue; - if (refuseconnections(e, idx, curdaemon == idx)) - { - if (Daemons[idx].d_socket >= 0) - { - /* close socket so peer fails quickly */ - (void) close(Daemons[idx].d_socket); - Daemons[idx].d_socket = -1; - } - - /* refuse connections for next 15 seconds */ - Daemons[idx].d_refuse_connections_until = now + 15; - } - else if (Daemons[idx].d_socket < 0 || - Daemons[idx].d_firsttime) - { - if (!Daemons[idx].d_firsttime && LogLevel > 8) - sm_syslog(LOG_INFO, NOQID, - "accepting connections again for daemon %s", - Daemons[idx].d_name); - - /* arrange to (re)open the socket if needed */ - (void) opendaemonsocket(&Daemons[idx], false); - Daemons[idx].d_firsttime = false; - } - } - - /* May have been sleeping above, check again */ - CHECK_RESTART; - - getrequests_checkdiskspace(e); - -#if XDEBUG - /* check for disaster */ - { - char jbuf[MAXHOSTNAMELEN]; - - expand("\201j", jbuf, sizeof(jbuf), e); - if (!wordinclass(jbuf, 'w')) - { - dumpstate("daemon lost $j"); - sm_syslog(LOG_ALERT, NOQID, - "daemon process doesn't have $j in $=w; see syslog"); - abort(); - } - else if (j_has_dot && strchr(jbuf, '.') == NULL) - { - dumpstate("daemon $j lost dot"); - sm_syslog(LOG_ALERT, NOQID, - "daemon process $j lost dot; see syslog"); - abort(); - } - } -#endif /* XDEBUG */ - -#if 0 - /* - ** Andrew Sun <asun@ieps-sun.ml.com> claims that this will - ** fix the SVr4 problem. But it seems to have gone away, - ** so is it worth doing this? - */ - - if (DaemonSocket >= 0 && - SetNonBlocking(DaemonSocket, false) < 0) - log an error here; -#endif /* 0 */ - (void) sm_releasesignal(SIGALRM); - - for (;;) - { - bool setproc = false; - int highest = -1; - fd_set readfds; - struct timeval timeout; - - CHECK_RESTART; - FD_ZERO(&readfds); - for (idx = 0; idx < NDaemons; idx++) - { - /* wait for a connection */ - if (Daemons[idx].d_socket >= 0) - { - if (!setproc && - !bitnset(D_ETRNONLY, - Daemons[idx].d_flags)) - { - sm_setproctitle(true, e, - "accepting connections"); - setproc = true; - } - if (Daemons[idx].d_socket > highest) - highest = Daemons[idx].d_socket; - SM_FD_SET(Daemons[idx].d_socket, - &readfds); - } - } - -#if NETUNIX - if (ControlSocket >= 0) - { - if (ControlSocket > highest) - highest = ControlSocket; - SM_FD_SET(ControlSocket, &readfds); - } -#endif /* NETUNIX */ - - timeout.tv_sec = 5; - timeout.tv_usec = 0; - - t = select(highest + 1, FDSET_CAST &readfds, - NULL, NULL, &timeout); - - /* Did someone signal while waiting? */ - CHECK_RESTART; - - curdaemon = -1; - if (doqueuerun()) - { - (void) runqueue(true, false, false, false); -#if _FFR_QUEUE_RUN_PARANOIA - lastrun = now; -#endif /* _FFR_QUEUE_RUN_PARANOIA */ - } -#if _FFR_QUEUE_RUN_PARANOIA - else if (CheckQueueRunners > 0 && QueueIntvl > 0 && - lastrun + QueueIntvl + CheckQueueRunners < now) - { - - /* - ** set lastrun unconditionally to avoid - ** calling checkqueuerunner() all the time. - ** That's also why we currently ignore the - ** result of the function call. - */ - - (void) checkqueuerunner(); - lastrun = now; - } -#endif /* _FFR_QUEUE_RUN_PARANOIA */ - - if (t <= 0) - { - timedout = true; - break; - } - - control = false; - errno = 0; - - /* look "round-robin" for an active socket */ - if ((idx = olddaemon + 1) >= NDaemons) - idx = 0; - for (i = 0; i < NDaemons; i++) - { - if (Daemons[idx].d_socket >= 0 && - SM_FD_ISSET(Daemons[idx].d_socket, - &readfds)) - { - lotherend = Daemons[idx].d_socksize; - memset(&RealHostAddr, '\0', - sizeof(RealHostAddr)); - t = accept(Daemons[idx].d_socket, - (struct sockaddr *)&RealHostAddr, - &lotherend); - - /* - ** If remote side closes before - ** accept() finishes, sockaddr - ** might not be fully filled in. - */ - - if (t >= 0 && - (lotherend == 0 || -# ifdef BSD4_4_SOCKADDR - RealHostAddr.sa.sa_len == 0 || -# endif /* BSD4_4_SOCKADDR */ - RealHostAddr.sa.sa_family != Daemons[idx].d_addr.sa.sa_family)) - { - (void) close(t); - t = -1; - errno = EINVAL; - } - olddaemon = curdaemon = idx; - break; - } - if (++idx >= NDaemons) - idx = 0; - } -#if NETUNIX - if (curdaemon == -1 && ControlSocket >= 0 && - SM_FD_ISSET(ControlSocket, &readfds)) - { - struct sockaddr_un sa_un; - - lotherend = sizeof(sa_un); - memset(&sa_un, '\0', sizeof(sa_un)); - t = accept(ControlSocket, - (struct sockaddr *)&sa_un, - &lotherend); - - /* - ** If remote side closes before - ** accept() finishes, sockaddr - ** might not be fully filled in. - */ - - if (t >= 0 && - (lotherend == 0 || -# ifdef BSD4_4_SOCKADDR - sa_un.sun_len == 0 || -# endif /* BSD4_4_SOCKADDR */ - sa_un.sun_family != AF_UNIX)) - { - (void) close(t); - t = -1; - errno = EINVAL; - } - if (t >= 0) - control = true; - } -#else /* NETUNIX */ - if (curdaemon == -1) - { - /* No daemon to service */ - continue; - } -#endif /* NETUNIX */ - if (t >= 0 || errno != EINTR) - break; - } - if (timedout) - { - timedout = false; - continue; - } - save_errno = errno; - (void) sm_blocksignal(SIGALRM); - if (t < 0) - { - errno = save_errno; - - /* let's ignore these temporary errors */ - if (save_errno == EINTR -#ifdef EAGAIN - || save_errno == EAGAIN -#endif /* EAGAIN */ -#ifdef ECONNABORTED - || save_errno == ECONNABORTED -#endif /* ECONNABORTED */ -#ifdef EWOULDBLOCK - || save_errno == EWOULDBLOCK -#endif /* EWOULDBLOCK */ - ) - continue; - - syserr("getrequests: accept"); - - if (curdaemon >= 0) - { - /* arrange to re-open socket next time around */ - (void) close(Daemons[curdaemon].d_socket); - Daemons[curdaemon].d_socket = -1; -#if SO_REUSEADDR_IS_BROKEN - /* - ** Give time for bound socket to be released. - ** This creates a denial-of-service if you can - ** force accept() to fail on affected systems. - */ - - Daemons[curdaemon].d_refuse_connections_until = - curtime() + 15; -#endif /* SO_REUSEADDR_IS_BROKEN */ - } - continue; - } - - if (!control) - { - /* set some daemon related macros */ - switch (Daemons[curdaemon].d_addr.sa.sa_family) - { - case AF_UNSPEC: - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{daemon_family}"), "unspec"); - break; -#if _FFR_DAEMON_NETUNIX -# if NETUNIX - case AF_UNIX: - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{daemon_family}"), "local"); - break; -# endif /* NETUNIX */ -#endif /* _FFR_DAEMON_NETUNIX */ -#if NETINET - case AF_INET: - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{daemon_family}"), "inet"); - break; -#endif /* NETINET */ -#if NETINET6 - case AF_INET6: - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{daemon_family}"), "inet6"); - break; -#endif /* NETINET6 */ -#if NETISO - case AF_ISO: - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{daemon_family}"), "iso"); - break; -#endif /* NETISO */ -#if NETNS - case AF_NS: - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{daemon_family}"), "ns"); - break; -#endif /* NETNS */ -#if NETX25 - case AF_CCITT: - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{daemon_family}"), "x.25"); - break; -#endif /* NETX25 */ - } - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{daemon_name}"), - Daemons[curdaemon].d_name); - if (Daemons[curdaemon].d_mflags != NULL) - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{daemon_flags}"), - Daemons[curdaemon].d_mflags); - else - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{daemon_flags}"), ""); - } - - /* - ** If connection rate is exceeded here, connection shall be - ** refused later by a new call after fork() by the - ** validate_connection() function. Closing the connection - ** at this point violates RFC 2821. - ** Do NOT remove this call, its side effects are needed. - */ - - connection_rate_check(&RealHostAddr, NULL); - - /* - ** Create a subprocess to process the mail. - */ - - if (tTd(15, 2)) - sm_dprintf("getrequests: forking (fd = %d)\n", t); - - /* - ** Advance state of PRNG. - ** This is necessary because otherwise all child processes - ** will produce the same PRN sequence and hence the selection - ** of a queue directory (and other things, e.g., MX selection) - ** are not "really" random. - */ -#if STARTTLS - /* XXX get some better "random" data? */ - seed = get_random(); - RAND_seed((void *) &NextDiskSpaceCheck, - sizeof(NextDiskSpaceCheck)); - RAND_seed((void *) &now, sizeof(now)); - RAND_seed((void *) &seed, sizeof(seed)); -#else /* STARTTLS */ - (void) get_random(); -#endif /* STARTTLS */ - -#if NAMED_BIND - /* - ** Update MX records for FallbackMX. - ** Let's hope this is fast otherwise we screw up the - ** response time. - */ - - if (FallbackMX != NULL) - (void) getfallbackmxrr(FallbackMX); -#endif /* NAMED_BIND */ - - if (tTd(93, 100)) - { - /* don't fork, handle connection in this process */ - pid = 0; - pipefd[0] = pipefd[1] = -1; - } - else - { - /* - ** Create a pipe to keep the child from writing to - ** the socket until after the parent has closed - ** it. Otherwise the parent may hang if the child - ** has closed it first. - */ - - if (pipe(pipefd) < 0) - pipefd[0] = pipefd[1] = -1; - - (void) sm_blocksignal(SIGCHLD); - pid = fork(); - if (pid < 0) - { - syserr("daemon: cannot fork"); - if (pipefd[0] != -1) - { - (void) close(pipefd[0]); - (void) close(pipefd[1]); - } - (void) sm_releasesignal(SIGCHLD); - (void) sleep(10); - (void) close(t); - continue; - } - } - - if (pid == 0) - { - char *p; - SM_FILE_T *inchannel, *outchannel = NULL; - - /* - ** CHILD -- return to caller. - ** Collect verified idea of sending host. - ** Verify calling user id if possible here. - */ - - /* Reset global flags */ - RestartRequest = NULL; - RestartWorkGroup = false; - ShutdownRequest = NULL; - PendingSignal = 0; - CurrentPid = getpid(); - close_sendmail_pid(); - - (void) sm_releasesignal(SIGALRM); - (void) sm_releasesignal(SIGCHLD); - (void) sm_signal(SIGCHLD, SIG_DFL); - (void) sm_signal(SIGHUP, SIG_DFL); - (void) sm_signal(SIGTERM, intsig); - - /* turn on profiling */ - /* SM_PROF(0); */ - - /* - ** Initialize exception stack and default exception - ** handler for child process. - */ - - sm_exc_newthread(fatal_error); - - if (!control) - { - macdefine(&BlankEnvelope.e_macro, A_TEMP, - macid("{daemon_addr}"), - anynet_ntoa(&Daemons[curdaemon].d_addr)); - (void) sm_snprintf(status, sizeof(status), "%d", - ntohs(Daemons[curdaemon].d_port)); - macdefine(&BlankEnvelope.e_macro, A_TEMP, - macid("{daemon_port}"), status); - } - - for (idx = 0; idx < NDaemons; idx++) - { - if (Daemons[idx].d_socket >= 0) - (void) close(Daemons[idx].d_socket); - Daemons[idx].d_socket = -1; - } - clrcontrol(); - - /* Avoid SMTP daemon actions if control command */ - if (control) - { - /* Add control socket process */ - proc_list_add(CurrentPid, - "console socket child", - PROC_CONTROL_CHILD, 0, -1, NULL); - } - else - { - proc_list_clear(); - - /* clean up background delivery children */ - (void) sm_signal(SIGCHLD, reapchild); - - /* Add parent process as first child item */ - proc_list_add(CurrentPid, "daemon child", - PROC_DAEMON_CHILD, 0, -1, NULL); - /* don't schedule queue runs if ETRN */ - QueueIntvl = 0; - - /* - ** Hack: override global variables if - ** the corresponding DaemonPortOption - ** is set. - */ -#if _FFR_SS_PER_DAEMON - if (Daemons[curdaemon].d_supersafe != - DPO_NOTSET) - SuperSafe = Daemons[curdaemon]. - d_supersafe; -#endif /* _FFR_SS_PER_DAEMON */ - if (Daemons[curdaemon].d_dm != DM_NOTSET) - set_delivery_mode( - Daemons[curdaemon].d_dm, e); - - if (Daemons[curdaemon].d_refuseLA != - DPO_NOTSET) - RefuseLA = Daemons[curdaemon]. - d_refuseLA; - if (Daemons[curdaemon].d_queueLA != DPO_NOTSET) - QueueLA = Daemons[curdaemon].d_queueLA; - if (Daemons[curdaemon].d_delayLA != DPO_NOTSET) - DelayLA = Daemons[curdaemon].d_delayLA; - if (Daemons[curdaemon].d_maxchildren != - DPO_NOTSET) - MaxChildren = Daemons[curdaemon]. - d_maxchildren; - - sm_setproctitle(true, e, "startup with %s", - anynet_ntoa(&RealHostAddr)); - } - - if (pipefd[0] != -1) - { - auto char c; - - /* - ** Wait for the parent to close the write end - ** of the pipe, which we will see as an EOF. - ** This guarantees that we won't write to the - ** socket until after the parent has closed - ** the pipe. - */ - - /* close the write end of the pipe */ - (void) close(pipefd[1]); - - /* we shouldn't be interrupted, but ... */ - while (read(pipefd[0], &c, 1) < 0 && - errno == EINTR) - continue; - (void) close(pipefd[0]); - } - - /* control socket processing */ - if (control) - { - control_command(t, e); - /* NOTREACHED */ - exit(EX_SOFTWARE); - } - - /* determine host name */ - p = hostnamebyanyaddr(&RealHostAddr); - if (strlen(p) > MAXNAME) /* XXX - 1 ? */ - p[MAXNAME] = '\0'; - RealHostName = newstr(p); - if (RealHostName[0] == '[') - { - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{client_resolve}"), - h_errno == TRY_AGAIN ? "TEMP" : "FAIL"); - } - else - { - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{client_resolve}"), "OK"); - } - sm_setproctitle(true, e, "startup with %s", p); - markstats(e, NULL, STATS_CONNECT); - - if ((inchannel = sm_io_open(SmFtStdiofd, - SM_TIME_DEFAULT, - (void *) &t, - SM_IO_RDONLY_B, - NULL)) == NULL || - (t = dup(t)) < 0 || - (outchannel = sm_io_open(SmFtStdiofd, - SM_TIME_DEFAULT, - (void *) &t, - SM_IO_WRONLY_B, - NULL)) == NULL) - { - syserr("cannot open SMTP server channel, fd=%d", - t); - finis(false, true, EX_OK); - } - sm_io_automode(inchannel, outchannel); - - InChannel = inchannel; - OutChannel = outchannel; - DisConnected = false; - -#if XLA - if (!xla_host_ok(RealHostName)) - { - message("421 4.4.5 Too many SMTP sessions for this host"); - finis(false, true, EX_OK); - } -#endif /* XLA */ - /* find out name for interface of connection */ - if (getsockname(sm_io_getinfo(InChannel, SM_IO_WHAT_FD, - NULL), &sa.sa, &len) == 0) - { - p = hostnamebyanyaddr(&sa); - if (tTd(15, 9)) - sm_dprintf("getreq: got name %s\n", p); - macdefine(&BlankEnvelope.e_macro, A_TEMP, - macid("{if_name}"), p); - - /* - ** Do this only if it is not the loopback - ** interface. - */ - - if (!isloopback(sa)) - { - char *addr; - char family[5]; - - addr = anynet_ntoa(&sa); - (void) sm_snprintf(family, - sizeof(family), - "%d", sa.sa.sa_family); - macdefine(&BlankEnvelope.e_macro, - A_TEMP, - macid("{if_addr}"), addr); - macdefine(&BlankEnvelope.e_macro, - A_TEMP, - macid("{if_family}"), family); - if (tTd(15, 7)) - sm_dprintf("getreq: got addr %s and family %s\n", - addr, family); - } - else - { - macdefine(&BlankEnvelope.e_macro, - A_PERM, - macid("{if_addr}"), NULL); - macdefine(&BlankEnvelope.e_macro, - A_PERM, - macid("{if_family}"), NULL); - } - } - else - { - if (tTd(15, 7)) - sm_dprintf("getreq: getsockname failed\n"); - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{if_name}"), NULL); - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{if_addr}"), NULL); - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{if_family}"), NULL); - } - break; - } - - /* parent -- keep track of children */ - if (control) - { - (void) sm_snprintf(status, sizeof(status), - "control socket server child"); - proc_list_add(pid, status, PROC_CONTROL, 0, -1, NULL); - } - else - { - (void) sm_snprintf(status, sizeof(status), - "SMTP server child for %s", - anynet_ntoa(&RealHostAddr)); - proc_list_add(pid, status, PROC_DAEMON, 0, -1, - &RealHostAddr); - } - (void) sm_releasesignal(SIGCHLD); - - /* close the read end of the synchronization pipe */ - if (pipefd[0] != -1) - { - (void) close(pipefd[0]); - pipefd[0] = -1; - } - - /* close the port so that others will hang (for a while) */ - (void) close(t); - - /* release the child by closing the read end of the sync pipe */ - if (pipefd[1] != -1) - { - (void) close(pipefd[1]); - pipefd[1] = -1; - } - } - if (tTd(15, 2)) - sm_dprintf("getreq: returning\n"); - -#if MILTER - /* set the filters for this daemon */ - if (Daemons[curdaemon].d_inputfilterlist != NULL) - { - for (i = 0; - (i < MAXFILTERS && - Daemons[curdaemon].d_inputfilters[i] != NULL); - i++) - { - InputFilters[i] = Daemons[curdaemon].d_inputfilters[i]; - } - if (i < MAXFILTERS) - InputFilters[i] = NULL; - } -#endif /* MILTER */ - return &Daemons[curdaemon].d_flags; -} - -/* -** GETREQUESTS_CHECKDISKSPACE -- check available diskspace. -** -** Parameters: -** e -- envelope. -** -** Returns: -** none. -** -** Side Effects: -** Modifies Daemon flags (D_ETRNONLY) if not enough disk space. -*/ - -static void -getrequests_checkdiskspace(e) - ENVELOPE *e; -{ - bool logged = false; - int idx; - time_t now; - - now = curtime(); - if (now < NextDiskSpaceCheck) - return; - - /* Check if there is available disk space in all queue groups. */ - if (!enoughdiskspace(0, NULL)) - { - for (idx = 0; idx < NDaemons; ++idx) - { - if (bitnset(D_ETRNONLY, Daemons[idx].d_flags)) - continue; - - /* log only if not logged before */ - if (!logged) - { - if (LogLevel > 8) - sm_syslog(LOG_INFO, NOQID, - "rejecting new messages: min free: %ld", - MinBlocksFree); - sm_setproctitle(true, e, - "rejecting new messages: min free: %ld", - MinBlocksFree); - logged = true; - } - setbitn(D_ETRNONLY, Daemons[idx].d_flags); - } - } - else - { - for (idx = 0; idx < NDaemons; ++idx) - { - if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags)) - continue; - - /* log only if not logged before */ - if (!logged) - { - if (LogLevel > 8) - sm_syslog(LOG_INFO, NOQID, - "accepting new messages (again)"); - logged = true; - } - - /* title will be set later */ - clrbitn(D_ETRNONLY, Daemons[idx].d_flags); - } - } - - /* only check disk space once a minute */ - NextDiskSpaceCheck = now + 60; -} - -/* -** OPENDAEMONSOCKET -- open SMTP socket -** -** Deals with setting all appropriate options. -** -** Parameters: -** d -- the structure for the daemon to open. -** 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 */ - -static int -opendaemonsocket(d, firsttime) - DAEMON_T *d; - bool firsttime; -{ - int on = 1; - int fdflags; - SOCKADDR_LEN_T socksize = 0; - int ntries = 0; - int save_errno; - - if (tTd(15, 2)) - sm_dprintf("opendaemonsocket(%s)\n", d->d_name); - - do - { - if (ntries > 0) - (void) sleep(5); - if (firsttime || d->d_socket < 0) - { -#if _FFR_DAEMON_NETUNIX -# if NETUNIX - if (d->d_addr.sa.sa_family == AF_UNIX) - { - int rval; - long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK|SFF_CREAT; - - /* if not safe, don't use it */ - rval = safefile(d->d_addr.sunix.sun_path, - RunAsUid, RunAsGid, - RunAsUserName, sff, - S_IRUSR|S_IWUSR, NULL); - if (rval != 0) - { - save_errno = errno; - syserr("opendaemonsocket: daemon %s: unsafe domain socket %s", - d->d_name, - d->d_addr.sunix.sun_path); - goto fail; - } - - /* Don't try to overtake an existing socket */ - (void) unlink(d->d_addr.sunix.sun_path); - } -# endif /* NETUNIX */ -#endif /* _FFR_DOMAIN_NETUNIX */ - d->d_socket = socket(d->d_addr.sa.sa_family, - SOCK_STREAM, 0); - if (d->d_socket < 0) - { - save_errno = errno; - syserr("opendaemonsocket: daemon %s: can't create server SMTP socket", - d->d_name); - fail: - if (bitnset(D_OPTIONAL, d->d_flags) && - (!transienterror(save_errno) || - ntries >= MAXOPENTRIES - 1)) - { - syserr("opendaemonsocket: daemon %s: optional socket disabled", - d->d_name); - setbitn(D_DISABLE, d->d_flags); - d->d_socket = -1; - return -1; - } - severe: - if (LogLevel > 0) - sm_syslog(LOG_ALERT, NOQID, - "daemon %s: problem creating SMTP socket", - d->d_name); - d->d_socket = -1; - continue; - } - - if (SM_FD_SETSIZE > 0 && d->d_socket >= SM_FD_SETSIZE) - { - save_errno = EINVAL; - syserr("opendaemonsocket: daemon %s: server SMTP socket (%d) too large", - d->d_name, d->d_socket); - goto fail; - } - - /* turn on network debugging? */ - if (tTd(15, 101)) - (void) setsockopt(d->d_socket, SOL_SOCKET, - SO_DEBUG, (char *)&on, - sizeof(on)); - - (void) setsockopt(d->d_socket, SOL_SOCKET, - SO_REUSEADDR, (char *)&on, sizeof(on)); - (void) setsockopt(d->d_socket, SOL_SOCKET, - SO_KEEPALIVE, (char *)&on, sizeof(on)); - -#ifdef SO_RCVBUF - if (d->d_tcprcvbufsize > 0) - { - if (setsockopt(d->d_socket, SOL_SOCKET, - SO_RCVBUF, - (char *) &d->d_tcprcvbufsize, - sizeof(d->d_tcprcvbufsize)) < 0) - syserr("opendaemonsocket: daemon %s: setsockopt(SO_RCVBUF)", d->d_name); - } -#endif /* SO_RCVBUF */ -#ifdef SO_SNDBUF - if (d->d_tcpsndbufsize > 0) - { - if (setsockopt(d->d_socket, SOL_SOCKET, - SO_SNDBUF, - (char *) &d->d_tcpsndbufsize, - sizeof(d->d_tcpsndbufsize)) < 0) - syserr("opendaemonsocket: daemon %s: setsockopt(SO_SNDBUF)", d->d_name); - } -#endif /* SO_SNDBUF */ - - if ((fdflags = fcntl(d->d_socket, F_GETFD, 0)) == -1 || - fcntl(d->d_socket, F_SETFD, - fdflags | FD_CLOEXEC) == -1) - { - save_errno = errno; - syserr("opendaemonsocket: daemon %s: failed to %s close-on-exec flag: %s", - d->d_name, - fdflags == -1 ? "get" : "set", - sm_errstring(save_errno)); - (void) close(d->d_socket); - goto severe; - } - - switch (d->d_addr.sa.sa_family) - { -#if _FFR_DAEMON_NETUNIX -# ifdef NETUNIX - case AF_UNIX: - socksize = sizeof(d->d_addr.sunix); - break; -# endif /* NETUNIX */ -#endif /* _FFR_DAEMON_NETUNIX */ -#if NETINET - case AF_INET: - socksize = sizeof(d->d_addr.sin); - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - socksize = sizeof(d->d_addr.sin6); - break; -#endif /* NETINET6 */ - -#if NETISO - case AF_ISO: - socksize = sizeof(d->d_addr.siso); - break; -#endif /* NETISO */ - - default: - socksize = sizeof(d->d_addr); - break; - } - - if (bind(d->d_socket, &d->d_addr.sa, socksize) < 0) - { - /* probably another daemon already */ - save_errno = errno; - syserr("opendaemonsocket: daemon %s: cannot bind", - d->d_name); - (void) close(d->d_socket); - goto fail; - } - } - if (!firsttime && - listen(d->d_socket, d->d_listenqueue) < 0) - { - save_errno = errno; - syserr("opendaemonsocket: daemon %s: cannot listen", - d->d_name); - (void) close(d->d_socket); - goto severe; - } - return socksize; - } while (ntries++ < MAXOPENTRIES && transienterror(save_errno)); - syserr("!opendaemonsocket: daemon %s: server SMTP socket wedged: exiting", - d->d_name); - /* NOTREACHED */ - return -1; /* avoid compiler warning on IRIX */ -} -/* -** SETUPDAEMON -- setup socket for daemon -** -** Parameters: -** daemonaddr -- socket for daemon -** -** Returns: -** port number on which daemon should run -** -*/ - -static unsigned short -setupdaemon(daemonaddr) - SOCKADDR *daemonaddr; -{ - unsigned short port; - - /* - ** Set up the address for the mailer. - */ - - if (daemonaddr->sa.sa_family == AF_UNSPEC) - { - memset(daemonaddr, '\0', sizeof(*daemonaddr)); -#if NETINET - daemonaddr->sa.sa_family = AF_INET; -#endif /* NETINET */ - } - - switch (daemonaddr->sa.sa_family) - { -#if NETINET - case AF_INET: - if (daemonaddr->sin.sin_addr.s_addr == 0) - daemonaddr->sin.sin_addr.s_addr = INADDR_ANY; - port = daemonaddr->sin.sin_port; - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - if (IN6_IS_ADDR_UNSPECIFIED(&daemonaddr->sin6.sin6_addr)) - daemonaddr->sin6.sin6_addr = in6addr_any; - port = daemonaddr->sin6.sin6_port; - break; -#endif /* NETINET6 */ - - default: - /* unknown protocol */ - port = 0; - break; - } - if (port == 0) - { -#ifdef NO_GETSERVBYNAME - port = htons(25); -#else /* NO_GETSERVBYNAME */ - { - register struct servent *sp; - - sp = getservbyname("smtp", "tcp"); - if (sp == NULL) - { - syserr("554 5.3.5 service \"smtp\" unknown"); - port = htons(25); - } - else - port = sp->s_port; - } -#endif /* NO_GETSERVBYNAME */ - } - - switch (daemonaddr->sa.sa_family) - { -#if NETINET - case AF_INET: - daemonaddr->sin.sin_port = port; - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - daemonaddr->sin6.sin6_port = port; - break; -#endif /* NETINET6 */ - - default: - /* unknown protocol */ - break; - } - return port; -} -/* -** CLRDAEMON -- reset the daemon connection -** -** Parameters: -** none. -** -** Returns: -** none. -** -** Side Effects: -** releases any resources used by the passive daemon. -*/ - -void -clrdaemon() -{ - int i; - - for (i = 0; i < NDaemons; i++) - { - if (Daemons[i].d_socket >= 0) - (void) close(Daemons[i].d_socket); - Daemons[i].d_socket = -1; - } -} - -/* -** GETMODIFIERS -- get modifier flags -** -** Parameters: -** v -- the modifiers (input text line). -** modifiers -- pointer to flag field to represent modifiers. -** -** Returns: -** (xallocat()ed) string representation of modifiers. -** -** Side Effects: -** fills in modifiers. -*/ - -char * -getmodifiers(v, modifiers) - char *v; - BITMAP256 modifiers; -{ - int l; - char *h, *f, *flags; - - /* maximum length of flags: upper case Option -> "OO " */ - l = 3 * strlen(v) + 3; - - /* is someone joking? */ - if (l < 0 || l > 256) - { - if (LogLevel > 2) - sm_syslog(LOG_ERR, NOQID, - "getmodifiers too long, ignored"); - return NULL; - } - flags = xalloc(l); - f = flags; - clrbitmap(modifiers); - for (h = v; *h != '\0'; h++) - { - if (isascii(*h) && !isspace(*h) && isprint(*h)) - { - setbitn(*h, modifiers); - if (flags != f) - *flags++ = ' '; - *flags++ = *h; - if (isupper(*h)) - *flags++ = *h; - } - } - *flags++ = '\0'; - return f; -} - -/* -** CHKDAEMONMODIFIERS -- check whether all daemons have set a flag. -** -** Parameters: -** flag -- the flag to test. -** -** Returns: -** true iff all daemons have set flag. -*/ - -bool -chkdaemonmodifiers(flag) - int flag; -{ - int i; - - for (i = 0; i < NDaemons; i++) - if (!bitnset((char) flag, Daemons[i].d_flags)) - return false; - return true; -} - -/* -** SETSOCKADDROPTIONS -- set options for SOCKADDR (daemon or client) -** -** Parameters: -** p -- the options line. -** d -- the daemon structure to fill in. -** -** Returns: -** none. -*/ - -static void -setsockaddroptions(p, d) - char *p; - DAEMON_T *d; -{ -#if NETISO - short portno; -#endif /* NETISO */ - char *port = NULL; - char *addr = NULL; - -#if NETINET - if (d->d_addr.sa.sa_family == AF_UNSPEC) - d->d_addr.sa.sa_family = AF_INET; -#endif /* NETINET */ -#if _FFR_SS_PER_DAEMON - d->d_supersafe = DPO_NOTSET; -#endif /* _FFR_SS_PER_DAEMON */ - d->d_dm = DM_NOTSET; - d->d_refuseLA = DPO_NOTSET; - d->d_queueLA = DPO_NOTSET; - d->d_delayLA = DPO_NOTSET; - d->d_maxchildren = DPO_NOTSET; - - 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 'A': /* address */ -#if !_FFR_DPO_CS - case 'a': -#endif /* !_FFR_DPO_CS */ - addr = v; - break; - - case 'c': - d->d_maxchildren = atoi(v); - break; - - case 'D': /* DeliveryMode */ - switch (*v) - { - case SM_QUEUE: - case SM_DEFER: - case SM_DELIVER: - case SM_FORK: - d->d_dm = *v; - break; - default: - syserr("554 5.3.5 Unknown delivery mode %c", - *v); - break; - } - break; - - case 'd': /* delayLA */ - d->d_delayLA = atoi(v); - break; - - case 'F': /* address family */ -#if !_FFR_DPO_CS - case 'f': -#endif /* !_FFR_DPO_CS */ - if (isascii(*v) && isdigit(*v)) - d->d_addr.sa.sa_family = atoi(v); -#if _FFR_DAEMON_NETUNIX -# ifdef NETUNIX - else if (sm_strcasecmp(v, "unix") == 0 || - sm_strcasecmp(v, "local") == 0) - d->d_addr.sa.sa_family = AF_UNIX; -# endif /* NETUNIX */ -#endif /* _FFR_DAEMON_NETUNIX */ -#if NETINET - else if (sm_strcasecmp(v, "inet") == 0) - d->d_addr.sa.sa_family = AF_INET; -#endif /* NETINET */ -#if NETINET6 - else if (sm_strcasecmp(v, "inet6") == 0) - d->d_addr.sa.sa_family = AF_INET6; -#endif /* NETINET6 */ -#if NETISO - else if (sm_strcasecmp(v, "iso") == 0) - d->d_addr.sa.sa_family = AF_ISO; -#endif /* NETISO */ -#if NETNS - else if (sm_strcasecmp(v, "ns") == 0) - d->d_addr.sa.sa_family = AF_NS; -#endif /* NETNS */ -#if NETX25 - else if (sm_strcasecmp(v, "x.25") == 0) - d->d_addr.sa.sa_family = AF_CCITT; -#endif /* NETX25 */ - else - syserr("554 5.3.5 Unknown address family %s in Family=option", - v); - break; - -#if MILTER - case 'I': -# if !_FFR_DPO_CS - case 'i': -# endif /* !_FFR_DPO_CS */ - d->d_inputfilterlist = v; - break; -#endif /* MILTER */ - - case 'L': /* listen queue size */ -#if !_FFR_DPO_CS - case 'l': -#endif /* !_FFR_DPO_CS */ - d->d_listenqueue = atoi(v); - break; - - case 'M': /* modifiers (flags) */ -#if !_FFR_DPO_CS - case 'm': -#endif /* !_FFR_DPO_CS */ - d->d_mflags = getmodifiers(v, d->d_flags); - break; - - case 'N': /* name */ -#if !_FFR_DPO_CS - case 'n': -#endif /* !_FFR_DPO_CS */ - d->d_name = v; - break; - - case 'P': /* port */ -#if !_FFR_DPO_CS - case 'p': -#endif /* !_FFR_DPO_CS */ - port = v; - break; - - case 'q': - d->d_queueLA = atoi(v); - break; - - case 'R': /* receive buffer size */ - d->d_tcprcvbufsize = atoi(v); - break; - - case 'r': - d->d_refuseLA = atoi(v); - break; - - case 'S': /* send buffer size */ -#if !_FFR_DPO_CS - case 's': -#endif /* !_FFR_DPO_CS */ - d->d_tcpsndbufsize = atoi(v); - break; - -#if _FFR_SS_PER_DAEMON - case 'T': /* SuperSafe */ - if (tolower(*v) == 'i') - d->d_supersafe = SAFE_INTERACTIVE; - else if (tolower(*v) == 'p') -# if MILTER - d->d_supersafe = SAFE_REALLY_POSTMILTER; -# else /* MILTER */ - (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, - "Warning: SuperSafe=PostMilter requires Milter support (-DMILTER)\n"); -# endif /* MILTER */ - else - d->d_supersafe = atobool(v) ? SAFE_REALLY - : SAFE_NO; - break; -#endif /* _FFR_SS_PER_DAEMON */ - - default: - syserr("554 5.3.5 PortOptions parameter \"%s\" unknown", - f); - } - } - - /* Check addr and port after finding family */ - if (addr != NULL) - { - switch (d->d_addr.sa.sa_family) - { -#if _FFR_DAEMON_NETUNIX -# if NETUNIX - case AF_UNIX: - if (strlen(addr) >= sizeof(d->d_addr.sunix.sun_path)) - { - errno = ENAMETOOLONG; - syserr("setsockaddroptions: domain socket name too long: %s > %d", - addr, sizeof(d->d_addr.sunix.sun_path)); - break; - } - - /* file safety check done in opendaemonsocket() */ - (void) memset(&d->d_addr.sunix.sun_path, '\0', - sizeof(d->d_addr.sunix.sun_path)); - (void) sm_strlcpy((char *)&d->d_addr.sunix.sun_path, - addr, - sizeof(d->d_addr.sunix.sun_path)); - break; -# endif /* NETUNIX */ -#endif /* _FFR_DAEMON_NETUNIX */ -#if NETINET - case AF_INET: - if (!isascii(*addr) || !isdigit(*addr) || - ((d->d_addr.sin.sin_addr.s_addr = inet_addr(addr)) - == INADDR_NONE)) - { - register struct hostent *hp; - - hp = sm_gethostbyname(addr, AF_INET); - if (hp == NULL) - syserr("554 5.3.0 host \"%s\" unknown", - addr); - else - { - while (*(hp->h_addr_list) != NULL && - hp->h_addrtype != AF_INET) - hp->h_addr_list++; - if (*(hp->h_addr_list) == NULL) - syserr("554 5.3.0 host \"%s\" unknown", - addr); - else - memmove(&d->d_addr.sin.sin_addr, - *(hp->h_addr_list), - INADDRSZ); -# if NETINET6 - freehostent(hp); - hp = NULL; -# endif /* NETINET6 */ - } - } - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - if (anynet_pton(AF_INET6, addr, - &d->d_addr.sin6.sin6_addr) != 1) - { - register struct hostent *hp; - - hp = sm_gethostbyname(addr, AF_INET6); - if (hp == NULL) - syserr("554 5.3.0 host \"%s\" unknown", - addr); - else - { - while (*(hp->h_addr_list) != NULL && - hp->h_addrtype != AF_INET6) - hp->h_addr_list++; - if (*(hp->h_addr_list) == NULL) - syserr("554 5.3.0 host \"%s\" unknown", - addr); - else - memmove(&d->d_addr.sin6.sin6_addr, - *(hp->h_addr_list), - IN6ADDRSZ); - freehostent(hp); - hp = NULL; - } - } - break; -#endif /* NETINET6 */ - - default: - syserr("554 5.3.5 address= option unsupported for family %d", - d->d_addr.sa.sa_family); - break; - } - } - - if (port != NULL) - { - switch (d->d_addr.sa.sa_family) - { -#if NETINET - case AF_INET: - if (isascii(*port) && isdigit(*port)) - d->d_addr.sin.sin_port = htons((unsigned short) - atoi((const char *) port)); - else - { -# ifdef NO_GETSERVBYNAME - syserr("554 5.3.5 invalid port number: %s", - port); -# else /* NO_GETSERVBYNAME */ - register struct servent *sp; - - sp = getservbyname(port, "tcp"); - if (sp == NULL) - syserr("554 5.3.5 service \"%s\" unknown", - port); - else - d->d_addr.sin.sin_port = sp->s_port; -# endif /* NO_GETSERVBYNAME */ - } - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - if (isascii(*port) && isdigit(*port)) - d->d_addr.sin6.sin6_port = htons((unsigned short) - atoi(port)); - else - { -# ifdef NO_GETSERVBYNAME - syserr("554 5.3.5 invalid port number: %s", - port); -# else /* NO_GETSERVBYNAME */ - register struct servent *sp; - - sp = getservbyname(port, "tcp"); - if (sp == NULL) - syserr("554 5.3.5 service \"%s\" unknown", - port); - else - d->d_addr.sin6.sin6_port = sp->s_port; -# endif /* NO_GETSERVBYNAME */ - } - break; -#endif /* NETINET6 */ - -#if NETISO - case AF_ISO: - /* assume two byte transport selector */ - if (isascii(*port) && isdigit(*port)) - portno = htons((unsigned short) atoi(port)); - else - { -# ifdef NO_GETSERVBYNAME - syserr("554 5.3.5 invalid port number: %s", - port); -# else /* NO_GETSERVBYNAME */ - register struct servent *sp; - - sp = getservbyname(port, "tcp"); - if (sp == NULL) - syserr("554 5.3.5 service \"%s\" unknown", - port); - else - portno = sp->s_port; -# endif /* NO_GETSERVBYNAME */ - } - memmove(TSEL(&d->d_addr.siso), - (char *) &portno, 2); - break; -#endif /* NETISO */ - - default: - syserr("554 5.3.5 Port= option unsupported for family %d", - d->d_addr.sa.sa_family); - break; - } - } -} -/* -** SETDAEMONOPTIONS -- set options for running the MTA daemon -** -** Parameters: -** p -- the options line. -** -** Returns: -** true if successful, false otherwise. -** -** Side Effects: -** increments number of daemons. -*/ - -#define DEF_LISTENQUEUE 10 - -struct dflags -{ - char *d_name; - int d_flag; -}; - -static struct dflags DaemonFlags[] = -{ - { "AUTHREQ", D_AUTHREQ }, - { "BINDIF", D_BINDIF }, - { "CANONREQ", D_CANONREQ }, - { "IFNHELO", D_IFNHELO }, - { "FQMAIL", D_FQMAIL }, - { "FQRCPT", D_FQRCPT }, - { "SMTPS", D_SMTPS }, - { "UNQUALOK", D_UNQUALOK }, - { "NOAUTH", D_NOAUTH }, - { "NOCANON", D_NOCANON }, - { "NOETRN", D_NOETRN }, - { "NOTLS", D_NOTLS }, - { "ETRNONLY", D_ETRNONLY }, - { "OPTIONAL", D_OPTIONAL }, - { "DISABLE", D_DISABLE }, - { "ISSET", D_ISSET }, - { NULL, 0 } -}; - -static void -printdaemonflags(d) - DAEMON_T *d; -{ - register struct dflags *df; - bool first = true; - - for (df = DaemonFlags; df->d_name != NULL; df++) - { - if (!bitnset(df->d_flag, d->d_flags)) - continue; - if (first) - sm_dprintf("<%s", df->d_name); - else - sm_dprintf(",%s", df->d_name); - first = false; - } - if (!first) - sm_dprintf(">"); -} - -bool -setdaemonoptions(p) - register char *p; -{ - if (NDaemons >= MAXDAEMONS) - return false; - Daemons[NDaemons].d_socket = -1; - Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE; - clrbitmap(Daemons[NDaemons].d_flags); - setsockaddroptions(p, &Daemons[NDaemons]); - -#if MILTER - if (Daemons[NDaemons].d_inputfilterlist != NULL) - Daemons[NDaemons].d_inputfilterlist = newstr(Daemons[NDaemons].d_inputfilterlist); -#endif /* MILTER */ - - if (Daemons[NDaemons].d_name != NULL) - Daemons[NDaemons].d_name = newstr(Daemons[NDaemons].d_name); - else - { - char num[30]; - - (void) sm_snprintf(num, sizeof(num), "Daemon%d", NDaemons); - Daemons[NDaemons].d_name = newstr(num); - } - - if (tTd(37, 1)) - { - sm_dprintf("Daemon %s flags: ", Daemons[NDaemons].d_name); - printdaemonflags(&Daemons[NDaemons]); - sm_dprintf("\n"); - } - ++NDaemons; - return true; -} -/* -** INITDAEMON -- initialize daemon if not yet done. -** -** Parameters: -** none -** -** Returns: -** none -** -** Side Effects: -** initializes structure for one daemon. -*/ - -void -initdaemon() -{ - if (NDaemons == 0) - { - Daemons[NDaemons].d_socket = -1; - Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE; - Daemons[NDaemons].d_name = "Daemon0"; - NDaemons = 1; - } -} -/* -** SETCLIENTOPTIONS -- set options for running the client -** -** Parameters: -** p -- the options line. -** -** Returns: -** none. -*/ - -static DAEMON_T ClientSettings[AF_MAX + 1]; - -void -setclientoptions(p) - register char *p; -{ - int family; - DAEMON_T d; - - memset(&d, '\0', sizeof(d)); - setsockaddroptions(p, &d); - - /* grab what we need */ - family = d.d_addr.sa.sa_family; - STRUCTCOPY(d, ClientSettings[family]); - setbitn(D_ISSET, ClientSettings[family].d_flags); /* mark as set */ - if (d.d_name != NULL) - ClientSettings[family].d_name = newstr(d.d_name); - else - { - char num[30]; - - (void) sm_snprintf(num, sizeof(num), "Client%d", family); - ClientSettings[family].d_name = newstr(num); - } -} -/* -** ADDR_FAMILY -- determine address family from address -** -** Parameters: -** addr -- the string representation of the address -** -** Returns: -** AF_INET, AF_INET6 or AF_UNSPEC -** -** Side Effects: -** none. -*/ - -static int -addr_family(addr) - char *addr; -{ -#if NETINET6 - SOCKADDR clt_addr; -#endif /* NETINET6 */ - -#if NETINET - if (inet_addr(addr) != INADDR_NONE) - { - if (tTd(16, 9)) - sm_dprintf("addr_family(%s): INET\n", addr); - return AF_INET; - } -#endif /* NETINET */ -#if NETINET6 - if (anynet_pton(AF_INET6, addr, &clt_addr.sin6.sin6_addr) == 1) - { - if (tTd(16, 9)) - sm_dprintf("addr_family(%s): INET6\n", addr); - return AF_INET6; - } -#endif /* NETINET6 */ -#if _FFR_DAEMON_NETUNIX -# if NETUNIX - if (*addr == '/') - { - if (tTd(16, 9)) - sm_dprintf("addr_family(%s): LOCAL\n", addr); - return AF_UNIX; - } -# endif /* NETUNIX */ -#endif /* _FFR_DAEMON_NETUNIX */ - if (tTd(16, 9)) - sm_dprintf("addr_family(%s): UNSPEC\n", addr); - return AF_UNSPEC; -} - -/* -** CHKCLIENTMODIFIERS -- check whether all clients have set a flag. -** -** Parameters: -** flag -- the flag to test. -** -** Returns: -** true iff all configured clients have set the flag. -*/ - -bool -chkclientmodifiers(flag) - int flag; -{ - int i; - bool flagisset; - - flagisset = false; - for (i = 0; i < AF_MAX; i++) - { - if (bitnset(D_ISSET, ClientSettings[i].d_flags)) - { - if (!bitnset((char) flag, ClientSettings[i].d_flags)) - return false; - flagisset = true; - } - } - return flagisset; -} - -#if MILTER -/* -** SETUP_DAEMON_FILTERS -- Parse per-socket filters -** -** Parameters: -** none -** -** Returns: -** none -*/ - -void -setup_daemon_milters() -{ - int idx; - - if (OpMode == MD_SMTP) - { - /* no need to configure the daemons */ - return; - } - - for (idx = 0; idx < NDaemons; idx++) - { - if (Daemons[idx].d_inputfilterlist != NULL) - { - milter_config(Daemons[idx].d_inputfilterlist, - Daemons[idx].d_inputfilters, - MAXFILTERS); - } - } -} -#endif /* MILTER */ -/* -** MAKECONNECTION -- make a connection to an SMTP socket on a 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. -** e -- the current envelope. -** enough -- time at which to stop further connection attempts. -** (0 means no limit) -** -** Returns: -** An exit code telling whether the connection could be -** made and if not why not. -** -** Side Effects: -** none. -*/ - -static jmp_buf CtxConnectTimeout; - -SOCKADDR CurHostAddr; /* address of current host */ - -int -makeconnection(host, port, mci, e, enough) - char *host; - volatile unsigned int port; - register MCI *mci; - ENVELOPE *e; - time_t enough; -{ - register volatile int addrno = 0; - volatile int s; - register struct hostent *volatile hp = (struct hostent *) NULL; - SOCKADDR addr; - SOCKADDR clt_addr; - int save_errno = 0; - volatile SOCKADDR_LEN_T addrlen; - volatile bool firstconnect = true; - SM_EVENT *volatile ev = NULL; -#if NETINET6 - volatile bool v6found = false; -#endif /* NETINET6 */ - volatile int family = InetMode; - SOCKADDR_LEN_T len; - volatile SOCKADDR_LEN_T socksize = 0; - volatile bool clt_bind; - BITMAP256 d_flags; - char *p; - extern ENVELOPE BlankEnvelope; - - /* retranslate {daemon_flags} into bitmap */ - clrbitmap(d_flags); - if ((p = macvalue(macid("{daemon_flags}"), e)) != NULL) - { - for (; *p != '\0'; p++) - { - if (!(isascii(*p) && isspace(*p))) - setbitn(bitidx(*p), d_flags); - } - } - -#if NETINET6 - v4retry: -#endif /* NETINET6 */ - clt_bind = false; - - /* Set up the address for outgoing connection. */ - if (bitnset(D_BINDIF, d_flags) && - (p = macvalue(macid("{if_addr}"), e)) != NULL && - *p != '\0') - { -#if NETINET6 - char p6[INET6_ADDRSTRLEN]; -#endif /* NETINET6 */ - - memset(&clt_addr, '\0', sizeof(clt_addr)); - - /* infer the address family from the address itself */ - clt_addr.sa.sa_family = addr_family(p); - switch (clt_addr.sa.sa_family) - { -#if NETINET - case AF_INET: - clt_addr.sin.sin_addr.s_addr = inet_addr(p); - if (clt_addr.sin.sin_addr.s_addr != INADDR_NONE && - clt_addr.sin.sin_addr.s_addr != INADDR_LOOPBACK) - { - clt_bind = true; - socksize = sizeof(struct sockaddr_in); - } - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - if (inet_addr(p) != INADDR_NONE) - (void) sm_snprintf(p6, sizeof(p6), - "IPv6:::ffff:%s", p); - else - (void) sm_strlcpy(p6, p, sizeof(p6)); - if (anynet_pton(AF_INET6, p6, - &clt_addr.sin6.sin6_addr) == 1 && - !IN6_IS_ADDR_LOOPBACK(&clt_addr.sin6.sin6_addr)) - { - clt_bind = true; - socksize = sizeof(struct sockaddr_in6); - } - break; -#endif /* NETINET6 */ - -#if 0 - default: - syserr("554 5.3.5 Address= option unsupported for family %d", - clt_addr.sa.sa_family); - break; -#endif /* 0 */ - } - if (clt_bind) - family = clt_addr.sa.sa_family; - } - - /* D_BINDIF not set or not available, fallback to ClientPortOptions */ - if (!clt_bind) - { - STRUCTCOPY(ClientSettings[family].d_addr, clt_addr); - switch (clt_addr.sa.sa_family) - { -#if NETINET - case AF_INET: - if (clt_addr.sin.sin_addr.s_addr == 0) - clt_addr.sin.sin_addr.s_addr = INADDR_ANY; - else - clt_bind = true; - if (clt_addr.sin.sin_port != 0) - clt_bind = true; - socksize = sizeof(struct sockaddr_in); - break; -#endif /* NETINET */ -#if NETINET6 - case AF_INET6: - if (IN6_IS_ADDR_UNSPECIFIED(&clt_addr.sin6.sin6_addr)) - clt_addr.sin6.sin6_addr = in6addr_any; - else - clt_bind = true; - socksize = sizeof(struct sockaddr_in6); - if (clt_addr.sin6.sin6_port != 0) - clt_bind = true; - break; -#endif /* NETINET6 */ -#if NETISO - case AF_ISO: - socksize = sizeof(clt_addr.siso); - clt_bind = true; - break; -#endif /* NETISO */ - default: - break; - } - } - - /* - ** Set up the address for the mailer. - ** Accept "[a.b.c.d]" syntax for host name. - */ - - SM_SET_H_ERRNO(0); - errno = 0; - memset(&CurHostAddr, '\0', sizeof(CurHostAddr)); - memset(&addr, '\0', sizeof(addr)); - SmtpPhase = mci->mci_phase = "initial connection"; - CurHostName = host; - - if (host[0] == '[') - { - p = strchr(host, ']'); - if (p != NULL) - { -#if NETINET - unsigned long hid = INADDR_NONE; -#endif /* NETINET */ -#if NETINET6 - struct sockaddr_in6 hid6; -#endif /* NETINET6 */ - - *p = '\0'; -#if NETINET6 - memset(&hid6, '\0', sizeof(hid6)); -#endif /* NETINET6 */ -#if NETINET - if (family == AF_INET && - (hid = inet_addr(&host[1])) != INADDR_NONE) - { - addr.sin.sin_family = AF_INET; - addr.sin.sin_addr.s_addr = hid; - } - else -#endif /* NETINET */ -#if NETINET6 - if (family == AF_INET6 && - anynet_pton(AF_INET6, &host[1], - &hid6.sin6_addr) == 1) - { - addr.sin6.sin6_family = AF_INET6; - addr.sin6.sin6_addr = hid6.sin6_addr; - } - else -#endif /* NETINET6 */ - { - /* try it as a host name (avoid MX lookup) */ - hp = sm_gethostbyname(&host[1], family); - if (hp == NULL && p[-1] == '.') - { -#if NAMED_BIND - int oldopts = _res.options; - - _res.options &= ~(RES_DEFNAMES|RES_DNSRCH); -#endif /* NAMED_BIND */ - p[-1] = '\0'; - hp = sm_gethostbyname(&host[1], - family); - p[-1] = '.'; -#if NAMED_BIND - _res.options = oldopts; -#endif /* NAMED_BIND */ - } - *p = ']'; - goto gothostent; - } - *p = ']'; - } - if (p == NULL) - { - extern char MsgBuf[]; - - usrerrenh("5.1.2", - "553 Invalid numeric domain spec \"%s\"", - host); - mci_setstat(mci, EX_NOHOST, "5.1.2", MsgBuf); - errno = EINVAL; - return EX_NOHOST; - } - } - else - { - /* contortion to get around SGI cc complaints */ - { - p = &host[strlen(host) - 1]; - hp = sm_gethostbyname(host, family); - if (hp == NULL && *p == '.') - { -#if NAMED_BIND - int oldopts = _res.options; - - _res.options &= ~(RES_DEFNAMES|RES_DNSRCH); -#endif /* NAMED_BIND */ - *p = '\0'; - hp = sm_gethostbyname(host, family); - *p = '.'; -#if NAMED_BIND - _res.options = oldopts; -#endif /* NAMED_BIND */ - } - } -gothostent: - if (hp == NULL) - { -#if NAMED_BIND - /* check for name server timeouts */ -# if NETINET6 - if (WorkAroundBrokenAAAA && family == AF_INET6 && - errno == ETIMEDOUT) - { - /* - ** An attempt with family AF_INET may - ** succeed By skipping the next section - ** of code, we will try AF_INET before - ** failing. - */ - - if (tTd(16, 10)) - sm_dprintf("makeconnection: WorkAroundBrokenAAAA: Trying AF_INET lookup (AF_INET6 failed)\n"); - } - else -# endif /* NETINET6 */ - { - if (errno == ETIMEDOUT || -# if _FFR_GETHBN_ExFILE -# ifdef EMFILE - errno == EMFILE || -# endif /* EMFILE */ -# ifdef ENFILE - errno == ENFILE || -# endif /* ENFILE */ -# endif /* _FFR_GETHBN_ExFILE */ - h_errno == TRY_AGAIN || - (errno == ECONNREFUSED && UseNameServer)) - { - save_errno = errno; - mci_setstat(mci, EX_TEMPFAIL, - "4.4.3", NULL); - errno = save_errno; - return EX_TEMPFAIL; - } - } -#endif /* NAMED_BIND */ -#if NETINET6 - /* - ** Try v6 first, then fall back to v4. - ** If we found a v6 address, but no v4 - ** addresses, then TEMPFAIL. - */ - - if (family == AF_INET6) - { - family = AF_INET; - goto v4retry; - } - if (v6found) - goto v6tempfail; -#endif /* NETINET6 */ - save_errno = errno; - mci_setstat(mci, EX_NOHOST, "5.1.2", NULL); - errno = save_errno; - return EX_NOHOST; - } - addr.sa.sa_family = hp->h_addrtype; - switch (hp->h_addrtype) - { -#if NETINET - case AF_INET: - memmove(&addr.sin.sin_addr, - hp->h_addr, - INADDRSZ); - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - memmove(&addr.sin6.sin6_addr, - hp->h_addr, - IN6ADDRSZ); - break; -#endif /* NETINET6 */ - - default: - if (hp->h_length > sizeof(addr.sa.sa_data)) - { - syserr("makeconnection: long sa_data: family %d len %d", - hp->h_addrtype, hp->h_length); - mci_setstat(mci, EX_NOHOST, "5.1.2", NULL); - errno = EINVAL; - return EX_NOHOST; - } - memmove(addr.sa.sa_data, hp->h_addr, hp->h_length); - break; - } - addrno = 1; - } - - /* - ** Determine the port number. - */ - - if (port == 0) - { -#ifdef NO_GETSERVBYNAME - port = htons(25); -#else /* NO_GETSERVBYNAME */ - register struct servent *sp = getservbyname("smtp", "tcp"); - - if (sp == NULL) - { - if (LogLevel > 2) - sm_syslog(LOG_ERR, NOQID, - "makeconnection: service \"smtp\" unknown"); - port = htons(25); - } - else - port = sp->s_port; -#endif /* NO_GETSERVBYNAME */ - } - -#if NETINET6 - if (addr.sa.sa_family == AF_INET6 && - IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr) && - ClientSettings[AF_INET].d_addr.sa.sa_family != 0) - { - /* - ** Ignore mapped IPv4 address since - ** there is a ClientPortOptions setting - ** for IPv4. - */ - - goto nextaddr; - } -#endif /* NETINET6 */ - - switch (addr.sa.sa_family) - { -#if NETINET - case AF_INET: - addr.sin.sin_port = port; - addrlen = sizeof(struct sockaddr_in); - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - addr.sin6.sin6_port = port; - addrlen = sizeof(struct sockaddr_in6); - break; -#endif /* NETINET6 */ - -#if NETISO - case AF_ISO: - /* assume two byte transport selector */ - memmove(TSEL((struct sockaddr_iso *) &addr), (char *) &port, 2); - addrlen = sizeof(struct sockaddr_iso); - break; -#endif /* NETISO */ - - default: - syserr("Can't connect to address family %d", addr.sa.sa_family); - mci_setstat(mci, EX_NOHOST, "5.1.2", NULL); - errno = EINVAL; -#if NETINET6 - if (hp != NULL) - freehostent(hp); -#endif /* NETINET6 */ - return EX_NOHOST; - } - - /* - ** Try to actually open the connection. - */ - -#if XLA - /* if too many connections, don't bother trying */ - if (!xla_noqueue_ok(host)) - { -# if NETINET6 - if (hp != NULL) - freehostent(hp); -# endif /* NETINET6 */ - return EX_TEMPFAIL; - } -#endif /* XLA */ - - for (;;) - { - if (tTd(16, 1)) - sm_dprintf("makeconnection (%s [%s].%d (%d))\n", - host, anynet_ntoa(&addr), ntohs(port), - (int) addr.sa.sa_family); - - /* save for logging */ - CurHostAddr = addr; - -#if HASRRESVPORT - if (bitnset(M_SECURE_PORT, mci->mci_mailer->m_flags)) - { - int rport = IPPORT_RESERVED - 1; - - s = rresvport(&rport); - } - else -#endif /* HASRRESVPORT */ - { - s = socket(addr.sa.sa_family, SOCK_STREAM, 0); - } - if (s < 0) - { - save_errno = errno; - syserr("makeconnection: cannot create socket"); -#if XLA - xla_host_end(host); -#endif /* XLA */ - mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); -#if NETINET6 - if (hp != NULL) - freehostent(hp); -#endif /* NETINET6 */ - errno = save_errno; - return EX_TEMPFAIL; - } - -#ifdef SO_SNDBUF - if (ClientSettings[family].d_tcpsndbufsize > 0) - { - if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, - (char *) &ClientSettings[family].d_tcpsndbufsize, - sizeof(ClientSettings[family].d_tcpsndbufsize)) < 0) - syserr("makeconnection: setsockopt(SO_SNDBUF)"); - } -#endif /* SO_SNDBUF */ -#ifdef SO_RCVBUF - if (ClientSettings[family].d_tcprcvbufsize > 0) - { - if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, - (char *) &ClientSettings[family].d_tcprcvbufsize, - sizeof(ClientSettings[family].d_tcprcvbufsize)) < 0) - syserr("makeconnection: setsockopt(SO_RCVBUF)"); - } -#endif /* SO_RCVBUF */ - - if (tTd(16, 1)) - sm_dprintf("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 (e->e_xfp != NULL) /* for debugging */ - (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); - errno = 0; /* for debugging */ - - if (clt_bind) - { - int on = 1; - - switch (clt_addr.sa.sa_family) - { -#if NETINET - case AF_INET: - if (clt_addr.sin.sin_port != 0) - (void) setsockopt(s, SOL_SOCKET, - SO_REUSEADDR, - (char *) &on, - sizeof(on)); - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - if (clt_addr.sin6.sin6_port != 0) - (void) setsockopt(s, SOL_SOCKET, - SO_REUSEADDR, - (char *) &on, - sizeof(on)); - break; -#endif /* NETINET6 */ - } - - if (bind(s, &clt_addr.sa, socksize) < 0) - { - save_errno = errno; - (void) close(s); - errno = save_errno; - syserr("makeconnection: cannot bind socket [%s]", - anynet_ntoa(&clt_addr)); -#if NETINET6 - if (hp != NULL) - freehostent(hp); -#endif /* NETINET6 */ - errno = save_errno; - return EX_TEMPFAIL; - } - } - - /* - ** Linux seems to hang in connect for 90 minutes (!!!). - ** Time out the connect to avoid this problem. - */ - - if (setjmp(CtxConnectTimeout) == 0) - { - int i; - - if (e->e_ntries <= 0 && TimeOuts.to_iconnect != 0) - ev = sm_setevent(TimeOuts.to_iconnect, - connecttimeout, 0); - else if (TimeOuts.to_connect != 0) - ev = sm_setevent(TimeOuts.to_connect, - connecttimeout, 0); - else - ev = NULL; - - switch (ConnectOnlyTo.sa.sa_family) - { -#if NETINET - case AF_INET: - addr.sin.sin_addr.s_addr = ConnectOnlyTo.sin.sin_addr.s_addr; - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - memmove(&addr.sin6.sin6_addr, - &ConnectOnlyTo.sin6.sin6_addr, - IN6ADDRSZ); - break; -#endif /* NETINET6 */ - } - if (tTd(16, 1)) - sm_dprintf("Connecting to [%s]...\n", anynet_ntoa(&addr)); - i = connect(s, (struct sockaddr *) &addr, addrlen); - save_errno = errno; - if (ev != NULL) - sm_clrevent(ev); - if (i >= 0) - break; - } - else - save_errno = errno; - - /* couldn't connect.... figure out why */ - (void) close(s); - - /* if running demand-dialed connection, try again */ - if (DialDelay > 0 && firstconnect && - bitnset(M_DIALDELAY, mci->mci_mailer->m_flags)) - { - if (tTd(16, 1)) - sm_dprintf("Connect failed (%s); trying again...\n", - sm_errstring(save_errno)); - firstconnect = false; - (void) sleep(DialDelay); - continue; - } - - if (LogLevel > 13) - sm_syslog(LOG_INFO, e->e_id, - "makeconnection (%s [%s]) failed: %s", - host, anynet_ntoa(&addr), - sm_errstring(save_errno)); - -#if NETINET6 -nextaddr: -#endif /* NETINET6 */ - if (hp != NULL && hp->h_addr_list[addrno] != NULL && - (enough == 0 || curtime() < enough)) - { - if (tTd(16, 1)) - sm_dprintf("Connect failed (%s); trying new address....\n", - sm_errstring(save_errno)); - switch (addr.sa.sa_family) - { -#if NETINET - case AF_INET: - memmove(&addr.sin.sin_addr, - hp->h_addr_list[addrno++], - INADDRSZ); - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - memmove(&addr.sin6.sin6_addr, - hp->h_addr_list[addrno++], - IN6ADDRSZ); - break; -#endif /* NETINET6 */ - - default: - memmove(addr.sa.sa_data, - hp->h_addr_list[addrno++], - hp->h_length); - break; - } - continue; - } - errno = save_errno; - -#if NETINET6 - if (family == AF_INET6) - { - if (tTd(16, 1)) - sm_dprintf("Connect failed (%s); retrying with AF_INET....\n", - sm_errstring(save_errno)); - v6found = true; - family = AF_INET; - if (hp != NULL) - { - freehostent(hp); - hp = NULL; - } - goto v4retry; - } - v6tempfail: -#endif /* NETINET6 */ - /* couldn't open connection */ -#if NETINET6 - /* Don't clobber an already saved errno from v4retry */ - if (errno > 0) -#endif /* NETINET6 */ - save_errno = errno; - if (tTd(16, 1)) - sm_dprintf("Connect failed (%s)\n", - sm_errstring(save_errno)); -#if XLA - xla_host_end(host); -#endif /* XLA */ - mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL); -#if NETINET6 - if (hp != NULL) - freehostent(hp); -#endif /* NETINET6 */ - errno = save_errno; - return EX_TEMPFAIL; - } - -#if NETINET6 - if (hp != NULL) - { - freehostent(hp); - hp = NULL; - } -#endif /* NETINET6 */ - - /* connection ok, put it into canonical form */ - mci->mci_out = NULL; - if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, - (void *) &s, - SM_IO_WRONLY_B, NULL)) == NULL || - (s = dup(s)) < 0 || - (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, - (void *) &s, - SM_IO_RDONLY_B, NULL)) == NULL) - { - save_errno = errno; - syserr("cannot open SMTP client channel, fd=%d", s); - mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); - if (mci->mci_out != NULL) - (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); - (void) close(s); - errno = save_errno; - return EX_TEMPFAIL; - } - sm_io_automode(mci->mci_out, mci->mci_in); - - /* set {client_flags} */ - if (ClientSettings[addr.sa.sa_family].d_mflags != NULL) - { - macdefine(&mci->mci_macro, A_PERM, - macid("{client_flags}"), - ClientSettings[addr.sa.sa_family].d_mflags); - } - else - macdefine(&mci->mci_macro, A_PERM, - macid("{client_flags}"), ""); - - /* "add" {client_flags} to bitmap */ - if (bitnset(D_IFNHELO, ClientSettings[addr.sa.sa_family].d_flags)) - { - /* look for just this one flag */ - setbitn(D_IFNHELO, d_flags); - } - - /* find out name for Interface through which we connect */ - len = sizeof(addr); - if (getsockname(s, &addr.sa, &len) == 0) - { - char *name; - char family[5]; - - macdefine(&BlankEnvelope.e_macro, A_TEMP, - macid("{if_addr_out}"), anynet_ntoa(&addr)); - (void) sm_snprintf(family, sizeof(family), "%d", - addr.sa.sa_family); - macdefine(&BlankEnvelope.e_macro, A_TEMP, - macid("{if_family_out}"), family); - - name = hostnamebyanyaddr(&addr); - macdefine(&BlankEnvelope.e_macro, A_TEMP, - macid("{if_name_out}"), name); - if (LogLevel > 11) - { - /* log connection information */ - sm_syslog(LOG_INFO, e->e_id, - "SMTP outgoing connect on %.40s", name); - } - if (bitnset(D_IFNHELO, d_flags)) - { - if (name[0] != '[' && strchr(name, '.') != NULL) - mci->mci_heloname = newstr(name); - } - } - else - { - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{if_name_out}"), NULL); - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{if_addr_out}"), NULL); - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{if_family_out}"), NULL); - } - - /* Use the configured HeloName as appropriate */ - if (HeloName != NULL && HeloName[0] != '\0') - mci->mci_heloname = newstr(HeloName); - - mci_setstat(mci, EX_OK, NULL, NULL); - return EX_OK; -} - -static void -connecttimeout(ignore) - int ignore; -{ - /* - ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD - ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE - ** DOING. - */ - - errno = ETIMEDOUT; - longjmp(CtxConnectTimeout, 1); -} -/* -** MAKECONNECTION_DS -- make a connection to a domain socket. -** -** Parameters: -** mux_path -- the path of the socket to connect to. -** mci -- a pointer to the mail connection information -** structure to be filled in. -** -** Returns: -** An exit code telling whether the connection could be -** made and if not why not. -** -** Side Effects: -** none. -*/ - -#if NETUNIX -int -makeconnection_ds(mux_path, mci) - char *mux_path; - register MCI *mci; -{ - int sock; - int rval, save_errno; - long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK; - struct sockaddr_un unix_addr; - - /* if not safe, don't connect */ - rval = safefile(mux_path, RunAsUid, RunAsGid, RunAsUserName, - sff, S_IRUSR|S_IWUSR, NULL); - - if (rval != 0) - { - syserr("makeconnection_ds: unsafe domain socket %s", - mux_path); - mci_setstat(mci, EX_TEMPFAIL, "4.3.5", NULL); - errno = rval; - return EX_TEMPFAIL; - } - - /* prepare address structure */ - memset(&unix_addr, '\0', sizeof(unix_addr)); - unix_addr.sun_family = AF_UNIX; - - if (strlen(mux_path) >= sizeof(unix_addr.sun_path)) - { - syserr("makeconnection_ds: domain socket name %s too long", - mux_path); - - /* XXX why TEMPFAIL but 5.x.y ? */ - mci_setstat(mci, EX_TEMPFAIL, "5.3.5", NULL); - errno = ENAMETOOLONG; - return EX_UNAVAILABLE; - } - (void) sm_strlcpy(unix_addr.sun_path, mux_path, - sizeof(unix_addr.sun_path)); - - /* initialize domain socket */ - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock == -1) - { - save_errno = errno; - syserr("makeconnection_ds: could not create domain socket %s", - mux_path); - mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); - errno = save_errno; - return EX_TEMPFAIL; - } - - /* connect to server */ - if (connect(sock, (struct sockaddr *) &unix_addr, - sizeof(unix_addr)) == -1) - { - save_errno = errno; - syserr("Could not connect to socket %s", mux_path); - mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL); - (void) close(sock); - errno = save_errno; - return EX_TEMPFAIL; - } - - /* connection ok, put it into canonical form */ - mci->mci_out = NULL; - if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, - (void *) &sock, SM_IO_WRONLY_B, NULL)) - == NULL - || (sock = dup(sock)) < 0 || - (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, - (void *) &sock, SM_IO_RDONLY_B, NULL)) - == NULL) - { - save_errno = errno; - syserr("cannot open SMTP client channel, fd=%d", sock); - mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); - if (mci->mci_out != NULL) - (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); - (void) close(sock); - errno = save_errno; - return EX_TEMPFAIL; - } - sm_io_automode(mci->mci_out, mci->mci_in); - - mci_setstat(mci, EX_OK, NULL, NULL); - errno = 0; - return EX_OK; -} -#endif /* NETUNIX */ -/* -** SHUTDOWN_DAEMON -- Performs a clean shutdown of the daemon -** -** Parameters: -** none. -** -** Returns: -** none. -** -** Side Effects: -** closes control socket, exits. -*/ - -void -shutdown_daemon() -{ - int i; - char *reason; - - sm_allsignals(true); - - reason = ShutdownRequest; - ShutdownRequest = NULL; - PendingSignal = 0; - - if (LogLevel > 9) - sm_syslog(LOG_INFO, CurEnv->e_id, "stopping daemon, reason=%s", - reason == NULL ? "implicit call" : reason); - - FileName = NULL; - closecontrolsocket(true); -#if XLA - xla_all_end(); -#endif /* XLA */ - - for (i = 0; i < NDaemons; i++) - { - if (Daemons[i].d_socket >= 0) - { - (void) close(Daemons[i].d_socket); - Daemons[i].d_socket = -1; - -#if _FFR_DAEMON_NETUNIX -# if NETUNIX - /* Remove named sockets */ - if (Daemons[i].d_addr.sa.sa_family == AF_UNIX) - { - int rval; - long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_MUSTOWN|SFF_EXECOK|SFF_CREAT; - - /* if not safe, don't use it */ - rval = safefile(Daemons[i].d_addr.sunix.sun_path, - RunAsUid, RunAsGid, - RunAsUserName, sff, - S_IRUSR|S_IWUSR, NULL); - if (rval == 0 && - unlink(Daemons[i].d_addr.sunix.sun_path) < 0) - { - sm_syslog(LOG_WARNING, NOQID, - "Could not remove daemon %s socket: %s: %s", - Daemons[i].d_name, - Daemons[i].d_addr.sunix.sun_path, - sm_errstring(errno)); - } - } -# endif /* NETUNIX */ -#endif /* _FFR_DAEMON_NETUNIX */ - } - } - - finis(false, true, EX_OK); -} -/* -** RESTART_DAEMON -- Performs a clean restart of the daemon -** -** Parameters: -** none. -** -** Returns: -** none. -** -** Side Effects: -** restarts the daemon or exits if restart fails. -*/ - -/* Make a non-DFL/IGN signal a noop */ -#define SM_NOOP_SIGNAL(sig, old) \ -do \ -{ \ - (old) = sm_signal((sig), sm_signal_noop); \ - if ((old) == SIG_IGN || (old) == SIG_DFL) \ - (void) sm_signal((sig), (old)); \ -} while (0) - -void -restart_daemon() -{ - bool drop; - int save_errno; - char *reason; - sigfunc_t ignore, oalrm, ousr1; - extern int DtableSize; - - /* clear the events to turn off SIGALRMs */ - sm_clear_events(); - sm_allsignals(true); - - reason = RestartRequest; - RestartRequest = NULL; - PendingSignal = 0; - - if (SaveArgv[0][0] != '/') - { - if (LogLevel > 3) - sm_syslog(LOG_INFO, NOQID, - "could not restart: need full path"); - finis(false, true, EX_OSFILE); - /* NOTREACHED */ - } - if (LogLevel > 3) - sm_syslog(LOG_INFO, NOQID, "restarting %s due to %s", - SaveArgv[0], - reason == NULL ? "implicit call" : reason); - - closecontrolsocket(true); -#if SM_CONF_SHM - cleanup_shm(DaemonPid == getpid()); -#endif /* SM_CONF_SHM */ - - /* close locked pid file */ - close_sendmail_pid(); - - /* - ** Want to drop to the user who started the process in all cases - ** *but* when running as "smmsp" for the clientmqueue queue run - ** daemon. In that case, UseMSP will be true, RunAsUid should not - ** be root, and RealUid should be either 0 or RunAsUid. - */ - - drop = !(UseMSP && RunAsUid != 0 && - (RealUid == 0 || RealUid == RunAsUid)); - - if (drop_privileges(drop) != EX_OK) - { - if (LogLevel > 0) - sm_syslog(LOG_ALERT, NOQID, - "could not drop privileges: %s", - sm_errstring(errno)); - finis(false, true, EX_OSERR); - /* NOTREACHED */ - } - - sm_close_on_exec(STDERR_FILENO + 1, DtableSize); - - /* - ** Need to allow signals before execve() to make them "harmless". - ** However, the default action can be "terminate", so it isn't - ** really harmless. Setting signals to IGN will cause them to be - ** ignored in the new process to, so that isn't a good alternative. - */ - - SM_NOOP_SIGNAL(SIGALRM, oalrm); - SM_NOOP_SIGNAL(SIGCHLD, ignore); - SM_NOOP_SIGNAL(SIGHUP, ignore); - SM_NOOP_SIGNAL(SIGINT, ignore); - SM_NOOP_SIGNAL(SIGPIPE, ignore); - SM_NOOP_SIGNAL(SIGTERM, ignore); -#ifdef SIGUSR1 - SM_NOOP_SIGNAL(SIGUSR1, ousr1); -#endif /* SIGUSR1 */ - - /* Turn back on signals */ - sm_allsignals(false); - - (void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron); - save_errno = errno; - - /* block signals again and restore needed signals */ - sm_allsignals(true); - - /* For finis() events */ - (void) sm_signal(SIGALRM, oalrm); - -#ifdef SIGUSR1 - /* For debugging finis() */ - (void) sm_signal(SIGUSR1, ousr1); -#endif /* SIGUSR1 */ - - errno = save_errno; - if (LogLevel > 0) - sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %s", - SaveArgv[0], sm_errstring(errno)); - finis(false, true, EX_OSFILE); - /* NOTREACHED */ -} -/* -** 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. -*/ - -struct hostent * -myhostname(hostbuf, size) - char hostbuf[]; - int size; -{ - register struct hostent *hp; - - if (gethostname(hostbuf, size) < 0 || hostbuf[0] == '\0') - (void) sm_strlcpy(hostbuf, "localhost", size); - hp = sm_gethostbyname(hostbuf, InetMode); -#if NETINET && NETINET6 - if (hp == NULL && InetMode == AF_INET6) - { - /* - ** It's possible that this IPv6 enabled machine doesn't - ** actually have any IPv6 interfaces and, therefore, no - ** IPv6 addresses. Fall back to AF_INET. - */ - - hp = sm_gethostbyname(hostbuf, AF_INET); - } -#endif /* NETINET && NETINET6 */ - if (hp == NULL) - return NULL; - if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL) - (void) cleanstrcpy(hostbuf, hp->h_name, size); - -#if NETINFO - if (strchr(hostbuf, '.') == NULL) - { - char *domainname; - - domainname = ni_propval("/locations", NULL, "resolver", - "domain", '\0'); - if (domainname != NULL && - strlen(domainname) + strlen(hostbuf) + 1 < size) - (void) sm_strlcat2(hostbuf, ".", domainname, size); - } -#endif /* NETINFO */ - - /* - ** If there is still no dot in the name, try looking for a - ** dotted alias. - */ - - if (strchr(hostbuf, '.') == NULL) - { - char **ha; - - for (ha = hp->h_aliases; ha != NULL && *ha != NULL; ha++) - { - if (strchr(*ha, '.') != NULL) - { - (void) cleanstrcpy(hostbuf, *ha, size - 1); - hostbuf[size - 1] = '\0'; - break; - } - } - } - - /* - ** If _still_ no dot, wait for a while and try again -- it is - ** possible that some service is starting up. This can result - ** in excessive delays if the system is badly configured, but - ** there really isn't a way around that, particularly given that - ** the config file hasn't been read at this point. - ** All in all, a bit of a mess. - */ - - if (strchr(hostbuf, '.') == NULL && - !getcanonname(hostbuf, size, true, NULL)) - { - sm_syslog(LOG_CRIT, NOQID, - "My unqualified host name (%s) unknown; sleeping for retry", - hostbuf); - message("My unqualified host name (%s) unknown; sleeping for retry", - hostbuf); - (void) sleep(60); - if (!getcanonname(hostbuf, size, true, NULL)) - { - sm_syslog(LOG_ALERT, NOQID, - "unable to qualify my own domain name (%s) -- using short name", - hostbuf); - message("WARNING: unable to qualify my own domain name (%s) -- using short name", - hostbuf); - } - } - return hp; -} -/* -** ADDRCMP -- compare two host addresses -** -** Parameters: -** hp -- hostent structure for the first address -** ha -- actual first address -** sa -- second address -** -** Returns: -** 0 -- if ha and sa match -** else -- they don't match -*/ - -static int -addrcmp(hp, ha, sa) - struct hostent *hp; - char *ha; - SOCKADDR *sa; -{ -#if NETINET6 - unsigned char *a; -#endif /* NETINET6 */ - - switch (sa->sa.sa_family) - { -#if NETINET - case AF_INET: - if (hp->h_addrtype == AF_INET) - return memcmp(ha, (char *) &sa->sin.sin_addr, INADDRSZ); - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - a = (unsigned char *) &sa->sin6.sin6_addr; - - /* Straight binary comparison */ - if (hp->h_addrtype == AF_INET6) - return memcmp(ha, a, IN6ADDRSZ); - - /* If IPv4-mapped IPv6 address, compare the IPv4 section */ - if (hp->h_addrtype == AF_INET && - IN6_IS_ADDR_V4MAPPED(&sa->sin6.sin6_addr)) - return memcmp(a + IN6ADDRSZ - INADDRSZ, ha, INADDRSZ); - break; -#endif /* NETINET6 */ - } - return -1; -} -/* -** GETAUTHINFO -- get the real host name associated with a file descriptor -** -** Uses RFC1413 protocol to try to get info from the other end. -** -** Parameters: -** fd -- the descriptor -** may_be_forged -- an outage that is set to true if the -** forward lookup of RealHostName does not match -** RealHostAddr; set to false if they do match. -** -** Returns: -** The user@host information associated with this descriptor. -*/ - -static jmp_buf CtxAuthTimeout; - -static void -authtimeout(ignore) - int ignore; -{ - /* - ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD - ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE - ** DOING. - */ - - errno = ETIMEDOUT; - longjmp(CtxAuthTimeout, 1); -} - -char * -getauthinfo(fd, may_be_forged) - int fd; - bool *may_be_forged; -{ - unsigned short SM_NONVOLATILE port = 0; - SOCKADDR_LEN_T falen; - register char *volatile p = NULL; - SOCKADDR la; - SOCKADDR_LEN_T lalen; -#ifndef NO_GETSERVBYNAME - register struct servent *sp; -# if NETINET - static unsigned short port4 = 0; -# endif /* NETINET */ -# if NETINET6 - static unsigned short port6 = 0; -# endif /* NETINET6 */ -#endif /* ! NO_GETSERVBYNAME */ - volatile int s; - int i = 0; - size_t len; - SM_EVENT *ev; - int nleft; - struct hostent *hp; - char *ostype = NULL; - char **ha; - char ibuf[MAXNAME + 1]; - static char hbuf[MAXNAME + MAXAUTHINFO + 11]; - - *may_be_forged = false; - falen = sizeof(RealHostAddr); - if (isatty(fd) || (i = getpeername(fd, &RealHostAddr.sa, &falen)) < 0 || - falen <= 0 || RealHostAddr.sa.sa_family == 0) - { - if (i < 0) - { - /* - ** ENOTSOCK is OK: bail on anything else, but reset - ** errno in this case, so a mis-report doesn't - ** happen later. - */ - - if (errno != ENOTSOCK) - return NULL; - errno = 0; - } - (void) sm_strlcpyn(hbuf, sizeof(hbuf), 2, RealUserName, - "@localhost"); - if (tTd(9, 1)) - sm_dprintf("getauthinfo: %s\n", hbuf); - return hbuf; - } - - if (RealHostName == NULL) - { - /* translate that to a host name */ - RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr)); - if (strlen(RealHostName) > MAXNAME) - RealHostName[MAXNAME] = '\0'; /* XXX - 1 ? */ - } - - /* cross check RealHostName with forward DNS lookup */ - if (anynet_ntoa(&RealHostAddr)[0] != '[' && - RealHostName[0] != '[') - { - int family; - - family = RealHostAddr.sa.sa_family; -#if NETINET6 && NEEDSGETIPNODE - /* - ** If RealHostAddr is an IPv6 connection with an - ** IPv4-mapped address, we need RealHostName's IPv4 - ** address(es) for addrcmp() to compare against - ** RealHostAddr. - ** - ** Actually, we only need to do this for systems - ** which NEEDSGETIPNODE since the real getipnodebyname() - ** already does V4MAPPED address via the AI_V4MAPPEDCFG - ** flag. A better fix to this problem is to add this - ** functionality to our stub getipnodebyname(). - */ - - if (family == AF_INET6 && - IN6_IS_ADDR_V4MAPPED(&RealHostAddr.sin6.sin6_addr)) - family = AF_INET; -#endif /* NETINET6 && NEEDSGETIPNODE */ - - /* try to match the reverse against the forward lookup */ - hp = sm_gethostbyname(RealHostName, family); - if (hp == NULL) - { - /* XXX: Could be a temporary error on forward lookup */ - *may_be_forged = true; - } - else - { - for (ha = hp->h_addr_list; *ha != NULL; ha++) - { - if (addrcmp(hp, *ha, &RealHostAddr) == 0) - break; - } - *may_be_forged = *ha == NULL; -#if NETINET6 - freehostent(hp); - hp = NULL; -#endif /* NETINET6 */ - } - } - - if (TimeOuts.to_ident == 0) - goto noident; - - lalen = sizeof(la); - switch (RealHostAddr.sa.sa_family) - { -#if NETINET - case AF_INET: - if (getsockname(fd, &la.sa, &lalen) < 0 || - lalen <= 0 || - la.sa.sa_family != AF_INET) - { - /* no ident info */ - goto noident; - } - port = RealHostAddr.sin.sin_port; - - /* create ident query */ - (void) sm_snprintf(ibuf, sizeof(ibuf), "%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 */ -# ifdef NO_GETSERVBYNAME - RealHostAddr.sin.sin_port = htons(113); -# else /* NO_GETSERVBYNAME */ - - /* - ** getservbyname() consumes about 5% of the time - ** when receiving a small message (almost all of the time - ** spent in this routine). - ** Hence we store the port in a static variable - ** to save this time. - ** The portnumber shouldn't change very often... - ** This code makes the assumption that the port number - ** is not 0. - */ - - if (port4 == 0) - { - sp = getservbyname("auth", "tcp"); - if (sp != NULL) - port4 = sp->s_port; - else - port4 = htons(113); - } - RealHostAddr.sin.sin_port = port4; - break; -# endif /* NO_GETSERVBYNAME */ -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - if (getsockname(fd, &la.sa, &lalen) < 0 || - lalen <= 0 || - la.sa.sa_family != AF_INET6) - { - /* no ident info */ - goto noident; - } - port = RealHostAddr.sin6.sin6_port; - - /* create ident query */ - (void) sm_snprintf(ibuf, sizeof(ibuf), "%d,%d\r\n", - ntohs(RealHostAddr.sin6.sin6_port), - ntohs(la.sin6.sin6_port)); - - /* create local address */ - la.sin6.sin6_port = 0; - - /* create foreign address */ -# ifdef NO_GETSERVBYNAME - RealHostAddr.sin6.sin6_port = htons(113); -# else /* NO_GETSERVBYNAME */ - if (port6 == 0) - { - sp = getservbyname("auth", "tcp"); - if (sp != NULL) - port6 = sp->s_port; - else - port6 = htons(113); - } - RealHostAddr.sin6.sin6_port = port6; - break; -# endif /* NO_GETSERVBYNAME */ -#endif /* NETINET6 */ - default: - /* no ident info */ - goto noident; - } - - s = -1; - if (setjmp(CtxAuthTimeout) != 0) - { - if (s >= 0) - (void) close(s); - goto noident; - } - - /* put a timeout around the whole thing */ - ev = sm_setevent(TimeOuts.to_ident, authtimeout, 0); - - /* connect to foreign IDENT server using same address as SMTP socket */ - s = socket(la.sa.sa_family, SOCK_STREAM, 0); - if (s < 0) - { - sm_clrevent(ev); - goto noident; - } - if (bind(s, &la.sa, lalen) < 0 || - connect(s, &RealHostAddr.sa, lalen) < 0) - goto closeident; - - if (tTd(9, 10)) - sm_dprintf("getauthinfo: sent %s", ibuf); - - /* send query */ - if (write(s, ibuf, strlen(ibuf)) < 0) - goto closeident; - - /* get result */ - p = &ibuf[0]; - nleft = sizeof(ibuf) - 1; - while ((i = read(s, p, nleft)) > 0) - { - char *s; - - p += i; - nleft -= i; - *p = '\0'; - if ((s = strchr(ibuf, '\n')) != NULL) - { - if (p > s + 1) - { - p = s + 1; - *p = '\0'; - } - break; - } - if (nleft <= 0) - break; - } - (void) close(s); - sm_clrevent(ev); - if (i < 0 || p == &ibuf[0]) - goto noident; - - if (p >= &ibuf[2] && *--p == '\n' && *--p == '\r') - p--; - *++p = '\0'; - - if (tTd(9, 3)) - sm_dprintf("getauthinfo: got %s\n", ibuf); - - /* parse result */ - p = strchr(ibuf, ':'); - if (p == NULL) - { - /* malformed response */ - goto noident; - } - while (isascii(*++p) && isspace(*p)) - continue; - if (sm_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 */ - while (isascii(*p) && isspace(*p)) - p++; - ostype = p; - p = strchr(p, ':'); - if (p == NULL) - { - /* malformed response */ - goto noident; - } - else - { - char *charset; - - *p = '\0'; - charset = strchr(ostype, ','); - if (charset != NULL) - *charset = '\0'; - } - - /* 1413 says don't do this -- but it's broken otherwise */ - while (isascii(*++p) && isspace(*p)) - continue; - - /* p now points to the authenticated name -- copy carefully */ - if (sm_strncasecmp(ostype, "other", 5) == 0 && - (ostype[5] == ' ' || ostype[5] == '\0')) - { - (void) sm_strlcpy(hbuf, "IDENT:", sizeof(hbuf)); - cleanstrcpy(&hbuf[6], p, MAXAUTHINFO); - } - else - cleanstrcpy(hbuf, p, MAXAUTHINFO); - len = strlen(hbuf); - (void) sm_strlcpyn(&hbuf[len], sizeof(hbuf) - len, 2, "@", - RealHostName == NULL ? "localhost" : RealHostName); - goto postident; - -closeident: - (void) close(s); - sm_clrevent(ev); - -noident: - /* put back the original incoming port */ - switch (RealHostAddr.sa.sa_family) - { -#if NETINET - case AF_INET: - if (port > 0) - RealHostAddr.sin.sin_port = port; - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - if (port > 0) - RealHostAddr.sin6.sin6_port = port; - break; -#endif /* NETINET6 */ - } - - if (RealHostName == NULL) - { - if (tTd(9, 1)) - sm_dprintf("getauthinfo: NULL\n"); - return NULL; - } - (void) sm_strlcpy(hbuf, RealHostName, sizeof(hbuf)); - -postident: -#if IP_SRCROUTE -# ifndef GET_IPOPT_DST -# define GET_IPOPT_DST(dst) (dst) -# endif /* ! GET_IPOPT_DST */ - /* - ** Extract IP source routing information. - ** - ** Format of output for a connection from site a through b - ** through c to d: - ** loose: @site-c@site-b:site-a - ** strict: !@site-c@site-b:site-a - ** - ** o - pointer within ipopt_list structure. - ** q - pointer within ls/ss rr route data - ** p - pointer to hbuf - */ - - if (RealHostAddr.sa.sa_family == AF_INET) - { - SOCKOPT_LEN_T ipoptlen; - int j; - unsigned char *q; - unsigned char *o; - int l; - struct IPOPTION ipopt; - - ipoptlen = sizeof(ipopt); - if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS, - (char *) &ipopt, &ipoptlen) < 0) - goto noipsr; - if (ipoptlen == 0) - goto noipsr; - o = (unsigned char *) ipopt.IP_LIST; - while (o != NULL && o < (unsigned char *) &ipopt + ipoptlen) - { - switch (*o) - { - case IPOPT_EOL: - o = NULL; - break; - - case IPOPT_NOP: - o++; - break; - - case IPOPT_SSRR: - case IPOPT_LSRR: - /* - ** Source routing. - ** o[0] is the option type (loose/strict). - ** o[1] is the length of this option, - ** including option type and - ** length. - ** o[2] is the pointer into the route - ** data. - ** o[3] begins the route data. - */ - - p = &hbuf[strlen(hbuf)]; - l = sizeof(hbuf) - (hbuf - p) - 6; - (void) sm_snprintf(p, SPACELEFT(hbuf, p), - " [%s@%.*s", - *o == IPOPT_SSRR ? "!" : "", - l > 240 ? 120 : l / 2, - inet_ntoa(GET_IPOPT_DST(ipopt.IP_DST))); - i = strlen(p); - p += i; - l -= strlen(p); - - j = o[1] / sizeof(struct in_addr) - 1; - - /* q skips length and router pointer to data */ - q = &o[3]; - for ( ; j >= 0; j--) - { - struct in_addr addr; - - memcpy(&addr, q, sizeof(addr)); - (void) sm_snprintf(p, - SPACELEFT(hbuf, p), - "%c%.*s", - j != 0 ? '@' : ':', - l > 240 ? 120 : - j == 0 ? l : l / 2, - inet_ntoa(addr)); - i = strlen(p); - p += i; - l -= i + 1; - q += sizeof(struct in_addr); - } - o += o[1]; - break; - - default: - /* Skip over option */ - o += o[1]; - break; - } - } - (void) sm_snprintf(p, SPACELEFT(hbuf, p), "]"); - goto postipsr; - } - -noipsr: -#endif /* IP_SRCROUTE */ - if (RealHostName != NULL && RealHostName[0] != '[') - { - p = &hbuf[strlen(hbuf)]; - (void) sm_snprintf(p, SPACELEFT(hbuf, p), " [%.100s]", - anynet_ntoa(&RealHostAddr)); - } - if (*may_be_forged) - { - p = &hbuf[strlen(hbuf)]; - (void) sm_strlcpy(p, " (may be forged)", SPACELEFT(hbuf, p)); - macdefine(&BlankEnvelope.e_macro, A_PERM, - macid("{client_resolve}"), "FORGED"); - } - -#if IP_SRCROUTE -postipsr: -#endif /* IP_SRCROUTE */ - - /* put back the original incoming port */ - switch (RealHostAddr.sa.sa_family) - { -#if NETINET - case AF_INET: - if (port > 0) - RealHostAddr.sin.sin_port = port; - break; -#endif /* NETINET */ - -#if NETINET6 - case AF_INET6: - if (port > 0) - RealHostAddr.sin6.sin6_port = port; - break; -#endif /* NETINET6 */ - } - - if (tTd(9, 1)) - sm_dprintf("getauthinfo: %s\n", hbuf); - return hbuf; -} -/* -** HOST_MAP_LOOKUP -- turn a hostname into canonical form -** -** Parameters: -** map -- a pointer to this map. -** 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 (unless MF_MATCHONLY is set, which will cause the -** status only to be returned). -*/ - -char * -host_map_lookup(map, name, av, statp) - MAP *map; - char *name; - char **av; - int *statp; -{ - register struct hostent *hp; -#if NETINET - struct in_addr in_addr; -#endif /* NETINET */ -#if NETINET6 - struct in6_addr in6_addr; -#endif /* NETINET6 */ - char *cp, *ans = NULL; - register STAB *s; - time_t now; -#if NAMED_BIND - time_t SM_NONVOLATILE retrans = 0; - int SM_NONVOLATILE retry = 0; -#endif /* NAMED_BIND */ - char hbuf[MAXNAME + 1]; - - /* - ** See if we have already looked up this name. If so, just - ** return it (unless expired). - */ - - now = curtime(); - s = stab(name, ST_NAMECANON, ST_ENTER); - if (bitset(NCF_VALID, s->s_namecanon.nc_flags) && - s->s_namecanon.nc_exp >= now) - { - if (tTd(9, 1)) - sm_dprintf("host_map_lookup(%s) => CACHE %s\n", - name, - s->s_namecanon.nc_cname == NULL - ? "NULL" - : s->s_namecanon.nc_cname); - errno = s->s_namecanon.nc_errno; - SM_SET_H_ERRNO(s->s_namecanon.nc_herrno); - *statp = s->s_namecanon.nc_stat; - if (*statp == EX_TEMPFAIL) - { - CurEnv->e_status = "4.4.3"; - message("851 %s: Name server timeout", - shortenstring(name, 33)); - } - if (*statp != EX_OK) - return NULL; - if (s->s_namecanon.nc_cname == NULL) - { - syserr("host_map_lookup(%s): bogus NULL cache entry, errno=%d, h_errno=%d", - name, - s->s_namecanon.nc_errno, - s->s_namecanon.nc_herrno); - return NULL; - } - if (bitset(MF_MATCHONLY, map->map_mflags)) - cp = map_rewrite(map, name, strlen(name), NULL); - else - cp = map_rewrite(map, - s->s_namecanon.nc_cname, - strlen(s->s_namecanon.nc_cname), - av); - return cp; - } - - /* - ** If we are running without a regular network connection (usually - ** dial-on-demand) and we are just queueing, we want to avoid DNS - ** lookups because those could try to connect to a server. - */ - - if (CurEnv->e_sendmode == SM_DEFER && - bitset(MF_DEFER, map->map_mflags)) - { - if (tTd(9, 1)) - sm_dprintf("host_map_lookup(%s) => DEFERRED\n", name); - *statp = EX_TEMPFAIL; - return NULL; - } - - /* - ** 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 (tTd(9, 1)) - sm_dprintf("host_map_lookup(%s) => ", name); -#if NAMED_BIND - if (map->map_timeout > 0) - { - retrans = _res.retrans; - _res.retrans = map->map_timeout; - } - if (map->map_retry > 0) - { - retry = _res.retry; - _res.retry = map->map_retry; - } -#endif /* NAMED_BIND */ - - /* set default TTL */ - s->s_namecanon.nc_exp = now + SM_DEFAULT_TTL; - if (*name != '[') - { - int ttl; - - (void) sm_strlcpy(hbuf, name, sizeof(hbuf)); - if (getcanonname(hbuf, sizeof(hbuf) - 1, !HasWildcardMX, &ttl)) - { - ans = hbuf; - if (ttl > 0) - s->s_namecanon.nc_exp = now + SM_MIN(ttl, - SM_DEFAULT_TTL); - } - } - else - { - if ((cp = strchr(name, ']')) == NULL) - { - if (tTd(9, 1)) - sm_dprintf("FAILED\n"); - return NULL; - } - *cp = '\0'; - - hp = NULL; -#if NETINET - if ((in_addr.s_addr = inet_addr(&name[1])) != INADDR_NONE) - hp = sm_gethostbyaddr((char *)&in_addr, - INADDRSZ, AF_INET); -#endif /* NETINET */ -#if NETINET6 - if (hp == NULL && - anynet_pton(AF_INET6, &name[1], &in6_addr) == 1) - hp = sm_gethostbyaddr((char *)&in6_addr, - IN6ADDRSZ, AF_INET6); -#endif /* NETINET6 */ - *cp = ']'; - - if (hp != NULL) - { - /* found a match -- copy out */ - ans = denlstring((char *) hp->h_name, true, true); -#if NETINET6 - if (ans == hp->h_name) - { - static char n[MAXNAME + 1]; - - /* hp->h_name is about to disappear */ - (void) sm_strlcpy(n, ans, sizeof(n)); - ans = n; - } - freehostent(hp); - hp = NULL; -#endif /* NETINET6 */ - } - } -#if NAMED_BIND - if (map->map_timeout > 0) - _res.retrans = retrans; - if (map->map_retry > 0) - _res.retry = retry; -#endif /* NAMED_BIND */ - - s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */ - - /* Found an answer */ - if (ans != NULL) - { - s->s_namecanon.nc_stat = *statp = EX_OK; - if (s->s_namecanon.nc_cname != NULL) - sm_free(s->s_namecanon.nc_cname); - s->s_namecanon.nc_cname = sm_strdup_x(ans); - if (bitset(MF_MATCHONLY, map->map_mflags)) - cp = map_rewrite(map, name, strlen(name), NULL); - else - cp = map_rewrite(map, ans, strlen(ans), av); - if (tTd(9, 1)) - sm_dprintf("FOUND %s\n", ans); - return cp; - } - - - /* No match found */ - s->s_namecanon.nc_errno = errno; -#if NAMED_BIND - s->s_namecanon.nc_herrno = h_errno; - if (tTd(9, 1)) - sm_dprintf("FAIL (%d)\n", h_errno); - switch (h_errno) - { - case TRY_AGAIN: - if (UseNameServer) - { - CurEnv->e_status = "4.4.3"; - message("851 %s: Name server timeout", - shortenstring(name, 33)); - } - *statp = EX_TEMPFAIL; - break; - - case HOST_NOT_FOUND: - case NO_DATA: - *statp = EX_NOHOST; - break; - - case NO_RECOVERY: - *statp = EX_SOFTWARE; - break; - - default: - *statp = EX_UNAVAILABLE; - break; - } -#else /* NAMED_BIND */ - if (tTd(9, 1)) - sm_dprintf("FAIL\n"); - *statp = EX_NOHOST; -#endif /* NAMED_BIND */ - s->s_namecanon.nc_stat = *statp; - return NULL; -} -/* -** HOST_MAP_INIT -- initialize host class structures -** -** Parameters: -** map -- a pointer to this map. -** args -- argument string. -** -** Returns: -** true. -*/ - -bool -host_map_init(map, args) - MAP *map; - char *args; -{ - register char *p = args; - - for (;;) - { - while (isascii(*p) && isspace(*p)) - p++; - if (*p != '-') - break; - switch (*++p) - { - case 'a': - map->map_app = ++p; - break; - - case 'T': - map->map_tapp = ++p; - break; - - case 'm': - map->map_mflags |= MF_MATCHONLY; - break; - - case 't': - map->map_mflags |= MF_NODEFER; - break; - - case 'S': /* only for consistency */ - map->map_spacesub = *++p; - break; - - case 'D': - map->map_mflags |= MF_DEFER; - break; - - case 'd': - { - char *h; - - while (isascii(*++p) && isspace(*p)) - continue; - h = strchr(p, ' '); - if (h != NULL) - *h = '\0'; - map->map_timeout = convtime(p, 's'); - if (h != NULL) - *h = ' '; - } - break; - - case 'r': - while (isascii(*++p) && isspace(*p)) - continue; - map->map_retry = atoi(p); - break; - } - while (*p != '\0' && !(isascii(*p) && isspace(*p))) - p++; - if (*p != '\0') - *p++ = '\0'; - } - if (map->map_app != NULL) - map->map_app = newstr(map->map_app); - if (map->map_tapp != NULL) - map->map_tapp = newstr(map->map_tapp); - return true; -} - -#if NETINET6 -/* -** ANYNET_NTOP -- convert an IPv6 network address to printable form. -** -** Parameters: -** s6a -- a pointer to an in6_addr structure. -** dst -- buffer to store result in -** dst_len -- size of dst buffer -** -** Returns: -** A printable version of that structure. -*/ - -char * -anynet_ntop(s6a, dst, dst_len) - struct in6_addr *s6a; - char *dst; - size_t dst_len; -{ - register char *ap; - - if (IN6_IS_ADDR_V4MAPPED(s6a)) - ap = (char *) inet_ntop(AF_INET, - &s6a->s6_addr[IN6ADDRSZ - INADDRSZ], - dst, dst_len); - else - { - char *d; - size_t sz; - - /* Save pointer to beginning of string */ - d = dst; - - /* Add IPv6: protocol tag */ - sz = sm_strlcpy(dst, "IPv6:", dst_len); - if (sz >= dst_len) - return NULL; - dst += sz; - dst_len -= sz; - ap = (char *) inet_ntop(AF_INET6, s6a, dst, dst_len); - - /* Restore pointer to beginning of string */ - if (ap != NULL) - ap = d; - } - return ap; -} - -/* -** ANYNET_PTON -- convert printed form to network address. -** -** Wrapper for inet_pton() which handles IPv6: labels. -** -** Parameters: -** family -- address family -** src -- string -** dst -- destination address structure -** -** Returns: -** 1 if the address was valid -** 0 if the address wasn't parseable -** -1 if error -*/ - -int -anynet_pton(family, src, dst) - int family; - const char *src; - void *dst; -{ - if (family == AF_INET6 && sm_strncasecmp(src, "IPv6:", 5) == 0) - src += 5; - return inet_pton(family, src, dst); -} -#endif /* NETINET6 */ -/* -** ANYNET_NTOA -- convert a network address to printable form. -** -** Parameters: -** sap -- a pointer to a sockaddr structure. -** -** Returns: -** A printable version of that sockaddr. -*/ - -#ifdef USE_SOCK_STREAM - -# if NETLINK -# include <net/if_dl.h> -# endif /* NETLINK */ - -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) - { -# if NETUNIX - case AF_UNIX: - if (sap->sunix.sun_path[0] != '\0') - (void) sm_snprintf(buf, sizeof(buf), "[UNIX: %.64s]", - sap->sunix.sun_path); - else - (void) sm_strlcpy(buf, "[UNIX: localhost]", sizeof(buf)); - return buf; -# endif /* NETUNIX */ - -# if NETINET - case AF_INET: - return (char *) inet_ntoa(sap->sin.sin_addr); -# endif /* NETINET */ - -# if NETINET6 - case AF_INET6: - ap = anynet_ntop(&sap->sin6.sin6_addr, buf, sizeof(buf)); - if (ap != NULL) - return ap; - break; -# endif /* NETINET6 */ - -# if NETLINK - case AF_LINK: - (void) sm_snprintf(buf, sizeof(buf), "[LINK: %s]", - link_ntoa((struct sockaddr_dl *) &sap->sa)); - return buf; -# endif /* NETLINK */ - default: - /* this case is needed when nothing is #defined */ - /* in order to keep the switch syntactically correct */ - break; - } - - /* unknown family -- just dump bytes */ - (void) sm_snprintf(buf, sizeof(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) sm_snprintf(bp, SPACELEFT(buf, 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; -# if NAMED_BIND - int saveretry; -# endif /* NAMED_BIND */ -# if NETINET6 - struct in6_addr in6_addr; -# endif /* NETINET6 */ - -# if NAMED_BIND - /* shorten name server timeout to avoid higher level timeouts */ - saveretry = _res.retry; - if (_res.retry * _res.retrans > 20) - _res.retry = 20 / _res.retrans; -# endif /* NAMED_BIND */ - - switch (sap->sa.sa_family) - { -# if NETINET - case AF_INET: - hp = sm_gethostbyaddr((char *) &sap->sin.sin_addr, - INADDRSZ, AF_INET); - break; -# endif /* NETINET */ - -# if NETINET6 - case AF_INET6: - hp = sm_gethostbyaddr((char *) &sap->sin6.sin6_addr, - IN6ADDRSZ, AF_INET6); - break; -# endif /* NETINET6 */ - -# if NETISO - case AF_ISO: - hp = sm_gethostbyaddr((char *) &sap->siso.siso_addr, - sizeof(sap->siso.siso_addr), AF_ISO); - break; -# endif /* NETISO */ - -# if NETUNIX - case AF_UNIX: - hp = NULL; - break; -# endif /* NETUNIX */ - - default: - hp = sm_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 NETINET || NETINET6 - if (hp != NULL && hp->h_name[0] != '[' -# if NETINET6 - && inet_pton(AF_INET6, hp->h_name, &in6_addr) != 1 -# endif /* NETINET6 */ -# if NETINET - && inet_addr(hp->h_name) == INADDR_NONE -# endif /* NETINET */ - ) - { - char *name; - - name = denlstring((char *) hp->h_name, true, true); -# if NETINET6 - if (name == hp->h_name) - { - static char n[MAXNAME + 1]; - - /* Copy the string, hp->h_name is about to disappear */ - (void) sm_strlcpy(n, name, sizeof(n)); - name = n; - } - freehostent(hp); -# endif /* NETINET6 */ - return name; - } -# endif /* NETINET || NETINET6 */ - -# if NETINET6 - if (hp != NULL) - { - freehostent(hp); - hp = NULL; - } -# endif /* NETINET6 */ - -# if NETUNIX - if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0') - return "localhost"; -# endif /* NETUNIX */ - { - static char buf[203]; - - (void) sm_snprintf(buf, sizeof(buf), "[%.200s]", - anynet_ntoa(sap)); - return buf; - } -} -#endif /* USE_SOCK_STREAM */ |