/*
  This software is available to you under a choice of one of two
  licenses.  You may choose to be licensed under the terms of the GNU
  General Public License (GPL) Version 2, available at
  <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
  license, available in the LICENSE.TXT file accompanying this
  software.  These details are also available at
  <http://openib.org/license.html>.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.

  Copyright (c) 2004 Topspin Communications.  All rights reserved.
  Copyright (c) 2005-2006 Mellanox Technologies Ltd.  All rights reserved.

  $Id$
*/

/*
 * system includes
 */
#if HAVE_CONFIG_H
#  include <config.h>
#endif /* HAVE_CONFIG_H */

#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define __USE_GNU
#define _GNU_SOURCE				  /* define RTLD_NEXT */
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/poll.h>
/*
 * SDP specific includes
 */
#include "libsdp.h"

/* We can not use sizeof(sockaddr_in6) as the extra scope_id field is not a must have */
#define IPV6_ADDR_IN_MIN_LEN 24

/* setsockopt() level and optname declarations */
#define SOL_SDP		1025
#define SDP_UNBIND	259		  /* Unbind socket */

/* HACK: filter ioctl errors for FIONREAD */
#define FIONREAD 0x541B

void __attribute__ ( ( constructor ) ) __sdp_init( void );
void __attribute__ ( ( destructor ) ) __sdp_fini( void );

/* --------------------------------------------------------------------- */
/* library type definitions.                                             */
/* --------------------------------------------------------------------- */

typedef int (
	*ioctl_func_t ) (
	int fd,
	int request,
	void *arg0,
	void *arg1,
	void *arg2,
	void *arg3,
	void *arg4,
	void *arg5,
	void *arg6,
	void *arg7 );

typedef int (
	*fcntl_func_t ) (
	int fd,
	int cmd,
	... );

typedef int (
	*socket_func_t ) (
	int domain,
	int type,
	int protocol );

typedef int (
	*setsockopt_func_t ) (
	int s,
	int level,
	int optname,
	const void *optval,
	socklen_t optlen );

typedef int (
	*connect_func_t ) (
	int sockfd,
	const struct sockaddr * serv_addr,
	socklen_t addrlen );

typedef int (
	*listen_func_t ) (
	int s,
	int backlog );

typedef int (
	*bind_func_t ) (
	int sockfd,
	const struct sockaddr * my_addr,
	socklen_t addrlen );

typedef int (
	*close_func_t ) (
	int fd );

typedef int (
	*dup_func_t ) (
	int fd );

typedef int (
	*dup2_func_t ) (
	int oldfd,
	int newfd );

typedef int (
	*getsockname_func_t ) (
	int fd,
	struct sockaddr * name,
	socklen_t * namelen );

typedef int (
	*getpeername_func_t ) (
	int fd,
	struct sockaddr * name,
	socklen_t * namelen );

typedef int (
	*accept_func_t ) (
	int fd,
	struct sockaddr * addr,
	socklen_t * addrlen );

typedef int (
	*select_func_t ) (
	int n,
	fd_set * readfds,
	fd_set * writefds,
	fd_set * exceptfds,
	struct timeval * timeout );

typedef int (
	*pselect_func_t ) (
	int n,
	fd_set * readfds,
	fd_set * writefds,
	fd_set * exceptfds,
	const struct timespec * timeout,
	const sigset_t * sigmask);

typedef int (
	*poll_func_t ) (
	struct pollfd *ufds, 
	unsigned long int nfds, 
	int timeout);

struct socket_lib_funcs
{
	ioctl_func_t ioctl;
	fcntl_func_t fcntl;
	socket_func_t socket;
	setsockopt_func_t setsockopt;
	connect_func_t connect;
	listen_func_t listen;
	bind_func_t bind;
	close_func_t close;
	dup_func_t dup;
	dup2_func_t dup2;
	getpeername_func_t getpeername;
	getsockname_func_t getsockname;
	accept_func_t accept;
	select_func_t select;
	pselect_func_t pselect;
	poll_func_t poll;
};										  /* socket_lib_funcs */

static int simple_sdp_library;
static int max_file_descriptors;
static int dev_null_fd;
volatile static int init_status = 0; /* 0: idle, 1:during, 2:ready */

/* --------------------------------------------------------------------- */
/* library static and global variables                                   */
/* --------------------------------------------------------------------- */
extern char *program_invocation_name, *program_invocation_short_name;

#ifdef RTLD_NEXT
static void *__libc_dl_handle = RTLD_NEXT;
#else
static void *__libc_dl_handle;
#endif

/* extra fd attributes we need for our algorithms */
struct sdp_extra_fd_attributes
{
	int shadow_fd;					  /* file descriptor of shadow sdp socket    */
	short last_accept_was_tcp;	  /* used by accept to alternate tcp and sdp */
	short is_sdp;					  /* 1 if the fd represents an sdp socket    */
#ifndef SDP_SUPPORTS_IPv6
	short local_is_ipv6;			  /* set to 1 if local address uses IPv6     */
	short remote_is_ipv6;		  /* set to 1 if remote address uses IPv6    */
#endif
};										  /* sdp_extra_fd_attributes */

/* stores the extra attributes struct by fd */
static struct sdp_extra_fd_attributes *libsdp_fd_attributes;

static struct socket_lib_funcs _socket_funcs = {
	.socket = NULL,
	/* Automatically sets all other elements to NULL */
};										  /* _socket_funcs */

/* --------------------------------------------------------------------- */
/* Prototypes                                                            */
/* --------------------------------------------------------------------- */
void __sdp_init(
	void );

/* --------------------------------------------------------------------- */
/*                                                                       */
/* local static functions.                                               */
/*                                                                       */
/* --------------------------------------------------------------------- */

/* ========================================================================= */
/*..init_extra_attribute -- initialize the set of extra attributes for a fd */
static void
init_extra_attribute(
	int fd )
{
	if ( ( 0 <= fd ) && ( max_file_descriptors > fd ) ) {
		libsdp_fd_attributes[fd].shadow_fd = -1;
		libsdp_fd_attributes[fd].is_sdp = 0;
		libsdp_fd_attributes[fd].last_accept_was_tcp = -1;
#ifndef SDP_SUPPORTS_IPv6
		libsdp_fd_attributes[fd].local_is_ipv6 = 0;
		libsdp_fd_attributes[fd].remote_is_ipv6 = 0;
#endif
	}
}

static inline int
is_valid_fd(
	int fd )
{
	return ( 0 <= fd ) && ( fd < max_file_descriptors );
}

/* ========================================================================= */
/*..get_shadow_fd_by_fd -- given an fd return its shadow fd if exists        */
static inline int
get_shadow_fd_by_fd(
	int fd )
{
	if ( is_valid_fd( fd ) )
		return libsdp_fd_attributes[fd].shadow_fd;
	else
		return -1;
}

/* ========================================================================= */
/*..set_shadow_for_fd --                                                     */
static inline void
set_shadow_for_fd(
	int fd,
	int shadow_fd )
{
	if ( is_valid_fd( fd ) )
		libsdp_fd_attributes[fd].shadow_fd = shadow_fd;
}

/* ========================================================================= */
/*..set_is_sdp_socket --                                                     */
static inline void
set_is_sdp_socket(
	int fd,
	short is_sdp )
{
	if ( is_valid_fd( fd ) )
		libsdp_fd_attributes[fd].is_sdp = is_sdp;
}

/* ========================================================================= */
/*..get_is_sdp_socket -- given an fd return 1 if it is an SDP socket         */
static inline int
get_is_sdp_socket(
	int fd )
{
	if ( is_valid_fd( fd ) )
		return libsdp_fd_attributes[fd].is_sdp;
	else
		return 0;
}

/* ========================================================================= */
/*..last_accept_was_tcp -- given an fd return 1 if last accept was tcp       */
static inline int
last_accept_was_tcp(
	int fd )
{
	if ( is_valid_fd( fd ) )
		return libsdp_fd_attributes[fd].last_accept_was_tcp;
	else
		return 0;
}

/* ========================================================================= */
/*..set_last_accept -- given an fd set last accept was tcp                   */
static inline void
set_last_accept(
	int fd,
	int was_tcp )
{
	if ( is_valid_fd( fd ) )
		libsdp_fd_attributes[fd].last_accept_was_tcp = was_tcp;
}

/* ========================================================================= */
/*..cleanup_shadow -- an error occured on an SDP socket, cleanup             */
static int
cleanup_shadow(
	int fd )
{
	int shadow_fd = get_shadow_fd_by_fd( fd );

	if ( shadow_fd == -1 )
		return 0;
	libsdp_fd_attributes[fd].shadow_fd = -1;
	libsdp_fd_attributes[fd].last_accept_was_tcp = 0;
	return ( _socket_funcs.close( shadow_fd ) );
}										  /* cleanup_shadow */

/* ========================================================================= */
/*..replace_fd_with_its_shadow -- perform all required for such promotion    */
static int
replace_fd_with_its_shadow(
	int fd )
{
	int shadow_fd = libsdp_fd_attributes[fd].shadow_fd;

	if ( shadow_fd == -1 ) {
		__sdp_log( 9, "Error replace_fd_with_its_shadow: no shadow for fd:%d\n",
					  fd );
		return EINVAL;
	}

	/* copy the attributes of the shadow before we clean them up */
	libsdp_fd_attributes[fd] = libsdp_fd_attributes[shadow_fd];
	libsdp_fd_attributes[fd].shadow_fd = -1;
	if ( _socket_funcs.dup2( shadow_fd, fd ) < 0 ) {
		init_extra_attribute( fd );
		_socket_funcs.close( shadow_fd );
		return EINVAL;
	}
	_socket_funcs.close( shadow_fd );
	return 0;
}

/* ========================================================================= */
/* is_ipv4_embedded_in_ipv6 -- return 1 if the given ipv6 address is ipv4   */
static int
is_ipv4_embedded_in_ipv6(
	const struct sockaddr_in6 *sin6 )
{
	static struct in6_addr ipv4_embedded_addr = {.s6_addr = {0}, };

	/* 10 first bytes must be 0 */
	if ( memcmp
		  ( &ipv4_embedded_addr.s6_addr[0], &sin6->sin6_addr.s6_addr[0], 10 ) )
		return ( 0 );

	/* next two must be all zeros or all ones */
	if ( ( ( sin6->sin6_addr.s6_addr[10] == 0 ) &&
			 ( sin6->sin6_addr.s6_addr[11] == 0 ) ) ||
		  ( ( sin6->sin6_addr.s6_addr[10] == 0xff ) &&
			 ( sin6->sin6_addr.s6_addr[11] == 0xff ) ) )
		return ( 1 );

	return ( 0 );
}

/* ========================================================================= */
/*..__sdp_sockaddr_to_sdp -- convert the address to sdp address. ret 0 if ok */
/* note: not required once SDP supports IPv6 ...                             */
int
__sdp_sockaddr_to_sdp(
	const struct sockaddr *addr_in,
	socklen_t addrlen,
	struct sockaddr_in *addr_out,
	int *was_ipv6 )
{
	const struct sockaddr_in *sin = ( const struct sockaddr_in * )addr_in;
	const struct sockaddr_in6 *sin6 = ( const struct sockaddr_in6 * )addr_in;
	char buf[MAX_ADDR_STR_LEN];

	/* currently SDP supports only IPv4 ... */
	if ( !addr_in ) {
		__sdp_log( 9, "Error __sdp_sockaddr_to_sdp: "
					  "provided NULL input pointer\n" );
		errno = EINVAL;
		return -1;
	}
	if ( !addr_out ) {
		__sdp_log( 9, "Error __sdp_sockaddr_to_sdp: "
					  "provided NULL output pointer\n" );
		errno = EINVAL;
		return -1;
	}

	if ( sin->sin_family == AF_INET ) {
		__sdp_log( 1, "__sdp_sockaddr_to_sdp: Given IPv4\n" );
		if ( addrlen < sizeof( struct sockaddr_in ) ) {
			__sdp_log( 9, "Error __sdp_sockaddr_to_sdp: "
						  "provided address length:%d < IPv4 length %d\n",
						  addrlen, sizeof( struct sockaddr_in ) );
			errno = EINVAL;
			return -1;
		}

		memcpy( addr_out, sin, sizeof( struct sockaddr_in ) );
		if ( was_ipv6 )
			*was_ipv6 = 0;
	} else if ( sin6->sin6_family == AF_INET6 ) {
		if ( addrlen < IPV6_ADDR_IN_MIN_LEN ) {
			__sdp_log( 9, "Error __sdp_sockaddr_to_sdp: "
						  "provided address length:%d < IPv6 length %d\n",
						  addrlen, IPV6_ADDR_IN_MIN_LEN );
			errno = EINVAL;
			return -1;
		}

		/* cannot convert IPv6 that is not IPv4 embedding */
		if ( !is_ipv4_embedded_in_ipv6( sin6 ) ) {
			__sdp_log( 9, "Error __sdp_sockaddr_to_sdp: "
						  "Given IPv6 address not an embedded IPv4\n" );
			errno = EINVAL;
			return -1;
		}
		memset( addr_out, 0, sizeof( struct sockaddr_in ) );
		memcpy( &addr_out->sin_addr, &( sin6->sin6_addr.s6_addr[12] ), 4 );

		if ( addr_out->sin_addr.s_addr == ntohl( 1 ) ) {
			addr_out->sin_addr.s_addr = htonl( INADDR_LOOPBACK );
			__sdp_log( 1,
						  "__sdp_sockaddr_to_sdp: Given IPv6 loopback address\n" );
		} else {
			__sdp_log( 1,
						  "__sdp_sockaddr_to_sdp: Given IPv4 embedded in IPv6\n" );
		}
		addr_out->sin_family = AF_INET;
		addr_out->sin_port = sin6->sin6_port;

		if ( __sdp_log_get_level(  ) <= 1 ) {
			if ( inet_ntop
				  ( addr_out->sin_family, ( void * )&( addr_out->sin_addr ), buf,
					 MAX_ADDR_STR_LEN ) == NULL ) {
				__sdp_log( 1,
							  "__sdp_sockaddr_to_sdp: "
							  "Converted IPv4 address is illegal\n" );
			} else {
				__sdp_log( 1, "__sdp_sockaddr_to_sdp: Converted IPv4 is:%s\n",
							  buf );
			}
		}
		if ( was_ipv6 )
			*was_ipv6 = 1;

	} else if ( sin->sin_family == 0 ) {
		__sdp_log( 1, "__sdp_sockaddr_to_sdp: Converted NULL address\n" );
		memcpy( addr_out, addr_in, addrlen );
	} else if ( sin->sin_family == AF_INET_SDP ) {
		__sdp_log( 1, "__sdp_sockaddr_to_sdp: Given SDP address\n" );
		memcpy( addr_out, addr_in, addrlen );
	} else {
		__sdp_log( 9, "Error __sdp_sockaddr_to_sdp: "
					  "address family <%d> is unknown\n", sin->sin_family );
		errno = EAFNOSUPPORT;
		return -1;
	}

	return 0;
}

/* ========================================================================= */
/*..sockaddr_from_sdp -- convert IPv4 address from sdp address to original one */
static int
sockaddr_from_sdp(
	int to_ipv6,
	const struct sockaddr_in *addr_in,
	struct sockaddr *addr_out,
	socklen_t * addrlen )
{
	struct sockaddr_in *sout = ( struct sockaddr_in * )addr_out;
	struct sockaddr_in6 *sout6 = ( struct sockaddr_in6 * )addr_out;
	char buf[MAX_ADDR_STR_LEN + 1];

	if ( !addr_out ) {
		__sdp_log( 9, "Error sockaddr_from_sdp: "
					  "provided NULL output pointer\n" );
		return -1;
	}

	if ( !addrlen ) {
		__sdp_log( 9, "Error sockaddr_from_sdp: "
					  "provided NULL output addrlen\n" );
		return -1;
	}

	/* TODO: currently SDP supports only IPv4, when IPv6 supported just change family */
	if ( !to_ipv6 ) {
		__sdp_log( 1, "sockaddr_from_sdp: Converting to IPv4\n" );
		if ( *addrlen < sizeof( struct sockaddr_in ) ) {
			__sdp_log( 9, "Error sockaddr_from_sdp: "
						  "provided address length:%d < IPv4 length %d",
						  *addrlen, sizeof( struct sockaddr_in ) );
			return -1;
		}
		*addrlen = sizeof( struct sockaddr_in );
		memcpy( addr_out, addr_in, *addrlen );
		sout->sin_family = AF_INET;
	} else {
		__sdp_log( 1, "sockaddr_from_sdp: "
					  "Converting to IPv6 (embedded IPv4)\n" );
		if ( *addrlen < IPV6_ADDR_IN_MIN_LEN ) {
			__sdp_log( 9, "Error sockaddr_from_sdp: "
						  "provided address length:%d < IPv6 length %d\n",
						  *addrlen, IPV6_ADDR_IN_MIN_LEN );
			return -1;
		} else if ( *addrlen < sizeof( struct sockaddr_in6 ) ) {
			*addrlen = IPV6_ADDR_IN_MIN_LEN;
		} else {
			*addrlen = sizeof( struct sockaddr_in6 );
		}
		memset( addr_out, 0, *addrlen );
		sout6->sin6_family = AF_INET6;
		sout6->sin6_port = addr_in->sin_port;

		/* special case is the loopback address */
		if ( addr_in->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) {
			memcpy( sout6->sin6_addr.s6_addr,
					  &in6addr_loopback, sizeof( struct in6_addr ) );
		} else {
			memcpy( &( sout6->sin6_addr.s6_addr[12] ), &addr_in->sin_addr, 4 );
			sout6->sin6_addr.s6_addr[10] = 0xff;
			sout6->sin6_addr.s6_addr[11] = 0xff;
		}

		if ( __sdp_log_get_level(  ) <= 1 ) {
			inet_ntop( AF_INET6, ( void * )&( sout6->sin6_addr ), buf,
						  MAX_ADDR_STR_LEN );
			__sdp_log( 1, "sockaddr_from_sdp: " "Obtained: %s\n", buf );
		}
	}
	return 0;
}

/* ========================================================================= */
/*..is_filtered_unsuported_sockopt -- return 1 if to filter sockopt failure  */
static inline int
is_filtered_unsuported_sockopt(
	int level,
	int optname )
{
	/* 
	 * TODO: until we know exactly which unsupported opts are really 
	 * a don't care we always pass the error
	 */
	return 0;
#if 0
	/* these are the SOL_TCP OPTS we should consider filterring */
	TCP_NODELAY 1					  /* Don't delay send to coalesce packets  */
		TCP_MAXSEG 2				  /* Set maximum segment size  */
		TCP_CORK 3					  /* Control sending of partial frames  */
		TCP_KEEPIDLE 4				  /* Start keeplives after this period */
		TCP_KEEPINTVL 5			  /* Interval between keepalives */
		TCP_KEEPCNT 6				  /* Number of keepalives before death */
		TCP_SYNCNT 7				  /* Number of SYN retransmits */
		TCP_LINGER2 8				  /* Life time of orphaned FIN-WAIT-2 state */
		TCP_DEFER_ACCEPT 9		  /* Wake up listener only when data arrive */
		TCP_WINDOW_CLAMP 10		  /* Bound advertised window */
		TCP_INFO 11					  /* Information about this connection. */
		TCP_QUICKACK 12			  /* Bock/reenable quick ACKs.  */
#endif
}

/* ========================================================================= */
/*..is_invalid_addr -- return 1 if given pointer is not valid                */
/* NOTE: invalidation of the size is going to happen during actual call      */
static inline int
is_invalid_addr(
	const void *p )
{
	/* HACK: on some systems we can not write to check for pointer validity */
	size_t ret = fcntl( dev_null_fd, F_GETLK, p );

	ret = ( errno == EFAULT );
	errno = 0;
	return ret;
}

/* ========================================================================= */
/*..get_addr_str -- fill in the given buffer with addr str or return 1       */
static int
get_addr_str(
	const struct sockaddr *addr,
	char *buf,
	size_t len )
{
	const struct sockaddr_in *sin = ( struct sockaddr_in * )addr;
	const struct sockaddr_in6 *sin6 = ( struct sockaddr_in6 * )addr;
	char const *conv_res;

	if ( sin->sin_family == AF_INET ) {
		conv_res = inet_ntop( AF_INET, ( void * )&( sin->sin_addr ), buf, len );
	} else if ( sin6->sin6_family == AF_INET6 ) {
		conv_res = inet_ntop( AF_INET6, ( void * )&sin6->sin6_addr, buf, len );
	} else {
		strncpy( buf, "unknown address family", len );
		conv_res = ( char * )1;
	}
	return conv_res == NULL;
}

/* --------------------------------------------------------------------- */
/*                                                                       */
/* Socket library function overrides.                                    */
/*                                                                       */
/* --------------------------------------------------------------------- */

/* ========================================================================= */
/*..ioctl -- replacement ioctl call. */
int
ioctl(
	int fd,
	int request,
	void *arg0,
	void *arg1,
	void *arg2,
	void *arg3,
	void *arg4,
	void *arg5,
	void *arg6,
	void *arg7 )
{
	int shadow_fd;
	int sret = 0;
	int ret = 0;

	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.ioctl ) {
		__sdp_log( 9, "Error ioctl: no implementation for ioctl found\n" );
		return -1;
	}

	shadow_fd = get_shadow_fd_by_fd( fd );

	__sdp_log( 2, "IOCTL: <%s:%d:%d> request <%d>\n",
				  program_invocation_short_name, fd, shadow_fd, request );

	ret = _socket_funcs.ioctl( fd, request,
										arg0, arg1, arg2, arg3, arg4, arg5, arg6,
										arg7 );

	/* HACK: avoid failing on FIONREAD error as SDP does not support it at the moment */
	if ( ( ret < 0 ) && get_is_sdp_socket( fd ) && ( request == FIONREAD ) ) {
		__sdp_log( 8, "Warning ioctl: "
					  "Ignoring FIONREAD error for SDP socket.\n" );
		ret = 0;
	}

	/* if shadow and no error on tcp */
	if ( ( ret >= 0 ) && ( -1 != shadow_fd ) ) {
		sret = _socket_funcs.ioctl( shadow_fd, request,
											 arg0, arg1, arg2, arg3, arg4, arg5, arg6,
											 arg7 );
		/* HACK: avoid failing on FIONREAD error as SDP does not support it at the moment */
		if ( ( sret < 0 ) && ( request == FIONREAD ) ) {
			__sdp_log( 8, "Warning ioctl: "
						  "Ignoring FIONREAD error for shadow SDP socket.\n" );
			sret = 0;
		}

		if ( sret < 0 ) {
			__sdp_log( 9, "Error ioctl: "
						  "<%d> calling ioctl for SDP socket, closing it.\n",
						  errno );
			cleanup_shadow( fd );
		}
	}

	__sdp_log( 2, "IOCTL: <%s:%d:%d> result <%d:%d>\n",
				  program_invocation_short_name, fd, shadow_fd, ret, sret );

	return ret;
}										  /* ioctl */

/* ========================================================================= */
/*..fcntl -- replacement fcntl call.                                         */
int
fcntl(
	int fd,
	int cmd,
	... )
{
	int shadow_fd;
	int sret = 0;
	int ret = 0;

	void *arg;
	va_list ap;

	va_start( ap, cmd );
	arg = va_arg( ap, void * );
	va_end( ap );


	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.fcntl ) {
		__sdp_log( 9, "Error fcntl: no implementation for fcntl found\n" );
		return -1;
	}

	shadow_fd = get_shadow_fd_by_fd( fd );

	__sdp_log( 2, "FCNTL: <%s:%d:%d> command <%d> argument <%p>\n",
				  program_invocation_short_name, fd, shadow_fd, cmd, arg );

	ret = _socket_funcs.fcntl( fd, cmd, arg );
	if ( ( ret >= 0 ) && ( -1 != shadow_fd ) ) {
		sret = _socket_funcs.fcntl( shadow_fd, cmd, arg );
		if ( sret < 0 ) {
			__sdp_log( 9, "Error fcntl:"
						  " <%d> calling fcntl(%d, %d, %p) for SDP socket. Closing it.\n",
						  shadow_fd, cmd, arg, errno );
			cleanup_shadow( fd );
		}
	}

	__sdp_log( 2, "FCNTL: <%s:%d:%d> result <%d:%d>\n",
				  program_invocation_short_name, fd, shadow_fd, ret, sret );

	return ret;
}										  /* fcntl */

/* ========================================================================= */
/*..setsockopt -- replacement setsockopt call.                               */
int
setsockopt(
	int fd,
	int level,
	int optname,
	const void *optval,
	socklen_t optlen )
{
	int shadow_fd;
	int sret = 0;
	int ret = 0;

	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.setsockopt ) {
		__sdp_log( 9, "Error setsockopt:"
					  " no implementation for setsockopt found\n" );
		return -1;
	}

	shadow_fd = get_shadow_fd_by_fd( fd );

	__sdp_log( 2, "SETSOCKOPT: <%s:%d:%d> level <%d> name <%d>\n",
				  program_invocation_short_name, fd, shadow_fd, level, optname );

	if (level == SOL_SOCKET && optname == SO_KEEPALIVE && get_is_sdp_socket(fd)) {
		level = AF_INET_SDP;
		__sdp_log( 2, "SETSOCKOPT: <%s:%d:%d> substitute level %d\n",
			   program_invocation_short_name, fd, shadow_fd, level );
	}

	ret = _socket_funcs.setsockopt( fd, level, optname, optval, optlen );
	if ( ( ret >= 0 ) && ( shadow_fd != -1 ) ) {
		if (level == SOL_SOCKET && optname == SO_KEEPALIVE &&
		    get_is_sdp_socket(shadow_fd)) {
			level = AF_INET_SDP;
			__sdp_log( 2, "SETSOCKOPT: <%s:%d:%d> substitute level %d\n",
				   program_invocation_short_name, fd, shadow_fd, level );
		}

		sret = _socket_funcs.setsockopt( shadow_fd, level,
													optname, optval, optlen );
		if ( sret < 0 ) {
			__sdp_log( 8, "Warning sockopts:"
						  " ignoring error on shadow SDP socket fd:<%d>\n", fd );
			/* 
			 * HACK: we should allow some errors as some sock opts are unsupported  
			 * __sdp_log(9, "Error %d calling setsockopt for SDP socket, closing\n", errno); 
			 * cleanup_shadow(fd); 
			 */
		}
	}

	/* Due to SDP limited implmentation of sockopts we ignore some errors */
	if ( ( ret < 0 ) && get_is_sdp_socket( fd ) &&
		  is_filtered_unsuported_sockopt( level, optname ) ) {
		__sdp_log( 8, "Warning sockopts: "
					  "ignoring error on non implemented sockopt on SDP socket"
					  " fd:<%d> level:<%d> opt:<%d>\n", fd, level, optval );
		ret = 0;
	}

	__sdp_log( 2, "SETSOCKOPT: <%s:%d:%d> result <%d:%d>\n",
				  program_invocation_short_name, fd, shadow_fd, ret, sret );

	return ret;
}										  /* setsockopt */

/* ========================================================================= */
/*..socket -- replacement socket call.                                       */
int
socket(
	int domain,
	int type,
	int protocol )
{
	int s = -1;
	int shadow_fd = -1;
	use_family_t family_by_prog;

	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.socket ) {
		__sdp_log( 9, "Error socket: no implementation for socket found\n" );
		return -1;
	}

	__sdp_log( 2, "SOCKET: <%s> domain <%d> type <%d> protocol <%d>\n",
				  program_invocation_short_name, domain, type, protocol );

	/* check to see if we can skip the shadow */
	if ( ( AF_INET == domain || AF_INET6 == domain ) &&
		  ( SOCK_STREAM == type ) )
		if ( simple_sdp_library )
			family_by_prog = USE_SDP;
		else
			family_by_prog = __sdp_match_by_program(  );
	else if ( AF_INET_SDP == domain )
		family_by_prog = USE_SDP;
	else
		family_by_prog = USE_TCP;

	if ( family_by_prog == USE_TCP ) {
		__sdp_log( 1, "SOCKET: making TCP only socket (no shadow)\n" );
		s = _socket_funcs.socket( domain, type, protocol );
		init_extra_attribute( s );
		set_is_sdp_socket( s, 0 );
		goto done;
	}

	if ( family_by_prog == USE_SDP ) {
		/* HACK: convert the protocol if IPPROTO_IP */
		if ( protocol == 0 )
			protocol = IPPROTO_TCP;

		__sdp_log( 1, "SOCKET: making SDP socket type:%d proto:%d\n",
					  type, protocol );
		s = _socket_funcs.socket( AF_INET_SDP, type, protocol );
		init_extra_attribute( s );
		set_is_sdp_socket( s, 1 );
		goto done;
	}

	/* HACK: if we fail creating the TCP socket should we abort ? */
	__sdp_log( 1, "SOCKET: making TCP socket\n" );
	s = _socket_funcs.socket( domain, type, protocol );
	init_extra_attribute( s );
	set_is_sdp_socket( s, 0 );
	if ( is_valid_fd( s ) ) {
		if ( ( ( AF_INET == domain ) || ( AF_INET6 == domain ) ) &&
			  ( SOCK_STREAM == type ) ) {

			if ( protocol == 0 )
				protocol = IPPROTO_TCP;
			__sdp_log( 1, "SOCKET: making SDP shadow socket type:%d proto:%d\n",
						  type, protocol );
			shadow_fd = _socket_funcs.socket( AF_INET_SDP, type, protocol );
			if ( is_valid_fd( shadow_fd ) ) {
				init_extra_attribute( shadow_fd );
				if ( libsdp_fd_attributes[s].shadow_fd != -1 ) {
					__sdp_log( 8, "Warning socket: "
								  "overriding existing shadow fd:%d for fd:%d\n",
								  libsdp_fd_attributes[s].shadow_fd, s );
				}
				set_is_sdp_socket( shadow_fd, 1 );
				set_shadow_for_fd( s, shadow_fd );
			} else {
				__sdp_log( 9,
							  "Error socket: <%d> calling socket for SDP socket\n",
							  errno );
				/* fail if we did not make the SDP socket */
				__sdp_log( 1, "SOCKET: closing TCP socket:<%d>\n", s );
				_socket_funcs.close( s );
				s = -1;
			}
		}
	} else {
		__sdp_log( 9, "Error socket: "
					  "ignoring SDP socket since TCP fd:%d out of range\n", s );
	}

 done:
	__sdp_log( 2, "SOCKET: <%s:%d:%d>\n",
				  program_invocation_short_name, s, shadow_fd );

	return s;
}										  /* socket */

/* ========================================================================= */
/*..get_fd_addr_port_num - obtain the port the fd is attached to             */
static int
get_fd_addr_port_num(
	int sd )
{
	struct sockaddr_storage addr;
	int ret;
	const struct sockaddr_in *sin;
	socklen_t addrlen = sizeof( addr );

	ret =
		_socket_funcs.getsockname( sd, ( struct sockaddr * )&addr, &addrlen );

	if ( ret ) {
		__sdp_log( 9,
					  "Error: in get_fd_addr_port_num - Failed to get getsockname\n" );
		return -1;
	}

	/* port num is in same location for IPv4 and IPv6 */
	sin = ( const struct sockaddr_in * )&addr;
	return ntohs( sin->sin_port );
}

/* ========================================================================= */
/*..set_addr_port_num - sets the port in the given address                   */
static int
set_addr_port_num(
	struct sockaddr *addr,
	int port )
{
	struct sockaddr_in *sin = ( struct sockaddr_in * )addr;

	/* port num is in same location for IPv4 and IPv6 */
	sin->sin_port = htons( port );
	return 0;
}

/* ========================================================================= */
/*..find_free_port - find same free port on both TCP and SDP                 */
#define MAX_BIND_ANY_PORT_TRIES 20000
static int
find_free_port(
	const struct sockaddr *tcp_addr,
	const socklen_t addrlen,
	const struct sockaddr *sdp_addr,
	const socklen_t sdp_addrlen,
	int *sdp_sd,
	int *tcp_sd )
{
	static int tcp_turn = 1;
	int tmp_turn = tcp_turn;
	int num_of_loops = 0;
	int port = -1;
	int tmp_sd[2];
	int tmp_addr_len[2];
	struct sockaddr tmp_addr[2];
	int tmp_family[2];
	unsigned int yes = 1;
	int ret;

	__sdp_log( 2, "find_free_port: starting search for common free port\n" );

	tmp_addr_len[0] = sdp_addrlen;
	tmp_family[0] = AF_INET_SDP;

	tmp_addr_len[1] = addrlen;
	tmp_family[1] = AF_INET;

	do {
		__sdp_log( 1, "find_free_port: taking loop (%d)\n",
					  ++num_of_loops );
		tmp_addr[0] = *sdp_addr;
		tmp_addr[1] = *tcp_addr;

		__sdp_log( 1, "find_free_port: creating the two sockets\n" );
		tmp_sd[0] =
			_socket_funcs.socket( AF_INET_SDP, SOCK_STREAM, IPPROTO_TCP );
		if ( tmp_sd[0] < 0 ) {
			__sdp_log( 8,
						  "Warning find_free_port: creating first socket failed\n" );
			goto done;
		}

		_socket_funcs.setsockopt(tmp_sd[0], SOL_SOCKET, SO_REUSEADDR, &yes,
										 sizeof( yes ) );

		tmp_sd[1] = _socket_funcs.socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
		if ( tmp_sd[1] < 0 ) {
			__sdp_log( 8,
						  "Warning find_free_port: creating second socket failed\n" );
			_socket_funcs.close( tmp_sd[0] );
			goto done;
		}

		_socket_funcs.setsockopt(tmp_sd[1], SOL_SOCKET, SO_REUSEADDR, &yes,
										 sizeof( yes ) );

		__sdp_log( 1, "find_free_port: binding first %s socket\n",
					  tmp_turn ? "tcp": "sdp");
		ret = _socket_funcs.bind( tmp_sd[tmp_turn],
										  &tmp_addr[tmp_turn], tmp_addr_len[tmp_turn] );
		if ( ret < 0 ) {
			__sdp_log( 8,
						  "Warning find_free_port: binding first socket failed:%s\n",
						  strerror( errno ) );
			_socket_funcs.close( tmp_sd[0] );
			_socket_funcs.close( tmp_sd[1] );
			goto done;
		}

		__sdp_log( 1, "find_free_port: listening on first socket\n" );
		ret = _socket_funcs.listen( tmp_sd[tmp_turn], 5 );
		if ( ret < 0 ) {
			__sdp_log( 8,
						  "Warning find_free_port: listening on first socket failed:%s\n",
						  strerror( errno ) );
			_socket_funcs.close( tmp_sd[0] );
			_socket_funcs.close( tmp_sd[1] );
			goto done;
		}

		port = get_fd_addr_port_num( tmp_sd[tmp_turn] );
		if ( port < 0 ) {
			__sdp_log( 8, "Warning find_free_port: first socket port:%d < 0\n",
						  port );
			_socket_funcs.close( tmp_sd[0] );
			_socket_funcs.close( tmp_sd[1] );
			goto done;
		}
		__sdp_log( 1, "find_free_port: first socket port:%u\n", port );

		set_addr_port_num( &tmp_addr[1 - tmp_turn], port );

		__sdp_log( 1, "find_free_port: binding second socket\n" );
		ret = _socket_funcs.bind( tmp_sd[1 - tmp_turn],
										  &tmp_addr[1 - tmp_turn],
										  tmp_addr_len[1 - tmp_turn] );
		if ( ret < 0 ) {
			if ( errno != EADDRINUSE ) {
				__sdp_log( 8, "Warning find_free_port: "
							  "binding second socket failed with %s\n",
							  strerror( errno ) );
				_socket_funcs.close( tmp_sd[0] );
				_socket_funcs.close( tmp_sd[1] );
				goto done;
			} else {
				__sdp_log( 1, "find_free_port: %s port %u was busy\n",
							  1 - tmp_turn ? "tcp": "sdp", 
							  ntohs( ((struct sockaddr_in*)&tmp_addr[1 - tmp_turn])->sin_port) );
			}

			/* close the sockets - we will need new ones ... */
			__sdp_log( 1, "find_free_port: closing the two sockets before next loop\n" );
			_socket_funcs.close( tmp_sd[0] );
			_socket_funcs.close( tmp_sd[1] );

			port = -1;
			/* we always start with tcp so we keep the original setting for now */
			/* tmp_turn = 1 - tmp_turn; */
		}

	} while ( ( port < 0 ) && ( num_of_loops < MAX_BIND_ANY_PORT_TRIES) );

	tcp_turn = tmp_turn;
	*sdp_sd = tmp_sd[0];
	*tcp_sd = tmp_sd[1];

 done:
	__sdp_log( 2, "find_free_port: return port:<%d>\n", port );
	return port;
}

/* ========================================================================= */
/*..check_legal_bind - check if given address is okay for both TCP and SDP   */
static int
check_legal_bind(
	const struct sockaddr *tcp_addr,
	const socklen_t addrlen,
	const struct sockaddr *sdp_addr,
	int *sdp_sd,
	int *tcp_sd )
{
	unsigned int yes = 1;
	int ret = -1;
	int sret = -1;

	__sdp_log( 2, "check_legal_bind: binding two temporary sockets\n" );
	*sdp_sd = _socket_funcs.socket( AF_INET_SDP, SOCK_STREAM, IPPROTO_TCP );
	if ( *sdp_sd < 0 ) {
		__sdp_log( 9, "Error check_legal_bind: "
					  "creating SDP socket failed\n" );
		goto done;
	}

	__sdp_log( 2, "check_legal_bind: reusing <%d> \n", *sdp_sd );
	sret =
		_socket_funcs.setsockopt( *sdp_sd, SOL_SOCKET, SO_REUSEADDR, &yes,
										  sizeof( yes ) );
	if ( sret < 0 ) {
		__sdp_log( 9, "Error bind: Could not setsockopt sdp_sd\n" );
	}

	*tcp_sd = _socket_funcs.socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
	if ( *tcp_sd < 0 ) {
		__sdp_log( 9, "Error check_legal_bind: "
					  "creating second socket failed:%s\n", strerror( errno ) );
		_socket_funcs.close( *sdp_sd );
		goto done;
	}

	__sdp_log( 2, "check_legal_bind: reusing <%d> \n", *tcp_sd );
	sret =
		_socket_funcs.setsockopt( *tcp_sd, SOL_SOCKET, SO_REUSEADDR, &yes,
										  sizeof( yes ) );
	if ( sret < 0 ) {
		__sdp_log( 9, "Error bind: Could not setsockopt tcp_sd\n" );
	}

	__sdp_log( 1, "check_legal_bind: binding SDP socket\n" );
	ret =
		_socket_funcs.bind( *sdp_sd, sdp_addr, sizeof( struct sockaddr_in ) );
	if ( ret < 0 ) {
		__sdp_log( 9, "Error check_legal_bind: "
					  "binding SDP socket failed:%s\n", strerror( errno ) );
		_socket_funcs.close( *sdp_sd );
		_socket_funcs.close( *tcp_sd );

		/* TCP and SDP without library return EINVAL */
		if (errno == EADDRINUSE)
			errno = EINVAL;

		goto done;
	}

	__sdp_log( 1, "check_legal_bind: binding TCP socket\n" );
	ret = _socket_funcs.bind( *tcp_sd, tcp_addr, addrlen );
	if ( ret < 0 ) {
		__sdp_log( 9, "Error check_legal_bind: "
					  "binding TCP socket failed:%s\n", strerror( errno ) );
		_socket_funcs.close( *sdp_sd );
		_socket_funcs.close( *tcp_sd );
		goto done;
	}
	ret = 0;
	__sdp_log( 2, "check_legal_bind: result:<%d>\n", ret );
 done:
	return ret;
}

/* ========================================================================= */
/*..close_and_bind - close an open fd and bind another one immediately       */
static int
close_and_bind(
	int old_sd,
	int new_sd,
	const struct sockaddr *addr,
	socklen_t addrlen )
{
	int ret;

	__sdp_log( 2, "close_and_bind: closing <%d> binding <%d>\n", old_sd,
				  new_sd );
	ret = _socket_funcs.close( old_sd );
	if ( ret < 0 ) {
		__sdp_log( 9, "Error bind: Could not close old_sd\n" );
		goto done;
	}

	ret = _socket_funcs.bind( new_sd, addr, addrlen );
	if ( ret < 0 )
		__sdp_log( 9, "Error bind: Could not bind new_sd\n" );

 done:
	__sdp_log( 2, "close_and_bind: returning <%d>\n", ret );
	return ret;
}

/* ========================================================================= */
/*..bind -- replacement bind call.                                           */
/* 
   As we do not know the role of this socket yet so we cannot choose AF. 
   We need to be able to handle shadow too.
   SDP sockets (may be shadow or not) must be using converted address 
   
	Since there is no way to "rebind" a socket we have to avoid "false" bind:
	1. When the given address for the bind includes a port we need to 
	   guarantee the port is free on both address families. We do that 
      by creating temporary sockets and biding them first. Then we close and
		re-use the address on the real sockets. 
	2. When ANY_PORT is requested we need to make sure the port we obtain from 
	   the first address family is also free on the second one. We use temporary
		sockets for that task too. We loop several times to find such common 
		available socket
*/
int
bind(
	int fd,
	const struct sockaddr *my_addr,
	socklen_t addrlen )
{
	int shadow_fd;
	struct sockaddr_in *my_sin = ( struct sockaddr_in * )my_addr;
	struct sockaddr_in sdp_addr;
	struct sockaddr_in *sdp_sin = ( struct sockaddr_in * )&sdp_addr;
	int ret, sret = -1;
	char buf[MAX_ADDR_STR_LEN];
	int was_ipv6;

	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.bind ) {
		__sdp_log( 9, "Error bind: no implementation for bind found\n" );
		return -1;
	}

   shadow_fd = get_shadow_fd_by_fd( fd );

	if ( ( my_addr == NULL ) || is_invalid_addr( my_addr ) ) {
		errno = EFAULT;
		__sdp_log( 9, "Error bind: illegal address provided\n" );
		return -1;
	}

	if ( get_addr_str( my_addr, buf, MAX_ADDR_STR_LEN ) ) {
		__sdp_log( 9, "Error bind: provided illegal address: %s\n",
					  strerror( errno ) );
		return -1;
	}

	__sdp_log( 2, "BIND: <%s:%d:%d> type <%d> IP <%s> port <%d>\n",
				  program_invocation_short_name, fd, shadow_fd,
				  my_sin->sin_family, buf, ntohs( my_sin->sin_port ) );

	if ( get_is_sdp_socket( fd ) || ( shadow_fd != -1 ) ) {
		if ( __sdp_sockaddr_to_sdp( my_addr, addrlen, &sdp_addr, &was_ipv6 ) ) {
			__sdp_log( 9, "Error bind: failed to convert address:%s for SDP\n",
						  buf );
			ret = -1;
			goto done;
		}
#ifndef SDP_SUPPORTS_IPv6
		if ( was_ipv6 )
			libsdp_fd_attributes[fd].local_is_ipv6 = 1;
#endif
	}

	if ( get_is_sdp_socket( fd ) ) {
		__sdp_log( 1, "BIND: binding SDP socket:<%d>\n", fd );
		ret =
			_socket_funcs.bind( fd, ( struct sockaddr * )&sdp_addr,
									  sizeof sdp_addr );
		goto done;
	} else if ( shadow_fd != -1 ) {
		/* we need to validate the given address or find a common port 
		 * so we use the following tmp address and sockets */
		struct sockaddr_storage tmp_my_addr;
		int sdp_sd = -1, tcp_sd = -1, port;

		memcpy( &tmp_my_addr, my_addr, addrlen );
		ret = 0;
		if ( ntohs( sdp_sin->sin_port ) == 0 ) {
			/* When we get ANY_PORT we need to make sure that both TCP 
			 * and SDP sockets will use the same port */

			port = find_free_port( my_addr, addrlen,
										  ( struct sockaddr * )&sdp_addr,
										  sizeof sdp_addr, &sdp_sd, &tcp_sd );
			if ( port < 0 ) {
				ret = -1;
				__sdp_log( 9, "BIND: Failed to find common free port\n" );
				/* We cannot bind both tcp and sdp on the same port, we will close
				 * the sdp and continue with tcp only */
				goto done;
			} else {
				/* copy the port to the tmp address */
				set_addr_port_num( ( struct sockaddr * )&tmp_my_addr, port );
				set_addr_port_num( ( struct sockaddr * )&sdp_addr, port );
			}
		} else {
			/* have a shadow but requested specific port - check that we 
			 * can actually bind the two addresses and then reuse */
			ret = check_legal_bind( my_addr, addrlen,
											( struct sockaddr * )&sdp_addr, &sdp_sd,
											&tcp_sd );

			if ( ret < 0 ) {
				__sdp_log( 9, "Error bind: "
							  "Provided address can not bind on the two sockets\n" );
			}
		}

		/* if we fail to find a common port or given address can not be used 
		 * we return error */
		if ( ret < 0 ) {
			/* Temporary sockets already closed by check_legal_bind or 
			 * find_free_port */
			errno = EADDRINUSE;
			goto done;
		}

		/* close temporary sockets and reuse their address */
		/* HACK: close_and_bind might race with other applications. */
		/* When the race occur we return EADDRINUSE */

		ret =
			close_and_bind( tcp_sd, fd, ( struct sockaddr * )&tmp_my_addr,
								 addrlen );

		if ( ret < 0 ) {
			__sdp_log( 9, "Error bind: " "Could not close_and_bind TCP side\n" );
			_socket_funcs.close( sdp_sd );
			goto done;
		}

		ret =
			close_and_bind( sdp_sd, shadow_fd, ( struct sockaddr * )&sdp_addr,
								 sizeof sdp_addr );

		if ( ret < 0 ) {
			__sdp_log( 9, "Error bind: " "Could not close_and_bind sdp side\n" );
			goto done;
		}

		goto done;
	}

	/* has shadow */
	/* we can only get here on single TCP socket */
	__sdp_log( 1, "BIND: binding TCP socket:<%d>\n", fd );
	ret = _socket_funcs.bind( fd, my_addr, addrlen );

 done:
	__sdp_log( 2, "BIND: <%s:%d:%d> result <%d:%d>\n",
				  program_invocation_short_name, fd, shadow_fd, ret, sret );

	return ret;
}										  /* bind */

/* ========================================================================= */
/*..connect -- replacement connect call.                                     */
/*
  Given the connect address we can take out AF decision                     
  if target AF == both it means SDP and fall back to TCP                   
  if any connect worked we are fine
*/
int
connect(
	int fd,
	const struct sockaddr *serv_addr,
	socklen_t addrlen )
{
	struct sockaddr_in *serv_sin = ( struct sockaddr_in * )serv_addr;
	struct sockaddr_storage sdp_addr;
	struct sockaddr_in *sdp_sin = ( struct sockaddr_in * )&sdp_addr;
	char buf[MAX_ADDR_STR_LEN];
	int shadow_fd;
	int ret = -1, dup_ret;
	int was_ipv6;
	use_family_t target_family;
	int fopts;

	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.connect ) {
		__sdp_log( 9, "Error connect: no implementation for connect found\n" );
		return -1;
	}

	shadow_fd = get_shadow_fd_by_fd( fd );

	if ( ( serv_addr == NULL ) || is_invalid_addr( serv_addr ) ) {
		errno = EFAULT;
		__sdp_log( 9, "Error connect: illegal address provided\n" );
		return -1;
	}

	if ( get_addr_str( serv_addr, buf, MAX_ADDR_STR_LEN ) ) {
		__sdp_log( 9, "Error connect: provided illegal address: %s\n",
					  strerror( errno ) );
		return EADDRNOTAVAIL;
	}

	__sdp_log( 2, "CONNECT: <%s:%d:%d> domain <%d> IP <%s> port <%d>\n",
				  program_invocation_short_name, fd, shadow_fd,
				  serv_sin->sin_family, buf, ntohs( serv_sin->sin_port ) );


	/* obtain the target address family */
	target_family = __sdp_match_connect( serv_addr, addrlen );

	/* if we do not have a shadow - just do the work */
	if ( shadow_fd == -1 ) {
		if ( get_is_sdp_socket( fd ) ) {
			if ( __sdp_sockaddr_to_sdp
				  ( serv_addr, addrlen, sdp_sin, &was_ipv6 ) ) {
				__sdp_log( 9,
							  "Error connect: "
							  "failed to convert address:%s to SDP\n", buf );
				ret = -1;
				goto done;
			}
#ifndef SDP_SUPPORTS_IPv6
			if ( was_ipv6 )
				libsdp_fd_attributes[fd].remote_is_ipv6 = 1;
#endif
			__sdp_log( 1, "CONNECT: connecting through SDP\n" );
			ret = _socket_funcs.connect( fd,
												  ( struct sockaddr * )&sdp_addr,
												  sizeof sdp_addr );
			if ( (ret == 0) || (errno == EINPROGRESS) ) {
				__sdp_log( 7, "CONNECT: connected SDP fd:%d to:%s port %d\n",
							  fd, buf, ntohs( serv_sin->sin_port ) );
			}
		} else {
			__sdp_log( 1, "CONNECT: connecting through TCP\n" );
			ret = _socket_funcs.connect( fd, serv_addr, addrlen );
			if ( (ret == 0) || (errno == EINPROGRESS) ) {
				__sdp_log( 7, "CONNECT: connected TCP fd:%d to:%s port %d\n",
							  fd, buf, ntohs( serv_sin->sin_port ) );
			}
		}
		goto done;
	}

	if ( ( target_family == USE_SDP ) || ( target_family == USE_BOTH ) ) {
		/* NOTE: the entire if sequence is negative logic */
		if ( __sdp_sockaddr_to_sdp( serv_addr, addrlen, sdp_sin, &was_ipv6 ) ) {
			__sdp_log( 9, "Error connect: "
						  "failed to convert to shadow address:%s to SDP\n", buf );
			ret = -1;
		} else {
#ifndef SDP_SUPPORTS_IPv6
			if ( was_ipv6 )
				libsdp_fd_attributes[fd].remote_is_ipv6 = 1;
#endif

			__sdp_log( 1, "CONNECT: connecting SDP fd:%d\n", shadow_fd );

			/* make the socket blocking on shadow SDP */
			fopts = _socket_funcs.fcntl(shadow_fd, F_GETFL);
			if ( ( target_family == USE_BOTH ) && ( fopts & O_NONBLOCK ) ) {
				__sdp_log( 1, "CONNECT: shadow_fd <%d> will be blocking during connect\n",
				           shadow_fd);
				_socket_funcs.fcntl(shadow_fd, F_SETFL, fopts & (~O_NONBLOCK));
			}

			ret = _socket_funcs.connect( shadow_fd,
												  ( struct sockaddr * )&sdp_addr,
												  sizeof sdp_addr );
			if ( (ret < 0) && (errno != EINPROGRESS)) {
				__sdp_log( 9, "Error connect: "
							  "failed for SDP fd:%d with error:%m\n",
							  shadow_fd );
			} else {
				__sdp_log( 7, "CONNECT: connected SDP fd:%d to:%s port %d\n",
							  fd, buf, ntohs( serv_sin->sin_port ) );
			}

			/* restore socket options */
			_socket_funcs.fcntl(shadow_fd, F_SETFL, fopts);
		}

		/* if target is SDP or we succeeded we need to dup SDP fd into TCP fd */
		if ( ( target_family == USE_SDP ) || ( ret >= 0 ) ) {
			dup_ret = replace_fd_with_its_shadow( fd );
			if ( dup_ret < 0 ) {
				__sdp_log( 9, "Error connect: "
							  "failed to dup2 shadow into orig fd:%d\n", fd );
				ret = dup_ret;
			} else {
				/* we can skip the TCP option if we are done */
				__sdp_log( 1, "CONNECT: "
							  "matched SDP fd:%d so shadow dup into TCP\n", fd );
				goto done;
			}
		}
	}

	if ( ( target_family == USE_TCP ) || ( target_family == USE_BOTH ) ) {
		__sdp_log( 1, "CONNECT: connecting TCP fd:%d\n", fd );
		ret = _socket_funcs.connect( fd, serv_addr, addrlen );
		if ( (ret < 0) && (errno != EINPROGRESS) ) 
			__sdp_log( 9, "Error connect: for TCP fd:%d failed with error:%m\n",
						  fd );
		else
			__sdp_log( 7, "CONNECT: connected TCP fd:%d to:%s port %d\n",
						  fd, buf, ntohs( serv_sin->sin_port ) );

		if ( ( target_family == USE_TCP ) || ( ret >= 0 ) || (errno == EINPROGRESS) ) {
			if ( cleanup_shadow( fd ) < 0 )
				__sdp_log( 9, "Error connect: failed to cleanup shadow for fd:%d\n",
					   fd );
		}
	}

 done:
	__sdp_log( 2, "CONNECT: <%s:%d:%d> result <%d>\n",
				  program_invocation_short_name, fd, shadow_fd, ret );

	return ret;
}										  /* connect */

/* ========================================================================= */
/*..listen -- replacement listen call.                                       */
/* 
   Now we know our role (passive/server) and our address so we can get AF.
   If both we should try listening on both
*/
int
listen(
	int fd,
	int backlog )
{
	use_family_t target_family;
	int shadow_fd;
	int ret = 0, sret = 0;
	struct sockaddr_storage tmp_sin;
	socklen_t tmp_sinlen = sizeof( tmp_sin );
	struct sockaddr_in *sin4 = ( struct sockaddr_in * )&tmp_sin;
	char buf[MAX_ADDR_STR_LEN];
	int actual_port;
	int was_ipv6;

	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.listen ) {
		__sdp_log( 9, "Error listen: no implementation for listen found\n" );
		return -1;
	}

	shadow_fd = get_shadow_fd_by_fd( fd );
	__sdp_log( 2, "LISTEN: <%s:%d:%d>\n",
				  program_invocation_short_name, fd, shadow_fd );

	/* if there is no shadow - simply call listen */
	if ( shadow_fd == -1 ) {
		__sdp_log( 1, "LISTEN: calling listen on fd:%d\n", fd );
		ret = _socket_funcs.listen( fd, backlog );
		goto done;
	}

	/* we need to obtain the address from the fd */
	if ( _socket_funcs.
		  getsockname( fd, ( struct sockaddr * )&tmp_sin, &tmp_sinlen ) < 0 ) {
		__sdp_log( 9, "Error listen: getsockname return <%d> for TCP socket\n",
					  errno );
		errno = EADDRNOTAVAIL;
		sret = -1;
		goto done;
	}

	if ( get_addr_str( ( struct sockaddr * )&tmp_sin, buf, MAX_ADDR_STR_LEN ) ) {
		__sdp_log( 9, "Error listen: provided illegal address: %s\n",
					  strerror( errno ) );
	}

	__sdp_log( 2, "LISTEN: <%s:%d:%d> domain <%d> IP <%s> port <%d>\n",
				  program_invocation_short_name, fd, shadow_fd,
				  sin4->sin_family, buf, ntohs( sin4->sin_port ) );

	target_family =
		__sdp_match_listen( ( struct sockaddr * )&tmp_sin, sizeof( tmp_sin ) );

	/* 
	 * in case of an implicit bind and "USE_BOTH" rule we need to first bind the 
	 * two sockets to the same port number 
	 */
	actual_port = ntohs(sin4->sin_port);

	/* do we need to implicit bind both */
	if ( ( actual_port == 0 ) && ( target_family == USE_BOTH ) ) {
		int sdp_sd = -1, tcp_sd = -1;
		struct sockaddr_storage sdp_addr;
		struct sockaddr_in *sdp_sin = ( struct sockaddr_in * )&sdp_addr;
		
		/* we do need an IPv4 address for sdp */
		if ( __sdp_sockaddr_to_sdp( ( struct sockaddr * )&tmp_sin, 
											 tmp_sinlen, sdp_sin, &was_ipv6 ) ) {
			__sdp_log( 9, "Error listen: "
						  "failed to convert to address:%s to SDP\n", buf );
			ret = -1;
		} else {
#ifndef SDP_SUPPORTS_IPv6
			if ( was_ipv6 )
				libsdp_fd_attributes[fd].local_is_ipv6 = 1;
#endif
		}

		actual_port = find_free_port( ( struct sockaddr * )&tmp_sin, tmp_sinlen,
												( struct sockaddr * )&sdp_addr, 
												sizeof(struct sockaddr_in),
												&sdp_sd, &tcp_sd );
		if ( actual_port < 0 ) {
			ret = -1;
			__sdp_log( 8,
						  "LISTEN: Failed to find common free port. Only TCP will be used.\n" );
			target_family = USE_TCP;
		} else {
			/* copy the port to the tmp address */
			set_addr_port_num( ( struct sockaddr * )sin4, actual_port );
			set_addr_port_num( ( struct sockaddr * )sdp_sin, actual_port );
			
			__sdp_log( 2, "LISTEN: BOTH on IP <%s> port <%d>\n",
						  buf, actual_port );
			/* perform the bind */
			ret = close_and_bind( tcp_sd, fd, 
										 ( struct sockaddr * )sin4,
										 tmp_sinlen );

			if ( ret < 0 ) {
				__sdp_log( 9, "Error listen: "
							  "Could not close_and_bind TCP side\n" );
			}

			ret = close_and_bind( sdp_sd, shadow_fd,
										 ( struct sockaddr * )sdp_sin,
										 sizeof(struct sockaddr_in) );
			if ( ret < 0 ) {
				__sdp_log( 9, "Error listen: "
							  "Could not close_and_bind SDP side\n" );
			}
		}
	}

	if ( ( target_family == USE_TCP ) || ( target_family == USE_BOTH ) ) {
		__sdp_log( 1, "LISTEN: calling listen on TCP fd:%d\n", fd );
		ret = _socket_funcs.listen( fd, backlog );
		if ( ret < 0 ) {
			__sdp_log( 9, "Error listen: failed with code <%d> on TCP fd:<%d>\n",
						  errno, fd );
		} else {
			__sdp_log( 7, "LISTEN: fd:%d listening on TCP bound to:%s port:%d\n",
						  fd, buf, actual_port );
		}
	}

	if ( ( target_family == USE_SDP ) || ( target_family == USE_BOTH ) ) {
		__sdp_log( 1, "LISTEN: calling listen on SDP fd:<%d>\n", shadow_fd );
		sret = _socket_funcs.listen( shadow_fd, backlog );
		if ( sret < 0 ) {
			__sdp_log( 9, "Error listen: failed with code <%d> SDP fd:<%d>\n",
						  errno, shadow_fd );
		} else {
			__sdp_log( 7, "LISTEN: fd:%d listening on SDP bound to:%s port:%d\n",
						  fd, buf, actual_port );
			{
				struct sockaddr_in tmp_addr;
				socklen_t namelen = sizeof( struct sockaddr_in );

				_socket_funcs.getsockname( shadow_fd,
													( struct sockaddr * )&tmp_addr,
													&namelen );
				__sdp_log( 1, "LISTEN: SDP listen address is:0x%x port:%d\n",
							  ntohl( tmp_addr.sin_addr.s_addr ),
							  ntohs( tmp_addr.sin_port ) );
			}
		}
	}

	/* cleanup the un-needed shadow if TCP and did not fail */
	if ( ( target_family == USE_TCP ) && ( ret >= 0 ) ) {
		__sdp_log( 1, "LISTEN: cleaning up shadow SDP\n" );
		if ( cleanup_shadow( fd ) < 0 )
			__sdp_log( 9, "Error listen: failed to cleanup shadow for fd:%d\n",
						  fd );
	}

	/* cleanup the TCP socket and replace with SDP */
	if ( ( target_family == USE_SDP ) && ( sret >= 0 ) ) {
		__sdp_log( 1, "LISTEN: cleaning TCP socket and dup2 SDP into it\n" );
		if ( 0 > ( sret = replace_fd_with_its_shadow( fd ) ) )
			__sdp_log( 9, "Error listen: "
						  "failed to dup2 shadow into orig fd:%d\n", fd );
	}

 done:
	__sdp_log( 2, "LISTEN: <%s:%d:%d> result <%d>\n",
				  program_invocation_short_name, fd, shadow_fd, ret );
	/* its a success only if both are ok */
	if ( ret < 0 )
		return ( ret );
	if ( sret < 0 )
		return ( sret );
	return 0;
}										  /* listen */

/* ========================================================================= */
/*..close -- replacement close call. */
int
close(
	int fd )
{
	int shadow_fd;
	int ret;

	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.close ) {
		__sdp_log( 9, "Error close: no implementation for close found\n" );
		return -1;
	}

	shadow_fd = get_shadow_fd_by_fd( fd );

	__sdp_log( 2, "CLOSE: <%s:%d:%d>\n",
				  program_invocation_short_name, fd, shadow_fd );

	if ( shadow_fd != -1 ) {
		__sdp_log( 1, "CLOSE: closing shadow fd:<%d>\n", shadow_fd );
		if ( cleanup_shadow( fd ) < 0 )
			__sdp_log( 9, "Error close: failed to cleanup shadow for fd:%d\n",
						  fd );
	}

	init_extra_attribute( fd );
	ret = _socket_funcs.close( fd );
	__sdp_log( 2, "CLOSE: <%s:%d:%d> result <%d>\n",
				  program_invocation_short_name, fd, shadow_fd, ret );
	return ret;
}										  /* close */

/* ========================================================================= */
/*..dup -- replacement dup call.                                             */
/* we duplicate the fd and its shadow if exists - ok if the main worked      */
int
dup(
	int fd )
{
	int newfd, new_shadow_fd = -1;
	int shadow_fd;

	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.dup ) {
		__sdp_log( 9, "Error dup: no implementation for dup found\n" );
		return -1;
	}

	shadow_fd = get_shadow_fd_by_fd( fd );

	__sdp_log( 2, "DUP: <%s:%d:%d>\n",
				  program_invocation_short_name, fd, shadow_fd );

	__sdp_log( 1, "DUP: duplication fd:<%d>\n", fd );
	newfd = _socket_funcs.dup( fd );

	if ( newfd == fd )
		return ( fd );

	if ( !is_valid_fd( newfd ) ) {
		__sdp_log( 9, "Error dup: new fd <%d> out of range.\n", newfd );
	} else {
		/* copy attributes from old fd */
		libsdp_fd_attributes[newfd] = libsdp_fd_attributes[fd];
		libsdp_fd_attributes[newfd].shadow_fd = -1;

		if ( shadow_fd != -1 ) {
			__sdp_log( 1, "DUP: duplication shadow fd:<%d>\n", shadow_fd );
			new_shadow_fd = _socket_funcs.dup( shadow_fd );
			if ( ( new_shadow_fd > max_file_descriptors ) ||
				  ( new_shadow_fd < 0 ) ) {
				__sdp_log( 9, "Error dup: new shadow fd <%d> out of range.\n",
							  new_shadow_fd );
			} else {
				libsdp_fd_attributes[new_shadow_fd] =
					libsdp_fd_attributes[shadow_fd];
				libsdp_fd_attributes[newfd].shadow_fd = new_shadow_fd;
			}
		}								  /* shadow exists */
	}

	__sdp_log( 2, "DUP: <%s:%d:%d> return <%d:%d>\n",
				  program_invocation_short_name, fd, shadow_fd, newfd,
				  new_shadow_fd );

	return newfd;
}										  /* dup */

/* ========================================================================= */
/*..dup2 -- replacement dup2 call.                                           */
/* since only the main new fd is given we only move the shadow if exists     */
int
dup2(
	int fd,
	int newfd )
{
	int shadow_fd;
	int shadow_newfd;
	int new_shadow_fd = -1;
	int ret = 0;

	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.dup2 ) {
		__sdp_log( 9, "Error dup2: no implementation for dup2 found\n" );
		return -1;
	}

	shadow_fd = get_shadow_fd_by_fd( fd );
	shadow_newfd = get_shadow_fd_by_fd( newfd );

	__sdp_log( 2, "DUP2: <%s:%d:%d>\n",
				  program_invocation_short_name, fd, shadow_fd );

	if ( newfd == fd ) {
		__sdp_log( 1, "DUP2: skip duplicating fd:<%d> into:<%d>\n", fd, newfd );
		goto done;
	}

	/* dup2 closes the target file desc if it is a valid fd */
	if ( shadow_newfd != -1 ) {
		__sdp_log( 1, "DUP2: closing newfd:<%d> shadow:<%d>\n", newfd,
					  shadow_newfd );
		ret = _socket_funcs.close( shadow_newfd );
		if ( ret != 0 ) {
			__sdp_log( 9,
						  "DUP2: fail to close newfd:<%d> shadow:<%d> with: %d %s\n",
						  newfd, shadow_newfd, ret, strerror( errno ) );
		}
	}

	__sdp_log( 1, "DUP2: duplicating fd:<%d> into:<%d>\n", fd, newfd );
	newfd = _socket_funcs.dup2( fd, newfd );
	if ( ( newfd > max_file_descriptors ) || ( newfd < 0 ) ) {
		__sdp_log( 9, "Error dup2: new fd <%d> out of range.\n", newfd );
	} else {
		/* copy attributes from old fd */
		libsdp_fd_attributes[fd].shadow_fd = -1;
		libsdp_fd_attributes[newfd] = libsdp_fd_attributes[fd];

		/* if it had a shadow create a new shadow */
		if ( shadow_fd != -1 ) {
			__sdp_log( 1, "DUP2: duplication shadow fd:<%d>\n", shadow_fd );
			new_shadow_fd = _socket_funcs.dup( shadow_fd );
			if ( ( new_shadow_fd > max_file_descriptors ) ||
				  ( new_shadow_fd < 0 ) ) {
				__sdp_log( 9, "Error dup2: new shadow fd <%d> out of range.\n",
							  new_shadow_fd );
			} else {
				libsdp_fd_attributes[new_shadow_fd] =
					libsdp_fd_attributes[shadow_fd];
				libsdp_fd_attributes[newfd].shadow_fd = new_shadow_fd;
			}
		}								  /* newfd is ok */
	}

 done:
	__sdp_log( 2, "DUP2: <%s:%d:%d> return <%d:%d>\n",
				  program_invocation_short_name, fd, shadow_fd, newfd,
				  new_shadow_fd );

	return newfd;
}										  /* dup */

/* ========================================================================= */
/*..getsockname -- replacement getsocknanme call.                            */
int
getsockname(
	int fd,
	struct sockaddr *name,
	socklen_t * namelen )
{
	int ret = 0;
	int to_ipv6 = 0;
	struct sockaddr_in tmp_addr;
	char buf[MAX_ADDR_STR_LEN];
	socklen_t saved_namelen = 0;

	if (init_status == 0) __sdp_init();

	/*
	 * ensure the SDP protocol family is not exposed to the user, since
	 * this is meant to be a transparency layer.
	 */
	if ( NULL == _socket_funcs.getsockname ) {
		__sdp_log( 9,
					  "Error getsockname: no implementation for getsockname found\n" );
		return -1;
	}

	/* double check provided pointers */
	if ( ( name == NULL ) || is_invalid_addr( name ) ) {
		errno = EFAULT;
		__sdp_log( 9, "Error getsockname: illegal address provided\n" );
		return -1;
	}

	if ( ( namelen != NULL ) && is_invalid_addr( namelen ) ) {
		errno = EFAULT;
		__sdp_log( 9,
					  "Error getsockname: illegal address length pointer provided\n" );
		return -1;
	}

	if ( namelen )
		saved_namelen = *namelen;

	__sdp_log( 2, "GETSOCKNAME <%s:%d>\n", program_invocation_short_name, fd );

	ret = _socket_funcs.getsockname( fd, name, namelen );

	/* we need address conversion - only if socket is sdp */
	if ( ( ret == 0 ) && get_is_sdp_socket( fd ) && name && namelen ) {

#ifndef SDP_SUPPORTS_IPv6
		to_ipv6 = libsdp_fd_attributes[fd].local_is_ipv6;
#endif
		*namelen = saved_namelen;
		memcpy( &tmp_addr, name, sizeof( struct sockaddr_in ) );
		/* HACK: need to make sure we convert into IPv6 if original was IPv6 */
		sockaddr_from_sdp( to_ipv6, &tmp_addr, name, namelen );
	}

	if ( __sdp_log_get_level(  ) <= 1 ) {
		if ( get_addr_str( name, buf, MAX_ADDR_STR_LEN ) ) {
			__sdp_log( 1, "GETSOCKNAME: " "address is illegal\n" );
		} else {
			__sdp_log( 1, "GETSOCKNAME: address is:%s port:%d\n", buf,
						  ntohs( ( ( struct sockaddr_in * )name )->sin_port ) );
		}
	}
	__sdp_log( 2, "GETSOCKNAME <%s:%d> result <%d>\n",
				  program_invocation_short_name, fd, ret );

	return ret;
}										  /* getsockname */

/* ========================================================================= */
/*..getpeername -- replacement getpeername call. */
int
getpeername(
	int fd,
	struct sockaddr *name,
	socklen_t * namelen )
{
	int ret = 0;
	int to_ipv6 = 0;
	struct sockaddr_in tmp_addr;
	socklen_t saved_namelen = 0;

	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.getpeername ) {
		__sdp_log( 9, "Error getpeername: "
					  "no implementation for getpeername found\n" );
		return -1;
	}

	/* double check provided pointers */
	if ( ( name == NULL ) || is_invalid_addr( name ) ) {
		errno = EFAULT;
		__sdp_log( 9, "Error getsockname: illegal address provided\n" );
		return -1;
	}

	if ( ( namelen != NULL ) && is_invalid_addr( namelen ) ) {
		errno = EFAULT;
		__sdp_log( 9,
					  "Error getsockname: illegal address length pointer provided\n" );
		return -1;
	}

	if ( namelen )
		saved_namelen = *namelen;

	__sdp_log( 2, "GETPEERNAME <%s:%d>\n", program_invocation_short_name, fd );

	ret = _socket_funcs.getpeername( fd, name, namelen );
	/* we need address conversion - only if socket is sdp */
	if ( ( ret == 0 ) && get_is_sdp_socket( fd ) && name && namelen ) {
#ifndef SDP_SUPPORTS_IPv6
		to_ipv6 = libsdp_fd_attributes[fd].remote_is_ipv6;
#endif

		*namelen = saved_namelen;
		memcpy( &tmp_addr, name, sizeof( struct sockaddr_in ) );
		sockaddr_from_sdp( to_ipv6, &tmp_addr, name, namelen );
	}

	__sdp_log( 2, "GETPEERNAME <%s:%d> result <%d:%d> family=%d s_addr=%d\n",
				  program_invocation_short_name, fd, ret,
				  ( !( 0 > ret ) ? 0 : -1 ), name->sa_family,
				  ( ( struct sockaddr_in * )name )->sin_addr.s_addr );

	return ret;
}										  /* getpeername */



/* ========================================================================= */
/*..accept -- replacement accept call.                                       */
/*
  If we have a shadow we need to decide which socket we want to accept on
  so we select first and then give priority based on previous selection
*/
int
accept(
	int fd,
	struct sockaddr *addr,
	socklen_t * addrlen )
{
	int shadow_fd;
	int ret = 0;
	fd_set fds;
	struct sockaddr tmp_addr;
	socklen_t saved_addrlen = 0;
	int to_ipv6 = 0;
	int fopts;
	char buf[MAX_ADDR_STR_LEN];

	if (init_status == 0) __sdp_init();
	
	shadow_fd = get_shadow_fd_by_fd( fd );
	
	/*
	 * ensure the SDP protocol family is not exposed to the user, since
	 * this is meant to be a transparency layer.
	 */
	if ( NULL == _socket_funcs.accept ) {
		__sdp_log( 9, "Error accept: no implementation for accept found\n" );
		return -1;
	}

	/* double check provided pointers */
	if ( ( addr != NULL ) && is_invalid_addr( addr ) ) {
		errno = EINVAL;
		__sdp_log( 9, "Error accept: illegal address provided\n" );
		return -1;
	}

	if ( ( addrlen != NULL ) && is_invalid_addr( addrlen ) ) {
		errno = EINVAL;
		__sdp_log( 9,
					  "Error accept: illegal address length pointer provided\n" );
		return -1;
	}

	if ( addr && addrlen )
		saved_addrlen = *addrlen;

	__sdp_log( 2, "ACCEPT: <%s:%d>\n", program_invocation_short_name, fd );

	if ( shadow_fd == -1 ) {
		fopts = _socket_funcs.fcntl(fd, F_GETFL);
		__sdp_log( 1, "ACCEPT: fd <%d> opts are <0x%x>\n",
					  fd, fopts);

		__sdp_log( 7, "ACCEPT: accepting on single fd:<%d>\n", fd );
		ret = _socket_funcs.accept( fd, addr, addrlen );
		if (ret < 0 && !(fopts & O_NONBLOCK && errno == EWOULDBLOCK))
			__sdp_log( 9, "Error accept: accept returned :<%d> %s\n",
						  ret, strerror( errno ) );
		set_is_sdp_socket( ret, get_is_sdp_socket( fd ) );
		libsdp_fd_attributes[ret].local_is_ipv6
			= libsdp_fd_attributes[fd].local_is_ipv6;
	} else {

#ifndef SDP_SUPPORTS_IPv6
		to_ipv6 = libsdp_fd_attributes[fd].local_is_ipv6;
#endif

		fopts = _socket_funcs.fcntl(shadow_fd, F_GETFL);
		__sdp_log( 1, "ACCEPT: shadow_fd <%d> opts are <0x%x>\n", 
					  shadow_fd, fopts);
		

		/* we need different behavior for NONBLOCK or signal IO and BLOCK */
		if ((fopts > 0) && (fopts & (O_NONBLOCK | FASYNC))) {
			__sdp_log( 1, "ACCEPT: accepting (nonblock) on SDP fd:<%d>\n",
						  shadow_fd );
			ret = _socket_funcs.accept( shadow_fd, addr, addrlen );
			if (ret >= 0) {
				if ( addr && addrlen ) {
					memcpy( &tmp_addr, addr, sizeof( struct sockaddr ) );
					*addrlen = saved_addrlen;
					set_is_sdp_socket( ret, 1 );
					libsdp_fd_attributes[ret].local_is_ipv6
						= libsdp_fd_attributes[fd].local_is_ipv6;
					sockaddr_from_sdp( 
						to_ipv6,
						( struct sockaddr_in * )&tmp_addr, addr, 
						addrlen );
				}
				__sdp_log( 7, "ACCEPT: accepted (nonblock) SDP fd:<%d>\n",
							  shadow_fd );
			} else {
				__sdp_log( 1, "ACCEPT: accept on SDP fd:<%d> return:%d errno:%d\n",
							  shadow_fd, ret, errno );

				__sdp_log( 1, "ACCEPT: accepting (nonblock) on TCP fd:<%d>\n", 
							  fd );				
				ret = _socket_funcs.accept( fd, addr, addrlen );
				if (ret >= 0) {
					__sdp_log( 7, "ACCEPT: accepted (nonblock) TCP fd:<%d>\n",
								  shadow_fd );
				}  else {
					__sdp_log( 1, "ACCEPT: accept on TCP fd:<%d> "
								  "return:%d errno:%d\n",
								  fd, ret, errno );
				}
			}
		} else {
			__sdp_log( 1, "ACCEPT: selecting both fd:<%d> and shadow:<%d>\n",
						  fd,
						  shadow_fd );
			FD_ZERO( &fds );
			FD_SET( fd, &fds );
			FD_SET( shadow_fd, &fds );
			ret =
				_socket_funcs.select(
					1 + ( ( fd > shadow_fd ) ? fd : shadow_fd ),
					&fds, NULL, NULL, NULL );
			if ( ret >= 0 ) {
				if ( last_accept_was_tcp( fd ) == 0 ) {
					if ( FD_ISSET( fd, &fds ) ) {
						set_last_accept( fd, 1 );
						__sdp_log( 7, "ACCEPT: accepting on TCP fd:<%d>\n", fd );
						ret = _socket_funcs.accept( fd, addr, addrlen );
					} else {
						__sdp_log( 7, "ACCEPT: accepting on SDP fd:<%d>\n",
									  shadow_fd );
						ret = _socket_funcs.accept( shadow_fd, addr, addrlen );
						if ( ( ret > 0 ) && addr && addrlen ) {
							memcpy( &tmp_addr, addr, sizeof( struct sockaddr ) );
							*addrlen = saved_addrlen;
							set_is_sdp_socket( ret, 1 );
							libsdp_fd_attributes[ret].local_is_ipv6
                                                                = libsdp_fd_attributes[fd].local_is_ipv6;
							sockaddr_from_sdp( 
								to_ipv6,
								( struct sockaddr_in * )&tmp_addr, addr, 
								addrlen );
						}
					}
				} else {	
					if ( FD_ISSET( shadow_fd, &fds ) ) {
						set_last_accept( fd, 1 );
						__sdp_log( 7, "ACCEPT: accepting on SDP fd:<%d>\n",
									  shadow_fd );
						ret = _socket_funcs.accept( shadow_fd, addr, addrlen );
						if ( ( ret > 0 ) && addr && addrlen ) {
							memcpy( &tmp_addr, addr, sizeof( struct sockaddr ) );
							*addrlen = saved_addrlen;
							set_is_sdp_socket( ret, 1 );
							libsdp_fd_attributes[ret].local_is_ipv6
                                                                = libsdp_fd_attributes[fd].local_is_ipv6;
							sockaddr_from_sdp( to_ipv6,
													 ( struct sockaddr_in * )&tmp_addr, 
													 addr,
													 addrlen );
						}
					} else {
						__sdp_log( 7, "ACCEPT: accepting on TCP fd:<%d>\n", fd );
						ret = _socket_funcs.accept( fd, addr, addrlen );
					}
				}
			} else {
			  if (errno != EINTR) {
				 __sdp_log( 9, "Error accept: select returned :<%d> (%d) %s\n",
								ret, errno, strerror( errno ) );
			  } else {
				 __sdp_log( 1, "ACCEPT: select returned :<%d> (%d) %s\n",
								ret, errno, strerror( errno ) );
			  }
			}
		} /* blocking mode */
	} /* shadow fd */
	
	if ((__sdp_log_get_level() <= 1) && (ret >= 0) && addr && addrlen) {
		get_addr_str( addr, buf, *addrlen );
		__sdp_log( 1, "ACCEPT: accepted from:%s port:%d into fd:%d\n", 
					  buf, 
					  ntohs( ( ( struct sockaddr_in * )addr )->sin_port),
					  ret);
	}
	__sdp_log( 2, "ACCEPT: <%s:%d> return <%d>\n",
				  program_invocation_short_name, fd, ret );

	return ret;
}										  /* accept */

/* ========================================================================= */
/*..select -- replacement socket call.                                       */
/* 
   if we have shadow we must select on it too - which requires a hack back 
   and forth
*/
int
select(
	int n,
	fd_set * readfds,
	fd_set * writefds,
	fd_set * exceptfds,
	struct timeval *timeout )
{
	int shadow_fd;
	int ret;
	int current;
	int maxi = 0;
	fd_set new_fds;

	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.select ) {
		__sdp_log( 9, "Error select: no implementation for select found\n" );
		return -1;
	}

	__sdp_log( 2, "SELECT: <%s:%d>\n", program_invocation_short_name, n );

	/* if we do not read - nothing to do */
	if ( readfds == NULL ) {
		ret = _socket_funcs.select( n, readfds, writefds, exceptfds, timeout );
		goto done;
	}

	FD_ZERO( &new_fds );
	if ( n > 0 ) {
		maxi = n - 1;
	}

	/* add shadow bits */
	for ( current = 0; current < n; current++ ) {
		if ( FD_ISSET( current, readfds ) ) {
			FD_SET( current, &new_fds );
			if ( current > maxi ) {
				maxi = current;
			}
			shadow_fd = get_shadow_fd_by_fd( current );
			if ( shadow_fd != -1 ) {
				__sdp_log( 1, "SELECT: adding fd:<%d> shadow_fd:<%d> to readfs\n",
							  current, shadow_fd );
				FD_SET( shadow_fd, &new_fds );
				if ( shadow_fd > maxi ) {
					maxi = shadow_fd;
				}
			}
		}
	}

	__sdp_log( 1, "SELECT: invoking select n=<%d>\n", 1 + maxi );
	ret = _socket_funcs.select( 1 + maxi,
										 &new_fds, writefds, exceptfds, timeout );

	/* remove the count and bits of the shadows */
	if ( ret >= 0 ) {
		for ( current = 0; current < n; current++ ) {
			shadow_fd = get_shadow_fd_by_fd( current );
			if ( shadow_fd == -1 ) {
				if ( FD_ISSET( current, readfds ) &&
					  FD_ISSET( current, &new_fds ) == 0 ) {
					FD_CLR( current, readfds );
				}
			} else {
				if ( FD_ISSET( current, readfds ) && FD_ISSET( current, &new_fds )
					  && FD_ISSET( shadow_fd, &new_fds ) ) {
					ret -= 1;
				}
				if ( FD_ISSET( current, readfds ) &&
					  FD_ISSET( current, &new_fds ) == 0 &&
					  FD_ISSET( shadow_fd, &new_fds ) == 0 ) {
					FD_CLR( current, readfds );
				}
			}
		}
	}

 done:

	__sdp_log( 2, "SELECT: <%s:%d> return <%d>\n",
				  program_invocation_short_name, n, ret );
	return ret;
}										  /* select */

/* ========================================================================= */
/*..pselect -- replacement socket call.                                      */
/* 
   if we have shadow we must pselect on it too - which requires a hack back 
   and forth
*/
int
pselect(
	int n,
	fd_set * readfds,
	fd_set * writefds,
	fd_set * exceptfds,
	const struct timespec *timeout,
	const sigset_t *sigmask)
{
	int shadow_fd;
	int ret;
	int current;
	int maxi = 0;
	fd_set new_fds;

	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.pselect ) {
		__sdp_log( 9, "Error pselect: no implementation for pselect found\n" );
		return -1;
	}

	__sdp_log( 2, "PSELECT: <%s:%d>\n", program_invocation_short_name, n );

	/* if we do not read - nothing to do */
	if ( readfds == NULL ) {
		ret = _socket_funcs.pselect( 
			n, readfds, writefds, exceptfds, timeout,  sigmask);
		goto done;
	}

	FD_ZERO( &new_fds );
	if ( n > 0 ) {
		maxi = n - 1;
	}

	/* add shadow bits */
	for ( current = 0; current < n; current++ ) {
		if ( FD_ISSET( current, readfds ) ) {
			FD_SET( current, &new_fds );
			if ( current > maxi ) {
				maxi = current;
			}
			shadow_fd = get_shadow_fd_by_fd( current );
			if ( shadow_fd != -1 ) {
				__sdp_log( 1, "PSELECT: adding fd:<%d> shadow_fd:<%d> to readfs\n",
							  current, shadow_fd );
				FD_SET( shadow_fd, &new_fds );
				if ( shadow_fd > maxi ) {
					maxi = shadow_fd;
				}
			}
		}
	}

	__sdp_log( 1, "PSELECT: invoking pselect n=<%d>\n", 1 + maxi );
	ret = _socket_funcs.pselect( 1 + maxi,
										  &new_fds, writefds, exceptfds, 
										  timeout, sigmask );

	/* remove the count and bits of the shadows */
	if ( ret >= 0 ) {
		for ( current = 0; current < n; current++ ) {
			shadow_fd = get_shadow_fd_by_fd( current );
			if ( shadow_fd == -1 ) {
				if ( FD_ISSET( current, readfds ) &&
					  FD_ISSET( current, &new_fds ) == 0 ) {
					FD_CLR( current, readfds );
				}
			} else {
				if ( FD_ISSET( current, readfds ) && FD_ISSET( current, &new_fds )
					  && FD_ISSET( shadow_fd, &new_fds ) ) {
					ret -= 1;
				}
				if ( FD_ISSET( current, readfds ) &&
					  FD_ISSET( current, &new_fds ) == 0 &&
					  FD_ISSET( shadow_fd, &new_fds ) == 0 ) {
					FD_CLR( current, readfds );
				}
			}
		}
	}

 done:

	__sdp_log( 2, "PSELECT: <%s:%d> return <%d>\n",
				  program_invocation_short_name, n, ret );
	return ret;
}										  /* pselect */

/* ========================================================================= */
/*..poll -- replacement socket call.                                      */
/* 
   if we have shadow we must poll on it too - which requires a hack back 
   and forth
*/
int
poll(
	struct pollfd *ufds,
	unsigned long int nfds, 
	int timeout)
{
	int ret;
	int shadow_fd;
	int current;
	int extra = 0;
	struct pollfd *poll_fds = NULL;
	struct pollfd *poll_fd_ptr = NULL;

	if (init_status == 0) __sdp_init();

	if ( NULL == _socket_funcs.poll ) {
		__sdp_log( 9, "Error poll: no implementation for poll found\n" );
		return -1;
	}

	__sdp_log( 2, "POLL: <%s:%d>\n", program_invocation_short_name, nfds );

	/* if we do not have any file desc - nothing to do */
	if ( ufds == NULL ) {
		ret = _socket_funcs.poll(ufds, nfds, timeout);
		goto done;
	}

	/* scan for how many extra fds are required */
	for ( current = 0; current < nfds; current++ ) {
		shadow_fd = get_shadow_fd_by_fd( ufds[current].fd );
		if ( shadow_fd != -1 )
			extra++;
	}
	
	if (!extra) {
		poll_fds = ufds;
	} else {
		poll_fds = (struct pollfd *)malloc((nfds + extra)*sizeof(struct pollfd));
		if (! poll_fds) {
		  __sdp_log( 9, "Error poll: malloc of extended pollfd array failed\n");
		  ret = -1;
		  errno = ENOMEM;
		  goto done;
		}
		poll_fd_ptr = poll_fds;
		for ( current = 0; current < nfds; current++ ) {
			*poll_fd_ptr = ufds[current];
			poll_fd_ptr++;
			shadow_fd = get_shadow_fd_by_fd( ufds[current].fd );
			if ( shadow_fd != -1 ) {
				__sdp_log( 1, "POLL: adding fd:<%d> shadow_fd:<%d> to readfs\n",
					  current, shadow_fd );
				*poll_fd_ptr = ufds[current];
				poll_fd_ptr->fd = shadow_fd;
				poll_fd_ptr++;
			}
		}
	}

	__sdp_log( 1, "POLL: invoking poll nfds=<%d>\n", nfds + extra );
	ret = _socket_funcs.poll( poll_fds, nfds + extra, timeout);

	/* refactor into original list if any events */
	if ( (ret > 0) && extra ) {
		poll_fd_ptr = poll_fds;
		for ( current = 0; current < nfds; current++ ) {
			shadow_fd = get_shadow_fd_by_fd( ufds[current].fd );
			if ( shadow_fd == -1 ) {
				ufds[current] = *poll_fd_ptr;
			} else {
				ufds[current] = *poll_fd_ptr;
				poll_fd_ptr++;
				if (poll_fd_ptr->revents) {
					if (ufds[current].revents)
						ret--;
					ufds[current].revents |= poll_fd_ptr->revents;
				}
			}
			poll_fd_ptr++;
		}
	}

	if (extra)
	  free(poll_fds);
 done:

	__sdp_log( 2, "POLL: <%s:%d> return <%d>\n",
				  program_invocation_short_name, nfds, ret );
	return ret;
}										  /* poll */

/* ========================================================================= */

/* --------------------------------------------------------------------- */
/*                                                                       */
/* Library load/unload initialization/cleanup                            */
/*                                                                       */
/* --------------------------------------------------------------------- */
/* ========================================================================= */
/*..__sdp_init -- intialize the library */
void
__sdp_init(
	void )
{
	char *config_file, *error_str;
	int fd;
	struct rlimit nofiles_limit;

	/* HACK: races might apply here: can we assume init is happening
		only within one thread ? */
	if (init_status != 0) return;
	init_status = 1;

	dev_null_fd = open( "/dev/null", O_WRONLY );

	/* figure out the max number of file descriptors */
	if ( getrlimit( RLIMIT_NOFILE, &nofiles_limit ) )
		max_file_descriptors = 1024;
	else
		max_file_descriptors = nofiles_limit.rlim_cur;

	/* allocate and initialize the shadow sdp sockets array */
	libsdp_fd_attributes =
		( struct sdp_extra_fd_attributes * )calloc( max_file_descriptors,
																  sizeof( struct
																			 sdp_extra_fd_attributes ) );
	for ( fd = 0; fd < max_file_descriptors; fd++ )
		init_extra_attribute( fd );

#ifndef RTLD_NEXT
	/*
	 * open libc for original socket call.
	 */
	__libc_dl_handle = dlopen( "/lib64/libc.so.6", RTLD_LAZY );
	if ( NULL == __libc_dl_handle ) {
		__libc_dl_handle = dlopen( "/lib/libc.so.6", RTLD_LAZY );
		if ( NULL == __libc_dl_handle ) {
			fprintf( stderr, "%s\n", dlerror(  ) );
			return;
		}
	}
#endif

	/*
	 * Get the original functions
	 */
	_socket_funcs.ioctl = dlsym( __libc_dl_handle, "ioctl" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.fcntl = dlsym( __libc_dl_handle, "fcntl" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.socket = dlsym( __libc_dl_handle, "socket" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.setsockopt = dlsym( __libc_dl_handle, "setsockopt" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.connect = dlsym( __libc_dl_handle, "connect" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.listen = dlsym( __libc_dl_handle, "listen" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.bind = dlsym( __libc_dl_handle, "bind" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.close = dlsym( __libc_dl_handle, "close" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.dup = dlsym( __libc_dl_handle, "dup" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.dup2 = dlsym( __libc_dl_handle, "dup2" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.getpeername = dlsym( __libc_dl_handle, "getpeername" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.getsockname = dlsym( __libc_dl_handle, "getsockname" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.accept = dlsym( __libc_dl_handle, "accept" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.select = dlsym( __libc_dl_handle, "select" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.pselect = dlsym( __libc_dl_handle, "pselect" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	_socket_funcs.poll = dlsym( __libc_dl_handle, "poll" );
	if ( NULL != ( error_str = dlerror(  ) ) ) {
		fprintf( stderr, "%s\n", error_str );
	}

	if ( getenv( "SIMPLE_LIBSDP" ) != NULL ) {
		simple_sdp_library = 1;
	}

	if ( getenv( "ALWAYS_USE_SDP" ) != NULL ) {
		simple_sdp_library = 1;
	}
		
#define LIBSDP_DEFAULT_CONFIG_FILE  SYSCONFDIR "/libsdp.conf"
	if ( ! simple_sdp_library ) {
		config_file = getenv( "LIBSDP_CONFIG_FILE" );
		if ( !config_file )
			config_file = LIBSDP_DEFAULT_CONFIG_FILE;
		
		if ( __sdp_parse_config( config_file ) ) {
			fprintf( stderr, "libsdp Error: failed to parse config file:%s. Using defaults.\n",
						config_file );
		}
	}

	__sdp_log( 1, "Max file descriptors:%d\n", max_file_descriptors );
	init_status = 2;

} /* __sdp_init */

/* ========================================================================= */
/*..__sdp_fini -- when the library is unloaded this is called */
void
__sdp_fini(
	void )
{
	struct use_family_rule *rule;
	for ( rule = __sdp_clients_family_rules_head; rule != NULL; rule = rule->next )
		free(rule->prog_name_expr);
	for ( rule = __sdp_servers_family_rules_head; rule != NULL; rule = rule->next )
		free(rule->prog_name_expr);
#ifndef RTLD_NEXT
	dlclose( __libc_dl_handle );
#endif
}										  /* _fini */
