[net.bugs.2bsd] sendmail on 2.9

lmc@denelcor.UUCP (Lyle McElhaney) (03/28/85)

Greetings,

I need to port sendmail from 4.2 onto an 11/44 running 2.9bsd. I am in the
process of doing it now; I would like to know if anyone else has done it.
At first glance, it doesn't seem to be trivial; running the make, the first
lib module, syslog, asked for 3 includes involved in the 4.2 networking
stuff. How hard is it to beat into submission?

Any experience, useful hacks, and notes on how to manipulate the config
files would be appreciated.

One of the reasons for sendmail is that I send considerable batched mail
out from here, and need the batched addressing feature. A question: How does
the sending sendmail know when it is ok to batch? I assume it can only be
done when the receiver is also sendmail; how can the sender know? Right now
I mail from a sendmail machine through a single alias to over 200 people in
a single message; senmail breaks it up and sends each message separately to
our mail interface machine, which does not have sendmail. If I do get sendmail
installed there, will it magically start batching (half of the mail is
routed directly to ihnp4, for instance)?

Thanks for any help.
-- 
Lyle McElhaney
{hao, stcvax, brl-bmd, nbires, csu-cs} !denelcor!lmc

satz@Shasta.ARPA (04/03/85)

I had a few problems bringing sendmail up on 2.9bsd when I did the port
last year.  There were three problems that stand out in my mind:

1) The timeout values needed to be declared and treated as longs. I think
Berkeley has fixed this in the later versions of sendmail.

2) When the socket call fails on 2.9 (which happened quite regularly on
the system I used), errno got trashed and the message would be returned
instead of requeued.

3) daemon.c needed to be rewritten.

Here is the daemon.c that I did.
------------------------------------------------------------------------
# include <errno.h>
# include "sendmail.h"

#ifndef DAEMON
SCCSID(@(#)4.1a_daemon.c	3.35+		12/5/82	(w/o daemon mode));
#else

# include <sys/socket.h>
# include <net/in.h>
# include <netdb.h>
# include <wait.h>

SCCSID(@(#)4.1b_daemon.c	3.35+		12/5/82	(with daemon mode));

/*
**  DAEMON.C -- routines to use when running as a daemon.
**
**	4.1b BSD version -- this version is not well supported.
**
**	This entire file is highly dependent on the 4.1b BSD
**	interprocess communication primitives.  No attempt has
**	been made to make this file portable to Version 7,
**	Version 6, MPX files, etc.  If you should try such a
**	thing yourself, I recommend chucking the entire file
**	and starting from scratch.  Basic semantics are:
**
**	getrequests()
**		Opens a port and initiates a connection.
**		Returns in a child.  Must set InChannel and
**		OutChannel appropriately.
**	clrdaemon()
**		Close any open files associated with getting
**		the connection; this is used when running the queue,
**		etc., to avoid having extra file descriptors during
**		the queue run and to avoid confusing the network
**		code (if it cares).
**	makeconnection(host, port, outfile, infile)
**		Make a connection to the named host on the given
**		port.  Set *outfile and *infile to the files
**		appropriate for communication.  Returns zero on
**		success, else an exit status describing the
**		error.
**
**	The semantics of both of these should be clean.
*/
/*
**  GETREQUESTS -- open mail IPC port and get requests.
**
**	Parameters:
**		none.
**
**	Returns:
**		none.
**
**	Side Effects:
**		Waits until some interesting activity occurs.  When
**		it does, a child is created to process it, and the
**		parent waits for completion.  Return from this
**		routine is always in the child. The file pointers
**		"InChannel" and "OutChannel" should be set to point
**		to the communication channel.
*/

struct sockaddr_in	SendmailAddress;/* internet address of sendmail */
int	DaemonSocket = -1;		/* fd describing socket */
char	*inet_ntoa();

getrequests()
{
	int t;
	union wait status;
	register struct servent *sp;

	/*
	**  Set up the address for the mailer.
	*/

	sp = getservbyname("smtp", "tcp");
	if (sp == NULL)
	{
		syserr("server \"smtp\" unknown");
# ifdef LOG
		if (LogLevel > 0)
			syslog(LOG_SALERT, "cannot get connection");
# endif LOG
		finis();
	}
	SendmailAddress.sin_family = AF_INET;
	SendmailAddress.sin_addr.s_addr = INADDR_ANY;
	SendmailAddress.sin_port = sp->s_port;

	/*
	**  Try to actually open the connection
	*/

	for (;;)
	{
		register int pid;
		struct sockaddr_in otherend;
		extern int RefuseLA;

# ifdef DEBUG
		if (tTd(15, 1))
			printf("getrequests: port 0x%x\n", SendmailAddress.sin_port);
# endif DEBUG

		/* get a socket for the SMTP connection */
		clrdaemon();
		while ((DaemonSocket = socket(SOCK_STREAM, 0, &SendmailAddress, SO_ACCEPTCONN)) < 0)
		{
			/* probably another daemon already */
			syserr("getrequests: can't create socket");
			sleep(30);
		}
# ifdef DEBUG
		if (tTd(15, 1))
			printf("getconnection: %d\n", DaemonSocket);
# endif DEBUG

		/* see if we are rejecting connections */
		while (getla() > RefuseLA)
			sleep(5);

		/* wait for a connection */
		do
		{
			errno = 0;
			t = accept(DaemonSocket, &otherend);
		} while (t < 0 && errno == EINTR);
		if (t < 0)
		{
			syserr("getrequests: accept");
			sleep(5);
			continue;
		}

		/*
		**  Create a subprocess to process the mail.
		*/

# ifdef DEBUG
		if (tTd(15, 1))
			printf("getrequests: forking (fd = %d)\n", DaemonSocket);
# endif DEBUG

		pid = fork();
		if (pid < 0)
		{
			syserr("daemon: cannot fork");
			sleep(10);
			(void) close(DaemonSocket);
			continue;
		}

		if (pid == 0)
		{
			extern struct hostent *gethostbyaddr();
			register struct hostent *hp;
			extern char *RealHostName;	/* srvrsmtp.c */
			char buf[MAXNAME];

			/*
			**  CHILD -- return to caller.
			**	Collect verified idea of sending host.
			**	Verify calling user id if possible here.
			*/

			/* determine host name */
			hp = gethostbyaddr(&otherend.sin_addr, sizeof otherend.sin_addr, AF_INET);
			if (hp != NULL)
				strcpy(buf, hp->h_name);
			else
				strcpy(buf, inet_ntoa(otherend.sin_addr));
			RealHostName = newstr(buf);

			InChannel = fdopen(DaemonSocket, "r");
			OutChannel = fdopen(DaemonSocket, "w");
# ifdef DEBUG
			if (tTd(15, 1))
				printf("getreq: returning\n");
# endif DEBUG
# ifdef LOG
			if (LogLevel > 11)
				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
# endif LOG
			return;
		}

		/*
		**  PARENT -- wait for child to terminate.
		**	Perhaps we should allow concurrent processing?
		*/

# ifdef DEBUG
		if (tTd(15, 1))
		{
			sleep(2);
			printf("getreq: parent waiting\n");
		}
# endif DEBUG

		/* close the port so that others will hang (for a while) */
		(void) close(DaemonSocket);

		/* pick up old zombies */
		while (wait3(&status, WNOHANG, 0) > 0)
			continue;
	}
	/*NOTREACHED*/
}
/*
**  CLRDAEMON -- reset the daemon connection
**
**	Parameters:
**		none.
**
**	Returns:
**		none.
**
**	Side Effects:
**		releases any resources used by the passive daemon.
*/

clrdaemon()
{
	if (tTd(15, 1))
		printf("clrdaemon: %d\n", DaemonSocket);
	if (DaemonSocket >= 0)
		(void) close(DaemonSocket);
	DaemonSocket = -1;
}
/*
**  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
**
**	Parameters:
**		host -- the name of the host.
**		port -- the port number to connect to.
**		outfile -- a pointer to a place to put the outfile
**			descriptor.
**		infile -- ditto for infile.
**
**	Returns:
**		An exit code telling whether the connection could be
**			made and if not why not.
**
**	Side Effects:
**		none.
*/

makeconnection(host, port, outfile, infile)
	char *host;
	u_short port;
	FILE **outfile;
	FILE **infile;
{
	register int s;

	/*
	**  Set up the address for the mailer.
	**	Accept "[a.b.c.d]" syntax for host name.
	*/

	if (host[0] == '[')
	{
		long hid;
		register char *p = index(host, ']');

		if (p != NULL)
		{
			*p = '\0';
			hid = inet_addr(&host[1]);
			*p = ']';
		}
		if (p == NULL || hid == -1)
		{
			usrerr("Invalid numeric domain spec \"%s\"", host);
			return (EX_NOHOST);
		}
		SendmailAddress.sin_addr.s_addr = hid;
	}
	else
	{
		register struct hostent *hp = gethostbyname(host);

		if (hp == NULL)
			return (EX_NOHOST);
		bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
	}

	/*
	**  Determine the port number.
	*/

	if (port != 0)
		SendmailAddress.sin_port = htons(port);
	else
	{
		register struct servent *sp = getservbyname("smtp", "tcp");

		if (sp == NULL)
		{
			syserr("makeconnection: server \"smtp\" unknown");
			return (EX_OSFILE);
		}
		SendmailAddress.sin_port = sp->s_port;
	}

	/*
	**  Try to actually open the connection.
	*/

# ifdef DEBUG
	if (tTd(16, 1))
		printf("makeconnection (%s %x)\n", host, SendmailAddress.sin_port);
# endif DEBUG

	errno = 0;
	s = socket(SOCK_STREAM, 0, 0, 0);
	if (s < 0)
	{
		int saverr = errno;

		syserr("makeconnection: no socket");
		errno = saverr;
		goto failure;
	}

# ifdef DEBUG
	if (tTd(16, 1))
		printf("makeconnection: %d\n", s);
# endif DEBUG
	(void) fflush(CurEnv->e_xfp);			/* for debugging */
	errno = 0;					/* for debugging */
	SendmailAddress.sin_family = AF_INET;
	if (connect(s, &SendmailAddress) < 0)
	{
		/* failure, decide if temporary or not */
	failure:
#ifdef DEBUG
		if (tTd(16, 1))
			printf("makeconnection failed: %d\n", errno);
#endif
		switch (errno)
		{
		  case EISCONN:
		  case ETIMEDOUT:
		  case EINPROGRESS:
		  case EALREADY:
		  case EADDRINUSE:
		  case EHOSTDOWN:
		  case ENETDOWN:
		  case ENETRESET:
		  case ENOBUFS:
		  case ENOTCONN:
		  case ECONNREFUSED:
		  case ECONNRESET:
		  case ECONNABORTED:
		  case EHOSTUNREACH:
		  case ENETUNREACH:
			/* there are others, I'm sure..... */
			return (EX_TEMPFAIL);

		  case EPERM:
			/* why is this happening? */
			syserr("makeconnection: funny failure, addr=%lx, port=%x",
				SendmailAddress.sin_addr.s_addr, SendmailAddress.sin_port);
			return (EX_TEMPFAIL);

		  default:
			return (EX_UNAVAILABLE);
		}
	}

	/* connection ok, put it into canonical form */
	*outfile = fdopen(s, "w");
	*infile = fdopen(s, "r");

	return (EX_OK);
}

/*
**  MYHOSTNAME -- return the name of this host.
**
**	Parameters:
**		hostbuf -- a place to return the name of this host.
**		size -- the size of hostbuf.
**
**	Returns:
**		A list of aliases for this host.
**		NULL if it can't find an alias list.
**
**	Side Effects:
**		none.
*/

char **
myhostname(hostbuf, size)
	char hostbuf[];
	int size;
{
	extern struct hostent *gethostbyname();
	struct hostent *hp;

	gethostname(hostbuf, size);
	hp = gethostbyname(hostbuf);
	if (hp != NULL) {
		strncpy(hostbuf, hp->h_name, size);
		return (hp->h_aliases);
	} else
		return (NULL);
}

# else DAEMON

/*
**  MYHOSTNAME -- stub version for case of no daemon code.
**
**	Can't convert to upper case here because might be a UUCP name.
**
**	Mark, you can change this to be anything you want......
*/

char **
myhostname(hostbuf, size)
	char hostbuf[];
	int size;
{
	register FILE *f;

	hostbuf[0] = '\0';
	f = fopen("/usr/include/whoami", "r");
	if (f != NULL)
	{
		(void) fgets(hostbuf, size, f);
		fixcrlf(hostbuf, TRUE);
		(void) fclose(f);
	}
	return (NULL);
}

#endif DAEMON