summaryrefslogtreecommitdiff
path: root/hscript/network.cc
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2019-10-12 06:43:55 -0500
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2019-10-12 06:43:55 -0500
commita94066d62a33e25ddb1eafce78627f2a3361f4ca (patch)
treeb01f295a529adefe276be8b28b80d9b8cf9e315a /hscript/network.cc
parent3def7b5e02e052ddc0993d2022d4f0f6ea2ae389 (diff)
downloadhorizon-a94066d62a33e25ddb1eafce78627f2a3361f4ca.tar.gz
horizon-a94066d62a33e25ddb1eafce78627f2a3361f4ca.tar.bz2
horizon-a94066d62a33e25ddb1eafce78627f2a3361f4ca.tar.xz
horizon-a94066d62a33e25ddb1eafce78627f2a3361f4ca.zip
hscript: Parse network addresses
Diffstat (limited to 'hscript/network.cc')
-rw-r--r--hscript/network.cc119
1 files changed, 116 insertions, 3 deletions
diff --git a/hscript/network.cc b/hscript/network.cc
index 9bcd9a8..f14381f 100644
--- a/hscript/network.cc
+++ b/hscript/network.cc
@@ -11,6 +11,7 @@
*/
#include <algorithm>
+#include <arpa/inet.h>
#include "network.hh"
#include "util/output.hh"
@@ -34,8 +35,10 @@ bool Network::execute(ScriptOptions) const {
Key *NetAddress::parseFromData(const std::string data, int lineno, int *errors,
int *warnings) {
long elements = std::count(data.cbegin(), data.cend(), ' ') + 1;
- std::string::size_type type_pos, addr_pos, prefix_pos, gw_pos;
+ std::string::size_type type_pos, addr_pos, prefix_pos, gw_pos, next_end;
std::string iface, type, addr, prefix, gw;
+ int real_prefix;
+ char addr_buf[16];
if(elements < 2) {
if(errors) *errors += 1;
@@ -49,7 +52,9 @@ Key *NetAddress::parseFromData(const std::string data, int lineno, int *errors,
iface = data.substr(0, type_pos);
/* theory: addr_pos could be npos, but that means 'read to end' anyway */
addr_pos = data.find_first_of(' ', type_pos + 1);
- type = data.substr(type_pos + 1, (addr_pos - type_pos - 1));
+ if(addr_pos == std::string::npos) next_end = std::string::npos;
+ else next_end = addr_pos - type_pos - 1;
+ type = data.substr(type_pos + 1, next_end);
/* ensure type is lower-cased, in case someone types 'DHCP' or 'SLAAC' */
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
@@ -96,7 +101,115 @@ Key *NetAddress::parseFromData(const std::string data, int lineno, int *errors,
return nullptr;
}
- return nullptr;
+ prefix_pos = data.find_first_of(' ', addr_pos + 1);
+ addr = data.substr(addr_pos + 1, (prefix_pos - addr_pos - 1));
+ gw_pos = data.find_first_of(' ', prefix_pos + 1);
+ if(gw_pos == std::string::npos) next_end = std::string::npos;
+ else next_end = gw_pos - prefix_pos - 1;
+ prefix = data.substr(prefix_pos + 1, next_end);
+ if(gw_pos != std::string::npos) {
+ gw = data.substr(gw_pos + 1);
+ }
+
+ if(addr.find(':') != std::string::npos) {
+ /* IPv6 */
+ if(::inet_pton(AF_INET6, addr.c_str(), &addr_buf) != 1) {
+ if(errors) *errors += 1;
+ output_error("installfile:" + std::to_string(lineno),
+ "netaddress: '" + addr + "' is not a valid IPv6 address",
+ "hint: a ':' was found, indicating this address is IPv6");
+ return nullptr;
+ }
+
+ try {
+ real_prefix = std::stoi(prefix);
+ } catch(const std::exception &) {
+ if(errors) *errors += 1;
+ output_error("installfile:" + std::to_string(lineno),
+ "netaddress: prefix length is not a number",
+ "prefix must be a decimal value between 1 and 128");
+ return nullptr;
+ }
+
+ if(real_prefix < 1 || real_prefix > 128) {
+ if(errors) *errors += 1;
+ output_error("installfile:" + std::to_string(lineno),
+ "netaddress: invalid IPv6 prefix length: " + prefix,
+ "prefix must be a decimal value between 1 and 128");
+ return nullptr;
+ }
+
+ if(gw.size() > 0 &&
+ ::inet_pton(AF_INET6, gw.c_str(), &addr_buf) != 1) {
+ if(errors) *errors += 1;
+ output_error("installfile:" + std::to_string(lineno),
+ "netaddress: '" + gw +
+ "' is not a valid IPv6 gateway",
+ "an IPv6 address must have an IPv6 gateway");
+ return nullptr;
+ }
+
+ return new NetAddress(lineno, iface, AddressType::Static, addr,
+ static_cast<uint8_t>(real_prefix), gw);
+ } else if(addr.find('.') != std::string::npos) {
+ /* IPv4 */
+ if(::inet_pton(AF_INET, addr.c_str(), &addr_buf) != 1) {
+ if(errors) *errors += 1;
+ output_error("installfile:" + std::to_string(lineno),
+ "netaddress: '" + addr + "' is not a valid IPv4 address");
+ return nullptr;
+ }
+
+ /* There are two kinds of prefixes for IPv4: prefix length, like IPv6,
+ * and mask, which is the old style used by i.e. Windows. */
+ if(::inet_pton(AF_INET, prefix.c_str(), &addr_buf) != 1) {
+ try {
+ real_prefix = std::stoi(prefix);
+ } catch(const std::exception &) {
+ if(errors) *errors += 1;
+ output_error("installfile:" + std::to_string(lineno),
+ "netaddress: can't parse prefix length/mask",
+ "a network mask or prefix length is required");
+ return nullptr;
+ }
+ } else {
+ uint32_t tmp;
+ memcpy(&tmp, addr_buf, 4);
+ tmp = ntohl(tmp);
+ real_prefix = 1;
+ while((tmp <<= 1) & 0x80000000) {
+ real_prefix++;
+ }
+ }
+
+ if(real_prefix < 1 || real_prefix > 32) {
+ if(errors) *errors += 1;
+ output_error("installfile:" + std::to_string(lineno),
+ "netaddress: invalid IPv4 prefix length: " + prefix,
+ "prefix must be between 1 and 32");
+ return nullptr;
+ }
+
+ if(gw.size() > 0 &&
+ ::inet_pton(AF_INET, gw.c_str(), &addr_buf) != 1) {
+ if(errors) *errors += 1;
+ output_error("installfile:" + std::to_string(lineno),
+ "netaddress: '" + gw +
+ "' is not a valid IPv4 gateway",
+ "an IPv4 address must have an IPv4 gateway");
+ return nullptr;
+ }
+
+ return new NetAddress(lineno, iface, AddressType::Static, addr,
+ static_cast<uint8_t>(real_prefix), gw);
+ } else {
+ /* IPvBad */
+ if(errors) *errors += 1;
+ output_error("installfile:" + std::to_string(lineno),
+ "netaddress: invalid address of unknown type",
+ "an IPv4 or IPv6 address is required");
+ return nullptr;
+ }
}
bool NetAddress::validate(ScriptOptions) const {