/* * network.cc - Implementation of the Key classes for network configuration * libhscript, the HorizonScript library for * Project Horizon * * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved. * This code is licensed under the AGPL 3.0 license, as noted in the * LICENSE-code file in the root directory of this repository. * * SPDX-License-Identifier: AGPL-3.0-only */ #include #include #include "network.hh" #include "util/output.hh" using namespace Horizon::Keys; Key *Network::parseFromData(const std::string &data, int lineno, int *errors, int *warnings) { bool value; if(!BooleanKey::parse(data, "installfile:" + std::to_string(lineno), "network", &value)) { if(errors) *errors += 1; return nullptr; } return new Network(lineno, value); } bool Network::execute(ScriptOptions) const { return false; } 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, next_end; std::string iface, type, addr, prefix, gw; int real_prefix; char addr_buf[16]; if(elements < 2) { if(errors) *errors += 1; output_error("installfile:" + std::to_string(lineno), "netaddress: missing address type", "one of 'dhcp', 'slaac', 'static' required"); return nullptr; } type_pos = data.find_first_of(' '); 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); 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); if(!type.compare("dhcp")) { if(elements > 2) { if(errors) *errors += 1; output_error("installfile:" + std::to_string(lineno), "netaddress: address type 'dhcp' does not " "accept further elements"); return nullptr; } return new NetAddress(lineno, iface, AddressType::DHCP, "", 0, ""); } else if(!type.compare("slaac")) { if(elements > 2) { if(errors) *errors += 1; output_error("installfile:" + std::to_string(lineno), "netaddress: address type 'slaac' does not " "accept further elements"); return nullptr; } return new NetAddress(lineno, iface, AddressType::SLAAC, "", 0, ""); } else if(type.compare("static")) { if(errors) *errors += 1; output_error("installfile:" + std::to_string(lineno), "netaddress: invalid address type '" + type + "'", "one of 'dhcp', 'slaac', 'static' required"); return nullptr; } /* static address */ if(elements < 4) { if(errors) *errors += 1; output_error("installfile:" + std::to_string(lineno), "netaddress: address type 'static' requires at least " "an IP address and prefix length"); return nullptr; } if(elements > 5) { if(errors) *errors += 1; output_error("installfile:" + std::to_string(lineno), "netaddress: too many elements to address type 'static'"); 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(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(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 { /* possible to validate an address in the Installation Environment? */ return true; } bool NetAddress::execute(ScriptOptions) const { return false; }