initial commit
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
#include "nm_connection.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <random>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
namespace radiantwave::network {
|
||||
|
||||
// ─── UUID Generation (standard RFC 4122 v4) ─────────────────────────────
|
||||
|
||||
namespace {
|
||||
|
||||
std::string generateUuid() {
|
||||
static std::random_device rd;
|
||||
static std::mt19937_64 gen(rd());
|
||||
static std::uniform_int_distribution<uint64_t> dis;
|
||||
|
||||
uint8_t bytes[16];
|
||||
uint64_t v1 = dis(gen);
|
||||
uint64_t v2 = dis(gen);
|
||||
std::memcpy(bytes, &v1, 8);
|
||||
std::memcpy(bytes + 8, &v2, 8);
|
||||
|
||||
bytes[6] = (bytes[6] & 0x0F) | 0x40; // Version 4
|
||||
bytes[8] = (bytes[8] & 0x3F) | 0x80; // Variant RFC 4122
|
||||
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
char buf[37];
|
||||
int p = 0;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
if (i == 4 || i == 6 || i == 8 || i == 10) {
|
||||
buf[p++] = '-';
|
||||
}
|
||||
buf[p++] = hex[(bytes[i] >> 4) & 0x0F];
|
||||
buf[p++] = hex[bytes[i] & 0x0F];
|
||||
}
|
||||
buf[36] = '\0';
|
||||
return std::string(buf, 36);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// ─── Connection Builder ────────────────────────────────────────────────
|
||||
|
||||
ConnectionSettings buildConnectionSettings(
|
||||
const std::string& ssid,
|
||||
const std::string& password,
|
||||
SecurityType security,
|
||||
const std::string& interface_name
|
||||
) {
|
||||
// Validate SSID
|
||||
if (!isValidSsid(ssid)) {
|
||||
throw std::invalid_argument("Invalid SSID: must be 1-32 bytes with no null characters");
|
||||
}
|
||||
|
||||
// Validate password if required
|
||||
if (requiresPassword(security)) {
|
||||
std::string error = validatePassword(password, security);
|
||||
if (!error.empty()) {
|
||||
throw std::invalid_argument(error);
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionSettings settings;
|
||||
|
||||
// ── connection section ─────────────────────────────────────────────
|
||||
settings["connection"] = {
|
||||
{"id", ssid},
|
||||
{"type", std::string("802-11-wireless")},
|
||||
{"uuid", generateUuid()},
|
||||
{"interface-name", interface_name},
|
||||
{"autoconnect", true},
|
||||
{"permissions", std::vector<std::string>()}
|
||||
};
|
||||
|
||||
// ── 802-11-wireless section ────────────────────────────────────────
|
||||
settings["802-11-wireless"] = {
|
||||
{"ssid", ssidToBytes(ssid)},
|
||||
{"mode", std::string("infrastructure")}
|
||||
};
|
||||
|
||||
// ── 802-11-wireless-security section ───────────────────────────────
|
||||
if (security != SecurityType::Open) {
|
||||
SettingDict security_settings;
|
||||
|
||||
switch (security) {
|
||||
case SecurityType::WEP:
|
||||
security_settings["key-mgmt"] = std::string("none");
|
||||
security_settings["wep-key0"] = password;
|
||||
security_settings["wep-key-type"] = static_cast<uint32_t>(1); // WEP_KEY_TYPE_PASSPHRASE
|
||||
break;
|
||||
|
||||
case SecurityType::WPA:
|
||||
case SecurityType::WPA2:
|
||||
security_settings["key-mgmt"] = std::string("wpa-psk");
|
||||
security_settings["auth-alg"] = std::string("open");
|
||||
security_settings["psk"] = password;
|
||||
security_settings["psk-flags"] = static_cast<uint32_t>(0);
|
||||
break;
|
||||
|
||||
case SecurityType::WPA3:
|
||||
security_settings["key-mgmt"] = std::string("sae");
|
||||
security_settings["psk"] = password;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
settings["802-11-wireless-security"] = security_settings;
|
||||
}
|
||||
|
||||
// ── ipv4 section ───────────────────────────────────────────────────
|
||||
settings["ipv4"] = {
|
||||
{"method", std::string("auto")}
|
||||
};
|
||||
|
||||
// ── ipv6 section ───────────────────────────────────────────────────
|
||||
settings["ipv6"] = {
|
||||
{"method", std::string("auto")}
|
||||
};
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
// ─── Security Detection ────────────────────────────────────────────────
|
||||
|
||||
SecurityType detectSecurityType(
|
||||
uint32_t ap_flags,
|
||||
uint32_t wpa_flags,
|
||||
uint32_t rsn_flags
|
||||
) {
|
||||
// WPA3 uses SAE (flag 0x0400)
|
||||
if (rsn_flags & nm_ap_wpa_flags::RsnSae) {
|
||||
return SecurityType::WPA3;
|
||||
}
|
||||
|
||||
// WPA2 uses RSN PSK (flag 0x0100)
|
||||
if (rsn_flags & nm_ap_wpa_flags::RsnPsk) {
|
||||
return SecurityType::WPA2;
|
||||
}
|
||||
|
||||
// WPA uses WPA flags (not RSN)
|
||||
if (wpa_flags != 0) {
|
||||
return SecurityType::WPA;
|
||||
}
|
||||
|
||||
// WEP uses privacy flag
|
||||
if (ap_flags & nm_ap_flags::Privacy) {
|
||||
return SecurityType::WEP;
|
||||
}
|
||||
|
||||
return SecurityType::Open;
|
||||
}
|
||||
|
||||
bool requiresPassword(SecurityType type) {
|
||||
return type != SecurityType::Open;
|
||||
}
|
||||
|
||||
// ─── SSID Utilities ────────────────────────────────────────────────────
|
||||
|
||||
std::vector<uint8_t> ssidToBytes(const std::string& ssid) {
|
||||
return std::vector<uint8_t>(ssid.begin(), ssid.end());
|
||||
}
|
||||
|
||||
std::string bytesToSsid(const std::vector<uint8_t>& bytes) {
|
||||
return std::string(bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
bool isValidSsid(const std::string& ssid) {
|
||||
// SSID must be 1-32 bytes
|
||||
if (ssid.empty() || ssid.size() > 32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SSID must not contain null bytes
|
||||
for (char c : ssid) {
|
||||
if (c == '\0') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ─── Password Validation ───────────────────────────────────────────────
|
||||
|
||||
std::string validatePassword(const std::string& password, SecurityType type) {
|
||||
switch (type) {
|
||||
case SecurityType::WEP:
|
||||
// WEP keys can be 5 or 13 ASCII characters, or 10/26 hex digits
|
||||
// For simplicity, we accept any non-empty string
|
||||
if (password.empty()) {
|
||||
return "WEP network requires a password";
|
||||
}
|
||||
break;
|
||||
|
||||
case SecurityType::WPA:
|
||||
case SecurityType::WPA2:
|
||||
// WPA/WPA2 PSK requires 8-63 characters
|
||||
if (password.empty()) {
|
||||
return "WPA/WPA2 network requires a password";
|
||||
}
|
||||
if (password.size() < 8) {
|
||||
return "WPA/WPA2 password must be at least 8 characters";
|
||||
}
|
||||
if (password.size() > 63) {
|
||||
return "WPA/WPA2 password must be at most 63 characters";
|
||||
}
|
||||
break;
|
||||
|
||||
case SecurityType::WPA3:
|
||||
// WPA3 SAE requires 8-63 characters
|
||||
if (password.empty()) {
|
||||
return "WPA3 network requires a password";
|
||||
}
|
||||
if (password.size() < 8) {
|
||||
return "WPA3 password must be at least 8 characters";
|
||||
}
|
||||
if (password.size() > 63) {
|
||||
return "WPA3 password must be at most 63 characters";
|
||||
}
|
||||
break;
|
||||
|
||||
case SecurityType::Open:
|
||||
// No password required
|
||||
break;
|
||||
}
|
||||
|
||||
return ""; // Valid
|
||||
}
|
||||
|
||||
} // namespace radiantwave::network
|
||||
Reference in New Issue
Block a user