#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"
NetAddress::NetAddress()
{
fAddress.sin_family = AF_INET;
fAddress.sin_addr.s_addr = 0;
fAddress.sin_port = 0;
}
NetAddress::NetAddress(const sockaddr_in& address)
{
fAddress = address;
}
NetAddress::NetAddress(const NetAddress& address)
{
fAddress = address.fAddress;
}
void
NetAddress::SetIP(int32 address)
{
fAddress.sin_addr.s_addr = B_HOST_TO_BENDIAN_INT32(address);
}
int32
NetAddress::GetIP() const
{
return B_BENDIAN_TO_HOST_INT32(fAddress.sin_addr.s_addr);
}
void
NetAddress::SetPort(uint16 port)
{
fAddress.sin_port = B_HOST_TO_BENDIAN_INT32(port);
}
uint16
NetAddress::GetPort() const
{
return B_BENDIAN_TO_HOST_INT16(fAddress.sin_port);
}
void
NetAddress::SetAddress(const sockaddr_in& address)
{
fAddress = address;
}
const sockaddr_in&
NetAddress::GetAddress() const
{
return fAddress;
}
bool
NetAddress::IsLocal() const
{
if (fAddress.sin_addr.s_addr == INADDR_ANY
|| fAddress.sin_addr.s_addr == INADDR_BROADCAST) {
return false;
}
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
return false;
#if defined(HAIKU_TARGET_PLATFORM_DANO)
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
sockaddr_in addr = fAddress;
addr.sin_port = 0;
bool result = (bind(fd, (sockaddr*)&addr, sizeof(addr)) == 0);
#endif
closesocket(fd);
return result;
}
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);
}
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);
}
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()
{
}
private:
Locker fLock;
};
NetAddressResolver::NetAddressResolver()
{
_Lock();
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();
}
NetAddressResolver::~NetAddressResolver()
{
if (fResolver) {
_Lock();
if (sResolver->ReleaseReference() == 1) {
delete sResolver;
sResolver = NULL;
}
_Unlock();
}
}
status_t
NetAddressResolver::InitCheck() const
{
return (fResolver ? B_OK : B_NO_INIT);
}
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);
}
void
NetAddressResolver::_Lock()
{
while (atomic_add(&sLockCounter, 1) > 0) {
atomic_add(&sLockCounter, -1);
snooze(10000);
}
}
void
NetAddressResolver::_Unlock()
{
atomic_add(&sLockCounter, -1);
}
NetAddressResolver::Resolver* volatile NetAddressResolver::sResolver = NULL;
int32 NetAddressResolver::sLockCounter = 0;