/*
 * udplink.c
 *
 *	Given a hostname or an IP address, confirms that IP connectivity
 *	exists to that host.
 *
 * 	calling:	udpecho host	[flag]
 *
 *				host - IP address or host name of target
 *				       host
 *
 *				flag - If found the code runs UNTIL it hits
 *				       a timeout
 *
 *	returns:	READ_TMO (100)	- host echo timed out
 *			READ_OK  (101)	- host echo returned or refused
 *
 */

#define MAX_TRIES 3	/* Number of tries to get a packet through */
#define MAX_WAIT  10	/* Time to read packet - maximum */
#define RETRY_TIME 1.0	/* Extra time before redoing a failed write/read */
#define READ_OK	101	/* What to return if we get ok data back */
#define READ_TMO 100	/* What to return if we don't */
#define LOOP_WAIT 120.0	/* Number of seconds to retry whole loop in */

#ifndef TRUE
#define TRUE (1==1)
#endif 	/* TRUE */

#ifndef FALSE
#define FALSE !(TRUE)
#endif	/* FALSE */

#ifndef boolean
#define boolean int
#endif	/* boolean */

#include "multinet_root:[multinet.include.sys]types.h"
#include "multinet_root:[multinet.include.sys]socket.h"
#include "multinet_root:[multinet.include.netinet]in.h"
#include <stdio.h>
#include "multinet_root:[multinet.include]netdb.h"
#include "multinet_root:[multinet.include]errno.h"
#include "multinet_root:[multinet.include.vms]inetiodef.h"

main(argc,argv)
int argc;
char *argv[];
{
	int s;				/* Pointer to socket structure 	*/
	int n;				/* Byte count from read/write  	*/
	int tries;			/* Count of packets tried      	*/
	boolean done;			/* Did we definitely succeed?  	*/
	char buf[8];			/* Outgoing data	       	*/
	char buf2[8];			/* Buffer for incoming data 	*/
	struct sockaddr_in sin;		/* For remote IP address	*/
	struct hostent *hp;		/* For remote IP name		*/
	struct servent *sp;		/* For echo server port		*/
	int exit_status;		/* To pass exit status		*/
	boolean error;			/* Error out or stay in		*/

	/*
	 *  First, create an IP-family socket on which to make the connection
	 */

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) {
		socket_perror("udplink: unable to create socket");
		exit(0x10000000);
	}

	/*
	 *  Then, get the UDP port number of the "echo" server.
	 *
	 */

#ifndef KNOWECHO
	sp = getservbyname("echo", "udp");
	if (sp == NULL) {
		fprintf(stderr, "udplink: echo/udp: unknown service\n");
		exit(0x10000000);
	}
#endif


	/*
	 * Create the sockaddr_in structure.  Fill in all but the address
	 * from what we have.  Based on content of argv[1] fill in using
	 * either gethostbyname or inet_addr().
 	 */

	sin.sin_family = AF_INET;
#ifndef KNOWECHO
	sin.sin_port = sp->s_port;
#else
	sin.sin_port = htons(7);
#endif	/* KNOWECHO */
	if (stringdigit(argv[1])) {	 	/* An IP address */
	   if ( (sin.sin_addr.s_addr = inet_addr(argv[1])) == -1) {
		fprintf(stderr, "udplink: cannot translate %s \n",argv[1]);
		exit(0x10000000);
	        }
	   }
	else 	{				/* A host name */
	   if ( (hp = gethostbyname(argv[1])) == NULL) {
		fprintf(stderr, "udplink: host unknown: %s\n",argv[1]);
		exit(0x10000000);
	   	}
           bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);
	   }

	/*
	 * Bind to a udp port so that we can get back any responses
	 */

	n = connect(s, &sin, sizeof(sin));
	if (n < 0) {
	    socket_perror("udplink: unable to bind to udp port\n");
	    exit(0x10000000);
	}

	/*
	 * Now send data out and read it until we either time out all
	 * our retries, or succeed...
	 */

	buf[sizeof(buf)]='\n';

	error = FALSE;
	while (!error) {

	tries = 0;
	done = FALSE;
	while ((tries++ < MAX_TRIES) && !(done)) {
	   n = socket_write(s,buf,sizeof(buf));
	   if (n >= 0) {
              n = socket_read_with_timeout(s, buf2, sizeof(buf2), MAX_WAIT);
              if (n >= 0) {		/* Read succeeded */
                 done = TRUE;		/* Exit inner loop */
		 error= FALSE;
		 exit_status = READ_OK;	/* Set exit status */
                 }
	      else { 			/* Read failed 	*/
                 if (socket_errno != ECONNREFUSED) {	/* Read timed out */
                    done = FALSE;	/* Continue inner loop */
		    error= TRUE;
		    exit_status = READ_TMO;	/* Set exit status */
#ifdef DEBUG
                    printf("RD tmo\n");
		    printf("%s \n",vms_errno_string());
#endif	/* DEBUG */
 		    lib$wait(&RETRY_TIME);
                    }
                 else {		/* Read had other err (ECONNREFUSED) */
                    done = TRUE;	/* exit inner loop */
		    error= FALSE;
		    exit_status = READ_OK;	/* set exit_status */
#ifdef DEBUG		
                    printf("RD err (ok)  %d\n",socket_errno);
		    printf("%s \n",vms_errno_string());
#endif
                   } /* End if */
                } /* end if */
             } /* end if */
	   else {		/* Write failed */
	     done = FALSE;
	     error= TRUE;
	     exit_status = READ_TMO;
#ifdef DEBUG		
             printf("WR er err dec: %d\n",socket_errno);
	     printf("%s \n",vms_errno_string());
#endif
	     } /* end if */
	  } /* end while tries */

	/* 
	 * If only one argument was entered then we're not in permanent
	 * mode.  No matter what, exit after one iteration.
	 */
        if (argc != 3) {
           error = TRUE;
           }
	else {
	   if (!error) lib$wait(&LOOP_WAIT);
	}

	}	/* End while !(error) */

       	exit(exit_status);
}


/*
 * stringdigit(char *a)		Return TRUE if all characters are digits
 */
int stringdigit(char *a)
{
   while (*a != '\0') {
      if ((*a != '.') && ((*a < '0') || (*a > '9'))) return(FALSE);
      a++;
   }
   return(TRUE);
}



/*
 * The following routine is taken from
 *	multinet_common_root:[multinet.examples]udpechoclient.c
 */

/*
 *  This routine does a normal read using $QIO and also sets a timer.
 *  If no data arrives within the specified time, the read is aborted.
 */

int socket_read_with_timeout(fd, buf, len, timeout)
int fd, len, timeout;
char *buf;
{
	unsigned short IOSB[4];
	int Status, TIME[2];


	/*
	 *  Start the read on the socket.
	 */

	TIME[0] = -10000000 * timeout;
	TIME[1] = -1;
	Status = SYS$QIO(1,
			 fd,
			 IO$_RECEIVE,
			 IOSB,
			 0, 0,
			 buf,
			 len,
			 0, 0, 0, 0);

	if (!(Status&1)) {
		vmserrno = Status;
		if (vmserrno & 0x8000) {
		    vmserrno |= 0x4;
		    socket_errno = (Status & 0x7fff) >> 3;
		} else {
		    socket_errno = EIO;
		}
		return(-1);
	}

	/*
	 *  Start the timer
	 */

	Status = SYS$SETIMR(2, TIME, 0, 0, 0);
	if (!(Status&1)) {
		vmserrno = Status;
		if (vmserrno & 0x8000) {
		    vmserrno |= 0x4;
		    socket_errno = (Status & 0x7fff) >> 3;
		} else {
		    socket_errno = EIO;
		}
		return(-1);
	}

	SYS$WFLOR(1, (1<<1)|(1<<2));

	/*
	 *  Check if timer went off, or packet was received
	 */

	if (IOSB[0] == 0) {
	    /*
	     *	Timer went off. Cancel the I/O
	     */
	    SYS$CANCEL(fd);
	    socket_errno = ETIMEDOUT;
	    vmserrno = 0x8004 | (ETIMEDOUT << 3);
	    return(-1);
	} else {
	    /*
	     *	I/O completed, cancel the timer.
	     */
	    SYS$CANTIM(0,0);
	    /*
	     *	    Check for errors
	     */
	    if (!(IOSB[0] & 1)) {
		    vmserrno = IOSB[0];
		    if (vmserrno & 0x8000) {
			vmserrno |= 0x4;
			socket_errno = (IOSB[0] & 0x7fff) >> 3;
		    } else {
			socket_errno = EIO;
		    }
		    return(-1);
	    }
	}
	/*
	 *	And the character count
	 */
	return(IOSB[1]);
}

