/*
 * Copyright (c) 1990-1996
 *	Joe Greco.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Joe Greco.
 * 4. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY JOE GRECO AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL JOE GRECO OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include "defs.h"

Prototype int make_tcp_sockaddr(struct sockaddr_in *addr, char *ascii);
Prototype int connect_tcp_socket(char *address, int opt, int port);
Prototype int connect_tcp_socket_by_sockaddr(struct sockaddr_in *address, int opt, int port);

#define	DIE(x)	if (errno != EWOULDBLOCK) {perror(x);} return(-1);
#define	BACKLOG	32	/* Maximum pending incoming TCP connections allowed */
#define	LIN_DEL	3	/* Seconds to wait for pending data to flush on close */
#define	X11_DSP	6000	/* Base X11 display port number */

#define bcopy(x, y, z) memcpy((y), (x), (z))
#ifdef	ULTRIX

static char *
strdup(char *x)
{
	char *rval;

	if (! (rval = malloc(strlen(x) + 1))) {
		return(NULL);
	}
	bcopy(x, rval, strlen(x) + 1);
	return(rval);
}

#endif

#if 0

int create_named_socket(pathname, opt)
char *pathname;
int opt;
{
	int s, i, j;
	struct sockaddr_un sun;

	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
		DIE("create_named_socket: socket");
	}

	sun.sun_family = AF_UNIX;
	strcpy(sun.sun_path, pathname);

	i = 1;
	j = sizeof(i);
	if (opt & OPT_REUSEADDR && setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&i, j) < 0) {
		close(s);
		DIE("create_named_socket: setsockopt");
	}
	if (bind(s, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
		close(s);
		DIE("create_named_socket: bind");
	}
	if (listen(s, BACKLOG) < 0) {
		close(s);
		DIE("create_named_socket: listen");
	}
	if (opt & OPT_NONBLOCK && fcntl(s, F_SETFL, O_NDELAY) < 0) {
		close(s);
		DIE("create_named_socket: fcntl ndelay");
	}
	if (opt & OPT_VERBOSE) {
		printf("accepting connections on named socket %s\n", pathname);
	}
	return(s);
}
#endif

int
create_tcp_listen_socket(int port, int opt, char *bindaddr)
{
	int s, i, j;
	struct sockaddr_in sin;
	struct linger lin;

	(void)memset((char *)&sin, 0, sizeof(sin));
	sin.sin_port = htons(port);
	sin.sin_family = AF_INET;
	if (bindaddr) {
		sin.sin_addr.s_addr = inet_addr(bindaddr);
	} else {
		sin.sin_addr.s_addr = htonl(INADDR_ANY);
	}

	lin.l_onoff = 1;
	lin.l_linger = LIN_DEL;

	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		DIE("create_tcp_listen_socket: socket");
	}

	i = 1;
	j = sizeof(i);
	if (opt & OPT_REUSEADDR && setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&i, j) < 0) {
		close(s);
		DIE("create_tcp_listen_socket: setsockopt");
	}

	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
		close(s);
		DIE("create_tcp_listen_socket: bind");
	}

	if (listen(s, BACKLOG) < 0) {
		close(s);
		DIE("create_tcp_listen_socket: listen");
	}
	if (opt & OPT_NONBLOCK && fcntl(s, F_SETFL, O_NDELAY) < 0) {
		close(s);
		DIE("create_tcp_listen_socket: fcntl ndelay");
	}
	if (opt & OPT_VERBOSE) {
		printf("accepting connections on port %d\n", port);
	}
	return(s);
}

int
accept_tcp_connection(int s, int opt)
{
	int ns;
	struct sockaddr_in sin;
	struct linger lin;
	int addrlen = sizeof(sin);
	struct hostent *host;
	char *hostname;
	int ka, kalen = sizeof(ka);

	if ((ns = accept(s, (struct sockaddr *)&sin, &addrlen)) < 0) {
		DIE("accept_tcp_connection: accept");
	}
	if (opt & OPT_LINGER && setsockopt(ns, SOL_SOCKET, SO_LINGER, (char *)&lin, sizeof(lin)) < 0) {
		close(ns);
		DIE("accept_tcp_connection: setsockopt linger");
	}
	ka = 1;
	if (opt & OPT_KEEPALIVE && setsockopt(ns, SOL_SOCKET, SO_KEEPALIVE, (void *)&ka, kalen) < 0) {
		close(ns);
		DIE("accept_tcp_connection: setsockopt keepalive");
	}
	if (opt & OPT_NONBLOCK && fcntl(ns, F_SETFL, O_NDELAY) < 0) {
		close(ns);
		DIE("accept_tcp_connection: fcntl ndelay");
	}
	if (opt & OPT_VERBOSE) {
		if (! (host = gethostbyaddr((char *)&sin.sin_addr.s_addr, sizeof(sin.sin_addr.s_addr), AF_INET))) {
			hostname = inet_ntoa(sin.sin_addr);
		} else {
			hostname = host->h_name;
		}
		printf("accepted tcp connection: fd %2d from %s.%d\n", ns, hostname, ntohs(sin.sin_port));
	}
	return(ns);
}


/*
 * int make_tcp_sockaddr(struct sockaddr_in *addr, char *ascii)
 *
 * Given an ASCII string, make an intelligent guess as to what the
 * user wants.  It's assumed we want an X11 socket, but others may be
 * specified.  We ambitiously try to deal with:
 *
 * dom.ain		(assumed to be port 6000)
 * a.b.c.d		( "       "      "    " )
 * dom.ain.6000
 * a.b.c.d.6000
 * dom.ain:0
 * a.b.c.d:0		
 */

int 
make_tcp_sockaddr(struct sockaddr_in *addr, char *ascii)
{
	struct hostent *host;
	int dots = 0;
	int numeric = 0;
	char *str = strdup(ascii);
	char *colon = strrchr(str, ':');
	char *lastdot = strrchr(str, '.');
	char *ptr;

	if (! str) {
		DIE("make_tcp_sockaddr: malloc");
	}

	addr->sin_port = htons(X11_DSP);
	addr->sin_family = AF_INET;

	/* Count the number of dots in the address. */
	for (ptr = str; *ptr; ptr++) {
		if (*ptr == '.') {
			dots++;
		}
	}

	/* Check if it seems to be numeric. */
	numeric = isdigit((int)*str);

	/* If numeric and four dots, we have a.b.c.d.6000 */
	if (numeric && dots == 4) {
		*lastdot = '\0';
		addr->sin_port = htons(atoi(lastdot + 1));
	}
	/* If nonnumeric and no colon, check if the last part is a port */
	if (! numeric && ! colon && lastdot && isdigit((int)*(lastdot + 1))) {
		*lastdot = '\0';
		addr->sin_port = htons(atoi(lastdot + 1));
	}
	/* We might have a :0 display. */
	if (colon) {
		*colon = '\0';
		addr->sin_port = htons(atoi(colon + 1) + X11_DSP);
	}
	/* Now do we have a numeric address */
	if (numeric) {
		addr->sin_addr.s_addr = inet_addr(str);
		free(str);
		return(0);
	}
	/* Or a name */
	if (! (host = gethostbyname(str))) {
		free(str);
		DIE("make_tcp_sockaddr: gethostbyname");
	}
	bcopy(host->h_addr_list[0], (char *)&addr->sin_addr.s_addr, sizeof(addr->sin_addr.s_addr));
	free(str);
	return(0);
}

int
connect_tcp_socket(char *address, int opt, int port)
{
	int s, i, j;
	struct sockaddr_in sin;
	struct linger lin;
	int ka, kalen = sizeof(ka);

	sin.sin_port = htons(port);
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;

	lin.l_onoff = 1;
	lin.l_linger = LIN_DEL;

	if (opt & OPT_VERBOSE) {
		printf("opening a connection to %s...", address);
	}
	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		DIE("connect_tcp_socket: socket");
	}

	i = 1;
	j = sizeof(i);
	if (opt & OPT_REUSEADDR && setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&i, j) < 0) {
		close(s);
		DIE("connect_tcp_socket: setsockopt");
	}

	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
		close(s);
		DIE("connect_tcp_socket: bind");
	}

	if (make_tcp_sockaddr(&sin, address) < 0) {
		close(s);
		return(-1);
	}
	if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
		close(s);
		DIE("connect_tcp_socket: connect");
	}

	if (opt & OPT_LINGER && setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&lin, sizeof(lin)) < 0) {
		close(s);
		DIE("connect_tcp_socket: setsockopt linger");
	}
	ka = 1;
	if (opt & OPT_KEEPALIVE && setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&ka, kalen) < 0) {
		close(s);
		DIE("accept_tcp_connection: setsockopt keepalive");
	}
	if (opt & OPT_NONBLOCK && fcntl(s, F_SETFL, O_NDELAY) < 0) {
		close(s);
		DIE("connect_tcp_socket: fcntl ndelay");
	}
	if (opt & OPT_VERBOSE) {
		printf(" connected\n");
	}
	return(s);
}

int
connect_tcp_socket_by_sockaddr(struct sockaddr_in *address, int opt, int port)
{
	int s, i, j;
	struct sockaddr_in sin;
	struct linger lin;
	int ka, kalen = sizeof(ka);

	sin.sin_port = htons(port);
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;

	lin.l_onoff = 1;
	lin.l_linger = LIN_DEL;

	if (opt & OPT_VERBOSE) {
		printf("opening a connection to %s...", inet_ntoa(address->sin_addr));
	}
	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		DIE("connect_tcp_socket_by_sockaddr: socket");
	}

	i = 1;
	j = sizeof(i);
	if (opt & OPT_REUSEADDR && setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&i, j) < 0) {
		close(s);
		DIE("create_tcp_socket_by_sockaddr: setsockopt");
	}

	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
		close(s);
		DIE("connect_tcp_socket_by_sockaddr: bind");
	}
	if (connect(s, (struct sockaddr *)address, sizeof(*address)) < 0) {
		close(s);
		DIE("connect_tcp_socket_by_sockaddr: bind");
	}
	if (opt & OPT_LINGER && setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&lin, sizeof(lin)) < 0) {
		close(s);
		DIE("connect_tcp_socket_by_sockaddr: setsockopt linger");
	}
	ka = 1;
	if (opt & OPT_KEEPALIVE && setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&ka, kalen) < 0) {
		close(s);
		DIE("accept_tcp_connection: setsockopt keepalive");
	}
	if (opt & OPT_NONBLOCK && fcntl(s, F_SETFL, O_NDELAY) < 0) {
		close(s);
		DIE("connect_tcp_socket_by_sockaddr: fcntl ndelay");
	}
	if (opt & OPT_VERBOSE) {
		printf(" connected\n");
	}
	return(s);
}



syntax highlighted by Code2HTML, v. 0.9.1