
/******************************************************************************
**
**  This program is free software; you can redistribute it and/or
**  modify it, however, you cannot sell it.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**
**  You should have received a copy of the license attached to the
**  use of this software.  If not, visit www.shmoo.com/osiris for
**  details.
**
******************************************************************************/

/*****************************************************************************
**
**  File:    socketapi.c
**  Date:    February 17, 2002
**
**  Author:  Brian Wotring
**  Purpose: network wrappers and routines
**
******************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#include <sys/types.h>

#include <openssl/ssl.h>
#include <openssl/err.h>

#ifdef WIN32

#include <winsock.h>
#include <errno.h>
#include <io.h>

#define EWOULDBLOCK WSAEWOULDBLOCK

#else

#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/time.h>

#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>

#endif

#include <fcntl.h>

#include "utilities.h"
#include "socketapi.h"

#ifndef WIN32
extern int errno;
extern int h_errno;
#endif


#define DEFAULT_CONNECT_TIMEOUT_SECONDS 10

/******************************************************************************
**
**    Function: int osi_socket
**
**    Purpose:  Used as a wrapper function for socket since it is used so
**              much, and also because of the vast difference between UNIX
**              BSDish style sockets and Winsock socket handling.
**
**    Return:   The valid descriptor is returned upon success, zero otherwise.
**
******************************************************************************/

int osi_socket( unsigned int family, unsigned int type, unsigned int protocol )
{
    int descriptor;

    if( ( descriptor = socket( family, type, protocol ) ) < 0 )
    {
        return 0;
    }

    return (int)descriptor;
}


/*****************************************************************************
**
**    Function: int osi_accept
**
**    Purpose:  Used as a wrapper function for accept because it is used
**              so much.  If a signal interuppts the accept function, it
**              continues to try forever.  If the function simply fails,
**              however, currently the function performs a fatal exit. 
**              ( this probably isn't a good thing. )
**
**    Return:   The newly opened socket associated with the incoming request.
**
******************************************************************************/

int osi_accept( unsigned int socket, struct sockaddr *remote_address,
                socklen_t *address_length )
{
    int connection_socket = 0;

    if( socket <= 0 )
    {
        return -1;
    }

    for(;;)
    {
        if( ( connection_socket = accept( socket, remote_address,
                                          address_length ) ) < 0 )
        {
            if( errno == EINTR )
            {
                continue;
            }

            return -1;
        }

        break;
    }

    return connection_socket;
}

/******************************************************************************
**
**    Function: int osi_ssl_accept
**
**    Purpose:  Used as a wrapper function for accept, bypasses and interupt.
**		        makes use of the accept with socket with the newly acquired
**		        client socket.
**
**    Return:   allocated SSL structure after doing an SSL handshake.
**
******************************************************************************/

SSL * osi_ssl_accept( unsigned int server_socket,
                      struct sockaddr *remote_address, 
                      socklen_t *address_length, 
                      SSL_CTX *ctx, 
                      osi_bool initiate )
{
    int client_socket;
    
    if( ( client_socket = osi_accept( server_socket, remote_address,
                                      address_length ) ) < 0 )
    {
        return NULL;
    }
    
    return osi_ssl_accept_with_socket( client_socket, ctx, initiate );
}

/******************************************************************************
**
**    Function: int osi_ssl_accept_with_socket
**
**    Purpose:  Used as a wrapper function for accept, bypasses and interupt.
**	            makes use of the accept with socket with the specified
**	            client socket.
**
**    Return:   allocated SSL structure after doing an SSL handshake.
**
******************************************************************************/

SSL * osi_ssl_accept_with_socket( unsigned int client_socket, SSL_CTX *ctx, 
                                  osi_bool initiate )
{    
    int result;
    int error;

    struct timeval to;

    fd_set read_set;
    fd_set write_set;
    
    SSL *ssl = NULL;
    
    if( ctx == NULL )
    {
        return NULL;
    }
    
    if( client_socket <= 0 )
    {
        return NULL;
    }
    
    /* we have a connection, accept with our ssl structure to */
    /* do server side SSL.                                    */
        
    ssl = SSL_new( ctx );
    
    if( ssl == NULL )
    {
        osi_close_socket( client_socket );
        return NULL;
    }
    
    SSL_set_fd( ssl, client_socket );

    /* SSL_accept might fail for non-blocking sockets, we keep looping on it. */
    /* until it returns a fatal error.                                        */

    for(;;)
    {
        to.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS;
        to.tv_usec = 0;

        if( initiate )
        {
            result = SSL_connect( ssl );
        }
        
        else
        {
            result = SSL_accept( ssl );
        }
                
        if( result <= 0 )
        {
            error = SSL_get_error( ssl, result );

            if( error == SSL_ERROR_WANT_READ )
            {
                FD_ZERO( &read_set );
                FD_SET( client_socket, &read_set );

                result = select( ( client_socket+1 ), &read_set,
                                 NULL, NULL, &to );

                /* data waiting to be read now. */

                if( result > 0 )
                {
                    continue;
                }
            }

            else if( error == SSL_ERROR_WANT_WRITE )
            {
                FD_ZERO( &write_set );
                FD_SET( client_socket, &write_set );

                result = select( ( client_socket+1 ), NULL,
                                  &write_set, NULL, &to );

                /* data can be written now. */

                if( result > 0 )
                {
                    continue;
                }
            }
            
            /* otherwise, this was a bad error. */
        
            osi_close_socket( client_socket );
            osi_ssl_destroy( &ssl );
        }
        
        break;
    }

    return ssl;
}

/******************************************************************************
**
**    Function: int osi_bind
**
**    Purpose:  Used as a wrapper function for bind because it is used so much.
**
**    Return:   The integer one is returned upon success, zero otherwise.
**
******************************************************************************/

int osi_bind( unsigned int socket, struct  sockaddr  *local_address,
              socklen_t sockaddr_length )
{
    int bind_result = 0;

    if( ( bind_result = bind( socket, local_address, sockaddr_length ) ) < 0 )
    {
        return 0;
    }

    return 1;
}


/******************************************************************************
**
**    Function: int osi_listen
**
**    Purpose:  Used as a wrapper function for listen because it is used
**              so much. the max_waiting argument is used to specify the
**              maximum number of pending requests that can exist on the
**              socket.
**
**    Return:   The integer one is returned upon success, zero otherwise.
**
******************************************************************************/

int osi_listen( unsigned int socket, unsigned int max_waiting )
{
    int listen_result = 0;

    if( max_waiting < 1 )
    {
        max_waiting = 400;
    }

    if( ( listen_result = listen( socket, max_waiting ) ) < 0 )
    {
        return 0;
    }

    return 1;
}

/******************************************************************************
**
**    Function: void osi_close_socket
**
**    Purpose:  full close of socket descriptor.
**
**    Return:   none.
**
******************************************************************************/

void osi_close_socket( unsigned int socket )
{
    if( socket > 0 )
    {
        close( socket );
    }
}

/******************************************************************************
**
**    Function: void osi_ssl_shutdown
**
**    Purpose:  handle proper shutdown of OpenSSL socket by verifying
**              we have done a bi-directional close of the tunnel.
**
**    Return:   none.
**
******************************************************************************/

void osi_ssl_shutdown( SSL *ssl )
{
    int sock = 0;
    int result = 0;

    if( ssl == NULL )
    {
        return;
    }

    sock = SSL_get_fd( ssl );
    result = SSL_shutdown( ssl );

    if( !result )
    {
        shutdown( sock, 1 );
        result = SSL_shutdown( ssl );
    }
}

/******************************************************************************
**
**    Function: void osi_ssl_destroy
**
**    Purpose:  full close of ssl connection.
**
**    Return:   none.
**
******************************************************************************/

void osi_ssl_destroy( SSL **ssl )
{
    if( ( ssl != NULL ) && ( (*ssl) != NULL ) )
    {
        SSL_free( (*ssl) );
        (*ssl) = NULL;
    }
}


/******************************************************************************
**
**    Function: int osi_read
**
**    Purpose:  Used as a wrapper function for read.
**
**    Return:   Upon success, the number of bytes read, negative one otherwise.
**
******************************************************************************/

int osi_read( unsigned int read_socket, char *buffer, unsigned int buffer_size )
{
    int bytes_read = 0;

#ifdef WIN32
    bytes_read = recv( read_socket, buffer, buffer_size, 0 );
#else
    bytes_read = read( read_socket, buffer, buffer_size );
#endif

    return bytes_read;
}

/******************************************************************************
**
**    Function: int osi_read_bytes
**
**    Purpose:  Used as a wrapper function for read, and reads until certain
**		        number of bytes have been read.
**
**    Return:   Upon success, the number of bytes read, negative one otherwise.
**
******************************************************************************/

int osi_read_bytes( unsigned int read_socket, char *buffer,
                    unsigned int buffer_size, int bytes_to_read )
{
    int bytes_read = 0;
    int bytes_left = bytes_to_read;
    int burst      = 0;

    char *temp = buffer;

    if( (unsigned int)bytes_to_read <= buffer_size )
    {
        while( bytes_left > 0 )
        {
            burst = osi_read( read_socket, temp, bytes_left );

            if( burst > 0 )
            {
                bytes_read += burst;
                bytes_left -= burst;

                temp += burst;
            }

            else if( burst < 0 )
            {

                /* if the socket is non-blocking, we would receive this */
                /* if it can't read, so we keep trying.                 */

#ifdef WIN32
                if( WSAGetLastError() == EWOULDBLOCK )
#else
                if( errno == EWOULDBLOCK )
#endif
                {
                    continue;
                }

                else
                {
                    return -1;
                }
            }

            else
            {
                break;
            }
        }
    }

    return bytes_read;
}


/******************************************************************************
**
**    Function: int osi_write
**
**    Purpose:  Used as a wrapper function for write/send.
**
**    Return:   Upon success, the number of bytes sent, negative one otherwise.
**
******************************************************************************/

int osi_write( unsigned int write_socket, const char *buffer,
               unsigned int buffer_size )
{
    int bytes_written = 0;

#ifdef WIN32
    bytes_written = send( write_socket, buffer, buffer_size, 0 );
#else
    bytes_written = write( write_socket, buffer, buffer_size );
#endif

    return bytes_written;
}

/******************************************************************************
**
**    Function: int osi_write_bytes
**
**    Purpose:  Used as a wrapper function for write.
**
**    Return:   Upon success, the number of bytes sent, negative one otherwise.
**
******************************************************************************/

int osi_write_bytes( unsigned int send_socket, const char *buffer,
                     unsigned int amount )
{
    int burst      = 0;
    int bytes_sent = 0;
    int bytes_left = amount;

    const char *temp = buffer;

    while( bytes_left > 0 )
    {
        burst = osi_write( send_socket, (const void *)temp,
                           (size_t)bytes_left );

        if( burst > 0 )
        {
            bytes_left -= burst;
            bytes_sent += burst;

            temp += burst;
        }

        else if( burst < 0 )
        {
            /* if the socket is non-blocking, we would receive this */
            /* if it can't write, so we keep trying.                */

#ifdef WIN32
                if( WSAGetLastError() == EWOULDBLOCK )
#else
                if( errno == EWOULDBLOCK )
#endif
			{
                continue;
            }

            else
            {
                return -1;
            }
        }
    }

    return bytes_sent;
}

/******************************************************************************
**
**    Function: int osi_ssl_read_bytes
**
**    Purpose:  Used as a wrapper function for read, and reads until certain
**		        number of bytes have been read over SSL connection.
**
**    Return:   Upon success, the number of bytes read, negative one otherwise.
**
******************************************************************************/

int osi_ssl_read_bytes( SSL *ssl, char *buffer, unsigned int buffer_size,
                        int bytes_to_read )
{
    int bytes_read = 0;
    int bytes_left = bytes_to_read;
    int burst      = 0;

    char *temp = buffer;
    
    if( ( ssl == NULL ) || ( buffer == NULL ) )
    {
        return -1;
    }

    if( (unsigned int)bytes_to_read <= buffer_size )
    {
        while( bytes_left > 0 )
        {
            burst = osi_ssl_read( ssl, temp, bytes_left );

            if( burst > 0 )
            {
                bytes_read += burst;
                bytes_left -= burst;

                temp += burst;
            }

            else if( burst < 0 )
            {
                /* if the socket is non-blocking, we would receive this */
                /* if it can't read, so we keep trying.                 */

#ifdef WIN32
                if( WSAGetLastError() == EWOULDBLOCK )
#else
                if( errno == EWOULDBLOCK )
#endif
                {
                    continue;
                }
                
                else if( ( SSL_get_error( ssl, burst ) == SSL_ERROR_WANT_READ) )
                {
                    continue;
                }

                else
                {
                    return -1;
                }
            }

            else
            {
                break;
            }
        }
    }

    return bytes_read;
}

/******************************************************************************
**
**    Function: int osi_ssl_write_bytes
**
**    Purpose:  Used as a wrapper function for write, over SSL connection.
**
**    Return:   Upon success, the number of bytes sent, negative one otherwise.
**
******************************************************************************/

int osi_ssl_write_bytes( SSL *ssl, const char *buffer, unsigned int amount )
{
    int burst      = 0;
    int bytes_sent = 0;
    int bytes_left = amount;

    const char *temp = buffer;

    if( ( ssl == NULL ) || ( buffer == NULL ) )
    {
        return -1;
    }

    while( bytes_left > 0 )
    {
        burst = osi_ssl_write( ssl, (const void *)temp, (size_t)bytes_left );

        if( burst > 0 )
        {
            bytes_left -= burst;
            bytes_sent += burst;

            temp += burst;
        }

        else if( burst < 0 )
        {
            /* if the socket is non-blocking, we would receive this */
            /* if it can't write, so we keep trying.                */

#ifdef WIN32
                if( WSAGetLastError() == EWOULDBLOCK )
#else
                if( errno == EWOULDBLOCK )
#endif
			{
                continue;
            }
            
            else if( ( SSL_get_error( ssl, burst ) == SSL_ERROR_WANT_WRITE ) )
            {
                continue;
            }

            else
            {
                return -1;
            }
        }
    }

    return bytes_sent;
}


/******************************************************************************
**
**    Function: int osi_ssl_read
**
**    Purpose:  Used as a wrapper function for read over SSL connection.
**
**    Return:   Upon success, the number of bytes read, negative one otherwise.
**
******************************************************************************/

int osi_ssl_read( SSL *ssl, char *buffer, unsigned int buffer_size )
{
    int bytes_read = 0;

    if( ( ssl != NULL ) && ( buffer != NULL ) )
    {
        bytes_read = SSL_read( ssl, buffer, buffer_size );
    }

    return bytes_read;
}

/******************************************************************************
**
**    Function: int osi_ssl_write
**
**    Purpose:  Used as a wrapper function for write/send over SSL connection.
**
**    Return:   Upon success, the number of bytes sent, negative one otherwise.
**
******************************************************************************/

int osi_ssl_write( SSL *ssl, const char *buffer, unsigned int buffer_size )
{
    int bytes_written = 0;
    
    if( ( ssl != NULL ) && ( buffer != NULL ) )
    {
        bytes_written = SSL_write( ssl, buffer, buffer_size );
    }

    return bytes_written;
}


/******************************************************************************
**
**    Function: int listen_on_local_port
**
**    Purpose:  Makes use of osi_socket, osi_bind, and osi_listen to try
**              to listen on a local port.  Connections will be accepted
**              from any host.
**
**    Return:   Upon success, the listening socket, negative one otherwise.
**
******************************************************************************/

int osi_listen_on_local_port( unsigned int port, unsigned int max_waiting )
{
    struct sockaddr_in local_address;
    int server_socket;

    memset( &local_address, 0, sizeof( local_address ) );

    local_address.sin_family = AF_INET;
    local_address.sin_addr.s_addr = htonl( INADDR_ANY );
    local_address.sin_port = htons( (unsigned short)port );

    if( !( server_socket = osi_socket( AF_INET, SOCK_STREAM, 0 ) ) )
    {
        return -1;
    }

    if ( !( osi_bind( server_socket, ( struct sockaddr *)&local_address,
                      sizeof( struct sockaddr ) ) ) )
    {
        return -1;
    }

    if ( !( osi_listen( server_socket, max_waiting ) ) )
    {
        return -1;
    }

    return server_socket;
}

/*******************************************************************************
**
**    Function: osi_connect
**
**    Purpose:  Used as a wrapper function for connect.
**
**    Return:   integer one is returned upon success, zero otherwise.
**
******************************************************************************/

int osi_connect( unsigned int  socket,  struct sockaddr *local_address,
                 socklen_t address_length )
{
    if( ( connect( socket, local_address, address_length ) ) < 0 )
    {
        osi_close_socket( socket );
        return 0;
    }

    return 1;
}


/******************************************************************************
**
**    Function: osi_connect_to_host_on_port
**
**    Purpose:  Makes use of osi_socket and osi_connect to connect
**              to the remote host and port specified.
**
**    Return:   Upon success, the connected socket, zero otherwise.
**
******************************************************************************/

int osi_connect_to_host_on_port( const char *host, unsigned int port )
{
    struct sockaddr_in remote_address;
    struct hostent *host_entity;

    int connection_socket             	= 0;
    char host_ip[MAX_HOSTNAME_LENGTH]   = "";

    /* if host is NULL, don't bother connecting. */

    if( host == NULL )
    {
        return 0;
    }

    /* get host from the hostname if it isn't an IP address. */
    /* then copy it into a buffer we know will not overflow. */

    if( ( host_entity = gethostbyname( host ) ) == NULL )
    {
        osi_strlcpy( host_ip, host, sizeof( host_ip ) );
    }

    else
    {
        osi_strlcpy( host_ip,
              (char *)inet_ntoa( * ( (struct in_addr *)host_entity->h_addr ) ), 
                     sizeof( host_ip ) );
    }

    remote_address.sin_family = AF_INET;
    remote_address.sin_port = htons( (unsigned short)port );
    remote_address.sin_addr.s_addr = inet_addr( host_ip );

    memset( &( remote_address.sin_zero ), 0, 8 );
    connection_socket = osi_socket( AF_INET, SOCK_STREAM, 0 );

    if( osi_connect( connection_socket, ( struct sockaddr * ) &remote_address,
                     sizeof( remote_address ) ) )
    {
        return connection_socket;
    }

    return 0;
}

/*****************************************************************************
**
**    Function: osi_ssl_connect_to_host_on_port
**
**    Purpose:  Makes use of osi_socket and osi_connect to connect to
**              the remote host and port specified, using secure sockets layer.
**
**    Return:   Upon success, the SSL structure, NULL otherwise.
**
******************************************************************************/

SSL * osi_ssl_connect_to_host_on_port( const char *host, unsigned int port,
                                       SSL_CTX *ctx, osi_bool initiate )
{
    SSL *ssl;
    
    struct sockaddr_in remote_address;
    struct hostent *host_entity;

    int connection_socket             	= 0;
    char host_ip[MAX_HOSTNAME_LENGTH]   = "";

    /* if host or ssl context is NULL, don't bother connecting. */

    if( ( host == NULL ) || ( ctx == NULL ) )
    {
        return NULL;
    }

    /* get host from the hostname if it isn't an IP address. */
    /* then copy it into a buffer we know will not overflow. */

    if( ( host_entity = gethostbyname( host ) ) == NULL )
    {
        osi_strlcpy( host_ip, host, sizeof( host_ip ) );
    }

    else
    {
        osi_strlcpy( host_ip,
              (char *)inet_ntoa( * ( (struct in_addr *)host_entity->h_addr ) ),
                     sizeof( host_ip ) );
    }

    remote_address.sin_family = AF_INET;
    remote_address.sin_port = htons( (unsigned short)port );
    remote_address.sin_addr.s_addr = inet_addr( host_ip );

    memset( &( remote_address.sin_zero ), 0, 8 );
    connection_socket = osi_socket( AF_INET, SOCK_STREAM, 0 );

    if( osi_connect( connection_socket, ( struct sockaddr * )&remote_address,
                     sizeof( remote_address ) ) )
    {
        int result;
        
        ssl = SSL_new( ctx );
        
        if( ssl == NULL )
        {
            osi_close_socket( connection_socket );
            return NULL;
        }
        
        SSL_set_fd( ssl, connection_socket );
        
        if( initiate )
        {
            result = SSL_connect( ssl );
        }
        
        else
        {
            result = SSL_accept( ssl );
        }
        
        if( result != -1 )
        {
            return ssl;
        }
        
        else
        {
            osi_close_socket( connection_socket );
            SSL_free( ssl );
            
            return NULL;
        }
    }

    return NULL;
}


/******************************************************************************
**
**    Function: int osi_data_avaliable
**
**    Purpose:  Determines if data is waiting to be read on the socket
**              specified. A time length of 1/10 of a second is used so
**              that the function will return.  Using NULL can cause hangs.
**
**    Return:   The integer one if data is waiting, zero otherwise.
**
******************************************************************************/

int osi_data_available( unsigned int read_socket )
{
    fd_set readSet;
    struct timeval wait_time = { 0, 100000 };

    FD_ZERO( &readSet );
    FD_SET( read_socket, &readSet );

    select( ( read_socket + 1 ), &readSet, NULL, NULL, &wait_time );

    if( FD_ISSET( read_socket, &readSet ) )
    {
        return 1;
    }

    return 0;
}


/******************************************************************************
**
**    Function: osi_set_socket_blocking
**
**    Purpose:  sets the passed socket to be a blocking socket.
**
**    Return:   0 if succesfull, -1 on error.
**
******************************************************************************/

#ifdef WIN32

int osi_set_socket_blocking( int connection_socket )
{
  u_long cmd_option = 0;

  if( ioctlsocket( connection_socket, FIONBIO, &cmd_option ) != SOCKET_ERROR )
  {
      return 0;
  }

  return -1;
}

#else

int osi_set_socket_blocking( int connection_socket )
{
    int socket_flags = fcntl( connection_socket, F_GETFL, 0 );

    if( socket_flags != -1 )
    {
        socket_flags &= ~O_NONBLOCK;
        fcntl( connection_socket, F_SETFL, socket_flags  );

        return 0;
    }

    return -1;
}

#endif

/******************************************************************************
**
**    Function: osi_set_socket_blocking
**
**    Purpose:  sets the passed socket to be a non-blocking socket.
**
**    Return:   0 if succesfull, -1 on error.
**
******************************************************************************/

#ifdef WIN32

int osi_set_socket_non_blocking( int connection_socket )
{
  u_long cmd_option = 1L;

  if( ioctlsocket( connection_socket, FIONBIO, &cmd_option ) != SOCKET_ERROR )
  {
	  return 0;
  }

  return -1;
}

#else

int osi_set_socket_non_blocking( int connection_socket )
{
    int socket_flags = fcntl( connection_socket, F_GETFL, 0 );

    if( socket_flags != -1 )
    {
        fcntl( connection_socket, F_SETFL, ( socket_flags | O_NONBLOCK ) );
        return 0;
    }

    return -1;
}

#endif


#ifndef HAVE_INET_PTON
#ifndef WIN32
int inet_pton( int family, const char *name, void *address )
{
    if( family == AF_INET )
    {
        struct in_addr in_val;
        
        if( inet_aton( name, &in_val ) )
        {
            memcpy( address, &in_val, sizeof( struct in_addr ) );
            return 1;
        }
        
        return 0;
    }
    
    errno = EAFNOSUPPORT;
    return -1;
}
#endif
#endif

#ifndef HAVE_INET_NTOP

const char * inet_ntop( int family, const void *address,
                        char *name, size_t length )
{
    const unsigned char *p = (const unsigned char *)address;
    
    if( family == AF_INET )
    {
        char temp[MAX_HOSTNAME_LENGTH];
        
        osi_snprintf( temp, sizeof( temp ), "%d.%d.%d.%d",
                      p[0], p[1], p[2], p[3] );
        
        if( strlen( temp ) >= length )
        {
            errno = ENOSPC;
            return NULL;
        }
        
        osi_strlcpy( name, temp, length );
        return name;
    }

#ifndef WIN32    
    errno = EAFNOSUPPORT;
#endif

    return NULL;
}

#endif

#ifndef HAVE_SOCKETPAIR

int socketpair( int family, int type, int protocol, int *pair )
{
    int length;
    int listen_socket;
    struct sockaddr_in sin[2];

    memset( sin,0,sizeof( sin ) );

    if( type != SOCK_STREAM )
    {
        return -1;
    }

    listen_socket = socket( family, type, protocol);

    if( listen_socket < 0 )
    {
        return -1;
    }

    sin[0].sin_family = family;
    sin[0].sin_port = htons(0);
    sin[0].sin_addr.s_addr = inet_addr( "127.0.0.1" );

    if( bind( listen_socket, (struct sockaddr *)&sin[0], sizeof(sin[0]) ) < 0 )
    {
        return -1;
    }

    length = sizeof( sin[0] );

    if( getsockname( listen_socket, (struct sockaddr *)&sin[0], &length ) < 0 )
    { 
        return -1;
    }

    /* modest listen queue. */

    if( listen( listen_socket, 10 ) < 0 )
    {
        return -1;
    }

    pair[1] = socket( family, type, protocol );

    if( pair[1] < 0 )
    {
        return -1;
    }

    //osi_set_socket_non_blocking( pair[1] );

    if( connect( pair[1], (struct sockaddr *)&sin[0], sizeof(sin[0]) ) != 0 )
    {
        return -1;
    }

    length = sizeof( sin[1] );

    pair[0] = accept( listen_socket, (struct sockaddr *)&sin[1], &length );

    if( pair[0] < 0 )
    {
        return -1;
    }

    osi_set_socket_non_blocking( pair[1] );
    close( listen_socket );

    return 0;
}

#endif  /* ifndef HAVE_SOCKETPAIR */
