dhcp4_test_utils.h 22.7 KB
Newer Older
1
// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// 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/.
6

7 8 9
/// @file   dhcp4_test_utils.h
///
/// @brief  This file contains utility classes used for DHCPv4 server testing
10

11 12 13 14
#ifndef DHCP4_TEST_UTILS_H
#define DHCP4_TEST_UTILS_H

#include <gtest/gtest.h>
15
#include <dhcp/iface_mgr.h>
16
#include <dhcp/option4_addrlst.h>
17
#include <dhcp/pkt4.h>
18 19
#include <dhcp/pkt_filter.h>
#include <dhcp/pkt_filter_inet.h>
20
#include <dhcpsrv/alloc_engine.h>
21 22
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/lease.h>
23
#include <dhcpsrv/lease_mgr_factory.h>
24
#include <dhcp4/dhcp4_srv.h>
25
#include <dhcp4/parser_context.h>
26
#include <asiolink/io_address.h>
27
#include <cc/command_interpreter.h>
28
#include <list>
29

30 31
#include <boost/shared_ptr.hpp>

32
namespace isc {
33
namespace dhcp {
34 35 36 37 38 39 40 41 42 43 44 45
namespace test {

/// @brief Dummy Packet Filtering class.
///
/// This class reports capability to respond directly to the client which
/// doesn't have address configured yet.
///
/// All packet and socket handling functions do nothing because they are not
/// used in unit tests.
class PktFilterTest : public PktFilter {
public:

46 47 48 49 50 51 52
    /// @brief Constructor.
    ///
    /// Sets the 'direct response' capability to true.
    PktFilterTest()
        : direct_resp_supported_(true) {
    }

53 54 55 56
    /// @brief Reports 'direct response' capability.
    ///
    /// @return always true.
    virtual bool isDirectResponseSupported() const {
57
        return (direct_resp_supported_);
58 59 60
    }

    /// Does nothing.
61
    virtual SocketInfo openSocket(Iface&,
62 63 64
                                  const isc::asiolink::IOAddress& addr,
                                  const uint16_t port, const bool, const bool) {
        return (SocketInfo(addr, port, 0));
65 66 67
    }

    /// Does nothing.
68
    virtual Pkt4Ptr receive(Iface&, const SocketInfo&) {
69 70 71 72 73 74 75 76
        return Pkt4Ptr();
    }

    /// Does nothing.
    virtual int send(const Iface&, uint16_t, const Pkt4Ptr&) {
        return (0);
    }

77 78 79 80
    /// @brief Holds a boolean value which indicates whether direct response
    /// capability is supported (true) or not (false).
    bool direct_resp_supported_;

81 82
};

83 84
typedef boost::shared_ptr<PktFilterTest> PktFilterTestPtr;

85 86 87 88 89
/// Forward definition for Dhcp4Client defined in dhcp4_client.h
/// dhcp4_client.h includes dhcp_test_utils.h (this file), so to avoid
/// circular dependencies, we need a forward class declaration.
class Dhcp4Client;

90 91 92 93 94 95 96 97 98 99 100 101 102
/// @brief "Naked" DHCPv4 server, exposes internal fields
class NakedDhcpv4Srv: public Dhcpv4Srv {
public:

    /// @brief Constructor.
    ///
    /// This constructor disables default modes of operation used by the
    /// Dhcpv4Srv class:
    /// - Send/receive broadcast messages through sockets on interfaces
    /// which support broadcast traffic.
    /// - Direct DHCPv4 traffic - communication with clients which do not
    /// have IP address assigned yet.
    ///
Francis Dupont's avatar
Francis Dupont committed
103
    /// Enabling these modes requires root privileges so they must be
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
    /// disabled for unit testing.
    ///
    /// Note, that disabling broadcast options on sockets does not impact
    /// the operation of these tests because they use local loopback
    /// interface which doesn't have broadcast capability anyway. It rather
    /// prevents setting broadcast options on other (broadcast capable)
    /// sockets which are opened on other interfaces in Dhcpv4Srv constructor.
    ///
    /// The Direct DHCPv4 Traffic capability can be disabled here because
    /// it is tested with PktFilterLPFTest unittest. The tests which belong
    /// to PktFilterLPFTest can be enabled on demand when root privileges can
    /// be guaranteed.
    ///
    /// @param port port number to listen on; the default value 0 indicates
    /// that sockets should not be opened.
    NakedDhcpv4Srv(uint16_t port = 0)
120 121 122 123
        : Dhcpv4Srv(port, false, false) {
        // Create a default lease database backend.
        std::string dbconfig = "type=memfile universe=4 persist=false";
        isc::dhcp::LeaseMgrFactory::create(dbconfig);
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
        // Create fixed server id.
        server_id_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
                                            asiolink::IOAddress("192.0.3.1")));
    }

    /// @brief Returns fixed server identifier assigned to the naked server
    /// instance.
    OptionPtr getServerID() const {
        return (server_id_);
    }

    /// @brief fakes packet reception
    /// @param timeout ignored
    ///
    /// The method receives all packets queued in receive queue, one after
    /// another. Once the queue is empty, it initiates the shutdown procedure.
    ///
    /// See fake_received_ field for description
    virtual Pkt4Ptr receivePacket(int /*timeout*/) {

        // If there is anything prepared as fake incoming traffic, use it
        if (!fake_received_.empty()) {
            Pkt4Ptr pkt = fake_received_.front();
            fake_received_.pop_front();
            return (pkt);
        }

        // If not, just trigger shutdown and return immediately
        shutdown();
        return (Pkt4Ptr());
    }

    /// @brief fake packet sending
    ///
    /// Pretend to send a packet, but instead just store it in fake_send_ list
    /// where test can later inspect server's response.
    virtual void sendPacket(const Pkt4Ptr& pkt) {
        fake_sent_.push_back(pkt);
    }

    /// @brief adds a packet to fake receive queue
    ///
    /// See fake_received_ field for description
    void fakeReceive(const Pkt4Ptr& pkt) {
        fake_received_.push_back(pkt);
    }

    virtual ~NakedDhcpv4Srv() {
    }

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
    /// @brief Runs processing DHCPREQUEST.
    ///
    /// @param request a message received from client
    /// @return DHCPACK or DHCPNAK message
    Pkt4Ptr processRequest(Pkt4Ptr& request) {
        AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4());
        return (processRequest(request, context));
    }

    /// @brief Runs processing DHCPRELEASE.
    ///
    /// @param release message received from client
    void processRelease(Pkt4Ptr& release) {
        AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4());
        processRelease(release, context);
    }

    /// @brief Runs processing DHCPDECLINE
    ///
    /// @param decline message received from client
    void processDecline(Pkt4Ptr& decline) {
        AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4());
        processDecline(decline, context);
    }

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
    /// @brief Dummy server identifier option used by various tests.
    OptionPtr server_id_;

    /// @brief packets we pretend to receive
    ///
    /// Instead of setting up sockets on interfaces that change between OSes, it
    /// is much easier to fake packet reception. This is a list of packets that
    /// we pretend to have received. You can schedule new packets to be received
    /// using fakeReceive() and NakedDhcpv4Srv::receivePacket() methods.
    std::list<Pkt4Ptr> fake_received_;

    std::list<Pkt4Ptr> fake_sent_;

    using Dhcpv4Srv::adjustIfaceData;
    using Dhcpv4Srv::appendServerID;
    using Dhcpv4Srv::processDiscover;
    using Dhcpv4Srv::processRequest;
    using Dhcpv4Srv::processRelease;
    using Dhcpv4Srv::processDecline;
    using Dhcpv4Srv::processInform;
    using Dhcpv4Srv::processClientName;
    using Dhcpv4Srv::createNameChangeRequests;
    using Dhcpv4Srv::acceptServerId;
    using Dhcpv4Srv::sanityCheck;
    using Dhcpv4Srv::srvidToString;
    using Dhcpv4Srv::classifyPacket;
225
    using Dhcpv4Srv::deferredUnpack;
226 227
    using Dhcpv4Srv::accept;
    using Dhcpv4Srv::acceptMessageType;
228
    using Dhcpv4Srv::selectSubnet;
229
    using Dhcpv4Srv::setSendResponsesToSource;
230
    using Dhcpv4Srv::VENDOR_CLASS_PREFIX;
231
    using Dhcpv4Srv::shutdown_;
232
    using Dhcpv4Srv::alloc_engine_;
233
    using Dhcpv4Srv::server_port_;
234
    using Dhcpv4Srv::client_port_;
235 236
};

237 238 239 240 241
// We need to pass one reference to the Dhcp4Client, which is defined in
// dhcp4_client.h. That header includes this file. To avoid circular
// dependencies, we use forward declaration here.
class Dhcp4Client;

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
/// @brief Base class for DHCPv4 server testing.
///
/// Currently it configures the test data path directory in
/// the @c CfgMgr. When the object is destroyed, the original
/// path is reverted.
class BaseServerTest : public ::testing::Test {
public:

    /// @brief Constructor.
    BaseServerTest();

    /// @brief Destructor.
    virtual ~BaseServerTest();

private:

    /// @brief Holds the original data directory.
    std::string original_datadir_;

};

class Dhcpv4SrvTest : public BaseServerTest {
264 265
public:

266 267 268 269 270
    enum ExpectedResult {
        SHOULD_PASS, // pass = accept decline, move lease to declined state.
        SHOULD_FAIL  // fail = reject the decline
    };

271 272 273 274
    /// @brief Constructor
    ///
    /// Initializes common objects used in many tests.
    /// Also sets up initial configuration in CfgMgr.
275
    Dhcpv4SrvTest();
276

Tomek Mrugalski's avatar
Tomek Mrugalski committed
277
    /// @brief destructor
278
    virtual ~Dhcpv4SrvTest();
279 280 281

    /// @brief Add 'Parameter Request List' option to the packet.
    ///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
282
    /// This function adds PRL option comprising the following option codes:
283 284 285 286 287 288 289
    /// - 5 - Name Server
    /// - 15 - Domain Name
    /// - 7 - Log Server
    /// - 8 - Quotes Server
    /// - 9 - LPR Server
    ///
    /// @param pkt packet to add PRL option to.
290
    void addPrlOption(Pkt4Ptr& pkt);
291 292 293 294 295 296 297 298 299

    /// @brief Configures options being requested in the PRL option.
    ///
    /// The lpr-servers option is NOT configured here although it is
    /// added to the 'Parameter Request List' option in the
    /// \ref addPrlOption. When requested option is not configured
    /// the server should not return it in its response. The goal
    /// of not configuring the requested option is to verify that
    /// the server will not return it.
300
    void configureRequestedOptions();
301 302 303 304

    /// @brief checks that the response matches request
    /// @param q query (client's message)
    /// @param a answer (server's message)
305
    void messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a);
306

307 308
    /// @brief Check that certain basic (always added when lease is acquired)
    /// are present in a message.
309
    ///
310 311 312
    /// @param pkt A message to be checked.
    /// @return Assertion result which indicates whether test passed or failed.
    ::testing::AssertionResult basicOptionsPresent(const Pkt4Ptr& pkt);
313

314 315
    /// @brief Check that certain basic (always added when lease is acquired)
    /// are not present.
316 317
    ///
    /// @param pkt A packet to be checked.
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
    /// @return Assertion result which indicates whether test passed or failed.
    ::testing::AssertionResult noBasicOptions(const Pkt4Ptr& pkt);

    /// @brief Check that certain requested options are present in the message.
    ///
    /// @param pkt A message to be checked.
    /// @return Assertion result which indicates whether test passed or failed.
    ::testing::AssertionResult requestedOptionsPresent(const Pkt4Ptr& pkt);

    /// @brief Check that certain options (requested with PRL option)
    /// are not present.
    ///
    /// @param pkt A packet to be checked.
    /// @return Assertion result which indicates whether test passed or failed.
    ::testing::AssertionResult noRequestedOptions(const Pkt4Ptr& pkt);
333 334 335 336

    /// @brief generates client-id option
    ///
    /// Generate client-id option of specified length
Francis Dupont's avatar
Francis Dupont committed
337
    /// Ids with different lengths are sufficient to generate
338 339 340 341
    /// unique ids. If more fine grained control is required,
    /// tests generate client-ids on their own.
    /// Sets client_id_ field.
    /// @param size size of the client-id to be generated
342
    OptionPtr generateClientId(size_t size = 4);
343 344 345 346 347

    /// @brief generate hardware address
    ///
    /// @param size size of the generated MAC address
    /// @param pointer to Hardware Address object
348
    HWAddrPtr generateHWAddr(size_t size = 6);
349 350 351 352 353 354

    /// Check that address was returned from proper range, that its lease
    /// lifetime is correct, that T1 and T2 are returned properly
    /// @param rsp response to be checked
    /// @param subnet subnet that should be used to verify assigned address
    ///        and options
355 356 357 358
    /// @param t1_present check that t1 must be present (true) or must not be
    /// present (false)
    /// @param t2_present check that t2 must be present (true) or must not be
    /// present (false)
359 360
    /// @param expected_valid check that lease lifetime has the not-zero
    /// expected value (zero value means that do not check).
361
    void checkAddressParams(const Pkt4Ptr& rsp, const Subnet4Ptr subnet,
362
                            bool t1_present = false,
363 364
                            bool t2_present = false,
                            uint32_t expected_valid = 0);
365 366 367 368 369 370

    /// @brief Basic checks for generated response (message type and trans-id).
    ///
    /// @param rsp response packet to be validated
    /// @param expected_message_type expected message type
    /// @param expected_transid expected transaction-id
371
    void checkResponse(const Pkt4Ptr& rsp, int expected_message_type,
372
                       uint32_t expected_transid);
373 374 375 376 377 378 379 380 381

    /// @brief Checks if the lease sent to client is present in the database
    ///
    /// @param rsp response packet to be validated
    /// @param client_id expected client-identifier (or NULL)
    /// @param HWAddr expected hardware address (not used now)
    /// @param expected_addr expected address
    Lease4Ptr checkLease(const Pkt4Ptr& rsp, const OptionPtr& client_id,
                         const HWAddrPtr&,
382
                         const isc::asiolink::IOAddress& expected_addr);
383 384 385 386

    /// @brief Checks if server response (OFFER, ACK, NAK) includes proper server-id
    /// @param rsp response packet to be validated
    /// @param expected_srvid expected value of server-id
387
    void checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid);
388 389

    /// @brief Checks if server response (OFFER, ACK, NAK) includes proper client-id
Tomek Mrugalski's avatar
Tomek Mrugalski committed
390 391 392 393 394
    ///
    /// This method follows values reported by CfgMgr in echoClientId() method.
    /// Depending on its configuration, the client-id is either mandatory or
    /// forbidden to appear in the response.
    ///
395 396
    /// @param rsp response packet to be validated
    /// @param expected_clientid expected value of client-id
397
    void checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid);
398

399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
    /// @brief Create packet from output buffer of another packet.
    ///
    /// This function creates a packet using an output buffer from another
    /// packet. This imitates reception of a packet from the wire. The
    /// unpack function is then called to parse packet contents and to
    /// create a collection of the options carried by this packet.
    ///
    /// This function is useful for unit tests which verify that the received
    /// packet is parsed correctly. In those cases it is usually inappropriate
    /// to create an instance of the packet, add options, set packet
    /// fields and use such packet as an input to processDiscover or
    /// processRequest function. This is because, such a packet has certain
    /// options already initialized and there is no way to verify that they
    /// have been initialized when packet instance was created or wire buffer
    /// processing. By creating a packet from the buffer we guarantee that the
    /// new packet is entirely initialized during wire data parsing.
    ///
    /// @param src_pkt A source packet, to be copied.
    /// @param [out] dst_pkt A destination packet.
    ///
    /// @return assertion result indicating if a function completed with
    /// success or failure.
    static ::testing::AssertionResult
422 423
    createPacketFromBuffer(const isc::dhcp::Pkt4Ptr& src_pkt,
                           isc::dhcp::Pkt4Ptr& dst_pkt);
424

425 426 427 428 429 430 431 432 433 434 435 436 437
    /// @brief Tests if Discover or Request message is processed correctly
    ///
    /// This test verifies that the Parameter Request List option is handled
    /// correctly, i.e. it checks that certain options are present in the
    /// server's response when they are requested and that they are not present
    /// when they are not requested or NAK occurs.
    ///
    /// @todo We need an additional test for PRL option using real traffic
    /// capture.
    ///
    /// @param msg_type DHCPDISCOVER or DHCPREQUEST
    void testDiscoverRequest(const uint8_t msg_type);

438 439 440
    /// @brief Runs DHCPv4 configuration from the JSON string.
    ///
    /// @param config String holding server configuration in JSON format.
441 442
    /// @param commit A boolean flag indicating if the new configuration
    /// should be committed (if true), or not (if false).
443 444 445 446 447
    /// @param open_sockets  A boolean flag indicating if sockets should
    /// be opened (if true), or not (if false).
    void configure(const std::string& config,
                   const bool commit = true,
                   const bool open_sockets = true);
448

449 450 451 452
    /// @brief Configure specified DHCP server using JSON string.
    ///
    /// @param config String holding server configuration in JSON format.
    /// @param srv Instance of the server to be configured.
453 454
    /// @param commit A boolean flag indicating if the new configuration
    /// should be committed (if true), or not (if false).
455 456 457 458 459 460
    /// @param open_sockets  A boolean flag indicating if sockets should
    /// be opened (if true), or not (if false).
    void configure(const std::string& config,
                   NakedDhcpv4Srv& srv,
                   const bool commit = true,
                   const bool open_sockets = true);
461

462 463 464 465 466 467 468 469 470 471 472 473
    /// @brief Configure specified DHCP server using JSON string.
    ///
    /// @param config String holding server configuration in JSON format.
    /// @param srv Instance of the server to be configured.
    /// @param commit A boolean flag indicating if the new configuration
    /// should be committed (if true), or not (if false).
    /// @param exp_rcode expected status code (default = 0 (success))
    /// @return (a pair of status code and a string with result)
    std::pair<int, std::string>
    configureWithStatus(const std::string& config, NakedDhcpv4Srv& srv,
                        const bool commit = true, const int exp_rcode = 0);

Josh Soref's avatar
Josh Soref committed
474
    /// @brief Pretends a packet of specified type was received.
475 476 477
    ///
    /// Instantiates fake network interfaces, configures passed Dhcpv4Srv,
    /// then creates a message of specified type and sends it to the
Andrei Pavel's avatar
Andrei Pavel committed
478
    /// server and then checks whether expected statistics were set
479 480 481 482 483 484 485 486 487
    /// appropriately.
    ///
    /// @param srv the DHCPv4 server to be used
    /// @param config JSON configuration to be used
    /// @param pkt_type type of the packet to be faked
    /// @param stat_name name of the expected statistic
    void pretendReceivingPkt(NakedDhcpv4Srv& srv, const std::string& config,
                             uint8_t pkt_type, const std::string& stat_name);

488 489
    /// @brief Create @c Dhcpv4Exchange from client's query.
    Dhcpv4Exchange createExchange(const Pkt4Ptr& query);
490

491 492 493 494 495 496 497 498 499
    /// @brief Performs 4-way exchange to obtain new lease.
    ///
    /// This is used as a preparatory step for Decline operation.
    ///
    /// @param client Client to be used to obtain a lease.
    void acquireLease(Dhcp4Client& client);

    /// @brief Tests if the acquired lease is or is not declined.
    ///
500
    /// @param client Dhcp4Client instance
501 502 503 504 505 506 507
    /// @param hw_address_1 HW Address to be used to acquire the lease.
    /// @param client_id_1 Client id to be used to acquire the lease.
    /// @param hw_address_2 HW Address to be used to decline the lease.
    /// @param client_id_2 Client id to be used to decline the lease.
    /// @param expected_result SHOULD_PASS if the lease is expected to
    /// be successfully declined, or SHOULD_FAIL if the lease is expected
    /// to not be declined.
508 509
    void acquireAndDecline(Dhcp4Client& client,
                           const std::string& hw_address_1,
510 511 512 513 514
                           const std::string& client_id_1,
                           const std::string& hw_address_2,
                           const std::string& client_id_2,
                           ExpectedResult expected_result);

515
    /// @brief This function cleans up after the test.
516
    virtual void TearDown();
517 518 519 520 521 522 523 524 525 526 527 528 529 530

    /// @brief A subnet used in most tests
    Subnet4Ptr subnet_;

    /// @brief A pool used in most tests
    Pool4Ptr pool_;

    /// @brief A client-id used in most tests
    ClientIdPtr client_id_;

    int rcode_;

    isc::data::ConstElementPtr comment_;

531 532
    /// @brief Server object under test.
    NakedDhcpv4Srv srv_;
533 534
};

Francis Dupont's avatar
Francis Dupont committed
535 536 537
/// @brief Patch the server config to add interface-config/re-detect=false
/// @param json the server config
inline void
538
disableIfacesReDetect(isc::data::ConstElementPtr json) {
Francis Dupont's avatar
Francis Dupont committed
539 540 541 542 543 544 545 546
    isc::data::ConstElementPtr ifaces_cfg = json->get("interfaces-config");
    if (ifaces_cfg) {
        isc::data::ElementPtr mutable_cfg =
            boost::const_pointer_cast<isc::data::Element>(ifaces_cfg);
        mutable_cfg->set("re-detect", isc::data::Element::create(false));
    }
}

547 548 549 550
/// @brief Runs parser in JSON mode, useful for parser testing
///
/// @param in string to be parsed
/// @return ElementPtr structure representing parsed JSON
551
inline isc::data::ElementPtr
552 553 554 555 556 557 558 559 560 561 562 563 564 565
parseJSON(const std::string& in)
{
    isc::dhcp::Parser4Context ctx;
    return (ctx.parseString(in, isc::dhcp::Parser4Context::PARSER_JSON));
}

/// @brief Runs parser in Dhcp4 mode
///
/// This is a simplified Dhcp4 mode, so no outer { } and "Dhcp4" is
/// needed. This format is used by most of the tests.
///
/// @param in string to be parsed
/// @param verbose display the exception message when it fails
/// @return ElementPtr structure representing parsed JSON
566
inline isc::data::ElementPtr
567 568 569 570
parseDHCP4(const std::string& in, bool verbose = false)
{
    try {
        isc::dhcp::Parser4Context ctx;
Francis Dupont's avatar
Francis Dupont committed
571 572
        isc::data::ElementPtr json;
        json = ctx.parseString(in, isc::dhcp::Parser4Context::SUBPARSER_DHCP4);
573
        disableIfacesReDetect(json);
Francis Dupont's avatar
Francis Dupont committed
574
        return (json);
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
    }
    catch (const std::exception& ex) {
        if (verbose) {
            std::cout << "EXCEPTION: " << ex.what() << std::endl;
        }
        throw;
    }
}

/// @brief Runs parser in option definition mode
///
/// This function parses specified text as JSON that defines option definitions.
///
/// @param in string to be parsed
/// @param verbose display the exception message when it fails
/// @return ElementPtr structure representing parsed JSON
591
inline isc::data::ElementPtr
Francis Dupont's avatar
Francis Dupont committed
592
parseOPTION_DEFS(const std::string& in, bool verbose = false)
593 594 595
{
    try {
        isc::dhcp::Parser4Context ctx;
Francis Dupont's avatar
Francis Dupont committed
596
        return (ctx.parseString(in, isc::dhcp::Parser4Context::PARSER_OPTION_DEFS));
597 598 599 600 601 602 603 604 605
    }
    catch (const std::exception& ex) {
        if (verbose) {
            std::cout << "EXCEPTION: " << ex.what() << std::endl;
        }
        throw;
    }
}

606 607 608
}; // end of isc::dhcp::test namespace
}; // end of isc::dhcp namespace
}; // end of isc namespace
609

Tomek Mrugalski's avatar
Tomek Mrugalski committed
610
#endif // DHCP4_TEST_UTILS_H