// **********************************************************************
//
// Copyright (c) 2002
// IONA Technologies, Inc.
// Waltham, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#ifndef OB_NET_H
#define OB_NET_H

#ifdef HAVE_WINSOCK_H

//
// Increasing FD_SETSIZE increases the number of simultaneous connections
// permitted on Win32.
//
#   ifndef FD_SETSIZE
#      define FD_SETSIZE 1024
#   endif

#   include <winsock.h>

namespace OB
{

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

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

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

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

inline bool ConnectionDown()
{
    return GetError() == WSAECONNRESET
	|| GetError() == WSAESHUTDOWN
	|| GetError() == WSAECONNABORTED;
}

inline bool NoBufs()
{
    return GetError() == WSAENOBUFS
        || GetError() == WSAEFAULT;
}

inline bool WouldBlock()
{
    return GetError() == WSAEWOULDBLOCK;
}

inline bool InProgress()
{
    return GetError() == WSAEWOULDBLOCK;
}

inline bool AcceptRetry()
{
    return GetError() == WSAEINTR;
}

inline bool IsInterrupted()
{
    return GetError() == WSAEINTR;
}

inline bool ConnectFailed()
{
    return GetError() == WSAECONNREFUSED
	|| GetError() == WSAETIMEDOUT
	|| GetError() == WSAENETUNREACH
	|| ConnectionDown();
}

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

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

} // End of namespace OB

#else

#   include <stdlib.h>
#   include <stdio.h>
#   include <unistd.h>
#   include <errno.h>
#   include <signal.h>
#   include <fcntl.h>
#   include <sys/types.h>
#   ifdef HAVE_SYS_PARAM_H
#       include <sys/param.h>
#   endif
#   ifdef HAVE_SYS_TIME_H
#       include <sys/time.h>
#   endif
#   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>

namespace OB
{

inline int GetError()
{
    return errno;
}

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

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

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

inline bool ConnectionDown()
{
    return GetError() == ECONNRESET
	|| GetError() == ESHUTDOWN
	|| GetError() == ECONNABORTED
	|| GetError() == EPIPE
	|| GetError() == EINVAL;
}

inline bool NoBufs()
{
    return GetError() == ENOBUFS;
}

inline bool WouldBlock()
{
    return GetError() == EAGAIN || GetError() == EWOULDBLOCK;
}

inline bool InProgress()
{
    return GetError() == EINPROGRESS;
}

inline bool AcceptRetry()
{
    switch(GetError())
    {
    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 IsInterrupted()
{
    switch(GetError())
    {
    case EINTR:

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

    return false;
}

inline bool ConnectFailed()
{
    return GetError() == ECONNREFUSED
	|| GetError() == ETIMEDOUT
	|| GetError() == ENETUNREACH
	|| ConnectionDown();
}

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

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

} // End of namespace OB

#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

#ifndef SHUT_RD
#   define SHUT_RD 0
#endif

#ifndef SHUT_WR
#   define SHUT_WR 1
#endif

#ifndef SHUT_RDWR
#   define SHUT_RDWR 2
#endif

#define OB_GETHOSTBYNAME_RETRY 3

namespace OB
{

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

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

//
// Get canonical hostname
//
char* GetCanonicalHostname(bool);

} // End of namespac OB

#endif
