--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2022-2025 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+//
+// service_bootp_test.cc author Bohdan Hryniv <bhryniv@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "../service_bootp.h"
+#include "../service_bootp.cc"
+#include "service_plugin_mock.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+
+namespace snort
+{
+namespace layer
+{
+static eth::EtherHdr s_mock_eth_hdr = {};
+static const eth::EtherHdr* s_mock_eth_hdr_ptr = &s_mock_eth_hdr;
+const eth::EtherHdr* get_eth_layer(const Packet*) { return s_mock_eth_hdr_ptr; }
+}
+}
+
+AppIdModule::AppIdModule() : Module("a", "b") { }
+AppIdModule::~AppIdModule() = default;
+
+static AppIdConfig test_config;
+static ServiceDiscovery mock_sd;
+static BootpServiceDetector detector(&mock_sd);
+static snort::Packet mock_pkt(false);
+static snort::SfIp mock_ip;
+static AppIdModule mock_module;
+
+AppIdInspector::AppIdInspector(AppIdModule&) : config(&test_config), ctxt(test_config) { }
+
+static AppIdInspector test_inspector(mock_module);
+static OdpContext test_odp(test_config, nullptr);
+static AppIdSession test_asd(IpProtocol::UDP, &mock_ip, 67, test_inspector, test_odp, 0, 0);
+
+static void build_bootp_header(uint8_t* buf, uint8_t op)
+{
+ memset(buf, 0, sizeof(ServiceBOOTPHeader));
+ ServiceBOOTPHeader* bh = reinterpret_cast<ServiceBOOTPHeader*>(buf);
+ bh->op = op;
+ bh->htype = 0x01;
+ bh->hlen = 0x06;
+}
+
+static void add_magic_cookie(uint8_t* buf)
+{
+ buf[0] = 0x63;
+ buf[1] = 0x82;
+ buf[2] = 0x53;
+ buf[3] = 0x63;
+}
+
+TEST_GROUP(bootp_parsing_tests)
+{
+ void setup() override
+ {
+ }
+ void teardown() override
+ {
+ }
+};
+
+TEST(bootp_parsing_tests, dhcp_request_truncated_option_header)
+{
+ uint8_t pkt[sizeof(ServiceBOOTPHeader) + 4 + 1];
+ build_bootp_header(pkt, 0x01);
+ add_magic_cookie(pkt + sizeof(ServiceBOOTPHeader));
+ pkt[sizeof(ServiceBOOTPHeader) + 4] = 0x35;
+
+ AppidChangeBits change_bits;
+ AppIdDiscoveryArgs args(pkt, sizeof(pkt), APP_ID_FROM_INITIATOR,
+ test_asd, &mock_pkt, change_bits);
+
+ int ret = detector.validate(args);
+ CHECK_EQUAL(APPID_NOT_COMPATIBLE, ret);
+}
+
+TEST(bootp_parsing_tests, dhcp_reply_truncated_option_header)
+{
+ uint8_t pkt[sizeof(ServiceBOOTPHeader) + 4 + 1];
+ build_bootp_header(pkt, 0x02);
+ add_magic_cookie(pkt + sizeof(ServiceBOOTPHeader));
+ pkt[sizeof(ServiceBOOTPHeader) + 4] = 0x35;
+
+ AppidChangeBits change_bits;
+ AppIdDiscoveryArgs args(pkt, sizeof(pkt), APP_ID_FROM_RESPONDER,
+ test_asd, &mock_pkt, change_bits);
+
+ int ret = detector.validate(args);
+ CHECK_EQUAL(APPID_NOMATCH, ret);
+}
+
+TEST(bootp_parsing_tests, dhcp_reply_multi_option_then_truncated)
+{
+ uint8_t pkt[sizeof(ServiceBOOTPHeader) + 4 + 4];
+ build_bootp_header(pkt, 0x02);
+ add_magic_cookie(pkt + sizeof(ServiceBOOTPHeader));
+ pkt[sizeof(ServiceBOOTPHeader) + 4] = 53;
+ pkt[sizeof(ServiceBOOTPHeader) + 5] = 1;
+ pkt[sizeof(ServiceBOOTPHeader) + 6] = 5;
+ pkt[sizeof(ServiceBOOTPHeader) + 7] = 0x01;
+
+ AppidChangeBits change_bits;
+ AppIdDiscoveryArgs args(pkt, sizeof(pkt), APP_ID_FROM_RESPONDER,
+ test_asd, &mock_pkt, change_bits);
+
+ int ret = detector.validate(args);
+ CHECK_EQUAL(APPID_NOMATCH, ret);
+}
+
+int main(int argc, char** argv)
+{
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}