root/src/add-ons/kernel/file_systems/netfs/shared/NetAddress.cpp
// NetAddress.cpp

#include <new>
#include <compatibility/bsd/features.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>

#if defined(HAIKU_TARGET_PLATFORM_DANO) || defined(HAIKU_TARGET_PLATFORM_DANO)
#       include <net/route.h>
#       include <sys/sockio.h>
#       include <stdlib.h>
#endif

#include <AutoLocker.h>
#include <ByteOrder.h>
#include <HashString.h>
#include <Referenceable.h>

#include "Compatibility.h"
#include "Locker.h"
#include "NetAddress.h"

// constructor
NetAddress::NetAddress()
{
        fAddress.sin_family = AF_INET;
        fAddress.sin_addr.s_addr = 0;
        fAddress.sin_port = 0;
}

// constructor
NetAddress::NetAddress(const sockaddr_in& address)
{
        fAddress = address;
}

// copy constructor
NetAddress::NetAddress(const NetAddress& address)
{
        fAddress = address.fAddress;
}

// SetIP
void
NetAddress::SetIP(int32 address)
{
        fAddress.sin_addr.s_addr = B_HOST_TO_BENDIAN_INT32(address);
}

// GetIP
int32
NetAddress::GetIP() const
{
        return B_BENDIAN_TO_HOST_INT32(fAddress.sin_addr.s_addr);
}

// SetPort
void
NetAddress::SetPort(uint16 port)
{
        fAddress.sin_port = B_HOST_TO_BENDIAN_INT32(port);
}

// GetPort
uint16
NetAddress::GetPort() const
{
        return B_BENDIAN_TO_HOST_INT16(fAddress.sin_port);
}

// SetAddress
void
NetAddress::SetAddress(const sockaddr_in& address)
{
        fAddress = address;
}

// GetAddress
const sockaddr_in&
NetAddress::GetAddress() const
{
        return fAddress;
}

// IsLocal
bool
NetAddress::IsLocal() const
{
        // special address?
        if (fAddress.sin_addr.s_addr == INADDR_ANY
                || fAddress.sin_addr.s_addr == INADDR_BROADCAST) {
                return false;
        }
        // create a socket and try to bind it to a port of this address
        int fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (fd < 0)
                return false;
#if defined(HAIKU_TARGET_PLATFORM_DANO)
        // BONE does allow you to bind to any address!
        // Therefore, we iterate over all routes, and see if there are any local
        // ones for this address.

        bool result = false;
        uint32 count;
        if (ioctl(fd, SIOCGRTSIZE, &count) == 0) {
                route_req_t* routes = (route_req_t*)malloc(count * sizeof(route_req_t));
                if (routes != NULL) {
                        route_table_req table;
                        table.rrtp = routes;
                        table.len = count * sizeof(route_req_t);
                        table.cnt = count;
                        if (ioctl(fd, SIOCGRTTABLE, &table) == 0) {
                                for (uint32 i = 0; i < table.cnt; i++) {
                                        if ((routes[i].flags & RTF_LOCAL) == 0)
                                                continue;
                                        if (((sockaddr_in*)&routes[i].dst)->sin_addr.s_addr
                                                        == fAddress.sin_addr.s_addr) {
                                                result = true;
                                                break;
                                        }
                                }
                        }
                        free(routes);
                }
        }
#else
        // bind it to a port
        sockaddr_in addr = fAddress;
        addr.sin_port = 0;
        bool result = (bind(fd, (sockaddr*)&addr, sizeof(addr)) == 0);
#endif
        closesocket(fd);

        return result;
}


// GetString
status_t
NetAddress::GetString(HashString* string, bool includePort) const
{
        if (!string)
                return B_BAD_VALUE;
        char buffer[32];
        uint32 ip = GetIP();
        if (includePort) {
                sprintf(buffer, "%" B_PRIu32 ".%" B_PRIu32 ".%" B_PRIu32 ".%" B_PRIu32
                        ":%hu", ip >> 24, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff,
                        GetPort());
        } else {
                sprintf(buffer, "%" B_PRIu32 ".%" B_PRIu32 ".%" B_PRIu32 ".%" B_PRIu32,
                        ip >> 24, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
        }
        return (string->SetTo(buffer) ? B_OK : B_NO_MEMORY);
}

// GetHashCode
uint32
NetAddress::GetHashCode() const
{
        return (fAddress.sin_addr.s_addr * 31 + fAddress.sin_port);
}

// =
NetAddress&
NetAddress::operator=(const NetAddress& address)
{
        fAddress = address.fAddress;
        return *this;
}

// ==
bool
NetAddress::operator==(const NetAddress& address) const
{
        return (fAddress.sin_addr.s_addr == address.fAddress.sin_addr.s_addr
                && fAddress.sin_port == address.fAddress.sin_port);
}

// !=
bool
NetAddress::operator!=(const NetAddress& address) const
{
        return !(*this == address);
}


// #pragma mark -

// Resolver
class NetAddressResolver::Resolver : public BReferenceable {
public:
        Resolver()
                : BReferenceable(),
                  fLock()
        {
        }

        status_t InitCheck() const
        {
                return fLock.InitCheck();
        }

        status_t GetHostAddress(const char* hostName, NetAddress* address)
        {
                AutoLocker<Locker> _(fLock);
                struct hostent* host = gethostbyname(hostName);
                if (!host)
                        return h_errno;
                if (host->h_addrtype != AF_INET || !host->h_addr_list[0])
                        return B_BAD_VALUE;
                sockaddr_in addr;
                addr.sin_family = AF_INET;
                addr.sin_port = 0;
                addr.sin_addr = *(in_addr*)host->h_addr_list[0];
                *address = addr;
                return B_OK;
        }

protected:
        virtual void LastReferenceReleased()
        {
                // don't delete
        }

private:
        Locker  fLock;
};

// constructor
NetAddressResolver::NetAddressResolver()
{
        _Lock();
        // initialize static instance, if not done yet
        if (sResolver) {
                sResolver->AcquireReference();
                fResolver = sResolver;
        } else {
                sResolver = new(std::nothrow) Resolver;
                if (sResolver) {
                        if (sResolver->InitCheck() != B_OK) {
                                delete sResolver;
                                sResolver = NULL;
                        }
                }
                fResolver = sResolver;
        }
        _Unlock();
}

// destructor
NetAddressResolver::~NetAddressResolver()
{
        if (fResolver) {
                _Lock();
                if (sResolver->ReleaseReference() == 1) {
                        delete sResolver;
                        sResolver = NULL;
                }
                _Unlock();
        }
}

// InitCheck
status_t
NetAddressResolver::InitCheck() const
{
        return (fResolver ? B_OK : B_NO_INIT);
}

// GetAddress
status_t
NetAddressResolver::GetHostAddress(const char* hostName, NetAddress* address)
{
        if (!fResolver)
                return B_NO_INIT;
        if (!hostName || !address)
                return B_BAD_VALUE;
        return fResolver->GetHostAddress(hostName, address);
}

// _Lock
void
NetAddressResolver::_Lock()
{
        while (atomic_add(&sLockCounter, 1) > 0) {
                atomic_add(&sLockCounter, -1);
                snooze(10000);
        }
}

// _Unlock
void
NetAddressResolver::_Unlock()
{
        atomic_add(&sLockCounter, -1);
}


// sResolver
NetAddressResolver::Resolver* volatile NetAddressResolver::sResolver = NULL;

// sLockCounter
int32 NetAddressResolver::sLockCounter = 0;