/*
 * 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.
 */

#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_linux)
/*
 * Linux-specific items
 */

#    undef LT_SOCKLEN_T
#    define LT_SOCKLEN_T socklen_t
#endif /* defined(LT_DIAL_linux) */

#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
 */

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

/*
 * Main program
 */

int main(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))) {
        // fallback to localhost if hostname cannot be resolved
        strcpy(hnm, "localhost");
        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(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(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(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);
}
