#include "networkAddress.h"
#include "debug.h"   // for print out of warnings, etc
#include "sysdefs.h"  // for MIN() definition

#include <stdlib.h>  // for atoi()
#include <errno.h>   // for errno
#ifdef UNIX
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netdb.h>
#include <unistd.h>  // for gethostname()

#ifdef SOLARIS
#include <sys/sockio.h> // for SIOCGIFADDR ioctl
#endif  // SOLARIS

#endif // UNIX


bool NetworkAddress::IsMulticast() const
{
    switch(type)
    {
            case IPv4:
                return ((htonl(0xf0000000) & ((struct sockaddr_in*)&addr)->sin_addr.s_addr)
                            == htonl(0xe0000000));

#ifdef HAVE_IPV6
        case IPv6:
        {
            if (IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6*)&addr)->sin6_addr)))
            {
                return (htonl(0xe0000000) == 
                        (htonl(0xf0000000) &
                         IN6_V4MAPPED_ADDR(&(((struct sockaddr_in6*)&addr)->sin6_addr))));
            }
            else
            {
                return (0 != IN6_IS_ADDR_MULTICAST(&(((struct sockaddr_in6*)&addr)->sin6_addr)) ? true : false);
            }
        }
#endif // HAVE_IPV6

#ifdef SIMULATOR_ADDRESS
            case SIM:
#ifdef NS2
                return (((struct sockaddr_sim *)&addr)->addr & 
                        0x80000000);
#endif // NS2
#ifdef OPNET  // For now our OPNET "model" uses broadcast address always
            return true;
#endif // OPNET
#endif // SIMULATOR_ADDRESS
            default:
                DMSG(0, "NetworkAddress: IsMulticast(): Invalid address type!\n");
                return false;
    }
}  // end IsMulticast()

bool NetworkAddress::IsLoopback() const
{
    switch(type)
    {
        case IPv4:
            return (htonl(0x7f000001) == ((struct sockaddr_in*)&addr)->sin_addr.s_addr);

#ifdef HAVE_IPV6
        case IPv6:
        {
            if (IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6*)&addr)->sin6_addr)))
                return (htonl(0x7f000001) ==
                        IN6_V4MAPPED_ADDR(&(((struct sockaddr_in6*)&addr)->sin6_addr)));
            else
                return (0 != IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6*)&addr)->sin6_addr)) ? true : false);
        }
#endif // HAVE_IPV6

#ifdef SIMULATOR_ADDRESS
        case SIM:
            return false;
#endif // SIMULATOR_ADDRESS

        default:
            DMSG(0, "NetworkAddress: IsLoopback(): Invalid address type!\n");
            return false;
    }
}  // end NetworkAddress::IsLoopback()

bool NetworkAddress::IsUnspecified() const
{
    switch(type)
    {
            case IPv4:
                return (0x00000000 == ((struct sockaddr_in*)&addr)->sin_addr.s_addr);
#ifdef HAVE_IPV6
        case IPv6:
        {
            if (IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6*)&addr)->sin6_addr)))
            {
                return (0x0000000 == 
                        IN6_V4MAPPED_ADDR(&(((struct sockaddr_in6*)&addr)->sin6_addr)));
            }
            else
            {
                return (0 != IN6_IS_ADDR_UNSPECIFIED(&(((struct sockaddr_in6*)&addr)->sin6_addr)) ? true : false);
            }
        }
#endif // HAVE_IPV6
#ifdef SIMULATOR_ADDRESS
            case SIM:
                return false;
#endif // SIMULATOR_ADDRESS
            default:
                DMSG(0, "NetworkAddress: IsUnspecified(): Invalid address type!\n");
                return false;
    }
}  // end IsUnspecified()


bool NetworkAddress::IsLinkLocal() const
{
    switch(type)
    {
#ifdef HAVE_IPV6
        case IPv6:
        {
            struct in6_addr* a = &(((struct sockaddr_in6*)&addr)->sin6_addr);
            return (IN6_IS_ADDR_MULTICAST(a) ? 
                    (0 != IN6_IS_ADDR_MC_LINKLOCAL(a) ? true : false) :
                    (0 != IN6_IS_ADDR_LINKLOCAL(a) ? true : false));
        }
#endif // HAVE_IPV6

        default:
           return false;
    }
}  // end NetworkAddress::IsLinkLocal()


bool NetworkAddress::IsSiteLocal() const
{
    switch(type)
    {
#ifdef HAVE_IPV6
        case IPv6:
        {
            struct in6_addr* a = &(((struct sockaddr_in6*)&addr)->sin6_addr);
            return (IN6_IS_ADDR_MULTICAST(a) ? 
                    (0 != IN6_IS_ADDR_MC_SITELOCAL(a) ? true : false) :
                    (0 != IN6_IS_ADDR_SITELOCAL(a) ? true : false));
        }
#endif // HAVE_IPV6

        default:
           return false;
    }
}  // end NetworkAddress::IsSiteLocal()

const char* NetworkAddress::HostAddressString() const
{
    switch (type)
    {
#ifdef WIN32
        case IPv4:
        case IPv6:
        {
            // Startup WinSock for name lookup
            if (!Win32Startup())
            {
                    DMSG(0, "NetworkAddress: HostAddressString(): Error initializing WinSock!\n");
                    return NULL;
            }
            static char buffer[64];
            unsigned long bufferSize = 64;
            WSAAddressToString((SOCKADDR *)&addr, sizeof(addr), NULL, buffer, &bufferSize);
            Win32Cleanup();
            // Get rid of trailing port number
            char* ptr = strrchr(buffer, ':');
            if (ptr) *ptr = '\0';
            return buffer;
        }
#else  // UNIX
        case IPv4:
            return inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr);
#ifdef HAVE_IPV6
        case IPv6:
        {
            static char buffer[64];
            return inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr, buffer, 64);
        }
#endif // HAVE_IPV6
#endif // if/else WIN32

#ifdef SIMULATOR_ADDRESS
            case SIM:
            {
                    static char text[32];
                    sprintf(text, "%u", ((struct sockaddr_sim*)&addr)->addr);
                    return text;
            }
#endif // SIMULATOR_ADDRESS

            default:
                DMSG(0, "NetworkAddress: HostAddressString(): Invalid address type!\n");
                return NULL;
    }  // end switch(type)
}  // end NetworkAddress::HostAddressString()


void NetworkAddress::SetPort(unsigned short thePort)
{
    switch(type)
    {
            case IPv4:
                ((struct sockaddr_in*)&addr)->sin_port = htons(thePort);
                break;

#ifdef HAVE_IPV6
        case IPv6:
            ((struct sockaddr_in6*)&addr)->sin6_port = htons(thePort);
                break;
#endif // HAVE_IPV6

#ifdef SIMULATOR_ADDRESS
                case SIM:
                ((struct sockaddr_sim*)&addr)->port = thePort;
                        break;
#endif // SIMULATOR_ADDRESS

            default:
                DMSG(0, "NetworkAddress::SetPort(): Invalid address type!\n");
                break;
    }
}  // end NetworkAddress::PortSet()

unsigned short NetworkAddress::Port() const
{
    switch(type)
    {
            case IPv4:
                return ntohs(((struct sockaddr_in *)&addr)->sin_port);

#ifdef HAVE_IPV6
        case IPv6:
            return ntohs(((struct sockaddr_in6 *)&addr)->sin6_port);
#endif // HAVE_IPV6

#ifdef SIMULATOR_ADDRESS
                case SIM:
                return (((struct sockaddr_sim*)&addr)->port);
#endif // SIMULATOR_ADDRESS

            default:
                DMSG(0, "NetworkAddress::Port(): Invalid address type!\n");
                return 0;  // port 0 is an invalid port
    }
}  // end NetworkAddress::Port()


void NetworkAddress::Reset(NetworkAddressType theType, bool zero)
{
    char value = zero ? 0x00 : 0xff;   
    switch (theType)
    {
        case IPv4:
            type = IPv4;
            len = 4;  
            memset(&((struct sockaddr_in*)&addr)->sin_addr, value, 4);
            ((struct sockaddr_in*)&addr)->sin_family = AF_INET;
            break;
#ifdef HAVE_IPV6
        case IPv6:
            type = IPv6;
            len  = 16;
            memset(&((struct sockaddr_in6*)&addr)->sin6_addr, value, 16);
            ((struct sockaddr_in6*)&addr)->sin6_family = AF_INET6;  
            break;
#endif // HAVE_IPV6
#ifdef SIMULATOR_ADDRESS
        case SIM:
            type = SIM;
            len = sizeof(SIMADDR);
            memset(&((struct sockaddr_sim*)&addr)->addr, value, sizeof(SIMADDR));
            break;
#endif // SIMULATOR_ADDRESS
        default:
            DMSG(0, "NetworkAddress::Reset() Invalid address type!\n");
            break;
    }
}  // end NetworkAddress::Reset();

bool NetworkAddress::SetAddress(const struct sockaddr* theAddr)
{
    switch (theAddr->sa_family)
    {
        case AF_INET:
            memcpy(&addr, theAddr, sizeof(struct sockaddr_in));
            type = IPv4;
            len = 4;
            return true;

#ifdef HAVE_IPV6
        case AF_INET6:
            memcpy(&addr, theAddr, sizeof(struct sockaddr_in6));
            type = IPv6;
            len = 16;
            return true;
#endif // HAVE_IPV6

        default:
            DMSG(1, "NetworkAddress::SetAddress() Invalid address type!\n");
            type = NETWORK_INVALID_ADDRESS;
            return false;
    }
}  // end NetworkAddress:SetAddress()


bool NetworkAddress::SetRawHostAddress(NetworkAddressType   theType,
                                       const char*          buffer,
                                       unsigned int         bufferLen)
{
    switch (theType)
    {       
        case IPv4:
            if (bufferLen > 4) return false;
            type = IPv4;
            len = 4;
            memset(&((struct sockaddr_in*)&addr)->sin_addr, 0, 4);
            memcpy(&((struct sockaddr_in*)&addr)->sin_addr, buffer, bufferLen);
            ((struct sockaddr_in*)&addr)->sin_family = AF_INET;
            return true;
#ifdef HAVE_IPV6
        case IPv6:
            if (bufferLen > 16) return false;
            type = IPv6;
            len  = 16;
            memset(&((struct sockaddr_in6*)&addr)->sin6_addr, 0, 16);
            memcpy(&((struct sockaddr_in6*)&addr)->sin6_addr, buffer, bufferLen);
            ((struct sockaddr_in6*)&addr)->sin6_family = AF_INET6;
            return true;
#endif // HAVE_IPV6  
#ifdef SIMULATOR_ADDRESS          
        case SIM:
            if (bufferLen > sizeof(SIMADDR)) return false;
            type = SIM;
            len = sizeof(SIMADDR);
            memset(&((struct sockaddr_sim*)&addr)->addr, 0, sizeof(SIMADDR));
            memcpy(&((struct sockaddr_sim*)&addr)->addr, buffer, bufferLen);
            return true;
#endif // SIMULATOR_ADDRESS
        default:
            DMSG(0, "NetworkAddress::SetRawHostAddress() Invalid address type!\n");
            return false;
    }
}  // end NetworkAddress::SetRawHostAddress()
        
        
const char* NetworkAddress::RawHostAddress() const
{
    switch (type)
    {
        case IPv4:
            return ((char*)&((struct sockaddr_in*)&addr)->sin_addr);
#ifdef HAVE_IPV6
        case IPv6:
            return ((char*)&((struct sockaddr_in6*)&addr)->sin6_addr);
#endif // HAVE_IPV6  
#ifdef SIMULATOR_ADDRESS          
        case SIM:
            return ((char*)&((struct sockaddr_sim*)&addr)->addr);
#endif // SIMULATOR_ADDRESS
        default:
            DMSG(0, "NetworkAddress::RawHostAddress() Invalid address type!\n");
            return NULL;
    }
}  // end NetworkAddress::RawHostAddress()

unsigned int NetworkAddress::PrefixLength() const
{
    unsigned char* ptr = NULL;
    unsigned int maxBytes = 0;
    switch (type)
    {
        case IPv4:
            ptr = ((unsigned char*)&((struct sockaddr_in*)&addr)->sin_addr);
            maxBytes = 4;
            break;
#ifdef HAVE_IPV6
        case IPv6:
            ptr = ((unsigned char*)&((struct sockaddr_in6*)&addr)->sin6_addr);
            maxBytes = 16;
            break;
#endif // HAVE_IPV6  
#ifdef SIMULATOR_ADDRESS          
        case SIM:
            ptr = ((unsigned char*)&((struct sockaddr_sim*)&addr)->addr);
            maxBytes = sizeof(SIMADDR) << 3;
            break;
#endif // SIMULATOR_ADDRESS
        default:
            DMSG(0, "NetworkAddress::PrefixLength() Invalid address type!\n");
            return 0;
    }
    unsigned int prefixLen = 0;
    for (unsigned int i = 0; i < maxBytes; i++)
    {
        if (0xff == *ptr)
        {
            prefixLen += 8;
            ptr++;   
        }
        else
        {
            unsigned char bit = 0x80;
            while (0 != (bit & *ptr))
            {
                bit >>= 1;
                prefixLen += 1;   
            }
            break;
        }   
    }
    return prefixLen;
}  // end NetworkAddress::PrefixLength()



void NetworkAddress::ApplyPrefixMask(unsigned int prefixLen)
{
    unsigned char* ptr = NULL;
    unsigned int maxLen = 0;
    switch (type)
    {
        case IPv4:
            ptr = ((unsigned char*)&((struct sockaddr_in*)&addr)->sin_addr);
            maxLen = 32;
            break;
#ifdef HAVE_IPV6
        case IPv6:
            ptr = ((unsigned char*)&((struct sockaddr_in6*)&addr)->sin6_addr);
            maxLen = 128;
            break;
#endif // HAVE_IPV6  
#ifdef SIMULATOR_ADDRESS          
        case SIM:
            ptr = ((unsigned char*)&((struct sockaddr_sim*)&addr)->addr);
            maxLen = sizeof(SIMADDR) << 3;
            break;
#endif // SIMULATOR_ADDRESS
        default:
            DMSG(0, "NetworkAddress::ApplyPrefixMask() Invalid address type!\n");
            return;
    }
    if (prefixLen >= maxLen) return;
    unsigned int nbytes = prefixLen >> 3;
    unsigned int remainder = prefixLen & 0x07;
    if (remainder) 
    {
        ptr[nbytes] &= (unsigned char)(0xff << (8 - remainder));
        nbytes++;
    }
    memset(ptr + nbytes, 0, len - nbytes);    
}  // end NetworkAddress::ApplyPrefixMask()

void NetworkAddress::GetSubnetAddress(unsigned int    prefixLen, 
                                      NetworkAddress& subnetAddr) const
{
    subnetAddr = *this;
    unsigned char* ptr = NULL;
    unsigned int maxLen = 0;
    switch (type)
    {
        case IPv4:
            ptr = ((unsigned char*)&((struct sockaddr_in*)&subnetAddr.addr)->sin_addr);
            maxLen = 32;
            break;
#ifdef HAVE_IPV6
        case IPv6:
            ptr = ((unsigned char*)&((struct sockaddr_in6*)&subnetAddr.addr)->sin6_addr);
            maxLen = 128;
            break;
#endif // HAVE_IPV6  
#ifdef SIMULATOR_ADDRESS          
        case SIM:
            ptr = ((unsigned char*)&((struct sockaddr_sim*)&subnetAddr.addr)->addr);
            maxLen = sizeof(SIMADDR) << 3;
            break;
#endif // SIMULATOR_ADDRESS
        default:
            DMSG(0, "NetworkAddress::GetSubnetAddress() Invalid address type!\n");
            return;
    }
    if (prefixLen >= maxLen) return;
    unsigned int nbytes = prefixLen >> 3;
    unsigned int remainder = prefixLen & 0x07;
    if (remainder) 
    {
        ptr[nbytes] &= (unsigned char)(0xff << (8 - remainder));
        nbytes++;
    }
    memset(ptr + nbytes, 0, len - nbytes);           
}  // end NetworkAddress::GetSubnetAddress()

void NetworkAddress::GetBroadcastAddress(unsigned int    prefixLen, 
                                         NetworkAddress& broadcastAddr) const
{
    broadcastAddr = *this;
    unsigned char* ptr = NULL;
    unsigned int maxLen = 0;
    switch (type)
    {
        case IPv4:
            ptr = ((unsigned char*)&((struct sockaddr_in*)&broadcastAddr.addr)->sin_addr);
            maxLen = 32;
            break;
#ifdef HAVE_IPV6
        case IPv6:
            ptr = ((unsigned char*)&((struct sockaddr_in6*)&broadcastAddr.addr)->sin6_addr);
            maxLen = 128;
            break;
#endif // HAVE_IPV6  
#ifdef SIMULATOR_ADDRESS          
        case SIM:
            ptr = ((unsigned char*)&((struct sockaddr_sim*)&broadcastAddr.addr)->addr);
            maxLen = sizeof(SIMADDR) << 3;
            break;
#endif // SIMULATOR_ADDRESS
        default:
            DMSG(0, "NetworkAddress::GetBroadcastAddress() Invalid address type!\n");
            return;
    }
    if (prefixLen >= maxLen) return;
    unsigned int nbytes = prefixLen >> 3;
    unsigned int remainder = prefixLen & 0x07;
    if (remainder) 
    {
        ptr[nbytes] |= (0x00ff >> remainder);
        nbytes++;
    }
    memset(ptr + nbytes, 0xff, len - nbytes);           
}  // end NetworkAddress::GetBroadcastAddress()


unsigned long NetworkAddress::EndIdentifier() const
{
    switch(type)
    {
        case IPv4:
            return ntohl(((struct sockaddr_in*)&addr)->sin_addr.s_addr);
#ifdef HAVE_IPV6
        case IPv6:
            return ntohl(IN6_V4MAPPED_ADDR(&(((struct sockaddr_in6*)&addr)->sin6_addr)));
#endif // HAVE_IPV6
#ifdef SIMULATOR_ADDRESS
        case SIM:
            return ((unsigned long)((struct sockaddr_sim*)&addr)->addr);
#endif // SIMULATOR_ADDRESS
        default:
            DMSG(0, "NetworkAddress::EndIdentifier(): Invalid address type!\n");
            return INADDR_NONE;
    }
}  // end NetworkAddress:IPv4HostAddr()


unsigned long NetworkAddress::IPv4HostAddr() const
{
    switch(type)
    {
        case IPv4:
            return ntohl(((struct sockaddr_in *)&addr)->sin_addr.s_addr);

        default:
            DMSG(0, "NetworkAddress::IPv4HostAddr(): Non-IPv4 address!\n");
            return INADDR_NONE;
    }
}  // end NetworkAddress:IPv4HostAddr()


int NetworkAddress::CompareHostAddr(const NetworkAddress* theAddr) const
{
    switch(type)
    {
        case IPv4:
            return memcmp(&((struct sockaddr_in *)&addr)->sin_addr,
                          &((struct sockaddr_in *)&theAddr->addr)->sin_addr,
                          4);
#ifdef HAVE_IPV6
        case IPv6:
            return memcmp(&((struct sockaddr_in6*)&addr)->sin6_addr,
                          &((struct sockaddr_in6*)&theAddr->addr)->sin6_addr,
                          16);
#endif // HAVE_IPV6
#ifdef SIMULATOR_ADDRESS
        case SIM:
        {
            SIMADDR addr1 = ((struct sockaddr_sim*)&addr)->addr;
            SIMADDR addr2 = ((struct sockaddr_sim*)&(theAddr->addr))->addr;  
            if (addr1 < addr2)
                return -1;
            else if (addr1 > addr2)
                return 1;
            else
                return 0;
        }
#endif // SIMULATOR_ADDRESS
        default:
            DMSG(0, "NetworkAddress: CompareHostAddr(): Invalid address type!\n");
            return -1;
     }
}  // end NetworkAddress::CompareHostAddress()

// (TBD) Provide a mechanism for async lookup with optional user interaction

#ifdef HAVE_IPV6
bool NetworkAddress::LookupHostAddress(const char *name)
{
#ifdef SIMULATOR_ADDRESS
    // No DNS for simulations
    SIMADDR theAddr;
    if (1 == sscanf(name, "%i", &theAddr))
    {
        ((struct sockaddr_sim*)&addr)->addr = theAddr;
        type = SIM;
        len = sizeof(SIMADDR);
        return true;
    }
    else
    {
        fprintf(stderr, "NetworkAddress::LookupHostAddress() Invalid simulator address!\n");
        return false;
    }
#else
   // Use DNS to look it up
   // Get host address, looking up by hostname if necessary
        
#ifdef WIN32
    // Startup WinSock for name lookup
    if (!Win32Startup())
    {
            DMSG(0, "NetworkAddress::LookupHostAddress() Error initializing WinSock!\n");
            return false;
    }
#endif //WIN32
    addrinfo* addrInfoPtr;
    if(0 != getaddrinfo(name, NULL, NULL, &addrInfoPtr)) 
    {
        DMSG(0, "NetworkAddress::LookupHostAddress() getaddrinfo() error: %s\n", strerror(errno));
        return false;
    }
#ifdef WIN32
    Win32Cleanup();
#endif //WIN32

    memcpy((char*)&addr, addrInfoPtr->ai_addr, addrInfoPtr->ai_addrlen);
    if (addrInfoPtr->ai_family == PF_INET)
    {
        type = IPv4;
        len =  4; // IPv4 host addresses are always 4 bytes in length
        freeaddrinfo(addrInfoPtr);
        return true;
    }
    else if (addrInfoPtr->ai_family == PF_INET6)
    {
        type = IPv6;
        len = 16; // IPv6 host addresses are always 16 bytes in length
        freeaddrinfo(addrInfoPtr);
        return true;
    }
    else
    {
        freeaddrinfo(addrInfoPtr);
        DMSG(0, "NetworkAddress::LookupHostAddress() getaddrinfo() returned unsupported address family!\n");
        return false; // flase
    }
#endif // !SIMULATOR_ADDRESS
}  // end NetworkAddress::LookupHostAddress (IPv6)

#else

bool NetworkAddress::LookupHostAddress(const char *name)
{
#ifdef SIMULATOR_ADDRESS
    SIMADDR theAddr;
    if (1 == sscanf(name, "%i", &theAddr))
    {
        ((struct sockaddr_sim*)&addr)->addr = theAddr;
        type = SIM;
        len = sizeof(SIMADDR);
        return true;
    }
    else
    {
        fprintf(stderr, "NetworkAddress::LookupHostAddress() Invalid simulator address!\n");
        return false;
    }
#else
    // Use DNS to look it up
    struct sockaddr_in* addrPtr = (struct sockaddr_in*)&addr;
    // Get host address, looking up by hostname if necessary
    if (INADDR_NONE != (addrPtr->sin_addr.s_addr = inet_addr(name)))
    {
            addrPtr->sin_family = AF_INET;   /* user entered dotted decimal IP address */
    }
    else
    {
#ifdef WIN32
        // Startup WinSock for name lookup
        if (!Win32Startup())
        {
                DMSG(0, "NetworkAddress::LookupHostAddress() Error initializing WinSock!\n");
                return false;

        }
#endif // Win32
        struct hostent *hp = gethostbyname(name);
#ifdef WIN32
        Win32Cleanup();
#endif // WIN32
        if(hp)
        {
            addrPtr->sin_family = hp->h_addrtype;
            memcpy((char*)&addrPtr->sin_addr,  hp->h_addr,  hp->h_length);
        }
        else
        {
            DMSG(0, "NetworkAddress::LookupHostAddress() gethostbyname() error: %s\n",
                strerror(errno));
            return false;
        }
    }
    if (addrPtr->sin_family == AF_INET)
    {
        type = IPv4;
        len =  4;  // IPv4 addresses are always 4 bytes in length
    return true;
    }
    else
    {
        DMSG(0, "NetworkAddress: gethostbyname() returned unsupported address family!\n");
        return false;
    }
#endif // !SIMULATOR_ADDRESS
}  // end NetworkAddress::LookupHostAddress()
#endif //if/else HAVE_IPV6

#ifdef HAVE_IPV6
bool NetworkAddress::LookupHostName(char* buf, unsigned int maxLen) const
{
#ifdef WIN32
        if (!Win32Startup())
        {
                DMSG(0, "Error initializing WinSock!\n");
                return false;
        }
#endif // WIN32
    struct hostent* hp = NULL;
    switch (type)
    {
        case IPv4:
            hp = gethostbyaddr((char*)&(((struct sockaddr_in*)&addr)->sin_addr),
                               4, AF_INET);
            break;

        case IPv6:
            hp = gethostbyaddr((char*)&(((struct sockaddr_in*)&addr)->sin_addr),
                               16, AF_INET6);
         break;

#ifdef SIMULATOR_ADDRESS
        case SIM:
            if (buf) strncpy(buf, HostAddressString(), maxLen);
            return true;
#endif // SIMULATOR_ADDRESS

        default:
            DMSG(0, "NetworkAddress::LookupHostName(): Invalid address type!\n");
            return false;
    }  // end switch(type)
#ifdef WIN32
        Win32Cleanup();
#endif // WIN32
    if (hp)
    {
        unsigned int nameLen = 0;
        char** alias = hp->h_aliases;
        // Use first alias by default
        if (alias && *alias && buf)
        {
            strncpy(buf, *alias, maxLen);
            nameLen = strlen(buf);
            nameLen = MIN(nameLen, maxLen);
            alias++;
            // Try to find the fully-qualified name (longest alias containing '.')
            while (*alias)
            {
                if (strchr(*alias, '.') && (nameLen < strlen(*alias)))
                {
                    strncpy(buf, *alias, maxLen);
                    nameLen = strlen(buf);
                    nameLen = MIN(nameLen, maxLen);
                }
                alias++;
            }
        }
        return true;
    }
    else
    {
        DMSG(0, "NetworkAddress::LookupHostName() gethostbyaddr() error: %s\n",
                strerror(errno));
        return false;
    }
}  // end NetworkAddress::LookupHostName() (IPv6)

#else  // old IPv4-only support

// (TBD) Provide a mechanism for async lookup with optional user interaction
bool NetworkAddress::LookupHostName(char* buf, unsigned int maxLen) const 
{
    char* addrPtr = (char*)&((struct sockaddr_in*)&addr)->sin_addr;
    switch (type)
    {
        case IPv4:
        {
#ifdef WIN32
            if (!Win32Startup())
            {
                DMSG(0, "NetworkAddress::LookupHostName() error starting WInSock!\n");
                return false;
            }
#endif // WIN32
            struct hostent *hp = gethostbyaddr(addrPtr, sizeof(struct in_addr), AF_INET);
#ifdef WIN32
            Win32Cleanup();
#endif // WIN32
            if(hp)
            {
                unsigned int nameLen = 0;
                char** alias = hp->h_aliases;
                // Use first alias by default
                if (alias && *alias && buf)
                {
                    strncpy(buf, *alias, maxLen);
                    nameLen = strlen(buf);
                    nameLen = MIN(nameLen, maxLen);
                    alias++;
                    // Try to find fully-qualified name (longest alias containing '.')
                    while (*alias)
                    {
                        if (strchr(*alias, '.') && (nameLen < strlen(*alias)))
                        {
                            strncpy(buf, *alias, maxLen);
                            nameLen = strlen(buf);
                            nameLen = MIN(nameLen, maxLen);
                        }
                        alias++;
                    }
                }
                return true;
            }
            else
            {
                DMSG(0, "NetworkAddress::LookupHostName() gethostbyaddr() error: %s\n",
                        strerror(errno));
                return false;
            }
        }

#ifdef SIMULATOR_ADDRESS
        case SIM:
            if (buf) strncpy(buf, HostAddressString(), maxLen);
            return true;
#endif // SIMULATOR_ADDRESS

            default:
                DMSG(0, "NetworkAddress: LookupHostName(): unsupported address type!\n");
                return false;
    }
}  // end NetworkAddress::LookupHostName()  (IPv4 only)
#endif // if/else HAVE_IPV6

bool NetworkAddress::LookupLocalHostAddress(char* buf, unsigned int maxLen)
{
    // Try to get fully qualified host name if possible
    char hostName[256];
    hostName[0] = '\0';
    hostName[255] = '\0';
#ifdef WIN32
    if (!Win32Startup())
    {
        DMSG(0, "NetworkAddress::LookupLocalHostAddress() error startup WinSock!\n");
        return false;
    }
#endif // WIN32
    int result = gethostname(hostName, 255);
#ifdef WIN32
    Win32Cleanup();
#endif  // WIN32
    if (result)
    {
        DMSG(0, "NetworkAddress::LookupLocalHostAddress(): gethostname() error: %s\n", strerror(errno));
        return false;
    }
    else
    {
        bool lookupOK = LookupHostAddress(hostName);
        if (lookupOK)
        {
            // Invoke LookupHostName() to get fully qualified name and use that address
            LookupHostName(hostName, 255);
            lookupOK = LookupHostAddress(hostName);
            maxLen = MIN(255, maxLen);
            if (buf) strncpy(buf, hostName, maxLen);
        }
#ifdef UNIX
        if (!lookupOK || (0x7f000001 == EndIdentifier()))
        {
            // darn it! lookup failed or we got the loopback address (127.0.0.1)... 
            // So, try another approach using the address of 
            // the first interface in the index list
            if (!lookupOK)
            {
                UINT32 loopbackAddr = htonl(0x7f000001); 
                SetRawHostAddress(IPv4, (char*)&loopbackAddr, 4);  
            }
            struct if_nameindex* ifdx = if_nameindex();
            if (ifdx)
            {
                struct if_nameindex* ptr = ifdx;  
                while ((0x7f000001 == EndIdentifier()) && (0 != ptr->if_index))
                {
                    struct ifreq req;
                    strcpy(req.ifr_name, ptr->if_name);
                    req.ifr_addr.sa_family = AF_INET;
                    int fd = socket(PF_INET, SOCK_DGRAM, 0);
                    if (ioctl(fd, SIOCGIFADDR, &req) >= 0)
                    {
                        if (AF_INET == req.ifr_addr.sa_family)
                        {
                            SetRawHostAddress(IPv4, 
                                              (char*)&(((struct sockaddr_in*)&req.ifr_addr)->sin_addr),
                                              4); 
                        }   
                    }
                    else
                    {
                        DMSG(0, "NetworkAddress::LookupLocalAddress() ioctl(SIOCGIFADDR) error: %s\n",
                                strerror(errno));   
                    }
                    ptr++;
                }
                if_freenameindex(ifdx);
                return true; 
            }
            else
            {
                DMSG(0, "NetworkAddress::LookupLocalHostAddress() unable to determine local IP address\n");
                return false;   
            }
        }
#endif // UNIX
        return lookupOK;
    }
}  // end NetworkAddress::LookupLocalHostAddress()

bool NetworkAddress::IsEqual(const NetworkAddress* theAddr) const
{
    switch(type)
    {
        case IPv4:
            if ((IPv4 == theAddr->type) &&
                (((struct sockaddr_in*)&addr)->sin_port ==
                 ((struct sockaddr_in*)&(theAddr->addr))->sin_port) &&
                (((struct sockaddr_in*)&addr)->sin_addr.s_addr ==
                 ((struct sockaddr_in*)&(theAddr->addr))->sin_addr.s_addr))
                return true;
            else
                return false;

#ifdef HAVE_IPV6
        case IPv6:
                if ((IPv6 == theAddr->type) &&
                (((struct sockaddr_in6*)&addr)->sin6_port ==
                 ((struct sockaddr_in6*)&(theAddr->addr))->sin6_port) &&
                (((struct sockaddr_in6*)&addr)->sin6_addr.s6_addr ==
                 ((struct sockaddr_in6*)&(theAddr->addr))->sin6_addr.s6_addr))
                return true;
            else
                return false;
#endif // HAVE_IPV6 

#ifdef SIMULATOR_ADDRESS
        case SIM:
            if ((SIM == theAddr->type) &&
                (((struct sockaddr_sim*)&addr)->port ==
                 ((struct sockaddr_sim*)&(theAddr->addr))->port) &&
                (((struct sockaddr_sim*)&addr)->addr ==
                 ((struct sockaddr_sim*)&(theAddr->addr))->addr))
                return true;
            else
                return false;

#endif // SIMULATOR_ADDRESS

            default:
                DMSG(0, "NetworkAddress: IsEqual(): Invalid address type!\n");
                return false;
    }
}  // end NetworkAddress::IsEqual()

bool NetworkAddress::HostIsEqual(const NetworkAddress *theAddr) const
{
    switch(type)
    {
        case IPv4:
            if ((IPv4 == theAddr->type) &&
                (((struct sockaddr_in *)&addr)->sin_addr.s_addr ==
                 ((struct sockaddr_in *)&(theAddr->addr))->sin_addr.s_addr))
                return true;
            else
                return false;
#ifdef HAVE_IPV6
        case IPv6:
            if ((IPv6 == theAddr->type) &&
                (0 == memcmp(((struct sockaddr_in6*)&addr)->sin6_addr.s6_addr,
                             ((struct sockaddr_in6*)&(theAddr->addr))->sin6_addr.s6_addr,
                             4*sizeof(UINT32))))
                return true;
            else
                return false;
#endif // HAVE_IPV6 

#ifdef SIMULATOR_ADDRESS
        case SIM:
            if ((SIM == theAddr->type) &&
                (((struct sockaddr_sim*)&addr)->addr ==
                 ((struct sockaddr_sim*)&(theAddr->addr))->addr))
                return true;
            else
                return false;
#endif // SIMULATOR_ADDRESS

            default:
                DMSG(0, "NetworkAddress::HostIsEqual(): Invalid address type!\n");
                return false;
    }
}  // end NetworkAddress::HostIsEqual()




PortItem::PortItem(unsigned short min, unsigned short max)
        : port_min(min), port_max(max), prev(NULL), next(NULL)
{
}

bool PortItem::Overlap(unsigned short portMin, unsigned short portMax)
{
        if ( ((portMin <= (port_max+1)) &&
                  (portMax >= (port_min-1))) ||
                 ((port_min <= (portMax+1)) &&
                  (port_max >= (portMin-1))))
                return true;
        else
                return false;
}  // end PortItem::Overlap()

void PortItem::Merge(unsigned short portMin, unsigned short portMax)
{
        port_min = MIN(portMin, port_min);
        port_max = MAX(portMax, port_max);
}  // end PortItem::Merge()

unsigned short PortItem::NextValue(unsigned short index)
{
        if (index)
        {
                if (index >= port_max)
                        return 0;
                else
                        return (index+1);
        }
        else
        {
                return port_min;
        }
}  // end PortItem::NextValue()

PortList::PortList()
        : top(NULL), bottom(NULL)
{
}

PortList::~PortList()
{
        Destroy();
}

void PortList::Destroy()
{
        PortItem* next;
        while ((next = top))
        {
                Remove(next);
                delete next;
        }
} // end PortList::Destroy();

void PortList::Insert(PortItem* theItem)
{
        PortItem* next = top;
        while (next)
        {
                if (theItem->port_min < next->port_min)
                {
                        if ((theItem->prev = next->prev))
                                theItem->prev->next = theItem;
                        else
                                top = theItem;
                        if ((theItem->next = next->next))
                                theItem->next->prev = theItem;
                        else
                                bottom = theItem;
                        Collapse();
                        return;
                }
                else
                {
                        next = next->next;
                }
        }  // end while(next)
        if ((theItem->prev = bottom))
                bottom->next = theItem;
        else
                top = theItem;
        bottom = theItem;
        theItem->next = NULL;
        Collapse();
}  // end PortList::Insert()

void PortList::Append(PortItem* theItem)
{
        if ((theItem->prev = bottom))
                bottom->next = theItem;
        else
                top = theItem;
        bottom = theItem;
        theItem->next = NULL;
}  // end PortList::Append()

void PortList::Remove(PortItem* theItem)
{
        if (theItem->next)
                theItem->next->prev = theItem->prev;
        else
                bottom = theItem->prev;
        if (theItem->prev)
                theItem->prev->next = theItem->next;
        else
                top = theItem->next;
}  // end PortList::Remove()

PortItem* PortList::FindMatch(unsigned short value)
{
        PortItem* next = top;
        while (next)
        {
                if ((value >= next->port_min) &&
                        (value <= next->port_max))
                        return next;
                next = next->next;
        }
        return NULL;
} // end PortList::FindMatch();

PortItem* PortList::FindOverlap(unsigned short portMin, unsigned short portMax)
{
        PortItem* next = top;
        while (next)
        {
                if (next->Overlap(portMin, portMax)) return next;
                next = next->next;
        }  // end while(next);
        return NULL;
}  // end PortList::FindOverlap()

void PortList::Collapse()
{
        PortItem* next = top;
        while (next)
        {
                PortItem* x = next;
                next = next->next;
                if (next)
                {
                        if (next->Overlap(x->port_min, x->port_max))
                        {
                                next->Merge(x->port_min, x->port_max);
                                Remove(x);
                                delete x;
                        }
                }
        }
}  // end PortList::Collapse()

PortPool::PortPool()
{
}

bool PortPool::AddPorts(const char* portText)
{
        unsigned int len = strlen(portText);
        char* text = new char[len+1];
        if (!text)
        {
                DMSG(0, "PortPool::AddPorts() Error allocating memory!\n");
                return false;
        }
        strcpy(text, portText);
        char* start = text;
        while (start)
        {
                char* end = strchr(start, ',');
                if (end) *end++ = '\0';
                char* range = strchr(start, '-');
                if (range)
                        *range++ = '\0';
                else
                        range = start;
                unsigned short portMin = atoi(start);
                unsigned short portMax = atoi(range);
                if (!portMin || !portMax || (portMin > portMax))
                {
                        DMSG(0, "PortPool::AddPorts() Invalid specification error!\n");
                        delete text;
                        return false;
                }
                PortItem* theItem = pool.FindOverlap(portMin, portMax);
                if (theItem)
                {
                        DMSG(0, "Found overlap between: %u-%u and %u-%u\n",
                                 portMin, portMax, theItem->port_min, theItem->port_max);
                        theItem->Merge(portMin, portMax);
                        pool.Collapse();
                }
                else
                {
                        theItem = new PortItem(portMin, portMax);
                        pool.Insert(theItem);
                }
                start = end;
        }  // end while(start);
        delete text;
        return true;
}  // end PortPool::AddPorts()

unsigned short PortPool::GetAvailablePort()
{
        PortItem* next = pool.Top();
        unsigned short candidate = 0;
        while (next)
        {
                candidate = next->NextValue(candidate);
                if (candidate)
                {
                        if (!used.FindMatch(candidate))
                        {
                                PortItem* theItem = new PortItem(candidate, candidate);
                                if (theItem)
                                {
                                        used.Append(theItem);
                                        return candidate;
                                }
                                else
                                {
                                        DMSG(0, "PortPool::GetAvailablePort() Error allocating memory!\n");
                                        return 0;
                                }
                        }
                }
                else
                {
                        next = next->Next();
                }
        }  // end while(next)
        DMSG(0, "PortPool::GetAvailablePort() All assigned ports are in use!\n");
        return 0;
}  // end PortPool::GetAvailablePort();

void PortPool::ReturnPort(unsigned short thePort)
{
        PortItem* theItem = used.FindMatch(thePort);
        if (theItem)
        {
                used.Remove(theItem);
                delete theItem;
        }
}  // end PortPool::ReturnPort()

void PortPool::WriteContent(char* textBuffer, unsigned maxLen)
{
        maxLen--; // leave space for '\0'
        textBuffer[0] = '\0';
        char text[64];
        PortItem* next = pool.Top();
        unsigned int len = 0;
        while (next)
        {
                if (next->port_min == next->port_max)
                        sprintf(text, "%hu", next->port_min);
                else
                        sprintf(text, "%hu-%hu", next->port_min, next->port_max);
                if ((next = next->next))
                {
                        strcat(text, ", ");
                }
                len += strlen(text);
                if (len < maxLen)
                        strcat(textBuffer, text);
                else
                        break;
        }  // end while(next);
}  // end PortPool::Display()



