jv@mh.nl.UUCP (Johan Vromans) (05/16/88)
comp.sources.misc: Volume 3, Issue 19 Submitted-By: "Johan Vromans" <jv@mh.nl.UUCP> Archive-Name: sm-smtp Lots of people asked me for this, so here it is ... This is a merger of Ian's standalone talker and MIT's talker. Peter Honeyman hacked on it to make it work correctly on the Arpanet. Adapted by Johan Vromans <jv@mh.nl>, May 1988 - simplified code - adapted to System V (Most notably: HP-UX) - added better host/domain name handling & setup - added time-routine (from smail) - added "config.h" for local settings, and moved common includes and portability code to "smtp.h" - produces a "smtpd" to be used under inetd, "sa-smtpd" (standalone) and "smtp" interface program. #---------------- cut here ---------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # Makefile # README # cmds.h # config.h # converse.c # dconverse.c # misc.c # miscerrs.h # mx.c # netio.c # smtp.8 # smtp.c # smtp.h # smtpd.c # sysexits.h # syslog.h # MANIFEST # This archive created: Mon May 16 17:16:19 1988 export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'Makefile'" '(800 characters)' if test -f 'Makefile' then echo shar: "will not over-write existing file 'Makefile'" else sed 's/^X//' << \SHAR_EOF > 'Makefile' X#!/bin/make X XCFLAGS = -O XLIBS = -lBSD -lbsdipc -lsyslog # HP-UX X#LIBS = # BSD X Xall: smtp smtpd X XSTD = netio.o misc.o X Xsmtp: smtp.o converse.o $(STD) X $(CC) $(CFLAGS) -o smtp smtp.o converse.o $(STD) $(LIBS) X Xsmtpd: smtpd.o dconverse.o $(STD) X $(CC) $(CFLAGS) -o smtpd smtpd.o dconverse.o $(STD) $(LIBS) X Xsa-smtpd: sa-smtpd.o dconverse.o $(STD) X $(CC) $(CFLAGS) -o sa-smtpd sa-smtpd.o dconverse.o $(STD) $(LIBS) X Xsmptd.o sa-smtpd.o smtp.o $(STD) converse.o: smtp.h X Xsmtp.h: config.h miscerrs.h X -touch smtp.h X Xsmtpd.o: smtpd.c X $(CC) -c $(CFLAGS) -DINETD smtpd.c X Xsa-smtpd.o: smtpd.c X $(CC) -c $(CFLAGS) -UINETD smtpd.c X mv smtpd.o sa-smtpd.o X Xclean: X rm -f smtp smtpd sa-smtpd *.o X Xlint: X lint smtp.c netio.c converse.c X lint smtpd.c netio.c dconverse.c X lint -DINETD smtpd.c netio.c dconverse.c X SHAR_EOF if test 800 -ne "`wc -c < 'Makefile'`" then echo shar: "error transmitting 'Makefile'" '(should have been 800 characters)' fi fi echo shar: "extracting 'README'" '(2322 characters)' if test -f 'README' then echo shar: "will not over-write existing file 'README'" else sed 's/^X//' << \SHAR_EOF > 'README' XThis is a merger of Ian's standalone talker and MIT's talker. XPeter Honeyman hacked on it to make it work correctly on the Arpanet. X XAdapted by Johan Vromans <jv@mh.nl>, May 1988 X X - simplified code X X - adapted to System V (Most notably: HP-UX) X X - added better host/domain name handling & setup X X - added time-routine (from smail) X X - added "config.h" for local settings, and moved common includes and X portability code to "smtp.h" X X - produces a "smtpd" to be used under inetd, "sa-smtpd" (standalone) and X "smtp" interface program. X XDisclaimers: it works for me. I didn't test "sa-smtpd", nor tried it under XBerkeley Unix. However, the code originated from BSD, and I tried to keep Xsystem dependent things intact. XI did not look at the "mx" program which is also included. X XHow I use it X------------ XI use a mail system based on smail 2.5. X XMy user agents can be Elm, mail or mailx, and pass the message to smail for Xaliasing and routing. Smail hands the message to either a localmail program X(for local delivery: user mailboxes, programs or files) or a remotemail Xprogram (for remote delivery: uucp or smtp). X XIn the routing database, all hosts I can connect to are considered X"UUCP"-hosts, and delivery is passed to a shell script. In this script, Xhosts are looked-up in the file /etc/hosts.smtp (with the names of TCP/IP Xhosts which speak smtp) and - if found - delivery is passed to the smtp Xprogram. X XOtherwise delivery is passed to uux as usual. X XOn the input side, smtp mail is caught by the smtpd program, and passed to Xsmail for further processing. X XWhy I don't use sendmail X------------------------ XUsing sendmail has a few drawbacks: X X - it is hard to configure and difficult to maintain X X - lots of the functionality of sendmail is also in smail. Who is aliasing, X who is routing, who is bouncing? X X - I cannot prevent sendmail from rewriting message headers X X - when something goes wrong, sendmail will try to do sensible things X which bypass the rest of the mail system X XSmail does everything I want, except for the SMTP delivery. Well - the Xprograms included do just that. X XAnd last but not least: it is more easy to maintain a few small, well Xdocumented and easy to understand programs than one big hard to understand/ Xconfigure/maintain program. X XMay 1988, Johan Vromans, Multihouse Research SHAR_EOF if test 2322 -ne "`wc -c < 'README'`" then echo shar: "error transmitting 'README'" '(should have been 2322 characters)' fi fi echo shar: "extracting 'cmds.h'" '(1024 characters)' if test -f 'cmds.h' then echo shar: "will not over-write existing file 'cmds.h'" else sed 's/^X//' << \SHAR_EOF > 'cmds.h' X#ifndef lint Xstatic char *cmds_sccsid = "@(#)cmds.h 1.5 87/04/06"; X#endif lint X/* cmds.h */ X X/* Copyright 1984 by the Massachusetts Institute of Technology */ X/* See permission and disclaimer notice in file "notice.h" */ X X/* EMACS_MODES: c !fill */ X X/* X * smtp command strings and associated codes. Note that the command code X * MUST be equal to the index of the command in the table. X */ X X Xstruct cmdtab { X char *c_name; /* command name */ X int c_len; /* command length */ X} cmdtab[] = { X#define NONE 0 /* no such command */ X { "", 0, }, X#define HELO 1 X { "HELO", 4, }, X#define MAIL 2 X { "MAIL FROM:", 10 }, X#define RCPT 3 X { "RCPT TO:", 8, }, X#define DATA 4 X { "DATA", 4, }, X#define QUIT 5 X { "QUIT", 4, }, X#define RSET 6 X { "RSET", 4, }, X#define NOOP 7 X { "NOOP", 4, }, X#define VRFY 8 X { "VRFY", 4, }, X/* sendmail compatibility */ X#define ONEX 9 X { "ONEX", 4, }, X#define VERB 10 X { "VERB", 4, }, X X { 0, 0, } /* end of table marker */ X}; X X#define toupper(c) (islower(c) ? ((c) - ('a' - 'A')) : (c)) SHAR_EOF if test 1024 -ne "`wc -c < 'cmds.h'`" then echo shar: "error transmitting 'cmds.h'" '(should have been 1024 characters)' fi fi echo shar: "extracting 'config.h'" '(1280 characters)' if test -f 'config.h' then echo shar: "will not over-write existing file 'config.h'" else sed 's/^X//' << \SHAR_EOF > 'config.h' X/* config.h for smtp package */ X X/* X * define either BSD or SYSV. X */ X#define SYSV /* SystemV with TCP/IP */ X/* # define BSD /* Berkeley 4.[23] */ X X/* X * If set, HOSTDOMAIN overrides HOSTNAME and MYDOM. X */ X/* #define HOSTDOMAIN "mhres.mh.nl" */ X X/* X * If not set, HOSTNAME will be fetched using uname (SYSV) or gethostname (BSD) X */ X/* #define HOSTNAME "mhres" */ X X/* X * If set, MYDOM will be appended to the host name. X */ X#define MYDOM ".mh.nl" X X/* X * Define server to use. Defaults to "smtp". X */ X/* #define SERVNAME "tsmtp" */ X X/* X * Define SYSLOG when the syslog rotuine can be used. For SYSV, it will X * use a PD syslog package. X */ X#define SYSLOG X X/* X * Define this if you want a log of mail transactions. See the code in X * smtpd.c. X */ X#define SIMPLELOG X X/* X * The mailer used to send the mail. X */ X#define MAILER "/bin/smail" X X/* X * Define HOOTING for printout of transaction (needed for "Transaction X * of session") X * Used by "netio" in "smtp" X */ X#define HOOTING X X/* X * Define NOSYSEXITS iy you don't have <sysexits.h> (or "sysexits.h" on SYSV. X */ X X/* define NOSYSEXITS */ X X/* X * Define this if your signal(2) takes a void function as its second arg. X * Otherwise leave it empty X */ X X/* define TYPESIG ((void)(*)()) */ X#ifndef TYPESIG X# define TYPESIG X#endif X SHAR_EOF if test 1280 -ne "`wc -c < 'config.h'`" then echo shar: "error transmitting 'config.h'" '(should have been 1280 characters)' fi fi echo shar: "extracting 'converse.c'" '(4950 characters)' if test -f 'converse.c' then echo shar: "will not over-write existing file 'converse.c'" else sed 's/^X//' << \SHAR_EOF > 'converse.c' X/* X * Do the necessary commands for a smtp transfer. Start by waiting for the X * connection to open, then send HELO, MAIL, RCPT, and DATA. Check the X * reply codes and give up if needed. X * X * This code modified from the MIT UNIX TCP implementation: X * Copyright 1984 Massachusetts Institute of Technology X * X * Permission to use, copy, modify, and distribute this file X * for any purpose and without fee is hereby granted, provided X * that this copyright and permission notice appear on all copies X * and supporting documentation, the name of M.I.T. not be used X * in advertising or publicity pertaining to distribution of the X * program without specific prior permission, and notice be given X * in supporting documentation that copying and distribution is X * by permission of M.I.T. M.I.T. makes no representations about X * the suitability of this software for any purpose. It is provided X * "as is" without express or implied warranty. X */ X X#include "smtp.h" X#include <signal.h> X X#define MAXTIME (60 * 5) /* way too long - die */ X Xint success, termcode; X Xint gethostname(); Xextern int death(); Xchar *strcat(), *strcpy(); Xextern char hostname[], hostdomain[]; X Xconverse(from, rcpt, sfi, sfo, mlfd) Xchar *from; /* from address */ Xchar *rcpt; /* to address */ XFILE *sfi; /* smtp input */ XFILE *sfo; /* smtp output */ XFILE *mlfd; /* mail file descriptor */ X{ X extern expect(); X extern char *sendhost; X char buf[MAXSTR]; X char host[64]; X X (void) signal(SIGALRM, TYPESIG death); X (void) alarm(MAXTIME); /* make sure we eventually go away */ X X expect(220, sfi, sfo); /* expect a service ready msg */ X/* (void) gethostname(host, sizeof host); */ X X if (sendhost == NULL) X (void) sprintf(buf, "HELO %s.%s\n", hostdomain); X else X (void) sprintf(buf, "HELO %s\n", sendhost); X tputs(buf, sfo); X expect(250, sfi, sfo); /* expect an OK */ X X (void) strcpy(buf, "MAIL FROM:<"); X (void) strcat(buf, from); X (void) strcat(buf, ">\n"); X tputs(buf, sfo); X expect(250, sfi, sfo); /* expect OK */ X X (void) strcpy(buf, "RCPT TO:<"); X (void) strcat(buf, rcpt); X (void) strcat(buf, ">\n"); X tputs(buf, sfo); X expect(250, sfi, sfo); /* expect OK */ X X tputs("DATA\n", sfo); X expect(354, sfi, sfo); X do_data(mlfd, sfo); X expect(250, sfi, sfo); /* hope data is OK */ X success = TRUE; X X tputs("QUIT\n", sfo); X /*expect(221, sfi, sfo);*/ /* who cares? */ X} X X X/* X * Send the data from the specified mail file out on the current smtp X * connection. Do the appropriate netascii conversion and starting '.' X * padding. Send the <CRLF>.<CRLF> at completion. X */ Xdo_data(fd, sfo) Xregister FILE *fd; /* mail file descriptor */ XFILE *sfo; /* smtp files */ X{ X register int c; /* current character */ X int nlseen = FALSE; /* newline */ X extern int debug; X X if (debug) X (void) printf("in do_data\n"); X while ((c = getc(fd)) != EOF) { X if (nlseen) { X nlseen = FALSE; X if (c == '.') X (void) putc('.', sfo); X } X if (c == '\n') { X (void) putc('\r', sfo); X nlseen = TRUE; X } X (void) putc(c, sfo); X#ifdef what_the_fuck_is_all_this_about X if (c == '\r') X (void) putc('\0', sfo); X#endif X } X if (!nlseen) { X (void) putc('\r', sfo); X (void) putc('\n', sfo); X } X#ifdef this_is_bullshit_too X (void) putc('\n', sfo); /* TODO: why is this line needed? */ X#endif X (void) putc('.', sfo); X (void) putc('\r', sfo); X (void) putc('\n', sfo); X (void) fflush(sfo); X if (ferror(sfo)) { X perror("write error in smtp"); X bomb(E_IOERR); X } X if (debug) X (void) printf("leaving do_data\n"); X} X X X/* X * Expect a reply message with the specified code. If the specified code X * is received return TRUE; otherwise print the error message out on the X * standard output and give up. Note that the reply can be a multiline X * message. X */ Xexpect(code, sfi, sfo) Xint code; XFILE *sfi, *sfo; X{ X int retcd; X char cmdbuf[MAXSTR], termbuf[MAXSTR]; X extern int debug; X X if (debug) X (void) fprintf(stderr, "expect %d ", code); X for (;;) { /* get whole reply */ X if (tgets(cmdbuf, sizeof cmdbuf, sfi) > 0) { /* get input line */ X if (cmdbuf[3] == '-') /* continuation line? */ X continue; X /* no, last line */ X if (sscanf(cmdbuf, "%d", &retcd) !=1 ){ X (void) fprintf(stderr, X "non-numeric command reply!\n"); X bomb(E_IOERR); X } X if (retcd == code) { X if (debug) X (void) fprintf(stderr," got it\n"); X return; X } X else { X if (debug) X (void) fprintf(stderr, X " FAIL (got %d)\n", retcd); X /* return the error line */ X (void) strcpy(termbuf, cmdbuf); X tputs ("QUIT\n", sfo); X break; X } X } X else if (success) X (void) strcpy(termbuf, "250 OK\n"); X else { X (void) perror("smtp"); X bomb(451); X } X } X termcode = !success; /* error return */ X if (debug) X (void) fprintf(stderr, " FALLOUT\n"); X bomb(retcd); /* map smtp errors to mailsys errors */ X} X X/* Maximum time to live elapsed. Die right now. */ Xdeath() X{ X (void) fprintf(stderr, "Max transfer length timeout.\n"); X (void) exit(1); X} SHAR_EOF if test 4950 -ne "`wc -c < 'converse.c'`" then echo shar: "error transmitting 'converse.c'" '(should have been 4950 characters)' fi fi echo shar: "extracting 'dconverse.c'" '(12608 characters)' if test -f 'dconverse.c' then echo shar: "will not over-write existing file 'dconverse.c'" else sed 's/^X//' << \SHAR_EOF > 'dconverse.c' X#ifndef lint Xstatic char *sccsid = "@(#)converse.c 1.8 87/05/14"; X#endif lint X/* Copyright 1984 Massachusetts Institute of Technology X XPermission to use, copy, modify, and distribute this program Xfor any purpose and without fee is hereby granted, provided Xthat this copyright and permission notice appear on all copies Xand supporting documentation, the name of M.I.T. not be used Xin advertising or publicity pertaining to distribution of the Xprogram without specific prior permission, and notice be given Xin supporting documentation that copying and distribution is Xby permission of M.I.T. M.I.T. makes no representations about Xthe suitability of this software for any purpose. It is pro- Xvided "as is" without express or implied warranty. */ X X/* X * smtpd - World's most trivial SMTP server. Only accepts the MAIL, FROM, X * RCPT, and DATA commands. Generates a date file for the mail X * daemon and kicks the mail daemon off. X */ X X#include "smtp.h" X X#ifdef BSD X#include <sgtty.h> X#endif X/* #include <ioctl.h> */ X#include <signal.h> X#include <sys/uio.h> X#include <sys/socket.h> X#include <netinet/in.h> X X#include "cmds.h" X X/* tunable constants */ X X#define SECONDS 1 X#define MINUTES 60 X#define HOURS (60 * MINUTES) X X#define SHORTTIME (5 * MINUTES) /* enough time for easy stuff */ X#define LONGTIME (2 * HOURS) /* max time, DATA to `.' */ X X#define DATAMODE 0660 /* mode for data file */ X X#ifdef MAILER Xchar *sigmaild = MAILER; X#else Xchar *sigmaild = "/bin/rmail"; X#endif X Xtypedef long in_name; /* internet host address */ X Xint buflen; /* size of string in cmd buffer */ X Xstatic char rcptlist[MAXSTR]; /* recipient list */ Xstatic char *rcptlast; /* end of rcptlist */ X XFILE *datafd; /* data file descriptor */ X Xchar dataname[NAMSIZ]; /* data file name */ X Xtypedef int event; X Xextern int death(); Xextern int alarmtr(); X Xextern char *strcpy(); Xextern char *index(); Xextern char *rindex(); Xextern char *strcpy(), *strcat(); X Xextern char hostdomain[]; Xextern char hostname[]; Xextern char arpanows[]; X X#ifdef SIMPLELOG X#include <sys/file.h> Xstatic char mailfrom[MAXSTR], rcptto[MAXSTR]; X#endif X X/* X * This is the routine which processes incoming smtp commands from the X * user. It goes to sleep awaiting network input. When a complete X * command is received, the tcp receiver task awakens us to process it. X * Currently only the commands listed in the command table are accepted. X * This routine never returns. X */ X/* ARGSUSED from */ Xconverse(fi, fo, from) XFILE *fi, *fo; Xstruct sockaddr_in *from; X{ X char greeting[MAXSTR]; X X (void) chdir("/tmp"); /* put temp files somewhere sensible */ X (void) signal(SIGALRM, TYPESIG alarmtr); X (void) alarm(SHORTTIME); /* make sure we eventually go away */ X setdates (); X (void) sprintf(greeting, "220 %s SMTP server ready at %s\n", X hostdomain, arpanows); X (void) tputs(greeting, fo); X do_helo(fi, fo); /* wait for the hello */ X for (;;) { /* until QUIT */ X do_mail(fi, fo); /* wait for the mail command */ X while (do_rcpt(fi, fo)) /* do all the recipients */ X ; X (void) alarm(LONGTIME); X do_data(fi, fo); /* do the data */ X } X} X X/* X * Wait for the user to send the HELO command. Punt out if he sends X * QUIT or RSET. X */ Xdo_helo(fi, fo) XFILE *fi, *fo; X{ X char cmdbuf[MAXSTR]; X char greeting[MAXSTR]; X X for (;;) { /* until HELO, QUIT, or RSET */ X buflen = tgets(cmdbuf, sizeof cmdbuf, fi); /* wait for command */ X switch (cmdparse(cmdbuf, buflen)) { X case QUIT: X case RSET: X quit(fi, fo); X case NOOP: X (void) tputs("250 OK\n", fo); X continue; X case HELO: X (void) sprintf(greeting, "250 %s Pleased to meet you\n", hostname); X (void) tputs(greeting, fo); X return; X case NONE: X bitch(cmdbuf, fo); X continue; X default: X (void) tputs("503 Expecting HELO\n", fo); X continue; X } X } X} X X/* X * Wait for the user to send the MAIL command. Punt out if he sends X * QUIT or RSET. X */ Xdo_mail(fi, fo) XFILE *fi, *fo; X{ X char cmdbuf[MAXSTR]; X X for (;;) { /* until MAIL, QUIT, or RSET */ X buflen = tgets(cmdbuf, sizeof cmdbuf, fi); /* wait for command */ X switch (cmdparse(cmdbuf, buflen)) { X case QUIT: X case RSET: X quit(fi, fo); X case NOOP: X (void) tputs("250 OK\n", fo); X continue; X case MAIL: X#ifdef SIMPLELOG X strcpy(mailfrom, cmdbuf); X#endif X (void) tputs("250 OK\n", fo); X return; X case NONE: X bitch(cmdbuf, fo); X continue; X default: X (void) tputs("503 Expecting MAIL\n", fo); X continue; X } X } X} X X/* X * Wait for the user to send the RCPT command. Punt out if he sends X * QUIT or RSET. Returns TRUE if a RCPT command was received, FALSE X * if a DATA command was received. X */ Xdo_rcpt(fi, fo) XFILE *fi, *fo; X{ X char cmdbuf[MAXSTR]; X X for (;;) { /* until RCPT, DATA, QUIT, or RSET */ X buflen = tgets(cmdbuf, sizeof cmdbuf, fi); /* wait for command */ X switch (cmdparse(cmdbuf, buflen)) { X case QUIT: X case RSET: X quit(fi, fo); X case NOOP: X (void) tputs("250 OK\n", fo); X continue; X case RCPT: X#ifdef SIMPLELOG X strcat(rcptto, cmdbuf); X#endif X if (!parse_rcpt(cmdbuf, buflen)) { X (void) tputs("501 Syntax error in recipient name\n", fo); X continue; X } X (void) tputs("250 OK\n", fo); X return(TRUE); X case DATA: X if (*rcptlist == 0) { X (void) tputs("503 Expecting RCPT\n", fo); X continue; X } X if (!init_xfr()) { /* set up data file */ X (void) tputs("451 Can't initialize transfer\n", fo); X death(E_CANTOPEN); X } X (void) tputs("354 Start mail input; end with <CRLF>.<CRLF>\n", fo); X return(FALSE); X case NONE: X bitch(cmdbuf, fo); X continue; X default: X (void) tputs("503 Expecting RCPT or DATA\n", fo); X continue; X } X } X} X Xdo_data(fi, fo) XFILE *fi, *fo; X{ X char cmd[MAXSTR]; X register char *buf = cmd; X int sysret; X X setdates (); X fprintf (datafd, "Received: by %s with SMTP; %s\n", X hostdomain, arpanows); X for (;;) { X if (tgets(buf, sizeof cmd, fi) < 0) X death(E_IOERR); X if (*buf == '.') { X buf++; /* hidden dot */ X if (*buf == '\n') X break; X } X (void) fputs(buf, datafd); X } X X (void) fclose(datafd); X X /* run mailer with rcptlist as args and message as input */ X /* TODO: check system status to see if OK */ X (void) sprintf(cmd, "%s %s <%s", sigmaild, rcptlist, dataname); X sysret = system(cmd); X if (sysret == 0) X (void) tputs("250 OK\n", fo); X else X (void) tputs("554 Transaction failed\n", fo); X X#ifdef SIMPLELOG X simplelog(abs(sysret)); X#endif X X /* shouldn't leave it around, but ... */ X if (sysret == 0) X (void) unlink(dataname); /* remove temporaries */ X X *dataname = *rcptlist = *rcptto = 0; X rcptlast = 0; X} X X/* X * Create the data file for the transfer. Get unique X * names and create the files. X */ Xinit_xfr() X{ X int dfd; /* file desc. for data file */ X X (void) tmpnam(dataname); X X if ((dfd = creat(dataname, DATAMODE)) < 0) X return FALSE; X datafd = fdopen(dfd, "w"); /* make stdio descriptor */ X if (datafd == NULL) X return FALSE; X X X return TRUE; X} X X/* X * Give up on the transfer. Unlink the data file (if any), X * close the tcp connection, and exit. X */ Xquit(fi, fo) XFILE *fi, *fo; X{ X char greeting[MAXSTR]; X X (void) sprintf(greeting, "221 %s Terminating\n", hostname); X (void) tputs(greeting, fo); X (void) fclose(fi); X (void) fclose(fo); X exit(0); X} X X/* X * Parse the command part off the specified buffer. Return the index X * of the command in the command table(or 0 if the command is not X * recognized). X * The commands and indices accepted are listed in the include file X * "cmds.h". X */ Xcmdparse(buf, len) Xchar *buf; Xint len; X{ X register char *cmdp, *bufp; /* command, buffer ptrs. */ X register struct cmdtab *ct; /* cmd table ptr */ X register int i; /* index in cmd table */ X int clen; /* length of this command */ X X for (ct = &cmdtab[1], i = 1; ct->c_name != NULL; ct++, i++) { X clen = ct->c_len; X if (len < clen) /* buffer shorter than command? */ X continue; X /* case-insensitive matching of command names */ X for (cmdp = ct->c_name, bufp = buf; X clen > 0 && *cmdp == toupper(*bufp); X cmdp++, bufp++, clen--) X ; X if (clen == 0) { /* success */ X /* sendmail compatibility */ X if (i == ONEX || i == VERB) X i = NOOP; X return i; X } X } X return 0; X} X Xstatic char *to; /* ptr. into request buffer */ X X/* X * Parse the recipient spec in the buffer. Start by stripping the X * command off the front of the buffer. Then call canon() to convert X * the recpient name into a format acceptable to the mailer daemon X * (ie. the original multiple-at-sign format). X * Returns TRUE if parsed successfully, FALSE otherwise. X */ X/* ARGSUSED len */ Xparse_rcpt(buf, len) Xchar *buf; /* command buffer */ Xint len; /* size of buffer string */ X{ X register char *from; /* ptr to recipient name */ X char *end; X X from = &buf[cmdtab[RCPT].c_len]; X while (*from == ' ' || *from == '\t') X from++; X if (*from == '<') { X end = index(from++, '>'); X if (end == 0) { X (void) printf("no > at end of string\n"); X return FALSE; X } X *end = 0; X } X if (rcptlast) { X rcptlast += strlen(rcptlast); X *rcptlast++ = ' '; X } else X rcptlast = rcptlist; X /* NB: we use the canonical name even if `bad' */ X if (canon(from, rcptlast)) /* canonicalize */ X#ifdef DEBUG X (void) printf("parsed ok: %s\n", rcptlast); X else X (void) printf("parsed bad: %s\n", rcptlast); X#endif X ; X return TRUE; X} X X/* X * Canonicalize the smtp-style path pointed to by from into the buffer X * pointed to by the external static variable to. The result will be X * a string containing the multiple-at-sign form, as desired by the X * mailer daemon. Also removes the '\' escape characters. X * The procedure follwed is recursive: this routine is recursively X * called for each "@host" in the from string. X * Returns TRUE if successful, or FALSE if the format of the recipient X * name is bad. X */ Xrcanon(from) Xregister char *from; /* start of string to canonicalize */ X{ X register char *end; /* end of this part of path */ X register int escseen; /* escape character seen */ X int atseen; /* '@' seen in mailbox */ X X escseen = atseen = FALSE; X if (*from == '@') { /* host name; find end */ X for (end = from; *end != '\0'; end++) { X if (escseen) X escseen = FALSE; X else if (*end == '\\') /* escape? */ X escseen = TRUE; X else if (*end == ',' || *end == ':') X break; X } X if (*end == '\0' || !rcanon(end+1)) { /* bad format? */ X#ifdef PICKY /*{*/ X (void) printf("no mailbox found\n"); X return FALSE; X } else X#else X } X#endif /* PICKY */ X { X escseen = FALSE; X for (*from = '%'; from < end; from++) { /* copy into to buffer */ X if (escseen) X escseen = FALSE; X else if (*from == '\\') { X escseen = TRUE; X continue; X } X *to++ = *from; X } X *to = '\0'; X return TRUE; X } X } else { X for (; *from; from++) { /* copy mailbox */ X if (escseen) X escseen = FALSE; X else if (*from == '\\') { X escseen = TRUE; X continue; X } else if (*from == '@') { /* end of username? */ X (void) printf("found @ in mailbox\n"); X *from = '%'; X atseen = TRUE; X } X *to++ = *from; X } X *to = 0; X return atseen; X } X} X X/* Time to live elapsed or io error. */ Xdeath(weapon) X{ X#ifdef SIMPLELOG X simplelog(weapon); X#endif X (void) printf("Time to die.\n"); X /*(void) unlink(dataname);*/ X exit(1); X} X Xalarmtr() X{ X death(E_TEMPFAIL); X} X Xcanon(in, out) Xchar *in, *out; X{ X char *at; X X to = out; X if (funnychars(in) || !rcanon(in) || (at = rindex(out, '%')) == 0) X return(FALSE); X *at = '@'; X return TRUE; X} X Xfunnychars(str) Xregister char *str; X{ X X for (;;) X switch(*str++) { X case '^': X case '&': X case '>': X case '<': X case '`': X case '|': X case ';': X case '\'': X return TRUE; X X case 0: X return FALSE; X } X} X X#ifdef SIMPLELOG Xsimplelog(retcode) X{ X char buf[1024], *bptr, *status; X int fd; X time_t t; X extern char *ctime(); X extern time_t time(); X X t = time(&t); X switch (retcode) { X case E_CANTOPEN: X status = "OPEN FAILED"; X break; X case E_IOERR: X status = "IO ERROR"; X break; X case E_TEMPFAIL: X status = "TIMED OUT"; X break; X case 0: X status = "OK"; X break; X default: X status = "DELIVERY FAILURE"; X break; X } X if (*mailfrom == 0) X strcpy(mailfrom, "UNKNOWN"); X if (*rcptto == 0) X strcpy(rcptto, "UNKNOWN"); X (void) sprintf(buf, "%s %s %s %s", mailfrom, rcptto, status, ctime(&t)); X for (bptr = buf; *bptr; bptr++) X if (*bptr == '\n' || *bptr == '\r') X *bptr = ' '; X strcat(bptr, "\n"); X if ((fd = open("/tmp/smtpd.log", O_WRONLY|O_APPEND)) >= 0) { X (void) write(fd, buf, strlen(buf)); X (void) close(fd); X } X} X#endif X Xbitch(buf, fo) Xchar *buf; XFILE *fo; X{ X char gripe[MAXSTR], *nlptr; X X if ((nlptr = index(buf, '\n')) != 0) X *nlptr = 0; X (void) sprintf(gripe, "502 %s ... Not recognized\n", buf); X (void) tputs(gripe, fo); X} SHAR_EOF if test 12608 -ne "`wc -c < 'dconverse.c'`" then echo shar: "error transmitting 'dconverse.c'" '(should have been 12608 characters)' fi fi echo shar: "extracting 'misc.c'" '(4018 characters)' if test -f 'misc.c' then echo shar: "will not over-write existing file 'misc.c'" else sed 's/^X//' << \SHAR_EOF > 'misc.c' X X/* X** Miscellaneous support functions for smtp (borrowed from smail) X*/ X X# include "smtp.h" X#ifdef BSD X# include <sys/timeb.h> X#endif X#ifdef SYSV X# include <sys/utsname.h> X#endif X Xchar hostdomain[256]; Xchar hostname[256]; X Xextern struct tm *localtime(); X Xstruct tm *gmt, *loc; /* GMT and local time structure */ Xtime_t now; /* current system time */ Xchar nows[50]; /* time in ctime format */ Xchar arpanows[50]; /* time in arpa format */ X Xsetdates() X{ X time_t time(); X struct tm *gmtime(); X char *ctime(), *arpadate(); X X (void) time(&now); X (void) strcpy(nows, ctime(&now)); X gmt = gmtime(&now); X loc = localtime(&now); X (void) strcpy(arpanows, arpadate(nows)); X} X X/* X** Note: This routine was taken from sendmail X** X** ARPADATE -- Create date in ARPANET format X** X** Parameters: X** ud -- unix style date string. if NULL, one is created. X** X** Returns: X** pointer to an ARPANET date field X** X** Side Effects: X** none X** X** WARNING: X** date is stored in a local buffer -- subsequent X** calls will overwrite. X** X** Bugs: X** Timezone is computed from local time, rather than X** from whereever (and whenever) the message was sent. X** To do better is very hard. X** X** Some sites are now inserting the timezone into the X** local date. This routine should figure out what X** the format is and work appropriately. X*/ X Xchar * Xarpadate(ud) X register char *ud; X{ X register char *p; X register char *q; X static char b[40]; X extern char *ctime(); X register int i; X#ifndef BSD X extern char *tzname[]; X extern long timezone; X char dspace[40]; X time_t t, time(); X int dst; /* dst active */ X long tz; X#else X /* V7 and 4BSD */ X struct timeb t; X extern struct timeb *ftime(); X extern char *timezone(); X#endif X X /* X ** Get current time. X ** This will be used if a null argument is passed and X ** to resolve the timezone. X */ X X#ifndef BSD X (void) time(&t); X if (ud == NULL) X ud = ctime(&t); X#else X /* V7 or 4BSD */ X ftime(&t); X if (ud == NULL) X ud = ctime(&t.time); X#endif X X /* X ** Crack the UNIX date line in a singularly unoriginal way. X */ X X q = b; X X p = &ud[0]; /* Mon */ X *q++ = *p++; X *q++ = *p++; X *q++ = *p++; X *q++ = ','; X *q++ = ' '; X X p = &ud[8]; /* 16 */ X if (*p == ' ') X p++; X else X *q++ = *p++; X *q++ = *p++; X *q++ = ' '; X X p = &ud[4]; /* Sep */ X *q++ = *p++; X *q++ = *p++; X *q++ = *p++; X *q++ = ' '; X X p = &ud[22]; /* 1979 */ X *q++ = *p++; X *q++ = *p++; X *q++ = ' '; X X p = &ud[11]; /* 01:03:52 */ X for (i = 8; i > 0; i--) X *q++ = *p++; X X /* -PST or -PDT */ X#ifndef BSD X dst = localtime(&t)->tm_isdst; X tz = timezone - (dst ? 3600 : 0); X p = tzname[dst]; X#else X p = timezone(t.timezone, localtime(&t.time)->tm_isdst); X if (p[3] != '\0') X { X /* hours from GMT */ X p += 3; X *q++ = *p++; X if (p[1] == ':') X *q++ = '0'; X else X *q++ = *p++; X *q++ = *p++; X p++; /* skip ``:'' */ X *q++ = *p++; X *q++ = *p++; X } X else X#endif X { X *q++ = ' '; X while (*p) *q++ = *p++; X } X#ifndef BSD X if (tz != 0) { X (void) sprintf (q, " (%c%02d%02d)", X ((tz > 0) ? '-' : '+'), X abs(tz/3600), X abs(tz%3600)/60); X q += strlen (q); X } X#endif X X *q = '\0'; X return (b); X} X X/* X** X** getmynames(): what is my host name and host domain? X** X** Hostname set by -h, failing that by #define HOSTNAME, failing X** that by gethostname() or uname(). X** X** Hostdomain set by -h, failing that by #define HOSTDOMAIN, X** failing that as hostname.MYDOM, or as just hostname. X** X** See defs.h for the inside story. X** X*/ X Xgetmynames() X{ X#ifdef HOSTNAME X if (!*hostname) X (void) strcpy(hostname, HOSTNAME); X#endif X#ifdef BSD X if (!*hostname) X gethostname(hostname, SMLBUF - 1); X#endif X#ifdef SYSV X if (!*hostname) { X struct utsname site; X X if (uname(&site) == 0) X (void) strcpy(hostname, site.nodename); X } X#endif X if (!*hostname) X return -1; X#ifdef HOSTDOMAIN X if (!*hostdomain) X (void) strcpy(hostdomain, HOSTDOMAIN); X#endif X#ifdef MYDOM X if (!*hostdomain) X (void) strcat(strcpy(hostdomain, hostname), MYDOM); X#endif X if (!*hostdomain) X (void) strcpy(hostdomain, hostname); X X return 1; X} SHAR_EOF if test 4018 -ne "`wc -c < 'misc.c'`" then echo shar: "error transmitting 'misc.c'" '(should have been 4018 characters)' fi fi echo shar: "extracting 'miscerrs.h'" '(197 characters)' if test -f 'miscerrs.h' then echo shar: "will not over-write existing file 'miscerrs.h'" else sed 's/^X//' << \SHAR_EOF > 'miscerrs.h' X/* these error numbers are local to the smtp programs, used by bomb() */ X#define E_USAGE -1 X#define E_NOHOST -2 X#define E_CANTOPEN -3 X#define E_OSFILE -4 X#define E_IOERR -5 X#define E_TEMPFAIL -6 SHAR_EOF if test 197 -ne "`wc -c < 'miscerrs.h'`" then echo shar: "error transmitting 'miscerrs.h'" '(should have been 197 characters)' fi fi echo shar: "extracting 'mx.c'" '(5751 characters)' if test -f 'mx.c' then echo shar: "will not over-write existing file 'mx.c'" else sed 's/^X//' << \SHAR_EOF > 'mx.c' X#include <stdio.h> X#include <netdb.h> X#include <sysexits.h> X#include <sys/errno.h> X#include <sys/types.h> X#include <sys/socket.h> X#include <netinet/in.h> X#include <arpa/nameser.h> X#include "miscerrs.h" X X/* imports */ Xextern int errno, h_errno; Xextern char *malloc(), *strcpy(), *inet_ntoa(); X X/* exports */ Xint mxconnect(); X X/* private */ X#define MAXMXLIST 10 Xstatic struct mxitem { X char *host; X u_short pref; X u_char localflag; X} MXlist[MAXMXLIST + 1]; Xstatic char *strsave(); Xstatic int buildmxlist(); Xstatic void mxsave(), mxinsert(), mxlocal(); Xstatic struct hostent *getmxhost(); X X#ifdef MXMAIN X X#define bomb return X Xmain(argc, argv) X char **argv; X{ int fd; X char buf[BUFSIZ], *crlf, *index(); X struct mxitem *mxp; X X for (;;) { X printf("domain: "); X if (argc > 1) X strcpy(buf, argv[1]); X else if (gets(buf) == 0) X break; X if ((fd = mxconnect(buf)) >= 0) X if (read(fd, buf, 512) > 0) { X if ((crlf = index(buf, '\r')) != 0) X strcpy(crlf, "\n"); X puts(buf); X } else X perror("read"); X close(fd); X if (argc > 1) X break; X for (mxp = MXlist; mxp < MXlist + MAXMXLIST + 1; mxp++) X mxp->host = 0; X } X return 0; X} X#endif X Xmxconnect(host) X char *host; X{ int s, lport, mxfatal; X char **addr, errbuf[256]; X struct hostent *hp; X struct servent *sp; X struct sockaddr_in sin; X struct mxitem *mxp; X X mxfatal = buildmxlist(host); X if (MXlist[0].host == 0) X MXlist[0].host = host; X if ((sp = getservbyname ("smtp", "tcp")) == NULL) { X (void)fprintf(stderr,"unknown service TCP/smtp\n"); X bomb(E_OSFILE); X } X (void) setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) 0, 0); X X /* slop in the loop -- i hate the socket dance */ X for (mxp = MXlist; mxp->host; mxp++) { X if ((s = rresvport(&lport)) < 0) { X perror("rresvport"); X bomb(E_CANTOPEN); X } X if ((hp = getmxhost(mxp->host)) == 0) { X (void) close(s); X if (mxfatal) X bomb(E_NOHOST); X continue; X } X bzero((char *)&sin, sizeof(sin)); X sin.sin_port = sp->s_port; X sin.sin_family = hp->h_addrtype; X for (addr = hp->h_addr_list; *addr; addr++) { X bcopy(*addr, (char *) &sin.sin_addr, hp->h_length); X if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { X sprintf(errbuf, "%s [%s]", mxp->host, inet_ntoa(sin.sin_addr)); X perror(errbuf); X continue; X } X return s; X } X close(s); X } X X bomb(E_TEMPFAIL); X} X X/* return 1 for fatal MX error (authoritative NXDOMAIN), 0 o.w. */ Xstatic int Xbuildmxlist(host) X char *host; X{ register HEADER *hp; X register char *cp; X register int n; X char q[PACKETSZ], a[PACKETSZ]; /* query, answer */ X char *eom, *bp; X int buflen, ancount, qdcount; X char hostbuf[BUFSIZ+1]; X u_short preference, reclen; X X if ((n = res_mkquery(QUERY, host, C_IN, T_MX, (char *) 0, 0, (struct rrec *) 0, q, sizeof(q))) < 0) X return 0; X n = res_send(q, n, a, sizeof(a)); X if (n < 0) X return 0; X eom = a + n; X hp = (HEADER *) a; X ancount = ntohs(hp->ancount); X qdcount = ntohs(hp->qdcount); X if (hp->rcode != NOERROR || ancount == 0) X return hp->rcode == NXDOMAIN && hp->aa; X bp = hostbuf; X buflen = sizeof(hostbuf); X cp = a + sizeof(HEADER); X while (--qdcount >= 0) X cp += dn_skip(cp) + QFIXEDSZ; X /* TODO: if type is CNAME, reissue query */ X while (--ancount >= 0 && cp < eom) { X cp += dn_skip(cp) /* name */ X + sizeof(u_short) /* type */ X + sizeof(u_short) /* class */ X + sizeof(u_long); /* ttl (see rfc973) */ X reclen = _getshort(cp); X cp += sizeof(u_short); X preference = _getshort(cp); X if ((n = dn_expand(a, eom, cp + sizeof(u_short), bp, buflen)) < 0) X break; X mxsave(bp, preference); X cp += reclen; X } X mxlocal(); X return 0; X} X X/* NOT TODO: issue WKS query. (just try to connect.) */ X Xstatic void Xmxsave(host, pref) X char *host; X u_short pref; X{ struct mxitem *mxp; X int localflag; X static char thishost[64]; X X if (*thishost == 0) X gethostname(thishost, sizeof(thishost)); X X if (MXlist[MAXMXLIST].host) X return; /* full */ X X localflag = (strcmp(thishost, host) == 0); X X /* insertion sort */ X for (mxp = MXlist; mxp < MXlist + MAXMXLIST; mxp++) { X if (mxp->host == 0) { X mxinsert(mxp, host, pref, localflag); X return; X } X if (pref < mxp->pref) { X mxinsert(mxp, host, pref, localflag); X return; X } X if (pref == mxp->pref) { X if (mxp->localflag) X return; X if (localflag) { X mxp->host = strsave(host); X mxp->pref = pref; X mxp->localflag = localflag; X (++mxp)->host = 0; X return; X } X mxinsert(mxp, host, pref, localflag); X return; X } X } X} X Xstatic void Xmxinsert(mxlistp, host, pref, localflag) X struct mxitem *mxlistp; X char *host; X u_short pref; X{ register struct mxitem *mxp; X X for (mxp = MXlist + MAXMXLIST - 1; mxp > mxlistp; --mxp) X *mxp = mxp[-1]; X mxp->host = strsave(host); X mxp->pref = pref; X mxp->localflag = localflag; X} X Xstatic char * Xstrsave(str) X register char *str; X{ register char *rval; X X if ((rval = malloc(strlen(str) + 1)) == 0) { X perror("malloc"); X bomb(-EX_SOFTWARE); X } X strcpy(rval, str); X return rval; X} X Xstatic void Xmxlocal() X{ register struct mxitem *mxp; X X if (MXlist[0].host == 0) X return; X X for (mxp = MXlist; mxp->host; mxp++) { X if (mxp->localflag) { X mxp->host = 0; X break; X } X } X} X Xstatic struct hostent * Xgetmxhost(host) X char *host; X{ struct hostent *hp, *gethostbyname(); X X if ((hp = gethostbyname(host)) != 0) X return hp; X X switch(h_errno) { X X case HOST_NOT_FOUND: X (void) fprintf(stderr, "unknown host (%s).\n", host); X break; X X case TRY_AGAIN: X (void) fprintf(stderr, "name server not responding (%s).\n", host); X break; X X case NO_RECOVERY: X (void) fprintf(stderr, "name server error (%s).\n", host); X break; X X case NO_ADDRESS: X (void) fprintf(stderr, "no IP address (%s).\n", host); X break; X X default: X (void) fprintf(stderr, "unknown resolver error (%s).\n", host); X break; X } X return 0; X} SHAR_EOF if test 5751 -ne "`wc -c < 'mx.c'`" then echo shar: "error transmitting 'mx.c'" '(should have been 5751 characters)' fi fi echo shar: "extracting 'netio.c'" '(1442 characters)' if test -f 'netio.c' then echo shar: "will not over-write existing file 'netio.c'" else sed 's/^X//' << \SHAR_EOF > 'netio.c' X#ifndef lint Xstatic char *sccsid = "@(#)netio.c 1.7 87/07/31"; X#endif lint X X#include "smtp.h" X#include <setjmp.h> X X#ifdef NOBOMB X#define bomb exit X#endif X Xchar *strcpy(), *strncat(); X Xint hooting = 0; /* true if not server */ X Xint Xtgets(line, size, fi) /* fgets from TCP */ Xchar *line; Xint size; XFILE *fi; X{ X register char *cr; X X *line = 0; X if (fgets(line, size, fi) == NULL) X return -1; X if (ferror(fi)) { X perror("error reading from smtp"); X bomb(E_IOERR); X } X X /* convert \r\n -> \n */ X cr = line + strlen(line) - 2; X if (cr >= line && *cr == '\r' && *(cr+1) == '\n') { /* CRLF there? */ X *cr++ = '\n'; X *cr = 0; X } else /* no CRLF present */ X cr += 2; /* point at NUL byte */ X X#ifdef HOOTING X if (hooting) (void) printf("<<< %s", line); X#endif X if (feof(fi)) { X perror("read eof from smtp"); X bomb(E_IOERR); X } X return cr - line; X} X Xint Xtputs(line, fo) /* fputs to TCP */ Xchar *line; XFILE *fo; X{ X char buf[MAXSTR]; X register char *nl; X extern int debug; X X (void) strcpy(buf, line); X#ifdef HOOTING X if (hooting) (void) printf(">>> %s", buf); X#endif X /* replace terminating \n by \r\n */ X nl = buf + strlen(buf) - 1; /* presumably \n */ X if (nl >= buf && *nl=='\n') { /* if it is ... */ X *nl++ = '\r'; X *nl++ = '\n'; X *nl = 0; X } else X printf("unterminated line: <%s>\n", buf); X X (void) fputs(buf, fo); X (void) fflush(fo); X if (ferror(fo)) { X (void) perror("error writing to smtp"); X bomb(E_IOERR); X } X return 0; X} SHAR_EOF if test 1442 -ne "`wc -c < 'netio.c'`" then echo shar: "error transmitting 'netio.c'" '(should have been 1442 characters)' fi fi echo shar: "extracting 'smtp.8'" '(4836 characters)' if test -f 'smtp.8' then echo shar: "will not over-write existing file 'smtp.8'" else sed 's/^X//' << \SHAR_EOF > 'smtp.8' X.TH SMTP 8 local "Public Domain" X.DA "4 May 1987" X.SH NAME Xsmtp, smtpd, in.smtpd \- SMTP talker and listeners X.br Xsmtpqer, runsmtpq, smtpq \- SMTP enqueuing, queue running and listing X.br Xcleansmtpq, returnsmtpmail \- SMTP queue cleaning and mail returning X.SH SYNOPSIS X.B /usr/lib/mail/smtp X[ X.B \-h Xhelohost X] Xtargethost sender recipient X.br X.B "cd /usr/spool/smtpq; /usr/lib/mail/smtpd" X.br X.B "cd /usr/spool/smtpq; /usr/etc/in.smtpd" X.RB sourcehost . sourceport X.br X.B /usr/lib/mail/smtpqer X[ X.B \-h Xhelohost X] Xtargethost sender recipient X.br X.B /usr/lib/mail/runsmtpq X.br X.B /usr/lib/mail/smtpq X.br X.B /usr/lib/mail/cleansmtpq X.br X.B /usr/lib/mail/returnsmtpmail X*.sh ... X.SH DESCRIPTION XThese routines provide a minimal stand-alone \s-2SMTP\s0 service. XIt consists of small programs that can be used with a central mail router like X.I upas, Xa system of your own construction, Xor X.IR sendmail . X.PP X.I smtp Xsends the mail message on its standard input to X.I targethost Xwhich in turn will deliver the message to X.I recipient Xas being from X.IR sender . X.I smtp Xcan be used with X.I sendmail Xinstead of the latter's built-in \s-2IPC\s0 mailer. XHere is a mailer definition we have used; Xyour mileage will vary: X.PP X.nf X.B XMether, P=/usr/lib/mail/smtp, F=SsDFuCX, S=11, R=21, A=smtp $h $g $u X.fi X.PP XEvery SMTP talker identifies itself in the X``host.domain'' Xpart of the \s-2SMTP\s0 \s-2HELO\s0 message. XNormally X.I smtp Xuses the X.IR gethostname (2) Xcall to get the hostname, Xand the compiled-in DOMAINNAME, Xto identify itself. XThe X.B \-h Xoption allows you to override both values with one string, Xi.e., X.BR \-h erewhon.peter.edu. XIf you are using X.I sendmail , Xthen X.B \-h Xshould be added to the Mether definition with ``-h$j'' X(but this has not yet been tested). XIn X.IR upas , Xa typical rule is X.PP X.nf X.B X^([^!]+)\e.(mil|gov|edu|com|uk|org|net)!(.+)$ | "smtpqer -h \fIdom.ain\fP \e1.\e2 \es \e3" X.fi X.PP X.I smtpd Xis a 4.2BSD (or later) network daemon: Xit listens on the SMTP port X(TCP port 25 usually) Xfor an SMTP talker, Xreceives a mail message from it Xand hands the message to X.I cmail X(or equivalent other mail system). X.I in.smtpd Xis a similar 4.3BSD (or later) X.I inetd Xdaemon: Xit expects an SMTP connection on file descriptor zero (0) Xand the X.I sourcehost Xand X.I sourceport Xas its argument X.RI ( sourcehost Xin hexademical; X.I sourceport Xin decimal). X.PP X.I smtpqer Xqueues its standard input Xand shell commands to invoke X.I smtp Xwith X.IR smtpqer 's Xstandard input and arguments, Xand to remove the queued files if X.I smtp Xis successful. XIt then runs X.IR runsmtpq . X.PP X.I runsmtpq Xlocks the SMTP queue, Xexecutes all the shell command files in the SMTP queue, Xand unlocks the SMTP queue. X.I runsmtpq Xis stolen outright from the SM paper in the proceedings Xof the Portland Usenix conference. X.PP X.I smtpq Xshows the contents of the SMTP queue. X.PP X.I cleansmtpq Xreturns to sender mail which has been in the SMTP queue Xfor more than three days and removes it from the queue. X.PP X.I returnsmtpmail Xtakes the names of ``envelope'' shell files, Xreturns the messages therein Xand removes the messages from the SMTP queue. X.SH FILES X.BR /usr/spool/smtpq " the SMTP queue" X.br X.IB queue /*.sh X\& envelopes X.br X.IB queue /*.msg X\& message contents X.br X.IB queue /*.errs X\& errors from running X.I smtp X.br X.BR temp.* " incoming messages, created in" X.IR smtpd 's Xcurrent directory X.SH "SEE ALSO" XRFC 821 \- Simple Mail Transfer Protocol, ARPA Internet, NIC, SRI X.SH DIAGNOSTICS XBy default, Xthe talker program exits with the return codes defined in X.I <sysexits.h> Xfor X.IR sendmail . XTo disable this, Xcompile with -DNOSYSEXITS; Xit will exit with code 1 for all errors. X.SH HISTORY XThe SMTP talker (client) was written by Ian Darwin in 1985, Xas a simple telnet-like program Xthat just talked from stdin to a remote SMTP. XIn late 1986, Xit was built into a full talker Xwith some functions stolen Xfrom the public domain MIT UNIX TCP/IP implementation. X.PP XThe SMTP listener (daemon) was synthesised by Geoff Collyer Xby smashing together an old X.I nicname Xdaemon written by Ian with more public-domain SMTP code from MIT. X.PP X.I smtpqer Xand X.I smtpq Xwere written by Geoff Collyer. X.IR runsmtpq , X.I cleansmtpq Xand X.I returnsmtpmail Xwere stolen from the SM Portland Usenix paper Xand adapted by Geoff Collyer. XPeter Honeyman installed it under X.I upas Xon the Internet and fixed a lot of bugs. XSome remain. X.SH BUGS XThe SMTP implementation is minimal, Xand could stand some fleshing out. X.PP X.I smtp Xand X.I smtpqer Xcould profitably handle multiple recipients on the same machine, Xbut dealing with bad recipients would be tricky. X.PP X.I smtpd Xand X.I in.smtpd Xcreate files named X.I temp.* Xin their current directories, Xso they should be run in the SMTP queue directory. X.PP X.I smtp Xand X.I smtpd Xlimit connect time to 30 minutes, Xwhich can be optimistic. SHAR_EOF if test 4836 -ne "`wc -c < 'smtp.8'`" then echo shar: "error transmitting 'smtp.8'" '(should have been 4836 characters)' fi fi echo shar: "extracting 'smtp.c'" '(4985 characters)' if test -f 'smtp.c' then echo shar: "will not over-write existing file 'smtp.c'" else sed 's/^X//' << \SHAR_EOF > 'smtp.c' X/* X * smtp -- client, send mail to remote smtp server X * Set up for 4.2BSD networking system. X * Adapted for System V by jv@mh.nl. X * TODO: X * better mapping of error numbers. X * allow multiple recipients (maybe) X * send stuff from cmds.h instead of hard-coded here X */ X X#define USAGE "usage: %s [-h helohost] targethost sender recipient\n" X X#include "smtp.h" X#include <sys/uio.h> /* needed for socket.h */ X#include <sys/socket.h> X#include <netinet/in.h> X#include <netdb.h> X X X#ifndef SERVNAME X#define SERVNAME "smtp" /* service we wanna talk to */ X#endif X Xchar *progname; Xint debug = 0; Xextern char hostname[]; Xextern char hostdomain[]; Xchar *sendhost = hostdomain; XFILE *sfi, *sfo; Xchar *host = "None"; Xchar *strcat(), *strcpy(); Xstatic char *makedomain(); X X#ifdef HOOTING Xextern int hooting; /* in netio */ X#endif X X/* X * main - parse arguments and handle options X */ Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X register int c; X int errflg = 0; X extern int optind; X extern char *optarg; X char *sender, *recip; X X progname = argv[0]; X getmynames (); X X while ((c = getopt(argc, argv, "dh:")) != EOF) X switch (c) { X case 'd': X ++debug; X break; X case 'h': /* alternate sending host */ X sendhost = optarg; X break; X case '?': X default: X errflg++; X break; X } X if (errflg || (argc - optind) < 3) { X (void) fprintf(stderr, USAGE, progname); X bomb(E_USAGE); X } X host = argv[optind++]; X sender = makedomain(argv[optind++], sendhost); X recip = makedomain(argv[optind++], host); X X setup(); /* open connection */ X X X#ifdef HOOTING X hooting = 1; X#endif X /* hold the conversation */ X X converse(sender, recip, sfi, sfo, stdin); X (void) sleep(5); /* drain? */ X X if (sfo!=NULL) X (void) fclose(sfo); X if (sfi!=NULL) X (void) fclose(sfi); X X return 0; X} X X/* X * setup -- setup tcp/ip connection to/from server X */ Xsetup() X{ X struct hostent *hp; X struct servent *sp; X struct sockaddr_in sin; X int s; X extern int errno; X X (void) bzero((char *)&sin, sizeof(sin)); X X if ((hp = gethostbyname(host)) == (struct hostent *) NULL) { X (void) fprintf(stderr, "unknown host (%s).\n", host); X bomb(E_NOHOST); X } X X (void) bcopy(hp->h_addr, &sin.sin_addr, hp->h_length); X X if ((sp = getservbyname (SERVNAME, "tcp")) == NULL) { X (void)fprintf(stderr,"unknown service TCP/%s\n", SERVNAME); X bomb(E_OSFILE); X } X sin.sin_port = sp->s_port; X sin.sin_family = hp->h_addrtype; X X if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) { X perror("setup - socket"); X bomb(E_CANTOPEN); X } X X if (connect(s, (struct sockaddr *) &sin, sizeof (sin)) < 0) { X perror("setup - connect"); X /* X * check for conditions that (we think) are temporary; X * try them later; bomb utterly on all others. X */ X if (errno == ETIMEDOUT || errno == ECONNREFUSED || X errno == EHOSTDOWN || errno == EHOSTUNREACH) X bomb(E_TEMPFAIL); X else X bomb(E_CANTOPEN); X } X X if (((sfi = fdopen(s, "r")) == (FILE *) NULL) || X ((sfo = fdopen(s, "w")) == (FILE *) NULL)) { X perror("setup - fdopen"); X bomb(E_CANTOPEN); X } X/* setbuf(sfi, (char *) 0);*/ X} X X/* X * bomb(code) - exit program, map smtp error code into mailsystem code X * Codes with E_ are defined in miscerrs.h. X * Codes with EX_ are from <sysexits.h> X * Lines with FOO are placeholders until we decrypt more appropriate codes. X */ Xbomb(code) Xint code; X{ X if (sfi != NULL) X (void) fclose(sfi); X if (sfo != NULL) X (void) fclose(sfo); X X#ifdef EX_OK X switch(code) { X case 451: /* host not responding */ X code = EX_UNAVAILABLE; /* service unavailable */ X break; X case 550: X code = EX_NOUSER; /* addressee unknown */ X break; X case 501: /* syntax error in address */ X case 554: /* */ X code = EX_DATAERR; /* data format error */ X break; X case E_IOERR: X code = EX_IOERR; /* input/output error */ X break; X case E_NOHOST: X code = EX_NOHOST; /* host name unknown */ X break; X case E_OSFILE: /* no "smtp" -> /etc/services f'd */ X code = EX_OSFILE; /* critical OS file missing */ X break; X case E_USAGE: X code = EX_USAGE; /* command line usage error */ X break; X case E_TEMPFAIL: X code = EX_TEMPFAIL; /* temp failure; user can retry */ X break; X#if 0 X case FOO: X code = EX_OSERR; /* system error (e.g., can't fork) */ X break; X case FOO: X code = EX_NOINPUT; /* cannot open input */ X break; X case FOO: X code = EX_CANTCREAT; /* can't create (user) output file */ X break; X case FOO: X code = EX_PROTOCOL; /* remote error in protocol */ X break; X case FOO: X code = EX_NOPERM; /* permission denied */ X break; X#endif /* NOTDEF */ X default: /* can't happen? */ X code = EX_SOFTWARE; /* internal software error */ X break; X } X X#else /* has no sysexits */ X code = 1; X#endif X X (void) exit (code); X} X Xstatic char * Xmakedomain(addr, domain) Xchar *addr, *domain; X{ X char *rval; X extern char *index(), *malloc(); X X if (index(addr, '@') != 0) X return addr; X X if ((rval = malloc(strlen(addr) + strlen(domain) + 2)) == 0) X#ifdef EX_SOFTWARE X bomb(EX_SOFTWARE); /* can't happen! */ X#else X bomb (3); X#endif X sprintf(rval, "%s@%s", addr, domain); X return rval; X} SHAR_EOF if test 4985 -ne "`wc -c < 'smtp.c'`" then echo shar: "error transmitting 'smtp.c'" '(should have been 4985 characters)' fi fi echo shar: "extracting 'smtp.h'" '(1022 characters)' if test -f 'smtp.h' then echo shar: "will not over-write existing file 'smtp.h'" else sed 's/^X//' << \SHAR_EOF > 'smtp.h' X/* smtp.h */ X X#include "config.h" X X/* smtp constants and the like */ X X/* tunable constants */ X#define MAXSTR 10240 /* maximum string length */ X#define NAMSIZ MAXSTR /* max file name length */ X X/* standard includes and portability */ X X#include <stdio.h> X#include <ctype.h> X#include <sys/types.h> X#include <errno.h> X#include "miscerrs.h" X X#ifdef BSD X#include <strings.h> X#include <sys/time.h> X#include <sys/wait.h> Xextern char *sprintf(); X# ifndef NOSYSEXITS X# include <sysexits.h> X# endif X#endif X X#ifdef SYSV X#include <string.h> X#include <time.h> X#include <fcntl.h> X#define index strchr X#define rindex strrchr X#define bcopy(a,b,n) memcpy(b,a,n) X#define bzero(a,n) memset(a,'\0',n) X#define SIGCHLD SIGCLD Xextern int sprintf(); X# ifndef NOSYSEXITS X# include "sysexits.h" X# endif X#endif X X#ifndef EX_SOFTWARE X# define EX_SOFTWARE 70 /* internal software error */ X#endif X X#ifdef SYSLOG X# ifdef SYSV X# include "syslog.h" X# else X# include <syslog.h> X# endif X#endif X X#ifndef TRUE X# define TRUE 1 X# define FALSE 0 X#endif SHAR_EOF if test 1022 -ne "`wc -c < 'smtp.h'`" then echo shar: "error transmitting 'smtp.h'" '(should have been 1022 characters)' fi fi echo shar: "extracting 'smtpd.c'" '(5297 characters)' if test -f 'smtpd.c' then echo shar: "will not over-write existing file 'smtpd.c'" else sed 's/^X//' << \SHAR_EOF > 'smtpd.c' X#ifndef lint Xstatic char *sccsid = "@(#)smtpd.c 1.7 87/07/31"; X#endif lint X/* X * smtpd - SMTP listener: receives SMTP mail & invokes cmail. X * SMTP is either Simple Mail Transfer Protocol or X * Sado-Masochistic Torture Procedure. X */ X X#include "smtp.h" X#include <signal.h> X#include <netdb.h> X#include <sys/uio.h> X#include <sys/socket.h> X#ifdef BSD X#include <sgtty.h> X#include <sys/wait.h> X#endif X#include <sys/resource.h> /* for wait3(2) */ X#include <netinet/in.h> X X/* forward declarations */ XFILE *popen(); X X#ifdef INETD X#ifdef BSD Xint reapchild(); X#endif X#endif X Xextern char **environ; Xextern int errno, sys_nerr; Xextern char *sys_errlist[]; X X#ifndef SERVNAME X#define SERVNAME "smtp" X#endif /* SERVNAME */ Xstruct sockaddr_in sin = { AF_INET }; Xstruct sockaddr_in from; X Xint debug; Xchar *progname; Xchar logm[MAXSTR]; X X/* X * main - parse arguments and handle options X */ X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X int c; X int errflg = 0; X extern int optind; X extern char *optarg; X X progname = argv[0]; X getmynames (); X X while ((c = getopt(argc, argv, "d")) != EOF) X switch (c) { X case 'd': X ++debug; X break; X case '?': X default: X errflg++; X break; X } X if (errflg) { X logit(LOG_CRIT, "Usage: %s [-d]\n", progname); X exit(2); X } X X if (optind >= argc) X process(); X#ifdef INETD X else if (optind == argc - 1) { /* one argument */ X if (sscanf(argv[optind], "%lx.%hd", &from.sin_addr.s_addr, X &from.sin_port) != 2) { X logit(LOG_CRIT, X "in.smtpd: bad arg from inetd: %s\n", X argv[optind]); X exit(2); X } X from.sin_family = AF_INET; X from.sin_addr.s_addr = htonl(from.sin_addr.s_addr); X from.sin_port = htons(from.sin_port); X process(); X } X#endif /* INETD */ X else { X logit(LOG_CRIT, "%s: too many args\n", progname); X exit(2); X } X X return 0; X} X X/* X * process - process input file X */ Xprocess() X{ X#ifndef INETD X int s, pid; X#endif /* INETD */ X struct servent *sp; X X sp = getservbyname(SERVNAME, "tcp"); X if (sp == 0) { X logit(LOG_CRIT, "tcp/%s: unknown service\n", SERVNAME); X exit(1); X } X sin.sin_port = sp->s_port; X X#ifdef INETD X /* connection on fd 0 from inetd */ X doit(0, &from); X /* NOTREACHED */ X exit(0); X#else /* INETD */ X#ifndef DEBUG X if (fork()) /* run in the background */ X exit(0); X for (s = 0; s < 10; s++) /* close most file descriptors */ X (void) close(s); X (void) open("/dev/null", 0); /* reopen them on harmless streams */ X (void) dup2(0, 1); X (void) dup2(0, 2); X { int tt = open("/dev/tty", 2); /* leave current process group */ X if (tt > 0) { X#ifdef TIOCNOTTY X (void) ioctl(tt, TIOCNOTTY, (char *)0); X#endif X (void) close(tt); X } X } X#endif /* DEBUG */ X /* create internet socket s; retry 5 times at 5 s. intervals if no luck */ X while ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) { X static int nlog = 0; X X if (nlog++ <= 5) X logit(LOG_CRIT, "socket", ""); X sleep(5); X } X /* set socket options, notably keepalive */ X if (debug) { X int debugval = 1; X X if (setsockopt(s, SOL_SOCKET, SO_DEBUG, X (char *)&debugval, sizeof(int)) < 0) X logit(LOG_CRIT, "setsockopt (SO_DEBUG)", ""); X } X if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)0, 0) < 0) X logit(LOG_CRIT, "setsockopt (SO_KEEPALIVE)", ""); X /* bind socket to SERVNAME (SMTP) port; retry as above on failure */ X while (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) { X static int nlog = 0; X X if (nlog++ <= 5) X logit(LOG_CRIT, "bind", ""); X sleep(5); X } X X#ifdef BSD X (void) signal(SIGCHLD, TYPESIG reapchild); X#else X (void) signal(SIGCHLD, SIG_IGN); /* SystemV takes care ... */ X#endif X X /* listen with 10 input buffers on socket (?) */ X if (listen(s, 10) == -1) X logit(LOG_CRIT, "listen", ""); X for (;;) { X int conn, fromlen = sizeof from; X X /* get a connection on fd conn; stores src host addr in from */ X conn = accept(s, (struct sockaddr *)&from, &fromlen); X if (conn < 0) { X static int nlog = 0; X X if (errno == EINTR) X continue; X if (++nlog <= 5) X logit(LOG_CRIT, "accept", ""); X sleep(1); X continue; X } X /* fork a child for this connection */ X if ((pid = fork()) < 0) X logit(LOG_CRIT, "can't fork!!", ""); X else if (pid == 0) { X (void) signal(SIGCHLD, SIG_DFL); X doit(conn, &from); /* listen to SMTP dialogue */ X /* NOTREACHED */ X exit(0); X } X (void) close(conn); X } X /*NOTREACHED*/ X#endif /* INETD */ X} X X#ifndef INETD X#ifdef BSD Xreapchild() X{ X union wait status; X X /* gross hack! */ X while (wait3(&status, WNOHANG, (struct rusage *)0) > 0); X X} X#endif /* BSD */ X#endif /* INETD */ X X/* X * handle some input. never returns. X */ Xdoit(f, fromaddr) Xint f; Xstruct sockaddr_in *fromaddr; /* internet addr of sending host */ X{ X FILE *fi, *fo; X X if ((fi = fdopen(f, "r")) == NULL) X logit(LOG_CRIT, "fdopen of socket for input", ""); X if ((fo = fdopen(f, "w")) == NULL) X logit(LOG_CRIT, "fdopen of socket for output", ""); X X converse(fi, fo, fromaddr); X /* NOTREACHED */ X return 0; X} X X#ifndef NOBOMB Xbomb(err) Xint err; X{ X death(err); X} X#endif X Xlogit (sev, fmt, str) Xint sev; Xchar *fmt, *str; X{ X#ifdef SYSLOG X (void) sprintf(logm, "%s: %s", progname, fmt); X (void) syslog(sev, logm, (str == "" && errno <= sys_nerr)? X sys_errlist[errno]: str); X#else X (void) sprintf(logm, "%s: %s", progname, fmt); X (void) fprintf(stderr, logm, (str == "" && errno <= sys_nerr)? X sys_errlist[errno]: str); X#endif X} SHAR_EOF if test 5297 -ne "`wc -c < 'smtpd.c'`" then echo shar: "error transmitting 'smtpd.c'" '(should have been 5297 characters)' fi fi echo shar: "extracting 'sysexits.h'" '(3662 characters)' if test -f 'sysexits.h' then echo shar: "will not over-write existing file 'sysexits.h'" else sed 's/^X//' << \SHAR_EOF > 'sysexits.h' X/* X** SYSEXITS.H -- Exit status codes for system programs. X** X** This include file attempts to categorize possible error X** exit statuses for system programs, notably delivermail X** and the Berkeley network. X** X** Error numbers begin at EX__BASE to reduce the possibility of X** clashing with other exit statuses that random programs may X** already return. The meaning of the codes is approximately X** as follows: X** X** EX_USAGE -- The command was used incorrectly, e.g., with X** the wrong number of arguments, a bad flag, a bad X** syntax in a parameter, or whatever. X** EX_DATAERR -- The input data was incorrect in some way. X** This should only be used for user's data & not X** system files. X** EX_NOINPUT -- An input file (not a system file) did not X** exist or was not readable. This could also include X** errors like "No message" to a mailer (if it cared X** to catch it). X** EX_NOUSER -- The user specified did not exist. This might X** be used for mail addresses or remote logins. X** EX_NOHOST -- The host specified did not exist. This is used X** in mail addresses or network requests. X** EX_UNAVAILABLE -- A service is unavailable. This can occur X** if a support program or file does not exist. This X** can also be used as a catchall message when something X** you wanted to do doesn't work, but you don't know X** why. X** EX_SOFTWARE -- An internal software error has been detected. X** This should be limited to non-operating system related X** errors as possible. X** EX_OSERR -- An operating system error has been detected. X** This is intended to be used for such things as "cannot X** fork", "cannot create pipe", or the like. It includes X** things like getuid returning a user that does not X** exist in the passwd file. X** EX_OSFILE -- Some system file (e.g., /etc/passwd, /etc/utmp, X** etc.) does not exist, cannot be opened, or has some X** sort of error (e.g., syntax error). X** EX_CANTCREAT -- A (user specified) output file cannot be X** created. X** EX_IOERR -- An error occurred while doing I/O on some file. X** EX_TEMPFAIL -- temporary failure, indicating something that X** is not really an error. In sendmail, this means X** that a mailer (e.g.) could not create a connection, X** and the request should be reattempted later. X** EX_PROTOCOL -- the remote system returned something that X** was "not possible" during a protocol exchange. X** EX_NOPERM -- You did not have sufficient permission to X** perform the operation. This is not intended for X** file system problems, which should use NOINPUT or X** CANTCREAT, but rather for higher level permissions. X** For example, kre uses this to restrict who students X** can send mail to. X** X** Maintained by Eric Allman (eric@berkeley, ucbvax!eric) -- X** please mail changes to me. X** X** @(#)sysexits.h 1.2 86/05/09 ACE (c) X*/ X X# define EX_OK 0 /* successful termination */ X X# define EX__BASE 64 /* base value for error messages */ X X# define EX_USAGE 64 /* command line usage error */ X# define EX_DATAERR 65 /* data format error */ X# define EX_NOINPUT 66 /* cannot open input */ X# define EX_NOUSER 67 /* addressee unknown */ X# define EX_NOHOST 68 /* host name unknown */ X# define EX_UNAVAILABLE 69 /* service unavailable */ X# define EX_SOFTWARE 70 /* internal software error */ X# define EX_OSERR 71 /* system error (e.g., can't fork) */ X# define EX_OSFILE 72 /* critical OS file missing */ X# define EX_CANTCREAT 73 /* can't create (user) output file */ X# define EX_IOERR 74 /* input/output error */ X# define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ X# define EX_PROTOCOL 76 /* remote error in protocol */ X# define EX_NOPERM 77 /* permission denied */ SHAR_EOF if test 3662 -ne "`wc -c < 'sysexits.h'`" then echo shar: "error transmitting 'sysexits.h'" '(should have been 3662 characters)' fi fi echo shar: "extracting 'syslog.h'" '(24 characters)' if test -f 'syslog.h' then echo shar: "will not over-write existing file 'syslog.h'" else sed 's/^X//' << \SHAR_EOF > 'syslog.h' X#include <sys/syslog.h> SHAR_EOF if test 24 -ne "`wc -c < 'syslog.h'`" then echo shar: "error transmitting 'syslog.h'" '(should have been 24 characters)' fi fi echo shar: "extracting 'MANIFEST'" '(144 characters)' if test -f 'MANIFEST' then echo shar: "will not over-write existing file 'MANIFEST'" else sed 's/^X//' << \SHAR_EOF > 'MANIFEST' XMakefile XREADME Xcmds.h Xconfig.h Xconverse.c Xdconverse.c Xmisc.c Xmiscerrs.h Xmx.c Xnetio.c Xsmtp.8 Xsmtp.c Xsmtp.h Xsmtpd.c Xsysexits.h Xsyslog.h XMANIFEST SHAR_EOF if test 144 -ne "`wc -c < 'MANIFEST'`" then echo shar: "error transmitting 'MANIFEST'" '(should have been 144 characters)' fi fi exit 0 # End of shell archive -- postmaster on node mhres (currently: Johan Vromans)