/* Web Polygraph       http://www.web-polygraph.org/
 * (C) 2003-2006 The Measurement Factory
 * Licensed under the Apache License, Version 2.0 */

#include "xstd/xstd.h"

#include "xstd/h/string.h"
#include "xstd/h/sys/socket.h"
#include "xstd/h/iostream.h"
#include "xstd/h/sstream.h"

#include "xstd/Assert.h"
#include "xstd/InAddress.h"


InAddress::InAddress(): theFamily(0), isKnown(false) {
}

InAddress::InAddress(const sockaddr_storage &s): theFamily(0), isKnown(false) {
	init(s);
}

InAddress::InAddress(const sockaddr_in &s):
	theIPvFour(s.sin_addr), theFamily(AF_INET), isKnown(true) {
}

InAddress::InAddress(const sockaddr_in6 &s):
	theIPvSix(s.sin6_addr), theFamily(AF_INET6), isKnown(true) {
}

InAddress::InAddress(const sockaddr &s): theFamily(0), isKnown(false) {
	init((const sockaddr_storage &)s);
}

InAddress::InAddress(const in_addr &a):
	theIPvFour(a), theFamily(AF_INET), isKnown(true) {
}

InAddress::InAddress(const in6_addr &a):
	theIPvSix(a), theFamily(AF_INET6), isKnown(true) {
}

void InAddress::init(const sockaddr_storage &s) {
	theFamily = s.ss_family;
	known(true);
	switch (theFamily) {
		case AF_INET:
			theIPvFour = ((const sockaddr_in &)s).sin_addr;
			break;
		case AF_INET6:
			theIPvSix = ((const sockaddr_in6 &)s).sin6_addr;
			break;
		case 0:
			known(false);
			break;
		default:
			Should(false);
			known(false);
			break;
	}
}

unsigned char *InAddress::rawOctets() const {
	switch(theFamily) {
		case AF_INET: return (unsigned char *) &theIPvFour;
		case AF_INET6: return (unsigned char *) &theIPvSix;
	}
	Assert(!theFamily); // just to distinguish unset from unknown
	Assert(false);
	return 0;
}

// compare() assumes len() does not depend on anything but theFamily
int InAddress::len() const {
	switch(theFamily) {
		case AF_INET: return 4;
		case AF_INET6: return 16;
	}
	return 0;
}

// a.b.c.d = 0.1.2.3
int InAddress::octet(int idx) const {
	Assert(0 <= idx && idx < len());
	return rawOctets()[idx];
}

// octet number couting from reverse/right side
// a.b.c.d = 3.2.1.0
int InAddress::roctet(int idx) const {
	return octet(len()-1-idx);
}

InAddress InAddress::broadcast(const InAddress &netmask) const {
	Assert(theFamily == netmask.theFamily);
	InAddress res(*this);
	unsigned char *ap = res.rawOctets();
	const unsigned char *mp = netmask.rawOctets();
	const int l = len();
	for (int i = 0; i < l; ++i)
		*(ap+i) |= ~(*(mp+i));
	return res;
}

sockaddr_in InAddress::sockAddrFour(int port) const {
	Assert(theFamily == AF_INET);
	sockaddr_in s;
	memset(&s, 0, sizeof(s));
	s.sin_family = theFamily;
	s.sin_addr = theIPvFour;
	s.sin_port = htons(port >= 0 ? port : 0);
#	if HAVE_SA_LEN
		s.sin_len = sizeof(s);
#	endif
	return s;
}

sockaddr_in6 InAddress::sockAddrSix(int port) const {
	Assert(theFamily == AF_INET6);
	sockaddr_in6 s;
	memset(&s, 0, sizeof(s));
	s.sin6_family = theFamily;
	s.sin6_addr = theIPvSix;
	s.sin6_port = htons(port >= 0 ? port : 0);
#	if HAVE_SA_LEN
		s.sin6_len = sizeof(s);
#	endif
	return s;
}

sockaddr_storage InAddress::sockAddr(int port) const {
	sockaddr_storage s;
	memset(&s, 0, sizeof(s));
	switch(s.ss_family = theFamily) {
		case AF_INET: {
			((sockaddr_in&)s) = sockAddrFour(port);
			break;
		}
		case AF_INET6: {
			((sockaddr_in6&)s) = sockAddrSix(port);
			break;
		}
		default: {
			Assert(false);
			break;
		}
	}
	return s;
}

int InAddress::pton(const char *p) {
	int rc = 0;
	if (*p == '[') {
		//
		// XXX ugly memory copy to remove [ ]
		//
		char copy[64];
		strncpy(copy, p+1, 64);
		strtok(copy, "]");
		rc = inet_pton(theFamily = AF_INET6, copy, &theIPvSix);
	} else {
		rc = inet_pton(theFamily = AF_INET, p, &theIPvFour);
	}

	if (rc)
		isKnown = true;
	else
		cerr << here << "inet_pton() can't parse " << p << endl;

	return rc;
}

#define INADDRESS_MAX_NTOP_SZ 64
const char *InAddress::rawImage() const {
	if (known()) {
		static char buf[INADDRESS_MAX_NTOP_SZ];
		inet_ntop(theFamily,
			rawOctets(),
			buf,
			INADDRESS_MAX_NTOP_SZ-1);
		return buf;
	} else {
		return "";
	}
}

const char *InAddress::image() const {
	if (theFamily == AF_INET6) {
		static char buf[INADDRESS_MAX_NTOP_SZ];
		ofixedstream os(buf, sizeof(buf));
		os << '[' << rawImage() << ']' << ends;
		return buf;
	}
	return rawImage();
}
#undef INADDRESS_MAX_NTOP_SZ


unsigned int InAddress::hash0() const {
	unsigned int h = 0;
	int i = len();
	while (i--) {
		h += octet(i);
		h += (h<<10);
		h ^= (h>>6);
	} 
	h += (h<<3);
	h ^= (h>>11);
	h += (h<<15); 
	return h;
}

unsigned int InAddress::hash1() const {
	return roctet(0) + (roctet(1) << 8);
}

bool InAddress::operator == (const InAddress &other) const {
	return compare(other) == 0;
}

int InAddress::compare(const InAddress &other) const {
	if (theFamily != other.theFamily)
		return theFamily > other.theFamily ? +1 : -1;

	return memcmp(rawOctets(), other.rawOctets(), len());
}

InAddress InAddress::NetMask(int bitCount) {
	InAddress A = InAddress::IPvFour();
	A.isKnown = true;
	Assert(0 <= bitCount && bitCount <= A.len()*8);
	unsigned char *cp = A.rawOctets();
	memset(cp, 0x00, A.len());
	for (; bitCount > 7; bitCount -= 8)
		*cp++ = 0xff;
	if (bitCount > 0)
		*cp = 0xff << (8 - bitCount);
	return A;
}

bool InAddress::sameSubnet(const InAddress &other, int bitCount) const {
	if (theFamily != other.theFamily)
		return false;
	int sz_bits = len() << 3;
	if (bitCount < 0 || bitCount > sz_bits)
		bitCount = sz_bits;
	unsigned char *cp1 = rawOctets();
	unsigned char *cp2 = other.rawOctets();
	for (; bitCount > 7; bitCount -= 8)
		if (*cp1++ != *cp2++)
			return false;
	return (*cp1 >> (8 - bitCount)) == (*cp2 >> (8 - bitCount));
}

int InAddress::prefixlen() const {
	// assume 'rawOctets' is a netmask
	// (lots of ones followed by lots of zeroes)
	int pfx = 0;
	int l = len();
	for (const unsigned char *cp = rawOctets(); l && *cp; l--, cp++) {
		switch (*cp) {
			case 0x80: pfx += 1; break;
			case 0xC0: pfx += 2; break;
			case 0xE0: pfx += 3; break;
			case 0xF0: pfx += 4; break;
			case 0xF8: pfx += 5; break;
			case 0xFC: pfx += 6; break;
			case 0xFE: pfx += 7; break;
			case 0xFF: pfx += 8; break;
			default: Assert(false);
		}
	}
	return pfx;
}


InAddress InAddress::IPvFour() {
	InAddress a;
	a.theFamily = AF_INET;
	a.theIPvFour.s_addr = INADDR_ANY;
	return a;
}

InAddress InAddress::IPvSix() {
	InAddress a;
	a.theFamily = AF_INET6;
	a.theIPvSix = in6addr_any;
	return a;
}

// these two port manipulation methods assume IPv4, but that is safe
// because all sockaddr_* structures have port at the same offset
unsigned short InAddress::GetPort(const sockaddr_storage &s) {
	const struct sockaddr_in &sin = (const struct sockaddr_in&)s;
	return ntohs(sin.sin_port);
}

void InAddress::SetPort(sockaddr_storage &s, int port) {
	struct sockaddr_in &sin = (struct sockaddr_in &)s;
	sin.sin_port = htons(port >= 0 ? port : 0);
}


syntax highlighted by Code2HTML, v. 0.9.1