wg: WireGuard config parser implementation

This commit is contained in:
Zac Gaetano 2026-05-06 19:18:10 -04:00
parent 3a35b8c2e9
commit b72eb3c7f1

158
src/wg/wgconfig.cpp Normal file
View file

@ -0,0 +1,158 @@
// src/wg/wgconfig.cpp
#include "wgconfig.h"
#include <algorithm>
#include <fstream>
#include <sstream>
#include <stdexcept>
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