Coverage Report

Created: 2026-02-14 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kea/fuzz/fuzz_http_endpoint_kea_dhcp4.cc
Line
Count
Source
1
// Copyright (C) 2024-2025 Internet Systems Consortium, Inc. ("ISC")
2
//
3
// This Source Code Form is subject to the terms of the Mozilla Public
4
// License, v. 2.0. If a copy of the MPL was not distributed with this
5
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7
#include <config.h>
8
9
#include <cassert>
10
#include <cstdlib>
11
#include <iostream>
12
13
#include <fuzz.h>
14
15
#include <asiolink/io_service.h>
16
#include <asiolink/interval_timer.h>
17
#include <cc/data.h>
18
#include <config/cmd_http_listener.h>
19
#include <dhcp4/ctrl_dhcp4_srv.h>
20
#include <dhcpsrv/cfgmgr.h>
21
#include <http/listener.h>
22
#include <http/post_request_json.h>
23
#include <http/response.h>
24
#include <http/response_json.h>
25
#include <http/tests/response_test.h>
26
#include <http/testutils/test_http_client.h>
27
#include <process/d_controller.h>
28
#include <util/filesystem.h>
29
#include <util/multi_threading_mgr.h>
30
31
using namespace isc::asiolink;
32
using namespace isc::config;
33
using namespace isc::data;
34
using namespace isc::dhcp;
35
using namespace isc::process;
36
using namespace isc::http;
37
using namespace isc::http::test;
38
using namespace isc::util;
39
using namespace isc::util::file;
40
using namespace std;
41
42
namespace {
43
44
static pid_t const PID(getpid());
45
static string const PID_STR(to_string(PID));
46
static string const ADDRESS("127.0.0.1");
47
static string const KEA_DHCP4_CONF(KEA_FUZZ_DIR() + "/kea-dhcp4-" + PID_STR + ".conf");
48
static string const KEA_DHCP4_CSV(KEA_FUZZ_DIR() + "/kea-dhcp4-" + PID_STR + ".csv");
49
50
static int PORT;
51
static string PORT_STR;
52
53
/// @brief Represents HTTP POST request with JSON body.
54
///
55
/// In addition to the requirements specified by the @ref PostHttpRequest
56
/// this class requires that the "Content-Type" is "application/json".
57
///
58
/// This class provides methods to parse and retrieve JSON data structures.
59
struct PostHttpRequestBytes : PostHttpRequest {
60
    /// @brief Constructor for inbound HTTP request.
61
0
    explicit PostHttpRequestBytes() : PostHttpRequest() {
62
0
        requireHeaderValue("Content-Type", "application/json");
63
0
    }
64
65
    /// @brief Constructor for outbound HTTP request.
66
    ///
67
    /// This constructor adds "Content-Type" header with the value of
68
    /// "application/json" to the context.
69
    ///
70
    /// @param method HTTP method, e.g. POST.
71
    /// @param uri URI.
72
    /// @param version HTTP version.
73
    /// @param host_header Host header to be included in the request. The default
74
    /// is the empty Host header.
75
    /// @param basic_auth Basic HTTP authentication credential. The default
76
    /// is no authentication.
77
    explicit PostHttpRequestBytes(const Method& method,
78
                                  const string& uri,
79
                                  const HttpVersion& version,
80
                                  const HostHttpHeader& host_header = HostHttpHeader(),
81
                                  const BasicHttpAuthPtr& basic_auth = BasicHttpAuthPtr())
82
231
        : PostHttpRequest(method, uri, version, host_header, basic_auth) {
83
231
        requireHeaderValue("Content-Type", "application/json");
84
231
        context()->headers_.push_back(HttpHeaderContext("Content-Type", "application/json"));
85
231
    }
86
87
    /// @brief Sets JSON body for an outbound message.
88
    ///
89
    /// @param body JSON structure to be used as a body.
90
231
    void setBodyAsBytes(vector<uint8_t> const& input) {
91
231
        context_->body_ = string(input.begin(), input.end());
92
231
    }
93
};
94
95
using PostHttpRequestBytesPtr = boost::shared_ptr<PostHttpRequestBytes>;
96
97
}  // namespace
98
99
extern "C" {
100
101
int
102
6
LLVMFuzzerInitialize() {
103
6
    static bool initialized(DoInitialization());
104
6
    assert(initialized);
105
106
6
    setenv("KEA_DHCP4_FUZZING_ROTATE_PORT", "true", 0);
107
108
    // The main focus is on fuzzing the raw HTTP endpoint without the authorization header.
109
    // So bypass the enforcement.
110
6
    PathChecker::enableEnforcement(false);
111
112
6
    return 0;
113
6
}
114
115
int
116
2
LLVMFuzzerTearDown() {
117
2
    try {
118
2
        remove(KEA_DHCP4_CONF.c_str());
119
2
    } catch (...) {
120
0
    }
121
2
    try {
122
0
        remove(KEA_DHCP4_CSV.c_str());
123
0
    } catch (...) {
124
0
    }
125
0
    return 0;
126
0
}
127
128
int
129
231
LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) {
130
231
    CfgMgr::instance().clear();
131
231
    ControlledDhcpv4Srv server;
132
133
231
    PORT = ControlledDhcpv4Srv::getInstance()->getServerPort();
134
231
    PORT_STR = to_string(PORT);
135
136
231
    writeToFile(KEA_DHCP4_CONF, R"(
137
231
      {
138
231
        "Dhcp4": {
139
231
          "control-sockets": [
140
231
            {
141
231
              "socket-address": ")" + ADDRESS + R"(",
142
231
              "socket-port": )" + PORT_STR + R"(,
143
231
              "socket-type": "http"
144
231
            }
145
231
          ],
146
231
          "lease-database": {
147
231
            "name": ")" + KEA_DHCP4_CSV + R"(",
148
231
            "persist": false,
149
231
            "type": "memfile"
150
231
          }
151
231
        }
152
231
      }
153
231
    )");
154
155
231
    server.init(KEA_DHCP4_CONF);
156
157
231
    HttpClient client(ControlledDhcpv4Srv::getInstance()->getIOService(), false);
158
231
    stringstream ss;
159
231
    ss << "http://" << ADDRESS << ":" << PORT_STR;
160
231
    Url url(ss.str());
161
162
    // Initiate request to the server.
163
231
    PostHttpRequestBytesPtr request(new PostHttpRequestBytes(
164
231
        HttpRequest::Method::HTTP_POST, "/", HttpVersion(1, 1)));
165
166
    // Body is a map with a specified parameter included.
167
231
    vector<uint8_t> const body(data, data + size);
168
231
    request->setBodyAsBytes(body);
169
231
    request->finalize();
170
231
    HttpResponseJsonPtr response(new HttpResponseJson());
171
231
    client.asyncSendRequest(
172
231
        url, TlsContextPtr(), request, response,
173
231
        [](boost::system::error_code const&,
174
231
           HttpResponsePtr const&,
175
231
           string const&) {
176
231
        });
177
178
231
    ControlledDhcpv4Srv::getInstance()->getIOService()->poll();
179
180
    // Make sure that the received responses are different. We check that by
181
    // comparing value of the sequence parameters.
182
231
    if (getenv("DEBUG")) {
183
0
        if (response) {
184
0
            cout << response->getBody() << endl;
185
0
        } else {
186
0
            cout << "no response" << endl;
187
0
        }
188
0
    }
189
231
    client.stop();
190
231
    ControlledDhcpv4Srv::getInstance()->getIOService()->poll();
191
231
    MultiThreadingMgr::instance().setMode(false);
192
193
231
    return 0;
194
231
}
195
196
}  // extern "C"