#include "protoAddress.h"
#include "protoDebug.h"   // for print out of warnings, etc

#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

#include <stdlib.h>  // for atoi()

ProtoAddress::ProtoAddress()
 : type(INVALID), length(0)
{
}

bool ProtoAddress::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 SIMULATE
            case SIM:
#ifdef NS2
                return (((struct sockaddr_sim *)&addr)->addr & 0x80000000 &&
			((struct sockaddr_sim *)&addr)->addr != 0xffffffff);
#endif // NS2
#ifdef OPNET  // For now our OPNET "model" uses broadcast address always
                return true;
#endif // OPNET
#endif // SIMULATE
            default:
                return false;
    }
}  // end IsMulticast()

bool ProtoAddress::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 SIMULATE
        case SIM:
            return false;
#endif // SIMULATE
        default:
            return false;
    }
}  // end ProtoAddress::IsLoopback()

bool ProtoAddress::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 SIMULATE
            case SIM:
                return false;
#endif // SIMULATE
            default:
                return false;
    }
}  // end IsUnspecified()

bool ProtoAddress::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
        case IPv4:
        default:
           return false;
    }
}  // end ProtoAddress::IsLinkLocal()

bool ProtoAddress::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
        case IPv4:
        default:
           return false;
    }
}  // end ProtoAddress::IsSiteLocal()

const char* ProtoAddress::GetHostString(char* buffer, unsigned int buflen) const
{
    static char altBuffer[256];
    altBuffer[255] = '\0';
    buflen = (NULL != buffer) ? buflen : 255;
#ifdef _UNICODE
    buflen /= 2;
#endif // _UNICODE
    buffer = (NULL != buffer) ? buffer : altBuffer;
    switch (type)
    {
#ifdef WIN32
        case IPv4:
        case IPv6:
        {
            // Startup WinSock for name lookup
            if (!Win32Startup())
            {
                DMSG(0, "ProtoAddress: GetHostString(): Error initializing WinSock!\n");
                return NULL;
            }
            unsigned long len = buflen;
            if (0 != WSAAddressToString((SOCKADDR*)&addr, sizeof(addr), NULL, (LPTSTR)buffer, &len))
                DMSG(0, "ProtoAddress::GetHostString() WSAAddressToString() error\n");
            Win32Cleanup();
#ifdef _UNICODE
            // Convert from unicode
            wcstombs(buffer, (wchar_t*)buffer, len);
#endif // _UNICODE
            // Get rid of trailing port number (TBD) test on Win32 IPV6
            char* ptr = strrchr(buffer, ':');
            if (ptr) *ptr = '\0';
            return buffer ? buffer : "(null)";
        }
#else 
#ifdef HAVE_IPV6
        case IPv4:
        {
            const char* result = inet_ntop(AF_INET, &((struct sockaddr_in*)&addr)->sin_addr, buffer, buflen);
            return result ? result : "(bad address)";
        }
        case IPv6:
        { 
            const char* result = inet_ntop(AF_INET6, &((struct sockaddr_in6*)&addr)->sin6_addr, buffer, buflen);
            return result ? result : "(bad address)";
        }
#else
        case IPv4:
            strncpy(buffer, inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr), buflen);
            return buffer;
#endif // HAVE_IPV6
#endif // if/else WIN32/UNIX
#ifdef SIMULATE
        case SIM:
        {
            char text[32];
            sprintf(text, "%u", ((struct sockaddr_sim*)&addr)->addr);
            strncpy(buffer, text, buflen);
            return buffer ? buffer : "(null)";
        }
#endif // SIMULATE
        default:
            DMSG(0, "ProtoAddress: GetHostString(): Invalid address type!\n");
            return "(invalid address)";
    }  // end switch(type)
}  // end ProtoAddress::GetHostString()

void ProtoAddress::PortSet(UINT16 thePort) {SetPort(thePort);}

void ProtoAddress::SetPort(UINT16 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 SIMULATE
                case SIM:
                ((struct sockaddr_sim*)&addr)->port = thePort;
                        break;
#endif // SIMULATE
            default:
                Reset(IPv4);
                SetPort(thePort);
                break;
    }
}  // end ProtoAddress::SetPort()

UINT16 ProtoAddress::GetPort() 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 SIMULATE
        case SIM:
            return (((struct sockaddr_sim*)&addr)->port);
#endif // SIMULATE
        default:
            return 0;  // port 0 is an invalid port
    }
}  // end ProtoAddress::Port()

void ProtoAddress::Reset(ProtoAddress::Type theType, bool zero)
{
    char value = zero ? 0x00 : 0xff;   
    switch (theType)
    {
        case IPv4:
            type = IPv4;
            length = 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;
            length  = 16;
            memset(&((struct sockaddr_in6*)&addr)->sin6_addr, value, 16);
            ((struct sockaddr_in6*)&addr)->sin6_family = AF_INET6;  
            break;
#endif // HAVE_IPV6
#ifdef SIMULATE
        case SIM:
            type = SIM;
            length = sizeof(SIMADDR);
            memset(&((struct sockaddr_sim*)&addr)->addr, value, sizeof(SIMADDR));
            break;
#endif // SIMULATE
        default:
            DMSG(0, "ProtoAddress::Init() Invalid address type!\n");
            break;
    }
    SetPort(0);
}  // end ProtoAddress::Init();

bool ProtoAddress::SetSockAddr(const struct sockaddr& theAddr)
{
    switch (theAddr.sa_family)
    {
        case AF_INET:
            ((struct sockaddr_in&)addr) = ((struct sockaddr_in&)theAddr);
            type = IPv4;
            length = 4;
            return true;
#ifdef HAVE_IPV6
        case AF_INET6:
            ((struct sockaddr_in6&)addr) = ((struct sockaddr_in6&)theAddr);
            type = IPv6;
            length = 16;
            return true;
#endif // HAVE_IPV6
        default:
            DMSG(4, "ProtoAddress::SetSockAddr() Invalid address type!\n");
            type = INVALID;
            length = 0;
            return false;
    }
}  // end ProtoAddress:SetAddress()

bool ProtoAddress::SetRawHostAddress(ProtoAddress::Type theType,
                                     const char*        buffer,
                                     UINT8              buflen)
{
    UINT16 thePort = GetPort();
    switch (theType)
    {       
        case IPv4:
            if (buflen > 4) return false;
            type = IPv4;
            length = 4;
            memset(&((struct sockaddr_in*)&addr)->sin_addr, 0, 4);
            memcpy(&((struct sockaddr_in*)&addr)->sin_addr, buffer, buflen);
            ((struct sockaddr_in*)&addr)->sin_family = AF_INET;
            break;
#ifdef HAVE_IPV6
        case IPv6:
            if (buflen > 16) return false;
            type = IPv6;
            length  = 16;
            memset(&((struct sockaddr_in6*)&addr)->sin6_addr, 0, 16);
            memcpy(&((struct sockaddr_in6*)&addr)->sin6_addr, buffer, buflen);
            ((struct sockaddr_in6*)&addr)->sin6_family = AF_INET6;
            break;
#endif // HAVE_IPV6  
#ifdef SIMULATE          
        case SIM:
            if (buflen > sizeof(SIMADDR)) return false;
            type = SIM;
            length = sizeof(SIMADDR);
            memset(&((struct sockaddr_sim*)&addr)->addr, 0, sizeof(SIMADDR));
            memcpy(&((struct sockaddr_sim*)&addr)->addr, buffer, buflen);
            break;
#endif // SIMULATE
        default:
            DMSG(0, "ProtoAddress::SetRawHostAddress() Invalid address type!\n");
            return false;
    }
    SetPort(thePort);
    return true;
}  // end ProtoAddress::SetRawHostAddress()
        
const char* ProtoAddress::GetRawHostAddress() 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 SIMULATE          
        case SIM:
            return ((char*)&((struct sockaddr_sim*)&addr)->addr);
#endif // SIMULATE
        default:
            DMSG(0, "ProtoAddress::RawHostAddress() Invalid address type!\n");
            return NULL;
    }
}  // end ProtoAddress::RawHostAddress()

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

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

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

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

UINT32 ProtoAddress::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 SIMULATE
        case SIM:
            return ((UINT32)((struct sockaddr_sim*)&addr)->addr);
#endif // SIMULATE
        default:
            DMSG(0, "ProtoAddress::EndIdentifier(): Invalid address type!\n");
            return INADDR_NONE;
    }
}  // end ProtoAddress:EndIdentifier()

bool ProtoAddress::HostIsEqual(const ProtoAddress& 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 SIMULATE
        case SIM:
            if ((SIM == theAddr.type) &&
                (((struct sockaddr_sim*)&addr)->addr ==
                 ((struct sockaddr_sim*)&(theAddr.addr))->addr))
                return true;
            else
                return false;
#endif // SIMULATE

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

int ProtoAddress::CompareHostAddr(const ProtoAddress& 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 SIMULATE
        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 // SIMULATE
        default:
            DMSG(0, "ProtoAddress: CompareHostAddr(): Invalid address type!\n");
            return -1;
     }
}  // end ProtoAddress::CompareHostAddress()

// (TBD) Provide a mechanism for async lookup with optional user interaction
bool ProtoAddress::ResolveFromString(const char* text)
{
    UINT16 thePort = GetPort();
#ifdef SIMULATE
    // Assume no DNS for simulations
    SIMADDR theAddr;
    if (1 == sscanf(text, "%i", &theAddr))
    {
        ((struct sockaddr_sim*)&addr)->addr = theAddr;
        type = SIM;
        length = sizeof(SIMADDR);
        SetPort(thePort);  // restore port number
        return true;
    }
    else
    {
        fprintf(stderr, "ProtoAddress::ResolveFromString() 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, "ProtoAddress::ResolveFromString() Error initializing WinSock!\n");
            return false;
    }
#endif //WIN32
#ifdef HAVE_IPV6  // Try to get address using getaddrinfo()
    addrinfo* addrInfoPtr;
    if(0 != getaddrinfo(text, NULL, NULL, &addrInfoPtr)) 
    {
        DMSG(4, "ProtoAddress::ResolveFromString() getaddrinfo() error: %s\n", GetErrorString());
        return false;
    }
#ifdef WIN32
    Win32Cleanup();
#endif // WIN32
    memcpy((char*)&addr, addrInfoPtr->ai_addr, addrInfoPtr->ai_addrlen);
    if (addrInfoPtr->ai_family == PF_INET)
    {
        type = IPv4;
        length =  4; // IPv4 host addresses are always 4 bytes in length
        freeaddrinfo(addrInfoPtr);
    }
    else if (addrInfoPtr->ai_family == PF_INET6)
    {
        type = IPv6;
        length = 16; // IPv6 host addresses are always 16 bytes in length
        freeaddrinfo(addrInfoPtr);
    }
    else
    {
        freeaddrinfo(addrInfoPtr);
        DMSG(0, "ProtoAddress::LookupHostAddress() getaddrinfo() returned unsupported address family!\n");
        return false; 
    }
    SetPort(thePort);  // restore port number
    return true;
#else   // Try to get address using inet_addr()/gethostbyname()
    // 1) is it a "dotted decimal" address?
    struct sockaddr_in* addrPtr = (struct sockaddr_in*)&addr;
    if (INADDR_NONE != (addrPtr->sin_addr.s_addr = inet_addr(text)))
    {
            addrPtr->sin_family = AF_INET;
    }
    else
    {
        // 2) Use "gethostbyname()" to lookup IPv4 address
        struct hostent *hp = gethostbyname(text);
#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, "ProtoAddress::ResolveFromString() gethostbyname() error: %s\n",
                    GetErrorString());
            return false;
        }
    }
    if (addrPtr->sin_family == AF_INET)
    {
        type = IPv4;
        length =  4;  // IPv4 addresses are always 4 bytes in length
        SetPort(thePort);  // restore port number
        return true;
    }
    else
    {
        DMSG(0, "ProtoAddress::ResolveFromString gethostbyname() returned unsupported address family!\n");
        return false;
    }    
#endif  // if/else HAVE_IPV6
#endif // !SIMULATE
}  // end ProtoAddress::ResolveFromString()

bool ProtoAddress::ResolveToName(char* buffer, unsigned int buflen) const
{
#ifdef WIN32
        if (!Win32Startup())
        {
                DMSG(0, "ProtoAddress::ResolveToName() 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;
#ifdef HAVE_IPV6
        case IPv6:
            hp = gethostbyaddr((char*)&(((struct sockaddr_in*)&addr)->sin_addr),
                               16, AF_INET6);
         break;
#endif // HAVE_IPV6
#ifdef SIMULATE
        case SIM:
            GetHostString(buffer, buflen);
            return true;
#endif // SIMULATE

        default:
            DMSG(0, "ProtoAddress::ResolveToName(): Invalid address type!\n");
            return false;
    }  // end switch(type)
#ifdef WIN32
        Win32Cleanup();
#endif // WIN32
        
    if (hp)
    {
        unsigned int nameLen = 0;
        unsigned int dotCount = 0;
        strncpy(buffer, hp->h_name, buflen);
        nameLen = strlen(hp->h_name);
        nameLen = nameLen < buflen ? nameLen : buflen;
        char* ptr = hp->h_name;
        while ((ptr = strchr(ptr, '.')))
        {
            ptr++;
            dotCount++;   
        }
        
        char** alias = hp->h_aliases;
        // Use first alias by default
        if (alias && *alias && buffer)
        {
            strncpy(buffer, *alias, buflen);
            nameLen = strlen(*alias);
            nameLen = nameLen < buflen ? nameLen : buflen;
            alias++;
            // Try to find the fully-qualified name 
            // (longest alias with most '.')
            while (*alias)
            {
                unsigned int dc = 0;
                ptr = *alias;
                while ((ptr = strchr(ptr, '.')))
                {
                    ptr++;
                    dc++;   
                }
                unsigned int alen = strlen(*alias);
                bool override = (dc > dotCount) || 
                                ((dc == dotCount) && (alen >nameLen));
                if (override)
                {
                    strncpy(buffer, *alias, buflen);
                    nameLen = alen;
                    dotCount = dc;
                    nameLen = nameLen < buflen ? nameLen : buflen;
                }
                alias++;
            }
        }
        return true;
    }
    else
    {
        DMSG(0, "ProtoAddress::ResolveToName() gethostbyaddr() error: %s\n",
                GetErrorString());
        return false;
    }
}  // end ProtoAddress::ResolveToName() 

bool ProtoAddress::ResolveLocalAddress(char* buffer, unsigned int buflen)
{
    UINT16 thePort = GetPort();  // save port number
    // Try to get fully qualified host name if possible
    char hostName[256];
    hostName[0] = '\0';
    hostName[255] = '\0';
#ifdef WIN32
    if (!Win32Startup())
    {
        DMSG(0, "ProtoAddress::ResolveLocalAddress() error startup WinSock!\n");
        return false;
    }
#endif // WIN32
    int result = gethostname(hostName, 255);
#ifdef WIN32
    Win32Cleanup();
#endif  // WIN32
    if (result)
    {
        DMSG(0, "ProtoAddress::ResolveLocalAddress(): gethostname() error: %s\n", GetErrorString());
        return false;
    }
    else
    {
        // Terminate at first '.' in name, if any (e.g. MacOS .local)
        char* dotPtr = strchr(hostName, '.');
        if (dotPtr) *dotPtr = '\0';
        bool lookupOK = ResolveFromString(hostName);
        if (lookupOK)
        {
            // Invoke ResolveToName() to get fully qualified name and use that address
            ResolveToName(hostName, 255);
            lookupOK = ResolveFromString(hostName);
            buflen = buflen < 255 ? buflen : 255;
            if (buffer) strncpy(buffer, hostName, buflen);
            SetPort(thePort);  // restore port number
        }
#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
            // (TBD) use ProtoSocket::GetInterfaceAddress method and make
            //       work for WIN32, too (move this to ProtoSocket?)
            if (!lookupOK)
            {
                UINT32 loopbackAddr = htonl(0x7f000001); 
                SetRawHostAddress(IPv4, (char*)&loopbackAddr, 4);  
            }
            struct if_nameindex* ifdx = if_nameindex();
            if (ifdx)
            {
                struct if_nameindex* ptr = ifdx;  
                int fd = socket(PF_INET, SOCK_DGRAM, 0);
                while ((0x7f000001 == EndIdentifier()) && (0 != ptr->if_index))
                {
                    struct ifreq req;
                    strcpy(req.ifr_name, ptr->if_name);
                    req.ifr_addr.sa_family = AF_INET;
                    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(4, "ProtoAddress::ResolveLocalAddress() ioctl(SIOCGIFADDR) error: %s\n",
                                GetErrorString());   
                    }
                    ptr++;
                }
                close(fd);
                if_freenameindex(ifdx);
                SetPort(thePort);  // restore port number
                ResolveToName(buffer, buflen);
                return true; 
            }
            else
            {
                DMSG(0, "ProtoAddress::ResolveLocalAddress() unable to determine local IP address\n");
                return false;   
            }
        }
#endif // UNIX
        return lookupOK;
    }
}  // end ProtoAddress::ResolveLocalAddress()

