wg: WireGuard config parser implementation
This commit is contained in:
parent
3a35b8c2e9
commit
b72eb3c7f1
1 changed files with 158 additions and 0 deletions
158
src/wg/wgconfig.cpp
Normal file
158
src/wg/wgconfig.cpp
Normal 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
|
||||
Loading…
Reference in a new issue