home *** CD-ROM | disk | FTP | other *** search
- #ifndef lint
- static char *sccsid = "@(#)smtpd.c 1.7 87/07/31";
- #endif lint
- /*
- * smtpd - SMTP listener: receives SMTP mail & invokes cmail.
- * SMTP is either Simple Mail Transfer Protocol or
- * Sado-Masochistic Torture Procedure.
- */
-
- #include "smtp.h"
- #include <signal.h>
- #include <netdb.h>
- #include <sys/uio.h>
- #include <sys/socket.h>
- #ifdef BSD
- #include <sgtty.h>
- #include <sys/wait.h>
- #endif
- #include <sys/resource.h> /* for wait3(2) */
- #include <netinet/in.h>
-
- /* forward declarations */
- FILE *popen();
-
- #ifdef INETD
- #ifdef BSD
- int reapchild();
- #endif
- #endif
-
- extern char **environ;
- extern int errno, sys_nerr;
- extern char *sys_errlist[];
-
- #ifndef SERVNAME
- #define SERVNAME "smtp"
- #endif /* SERVNAME */
- struct sockaddr_in sin = { AF_INET };
- struct sockaddr_in from;
-
- int debug;
- char *progname;
- char logm[MAXSTR];
-
- /*
- * main - parse arguments and handle options
- */
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- int c;
- int errflg = 0;
- extern int optind;
- extern char *optarg;
-
- progname = argv[0];
- getmynames ();
-
- while ((c = getopt(argc, argv, "d")) != EOF)
- switch (c) {
- case 'd':
- ++debug;
- break;
- case '?':
- default:
- errflg++;
- break;
- }
- if (errflg) {
- logit(LOG_CRIT, "Usage: %s [-d]\n", progname);
- exit(2);
- }
-
- if (optind >= argc)
- process();
- #ifdef INETD
- else if (optind == argc - 1) { /* one argument */
- if (sscanf(argv[optind], "%lx.%hd", &from.sin_addr.s_addr,
- &from.sin_port) != 2) {
- logit(LOG_CRIT,
- "in.smtpd: bad arg from inetd: %s\n",
- argv[optind]);
- exit(2);
- }
- from.sin_family = AF_INET;
- from.sin_addr.s_addr = htonl(from.sin_addr.s_addr);
- from.sin_port = htons(from.sin_port);
- process();
- }
- #endif /* INETD */
- else {
- logit(LOG_CRIT, "%s: too many args\n", progname);
- exit(2);
- }
-
- return 0;
- }
-
- /*
- * process - process input file
- */
- process()
- {
- #ifndef INETD
- int s, pid;
- #endif /* INETD */
- struct servent *sp;
-
- sp = getservbyname(SERVNAME, "tcp");
- if (sp == 0) {
- logit(LOG_CRIT, "tcp/%s: unknown service\n", SERVNAME);
- exit(1);
- }
- sin.sin_port = sp->s_port;
-
- #ifdef INETD
- /* connection on fd 0 from inetd */
- doit(0, &from);
- /* NOTREACHED */
- exit(0);
- #else /* INETD */
- #ifndef DEBUG
- if (fork()) /* run in the background */
- exit(0);
- for (s = 0; s < 10; s++) /* close most file descriptors */
- (void) close(s);
- (void) open("/dev/null", 0); /* reopen them on harmless streams */
- (void) dup2(0, 1);
- (void) dup2(0, 2);
- { int tt = open("/dev/tty", 2); /* leave current process group */
- if (tt > 0) {
- #ifdef TIOCNOTTY
- (void) ioctl(tt, TIOCNOTTY, (char *)0);
- #endif
- (void) close(tt);
- }
- }
- #endif /* DEBUG */
- /* create internet socket s; retry 5 times at 5 s. intervals if no luck */
- while ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
- static int nlog = 0;
-
- if (nlog++ <= 5)
- logit(LOG_CRIT, "socket", "");
- sleep(5);
- }
- /* set socket options, notably keepalive */
- if (debug) {
- int debugval = 1;
-
- if (setsockopt(s, SOL_SOCKET, SO_DEBUG,
- (char *)&debugval, sizeof(int)) < 0)
- logit(LOG_CRIT, "setsockopt (SO_DEBUG)", "");
- }
- if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)0, 0) < 0)
- logit(LOG_CRIT, "setsockopt (SO_KEEPALIVE)", "");
- /* bind socket to SERVNAME (SMTP) port; retry as above on failure */
- while (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
- static int nlog = 0;
-
- if (nlog++ <= 5)
- logit(LOG_CRIT, "bind", "");
- sleep(5);
- }
-
- #ifdef BSD
- (void) signal(SIGCHLD, TYPESIG reapchild);
- #else
- (void) signal(SIGCHLD, SIG_IGN); /* SystemV takes care ... */
- #endif
-
- /* listen with 10 input buffers on socket (?) */
- if (listen(s, 10) == -1)
- logit(LOG_CRIT, "listen", "");
- for (;;) {
- int conn, fromlen = sizeof from;
-
- /* get a connection on fd conn; stores src host addr in from */
- conn = accept(s, (struct sockaddr *)&from, &fromlen);
- if (conn < 0) {
- static int nlog = 0;
-
- if (errno == EINTR)
- continue;
- if (++nlog <= 5)
- logit(LOG_CRIT, "accept", "");
- sleep(1);
- continue;
- }
- /* fork a child for this connection */
- if ((pid = fork()) < 0)
- logit(LOG_CRIT, "can't fork!!", "");
- else if (pid == 0) {
- (void) signal(SIGCHLD, SIG_DFL);
- doit(conn, &from); /* listen to SMTP dialogue */
- /* NOTREACHED */
- exit(0);
- }
- (void) close(conn);
- }
- /*NOTREACHED*/
- #endif /* INETD */
- }
-
- #ifndef INETD
- #ifdef BSD
- reapchild()
- {
- union wait status;
-
- /* gross hack! */
- while (wait3(&status, WNOHANG, (struct rusage *)0) > 0);
-
- }
- #endif /* BSD */
- #endif /* INETD */
-
- /*
- * handle some input. never returns.
- */
- doit(f, fromaddr)
- int f;
- struct sockaddr_in *fromaddr; /* internet addr of sending host */
- {
- FILE *fi, *fo;
-
- if ((fi = fdopen(f, "r")) == NULL)
- logit(LOG_CRIT, "fdopen of socket for input", "");
- if ((fo = fdopen(f, "w")) == NULL)
- logit(LOG_CRIT, "fdopen of socket for output", "");
-
- converse(fi, fo, fromaddr);
- /* NOTREACHED */
- return 0;
- }
-
- #ifndef NOBOMB
- bomb(err)
- int err;
- {
- death(err);
- }
- #endif
-
- logit (sev, fmt, str)
- int sev;
- char *fmt, *str;
- {
- #ifdef SYSLOG
- (void) sprintf(logm, "%s: %s", progname, fmt);
- (void) syslog(sev, logm, (str == "" && errno <= sys_nerr)?
- sys_errlist[errno]: str);
- #else
- (void) sprintf(logm, "%s: %s", progname, fmt);
- (void) fprintf(stderr, logm, (str == "" && errno <= sys_nerr)?
- sys_errlist[errno]: str);
- #endif
- }
-