/*
 *
 *	PMNETD
 *	PortMaster NETdata Daemon
 *
 *
 *	Lucent Technologies Remote Access
 *	4464 Willow Road
 *	Pleasanton, CA   94588
 *
 *	Copyright 1998 Lucent Technologies, Inc. All Rights Reserved.
 *
 *	This software is provided under license from Lucent
 *	Technologies, Inc., the terms and conditions of which are set
 *	forth in a Software License Agreement that is contained in an
 *	End User Agreement contained in the product packaging, and
 *	electronically on the Lucent Remote Access ftp site. This
 *	software may only be used in conjunction with Lucent (or Lucent
 *	authorized) PortMaster(R) products.  Lucent makes no warranties
 *	to any licensee concerning the applicability of the software to
 *	licensee's specific requirements or the suitability of the
 *	software for any intended use.  Licensee shall not remove,
 *	modify or alter any copyright and/or other proprietary rights
 *	notice and must faithfully reproduce all such notices on any
 *	copies or modifications to this software that it makes.
 *
 *	Lucent Technologies, Inc. makes no representations about the
 *	suitability of this software for any purpose.  It is provided
 *	"as is" without express or implied warranty.
 *
 */


/************************************************************************

1998/09/18

		PMNETD: PortMaster Netdata Daemon

The pmnetd program was written to replace the in.pmd program and is
likely to work as a substitute in almost all situations.  This program
is not supported by Lucent Remote Access but is provided for the
convenience of our customers.

If you port pmnetd to any other systems or fix any bugs in it and would
like to share those with us, please send email to
pmnetd-feedback@livingston.com.  If you make changes in your copy of
pmnetd.c please update the version string that the -v flag prints.

pmnetd provides a gateway between a pseudo-tty and a Lucent
PortMaster(R) serial port, usually connected to a modem or printer.


	Running pmnetd

By default, pmnetd runs in the background.

You can use the following command line options with pmnetd:

	-v			Returns version information
	-x			Program stays in foreground and dumps all
				debug information to stdout
	-d [device file]	Specifies the device file
				(default is /etc/pmnet/devices)
	-l [log file]		Logs debug messages to the file specified

The /etc/pmnet/devices file has the following format:

#Device		Pin?	Sec?	PortMaster	Port	Username	Password

/dev/ttyr1	no	no	192.168.5.10	6001
/dev/ttyr2	no	no	192.168.5.10	6002
/dev/ttyr3	yes	no	192.168.5.10	6003
/dev/ttyra	no	no	testing.edu.com	1234
/dev/ttyrb	yes	no	1.1.1.1		1234
/dev/ttyrc	no	no	2.2.2.2
/dev/ttyr4	yes	yes	192.168.5.10	23	testu		testp
/dev/ttyr5	no	no	dial.edu.com	7000

"Device" column - Place the full path to the pseudo-tty device to map.

"Pin" column - Enter either "yes" or "no" to inform pmnetd to keep this
connection always active ("pinned up"), or to connect to the device
only when a tty open is detected and disconnect when a tty close is
detected.

"Sec" column - Enter either "yes" or "no" to inform pmnetd whether this
is an "outbound" security port on the PortMaster or not.  If you enter
"yes", pmnetd does the following:
1. Expects a login prompt when it connects to the PortMaster 
   at the specified port, and sends the login specified
2. Expects a password prompt, and sends the password specified
3. Expects a message from the PortMaster that it has established a 
   connection to a port

If Sec is set to "yes" then Pin should be set to "yes" as well.
For more information, see technical notes on "Outbound Security."

"PortMaster" column - Enter the IP address or hostname of the PortMaster.

"Port" column - Enter the port to connect to.  If "Sec" is set to no, enter
the  Netdata TCP port set on the serial port of the PortMaster.  If Sec
is set to "yes", enter the port number 23.

The "Username" and "Password" fields should be filled in when using the
outbound security option.


	Compiling pmnetd

To compile pmnetd.c, define one of the following OS_ defines, depending
on your operating system.  Here is an example for SunOS or Solaris using
gcc:

  gcc -DOS_SUNOS -o pmnetd pmnetd.c

The following defines are available, although some have not been tested:

Define		Tested?		OS Versions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OS_LINUX	TESTED: WORKS	Linux 1.0 and 2.0
OS_SUNOS	TESTED: WORKS	SunOS 4.1.4 and Solaris 5.5.1
OS_SYS_BSD	TESTED: WORKS	BSDI 2.0 and 3.0
OS_HPUX		TESTED: WORKS	HP-UX B.10.01
OS_RS6000	UNTESTED
OS_MIPS		UNTESTED

________________________________________________________________________

        Copyright and Trademarks

Copyright 1998 Lucent Technologies. All rights reserved.

PortMaster, ComOS, and ChoiceNet are registered trademarks of Lucent
Technologies, Inc. RADIUS ABM, PMVision, PMconsole, and IRX are
trademarks of Lucent Technologies, Inc. ProVision is a service mark of
Lucent Technologies, Inc. All other marks are the property of their
respective owners.

        Notices

Lucent Technologies, Inc. makes no representations or warranties
with respect to the contents or use of this publication, and specifically
disclaims any express or implied warranties of merchantability or
fitness for any particular purpose. Further, Lucent Technologies,
Inc. reserves the right to revise this publication and to make changes
to its content, any time, without obligation to notify any person or
entity of such revisions or changes.

        Contacting Lucent Remote Access Technical Support

This software is not supported by Lucent, but you can email suggestions
to pmnetd-feedback@livingston.com

************************************************************************/

static char sccsid[] =
"$Id: pmnetd.c,v 1.1 1998/09/18 10:55:30 cdr Exp $ Copyright 1998 Lucent Technologies Inc";

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <ctype.h>
#include <netdb.h>
#include <errno.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/dir.h>
#include <sys/ioctl.h>
#include <netinet/in.h>

#if defined(OS_RS6000) | defined(OS_LINUX)
#include <dirent.h>
#endif

#if defined(OS_HPUX)
#include <termios.h>
#include <sys/bsdtty.h>
#include <sys/ptyio.h>
#endif

#define	VERSION			"1.5"
#define MAXLEN			1024
#define DEFAULT_DEVICE_LIST	"/etc/pmnet/devices"
#define MATCH_MAX		5
#define MATCH_LEN		80
#define CONNECT_TIME		5	/* # secs to wait between connecttrys */
#define IDLE_TIMEOUT		30	/* # secs to wait until dropping a
					   non-pinned connection */
#define NBSIZE		4096

#if defined(OS_HPUX)
#define TIOCTCNTL	TIOCPKT
#define MAJOR_TTY	17
#define MAJOR_PTY	16
#else /* HPUX */
#define MAJOR_TTY	20
#define MAJOR_PTY	21
#endif /* HPUX */

#if defined(OS_RS6000) | defined(OS_LINUX)
#define direct dirent
#endif

#if defined(OS_RS6000) | defined(OS_MIPS) | defined(OS_ULTRIX) | defined(OS_SYS_BSD)
#define TIOCTCNTL       TIOCPKT
#endif

#if defined(OS_MIPS)
#define NO_SETSID	1
#endif /* OS_MIPS */

#if !defined(O_NOCTTY)
#define O_NOCTTY	0
#endif

#ifdef OS_SYS_BSD
#undef MAJOR_TTY
#undef MAJOR_PTY
#define MAJOR_TTY	5
#define MAJOR_PTY	6
#endif /* SYS_BSD */

struct PMNET_Global_Information
{
	int	ReStart;
	char	*progname;
	FILE	*dout;
} gi;

struct PortMaster_List
{
	char ttyname[MAXLEN];
	char ptyname[MAXLEN];
	int ptyfd;
	int netfd;
	char ipaddr[17];
	int port;
	int pin;
	int open;
	int security;
	char username[17];
	char password[17];
	time_t lt;
	time_t la;
	char to_buf[(MAXLEN*2)];
	char from_buf[(MAXLEN*2)];
	struct PortMaster_List *next;
	struct PortMaster_List *prev;
} pmstruct, *pmfirst, *pmlast, *pmcur, *pmnew;

char	PrevLine[MAXLEN];
char	LastLine[MAXLEN];
char	device_fn[MAXLEN];
int	_PmListInit;
int	TimedOut;

struct	PortMaster_List *begin_pmlist();
struct	PortMaster_List *new_pmlist();
struct	PortMaster_List *add_pm(char ttyname[MAXLEN], char ipaddr[17], int port, int pin, int security, char username[17], char password[17]);
void	GeneralSetup(char dfn[256]);
void	sig_fatal(int sig);
void	sig_hup(int sig);
void	Debug(char txt[256]);
void	free_pmlist();
void	usage();
void	ResetGlobalInfo();
void	PipeLoop();
void	ConnectPinnedNETfds();
void	DisconnectPortMaster(struct PortMaster_List *pml);
void	CreatePTYs();
char	*Version();
char	*ResolveDNS(char hostname[256]);
char	*findpty(char *ttyname);
char	*dev2pty(dev_t devno);
int	DoRead(int fd, int timeout, int matches,
					char txt[MATCH_MAX][MATCH_LEN]);
int	DoWrite(int fd, char *txt1, char *txt2);
int	main(int argc, char **argv);
int	LoadDevices(char fn[256]);
int	ConnectPortMaster(struct PortMaster_List *pml);
int	getpty(struct PortMaster_List *pmcur);
int	SecureLogin(struct PortMaster_List *pml);
#ifdef OS_HPUX
int	getdtablesize();
#endif

void usage()
{
	printf("Usage: %s [-v] [-x] [-d device file] [-l logfile]\n", gi.progname);
	exit(0);
}

void ResetGlobalInfo()
{
	gi.ReStart = 0;
	gi.progname = NULL;
	gi.dout = NULL;
}

char *Version()
{
	char *ret;

	ret = malloc(256);
	sprintf(ret, "Livingston PortMaster netdata daemon, version %s\n", VERSION);
	return (char *)ret;
}

int main(int argc, char **argv)
{
	ResetGlobalInfo();

	strcpy(device_fn, DEFAULT_DEVICE_LIST);
	gi.progname = (char *)*argv;
	argc--;
	argv++;
	while(argc)
	{
		if (**argv != '-')
			usage();
		switch( *( *argv+1 ) )
		{
			case 'v':
				printf("%s", Version());
				exit(0);
			case 'x':
				gi.dout = stdout;
				break;
			case 'l':
				argc--;
				argv++;
				if (argc == 0)
					usage();
				gi.dout = fopen(*argv, "at");
				if (!gi.dout)
				{
					printf("%s: Error: Could not open %s for append!\n", gi.progname, *argv);
					exit(0);
				}
				break;
			case 'd':
				argc--;
				argv++;
				if (argc == 0)
					usage();
				strcpy(device_fn, *argv);
				break;
			default:
				usage();
		}
		argc--;
		argv++;
	}
	if (gi.dout != stdout)
	{
		if (fork() > 0)
			exit(0);
	}
	signal(SIGHUP, sig_hup);
	signal(SIGINT, sig_fatal);
	signal(SIGQUIT, sig_fatal);
	signal(SIGILL, sig_fatal);
	signal(SIGTRAP, sig_fatal);
	signal(SIGIOT, sig_fatal);
	signal(SIGFPE, sig_fatal);
	signal(SIGTERM, sig_fatal);
	GeneralSetup(device_fn);
	PipeLoop();
	return 0;
}

void GeneralSetup(char dfn[256])
{
	char tmp[256];
	int ret;

	Debug(Version());
	sprintf(tmp, "Loading Devices from %s\n", dfn);
	Debug(tmp);
	ret = LoadDevices(dfn);
	sprintf(tmp, "Loaded %d devices:\n", ret);
	Debug(tmp);
	if (ret == 0)
	{
		Debug("No Devices loaded, exiting...\n");
		exit(0);
	}
	pmcur = pmfirst;
	while(pmcur != NULL)
	{
		sprintf(tmp, "%s [%s, Port %d]", pmcur->ttyname,
			pmcur->ipaddr, pmcur->port);
		if (pmcur->pin == 1)
			strcat(tmp, " Pinned UP.");
		if (pmcur->security == 1)
		{
			strcat(tmp, " Security ON: U:");
			strcat(tmp, pmcur->username);
			strcat(tmp, " P:");
			strcat(tmp, pmcur->password);
		}
		strcat(tmp, "\n");
		Debug(tmp);	
		pmcur = pmcur->next;
	}
	CreatePTYs();
	ConnectPinnedNETfds();
}

void sig_fatal(int sig)
{
	char tmp[256];

	sprintf(tmp, "Exiting on signal %d\n", sig);
	Debug(tmp);
	pmcur = pmfirst;
	while(pmcur != NULL)
	{
		if (pmcur->netfd > 0)
			close(pmcur->netfd);
		if (pmcur->ptyfd > 0)
			close(pmcur->ptyfd);
		pmcur = pmcur->next;
	}
	free_pmlist();
	exit(1);
}

void sig_hup(int sig)
{
	gi.ReStart = 1;
}

void Debug(char txt[256])
{
	long t;
	char tmp[30];

	if (gi.dout == NULL)
		return;
	time(&t);
	strcpy(tmp, (char *)ctime(&t));
	tmp[strlen(tmp)-1] = '\0';
	fprintf(gi.dout, "[%s] %s", tmp, txt);
	fflush(gi.dout);
	return;
}

void ADebug(char txt[256])
{
	if (gi.dout == NULL)
		return;
	fprintf(gi.dout, "%s", txt);
	fflush(gi.dout);
	return;
}

int LoadDevices(char fn[256])
{
	FILE *f1;
	char tmp[256], *p;
	char s[256], s1[30], s2[30], s3[30], s4[30], s5[30], s6[30], s7[30];
	struct PortMaster_List *newpm;
	int ret, num, lnum, pin, security;

	f1 = fopen(fn, "rt");
	if (!f1)
	{
		sprintf(tmp, "Error opening %s for read\n", fn);
		Debug(tmp);
		exit(0);
	}
	num = 0;
	lnum = 0;
	while(fgets(s, 256, f1) != NULL)
	{
		lnum++;
		if (strlen(s) < 5)
			continue;
		if (s[0] == '#')
			continue;
		if (s[strlen(s)-1] == '\r' || s[strlen(s)-1] == '\n')
			s[strlen(s)-1] = '\0';
		ret = sscanf(s, "%s %s %s %s %s %s %s", s1, s2, s3, s4, s5,
			s6, s7);
		if (ret != 5 && ret != 7)
		{
			sprintf(tmp, "Parse error, line %d: Not enough arguments! Skipping\n", lnum);
			Debug(tmp);
			continue;
		}
		p = (char *)ResolveDNS(s4);
		if (p == NULL)
		{
			sprintf(tmp,  "Parse error, line %d: Unable to resolve DNS on %s, skipping\n", lnum, s4);
			Debug(tmp);
			continue;
		}
		strcpy(s4, p);
		tolower(s2[0]);
		tolower(s3[0]);
		if (s2[0] == 'y')
			pin = 1;
		else
			pin = 0;
		if (s3[0] == 'y')
			security = 1;
		else
			security = 0;
		if (ret != 7 && security == 1)
		{
			sprintf(tmp, "Parse error, line %d: No username/password specified for security! Skipping\n", lnum);
			Debug(tmp);
			continue;
		}
		else
		{
			if (s6[0] == '\"' && s6[strlen(s6)-1] == '\"')
			{
				strcpy(tmp, s6);
				strcpy(s6, &tmp[1]);
				s6[strlen(s6)-1] = '\0';
			}
			if (s7[0] == '\"' && s7[strlen(s7)-1] == '\"')
			{
				strcpy(tmp, s7);
				strcpy(s7, &tmp[1]);
				s7[strlen(s7)-1] = '\0';
			}
		}
		if (strncmp(s1, "/dev/", 5))
		{
			sprintf(tmp, "Parse error, line %d: Device does not start with /dev/...Skipping\n", lnum);
			Debug(tmp);
			continue;
		}
		if (ret == 5)
			newpm = add_pm(s1, s4, atoi(s5), pin, security, "", "");
		else if (ret == 7)
			newpm = add_pm(s1, s4, atoi(s5), pin, security, s6, s7);
		if (newpm != NULL)
			num++;
	}
	fclose(f1);
	return num;
}

char *ResolveDNS(char hostname[256])
{
	struct hostent *hp;
	long maddr, xbyte;
	char *ipaddr = NULL;
	int addr_byte[4], i, len, ipchk;

	if (ipaddr != NULL)
		free(ipaddr);
	ipaddr = malloc(17);
	len = strlen(hostname);
	ipchk = 0;
	for(i = 0;i < len;i++)
		if (!isdigit(hostname[i]) && hostname[i] != '.')
		{
			ipchk = 1;
			break;
		}
	if (ipchk == 0)
		strcpy(ipaddr, hostname);
	else
	{
		hp = gethostbyname(hostname);
		if (hp == 0)
			return(NULL);
		maddr = (long)ntohl(*(long *)hp->h_addr);
		for(i = 0;i < 4;i++)
		{
			xbyte = maddr >> (i*8);
			xbyte = xbyte & (long)0x000000FF;
			addr_byte[i] = xbyte;
		}
		sprintf(ipaddr, "%u.%u.%u.%u", addr_byte[3], addr_byte[2], addr_byte[1],
			addr_byte[0]);
	}
	return (char *)ipaddr;
}

void GotAlarm(int sig)
{
	TimedOut = 1;
}

/* Read in text from FD until we receive a match in txt[][] */
int DoRead(int fd, int timeout, int matches, char txt[MATCH_MAX][MATCH_LEN])
{
	fd_set	fds;
	struct timeval tv;
	char line[MAXLEN+1];
	int i, len, pos, ret;

	signal(SIGALRM, GotAlarm);
	TimedOut = 0;
	alarm(timeout);

	for(;;)
	{	
		line[0] = '\0';
		pos = -1;
		len = 1;
		while(len > 0 && line[pos] != '\n' && line[pos] != '\r')
		{
			FD_ZERO(&fds);
			while(!FD_ISSET(fd, &fds)) {
				if (TimedOut != 0)
					return -1;
				FD_ZERO(&fds);
				FD_SET(fd, &fds);
				tv.tv_sec = 1;
				tv.tv_usec = 0;
				ret = select(5, &fds, NULL, NULL, &tv);
				if (ret == -1)
					return 0;
			}
			pos++;
			len = read(fd, &line[pos], 1);
			line[pos+1] = '\0';
			if (line[pos] == '\r')
			{
				line[pos] = '\0';
				strcpy(PrevLine, LastLine);
				strcpy(LastLine, line);
				line[0] = '\0';
				pos = -1;
				continue;
			}
			if (len < 0)
			{
				alarm(0);
				return -1;
			}
			if (strstr(line, "-- Press Return for More --"))
			{
				DoWrite(fd, "\n", "");
				line[0] = '\0';
				pos = -1;
				continue;
			}
			for(i = 0;i < matches;i++)
			{
				if (strstr(line, txt[i]))
				{
					alarm(0);
					line[pos+1] = '\0';
					strcpy(PrevLine, LastLine);
					strcpy(LastLine, line);
					return (i + 1);
				}
			}
		}
	}
	return 0;
}

/* Write txt1 and txt2 to fd */
int DoWrite(int fd, char *txt1, char *txt2)
{
	int ret, len;

	len = strlen(txt1);
	ret = write(fd, txt1, len);
	if (ret != len)
		return -1;
	if (strcmp(txt2, ""))
	{
		len = strlen(txt2);
		ret = write(fd, txt2, len);
		if (ret != len)
			return -1;
	}
	return 1;
}

void ConnectPinnedNETfds()
{
	Debug("Creating and connecting PINNED socket connections:\n");
	pmcur = pmfirst;
	while(pmcur != NULL)
	{
		if (pmcur->pin == 1)
			ConnectPortMaster(pmcur);
		pmcur = pmcur->next;
	}
}

int ConnectPortMaster(struct PortMaster_List *pml)
{
	struct sockaddr_in pmaddr;
	char tmp[256];
	int ret;

	sprintf(tmp, "Connecting (%s) [%s:%d]: ", pml->ttyname, pml->ipaddr, pml->port);
	Debug(tmp);
	if (pml->netfd > 0)
	{
		sprintf(tmp, "Already Connected!  Socket #%d\n", pml->netfd);
		ADebug(tmp);
		return 0;
	}
	if ( (pml->netfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		strcpy(tmp, "Failed!\n");
		ADebug(tmp);
		time(&pml->lt);
		pml->netfd = -1;
		return 0;
	}
	sprintf(tmp, "Socket #%d: ", pml->netfd);
	ADebug(tmp);
	pmaddr.sin_family = AF_INET;
	pmaddr.sin_addr.s_addr = (unsigned long)inet_addr(pml->ipaddr);
	pmaddr.sin_port = htons(pml->port);
	if (connect(pml->netfd, (struct sockaddr *)&pmaddr, sizeof(pmaddr)) < 0)
	{
		strcpy(tmp, "Failed!\n");
		ADebug(tmp);
		time(&pml->lt);
		pml->netfd = -1;
		return 0;
	}
	ADebug(" Connected!");
	if (pmcur->security == 1)
	{
		ADebug(" Logging on..\n");
		if ( (ret = SecureLogin(pml)) == 1)
		{
			sprintf(tmp, "Login Successful! (%s) [%s:%d]", pml->ttyname, pml->ipaddr, pml->port);
			Debug(tmp);
			strcpy(pmcur->to_buf, "");
			strcpy(pmcur->from_buf, "");
		}
		else
		{
			sprintf(tmp, "Login Failed! (%s) [%s:%d]: ", pml->ttyname, pml->ipaddr, pml->port);
			if (ret == -1)
				strcat(tmp, "Invalid Login\n");
			else if (ret == -2)
				strcat(tmp, "Received Prompt\n");
			else if (ret == -3)
				strcat(tmp, "Timed Out\n");
			else if (ret == -4)
				strcat(tmp, "No Available Ports\n");
			else
				strcat(tmp, "\n");
			Debug(tmp);
			if (pmcur->netfd >= 0)
				close(pmcur->netfd);
			pmcur->netfd = -1;
			time(&pml->lt);
			return 0;
		}
	}
	ADebug("\n");
	return 1;
}

void DisconnectPortMaster(struct PortMaster_List *pml)
{
	close(pml->netfd);
	pml->netfd = -1;
	return;
}

void free_pmlist()
{
	if (pmlast != NULL)
	{
		while(pmlast->prev != NULL)
		{
			pmcur = pmlast->prev;
			free(pmlast);
			pmlast = pmcur;
		}
		free(pmcur);
	}
	pmcur = NULL;
	pmfirst = pmcur;
	pmlast = pmfirst;
	_PmListInit = 0;
}

struct PortMaster_List *begin_pmlist()
{
	pmfirst = malloc(sizeof(pmstruct));
	pmfirst->next = NULL;
	pmfirst->prev = NULL;
	pmlast = pmfirst;
	pmcur = pmfirst;
	_PmListInit = 123;
	return (struct PortMaster_List *)pmfirst;
}

struct PortMaster_List *new_pmlist()
{
	if (_PmListInit != 123)
		return (struct PortMaster_List *)begin_pmlist();
	pmnew = malloc(sizeof(pmstruct));
	pmnew->next = NULL;
	pmnew->prev = pmlast;
	pmlast->next = pmnew;
	pmlast = pmnew;
	return (struct PortMaster_List *)pmnew;
}

struct PortMaster_List *add_pm(char ttyname[MAXLEN], char ipaddr[17], int port, int pin, int security, char username[17], char password[17])
{
	struct PortMaster_List *newpm;

	newpm = new_pmlist();
	strcpy(newpm->ttyname, ttyname);
	strcpy(newpm->ipaddr, ipaddr);
	strcpy(newpm->username, username);
	strcpy(newpm->password, password);
	newpm->port = port;
	newpm->pin = pin;
	newpm->security = security;

	strcpy(newpm->ptyname, "");
	newpm->ptyfd = -1;
	newpm->netfd = -1;
	newpm->open = 0;
	return (struct PortMaster_List *)newpm;
}

void CreatePTYs()
{
	struct PortMaster_List *pl1, *pl2;
	char tmp[256];

	pmcur = pmfirst;

	while(pmcur != NULL)
	{
		if ( (pmcur->ptyfd = getpty(pmcur)) < 0 )
		{
			sprintf(tmp, "CreatePTYs: (%s) (%s): Failed! Removing...\n", pmcur->ttyname, pmcur->ptyname);
			Debug(tmp);
			pl1 = pmcur->prev;
			pl2 = pmcur->next;
			if (pl1 != NULL)
				pl1->next = pl2;
			if (pl2 != NULL)
				pl2->prev = pl1;
			free(pmcur);
			if (pl2 == NULL)
				break;
			pmcur = pl2;
			if (pmcur->prev == NULL)
				pmfirst = pmcur;
			if (pmcur->next == NULL)
				pmlast = pmcur;
			continue;
		}
		else
		{
			sprintf(tmp, "Created PTYfd [%d], for (%s) (%s)\n", pmcur->ptyfd, pmcur->ttyname, pmcur->ptyname);
			Debug(tmp);
		}
		pmcur = pmcur->next;
	}
}

int getpty(struct PortMaster_List *pmcur)
{
	char	*ptyname;
	int	ptyfd;
#ifdef OS_HPUX
	int	on = 1;
#endif

	if ((ptyname = findpty(pmcur->ttyname)) == (char *)NULL)
		return -1;
	strcpy(pmcur->ptyname, ptyname);
	if ( (ptyfd = open(pmcur->ptyname, O_RDWR | O_NOCTTY)) < 0)
		return -1;
	/* Set modes */
/*
#ifndef OS_SUNOS
	ioctl(ptyfd, FIONBIO, &on);
	ioctl(ptyfd, TIOCPKT, &on);
#endif
*/
#ifdef OS_HPUX
	ioctl(ptyfd, TIOCMONITOR, &on);
#endif
	return(ptyfd);
}

char *findpty(char *ttyname)
{
	struct stat	stb;
	char		*find_pty_device = NULL;
	/*
	dev_t		device_type;
	char		*ptyname;
	*/

	if (find_pty_device != NULL)
		free(find_pty_device);
	find_pty_device = (char *)malloc(256);
	/*
	 * The device will come in using the form:
	 * 	"/dev/ttyp1"
	 * this needs to be converted to the "pty" format name
	 * which represents the other side of the psuedo device.
	 * If we don't find it using this simple conversion, then
	 * we will see if it is cached from the previous lookup for the
	 * port and finally we will look for the coresponding pty using
	 * readdir in /dev
	 */
	if(strncmp(ttyname, "/dev/tty", 8) == 0)
	{
		strcpy(find_pty_device, ttyname);
		find_pty_device[5] = 'p';
		if (stat(find_pty_device, &stb) == 0)
			return find_pty_device;
	}

#if defined(OS_RS6000)
	/* Try the RS6000 form of multiplexed ttys/ptys */
	if(strncmp(ttyname, "/dev/pty/", 9) == 0)
	{
		strcpy(find_pty_device, ttyname);
		find_pty_device[7] = 'c';
		if (stat(find_pty_device, &stb) == 0)
			return(find_pty_device);
	}
	return((char *)NULL);
#endif

	return ((char *)NULL);

	/* NOT SURE WHAT PLATFORM TAKES THIS YET.. */
	
	/* Use the more involved process */
	/*
	if(stat(ttyname, &stb) < 0)
		return((char *)NULL);

	if(stb.st_mode & S_IFMT != S_IFCHR)
		return((char *)NULL);

	if(major(stb.st_rdev) != MAJOR_TTY)
		return((char *)NULL);

	device_type = makedev(MAJOR_PTY, minor(stb.st_rdev));

	ptyname = dev2pty(device_type);
	return(ptyname);
	*/
}

char *dev2pty(dev_t devno)
{
	register struct direct	*dp;
	DIR			*dirp;
	struct stat		stb;
	char			tmpname[256];
	char			*find_pty_device = NULL;

	if (find_pty_device != NULL)
		free(find_pty_device);
	find_pty_device = (char *)malloc(256);

	if ((dirp = opendir("/dev")) == NULL)
		return((char *)NULL);

	if ((dp = readdir(dirp)) == NULL)
	{
		closedir(dirp);
		return((char *)NULL);
	}
	for( ; dp != NULL; dp = readdir(dirp))
	{
#if defined(OS_LINUX)
		strncpy(tmpname, dp->d_name, dp->d_reclen);
		tmpname[dp->d_reclen] = '\0';
#else
		strncpy(tmpname, dp->d_name, dp->d_namlen);
		tmpname[dp->d_namlen] = '\0';
#endif
		sprintf(find_pty_device, "/dev/%s", tmpname);
		if(stat(find_pty_device, &stb) < 0)
			continue;
		if((stb.st_mode & S_IFMT) != S_IFCHR)
			continue;
		if(stb.st_rdev == devno)
		{
			closedir(dirp);
			return(find_pty_device);
		}
	}
	closedir(dirp);
	return((char *)NULL);
}

int SecureLogin(struct PortMaster_List *pml)
{
	char txt[MATCH_MAX][MATCH_LEN];
	char tmp[MAXLEN*2];
	int ret;

	strcpy(txt[0], "ogin: ");
	ret = DoRead(pml->netfd, 5, 1, txt);
	if (ret != 1)
		return -3;
	ret = DoWrite(pml->netfd, pml->username, "\n");
	if (ret < 1)
		return 0;
	strcpy(txt[0], "assword: ");
	ret = DoRead(pml->netfd, 5, 1, txt);
	if (ret != 1)
		return -3;
	ret = DoWrite(pml->netfd, pml->password, "\n");
	if (ret < 1)
		return 0;
	strcpy(txt[0], ">");
	strcpy(txt[1], "Invalid");
	strcpy(txt[2], "No available ports");
	strcpy(txt[3], "Session established");
	ret = DoRead(pml->netfd, 5, 4, txt);
	if (ret == 1)
		return -2;
	if (ret == 2)
		return -1;
	if (ret == 3)
		return -4;
	if (ret != 4)
		return -3;
	read(pml->netfd, tmp, sizeof(tmp));
	return 1;
}

void PipeLoop()
{
	struct timeval	timeout;
	fd_set		readfds;
	fd_set		writefds;
	time_t		lt;
	char		tmp[2040], c[5];
	int		status, nds, len, i;

	nds = getdtablesize();
	for(;;)
	{
		if (gi.ReStart == 1)
		{
			Debug("Received HUP signal, Restarting...\n");
			pmcur = pmfirst;
			while(pmcur != NULL)
			{
				if (pmcur->ptyfd > 0)
					close(pmcur->ptyfd);
				if (pmcur->netfd > 0)
					close(pmcur->netfd);
				pmcur = pmcur->next;
			}
			free_pmlist();
			gi.ReStart = 0;
			GeneralSetup(device_fn);
		}
		FD_ZERO(&readfds);
		FD_ZERO(&writefds);
		pmcur = pmfirst;
		while(pmcur != NULL)
		{
			if (pmcur->ptyfd <= 0)
			{
				pmcur = pmcur->next;
				continue;
			}
			if (pmcur->netfd < 0 && pmcur->pin == 1)
			{
				time(&lt);
				if (lt > (pmcur->lt + CONNECT_TIME))
					ConnectPortMaster(pmcur);
			}
			if (pmcur->netfd >= 0 && pmcur->pin != 1)
			{
				time(&lt);
				if (lt > (pmcur->la + IDLE_TIMEOUT))
				{
					sprintf(tmp, "Disconnecting (%s) [%s:%d]: Idle Timeout\n", pmcur->ttyname, pmcur->ipaddr, pmcur->port);
					Debug(tmp);
					close(pmcur->netfd);
					pmcur->netfd = -1;
					pmcur = pmcur->next;
					continue;
				}
			}
			FD_SET(pmcur->ptyfd, &readfds);
			if (pmcur->netfd > 0)
				FD_SET(pmcur->netfd, &readfds);
			if (strlen(pmcur->to_buf) > 0)
			{
				if (pmcur->open != 1)
				{
					sprintf(tmp, "(%s): Detected TTY Open\n", pmcur->ttyname);
					Debug(tmp);
					pmcur->open = 1;
				}
				if (pmcur->netfd < 0)
				{
					time(&lt);
					if (lt > (pmcur->lt + CONNECT_TIME))
						ConnectPortMaster(pmcur);
				}
				if (pmcur->netfd > 0)
					FD_SET(pmcur->netfd, &writefds);
			}
			if (strlen(pmcur->from_buf) > 0)
				FD_SET(pmcur->ptyfd, &writefds);
			pmcur = pmcur->next;
		}
		timeout.tv_sec = 5;
		timeout.tv_usec = 0;
		status = select(nds, &readfds, &writefds, NULL, &timeout);
		if (status == -1)
		{
			Debug("Select failed!\n");
			sig_fatal(500);
		}
		if (status == 0)
			continue;
		pmcur = pmfirst;
		while(pmcur != NULL)
		{
			if (pmcur->netfd < 0)
			{
				pmcur = pmcur->next;
				continue;
			}
			if (strlen(pmcur->to_buf) > 0 && FD_ISSET(pmcur->netfd, &writefds))
			{
				if (pmcur->open != 1)
				{
					sprintf(tmp, "(%s): Detected TTY Open\n", pmcur->ttyname);
					Debug(tmp);
					pmcur->open = 1;
				}
				time(&pmcur->la);
				len = write(pmcur->netfd, pmcur->to_buf, strlen(pmcur->to_buf));
				if (len != strlen(pmcur->to_buf))
					Debug("Didn't write full amount to netfd!\n");
				strcpy(pmcur->to_buf, "");
			}
			if (strlen(pmcur->from_buf) > 0 && FD_ISSET(pmcur->ptyfd, &writefds))
			{
				if (pmcur->open != 1)
				{
					sprintf(tmp, "(%s): Detected TTY Open\n", pmcur->ttyname);
					Debug(tmp);
					pmcur->open = 1;
					strcpy(pmcur->to_buf, "");
					strcpy(pmcur->from_buf, "");
				}
				else
				{
					time(&pmcur->la);
					len = write(pmcur->ptyfd, pmcur->from_buf, strlen(pmcur->from_buf));
					if (len != strlen(pmcur->from_buf))
						Debug("Didn't write full amount to ptyfd!\n");
					if (len < 0)
					{
						close(pmcur->ptyfd);
						pmcur->ptyfd = getpty(pmcur);
						if (pmcur->pin == 1 || pmcur->netfd < 0) {
							sprintf(tmp, "(%s): Detected TTY Close\n", pmcur->ttyname); 
							Debug(tmp);
						} else {
							sprintf(tmp, "Disconnecting (%s) [%s:%d]: Detected TTY Close\n", pmcur->ttyname, pmcur->ipaddr, pmcur->port);
							Debug(tmp);
							close(pmcur->netfd);
							pmcur->netfd = -1;
						}
						pmcur->open = 0;
						FD_CLR(pmcur->ptyfd, &readfds);
					}
					if (pmcur->open != 0) {
						strcpy(pmcur->from_buf, "");
					}
				}
			}
			pmcur = pmcur->next;
		}
		pmcur = pmfirst;
		while(pmcur != NULL)
		{
			if (FD_ISSET(pmcur->ptyfd, &readfds))
			{
				time(&pmcur->la);
				len = read(pmcur->ptyfd, tmp, sizeof(tmp));
				if (len < 0)
				{
					close(pmcur->ptyfd);
					pmcur->ptyfd = getpty(pmcur);
					if (pmcur->pin == 1 || pmcur->netfd < 0) {
						sprintf(tmp, "(%s): Detected TTY Close\n", pmcur->ttyname);
						Debug(tmp);
					} else {
						sprintf(tmp, "Disconnecting (%s) [%s:%d]: Detected TTY Close\n", pmcur->ttyname, pmcur->ipaddr, pmcur->port);
						Debug(tmp);
						close(pmcur->netfd);
						pmcur->netfd = -1;
					}
					strcpy(pmcur->to_buf, "");
					strcpy(pmcur->from_buf, "");
					pmcur->open = 0;
				}
				else
				{
					strcpy(pmcur->to_buf, "");
					for(i = 0;i < len;i++)
					{
						if (tmp[i] == '\0')
							continue;
						sprintf(c, "%c", tmp[i]);
						strcat(pmcur->to_buf, c);
					}
				}
			}
			if (pmcur->netfd > 0 && FD_ISSET(pmcur->netfd, &readfds))
			{
				time(&pmcur->la);
				len = read(pmcur->netfd, tmp, sizeof(tmp));
				if (len < 1)
				{
					sprintf(tmp, "Received Disconnect: (%s) [%s:%d]: Connection Closed by foreign Host\n", pmcur->ttyname, pmcur->ipaddr, pmcur->port);
					Debug(tmp);
					if (pmcur->netfd >= 0)
						close(pmcur->netfd);
					pmcur->netfd = -1;
				}
				else
				{
					strcpy(pmcur->from_buf, "");
					for(i = 0;i < len;i++)
					{
						if (tmp[i] == '\0')
							continue;
						sprintf(c, "%c", tmp[i]);
						strcat(pmcur->from_buf, c);
					}
				}
			}
			pmcur = pmcur->next;
		}
	}
}

#ifdef OS_HPUX
int getdtablesize()
{
	return(NFDBITS);
}
#endif

