#include #include #include "packeteer/l7/tftp.hpp" using namespace packeteer::net; namespace { std::vector build_rrq(const std::string& filename, const std::string& mode) { std::vector bytes = {0x00, 0x01}; // opcode RRQ bytes.insert(bytes.end(), filename.begin(), filename.end()); bytes.push_back(0); bytes.insert(bytes.end(), mode.begin(), mode.end()); bytes.push_back(0); return bytes; } } // namespace TEST_CASE("parse_tftp decodes an RRQ with filename and mode") { auto msg = parse_tftp(build_rrq("boot.img", "octet")); REQUIRE(msg.has_value()); CHECK(msg->opcode == kTftpRrq); REQUIRE(msg->filename.has_value()); CHECK(*msg->filename == "boot.img"); REQUIRE(msg->mode.has_value()); CHECK(*msg->mode == "octet"); } TEST_CASE("parse_tftp decodes a DATA block number") { std::vector bytes = {0x00, 0x03, 0x00, 0x07, 'h', 'i'}; auto msg = parse_tftp(bytes); REQUIRE(msg.has_value()); CHECK(msg->opcode == kTftpData); REQUIRE(msg->block.has_value()); CHECK(*msg->block == 7); } TEST_CASE("parse_tftp decodes an ACK block number") { std::vector bytes = {0x00, 0x04, 0x00, 0x07}; auto msg = parse_tftp(bytes); REQUIRE(msg.has_value()); CHECK(msg->opcode == kTftpAck); REQUIRE(msg->block.has_value()); CHECK(*msg->block == 7); } TEST_CASE("parse_tftp decodes an ERROR code and message") { std::vector bytes = {0x00, 0x05, 0x00, 0x01}; std::string message = "File not found"; bytes.insert(bytes.end(), message.begin(), message.end()); bytes.push_back(0); auto msg = parse_tftp(bytes); REQUIRE(msg.has_value()); CHECK(msg->opcode == kTftpError); REQUIRE(msg->error_code.has_value()); CHECK(*msg->error_code == 1); REQUIRE(msg->error_message.has_value()); CHECK(*msg->error_message == "File not found"); } TEST_CASE("parse_tftp rejects an opcode outside the known range") { std::vector bytes = {0x00, 0x99}; CHECK_FALSE(parse_tftp(bytes).has_value()); } TEST_CASE("parse_tftp handles an RRQ with a truncated filename gracefully") { std::vector bytes = {0x00, 0x01, 'a', 'b'}; // no null terminator auto msg = parse_tftp(bytes); REQUIRE(msg.has_value()); CHECK(msg->opcode == kTftpRrq); CHECK_FALSE(msg->filename.has_value()); } TEST_CASE("TftpDissector claims port 69 and formats an RRQ") { TftpDissector dissector; CHECK(dissector.port() == kTftpPort); auto summary = dissector.summarize(build_rrq("boot.img", "octet")); REQUIRE(summary.has_value()); CHECK(*summary == "TFTP RRQ boot.img (octet)"); }