/*
 * network.c -- handles stuff dealing with connecting and name resolving
 *
 * Written by Jeremy Nelson in 1995
 * See the COPYRIGHT file or do /help ircii copyright
 */

#include "irc.h"
#include "ircaux.h"

#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif

/*
	#define SERVICE_SERVER 0
	#define SERVICE_CLIENT 1
	#define PROTOCOL_TCP 0
	#define PROTOCOL_UDP 1
*/
/*
 * connect_by_number:  Wheeeee. Yet another monster function i get to fix
 * for the sake of it being inadequate for extension.
 *
 * we now take four arguments:
 *
 *	- hostname - name of the host (pathname) to connect to (if applicable)
 *	- portnum - port number to connect to or listen on (0 if you dont care)
 *	- service -	0 - set up a listening socket
 *			1 - set up a connecting socket
 *	- protocol - 	0 - use the TCP protocol
 *			1 - use the UDP protocol
 *
 *
 * Returns:
 *	Non-negative number -- new file descriptor ready for use
 *	-1 -- could not open a new file descriptor or 
 *		an illegal value for the protocol was specified
 *	-2 -- call to bind() failed
 *	-3 -- call to listen() failed.
 *	-4 -- call to connect() failed
 *	-5 -- call to getsockname() failed
 *	-6 -- the name of the host could not be resolved
 *	-7 -- illegal or unsupported request
 *
 *
 * Credit: I couldnt have put this together without the help of BSD4.4-lite
 * User Supplimentary Document #20 (Inter-process Communications tutorial)
 */
int connect_by_number(char *hostn, unsigned short *portnum, int service, int protocol)
{
	int fd = -1;
	int is_unix = (hostn && *hostn == '/');
	int sock_type, proto_type;

	sock_type = (is_unix) ? AF_UNIX : AF_INET;
	proto_type = (protocol == PROTOCOL_TCP) ? SOCK_STREAM : SOCK_DGRAM;

	if ((fd = socket(sock_type, proto_type, 0)) < 0)
		return -1;

#if 0
	info = fcntl(fd, F_GETFL, 0);
	info |= O_NONBLOCK;
	fcntl(fd, F_SETFL, info);
#endif
	set_socket_options (fd);

	/* Unix domain server */
#ifdef HAVE_SYS_UN_H
	if (is_unix && (service == SERVICE_SERVER))
	{
		struct sockaddr_un name;

		bzero (&name, sizeof(struct sockaddr_un));
		name.sun_family = AF_UNIX;
		strcpy(name.sun_path, hostn);

		if (bind(fd, (struct sockaddr *)&name, sizeof(struct sockaddr_un)))
			return close(fd), -2;

		if (protocol == PROTOCOL_TCP)
			if (listen(fd, 4) < 0)
				return close(fd), -3;
	}

	/* Unix domain client */
	else if (is_unix && (service == SERVICE_CLIENT))
	{
		struct sockaddr_un name;

		bzero (&name, sizeof(struct sockaddr_un));
		name.sun_family = AF_UNIX;
		strcpy(name.sun_path, hostn);

		alarm(CONNECT_TIMEOUT);
		if (connect (fd, (struct sockaddr *)&name, sizeof(name)) < 0)
		{
			alarm(0);
			return close(fd), -4;
		}
		alarm(0);
	}

	else
#endif
	/* Inet domain server */
	if (!is_unix && (service == SERVICE_SERVER))
	{
		int length;
		struct sockaddr_in name;

		bzero (&name, sizeof(struct sockaddr_in));
		name.sin_family = AF_INET;
		name.sin_addr.s_addr = htonl(INADDR_ANY);
		name.sin_port = htons(*portnum);
		/*name.sin_port = *portnum; ??? */

		if (bind(fd, (struct sockaddr *)&name, sizeof(name)))
			return close(fd), -2;

		length = sizeof (name);
		if (getsockname(fd, (struct sockaddr *)&name, &length))
			return close(fd), -5;

		*portnum = ntohs(name.sin_port);

		if (protocol == PROTOCOL_TCP)
			if (listen(fd, 4) < 0)
				return close(fd), -3;
	}

	/* Inet domain client */
	else if (!is_unix && (service == SERVICE_CLIENT))
	{
		struct sockaddr_in server;
		struct sockaddr_in localaddr;
		struct hostent *hp;

		/*
		 * Doing this bind is bad news unless you are sure that
		 * the hostname is valid.  This is not true for me at home,
		 * since i dynamic-ip it.
		 */
		if (LocalHostName)
		{
			bzero(&localaddr, sizeof(struct sockaddr_in));
			localaddr.sin_family = AF_INET;
			localaddr.sin_addr = LocalHostAddr;
			localaddr.sin_port = 0;
			if (bind(fd, (struct sockaddr *)&localaddr, sizeof(localaddr)))
				return close(fd), -2;
		}

		bzero(&server, sizeof(struct sockaddr_in));
		if (!(hp = resolv(hostn)))
			return close(fd), -6;

		bcopy(hp->h_addr, &(server.sin_addr), hp->h_length);
		server.sin_family = AF_INET;
		server.sin_port = htons(*portnum);

		alarm(CONNECT_TIMEOUT);
		if (connect (fd, (struct sockaddr *)&server, sizeof(server)) < 0)
		{
			alarm(0);
			return close(fd), -4;
		}
		alarm(0);
	}

	/* error */
	else
		return close(fd), -7;

	return fd;
}


#ifdef __STDC__
extern struct hostent *resolv (const char *stuff)
#else
extern struct hostent *resolv (stuff)
const char *stuff;
#endif
{
	struct hostent *hep;

	if ((hep = lookup_host(stuff)) == NULL)
		hep = lookup_ip(stuff);

	return hep;
}

#ifdef __STDC__
extern struct hostent *lookup_host (const char *host)
#else
extern struct hostent *lookup_host (host)
const char *host;
#endif
{
	struct hostent *hep;

	alarm(1);
	hep = gethostbyname(host);
	alarm(0);
	return hep;
}

#ifdef __STDC__
extern char *host_to_ip (const char *host)
#else
extern char *host_to_ip (host)
const char *host;
#endif
{
	struct hostent *hep = lookup_host(host);
	static char ip[256];

	return (hep ? 
		sprintf(ip,"%u.%u.%u.%u", 	hep->h_addr[0] & 0xff,
						hep->h_addr[1] & 0xff,
						hep->h_addr[2] & 0xff,
						hep->h_addr[3] & 0xff),
		ip : empty_string);
}

#ifdef __STDC__
extern struct hostent *lookup_ip (const char *ip)
#else
extern struct hostent *lookup_ip (ip)
const char *ip;
#endif
{
	int b1 = 0, b2 = 0, b3 = 0, b4 = 0;
	char foo[4];
	struct hostent *hep;

	sscanf(ip,"%d.%d.%d.%d", &b1, &b2, &b3, &b4);
	foo[0] = b1;
	foo[1] = b2;
	foo[2] = b3;
	foo[3] = b4;

	alarm(1);
	hep = gethostbyaddr(foo, 4, AF_INET);
	alarm(0);

	return hep;
}

#ifdef __STDC__
extern char *ip_to_host (const char *ip)
#else
extern char *ip_to_host (ip)
const char *ip;
#endif
{
	struct hostent *hep = lookup_ip(ip);
	static char host[128];

	return (hep ?
		strcpy(host, hep->h_name): 
		empty_string);
}

#ifdef __STDC__
extern char *one_to_another (const char *what)
#else
extern char *one_to_another (what)
const char *what;
#endif
{
	/* if the last char is a digit, it's an ip, else a hostname. */
	if (!isdigit(what[strlen(what)-1]))
		return host_to_ip (what);
	else
		return ip_to_host (what);
}


