daemon.c revision 3ee0e49223f178da635734759b9167f924321ff0
/*
* Copyright (c) 1998-2006 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.
*
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sendmail.h>
#if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__)
# define USE_SOCK_STREAM 1
#endif /* defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) */
#if defined(USE_SOCK_STREAM)
# 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
#endif /* STARTTLS */
#if IP_SRCROUTE && NETINET
# include <netinet/in_systm.h>
# if HAS_IN_H
# ifndef IPOPTION
# endif /* ! IPOPTION */
# else /* HAS_IN_H */
# ifndef IPOPTION
# define IP_LIST ipopt_list
# endif /* ! IPOPTION */
# endif /* HAS_IN_H */
#endif /* IP_SRCROUTE && NETINET */
/* structure to describe a daemon or a client */
struct daemon
{
int d_socket; /* fd for socket */
unsigned short d_port; /* port number */
int d_listenqueue; /* size of listen queue */
int d_tcprcvbufsize; /* size of TCP receive buffer */
int d_tcpsndbufsize; /* size of TCP send buffer */
bool d_firsttime;
int d_socksize;
char *d_mflags; /* flags for use in macro */
char *d_name; /* user-supplied name */
#if MILTER
char *d_inputfilterlist;
#endif /* MILTER */
int d_supersafe;
#endif /* _FFR_SS_PER_DAEMON */
int d_dm; /* DeliveryMode */
#endif /* _FFR_DM_PER_DAEMON */
};
/* see also sendmail.h: SuperSafe values */
static void connecttimeout __P((int));
static int addr_family __P((char *));
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.
*/
getrequests(e)
ENVELOPE *e;
{
int t;
int i, olddaemon = 0;
#if XDEBUG
bool j_has_dot;
#endif /* XDEBUG */
#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();
{
}
/*
** Try to actually open the connection.
*/
{
{
sm_dprintf("getrequests: daemon %s: port %d\n",
}
}
/* get a socket for the SMTP connection */
if (opencontrolsocket() < 0)
"daemon could not open control socket %s: %s",
/* If there are any queue runners released reapchild() co-ord's */
/* write the pid to file, command line args to syslog */
log_sendmail_pid(e);
#if XDEBUG
{
char jbuf[MAXHOSTNAMELEN];
}
#endif /* XDEBUG */
/* Add parent process as first item */
{
sm_dprintf("getrequests: daemon %s: %d\n",
}
for (;;)
{
auto SOCKADDR_LEN_T lotherend;
bool timedout = false;
bool control = false;
int save_errno;
int pipefd[2];
#if STARTTLS
long seed;
#endif /* STARTTLS */
/* see if we are rejecting connections */
(void) sm_blocksignal(SIGALRM);
{
/*
** XXX do this call outside the loop?
** no: refuse_connections may sleep().
*/
continue;
continue;
{
{
/* close socket so peer fails quickly */
}
/* refuse connections for next 15 seconds */
}
{
"accepting connections again for daemon %s",
/* arrange to (re)open the socket if needed */
}
}
/* May have been sleeping above, check again */
#if XDEBUG
/* check for disaster */
{
char jbuf[MAXHOSTNAMELEN];
{
dumpstate("daemon lost $j");
"daemon process doesn't have $j in $=w; see syslog");
abort();
}
{
dumpstate("daemon $j lost dot");
"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)
#endif /* 0 */
(void) sm_releasesignal(SIGALRM);
for (;;)
{
bool setproc = false;
int highest = -1;
{
/* wait for a connection */
{
if (!setproc &&
{
sm_setproctitle(true, e,
"accepting connections");
setproc = true;
}
&readfds);
}
}
#if NETUNIX
if (ControlSocket >= 0)
{
if (ControlSocket > highest)
}
#endif /* NETUNIX */
/* Did someone signal while waiting? */
curdaemon = -1;
if (doqueuerun())
{
(void) runqueue(true, false, false, false);
#endif /* _FFR_QUEUE_RUN_PARANOIA */
}
else if (CheckQueueRunners > 0 && QueueIntvl > 0 &&
{
/*
** 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();
}
#endif /* _FFR_QUEUE_RUN_PARANOIA */
if (t <= 0)
{
timedout = true;
break;
}
control = false;
errno = 0;
/* look "round-robin" for an active socket */
idx = 0;
for (i = 0; i < NDaemons; i++)
{
&readfds))
{
sizeof RealHostAddr);
(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
# endif /* BSD4_4_SOCKADDR */
{
(void) close(t);
t = -1;
}
break;
}
idx = 0;
}
#if NETUNIX
{
struct sockaddr_un sa_un;
t = accept(ControlSocket,
&lotherend);
/*
** If remote side closes before
** accept() finishes, sockaddr
** might not be fully filled in.
*/
if (t >= 0 &&
(lotherend == 0 ||
# ifdef BSD4_4_SOCKADDR
# endif /* BSD4_4_SOCKADDR */
{
(void) close(t);
t = -1;
}
if (t >= 0)
control = true;
}
#else /* NETUNIX */
if (curdaemon == -1)
{
/* No daemon to service */
continue;
}
#endif /* NETUNIX */
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 */
/*
** Give time for bound socket to be released.
** This creates a denial-of-service if you can
** force accept() to fail on affected systems.
*/
curtime() + 15;
#endif /* SO_REUSEADDR_IS_BROKEN */
}
continue;
}
if (!control)
{
/* set some daemon related macros */
{
case AF_UNSPEC:
break;
# if NETUNIX
case AF_UNIX:
break;
# endif /* NETUNIX */
#endif /* _FFR_DAEMON_NETUNIX */
#if NETINET
case AF_INET:
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
break;
#endif /* NETINET6 */
#if NETISO
case AF_ISO:
break;
#endif /* NETISO */
#if NETNS
case AF_NS:
break;
#endif /* NETNS */
#if NETX25
case AF_CCITT:
break;
#endif /* NETX25 */
}
macid("{daemon_name}"),
macid("{daemon_flags}"),
else
}
/*
** 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.
*/
/*
** Create a subprocess to process the mail.
*/
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);
#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 */
{
/* don't fork, handle connection in this process */
pid = 0;
}
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.
*/
(void) sm_blocksignal(SIGCHLD);
if (pid < 0)
{
syserr("daemon: cannot fork");
if (pipefd[0] != -1)
{
}
(void) sm_releasesignal(SIGCHLD);
(void) sleep(10);
(void) close(t);
continue;
}
}
if (pid == 0)
{
char *p;
/*
** CHILD -- return to caller.
** Collect verified idea of sending host.
** Verify calling user id if possible here.
*/
/* Reset global flags */
RestartWorkGroup = false;
PendingSignal = 0;
CurrentPid = getpid();
(void) sm_releasesignal(SIGALRM);
(void) sm_releasesignal(SIGCHLD);
/* turn on profiling */
/* SM_PROF(0); */
/*
** Initialize exception stack and default exception
** handler for child process.
*/
if (!control)
{
macid("{daemon_addr}"),
}
{
}
clrcontrol();
/* Avoid SMTP daemon actions if control command */
if (control)
{
/* Add control socket process */
"console socket child",
}
else
{
/* clean up background delivery children */
/* Add parent process as first child item */
/* don't schedule queue runs if ETRN */
QueueIntvl = 0;
#endif /* _FFR_SS_PER_DAEMON */
#endif /* _FFR_DM_PER_DAEMON */
sm_setproctitle(true, e, "startup with %s",
}
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 */
/* we shouldn't be interrupted, but ... */
continue;
}
/* control socket processing */
if (control)
{
control_command(t, e);
/* NOTREACHED */
}
/* determine host name */
p = hostnamebyanyaddr(&RealHostAddr);
p[MAXNAME] = '\0';
RealHostName = newstr(p);
if (RealHostName[0] == '[')
{
macid("{client_resolve}"),
}
else
{
}
sm_setproctitle(true, e, "startup with %s", p);
(void *) &t,
(t = dup(t)) < 0 ||
(void *) &t,
{
syserr("cannot open SMTP server channel, fd=%d",
t);
}
DisConnected = false;
#if XLA
if (!xla_host_ok(RealHostName))
{
message("421 4.4.5 Too many SMTP sessions for this host");
}
#endif /* XLA */
/* find out name for interface of connection */
{
p = hostnamebyanyaddr(&sa);
sm_dprintf("getreq: got name %s\n", p);
macid("{if_name}"), p);
/*
** Do this only if it is not the loopback
** interface.
*/
if (!isloopback(sa))
{
char *addr;
char family[5];
(void) sm_snprintf(family,
sizeof(family),
sm_dprintf("getreq: got addr %s and family %s\n",
}
else
{
}
}
else
{
sm_dprintf("getreq: getsockname failed\n");
}
break;
}
/* parent -- keep track of children */
if (control)
{
"control socket server child");
}
else
{
"SMTP server child for %s",
&RealHostAddr);
}
(void) sm_releasesignal(SIGCHLD);
/* close the read end of the synchronization pipe */
if (pipefd[0] != -1)
{
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 */
{
}
}
sm_dprintf("getreq: returning\n");
#if MILTER
/* set the filters for this daemon */
{
for (i = 0;
(i < MAXFILTERS &&
i++)
{
}
if (i < MAXFILTERS)
InputFilters[i] = NULL;
}
#endif /* MILTER */
}
/*
** GETREQUESTS_CHECKDISKSPACE -- check available diskspace.
**
** Parameters:
** e -- envelope.
**
** Returns:
** none.
**
** Side Effects:
** Modifies Daemon flags (D_ETRNONLY) if not enough disk space.
*/
static void
ENVELOPE *e;
{
bool logged = false;
int idx;
if (now < NextDiskSpaceCheck)
return;
/* Check if there is available disk space in all queue groups. */
if (!enoughdiskspace(0, NULL))
{
{
continue;
/* log only if not logged before */
if (!logged)
{
if (LogLevel > 8)
"rejecting new messages: min free: %ld",
sm_setproctitle(true, e,
"rejecting new messages: min free: %ld",
logged = true;
}
}
}
else
{
{
continue;
/* log only if not logged before */
if (!logged)
{
if (LogLevel > 8)
"accepting new messages (again)");
logged = true;
}
/* title will be set later */
}
}
/* only check disk space once a minute */
}
/*
** 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.
*/
static int
DAEMON_T *d;
bool firsttime;
{
int on = 1;
int fdflags;
SOCKADDR_LEN_T socksize = 0;
int ntries = 0;
int save_errno;
do
{
if (ntries > 0)
(void) sleep(5);
{
# if NETUNIX
{
int rval;
/* if not safe, don't use it */
if (rval != 0)
{
save_errno = errno;
syserr("opendaemonsocket: daemon %s: unsafe domain socket %s",
d->d_name,
goto fail;
}
/* Don't try to overtake an existing socket */
}
# endif /* NETUNIX */
#endif /* _FFR_DOMAIN_NETUNIX */
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:
(!transienterror(save_errno) ||
{
syserr("opendaemonsocket: daemon %s: optional socket disabled",
d->d_name);
d->d_socket = -1;
return -1;
}
if (LogLevel > 0)
"daemon %s: problem creating SMTP socket",
d->d_name);
d->d_socket = -1;
continue;
}
{
save_errno = EINVAL;
syserr("opendaemonsocket: daemon %s: server SMTP socket (%d) too large",
goto fail;
}
/* turn on network debugging? */
sizeof on);
#ifdef SO_RCVBUF
if (d->d_tcprcvbufsize > 0)
{
(char *) &d->d_tcprcvbufsize,
sizeof(d->d_tcprcvbufsize)) < 0)
}
#endif /* SO_RCVBUF */
#ifdef SO_SNDBUF
if (d->d_tcpsndbufsize > 0)
{
(char *) &d->d_tcpsndbufsize,
sizeof(d->d_tcpsndbufsize)) < 0)
}
#endif /* SO_SNDBUF */
{
save_errno = errno;
syserr("opendaemonsocket: daemon %s: failed to %s close-on-exec flag: %s",
d->d_name,
goto severe;
}
{
# ifdef NETUNIX
case AF_UNIX:
break;
# endif /* NETUNIX */
#endif /* _FFR_DAEMON_NETUNIX */
#if NETINET
case AF_INET:
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
break;
#endif /* NETINET6 */
#if NETISO
case AF_ISO:
break;
#endif /* NETISO */
default:
break;
}
{
/* probably another daemon already */
save_errno = errno;
syserr("opendaemonsocket: daemon %s: cannot bind",
d->d_name);
goto fail;
}
}
if (!firsttime &&
{
save_errno = errno;
syserr("opendaemonsocket: daemon %s: cannot listen",
d->d_name);
goto severe;
}
return socksize;
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
{
unsigned short port;
/*
** Set up the address for the mailer.
*/
{
#if NETINET
#endif /* NETINET */
}
{
#if NETINET
case AF_INET:
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
break;
#endif /* NETINET6 */
default:
/* unknown protocol */
port = 0;
break;
}
if (port == 0)
{
#ifdef NO_GETSERVBYNAME
#else /* NO_GETSERVBYNAME */
{
{
syserr("554 5.3.5 service \"smtp\" unknown");
}
else
}
#endif /* NO_GETSERVBYNAME */
}
{
#if NETINET
case AF_INET:
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
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
{
int i;
for (i = 0; i < NDaemons; i++)
{
}
}
/*
** 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 *
char *v;
{
int l;
char *h, *f, *flags;
/* maximum length of flags: upper case Option -> "OO " */
/* is someone joking? */
if (l < 0 || l > 256)
{
if (LogLevel > 2)
"getmodifiers too long, ignored");
return NULL;
}
f = flags;
for (h = v; *h != '\0'; h++)
{
{
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
int flag;
{
int i;
for (i = 0; i < NDaemons; i++)
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 */
#if NETINET
#endif /* NETINET */
d->d_supersafe = SAFE_NOTSET;
#endif /* _FFR_SS_PER_DAEMON */
#endif /* _FFR_DM_PER_DAEMON */
while (p != NULL)
{
register char *f;
register char *v;
p++;
if (*p == '\0')
break;
f = p;
p = strchr(p, ',');
if (p != NULL)
*p++ = '\0';
v = strchr(f, '=');
if (v == NULL)
continue;
continue;
*f = toupper(*f);
switch (*f)
{
case 'A': /* address */
addr = 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;
#endif /* _FFR_DM_PER_DAEMON */
case 'F': /* address family */
# ifdef NETUNIX
else if (sm_strcasecmp(v, "unix") == 0 ||
sm_strcasecmp(v, "local") == 0)
# endif /* NETUNIX */
#endif /* _FFR_DAEMON_NETUNIX */
#if NETINET
else if (sm_strcasecmp(v, "inet") == 0)
#endif /* NETINET */
#if NETINET6
else if (sm_strcasecmp(v, "inet6") == 0)
#endif /* NETINET6 */
#if NETISO
else if (sm_strcasecmp(v, "iso") == 0)
#endif /* NETISO */
#if NETNS
else if (sm_strcasecmp(v, "ns") == 0)
#endif /* NETNS */
#if NETX25
else if (sm_strcasecmp(v, "x.25") == 0)
#endif /* NETX25 */
else
syserr("554 5.3.5 Unknown address family %s in Family=option",
v);
break;
#if MILTER
case 'I':
d->d_inputfilterlist = v;
break;
#endif /* MILTER */
case 'L': /* listen queue size */
d->d_listenqueue = atoi(v);
break;
case 'M': /* modifiers (flags) */
break;
case 'N': /* name */
d->d_name = v;
break;
case 'P': /* port */
port = v;
break;
case 'R': /* receive buffer size */
d->d_tcprcvbufsize = atoi(v);
break;
case 'S': /* send buffer size */
d->d_tcpsndbufsize = atoi(v);
break;
case 'T': /* SuperSafe */
if (tolower(*v) == 'i')
d->d_supersafe = SAFE_INTERACTIVE;
else if (tolower(*v) == 'p')
# if MILTER
# else /* MILTER */
"Warning: SuperSafe=PostMilter requires Milter support (-DMILTER)\n");
# endif /* MILTER */
else
: 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 NETUNIX
case AF_UNIX:
{
syserr("setsockaddroptions: domain socket name too long: %s > %d",
break;
}
/* file safety check done in opendaemonsocket() */
addr,
break;
# endif /* NETUNIX */
#endif /* _FFR_DAEMON_NETUNIX */
#if NETINET
case AF_INET:
== INADDR_NONE))
{
syserr("554 5.3.0 host \"%s\" unknown",
addr);
else
{
hp->h_addr_list++;
syserr("554 5.3.0 host \"%s\" unknown",
addr);
else
*(hp->h_addr_list),
INADDRSZ);
# if NETINET6
# endif /* NETINET6 */
}
}
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
{
syserr("554 5.3.0 host \"%s\" unknown",
addr);
else
{
hp->h_addr_list++;
syserr("554 5.3.0 host \"%s\" unknown",
addr);
else
*(hp->h_addr_list),
}
}
break;
#endif /* NETINET6 */
default:
syserr("554 5.3.5 address= option unsupported for family %d",
break;
}
}
{
{
#if NETINET
case AF_INET:
else
{
# ifdef NO_GETSERVBYNAME
syserr("554 5.3.5 invalid port number: %s",
port);
# else /* NO_GETSERVBYNAME */
syserr("554 5.3.5 service \"%s\" unknown",
port);
else
# endif /* NO_GETSERVBYNAME */
}
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
else
{
# ifdef NO_GETSERVBYNAME
syserr("554 5.3.5 invalid port number: %s",
port);
# else /* NO_GETSERVBYNAME */
syserr("554 5.3.5 service \"%s\" unknown",
port);
else
# endif /* NO_GETSERVBYNAME */
}
break;
#endif /* NETINET6 */
#if NETISO
case AF_ISO:
/* assume two byte transport selector */
else
{
# ifdef NO_GETSERVBYNAME
syserr("554 5.3.5 invalid port number: %s",
port);
# else /* NO_GETSERVBYNAME */
syserr("554 5.3.5 service \"%s\" unknown",
port);
else
# endif /* NO_GETSERVBYNAME */
}
(char *) &portno, 2);
break;
#endif /* NETISO */
default:
syserr("554 5.3.5 Port= option unsupported for family %d",
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
DAEMON_T *d;
{
bool first = true;
{
continue;
if (first)
else
first = false;
}
if (!first)
sm_dprintf(">");
}
bool
register char *p;
{
if (NDaemons >= MAXDAEMONS)
return false;
#if MILTER
#endif /* MILTER */
else
{
char num[30];
}
{
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
{
if (NDaemons == 0)
{
NDaemons = 1;
}
}
/*
** SETCLIENTOPTIONS -- set options for running the client
**
** Parameters:
** p -- the options line.
**
** Returns:
** none.
*/
void
register char *p;
{
int family;
DAEMON_T d;
memset(&d, '\0', sizeof d);
setsockaddroptions(p, &d);
/* grab what we need */
else
{
char num[30];
}
}
/*
** 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
char *addr;
{
#if NETINET6
#endif /* NETINET6 */
#if NETINET
{
return AF_INET;
}
#endif /* NETINET */
#if NETINET6
{
return AF_INET6;
}
#endif /* NETINET6 */
# if NETUNIX
if (*addr == '/')
{
return AF_UNIX;
}
# endif /* NETUNIX */
#endif /* _FFR_DAEMON_NETUNIX */
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
int flag;
{
int i;
bool flagisset;
flagisset = false;
for (i = 0; i < AF_MAX; i++)
{
{
return false;
flagisset = true;
}
}
return flagisset;
}
#if MILTER
/*
** SETUP_DAEMON_FILTERS -- Parse per-socket filters
**
** Parameters:
** none
**
** Returns:
** none
*/
void
{
int idx;
{
/* no need to configure the daemons */
return;
}
{
{
}
}
}
#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;
int
char *host;
volatile unsigned int port;
ENVELOPE *e;
{
register volatile int addrno = 0;
volatile int s;
int save_errno = 0;
volatile SOCKADDR_LEN_T addrlen;
volatile bool firstconnect = true;
#if NETINET6
volatile bool v6found = false;
#endif /* NETINET6 */
volatile SOCKADDR_LEN_T socksize = 0;
volatile bool clt_bind;
char *p;
extern ENVELOPE BlankEnvelope;
/* retranslate {daemon_flags} into bitmap */
{
for (; *p != '\0'; p++)
{
}
}
#if NETINET6
#endif /* NETINET6 */
clt_bind = false;
/* Set up the address for outgoing connection. */
*p != '\0')
{
#if NETINET6
char p6[INET6_ADDRSTRLEN];
#endif /* NETINET6 */
/* infer the address family from the address itself */
{
#if NETINET
case AF_INET:
{
clt_bind = true;
socksize = sizeof (struct sockaddr_in);
}
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
if (inet_addr(p) != INADDR_NONE)
"IPv6:::ffff:%s", p);
else
{
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",
break;
#endif /* 0 */
}
if (clt_bind)
}
/* D_BINDIF not set or not available, fallback to ClientPortOptions */
if (!clt_bind)
{
{
#if NETINET
case AF_INET:
else
clt_bind = true;
clt_bind = true;
socksize = sizeof (struct sockaddr_in);
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
else
clt_bind = true;
socksize = sizeof (struct sockaddr_in6);
clt_bind = true;
break;
#endif /* NETINET6 */
#if NETISO
case AF_ISO:
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;
CurHostName = host;
if (host[0] == '[')
{
if (p != NULL)
{
#if NETINET
unsigned long hid = INADDR_NONE;
#endif /* NETINET */
#if NETINET6
struct sockaddr_in6 hid6;
#endif /* NETINET6 */
*p = '\0';
#if NETINET6
#endif /* NETINET6 */
#if NETINET
{
}
else
#endif /* NETINET */
#if NETINET6
{
}
else
#endif /* NETINET6 */
{
/* try it as a host name (avoid MX lookup) */
{
#if NAMED_BIND
#endif /* NAMED_BIND */
p[-1] = '\0';
family);
p[-1] = '.';
#if NAMED_BIND
#endif /* NAMED_BIND */
}
*p = ']';
goto gothostent;
}
*p = ']';
}
if (p == NULL)
{
extern char MsgBuf[];
usrerrenh("5.1.2",
"553 Invalid numeric domain spec \"%s\"",
host);
return EX_NOHOST;
}
}
else
{
/* contortion to get around SGI cc complaints */
{
{
#if NAMED_BIND
#endif /* NAMED_BIND */
*p = '\0';
*p = '.';
#if NAMED_BIND
#endif /* NAMED_BIND */
}
}
{
#if NAMED_BIND
/* check for name server timeouts */
# if NETINET6
{
/*
** An attempt with family AF_INET may
** succeed By skipping the next section
** of code, we will try AF_INET before
** failing.
*/
sm_dprintf("makeconnection: WorkAroundBrokenAAAA: Trying AF_INET lookup (AF_INET6 failed)\n");
}
else
# endif /* NETINET6 */
{
{
save_errno = errno;
"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.
*/
{
goto v4retry;
}
if (v6found)
goto v6tempfail;
#endif /* NETINET6 */
save_errno = errno;
errno = save_errno;
return EX_NOHOST;
}
switch (hp->h_addrtype)
{
#if NETINET
case AF_INET:
INADDRSZ);
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
break;
#endif /* NETINET6 */
default:
{
syserr("makeconnection: long sa_data: family %d len %d",
return EX_NOHOST;
}
break;
}
addrno = 1;
}
/*
** Determine the port number.
*/
if (port == 0)
{
#ifdef NO_GETSERVBYNAME
#else /* NO_GETSERVBYNAME */
{
if (LogLevel > 2)
"makeconnection: service \"smtp\" unknown");
}
else
#endif /* NO_GETSERVBYNAME */
}
#if NETINET6
{
/*
** Ignore mapped IPv4 address since
** there is a ClientPortOptions setting
** for IPv4.
*/
goto nextaddr;
}
#endif /* NETINET6 */
{
#if NETINET
case AF_INET:
addrlen = sizeof (struct sockaddr_in);
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
addrlen = sizeof (struct sockaddr_in6);
break;
#endif /* NETINET6 */
#if NETISO
case AF_ISO:
/* assume two byte transport selector */
addrlen = sizeof (struct sockaddr_iso);
break;
#endif /* NETISO */
default:
#if NETINET6
#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
# endif /* NETINET6 */
return EX_TEMPFAIL;
}
#endif /* XLA */
for (;;)
{
sm_dprintf("makeconnection (%s [%s].%d (%d))\n",
/* save for logging */
CurHostAddr = addr;
#if HASRRESVPORT
{
}
else
#endif /* HASRRESVPORT */
{
}
if (s < 0)
{
save_errno = errno;
syserr("makeconnection: cannot create socket");
#if XLA
#endif /* XLA */
#if NETINET6
#endif /* NETINET6 */
errno = save_errno;
return EX_TEMPFAIL;
}
#ifdef SO_SNDBUF
{
syserr("makeconnection: setsockopt(SO_SNDBUF)");
}
#endif /* SO_SNDBUF */
#ifdef SO_RCVBUF
{
syserr("makeconnection: setsockopt(SO_RCVBUF)");
}
#endif /* SO_RCVBUF */
sm_dprintf("makeconnection: fd=%d\n", s);
/* turn on network debugging? */
{
int on = 1;
}
errno = 0; /* for debugging */
if (clt_bind)
{
int on = 1;
{
#if NETINET
case AF_INET:
(void) setsockopt(s, SOL_SOCKET,
(char *) &on,
sizeof on);
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
(void) setsockopt(s, SOL_SOCKET,
(char *) &on,
sizeof on);
break;
#endif /* NETINET6 */
}
{
save_errno = errno;
(void) close(s);
errno = save_errno;
syserr("makeconnection: cannot bind socket [%s]",
anynet_ntoa(&clt_addr));
#if NETINET6
#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;
connecttimeout, 0);
else if (TimeOuts.to_connect != 0)
connecttimeout, 0);
else
{
#if NETINET
case AF_INET:
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
break;
#endif /* NETINET6 */
}
save_errno = errno;
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 &&
{
sm_dprintf("Connect failed (%s); trying again...\n",
firstconnect = false;
continue;
}
if (LogLevel > 13)
"makeconnection (%s [%s]) failed: %s",
#if NETINET6
#endif /* NETINET6 */
{
sm_dprintf("Connect failed (%s); trying new address....\n",
{
#if NETINET
case AF_INET:
INADDRSZ);
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
break;
#endif /* NETINET6 */
default:
break;
}
continue;
}
errno = save_errno;
#if NETINET6
{
sm_dprintf("Connect failed (%s); retrying with AF_INET....\n",
v6found = true;
{
}
goto v4retry;
}
#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;
sm_dprintf("Connect failed (%s)\n",
#if XLA
#endif /* XLA */
#if NETINET6
#endif /* NETINET6 */
errno = save_errno;
return EX_TEMPFAIL;
}
#if NETINET6
{
}
#endif /* NETINET6 */
/* connection ok, put it into canonical form */
(void *) &s,
(s = dup(s)) < 0 ||
(void *) &s,
{
save_errno = errno;
syserr("cannot open SMTP client channel, fd=%d", s);
(void) close(s);
errno = save_errno;
return EX_TEMPFAIL;
}
/* set {client_flags} */
{
macid("{client_flags}"),
}
else
/* "add" {client_flags} to bitmap */
{
/* look for just this one flag */
}
/* find out name for Interface through which we connect */
{
char *name;
char family[5];
if (LogLevel > 11)
{
/* log connection information */
"SMTP outgoing connect on %.40s", name);
}
{
}
}
else
{
}
#if _FFR_HELONAME
/* Use the configured HeloName as appropriate */
#endif /* _FFR_HELONAME */
return EX_OK;
}
static void
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.
*/
}
/*
** 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
char *mux_path;
{
int sock;
int rval, save_errno;
struct sockaddr_un unix_addr;
/* if not safe, don't connect */
if (rval != 0)
{
syserr("makeconnection_ds: unsafe domain socket %s",
mux_path);
return EX_TEMPFAIL;
}
/* prepare address structure */
{
syserr("makeconnection_ds: domain socket name %s too long",
mux_path);
/* XXX why TEMPFAIL but 5.x.y ? */
return EX_UNAVAILABLE;
}
/* initialize domain socket */
if (sock == -1)
{
save_errno = errno;
syserr("makeconnection_ds: could not create domain socket %s",
mux_path);
errno = save_errno;
return EX_TEMPFAIL;
}
/* connect to server */
sizeof(unix_addr)) == -1)
{
save_errno = errno;
errno = save_errno;
return EX_TEMPFAIL;
}
/* connection ok, put it into canonical form */
== NULL
== NULL)
{
save_errno = errno;
errno = save_errno;
return EX_TEMPFAIL;
}
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
{
int i;
char *reason;
sm_allsignals(true);
PendingSignal = 0;
if (LogLevel > 9)
closecontrolsocket(true);
#if XLA
xla_all_end();
#endif /* XLA */
for (i = 0; i < NDaemons; i++)
{
{
# if NETUNIX
/* Remove named sockets */
{
int rval;
/* if not safe, don't use it */
if (rval == 0 &&
{
"Could not remove daemon %s socket: %s: %s",
}
}
# endif /* NETUNIX */
#endif /* _FFR_DAEMON_NETUNIX */
}
}
}
/*
** RESTART_DAEMON -- Performs a clean restart of the daemon
**
** Parameters:
** none.
**
** Returns:
** none.
**
** Side Effects:
** restarts the daemon or exits if restart fails.
*/
do \
{ \
} while (0)
void
{
bool drop;
int save_errno;
char *reason;
extern int DtableSize;
/* clear the events to turn off SIGALRMs */
sm_allsignals(true);
PendingSignal = 0;
if (SaveArgv[0][0] != '/')
{
if (LogLevel > 3)
"could not restart: need full path");
/* NOTREACHED */
}
if (LogLevel > 3)
SaveArgv[0],
closecontrolsocket(true);
#if SM_CONF_SHM
#endif /* SM_CONF_SHM */
/* close locked pid file */
/*
** 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.
*/
{
if (LogLevel > 0)
"could not drop privileges: %s",
/* NOTREACHED */
}
/*
** 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.
*/
#ifdef SIGUSR1
#endif /* SIGUSR1 */
/* Turn back on signals */
sm_allsignals(false);
save_errno = errno;
/* block signals again and restore needed signals */
sm_allsignals(true);
/* For finis() events */
#ifdef SIGUSR1
/* For debugging finis() */
#endif /* SIGUSR1 */
errno = save_errno;
if (LogLevel > 0)
/* 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 *
char hostbuf[];
int size;
{
{
/*
** 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.
*/
}
#endif /* NETINET && NETINET6 */
return NULL;
#if NETINFO
{
char *domainname;
"domain", '\0');
if (domainname != NULL &&
}
#endif /* NETINFO */
/*
** If there is still no dot in the name, try looking for a
** dotted alias.
*/
{
char **ha;
{
{
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.
*/
{
"My unqualified host name (%s) unknown; sleeping for retry",
hostbuf);
message("My unqualified host name (%s) unknown; sleeping for retry",
hostbuf);
(void) sleep(60);
{
"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
char *ha;
{
#if NETINET6
unsigned char *a;
#endif /* NETINET6 */
{
#if NETINET
case AF_INET:
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
/* Straight binary comparison */
/* If IPv4-mapped IPv6 address, compare the IPv4 section */
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
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.
*/
}
char *
int fd;
bool *may_be_forged;
{
unsigned short SM_NONVOLATILE port = 0;
register char *volatile p = NULL;
#ifndef NO_GETSERVBYNAME
# 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;
int nleft;
char **ha;
*may_be_forged = false;
falen = sizeof RealHostAddr;
{
if (i < 0)
{
/*
** ENOTSOCK is OK: bail on anything else, but reset
** errno in this case, so a mis-report doesn't
** happen later.
*/
return NULL;
errno = 0;
}
"@localhost");
return hbuf;
}
if (RealHostName == NULL)
{
/* translate that to a host name */
}
/* cross check RealHostName with forward DNS lookup */
RealHostName[0] != '[')
{
int 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().
*/
#endif /* NETINET6 && NEEDSGETIPNODE */
/* try to match the reverse against the forward lookup */
{
/* XXX: Could be a temporary error on forward lookup */
*may_be_forged = true;
}
else
{
{
break;
}
#if NETINET6
#endif /* NETINET6 */
}
}
goto noident;
{
#if NETINET
case AF_INET:
lalen <= 0 ||
{
/* no ident info */
goto noident;
}
/* create ident query */
/* create local address */
/* create foreign address */
# ifdef NO_GETSERVBYNAME
# 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)
{
else
}
break;
# endif /* NO_GETSERVBYNAME */
#endif /* NETINET */
#if NETINET6
case AF_INET6:
lalen <= 0 ||
{
/* no ident info */
goto noident;
}
/* create ident query */
/* create local address */
/* create foreign address */
# ifdef NO_GETSERVBYNAME
# else /* NO_GETSERVBYNAME */
if (port6 == 0)
{
else
}
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 */
/* connect to foreign IDENT server using same address as SMTP socket */
if (s < 0)
{
goto noident;
}
goto closeident;
/* send query */
goto closeident;
/* get result */
p = &ibuf[0];
{
char *s;
p += i;
nleft -= i;
*p = '\0';
{
if (p > s + 1)
{
p = s + 1;
*p = '\0';
}
break;
}
if (nleft <= 0)
break;
}
(void) close(s);
if (i < 0 || p == &ibuf[0])
goto noident;
p--;
*++p = '\0';
/* parse result */
if (p == NULL)
{
/* malformed response */
goto noident;
}
continue;
{
/* presumably an error string */
goto noident;
}
p += 6;
p++;
if (*p++ != ':')
{
/* either useridxx or malformed response */
goto noident;
}
/* p now points to the OSTYPE field */
p++;
ostype = p;
p = strchr(p, ':');
if (p == NULL)
{
/* malformed response */
goto noident;
}
else
{
char *charset;
*p = '\0';
*charset = '\0';
}
/* 1413 says don't do this -- but it's broken otherwise */
continue;
/* p now points to the authenticated name -- copy carefully */
{
}
else
goto postident;
(void) close(s);
/* put back the original incoming port */
{
#if NETINET
case AF_INET:
if (port > 0)
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
if (port > 0)
break;
#endif /* NETINET6 */
}
if (RealHostName == NULL)
{
sm_dprintf("getauthinfo: NULL\n");
return NULL;
}
#if IP_SRCROUTE
# ifndef GET_IPOPT_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.
** p - pointer to hbuf
*/
{
int j;
unsigned char *q;
unsigned char *o;
int l;
goto noipsr;
if (ipoptlen == 0)
goto noipsr;
{
switch (*o)
{
case IPOPT_EOL:
o = NULL;
break;
case IPOPT_NOP:
o++;
break;
case IPOPT_SSRR:
case IPOPT_LSRR:
/*
** Source routing.
** 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.
*/
" [%s@%.*s",
l > 240 ? 120 : l / 2,
i = strlen(p);
p += i;
l -= strlen(p);
/* q skips length and router pointer to data */
q = &o[3];
for ( ; j >= 0; j--)
{
(void) sm_snprintf(p,
"%c%.*s",
j != 0 ? '@' : ':',
l > 240 ? 120 :
j == 0 ? l : l / 2,
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;
}
}
goto postipsr;
}
#endif /* IP_SRCROUTE */
{
}
if (*may_be_forged)
{
}
#if IP_SRCROUTE
#endif /* IP_SRCROUTE */
/* put back the original incoming port */
{
#if NETINET
case AF_INET:
if (port > 0)
break;
#endif /* NETINET */
#if NETINET6
case AF_INET6:
if (port > 0)
break;
#endif /* NETINET6 */
}
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 *
char *name;
char **av;
int *statp;
{
#if NETINET
#endif /* NETINET */
#if NETINET6
#endif /* NETINET6 */
register STAB *s;
#if NAMED_BIND
int SM_NONVOLATILE retry = 0;
#endif /* NAMED_BIND */
/*
** See if we have already looked up this name. If so, just
** return it (unless expired).
*/
{
sm_dprintf("host_map_lookup(%s) => CACHE %s\n",
name,
? "NULL"
: s->s_namecanon.nc_cname);
if (*statp == EX_TEMPFAIL)
{
message("851 %s: Name server timeout",
}
return 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;
}
else
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.
*/
{
*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 NAMED_BIND
if (map->map_timeout > 0)
{
}
{
}
#endif /* NAMED_BIND */
/* set default TTL */
if (*name != '[')
{
int ttl;
{
if (ttl > 0)
}
}
else
{
{
sm_dprintf("FAILED\n");
return NULL;
}
*cp = '\0';
#if NETINET
#endif /* NETINET */
#if NETINET6
#endif /* NETINET6 */
*cp = ']';
{
/* found a match -- copy out */
#if NETINET6
{
static char n[MAXNAME + 1];
/* hp->h_name is about to disappear */
(void) sm_strlcpy(n, ans, sizeof n);
ans = n;
}
#endif /* NETINET6 */
}
}
#if NAMED_BIND
if (map->map_timeout > 0)
#endif /* NAMED_BIND */
/* Found an answer */
{
else
return cp;
}
/* No match found */
#if NAMED_BIND
switch (h_errno)
{
case TRY_AGAIN:
if (UseNameServer)
{
message("851 %s: Name server timeout",
}
*statp = EX_TEMPFAIL;
break;
case HOST_NOT_FOUND:
case NO_DATA:
break;
case NO_RECOVERY:
*statp = EX_SOFTWARE;
break;
default:
*statp = EX_UNAVAILABLE;
break;
}
#else /* NAMED_BIND */
sm_dprintf("FAIL\n");
#endif /* NAMED_BIND */
return NULL;
}
/*
** HOST_MAP_INIT -- initialize host class structures
**
** Parameters:
** map -- a pointer to this map.
** args -- argument string.
**
** Returns:
** true.
*/
bool
char *args;
{
register char *p = args;
for (;;)
{
p++;
if (*p != '-')
break;
switch (*++p)
{
case 'a':
break;
case 'T':
break;
case 'm':
break;
case 't':
break;
case 'S': /* only for consistency */
map->map_spacesub = *++p;
break;
case 'D':
break;
case 'd':
{
char *h;
continue;
h = strchr(p, ' ');
if (h != NULL)
*h = '\0';
if (h != NULL)
*h = ' ';
}
break;
case 'r':
continue;
break;
}
p++;
if (*p != '\0')
*p++ = '\0';
}
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 *
char *dst;
{
register char *ap;
if (IN6_IS_ADDR_V4MAPPED(s6a))
else
{
char *d;
/* Save pointer to beginning of string */
d = dst;
/* Add IPv6: protocol tag */
return NULL;
/* Restore pointer to beginning of string */
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
int family;
const char *src;
void *dst;
{
src += 5;
}
#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
# endif /* NETLINK */
char *
{
register char *bp;
register char *ap;
int l;
static char buf[100];
return "NULLADDR";
return "0";
{
# if NETUNIX
case AF_UNIX:
else
return buf;
# endif /* NETUNIX */
# if NETINET
case AF_INET:
# endif /* NETINET */
# if NETINET6
case AF_INET6:
return ap;
break;
# endif /* NETINET6 */
# if NETLINK
case AF_LINK:
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 */
{
*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 *
{
# if NAMED_BIND
int saveretry;
# endif /* NAMED_BIND */
# if NETINET6
# endif /* NETINET6 */
# if NAMED_BIND
/* shorten name server timeout to avoid higher level timeouts */
# endif /* NAMED_BIND */
{
# if NETINET
case AF_INET:
break;
# endif /* NETINET */
# if NETINET6
case AF_INET6:
break;
# endif /* NETINET6 */
# if NETISO
case AF_ISO:
break;
# endif /* NETISO */
# if NETUNIX
case AF_UNIX:
break;
# endif /* NETUNIX */
default:
break;
}
# if NAMED_BIND
# endif /* NAMED_BIND */
# if NETINET6
# endif /* NETINET6 */
# if NETINET
# endif /* NETINET */
)
{
char *name;
# if NETINET6
{
static char n[MAXNAME + 1];
/* Copy the string, hp->h_name is about to disappear */
(void) sm_strlcpy(n, name, sizeof n);
name = n;
}
# endif /* NETINET6 */
return name;
}
# endif /* NETINET || NETINET6 */
# if NETINET6
{
}
# endif /* NETINET6 */
# if NETUNIX
return "localhost";
# endif /* NETUNIX */
{
static char buf[203];
anynet_ntoa(sap));
return buf;
}
}
#endif /* USE_SOCK_STREAM */