/* * Worldvisions Weaver Software: * Copyright (C) 1997-2002 Net Integration Technologies, Inc. * * Device-independent and device-specific hardware/protocol address * classes that can store themselves efficiently as well as create a * printable string version of themselves. */ #ifndef _WIN32 #include #include #include #ifdef MACOS #include #endif #include #endif #include "wvaddr.h" #include #ifndef ARPHRD_IPSEC // From ipsec_tunnel #define ARPHRD_IPSEC 31 #endif // workaround for functions called sockaddr() -- oops. typedef struct sockaddr sockaddr_bin; /* A list of Linux ARPHRD_* types, one for each element of CapType. */ int WvEncap::extypes[] = { #ifdef _WIN32 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 #else // hardware encapsulation 0, // Unknown ARPHRD_LOOPBACK, 0, // Ethertap ARPHRD_ETHER, ARPHRD_ARCNET, ARPHRD_SLIP, ARPHRD_CSLIP, ARPHRD_PPP, ARPHRD_IPSEC, // protocol encapsulation AF_INET, // IPv4 AF_UNIX // Unix domain socket #endif }; /* Printable strings corresponding to each element of CapType */ const char WvEncap::strings[][20] = { // hardware encapsulation "Unknown", "Loopback", "Ethertap", "Ethernet", "ARCnet", "SLIP", "CSLIP", "PPP", "IPsec", // protocol encapsulation "IP", // IPv4 "Unix", // Unix domain socket }; /* Figure out the CapType corresponding to a Linux ARPHRD_* type or * sockaddr sa_family type. */ WvEncap::WvEncap(int extype) { for (int count=0; count < NUM_ENCAP_TYPES; count++) { if (extype == extypes[count]) { cap = (CapType)count; return; } } cap = Unknown; } /* Find the hash value of a WvAddr, for use with WvHashTable */ unsigned WvHash(const WvAddr &addr) { return addr.WvHash(); } /* Create an object of the appropriate WvAddr-derived class based on the * address and type stored in 'addr'. */ WvAddr *WvAddr::gen(struct sockaddr *addr) { WvEncap encap(addr->sa_family); switch (encap.cap) { case WvEncap::Loopback: return new WvStringAddr("Loopback", WvEncap::Loopback); case WvEncap::IPv4: return new WvIPPortAddr((sockaddr_in *)addr); #ifndef _WIN32 case WvEncap::ARCnet: return new WvARCnetAddr(addr); case WvEncap::Ethertap: case WvEncap::Ethernet: return new WvEtherAddr(addr); case WvEncap::IPsec: return new WvStringAddr("IPsec", WvEncap::IPsec); #endif default: return new WvStringAddr("Unknown", WvEncap::Unknown); } } bool WvAddr::isbroadcast() const { return false; // default is no support for broadcasts } const unsigned char *WvAddr::rawdata() const { return NULL; } size_t WvAddr::rawdata_len() const { return 0; } unsigned WvAddr::WvHash() const { unsigned hash = 0; const unsigned char *cptr, *raw = rawdata(); int len = rawdata_len(), width; if (!raw || !len) return 0; width = (sizeof(hash)*8 / len) + 1; for (cptr = raw; len; len--) hash = (hash << width) ^ *(cptr++); return hash; } bool WvAddr::comparator(const WvAddr *a2, bool first_pass) const { if (type() != a2->type()) return false; const unsigned char *raw1, *raw2; size_t len; len = rawdata_len(); if (len != a2->rawdata_len()) return false; raw1 = rawdata(); raw2 = a2->rawdata(); if (!raw1 && !raw2) return true; if (!raw1 || !raw2) return false; return !memcmp(raw1, raw2, len); } WvStringAddr::WvStringAddr(WvStringParm s, const WvEncap &_cap) : addr(s), cap(_cap) { } WvStringAddr::WvStringAddr(const struct sockaddr *_addr) : addr((char *)_addr->sa_data), cap(_addr->sa_family) { } WvStringAddr::~WvStringAddr() { // nothing to do } WvEncap WvStringAddr::encap() const { return cap; } const unsigned char *WvStringAddr::rawdata() const { return (const unsigned char *)(const char *)addr; } size_t WvStringAddr::rawdata_len() const { return strlen(addr); } sockaddr_bin *WvStringAddr::sockaddr() const { sockaddr_bin *sa = new sockaddr_bin; memset(sa, 0, sizeof(*sa)); strncpy(sa->sa_data, addr, sizeof(sa->sa_data)); return sa; } size_t WvStringAddr::sockaddr_len() const { return sizeof(sockaddr_bin); } WvString WvStringAddr::printable() const { return addr; } #ifndef _WIN32 /* create a WvEtherAddr from a printable string in the format: * AA:BB:CC:DD:EE:FF (six hex numbers, separated by colons) */ void WvEtherAddr::string_init(char const string[]) { char *endptr = NULL; unsigned char *cptr = binaddr; memset(binaddr, 0, ETHER_ADDR_LEN); for (unsigned int count=0; count < ETHER_ADDR_LEN; count++) { *cptr++ = strtoul(endptr ? endptr : string, &endptr, 16); if (!endptr || !*endptr || endptr==string) break; endptr++; } } WvEtherAddr::~WvEtherAddr() { // nothing to do } /* Generate a printable version of an ethernet address. */ WvString WvEtherAddr::printable() const { char s[ETHER_ADDR_LEN*3], *cptr = s; for (unsigned int count = 0; count < ETHER_ADDR_LEN; count++) { if (cptr > s) *cptr++ = ':'; sprintf(cptr, "%02X", binaddr[count]); cptr += 2; } *cptr = 0; return WvString("%s", s); // create a dynamic WvString } WvEncap WvEtherAddr::encap() const { return WvEncap(WvEncap::Ethernet); } // FF:FF:FF:FF:FF:FF is the ethernet broadcast address. bool WvEtherAddr::isbroadcast() const { for (unsigned int count = 0; count < ETHER_ADDR_LEN; count++) if (binaddr[count] != 0xFF) return false; return true; } const unsigned char *WvEtherAddr::rawdata() const { return binaddr; } size_t WvEtherAddr::rawdata_len() const { return ETHER_ADDR_LEN; } sockaddr_bin *WvEtherAddr::sockaddr() const { sockaddr_bin *sa = new sockaddr_bin; memset(sa, 0, sizeof(*sa)); sa->sa_family = ARPHRD_ETHER; memcpy(sa->sa_data, binaddr, ETHER_ADDR_LEN); return sa; } size_t WvEtherAddr::sockaddr_len() const { return sizeof(sockaddr_bin); } WvARCnetAddr::~WvARCnetAddr() { // nothing to do } WvString WvARCnetAddr::printable() const { WvString s(" "); sprintf(s.edit(), "%02X", binaddr); return s; } WvEncap WvARCnetAddr::encap() const { return WvEncap(WvEncap::ARCnet); } const unsigned char *WvARCnetAddr::rawdata() const { return &binaddr; } size_t WvARCnetAddr::rawdata_len() const { return 1; } sockaddr_bin *WvARCnetAddr::sockaddr() const { sockaddr_bin *sa = new sockaddr_bin; memset(sa, 0, sizeof(*sa)); sa->sa_family = ARPHRD_ARCNET; sa->sa_data[0] = binaddr; return sa; } size_t WvARCnetAddr::sockaddr_len() const { return sizeof(sockaddr_bin); } #endif //_WIN32 /* create an IP address from a dotted-quad string. We don't support * gethostname()-style lookups here, because they happen only synchronously. * Streams that need hostname lookups will have to do it themselves. */ void WvIPAddr::string_init(const char string[]) { const char *iptr, *nptr; unsigned char *cptr = binaddr; memset(binaddr, 0, 4); nptr = string; for (int count=0; count < 4 && nptr; count++) { iptr = nptr; nptr = strchr(iptr, '.'); if (nptr) nptr++; *cptr++ = strtol(iptr, NULL, 10); if (!nptr) break; } } WvIPAddr::~WvIPAddr() { // nothing to do } bool WvIPAddr::comparator(const WvAddr *a2, bool first_pass) const { if (a2->type() == WVIPADDR) return !memcmp(binaddr, ((WvIPAddr *)a2)->binaddr, sizeof(binaddr)); else if (first_pass) return a2->comparator(this, false); else { const unsigned char *raw1, *raw2; size_t len; len = rawdata_len(); if (len != a2->rawdata_len()) return false; raw1 = rawdata(); raw2 = a2->rawdata(); if (!raw1 && !raw2) return true; if (!raw1 || !raw2) return false; return !memcmp(raw1, raw2, len); } } /* Generate a printable version of an IP address. */ WvString WvIPAddr::printable() const { return WvString("%s.%s.%s.%s", binaddr[0], binaddr[1], binaddr[2], binaddr[3]); } /* AND two IP addresses together (handle netmasks) */ WvIPAddr WvIPAddr::operator& (const WvIPAddr &a2) const { unsigned char obin[4]; for (int count=0; count<4; count++) obin[count] = binaddr[count] & a2.binaddr[count]; return WvIPAddr(obin); } /* OR two IP addresses together (for broadcasts, etc) */ WvIPAddr WvIPAddr::operator| (const WvIPAddr &a2) const { unsigned char obin[4]; for (int count=0; count<4; count++) obin[count] = binaddr[count] | a2.binaddr[count]; return WvIPAddr(obin); } /* XOR two IP addresses together (for binary operations) */ WvIPAddr WvIPAddr::operator^ (const WvIPAddr &a2) const { unsigned char obin[4]; for (int count=0; count<4; count++) obin[count] = binaddr[count] ^ a2.binaddr[count]; return WvIPAddr(obin); } /* invert all the bits of an IP address (for useful binary operations) */ WvIPAddr WvIPAddr::operator~ () const { unsigned char obin[4]; for (int count=0; count<4; count++) obin[count] = ~binaddr[count]; return WvIPAddr(obin); } /* add an integer value to an IP address: * eg. 192.168.42.255 + 1 == 192.168.43.0 */ WvIPAddr WvIPAddr::operator+ (int n) const { uint32_t newad = htonl(ntohl(addr()) + n); return WvIPAddr((unsigned char *)&newad); } WvIPAddr WvIPAddr::operator- (int n) const { uint32_t newad = htonl(ntohl(addr()) - n); return WvIPAddr((unsigned char *)&newad); } WvEncap WvIPAddr::encap() const { return WvEncap(WvEncap::IPv4); } const unsigned char *WvIPAddr::rawdata() const { return binaddr; } size_t WvIPAddr::rawdata_len() const { return 4; } /* Generate a struct sockaddr (suitable for sendto()) from this IP * address. Don't forget to delete it after you're done! */ sockaddr_bin *WvIPAddr::sockaddr() const { sockaddr_in *sin = new sockaddr_in; memset(sin, 0, sizeof(*sin)); sin->sin_family = AF_INET; sin->sin_addr.s_addr = addr(); sin->sin_port = 0; return (sockaddr_bin *)sin; } size_t WvIPAddr::sockaddr_len() const { return sizeof(sockaddr_in); } WvIPNet::WvIPNet() { } WvIPNet::WvIPNet(const WvIPNet &_net) : WvIPAddr(_net), mask(_net.netmask()) { } // If the netmask is not specified, it will default to all 1's. void WvIPNet::string_init(const char string[]) { const char *maskptr; int bits; uint32_t imask; maskptr = strchr(string, '/'); if (!maskptr) { mask = WvIPAddr("255.255.255.255"); return; } maskptr++; if (strchr(maskptr, '.')) mask = WvIPAddr(maskptr); else { bits = atoi(maskptr); if (bits > 0) imask = htonl(~(((uint32_t)1 << (32-bits)) - 1)); // see below else imask = 0; mask = WvIPAddr((unsigned char *)&imask); } } WvIPNet::WvIPNet(const WvIPAddr &base, const WvIPAddr &_mask) : WvIPAddr(base), mask(_mask) { } WvIPNet::WvIPNet(const WvIPAddr &base, int bits) : WvIPAddr(base) { uint32_t imask; if (bits > 0) // <<32 is a bad idea! imask = htonl(~(((uint32_t)1 << (32-bits)) - 1)); else imask = 0; mask = WvIPAddr((unsigned char *)&imask); } WvIPNet::~WvIPNet() { // nothing to do } WvString WvIPNet::printable() const { if (bits() < 32) return WvString("%s/%s", network(), bits()); else return WvIPAddr::printable(); } unsigned WvIPNet::WvHash() const { return WvIPAddr::WvHash() + mask.WvHash(); } bool WvIPNet::comparator(const WvAddr *a2, bool first_pass) const { if (a2->type() == WVIPNET) return WvIPAddr::comparator(a2, false) && mask == ((WvIPNet *)a2)->mask; else if (first_pass) return a2->comparator(this, false); else return WvIPAddr::comparator(a2, false); } void WvIPNet::include(const WvIPNet &addr) { mask = mask & addr.mask & ~(*this ^ addr); } bool WvIPNet::includes(const WvIPNet &addr) const { return (addr.base() & netmask()) == network() && (addr.netmask() & netmask()) == netmask(); } int WvIPNet::bits() const { int bits = 0; uint32_t val = ntohl(mask.addr()); do { bits += val >> 31; } while ((val <<= 1) & (1 << 31)); return bits; } void WvIPNet::normalize() { if (bits() > 0) { uint32_t val = htonl(~(((uint32_t)1 << (32-bits())) - 1)); mask = WvIPAddr((unsigned char *)&val); } else mask = WvIPAddr(); // empty netmask } WvIPPortAddr::WvIPPortAddr() { port = 0; } WvIPPortAddr::WvIPPortAddr(const WvIPAddr &_ipaddr, uint16_t _port) : WvIPAddr(_ipaddr) { port = _port; } static bool all_digits(const char *s) { for (; *s; s++) if (!isdigit((unsigned char)*s)) return false; return true; } // If no port is specified (after a ':' or a space or a tab) it defaults to 0. void WvIPPortAddr::string_init(const char string[]) { // special case for an all-numeric string: it must be just a port, // with default address 0.0.0.0. if (all_digits(string)) { *this = WvIPAddr(); port = atoi(string); return; } const char *cptr = strchr(string, ':'); if (!cptr) cptr = strchr(string, ' '); if (!cptr) cptr = strchr(string, '\t'); // avoid calling getservbyname() if we can avoid it, since it's really // slow and people like to create WvIPPortAddr objects really often. if (cptr && strcmp(cptr+1, "0")) { port = atoi(cptr+1); if (!port) { struct servent *serv = getservbyname(cptr+1, NULL); if (serv) port = ntohs(serv->s_port); } } else port = 0; } WvIPPortAddr::WvIPPortAddr(uint16_t _port) : WvIPAddr("0.0.0.0") { port = _port; } WvIPPortAddr::WvIPPortAddr(const char string[], uint16_t _port) : WvIPAddr(string) { port = _port; } WvIPPortAddr::~WvIPPortAddr() { // nothing to do } /* Generate a printable version of an IP+Port Address. */ WvString WvIPPortAddr::printable() const { return WvString("%s:%s", WvIPAddr::printable(), WvString(port)); } /* Generate a struct sockaddr (suitable for sendto()) from this IP+Port * address. Don't forget to delete it after you're done! */ sockaddr_bin *WvIPPortAddr::sockaddr() const { sockaddr_in *sin = (sockaddr_in *)WvIPAddr::sockaddr(); sin->sin_port = htons(port); return (sockaddr_bin *)sin; } unsigned WvIPPortAddr::WvHash() const { return WvIPAddr::WvHash() + port; } bool WvIPPortAddr::comparator(const WvAddr *a2, bool first_pass) const { if (a2->type() == WVIPPORTADDR) return WvIPAddr::comparator(a2, false) && port == ((WvIPPortAddr *)a2)->port; else if (first_pass) return a2->comparator(this, false); else return WvIPAddr::comparator(a2, false); } #ifndef _WIN32 WvUnixAddr::WvUnixAddr(const char *_sockname) : sockname(_sockname) { if (!sockname) sockname = "/"; } WvUnixAddr::WvUnixAddr(WvStringParm _sockname) : sockname(_sockname) { if (!sockname) sockname = "/"; } WvUnixAddr::WvUnixAddr(const WvUnixAddr &_addr) : sockname(_addr.sockname) { // nothing else needed } WvUnixAddr::~WvUnixAddr() { // nothing special } WvString WvUnixAddr::printable() const { return sockname; } WvEncap WvUnixAddr::encap() const { return WvEncap::Unix; } /* don't forget to delete the returned object when you're done! */ sockaddr_bin *WvUnixAddr::sockaddr() const { sockaddr_un *addr = new sockaddr_un; memset(addr, 0, sizeof(*addr)); addr->sun_family = AF_UNIX; size_t max = strlen(sockname); if (max > sizeof(addr->sun_path) - 2) // appease valgrind max = sizeof(addr->sun_path) - 2; strncpy(addr->sun_path, sockname, max); if (addr->sun_path[0] == '@') // user wants an "abstract" socket addr->sun_path[0] = 0; // leading byte should be nul return (sockaddr_bin *)addr; } size_t WvUnixAddr::sockaddr_len() const { sockaddr_un *fake; size_t max = sizeof(fake->sun_path); size_t val = strlen(sockname); if (val > max) val = max; return sizeof(fake->sun_family) + val; } const unsigned char *WvUnixAddr::rawdata() const { return (const unsigned char *)(const char *)sockname; } size_t WvUnixAddr::rawdata_len() const { return strlen(sockname); } #endif // _WIN32