/**************************************************************************
 * LPRng IFHP Filter
 * Copyright 1994-1999 Patrick Powell, San Diego, CA <papowell@astart.com>
 **************************************************************************/
/**** HEADER *****/
 static char *const _id = "$Id: open_device.c,v 1.23 2004/02/25 15:56:25 papowell Exp papowell $";

#include "ifhp.h"

void Open_device( char *device )
{
	int fd = -1;
	struct stat statb;
	int attempt = 0;

	DEBUG1("Open_device: device '%s'", device);

	if( fstat(1,&statb) == 0 && S_ISSOCK(statb.st_mode) ){
		char msg[32];
		DEBUG1("Open_device: closing socket");
		shutdown(1,1);
		while( read(1,msg,sizeof(msg)) > 0 );
	}
	close(1);
	DEBUG1("Open_device: closed fd 1");
	Errorcode = 0;
	if( (device[0] == '/') ){
		for( ; fd < 0; ++attempt, plp_usleep(Dev_sleep * 1000) ){
			DEBUG1("Open_device: device '%s', attempt %d",
				device, attempt);
			fd = open( device, (Status?O_RDWR:O_WRONLY)|O_APPEND|O_CREAT|O_NONBLOCK, 0600 );
			/*Set_max_fd(fd);*/
			if( fd < 0 ){
				if( Dev_retries < 0 || (Dev_retries >0 && attempt >= Dev_retries) ){
					Errorcode = JABORT;
					LOGERR_DIE(LOG_INFO)"Open_device: open '%s' failed", device);
				}
			} else {
				break;
			}
		}
		if( isatty( fd ) && Stty_args){
			Do_stty( fd, Stty_args );
		}
		/* can we get status */
	} else {
		fd = Link_open( device );
	}
	if( fd != 1 ){
		Errorcode = JABORT;
		LOGERR_DIE(LOG_INFO)"Open_device: did not open on fd 1");
	}
	if( Status ){
		Status = Fd_readable(1, &Poll_for_status);
	}
	Set_block_io(1);
	Set_keepalive(1);
	DEBUG1("Open_device: success");
}

void Set_keepalive( int sock )
{
#ifdef SO_KEEPALIVE
	int option = 0;
	int len;
	len = sizeof( option );
	setsockopt( sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&option, sizeof(option) );
#endif
}

int Link_open( char * device )
{ 
	char *s, *end;
	int fd = -1, port, option, attempt = 1;
	struct hostent *hostent;
	struct sockaddr_in sin;
	int src_port = 0;
	char *dcopy = 0;
	dcopy = safestrdup( device,__FILE__,__LINE__ );
	if( (end = strchr( dcopy, '%' )) == 0 ){
		Errorcode = JABORT;
		FATAL(LOG_INFO)"Link_open: missing port number '%s'",device );
	}
	*end++ = 0; 
	s = end;
	port = strtol( end, &s, 0 );
	if( port <= 0 ){
		Errorcode = JABORT;
		FATAL(LOG_INFO)"Link_open: bad port number '%s'",end );
	}
	end = s;
	if( end && *end ){
		*end++ = 0;
		src_port = strtol( end, 0, 0 );
	}
	hostent = gethostbyname(dcopy);
	if( hostent && hostent->h_addrtype != AF_INET ){
		Errorcode = JABORT;
		FATAL(LOG_INFO)"Link_open: bad address type for host '%s'", device);
		cleanup(0);
	}
	for( ; fd < 0; ++attempt, plp_usleep(Dev_sleep * 1000) ){
		memset( &sin, 0, sizeof(sin) );
		sin.sin_family = AF_INET;
			/*
			 * set up the address information
			 */
		if( hostent ){
			memmove( &sin.sin_addr, hostent->h_addr, hostent->h_length );
		} else {
			sin.sin_addr.s_addr = inet_addr(dcopy);
			if( (int)sin.sin_addr.s_addr == -1){
				Errorcode = JABORT;
				FATAL(LOG_INFO)"Link_open: getconnection: unknown host '%s'", device);
				cleanup(0);
			}
		}
		sin.sin_port = htons( port );
		DEBUG1("Link_open: destination '%s' port %d, attempt %d",
			inet_ntoa( sin.sin_addr ), ntohs( sin.sin_port ), attempt );
		fd = socket (AF_INET, SOCK_STREAM, 0);
		if( fd < 0 ){
			Errorcode = JABORT;
			LOGERR_DIE(LOG_INFO) "Link_open: socket() failed" ); 
			if( Dev_retries <= 0 || (Dev_retries > 0 && attempt >= Dev_retries) ){
				Errorcode = JABORT;
				LOGERR_DIE(LOG_INFO)"Link_open: open '%s' failed", device);
			}
			continue;
		}
		option = 1;
		if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
				(char *)&option, sizeof(option) ) ){
			Errorcode = JABORT;
			LOGERR_DIE(LOG_INFO) "Link_open: setsockopt failed" );
		}
		if( src_port ){
			struct sockaddr_in src_sin;
			memset( &src_sin, 0, sizeof(src_sin) );
			src_sin.sin_family = AF_INET;
			src_sin.sin_port = htons( src_port );
			if( bind( fd, (struct sockaddr *)&src_sin,
				sizeof( src_sin )) < 0 ){
				LOGERR(LOG_INFO)"Link_open: bind failed");
				close(fd);
				fd = -1;
				++src_port;
				continue;
			}
		}
		if( connect(fd, (struct sockaddr *) & sin, sizeof (sin)) < 0 ){
			Errorcode = JFAIL;
			LOGERR(LOG_INFO)"Link_open: connect to '%s port %d' failed",
				inet_ntoa( sin.sin_addr ), ntohs( sin.sin_port ) );
			close(fd);
			fd = -1;
			continue;
		}
		if( fd >= 0 ) break;
	}
	if( dcopy ) free(dcopy); dcopy = 0;
	return(fd);
}

#if !defined(HAVE_INET_NTOP)
/***************************************************************************
 * inet_ntop( int family, const void *addr,
 *    const char *strptr, int len )
 *  p means presentation, i.e. - ASCII C string
 *  n means numeric, i.e. - network byte ordered address
 * family = AF_Protocol or AF_Protocol6
 * addr   = binary to convert
 * strptr = string where to place
 * len    = length
 ***************************************************************************/
 const char *inet_ntop( int family, const void *addr,
	char *str, size_t len )
{
	char *s;
	if( family != AF_INET ){
		FATAL(LOG_INFO) "inet_ntop: bad family '%d'", family );
	}
	s = inet_ntoa(((struct in_addr *)addr)[0]);
	strncpy( str, s, len );
	return(str);
}
#endif

const char *inet_ntop_sockaddr( struct sockaddr *addr,
    char *str, int len )
{
    void *a = 0;
    /* we get the printable from the socket address */
    if( addr->sa_family == AF_INET ){
        a = &((struct sockaddr_in *)addr)->sin_addr;
#if defined(IN6_ADDR)
    } else if( addr->sa_family == AF_INET6 ){
        a = &((struct sockaddr_in6 *)addr)->sin6_addr;
#endif
    } else {
        FATAL(LOG_INFO)"inet_ntop_sockaddr: bad family '%d'",
            addr->sa_family );
    }
    return( inet_ntop( addr->sa_family, a, str, len ) );
}
