#include "sys/inet/tcp_user.h"
/*
 * special debugging added to tcp_accept for tcpgate
 */
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include "config.h"

extern errno;
extern char *errstr;

tcp_sock()
{
	int fd, n;
	char name[32];

	for(n = 01; n < 100; n += 2){
		sprintf(name, "/dev/tcp%02d", n);
		fd = open(name, 2);
		if(fd >= 0){
			flabclr(fd);		/* for security unix */
			return(fd);
		}
		if(errno != ENXIO)
			break;
	}
	perror(name);
	return(-1);
}

static
tcp_rv(fd, tp)
	int fd;
	struct tcpuser *tp;
{
	int n;

	n = read(fd, (char *)tp, sizeof(struct tcpuser));
	if (n != sizeof(struct tcpuser)){
		if(errno==EINTR){
			errstr = "timeout";
		} else {
			errstr = "connection rejected";
			errno = EACCES;
		}
		return -1;
	}

	switch(tp->code){
	case TCPC_OK:
		return 0;
	case TCPC_BADDEV:
		errstr = "tcp dev broken";
		errno = EIO;
		break;
	case TCPC_NOROUTE:
		errstr = "no route";
		errno = ENOENT;
		break;
	case TCPC_BADLOCAL:
		errstr = "invalid local address";
		errno = EINVAL;
		break;
	case TCPC_BOUND:
		errstr = "address(es) already in use";
		errno = ENOENT;
		break;
	default:
		errstr = "unknown error";
		errno = EIO;
		break;
	}
	return -1;
}

tcp_connect(fd, tp)
	int fd;
	struct tcpuser *tp;
{
	tp->code = TCPC_CONNECT;
	if (write(fd, (char *)tp, sizeof(struct tcpuser))!=sizeof(struct tcpuser)){
		errstr = "can't write tcp dev";
		return -1;
	}
	return tcp_rv(fd, tp);
}

tcp_listen(fd, tp)
	int fd;
	struct tcpuser *tp;
{
	tp->code = TCPC_LISTEN;
	if (write(fd, (char *)tp, sizeof(struct tcpuser))!=sizeof(struct tcpuser)){
		errstr = "can't write tcp dev";
		return -1;
	}
	return tcp_rv(fd, tp);
}

tcp_accept(fd, tp)
	int fd;
	struct tcpuser *tp;
{
	char name[128];
	int nfd;
	int n;

	if ((n=read(fd, (char *)tp, sizeof(struct tcpuser)))!=sizeof(struct tcpuser)) {
		log("accept: read %d, errno %d\n", n, errno);
		return -1;
	}
	if (tp->code != TCPC_OK) {
		log("accept: bad code %d\n", tp->code);
		return -1;
	}
	sprintf(name, "/dev/tcp%02d", tp->param);
	nfd = open(name, 2);
	if (nfd < 0)
		log("accept: can't open %s, errno %d\n", name, errno);
	if(nfd>=0)
		flabclr(nfd);		/* for security unix */
	return nfd;
}

tcp_rcmd(host, port, locuser, remuser, cmd, fd2p)
char *host, *port, *locuser, *remuser, *cmd;
int *fd2p;
{
	char buf[1];
	int fd;
	struct in_service *sp;
	struct tcpuser tu;
	in_addr in_address();

	tu.faddr = in_address(host);
	if(tu.faddr == 0){
		fprintf(stderr, "%s: unknown host\n", host);
		return(-1);
	}
	sp = in_service(port, "tcp", 0L);
	if(sp == 0){
		fprintf(stderr, "%s: unknown service\n", port);
		return(-1);
	}
	tu.fport = sp->port;
	fd = tcp_sock();
	if(fd < 0){
		perror(host);
		return(-1);
	}
	tu.laddr = INADDR_ANY;
	tu.lport = 0;
	tu.param = 0;
	if(tcp_connect(fd, &tu) < 0){
		fprintf(stderr, "%s: connection failed\n", host);
		goto bad;
	}

	if(fd2p == 0)
		write(fd, "", 1);
	else
		goto bad;	/* later */

	write(fd, locuser, strlen(locuser)+1);
	write(fd, remuser, strlen(remuser)+1);
	write(fd, cmd, strlen(cmd)+1);

	if(read(fd, buf, 1) != 1){
		perror(host);
		goto bad;
	}
	if(buf[0] != 0){
		while(read(fd, buf, 1) == 1){
			write(2, buf, 1);
			if(buf[0] == '\n')
				break;
		}
		goto bad;
	}
	return(fd);
bad:
	close(fd);
	return(-1);
}
