You are on page 1of 59

/*

*
*
*/
/*
*
*
*/

Copyright 1988 by Rayan S. Zachariassen, all rights reserved.


This will be free software, but only when it is finished.
Several extensive changes by Matti Aarnio <mea@nic.funet.fi>
Copyright 1991-1996.

/*
* ZMailer SMTP server.
*/
char *VerbID = "ZMailer SMTP server %s";
char *Copyright = "Copyright 1990 Rayan S. Zachariassen";
char *Copyright2= "Copyright 1991-1996 Matti Aarnio";
/* Timing parameters -- when expired, session is killed ... */
#define SMTP_COMMAND_ALARM_IVAL 1200 /* 20 minutes.. */
#define SMTP_DATA_TIME_PER_LINE 600 /* 10 minutes of life.. */
/*
* The smtpserver connects to the router to ask it various questions, like,
* is this a valid address? What is the alias expansion of that? etc.
* This is done through a portal function called "server". Its only standard
* argument is a keyword for what we want done. These are the definitions:
*/
#define ROUTER_SERVER

"server"

/* name of portal function */

#define
#define
#define
#define
#define
#define

"init"
"hello"
"from"
"to"
"verify"
"expand"

/*
/*
/*
/*
/*
/*

RKEY_INIT
RKEY_HELLO
RKEY_FROM
RKEY_TO
RKEY_VERIFY
RKEY_EXPAND

#define SMTPLINESIZE

initialize state of server */


connection from a client */
mail from address verification */
recipient to address verification */
verify this address */
expand this address */

8192

#include "hostenv.h"
#ifdef HAVE_RESOLVER
# define USE_INET
#endif
#include <stdio.h>
#include "malloc.h"
#include <sys/types.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/file.h>
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#include "zmsignal.h"
#include <errno.h>
#ifdef HAVE_STDARG_H
# include <stdarg.h>
#else
# include <varargs.h> /* If no <stdarg.h>, then presume <varargs.h> ... */

#endif
#include "mail.h"
#ifdef HAVE_WAITPID
# include <sys/wait.h>
#else
# ifdef HAVE_WAIT3
# include <sys/wait.h> /* Has BSD wait3() */
# else
# ifdef HAVE_SYS_WAIT_H /* POSIX.1 compatible */
# include <sys/wait.h>
# else /* Not POSIX.1 compatible, lets fake it.. */
extern int wait();
# endif
# endif
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
# include <netinet/in.h>
#endif
#include "syslog.h" /* we use local -- we should anyway */
#include "identuser.h"
#ifndef SIGCHLD
#define SIGCHLD SIGCLD
#endif /* SIGCHLD */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 256
#endif /* MAXHOSTNAMELEN */
/*
* Early inetd's, which may be found on 4.2BSD based systems (e.g.
* Sun OS 3.x), are incapable of passing a flag to indicate we are
* being run by inetd rather than directly. Some hackery to detect
* when we are being run by the 4.2 inetd is included by defining
* CHECK42INETD. Should be deleted at the earliest possible opportunity.
*/
/* #define CHECK42INETD */
/* heuristics to detect the 4.2 inetd */
/* [mea@utu.fi] - 4-Feb-95, I disabled this.. */
typedef enum { Null, Hello, Mail, MailOrHello, Recipient,
RecipientOrData, Data, Send, SendOrMail,
SendAndMail, Reset, Verify, Expand, Help,
NoOp, Quit, Turn, Tick, Verbose, DebugMode,
Turnme, BData,
Hello2, Mail2, Send2, Verify2 /* 8-bit extensions */
} Command;
Command state;
struct command {
char
*verb;
Command cmd;
} command_list[] = {

/*
{
/*
{
{
{
{
{
{
{
{
{
{
{
/*
{
{
/*
{
{
/*
{
{
{
{
/*
{
/*
{
{
{
{
{
/*
{
/*
{

8-bit smtp extensions */


"EHLO",
Hello2
Normal stuff.. */
"HELO",
Hello
"MAIL",
Mail
"RCPT",
Recipient
"DATA",
Data
"BDAT",
BData,
"RSET",
Reset
"VRFY",
Verify
"EXPN",
Expand
"HELP",
Help
"NOOP",
NoOp
"QUIT",
Quit
ZMailer speciality, and an alias for
"ETRN",
Turnme
"TURNME",
Turnme
sendmail extensions */
"VERB",
Verbose
"ONEX",
NoOp
Depreciated */
"SEND",
Send
"SOML",
SendOrMail
"SAML",
SendAndMail
"TURN",
Turn
bsmtp extensions */
"TICK",
Tick
8-bit smtp extensions -- depreciated
"EMAL",
Mail2
"ESND",
Send2
"ESOM",
Send2
"ESAM",
Send2
"EVFY",
Verify2
To fool loosers.. */
"DEBUG",
DebugMode
End of the list */
0,
Null

};
struct smtpconf {
char
*pattern;
int
maxloadavg;
char
*flags;
struct smtpconf *next;
};
#include "relaytest.h"
struct relaytest *relaytargets
= NULL;
struct relaytest *relaysourcenets = NULL;
struct relaytest *relaysourcedoms = NULL;
int accept_sourcenet = 1, accept_sourcedomain = 1;
struct relaytest
struct relaytest
struct relaytest
int reject_net =

*rejectnets
= NULL;
*rejectsource = NULL;
*rejecttarget = NULL;
0, reject_source = 0;

extern char *rfc821_domain __((char *s, int strict));


extern char *rfc821_path __((char *s, int strict));

},
},
},
},
},
},
},
},
},
},
},
},
it */
},
},
},
},
},
},
},
},
},
*/
},
},
},
},
},
},
}

extern
extern
extern
extern

char
char
char
void

*rfc821_path2 __((char *s, int strict));


*rfc821_error;
*rfc821_error_ptr;
type821err __(());

extern struct smtpconf *readcffile __(()), *findcf __(());


struct smtpconf *cfhead = NULL;
char
extern
char
char
char
char
int
int
int
int
int
int
int
int
int
FILE
int

*progname, *cmdline, *eocmdline, *logfile;


char *postoffice; /* Defined at mail.c -- mail_open() and friends */
*routerprog = NULL;
myhostname[MAXHOSTNAMELEN+1];
rhostname[MAXHOSTNAMELEN+1];
ihostaddr[3*4+1*3+2+1 /* == 18 for '[255.255.255.255]' */ +10/*slack*/];
rport;
debug
= 0;
skeptical = 1;
checkhelo = 0;
verbose
= 0;
daemon_flg = 1;
rcpt_count = 0;
sender_ok = 0;
pid, routerpid;
*logfp = NULL;
D_alloc = 0;

char *helplines[20] = { NULL, };


static
static
static
static
static
static
static
static
static

char
char
char
char
char
char
char
char
char

*m200
*m400
*m430
*m454
*m540
*m550
*m551
*m552
*m554

=
=
=
=
=
=
=
=
=

"2.0.0";
"4.0.0";
"4.3.0";
"4.5.4";
"5.4.0";
"5.5.0";
"5.5.1";
"5.5.2";
"5.5.4";

/*
* The "style" variable controls when the router is interrogated about the
* validity of something. It is a string of letter-flags:
* f: check MAIL FROM addresses
* t: check RCPT TO addresses
* v: check VRFY command argument
* e: check EXPN command argument
* R: Demand strict conformance to RFC821/822 at address arguments
*/
char

*style = "ve";

#define STYLE(i,c)
#define
#define
#define
char

WITH_SMTP
WITH_ESMTP
WITH_BSMTP
*with_protocol

(strchr(((i)==NULL ? style : (i)->flags), (c)) != NULL)


"SMTP"
"ESMTP"
"BSMTP"
= WITH_SMTP;

struct command *carp = NULL;


FILE *mfp = NULL;
struct smtpconf *cfinfo = NULL;

long availspace = -1; /* available diskspace/2 in bytes


*/
long maxsize
= 0;
int percent_accept = 0;
long sizeoptval = 0;
/* "MAIL FROM:<xxx> SIZE=nnn" -value
*/
long sizeoptsum = 0;
int maxloadavg = 999; /* Maximum load-average that is tolerated
with smtp-server actively receiving..
Default value of 999 is high enough
so that it will never block -- use
"-L 10" to define lower limit (10) */
char helobuf[SMTPLINESIZE];

unsigned short ident_flag = 0;


# ifndef
IDENT_TIMEOUT
# define
IDENT_TIMEOUT 5
# endif /* IDENT_TIMEOUT */
char
ident_username[BUFSIZ]; /* It can be mighty big.. */
static void
setrfc1413ident();
extern char *strerror __((int));
extern int getmyhostname(), isit42inetd();
extern int getpeername();
static RETSIGTYPE reaper __((int sig));
static RETSIGTYPE timedout __((int sig));
extern void detach __((void)), prversion __(());
static void smtpserver __((int insecure, struct sockaddr_in *));
static void setrhostname __((struct sockaddr_in *));
extern int killprevious __(());
extern void settrusteduser __(());
static void killr __((int pid));
extern char *dottedquad __(());
extern char *Version;
extern char *VersionNumb;
extern u_char *rfc822date __((time_t *));
static int mvdata __((FILE *, char *));
static int mvbdata __((FILE *, char *, long, int *));
#if defined(HAVE_STDARG_H) && defined(HAVE_VPRINTF)
extern void type __((int code, char *status, char *fmt, ...));
#else
extern void type __((/* int code, char *fmt, ... */));
#endif
extern void debug_report __((int, char *, char *));
static void help __((struct smtpconf *, char *));
extern time_t time __((time_t *));
static char *router __((char*, int, char*));
static int partridge __((char*, struct smtpconf *));
#ifndef MALLOC_TRACE /* turns these into macroes.. */
# ifndef __XMALLOC_H__ /* at ../include/malloc.h */
extern univptr_t emalloc __((size_t));
extern univptr_t erealloc __((void *, size_t));
# endif
#endif
extern void runasrootuser __((void));
extern int runastrusteduser __((void));
extern char **environ;
extern int kill __((pid_t,int));

static void reporterr __((long, char*));


extern int errno;
extern int optind;
extern char *optarg;
#ifndef strchr
extern char *strchr(), *strrchr();
#endif
extern int cistrcmp(), cistrncmp();
#ifdef HAVE_STDARG_H
static void report __((char *cp, ...));
#else
static void report __(());
#endif
#ifndef CISTREQN
# define CISTREQN(x, y, n) (cistrncmp((char *)(x), (char *)(y), n) == 0)
#endif
#ifndef CISTREQ
# define CISTREQ(x, y) (cistrcmp((char *)(x), (char *)(y)) == 0)
#endif
#ifdef
#undef
#define
#endif

lint
putc
putc
fputc
/* lint */

char *msg_toohighload = "421 Sorry, the system is too loaded for email reception
at the moment\r\n"; /* XX: ??? */
void
openlogfp(insecure)
int insecure;
{
/* opening the logfile should be done before we reset the uid */
pid = getpid();
if (logfp != NULL)
fclose(logfp);
logfp = NULL;
if (logfile != NULL) {
int fd;
if ((fd = open(logfile, O_CREAT|O_APPEND|O_WRONLY, 0644)) < 0) {
if ( !insecure )
fprintf(stderr,
"%s: cannot open logfile \"%s\": %s\n",
progname, logfile, strerror(errno));
} else {
logfp = fdopen(fd, "a");
/* XX: */
setvbuf(logfp,NULL,_IOLBF,BUFSIZ); /* Line-buffered */
}
} else
logfp = NULL;
}
int
main(argc, argv)

int argc;
char **argv;
{
int inetd, errflg, c, raddrlen, s, msgfd, version, i;
char *mailshare, path[1024];
#ifdef USE_INET
struct sockaddr_in sad, raddr;
#endif /* USE_INET */
u_short port = 0;
int port_set = 0;
SIGNAL_HANDLE(SIGPIPE, SIG_IGN);
daemon_flg = 1;
inetd = errflg = version = 0;
logfile = NULL;
progname = argv[0];
cmdline = &argv[0][0];
eocmdline = cmdline;
for (i = 0; i < argc; ++i)
eocmdline += strlen(argv[i]) + 1;
*myhostname = 0;
if (getmyhostname(myhostname, sizeof myhostname) < 0) {
fprintf(stderr, "%s: gethostname('%s'): %s\n",
progname, myhostname, strerror(errno));
exit(1);
}
/* optarg = NULL; */
while ((c = getopt(argc, argv, "aBd:ighl:np:L:M:P:R:s:Vv")) != EOF) {
switch (c) {
case 'a':
ident_flag = 1;
break;
case 'B':
with_protocol = WITH_BSMTP;
break;
case 'd':
debug = atoi(optarg);
break;
case 'M':
maxsize = atoi(optarg);
if (maxsize < 0)
maxsize = 0;
break;
case 'i': /* interactive */
daemon_flg = 0;
break;
case 'v':
verbose = 1; /* in conjunction with -i */
break;
case 'g': /* gullible */
skeptical = 0;
break;
case 'h': /* checkhelo */
checkhelo = 1;
break;
case 'l': /* log file */
logfile = strdup(optarg);
break;
case 'n': /* running under inetd */

inetd = 1;
break;
case 's': /* checking style */
style = strdup(optarg);
break;
case 'L': /* Max LoadAverage */
maxloadavg = atoi(optarg);
if (maxloadavg < 1)
maxloadavg = 10; /* Humph.. */
break;
#ifdef USE_INET
case 'p':
port = htons((u_short)atoi(optarg));
port_set = 1;
break;
#endif /* USE_INET */
case 'R': /* router binary used for verification */
routerprog = strdup(optarg);
break;
case 'P':
postoffice = strdup(optarg);
break;
case 'V':
prversion("smtpserver");
exit(0);
break; /* paranoia */
default:
fprintf(stderr,
"%s: Unknown option, c=%d ('%c')\n",progname,c,c);
++errflg;
break;
}
}
#ifdef CHECK42INETD
/*
* If no flags set and we have one argument, check
* argument format to see if it's from the 4.2 inetd.
*/
if (!errflg && daemon_flg && skeptical
&& !inetd && port == 0 && optind == argc-1)
if (isit42inetd(argv[optind])) {
inetd = 1;
optind++;
}
#endif /* CHECK42INETD */
if (errflg || optind != argc) {
#ifdef USE_INET
fprintf(stderr,
"Usage: %s [-aBivgnV] [-s xx] [-L maxLoadAvg] [-M SMTPmaxsize]
[-R rtrprog] [-p port#] [-P postoffice] [-l logfile]\n",
progname);
#else /* !USE_INET */
fprintf(stderr,
"Usage: %s [-aBivgnV] [-s xx] [-L maxLoadAvg] [-M SMTPmaxsize]
[-R rtrprog] [-P postoffice] [-l logfile]\n",
progname);
#endif /* USE_INET */
exit(1);
}

pid = getpid();
if (!logfp)
openlogfp(daemon_flg);
if ((mailshare = getzenv("MAILSHARE")) == NULL)
mailshare = MAILSHARE;
if (strchr(progname, '/') != NULL)
sprintf(path, "%s/%s.conf", mailshare, strrchr(progname,'/')+1);
else
sprintf(path, "%s/%s.conf", mailshare, progname);
cfhead = readcffile(path);
rport = -1;
if (!daemon_flg) {
strcpy(rhostname, "stdin");
ihostaddr[0] = '\0';
sprintf(ident_username, "uid#%d@localhost", getuid());
smtpserver(0,NULL);
} else
#ifdef USE_INET
if (inetd) {
if (maxloadavg != 999 &&
maxloadavg < loadavg_current()) {
write(1, msg_toohighload, strlen(msg_toohighload));
exit(1);
}
raddrlen = sizeof raddr;
if (getpeername(0,(struct sockaddr *)&raddr, &raddrlen) == -1) {
fprintf(stderr, "%s: getpeername(0): %s\n",
progname, strerror(errno));
exit(1);
}
setrhostname(&raddr);
openlog("smtpserver", LOG_PID, LOG_MAIL);
if (ident_flag != 0)
setrfc1413ident(0,&raddr);
else
strcpy(ident_username, "IDENT-NOT-QUERIED");
sprintf(ident_username+strlen(ident_username),
"@%s (port %d)",rhostname, rport);
syslog(LOG_INFO, "connection from %s\n", ident_username);
pid = getpid();
if (!logfp)
openlogfp(daemon_flg);
if (logfp != NULL)
fprintf(logfp, "%d#\tconnection from %s\n",
pid, ident_username);
#if 0
SIGNAL_HANDLE(SIGCHLD, SIG_DFL);
#else
SIGNAL_HANDLE(SIGCHLD, reaper);
#endif
SIGNAL_HANDLE(SIGALRM, timedout);
SIGNAL_HANDLE(SIGHUP, SIG_IGN);
SIGNAL_HANDLE(SIGTERM, SIG_DFL);
smtpserver(1,&raddr);

} else { /* Not from under the inetd -- standalone server */


if (postoffice == NULL
&& (postoffice = getzenv("POSTOFFICE")) == NULL)
postoffice = POSTOFFICE;
if (!port_set) {
if (killprevious(SIGTERM, PID_SMTPSERVER) != 0) {
fprintf(stderr,
"%s: Can't write my pidfile! Disk full ?\n",
progname);
exit(2);
}
fflush(stdout);
fflush(stderr);
}
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
fprintf(stderr,
"%s: socket(AF_INET, SOCK_STREAM): %s\n",
progname, strerror(errno));
exit(1);
}
i = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (caddr_t)&i, sizeof i) <
0) {
fprintf(stderr,
"%s: setsockopt(SO_REUSEADDR): %s\n",
progname, strerror(errno));
exit(1);
}
#ifdef SO_REUSEPORT
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (caddr_t)&i, sizeof i) <
0) {
fprintf(stderr,
"%s: setsockopt(SO_REUSEPORT): %s\n",
progname, strerror(errno));
exit(1);
}
#endif
if (port <= 0) {
struct servent *service;
#ifdef IPPORT_SMTP
port = htons(IPPORT_SMTP);
#endif /* !IPPORT_SMTP */
if ((service = getservbyname("smtp", "tcp")) == NULL) {
fprintf(stderr,
"%s: no SMTP service entry, using default\n",
progname);
} else
port = service->s_port;
}
sad.sin_family = AF_INET;
sad.sin_addr.s_addr = INADDR_ANY;
sad.sin_port = port;
if (bind(s, (struct sockaddr *)&sad, sizeof sad) < 0) {
fprintf(stderr, "%s: bind(): %s\n",
progname, strerror(errno));
exit(1);
}
/* Set the listen limit HIGH; there has been an active
denial-of-service attack where people send faked SYNs

to open a connection to our system who does not want to


talk with us at all -- or whose traffic goes thru a
routing black-hole, who just plain don't exist on some
existing network. The net result being that our SYN-ACKs
will never get ACKed, and no connection built...
The SYN_RECV-queue can grow also due to routing diode
behind of which some poor fellow is trying to get our
attention, but our replies do not reach back to him/her. */
/* Often the system enforces some upper bound for this
limit, and you can't exceed it -- but some new systems
allow rather high limits, lets try to use it!
(The classical default is: 5) */
if (listen(s, 2000) < 0) {
fprintf(stderr, "%s: listen(): %s\n",
progname, strerror(errno));
exit(1);
}
settrusteduser(); /* dig out the trusted user ID */
closelog();
/* close the syslog too.. */
detach();
/* this must NOT close fd's */
/* Close fd's 0, 1, 2 now */
close(0); close(1); close(2);
open("/dev/null", O_RDWR, 0);
dup(0);
dup(0); /* fd's 0, 1, 2 are in use again.. */
if (!port_set || port != htons(25))
killprevious(0, PID_SMTPSERVER); /* deposit pid */
#if 1
pid = getpid();
if (!logfp)
openlogfp(daemon_flg);
if (logfp != NULL) {
time_t now;
char *cp;
time(&now);
cp = (char*) rfc822date(&now);
fprintf(logfp,"00000#\tstarted server pid %d at %s", pid, cp);
/*fprintf(logfp,"00000#\tfileno(logfp) = %d\n",fileno(logfp));*/
fflush(logfp);
}
#endif
#if 0
SIGNAL_HANDLE(SIGCHLD, SIG_DFL);
#else
SIGNAL_HANDLE(SIGCHLD, reaper);
#endif
SIGNAL_HANDLE(SIGALRM, timedout);
SIGNAL_HANDLE(SIGHUP, SIG_IGN);
SIGNAL_HANDLE(SIGTERM, SIG_DFL);
while (1) {
int childpid;
raddrlen = sizeof(raddr);
msgfd = accept(s, (struct sockaddr *)&raddr, &raddrlen);
if (msgfd < 0) {
int err = errno;
switch (err) {
case EINTR:
/* very common.. */

continue;
ECONNRESET:
ECONNABORTED:
ENETUNREACH:
ENETRESET:
ETIMEDOUT:
ECONNREFUSED:
EHOSTDOWN:
EHOSTUNREACH:

case
case
case
case
case
case
case
case

/* seen to happen! */

/* unlikely.. */
/* unlikely.. */

#ifdef ENOSR
case ENOSR:
#endif
#ifdef EPROTO
case EPROTO:
#endif
continue;
default:
break;
}
fprintf(stderr,
"%s: accept(): %s\n", progname, strerror(err));
if (!logfp)
openlogfp(daemon_flg);
fprintf(logfp, "000000#\taccept(): %s\n", strerror(err));
fflush(logfp);
exit(1);
}
if ((childpid = fork()) < 0) { /* can't fork! */
close(msgfd);
fprintf(stderr,
"%s: fork(): %s\n",
progname, strerror(errno));
sleep(5);
continue;
} else if (childpid == 0) { /* child */
close(s); /* Listening socket.. */
pid = getpid();
if (logfp) /* Open the logfp latter.. */
fclose(logfp);
logfp = NULL;
if (maxloadavg != 999 &&
maxloadavg < loadavg_current()) {
write(msgfd, msg_toohighload,
strlen(msg_toohighload));
exit(1);
}
SIGNAL_HANDLE(SIGTERM, SIG_IGN);
rport = raddr.sin_port;
setrhostname(&raddr);
openlog("smtpserver", LOG_PID, LOG_MAIL);
if (ident_flag != 0)
setrfc1413ident(msgfd,&raddr);
else
strcpy(ident_username, "IDENT-NOT-QUERIED");

sprintf(ident_username+strlen(ident_username),
"@%s (port %d)", rhostname, rport);
syslog(LOG_INFO, "connection from %s", ident_username);
pid = getpid();
if (!logfp)
openlogfp(daemon_flg);
if (logfp != NULL)
fprintf(logfp,
"%d#\tconnection from %s\n", pid, ident_username);
/* if (logfp) fprintf(logfp,"%d#\tInput fd=%d\n",getpid(),msgfd); */
if (msgfd != 0)
dup2(msgfd, 0);
dup2(0, 1);
if (msgfd > 1)
close(msgfd);
smtpserver(1,&raddr);
if (routerpid > 0)
killr(routerpid);
_exit(0);
} else
close(msgfd);
#else

}
} /* if (inetd) */
/* !USE_INET */
fprintf(stderr,
"%s: no daemon mode since no IPC available\n",
argv[0]);
exit(1);

#endif /* !USE_INET */
if (routerpid > 0)
killr(routerpid);
exit(0);
/* NOTREACHED */
return 0;
}
#ifdef CHECK42INETD
/*
* The 4.2 BSD inetd runs its servers with exactly one argument having
* the form:
*
xxxxxxxx.dddd
*
* where xxxxxxxxx is the remote IP host address in hexadecimal and
* dddd is the remote port number in decimal. While we don't use these
* (the host has to support getpeername()), this routine checks for
* the correct form of the argument.
*/
int
isit42inetd(arg)
char *arg;
{
register int i;

for (i = 0; i < 8; i++)


/* exactly 8 hex digits */
if (!isxdigit(arg[i]))
return 0;
if (arg[8] != '.')
/* period next */
return 0;
for (i = 9; arg[i] != '\0'; i++) /* now one or more decimal digits */
if (!isdigit(arg[i]))
return 0;
if (i == 9)
return 0;
return 1;
/* okay! */
}
#endif /* CHECK42INETD */
#ifdef USE_INET
/*
* set the (default) remote host name, possibly based on the remote IP
* host address if we are feeling untrusting.
*/
static void
setrhostname(sin)
struct sockaddr_in *sin;
{
struct hostent *hp;
sprintf(ihostaddr, "[%s]", dottedquad(&sin->sin_addr));
if (skeptical) {
hp = gethostbyaddr((char *)&(sin->sin_addr),
sizeof (struct in_addr), sin->sin_family);
if (hp != NULL)
strcpy(rhostname, hp->h_name);
else
strcpy(rhostname, ihostaddr);
} else {
strcpy(rhostname, ihostaddr);
}
}
#endif /* USE_INET */
static RETSIGTYPE
timedout(sig)
int sig;
{
reporterr(0,"SMTP protocol timed out");
/* If there is something going on, kill the file.. */
if (mfp != NULL)
mail_abort(mfp);
mfp = NULL;
if (routerpid > 0)
killr(routerpid);
exit(0);
}
static RETSIGTYPE
reaper(sig)

int sig;
{
int status;
#ifdef HAVE_WAITPID
while (waitpid(-1, &status, WNOHANG) > 0)
#else
#ifdef HAVE_WAIT3
while (wait3(&status, WNOHANG, (struct rusage *)NULL) > 0)
#else /* ... plain simple waiting wait() ... */
/* This can freeze at wait() ? Who could test ? A system
without wait3()/waitpid(), but with BSD networking ??? */
while (wait(&status) > 0)
#endif /* WNOHANG */
#endif
continue;
SIGNAL_HANDLE(SIGCHLD, reaper);
}
static void
reporterr(tell, msg)
long tell;
char *msg;
{
syslog(LOG_ERR,
"aborted (%ldc) from %s/%d: %s", tell, rhostname, rport, msg);
if (logfp != NULL) {
fprintf(logfp, "%d#\taborted: %s\n", pid, msg);
fflush(logfp);
}
}
/* Support routine: Our own buffering for stdinput */
int
int
char
int
int

s_bufread = 0;
s_readout = 0;
s_buffer[SMTPLINESIZE];
s_inputfd = 0; /* STDIN */
s_status = 0;

int s_feof()
{
return s_status;
}
int s_getc()
{
int rc = 0;
if (s_status) return s_status;
if (s_readout >= s_bufread) {
int rc = read(s_inputfd, s_buffer, sizeof(s_buffer));
if (rc <= 0) {
s_status = EOF;
return EOF;
}
s_bufread = rc;
s_readout = 0;
}

if (rc) return EOF;


return (unsigned char) s_buffer[s_readout++];
}
int s_hasinput()
{
if (s_readout >= s_bufread) {
/* So if it did dry up, try non-blocking read */
int flags = fcntl(s_inputfd,F_GETFL,0);
s_readout = 0;
#if defined(O_NONBLOCK) || defined(FNDELAY)
#ifdef O_NONBLOCK
fcntl(s_inputfd,F_SETFL,flags | O_NONBLOCK);
#else
fcntl(s_inputfd,F_SETFL,flags | FNDELAY);
#endif
s_bufread = read(s_inputfd,s_buffer,sizeof(s_buffer));
fcntl(s_inputfd,F_SETFL,flags);
if (s_bufread > 0) return s_bufread;
#endif
return 0;
}
return (s_bufread - s_readout);
}
void s_setup(fd)
int fd;
{
s_status = 0;
s_inputfd = fd;
s_bufread = -1;
s_readout = 0;
}

char *xtext_string(str)
char *str;
{
/* Verify that the input is valid RFC 1981 XTEXT string! */
char *str0 = str;
while (*str) {
unsigned char c = *str;
if (c == ' ' || c == '\t')
break;
if ('!' <= c && c <= '~' && c != '+' && c != '=')
; /* is ok! */
else if (c == '+') {
c = *++str;
if (!(('0' <= c && c <= '9') || ('A' <= c && c <= 'F'))) {
goto err;
}
c = *++str;
if (!(('0' <= c && c <= '9') || ('A' <= c && c <= 'F'))) {
goto err;

}
} else {
goto err;
}
++str;
}
if (str == str0) {
err:
rfc821_error_ptr = str;
return NULL;
}
return str;
}
char *rfc822atom(str)
char *str;
{
char *str0 = str;
while (*str != 0) {
u_char c = *str;
/* XX: SHOULD do this with a charmap test! */
if (c <= ' ' || c > 126 ||
c == '(' || c == ')' ||
c == '<' || c == '>' ||
c == ',' || c == ';' ||
c == ':' || c == '\\' ||
c == '"' || c == '[' || c == ']') {
if (str > str0)
return str;
goto err;
}
++str;
}
if (str == str0) {
err:
rfc821_error_ptr = str;
return NULL;
}
return str;
}
char *orcpt_string(str)
char *str;
{
/* Verify that the input is valid RFC 1981 XTEXT string! */
str = rfc822atom(str);
if (str == NULL) return NULL;
if (*str != ';') {
rfc821_error_ptr = str-1;
return NULL;
}
return xtext_string(++str);
}
/* SMTP-server verb subroutines */
void
smtp_helo(buf,cp)

char *buf, *cp;


{
if (state != Hello && state != MailOrHello) {
switch (state) {
case Mail:
cp = "Waiting for MAIL command";
break;
case Recipient:
cp = "Waiting for RCPT command";
break;
default:
cp = NULL;
break;
}
type(503, NULL, cp);
return;
}
while (*cp == ' ' || *cp == '\t') ++cp;
*helobuf = 0;
sscanf(cp, "%s", helobuf);
/*
* Craig P. says we have to spit back syntactically
* invalid helo parameters at this stage, which is
* hard to do right since it requires a full '822
* tokenizer. We do a half-hearted attempt here.
*/
/*
* Matti A. says we have a more-than-half-hearted
* tokenizer -- RFC821SCN.C, lets use it :)
* We need it for proper handling of ESMTP anyway
*/
if (checkhelo && skeptical && partridge(helobuf, cfinfo)) {
rfc821_error_ptr += (cp - helobuf);
type821err(-501, "", buf,
"Invalid `%s' parameter!",
buf);
type(501, "", "Err: %s", rfc821_error);
strcpy(helobuf,"Bad.Helo.Input");
return;
}
/* At least check the input, though say "Ok, Master" while
complaining.. */
if (!checkhelo && partridge(helobuf, cfinfo)) {
if (carp->cmd != Hello) {
rfc821_error_ptr += (cp - helobuf);
type821err(-250, "", buf, "Invalid `%s' parameter!", buf);
type(-250, NULL, "Err: %s", rfc821_error);
}
if (carp->cmd == Hello)
strcpy(helobuf,"Bad.HELO.Input");
else
strcpy(helobuf,"Bad.EHLO.Input");
}
cfinfo = findcf(helobuf);
if (cfinfo != NULL && *(cfinfo->flags) == '!') {
if (cfinfo->flags[1] != '\0')
type(501, NULL, "%s", (cfinfo->flags)+1);
else

type(501, NULL, "Sorry, access denied.");


return;
}
{ char *s=NULL;
if (STYLE(cfinfo, 'h')) {
char argbuf[MAXHOSTNAMELEN+30];
sprintf(argbuf,"%s %s",rhostname,
ihostaddr&&(ihostaddr[0]!='\0')?ihostaddr:"[0.0.0.0]");
if ((s = router(RKEY_HELLO, 1, argbuf)) == NULL)
/* the error was printed in router() */
return;
if (atoi(s) / 100 != 2) {
/* verification failed */
type(atoi(s), s+4, "Failed", "Failed");
free(s);
return;
}
else {
type(-atoi(s), s+4, "Ok", "Ok");
free(s);
}
}
}
/* Check helobuf corresponds to the reverse address */
if (skeptical && ihostaddr[0] != '\0'
&& rhostname[0] != '\0' && rhostname[0] != '['
&& !CISTREQ(helobuf, rhostname)) {
if (checkhelo) {
type(-250, NULL, "%s That hostname is inconsistent with",
myhostname);
type(-250, NULL, "%s your address to name mapping.",
myhostname);
}
type(carp->cmd==Hello2 ? -250: 250, NULL,
"%s expected \"%s %s\"", myhostname,
carp->verb, rhostname);
} else
type(carp->cmd==Hello2 ? -250: 250, NULL,
"%s Hello %s", myhostname, helobuf);
if (carp->cmd == Hello2) {
/* ESMTP -- RFC 1651,1652,1653,1428 thingies */
char sizebuf[20];
sprintf(sizebuf,"SIZE %ld",maxsize); /* 0: No fixed max size
in force, else:
The FIXED maximum */
type(-250,NULL, sizebuf);
type(-250,NULL, "8BITMIME");
type(-250,NULL, "PIPELINING");
type(-250,NULL, "CHUNKING"); /* RFC 1830 */
type(-250,NULL, "ENHANCEDSTATUSCODES");
type(-250,NULL, "EXPN");
type(-250,NULL, "VRFY");
type(-250,NULL, "DSN");
type(-250,NULL, "X-RCPTLIMIT 10000"); /* VERY HIGH figure, normal is 1
00 */
type(-250,NULL, "X-TURNME");
type(-250,NULL, "ETRN");
type( 250,NULL, "HELP");

with_protocol = WITH_ESMTP;
}
state = MailOrHello;
}
void
smtp_mail(buf,cp,insecure)
char *buf, *cp;
int insecure;
{
char *s;
int rc;
char *drpt_envid;
char *drpt_ret;
char *bodytype = NULL;
sender_ok = 0; /* Set it, when we are sure.. */
/* For ESMTP SIZE-option use we need to know
how much space we have, it is easiest by
opening the spool file here, and asking
filesystem to report free space..
We use at most half of the free space,
and each recipient will use the claimed
size, thus marking up its reception..

*/

if (mfp == NULL
&& (mfp = mail_open(MSG_RFC822)) == NULL) {
type(452,m430, "mail_open() failed with errno %d: %s", errno, strerror
(errno));
return;
}
availspace = fd_statfs(FILENO(mfp));
if (availspace < 0)
availspace = 2000000000; /* Over 2G ? */
availspace >>= 1;
if (carp->cmd == Mail2 || carp->cmd == Send2) {
with_protocol = WITH_ESMTP;
}
if (state != Mail && state != MailOrHello) {
switch (state) {
case Hello:
cp = "Waiting for HELO/EHLO command";
break;
case Recipient:
cp = "Waiting for RCPT command";
break;
default:
cp = NULL;
break;
}
type(503, m551, cp);
return;
}
if (!CISTREQN(cp, "From:", 5)) {
type(501, m552, "where is From: in that?");
return;
}

for (cp = cp+5; *cp != '\0' && *cp != '<'; ++cp)


if (!isascii(*cp) || !isspace(*cp)) {
type(501, m552, "where is <...> in that?");
return;
}
if (*cp == '\0') {
type(501, m552, "where is <...> in that?");
return;
} else if (*cp != '<') {
type(501, m552, "strangeness between : and <");
return;
}
if (*(cp+1) == '<') {
type(501, m552, "there are too many <'s in that!");
return;
}
/* "<" [ <a-t-l> ":" ] <localpart> "@" <domain> ">" */
s = rfc821_path(cp,STYLE(cfinfo, 'R'));
if (s == cp) {
/* Failure.. */
type821err(501,m552,buf,"Path data: %s",rfc821_error);
return;
}
if (*s == '>') {
type(501, m552, "there are too many >'s in that!");
return;
}
++cp;
/* Skip the initial '<' */
*(s-1) = 0;
/* Scrub the final '>' */
/* BODY=8BITMIME SIZE=nnnn ENVID=xxxxxx RET=xxxx */
sizeoptval = -1;
sizeoptsum = -1;
drpt_envid = NULL;
drpt_ret = NULL;
rc = 0;
while (*s) {
while (*s && (*s == ' '||*s == '\t')) ++s;
if (CISTREQN("RET=",s,4)) {
if (drpt_ret) {
type(501,m554,"RET-param double defined!");
return;
}
s += 4;
drpt_ret = s;
if (CISTREQN("FULL",s,4) ||
CISTREQN("HDRS",s,4))
s += 4;
if (*s && *s != ' ' && *s != '\t') {
type(501,m454,"RET-param data error");
return;
}
if (*s) *s++ = '\0';
continue;
}
if (CISTREQN("BODY=",s,5)) {
/* Actually we do not use this data... */
s += 5;
if (bodytype != NULL) {

type(501,m554,"BODY= double definition!");


rc = 1;
break;
}
if (CISTREQN(s,"8BITMIME",8)) {
bodytype = "8BITMIME";
s += 8;
} else if (CISTREQN(s,"BINARYMIME",10)) {
bodytype = "BINARYMIME";
s += 10;
} else if (CISTREQN(s,"7BIT",4)) {
bodytype = "7BIT";
s += 4;
}
if (*s && *s != ' ' && *s != '\t') {
type(455, m554, "BODY-param data error, must be one of: 8BITMIME/B
INARYMIME/7BIT");
rc = 1;
break;
}
continue;
}
if (CISTREQN("SIZE=",s,5)) {
s += 5;
if (sizeoptval != -1) {
type(455,m554,"SIZE-param double definition!");
rc = 1;
break;
}
/* This data we use, gather the value*/
sizeoptval = 0;
while (isascii(*s) && isdigit(*s)) {
sizeoptval *= 10;
sizeoptval += (*s - '0');
++s;
}
if (*s && *s != ' ' && *s != '\t') {
type(501,m554,"SIZE-param data error");
rc = 1;
break;
}
continue;
}
/* IETF-NOTARY SMTP-DRPT extensions */
if (CISTREQN("ENVID=",s,6)) {
if (drpt_envid != NULL) {
type(501,m554,"ENVID double definition!");
rc = 1;
break;
}
drpt_envid = s+6;
s = xtext_string(s+6);
if (s == NULL) {
type821err(-501,m554,buf,"Invalid ENVID value '%s'", drpt_envid);
type(501,m554,"ENVID data contains illegal characters!");
rc = 1;
break;
}
*s++ = 0;
if (*drpt_envid == 0) {

type(501,m554,"ENVID= without data!");


rc = 1;
break;
}
continue;
}
type(555,m454,"Unknown MAIL FROM:<> parameter: %s",s);
rc = 1;
break;
}
if (rc != 0) return; /* Error(s) in previous loop.. */
/*printf(" <path>: len=%d \"%s\"\n",cp-s,cp);*/
s = NULL;
if (*cp != '\0' && STYLE(cfinfo, 'f')) {
if ((s = router(RKEY_FROM, 1, cp)) == NULL)
/* the error was printed in router() */
return;
if (atoi(s) / 100 != 2) {
/* verification failed */
type(atoi(s), s+4, "Failed", "Failed");
free(s);
return;
}
}
reject_source = 0;
accept_sourcedomain = 1;
if (relaysourcedoms) {
accept_sourcedomain = reltest(relaysourcedoms, cp);
}
if (!accept_sourcedomain && rejectsource) {
reject_source = reltest(rejectsource, cp);
if (reject_source) {
type(553,"5.1.8","The source address is in a reject list");
if (s) free(s);
return;
}
}
if (reject_source < 0)
reject_source = 0;
if (mfp == NULL &&
(mfp = mail_open(MSG_RFC822)) == NULL) {
if (s)
free(s);
type(452,m430, (char *)NULL);
return;
}
rewind(mfp);
if (insecure)
fprintf(mfp, "external\n");
if (ihostaddr[0] != '\0')
fprintf(mfp, "rcvdfrom %s (%s)\n",
helobuf, ihostaddr);
else
fprintf(mfp, "rcvdfrom %s\n", helobuf);
if (bodytype != NULL)
fprintf(mfp, "bodytype %s\n", bodytype);

fprintf(mfp, "with %s\n", with_protocol);


if (ident_flag)
fprintf(mfp, "identinfo %s\n", ident_username);
if (*cp == '\0')
fprintf(mfp, "channel error\n"); /* Empty MAIL FROM:<> */
else
fprintf(mfp, "from <%s>\n", cp);
if (drpt_envid != NULL)
fprintf(mfp, "envid %s\n", drpt_envid);
if (drpt_ret != NULL)
fprintf(mfp, "notaryret %s\n", drpt_ret);
if (ferror(mfp)) {
type(452,m430,(char *)NULL);
clearerr(mfp);
} else if (sizeoptval > maxsize && maxsize > 0) {
type(552,"5.3.4","The message size exceeds fixed maximum of %ld chars
for message size", maxsize);
} else if (sizeoptval > availspace) {
type(452,"4.3.1","Try again later, insufficient storage available at t
he moment");
} else {
if (s)
type(atoi(s), s+4, "Ok", "Ok");
else
type(250, "2.1.0", (char *)NULL);
sender_ok = 1;
}
if (s)
free(s);
state = Recipient;
rcpt_count = 0;
}
void
smtp_rcpt(buf,cp)
char *buf, *cp;
{
char *s;
char *drpt_notify, *drpt_orcpt;
if (state != Recipient && state != RecipientOrData) {
switch (state) {
case Hello:
cp = "Waiting for HELO command";
break;
case Mail:
case MailOrHello:
cp = "Waiting for MAIL command";
break;
default:
cp = NULL;
break;
}
type(503, m551, cp);
return;
}
if (!CISTREQN(cp, "To:", 3)) {
type(501, m552, "where is To: in that?");

return;
}
for (cp = cp+3; *cp != '\0' && *cp != '<'; ++cp)
if (!isascii(*cp) || !isspace(*cp)) {
type(501, m552, "where is <...> in that?");
return;
}
if (*cp == '\0') {
type(501, m552, "where is <...> in that?");
return;
} else if (*cp != '<') {
type(501, m552, "strangeness between : and <");
return;
}
if (*(cp+1) == '<') {
type(501, m552, "there are too many <'s in that!");
return;
}
/* "<" [ <a-t-l> ":" ] <localpart> "@" <domain> ">" */
s = rfc821_path(cp,STYLE(cfinfo, 'R'));
if (s == cp) {
/* Failure.. */
type821err(501,m552,buf,"Path data: %s",rfc821_error);
return;
}
if (*s == '>') {
type(501, m552, "there are too many >'s in that!");
return;
}
++cp;
/* Skip the initial '<' */
*(s-1) = 0;
/* Scrub the final '>' */
/*printf(" <path>: len=%d \"%s\"\n",cp-s,cp);*/
if (*cp == '\0') {
type(501, "5.1.3", "what is a null recipient?");
return;
}
drpt_notify = NULL;
drpt_orcpt = NULL;
while (*s) {
while (isascii(*s) && isspace(*s)) ++s;
/* IETF-NOTARY SMTP-RCPT-DRPT extensions */
if (CISTREQN("NOTIFY=",s,7)) {
if (drpt_notify) {
type(501,m554,"NOTIFY-param double defined!");
return;
}
drpt_notify = s;
s += 7;
while (*s) {
if (CISTREQN("SUCCESS",s,7))
s += 7;
else if (CISTREQN("FAILURE",s,7))
s += 7;
else if (CISTREQN("DELAY",s,5))
s += 5;

else if (CISTREQN("NEVER",s,5))
s += 5;
if (*s != ',')
break;
++s;
}
if (*s && *s != ' ' && *s != '\t') {
type(455,m454,"NOTIFY-param data error");
return;
}
if (*s) *s++ = '\0';
continue;
}
if (CISTREQN("ORCPT=",s,6)) {
if (drpt_orcpt) {
type(501,m554,"ORCPT-param double defined!");
return;
}
drpt_orcpt = s;
s = orcpt_string(s+6);
if (s == NULL) {
type821err(-501,m454,buf,"Invalid ORCPT value '%s'", drpt_orcpt);
type(501,m454,"ORCPT-param data error!");
return;
}
if (s && *s != 0) *s = '\0';
++s;
continue;
}
type(555,"Unknown RCPT TO:<> parameter: %s",s);
return;
}
s = NULL;
if (STYLE(cfinfo, 't')) {
if ((s = router(RKEY_TO, 1, cp)) == NULL)
/* the error was printed in router() */
return;
if (atoi(s) / 100 != 2) {
/* verification failed */
type(atoi(s), s+4, "Failed", "Failed");
free(s);
return;
}
}
/* FIRST 'todsn', THEN 'to' -HEADER */
/* IETF-NOTARY DSN data: */
fputs("todsn",mfp);
if (drpt_notify) fprintf(mfp, " %s", drpt_notify);
if (drpt_orcpt) {
fprintf(mfp, " %s", drpt_orcpt);
} else {
char *s = cp;
fputs(" ORCPT=rfc822;",mfp);
while (*s) {
u_char c = *s;
if ('!' <= c && c <= '~' && c != '+' && c != '=')

putc(c,mfp);
else
fprintf(mfp,"+%02X",c);
++s;
}
}
putc('\n',mfp);
/* Normal "RCPT TO:<>" data: */
fprintf(mfp, "to <%s>\n", cp);
if (sizeoptval < 0)
sizeoptval = 0;
sizeoptsum += sizeoptval;
if (ferror(mfp)) {
type(452,m430,(char *)NULL);
clearerr(mfp);
} else if (maxsize > 0 && sizeoptsum > maxsize) {
type(552, "5.3.4", "Message size exceeds fixed maximum size of %ld cha
rs for acceptable email", maxsize);
} else if (sizeoptsum > availspace) {
type(452, "4.3.1", "insufficient storage space, try again later");
} else if (s) {
type(atoi(s), s+4, "Ok", "Ok");
++rcpt_count;
} else {
if (sizeoptval)
type(250, "2.1.5", "Ok; can accomodate %d byte message",sizeoptval);
else
type(250, "2.1.5", (char *)NULL);
++rcpt_count;
}
if (s)
free(s);
state = RecipientOrData;
}
static void
smtp_turnme(name,cp)
char *name, *cp;
{
FILE *mfp = mail_open(MSG_RFC822);
if (!mfp) {
type(452,m400,"Failed to initiate TURNME request; Disk full?");
fflush(stdout);
return;
}
fprintf(mfp,"%c%c%s\n",_CF_TURNME,_CFTAG_NORMAL,cp);
/* printf("050-My uid=%d/%d\r\n",getuid(),geteuid()); */
runasrootuser();
if (mail_close_alternate(mfp,TRANSPORTDIR,"")) {
type(452,m400,"Failed to initiate TURNME request; Permission denied?"
);
} else {
type(-250,m200,"A TURNME request is initiated - lets hope the system")
;
type(-250,m200,"has resources to honour it. We call the remote, if")
;
type( 250,m200,"we have anything to send there.");
}
runastrusteduser();

fflush(stdout);
}
static int
smtp_data(buf,cp)
char *buf, *cp;
{
int filsiz, inum;
struct stat stbuf;
long tell;
char msg[2048];
if (state != RecipientOrData) {
switch (state) {
case Hello:
cp = "Waiting for HELO command";
break;
case Mail:
case MailOrHello:
cp = "Waiting for MAIL command";
break;
case Recipient:
cp = "Waiting for RCPT command";
break;
case BData:
cp = "Must not intermix BDAT and DATA in same transaction!";
break;
default:
cp = NULL;
break;
}
type(503, m552, cp);
fflush(stdout);
return 0;
}
if (sender_ok == 0) {
type(550, "5.1.7", "No valid sender, rejecting all recipients");
fflush(stdout);
state = MailOrHello;
return 0;
}
if (rcpt_count == 0) {
/* No valid recipients! */
type(550, "5.1.3", "No valid recipient at RCPT addresses, or no RCPT a
ddresses at all");
fflush(stdout);
state = MailOrHello;
return 0;
}
rcpt_count = 0; /* now we can zero it.. */
type(354, NULL, (char *)NULL);
fflush(stdout);
fputs("env-end\n",mfp);
/* We set alarm()s inside the mvdata() */
*msg = 0;
filsiz = mvdata(mfp, msg);
alarm(0);
/* cancel the alarm() */
tell = ftell(mfp);
if (fstat(FILENO(mfp), &stbuf) < 0)

inum = 0;
else
inum = stbuf.st_ino;
if (*msg != 0) {
mail_abort(mfp);
mfp = NULL;
type(452,m430,"%s",msg);
fflush(stdout);
} else if (s_feof()) {
/* [mea@utu.fi] says this can happen */
mail_abort(mfp);
mfp = NULL;
reporterr(tell, "premature EOF on DATA input");
fflush(stdout);
return -1;
} else if (ferror(mfp)) {
type(452,m430,(char *)NULL);
fflush(stdout);
clearerr(mfp);
mail_abort(mfp);
mfp = NULL;
reporterr(tell, "message file error");
} else if (maxsize > 0 && filsiz > maxsize) {
mail_abort(mfp);
mfp = NULL;
type(552, "5.3.4", "Message size exceeded fixed maximum size of %ld ch
ars for acceptable email", maxsize);
fflush(stdout);
} else if (mail_close(mfp) == EOF) {
type(452,m430,(char *)NULL);
fflush(stdout);
mfp = NULL;
reporterr(tell, "message file close failed");
} else {
mfp = NULL;
type(250, "2.6.0", "Roger");
fflush(stdout);
syslog(LOG_INFO,
"accepted id %d (%ldc) from %s/%d",
inum, tell, rhostname, rport);
if (logfp != NULL) {
fprintf(logfp,
"%d#\t%d: %ld bytes\n",
pid, inum, tell);
fflush(logfp);
}
}
state = MailOrHello;
fflush(stdout);
return 0;
}
static int
smtp_bdata(buf,cp)
char *buf, *cp;
{
int filsiz, inum, rc;
struct stat stbuf;
long tell;

char msg[2048];
long bdata_chunksize;
int bdata_last;
static int bdata_blocknum = 0;
static int mvb_state;
if (state == RecipientOrData) {
state = BData;
bdata_blocknum = 0;
mvb_state = -1;
}
*msg = 0;
rc = sscanf(cp,"%ld %s %s",&bdata_chunksize,msg, msg+8);
++bdata_blocknum;
bdata_last = CISTREQ(msg,"LAST");
if (!(bdata_chunksize > 0L
&& (rc == 1 || (rc == 2 && bdata_last)))) {
type(501,m552,NULL);
return 0;
}
if (bdata_blocknum == 1) {
fputs("env-end\n",mfp);
}
/* We set alarm()s inside the mvdata() */
*msg = 0;
filsiz = mvbdata(mfp, msg, bdata_chunksize, &mvb_state);
alarm(0);
/* cancel the alarm() */
if (mfp)
tell = ftell(mfp);
else
tell = 0;
if (state != BData) {
switch (state) {
case Hello:
cp = "Waiting for HELO command";
break;
case Mail:
case MailOrHello:
cp = "Waiting for MAIL command";
break;
case Recipient:
cp = "Waiting for RCPT command";
break;
default:
cp = NULL;
break;
}
type(503, m552,cp);
fflush(stdout);
if (mfp) mail_abort(mfp);
mfp = NULL;
return 0;
}
if (bdata_blocknum == 1) {
if (sender_ok == 0 || rcpt_count == 0) {

char *cp = "No valid sender, rejecting all recipients";


if (sender_ok != 0)
cp = "No valid recipient at RCPT addresses, or no RCPT addresses a
t all";
type(550, "5.1.3", cp);
fflush(stdout);
state = MailOrHello;
if (mfp) mail_abort(mfp);
mfp = NULL;
return 0;
}
rcpt_count = 0; /* now we can zero them.. */
sender_ok = 0;
}
if (mfp == NULL) {
type(503,m552,"BDAT block discarded due to earlier error");
} else if (*msg != 0) {
mail_abort(mfp);
clearerr(mfp);
mfp = NULL;
type(452,"%s",msg);
} else if (s_feof()) {
/* [mea@utu.fi] says this can happen */
mail_abort(mfp);
clearerr(mfp);
mfp = NULL;
reporterr(tell, "premature EOF on BDAT input");
return -1;
} else if (ferror(mfp)) {
type(452,m400,(char *)NULL);
clearerr(mfp);
mail_abort(mfp);
clearerr(mfp);
mfp = NULL;
reporterr(tell, "write to spool file failed");
} else if (maxsize > 0 && tell > maxsize) {
mail_abort(mfp);
mfp = NULL;
type(552, "5.3.4", "Message size exceeded fixed maximum size of %ld ch
ars for acceptable email", maxsize);
} else if (bdata_last) {
if (fstat(FILENO(mfp), &stbuf) < 0)
inum = 0;
else
inum = stbuf.st_ino;
if (mail_close(mfp) == EOF) {
type(452,m400,(char *)NULL);
mfp = NULL;
reporterr(tell, "message file close failed");
} else {
mfp = NULL;
type(250, "2.6.0", "Roger, stored %ld bytes into spool", tell);
syslog(LOG_INFO,
"accepted id %d (%ldc) from %s/%d",
inum, tell, rhostname, rport);
if (logfp != NULL) {
fprintf(logfp, "%d#\t%d: %ld bytes\n", pid, inum, tell);
fflush(logfp);

}
}
} else { /* Not last chunk! */
type(250,"2.6.0","Received %ld bytes",bdata_chunksize);
}
if (bdata_last) {
state = MailOrHello;
}
/* Flush, if no more input available -- pipelining optimization */
if (!s_hasinput())
fflush(stdout);
return 0;
}
static void
smtp_verify(buf,cp,cfinfo)
char *buf, *cp;
struct smtpconf *cfinfo;
{
char *s;
int cfi;
if (state == Hello) {
type(503, m551, "Waiting for HELO/EHLO command");
return;
}
while (*cp == ' ' || *cp == '\t') ++cp;
if (*cp == '<')
s = rfc821_path(cp,cfi); /* with < > */
else
s = rfc821_path2(cp,cfi); /* Without < > */
if (s == cp) {
type821err(501,m552, buf,"Path data: %s",rfc821_error);
return;
}
while (*s == ' ' || *s == '\t') ++s;
if (*s != 0) {
type(501, m552, "Growl! Extra junk after the VRFY argument!");
return;
}
if ((cfi = STYLE(cfinfo, 'v'))) {
if ((s = router(RKEY_VERIFY, 0, cp)) != NULL) {
/* printf("%s\r\n", s);
free(s); */
} else
type(501,m540, "Unable to verify that address");
} else
type(252, "2.5.2", (char *)NULL); /* Syntax ok */
}
static void
smtp_expand(buf,cp,cfinfo)
char *buf, *cp;
struct smtpconf *cfinfo;
{
char *s;
int cfi;
if (state == Hello) {
type(503, m551, "Waiting for HELO/EHLO command");

return;
}
if ((cfi = STYLE(cfinfo, 'e'))) {
while (*cp == ' ' || *cp == '\t') ++cp;
if (*cp == '<')
s = rfc821_path(cp,cfi); /* with < > */
else
s = rfc821_path2(cp,cfi); /* Without < > */
if (s == cp) {
type821err(501,m552,buf,"Path data: %s",rfc821_error);
return;
}
while (*s == ' ' || *s == '\t') ++s;
if (*s != 0) {
type(501, m552, "Growl! Extra junk after the EXPN argument!");
return;
}
if ((s = router(RKEY_EXPAND, 0, cp)) != NULL) {
/* printf("%s\r\n", s);
free(s); */
} else
type(501,m540, "Unable to verify that address");
} else
type(502,m540, (char *)NULL);
}
/* The SMTP-server itself */
static void
smtpserver(insecure,raddr)
int insecure;
struct sockaddr_in *raddr;
{
char *cp;
time_t now;
int cfi;
struct sockaddr_in localsock;
int localsocksize;
int VerboseCommand = 0;
pid = getpid();
if (!logfp)
openlogfp(insecure);
runastrusteduser();
mfp = NULL;
report("(connected)");
now = time((time_t *)0);
cp = (char *)rfc822date(&now);
if (*(cp + strlen(cp) - 1) == '\n')
*(cp + strlen(cp) - 1) = '\0';
s_setup(FILENO(stdin));
/* Lets figure-out who we are this time around -- we may be on
a machine with multiple identities per multiple interfaces,
or via virtual IP-numbers, or ... */
localsocksize = sizeof(localsock);

if (getsockname(s_inputfd,
(struct sockaddr *)&localsock,&localsocksize) == 0 &&
localsocksize == sizeof(localsock)) {
/* Success with it :) */
struct hostent *hostent = gethostbyaddr((void*)&localsock.sin_addr,4,
localsock.sin_family);
if (hostent)
strcpy(myhostname,hostent->h_name);
}
if (rejectnets != NULL)
reject_net = reltestaddr(rejectnets,raddr);
if (relaytargets != NULL &&
relaysourcenets != NULL) {
accept_sourcenet = reltestaddr(relaysourcenets,raddr);
}
/* re-opening the log ?? */
openlog("smtpserver", LOG_PID, LOG_MAIL);
#ifdef HAVE_TCPD_H /* TCP-Wrapper code */
if (wantconn(s_inputfd, "smtp-receiver") == 0) {
syslog(LOG_WARNING, "refusing connection from %s/%s",
rhostname, ident_username);
type(421, NULL, "%s ZMailer Server %s WILL NOT TALK TO YOU at %s",
myhostname, VersionNumb, cp);
fflush(stdout);
sleep(1);
exit(0);
}
#endif
if (reject_net) {
type(-553, NULL, "%s - You are on our reject-IP-address -list, GO AWAY
!",
myhostname);
type(553, NULL, "Ask HELP for our contact information.");
} else
type(220, NULL, "%s ZMailer Server %s ESMTP%s ready at %s",
myhostname, VersionNumb, ident_flag ? "+IDENT":"", cp);
fflush(stdout);
state = Hello;
if ((!insecure
|| (ihostaddr[0] != '\0' && strcmp(ihostaddr, "[127.0.0.1]") == 0))
&& ((cfinfo = findcf("127.0.0.1")) == NULL
|| strcmp(cfinfo->flags, "-") == 0))
state = MailOrHello;
cfinfo = NULL;
if (logfp != NULL) {
if (insecure)
fprintf(logfp, "%d#\tremote from %s\n",
pid, ihostaddr);
else
fprintf(logfp, "%d#\tlocal from uid#%d\n",
pid, getuid());
fflush(logfp);
}

while (1) {
char buf[SMTPLINESIZE];

/* limits size of SMTP commands...


On the other hand, limit is asked
to be only 1000 chars, not 8k.. */

int rc = -1;
int c, co = -1;
int i;
char *eobuf;
/* Alarm processing on the SMTP protocol channel */
alarm(SMTP_COMMAND_ALARM_IVAL);
/* Our own fgets() -- gets also NULs, flags illegals.. */
i = 0;
while ((c = s_getc()) != EOF && i < (sizeof(buf)-1)) {
if (c == '\n') {
/* *s++ = c; */ /* Don't save it! No need */
break;
} else if (co == '\r' && rc < 0)
rc = i; /* Spurious CR on the input.. */
if (c == '\0'
&& rc < 0) rc = i;
if ((c & 0x80) != 0 && rc < 0) rc = i;
if (c != '\r' && c != '\t' &&
(c < 32 || c == 127) && rc < 0)
rc = i;
buf[i++] = c;
}
buf[i] = '\0';
eobuf = &buf[i];
/* Buf end ptr.. */
alarm(0);
/* Cancel the alarm */
if (c == EOF && i == 0) {
/* XX: ??? Uh, Hung up on us ? */
if (mfp != NULL)
mail_abort(mfp);
mfp = NULL;
break;
}
if (c != '\n') {
type(500,m552, "Line too long/not terminated with CRLF..");
continue;
}
/* Zap the possible trailing \r */
if ((eobuf > buf) && (eobuf[-1] == '\r'))
*--eobuf = '\0';
/* Chop the trailing spaces */
while ((eobuf > buf) && (eobuf[-1] == ' ' ||
eobuf[-1] == '\t'))
*--eobuf = '\0';
if (logfp != NULL) {
fprintf(logfp, "%dr\t%s\n", pid, buf);
fflush(logfp);
}
if (rc >= 0) {
rfc821_error_ptr = buf + rc;

type821err(500, m552, buf,


"Illegal input characters: %s",
((buf[rc]=='\0') ? "NUL on SMTP input" :
((buf[rc]&0x80) ? "8-bit char on SMTP input":
"Control chars on SMTP input")));
fflush(stdout);
continue;
}
if (verbose && !daemon_flg)
fprintf(stdout,"%s\n",buf); /* XX: trace.. */
report("%.100s", buf);
for (cp = buf; isascii(*cp) && isalpha(*cp); ++cp)
continue;
if (cp > buf + 6) { /* "DEBUG" is longest of them.. */
type(550,m552,"Syntax error");
fflush(stdout);
continue;
}
c = *cp;
if (c != '\0')
*cp = '\0';
for (carp = &command_list[0]; carp->verb != NULL; ++carp) {
if (CISTREQ(carp->verb, buf))
break;
}
*cp++ = c;
if (carp->verb == NULL) {
type(550, m552, "Unknown command '%s'", buf);
syslog(LOG_WARNING,
"unknown SMTP command '%s' from %s/%d",
buf, rhostname, rport);
fflush(stdout);
continue;
}
if (reject_net && carp->cmd != Quit && carp->cmd != Help) {
type(-553,NULL,"You are on our reject-IP-address -list, GO AWAY!");
type(553, NULL, "Ask HELP for our contact information.");
fflush(stdout);
continue;
}
switch (carp->cmd) {
case Null:
type(550,m550,"panic!");
fflush(stdout);
break;
case Hello:
case Hello2:
/* This code is LONG.. */
smtp_helo(buf,cp);
fflush(stdout);
break;
case Mail:
case Mail2:
case Send:
case Send2:
case SendOrMail:
case SendAndMail:
/* This code is LONG.. */

smtp_mail(buf,cp,insecure);
if (!s_hasinput())
fflush(stdout);
else
/* if (verbose) */
fprintf(logfp,"%d#\t-- pipeline input exists %d bytes\n", pi
d, s_hasinput());
break;
case Recipient:
/* This code is LONG.. */
smtp_rcpt(buf,cp);
if (!s_hasinput())
fflush(stdout);
else
/* if (verbose) */
fprintf(logfp,"%d#\t-- pipeline input exists %d bytes\n", pi
d, s_hasinput());
break;
case Data:
if (smtp_data(buf,cp) < 0)
return;
break;
case BData:
if (smtp_bdata(buf,cp) < 0)
return;
if (!s_hasinput())
fflush(stdout);
else
/* if (verbose) */
fprintf(logfp,"%d#\t-- pipeline input exists %d bytes\n", pi
d, s_hasinput());
break;
case Reset:
if (mfp != NULL) {
mail_abort(mfp);
mfp = NULL;
}
if (state != Hello)
state = MailOrHello;
type(250,m200,NULL);
fflush(stdout);
break;
case Help:
help(cfinfo, cp);
fflush(stdout);
break;
case Verify:
case Verify2:
smtp_verify(buf,cp,cfinfo);
fflush(stdout);
break;
case Expand:
smtp_expand(buf,cp,cfinfo);
fflush(stdout);
break;
case Turnme:
smtp_turnme(carp->verb,cp);
fflush(stdout);
break;
case Turn:

type(502, m551, (char *)NULL);


fflush(stdout);
break;
case NoOp:
type(250, m200, (char *)NULL);
fflush(stdout);
break;
case Verbose:
type(-250, m200, VerbID, Version);
type(-250, m200, Copyright);
type(250, m200, Copyright2);
fflush(stdout);
VerboseCommand = 1;
break;
case DebugMode:
++debug;
debug_report(VerboseCommand,rhostname,buf);
fflush(stdout);
break;
case Tick:
type(250, m200, "%s", buf);
fflush(stdout);
with_protocol = WITH_BSMTP;
break;
case Quit:
if (mfp != NULL)
mail_abort(mfp);
type(221, m200, NULL, "Out");
fflush(stdout);
return;
default:
break;
} /* case */
} /* while (1) */
if (mfp != NULL)
mail_abort(mfp);
reporterr(lseek(0,(off_t)0,SEEK_CUR), "session terminated");
}
#define TYPE_(m)
#define TYPE(m)

type(-214, NULL, "%s", m);


type(214, NULL, "%s", m);

/*
* parse the query string and print an appropriate help message.
*/
static void
help(cfinfo, query)
struct smtpconf *cfinfo;
char *query;
{
int
col;
char
*cp;
struct command *carp;
for (carp = &command_list[0]; carp->verb != NULL; ++carp) {
if (CISTREQ(carp->verb, query))
break;
}
switch (carp->cmd) {

case Hello:
case Hello2:
TYPE_("EHLO your.domain.name");
TYPE_("HELO your.domain.name");
TYPE_("\tThe 'EHLO' is for Extended SMTP feature recognition, an
d is preferred!.");
TYPE_("\tIt is polite to introduce yourself before talking.");
TYPE ("\tI will in fact ignore you until you do!");
break;
case Mail:
case Mail2:
TYPE_("MAIL FROM:<sender> (ESMTP parameters)");
TYPE_("EMAL FROM:<sender>");
TYPE_("\tSpecify the originator address for the next message.");
if (STYLE(cfinfo, 'f')) {
TYPE ("\tThe address will be checked before it is accept
ed.");
} else {
TYPE ("\tAny address will be accepted here, but may be r
ejected later.");
}
break;
case Recipient:
TYPE_("RCPT TO:<recipient> (ESMTP parameters)");
TYPE_("\tSpecify a destination address for the next message.");
if (STYLE(cfinfo, 't')) {
TYPE ("\tThe address will be checked before it is accept
ed.");
} else {
TYPE ("\tAny address will be accepted here, but may be r
ejected later.");
}
break;
case Data:
TYPE_("DATA");
TYPE_("\tStart collecting the message itself. The text data");
TYPE ("\tis terminated by a <CRLF>.<CRLF> combination.");
break;
case BData:
TYPE_("BDAT nnn [LAST]");
TYPE_("\tESMTP \"CHUNKING\" service extension; See RFC 1830");
break;
case Reset:
TYPE_("RSET");
TYPE_("\tReset the state of the SMTP server to be ready for");
TYPE_("\tthe next message, and abort any current transaction.");
TYPE_("");
switch (state) {
case Hello:
cp = "Waiting for \"HELO\" command";
break;
case Mail:
cp = "Waiting for \"MAIL\" command";
break;
case MailOrHello:
cp = "Waiting for \"MAIL\" or \"EHLO\"/\"HELO\" command"
;
break;
case Recipient:
cp = "Waiting for \"RCPT\" command";

case
case
case
case
case

case

case
case

case

case

case

case

break;
case RecipientOrData:
cp = "Waiting for \"RCPT\" or \"DATA\" command";
default:
cp = "Unknown";
break;
}
type(214,NULL,"The current state is: %s.", cp);
break;
Send:
Send2:
SendOrMail:
SendAndMail:
Turn:
TYPE_(carp->verb);
TYPE ("\tThis command will never be implemented.");
break;
Turnme:
type(-214,NULL,"%s hostname", carp->verb);
TYPE_("\tThis command schedules (at least tries to) all");
TYPE_("\toutbound traffic to ``hostname'' host.");
TYPE_("\tFor security reasons this server will initiate the");
TYPE ("\tSMTP-transport towards relay/recipient SMTP-server.");
break;
Verify:
Verify2:
TYPE_("VRFY <recipient>");
TYPE_("EVFY <recipient>");
if (STYLE(cfinfo, 'v')) {
TYPE_("\tPrints the recipients for the given address.")
TYPE ("\tIf the address is local, it is not expanded.");
} else {
TYPE ("\tThis command is disabled.");
}
break;
Expand:
TYPE_("EXPN <recipient>");
if (STYLE(cfinfo, 'e')) {
TYPE_("\tPrints the recipients for the given address.")
TYPE ("\tIf the address is local, it is expanded.");
} else {
TYPE ("\tThis command is disabled.");
}
break;
NoOp:
TYPE_(carp->verb);
TYPE ("\tThis command does nothing.");
break;
Quit:
TYPE_("QUIT");
TYPE ("\tTerminate the SMTP protocol conversation.");
break;
Verbose:
TYPE_("VERB");
TYPE_("\tPrints out the SMTP server version and copyright notice

.");
TYPE ("\tThis command has no other effect.");
break;
case Tick:
TYPE_("TICK id");

TYPE ("\tThis BSMTP command is just reflected back at you.");


break;
case Help:
TYPE_("HELP [command]");
TYPE_("\tReminder of what the SMTP command does, or prints:");
TYPE_("");
/* fall through */
case Null:
default:
TYPE_(Copyright);
TYPE_(Copyright2);
TYPE_("");
if (helplines[0] != NULL) {
int i;
for (i = 0; helplines[i] != NULL; ++i)
TYPE_(helplines[i]);
TYPE_("");
}
printf("214-The following commands are recognized:");
if (logfp)
fprintf(logfp,"%dw\t214-The following commands are recognized:
",pid);
col = 100;
for (carp = &command_list[0]; carp->verb != NULL; ++carp) {
if (col > 70) {
col = 12;
printf("\r\n214-\t%s", carp->verb);
if (logfp)
fprintf(logfp, "\n%dw\t214\t%s", pid, carp->ve
rb);
} else {
printf(", %s", carp->verb);
if (logfp)
fprintf(logfp,", %s", carp->verb);
col += 6;
}
}
printf("\r\n");
if (logfp)
fprintf(logfp,"\n");
TYPE_("");
TYPE_("The normal sequence is: EHLO/HELO (MAIL RCPT+ DATA)+ QUIT
.");
TYPE_("");
TYPE_("This mailer will always accept 8-bit and binary message d
ata");
TYPE_("though you are better to use MIME format!");
TYPE_("");
type(-214,NULL,"For local information contact: postmaster@%s",
myhostname);
type(214,NULL,"SMTP server comments and bug reports to: <zmhacks
@nic.funet.fi>");
break;
}
fflush(stdout);
if (logfp)
fflush(logfp);
}
/* If the argument is not in valid domain name syntax, return 1 */

static int
partridge(s, cfinfo)
char *s;
struct smtpconf *cfinfo;
{
char *p = rfc821_domain(s,STYLE(cfinfo, 'R'));
if (p == s) return 1;
while (*p == ' ' || *p == '\n') ++p;
if (*p == 0) return 0;
rfc821_error_ptr = p;
rfc821_error = "Spurious junk after the DOMAIN in HELO/EHLO argument";
return 1;
}
#if 0 /* tmalloc() is in the library, isn't it ? */
univptr_t
tmalloc(n)
int n;
{
return emalloc((u_int)n);
}
#endif
/*
* In theory, this should modify the command that ps shows for this process.
* This is known to not be portable, hopefully it will break badly on systems
* where it doesn't work.
*/
static void
#ifdef HAVE_STDARG_H
report(char *cp
#ifdef __STDC__
, ...
#endif
)
#else
/* VARARGS */
report(va_alist)
va_dcl
#endif
{
va_list ap;
char buf[8192];
#ifdef HAVE_STDARG_H
va_start(ap, cp);
#else
char *cp;
va_start(ap);
cp = va_arg(ap, char *);
#endif
sprintf(buf, "-%s ", rhostname);
#ifdef HAVE_VPRINTF
vsprintf(buf+strlen(buf), cp, ap);
#else /* !HAVE_VPRINTF */
sprintf(buf+strlen(buf), cp, va_arg(ap, char *));

#endif /* HAVE_VPRINTF */
for (cp = buf+strlen(buf); cp < buf + (eocmdline - cmdline); ++cp)
*cp = ' ';
buf[eocmdline-cmdline] = '\0';
strcpy(cmdline, buf);
va_end(ap);
}
/*
* The way VRFY and EXPN are implemented, and even MAIL FROM and RCPT TO
* checking, we somehow connect to a router and ask it to do stuff for us.
* There are three routines, one to connect to the router, one to kill it
* off again, and the line-getting routine that gets everything the router
* prints at us, one line at a time.
*/
char promptbuf[30];
int promptlen;
FILE *tofp, *fromfp;
extern char *mgets __((int *, FILE *));
char *mgets(flagp, fp)
int *flagp;
FILE *fp;
{
register int bufsize, c;
register char *cp;
char *buf;
int ccnt = 0;
if (*flagp) {
*flagp = 0;
return NULL;
}
bufsize = 16;
cp = buf = emalloc(bufsize);
while ((c = getc(fp)) != EOF) {
*cp = c;
++ccnt;
if (c == '\n') {
*flagp = 0;
break;
}
++cp;
if (strncmp(cp - promptlen, promptbuf, promptlen) == 0) {
free(buf);
buf = NULL;
*flagp = 1;
return NULL;
}
if (bufsize - 2 < ccnt) {
bufsize <<= 1;
buf = erealloc(buf, bufsize);
cp = buf + ccnt;
}
}
if (buf)

*cp = '\0';
if (debug && buf)
fprintf(stdout, "000 '%s'\r\n",buf);
if (c == EOF) {
printf("got EOF!\n");
free(buf);
return NULL;
}
return buf;
}
char *newenviron[] = { "SMTPSERVER=y", (char *)0 };
int
callr()
{
int sawend, pid, to[2], from[2];
char *bufp, *cp;
if (pipe(to) < 0 || pipe(from) < 0)
return -1;
#if 0
SIGNAL_HANDLE(SIGCHLD, SIG_DFL); /* ?? SIG_DFL on SVR4 ? */
#else
SIGNAL_HANDLE(SIGCHLD, reaper);
#endif
SIGNAL_HANDLE(SIGPIPE, SIG_IGN);
if (routerprog == NULL) {
if ((cp = getzenv("MAILBIN")) == NULL) {
syslog(LOG_ERR, "MAILBIN unspecified in zmailer.conf");
return -1;
}
routerprog = emalloc(strlen(cp)+sizeof "router"+2);
sprintf(routerprog, "%s/router", cp);
}
if ((pid = fork()) == 0) { /* child */
pid = getpid();
dup2(to[0], 0);
dup2(from[1], 1);
dup2(from[1], 2);
close(to[0]); close(to[1]);
close(from[0]); close(from[1]);
runasrootuser();
/* XXX: security alert! */
environ = newenviron;
execl(routerprog, "router", "-io-i", (char *)NULL);
#define BADEXEC "8@$#&(\n\n"
write(1, BADEXEC, strlen(BADEXEC));
_exit(1);
} else if (pid < 0)
return -1;
close(to[0]); close(from[1]);
tofp = fdopen(to[1], "w");
fromfp = fdopen(from[0], "r");

/* stuff a command to print something recognizable down the pipe */


sprintf(promptbuf, "%d# ", getpid());
promptlen = strlen(promptbuf);
/*
* Gotta be careful here: ihostaddr is generated within the program
* from the IP address, but rhostname is specified by the remote host.
* Although it may be caught by partridge(), lets make extra sure.
*/
for (cp = rhostname; *cp; ++cp)
if (*cp == '\'')
*cp = '?';
if (rhostname[strlen(rhostname)-1] == '\\')
rhostname[strlen(rhostname)-1] = '?';
fprintf(tofp, "PS1='%s' ; %s %s '%s' '%s'\n", promptbuf,
ROUTER_SERVER, RKEY_INIT, rhostname, ihostaddr);
fflush(tofp);
sawend = 0;
while ((bufp = mgets(&sawend, fromfp)) != NULL) {
if (strncmp(cp, BADEXEC, strlen(BADEXEC)-2) == 0) {
free(cp);
killr(pid);
return -1;
}
/*printf("241%c%s\n", sawend == 1 ? ' ' : '-', buf);*/
free(bufp);
}
return pid;
}
static void
killr(pid)
int pid;
{
if (pid > 0) {
fclose(tofp);
fclose(fromfp);
kill(pid, SIGKILL);
}
}
/*
* Now we can do VRFY et al using the router we have connected to.
*/
static char *
router(function, holdlast, args)
char *function, *args;
int holdlast;
{
char *bufp, *prevb = NULL;
int sawend = 0;
if (args == NULL) {
type(501,NULL,NULL);

return NULL;
}
if (routerpid <= 0 && (routerpid = callr()) <= 0) {
type(451,NULL,NULL);
return NULL;
}
fprintf(tofp, "%s %s \"", ROUTER_SERVER, function);
/* Process all double-quotes and backslashes so that
no surprises happen.. */
for ( ; *args ; ++args ) {
if (*args == '\\' || *args == '"')
putc('\\', tofp);
putc(*args, tofp);
}
fprintf(tofp, "\"\n");
fflush(tofp);
for (;;) {
/*
* We want to give the router the opportunity to report
* result codes, e.g. for boolean requests. If the first
* three characters are digits and the 4th a space or '-',
* then pass through.
*/
bufp = mgets(&sawend, fromfp);
if (!bufp) break;
if (debug) {
fprintf(stdout,"001 Got string: '%s'\r\n", bufp);
fflush(stdout);
}
if (prevb != NULL) {
if (strlen(prevb) > 4 &&
isdigit(prevb[0]) && isdigit(prevb[1]) && isdigit(prevb[2])
&& (prevb[3] == ' ' || prevb[3] == '-')) {
printf("%s\r\n", prevb);
} else {
printf("250-%s\r\n", prevb);
}
free(prevb);
}
prevb = bufp;
} /* for (;;) */
if (!holdlast && prevb) {
/* -------- Print the last line too -------- */
if (strlen(prevb) > 4 &&
isdigit(prevb[0]) && isdigit(prevb[1]) && isdigit(prevb[2])
&& (prevb[3] == ' ')) {
printf("%s\r\n", prevb);
} else {
printf("250 %s\r\n", prevb);
}
free(prevb);
prevb = "<any-pointer>";

} else {
/* Uhhh.... No output! */
if (!prevb)
printf("500 **INTERNAL*ERROR**\r\n");
}
fflush(stdout);
return prevb; /* It may be unfreeable pointer... */
}
void
type821err(code,status,inbuf,msg,s1,s2,s3)
int code;
char *status, *msg, *inbuf;
void *s1, *s2, *s3; /* XX: Propably not portable.. */
{
char *s = inbuf+3+strlen(status)+1;
int maxcnt = 200;
printf("%03d-%s ", code < 0 ? -code : code, status);
if (logfp != NULL)
fprintf(logfp, "%dw\t%03d-%s ", pid, code<0 ? -code : code, status);
while (s < rfc821_error_ptr && --maxcnt >= 0) {
++s;
putc(' ',stdout);
if (logfp != NULL)
putc(' ',logfp);
}
printf("^\r\n");
if (logfp != NULL)
fprintf(logfp,"^\n");
type(code,status,msg,s1,s2,s3);
}
#ifdef HAVE_VPRINTF
#ifdef HAVE_STDARG_H
void
type(int code, char *status, char *fmt
#ifdef __STDC__
, ...
#endif
)
#else
/* VARARGS2 */
void
type(code, status, fmt, va_alist)
int code;
char *status, *fmt;
va_dcl
#endif
#else /* No VPRINTF */
/* VARARGS2 */
void
type(code, status, fmt, s1, s2, s3, s4, s5, s6)
int code;
char *status, *fmt, *s1, *s2, *s3, *s4, *s5, *s6;
#endif
{

char format[BUFSIZ];
char *text = NULL;
char c;
if (code < 0) {
code = -code;
c = '-';
} else
c = ' ';
printf("%03d%c", code, c);
if (status && status[0] != 0)
printf("%s ", status);
if (logfp != NULL) {
fprintf(logfp, "%dw\t%03d%c", pid, code, c);
if (status && status[0] != 0)
fprintf(logfp, "%s ", status);
}
switch (code) {
case 211: /* System status */
text = "%s";
break;
case 214: /* Help message */
text = "%s";
break;
case 220: /* Service ready */
sprintf(format, "%s %%s", myhostname);
text = format;
break;
case 221: /* Service closing transmission channel */
sprintf(format, "%s %%s", myhostname);
text = format;
break;
case 250: /* Requested mail action okay, completed */
text = "Ok";
break;
case 251: /* User not local; will forward to <forward-path> */
text = "User not local; will forward to <%s>";
break;
case 252: /* Cannot VRFY user, but will accept message and attempt deliv
ery */
text = "Cannot VRFY user, but will accept message and attempt de
livery";
break;
case 354: /* Start mail input; end with <CRLF>.<CRLF> */
text = "Start mail input; end with <CRLF>.<CRLF>";
break;
case 421: /* Service not available, closing transmission channel */
sprintf(format, "%s %%s", myhostname);
text = format;
break;
case 450: /* Requested mail action not taken: mailbox unavailable */
text = "Requested mail action not taken: mailbox unavailable";
break;
case 451: /* Requested action aborted: local error in processing */
text = "Requested action aborted: local error in processing";
break;
case 452: /* Requested action not taken: insufficient system storage */

case
case
case
case
case
case
case
case

text = "Requested action not taken: insufficient storage";


break;
500: /* Syntax error, command unrecognized */
text = "Syntax error, command unrecognized";
break;
501: /* Syntax error in parameters or arguments */
text = "Syntax error in parameters or arguments";
break;
502: /* Command not implemented */
text = "Command not implemented";
break;
503: /* Bad sequence of commands */
text = "Bad sequence of commands";
break;
504: /* Command parameter not implemented */
text = "Command parameter not implemented";
break;
550: /* Requested action not taken: mailbox unavailable */
text = "Requested action not taken: mailbox unavailable";
break;
551: /* User not local; please try <forward-path> */
text = "User not local; please try <%s>";
break;
552: /* Requested mail action aborted: exceeded storage allocation

*/
text = "Requested mail action aborted: exceeded storage allocati
on";
break;
case 553: /* Requested action not taken: mailbox name not allowed */
text = "Requested action not taken: mailbox name not allowed";
break;
case 554: /* Transaction failed */
text = "Transaction failed";
break;
}
#ifdef HAVE_VPRINTF
{
va_list ap;
#ifdef HAVE_STDARG_H
va_start(ap,fmt);
#else
va_start(ap);
#endif
if (fmt != NULL)
vprintf(fmt, ap);
else
vprintf(text, ap);
printf("\r\n");
if (logfp != NULL) {
if (fmt != NULL)
vfprintf(logfp, fmt, ap);
else
vfprintf(logfp, text, ap);
fprintf(logfp, "\n");
fflush(logfp);
}
va_end(ap);
}
#else
if (fmt != NULL)

printf(fmt, s1, s2, s3, s4, s5, s6);


else
printf(text, s1, s2, s3, s4, s5, s6);
printf("\r\n");
if (logfp != NULL) {
if (fmt != NULL)
fprintf(logfp, fmt, s1, s2, s3, s4, s5, s6);
else
fprintf(logfp, text, s1, s2, s3, s4, s5, s6);
fprintf(logfp, "\n");
fflush(logfp);
}
#endif
}
static void header_to_mime __((char *, int *, int));
static void header_to_mime(buf, lenptr, maxlen)
char *buf;
int *lenptr;
int maxlen;
{
/* XXX: HEADERS -> MIME-2 */
}
/* Implement SMTP DATA filter */
/*
* The state table is indexed by the current character (across), and
* the current state (down). Column 0 is for any character that is not
* a '\r', '\n', or '.'. The table entries encode the next state, and
* what to output. An entry of EOF means exit. The next state is encoded
* in the l.s.byte, and what to output is encoded in the next-l.s.byte.
* If the next-l.s.byte is null, the current input character is output,
* if the high bit is set, nothing is output, else the entire byte is
* output followed by the current character.
*/
#define O_
#define N_
#define X_
int
/*
/*

(0200 << 8)
('\r' << 8)
~0

states[] = {
current input
*
'\r'
0, O_|15,
0, O_|20,
0, O_|15,
N_|0,
15,
N_|0,
15,

character
'\n'
'.'
10,
0,
X_,
0,
10,
O_|5,
10,
N_|0,
X_,
N_|0,

/* don't print anything flag */


/* print '\r' then current input */
/* exit. must have (X_&O_) != 0 */
*/
EOF
X_,
X_,
X_,
X_,
X_,

/*
/*
/*
/*
/*

states */
0: during line */
5: "^." */
10: "^" (start state) */
15: seen a \r */
20: "^.\r" */

};
/*
* Quick way of getting the column number of the state table,
* that corresponds to the input character.
*/
static char
indexnum[256+1] = {
#if 0
idxnum['\r'] = 1;
idxnum['\n'] = 2;

idxnum['.'] = 3;
#if EOF == -1
idxnum[EOF] = 4;
#endif
#endif
4, /* EOF */
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
};

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

2,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
3,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0, /* ...'\n'..'\r'.. */
0,
0, /* ... '.' .. */
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0

/*
* Copy bytes from stdin to out, obeying sensible SMTP DATA input heuristics.
*
* If you can improve on this heavily optimized routine, I'd like to see it.
* This version goes at better than 100kB/cpu-sec on a Sun 3/180.
*/
static int /* count of bytes */
mvdata(out,msg)
register FILE *out;
char *msg;
{
register int c, state, *sts, endstate, cnt;
register char *idxnum;
int timercnt = 1024;
#ifdef NO_INCOMING_HEADER_PROCESSING
idxnum = indexnum+1;
state = 10;
endstate = X_;
sts = states;
cnt = 0;
#else
char linebuf[4000], *s, *eol;
int col;
int insubject = 0;
int has8bit = 0; /* In headers */
int has8bitsum = 0;
int from__err = 0;
int linecnt = 0;
int was8bit = 0;
fflush(stdout);
idxnum = indexnum+1;

state = 10;
endstate = X_;
sts = states;
cnt = 0;
col = 0;
alarm(SMTP_DATA_TIME_PER_LINE);
/* ================ Input the email headers ================ */
/*
and analyze them a bit (Precedence:)
*/
/*
... that only "Subject:" has 8-bit chars ...
*/
mail_priority = _MAILPRIO_NORMAL;
for (;;) {
c = s_getc();
/* An EOF in here is an error! */
#if EOF != -1
if (c == EOF) return EOF;
#else
if (c < 0) return EOF;
#endif
if (--timercnt <= 0) {
/* Re-arm for every kilobyte */
alarm(SMTP_DATA_TIME_PER_LINE);
timercnt = 1024;
}
++cnt;
if ((state = sts[state+idxnum[c]]) & ~0xff) {
if (state & O_) {
if (state == endstate) {
if (col > 0) {
if (has8bit)
header_to_mime(linebuf,&col,sizeof(linebuf));
fwrite(linebuf,1,col,out);
}
return cnt;
}
state = (char)state;
continue;
}
if (col < (sizeof(linebuf)-1))
linebuf[col++] = (state >> 8);
state = (char)state;
}
if (0x80 & c)
has8bit = 1;
if (col < (sizeof(linebuf)-1))
linebuf[col++] = c;
/* LF, or something else ? Here the else.. */
if (c != '\n')
continue;
if (col < sizeof(linebuf))
linebuf[col] = 0;
/* We have a string - header line - in ``col'' first
char positions of the ``linebuf'' array */

/* See what this header is about -- or if the body starts.. */


if (col > sizeof(linebuf)-1) col = sizeof(linebuf)-1;
eol = linebuf + col;
*eol = 0;
/* See if the line is all white-space: */
for (s = linebuf; s < eol; ++s)
if (*s != ' ' && *s != '\t' &&
*s != '\r' && *s != '\n')
break;
if (s == eol) { /* All-blank line */
if (col > 0)
fwrite(linebuf,1,col,out);
break;
/* Into the body processing */
}
++linecnt;
if (*linebuf == ' ' || *linebuf == '\t') {
if (has8bit)
header_to_mime(linebuf,&col,sizeof(linebuf));
has8bit = 0;
if (col > 0)
fwrite(linebuf,1,col,out);
col = 0;
continue;
/* continuation line.. */
}
/* ================ PROCESS THE HEADERS! ================ */
if (CISTREQN(linebuf,"Subject:",8)) {
insubject = 1;
} else {
if (!insubject) {
#if 0
if (has8bit)
sprintf(msg,"Header line \"%s\" contains illegal 8-bit cha
rs", linebuf);
#endif
has8bitsum += has8bit;
}
}
if (linecnt == 1 && (strncmp("From ",linebuf,5)==0 ||
strncmp(">From ",linebuf,6)==0)) {
#if 0
from__err = 1;
sprintf(msg,"Message starts with illegal \"%s\" line",linebuf)
;
#endif
/* DO NOT WRITE THIS LINE OUT! */
col = 0;
has8bit = 0;
continue;
}
if (has8bit)
header_to_mime(linebuf,&col,sizeof(linebuf));
has8bit = 0;
/* Write the line out */
if (col > 0)
fwrite(linebuf,1,col,out);
col = 0;

has8bit = 0;
if (CISTREQN(linebuf,"Precedence:",11)) {
s = linebuf+11;
while (*s == ' ' || *s == '\t') ++s;
if ((eol - s) < 4) continue; /* Hmm.. */
if (CISTREQN("junk",s,4)) {
mail_priority = _MAILPRIO_JUNK;
} else if (CISTREQN("bulk",s,5)) {
mail_priority = _MAILPRIO_BULK;
}
}
}
if (logfp && verbose)
fprintf(logfp,"%d#\t(mail_priority=%d)\n",pid,mail_priority);
#endif
/* ================ Normal email BODY input.. ================ */
for (;;) {
c = s_getc();
#if EOF != -1
if (c == EOF)
/* a little slower... */
break;
#endif
if (--timercnt <= 0) {
/* Re-arm for every kilobyte */
alarm(SMTP_DATA_TIME_PER_LINE);
timercnt = 1024;
}
++cnt;
if ((state = sts[state+idxnum[c]]) & ~0xff) {
if (state & O_) {
if (state == endstate)
break;
state = (char)state;
continue;
}
if (!ferror(out))
putc((state>>8), out);
state = (char)state;
}
if (!ferror(out))
putc(c, out);
}
fflush(stdout);
return cnt;
}
/*
* BDAT -- For ESMTP CHUNKING extension (rfc 1830)
*/
static int /* count of bytes */
mvbdata(out,msg, incount, statep)
register FILE *out;
char *msg;
register long incount;
int *statep;
{
register int c, state, cnt;
int timercnt = 1024;

state = *statep;
cnt = 0;
/* XX: header processing REMOVED from BDAT processing */
/* ================ Normal email BODY input.. ================ */
for ( ; incount > 0; --incount) {
c = s_getc();
#if EOF != -1
if (c == EOF)
/* a little slower... */
break;
#endif
if (--timercnt <= 0) {
/* Re-arm for every kilobyte */
alarm(SMTP_DATA_TIME_PER_LINE);
timercnt = 1024;
}
++cnt;
/* Canonize CR+LF --> LF (UNIX style) */
if (c == '\r') { /* Suspend sending, this is our 'state' */
/* do nothing, 'state = c' is done after this if-else */
} else if (state == '\r') {
if (c != '\n') {
/* Suspended lone CR */
if (out) { /* We just discard it, if no output stream */
if (!ferror(out))
putc(state,out);
if (!ferror(out))
putc(c,out);
}
} else {
/* CR + LF -- forget the CR */
if (out)
if (!ferror(out))
putc(c,out);
}
} else {
/* Anything else, just output it! */
if (out)
if (!ferror(out))
putc(c,out);
}
state = c;
}
*statep = state;
return cnt;
}
static void
setrfc1413ident(sd,raddr)
int sd;
struct sockaddr_in *raddr;
{
char *cp;
struct in_addr
ident_inlocal;
struct in_addr
ident_inremote;
unsigned short
ident_local;
unsigned short
ident_remote;
int rc;

if ((rc = ident_fd2(sd, &ident_inlocal, &ident_inremote,


&ident_local, &ident_remote)) != 0) {
sprintf(ident_username, "IDENT-FD2-FAULT-%d-%d", rc, errno);
return;
} else {
cp = ident_tcpuser3(&ident_inlocal, &ident_inremote,
ident_local, ident_remote,
IDENT_TIMEOUT);
if (cp != NULL)
strncpy(ident_username, cp, MAXHOSTNAMELEN);
else
strncpy(ident_username, "IDENT-CALL-FAULT", MAXHOSTNAMELEN);
}
}
/* as in: SKIPWHILE(isascii,cp) */
#define SKIPWHILE(X,Y) while (*Y != '\0' && isascii(*Y) && X(*Y)) { ++Y; }
void
cfparam(str)
char *str;
{
char *name, *param1, *param2;
name = strchr(str,'\n'); /* The trailing newline chopper ... */
if (name) *name = 0;
SKIPWHILE(!isspace,str);
SKIPWHILE(isspace,str);
name = str;
SKIPWHILE(!isspace,str);
if (*str != 0) *str++ = 0;
if (strcasecmp(name,"help")== 0) {
int i = 0, helpmax = (sizeof(helplines)/sizeof(char*)) -2;
while (helplines[i] != NULL && i < helpmax) ++i;
param2 = strchr(str,'\n');
if (param2) *param2 = 0;
helplines[i] = strdup(str);
helplines[i+1] = NULL; /* This will always stay within the array... */
return;
}
SKIPWHILE(isspace,str);
param1 = str;
SKIPWHILE(!isspace,str);
if (*str != 0) *str++ = 0;
SKIPWHILE(isspace,str);
param2 = str;
SKIPWHILE(!isspace,str);
if (*str != 0) *str++ = 0;
if (strcasecmp(name,"maxsize") == 0) {
sscanf(param1,"%d",&maxsize);
return;
}

if (strcasecmp(name,"accept-percent-kludge") == 0) {
percent_accept = 1;
return;
}
if (strcasecmp(name,"reject-percent-kludge") == 0) {
percent_accept = -1;
return;
}
/* Following have two parameters: DBTYPE and DBPATH */
if (strcasecmp(name,"relaytargets") == 0) {
reldefine(&relaytargets,param1,param2);
return;
}
if (strcasecmp(name,"relaycustnets") == 0) {
reldefine(&relaysourcenets,param1,param2);
return;
}
if (strcasecmp(name,"rejectnets") == 0) {
reldefine(&rejectnets,param1,param2);
return;
}
if (strcasecmp(name,"rejectsource") == 0) {
reldefine(&rejectsource,param1,param2);
return;
}
if (strcasecmp(name,"rejecttarget") == 0) {
reldefine(&rejecttarget,param1,param2);
return;
}
}
struct smtpconf *
readcffile(name)
char *name;
{
FILE *fp;
struct smtpconf scf, *head, *tail = NULL;
char c, *cp, buf[1024];
if ((fp = fopen(name, "r")) == NULL)
return NULL;
head = NULL;
buf[sizeof(buf)-1] = 0;
while (fgets(buf, sizeof buf, fp) != NULL) {
c = buf[0];
if (c == '#' || (isascii(c) && isspace(c)))
continue;
if (buf[sizeof(buf)-1] != 0 &&
buf[sizeof(buf)-1] != '\n') {
int cc;
while ((cc = getc(fp)) != '\n' &&
cc != EOF)
; /* Scan until end-of-line */
}
buf[sizeof(buf)-1] = 0; /* Trunc, just in case.. */
if (strncmp(buf,"PARAM",5)==0) {
cfparam(buf);
continue;
}

scf.flags = "";
scf.next = NULL;
cp = buf;
SKIPWHILE(!isspace,cp);
c = *cp;
*cp = '\0';
scf.pattern = strdup(buf);
scf.maxloadavg = 999;
if (c != '\0') {
++cp;
SKIPWHILE(isspace,cp);
if (*cp && isascii(*cp) && isdigit(*cp)) {
/* Sanity-check -- 2 is VERY LOW */
if ((scf.maxloadavg = atoi(cp)) < 2)
scf.maxloadavg = 2;
SKIPWHILE(isdigit,cp);
SKIPWHILE(isspace,cp);
}
scf.flags = strdup(cp);
if ((cp = strchr(scf.flags, '\n')) != NULL)
*cp = '\0';
}
for (cp = scf.pattern; *cp != '\0'; ++cp)
if (isascii(*cp) && isalpha(*cp) && isupper(*cp))
*cp = tolower(*cp);
if (head == NULL) {
head = tail = (struct smtpconf *)emalloc(sizeof scf);
*head = scf;
} else {
tail->next = (struct smtpconf *)emalloc(sizeof scf);
*(tail->next) = scf;
tail = tail->next;
}
}
fclose(fp);
return head;
}
struct smtpconf *
findcf(h)
char *h;
{
struct smtpconf *scfp;
register char *cp, *s;
#ifndef USE_ALLOCA
cp = emalloc(strlen(h)+1);
#else
cp = alloca(strlen(h)+1);
#endif
for (s = cp; *h != '\0'; ++h) {
if (isascii(*h) && isalpha(*h) && isupper(*h))
*s++ = tolower(*h);
else
*s++ = *h;
}
*s = '\0';
for (scfp = cfhead; scfp != NULL; scfp = scfp->next) {
if (strmatch(scfp->pattern, cp)) {
#ifndef USE_ALLOCA

free(cp);
#endif
return scfp;
}
}
#ifndef USE_ALLOCA
free(cp);
#endif
return NULL;
}

You might also like