home *** CD-ROM | disk | FTP | other *** search
- #ifndef lint
- static char *sccsid = "@(#)converse.c 1.8 87/05/14";
- #endif lint
- /* Copyright 1984 Massachusetts Institute of Technology
-
- Permission to use, copy, modify, and distribute this program
- for any purpose and without fee is hereby granted, provided
- that this copyright and permission notice appear on all copies
- and supporting documentation, the name of M.I.T. not be used
- in advertising or publicity pertaining to distribution of the
- program without specific prior permission, and notice be given
- in supporting documentation that copying and distribution is
- by permission of M.I.T. M.I.T. makes no representations about
- the suitability of this software for any purpose. It is pro-
- vided "as is" without express or implied warranty. */
-
- /*
- * smtpd - World's most trivial SMTP server. Only accepts the MAIL, FROM,
- * RCPT, and DATA commands. Generates a date file for the mail
- * daemon and kicks the mail daemon off.
- */
-
- #include "smtp.h"
-
- #ifdef BSD
- #include <sgtty.h>
- #endif
- /* #include <ioctl.h> */
- #include <signal.h>
- #include <sys/uio.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
-
- #include "cmds.h"
-
- /* tunable constants */
-
- #define SECONDS 1
- #define MINUTES 60
- #define HOURS (60 * MINUTES)
-
- #define SHORTTIME (5 * MINUTES) /* enough time for easy stuff */
- #define LONGTIME (2 * HOURS) /* max time, DATA to `.' */
-
- #define DATAMODE 0660 /* mode for data file */
-
- #ifdef MAILER
- char *sigmaild = MAILER;
- #else
- char *sigmaild = "/bin/rmail";
- #endif
-
- typedef long in_name; /* internet host address */
-
- int buflen; /* size of string in cmd buffer */
-
- static char rcptlist[MAXSTR]; /* recipient list */
- static char *rcptlast; /* end of rcptlist */
-
- FILE *datafd; /* data file descriptor */
-
- char dataname[NAMSIZ]; /* data file name */
-
- typedef int event;
-
- extern int death();
- extern int alarmtr();
-
- extern char *strcpy();
- extern char *index();
- extern char *rindex();
- extern char *strcpy(), *strcat();
-
- extern char hostdomain[];
- extern char hostname[];
- extern char arpanows[];
-
- #ifdef SIMPLELOG
- #include <sys/file.h>
- static char mailfrom[MAXSTR], rcptto[MAXSTR];
- #endif
-
- /*
- * This is the routine which processes incoming smtp commands from the
- * user. It goes to sleep awaiting network input. When a complete
- * command is received, the tcp receiver task awakens us to process it.
- * Currently only the commands listed in the command table are accepted.
- * This routine never returns.
- */
- /* ARGSUSED from */
- converse(fi, fo, from)
- FILE *fi, *fo;
- struct sockaddr_in *from;
- {
- char greeting[MAXSTR];
-
- (void) chdir("/tmp"); /* put temp files somewhere sensible */
- (void) signal(SIGALRM, TYPESIG alarmtr);
- (void) alarm(SHORTTIME); /* make sure we eventually go away */
- setdates ();
- (void) sprintf(greeting, "220 %s SMTP server ready at %s\n",
- hostdomain, arpanows);
- (void) tputs(greeting, fo);
- do_helo(fi, fo); /* wait for the hello */
- for (;;) { /* until QUIT */
- do_mail(fi, fo); /* wait for the mail command */
- while (do_rcpt(fi, fo)) /* do all the recipients */
- ;
- (void) alarm(LONGTIME);
- do_data(fi, fo); /* do the data */
- }
- }
-
- /*
- * Wait for the user to send the HELO command. Punt out if he sends
- * QUIT or RSET.
- */
- do_helo(fi, fo)
- FILE *fi, *fo;
- {
- char cmdbuf[MAXSTR];
- char greeting[MAXSTR];
-
- for (;;) { /* until HELO, QUIT, or RSET */
- buflen = tgets(cmdbuf, sizeof cmdbuf, fi); /* wait for command */
- switch (cmdparse(cmdbuf, buflen)) {
- case QUIT:
- case RSET:
- quit(fi, fo);
- case NOOP:
- (void) tputs("250 OK\n", fo);
- continue;
- case HELO:
- (void) sprintf(greeting, "250 %s Pleased to meet you\n", hostname);
- (void) tputs(greeting, fo);
- return;
- case NONE:
- bitch(cmdbuf, fo);
- continue;
- default:
- (void) tputs("503 Expecting HELO\n", fo);
- continue;
- }
- }
- }
-
- /*
- * Wait for the user to send the MAIL command. Punt out if he sends
- * QUIT or RSET.
- */
- do_mail(fi, fo)
- FILE *fi, *fo;
- {
- char cmdbuf[MAXSTR];
-
- for (;;) { /* until MAIL, QUIT, or RSET */
- buflen = tgets(cmdbuf, sizeof cmdbuf, fi); /* wait for command */
- switch (cmdparse(cmdbuf, buflen)) {
- case QUIT:
- case RSET:
- quit(fi, fo);
- case NOOP:
- (void) tputs("250 OK\n", fo);
- continue;
- case MAIL:
- #ifdef SIMPLELOG
- strcpy(mailfrom, cmdbuf);
- #endif
- (void) tputs("250 OK\n", fo);
- return;
- case NONE:
- bitch(cmdbuf, fo);
- continue;
- default:
- (void) tputs("503 Expecting MAIL\n", fo);
- continue;
- }
- }
- }
-
- /*
- * Wait for the user to send the RCPT command. Punt out if he sends
- * QUIT or RSET. Returns TRUE if a RCPT command was received, FALSE
- * if a DATA command was received.
- */
- do_rcpt(fi, fo)
- FILE *fi, *fo;
- {
- char cmdbuf[MAXSTR];
-
- for (;;) { /* until RCPT, DATA, QUIT, or RSET */
- buflen = tgets(cmdbuf, sizeof cmdbuf, fi); /* wait for command */
- switch (cmdparse(cmdbuf, buflen)) {
- case QUIT:
- case RSET:
- quit(fi, fo);
- case NOOP:
- (void) tputs("250 OK\n", fo);
- continue;
- case RCPT:
- #ifdef SIMPLELOG
- strcat(rcptto, cmdbuf);
- #endif
- if (!parse_rcpt(cmdbuf, buflen)) {
- (void) tputs("501 Syntax error in recipient name\n", fo);
- continue;
- }
- (void) tputs("250 OK\n", fo);
- return(TRUE);
- case DATA:
- if (*rcptlist == 0) {
- (void) tputs("503 Expecting RCPT\n", fo);
- continue;
- }
- if (!init_xfr()) { /* set up data file */
- (void) tputs("451 Can't initialize transfer\n", fo);
- death(E_CANTOPEN);
- }
- (void) tputs("354 Start mail input; end with <CRLF>.<CRLF>\n", fo);
- return(FALSE);
- case NONE:
- bitch(cmdbuf, fo);
- continue;
- default:
- (void) tputs("503 Expecting RCPT or DATA\n", fo);
- continue;
- }
- }
- }
-
- do_data(fi, fo)
- FILE *fi, *fo;
- {
- char cmd[MAXSTR];
- register char *buf = cmd;
- int sysret;
-
- setdates ();
- fprintf (datafd, "Received: by %s with SMTP; %s\n",
- hostdomain, arpanows);
- for (;;) {
- if (tgets(buf, sizeof cmd, fi) < 0)
- death(E_IOERR);
- if (*buf == '.') {
- buf++; /* hidden dot */
- if (*buf == '\n')
- break;
- }
- (void) fputs(buf, datafd);
- }
-
- (void) fclose(datafd);
-
- /* run mailer with rcptlist as args and message as input */
- /* TODO: check system status to see if OK */
- (void) sprintf(cmd, "%s %s <%s", sigmaild, rcptlist, dataname);
- sysret = system(cmd);
- if (sysret == 0)
- (void) tputs("250 OK\n", fo);
- else
- (void) tputs("554 Transaction failed\n", fo);
-
- #ifdef SIMPLELOG
- simplelog(abs(sysret));
- #endif
-
- /* shouldn't leave it around, but ... */
- if (sysret == 0)
- (void) unlink(dataname); /* remove temporaries */
-
- *dataname = *rcptlist = *rcptto = 0;
- rcptlast = 0;
- }
-
- /*
- * Create the data file for the transfer. Get unique
- * names and create the files.
- */
- init_xfr()
- {
- int dfd; /* file desc. for data file */
-
- (void) tmpnam(dataname);
-
- if ((dfd = creat(dataname, DATAMODE)) < 0)
- return FALSE;
- datafd = fdopen(dfd, "w"); /* make stdio descriptor */
- if (datafd == NULL)
- return FALSE;
-
-
- return TRUE;
- }
-
- /*
- * Give up on the transfer. Unlink the data file (if any),
- * close the tcp connection, and exit.
- */
- quit(fi, fo)
- FILE *fi, *fo;
- {
- char greeting[MAXSTR];
-
- (void) sprintf(greeting, "221 %s Terminating\n", hostname);
- (void) tputs(greeting, fo);
- (void) fclose(fi);
- (void) fclose(fo);
- exit(0);
- }
-
- /*
- * Parse the command part off the specified buffer. Return the index
- * of the command in the command table(or 0 if the command is not
- * recognized).
- * The commands and indices accepted are listed in the include file
- * "cmds.h".
- */
- cmdparse(buf, len)
- char *buf;
- int len;
- {
- register char *cmdp, *bufp; /* command, buffer ptrs. */
- register struct cmdtab *ct; /* cmd table ptr */
- register int i; /* index in cmd table */
- int clen; /* length of this command */
-
- for (ct = &cmdtab[1], i = 1; ct->c_name != NULL; ct++, i++) {
- clen = ct->c_len;
- if (len < clen) /* buffer shorter than command? */
- continue;
- /* case-insensitive matching of command names */
- for (cmdp = ct->c_name, bufp = buf;
- clen > 0 && *cmdp == toupper(*bufp);
- cmdp++, bufp++, clen--)
- ;
- if (clen == 0) { /* success */
- /* sendmail compatibility */
- if (i == ONEX || i == VERB)
- i = NOOP;
- return i;
- }
- }
- return 0;
- }
-
- static char *to; /* ptr. into request buffer */
-
- /*
- * Parse the recipient spec in the buffer. Start by stripping the
- * command off the front of the buffer. Then call canon() to convert
- * the recpient name into a format acceptable to the mailer daemon
- * (ie. the original multiple-at-sign format).
- * Returns TRUE if parsed successfully, FALSE otherwise.
- */
- /* ARGSUSED len */
- parse_rcpt(buf, len)
- char *buf; /* command buffer */
- int len; /* size of buffer string */
- {
- register char *from; /* ptr to recipient name */
- char *end;
-
- from = &buf[cmdtab[RCPT].c_len];
- while (*from == ' ' || *from == '\t')
- from++;
- if (*from == '<') {
- end = index(from++, '>');
- if (end == 0) {
- (void) printf("no > at end of string\n");
- return FALSE;
- }
- *end = 0;
- }
- if (rcptlast) {
- rcptlast += strlen(rcptlast);
- *rcptlast++ = ' ';
- } else
- rcptlast = rcptlist;
- /* NB: we use the canonical name even if `bad' */
- if (canon(from, rcptlast)) /* canonicalize */
- #ifdef DEBUG
- (void) printf("parsed ok: %s\n", rcptlast);
- else
- (void) printf("parsed bad: %s\n", rcptlast);
- #endif
- ;
- return TRUE;
- }
-
- /*
- * Canonicalize the smtp-style path pointed to by from into the buffer
- * pointed to by the external static variable to. The result will be
- * a string containing the multiple-at-sign form, as desired by the
- * mailer daemon. Also removes the '\' escape characters.
- * The procedure follwed is recursive: this routine is recursively
- * called for each "@host" in the from string.
- * Returns TRUE if successful, or FALSE if the format of the recipient
- * name is bad.
- */
- rcanon(from)
- register char *from; /* start of string to canonicalize */
- {
- register char *end; /* end of this part of path */
- register int escseen; /* escape character seen */
- int atseen; /* '@' seen in mailbox */
-
- escseen = atseen = FALSE;
- if (*from == '@') { /* host name; find end */
- for (end = from; *end != '\0'; end++) {
- if (escseen)
- escseen = FALSE;
- else if (*end == '\\') /* escape? */
- escseen = TRUE;
- else if (*end == ',' || *end == ':')
- break;
- }
- if (*end == '\0' || !rcanon(end+1)) { /* bad format? */
- #ifdef PICKY /*{*/
- (void) printf("no mailbox found\n");
- return FALSE;
- } else
- #else
- }
- #endif /* PICKY */
- {
- escseen = FALSE;
- for (*from = '%'; from < end; from++) { /* copy into to buffer */
- if (escseen)
- escseen = FALSE;
- else if (*from == '\\') {
- escseen = TRUE;
- continue;
- }
- *to++ = *from;
- }
- *to = '\0';
- return TRUE;
- }
- } else {
- for (; *from; from++) { /* copy mailbox */
- if (escseen)
- escseen = FALSE;
- else if (*from == '\\') {
- escseen = TRUE;
- continue;
- } else if (*from == '@') { /* end of username? */
- (void) printf("found @ in mailbox\n");
- *from = '%';
- atseen = TRUE;
- }
- *to++ = *from;
- }
- *to = 0;
- return atseen;
- }
- }
-
- /* Time to live elapsed or io error. */
- death(weapon)
- {
- #ifdef SIMPLELOG
- simplelog(weapon);
- #endif
- (void) printf("Time to die.\n");
- /*(void) unlink(dataname);*/
- exit(1);
- }
-
- alarmtr()
- {
- death(E_TEMPFAIL);
- }
-
- canon(in, out)
- char *in, *out;
- {
- char *at;
-
- to = out;
- if (funnychars(in) || !rcanon(in) || (at = rindex(out, '%')) == 0)
- return(FALSE);
- *at = '@';
- return TRUE;
- }
-
- funnychars(str)
- register char *str;
- {
-
- for (;;)
- switch(*str++) {
- case '^':
- case '&':
- case '>':
- case '<':
- case '`':
- case '|':
- case ';':
- case '\'':
- return TRUE;
-
- case 0:
- return FALSE;
- }
- }
-
- #ifdef SIMPLELOG
- simplelog(retcode)
- {
- char buf[1024], *bptr, *status;
- int fd;
- time_t t;
- extern char *ctime();
- extern time_t time();
-
- t = time(&t);
- switch (retcode) {
- case E_CANTOPEN:
- status = "OPEN FAILED";
- break;
- case E_IOERR:
- status = "IO ERROR";
- break;
- case E_TEMPFAIL:
- status = "TIMED OUT";
- break;
- case 0:
- status = "OK";
- break;
- default:
- status = "DELIVERY FAILURE";
- break;
- }
- if (*mailfrom == 0)
- strcpy(mailfrom, "UNKNOWN");
- if (*rcptto == 0)
- strcpy(rcptto, "UNKNOWN");
- (void) sprintf(buf, "%s %s %s %s", mailfrom, rcptto, status, ctime(&t));
- for (bptr = buf; *bptr; bptr++)
- if (*bptr == '\n' || *bptr == '\r')
- *bptr = ' ';
- strcat(bptr, "\n");
- if ((fd = open("/tmp/smtpd.log", O_WRONLY|O_APPEND)) >= 0) {
- (void) write(fd, buf, strlen(buf));
- (void) close(fd);
- }
- }
- #endif
-
- bitch(buf, fo)
- char *buf;
- FILE *fo;
- {
- char gripe[MAXSTR], *nlptr;
-
- if ((nlptr = index(buf, '\n')) != 0)
- *nlptr = 0;
- (void) sprintf(gripe, "502 %s ... Not recognized\n", buf);
- (void) tputs(gripe, fo);
- }
-