/*
 * @(#)netcat.c--Copy to network port
 *
 * No rights reserved, no responsibility accepted.
 * ShadeTree Software, Inc.
 */

static	char *what[]={
    "@(#)netcat--Copy to network port",
    "@(#)STS/KBS 2apr1996",
    0,
};

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/time.h>

int	debugl;
char	*iam;

extern	char * optarg;		/* stuff for getopt */
extern	int optind, optopt;	/* stuff for getopt */

void
usage(void)
{
    fprintf(stderr,"usage: %s [-d] -hhostname -pportnumber\n",iam);
    fprintf(stderr,"       -d  debug messages to stderr\n");
    exit(1);
}

int
main(
    int		ac,
    char	*av[]
)
{
    int		s;
    struct	sockaddr_in sin;
    int		c;
    struct	hostent *hp;
    char	buf[1024];
    int		bufr,bufw;
    char	*hostname;
    int		port;
    int		eret;
    int		maxfd;
    int		osocks[FD_SETSIZE];		/* socket table */
    int		oc;
    fd_set	allmask,rmask;			/* select masks */
    int		rz,wz;				/* read/write sizes */
    int		sc;				/* select fd */
    int		sv;				/* select() value */


    iam = av[0];
    hostname = (char *)0;
    port = -1;

    while(( oc = getopt(ac,av,"dh:p:")) != -1 )
    {
	switch(oc)
	{
	 case 'd':
	    debugl++;
	    break;
	 case 'h':
	    hostname = optarg;
	    break;
	 case 'p':
	    port = atoi(optarg);
	    break;
	 case ':':
	 case '?':
	 default:
	    usage();
	    break;
	}
    }
    if(optind < ac)
    {
	fprintf(stderr,"Too many arguments (%s)\n",av[optind]);
	usage();
    }
    if(hostname==0 || port<0) {
	if(!hostname)
	{
	    fprintf(stderr,"No host name\n");
	}
	if(port < 0 || port > 65535)
	{
	    fprintf(stderr,"Missing or invalid port number\n");
	}
	usage();
    }

    if(debugl)
    {
	fprintf(stderr,"Host: %s\n",hostname);
	fprintf(stderr,"Port: %d\n",port);
    }

    eret=0;

    setbuf(stderr,NULL);

    hp = gethostbyname(hostname);
    if(debugl) fprintf(stderr,"gethostbyname(%s)=0x%x\n",hostname,hp);
    if(!hp) {
	herror(hostname);
	return(2);
    }
    memcpy( (char *)&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
    sin.sin_family = hp->h_addrtype;
    sin.sin_port = htons(port);
    if(debugl) fprintf(stderr,"   ip = %s\n",inet_ntoa(sin.sin_addr));

    for(;;) {
	s=socket(AF_INET, SOCK_STREAM, 0);
	if(debugl) fprintf(stderr,"socket()=%d\n",s);
	if(s<0) {
	    perror("Can't open socket");
	    return(2);
	}

	c=connect(s,(struct sockaddr *)&sin,sizeof(sin));
	if(debugl) fprintf(stderr,"connect()=%d\n",c);

	if(c) {
	    if(debugl) {
		sprintf(buf,"Connect to port %d on %s",port,hostname);
		perror(buf);
	    }
	    close(s);
	    sleep(5);
	} else {
	    break;
	}
    }

    /*
     * Setup select mask.
     * Initialize to our socket and stdin
     */
    FD_ZERO(&allmask);
    FD_SET(0,&allmask);
    FD_SET(s,&allmask);
    maxfd = s;
    osocks[0] = s;	/* stdin -> socket */
    osocks[s] = 1;	/* socket -> stderr */

    /*
     * Wait for activity
     */
    for(;;) {
	if(debugl) fprintf(stderr,"\n>"); /**/

	memcpy(&rmask,&allmask,sizeof(allmask));
	sv = select(maxfd+1,&rmask,0,0,0);
	if(sv<0) {
	    perror("select");
	    exit(2);
	}

	/*
	 * Check for activity
	 */
	for(sc = 0; sc <= maxfd; sc++) {
	    if(FD_ISSET(sc,&rmask)) {
		FD_CLR(sc,&rmask);
		if(debugl) fprintf(stderr,"(s%d)",sc); /**/
		rz = read(sc,buf,sizeof(buf));
		if(debugl) fprintf(stderr,"(r%d)",rz); /**/
		if(rz <= 0) {
		    if(rz==0) {
			if(debugl)
			{
			    fprintf(stderr,"EOF from %d--disconnecting\n",sc);
			    goto close_n_exit;
			}
			eret = 0;
		    } else {
			sprintf(buf,"read from socket %d--disconnecting",sc);
			perror(buf);
			eret = 2;
		    }
		    goto close_n_exit;
		}
		wz = write(osocks[sc],buf,rz);
		if (debugl) fprintf(stderr,"(s%d)(w%d)",osocks[sc],wz); /**/
		if(wz <= 0) {
		    if(wz==0) {
			fprintf(
			    stderr,
			    "EOF on write to %d--disconnecting\n",
			    osocks[sc]
			);
			eret = 3;
		    } else {
			sprintf(
			    buf,
			    "write to socket %d--disconnecting",
			    osocks[sc]
			);
			perror(buf);
			eret = 2;
		    }
		    goto close_n_exit;
		} 
	    }
	}
    }

close_n_exit:
    close(s);

    return(eret);
}

