Listing 2: Adam Zell's version of newping

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#define SVC_PORT	"time"
#define DEF_TOUT	20 /* default timeout */
#define DEF_PORT	37 /* default port */

#define DEBUG		0X1 /* option flags */
#define VERBOSE		0X2

#define is_opt(x)	(opts & (x))
#define add_opt(x)	(opts |= (x))

typedef enum {             /* use enum instead of multiple #defines */
    OK_RESPONSE		= 0,
    TOUT_CONNECT	= 1,
    TOUT_RESPONSE	= 2,
    REF_CONNECT		= 3,
    BAD_NET		= 4,
    BAD_HOST		= 5,
    BAD_USAGE		= 6,
    UNKNOWN_ERR		= 255
} errtype;

static int hoststring(const char *);

static void die(errtype, const char *, const char *);
static void pexit(errtype);

static void noconnect(int); /* signal handlers */
static void noresponse(int);

/* all globals are volatile */
static volatile int timeout = DEF_TOUT, totsecs = 1, sckt = -1, opts;
static volatile const char *hostname;

int
main(int argc, char *argv[]) {
    struct sockaddr_in phost;
    struct sigaction phan;
    int res;
    const struct protoent *pent = getprotobyname("tcp");
    const char *hname, *pname = strrchr(argv[0], '/');

    if (pname) /* isolate name of program */
        pname++;
    else
        pname = argv[0];

    argc--;
    argv++;

    if (pent == NULL) /* tcp not available */
        die(UNKNOWN_ERR, pname, "");

    if (argv[0] && argv[0][0] == '-') { /* process options */
        const char *p = argv[0] + 1;

        if (*p == '\0') /* check for newping - */
            die(BAD_USAGE, pname, "");
        for (; *p; p++) {
            if (*p == 'd')
                add_opt(DEBUG);
            else if (*p == 'v')
                add_opt(VERBOSE);
            else die(BAD_USAGE, pname, "");
        }
        argc--;
        argv++;
    }

    if (argc == 0 || argc > 2)
        die(BAD_USAGE, pname, "");

    hostname = hname = argv[0]; /* get the host name */

    if (argv[1]) { /* if non-NULL, convert to decimal */
        timeout = atoi(argv[1]);
        if (timeout <= 0)
            die(BAD_USAGE, pname, "");
    }

    if (hoststring(hname)) { /* is it a string? */
        const struct hostent *host = gethostbyname(hname);

        if (host) { /* found it */
            phost.sin_family = host->h_addrtype;
            memcpy(&phost.sin_addr.s_addr, host->h_addr, host->h_length);
        }
        else
            die(TOUT_CONNECT, pname, hname);
    }
    else { /* is in form 000.00.00.00 */
        unsigned long ipaddr = inet_addr(hname);

        if (ipaddr != INADDR_NONE) { /* found it */
            phost.sin_family = AF_INET;
            phost.sin_addr.s_addr = ipaddr;
        }
        else
            die(TOUT_CONNECT, pname, hname);
    }

    if (is_opt(DEBUG))
        printf("The IP address for '%s' is: %0lX\n", hname,
          phost.sin_addr.s_addr);

    phost.sin_port = DEF_PORT;

    if (is_opt(DEBUG))
        printf("Service %s recognized as port #%u\n",
          SVC_PORT, phost.sin_port);

    sckt = socket(PF_INET, SOCK_STREAM, pent->p_proto);
    if (sckt < 0) { /* socket() returns -1 on failure */
        perror("socket");
        pexit(UNKNOWN_ERR);
    }

    if (is_opt(DEBUG))
        printf("Socket open.  Descriptor Number %d\n", sckt);

    phan.sa_handler = &noconnect; /* set up sig handler using sigaction() */
    sigemptyset(&phan.sa_mask);   /* just block alarm signal */
    phan.sa_flags = 0;
/* if defined, SA_RESTART automatically restarts interrupted system calls */
#ifdef SA_RESTART
    phan.sa_flags = SA_RESTART;
#endif /* SA_RESTART */
    sigaction(SIGALRM, &phan, NULL);
    alarm(1);

    res = connect(sckt, (struct sockaddr *)&phost, sizeof(phost));
    while (res < 0) { /* connect returns -1 on failure */
        if (errno != EINTR && errno != EISCONN)
            perror("connect");
        switch(errno) {
          case EINTR: /* we were interrupted...try again */
          case EISCONN:
            close(sckt);
            sckt = socket(PF_INET, SOCK_STREAM, pent->p_proto);
            if (sckt < 0) {
                perror("socket");
                pexit(UNKNOWN_ERR);
            }
            break;
          case ECONNRESET:
          case ECONNREFUSED:
            pexit(REF_CONNECT);
            break;
          case EHOSTUNREACH:
            pexit(BAD_HOST);
            break;
          case ENETRESET:
          case ENETDOWN:
          case ENETUNREACH:
            pexit(BAD_NET);
            break;
          default:
            pexit(UNKNOWN_ERR);
            break;
        }
        res = connect(sckt, (struct sockaddr *)&phost, sizeof(phost));
    } 

    if (is_opt(DEBUG) && is_opt(VERBOSE))
        printf("Connect made (returned with %d)\n", res);

    phan.sa_handler = &noresponse; /* register 2nd phase handler */
    sigaction(SIGALRM, &phan, NULL);

    do {
        char buf[32];

        res = recv(sckt, buf, sizeof(buf), 0); /* recv returns -1 on failure */
        if (res < 0 && errno != EINTR) { /* if EINTR, try again */
            perror("recv");
            switch(errno) {
              case ECONNABORTED:
              case ENOTCONN:
                pexit(TOUT_CONNECT);
                break;
              case ECONNRESET:
              case ENETRESET:
                pexit(BAD_NET);
                break;
              default:
                pexit(UNKNOWN_ERR);
                break;
            }
        }
    } while (res < 0);

    if (is_opt(DEBUG) && is_opt(VERBOSE))
        printf("Received something.  Len = %d  Total Elapsed Time: %d\n",
          res, totsecs);
    
    phan.sa_handler = SIG_IGN; /* just ignore signal now */
    sigaction(SIGALRM, &phan, NULL);
    printf("%s is alive (%d)\n", hname, totsecs);
    pexit(OK_RESPONSE);
    return 0;
}

/* is string in form of mach.net.com or 123.45.67? */
static int
hoststring(const char *p) {
    for (; *p; p++) {
        if (!isdigit(*p) && *p != '.')
            break;
    }
    return (*p != '\0');
}

/* limited error output */
static void
die(errtype which, const char *p, const char *q) {
    if (which == BAD_USAGE)
        fprintf(stderr, "usage: %s [-dv] host [timeout]\n", p);
    else if (which == TOUT_CONNECT)
        fprintf(stderr, "%s: unknown host '%s'\n", p, q);
    else
        fprintf(stderr, "%s: unknown protocol TCP/IP\n", p);
    exit(which);
}

static void
pexit(errtype which) {
    if (sckt > -1) /* close socket if necessary */
        close(sckt);
    exit(which);
}

static void
noconnect(int which) {
    alarm(1);
    totsecs++;
    if (is_opt(DEBUG) || is_opt(VERBOSE))
        printf("No connection after %d seconds!\n", totsecs - 1);
    if (totsecs > timeout) {
        fprintf(stderr, "'%s' not acknowledging connect attempt\n", hostname);
        pexit(TOUT_CONNECT);
    }
}

static void
noresponse(int which) {
    alarm(1);
    totsecs++;
    if (is_opt(DEBUG) || is_opt(VERBOSE))
        printf("No connection after %d seconds!\n", totsecs - 1);
    if (totsecs > timeout) {
        fprintf(stderr, "'%s' not acknowledging connect attempt\n", hostname);
        pexit(TOUT_RESPONSE);
    }
}



