Professional Documents
Culture Documents
*
*
*/
/*
*
*
*/
/*
* 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"
#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
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[] = {
/*
{
/*
{
{
{
{
{
{
{
{
{
{
{
/*
{
{
/*
{
{
/*
{
{
{
{
/*
{
/*
{
{
{
{
{
/*
{
/*
{
};
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;
},
},
},
},
},
},
},
},
},
},
},
},
it */
},
},
},
},
},
},
},
},
},
*/
},
},
},
},
},
},
}
extern
extern
extern
extern
char
char
char
void
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
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);
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;
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;
}
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)
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;
}
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) {
}
}
} 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];
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;
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:
/*
* 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");
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");
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 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)
(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,
/*
/*
/*
/*
/*
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 */
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 (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;
}