packages feed

fudgets-0.18.3: xlib/socketlib/inet.c

/*
 * (c) Copyright 1991 by Panagiotis Tsirigotis
 * Read the file COPYRIGHT for more details.
 * 
 */
/*
 * Internet support library.
 *
 * INTERFACE:
 *
 *		in_address			: form an internet address
 *		in_bind				: bind a socket to an internet address
 *		in_bind_opt			: bind a socket to an internet address and
 *								  set (for now, binary) options
 *		in_connect			: connect to an internet address
 *		in_connect_opt		: connect to an internet address and
 *								  set (for now, binary) options
 *
 */


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
/* #include <stdarg.h> */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>    /* on some systems, errno is not a variable */
/* extern int errno ; */

#include <sys/param.h>
#ifdef MAXHOSTNAMELEN
#define HOST_NAME_LEN		MAXHOSTNAMELEN
#else
#define HOST_NAME_LEN		32
#endif

#include "defs.h"

#if defined(__NetBSD__) || defined(_HPUX_SOURCE)
extern int h_errno;
#endif

/*
 * $Header: /home/magnus/cvs/Fudgets/xlib/socketlib/inet.c,v 1.7 2013-07-28 12:09:13 hallgren Exp $
 */

#define PRIVATE				static

static char *version = VERSION ;

enum status_e { OK, ERROR } ;


/*
 * Close a file descriptor without destroying errno
 */
#define CLOSE( sd, t )				t = errno ;						\
											(void) close( sd ) ;			\
											errno = t
#ifndef FALSE
#define FALSE			0
#define TRUE			1
#endif


/*
 * Returns TRUE if the host argument has the form:
 *		<num>.<num>
 *		<num>.<num>.<num>
 *		<num>.<num>.<num>.<num>
 */
PRIVATE int is_number_address( host )
	char *host ;
{
	register int dot_count = 0 ;
	register char *p ;

	if ( host == NULL )
		return( FALSE ) ;
	
	for ( p = host ; *p ; p++ )
		if ( isascii( *p ) && isdigit( *p ) )		/* skip digits */
			continue ;
		else if ( *p == '.' )							/* count dots */
		{
			dot_count++ ;
			if ( dot_count > 3 )
				return( FALSE ) ;
		}
		else													/* reject everything else */
			return( FALSE ) ;
	return( dot_count > 0 ) ;
}



/*
 * Form an Internet address.
 * If host is NULL, the current host is assumed (but this does not
 * guarantee the use of the loopback address)
 *
 * Return value:
 *		0					: if successful
 *		RESOLVER_ERROR	: if an error occurs
 *
 * In case of error, errno is set to the value of h_errno, if the OS
 * supports h_errno; if not, the value of errno is undefined.
 */
int in_address( host, port, address )
	char *host ;
	short port ;
	struct sockaddr_in *address ;
{
	char host_name[ HOST_NAME_LEN ] ;
	char *the_host ;
	struct hostent *hp ;

	bzero( (char *) address, sizeof( *address ) ) ;

	/*
	 * Determine the host name
	 */
	if ( host == NULL )
	{
		(void) gethostname( host_name, HOST_NAME_LEN ) ;
		the_host = host_name ;
	}
	else
		the_host = host ;
	
	/*
	 * Get host address
	 */
	if ( is_number_address( host ) )
		address->sin_addr.s_addr = inet_addr( host ) ;
	else
	{

#ifndef HAS_H_ERRNO
		if ( ( hp = gethostbyname( the_host ) ) == NULL )
			return( RESOLVER_ERROR ) ;
#else
		int tries = 0 ;

		while ( ( hp = gethostbyname( the_host ) ) == NULL &&
				  (h_errno == TRY_AGAIN
#if __FreeBSD__ == 2
   /* Needed because of FreeBSD 2.2-961014-SNAP bug? */
				   || h_errno == NETDB_INTERNAL
#endif
				   ) &&
				  tries < MAX_TRIES )
			tries++ ;
		if ( hp == NULL )
		{
			errno = h_errno ;
			return( RESOLVER_ERROR ) ;
		}
#endif	/* HAS_H_ERRNO */

		bcopy( hp->h_addr, (char *) &address->sin_addr, hp->h_length ) ;
	}

	address->sin_family = AF_INET ;
	address->sin_port = htons( port ) ;

	return( 0 ) ;
}



/*
 * Bind an internet address. Port is the port that will be used; the
 * address is that of the machine the program runs on.
 * Type is the type of socket; this also defines the protocol
 * that will be used (i.e. for SOCK_STREAM use TCP etc)
 *
 * Return value:
 *		a file descriptor	:  if the operation is successful
 *		SYSCALL_ERROR 		: if there is a system call error
 *		RESOLVER_ERROR 	: if there is a resolver error
 * In case of error, errno contains a description of the error
 */
PRIVATE	int bind_address() ;

int in_bind( port, type )
	short port ;
	int type ;
{
	int sd ;

	if ( ( sd = socket( AF_INET, type, 0 ) ) == -1 )
		return( SYSCALL_ERROR ) ;
	
	return( bind_address( sd, port ) ) ;
}

#if 0
PRIVATE	va_list apply_options() ;
#endif

/*
 * Same as in_bind but also sets socket-level options.
 * The option list is a list of pairs (option, option_value) terminated
 * by NULL.
 */
#if 0
int in_bind_opt( port, type, va_alist )
	short port ;
	int type ;
	va_dcl
{
	int sd ;
	enum status_e status ;
	va_list ap ;
	
	if ( ( sd = socket( AF_INET, type, 0 ) ) == -1 )
		return( SYSCALL_ERROR ) ;
	
	va_start( ap ) ;
	ap = apply_options( sd, ap, &status );
	va_end( ap ) ;
	if ( status == ERROR )
		return( OPTION_ERROR ) ;

	return( bind_address( sd, port ) ) ;
}
#endif



/*
 * bind_address binds the address <INADDR_ANY,port> to a socket.
 * Return value:
 *		socket			: if successful
 *		SYSCALL_ERROR	: if bind fails
 *		RESOLVER_ERROR	: if it can't get the address of the local host
 */
PRIVATE int bind_address( sd, port )
	int sd ;
	short port ;
{
	struct sockaddr_in sin ;
	int errno_save ;

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

	if ( bind( sd, (struct sockaddr *) &sin, sizeof( sin ) ) == -1 )
	{
		CLOSE( sd, errno_save ) ;
		return( SYSCALL_ERROR ) ;
	}
	return( sd ) ;
}


/*
 * Apply the options in the option_list.
 * Return value:
 *		The remaining part of the argument list
 *
 * The status argument shows if there has been an error.
 * In case of error, the socket is closed.
 */
#if 0
PRIVATE va_list apply_options( sd, option_list, status )
	int sd ;
	va_list option_list ;
	enum status_e *status ;
{
	register int option ;
	int errno_save ;

	/*
	 * Apply the options
	 */
	while ( ( option = va_arg( option_list, int ) ) != NULL )
	{
		int option_value ;

		/*
		 * For the moment accept only boolean options
		 */
		switch ( option )
		{
			case SO_DEBUG:
			case SO_REUSEADDR:
			case SO_KEEPALIVE:
			case SO_DONTROUTE:
			case SO_BROADCAST:
			case SO_OOBINLINE:
				option_value = va_arg( option_list, int ) ;
				if ( setsockopt( sd, SOL_SOCKET, option,
							&option_value, sizeof( option_value ) ) == -1 )
				{
					CLOSE( sd, errno_save ) ;
					*status = ERROR ;
					return( option_list ) ;
				}
				break ;

			default:
				break ;
		}
	}
	*status = OK ;
	return( option_list ) ;
}
#endif

PRIVATE	int connect_to_host() ;

/*
 * Connect to an internet address. The address is specified by the
 * host, port arguments. Type is the type of the socket which
 * defines the type of the protocol to be used.
 * If host is NULL, then the local host is implied.
 *
 * Return value:
 *		a file descriptor	:  if the operation is successful
 *		SYSCALL_ERROR 		: if there is a system call error
 *		RESOLVER_ERROR 	: if there is a resolver error
 * In case of error errno contains a description of the error
 */
int in_connect( host, port, type )
	char *host ;
	short port ;
	int type ;
{
	int sd ;

	if ( ( sd = socket( AF_INET, type, 0 ) ) == -1 )
		return( SYSCALL_ERROR ) ;
	
	return( connect_to_host( sd, host, port ) ) ;
}



/*
 * Same as in_connect but also sets socket-level options
 */
#if 0
int in_connect_opt( host, port, type, va_alist )
	char *host ;
	short port ;
	int type ;
	va_dcl
{
	int sd ;
	enum status_e status ;
	va_list ap ;
	int connect_to_host() ;
	va_list apply_options() ;

	if ( ( sd = socket( AF_INET, type, 0 ) ) == -1 )
		return( SYSCALL_ERROR ) ;
	
	va_start( ap ) ;
	ap = apply_options( sd, ap, &status ) ;
	va_end( ap ) ;
	if ( status == ERROR )
		return( OPTION_ERROR ) ;
	
	return( connect_to_host( sd, host, port ) ) ;
}
#endif

/*
 * connect_to_host tries to connect socket sd to the address <host,port>
 * Return value:
 *		socket				: if the operation is successful
 *		RESOLVER_ERROR		: if there is a resolver error
 *		SYSCALL_ERROR		: if connect(2) fails
 */
PRIVATE int connect_to_host( sd, host, port )
	int sd ;
	char *host ;
	short port ;
{
	struct sockaddr_in sin ;
	int errno_save ;

	if ( in_address( host, port, &sin ) == RESOLVER_ERROR )
	{
		CLOSE( sd, errno_save ) ;
		return( RESOLVER_ERROR ) ;
	}

	if ( connect( sd, (struct sockaddr *) &sin, sizeof( sin ) ) == -1 )
	{
		CLOSE( sd, errno_save ) ;
		return( SYSCALL_ERROR ) ;
	}
	return( sd ) ;
}