/* netio/fget_netio_handle.c.  Generated from netio_handle.c.in by configure. */

/*
**  Copyright 1998-2004 University of Illinois Board of Trustees
**  Copyright 1998-2004 Mark D. Roth
**  All rights reserved.
**
**  fget_netio_handle.c - code to initialize a NETIO handle
**
**  Mark D. Roth <roth@feep.net>
*/

#include <fget_netio_internal.h>

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

#ifdef STDC_HEADERS
# include <stdlib.h>
# include <string.h>
# include <stdarg.h>
#endif

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif


const int netio_is_thread_safe =
#if defined(HAVE_GETHOSTBYNAME_R) && defined(HAVE_GETSERVBYNAME_R)
					1;
#else
					0;
#endif


int
fget_netio_service_port(char *service)
{
	struct servent *sp;
#ifdef HAVE_GETSERVBYNAME_R
	struct servent sent;
	char buf[10240];
#endif

#ifdef HAVE_GETSERVBYNAME_R
	if (getservbyname_r(service, "tcp", &sent, buf, sizeof(buf), &sp) == 0
	    && sp != NULL)
#else /* ! HAVE_GETSERVBYNAME_R */
	if ((sp = getservbyname(service, "tcp")) != NULL)
#endif /* HAVE_GETSERVBYNAME_R */
		return sp->s_port;

	return -1;
}


/*
** netio_new() - open a connection to hostname
*/
int
fget_netio_vnew(NETIO **niop, size_t size, char *hostname,
			  time_t timeout, unsigned short flags, va_list args)
{
	struct sockaddr_in addr;
	socklen_t addrlen = sizeof(addr);
	int i, host_idx, sock_flags;
	char *cp;
	struct hostent *hp;
#ifdef HAVE_GETHOSTBYNAME_R
	struct hostent hent;
	int h_errno_r;
	char buf[10240];
#endif

#ifdef DEBUG
	printf("==> fget_netio_new(niop=0x%lx, size=%lu, "
	       "timeout=%lu, hostname=\"%s\", flags=%hu, ...)\n",
	       niop, (unsigned long)size, (unsigned long)timeout,
	       hostname, flags);
#endif

	/*
	** allocate and initialize new NETIO handle
	*/
	*niop = (NETIO *)calloc(1, sizeof(NETIO));
	if (*niop == NULL)
		return -1;

	/* initialize socket to -1 */
	(*niop)->n_fd = -1;

	/* initialize buffer */
	(*niop)->n_buf = (char *)calloc(1, size);
	if ((*niop)->n_buf == NULL)
		goto connect_error;
	(*niop)->n_writep = (*niop)->n_buf;
	(*niop)->n_bufsize = size;

	/* save the host we're trying to connect to */
	strlcpy((*niop)->n_host, hostname, sizeof((*niop)->n_host));

	/* initialize options */
	fget_netio_init_options(*niop);
	fget_netio_vset_options(*niop, args);

	/*
	** initialize address structure
	*/
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;

	/*
	** determine port to connect to
	*/
	if ((cp = strchr(hostname, ':')) != NULL)
	{
		/*
		** caller supplied explicit port number or service name
		*/

		*cp++ = '\0';

		if (strspn(cp, "0123456789") == strlen(cp))
		{
			/* all numeric - it's a literal port number */
			addr.sin_port = htons(atoi(cp));
		}
		else
		{
			/* non-numeric - it's a service name */
			i = fget_netio_service_port(cp);
			if (i == -1)
			{
				errno = EINVAL;
				goto connect_error;
			}
			addr.sin_port = i;
		}
	}
	else
	{
		/*
		** no explicit port specified
		** fall back to application defaults
		*/

		/* first, try default service name */
		if ((*niop)->n_default_service != NULL)
		{
			i = fget_netio_service_port((*niop)->n_default_service);
			if (i != -1)
				addr.sin_port = i;
		}

		/* if that didn't work, try default port */
		if (addr.sin_port == 0
		    && (*niop)->n_default_port != 0)
		{
			addr.sin_port = htons((*niop)->n_default_port);
		}
	}

	/*
	** look up hostname
	*/
#ifdef HAVE_GETHOSTBYNAME_R
	if (gethostbyname_r(hostname, &hent, buf, sizeof(buf),
			    &hp, &h_errno_r) != 0
	    || hp == NULL)
#else /* ! HAVE_GETHOSTBYNAME_R */
	hp = gethostbyname(hostname);
	if (hp == NULL)
#endif /* HAVE_GETHOSTBYNAME_R */
	{
		errno = EINVAL;
		goto connect_error;
	}

	for (host_idx = 0; hp->h_addr_list[host_idx] != NULL; host_idx++)
	{
		memcpy(&(addr.sin_addr.s_addr), hp->h_addr_list[host_idx],
		       hp->h_length);

		(*niop)->n_fd = socket(AF_INET, SOCK_STREAM, 0);
		if ((*niop)->n_fd == -1)
		{
#ifdef DEBUG
			fprintf(stderr, "niop_connect(): socket(): %s\n",
				strerror(errno));
#endif
			goto connect_error;
		}

		/*
		** make socket non-blocking so that the
		** connect() timeout will work below
		*/
		sock_flags = fcntl((*niop)->n_fd, F_GETFL);
		if (sock_flags == -1)
			goto connect_error;
		BIT_SET(sock_flags, O_NONBLOCK);
		if (fcntl((*niop)->n_fd, F_SETFL, sock_flags) == -1)
			goto connect_error;

		if (connect((*niop)->n_fd, (struct sockaddr *)&addr,
			    addrlen) == -1)
		{
#ifdef DEBUG
			fprintf(stderr, "niop_connect(): connect(): %s\n",
				strerror(errno));
#endif
			if (errno == EINPROGRESS)
			{
				i = fget_netio_wait(*niop, 1,
							      timeout);
				if (i == -1)
					goto connect_error;
				if (i == 0)
					break;

				/* if (i == 1), fall through */
				errno = ETIMEDOUT;
			}

			if (BIT_ISSET(flags, NETIO_CONNECT_DNS_RR))
			{
				close((*niop)->n_fd);
				(*niop)->n_fd = -1;
				continue;
			}

			goto connect_error;
		}

		break;
	}

	/* failed on all IP addresses */
	if ((*niop)->n_fd == -1)
		goto connect_error;

	/* socket no longer needs to be non-blocking */
	BIT_CLEAR(sock_flags, O_NONBLOCK);
	if (fcntl((*niop)->n_fd, F_SETFL, sock_flags) == -1)
		goto connect_error;

	return 0;

  connect_error:
	if (*niop != NULL)
	{
		fget_netio_free(*niop);
		*niop = NULL;
	}
	return -1;
}


int
fget_netio_new(NETIO **niop, size_t size, char *hostname,
			 time_t timeout, unsigned short flags, ...)
{
	va_list args;
	int retval;

	va_start(args, flags);
	retval = fget_netio_vnew(niop, size, hostname,
					   timeout, flags, args);
	va_end(args);

	return retval;
}


char *
fget_netio_get_host(NETIO *nio)
{
	return nio->n_host;
}


int
fget_netio_fd(NETIO *nio)
{
	return nio->n_fd;
}


int
fget_netio_local_addr(NETIO *nio,
				struct sockaddr *addr, socklen_t *addrlen,
				char *buf, size_t buflen)
{
	struct sockaddr_in tmp_addr;
	socklen_t tmp_addr_len;

	if (addr == NULL)
	{
		tmp_addr_len = sizeof(tmp_addr);

		addr = (struct sockaddr *)&tmp_addr;
		addrlen = &tmp_addr_len;
	}

	if (getsockname(nio->n_fd, addr, addrlen) == -1)
		return -1;

	if (buf != NULL)
	{
		snprintf(buf, buflen, "%s:%d",
			 inet_ntoa(((struct sockaddr_in *)addr)->sin_addr),
			 ntohl(((struct sockaddr_in *)addr)->sin_port));
	}

	return 0;
}


int
fget_netio_remote_addr(NETIO *nio,
				 struct sockaddr *addr, socklen_t *addrlen,
				 char *buf, size_t buflen)
{
	struct sockaddr_in tmp_addr;
	socklen_t tmp_addr_len;

	if (addr == NULL)
	{
		tmp_addr_len = sizeof(tmp_addr);

		addr = (struct sockaddr *)&tmp_addr;
		addrlen = &tmp_addr_len;
	}

	if (getpeername(nio->n_fd, addr, addrlen) == -1)
		return -1;

	if (buf != NULL)
	{
		snprintf(buf, buflen, "%s:%d",
			 inet_ntoa(((struct sockaddr_in *)addr)->sin_addr),
			 ntohl(((struct sockaddr_in *)addr)->sin_port));
	}

	return 0;
}


int
fget_netio_free(NETIO *nio)
{
	if (nio->n_fd != -1)
	{
		/*
		** ignore return value of shutdown(), since
		** it's just a formality anyway
		**
		** (there are unconfirmed reports that shutdown() fails
		** on some platforms when called on a listen() socket)
		*/
		shutdown(nio->n_fd, SHUT_RDWR);

		if (close(nio->n_fd) == -1)
			return -1;
		nio->n_fd = -1;
	}

	if (nio->n_buf != NULL)
		free(nio->n_buf);

	if (nio->n_default_service != NULL)
		free(nio->n_default_service);

	free(nio);

	return 0;
}




syntax highlighted by Code2HTML, v. 0.9.1