/*
 * LTsock.c -- Lsof Test IPv4 sockets
 *
 * V. Abell
 * Purdue University
 */


/*
 * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
 * 47907.  All rights reserved.
 *
 * Written by V. Abell.
 *
 * This software is not subject to any license of the American Telephone
 * and Telegraph Company or the Regents of the University of California.
 *
 * Permission is granted to anyone to use this software for any purpose on
 * any computer system, and to alter it and redistribute it freely, subject
 * to the following restrictions:
 *
 * 1. Neither the authors nor Purdue University are responsible for any
 *    consequences of the use of this software.
 *
 * 2. The origin of this software must not be misrepresented, either by
 *    explicit claim or by omission.  Credit to the authors and Purdue
 *    University must appear in documentation and sources.
 *
 * 3. Altered versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 *
 * 4. This notice may not be removed or altered.
 */

#ifndef lint
static char copyright[] =
"@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
#endif

#include "LsofTest.h"
#include "lsof_fields.h"

#include <netdb.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


/*
 * Pre-definitions that make be changed or revoked by dialects
 */

#define SIGHANDLER_T	void		/* signal handler function type */
#define	LT_SOCKLEN_T	int		/* socket length type */


#if	defined(LT_DIAL_aix)
/*
 * AIX-specific items
 */

#undef	LT_SOCKLEN_T
#define	LT_SOCKLEN_T	size_t
#endif	/* defined(LT_DIAL_aix) */


#if	defined(LT_DIAL_darwin)
/*
 * Darwin-specific items
 */

# if	LT_VERS>=800
#undef	LT_SOCKLEN_T
#define	LT_SOCKLEN_T	socklen_t
# endif	/* LT_VERS>=800 */
#endif	/* defined(LT_DIAL_darwin) */


#if	defined(LT_DIAL_freebsd)
/*
 * FreeBSD-specific items
 */
#undef	LT_SOCKLEN_T
#define	LT_SOCKLEN_T	socklen_t
#endif	/* defined(LT_DIAL_freebsd) */


#if	defined(LT_DIAL_hpux)
/*
 * HP-UX-specific items
 */

# if	LT_VERS>=1123 && defined(__GNUC__)
#undef	LT_SOCKLEN_T
#define	LT_SOCKLEN_T	size_t
# endif	/* LT_VERS>=1123 && defined(__GNUC__) */
#endif	/* defined(LT_DIAL_hpux) */


#if	defined(LT_DIAL_ou)
/*
 * OpenUNIX-specific items
 */

#undef	LT_SOCKLEN_T
#define	LT_SOCKLEN_T	size_t
#endif	/* defined(LT_DIAL_ou) */


#if	defined(LT_DIAL_uw)
/*
 * UnixWare-specific items
 */

#undef	LT_SOCKLEN_T
#define	LT_SOCKLEN_T	size_t
#endif	/* defined(LT_DIAL_uw) */


/*
 * Local definitions
 */

#define	ALARMTM		30		/* alarm timer */

#define	LT_CLNT		0		/* child process index */
#define	LT_SRVR		1		/* parent process index */

#define	LT_FNF		0		/* file not found */
#define LT_FBYIP	1		/* file found by IP address */
#define	LT_FBYHN	2		/* file found by host name */
#define	LT_FBYPORT	4		/* file found by port */

#if	!defined(MAXHOSTNAMELEN)
#define	MAXHOSTNAMELEN	256		/* maximum host name length */
#endif	/* !defined(MAXHOSTNAMELEN) */

#if	!defined(MAXPATHLEN)
#define	MAXPATHLEN	1024		/* maximum path length */
#endif	/* !defined(MAXPATHLEN) */


/*
 * Local structure definitions.
 */


typedef struct fdpara {		/* file descriptor parameters */
    int fd;			/* FD */
    char *fds;			/* FD in ASCII */
    int ff;			/* file found flags (see LT_F*) */
    char *host;			/* host name */
    int hlen;			/* strlen(host) */
    char *ipaddr;		/* dotted IP address */
    int ilen;			/* strlen(ipaddr) */
    pid_t pid;			/* PID of process */
    char *port;			/* port in ASCII */
    int plen;			/* strlen(port) */
    struct sockaddr_in sa;	/* socket's address */
} fdpara_t;


/*
 * Globals
 */

pid_t CPid = (pid_t)0;		/* client PID */
fdpara_t FdPara[2];		/* file descriptor parameters */
#define	NFDPARA	(sizeof(FdPara) /sizeof(fdpara_t))
struct sockaddr_in Myad;	/* my (server) socket address */
pid_t MyPid = (pid_t)0;		/* PID of this process */
char *Pn = (char *)NULL;	/* program name */
char *PtNm[] = { "client", "server" };
				/* program type name */
int Ssock = -1;			/* server socket */


/*
 * Local function prototypes
 */

_PROTOTYPE(static void CleanupClnt,(void));
_PROTOTYPE(static void CleanupSrvr,(void));
_PROTOTYPE(static SIGHANDLER_T HandleClntAlarm,(int sig));
_PROTOTYPE(static SIGHANDLER_T HandleSrvrAlarm,(int sig));
_PROTOTYPE(static char *FindSock,(int fn));
_PROTOTYPE(static void StartClnt,(struct sockaddr_in *cad));


/*
 * Main program
 */

int
main(argc, argv)
    int argc;				/* argument count */
    char *argv[];			/* arguments */
{
    struct sockaddr_in aa;		/* accept address */
    struct sockaddr_in ba;		/* bind address */
    char buf[2048];			/* temporary buffer */
    int bufl = sizeof(buf);		/* size of buf[] */
    struct sockaddr_in ca;		/* connect address */
    char *cem;				/* current error message pointer */
    char *ep;				/* error message parameter */
    char hnm[MAXHOSTNAMELEN + 1];	/* this host's name */
    char *host;				/* host name */
    struct hostent *hp;			/* this host's hostent structure */
    char *ipaddr;			/* IP address */
    char *pem = (char *)NULL;		/* previous error message */
    char *port;				/* port */
    LT_SOCKLEN_T sal;			/* socket address length */
    char *tcp;				/* temporary character size */
    int ti, tj, tk;			/* temporary indexes */
    int tsfd;				/* temporary socket FD */
    int xv = 0;				/* exit value */
/*
 * Get program name and PID, issue start message, and build space prefix.
 */
    if ((Pn = strrchr(argv[0], '/')))
	Pn++;
    else
	Pn = argv[0];
    MyPid = getpid();
    (void) printf("%s ... ", Pn);
    (void) fflush(stdout);
    PrtMsg((char *)NULL, Pn);
/*
 * Initalize the FdPara[] array before any CleanupClnt() call.
 */
    for (ti = 0; ti < NFDPARA; ti++) {
	(void) memset((void *)&FdPara[ti], 0, sizeof(fdpara_t));
	FdPara[ti].fd = -1;
	FdPara[ti].ff = LT_FNF;
    }
/*
 * Process arguments.
 */
    if (ScanArg(argc, argv, "h", Pn))
	xv = 1;
    if (xv || LTopt_h) {
	(void) PrtMsg("usage: [-h]", Pn);
	PrtMsgX("       -h       print help (this panel)", Pn, CleanupSrvr,
		xv);
    }
/*
 * See if lsof can be executed and can access kernel memory.
 */
    if ((cem = IsLsofExec()))
	(void) PrtMsgX(cem, Pn, CleanupSrvr, 1);
    if ((cem = CanRdKmem()))
	(void) PrtMsgX(cem, Pn, CleanupSrvr, 1);
/*
 * Get the host name and its IP address.  Convert the IP address to dotted
 * ASCII form.
 */
    if (gethostname(hnm, sizeof(hnm) - 1)) {
	cem = "ERROR!!!  can't get this host's name";
	goto print_errno;
    }
    hnm[sizeof(hnm) - 1] = '\0';
    if (!(hp = gethostbyname(hnm))) {
	(void) snprintf(buf, bufl - 1, "ERROR!!!  can't get IP address for %s",
	    hnm);
	buf[bufl - 1] = '\0';
	cem = buf;
	goto print_errno;
    }
    (void) memset((void *)&Myad, 0, sizeof(Myad));
    if ((ti = hp->h_length) > sizeof(Myad.sin_addr))
	ti = sizeof(Myad.sin_addr);
    (void) memcpy((void *)&Myad.sin_addr, (void *)hp->h_addr, ti);
    Myad.sin_family = hp->h_addrtype;
/*
 * Get INET domain socket FDs.
 */
    for (ti = 0; ti < NFDPARA; ti++) {
	if ((tsfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
	    ep = "socket";

print_errno_by_ti:

	/*
	 * Report socket function error.
	 *
	 * Entry: ep   = function name
	 *	  hnm  = this host's name
	 *	  Myad = this host's IP address
	 *	  ti   =  FdPara[] index
	 */
	    (void) snprintf(buf, bufl - 1, "ERROR!!!  %s %s() failure",
		PtNm[ti], ep);
	    buf[bufl - 1] = '\0';
	    PrtMsg(buf, Pn);
	    (void) snprintf(buf, bufl - 1, "    host: %s",
		FdPara[ti].host ? FdPara[ti].host : hnm);
	    buf[bufl - 1] = '\0';
	    PrtMsg(buf, Pn);
	    (void) snprintf(buf, bufl - 1, "    IP: %s",
		FdPara[ti].ipaddr ? FdPara[ti].ipaddr
				  : inet_ntoa(Myad.sin_addr));
	    buf[bufl - 1] = '\0';
	    cem = buf;

print_errno:

	/*
	 * Report errno.
	 *
	 * Entry: errno = error number
	 */
	    PrtMsg(cem, Pn);
	    (void) snprintf(buf, bufl - 1, "    Errno %d: %s", errno,
		strerror(errno));
	    buf[bufl - 1] = '\0';
	    PrtMsgX(buf, Pn, CleanupSrvr, 1);
	}
    /*
     * Put the FD just acquired in FdPara[ti].fd.
     *
     * Set the file-not-found to LT_FNF.
     *
     * Save the server socket if this FdPara[] is for it.
     */
	FdPara[ti].fd = tsfd;
	(void) snprintf(buf, bufl - 1, "%d", tsfd);
	buf[bufl - 1] = '\0';
	FdPara[ti].fds = MkStrCpy(buf, &tj);
	if (ti == LT_SRVR)
	    Ssock = tsfd;
    }
/*
 * Bind the host name to the server socket.
 *
 * Get and save the server's socket address.
 *
 * Initiate a listen with an address list of one.
 */
    (void) memcpy((void *)&ba, (void *)&Myad, sizeof(ba));
    ti = LT_SRVR;
    FdPara[ti].pid = MyPid;
    if (bind(Ssock, (struct sockaddr *)&ba, sizeof(ba)) < 0) {
	ep = "bind";
	goto print_errno_by_ti;
    }
    sal = (LT_SOCKLEN_T)sizeof(ca);
    if (getsockname(Ssock, (struct sockaddr *)&ca, &sal)) {
	ep = "getsockname";
	goto print_errno_by_ti;
    }
    (void) memcpy((void *)&FdPara[ti].sa, (void *)&ca, sizeof(FdPara[ti].sa));
    if (listen(Ssock, 1) < 0) {
	ep = "listen";
	goto print_errno_by_ti;
    }
/*
 * Fork a child process to run as the client.
 */
    switch ((CPid = (pid_t)fork())) {
    case (pid_t)0:

    /*
     * This is the child.  Start the client.
     */
	StartClnt(&ca);
	(void) PrtMsgX("ERROR!!!  unexpected client return", Pn, CleanupSrvr,
		       1);
    case (pid_t)-1:

    /*
     * This is a fork error.
     */
	cem = "ERROR!!! fork() error";
	goto print_errno;
    default:

    /*
     * This is the parent.
     *
     * Save the client's PID.
     *
     * Close the client's socket.
     */
	FdPara[LT_CLNT].pid = CPid;
	if (FdPara[LT_CLNT].fd >= 0) {
	    (void) close(FdPara[LT_CLNT].fd);
	    FdPara[LT_CLNT].fd = -1;
	}
    }
/*
 * Set a SIGALRM, then accept() the connection from the client.
 *
 * Save the client's socket address.
 *
 * Replace the server's FD with the accepted one and close the original.
 */
    sal = (LT_SOCKLEN_T)sizeof(aa);
    (void) alarm(0);
    (void) signal(SIGALRM, HandleSrvrAlarm);
    (void) alarm(ALARMTM);
    tsfd = FdPara[LT_SRVR].fd = accept(Ssock, (struct sockaddr *)&aa, &sal);
    (void) alarm(0);
    (void) signal(SIGALRM, SIG_DFL);
    if (tsfd < 0) {
	ep = "accept";
	goto print_errno_by_ti;
    }
    (void) snprintf(buf, bufl - 1, "%d", tsfd);
    buf[bufl - 1] = '\0';
    if (FdPara[LT_SRVR].fds)
	(void) free((void *)FdPara[LT_SRVR].fds);
    FdPara[LT_SRVR].fds = MkStrCpy(buf, &tj);
    ti = LT_CLNT;
    (void) memcpy((void *)&FdPara[ti].sa, (void *)&aa, sizeof(FdPara[ti].sa));
    (void) close(Ssock);
    Ssock = -1;
/*
 * Convert the client and server IP address to ASCII form.
 *
 * Look up the client and server host names for their IP addresses.
 *
 * Convert the port from the socket address to host form.
 */
    for (ti = 0; ti < NFDPARA; ti++) {
	tcp = inet_ntoa(FdPara[ti].sa.sin_addr);
	FdPara[ti].ipaddr = MkStrCpy(tcp, &FdPara[ti].ilen);
	(void) snprintf(buf, bufl - 1, "%d",
	    (int)ntohs(FdPara[ti].sa.sin_port));
	buf[bufl - 1] = '\0';
	FdPara[ti].port = MkStrCpy(buf, &FdPara[ti].plen);
	if (!(hp = gethostbyaddr((char *)&FdPara[ti].sa.sin_addr,
				 sizeof(FdPara[ti].sa.sin_addr),
				 FdPara[ti].sa.sin_family))
	) {
	    ep = "gethostbyaddr";
	    goto print_errno_by_ti;
	}
	if (hp->h_name)
	    FdPara[ti].host = MkStrCpy(hp->h_name, &FdPara[ti].hlen);
	else {

	/*
	 * The connected client's socket address can't be mapped to a host
	 * name.
	 */

	    (void) snprintf(buf, bufl - 1,
		"ERROR!!!  can't map %s (client) to a host name",
		FdPara[ti].ipaddr);
	    buf[bufl - 1] = '\0';
	    PrtMsgX(buf, Pn, CleanupSrvr, 1);
	}
    }
/*
 * Call lsof three times to find the two sockets: 1) by host name and port;
 * 2) by IP address and port; and 3) by port.
 */
    if ((cem = FindSock(LT_FBYHN)))
	PrtMsgX(cem, Pn, CleanupSrvr, 1);
    if ((cem = FindSock(LT_FBYIP)))
	PrtMsgX(cem, Pn, CleanupSrvr, 1);
    if ((cem = FindSock(LT_FBYPORT)))
	PrtMsgX(cem, Pn, CleanupSrvr, 1);
/*
 * Check the FindSock() results.
 */
    for (pem = (char *)NULL, ti = 0; ti < NFDPARA; ti++) {
	if ((tj = FdPara[ti].ff) != (LT_FBYHN | LT_FBYIP | LT_FBYPORT)) {
	    host = FdPara[ti].host;
	    ipaddr = FdPara[ti].ipaddr;
	    port = FdPara[ti].port;

	/*
	 * This FD wasn't found by some search method.
	 */
	    if (!(tj & LT_FBYHN)) {

	    /*
	     * The search by host name and port failed.
	     */
		(void) snprintf(buf, bufl - 1,
		    "ERROR!!!  no %s socket by host and port: %s@%s",
		    PtNm[ti], host, port);
		buf[bufl - 1] = '\0';
		if (pem)
		    (void) PrtMsg(pem, Pn);
		pem = MkStrCpy(buf, &tk);
	    }
	    if (!(tj & LT_FBYIP)) {

	    /*
	     * The search by IP address and port failed.
	     */
		(void) snprintf(buf, bufl - 1,
		    "ERROR!!!  no %s socket by IP and port: %s@%s",
		    PtNm[ti], ipaddr, port);
		buf[bufl - 1] = '\0';
		if (pem)
		    (void) PrtMsg(pem, Pn);
		pem = MkStrCpy(buf, &tk);
	    }
	    if (!(tj & LT_FBYPORT)) {

	    /*
	     * The search by port number failed.
	     */
		(void) snprintf(buf, bufl - 1,
		    "ERROR!!!  no %s socket by port: %s",
		    PtNm[ti], port);
		buf[bufl - 1] = '\0';
		if (pem)
		    (void) PrtMsg(pem, Pn);
		pem = MkStrCpy(buf, &tk);
	    }
	}
    }
    if (pem)
	(void) PrtMsgX(pem, Pn, CleanupSrvr, 1);
/*
 * Exit successfully.
 */
    (void) PrtMsgX("OK", Pn, CleanupSrvr, 0);
    return(0);
}


/*
 * ClntCleanup() -- release client resources
 */

static void
CleanupClnt()
{
    int tfd;				/* temporary file descriptor */

    if ((tfd = FdPara[LT_CLNT].fd) >= 0) {
	(void) shutdown(tfd, 2);
	(void) close(tfd);
	FdPara[LT_CLNT].fd = -1;
    }
}


/*
 * CleanupSrvr() -- release server resources
 */

static void
CleanupSrvr()
{
    int tfd;				/* temporary file descriptor */
    int ti;				/* temporary index */
    pid_t wpid;				/* wait() PID */

    if ((Ssock >= 0) && (Ssock != FdPara[LT_SRVR].fd)) {
	(void) shutdown(Ssock, 2);
	(void) close(Ssock);
	Ssock = -1;
    }
    for (ti = 0; ti < NFDPARA; ti++) {
	if ((tfd = FdPara[ti].fd) >= 0) {
	    (void) shutdown(tfd, 2);
	    (void) close(tfd);
	    FdPara[ti].fd = -1;
	}
    }
    if (CPid > 0) {
	wpid = wait3(NULL, WNOHANG, NULL);
	if (wpid != CPid) {
	    kill(CPid, SIGKILL);
	    (void) wait3(NULL, WNOHANG, NULL);
	}
	CPid = (pid_t)0;
    }
}


/*
 * FindSock() -- find sockets with lsof
 */

static char *
FindSock(fn)
    int fn;				/* function -- an LT_FBY* value */
{
    char buf[2048];			/* temporary buffer */
    int bufl = sizeof(buf);		/* size of buf[] */
    char *cem;				/* current error message pointer */
    LTfldo_t *cmdp;			/* command pointer */
    LTfldo_t *fop;			/* field output pointer */
    int nf;				/* number of fields */
    int nl;				/* name length */
    LTfldo_t *nmp;			/* name pointer */
    char *opv[5];			/* option vector for ExecLsof() */
    char *pem = (char *)NULL;		/* previous error message pointer */
    pid_t pid;				/* PID */
    int pids = 0;			/* PID found status */
    int pl;				/* port length */
    int px;				/* process index -- LT_CLNT or
					 * LT_SRVR */
    char *tcp, *tcp1;			/* temporary character pointers */
    int ti, tj;				/* temporary integers */
    LTfldo_t *typ;			/* file type pointer */
/*
 * Check the function and determine the first lsof option from it.
 */
    ti = 0;
    switch (fn) {
    case LT_FBYHN:
	opv[ti++] = "-P";
	for (tj = 0; tj < NFDPARA; tj++) {
	    (void) snprintf(buf, bufl - 1, "-i@%s:%s", FdPara[tj].host,
		FdPara[tj].port);
	    buf[bufl - 1] = '\0';
	    opv[ti++] = MkStrCpy(buf, &pl);
	}
	break;
    case LT_FBYIP:
	opv[ti++] = "-Pn";
	for (tj = 0; tj < NFDPARA; tj++) {
	    (void) snprintf(buf, bufl - 1, "-i@%s:%s", FdPara[tj].ipaddr,
		FdPara[tj].port);
	    buf[bufl - 1] = '\0';
	    opv[ti++] = MkStrCpy(buf, &pl);
	}
	break;
    case LT_FBYPORT:
	opv[ti++] = "-P";
	for (tj = 0; tj < NFDPARA; tj++) {
	    (void) snprintf(buf, bufl - 1, "-i:%s", FdPara[tj].port);
	    buf[bufl - 1] = '\0';
	    opv[ti++] = MkStrCpy(buf, &pl);
	}
	break;
    default:
	(void) snprintf(buf, bufl - 1,
	    "ERROR!!!  illegal FindSock() function: %d", fn);
	buf[bufl - 1] = '\0';
	return(MkStrCpy(buf, &ti));
    }
/*
 * Complete the option vector and start lsof execution.
 */

#if	defined(USE_LSOF_C_OPT)
    opv[ti++] = "-C";
#endif	/* defined(USE_LSOF_C_OPT) */

    opv[ti] = (char *)NULL;
    if ((cem = ExecLsof(opv)))
	return(cem);
/*
 * Read lsof output.
 */
    while ((((FdPara[LT_CLNT].ff & fn) == 0)
    ||	    ((FdPara[LT_SRVR].ff & fn) == 0))
    &&	   (fop = RdFrLsof(&nf, &cem))
    ) {
	if (cem) {
	    if (pem)
		(void) PrtMsg(pem, Pn);
	    return(cem);
	}
	switch (fop->ft) {
	case LSOF_FID_PID:

	/*
	 * This is a process information line.
	 */
	    pid = (pid_t)atoi(fop->v);
	    pids = 1;
	    cmdp = (LTfldo_t *)NULL;
	    for (fop++, ti = 1; ti < nf; fop++, ti++) {
		switch (fop->ft) {
		case LSOF_FID_CMD:
		    cmdp = fop;
		    break;
		}
	    }
	    if (!cmdp || ((pid != CPid) && (pid != MyPid)))
		pids = 0;
	    break;
	case LSOF_FID_FD:

	/*
	 * This is a file descriptor line.
	 *
	 * Identify the process -- client or server.
	 */
	    if (!pids)
		break;
	    if (pid == CPid)
		px = LT_CLNT;
	    else if (pid == MyPid)
		px = LT_SRVR;
	    else
		break;
	/*
	 * Make sure the FD matches the identified process.
	 */
	    if (strcmp(fop->v, FdPara[px].fds))
		break;
	/*
	 * Scan for name and type.
	 */
	    nmp = typ  = (LTfldo_t *)NULL;
	    for (fop++, ti = 1; ti < nf; fop++, ti++) {
		switch (fop->ft) {
		case LSOF_FID_NAME:
		    nmp = fop;
		    break;
		case LSOF_FID_TYPE:
		    typ = fop;
		    break;
		}
	    }
	/*
	 * Check the type of the file.
	 */
	    if (!typ
	    ||  (strcasecmp(typ->v, "inet") && strcasecmp(typ->v, "ipv4"))
	    ) {
		break;
	    }
	/*
	 * Check the addess in the name, based on the calling function.
	 */
	    if (!nmp)
		break;
	    tcp = nmp->v;
	    switch (fn) {
	    case LT_FBYHN:
		if (((nl = FdPara[px].hlen) <= 0)
		||  !(tcp1 = FdPara[px].host)
		||  strncasecmp(tcp, tcp1, nl)
		) {
		    break;
		}
		tcp += nl;
		if ((*tcp++ != ':')
		||  !(tcp1 = FdPara[px].port)
		||  ((pl = FdPara[px].plen) <= 0)
		||  strncmp(tcp, tcp1, pl)
		) {
		    break;
		}
		tcp += pl;
		if ((*tcp == '-') || (*tcp == ' ') || !*tcp) {
		    FdPara[px].ff |= LT_FBYHN;
		}
		break;
	    case LT_FBYIP:
		if (((nl = FdPara[px].ilen) <= 0)
		||  !(tcp1 = FdPara[px].ipaddr)
		||  strncasecmp(tcp, tcp1, nl)
		) {
		    break;
		}
		tcp += nl;
		if ((*tcp++ != ':')
		||  !(tcp1 = FdPara[px].port)
		||  ((pl = FdPara[px].plen) <= 0)
		||  strncmp(tcp, tcp1, pl)
		) {
		    break;
		}
		tcp += pl;
		if ((*tcp == '-') || (*tcp == ' ') || !*tcp) {
		    FdPara[px].ff |= LT_FBYIP;
		}
		break;
	    case LT_FBYPORT:
		if (!(tcp = strchr(tcp, ':')))
		    break;
		tcp++;
		if (!(tcp1 = FdPara[px].port)
		||  ((pl = FdPara[px].plen) <= 0)
		||  strncmp(tcp, tcp1, pl)
		) {
		    break;
		}
		tcp += pl;
		if ((*tcp == '-') || (*tcp == ' ') || !*tcp) {
		    FdPara[px].ff |= LT_FBYPORT;
		}
		break;
	    }
	}
    }
/*
 * Clean up and return.
 */
    (void) StopLsof();
    return(pem);
}


/*
 * HandleClntAlarm() -- handle client alarm
 */

static SIGHANDLER_T
HandleClntAlarm(sig)
    int sig;				/* the signal (SIGALRM) */
{
    (void) PrtMsgX("ERROR!!!  client caught an alarm signal", Pn,
	CleanupClnt, 1);
}


/*
 * Handle SrvrAlarm() -- handle server alarm
 */

static SIGHANDLER_T
HandleSrvrAlarm(sig)
    int sig;				/* the signal (SIGALRM) */
{
    (void) PrtMsgX("ERROR!!!  server caught an alarm signal.", Pn,
	CleanupSrvr, 1);
}


/*
 * StartClnt() -- start network client
 */

static void
StartClnt(cad)
    struct sockaddr_in *cad;		/* connection address */
{
    struct sockaddr_in ba;		/* bind address */
    int br;				/* bytes read */
    char buf[2048];			/* temporary buffer */
    int bufl = sizeof(buf);		/* size of buf[] */
    int cr;				/* connect() reply */
    char *em;				/* error message pointer */
    int fd = FdPara[LT_CLNT].fd;	/* client's socket FD */
/*
 * Close the server's sockets.
 */
    if ((Ssock >= 0) && (Ssock != FdPara[LT_SRVR].fd)) {
	(void) close(Ssock);
	Ssock = -1;
    }
    if (FdPara[LT_SRVR].fd >= 0) {
	(void) close(FdPara[LT_SRVR].fd);
	FdPara[LT_SRVR].fd = -1;
    }
/*
 * Bind to the local address.
 */
    (void) memcpy((void *)&ba, (void *)&Myad, sizeof(ba));
    if (bind(fd, (struct sockaddr *)&ba, sizeof(ba)) < 0) {
	em = "bind";

client_errno:

	(void) snprintf(buf, bufl - 1,
	    "ERROR!!!  client %s error: %s", em, strerror(errno));
	buf[bufl - 1] = '\0';
	(void) PrtMsgX(em, Pn, CleanupClnt, 1);
    }
/*
 * Set an alarm timeout and connect to the server.
 */
    (void) signal(SIGALRM, HandleClntAlarm);
    (void) alarm(ALARMTM);
    cr = connect(fd, (struct sockaddr *)cad, sizeof(struct sockaddr_in));
    (void) alarm(0);
    (void) signal(SIGALRM, SIG_DFL);
    if (cr) {
	em = "connect";
	goto client_errno;
    }
/*
 * Sleep until the socket closes or the parent kills the process.
 */
    for (br = 0; br >= 0;) {
	sleep(1);
	br = read(fd, buf, bufl);
    }
    (void) CleanupClnt();
    exit(0);
}
