fixed formatting, that lead to undefined behavior
This commit is contained in:
@@ -21,7 +21,9 @@ namespace snoop
|
||||
auto b = s.find_first_not_of(" \t\r\n");
|
||||
auto e = s.find_last_not_of(" \t\r\n");
|
||||
if (b == std::string::npos)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return s.substr(b, e - b + 1);
|
||||
}
|
||||
|
||||
@@ -31,13 +33,19 @@ namespace snoop
|
||||
std::string out;
|
||||
FILE *pipe = popen((cmd + " 2>&1").c_str(), "r");
|
||||
if (!pipe)
|
||||
{
|
||||
throw std::runtime_error("popen failed: " + cmd);
|
||||
}
|
||||
while (fgets(buf.data(), (int)buf.size(), pipe) != nullptr)
|
||||
{
|
||||
out.append(buf.data());
|
||||
}
|
||||
int rc = pclose(pipe);
|
||||
int exitCode = WIFEXITED(rc) ? WEXITSTATUS(rc) : rc;
|
||||
if (exitCode != 0)
|
||||
{
|
||||
spdlog::warn("Command '{}' exited with code {}", cmd, exitCode);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -46,12 +54,16 @@ namespace snoop
|
||||
{
|
||||
std::string id = Trim(Exec("keyctl search @s user iot-client-key | tail -n1"));
|
||||
if (id.empty())
|
||||
{
|
||||
throw std::runtime_error("iot-client-key not found in keyring");
|
||||
}
|
||||
// Create a secure temp file
|
||||
char tmpl[] = "/run/iot/iot-keyXXXXXX";
|
||||
int fd = mkstemp(tmpl);
|
||||
if (fd < 0)
|
||||
{
|
||||
throw std::runtime_error("mkstemp failed for client key");
|
||||
}
|
||||
close(fd);
|
||||
std::filesystem::path p(tmpl);
|
||||
|
||||
@@ -80,7 +92,9 @@ namespace snoop
|
||||
name.push_back('\0');
|
||||
fd = mkstemp(name.data());
|
||||
if (fd < 0)
|
||||
{
|
||||
throw std::runtime_error("mkstemp failed");
|
||||
}
|
||||
fchmod(fd, S_IRUSR | S_IWUSR);
|
||||
path = name.data();
|
||||
}
|
||||
@@ -92,7 +106,9 @@ namespace snoop
|
||||
{
|
||||
ssize_t w = ::write(fd, p + off, n - off);
|
||||
if (w <= 0)
|
||||
{
|
||||
throw std::runtime_error("write failed");
|
||||
}
|
||||
off += (size_t)w;
|
||||
}
|
||||
fsync(fd);
|
||||
@@ -100,7 +116,9 @@ namespace snoop
|
||||
~TempFile()
|
||||
{
|
||||
if (fd >= 0)
|
||||
{
|
||||
::close(fd);
|
||||
}
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(path, ec);
|
||||
}
|
||||
@@ -111,12 +129,16 @@ namespace snoop
|
||||
// 1) get key id
|
||||
std::string id = Trim(Exec("keyctl search @s user iot-client-key | tail -n1"));
|
||||
if (id.empty())
|
||||
{
|
||||
throw std::runtime_error("iot-client-key not found in keyring");
|
||||
}
|
||||
|
||||
// 2) capture payload (no redirection to file)
|
||||
std::string bytes = Exec("keyctl pipe " + id);
|
||||
if (bytes.empty())
|
||||
{
|
||||
throw std::runtime_error("keyctl pipe returned empty payload");
|
||||
}
|
||||
|
||||
return std::vector<uint8_t>(bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
@@ -207,7 +207,9 @@ namespace snoop
|
||||
// Setup CA/CRT (from enrollment)
|
||||
std::filesystem::path ca = "/etc/iot/keys/issuing_ca.pem";
|
||||
if (!std::filesystem::exists(ca))
|
||||
{
|
||||
ca = "/etc/iot/keys/ca_chain.pem";
|
||||
}
|
||||
const std::filesystem::path crt = "/etc/iot/keys/device.crt.pem";
|
||||
|
||||
if (!needReinit)
|
||||
@@ -337,7 +339,9 @@ namespace snoop
|
||||
// certs from enrollment
|
||||
std::filesystem::path ca = "/etc/iot/keys/issuing_ca.pem";
|
||||
if (!std::filesystem::exists(ca))
|
||||
{
|
||||
ca = "/etc/iot/keys/ca_chain.pem";
|
||||
}
|
||||
const std::filesystem::path crt = "/etc/iot/keys/device.crt.pem";
|
||||
|
||||
while (!m_isIntermission)
|
||||
|
||||
@@ -36,7 +36,9 @@ namespace snoop
|
||||
std::string out;
|
||||
FILE *pipe = popen((cmd + " 2>&1").c_str(), "r");
|
||||
if (!pipe)
|
||||
{
|
||||
throw std::runtime_error("popen failed: " + cmd);
|
||||
}
|
||||
while (fgets(buf.data(), (int)buf.size(), pipe) != nullptr)
|
||||
{
|
||||
out.append(buf.data());
|
||||
@@ -44,17 +46,23 @@ namespace snoop
|
||||
auto rc = pclose(pipe);
|
||||
int exitCode = WIFEXITED(rc) ? WEXITSTATUS(rc) : rc;
|
||||
if (exitCode != 0)
|
||||
{
|
||||
spdlog::warn("Command '{}' exited with code {}", cmd, exitCode);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static void WriteFile(const std::filesystem::path &p, const std::string &data, bool createParents = true)
|
||||
{
|
||||
if (createParents)
|
||||
{
|
||||
std::filesystem::create_directories(p.parent_path());
|
||||
}
|
||||
std::ofstream f(p, std::ios::binary);
|
||||
if (!f)
|
||||
{
|
||||
throw std::runtime_error("Cannot open file for write: " + p.string());
|
||||
}
|
||||
f.write(data.data(), (std::streamsize)data.size());
|
||||
}
|
||||
|
||||
@@ -62,7 +70,9 @@ namespace snoop
|
||||
{
|
||||
std::ifstream f(p, std::ios::binary);
|
||||
if (!f)
|
||||
{
|
||||
throw std::runtime_error("Cannot open file for read: " + p.string());
|
||||
}
|
||||
std::ostringstream ss;
|
||||
ss << f.rdbuf();
|
||||
return ss.str();
|
||||
@@ -97,9 +107,11 @@ namespace snoop
|
||||
{
|
||||
auto serial = Trim(m[1].str());
|
||||
if (!serial.empty())
|
||||
{
|
||||
return serial;
|
||||
}
|
||||
}
|
||||
}
|
||||
spdlog::warn("CPU Serial not found, using fallback");
|
||||
return fallback;
|
||||
}
|
||||
@@ -221,7 +233,9 @@ namespace snoop
|
||||
std::filesystem::path(csrName));
|
||||
|
||||
if (!std::filesystem::exists(keyName) || !std::filesystem::exists(csrName))
|
||||
{
|
||||
throw std::runtime_error("CSR or key was not generated");
|
||||
}
|
||||
|
||||
std::filesystem::create_directories(keystoreDir);
|
||||
|
||||
@@ -232,8 +246,9 @@ namespace snoop
|
||||
kek);
|
||||
|
||||
if (!std::filesystem::exists(encKeyPath))
|
||||
{
|
||||
throw std::runtime_error("Encrypted key not created: " + encKeyPath.string());
|
||||
|
||||
}
|
||||
// shred plaintext key
|
||||
const std::string shredCmd = "shred -u " + keyName;
|
||||
spdlog::info("Shredding plaintext private key with '{}'", shredCmd);
|
||||
@@ -259,8 +274,9 @@ namespace snoop
|
||||
std::filesystem::path(csrName));
|
||||
|
||||
if (!std::filesystem::exists(csrName))
|
||||
{
|
||||
throw std::runtime_error("CSR was not generated from existing key");
|
||||
|
||||
}
|
||||
// TempFile destructor will securely delete the decrypted key file
|
||||
}
|
||||
|
||||
@@ -320,10 +336,15 @@ namespace snoop
|
||||
spdlog::info("POST {} (multipart)", path);
|
||||
auto res = cli.Post(path.c_str(), items);
|
||||
if (!res)
|
||||
{
|
||||
throw std::runtime_error("Enroll request failed (no response)");
|
||||
}
|
||||
spdlog::info("Enroll response status: {}", res->status);
|
||||
if (res->status != 200 && res->status != 201)
|
||||
{
|
||||
spdlog::info("Enroll error: " + res->body);
|
||||
throw std::runtime_error("Enroll failed: HTTP " + std::to_string(res->status));
|
||||
}
|
||||
|
||||
// 6) Expect JSON: { "certificate": "...", "issuing_ca":"...", "ca_chain":"..." }
|
||||
nlohmann::json j;
|
||||
@@ -336,12 +357,16 @@ namespace snoop
|
||||
throw std::runtime_error(std::string("Enroll: invalid JSON: ") + e.what());
|
||||
}
|
||||
if (!j.contains("certificate") || !j["certificate"].is_string())
|
||||
{
|
||||
throw std::runtime_error("Enroll response missing or invalid 'certificate'");
|
||||
}
|
||||
WriteFile(certPath, j["certificate"].get<std::string>());
|
||||
if (j.contains("issuing_ca"))
|
||||
{
|
||||
if (!j["issuing_ca"].is_string())
|
||||
{
|
||||
throw std::runtime_error("'issuing_ca' must be a string PEM");
|
||||
}
|
||||
WriteFile(caPath, j["issuing_ca"].get<std::string>());
|
||||
}
|
||||
|
||||
@@ -455,15 +480,21 @@ namespace snoop
|
||||
auto res = cli->Post(path.c_str(), items);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
throw std::runtime_error("Renew request failed (no response)");
|
||||
}
|
||||
spdlog::info("Renew response status: {}", res->status);
|
||||
if (res->status != 200)
|
||||
{
|
||||
throw std::runtime_error("Renew failed: HTTP " + std::to_string(res->status));
|
||||
}
|
||||
|
||||
nlohmann::json j = nlohmann::json::parse(res->body);
|
||||
|
||||
if (!j.contains("certificate") || !j["certificate"].is_string())
|
||||
{
|
||||
throw std::runtime_error("Renew response missing or invalid 'certificate'");
|
||||
}
|
||||
WriteFile(certPath, j["certificate"].get<std::string>());
|
||||
|
||||
if (j.contains("issuing_ca") && j["issuing_ca"].is_string())
|
||||
@@ -475,11 +506,17 @@ namespace snoop
|
||||
{
|
||||
std::string chainPem;
|
||||
if (j["ca_chain"].is_string())
|
||||
{
|
||||
chainPem = j["ca_chain"].get<std::string>();
|
||||
}
|
||||
else if (j["ca_chain"].is_array())
|
||||
{
|
||||
chainPem = JoinPemArray(j["ca_chain"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("'ca_chain' must be string or array");
|
||||
}
|
||||
WriteFile(chainPath, chainPem);
|
||||
}
|
||||
|
||||
@@ -525,7 +562,9 @@ namespace snoop
|
||||
{
|
||||
std::ifstream f(path, std::ios::binary);
|
||||
if (!f)
|
||||
{
|
||||
throw std::runtime_error("Failed to open file: " + path);
|
||||
}
|
||||
std::ostringstream ss;
|
||||
ss << f.rdbuf();
|
||||
return ss.str();
|
||||
|
||||
@@ -49,7 +49,9 @@ namespace snoop
|
||||
{
|
||||
std::lock_guard lk(m_mtx);
|
||||
if (m_started)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse & normalize the provided URL now (extract token, add trailing /whip)
|
||||
m_endpoint = ParseWhipUrl(m_p.whipUrl);
|
||||
@@ -101,7 +103,8 @@ namespace snoop
|
||||
m_pc->onGatheringStateChange([this](rtc::PeerConnection::GatheringState s)
|
||||
{
|
||||
spdlog::info("WHIP gathering state: {}", (int)s);
|
||||
if (s == rtc::PeerConnection::GatheringState::Complete) {
|
||||
if (s == rtc::PeerConnection::GatheringState::Complete)
|
||||
{
|
||||
PatchSdpFrag("a=end-of-candidates");
|
||||
} });
|
||||
|
||||
@@ -136,7 +139,9 @@ namespace snoop
|
||||
{
|
||||
std::lock_guard lk(m_mtx);
|
||||
if (!m_started)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_trackOpen = false;
|
||||
m_track.reset();
|
||||
@@ -150,7 +155,9 @@ namespace snoop
|
||||
{
|
||||
std::lock_guard lk(m_mtx);
|
||||
if (!m_track || !m_started || !m_trackOpen.load())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<uint8_t, 12> rtp{};
|
||||
rtp[0] = 0x80; // V=2
|
||||
@@ -233,7 +240,9 @@ namespace snoop
|
||||
{
|
||||
size_t amp = query.find('&', pos);
|
||||
if (amp == std::string::npos)
|
||||
{
|
||||
amp = query.size();
|
||||
}
|
||||
auto kv = query.substr(pos, amp - pos);
|
||||
auto eq = kv.find('=');
|
||||
if (eq != std::string::npos)
|
||||
@@ -252,9 +261,13 @@ namespace snoop
|
||||
// MediaMTX wants: /whip/<original...>/whip
|
||||
// Your incoming URLs already start with /whip/... — we just ensure they end with /whip.
|
||||
if (rawPath.empty())
|
||||
{
|
||||
rawPath = "/";
|
||||
}
|
||||
if (rawPath.back() == '/')
|
||||
{
|
||||
rawPath.pop_back();
|
||||
}
|
||||
if (rawPath.rfind("/whip", std::string::npos) != rawPath.size() - 5)
|
||||
{
|
||||
rawPath += "/whip";
|
||||
@@ -266,7 +279,9 @@ namespace snoop
|
||||
static std::tuple<std::string, std::string> ExtractAnswerAndLocation(const httplib::Result &r)
|
||||
{
|
||||
if (!r)
|
||||
{
|
||||
throw std::runtime_error("No HTTP result");
|
||||
}
|
||||
if (r->status != 201 && r->status != 200)
|
||||
{
|
||||
throw std::runtime_error("Unexpected WHIP status: " + std::to_string(r->status));
|
||||
@@ -274,7 +289,9 @@ namespace snoop
|
||||
std::string answer = r->body;
|
||||
std::string resourceUrl;
|
||||
if (r->has_header("Location"))
|
||||
{
|
||||
resourceUrl = r->get_header_value("Location");
|
||||
}
|
||||
return {answer, resourceUrl};
|
||||
}
|
||||
|
||||
@@ -294,7 +311,9 @@ namespace snoop
|
||||
|
||||
auto res = cli->Post(m_endpoint.path.c_str(), hs, sdpOffer, "application/sdp");
|
||||
if (!res)
|
||||
{
|
||||
throw std::runtime_error("No HTTP result (network?)");
|
||||
}
|
||||
|
||||
const auto ctype = res->get_header_value("Content-Type");
|
||||
const bool has_loc = res->has_header("Location");
|
||||
@@ -313,7 +332,9 @@ namespace snoop
|
||||
std::string answer = res->body;
|
||||
std::string resourceUrl;
|
||||
if (res->has_header("Location"))
|
||||
{
|
||||
resourceUrl = res->get_header_value("Location");
|
||||
}
|
||||
|
||||
if (answer.find("a=ice-ufrag:") == std::string::npos)
|
||||
{
|
||||
@@ -334,7 +355,9 @@ namespace snoop
|
||||
// If MediaMTX returns an absolute Location for the resource, we parse it;
|
||||
// otherwise we reuse the same host/port and use Location as path.
|
||||
if (!m_resourceUrl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ParsedUrl target;
|
||||
try
|
||||
@@ -393,7 +416,9 @@ namespace snoop
|
||||
void PatchSdpFrag(const std::string &sdpfrag)
|
||||
{
|
||||
if (!m_resourceUrl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Reuse the same host/port and just set path = Location
|
||||
ParsedUrl target = m_endpoint;
|
||||
@@ -411,7 +436,9 @@ namespace snoop
|
||||
// MUST include CRLF at end of body
|
||||
std::string body = sdpfrag;
|
||||
if (body.empty() || body.back() != '\n')
|
||||
{
|
||||
body += "\r\n";
|
||||
}
|
||||
|
||||
auto res = cli->Patch(target.path.c_str(), hs, body, "application/trickle-ice-sdpfrag");
|
||||
if (!res || !(res->status == 200 || res->status == 201 || res->status == 204))
|
||||
|
||||
Reference in New Issue
Block a user