1085 lines
24 KiB
C
1085 lines
24 KiB
C
/* SEPARATE_QUIT was originally a patch to deal with Wampes, which has
|
|
* a bad receive line parser. It results in one extra packet being sent per
|
|
* smtp send session, but it does reduce code size 96 bytes so it may be
|
|
* worth the small performance penalty to just leave it active in standard
|
|
* distribution. K5JB
|
|
* But, not wanting the performance penalty....
|
|
* This experimental version has a smtp t wampes command. wampes is default
|
|
* off, and smtp works pretty much like previous net. When wampes is on,
|
|
* QUIT sent only after a "250 Sent" is received, and then connection closed.
|
|
* It also has simplified eom handling which on short messages will cause
|
|
* an extra packet containging eom to be sent. Definately a low performance
|
|
* smtp.
|
|
* 8/20/94, after posting k35 for Unix, and before releasing MS-DOS, found
|
|
* interesting problems with null To: and From: fields. Marked with k35a
|
|
*/
|
|
/*
|
|
* Client routines for Simple Mail Transfer Protocol ala RFC821
|
|
* A.D. Barksdale Garbee II, aka Bdale, N3EUA
|
|
* Copyright 1986 Bdale Garbee, All Rights Reserved.
|
|
* Permission granted for non-commercial copying and use, provided
|
|
* this notice is retained.
|
|
* Modified 14 June 1987 by P. Karn for symbolic target addresses,
|
|
* also rebuilt locking mechanism
|
|
* Limit on max simultaneous sessions, reuse of connections - 12/87 NN2Z
|
|
* Added return of mail to sender as well as batching of commands 1/88 nn2z
|
|
*/
|
|
#include <stdio.h>
|
|
#ifdef _OSK
|
|
#include <modes.h>
|
|
#else
|
|
#include <fcntl.h>
|
|
#endif
|
|
#ifdef MSDOS
|
|
#include <io.h>
|
|
#endif
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#ifdef UNIX
|
|
#include <sys/types.h>
|
|
#endif
|
|
#include "global.h"
|
|
#include "netuser.h"
|
|
#include "mbuf.h"
|
|
#include "timer.h"
|
|
#include "tcp.h"
|
|
#include "smtp.h"
|
|
#include "trace.h"
|
|
#include "cmdparse.h"
|
|
|
|
#ifndef FALSE
|
|
#define FALSE 0
|
|
#define TRUE 1
|
|
#endif
|
|
|
|
int wampes = 0;
|
|
|
|
extern int16 lport; /* local port placeholder */
|
|
extern int32 resolve();
|
|
static struct timer smtpcli_t;
|
|
int start_timer(),stop_timer(); /* should be void, but ain't */
|
|
int router_queue();
|
|
int32 gateway;
|
|
void free(),log();
|
|
|
|
#ifdef SMTPTRACE
|
|
int16 smtptrace = 0; /* used for trace level */
|
|
int dosmtptrace();
|
|
#endif
|
|
|
|
int16 smtpmaxcli = MAXSESSIONS; /* the max client connections allowed */
|
|
int16 smtpsessions = 0; /* number of client connections currently open */
|
|
int16 smtpmode = 0;
|
|
|
|
static struct smtp_cb *cli_session[MAXSESSIONS]; /* queue of client sessions */
|
|
|
|
static char quitcmd[] = "QUIT\015\012";
|
|
static char eom[] = "\015\012.\015\012";
|
|
char range[] = "Out of range"; /* k34 used elsewhere */
|
|
|
|
int smtptick(),dogateway(),dosmtpmaxcli(),mlock(),dotimer(),nextjob();
|
|
int setsmtpmode(),sendwindow();
|
|
void quit(),abort_trans(),retmail(),sendit(),del_session(),del_job();
|
|
void execjobs(),smtp_transaction(),logerr();
|
|
struct smtp_cb *newcb(),*lookup();
|
|
struct smtp_job *setupjob();
|
|
|
|
struct cmds smtpcmds[] = {
|
|
"gateway", dogateway, 0, NULLCHAR, NULLCHAR,
|
|
"kick", smtptick, 0, NULLCHAR, NULLCHAR,
|
|
"maxclients", dosmtpmaxcli, 0, NULLCHAR, range, /* in global.h */
|
|
"mode", setsmtpmode, 0, NULLCHAR, "smtp mode [queue | route]",
|
|
"timer", dotimer, 0, NULLCHAR, NULLCHAR,
|
|
#ifdef SMTPTRACE
|
|
"trace", dosmtptrace, 0, NULLCHAR, NULLCHAR,
|
|
#endif
|
|
NULLCHAR, NULLFP, 0,
|
|
"subcommands: gateway kick maxclients mode timer trace",
|
|
NULLCHAR
|
|
};
|
|
|
|
dosmtp(argc,argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
int subcmd();
|
|
return subcmd(smtpcmds,argc,argv);
|
|
}
|
|
|
|
static int
|
|
dosmtpmaxcli(argc,argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
int x,atoi();
|
|
if (argc < 2)
|
|
printf("%d\n",smtpmaxcli);
|
|
else {
|
|
x = atoi(argv[1]);
|
|
if (x > MAXSESSIONS) /* simplified, k34 */
|
|
return -1;
|
|
else
|
|
smtpmaxcli = x;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
setsmtpmode(argc,argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
if (argc < 2) {
|
|
printf("smtp mode: %s\n",
|
|
(smtpmode & QUEUE) ? "queue" : "route");
|
|
} else {
|
|
switch(*argv[1]) {
|
|
case 'q':
|
|
smtpmode |= QUEUE;
|
|
break;
|
|
case 'r':
|
|
smtpmode &= ~QUEUE;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
dogateway(argc,argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
char *inet_ntoa();
|
|
int32 n;
|
|
extern char badhost[];
|
|
|
|
if(argc < 2){
|
|
printf("%s\n",inet_ntoa(gateway));
|
|
} else if((n = resolve(argv[1])) == 0){
|
|
printf(badhost,argv[1]);
|
|
return 1;
|
|
} else
|
|
gateway = n;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef SMTPTRACE
|
|
static int
|
|
dosmtptrace(argc,argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
if (argc < 2)
|
|
printf("%d\n",smtptrace);
|
|
else
|
|
smtptrace = atoi(argv[1]);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* Set outbound spool poll interval */
|
|
int /* made global so smtpserv.c could see it on startup k34 */
|
|
dotimer(argc,argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
int smtptick();
|
|
long atol(); /* K5JB */
|
|
|
|
if(argc < 2){
|
|
printf("%lu/%lu %s\n",
|
|
(smtpcli_t.start - smtpcli_t.count)/(1000/MSPTICK),
|
|
smtpcli_t.start/(1000/MSPTICK),wampes ? "W" : "");
|
|
return 0;
|
|
}
|
|
if(argv[1][0] == 'w'){ /* undocumented command to deal with Wampes */
|
|
wampes = !wampes;
|
|
return 0;
|
|
}
|
|
stop_timer(&smtpcli_t); /* k34 */
|
|
smtpcli_t.count = 0; /* k34 */
|
|
smtpcli_t.func = (void (*)())smtptick;/* what to call on timeout */
|
|
smtpcli_t.arg = NULLCHAR; /* dummy value */
|
|
smtpcli_t.start = atol(argv[1])*(1000/MSPTICK); /* set timer duration */
|
|
start_timer(&smtpcli_t); /* and fire it up */
|
|
return 0;
|
|
}
|
|
|
|
/* this is the routine that gets called every so often to do outgoing mail
|
|
processing */
|
|
|
|
int /* only needs to be int to match subcmds */
|
|
smtptick()
|
|
{
|
|
register struct smtp_cb *cb;
|
|
struct smtp_job *jp;
|
|
#ifdef SMTPTRACE
|
|
struct list *ap;
|
|
#endif
|
|
char tmpstring[LINELEN], wfilename[13], prefix[9];
|
|
char from[LINELEN], to[LINELEN];
|
|
char *cp, *cp1;
|
|
int32 destaddr;
|
|
FILE *wfile;
|
|
void rip(),filedir();
|
|
|
|
#ifdef SMTPTRACE
|
|
if (smtptrace > 5) {
|
|
printf("smtp daemon entered\n");
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
for(filedir(mailqueue,0,wfilename);wfilename[0] != '\0';
|
|
filedir(mailqueue,1,wfilename)){
|
|
|
|
/* save the prefix of the file name which is job id */
|
|
cp = wfilename;
|
|
cp1 = prefix;
|
|
while (*cp && *cp != '.')
|
|
*cp1++ = *cp++;
|
|
*cp1 = '\0';
|
|
|
|
/* lock this file from the smtp daemon */
|
|
if (mlock(mailqdir,prefix))
|
|
continue;
|
|
|
|
sprintf(tmpstring,"%s/%s",mailqdir,wfilename);
|
|
if ((wfile = fopen(tmpstring,"r")) == NULLFILE) {
|
|
/* probably too many open files */
|
|
(void) rmlock(mailqdir,prefix);
|
|
/* continue to next message. The failure
|
|
* may be temporary */
|
|
continue;
|
|
}
|
|
|
|
(void) fgets(tmpstring,LINELEN,wfile); /* read target host */
|
|
rip(tmpstring);
|
|
|
|
if ((destaddr = mailroute(tmpstring)) == 0) {
|
|
fclose(wfile);
|
|
printf("** smtp: Unknown address %s\n",tmpstring);
|
|
fflush(stdout);
|
|
(void) rmlock(mailqdir,prefix);
|
|
continue;
|
|
}
|
|
|
|
if ((cb = lookup(destaddr)) == NULLCB) {
|
|
/* there are enough processes running already */
|
|
if (smtpsessions >= smtpmaxcli) {
|
|
#ifdef SMTPTRACE
|
|
if (smtptrace) {
|
|
printf("smtp daemon: too many processes\n");
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
fclose(wfile);
|
|
(void) rmlock(mailqdir,prefix);
|
|
break;
|
|
}
|
|
if ((cb = newcb()) == NULLCB) {
|
|
fclose(wfile);
|
|
(void) rmlock(mailqdir,prefix);
|
|
break;
|
|
}
|
|
cb->ipdest = destaddr;
|
|
} else {
|
|
/* This system is already is sending mail lets not
|
|
* interfere with its send queue.
|
|
*/
|
|
if (cb->state != CLI_INIT_STATE) {
|
|
fclose(wfile);
|
|
(void) rmlock(mailqdir,prefix);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
(void) fgets(from,LINELEN,wfile); /* read from */
|
|
rip(from);
|
|
if ((jp = setupjob(cb,prefix,from)) == NULLJOB) {
|
|
fclose(wfile);
|
|
(void) rmlock(mailqdir,prefix);
|
|
del_session(cb);
|
|
break;
|
|
}
|
|
while (fgets(to,LINELEN,wfile) != NULLCHAR) {
|
|
rip(to);
|
|
if (addlist(&jp->to,to,DOMAIN) == NULLLIST) {
|
|
fclose(wfile);
|
|
del_session(cb);
|
|
}
|
|
}
|
|
fclose(wfile);
|
|
#ifdef SMTPTRACE
|
|
if (smtptrace > 1) {
|
|
printf("queue job %s From: %s To:",prefix,from);
|
|
for (ap = jp->to; ap != NULLLIST; ap = ap->next)
|
|
printf(" %s",ap->val);
|
|
printf("\n");
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* start sending that mail */
|
|
execjobs();
|
|
|
|
/* Restart timer */
|
|
start_timer(&smtpcli_t);
|
|
}
|
|
|
|
/* this is the master state machine that handles a single SMTP transaction */
|
|
void
|
|
smtp_transaction(cb)
|
|
register struct smtp_cb *cb;
|
|
{
|
|
void smtp_cts();
|
|
register char reply;
|
|
register struct list *tp;
|
|
int16 cnt;
|
|
struct mbuf *bp,*bpl;
|
|
char tbuf[LINELEN];
|
|
int rcode;
|
|
|
|
#ifdef SMTPTRACE
|
|
if (smtptrace > 5)
|
|
printf("smtp_transaction() enter state=%u\n",cb->state);
|
|
if (smtptrace) {
|
|
printf("%s\n",cb->buf);
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
/* Another line follows; ignore this one */
|
|
if(cb->buf[0] == '0' || cb->buf[3] == '-')
|
|
return;
|
|
|
|
reply = cb->buf[0];
|
|
rcode = atoi(cb->buf);
|
|
|
|
/* if service shuting down */
|
|
if (rcode == 421) {
|
|
quit(cb);
|
|
return;
|
|
}
|
|
|
|
switch(cb->state) {
|
|
case CLI_OPEN_STATE:
|
|
if (reply != '2')
|
|
quit(cb);
|
|
else {
|
|
cb->state = CLI_HELO_STATE;
|
|
sendit(cb,"HELO %s\015\012MAIL FROM:<%s>\015\012",
|
|
hostname,cb->jobq->from);
|
|
}
|
|
break;
|
|
case CLI_HELO_STATE:
|
|
if (reply != '2')
|
|
quit(cb);
|
|
else
|
|
cb->state = CLI_MAIL_STATE;
|
|
break;
|
|
case CLI_MAIL_STATE:
|
|
if (reply != '2')
|
|
quit(cb);
|
|
else {
|
|
cb->state = CLI_RCPT_STATE;
|
|
cb->rcpts = 0;
|
|
bpl = NULLBUF;
|
|
for (tp = cb->jobq->to; tp != NULLLIST; tp = tp->next){
|
|
sprintf(tbuf,"RCPT TO:<%s>\015\012",tp->val);
|
|
bp = qdata(tbuf,(int16)strlen(tbuf));
|
|
if (bp == NULLBUF) {
|
|
free_p(bpl);
|
|
quit(cb);
|
|
return;
|
|
}
|
|
append(&bpl,bp);
|
|
cb->rcpts++;
|
|
#ifdef SMTPTRACE
|
|
if (smtptrace) {
|
|
printf(">>> %s",tbuf);
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
}
|
|
send_tcp(cb->tcb,bpl);
|
|
}
|
|
break;
|
|
case CLI_RCPT_STATE:
|
|
if (reply == '5') {
|
|
logerr(cb);
|
|
} else if (reply == '2') {
|
|
cb->goodrcpt =1;
|
|
} else {
|
|
/* some kind of temporary failure */
|
|
abort_trans(cb);
|
|
break;
|
|
}
|
|
/* if more rcpts stay in this state */
|
|
if (--cb->rcpts != 0)
|
|
break;
|
|
|
|
/* check for no good rcpt on the list */
|
|
if (cb->goodrcpt == 0) {
|
|
if (cb->errlog != NULLLIST)
|
|
retmail(cb);
|
|
(void) unlink(cb->wname); /* unlink workfile */
|
|
(void) unlink(cb->tname); /* unlink text */
|
|
abort_trans(cb);
|
|
break;
|
|
}
|
|
/* if this file open fails abort */
|
|
if ((cb->tfile = fopen(cb->tname,"r")) == NULLFILE)
|
|
abort_trans(cb);
|
|
else {
|
|
/* optimize for slow packet links by sending
|
|
* DATA cmd and the 1st window of text
|
|
*/
|
|
if (cb->tcb->window <= cb->tcb->sndcnt)
|
|
cnt = 0;
|
|
else
|
|
cnt = cb->tcb->window - cb->tcb->sndcnt;
|
|
bp = qdata("DATA\015\012",6);
|
|
cb->cts = 1;
|
|
cb->state = CLI_SEND_STATE;
|
|
if (sendwindow(cb,bp,cnt) == EOF)
|
|
cb->cts = 0;
|
|
}
|
|
break;
|
|
case CLI_SEND_STATE:
|
|
if (reply == '3'){ /* e.g. 354 Enter mail... */
|
|
cb->state = CLI_UNLK_STATE;
|
|
} else {
|
|
/* negate cts to inhibit transmit upcall from sending data */
|
|
cb->cts = 0;
|
|
quit(cb);
|
|
}
|
|
break;
|
|
case CLI_UNLK_STATE:
|
|
/* if a good transfer or permanent failure remove job */
|
|
if (reply == '2' || reply == '5') {
|
|
if (reply == '5')
|
|
logerr(cb);
|
|
/* Must have been "250 Sent" so close and unlink the textfile */
|
|
if(cb->tfile != NULLFILE) {
|
|
fclose(cb->tfile);
|
|
cb->tfile = NULLFILE;
|
|
}
|
|
if (cb->errlog != NULLLIST)
|
|
retmail(cb);
|
|
(void) unlink(cb->tname);
|
|
(void) unlink(cb->wname); /* unlink workfile */
|
|
log(cb->tcb,"SMTP sent job %s To: %s From: %s",
|
|
cb->jobq->jobname,cb->jobq->to->val,cb->jobq->from);
|
|
}
|
|
if (nextjob(cb)) {
|
|
cb->state = CLI_MAIL_STATE;
|
|
sendit(cb,"MAIL FROM:<%s>\015\012",cb->jobq->from);
|
|
} else{
|
|
/* if !wampes, quit command appended to prev pkt in sendwindow() */
|
|
cb->state = CLI_QUIT_STATE;
|
|
if(wampes)
|
|
quit(cb); /* Will set state to QUIT, send it and close tcp k34 */
|
|
else
|
|
cb->state = CLI_QUIT_STATE;
|
|
}
|
|
break;
|
|
case CLI_IDLE_STATE: /* used after a RSET and more mail to send */
|
|
if (reply != '2')
|
|
quit(cb);
|
|
else {
|
|
cb->state = CLI_MAIL_STATE;
|
|
sendit(cb,"MAIL FROM:<%s>\015\012",cb->jobq->from);
|
|
}
|
|
break;
|
|
case CLI_QUIT_STATE: /* "221 Closing" will be ignored */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* abort the current job.
|
|
* If more work exists set up the next job if
|
|
* not then shut down.
|
|
*/
|
|
static void
|
|
abort_trans(cb)
|
|
register struct smtp_cb *cb;
|
|
{
|
|
if(cb->tfile != NULLFILE) {
|
|
fclose(cb->tfile);
|
|
cb->tfile = NULLFILE;
|
|
}
|
|
if (nextjob(cb)) {
|
|
sendit(cb,"RSET\015\012");
|
|
cb->state = CLI_IDLE_STATE;
|
|
} else
|
|
quit(cb);
|
|
}
|
|
|
|
/* close down link after completion or a failure */
|
|
static void
|
|
quit(cb)
|
|
struct smtp_cb *cb;
|
|
{
|
|
cb->state = CLI_QUIT_STATE;
|
|
sendit(cb,quitcmd); /* issue a quit command */
|
|
close_tcp(cb->tcb); /* close up connection */
|
|
}
|
|
|
|
/* smtp receiver upcall routine. fires up the state machine to parse input */
|
|
static
|
|
void
|
|
smtp_rec(tcb,cnt)
|
|
struct tcb *tcb;
|
|
int16 cnt;
|
|
{
|
|
register struct smtp_cb *cb;
|
|
char *inet_ntoa();
|
|
char c;
|
|
struct mbuf *bp;
|
|
|
|
#ifdef SMTPTRACE
|
|
if (smtptrace > 7) {
|
|
printf("smtp_rec called\n");
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
cb = (struct smtp_cb *)tcb->user; /* point to our struct */
|
|
recv_tcp(tcb,&bp,cnt); /* suck up chars from low level routine */
|
|
|
|
/* Assemble input line in buffer, return if incomplete */
|
|
while(pullup(&bp,&c,1) == 1) {
|
|
switch(c) {
|
|
case '\015': /* strip cr's */
|
|
continue;
|
|
case '\012': /* line is finished, go do it! */
|
|
cb->buf[cb->cnt] = '\0';
|
|
smtp_transaction(cb);
|
|
cb->cnt = 0;
|
|
break;
|
|
default: /* other chars get added to buffer */
|
|
if(cb->cnt != LINELEN-1)
|
|
cb->buf[cb->cnt++] = c;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* smtp transmitter ready upcall routine. twiddles cts flag */
|
|
static
|
|
void
|
|
smtp_cts(tcb,cnt)
|
|
struct tcb *tcb;
|
|
int16 cnt;
|
|
{
|
|
register struct smtp_cb *cb;
|
|
|
|
#ifdef SMTPTRACE
|
|
if (smtptrace > 7) {
|
|
printf("smtp_cts called avail %d\n",cnt);
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
cb = (struct smtp_cb *)tcb->user; /* point to our struct */
|
|
|
|
/* don't do anything until/unless we're supposed to be sending */
|
|
if(cb->cts == 0)
|
|
return;
|
|
|
|
if (sendwindow(cb,NULLBUF,cnt) == EOF)
|
|
cb->cts = 0;
|
|
}
|
|
|
|
/* fill the rest of the window with data and send out the eof commands.
|
|
* It is done this way to minimize the number of packets sent.
|
|
*/
|
|
static int
|
|
sendwindow(cb,ibp,cnt)
|
|
register struct smtp_cb *cb;
|
|
struct mbuf *ibp;
|
|
int16 cnt;
|
|
{
|
|
struct mbuf *bpl;
|
|
register struct mbuf *bp;
|
|
char *cp;
|
|
int c;
|
|
#if (UNIX || _OSK)
|
|
static int savedch = FALSE; /* added k29 - this mod costs ?? bytes when
|
|
applied to the Unix version */
|
|
#endif
|
|
|
|
bpl = ibp;
|
|
if((bp = alloc_mbuf(cnt)) == NULLBUF){
|
|
/* Hard to know what to do here */
|
|
return EOF;
|
|
}
|
|
cp = bp->data;
|
|
|
|
/* This routine used also in finger.c and ftp.c in order to not
|
|
* have a chronic queue of (window - 1). If necessary to handle
|
|
* ill formed MS-DOS text files, just remove the #ifs -- K5JB k29
|
|
*/
|
|
#if (UNIX || _OSK)
|
|
if(savedch){ /* unlikely case */
|
|
*cp++ = '\015';
|
|
*cp++ = '\012';
|
|
bp->cnt += 2;
|
|
cnt -= 2;
|
|
savedch = FALSE;
|
|
}
|
|
#endif
|
|
while(cnt > 0 && (c = getc(cb->tfile)) != EOF){
|
|
switch((char)c){
|
|
case '\032': /* Ctrl-Z */
|
|
break;
|
|
#if (UNIX || _OSK)
|
|
#ifdef _OSK
|
|
case '\012': /* OS9/OSK uses \r as an EOL char */
|
|
break;
|
|
case '\015':
|
|
c = '\012';
|
|
#else
|
|
case '\015':
|
|
break;
|
|
case '\012':
|
|
#endif
|
|
if(cnt == 1){ /* unlikely case, no room for 2 chars */
|
|
savedch = TRUE;
|
|
cnt = 0; /* make it short; drop out */
|
|
break;
|
|
}
|
|
*cp++ = '\015';
|
|
bp->cnt++;
|
|
cnt--; /* fall through */
|
|
#endif
|
|
default:
|
|
*cp++ = (char)c;
|
|
bp->cnt++;
|
|
cnt--;
|
|
break;
|
|
|
|
} /* switch */
|
|
} /* while */
|
|
append(&bpl,bp);
|
|
send_tcp(cb->tcb,bpl); /* note that we aren't closing tcp yet */
|
|
if (c == EOF){ /* EOF seen */
|
|
fclose(cb->tfile);
|
|
cb->tfile = NULLFILE;
|
|
|
|
/* on mail that takes more than one frame, eom will be appended to it */
|
|
/* else it will be prepended to the QUIT if !wampes */
|
|
/* no extra line is added here -- watch out for protocol problems */
|
|
sendit(cb,cp[-1] == '\012' ? &eom[2] : eom);
|
|
|
|
if(!wampes)
|
|
/* send the quit in this packet if last job */
|
|
if (cb->jobq->next == NULLJOB){
|
|
sendit(cb,quitcmd);
|
|
close_tcp(cb->tcb); /* close up connection */
|
|
}
|
|
|
|
return EOF; /* If wampes, close_tcp closed with quit() */
|
|
} else /* not eof */
|
|
return 0;
|
|
}
|
|
|
|
/* smtp state change upcall routine. */
|
|
static
|
|
void
|
|
smtp_state(tcb,unused,new)
|
|
register struct tcb *tcb;
|
|
char unused,new;
|
|
{
|
|
register struct smtp_cb *cb;
|
|
extern char *tcpstates[];
|
|
|
|
#ifdef SMTPTRACE
|
|
if (smtptrace > 7) {
|
|
printf("smtp_state called: %s\n",tcpstates[new]);
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
cb = (struct smtp_cb *)tcb->user;
|
|
switch(new) {
|
|
case ESTABLISHED:
|
|
cb->state = CLI_OPEN_STATE; /* shouldn't be needed */
|
|
break;
|
|
case CLOSE_WAIT:
|
|
close_tcp(tcb); /* shut things down */
|
|
break;
|
|
case CLOSED:
|
|
/* if this close was not done by us ie. a RST */
|
|
if(cb->tfile != NULLFILE)
|
|
fclose(cb->tfile);
|
|
del_session(cb);
|
|
del_tcp(tcb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Send message back to server */
|
|
/*VARARGS*/
|
|
static void
|
|
sendit(cb,fmt,arg1,arg2)
|
|
struct smtp_cb *cb;
|
|
char *fmt,*arg1,*arg2;
|
|
{
|
|
struct mbuf *bp;
|
|
char tmpstring[256];
|
|
|
|
#ifdef SMTPTRACE
|
|
if (smtptrace) {
|
|
printf(">>> ");
|
|
printf(fmt,arg1,arg2);
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
sprintf(tmpstring,fmt,arg1,arg2);
|
|
bp = qdata(tmpstring,(int16)strlen(tmpstring));
|
|
send_tcp(cb->tcb,bp);
|
|
}
|
|
|
|
/* create mail lockfile */
|
|
int
|
|
mlock(dir,id)
|
|
char *dir,*id;
|
|
{
|
|
char lockname[LINELEN];
|
|
int fd;
|
|
/* Try to create the lock file in an atomic operation */
|
|
sprintf(lockname,"%s/%s.lck",dir,id);
|
|
#ifdef _OSK
|
|
if ((fd = create(lockname,2,3))==-1)
|
|
return -1;
|
|
#else
|
|
if((fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT,0600)) == -1)
|
|
return -1;
|
|
#endif
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
/* remove mail lockfile */
|
|
int
|
|
rmlock(dir,id)
|
|
char *dir,*id;
|
|
{
|
|
char lockname[LINELEN];
|
|
sprintf(lockname,"%s/%s.lck",dir,id);
|
|
return(unlink(lockname));
|
|
}
|
|
|
|
/* free the message struct and data */
|
|
static void
|
|
del_session(cb)
|
|
register struct smtp_cb *cb;
|
|
{
|
|
register struct smtp_job *jp,*tp;
|
|
register int i;
|
|
|
|
if (cb == NULLCB)
|
|
return;
|
|
for(i=0; i<MAXSESSIONS; i++)
|
|
if(cli_session[i] == cb) {
|
|
cli_session[i] = NULLCB;
|
|
break;
|
|
}
|
|
|
|
if(cb->wname != NULLCHAR)
|
|
free(cb->wname);
|
|
if(cb->tname != NULLCHAR)
|
|
free(cb->tname);
|
|
for (jp = cb->jobq; jp != NULLJOB;jp = tp) {
|
|
tp = jp->next;
|
|
del_job(jp);
|
|
}
|
|
del_list(cb->errlog);
|
|
free((char *)cb);
|
|
smtpsessions--; /* number of connections active */
|
|
}
|
|
|
|
void
|
|
del_job(jp)
|
|
register struct smtp_job *jp;
|
|
{
|
|
if ( *jp->jobname != '\0')
|
|
(void) rmlock(mailqdir,jp->jobname);
|
|
if(jp->from != NULLCHAR)
|
|
free(jp->from);
|
|
del_list(jp->to);
|
|
free((char *)jp);
|
|
}
|
|
|
|
/* delete a list of list structs */
|
|
void
|
|
del_list(lp)
|
|
struct list *lp;
|
|
{
|
|
register struct list *tp, *tp1;
|
|
for (tp = lp; tp != NULLLIST; tp = tp1) {
|
|
tp1 = tp->next;
|
|
if(tp->val != NULLCHAR)
|
|
free(tp->val);
|
|
free((char *)tp);
|
|
}
|
|
}
|
|
|
|
/* return message to sender */
|
|
static void
|
|
retmail(cb)
|
|
struct smtp_cb *cb;
|
|
{
|
|
register struct list *lp;
|
|
register FILE *tfile;
|
|
register int c;
|
|
FILE *infile,*tmpfile();
|
|
char *host,*to;
|
|
char *from; /* k35a */
|
|
time_t t,time();
|
|
#ifdef SMTPTRACE
|
|
if (smtptrace > 5) {
|
|
printf("smtp job %s returned to sender\n",cb->wname);
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
/* A null From<> so no looping replys to MAIL-DAEMONS -- Wrong!
|
|
* NET won't mail a message with a null From<> field. k35a
|
|
*/
|
|
|
|
to = cb->jobq->from;
|
|
if (*to == '\0')
|
|
return;
|
|
if ((host = index(to,'@')) == NULLCHAR)
|
|
host = hostname;
|
|
else
|
|
host++;
|
|
#ifdef MSDOS /* k35a */
|
|
if ((infile = fopen(cb->tname,"rt")) == NULLFILE)
|
|
#else
|
|
if ((infile = fopen(cb->tname,"r")) == NULLFILE)
|
|
#endif
|
|
return;
|
|
if ((tfile = tmpfile()) == NULLFILE) {
|
|
fclose(infile);
|
|
return;
|
|
}
|
|
if ((from = malloc((unsigned)strlen(hostname)+15)) == NULLCHAR)
|
|
return;
|
|
sprintf(from,"MAILER-DAEMON@%s",hostname);
|
|
time(&t);
|
|
fprintf(tfile,"Date: %s",ptime(&t));
|
|
fprintf(tfile,"Message-Id: <%ld@%s>\n",get_msgid(),hostname);
|
|
fprintf(tfile,"From: %s\n",from);
|
|
fprintf(tfile,"To: %s\n",to);
|
|
fprintf(tfile,"Subject: Failed mail\n\n");
|
|
fprintf(tfile," ===== transcript follows =====\n\n");
|
|
|
|
for (lp = cb->errlog; lp != NULLLIST; lp = lp->next)
|
|
fprintf(tfile,"%s\n",lp->val);
|
|
|
|
fprintf(tfile,"\n ===== Unsent message follows ====\n");
|
|
|
|
while((c = getc(infile)) != EOF)
|
|
if (putc(c,tfile) == EOF)
|
|
break;
|
|
fclose(infile);
|
|
fseek(tfile,0L,0);
|
|
if ((smtpmode & QUEUE) != 0)
|
|
router_queue(cb->tcb,tfile,from,to); /* had null "from" k35a */
|
|
else
|
|
queuejob(cb->tcb,tfile,host,to,from); /* ditto */
|
|
#if (defined(_OSK) && !defined(_UCC))
|
|
tmpclose(tfile);
|
|
#else
|
|
fclose(tfile);
|
|
#endif
|
|
free(from);
|
|
}
|
|
|
|
/* look to see if a smtp control block exists for this ipdest */
|
|
static struct smtp_cb *
|
|
lookup(destaddr)
|
|
int32 destaddr;
|
|
{
|
|
register int i;
|
|
|
|
for(i=0; i<MAXSESSIONS; i++) {
|
|
if (cli_session[i] == NULLCB)
|
|
continue;
|
|
if(cli_session[i]->ipdest == destaddr)
|
|
return cli_session[i];
|
|
}
|
|
return NULLCB;
|
|
}
|
|
|
|
/* create a new smtp control block */
|
|
static struct smtp_cb *
|
|
newcb()
|
|
{
|
|
register int i;
|
|
register struct smtp_cb *cb;
|
|
|
|
for(i=0; i<MAXSESSIONS; i++) {
|
|
if(cli_session[i] == NULLCB) {
|
|
cb = (struct smtp_cb *)calloc(1,sizeof(struct smtp_cb));
|
|
if (cb == NULLCB)
|
|
return(NULLCB);
|
|
cb->wname = malloc((unsigned)strlen(mailqdir)+JOBNAME);
|
|
if (cb->wname == NULLCHAR) {
|
|
free((char *)cb);
|
|
return(NULLCB);
|
|
}
|
|
cb->tname = malloc((unsigned)strlen(mailqdir)+JOBNAME);
|
|
if (cb->tname == NULLCHAR) {
|
|
free(cb->wname);
|
|
free((char *)cb);
|
|
return(NULLCB);
|
|
}
|
|
cb->state = CLI_INIT_STATE;
|
|
cli_session[i] = cb;
|
|
smtpsessions++; /* number of connections active */
|
|
return(cb);
|
|
}
|
|
}
|
|
return NULLCB;
|
|
}
|
|
|
|
static void
|
|
execjobs()
|
|
{
|
|
struct socket lsocket, fsocket;
|
|
void smtp_rec(), smtp_cts(), smtp_state();
|
|
register struct smtp_cb *cb;
|
|
register int i;
|
|
|
|
for(i=0; i<MAXSESSIONS; i++) {
|
|
cb = cli_session[i];
|
|
if (cb == NULLCB)
|
|
continue;
|
|
if(cb->state != CLI_INIT_STATE)
|
|
continue;
|
|
|
|
sprintf(cb->tname,"%s/%s.txt",mailqdir,cb->jobq->jobname);
|
|
sprintf(cb->wname,"%s/%s.wrk",mailqdir,cb->jobq->jobname);
|
|
|
|
/* setup the socket */
|
|
fsocket.address = cb->ipdest;
|
|
fsocket.port = SMTP_PORT;
|
|
lsocket.address = ip_addr; /* our ip address */
|
|
lsocket.port = lport++; /* next unused port */
|
|
#ifdef SMTPTRACE
|
|
if (smtptrace) {
|
|
printf("Trying Connection to %s\n",inet_ntoa(fsocket.address));
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
|
|
/* open smtp connection */
|
|
cb->state = CLI_OPEN_STATE; /* init state placeholder */
|
|
cb->tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,tcp_window,
|
|
smtp_rec,smtp_cts,smtp_state,0,(char *)cb);
|
|
cb->tcb->user = (char *)cb; /* Upward pointer */
|
|
}
|
|
}
|
|
|
|
/* add this job to control block queue */
|
|
struct smtp_job *
|
|
setupjob(cb,id,from)
|
|
struct smtp_cb *cb;
|
|
char *id,*from;
|
|
{
|
|
register struct smtp_job *p1,*p2;
|
|
|
|
p1 = (struct smtp_job *)calloc(1,sizeof(struct smtp_job));
|
|
if (p1 == NULLJOB)
|
|
return NULLJOB;
|
|
p1->from = malloc((unsigned)strlen(from) + 1);
|
|
if (p1->from == NULLCHAR) {
|
|
free((char *)p1);
|
|
return NULLJOB;
|
|
}
|
|
strcpy(p1->from,from);
|
|
strcpy(p1->jobname,id);
|
|
/* now add to end of jobq */
|
|
if ((p2 = cb->jobq) == NULLJOB)
|
|
cb->jobq = p1;
|
|
else {
|
|
while(p2->next != NULLJOB)
|
|
p2 = p2->next;
|
|
p2->next = p1;
|
|
}
|
|
return p1;
|
|
}
|
|
|
|
/* called to advance to the next job */
|
|
static int
|
|
nextjob(cb)
|
|
register struct smtp_cb *cb;
|
|
{
|
|
register struct smtp_job *jp;
|
|
|
|
|
|
jp = cb->jobq->next;
|
|
del_job(cb->jobq);
|
|
if (jp == NULLJOB) {
|
|
cb->jobq = NULLJOB;
|
|
return 0;
|
|
}
|
|
/* remove the error log of previous message */
|
|
del_list(cb->errlog);
|
|
cb->errlog = NULLLIST;
|
|
cb->goodrcpt = 0;
|
|
cb->jobq = jp;
|
|
sprintf(cb->tname,"%s/%s.txt",mailqdir,jp->jobname);
|
|
sprintf(cb->wname,"%s/%s.wrk",mailqdir,jp->jobname);
|
|
#ifdef SMTPTRACE
|
|
if (smtptrace > 5) {
|
|
printf("sending job %s\n",jp->jobname);
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
/* mail routing funtion. For now just used the hosts file */
|
|
int32
|
|
mailroute(dest)
|
|
char *dest;
|
|
{
|
|
int32 destaddr;
|
|
|
|
/* look up address or use the gateway */
|
|
if ((destaddr = resolve(dest)) == 0)
|
|
if (gateway != 0)
|
|
destaddr = gateway; /* Use the gateway */
|
|
return destaddr;
|
|
|
|
}
|
|
|
|
/* save error reply for in error list */
|
|
static void
|
|
logerr(cb)
|
|
struct smtp_cb *cb;
|
|
{
|
|
register struct list *lp,*tp;
|
|
if ((tp = (struct list *)calloc(1,sizeof(struct list))) == NULLLIST)
|
|
return;
|
|
if ((tp->val = malloc((unsigned)strlen(cb->buf)+1)) == NULLCHAR) {
|
|
free((char *)tp);
|
|
return;
|
|
}
|
|
/* find end of list */
|
|
if ((lp = cb->errlog) == NULLLIST)
|
|
cb->errlog = tp;
|
|
else {
|
|
while(lp->next != NULLLIST)
|
|
lp = lp->next;
|
|
lp->next = tp;
|
|
}
|
|
strcpy(tp->val,cb->buf);
|
|
}
|