/* 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