/* Web Polygraph http://www.web-polygraph.org/ * (C) 2003-2006 The Measurement Factory * Licensed under the Apache License, Version 2.0 */ #include "pgl/pgl.h" #include #include "xstd/Assert.h" #include "xstd/gadgets.h" #include "base/AnyToString.h" #include "pgl/PglNetAddrParts.h" PglNetAddrParts::PglNetAddrParts(const String &anImage): theImage(anImage), theHostKind(hkNone), theSubnet(-1) { theBuf = theImage; (void)parse(); } // splits image into known parts and sets the address kind appropriately bool PglNetAddrParts::parse() { // the order is important return parseIfname() && determineHostKind() && parseSubnet() && parseHosts() && parsePorts(); } // also determined address kind bool PglNetAddrParts::parseIfname() { // We have a conflict with '::'. It could be either // an ifname, or part of an IPv6 address. We require // IPv6 addrs be delimited by []. So, if there is no // square bracket before the occurance of '::', we // have an ifname, not an IPv6 address. const char *columns = theBuf.str("::"); // ifname end? const char *bracket = theBuf.chr('['); // IPv6 start? if (columns && (!bracket || columns < bracket)) { // ifname theIfname = theBuf(0, columns - theBuf.cstr()); theBuf = theBuf(columns+2 - theBuf.cstr(), theBuf.len()); if (!theIfname.len()) { theError = "empty interface name at the start of " + theBuf; return false; } } return true; } bool PglNetAddrParts::determineHostKind() { // IPv6 must start with a '[' followed by a hex number if (theBuf.len() > 1 && theBuf[0] == '[' && isxdigit(theBuf[1])) { theHostKind = hkIPv6; return true; } // '[' is not allowed in IPv4 addresses, no need to skip if (theBuf.len() > 0 && isdigit(theBuf[0])) { theHostKind = hkIPv4; return true; } // may start with '[' but we do not care if (theBuf.len() > 0) { theHostKind = hkDomainName; return true; } theError = "empty domain name or IP address?"; return false; } bool PglNetAddrParts::parseSubnet() { if (const char *slash = theBuf.chr('/')) { if (theHostKind == hkDomainName) { theError = String("domain names cannot have subnets, " "but found one after ") + slash; return false; } const char *send = 0; if (!isInt(slash+1, theSubnet, &send, 10)) { theError = String("cannot parse subnet after ") + slash; return false; } if (*send) { theError = String("garbage following subnet near ") + send; return false; } Should(theHostKind == hkIPv4 || theHostKind == hkIPv6); const int maxSubnet = theHostKind == hkIPv4 ? 32 : 128; if (!(1 <= theSubnet && theSubnet <= maxSubnet)) { theError = String("subnet ") + AnyToString(theSubnet) + " is larger than maximum subnet for this IP type (" + AnyToString(maxSubnet) + ")"; return false; } theBuf = theBuf(0, slash - theBuf.cstr()); } return true; } bool PglNetAddrParts::parseHosts() { if (theHostKind == hkIPv6) { if (const char *hostsEnd = theBuf.chr(']')) { hostsEnd++; theHosts = theBuf(0, hostsEnd - theBuf.cstr()); theBuf = theBuf(hostsEnd - theBuf.cstr(), theBuf.len()); return true; } else { theError = "IPv6 address is not terminated by ']' near " + theBuf; return false; } } else { const char *ports = theBuf.chr(':'); if (!ports) ports = theBuf.cstr() + theBuf.len(); // end of string theHosts = theBuf(0, ports - theBuf.cstr()); if (!theHosts.len()) { theError = "empty network address near " + theBuf; return false; } theBuf = theBuf(ports - theBuf.cstr(), theBuf.len()); return true; } } bool PglNetAddrParts::parsePorts() { if (!theBuf.len() || (theBuf[0] == ':' && theBuf.len() > 1)) { thePorts = theBuf; theBuf = String(); return true; } else { theError = "garbage at the end of address near " + theBuf; return false; } } bool PglNetAddrParts::single() const { if (thePorts.chr('-')) return false; if (theHostKind == hkIPv6 || theHostKind == hkIPv4) return !theHosts.chr('-'); if (theHostKind == hkDomainName) return !theHosts.chr('['); return Should(false); } const String &PglNetAddrParts::host() const { Should(single()); return hosts(); } int PglNetAddrParts::port() const { if (!Should(single())) return -1; int port = -1; if (isInt(thePorts.cstr()+1, port)) return port; return -1; }