diff --git a/src/wg/wgconfig.cpp b/src/wg/wgconfig.cpp new file mode 100644 index 0000000..42477e2 --- /dev/null +++ b/src/wg/wgconfig.cpp @@ -0,0 +1,158 @@ +// src/wg/wgconfig.cpp +#include "wgconfig.h" + +#include +#include +#include +#include + +namespace wg { + +// ── helpers ─────────────────────────────────────────────────────────────────── + +static std::string trim(const std::string &s) { + auto a = s.find_first_not_of(" \t\r\n"); + if (a == std::string::npos) return {}; + auto b = s.find_last_not_of(" \t\r\n"); + return s.substr(a, b - a + 1); +} + +static std::string toLower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + return s; +} + +// ── parsing ─────────────────────────────────────────────────────────────────── + +bool Config::parse(const std::string &text, Config &out, std::string &errOut) { + out = {}; + std::string section; + std::istringstream ss(text); + std::string line; + int lineNo = 0; + + while (std::getline(ss, line)) { + ++lineNo; + // Strip inline comments + auto hash = line.find('#'); + if (hash != std::string::npos) line = line.substr(0, hash); + line = trim(line); + if (line.empty()) continue; + + if (line.front() == '[' && line.back() == ']') { + section = toLower(line.substr(1, line.size() - 2)); + continue; + } + + auto eq = line.find('='); + if (eq == std::string::npos) continue; + + std::string key = toLower(trim(line.substr(0, eq))); + std::string val = trim(line.substr(eq + 1)); + + if (section == "interface") { + if (key == "privatekey") out.m_privateKey = val; + else if (key == "address") out.m_address = val; + // DNS / MTU / etc. ignored — Artemis sets them via the OS API + } else if (section == "peer") { + if (key == "publickey") out.m_peerPubKey = val; + else if (key == "presharedkey") out.m_presharedKey = val; + else if (key == "endpoint") out.m_endpoint = val; + else if (key == "persistentkeepalive") { + try { out.m_keepalive = std::stoi(val); } catch (...) {} + } + else if (key == "allowedips") { + // May be a comma-separated list + std::istringstream csv(val); + std::string cidr; + while (std::getline(csv, cidr, ',')) { + cidr = trim(cidr); + if (!cidr.empty()) + out.m_allowedIPs.push_back(cidr); + } + } + } + } + + // Validate required fields + if (out.m_privateKey.empty()) { + errOut = "Missing [Interface] PrivateKey"; + return false; + } + if (out.m_address.empty()) { + errOut = "Missing [Interface] Address"; + return false; + } + if (out.m_peerPubKey.empty()) { + errOut = "Missing [Peer] PublicKey"; + return false; + } + if (out.m_endpoint.empty()) { + errOut = "Missing [Peer] Endpoint"; + return false; + } + + out.m_valid = true; + return true; +} + +bool Config::fromFile(const std::string &path, Config &out, std::string &errOut) { + std::ifstream f(path); + if (!f) { + errOut = "Cannot open file: " + path; + return false; + } + std::ostringstream ss; + ss << f.rdbuf(); + return parse(ss.str(), out, errOut); +} + +bool Config::fromString(const std::string &conf, Config &out, std::string &errOut) { + return parse(conf, out, errOut); +} + +// ── derived accessors ───────────────────────────────────────────────────────── + +std::string Config::addressIP() const { + auto slash = m_address.find('/'); + return (slash != std::string::npos) ? m_address.substr(0, slash) : m_address; +} + +int Config::addressPrefix() const { + auto slash = m_address.find('/'); + if (slash == std::string::npos) return 32; + try { return std::stoi(m_address.substr(slash + 1)); } + catch (...) { return 32; } +} + +// Endpoint formats: "host:port" or "[ipv6]:port" +std::string Config::endpointHost() const { + if (m_endpoint.empty()) return {}; + if (m_endpoint.front() == '[') { + // IPv6: [::1]:51820 + auto close = m_endpoint.find(']'); + if (close == std::string::npos) return m_endpoint; + return m_endpoint.substr(1, close - 1); + } + auto colon = m_endpoint.rfind(':'); + if (colon == std::string::npos) return m_endpoint; + return m_endpoint.substr(0, colon); +} + +int Config::endpointPort() const { + if (m_endpoint.empty()) return 51820; + std::string portStr; + if (m_endpoint.front() == '[') { + auto close = m_endpoint.find(']'); + if (close == std::string::npos || close + 2 >= m_endpoint.size()) return 51820; + portStr = m_endpoint.substr(close + 2); + } else { + auto colon = m_endpoint.rfind(':'); + if (colon == std::string::npos) return 51820; + portStr = m_endpoint.substr(colon + 1); + } + try { return std::stoi(portStr); } + catch (...) { return 51820; } +} + +} // namespace wg