1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// Copyright (C) 2015-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>
#include <cc/data.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/cfg_duid.h>
#include <dhcpsrv/parsers/duid_config_parser.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <dhcpsrv/testutils/config_result_check.h>
#include <testutils/test_to_element.h>
#include <util/encode/encode.h>
#include <gtest/gtest.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <limits><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <sstream><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <string><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

using namespace isc;
using namespace isc::data;
using namespace isc::dhcp;

namespace {

/// @brief Test fixture class for @c DUIDConfigParser
class DUIDConfigParserTest : public ::testing::Test {
public:

    /// @brief constructor
    ///
    /// Initializes cfg_duid_ to a new empty object
    DUIDConfigParserTest()
        :cfg_duid_(new CfgDUID()){
    }

    /// @brief Creates simple configuration with DUID type only.
    ///
    /// @param duid_type DUID type in the textual format.
    std::string createConfigWithType(const std::string& duid_type) const;

    /// @brief Creates simple configuration with DUID type and one
    /// numeric parameter.
    ///
    /// @param name Parameter name.
    /// @param value Parameter value.
    std::string createConfigWithInteger(const std::string& name,
                                        const int64_t value) const;

    /// @brief Parse configuration.
    ///
    /// @param config String representing DUID configuration.
    void build(const std::string& config) const;

    /// @brief Test that only a DUID type can be specified.
    ///
    /// @param duid_type DUID type in numeric format.
    /// @param duid_type_text DUID type in textual format.
    void testTypeOnly(const DUID::DUIDType& duid_type,
                      const std::string& duid_type_text) const;

    /// @brief Test that invalid configuration is rejected.
    ///
    /// @param config Holds JSON configuration to be used.
    void testInvalidConfig(const std::string& config) const;

    /// @brief Test out of range numeric values.
    ///
    /// @param param_name Parameter name.
    /// @tparam Type of the numeric parameter.
    template<typename NumericType>
    void testOutOfRange(const std::string& param_name) {
        // Obtain maximum value for the specified numeric type.
        const uint64_t max_value = std::numeric_limits<NumericType>::max();

        // Negative values are not allowed.
        EXPECT_THROW(build(createConfigWithInteger(param_name, -1)),
                     DhcpConfigError);
        // Zero is allowed.
        EXPECT_NO_THROW(build(createConfigWithInteger(param_name, 0)));
        // Maximum value.
        EXPECT_NO_THROW(build(createConfigWithInteger(param_name, max_value)));
        // Value greater than maximum should result in exception.
        EXPECT_THROW(build(createConfigWithInteger(param_name, max_value + 1)),
                     DhcpConfigError);
    }

    /// @brief Converts vector to string of hexadecimal digits.
    ///
    /// @param vec Input vector.
    /// @return String of hexadecimal digits converted from vector.
    std::string toString(const std::vector<uint8_t>& vec) const;

    /// Config DUID pointer
    CfgDUIDPtr cfg_duid_;
};

std::string
DUIDConfigParserTest::createConfigWithType(const std::string& duid_type) const {
    std::ostringstream s;
    s << "{ \"type\": \"" << duid_type << "\" }";
    return (s.str());
}

std::string
DUIDConfigParserTest::createConfigWithInteger(const std::string& name,
                                              const int64_t value) const {
    std::ostringstream s;
    s << "{ \"type\": \"LLT\", \"" << name << "\": " << value << " }";
    return (s.str());
}

void
DUIDConfigParserTest::build(const std::string& config) const {
    ElementPtr config_element = Element::fromJSON(config);
    DUIDConfigParser parser;
    parser.parse(cfg_duid_, config_element);
}

void
DUIDConfigParserTest::testTypeOnly(const DUID::DUIDType& duid_type,
                                   const std::string& duid_type_text) const {
    // Use DUID configuration with only a "type".
    ASSERT_NO_THROW(build(createConfigWithType(duid_type_text)));

    // Make sure that the type is correct and that other parameters are set
    // to their defaults.
    ASSERT_TRUE(cfg_duid_);
    EXPECT_EQ(duid_type, cfg_duid_->getType());
    EXPECT_TRUE(cfg_duid_->getIdentifier().empty());
    EXPECT_EQ(0, cfg_duid_->getHType());
    EXPECT_EQ(0, cfg_duid_->getTime());
    EXPECT_EQ(0, cfg_duid_->getEnterpriseId());
}

void
DUIDConfigParserTest::testInvalidConfig(const std::string& config) const {
    EXPECT_THROW(build(config), DhcpConfigError);
}

std::string
DUIDConfigParserTest::toString(const std::vector<uint8_t>& vec) const {
    try {
        return (util::encode::encodeHex(vec));
    } catch (...) {
        ADD_FAILURE() << "toString: unable to encode vector to"
            " hexadecimal string";
    }
    return ("");
}

// This test verifies that it is allowed to specify a DUID-LLT type.
TEST_F(DUIDConfigParserTest, typeOnlyLLT) {<--- syntax error
    testTypeOnly(DUID::DUID_LLT, "LLT");
}

// This test verifies that it is allowed to specify a DUID-EN type.
TEST_F(DUIDConfigParserTest, typeOnlyEN) {
    testTypeOnly(DUID::DUID_EN, "EN");
}

// This test verifies that it is allowed to specify a DUID-LL type.
TEST_F(DUIDConfigParserTest, typeOnlyLL) {
    testTypeOnly(DUID::DUID_LL, "LL");
}

// This test verifies that using unsupported DUID type will result in
// configuration error.
TEST_F(DUIDConfigParserTest, typeInvalid) {
    testInvalidConfig(createConfigWithType("WRONG"));
}

// This test verifies that DUID type is required.
TEST_F(DUIDConfigParserTest, noType) {
    // First check that the configuration with DUID type specified is
    // accepted.
    ASSERT_NO_THROW(build("{ \"type\": \"LLT\", \"time\": 1 }"));
    // Now remove the type and expect an error.
    testInvalidConfig("{ \"time\": 1 }");
}

// This test verifies that all parameters can be set.
TEST_F(DUIDConfigParserTest, allParameters) {
    // Set all parameters.
    std::string config = "{"
        " \"type\": \"EN\","
        " \"identifier\": \"ABCDEF\","
        " \"time\": 100,"
        " \"htype\": 8,"
        " \"enterprise-id\": 2024,"
        " \"persist\": false"
        "}";
    ASSERT_NO_THROW(build(config));

    // Verify that parameters have been set correctly.
    ASSERT_TRUE(cfg_duid_);
    EXPECT_EQ(DUID::DUID_EN, cfg_duid_->getType());
    EXPECT_EQ("ABCDEF", toString(cfg_duid_->getIdentifier()));
    EXPECT_EQ(8, cfg_duid_->getHType());
    EXPECT_EQ(100, cfg_duid_->getTime());
    EXPECT_EQ(2024, cfg_duid_->getEnterpriseId());
    EXPECT_FALSE(cfg_duid_->persist());

    // Check the config can be got back.
    isc::test::runToElementTest<CfgDUID>(config, *cfg_duid_);
}

// Test out of range values for time.
TEST_F(DUIDConfigParserTest, timeOutOfRange) {
    testOutOfRange<uint32_t>("time");
}

// Test out of range values for hardware type.
TEST_F(DUIDConfigParserTest, htypeOutOfRange) {
    testOutOfRange<uint16_t>("htype");
}

// Test out of range values for enterprise id.
TEST_F(DUIDConfigParserTest, enterpriseIdOutOfRange) {
    testOutOfRange<uint32_t>("enterprise-id");
}

} // end of anonymous namespace