// **********************************************************************
//
// Copyright (c) 1999
// Object Oriented Concepts, Inc.
// Billerica, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#ifndef OB_NET_H
#define OB_NET_H

#ifdef HAVE_WINSOCK_H

#   include <winsock.h>

inline int OBGetError()
{
    return WSAGetLastError();
}

inline void OBSetError(int err)
{
    WSASetLastError(err);
}

inline int OBCreateSocket()
{
    return socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
}

inline int OBCloseSocket(int fd)
{
    return closesocket(fd);
}

inline bool OBConnectionDown()
{
    return OBGetError() == WSAECONNRESET;
}

inline bool OBWouldBlock()
{
    return OBGetError() == WSAEWOULDBLOCK;
}

inline bool OBInProgress()
{
    return OBGetError() == WSAEWOULDBLOCK;
}

inline bool OBAcceptRetry()
{
    return OBGetError() == WSAEINTR;
}

inline bool OBIsInterrupted()
{
    return OBGetError() == WSAEINTR;
}

inline void OBSetBlock(int fd)
{
    unsigned long arg = 0;
    ioctlsocket(fd, FIONBIO, &arg);
}

inline void OBSetNoBlock(int fd)
{
    unsigned long arg = 1;
    ioctlsocket(fd, FIONBIO, &arg);
}

#else

#   include <stdlib.h>
#   include <stdio.h>
#   include <unistd.h>
#   include <errno.h>
#   include <signal.h>
#   include <fcntl.h>
#   include <sys/types.h>
#   include <sys/param.h>
#   include <sys/time.h>
#   include <sys/socket.h>
#   ifdef HAVE_BSTRING_H
#       include <bstring.h>
#   endif
#   ifdef HAVE_STRINGS_H
#       include <strings.h>
#   endif
#   ifdef HAVE_SYS_SELECT_H
#	include <sys/select.h>
#   endif
#   include <netinet/in.h>
#   include <netinet/tcp.h>

//
// HPUX 11.x B.11.00 has a broken h_errno declaration in netdb.h. This
// declaration must be before the inclusion of netdb.h.
//
#    if defined(HAVE_BROKEN_H_ERRNO_DECL) && defined(__hpux)
extern "C"
{
     extern int *__h_errno();
};
#    endif

#   include <netdb.h>
#   include <arpa/inet.h>

inline int OBGetError()
{
    return errno;
}

inline void OBSetError(int err)
{
    errno = err;
}

inline int OBCreateSocket()
{
    return socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
}

inline int OBCloseSocket(int fd)
{
    return close(fd);
}

inline bool OBConnectionDown()
{
    return OBGetError() == ECONNRESET || OBGetError() == EPIPE;
}

inline bool OBWouldBlock()
{
    return OBGetError() == EAGAIN || OBGetError() == EWOULDBLOCK;
}

inline bool OBInProgress()
{
    return OBGetError() == EINPROGRESS;
}

inline bool OBAcceptRetry()
{
    switch(OBGetError())
    {
    case EINTR:
	
#ifdef EPROTO
	//
	// This error can occur on Solaris.  accept() returns EPROTO
	// is a connection was closed remotely before it was accepted.
	//
    case EPROTO:
#endif
        
#ifdef ECONNABORTED
	//
	// In Solaris 2.6 and later, you may get ECONNABORTED (instead
	// of EPROTO) under certain race conditions and that is the
	// error code in the Posix standard too.  So you need to do
	// "ignore-and-retry" for EINTR, ECONNABORTED and EPROTO if you
	// need portability across releases of Solaris. If you need to
	// be portable across several variants of Linux/BSD/Unix etc,
	// you may need to do some #ifdef's since even the constant
	// EPROTO/ECONNABORTED may not be there on some platforms. A
	// good example code to look at is the code around the
	// "accept()" call in the Apache Web server source code
	// available from the site http://www.apache.org
	//
    case ECONNABORTED:
#endif
        
#ifdef ECONNRESET
    case ECONNRESET:
#endif
        
#ifdef ETIMEDOUT
    case ETIMEDOUT:
#endif
        return true;
    }
    return false;
}

inline bool OBIsInterrupted()
{
    switch(OBGetError())
    {
    case EINTR:

	//
	// Not positive on this, but it appears from some newsgroup
	// postings that some OS's #define EAGAIN EWOULDBLOCK
	//
#if EAGAIN != EWOULDBLOCK
    case EAGAIN:
#endif

#ifdef EPROTO
    case EPROTO:
#endif
        
	return true;
    }
    return false;
}

inline void OBSetBlock(int fd)
{
    int flags = fcntl(fd, F_GETFL);
    flags &= ~O_NONBLOCK;
    fcntl(fd, F_SETFL, flags);
}

inline void OBSetNoBlock(int fd)
{
    int flags = fcntl(fd, F_GETFL);
    flags |= O_NONBLOCK;
    fcntl(fd, F_SETFL, flags);
}

#endif

#ifdef HAVE_NO_GETHOSTNAME_PROTOTYPE
extern "C"
{
    int gethostname(char*,  int);
}
#endif

#ifdef HAVE_NO_H_ERRNO_DECLARATION
extern "C"
{
    extern int h_errno;
}
#endif

#ifndef MAXHOSTNAMELEN
#   define MAXHOSTNAMELEN 1024
#endif

#ifndef IPPORT_MAXPORT
#   define IPPORT_MAXPORT 65535
#endif

#ifndef IPPORT_USERRESERVED
#   define IPPORT_USERRESERVED 5000
#endif

#ifndef INADDR_ANY
#   define INADDR_ANY ((unsigned) 0x00000000)
#endif

#ifndef INADDR_NONE
#   define INADDR_NONE ((unsigned) 0xffffffff)
#endif

#ifndef INADDR_LOOPBACK
#   define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1 */
#endif

#define OB_GETHOSTBYNAME_RETRY 3

//
// Get an error message for a given errno (hides differences between
// Unix and NT)
//
const char* OBLastError(); // For "regular" errors
const char* OBLastHError(); // For gethostbyname()-related errors

//
// Get internet address
//
void OBGetInAddr(const char*, struct sockaddr_in&);

#endif
