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

#include <OB/Basic.h>
#include <OB/Except.h>
#include <OB/Net.h>

// ----------------------------------------------------------------------
// Get an error message for a given errno (hides differences between
// Unix and NT)
// ----------------------------------------------------------------------

#ifdef WIN32

const char*
OBLastError()
{
    int err = OBGetError();
    const char* msg = "unknown error!";

    switch(err)
    {
    case WSAEINTR:
	msg = "error number WSAEINTR";
	break;
	
    case WSAEBADF:
	msg = "error number WSAEBADF";
	break;
	
    case WSAEACCES:
	msg = "error number WSAEACCES";
	break;
	
    case WSAEFAULT:
	msg = "error number WSAEFAULT";
	break;
	
    case WSAEINVAL:
	msg = "error number WSAEINVAL";
	break;
	
    case WSAEMFILE:
	msg = "error number WSAEMFILE";
	break;
	
    case WSAEWOULDBLOCK:
	msg = "error number WSAEWOULDBLOCK";
	break;
	
    case WSAEINPROGRESS:
	msg = "error number WSAEINPROGRESS";
	break;
	
    case WSAEALREADY:
	msg = "error number WSAEALREADY";
	break;
	
    case WSAENOTSOCK:
	msg = "error number WSAENOTSOCK";
	break;
	
    case WSAEDESTADDRREQ:
	msg = "error number WSAEDESTADDRREQ";
	break;
	
    case WSAEMSGSIZE:
	msg = "error number WSAEMSGSIZE";
	break;
	
    case WSAEPROTOTYPE:
	msg = "error number WSAEPROTOTYPE";
	break;
	
    case WSAENOPROTOOPT:
	msg = "error number WSAENOPROTOOPT";
	break;
	
    case WSAEPROTONOSUPPORT:
	msg = "error number WSAEPROTONOSUPPORT";
	break;
	
    case WSAESOCKTNOSUPPORT:
	msg = "error number WSAESOCKTNOSUPPORT";
	break;
	
    case WSAEOPNOTSUPP:
	msg = "error number WSAEOPNOTSUPP";
	break;
	
    case WSAEPFNOSUPPORT:
	msg = "error number WSAEPFNOSUPPORT";
	break;
	
    case WSAEAFNOSUPPORT:
	msg = "error number WSAEAFNOSUPPORT";
	break;
	
    case WSAEADDRINUSE:
	msg = "error number WSAEADDRINUSE";
	break;
	
    case WSAEADDRNOTAVAIL:
	msg = "error number WSAEADDRNOTAVAIL";
	break;
	
    case WSAENETDOWN:
	msg = "error number WSAENETDOWN";
	break;
	
    case WSAENETUNREACH:
	msg = "error number WSAENETUNREACH";
	break;
	
    case WSAENETRESET:
	msg = "error number WSAENETRESET";
	break;
	
    case WSAECONNABORTED:
	msg = "error number WSAECONNABORTED";
	break;
	
    case WSAECONNRESET:
	msg = "error number WSAECONNRESET";
	break;
	
    case WSAENOBUFS:
	msg = "error number WSAENOBUFS";
	break;
	
    case WSAEISCONN:
	msg = "error number WSAEISCONN";
	break;
	
    case WSAENOTCONN:
	msg = "error number WSAENOTCONN";
	break;
	
    case WSAESHUTDOWN:
	msg = "error number WSAESHUTDOWN";
	break;
	
    case WSAETOOMANYREFS:
	msg = "error number WSAETOOMANYREFS";
	break;
	
    case WSAETIMEDOUT:
	msg = "error number WSAETIMEDOUT";
	break;
	
    case WSAECONNREFUSED:
	msg = "error number WSAECONNREFUSED";
	break;
	
    case WSAELOOP:
	msg = "error number WSAELOOP";
	break;
	
    case WSAENAMETOOLONG:
	msg = "error number WSAENAMETOOLONG";
	break;
	
    case WSAEHOSTDOWN:
	msg = "error number WSAEHOSTDOWN";
	break;
	
    case WSAEHOSTUNREACH:
	msg = "error number WSAEHOSTUNREACH";
	break;
	
    case WSAENOTEMPTY:
	msg = "error number WSAENOTEMPTY";
	break;
	
    case WSAEPROCLIM:
	msg = "error number WSAEPROCLIM";
	break;
	
    case WSAEUSERS:
	msg = "error number WSAEUSERS";
	break;
	
    case WSAEDQUOT:
	msg = "error number WSAEDQUOT";
	break;
	
    case WSAESTALE:
	msg = "error number WSAESTALE";
	break;
	
    case WSAEREMOTE:
	msg = "error number WSAEREMOTE";
	break;
	
    case WSAEDISCON:
	msg = "error number WSAEDISCON";
	break;
	
    case WSASYSNOTREADY:
	msg = "error number WSASYSNOTREADY";
	break;
	
    case WSAVERNOTSUPPORTED:
	msg = "error number WSAVERNOTSUPPORTED";
	break;
	
    case WSANOTINITIALISED:
	msg = "error number WSANOTINITIALISED";
	break;
	
    case WSAHOST_NOT_FOUND:
	msg = "error number WSAHOST_NOT_FOUND";
	break;
	
    case WSATRY_AGAIN:
	msg = "error number WSATRY_AGAIN";
	break;
	
    case WSANO_RECOVERY:
	msg = "error number WSANO_RECOVERY";
	break;
	
    case WSANO_DATA:
	msg = "error number WSANO_DATA";
	break;
    }

    return msg;
}

const char*
OBLastHError()
{
    return OBLastError();
}

#else

#   ifndef HAVE_BSD_NETINET
#	include <netdb.h> // For h_error
#   else
#	include <bsd/netdb.h> // For h_error
#   endif

#   ifndef NETDB_INTERNAL
#       define NETDB_INTERNAL -1
#   endif

#   ifndef NETDB_SUCCESS
#       define NETDB_SUCCESS 0
#   endif

const char*
OBLastError()
{
    return strerror(errno);
}

const char*
OBLastHError()
{
    const char* msg = "unknown error!";

    switch(h_errno)
    {
    case NETDB_INTERNAL:
	msg = "internal problem";
	break;

    case NETDB_SUCCESS:
	msg = "no problem";
	break;

    case HOST_NOT_FOUND:
	msg = "no such host is known";
	break;
	
    case TRY_AGAIN:
	msg = "temporary error, try again";
	break;
	
    case NO_RECOVERY:
	msg = "unexpected server failure";
	break;
	
    case NO_DATA:
	msg = "name has no IP address";
	break;
    }

    return msg;
}
#endif

// ----------------------------------------------------------------------
// Get internet address
// ----------------------------------------------------------------------

//
// We don't use gethostbyname_r here, since there are different
// implementations in different Unix versions. Instead, we use a
// mutex.
//
#ifdef HAVE_JTC
static JTCMutex GethostbynameMutex;
#endif

void
OBGetInAddr(const char* host, struct sockaddr_in& addr)
{
    addr.sin_addr.s_addr = inet_addr(host);
    if(addr.sin_addr.s_addr == INADDR_NONE)
    {
#ifdef HAVE_JTC
	JTCSynchronized sync(GethostbynameMutex);
#endif
	struct hostent* ent;
	int retry = OB_GETHOSTBYNAME_RETRY;
	do
	{
	    ent = gethostbyname(host);
	}
#ifdef WIN32
	while(!ent && WSAGetLastError() == WSATRY_AGAIN && retry-- > 0);
#else
	while(!ent && h_errno == TRY_AGAIN && retry-- > 0);
#endif

	if(!ent)
	{
	    throw CORBA_COMM_FAILURE(OBLastHError(), OBMinorGethostbyname,
				     CORBA_COMPLETED_NO);
	}
	
	memcpy(&addr.sin_addr, ent -> h_addr, ent -> h_length);
    }
}
