#include <pwd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/signal.h>
#include <syslog.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#if (defined(sun) && !defined(SOLARIS)) || defined(sgi)
#include <strings.h>
#else
#include <string.h>
#endif
#include "socks.h"
#include <fcntl.h>
#if defined(AIX)
#include <sys/mode.h>
#endif
#if (defined(sgi) && defined(SYSTYPE_SVR4)) || defined(__FreeBSD__)
#include <sys/stat.h>
#endif

#include "ident.h"
#define IDENTD_TIMEOUT 15 /* 15 seconds */
static int	use_identd = 0;

#define STREQ(a, b)	(strcmp(a, b) == 0)

char socks_cmd[] = "connect";

static char *sockd_conf = SOCKD_CONF;
static char server_version[] = "4.1";
static unsigned short socks_port;
static unsigned short socks_client_port;
extern char	*socks_porttoserv();
extern char	*socks_saddrtoname();
extern	void	socks_mkargs();
extern	int socks_GetAddr();
extern	int socks_GetQuad();
extern	long	socks_GetPort();
extern	int	socks_check_user();
#define NAMELEN	128
char	socks_src_name[NAMELEN], socks_src_user[NAMELEN];
char	socks_real_user[NAMELEN];
char	socks_dst_name[NAMELEN], socks_dst_serv[NAMELEN];

static char log_msg[1024];

#ifdef DEBUG
static char buf[1024];
#endif

static struct config *cfPtr, **cfEntries = NULL;
static int cfNtries = 0;

#ifdef MULTIHOMED_SERVER
static struct config *rtPtr, **rtEntries = NULL;
static int rtNtries = 0;
#endif /* MULTIHOMED_SERVER */

#define BAD_ID_STR	"#BAD_ID:"
#define NO_IDENTD_STR	"#NO_IDENTD:"

#ifdef NOT_THROUGH_INETD
static char bad_id_cmd[1024];
static char no_identd_cmd[1024];
void rereadConfig();
void reapChild();
/* void killChildren(); */
void dumpconf();
#endif /* #ifdef NOT_THROUGH_INETD */

static u_int32	from_in = 0L, from_out = 0L;

/*
**  Current version for response messages
*/
int		Version = 0;

void die()
{
	syslog(LOG_HIGH, "timed-out -- %s", log_msg);
	exit(1);
}


#ifdef FOR_PS
main(argc, argv, envp)
int	argc;
char	*argv[];
char	*envp[];
#else /* FOR_PS not defined */
main(argc, argv)
int	argc;
char	*argv[];
#endif /* #ifdef FOR_PS */
{
	char			c;
	int			inp, in, out, nindex=0;
	int			i, n, len = sizeof(struct sockaddr_in);
	struct sockaddr_in	sin, from, dstsin;
	int			fromlen = sizeof(struct sockaddr_in);
	Socks_t			dst;
	int			one = 1;
	struct servent	*sp;
	int	permit;
#ifdef NOT_THROUGH_INETD
/*
	int pid;
*/
	pid_t pid;
	struct passwd *pw;
	sigset_t mask;
	int devnull;
	char pidstr[32];
	int pidf, pidlen;
#endif

#if defined(FOR_PS) && !defined(SYSV)
#define MAXUSERENVIRON 100
	char *UserEnviron[MAXUSERENVIRON+1];    /* saved user environment */
	extern char **environ;
	char ps_buf[1024];
	char **Argv = NULL;   /* pointer to argument vector */
	char *LastArgv = NULL;        /* end of argv */
#define newstr(s)    strcpy(malloc(strlen(s) + 1), s)
  
	for (i = 0; i < MAXUSERENVIRON && envp[i] != NULL; i++)
		UserEnviron[i] = newstr(envp[i]);
	UserEnviron[i] = NULL;
	environ = UserEnviron;

	/*
	**  Save start and extent of argv for setproctitle.
	*/

	Argv = argv;
	if (i > 0)
		LastArgv = envp[i - 1] + strlen(envp[i - 1]);
	else
		LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
#endif /* FOR_PS && !SYSV */

/* if started by root, change the uid to nobody. */
#ifdef NOT_THROUGH_INETD
/*
	if (getuid() == 0) {
		if((pw = getpwnam("nobody")) == (struct passwd *)0) {
			fprintf(stderr, "Terminated: User nobody not in password file\n");
			exit(1);
		}
		if (setuid(pw->pw_uid) < 0) {
			fprintf(stderr, "Terminated: Can't change uid to nobody\n");
			exit(1);
		}
	}
*/
#endif /* ifdef NOT_THROUGH_INETD */

	socks_port = htons(SOCKS_DEF_PORT);
	bzero((char *)&sin, sizeof(sin));
	bzero((char *)&from, sizeof(from));
	bzero((char *)&dstsin, sizeof(dstsin));

	if (argc >= 2) {
		if (strcmp(argv[1],"-ver") == 0) {
#ifdef MULTIHOMED_SERVER
# ifdef NOT_THROUGH_INETD
		printf(" CSTC multi-homed, stand-alone SOCKS proxy server version %s.\n", CSTC_RELEASE);
# else
		printf(" CSTC multi-homed, inetd-controlled SOCKS proxy server version %s.\n", CSTC_RELEASE);
# endif
#else /* MULTIHOMED_SERVER not defined */
# ifdef NOT_THROUGH_INETD
		printf(" CSTC single-homed, stand-alone SOCKS proxy server version %s.\n", CSTC_RELEASE);
# else
		printf(" CSTC single-homed, inetd-controlled SOCKS proxy server version %s.\n", CSTC_RELEASE);
# endif
#endif /* #ifdef MULTIHOMED_SERVER */

#if defined(SUPPORT_RCMD)
		printf(" Supports clients that use Rrcmd().\n");
#else /* SUPPORT_RCMD not defined */
		printf(" Does not support clients that use Rrcmd().\n");
#endif /* #if defined(SUPPORT_RCMD) */
		exit(1);
		} else if (strcmp(argv[1], "-i") == 0)
			use_identd = 1;
		else if (strcmp(argv[1], "-I") == 0)
			use_identd = 2; /* strict use of identd */
		else ;
	}
	strcpy(socks_real_user,"unknown");

	if ((sp = getservbyname("socks", "tcp")) != NULL)
		socks_port = sp->s_port;

#ifndef LOG_DAEMON
	(void) openlog("sockd", LOG_PID);
#else
	(void) openlog("sockd", LOG_PID, SYSLOG_FAC);
#endif

#ifdef NOT_THROUGH_INETD
    if ((pid = fork()) == -1) {
		syslog(LOG_HIGH, "starup could not fork (%m)!");
        exit(1);
	}
    if (pid)
        exit(0);

	/* we are backgrounded now */
	syslog(LOG_LOW, "sockd server starting");
    (void) setsid();
    (void) chdir("/");
	devnull = open("/dev/null", O_RDWR, 0);

	if (devnull != -1) {
		(void) dup2(devnull, 0);
		(void) dup2(devnull, 1);
		(void) dup2(devnull, 2);
		if (devnull > 2)
			(void) close(devnull);
    }

	/* read the configuration (for non-inetd, once for all future children) */
	if (!readConfig()) {
		syslog(LOG_HIGH, "Terminate");
		exit(1);
	}

#ifdef MULTIHOMED_SERVER
	/* read the route configuration (for non-inetd, once for all future children) */
	if (!readRoute()) {
		syslog(LOG_HIGH, "Terminate");
		exit(1);
	}

#endif /* MULTIHOMED_SERVER */	
	sigemptyset(&mask);
	sigaddset(&mask, SIGCHLD);
	sigprocmask(SIG_UNBLOCK, &mask, NULL);
	/* signal(SIGTERM, killChildren); */
	signal(SIGHUP, rereadConfig);
	signal(SIGCHLD, reapChild);
	signal(SIGUSR1, dumpconf);

	inp  = socket(AF_INET, SOCK_STREAM, 0);
	sin.sin_family = AF_INET;
	sin.sin_port = socks_port;
	sin.sin_addr.s_addr = htonl(INADDR_ANY);

	if (bind(inp, &sin, sizeof(sin)) < 0) {
		syslog(LOG_HIGH, "error -- main bind() %m");
		exit(1);
	}

	if (listen(inp, MAX_CLIENTS) < 0) {
		syslog(LOG_HIGH, "error -- main listen() %m");
		exit(1);
	}
/* Put our process id in /tmp/sockd.pid */
#define PID_FILE "/tmp/sockd.pid"
	if ((pidf = open(PID_FILE, O_RDWR|O_CREAT|O_TRUNC,
		S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) {
		syslog(LOG_HIGH, "Cannot create/access %s", PID_FILE);
	} else {
		sprintf(pidstr, "%d\n", getpid());
		if (write(pidf, pidstr, strlen(pidstr)) < 0)
			syslog(LOG_HIGH, "Cannot write to /%s", PID_FILE);
		close(pidf);
	}
	/* parent stays within this loop */
	for (;;) {
		int pid;
	
		if ((in = accept(inp, &sin, &len)) < 0) {
			if (errno == EINTR) continue;	/* caught SIGCHLD */
			syslog(LOG_HIGH, "error -- main accept() %m");
			exit(1);
		}
	    
		if ((pid = fork()) == 0) break;	/* child */
		if (pid == -1) syslog(LOG_HIGH, "fork failed - %m");
	    /* parent stays in loop */
		close(in);
	}
	/* in child now */
	signal(SIGHUP, SIG_IGN);
	signal(SIGCHLD, SIG_IGN);
	signal(SIGTERM, SIG_DFL);
	closelog();
	close(inp);
#ifndef LOG_DAEMON
	(void) openlog("sockd", LOG_PID);
#else
	(void) openlog("sockd", LOG_PID, SYSLOG_FAC);
#endif	/* LOG_DAEMON */
#else
	in = dup(0);
#endif /* NOT_THROUGH_INETD */

	if (getpeername(in, (struct sockaddr *)&from, &fromlen) < 0) {
		syslog(LOG_HIGH, "error -- unable to get client address.");
		exit(1);
	}
	socks_client_port = ntohs(from.sin_port);
#ifdef DEBUG
	syslog(LOG_LOW, "socks_client_port=%u", socks_client_port);
#endif /* #ifdef DEBUG */

	socks_saddrtoname(&from.sin_addr, socks_src_name, sizeof(socks_src_name));

	if (socks_GetDst(in, &dst) < 0) {
		syslog(LOG_HIGH, "Error in socks_GetDst: %m; from host %s", socks_src_name);
		exit(1);
	}

	if (dst.version != SOCKS_VERSION) {
		syslog(LOG_HIGH, "error -- wrong version (0x%2x) from host %s.",
			dst.version, socks_src_name);
		exit(1);
	}

	if (dst.cmd != SOCKS_CONNECT && dst.cmd != SOCKS_BIND) {
		syslog(LOG_HIGH, "error -- undefined command (0x%2x) from host %s",
			dst.cmd, socks_src_name);
		exit(1);
	}

	dstsin.sin_family      = AF_INET;
	dstsin.sin_addr.s_addr = dst.host;
	dstsin.sin_port        = dst.port;

	while (read(in, &c, 1) == 1)
		if (c == '\0')
			break;
		else {
			if (nindex < sizeof(socks_src_user) - 1)
			socks_src_user[nindex++] = c;
		}
	socks_src_user[nindex] = '\0';
	if (dstsin.sin_addr.s_addr == 0)
		strcpy(socks_dst_name, "Unspecified.Host");
	else
		socks_saddrtoname(&dstsin.sin_addr, socks_dst_name, sizeof(socks_dst_name));
	socks_porttoserv(dstsin.sin_port, socks_dst_serv, sizeof(socks_dst_serv));

	permit = Validate(&from, &dstsin, in);
	if (dst.cmd == SOCKS_CONNECT) {
		strcpy(socks_cmd, "connect");
		sprintf(log_msg, "Connect from %s(%s)@%s to %s (%s)",
			socks_src_user, socks_real_user, socks_src_name, socks_dst_name, socks_dst_serv);
#ifdef FOR_PS
		sprintf(ps_buf, "%s: %s(c) to %s",
			socks_src_user, socks_dst_serv, socks_dst_name);
		setproctitle(ps_buf, Argv, LastArgv);
#endif /* #ifdef FOR_PS */

	} else {
		strcpy(socks_cmd, "bind");
		sprintf(log_msg, "Bind from %s(%s)@%s for %s",
			socks_src_user, socks_real_user, socks_src_name, socks_dst_name);
#ifdef FOR_PS
		sprintf(ps_buf, "%s: %s(b) to %s",
			socks_src_user, socks_dst_serv, socks_dst_name);
		setproctitle(ps_buf, Argv, LastArgv);
#endif /* #ifdef FOR_PS */
	}
	if (permit == 1) 
		;
	else if (permit == 0) {
		syslog(LOG_LOW, "refused -- %s", log_msg);
		exit(1);
	} else if (permit == -1) {
		syslog(LOG_LOW, "cannot connect to identd on %s", socks_src_name);
	} else if (permit == -2) {
		syslog(LOG_LOW, "refused -- %s", log_msg);
		syslog(LOG_LOW, "cannot connect to identd on %s", socks_src_name);
		dst.cmd = SOCKS_NO_IDENTD;
		socks_SendDst(in, &dst);
		exit(1);
	} else if (permit == -3) {
		syslog(LOG_LOW, "refused -- %s", log_msg);
		syslog(LOG_LOW, "*Alert*: real user is %s, not %s", socks_real_user, socks_src_user);
		dst.cmd = SOCKS_BAD_ID;
		socks_SendDst(in, &dst);
		exit(1);
	} else {
		syslog(LOG_HIGH, "refused -- %s", log_msg);
		syslog(LOG_HIGH, "Unexpected result from Validate");
		exit(1);
	}


#ifdef DEBUG
	strcpy(buf, inet_ntoa(from.sin_addr));
	syslog(LOG_LOW,"USER:%s,  SRC:%s,  DST:%s, PORT:%u",
		socks_src_user, buf, inet_ntoa(dstsin.sin_addr),
		ntohs(dstsin.sin_port));
#endif /* DEBUG */

	/*
	**  Kill a connecting off if bind or connect takes too
	**    long to complete
	*/
	signal(SIGALRM, die);
	/*alarm(60*5);*/		/* 5 minutes */
	alarm(60*2);

	if (dst.cmd == SOCKS_CONNECT) {
		DoConnect(in, &dst);
	}
	if (dst.cmd == SOCKS_BIND) {
		DoNewBind(in, &dst);
	}
}

socks_fail(str, in, ndst)
char	*str;
int	in;
Socks_t	*ndst;
{
	syslog(LOG_LOW, "failed -- %s.  Error code: %s %m", log_msg, str);
	ndst->cmd = SOCKS_FAIL;
	socks_SendDst(in, ndst);
	exit(1);
}


/*
** Actually connect a socket to the outside world,
*/
DoConnect(in, dst)
int	in;
Socks_t	*dst;
{
	int			out;
	struct sockaddr_in	sin;
	Socks_t			ndst;
	int	outport = IPPORT_RESERVED - 1;
	int	turnon = 1;

	bzero((char *)&sin, sizeof(sin));
#if defined(SO_OOBINLINE)
	setsockopt(in, SOL_SOCKET, SO_OOBINLINE, &turnon, sizeof(turnon));
#endif
#if defined(SUPPORT_RCMD)
#ifdef DEBUG
	syslog(LOG_LOW, "DoConnect(): client port=%u", socks_client_port);
#endif /* #ifdef DEBUG */
	if ((socks_client_port < IPPORT_RESERVED) && 
		(socks_client_port >= IPPORT_RESERVED/2)) {
		if ((out = rresvport(&outport)) < 0)
			socks_fail("rresvport()", in, &ndst);
#if !defined(SCO) && !defined(ISC)
#if defined(hpux)
		ioctl(out, FIOSSAIOOWN, getpid());
#else /* hpux not defined */
		fcntl(out, F_SETOWN, getpid());
#endif /* #if defined(hpux) */
#endif /* #if !defined(SCO) && !defined(ISC) */
	} else if ((out = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		socks_fail("socket()", in, &ndst);
#else /* SUPPORT_RCMD is not defined */
	if ((out = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		socks_fail("socket()", in, &ndst);
#endif /* #if defined(SUPPORT_RCMD) */

	sin.sin_family = AF_INET;
	sin.sin_port = dst->port;
	sin.sin_addr.s_addr = dst->host;

	ndst.version = Version;
	ndst.cmd = SOCKS_RESULT;

	if (connect(out, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) < 0)
		socks_fail("connect()", in, &ndst);

	syslog(LOG_LOW, "connected -- %s", log_msg);
#if defined(SO_OOBINLINE)
	setsockopt(out, SOL_SOCKET, SO_OOBINLINE, &turnon, sizeof(turnon));
#endif
	socks_SendDst(in, &ndst);
	Pump(in, out);
	syslog(LOG_LOW, "terminated -- %s.", log_msg);
	syslog(LOG_LOW, "%lu bytes from %s, %lu bytes from %s", from_in, socks_src_name, from_out, socks_dst_name);
}

/*
**  Set up a socket to be connected to from the outside world.
**   diffrence between this an the Version1 protocal is that
**   the socket has to be bound from a specific host that
**   is passed.
*/
DoNewBind(in, dst)
int	in;
Socks_t	*dst;
{
	struct in_addr sockd_route();
	int			new, out, len = sizeof(struct sockaddr_in);
	struct sockaddr_in	sin;
	Socks_t			ndst;
	char	dsthost[16];
	char	socks_dst_name[NAMELEN], socks_dst_serv[NAMELEN];
	int	outport = IPPORT_RESERVED - 1;
	int	turnon = 1;

	bzero((char *)&sin, sizeof(sin));
#if defined(SO_OOBINLINE)
	setsockopt(in, SOL_SOCKET, SO_OOBINLINE, &turnon, sizeof(turnon));
#endif

	sin.sin_family = AF_INET;
	ndst.version = Version;
	ndst.cmd  = SOCKS_RESULT;
	sin.sin_port = htons(0);
#ifdef MULTIHOMED_SERVER
	sin.sin_addr = sockd_route(dst->host);
#else
	sin.sin_addr.s_addr = htonl(INADDR_ANY);
#endif

#if defined(SUPPORT_RCMD)
#ifdef DEBUG
	syslog(LOG_LOW, "DoNewBind(): client port=%u", socks_client_port);
#endif /* #ifdef DEBUG */
	if ((socks_client_port < IPPORT_RESERVED) && (socks_client_port >= IPPORT_RESERVED/2)){
		if((out = rresvport(&outport)) < 0)
			socks_fail("rresrvport()", in, &ndst);
#ifdef DEBUG
		syslog(LOG_LOW, "DoNewBind(): outport=%d", outport);
#endif /* #ifdef DEBUG */
#if !defined(SCO) && !defined(ISC)
#if defined(hpux)
		ioctl(out, FIOSSAIOOWN, getpid());
#else /* hpux not defined */
		fcntl(out, F_SETOWN, getpid());
#endif /* #if defined(hpux) */
#endif /* #if !defined(SCO) && !defined(ISC) */
		sin.sin_port = htons((short)outport);
#ifdef DEBUG
		syslog(LOG_LOW, "DoNewBind(): sin.sin_addr=%s, sin.sin_port=%u",
		    inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
#endif /* #ifdef DEBUG */
	} else {
#endif /* #if defined(SUPPORT_RCMD) */
	if ((out = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		socks_fail("socket()", in, &ndst);

	if (bind(out, (struct sockaddr *)&sin, sizeof(sin)) < 0)
		socks_fail("bind()", in, &ndst);
	if (getsockname(out, (struct sockaddr *)&sin, &len) < 0)
		socks_fail("getsockname()", in, &ndst);
#if defined(SUPPORT_RCMD)
	}
#endif /* if defined(SUPPORT_RCMD) */

	ndst.port = sin.sin_port;
	ndst.host = sin.sin_addr.s_addr;

	if (listen(out, 1) < 0)
		socks_fail("listen()", in, &ndst);

	socks_SendDst(in, &ndst);

	len = sizeof(struct sockaddr_in);
	if ((new = accept(out, (struct sockaddr *)&sin, &len)) < 0)
		socks_fail("accept()", in, &ndst);
#if defined(SO_OOBINLINE)
	setsockopt(new, SOL_SOCKET, SO_OOBINLINE, &turnon, sizeof(turnon));
#endif
	close(out);

	if (sin.sin_addr.s_addr == 0)
		strcpy(socks_dst_name, "Unspecified.Host");
	else
		socks_saddrtoname(&sin.sin_addr, socks_dst_name, sizeof(socks_dst_name));
	socks_porttoserv(sin.sin_port, socks_dst_serv, sizeof(socks_dst_serv));

#ifdef SHORTENED_RBIND
	if ((dst->host != 0L) && (sin.sin_addr.s_addr != dst->host)) {
#else
	if (sin.sin_addr.s_addr != dst->host) {
#endif
	{
		struct	in_addr	inaddr;

		inaddr.s_addr = dst->host;
		strncpy(dsthost, inet_ntoa(inaddr), sizeof(dsthost));
	}
		syslog(LOG_LOW, "failed -- %s. Error: connected to wrong host %s (%s)",
			log_msg, socks_dst_name, socks_dst_serv);
		ndst.cmd = SOCKS_FAIL;
		socks_SendDst(in, &ndst);
		exit(1);
	}

	syslog(LOG_LOW, "connected -- %s (%s)", log_msg, socks_dst_serv);
	ndst.port = sin.sin_port;
	ndst.host = sin.sin_addr.s_addr;
	socks_SendDst(in, &ndst);
	Pump(in, new);
	syslog(LOG_LOW, "terminated -- %s (%s).", log_msg, socks_dst_serv);
	syslog(LOG_LOW, "%lu bytes from %s, %lu bytes from %s", from_in, socks_src_name, from_out,  socks_dst_name);
}

/*
**  Now just pump the packets/character through..
*/
Pump(in, out)
int	in, out;
{
	static char		buf[4096];
	fd_set			fds;
	int			s, n, fdsbits;
	static struct timeval	tout = { SOCKS_TIMEOUT, 0 };
/* >>> Andy McFadden fadden@uts.amdahl.com */
 	struct linger ling;	/* for linger */
 	int length;		/* for linger */

	alarm(0);

 	/*
 	 * ATM: use SO_LINGER so it won't hang up on client
 	 */
 	ling.l_onoff = 1;   /* turn it on */
 	ling.l_linger = /*3*/  10;
 	length = sizeof(ling);
 	if (setsockopt(in,  SOL_SOCKET, SO_LINGER, &ling, length) < 0)
 		syslog(LOG_LOW, "setsockopt (SO_LINGER): %m");
 	if (setsockopt(out, SOL_SOCKET, SO_LINGER, &ling, length) < 0)
 		syslog(LOG_LOW, "setsockopt (SO_LINGER): %m");
/* <<< Andy McFadden fadden@uts.amdahl.com */

	FD_ZERO(&fds);
	if (in > out)
		fdsbits = in + 1;
	else
		fdsbits = out +1;

	while (1) {
		tout.tv_sec = SOCKS_TIMEOUT;
		tout.tv_usec = 0;
		FD_SET(in, &fds);
		FD_SET(out, &fds);
		if ((s = select(fdsbits, &fds, NULL,NULL, &tout)) > 0) {
			if (FD_ISSET(in, &fds)) {
				if ((n = read(in, buf, sizeof buf)) > 0) {
					from_in += n;
					if (write(out, buf, n) < 0) {
						goto bad;
					}
				} else {
					goto bad;
				}
			}
			if (FD_ISSET(out, &fds)) {
				if ((n = read(out, buf, sizeof buf)) > 0) {
					from_out += n;
					if (write(in, buf, n) < 0) {
						goto bad;
					}
				} else {
					goto bad;
				}
			}
		} else if ((s == 0) || ((s < 0) && (errno == EINTR))) {
				continue;
		} else {
			syslog(LOG_LOW, "select %m\n");
			goto bad;
		}
	}

bad:
	;	/* Make the goto happy */
}


#ifdef FOR_PS

/*
**  SETPROCTITLE -- set process title for ps
**
**    Parameters:
**	    fmt -- a printf style format string.
**	    a, b, c -- possible parameters to fmt.
**
**    Returns:
**	    none.
**
**      Side Effects:
**	      Clobbers argv of our main procedure so ps(1) will
**	      display the title.
**
**    Stolen from IDA Sendmail - I don't think it's UCB code.
*/

/*VARARGS1*/
setproctitle(buf, Argv, LastArgv)
char *buf;
char **Argv, *LastArgv;
{
#if defined(FOR_PS) && !defined(SYSV)
	register char *p;
	register int i;

	/* make ps print "(sockd)" */
	p = Argv[0];
	*p++ = '-';

	i = strlen(buf);
	if (i > LastArgv - p - 2)
	{
		i = LastArgv - p - 2;
		buf[i] = '\0';
	}
	(void) strcpy(p, buf);
	p += i;
	while (p < LastArgv)
		*p++ = ' ';
#endif /* FOR_PS && !SYSV */
}
#endif /* #ifdef FOR_PS */

#ifdef MULTIHOMED_SERVER

static char *sockd_route_file = SOCKD_ROUTE_FILE;

#ifndef NOT_THROUGH_INETD

struct in_addr sockd_route(dsthost)
u_int32 dsthost;
{
	struct in_addr interface, destip, destmask;

#else /* NOT_THROUGH_INETD defined */

int readRoute()
{
#endif /* #infdef NOT_THROUGH_INETD */
	FILE	*fd;
	static char buf[1024];
	char	*bp;
	int	linenum = 0;
	char	*argv[3];	
	int	argc;
	int	badline = 0;
	int	error = 0;
	
	if ((fd = fopen(sockd_route_file, "r")) == NULL) {
		syslog(LOG_HIGH, "Unable to open routing file (%s): %m", sockd_route_file);
		exit(1);
	}

#ifdef NOT_THROUGH_INETD
	rtEntries = (struct config **) malloc(CONF_INCR *sizeof(struct config **));
	rtPtr = *rtEntries;
#endif /* #ifdef NOT_THROUGH_INETD */
	
	while (fgets(buf, sizeof(buf) - 1, fd) != NULL) {
		linenum++;
		/* Comment starts with # anywhere in the line */
		if ((bp = index(buf, '\n')) != NULL)
			*bp ='\0';
		for (bp = buf; *bp; bp++ ) {
			if (*bp == '#') {
				*bp = '\0';
				break;
			} else if (*bp == '\t')
				*bp = ' ';
		}
		socks_mkargs(buf, &argc, argv, 3);
		if (argc == 0)
			continue;
		if (argc != 3) {
			syslog(LOG_LOW, "Invalid entry at line %d in file %s", linenum, sockd_route_file);
			exit(1);
		}
		
#ifndef NOT_THROUGH_INETD
		if (socks_GetQuad(argv[0], &interface) == -1) {
			syslog(LOG_HIGH, "Illegal interface field at line %d in file %s", linenum, sockd_route_file);
			exit(1);
		}
		if (socks_GetAddr(argv[1], &destip) == -1) {
			syslog(LOG_HIGH, "Illegal destination IP at line %d in file %s", linenum, sockd_route_file);
			exit(1);
		}
		if (socks_GetQuad(argv[2], &destmask) == -1) {
			syslog(LOG_HIGH, "Illegal destination mask at line %d in file %s", linenum, sockd_route_file);
			exit(1);
		}
		if ((destip.s_addr & destmask.s_addr) == (dsthost & destmask.s_addr)) {
			fclose(fd);
			return(interface);
		}
	}
	fclose(fd);
    {
	struct in_addr inaddr;

	inaddr.s_addr = dsthost;
	syslog(LOG_LOW, "Cannot find appropriate interface to communicate with %s\n", inet_ntoa(inaddr));
    }
	exit(1);
}

#else /* NOT_THROUGH_INETD defined */
		/* cache parsed entries */
		if (rtPtr - *rtEntries >= rtNtries) 
		    rtEntries = (struct config **) realloc(rtEntries, (rtNtries +CONF_INCR) *sizeof(struct config **));
		*(rtEntries +rtNtries) = (struct config *) malloc(sizeof (struct config));
		rtPtr = *(rtEntries +rtNtries);
		rtNtries++;

		if (socks_GetQuad(argv[0], &rtPtr->interface) == -1) {
			syslog(LOG_HIGH, "Illegal interface field at line %d in file %s", linenum, sockd_route_file);
			exit(1);
		}
		if (socks_GetAddr(argv[1], &rtPtr->daddr) == -1) {
			syslog(LOG_HIGH, "Illegal destination IP at line %d in file %s", linenum, sockd_route_file);
			exit(1);
		}
		if (socks_GetQuad(argv[2], &rtPtr->dmask) == -1) {
			syslog(LOG_HIGH, "Illegal destination mask at line %d in file %s", linenum, sockd_route_file);
			exit(1);
		}
	}
	syslog(LOG_LOW, "Parsed %d lines in route file", rtNtries);
	fclose(fd);
	if (!rtNtries) {
		syslog(LOG_LOW, "No valid lines found in file %s", sockd_route_file);
		exit(1);
	}
	return 1;
}

struct in_addr sockd_route(dsthost)
u_int32 dsthost;
{
struct in_addr dummy;
int i;

for (i = 0; i < rtNtries; i++) {
	rtPtr = *(rtEntries +i);

	if ((rtPtr->daddr.s_addr & rtPtr->dmask.s_addr) == (dsthost & rtPtr->dmask.s_addr)) {
	return(rtPtr->interface);
	}
}
    {
	struct in_addr inaddr;

	inaddr.s_addr = dsthost;
	syslog(LOG_LOW, "Cannot find appropriate interface to communicate with %s\n", inet_ntoa(inaddr));
    }
	exit(1);
}
#endif /* #ifndef NOT_THROUGH_INETD */

#endif /* #ifdef MULTIHOMED_SERVER */


#ifndef NOT_THROUGH_INETD

check_sp_conf(fd, s, src, dst)
FILE	*fd;
char	*s;
struct sockaddr_in	*src, *dst;
{
	char	buf[1024], *p;
	while (fgets(buf, sizeof(buf) - 1, fd) != NULL) {
		if ((p = index(buf, '\n')) != NULL)
			*p = '\0';
		if (strncmp(s, buf, strlen(s)) == 0) {
			socks_shell_cmd(buf+strlen(s), src, dst);
			break;
		}
	}
	return;
}

Validate(src, dst, in)
struct sockaddr_in      *src, *dst;
int     in;
{
        unsigned short  dst_sin_port = ntohs(dst->sin_port); /* dst->sin_port in host byte order */
	IDENT   *ident_lookup(), *idp;

#else /* NOT_THROUGH_INETD defined */


/* read the config file once after startup and cache all parsed configuration.
* also re-read after receiving a HUP signal.
*/
readConfig()
{
#endif /* #ifndef NOT_THROUGH_INETD */
	FILE		*fd;
	static char	buf[1024];
	char		*bp;
	int		linenum = 0, permit;
	char		*argv[10];
	int		argc;
	struct in_addr	saddr, smask, daddr, dmask;
	unsigned short	dport;
	long	p;
	char		*userlist;
	int		next_arg;
	int		useIdentd;
	char	*cmdp;
	Portcmp tst;

	if ((fd = fopen(sockd_conf, "r")) == NULL) {
		syslog(LOG_HIGH, "Unable to open config file (%s): %m", sockd_conf);
		exit(1);
	}
#ifdef NOT_THROUGH_INETD
	cfEntries = (struct config **) malloc(CONF_INCR *sizeof(struct config **));
	cfPtr = *cfEntries;
	*bad_id_cmd = '\0';
	*no_identd_cmd ='\0';
#endif /* #ifdef NOT_THROUGH_INETD */

	while (fgets(buf, sizeof(buf) - 1, fd) != NULL) {
		linenum++;
		useIdentd = use_identd;
		/*
		**  Comments start with a '#' anywhere on the line
		*/
		cmdp = (char *)0;
		if ((bp = index(buf, '\n')) != NULL)
			*bp = '\0';
		for (bp = buf; *bp != '\0'; bp++) {
			if (*bp == ':') {
				*bp++ = '\0';
				cmdp = bp;
				break;
			} else if (*bp == '#') {
#ifdef NOT_THROUGH_INETD
				if ((*bad_id_cmd == '\0') && !strncmp(bp, BAD_ID_STR, strlen(BAD_ID_STR)))
					strcpy(bad_id_cmd, bp +strlen(BAD_ID_STR));
				if ((*no_identd_cmd == '\0') && !strncmp(bp, NO_IDENTD_STR, strlen(NO_IDENTD_STR)))
					strcpy(no_identd_cmd, bp +strlen(NO_IDENTD_STR));
#endif /* #ifdef NOT_THROUGH_INETD */
				*bp = '\0';
				break;
			} else if (*bp == '\t')
				*bp = ' ';
		}

		socks_mkargs(buf, &argc, argv, 8);
		if (argc == 0)
			continue;
		if ((argc < 3) || (argc > 9)) {
			syslog(LOG_HIGH, "Invalid entry at line %d in file %s", linenum, sockd_conf);
			exit(1);
		}
		
		/* first parse all args */
		if (STREQ(argv[0], "permit")) {
			permit = 1;
		} else if (STREQ(argv[0], "deny")) {
			permit = 0;
		} else {
			syslog(LOG_HIGH, "Invalid permit/deny field at line %d in file %s", linenum, sockd_conf);
			exit(1);
		}

		userlist = (char *)0;
		next_arg = 1;

		if (strncmp(argv[next_arg], "?=", 2) == 0) {
			switch (argv[next_arg++][2]) {
				case 'I':
					useIdentd = 2;
					break;
				case 'i':
					useIdentd = 1;
					break;
				case 'n':
				case '\0':
					useIdentd = 0;
					break;
				default:
					syslog(LOG_HIGH, "Invalid ?= field at line %d in file %s", linenum, sockd_conf);
					exit(1);
			}
		}
		/*
		syslog(LOG_LOW,"useIdentd=%d",useIdentd);
		*/
		if (strncmp(argv[next_arg], "*=", 2) == 0) {
			if (argv[next_arg][2]) {
				userlist = argv[next_arg] + 2;
			}
			next_arg++;
		}
		if(argc <= next_arg+1) {
			syslog(LOG_HIGH, "Invalid entry at line %d in file %s", linenum, sockd_conf);
			exit(1);
		}

		if (socks_GetAddr(argv[next_arg++], &saddr) == -1) {
			syslog(LOG_HIGH, "Illegal source IP at line %d in file %s", linenum, sockd_conf);
			exit(1);
		}
		if (socks_GetQuad(argv[next_arg++], &smask) == -1) {
			syslog(LOG_HIGH, "Illegal source mask at line %d in file %s", linenum, sockd_conf);
			exit(1);
		}

		if ((argc > next_arg + 1) &&
			!(STREQ(argv[next_arg], "eq") || STREQ(argv[next_arg], "neq") ||
			  STREQ(argv[next_arg], "lt") || STREQ(argv[next_arg], "gt") ||
			  STREQ(argv[next_arg], "le") || STREQ(argv[next_arg], "ge"))) {
			if (socks_GetAddr(argv[next_arg++], &daddr) == -1) {
				syslog(LOG_HIGH, "Illegal destination IP at line %d in file %s", linenum, sockd_conf);
				exit(1);
			}
			if (socks_GetQuad(argv[next_arg++], &dmask) == -1) {
				syslog(LOG_HIGH, "Illegal destination mask at line %d in file %s", linenum, sockd_conf);
				exit(1);
			}

		} else {
			daddr.s_addr = 0;
			dmask.s_addr = 0;
		}
		if (argc > next_arg + 1) {
			if (STREQ(argv[next_arg], "eq"))
				tst = e_eq;
			else if (STREQ(argv[next_arg], "neq"))
				tst = e_neq;
			else if (STREQ(argv[next_arg], "lt"))
				tst = e_lt;
			else if (STREQ(argv[next_arg], "gt"))
				tst = e_gt;
			else if (STREQ(argv[next_arg], "le"))
				tst = e_le;
			else if (STREQ(argv[next_arg], "ge"))
				tst = e_ge;
			else {
				syslog(LOG_HIGH, "Invalid comparison at line %d in file %s", linenum, sockd_conf);
				exit(1);
			}
				
			if (((p = socks_GetPort(argv[next_arg+1])) < 0) ||
				(p >= (1L << 16))) {
				syslog(LOG_HIGH, "Invalid port number at line %d in file %s", linenum, sockd_conf);
				exit(1);
			} else {
				dport = p;
			}
		} else {
			tst = e_nil;
			dport = 0;
		}

#ifdef DEBUG
		{
			char msg[1024];
			if (userlist) 
				sprintf(msg,"%s %s 0x%08x 0x%08x 0x%08x 0x%08x %s %u",
					permit ? "permit" : "deny",
					userlist,
					saddr.s_addr, smask.s_addr, daddr.s_addr, dmask.s_addr,
						tst == e_eq ? "==" :
						tst == e_neq ? "!=" :
						tst == e_lt ? "<" :
						tst == e_gt ? ">" :
						tst == e_le ? "<=" :
						tst == e_ge ? ">=" : "NIL",
						dport);
			else
				sprintf(msg,"%s 0x%08x 0x%08x 0x%08x 0x%08x %s %u",
					permit ? "permit" : "deny",
					saddr.s_addr, smask.s_addr, daddr.s_addr, dmask.s_addr,
						tst == e_eq ? "==" :
						tst == e_neq ? "!=" :
						tst == e_lt ? "<" :
						tst == e_gt ? ">" :
						tst == e_le ? "<=" :
						tst == e_ge ? ">=" : "NIL",
						dport);
			syslog(LOG_LOW, "%s", msg);
		}
#endif /* DEBUG */


#ifndef NOT_THROUGH_INETD
	/* comparisons of port numbers must be done in host order */

		if((saddr.s_addr & smask.s_addr) == (src->sin_addr.s_addr & smask.s_addr) &&
		    (daddr.s_addr & dmask.s_addr) == (dst->sin_addr.s_addr & dmask.s_addr) &&
		    socks_check_user(userlist, socks_src_user)) {
			if (tst == e_nil) 
				goto GotIt;
			if ((tst == e_eq) && (dst_sin_port == dport))
				goto GotIt;
			if ((tst == e_neq) && (dst_sin_port != dport))
				goto GotIt;
			if ((tst == e_lt) && (dst_sin_port < dport))
				goto GotIt;
			if ((tst == e_gt) && (dst_sin_port > dport))
				goto GotIt;
			if ((tst == e_le) && (dst_sin_port <= dport))
				goto GotIt;
			if ((tst == e_ge) && (dst_sin_port >= dport))
				goto GotIt;
		}
	}

	fclose(fd);
	return 0;

GotIt:
	if ((useIdentd == 0) || (permit == 0)) {
		fclose(fd);
		if (cmdp != (char *)0) {
			socks_shell_cmd(cmdp, src, dst);
		}
		return permit;
	}
	fseek(fd, 0L, 0);
	if ((idp = ident_lookup(in, IDENTD_TIMEOUT)) == ((IDENT *)0)) {
		check_sp_conf(fd, NO_IDENTD_STR, src, dst);
		permit = -useIdentd;
	} else {
		strncpy(socks_real_user, idp->identifier, sizeof(socks_real_user));
		if (strcmp(socks_src_user, socks_real_user)) {
#if defined(SUPPORT_RCMD)
			if ((socks_client_port >= IPPORT_RESERVED) ||
			   (socks_client_port < IPPORT_RESERVED/2) ||
			   strcmp(socks_src_user, "root")) {
#endif /* #if defined(SUPPORT_RCMD) */
			check_sp_conf(fd, BAD_ID_STR, src, dst);
			permit = -3;
#if defined(SUPPORT_RCMD)
			}
#endif /* #if defined(SUPPORT_RCMD) */
		}
	}
	ident_free(idp);
	fclose(fd);
	if ((permit >= -1) && cmdp)
		socks_shell_cmd(cmdp, src, dst);
	return permit;
}

#else /* NOT_THROUGH_INETD defined */
		/* save all parsed arguments */
		
		if (cfPtr - *cfEntries >= cfNtries) 
			cfEntries = (struct config **) realloc(cfEntries, (cfNtries +CONF_INCR) *sizeof(struct config **));
		*(cfEntries +cfNtries) = (struct config *) malloc(sizeof (struct config));
		cfPtr = *(cfEntries +cfNtries);
		cfNtries++;
		cfPtr->action = permit;
		cfPtr->use_identd = useIdentd;
		cfPtr->tst = tst;
		if (userlist) {
			cfPtr->userlist = (char *) malloc(strlen(userlist) +1);
			strcpy(cfPtr->userlist, userlist);
		} else
			cfPtr->userlist = NULL;
		cfPtr->saddr.s_addr = saddr.s_addr;
		cfPtr->smask.s_addr = smask.s_addr;
		cfPtr->daddr.s_addr = daddr.s_addr;
		cfPtr->dmask.s_addr = dmask.s_addr;
		if (cmdp) {
			cfPtr->cmdp = (char *) malloc(strlen(cmdp) +1);
			strcpy(cfPtr->cmdp, cmdp);
		} else cfPtr->cmdp = NULL;
		cfPtr->dport = dport;
	}
	syslog(LOG_LOW, "parsed %d config lines", cfNtries);

	fclose(fd);
	if (!cfNtries) {
		syslog(LOG_HIGH, "no valid lines found in file %s", sockd_conf);
		exit(1);
	}
	return 1;
}


void rereadConfig()
{
	int i;
	int fd;

	syslog(LOG_LOW, "Received SIGHUP, rereading config file");
	if (cfNtries) {
		for (i = 0; i < cfNtries; i++) {
			cfPtr = *(cfEntries +i);
			if (cfPtr->userlist) free(cfPtr->userlist);
			if (cfPtr->cmdp) free(cfPtr->cmdp);
			free(cfPtr);
		}
		free(cfEntries);
		cfNtries = 0;
	}
	if (!readConfig())
		syslog(LOG_HIGH, "Terminate");
#ifdef MULTIHOMED_SERVER
	syslog(LOG_LOW, "Rereading route file");
	if (rtNtries) {
		for (i = 0; i < rtNtries; i++) {
			rtPtr = *(rtEntries +i);
			free(rtPtr);
		}
		free(rtEntries);
		rtNtries = 0;
	}
	if (!readRoute())
		syslog(LOG_HIGH, "Terminate");
#endif /* MULTIHOMED_SERVER */
}

/* system V machines may have to reset the signal here */
void reapChild()
{
	int status;

#if defined(NO_WAITPID)
	while (wait3(&status, WNOHANG, NULL) > 0) ;
#else
	while (waitpid(-1, &status, WNOHANG) > 0) ;
#endif
	signal(SIGCHLD, reapChild);
}
/*
void killChildren()
{
	kill(0, SIGTERM);
	exit();
}
*/

Validate(src, dst, in)
struct sockaddr_in	*src, *dst;
int	in;
{
	int		permit;
	unsigned short	dst_sin_port = ntohs(dst->sin_port); /* dst->sin_port in host byte order */
	IDENT	*ident_lookup(), *idp;
	int	i;

	for (i = 0; i < cfNtries; i++) {
		cfPtr = *(cfEntries +i);
		
	/* comparisons of port numbers must be done in host order */
		if((cfPtr->saddr.s_addr & cfPtr->smask.s_addr) == (src->sin_addr.s_addr & cfPtr->smask.s_addr) &&
			(cfPtr->daddr.s_addr & cfPtr->dmask.s_addr) == (dst->sin_addr.s_addr & cfPtr->dmask.s_addr) &&
			socks_check_user(cfPtr->userlist, socks_src_user)) {
			if (cfPtr->tst == e_nil) 
				goto GotIt;
			if ((cfPtr->tst == e_eq) && (dst_sin_port == cfPtr->dport))
				goto GotIt;
			if ((cfPtr->tst == e_neq) && (dst_sin_port != cfPtr->dport))
				goto GotIt;
			if ((cfPtr->tst == e_lt) && (dst_sin_port < cfPtr->dport))
				goto GotIt;
			if ((cfPtr->tst == e_gt) && (dst_sin_port > cfPtr->dport))
				goto GotIt;
			if ((cfPtr->tst == e_le) && (dst_sin_port <= cfPtr->dport))
				goto GotIt;
			if ((cfPtr->tst == e_ge) && (dst_sin_port >= cfPtr->dport))
				goto GotIt;
		}
	}
	return 0;

	GotIt:
	permit = cfPtr->action;

	if ((cfPtr->use_identd == 0) || (permit == 0)) {
		if (cfPtr->cmdp != (char *)0) {
			socks_shell_cmd(cfPtr->cmdp, src, dst);
		}
		return permit;
	}
	if ((idp = ident_lookup(in, IDENTD_TIMEOUT)) == ((IDENT *)0)) {
		if (*no_identd_cmd) socks_shell_cmd(no_identd_cmd, src, dst);
		permit = -cfPtr->use_identd;
	} 
	else {
		strncpy(socks_real_user, idp->identifier, sizeof(socks_real_user));
		if (strcmp(socks_src_user, socks_real_user)) {
#if defined(SUPPORT_RCMD)
			if ((socks_client_port >= IPPORT_RESERVED) ||
			   (socks_client_port < IPPORT_RESERVED/2) ||
			   strcmp(socks_src_user, "root")) {
#endif /* SUPPORT_RCMD */
			if (*bad_id_cmd) socks_shell_cmd(bad_id_cmd, src, dst);
			permit = -3;
#if defined(SUPPORT_RCMD)
			}
#endif /* SUPPORT_RCMD */
		}
	}
	ident_free(idp);
	if ((permit >= -1) && cfPtr->cmdp)
		socks_shell_cmd(cfPtr->cmdp, src, dst);
	return permit;
}

void dumpconf()
{
	char buf[1024], *bp = buf;
	char buf2[30];
	int i, t;
	struct in_addr addr;
	
	syslog(LOG_HIGH, "Received SIGUSR1\n");
	t = cfNtries;
	if (*no_identd_cmd) t++;
	if (*bad_id_cmd) t++;
	syslog(LOG_HIGH,"Effective sockd.conf entries: %d",t);
	for (i = 0; i < cfNtries; i++) {
		*bp = '\0';
		buf2[0] = '\0';
		cfPtr = *(cfEntries + i);
		strcat(bp, cfPtr->action ? "permit " : "deny ");
		t = cfPtr->use_identd;
		strcat(bp, t == 0 ? "?=n " :
				t == 1 ? "?=i " :
				t == 2 ? "?=I " :
				"?=*badvalue* ");
		if (cfPtr->userlist != NULL) {
			strcat(bp, "*=");
			strcat(bp, cfPtr->userlist);
			strcat(bp, " ");
		}
		addr.s_addr = cfPtr->saddr.s_addr;
		strcat(bp, inet_ntoa(addr));
		strcat(bp, " ");
		addr.s_addr = cfPtr->smask.s_addr;
		strcat(bp, inet_ntoa(addr));
		strcat(bp, " ");
		addr.s_addr = cfPtr->daddr.s_addr;
		strcat(bp, inet_ntoa(addr));
		strcat(bp, " ");
		addr.s_addr = cfPtr->dmask.s_addr;
		strcat(bp, inet_ntoa(addr));
		strcat(bp, " ");
	
		switch (cfPtr->tst) {
		case e_lt:
			sprintf(buf2,"lt %d ", cfPtr->dport);
			break;
		case e_gt:
			sprintf(buf2,"gt %d ", cfPtr->dport);
			break;
		case e_eq:
			sprintf(buf2,"eq %d ", cfPtr->dport);
			break;
		case e_neq:
			sprintf(buf2,"neq %d ", cfPtr->dport);
			break;
		case e_le:
			sprintf(buf2,"le %d ", cfPtr->dport);
			break;
		case e_ge:
			sprintf(buf2,"ge %d ", cfPtr->dport);
			break;
		case e_nil:
			break;
		default:
			sprintf(buf2, "*badcmp* %d ", cfPtr->dport);
		}
		strcat(bp, buf2);
	
		if (cfPtr->cmdp) {
			strcat(bp, ": ");
			strcat(bp, cfPtr->cmdp);
		}
		syslog(LOG_HIGH, "CF%d>>%s<<",i+1,bp);
	}
	if (*no_identd_cmd)
		syslog(LOG_HIGH, "CF%d>>%s %s<<", i+1, NO_IDENTD_STR, no_identd_cmd);
	if (*bad_id_cmd)
		syslog(LOG_HIGH, "CF%d>>%s %s<<", i+2, BAD_ID_STR, bad_id_cmd);
#ifdef MULTIHOMED_SERVER
	syslog(LOG_HIGH,"Effective sockd.route entries: %d",rtNtries);
	for (i = 0; i < rtNtries; i++) {
		*bp = '\0';
		rtPtr = *(rtEntries + i);
		addr.s_addr = rtPtr->interface.s_addr;
		strcat(bp, inet_ntoa(addr));
		strcat(bp, " ");
		addr.s_addr = rtPtr->daddr.s_addr;
		strcat(bp, inet_ntoa(addr));
		strcat(bp, " ");
		addr.s_addr = rtPtr->dmask.s_addr;
		strcat(bp, inet_ntoa(addr));
		syslog(LOG_HIGH, "RT%d>>%s<<\n",i+1,bp);
	}
#endif /* #ifdef MULTIHOMED_SERVER */
}

#endif /* #ifdef NOT_THROUGH_INETD */
