Cover V02, I05
Article
Listing 1
Listing 2

sep93.tar


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);
}
}

/* end of file */