--- /dev/null
+#!/bin/bash -eu
+# Copyright (C) 2025 Ada Logics Ltd.
+#
+# 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/.
+################################################################################
+
+# Compile log4cplus
+cd $SRC/log4cplus
+./configure --prefix=/usr --enable-static --disable-shared --with-pic
+make -j"$(nproc)"
+make install
+
+# Configure flags
+cd $SRC/kea
+export CXXFLAGS="${CXXFLAGS:-} -gdwarf-4"
+export LDFLAGS="${LDFLAGS:-} -gdwarf-4"
+
+CPP_ARGS="-stdlib=libc++ \
+ -DCHRONO_SAME_DURATION=1 -D_LIBCPP_ENABLE_CXX17_REMOVED_AUTO_PTR \
+ -D_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION -D_GLIBCXX_USE_DEPRECATED=1"
+LD_ARGS="-stdlib=libc++"
+
+if [ "$SANITIZER" = "coverage" ]; then
+ CPP_ARGS="${CPP_ARGS} -fprofile-instr-generate -fcoverage-mapping"
+fi
+
+if [ "$SANITIZER" = "coverage" ] || [ "$SANITIZER" = "introspector" ] || [ "$SANITIZER" = "none" ]; then
+ SANITIZER_CHOICE=
+else
+ SANITIZER_CHOICE="-D b_sanitize=${SANITIZER}"
+ CPP_ARGS="${CPP_ARGS} -fsanitize=fuzzer-no-link"
+fi
+
+meson setup build --prefix="$OUT" $SANITIZER_CHOICE -D cpp_std=c++17 \
+ -D fuzz=enabled -D tests=enabled -D crypto=openssl -D default_library=static \
+ -D default_both_libraries=static -D cpp_args="$CPP_ARGS" -D cpp_link_args="$LD_ARGS" \
+ -D postgresql=enabled -D mysql=enabled -D krb5=enabled -D b_ndebug=true
+meson compile --verbose -C build
+
+# Package static library
+find $SRC/kea/build/src/lib -type f -name '*.o' -print0 | xargs -0 llvm-ar rcsD libkea.a
+llvm-ranlib libkea.a
+
+# Find necessary static libraries
+BUILD_BASEDIR="$SRC/kea/build/src"
+KEA_STATIC_LIBS="/usr/lib/liblog4cplus.a libkea.a "
+KEA_STATIC_LIBS+=$(find $BUILD_BASEDIR/bin \( -path '/src/kea/build/src/bin/dhcp4/*' -o -path '/src/kea/build/src/bin/dhcp6/*' \) -prune -o -type f -name '*.a' -print)
+KEA_STATIC_LIBS_TEST="$KEA_STATIC_LIBS $SRC/kea/build/subprojects/googletest-1.15.2/googletest/libgtest-all.a"
+
+INCLUDES="-I. -I$SRC -I$SRC/kea-fuzzer -Isrc -Ibuild -Isrc/lib -Isrc/bin -Isrc/hooks -Isrc/hooks/d2 -Isrc/hooks/d2/gss_tsig "
+INCLUDES+="-Isrc/hooks/dhcp/pgsql -Isrc/hooks/dhcp/mysql -Isrc/hooks/dhcp/user_chk -I/usr/include/postgresql -I/usr/include/mariadb"
+KEA_INCLUDES="$INCLUDES -I/src/kea/subprojects/googletest-1.15.2/googletest/include -Ifuzz"
+LIBS="-lpthread -ldl -lm -lc++ -lc++abi -lssl -lcrypto -lkrb5 -lgssapi_krb5"
+export CXXFLAGS="${CXXFLAGS} -std=c++17 -stdlib=libc++ -Wno-unused-parameter -Wno-unused-value"
+
+# Build non-dhcp specific fuzzers
+for fuzzer in fuzz_cc fuzz_d2 fuzz_agent fuzz_util fuzz_dhcpsrv fuzz_dhcpsrv_csv_lease fuzz_dns fuzz_encode fuzz_cryptolink
+do
+ extra_lib=""
+ case "$fuzzer" in fuzz_hook_tsig)
+ extra_lib="$SRC/kea/build/src/hooks/d2/gss_tsig/libddns_gss_tsig.a"
+ ;;
+ esac
+
+ # fuzz_dns, fuzz_encode, and fuzz_cryptolink don't need helper_func.cc
+ if [ "$fuzzer" = "fuzz_dns" ] || [ "$fuzzer" = "fuzz_encode" ] || [ "$fuzzer" = "fuzz_cryptolink" ]; then
+ $CXX $CXXFLAGS "$SRC/kea-fuzzer/${fuzzer}.cc" \
+ -Wl,--start-group $KEA_STATIC_LIBS $extra_lib -Wl,--end-group \
+ $INCLUDES $LIBS $LIB_FUZZING_ENGINE -o "$OUT/${fuzzer}"
+ else
+ $CXX $CXXFLAGS "$SRC/kea-fuzzer/helper_func.cc" \
+ "$SRC/kea-fuzzer/${fuzzer}.cc" \
+ -Wl,--start-group $KEA_STATIC_LIBS $extra_lib -Wl,--end-group \
+ $INCLUDES $LIBS $LIB_FUZZING_ENGINE -o "$OUT/${fuzzer}"
+ fi
+
+ if [ -f "$SRC/kea-fuzzer/${fuzzer}.dict" ]; then
+ cp $SRC/kea-fuzzer/${fuzzer}.dict $OUT
+ fi
+done
+
+for DHCPVER in 4 6
+do
+ for fuzzer in fuzz_dhcp_parser fuzz_eval fuzz_dhcp_pkt fuzz_pgsql \
+ fuzz_mysql fuzz_dhcp_pkt_process fuzz_hook_run_script \
+ fuzz_hook_radius fuzz_hook_ddns_tuning fuzz_hook_lease_query \
+ fuzz_hook_flex_id fuzz_hook_user_chk
+ do
+ extra_lib=""
+ case "$fuzzer" in fuzz_pgsql)
+ extra_lib="$SRC/kea-fuzzer/pgmock.cc "
+ extra_lib+="$SRC/kea/build/src/hooks/dhcp/pgsql/libdhcp_pgsql.a"
+ ;;
+ esac
+ case "$fuzzer" in fuzz_mysql)
+ extra_lib="$SRC/kea-fuzzer/mysqlmock.cc "
+ extra_lib+="$SRC/kea/build/src/hooks/dhcp/mysql/libdhcp_mysql.a"
+ ;;
+ esac
+ case "$fuzzer" in fuzz_dhcp_pkt_process)
+ extra_lib="$SRC/kea/build/src/hooks/dhcp/lease_cmds/libdhcp_lease_cmds.a"
+ cp $SRC/kea-fuzzer/fuzz_dhcp_pkt.dict $OUT/${fuzzer}${DHCPVER}.dict
+ ;;
+ esac
+ case "$fuzzer" in fuzz_hook_run_script)
+ extra_lib="$SRC/kea/build/src/hooks/dhcp/run_script/libdhcp_run_script.a"
+ cp $SRC/kea-fuzzer/fuzz_dhcp_pkt.dict $OUT/${fuzzer}${DHCPVER}.dict
+ ;;
+ esac
+ case "$fuzzer" in fuzz_hook_radius)
+ extra_lib="$SRC/kea/build/src/hooks/dhcp/radius/libdhcp_radius.a"
+ cp $SRC/kea-fuzzer/fuzz_dhcp_pkt.dict $OUT/${fuzzer}${DHCPVER}.dict
+ ;;
+ esac
+ case "$fuzzer" in fuzz_hook_ddns_tuning)
+ extra_lib="$SRC/kea/build/src/hooks/dhcp/ddns_tuning/libdhcp_ddns_tuning.a"
+ cp $SRC/kea-fuzzer/fuzz_dhcp_pkt.dict $OUT/${fuzzer}${DHCPVER}.dict
+ ;;
+ esac
+ case "$fuzzer" in fuzz_hook_lease_query)
+ extra_lib="$SRC/kea/build/src/hooks/dhcp/lease_query/libdhcp_lease_query.a"
+ cp $SRC/kea-fuzzer/fuzz_dhcp_pkt.dict $OUT/${fuzzer}${DHCPVER}.dict
+ ;;
+ esac
+ case "$fuzzer" in fuzz_hook_flex_id)
+ extra_lib="$SRC/kea/build/src/hooks/dhcp/flex_id/libdhcp_flex_id.a"
+ cp $SRC/kea-fuzzer/fuzz_dhcp_pkt.dict $OUT/${fuzzer}${DHCPVER}.dict
+ ;;
+ esac
+ case "$fuzzer" in fuzz_hook_user_chk)
+ extra_lib="$SRC/kea/build/src/hooks/dhcp/user_chk/libdhcp_user_chk.a"
+ cp $SRC/kea-fuzzer/fuzz_dhcp_pkt.dict $OUT/${fuzzer}${DHCPVER}.dict
+ ;;
+ esac
+
+ $CXX $CXXFLAGS -Wl,--start-group "$SRC/kea-fuzzer/helper_func.cc" \
+ "$SRC/kea-fuzzer/${fuzzer}${DHCPVER}.cc" $extra_lib \
+ $KEA_STATIC_LIBS $BUILD_BASEDIR/bin/dhcp$DHCPVER/libdhcp$DHCPVER.a \
+ -Wl,--end-group $INCLUDES $LIBS \
+ $LIB_FUZZING_ENGINE -o "$OUT/${fuzzer}${DHCPVER}"
+
+ if [ -f "$SRC/kea-fuzzer/${fuzzer}.dict" ]; then
+ cp $SRC/kea-fuzzer/${fuzzer}.dict $OUT/${fuzzer}${DHCPVER}.dict
+ fi
+ done
+
+ # Compile fuzzers from kea repository
+ for fuzzer in fuzz_config_kea_dhcp fuzz_http_endpoint_kea_dhcp fuzz_packets_kea_dhcp fuzz_unix_socket_kea_dhcp
+ do
+ # Skip fuzz_http_endpoint_kea_dhcp6 as it requires real IPv6 binding which is not enabled in OSS-Fuzz
+ if [ "$fuzzer" = "fuzz_http_endpoint_kea_dhcp" ] && [ "$DHCPVER" = "6" ]; then
+ continue
+ fi
+
+ $CXX $CXXFLAGS -Wl,--start-group "$SRC/kea/fuzz/${fuzzer}${DHCPVER}.cc" \
+ $SRC/kea/fuzz/fuzz.cc $KEA_STATIC_LIBS_TEST \
+ $BUILD_BASEDIR/bin/dhcp$DHCPVER/libdhcp$DHCPVER.a \
+ $KEA_INCLUDES $LIBS $LIB_FUZZING_ENGINE -Wl,--end-group \
+ -o "$OUT/${fuzzer}${DHCPVER}"
+ done
+done
+
+# Prepare maximum size option for configuration parsing related fuzzers
+for fuzzer in fuzz_eval4 fuzz_eval6 fuzz_dhcp_parser4 fuzz_dhcp_parser6 \
+ fuzz_dhcp_pkt4 fuzz_dhcp_pkt6 fuzz_cc fuzz_d2 fuzz_agent \
+ fuzz_config_kea_dhcp4 fuzz_config_kea_dhcp6
+do
+ echo -e "[libfuzzer]\nmax_len=25600" > $OUT/$fuzzer.options
+done
+
+# Prepare the seeds
+zip -j $OUT/fuzz_dhcpsrv_seed_corpus.zip $SRC/kea-fuzzer/corp/*.json
+zip -j $OUT/fuzz_dhcp_parser4_seed_corpus.zip $SRC/kea-fuzzer/corp/*.json
+zip -j $OUT/fuzz_dhcp_parser6_seed_corpus.zip $SRC/kea-fuzzer/corp/*.json
+zip -j $OUT/fuzz_agent_seed_corpus.zip $SRC/kea/src/bin/agent/tests/testdata/*.json
+zip -j $OUT/fuzz_d2_seed_corpus.zip $SRC/kea/src/bin/d2/tests/testdata/*.json
--- /dev/null
+// This is an example configuration of the Kea DHCPv4 server 1:
+//
+// - uses High Availability hooks library and Lease Commands hooks library
+// to enable High Availability function for the DHCP server. This config
+// file is for the primary (the active) server.
+// - uses memfile, which stores lease data in a local CSV file
+// - it assumes a single /24 addressing over a link that is directly reachable
+// (no DHCP relays)
+// - there is a handful of IP reservations
+//
+// It is expected to run with a standby (the passive) server, which has a very similar
+// configuration. The only difference is that "this-server-name" must be set to "server2" on the
+// other server. Also, the interface configuration depends on the network settings of the
+// particular machine.
+
+{
+
+"Dhcp4": {
+
+ // Add names of your network interfaces to listen on.
+ "interfaces-config": {
+ // The DHCPv4 server listens on this interface. When changing this to
+ // the actual name of your interface, make sure to also update the
+ // interface parameter in the subnet definition below.
+ "interfaces": [ "enp0s8" ]
+ },
+
+ // Control socket is required for communication between the Control
+ // Agent and the DHCP server. High Availability requires Control Agent
+ // to be running because lease updates are sent over the RESTful
+ // API between the HA peers.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Use Memfile lease database backend to store leases in a CSV file.
+ // Depending on how Kea was compiled, it may also support SQL databases
+ // (MySQL and/or PostgreSQL) and even Cassandra. Those database backends
+ // require more parameters, like name, host and possibly user and password.
+ // There are dedicated examples for each backend. See Section 7.2.2 "Lease
+ // Storage" for details.
+ "lease-database": {
+ // Memfile is the simplest and easiest backend to use. It's an in-memory
+ // database with data being written to a CSV file. It is very similar to
+ // what ISC DHCP does.
+ "type": "memfile"
+ },
+
+ // Let's configure some global parameters. The home network is not very dynamic
+ // and there's no shortage of addresses, so no need to recycle aggresively.
+ "valid-lifetime": 43200, // leases will be valid for 12h
+ "renew-timer": 21600, // clients should renew every 6h
+ "rebind-timer": 32400, // clients should start looking for other servers after 9h
+
+ // Kea will clean up its database of expired leases once per hour. However, it
+ // will keep the leases in expired state for 2 days. This greatly increases the
+ // chances for returning devices to get the same address again. To guarantee that,
+ // use host reservation.
+ "expired-leases-processing": {
+ "reclaim-timer-wait-time": 3600,
+ "hold-reclaimed-time": 172800,
+ "max-reclaim-leases": 0,
+ "max-reclaim-time": 0
+ },
+
+ // HA requires two hooks libraries to be loaded: libdhcp_lease_cmds.so and
+ // libdhcp_ha.so. The former handles incoming lease updates from the HA peers.
+ // The latter implements high availability feature for Kea. Note the library name
+ // should be the same, but the path is OS specific.
+ "hooks-libraries": [
+ // The lease_cmds library must be loaded because HA makes use of it to
+ // deliver lease updates to the server as well as synchronize the
+ // lease database after failure.
+ {
+ "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
+ },
+
+ {
+ // The HA hooks library should be loaded.
+ "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ // Each server should have the same HA configuration, except for the
+ // "this-server-name" parameter.
+ "high-availability": [ {
+ // This parameter points to this server instance. The respective
+ // HA peers must have this parameter set to their own names.
+ "this-server-name": "server1",
+ // The HA mode is set to hot-standby. In this mode, the active server handles
+ // all the traffic. The standby takes over if the primary becomes unavailable.
+ "mode": "hot-standby",
+ // Heartbeat is to be sent every 10 seconds if no other control
+ // commands are transmitted.
+ "heartbeat-delay": 10000,
+ // Maximum time for partner's response to a heartbeat, after which
+ // failure detection is started. This is specified in milliseconds.
+ // If we don't hear from the partner in 60 seconds, it's time to
+ // start worrying.
+ "max-response-delay": 60000,
+ // The following parameters control how the server detects the
+ // partner's failure. The ACK delay sets the threshold for the
+ // 'secs' field of the received discovers. This is specified in
+ // milliseconds.
+ "max-ack-delay": 5000,
+ // This specifies the number of clients which send messages to
+ // the partner but appear to not receive any response.
+ "max-unacked-clients": 5,
+ // This specifies the maximum timeout (in milliseconds) for the server
+ // to complete sync. If you have a large deployment (high tens or
+ // hundreds of thousands of clients), you may need to increase it
+ // further. The default value is 60000ms (60 seconds).
+ "sync-timeout": 60000,
+ "peers": [
+ // This is the configuration of this server instance.
+ {
+ "name": "server1",
+ // This specifies the URL of our server instance. The
+ // Control Agent must run along with our DHCPv4 server
+ // instance and the "http-host" and "http-port" must be
+ // set to the corresponding values.
+ "url": "http://192.168.1.2:8000/",
+ // This server is primary. The other one must be
+ // secondary.
+ "role": "primary"
+ },
+ // This is the configuration of our HA peer.
+ {
+ "name": "server2",
+ // Specifies the URL on which the partner's control
+ // channel can be reached. The Control Agent is required
+ // to run on the partner's machine with "http-host" and
+ // "http-port" values set to the corresponding values.
+ "url": "http://192.168.1.3:8000/",
+ // The partner is a secondary. Our is primary.
+ "role": "standby"
+ }
+ ]
+ } ]
+ }
+ }
+ ],
+
+ // This example contains a single subnet declaration.
+ "subnet4": [
+ {
+ // Subnet prefix.
+ "subnet": "192.168.1.0/24",
+
+ // There are no relays in this network, so we need to tell Kea that this subnet
+ // is reachable directly via the specified interface.
+ "interface": "enp0s8",
+
+ // Specify a dynamic address pool.
+ "pools": [
+ {
+ "pool": "192.168.1.100-192.168.1.199"
+ }
+ ],
+
+ // These are options that are subnet specific. In most cases, you need to define at
+ // least routers option, as without this option your clients will not be able to reach
+ // their default gateway and will not have Internet connectivity. If you have many
+ // subnets and they share the same options (e.g. DNS servers typically is the same
+ // everywhere), you may define options at the global scope, so you don't repeat them
+ // for every network.
+ "option-data": [
+ {
+ // For each IPv4 subnet you typically need to specify at least one router.
+ "name": "routers",
+ "data": "192.168.1.1"
+ },
+ {
+ // Using cloudflare or Quad9 is a reasonable option. Change this
+ // to your own DNS servers is you have them. Another popular
+ // choice is 8.8.8.8, owned by Google. Using third party DNS
+ // service raises some privacy concerns.
+ "name": "domain-name-servers",
+ "data": "1.1.1.1,9.9.9.9"
+ }
+ ],
+
+ // Some devices should get a static address. Since the .100 - .199 range is dynamic,
+ // let's use the lower address space for this. There are many ways how reservation
+ // can be defined, but using MAC address (hw-address) is by far the most popular one.
+ // You can use client-id, duid and even custom defined flex-id that may use whatever
+ // parts of the packet you want to use as identifiers. Also, there are many more things
+ // you can specify in addition to just an IP address: extra options, next-server, hostname,
+ // assign device to client classes etc. See the Kea ARM, Section 8.3 for details.
+ // The reservations are subnet specific.
+ "reservations": [
+ {
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "ip-address": "192.168.1.10"
+ },
+ {
+ "client-id": "01:11:22:33:44:55:66",
+ "ip-address": "192.168.1.11"
+ }
+ ]
+ }
+ ],
+
+ // Logging configuration starts here.
+ "loggers": [
+ {
+ // This section affects kea-dhcp4, which is the base logger for DHCPv4 component. It tells
+ // DHCPv4 server to write all log messages (on severity INFO or higher) to a file. The file
+ // will be rotated once it grows to 2MB and up to 4 files will be kept. The debuglevel
+ // (range 0 to 99) is used only when logging on DEBUG level.
+ "name": "kea-dhcp4",
+ "output_options": [
+ {
+ "output": "/var/log/kea-dhcp4.log",
+ "maxsize": 2048000,
+ "maxver": 4
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0
+ }
+ ]
+}
+}
--- /dev/null
+// This is an example configuration of the Kea DHCPv4 server 2:
+//
+// - uses High Availability hooks library and Lease Commands hooks library
+// to enable High Availability function for the DHCP server. This config
+// file is for the primary (the active) server.
+// - uses memfile, which stores lease data in a local CSV file
+// - it assumes a single /24 addressing over a link that is directly reachable
+// (no DHCP relays)
+// - there is a handful of IP reservations
+//
+// It is expected to run with a primary (the active) server, which has a very similar
+// configuration. The only difference is that "this-server-name" must be set to "server2" on the
+// other server. Also, the interface configuration depends on the network settings of the
+// particular machine.
+
+{
+
+"Dhcp4": {
+
+ // Add names of your network interfaces to listen on.
+ "interfaces-config": {
+ // The DHCPv4 server listens on this interface. When changing this to
+ // the actual name of your interface, make sure to also update the
+ // interface parameter in the subnet definition below.
+ "interfaces": [ "enp0s8" ]
+ },
+
+ // Control socket is required for communication between the Control
+ // Agent and the DHCP server. High Availability requires Control Agent
+ // to be running because lease updates are sent over the RESTful
+ // API between the HA peers.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Use Memfile lease database backend to store leases in a CSV file.
+ // Depending on how Kea was compiled, it may also support SQL databases
+ // (MySQL and/or PostgreSQL) and even Cassandra. Those database backends
+ // require more parameters, like name, host and possibly user and password.
+ // There are dedicated examples for each backend. See Section 7.2.2 "Lease
+ // Storage" for details.
+ "lease-database": {
+ // Memfile is the simplest and easiest backend to use. It's an in-memory
+ // database with data being written to a CSV file. It is very similar to
+ // what ISC DHCP does.
+ "type": "memfile"
+ },
+
+ // Let's configure some global parameters. The home network is not very dynamic
+ // and there's no shortage of addresses, so no need to recycle aggresively.
+ "valid-lifetime": 43200, // leases will be valid for 12h
+ "renew-timer": 21600, // clients should renew every 6h
+ "rebind-timer": 32400, // clients should start looking for other servers after 9h
+
+ // Kea will clean up its database of expired leases once per hour. However, it
+ // will keep the leases in expired state for 2 days. This greatly increases the
+ // chances for returning devices to get the same address again. To guarantee that,
+ // use host reservation.
+ "expired-leases-processing": {
+ "reclaim-timer-wait-time": 3600,
+ "hold-reclaimed-time": 172800,
+ "max-reclaim-leases": 0,
+ "max-reclaim-time": 0
+ },
+
+ // HA requires two hooks libraries to be loaded: libdhcp_lease_cmds.so and
+ // libdhcp_ha.so. The former handles incoming lease updates from the HA peers.
+ // The latter implements high availability feature for Kea. Note the library name
+ // should be the same, but the path is OS specific.
+ "hooks-libraries": [
+ // The lease_cmds library must be loaded because HA makes use of it to
+ // deliver lease updates to the server as well as synchronize the
+ // lease database after failure.
+ {
+ "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
+ },
+
+ {
+ // The HA hooks library should be loaded.
+ "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ // Each server should have the same HA configuration, except for the
+ // "this-server-name" parameter.
+ "high-availability": [ {
+ // This parameter points to this server instance. The respective
+ // HA peers must have this parameter set to their own names.
+ "this-server-name": "server2",
+ // The HA mode is set to hot-standby. In this mode, the active server handles
+ // all the traffic. The standby takes over if the primary becomes unavailable.
+ "mode": "hot-standby",
+ // Heartbeat is to be sent every 10 seconds if no other control
+ // commands are transmitted.
+ "heartbeat-delay": 10000,
+ // Maximum time for partner's response to a heartbeat, after which
+ // failure detection is started. This is specified in milliseconds.
+ // If we don't hear from the partner in 60 seconds, it's time to
+ // start worrying.
+ "max-response-delay": 60000,
+ // The following parameters control how the server detects the
+ // partner's failure. The ACK delay sets the threshold for the
+ // 'secs' field of the received discovers. This is specified in
+ // milliseconds.
+ "max-ack-delay": 5000,
+ // This specifies the number of clients which send messages to
+ // the partner but appear to not receive any response.
+ "max-unacked-clients": 5,
+ // This specifies the maximum timeout (in milliseconds) for the server
+ // to complete sync. If you have a large deployment (high tens or
+ // hundreds of thousands of clients), you may need to increase it
+ // further. The default value is 60000ms (60 seconds).
+ "sync-timeout": 60000,
+ "peers": [
+ // This is the configuration of this server instance.
+ {
+ "name": "server1",
+ // This specifies the URL of the partner's server instance. The
+ // Control Agent must run along with partner DHCPv4 server
+ // instance and the "http-host" and "http-port" must be
+ // set to the corresponding values.
+ "url": "http://192.168.1.2:8000/",
+ // That server is primary. Our server must be
+ // standby.
+ "role": "primary"
+ },
+ // This is our (server2) configuration.
+ {
+ "name": "server2",
+ // Specifies the URL on which the control agent for this server
+ // can be reached. The Control Agent is required
+ // to run on this machine with "http-host" and
+ // "http-port" values set to the corresponding values.
+ "url": "http://192.168.1.3:8000/",
+ // Our server is standby. The other server is primary.
+ "role": "standby"
+ }
+ ]
+ } ]
+ }
+ }
+ ],
+
+ // This example contains a single subnet declaration.
+ "subnet4": [
+ {
+ // Subnet prefix.
+ "subnet": "192.168.1.0/24",
+
+ // There are no relays in this network, so we need to tell Kea that this subnet
+ // is reachable directly via the ethX interface.
+ "interface": "enp0s8",
+
+ // Specify a dynamic address pool.
+ "pools": [
+ {
+ "pool": "192.168.1.100-192.168.1.199"
+ }
+ ],
+
+ // These are options that are subnet specific. In most cases, you need to define at
+ // least routers option, as without this option your clients will not be able to reach
+ // their default gateway and will not have Internet connectivity. If you have many
+ // subnets and they share the same options (e.g. DNS servers typically is the same
+ // everywhere), you may define options at the global scope, so you don't repeat them
+ // for every network.
+ "option-data": [
+ {
+ // For each IPv4 subnet you typically need to specify at least one router.
+ "name": "routers",
+ "data": "192.168.1.1"
+ },
+ {
+ // Using cloudflare or Quad9 is a reasonable option. Change this
+ // to your own DNS servers is you have them. Another popular
+ // choice is 8.8.8.8, owned by Google. Using third party DNS
+ // service raises some privacy concerns.
+ "name": "domain-name-servers",
+ "data": "1.1.1.1,9.9.9.9"
+ }
+ ],
+
+ // Some devices should get a static address. Since the .100 - .199 range is dynamic,
+ // let's use the lower address space for this. There are many ways how reservation
+ // can be defined, but using MAC address (hw-address) is by far the most popular one.
+ // You can use client-id, duid and even custom defined flex-id that may use whatever
+ // parts of the packet you want to use as identifiers. Also, there are many more things
+ // you can specify in addition to just an IP address: extra options, next-server, hostname,
+ // assign device to client classes etc. See the Kea ARM, Section 8.3 for details.
+ // The reservations are subnet specific.
+ "reservations": [
+ {
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "ip-address": "192.168.1.10"
+ },
+ {
+ "client-id": "01:11:22:33:44:55:66",
+ "ip-address": "192.168.1.11"
+ }
+ ]
+ }
+ ],
+
+ // Logging configuration starts here.
+ "loggers": [
+ {
+ // This section affects kea-dhcp4, which is the base logger for DHCPv4 component. It tells
+ // DHCPv4 server to write all log messages (on severity INFO or higher) to a file. The file
+ // will be rotated once it grows to 2MB and up to 4 files will be kept. The debuglevel
+ // (range 0 to 99) is used only when logging on DEBUG level.
+ "name": "kea-dhcp4",
+ "output_options": [
+ {
+ "output": "/var/log/kea-dhcp4.log",
+ "maxsize": 2048000,
+ "maxver": 4
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0
+ }
+ ]
+}
+}
--- /dev/null
+{
+# DHCPv6 configuration starts on the next line
+"Dhcp6": {
+
+# First we set up global values
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+ "preferred-lifetime": 3000,
+
+# Next we set up the interfaces to be used by the server.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+# And we specify the type of lease database
+ "lease-database": {
+ "type": "memfile",
+ "persist": true,
+ "name": "/var/lib/kea/dhcp6.leases"
+ },
+
+# Finally, we list the subnets from which we will be leasing addresses.
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "pools": [
+ {
+ "pool": "2001:db8:1::1-2001:db8:1::ffff"
+ }
+ ]
+ }
+ ]
+# DHCPv6 configuration ends with the next line
+}
+
+}
--- /dev/null
+{
+ "Dhcp6": {
+ "client-classes": [
+ {
+ "name": "reserved_class"
+ },
+ {
+ "name": "unreserved_class",
+ "test": "not member('reserved_class')"
+ }
+ ],
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:fe",
+ "client-classes": [ "reserved_class" ]
+ }
+ ],
+ "reservations-global": true,
+ "reservations-in-subnet": false,
+ "shared-networks": [
+ {
+ "name": "net",
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "pools": [
+ {
+ "pool": "2001:db8:1::10 - 2001:db8:1::20",
+ "client-classes": [ "unreserved_class" ]
+ },
+ {
+ "pool": "2001:db8:1::30 - 2001:db8:1::40",
+ "client-classes": [ "unreserved_class" ]
+ }
+ ]
+ },
+ {
+ "id": 2,
+ "subnet": "2001:db8:2::/64",
+ "pools": [
+ {
+ "pool": "2001:db8:2::10 - 2001:db8:2::20",
+ "client-classes": [ "reserved_class" ]
+ },
+ {
+ "pool": "2001:db8:2::30 - 2001:db8:2::40",
+ "client-classes": [ "reserved_class" ]
+ }
+ ]
+ },
+ {
+ "id": 3,
+ "subnet": "2001:db8:3::/64",
+ "pools": [
+ {
+ "pool": "2001:db8:3::10 - 2001:db8:3::20"
+ },
+ {
+ "pool": "2001:db8:3::30 - 2001:db8:3::40"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
--- /dev/null
+"Dhcp6": {
+ "client-classes": [
+ {
+ "name": "blocked",
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8::2"
+ }
+ ]
+ }
+ ],
+ "reservations": [
+ // Clients on this list will be added to the KNOWN class. Some
+ // will also be added to the blocked class.
+ { "duid": "01:02:03:04:05:0A:0B:0C:0D:0E",
+ "client-classes": [ "blocked" ] },
+ { "duid": "02:03:04:05:0A:0B:0C:0D:0E:0F" }
+ ],
+ "reservations-in-subnet": true,
+
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/48",
+ "pools": [
+ {
+ "pool": "2001:db8:1:1::/64"
+ }
+ ],
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8::1"
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "Dhcp6": {
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+ "preferred-lifetime": 3000,
+ "interfaces-config": {
+ "interfaces": ["eth0"]
+ },
+ "lease-database": {
+ "type": "memfile",
+ "persist": true,
+ "name": "/var/lib/kea/dhcp6.leases"
+ },
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "dns-servers",
+ "data": "2001:db8::1, 2001:db8::2"
+ }
+ ],
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1::/64",
+ "id": 1024,
+ "pools": [
+ {
+ "start": "2001:db8:1::100",
+ "end": "2001:db8:1::200"
+ }
+ ]
+ }
+ ]
+ }
+}
--- /dev/null
+lease_id,ip_address,subnet_id,state,valid_since,valid_until,mac_address,client_id,hostname
+1,192.168.1.100,1,0,2023-10-27 10:00:00,2023-10-27 11:00:00,00:11:22:33:44:55,None,device1
+2,192.168.1.101,1,0,2023-10-27 10:05:00,2023-10-27 11:05:00,AA:BB:CC:DD:EE:FF,None,device2
\ No newline at end of file
--- /dev/null
+EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE_NUMBER,HIRE_DATE,JOB_ID,SALARY,COMMISSION_PCT,MANAGER_ID,DEPARTMENT_ID
+198,Donald,OConnell,DOCONNEL,650.507.9833,21-JUN-07,SH_CLERK,2600, - ,124,50
+199,Douglas,Grant,DGRANT,650.507.9844,13-JAN-08,SH_CLERK,2600, - ,124,50
+200,Jennifer,Whalen,JWHALEN,515.123.4444,17-SEP-03,AD_ASST,4400, - ,101,10
+201,Michael,Hartstein,MHARTSTE,515.123.5555,17-FEB-04,MK_MAN,13000, - ,100,20
+202,Pat,Fay,PFAY,603.123.6666,17-AUG-05,MK_REP,6000, - ,201,20
+203,Susan,Mavris,SMAVRIS,515.123.7777,07-JUN-02,HR_REP,6500, - ,101,40
+204,Hermann,Baer,HBAER,515.123.8888,07-JUN-02,PR_REP,10000, - ,101,70
+205,Shelley,Higgins,SHIGGINS,515.123.8080,07-JUN-02,AC_MGR,12008, - ,101,110
+206,William,Gietz,WGIETZ,515.123.8181,07-JUN-02,AC_ACCOUNT,8300, - ,205,110
+100,Steven,King,SKING,515.123.4567,17-JUN-03,AD_PRES,24000, - , - ,90
+101,Neena,Kochhar,NKOCHHAR,515.123.4568,21-SEP-05,AD_VP,17000, - ,100,90
+102,Lex,De Haan,LDEHAAN,515.123.4569,13-JAN-01,AD_VP,17000, - ,100,90
+103,Alexander,Hunold,AHUNOLD,590.423.4567,03-JAN-06,IT_PROG,9000, - ,102,60
+104,Bruce,Ernst,BERNST,590.423.4568,21-MAY-07,IT_PROG,6000, - ,103,60
+105,David,Austin,DAUSTIN,590.423.4569,25-JUN-05,IT_PROG,4800, - ,103,60
+106,Valli,Pataballa,VPATABAL,590.423.4560,05-FEB-06,IT_PROG,4800, - ,103,60
+107,Diana,Lorentz,DLORENTZ,590.423.5567,07-FEB-07,IT_PROG,4200, - ,103,60
+108,Nancy,Greenberg,NGREENBE,515.124.4569,17-AUG-02,FI_MGR,12008, - ,101,100
+109,Daniel,Faviet,DFAVIET,515.124.4169,16-AUG-02,FI_ACCOUNT,9000, - ,108,100
+110,John,Chen,JCHEN,515.124.4269,28-SEP-05,FI_ACCOUNT,8200, - ,108,100
+111,Ismael,Sciarra,ISCIARRA,515.124.4369,30-SEP-05,FI_ACCOUNT,7700, - ,108,100
+112,Jose Manuel,Urman,JMURMAN,515.124.4469,07-MAR-06,FI_ACCOUNT,7800, - ,108,100
+113,Luis,Popp,LPOPP,515.124.4567,07-DEC-07,FI_ACCOUNT,6900, - ,108,100
+114,Den,Raphaely,DRAPHEAL,515.127.4561,07-DEC-02,PU_MAN,11000, - ,100,30
+115,Alexander,Khoo,AKHOO,515.127.4562,18-MAY-03,PU_CLERK,3100, - ,114,30
+116,Shelli,Baida,SBAIDA,515.127.4563,24-DEC-05,PU_CLERK,2900, - ,114,30
+117,Sigal,Tobias,STOBIAS,515.127.4564,24-JUL-05,PU_CLERK,2800, - ,114,30
+118,Guy,Himuro,GHIMURO,515.127.4565,15-NOV-06,PU_CLERK,2600, - ,114,30
+119,Karen,Colmenares,KCOLMENA,515.127.4566,10-AUG-07,PU_CLERK,2500, - ,114,30
+120,Matthew,Weiss,MWEISS,650.123.1234,18-JUL-04,ST_MAN,8000, - ,100,50
+121,Adam,Fripp,AFRIPP,650.123.2234,10-APR-05,ST_MAN,8200, - ,100,50
+122,Payam,Kaufling,PKAUFLIN,650.123.3234,01-MAY-03,ST_MAN,7900, - ,100,50
+123,Shanta,Vollman,SVOLLMAN,650.123.4234,10-OCT-05,ST_MAN,6500, - ,100,50
+124,Kevin,Mourgos,KMOURGOS,650.123.5234,16-NOV-07,ST_MAN,5800, - ,100,50
+125,Julia,Nayer,JNAYER,650.124.1214,16-JUL-05,ST_CLERK,3200, - ,120,50
+126,Irene,Mikkilineni,IMIKKILI,650.124.1224,28-SEP-06,ST_CLERK,2700, - ,120,50
+127,James,Landry,JLANDRY,650.124.1334,14-JAN-07,ST_CLERK,2400, - ,120,50
+128,Steven,Markle,SMARKLE,650.124.1434,08-MAR-08,ST_CLERK,2200, - ,120,50
+129,Laura,Bissot,LBISSOT,650.124.5234,20-AUG-05,ST_CLERK,3300, - ,121,50
+130,Mozhe,Atkinson,MATKINSO,650.124.6234,30-OCT-05,ST_CLERK,2800, - ,121,50
+131,James,Marlow,JAMRLOW,650.124.7234,16-FEB-05,ST_CLERK,2500, - ,121,50
+132,TJ,Olson,TJOLSON,650.124.8234,10-APR-07,ST_CLERK,2100, - ,121,50
+133,Jason,Mallin,JMALLIN,650.127.1934,14-JUN-04,ST_CLERK,3300, - ,122,50
+134,Michael,Rogers,MROGERS,650.127.1834,26-AUG-06,ST_CLERK,2900, - ,122,50
+135,Ki,Gee,KGEE,650.127.1734,12-DEC-07,ST_CLERK,2400, - ,122,50
+136,Hazel,Philtanker,HPHILTAN,650.127.1634,06-FEB-08,ST_CLERK,2200, - ,122,50
+137,Renske,Ladwig,RLADWIG,650.121.1234,14-JUL-03,ST_CLERK,3600, - ,123,50
+138,Stephen,Stiles,SSTILES,650.121.2034,26-OCT-05,ST_CLERK,3200, - ,123,50
+139,John,Seo,JSEO,650.121.2019,12-FEB-06,ST_CLERK,2700, - ,123,50
+140,Joshua,Patel,JPATEL,650.121.1834,06-APR-06,ST_CLERK,2500, - ,123,50
\ No newline at end of file
--- /dev/null
+{"web-app": {
+ "servlet": [
+ {
+ "servlet-name": "cofaxCDS",
+ "servlet-class": "org.cofax.cds.CDSServlet",
+ "init-param": {
+ "configGlossary:installationAt": "Philadelphia, PA",
+ "configGlossary:adminEmail": "ksm@pobox.com",
+ "configGlossary:poweredBy": "Cofax",
+ "configGlossary:poweredByIcon": "/images/cofax.gif",
+ "configGlossary:staticPath": "/content/static",
+ "templateProcessorClass": "org.cofax.WysiwygTemplate",
+ "templateLoaderClass": "org.cofax.FilesTemplateLoader",
+ "templatePath": "templates",
+ "templateOverridePath": "",
+ "defaultListTemplate": "listTemplate.htm",
+ "defaultFileTemplate": "articleTemplate.htm",
+ "useJSP": false,
+ "jspListTemplate": "listTemplate.jsp",
+ "jspFileTemplate": "articleTemplate.jsp",
+ "cachePackageTagsTrack": 200,
+ "cachePackageTagsStore": 200,
+ "cachePackageTagsRefresh": 60,
+ "cacheTemplatesTrack": 100,
+ "cacheTemplatesStore": 50,
+ "cacheTemplatesRefresh": 15,
+ "cachePagesTrack": 200,
+ "cachePagesStore": 100,
+ "cachePagesRefresh": 10,
+ "cachePagesDirtyRead": 10,
+ "searchEngineListTemplate": "forSearchEnginesList.htm",
+ "searchEngineFileTemplate": "forSearchEngines.htm",
+ "searchEngineRobotsDb": "WEB-INF/robots.db",
+ "useDataStore": true,
+ "dataStoreClass": "org.cofax.SqlDataStore",
+ "redirectionClass": "org.cofax.SqlRedirection",
+ "dataStoreName": "cofax",
+ "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
+ "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
+ "dataStoreUser": "sa",
+ "dataStorePassword": "dataStoreTestQuery",
+ "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
+ "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
+ "dataStoreInitConns": 10,
+ "dataStoreMaxConns": 100,
+ "dataStoreConnUsageLimit": 100,
+ "dataStoreLogLevel": "debug",
+ "maxUrlLength": 500}},
+ {
+ "servlet-name": "cofaxEmail",
+ "servlet-class": "org.cofax.cds.EmailServlet",
+ "init-param": {
+ "mailHost": "mail1",
+ "mailHostOverride": "mail2"}},
+ {
+ "servlet-name": "cofaxAdmin",
+ "servlet-class": "org.cofax.cds.AdminServlet"},
+
+ {
+ "servlet-name": "fileServlet",
+ "servlet-class": "org.cofax.cds.FileServlet"},
+ {
+ "servlet-name": "cofaxTools",
+ "servlet-class": "org.cofax.cms.CofaxToolsServlet",
+ "init-param": {
+ "templatePath": "toolstemplates/",
+ "log": 1,
+ "logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
+ "logMaxSize": "",
+ "dataLog": 1,
+ "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
+ "dataLogMaxSize": "",
+ "removePageCache": "/content/admin/remove?cache=pages&id=",
+ "removeTemplateCache": "/content/admin/remove?cache=templates&id=",
+ "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
+ "lookInContext": 1,
+ "adminGroupID": 4,
+ "betaServer": true}}],
+ "servlet-mapping": {
+ "cofaxCDS": "/",
+ "cofaxEmail": "/cofaxutil/aemail/*",
+ "cofaxAdmin": "/admin/*",
+ "fileServlet": "/static/*",
+ "cofaxTools": "/tools/*"},
+
+ "taglib": {
+ "taglib-uri": "cofax.tld",
+ "taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
--- /dev/null
+[
+ {
+ "name": "Meowsy",
+ "species" : "cat",
+ "foods": {
+ "likes": ["tuna", "catnip"],
+ "dislikes": ["ham", "zucchini"]
+ }
+ },
+ {
+ "name": "Barky",
+ "species" : "dog",
+ "foods": {
+ "likes": ["bones", "carrots"],
+ "dislikes": ["tuna"]
+ }
+ },
+ {
+ "name": "Purrpaws",
+ "species" : "cat",
+ "foods": {
+ "likes": ["mice"],
+ "dislikes": ["cookies"]
+ }
+ }
+]
\ No newline at end of file
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <agent/parser_context.h>
+#include <agent/simple_parser.h>
+#include <agent/ca_cfg_mgr.h>
+
+#include <cc/data.h>
+#include <exceptions/exceptions.h>
+
+#include <string>
+#include <memory>
+
+using namespace isc::agent;
+using namespace isc::data;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ bool checkOnly = fdp.ConsumeBool();
+
+ AgentSimpleParser simpleParser;
+ CtrlAgentCfgContextPtr ctxPtr(new CtrlAgentCfgContext());
+ ParserContext ctx;
+ ElementPtr elem;
+
+ // Generate random parsing mode
+ const uint8_t mode = fdp.ConsumeIntegralInRange<uint8_t>(0, 2);
+ ParserContext::ParserType type = ParserContext::PARSER_JSON;
+ if (mode == 1) {
+ type = ParserContext::PARSER_AGENT;
+ } else if (mode == 2) {
+ type = ParserContext::PARSER_SUB_AGENT;
+ }
+
+ const std::string payload = fdp.ConsumeRemainingBytesAsString();
+
+ // Target context parseString
+ try {
+ ctx.parseString(payload, type);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Parse payload to JSON
+ try {
+ elem = Element::fromJSON(payload);
+ } catch (...) {
+ // If failed to parse the payload, early exit
+ return 0;
+ }
+
+ // Target SimpleParser
+ try {
+ AgentSimpleParser::setAllDefaults(elem);
+ simpleParser.checkTlsSetup(elem);
+ simpleParser.parse(ctxPtr, elem, checkOnly);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+"{"
+"}"
+"["
+"]"
+":"
+","
+"\""
+"\"\""
+"null"
+"true"
+"false"
+" "
+"\\n"
+"\\r\\n"
+"\"Control-agent\""
+"\"control-agent\""
+"\"http-host\""
+"\"http-port\""
+"\"http-ciphers\""
+"\"http-headers\""
+"\"http-max-body-size\""
+"\"http-status\""
+"\"http-response-headers\""
+"\"localhost\""
+"\"127.0.0.1\""
+"\"::1\""
+"\"0.0.0.0\""
+"0"
+"53"
+"67"
+"80"
+"443"
+"8080"
+"65535"
+"\"tls\""
+"\"enable-tls\""
+"\"cert-file\""
+"\"key-file\""
+"\"ca-file\""
+"\"trust-anchor\""
+"\"verify-peer\""
+"\"cipher-list\""
+"\"cipher-suite\""
+"\"protocols\""
+"\"min-tls-version\""
+"\"max-tls-version\""
+"\"/etc/kea/ctrl-agent.crt\""
+"\"/etc/kea/ctrl-agent.key\""
+"\"/etc/ssl/certs/ca-certificates.crt\""
+"\"/tmp/kea.crt\""
+"\"/tmp/kea.key\""
+"\"REQUIRED\""
+"\"OPTIONAL\""
+"\"NONE\""
+"\"TLSv1.2\""
+"\"TLSv1.3\""
+"\"control-sockets\""
+"\"dhcp4\""
+"\"dhcp6\""
+"\"d2\""
+"\"netconf\""
+"\"socket-type\""
+"\"socket-name\""
+"\"socket-url\""
+"\"unix\""
+"\"http\""
+"\"/tmp/kea4-ctrl.sock\""
+"\"/tmp/kea6-ctrl.sock\""
+"\"/tmp/kea-d2-ctrl.sock\""
+"\"http://127.0.0.1:8000/\""
+"\"http://[::1]:8000/\""
+"\"commands\""
+"\"command\""
+"\"arguments\""
+"\"access-list\""
+"\"whitelist\""
+"\"blacklist\""
+"\"allowed-clients\""
+"\"paths\""
+"\"path\""
+"\"GET\""
+"\"POST\""
+"\"Logging\""
+"\"name\""
+"\"output_options\""
+"\"output\""
+"\"flush\""
+"\"maxsize\""
+"\"maxver\""
+"\"file-name\""
+"\"severity\""
+"\"debuglevel\""
+"\"INFO\""
+"\"WARN\""
+"\"ERROR\""
+"\"DEBUG\""
+"\"kea-ctrl-agent\""
+"\"stdout\""
+"\"stderr\""
+"\"/var/log/kea-ctrl-agent.log\""
+"\"interfaces\""
+"\"hooks-libraries\""
+"\"library\""
+"\"parameters\""
+"\"user-context\""
+"\"comment\""
+"\"config-control\""
+"\"config-databases\""
+"\"database\""
+"\"type\""
+"\"host\""
+"\"port\""
+"\"user\""
+"\"password\""
+"\"name\""
+"\"Control-agent\":{"
+"\"http-host\":\""
+"\"http-port\":"
+"\"enable-tls\":"
+"\"cert-file\":\""
+"\"key-file\":\""
+"\"ca-file\":\""
+"\"verify-peer\":"
+"\"control-sockets\":{"
+"\"dhcp4\":{"
+"\"dhcp6\":{"
+"\"d2\":{"
+"\"netconf\":{"
+"\"socket-type\":\""
+"\"socket-name\":\""
+"\"Logging\":[{"
+"\"output_options\":[{"
+"\"severity\":\""
+"\"debuglevel\":"
+"}]"
+"}]"
+"}"
+"\"\""
+"\" \""
+"\"\\u0000\""
+"\"\\uD800\""
+"\"\\x\""
+"-1"
+"2147483647"
+"4294967295"
+"9223372036854775807"
+"3.14159"
+"\"[],{}:\""
+"\":{"
+"\": ["
+"]}"
+"}]"
+"},"
+"],"
+"\" : "
+" , "
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <exceptions/exceptions.h>
+#include <cc/data.h>
+#include <cc/json_feed.h>
+#include <cc/simple_parser.h>
+#include <asiolink/io_address.h>
+
+#include <string>
+#include <vector>
+
+using namespace isc;
+using namespace isc::data;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ ElementPtr elem;
+
+ std::string val1 = fdp.ConsumeRandomLengthString(8);
+ std::string val2 = fdp.ConsumeRandomLengthString(8);
+ Element::types type1 = static_cast<Element::types>(fdp.ConsumeIntegralInRange<int>(0, 8));
+ Element::types type2 = static_cast<Element::types>(fdp.ConsumeIntegralInRange<int>(0, 8));
+
+ SimpleRequiredKeywords required;
+ required.push_back(val1);
+ required.push_back(val2);
+
+ SimpleKeywords keywords;
+ keywords[val1] = type1;
+ keywords[val2] = type2;
+
+ ParamsList params;
+ params.push_back(val1);
+ params.push_back(val2);
+
+ const std::string payload = fdp.ConsumeRemainingBytesAsString();
+
+ // Target JSONFeed with random data
+ try {
+ config::JSONFeed feed;
+ feed.initModel();
+ feed.postBuffer(payload.c_str(), payload.length());
+ feed.poll();
+ feed.needData();
+ feed.feedOk();
+ feed.getProcessedText();
+ feed.toElement();
+ feed.getErrorMessage();
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Try parse payload to Element pointer
+ try {
+ elem = Element::fromJSON(payload);
+ } catch (...) {
+ // Early exit for invalid json
+ return 0;
+ }
+
+ // Target parseIntTriplet
+ try {
+ SimpleParser parser;
+ parser.parseIntTriplet(elem, val1);
+ parser.parseIntTriplet(elem, val2);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target checkRequired
+ try {
+ SimpleParser::checkRequired(required, elem);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target checkKeywords
+ try {
+ SimpleParser::checkKeywords(keywords, elem);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target deriveParams
+ try {
+ SimpleParser::deriveParams(elem, Element::createMap(), params);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dns/tsig.h>
+#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_hmac.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <memory>
+
+using namespace isc::dns;
+using namespace isc::cryptolink;
+using namespace isc::util;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ std::string secret = fdp.ConsumeRandomLengthString(64);
+ std::string tsig_data = fdp.ConsumeRandomLengthString(1024);
+
+ // TSIGContext
+ try {
+ // Create TSIGContext
+ TSIGKey tsigkey("fuzz_key:" + secret);
+ TSIGContext ctx(tsigkey);
+
+ // TSIGContext sign
+ uint16_t qid = fdp.ConsumeIntegral<uint16_t>();
+ ctx.sign(qid, tsig_data.data(), tsig_data.size());
+
+ // Create TSIGRecord
+ Name name("fuzz_record");
+ TSIGRecord record(name, rdata::any::TSIG(fdp.ConsumeRandomLengthString(1024)));
+
+ ctx.verify(&record, tsig_data.data(), tsig_data.size());
+ } catch (const isc::Exception&) {
+ // Slient ezceptions
+ }
+
+ // HMAC
+ try {
+ // HMAC Sign
+ OutputBuffer hmac(256);
+ signHMAC(tsig_data.data(), tsig_data.size(), secret.data(), secret.size(),
+ static_cast<HashAlgorithm>(fdp.ConsumeIntegralInRange<int>(0, 6)), hmac);
+
+ // HMAC Verify
+ std::string sig = fdp.ConsumeRandomLengthString(256);
+ verifyHMAC(tsig_data.data(), tsig_data.size(), secret.data(), secret.size(),
+ static_cast<HashAlgorithm>(fdp.ConsumeIntegralInRange<int>(0, 6)),
+ sig.data(), sig.size());
+ } catch (const isc::Exception&) {
+ // Slient ezceptions
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logics Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <exceptions/exceptions.h>
+#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_hash.h>
+#include <cryptolink/crypto_hmac.h>
+#include <cryptolink/crypto_rng.h>
+
+#include <string>
+#include <vector>
+#include <cstddef>
+
+using namespace isc::cryptolink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size < 3) {
+ return 0;
+ }
+
+ FuzzedDataProvider fdp(data, size);
+
+ // Choose which crypto operation to test
+ uint8_t path = fdp.ConsumeIntegralInRange<uint8_t>(0, 9);
+
+ // Pick a hash algorithm
+ HashAlgorithm hash_alg = fdp.PickValueInArray({
+ HashAlgorithm::MD5,
+ HashAlgorithm::SHA1,
+ HashAlgorithm::SHA224,
+ HashAlgorithm::SHA256,
+ HashAlgorithm::SHA384,
+ HashAlgorithm::SHA512
+ });
+
+ try {
+ switch (path) {
+ case 0: {
+ // Test Hash creation and update
+ Hash* hash = CryptoLink::getCryptoLink().createHash(hash_alg);
+ if (hash) {
+ std::vector<uint8_t> input_data = fdp.ConsumeRemainingBytes<uint8_t>();
+ if (!input_data.empty()) {
+ hash->update(input_data.data(), input_data.size());
+ }
+ std::vector<uint8_t> digest = hash->final(hash->getOutputLength());
+ delete hash;
+ }
+ break;
+ }
+
+ case 1: {
+ // Test Hash with multiple updates
+ Hash* hash = CryptoLink::getCryptoLink().createHash(hash_alg);
+ if (hash) {
+ size_t num_updates = fdp.ConsumeIntegralInRange<size_t>(1, 10);
+ for (size_t i = 0; i < num_updates && fdp.remaining_bytes() > 0; i++) {
+ size_t chunk_size = fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes());
+ std::vector<uint8_t> chunk = fdp.ConsumeBytes<uint8_t>(chunk_size);
+ if (!chunk.empty()) {
+ hash->update(chunk.data(), chunk.size());
+ }
+ }
+ std::vector<uint8_t> digest = hash->final(hash->getOutputLength());
+ delete hash;
+ }
+ break;
+ }
+
+ case 2: {
+ // Test Hash with OutputBuffer
+ Hash* hash = CryptoLink::getCryptoLink().createHash(hash_alg);
+ if (hash) {
+ std::vector<uint8_t> input_data = fdp.ConsumeBytes<uint8_t>(
+ fdp.ConsumeIntegralInRange<size_t>(0, size)
+ );
+ if (!input_data.empty()) {
+ hash->update(input_data.data(), input_data.size());
+ }
+ isc::util::OutputBuffer result(hash->getOutputLength());
+ size_t len = fdp.ConsumeIntegralInRange<size_t>(0, hash->getOutputLength() * 2);
+ hash->final(result, len);
+ delete hash;
+ }
+ break;
+ }
+
+ case 3: {
+ // Test Hash with void* result
+ Hash* hash = CryptoLink::getCryptoLink().createHash(hash_alg);
+ if (hash) {
+ std::vector<uint8_t> input_data = fdp.ConsumeBytes<uint8_t>(
+ fdp.ConsumeIntegralInRange<size_t>(0, size)
+ );
+ if (!input_data.empty()) {
+ hash->update(input_data.data(), input_data.size());
+ }
+ std::vector<uint8_t> result(hash->getOutputLength() * 2);
+ size_t len = fdp.ConsumeIntegralInRange<size_t>(0, result.size());
+ hash->final(result.data(), len);
+ delete hash;
+ }
+ break;
+ }
+
+ case 4: {
+ // Test HMAC creation and signing
+ size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(1, 256);
+ std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len);
+ if (secret.empty()) {
+ secret.push_back(0); // Ensure non-empty secret
+ }
+
+ HMAC* hmac = CryptoLink::getCryptoLink().createHMAC(
+ secret.data(), secret.size(), hash_alg
+ );
+ if (hmac) {
+ std::vector<uint8_t> input_data = fdp.ConsumeRemainingBytes<uint8_t>();
+ if (!input_data.empty()) {
+ hmac->update(input_data.data(), input_data.size());
+ }
+ std::vector<uint8_t> signature = hmac->sign(hmac->getOutputLength());
+ delete hmac;
+ }
+ break;
+ }
+
+ case 5: {
+ // Test HMAC with multiple updates
+ size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(1, 256);
+ std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len);
+ if (secret.empty()) {
+ secret.push_back(0);
+ }
+
+ HMAC* hmac = CryptoLink::getCryptoLink().createHMAC(
+ secret.data(), secret.size(), hash_alg
+ );
+ if (hmac) {
+ size_t num_updates = fdp.ConsumeIntegralInRange<size_t>(1, 10);
+ for (size_t i = 0; i < num_updates && fdp.remaining_bytes() > 0; i++) {
+ size_t chunk_size = fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes());
+ std::vector<uint8_t> chunk = fdp.ConsumeBytes<uint8_t>(chunk_size);
+ if (!chunk.empty()) {
+ hmac->update(chunk.data(), chunk.size());
+ }
+ }
+ std::vector<uint8_t> signature = hmac->sign(hmac->getOutputLength());
+ delete hmac;
+ }
+ break;
+ }
+
+ case 6: {
+ // Test HMAC with OutputBuffer
+ size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(1, 256);
+ std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len);
+ if (secret.empty()) {
+ secret.push_back(0);
+ }
+
+ HMAC* hmac = CryptoLink::getCryptoLink().createHMAC(
+ secret.data(), secret.size(), hash_alg
+ );
+ if (hmac) {
+ std::vector<uint8_t> input_data = fdp.ConsumeBytes<uint8_t>(
+ fdp.ConsumeIntegralInRange<size_t>(0, size)
+ );
+ if (!input_data.empty()) {
+ hmac->update(input_data.data(), input_data.size());
+ }
+ isc::util::OutputBuffer result(hmac->getOutputLength());
+ size_t len = fdp.ConsumeIntegralInRange<size_t>(0, hmac->getOutputLength() * 2);
+ hmac->sign(result, len);
+ delete hmac;
+ }
+ break;
+ }
+
+ case 7: {
+ // Test HMAC verification
+ size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(1, 256);
+ std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len);
+ if (secret.empty()) {
+ secret.push_back(0);
+ }
+
+ HMAC* hmac = CryptoLink::getCryptoLink().createHMAC(
+ secret.data(), secret.size(), hash_alg
+ );
+ if (hmac) {
+ std::vector<uint8_t> input_data = fdp.ConsumeBytes<uint8_t>(
+ fdp.ConsumeIntegralInRange<size_t>(0, size)
+ );
+ if (!input_data.empty()) {
+ hmac->update(input_data.data(), input_data.size());
+ }
+
+ // Generate signature
+ std::vector<uint8_t> signature = hmac->sign(hmac->getOutputLength());
+
+ // Verify with same data (should succeed)
+ HMAC* verify_hmac = CryptoLink::getCryptoLink().createHMAC(
+ secret.data(), secret.size(), hash_alg
+ );
+ if (verify_hmac) {
+ if (!input_data.empty()) {
+ verify_hmac->update(input_data.data(), input_data.size());
+ }
+ verify_hmac->verify(signature.data(), signature.size());
+ delete verify_hmac;
+ }
+ delete hmac;
+ }
+ break;
+ }
+
+ case 8: {
+ // Test HMAC with long secret (should be hashed)
+ size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(256, 1024);
+ std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len);
+ if (secret.size() < 64) {
+ secret.resize(64, 0x42); // Pad to ensure long secret
+ }
+
+ HMAC* hmac = CryptoLink::getCryptoLink().createHMAC(
+ secret.data(), secret.size(), hash_alg
+ );
+ if (hmac) {
+ std::vector<uint8_t> input_data = fdp.ConsumeRemainingBytes<uint8_t>();
+ if (!input_data.empty()) {
+ hmac->update(input_data.data(), input_data.size());
+ }
+ std::vector<uint8_t> signature = hmac->sign(hmac->getOutputLength());
+ delete hmac;
+ }
+ break;
+ }
+
+ case 9: {
+ // Test RNG generation
+ size_t rng_len = fdp.ConsumeIntegralInRange<size_t>(0, 1024);
+ std::vector<uint8_t> random_data = isc::cryptolink::random(rng_len);
+
+ // Test Qid generation
+ uint16_t qid = isc::cryptolink::generateQid();
+ (void)qid; // Use the variable
+ break;
+ }
+ }
+ } catch (const isc::Exception&) {
+ // Expected for invalid algorithms, key lengths, etc.
+ } catch (const std::exception&) {
+ // Catch any standard library exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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/.
+////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <d2/parser_context.h>
+#include <d2srv/d2_cfg_mgr.h>
+#include <d2srv/d2_simple_parser.h>
+#include <d2srv/d2_update_message.h>
+#include <dhcp_ddns/ncr_msg.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/tsig.h>
+#include <dns/tsigkey.h>
+
+#include <cc/data.h>
+#include <exceptions/exceptions.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <memory>
+
+using namespace isc::d2;
+using namespace isc::data;
+using namespace isc::dhcp_ddns;
+using namespace isc::dns;
+using namespace isc::util;
+
+static const D2ParserContext::ParserType types[] = {
+ D2ParserContext::PARSER_JSON,
+ D2ParserContext::PARSER_DHCPDDNS,
+ D2ParserContext::PARSER_SUB_DHCPDDNS,
+ D2ParserContext::PARSER_TSIG_KEY,
+ D2ParserContext::PARSER_TSIG_KEYS,
+ D2ParserContext::PARSER_DDNS_DOMAIN,
+ D2ParserContext::PARSER_DDNS_DOMAINS,
+ D2ParserContext::PARSER_DNS_SERVER,
+ D2ParserContext::PARSER_DNS_SERVERS,
+ D2ParserContext::PARSER_HOOKS_LIBRARY
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ bool checkOnly = fdp.ConsumeBool();
+ uint8_t index = fdp.ConsumeIntegralInRange<uint8_t>(0, static_cast<uint8_t>(sizeof(types) / sizeof(types[0]) - 1));
+
+ D2SimpleParser simpleParser;
+ D2CfgContextPtr ctxPtr(new D2CfgContext());
+ D2ParserContext ctx;
+ ElementPtr elem;
+
+ // Generate random parsing mode
+ D2ParserContext::ParserType type = types[index];
+
+ const std::string payload = fdp.ConsumeRemainingBytesAsString();
+
+ // Target context parseString
+ try {
+ ctx.parseString(payload, type);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Parse payload to JSON
+ try {
+ elem = Element::fromJSON(payload);
+ } catch (...) {
+ // If failed to parse the payload, early exit
+ return 0;
+ }
+
+ // Target SimpleParser
+ try {
+ D2SimpleParser::setAllDefaults(elem);
+ simpleParser.parse(ctxPtr, elem, checkOnly);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Prepare buffer
+ InputBuffer buf(reinterpret_cast<const uint8_t*>(payload.data()), payload.size());
+
+ // Target NameChangeRequest::fromtFormat
+ try {
+ NameChangeRequest::fromFormat(NameChangeFormat::FMT_JSON, buf);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target Message fromWire
+ try {
+ Message msg(Message::PARSE);
+ msg.fromWire(buf);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target D2UpdateMessage fromWire (Inbound)
+ try {
+ TSIGKey tsigkey("fuzz_key:fuzz_key");
+ TSIGContext tsigctx(tsigkey);
+ D2UpdateMessage message(D2UpdateMessage::INBOUND);
+ message.fromWire(payload.data(), payload.size(), &tsigctx) ;
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target D2UpdateMessage fromWire (Outbound)
+ try {
+ TSIGKey tsigkey("fuzz_key:fuzz_key");
+ TSIGContext tsigctx(tsigkey);
+ D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
+ message.fromWire(payload.data(), payload.size(), &tsigctx);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+"{"
+"}"
+"["
+"]"
+":"
+","
+"\""
+"\"\""
+"null"
+"true"
+"false"
+" "
+"\\n"
+"\\r\\n"
+"\"DhcpDdns\""
+"\"dhcp-ddns\""
+"\"ip-address\""
+"\"port\""
+"\"dns-servers\""
+"\"dns-server-timeout\""
+"\"enable-updates\""
+"\"ncr-protocol\""
+"\"ncr-format\""
+"\"max-queue-size\""
+"\"max-ncrs-per-iteration\""
+"\"qualifying-suffix\""
+"\"forward-ddns\""
+"\"reverse-ddns\""
+"\"ddns-domains\""
+"\"ddns-replace-client-name\""
+"\"override-client-update\""
+"\"generated-prefix\""
+"\"remove-on-renew\""
+"\"update-on-renew\""
+"\"resend-on-failure\""
+"\"retry-interval\""
+"\"ttl\""
+"\"rrtype\""
+"\"rrclass\""
+"\"fqdn\""
+"\"lease-address\""
+"\"lease6-address\""
+"\"client-id\""
+"\"dhcid\""
+"\"change-type\""
+"\"ADD\""
+"\"REMOVE\""
+"\"A\""
+"\"AAAA\""
+"\"PTR\""
+"\"TXT\""
+"\"IN\""
+"\"UDP\""
+"\"TCP\""
+"\"JSON\""
+"\"WIRE\""
+"\"tsig-keys\""
+"\"tsig-keys-dns\""
+"\"tsig-keys-servers\""
+"\"tsig-keys-local\""
+"\"tsig-keys-remote\""
+"\"tsig-keys-names\""
+"\"name\""
+"\"algorithm\""
+"\"secret\""
+"\"digest-bits\""
+"\"HMAC-MD5\""
+"\"HMAC-SHA1\""
+"\"HMAC-SHA224\""
+"\"HMAC-SHA256\""
+"\"HMAC-SHA384\""
+"\"HMAC-SHA512\""
+"\"control-socket\""
+"\"socket-type\""
+"\"socket-name\""
+"\"unix\""
+"\"http\""
+"\"http-host\""
+"\"http-port\""
+"\"http-ciphers\""
+"\"http-headers\""
+"\"http-max-body-size\""
+"\"Logging\""
+"\"output_options\""
+"\"output\""
+"\"file-name\""
+"\"severity\""
+"\"debuglevel\""
+"\"INFO\""
+"\"WARN\""
+"\"ERROR\""
+"\"DEBUG\""
+"\"stdout\""
+"\"stderr\""
+"\"/var/log/kea-dhcp-ddns.log\""
+"\"127.0.0.1\""
+"\"::1\""
+"\"0.0.0.0\""
+"0"
+"53"
+"53001"
+"65535"
+"\"Control-agent\""
+"\"hooks-libraries\""
+"\"library\""
+"\"parameters\""
+"\"user-context\""
+"\"comment\""
+"\"DhcpDdns\":{"
+"\"dns-servers\":[{"
+"\"ip-address\":\""
+"\"port\":"
+"\"tsig-keys\":["
+"\"tsig-keys\":[{"
+"\"name\":\""
+"\"algorithm\":\""
+"\"secret\":\""
+"\"digest-bits\":"
+"\"forward-ddns\":{"
+"\"reverse-ddns\":{"
+"\"ddns-domains\":["
+"\"ddns-domains\":[{"
+"\"qualifying-suffix\":\""
+"\"enable-updates\":"
+"\"ncr-protocol\":\""
+"\"ncr-format\":\""
+"\"max-queue-size\":"
+"\"max-ncrs-per-iteration\":"
+"\"dns-server-timeout\":"
+"\"control-socket\":{"
+"\"socket-type\":\""
+"\"socket-name\":\""
+"\"Logging\":[{"
+"\"output_options\":[{"
+"\"severity\":\""
+"\"debuglevel\":"
+"}]"
+"}]"
+"}"
+"\"\""
+"\" \""
+"\"\\u0000\""
+"\"\\uD800\""
+"\"\\x\""
+"-1"
+"2147483647"
+"4294967295"
+"9223372036854775807"
+"3.14159"
+"\"[],{}:\""
+"\":{"
+"\": ["
+"]}"
+"}]"
+"},"
+"],"
+"\" : "
+" , "
--- /dev/null
+"{"
+"}"
+"["
+"]"
+":"
+","
+"Dhcp4"
+"interfaces-config"
+"interfaces"
+"*"
+"rebind-timer"
+"renew-timer"
+"valid-lifetime"
+"subnet4"
+"id"
+"pools"
+"pool"
+"subnet"
+"interface"
+"option-data"
+"name"
+"data"
+"csv-format"
+"always-send"
+"never-send"
+"hosts-database"
+"type"
+"mysql"
+"postgresql"
+"user"
+"password"
+"hooks-libraries"
+"hosts-databases"
+"control-socket"
+"control-sockets"
+"authentication"
+"clients"
+"user-file"
+"password-file"
+"client-classes"
+"test"
+"template-test"
+"option-def"
+"code"
+"uint32"
+"next-server"
+"server-hostname"
+"boot-file-name"
+"user-context"
+"secure"
+"eth0"
+"eth1"
+"10.254.226.0/25"
+"10.254.226.0/24"
+"192.0.2.0/24"
+"192.0.2.1 - 192.0.2.100"
+"default-ip-ttl"
+"ip-forwarding"
+"false"
+"true"
+"FF"
+"interfaces-config\": { \"interfaces\": [ \"*\" ] }"
+"option-data\": ["
+"{ \"name\": "
+"{ \"code\": "
+"{ \"pool\": "
+"subnet4\": [ {"
+"pools\": [ {"
+"id\": 1"
+"interface\": \"eth0\""
+"Dhcp6"
+"subnet6"
+"pd-pools"
+"lease-database"
+"preferred-lifetime"
+"min-preferred-lifetime"
+"max-preferred-lifetime"
+"min-valid-lifetime"
+"max-valid-lifetime"
+"rapid-commit"
+"duid"
+"duid-type"
+"interface-id"
+"relay"
+"relay-supplied-options"
+"ia-na"
+"ia-ta"
+"ia-pd"
+"prefix"
+"prefix-len"
+"delegated-len"
+"excluded-prefix"
+"excluded-prefix-len"
+"dns-servers"
+"domain-search"
+"renew-timer\": "
+"rebind-timer\": "
+"valid-lifetime\": "
+"preferred-lifetime\": "
+"rapid-commit\": true"
+"rapid-commit\": false"
+"lease-database\": {"
+"type\": \"memfile\""
+"type\": \"mysql\""
+"type\": \"postgresql\""
+"persist\": true"
+"persist\": false"
+"subnet6\": [ {"
+"pd-pools\": [ {"
+"{ \"prefix\": "
+"{ \"prefix-len\": "
+"{ \"delegated-len\": "
+"{ \"excluded-prefix\": "
+"{ \"excluded-prefix-len\": "
+"{ \"interface-id\": "
+"option-data\": [ {"
+"{ \"name\": \"dns-servers\", \"data\": "
+"{ \"name\": \"domain-search\", \"data\": "
+"{ \"name\": \"client-id\", \"data\": "
+"{ \"name\": \"server-id\", \"data\": "
+"{ \"name\": \"elapsed-time\", \"data\": "
+"{ \"name\": \"oro\", \"data\": "
+"{ \"name\": \"rapid-commit\" "
+"client-classes\": ["
+"user-context\": {"
+"hooks-libraries\": ["
+"authentication\": {"
+"interfaces-config\": { \"interfaces\": ["
+"2001:db8::/32"
+"2001:db8:1::/48"
+"2001:db8:1::/64"
+"2001:db8:2::/56"
+"2001:db8:1::10 - 2001:db8:1::ffff"
+"fe80::1"
+"::1"
+"::/0"
+"duid\": \"00:01:00:01:24:60:9b:d0:aa:bb:cc:dd:ee:ff\""
+"server-id\": \"00:02:00:01:aa:bb:cc:dd:ee:ff:00:11:22:33\""
+"client-id\": \"00:03:00:01:11:22:33:44:55:66\""
+"prefix\": \"2001:db8:2::\""
+"prefix-len\": 56"
+"delegated-len\": 64"
+"excluded-prefix\": \"2001:db8:2::\""
+"excluded-prefix-len\": 64"
+"pool\": \"2001:db8:1::/64\""
+"pool\": \"2001:db8:1::10 - 2001:db8:1::ffff\""
+"interface\": \"eth1\""
+"null"
+"0"
+"1"
+"56"
+"64"
+"3600"
+"7200"
+"86400"
+"socket-address"
+"socket-port"
+"cert-required"
+"http-headers"
+"http_header_params"
+"value"
+"http_header"
+"basic"
+"auth_params"
+"realm"
+"directory"
+"dhcp-queue-control"
+"queue_control_params"
+"enable-queue"
+"queue-type"
+"capacity"
+"constant string"
+"sub_dhcp_ddns"
+"dhcp_ddns_params"
+"enable-updates"
+"server-ip"
+"server-port"
+"sender-ip"
+"max-queue-size"
+"ncr-format"
+"config-control"
+"udp"
+"config-databases"
+"config-fetch-wait-time"
+"lenient-option-parsing"
+"compatibility"
+"ignore-rai-link-selection"
+"pool-id"
+"option-data-map"
+"encapsulate"
+"encapsulate_space"
+"additional-class"
+"prefix/len"
+"min-max"
+"/"
+"-"
+"ip-addresses"
+"socket-type"
+"unix"
+"http"
+"https"
+"ignore-dhcp-server-identifier"
+"exclude-first-last-24"
+"ncr-protocol"
+"UDP"
+"JSON"
+"PAD"
+"END"
+"127.0.0.1"
+"0.0.0.0"
+"::"
+"2001:db8::"
+"2001:db8::/64"
+"192.0.2.1-192.0.2.254"
+"192.0.2.1"
+"255.255.255.255"
+"127.0.0.1/24"
+"::/128"
+"0/0"
+"len"
+"min"
+"max"
+"poolMaker"
+"poolMaker(min,max)"
+"poolMaker(addr,len)"
+"ip-address"
+"ip"
+"address"
+"addresses"
+"subnet-id"
+"subnet-id-max"
+"reservations"
+"reservations-list"
+"reservation"
+"host-reservation"
+"host-reservations"
+"option-space"
+"DHCP4_OPTION_SPACE"
+"DHCP6_OPTION_SPACE"
+"encapsulates"
+"array_type"
+"record_types"
+"record"
+"record-field"
+"pad"
+"end"
+"server"
+"sender"
+"IPv4"
+"IPv6"
+"v4"
+"v6"
+"IPv4_zero"
+"IPv6_zero"
+"address/mask"
+"fe80::/64"
+"192.168.1.0/24"
+"10.0.0.0/8"
+"invalid-ip"
+"not-an-ip"
+"BADVALUE"
+"BadValue"
+"DhcpConfigError"
+"D2ClientError"
+"Unexpected"
+"OutOfRange"
+"Failed to parse pool"
+"Failed to create pool"
+"Failed to create subnet"
+"Invalid prefix length"
+"prefix length"
+"Missing /"
+"Invalid subnet syntax"
+"Failed to convert"
+"invalid option code"
+"invalid option space name"
+"invalid parameter next-server"
+"map"
+"list"
+"string"
+"integer"
+"boolean"
+"stringValue"
+"intValue"
+"contains"
+"get"
+"getPosition"
+"getType"
+"listValue"
+"mapValue"
+"size"
+"add"
+"addAddress"
+"addRecordField"
+"validate"
+"parse"
+"parseDdnsParameters"
+"parseCacheParams"
+"parseAdaptiveLeaseTimeParam"
+"parseOfferLft"
+"parseAllocatorParams"
+"parsePdAllocatorParams"
+"parseTeePercents"
+"parseDdnsParams"
+"createOptionDataListParser"
+"createPoolsListParser"
+"createPoolConfigParser"
+"createPdPoolConfigParser"
+"createAllocatorParamsParser"
+"createPdAllocatorParamsParser"
+"HTTP"
+"HTTPS"
+"unix-socket"
+"sender-port"
+"option"
+"dhcp4"
+"dhcp6"
+"match-client-id"
+"authoritative"
+"offer-lifetime"
+"t1-percent"
+"t2-percent"
+"ipv4-range-format"
+"ipv6-range-format"
+"ipv6-prefix-format"
+"invalid-option"
+"invalid-parameter"
+"NotFound"
+"Error"
+"Exception"
+s0="end of file"
+s1="error"
+s2="invalid token"
+s3="null"
+s4="Dhcp4"
+s5="config-control"
+s6="config-databases"
+s7="config-fetch-wait-time"
+s8="interfaces-config"
+s9="interfaces"
+s10="dhcp-socket-type"
+s11="raw"
+s12="udp"
+s13="outbound-interface"
+s14="same-as-inbound"
+s15="use-routing"
+s16="re-detect"
+s17="service-sockets-require-all"
+s18="service-sockets-retry-wait-time"
+s19="service-sockets-max-retries"
+s20="sanity-checks"
+s21="lease-checks"
+s22="extended-info-checks"
+s23="echo-client-id"
+s24="match-client-id"
+s25="authoritative"
+s26="next-server"
+s27="server-hostname"
+s28="boot-file-name"
+s29="offer-lifetime"
+s30="stash-agent-options"
+s31="lease-database"
+s32="hosts-database"
+s33="hosts-databases"
+s34="type"
+s35="user"
+s36="password"
+s37="host"
+s38="port"
+s39="persist"
+s40="lfc-interval"
+s41="readonly"
+s42="connect-timeout"
+s43="read-timeout"
+s44="write-timeout"
+s45="tcp-user-timeout"
+s46="max-reconnect-tries"
+s47="reconnect-wait-time"
+s48="on-fail"
+s49="stop-retry-exit"
+s50="serve-retry-exit"
+s51="serve-retry-continue"
+s52="retry-on-startup"
+s53="max-row-errors"
+s54="trust-anchor"
+s55="cert-file"
+s56="key-file"
+s57="ssl-mode"
+s58="disable"
+s59="prefer"
+s60="require"
+s61="verify-ca"
+s62="verify-full"
+s63="cipher-list"
+s64="valid-lifetime"
+s65="min-valid-lifetime"
+s66="max-valid-lifetime"
+s67="renew-timer"
+s68="rebind-timer"
+s69="calculate-tee-times"
+s70="t1-percent"
+s71="t2-percent"
+s72="cache-threshold"
+s73="cache-max-age"
+s74="adaptive-lease-time-threshold"
+s75="decline-probation-period"
+s76="server-tag"
+s77="statistic-default-sample-count"
+s78="statistic-default-sample-age"
+s79="ddns-send-updates"
+s80="ddns-override-no-update"
+s81="ddns-override-client-update"
+s82="ddns-replace-client-name"
+s83="ddns-generated-prefix"
+s84="ddns-qualifying-suffix"
+s85="ddns-update-on-renew"
+s86="ddns-use-conflict-resolution"
+s87="ddns-ttl-percent"
+s88="ddns-ttl"
+s89="ddns-ttl-min"
+s90="ddns-ttl-mix"
+s91="store-extended-info"
+s92="subnet4"
+s93="4o6-interface"
+s94="4o6-interface-id"
+s95="4o6-subnet"
+s96="option-def"
+s97="option-data"
+s98="name"
+s99="data"
+s100="code"
+s101="space"
+s102="csv-format"
+s103="always-send"
+s104="never-send"
+s105="record-types"
+s106="encapsulate"
+s107="array"
+s108="parked-packet-limit"
+s109="allocator"
+s110="ddns-conflict-resolution-mode"
+s111="check-with-dhcid"
+s112="no-check-with-dhcid"
+s113="check-exists-with-dhcid"
+s114="no-check-without-dhcid"
+s115="shared-networks"
+s116="pools"
+s117="pool"
+s118="user-context"
+s119="comment"
+s120="subnet"
+s121="interface"
+s122="id"
+s123="reservations-global"
+s124="reservations-in-subnet"
+s125="reservations-out-of-pool"
+s126="host-reservation-identifiers"
+s127="client-classes"
+s128="require-client-classes"
+s129="evaluate-additional-classes"
+s130="test"
+s131="template-test"
+s132="only-if-required"
+s133="only-in-additional-list"
+s134="client-class"
+s135="pool-id"
+s136="reservations"
+s137="ip-address"
+s138="duid"
+s139="hw-address"
+s140="circuit-id"
+s141="client-id"
+s142="hostname"
+s143="flex-id"
+s144="relay"
+s145="ip-addresses"
+s146="hooks-libraries"
+s147="library"
+s148="parameters"
+s149="expired-leases-processing"
+s150="reclaim-timer-wait-time"
+s151="flush-reclaimed-timer-wait-time"
+s152="hold-reclaimed-time"
+s153="max-reclaim-leases"
+s154="max-reclaim-time"
+s155="unwarned-reclaim-cycles"
+s156="dhcp4o6-port"
+s157="multi-threading"
+s158="enable-multi-threading"
+s159="thread-pool-size"
+s160="packet-queue-size"
+s161="control-socket"
+s162="control-sockets"
+s163="socket-type"
+s164="unix"
+s165="http"
+s166="https"
+s167="socket-name"
+s168="socket-address"
+s169="socket-port"
+s170="authentication"
+s171="basic"
+s172="realm"
+s173="directory"
+s174="clients"
+s175="user-file"
+s176="password-file"
+s177="cert-required"
+s178="http-headers"
+s179="value"
+s180="dhcp-queue-control"
+s181="enable-queue"
+s182="queue-type"
+s183="capacity"
+s184="dhcp-ddns"
+s185="enable-updates"
+s186="server-ip"
+s187="server-port"
+s188="sender-ip"
+s189="sender-port"
+s190="max-queue-size"
+s191="ncr-protocol"
+s192="ncr-format"
+s193="tcp"
+s194="JSON"
+s195="when-present"
+s196="never"
+s197="always"
+s198="when-not-present"
+s199="hostname-char-set"
+s200="hostname-char-replacement"
+s201="early-global-reservations-lookup"
+s202="ip-reservations-unique"
+s203="reservations-lookup-first"
+s204="loggers"
+s205="output-options"
+s206="output"
+s207="debuglevel"
+s208="severity"
+s209="flush"
+s210="maxsize"
+s211="maxver"
+s212="pattern"
+s213="compatibility"
+s214="lenient-option-parsing"
+s215="ignore-dhcp-server-identifier"
+s216="ignore-rai-link-selection"
+s217="exclude-first-last-24"
+s218="TOPLEVEL_JSON"
+s219="TOPLEVEL_DHCP4"
+s220="SUB_DHCP4"
+s221="SUB_INTERFACES4"
+s222="SUB_SUBNET4"
+s223="SUB_POOL4"
+s224="SUB_RESERVATION"
+s225="SUB_OPTION_DEFS"
+s226="SUB_OPTION_DEF"
+s227="SUB_OPTION_DATA"
+s228="SUB_HOOKS_LIBRARY"
+s229="SUB_DHCP_DDNS"
+s230="SUB_CONFIG_CONTROL"
+s231="constant string"
+s232="integer"
+s233="floating point"
+s234="boolean"
+s235="$accept"
+s236="start"
+s237="value"
+s238="sub_json"
+s239="map2"
+s240="map_value"
+s241="map_content"
+s242="not_empty_map"
+s243="list_generic"
+s244="list_content"
+s245="not_empty_list"
+s246="list_strings"
+s247="list_strings_content"
+s248="not_empty_list_strings"
+s249="unknown_map_entry"
+s250="syntax_map"
+s251="global_object"
+s252="global_object_comma"
+s253="sub_dhcp4"
+s254="global_params"
+s255="global_param"
+s256="valid_lifetime"
+s257="min_valid_lifetime"
+s258="max_valid_lifetime"
+s259="renew_timer"
+s260="rebind_timer"
+s261="calculate_tee_times"
+s262="t1_percent"
+s263="t2_percent"
+s264="cache_threshold"
+s265="cache_max_age"
+s266="adaptive_lease_time_threshold"
+s267="decline_probation_period"
+s268="server_tag"
+s269="parked_packet_limit"
+s270="allocator"
+s271="echo_client_id"
+s272="match_client_id"
+s273="authoritative"
+s274="ddns_send_updates"
+s275="ddns_override_no_update"
+s276="ddns_override_client_update"
+s277="ddns_replace_client_name"
+s278="ddns_replace_client_name_value"
+s279="ddns_generated_prefix"
+s280="ddns_qualifying_suffix"
+s281="ddns_update_on_renew"
+s282="ddns_use_conflict_resolution"
+s283="ddns_conflict_resolution_mode"
+s284="ddns_conflict_resolution_mode_value"
+s285="ddns_ttl_percent"
+s286="ddns_ttl"
+s287="ddns_ttl_min"
+s288="ddns_ttl_max"
+s289="hostname_char_set"
+s290="hostname_char_replacement"
+s291="store_extended_info"
+s292="statistic_default_sample_count"
+s293="statistic_default_sample_age"
+s294="early_global_reservations_lookup"
+s295="ip_reservations_unique"
+s296="reservations_lookup_first"
+s297="offer_lifetime"
+s298="stash_agent_options"
+s299="interfaces_config"
+s300="interfaces_config_params"
+s301="interfaces_config_param"
+s302="sub_interfaces4"
+s303="interfaces_list"
+s304="dhcp_socket_type"
+s305="socket_type"
+s306="outbound_interface"
+s307="outbound_interface_value"
+s308="re_detect"
+s309="service_sockets_require_all"
+s310="service_sockets_retry_wait_time"
+s311="service_sockets_max_retries"
+s312="lease_database"
+s313="sanity_checks"
+s314="sanity_checks_params"
+s315="sanity_checks_param"
+s316="lease_checks"
+s317="extended_info_checks"
+s318="hosts_database"
+s319="hosts_databases"
+s320="database_list"
+s321="not_empty_database_list"
+s322="database"
+s323="database_map_params"
+s324="database_map_param"
+s325="database_type"
+s326="user"
+s327="password"
+s328="host"
+s329="port"
+s330="name"
+s331="persist"
+s332="lfc_interval"
+s333="readonly"
+s334="connect_timeout"
+s335="read_timeout"
+s336="write_timeout"
+s337="tcp_user_timeout"
+s338="max_reconnect_tries"
+s339="reconnect_wait_time"
+s340="on_fail"
+s341="on_fail_mode"
+s342="retry_on_startup"
+s343="max_row_errors"
+s344="trust_anchor"
+s345="cert_file"
+s346="key_file"
+s347="ssl_mode"
+s348="cipher_list"
+s349="host_reservation_identifiers"
+s350="host_reservation_identifiers_list"
+s351="host_reservation_identifier"
+s352="duid_id"
+s353="hw_address_id"
+s354="circuit_id"
+s355="client_id"
+s356="flex_id"
+s357="dhcp_multi_threading"
+s358="multi_threading_params"
+s359="multi_threading_param"
+s360="enable_multi_threading"
+s361="thread_pool_size"
+s362="packet_queue_size"
+s363="hooks_libraries"
+s364="hooks_libraries_list"
+s365="not_empty_hooks_libraries_list"
+s366="hooks_library"
+s367="sub_hooks_library"
+s368="hooks_params"
+s369="hooks_param"
+s370="library"
+s371="parameters"
+s372="expired_leases_processing"
+s373="expired_leases_params"
+s374="expired_leases_param"
+s375="reclaim_timer_wait_time"
+s376="flush_reclaimed_timer_wait_time"
+s377="hold_reclaimed_time"
+s378="max_reclaim_leases"
+s379="max_reclaim_time"
+s380="unwarned_reclaim_cycles"
+s381="subnet4_list"
+s382="subnet4_list_content"
+s383="not_empty_subnet4_list"
+s384="subnet4"
+s385="sub_subnet4"
+s386="subnet4_params"
+s387="subnet4_param"
+s388="subnet"
+s389="subnet_4o6_interface"
+s390="subnet_4o6_interface_id"
+s391="subnet_4o6_subnet"
+s392="interface"
+s393="client_class"
+s394="network_client_classes"
+s395="require_client_classes"
+s396="evaluate_additional_classes"
+s397="reservations_global"
+s398="reservations_in_subnet"
+s399="reservations_out_of_pool"
+s400="id"
+s401="shared_networks"
+s402="shared_networks_content"
+s403="shared_networks_list"
+s404="shared_network"
+s405="shared_network_params"
+s406="shared_network_param"
+s407="option_def_list"
+s408="sub_option_def_list"
+s409="option_def_list_content"
+s410="not_empty_option_def_list"
+s411="option_def_entry"
+s412="sub_option_def"
+s413="option_def_params"
+s414="not_empty_option_def_params"
+s415="option_def_param"
+s416="option_def_name"
+s417="code"
+s418="option_def_code"
+s419="option_def_type"
+s420="option_def_record_types"
+s421="space"
+s422="option_def_space"
+s423="option_def_encapsulate"
+s424="option_def_array"
+s425="option_data_list"
+s426="option_data_list_content"
+s427="not_empty_option_data_list"
+s428="option_data_entry"
+s429="sub_option_data"
+s430="option_data_params"
+s431="not_empty_option_data_params"
+s432="option_data_param"
+s433="option_data_name"
+s434="option_data_data"
+s435="option_data_code"
+s436="option_data_space"
+s437="option_data_csv_format"
+s438="option_data_always_send"
+s439="option_data_never_send"
+s440="option_data_client_classes"
+s441="pools_list"
+s442="pools_list_content"
+s443="not_empty_pools_list"
+s444="pool_list_entry"
+s445="sub_pool4"
+s446="pool_params"
+s447="pool_param"
+s448="pool_entry"
+s449="pool_id"
+s450="user_context"
+s451="comment"
+s452="reservations"
+s453="reservations_list"
+s454="not_empty_reservations_list"
+s455="reservation"
+s456="sub_reservation"
+s457="reservation_params"
+s458="not_empty_reservation_params"
+s459="reservation_param"
+s460="next_server"
+s461="server_hostname"
+s462="boot_file_name"
+s463="ip_address"
+s464="duid"
+s465="hw_address"
+s466="client_id_value"
+s467="circuit_id_value"
+s468="flex_id_value"
+s469="hostname"
+s470="reservation_client_classes"
+s471="relay"
+s472="relay_map"
+s473="ip_addresses"
+s474="client_classes"
+s475="client_classes_list"
+s476="client_class_entry"
+s477="client_class_params"
+s478="not_empty_client_class_params"
+s479="client_class_param"
+s480="client_class_name"
+s481="client_class_test"
+s482="client_class_template_test"
+s483="only_if_required"
+s484="only_in_additional_list"
+s485="dhcp4o6_port"
+s486="control_socket"
+s487="control_sockets"
+s488="control_socket_list"
+s489="not_empty_control_socket_list"
+s490="control_socket_entry"
+s491="control_socket_params"
+s492="control_socket_param"
+s493="control_socket_type"
+s494="control_socket_type_value"
+s495="control_socket_name"
+s496="control_socket_address"
+s497="control_socket_port"
+s498="cert_required"
+s499="http_headers"
+s500="http_header_list"
+s501="not_empty_http_header_list"
+s502="http_header"
+s503="http_header_params"
+s504="http_header_param"
+s505="header_value"
+s506="authentication"
+s507="auth_params"
+s508="auth_param"
+s509="auth_type"
+s510="auth_type_value"
+s511="realm"
+s512="directory"
+s513="clients"
+s514="clients_list"
+s515="not_empty_clients_list"
+s516="basic_auth"
+s517="clients_params"
+s518="clients_param"
+s519="user_file"
+s520="password_file"
+s521="dhcp_queue_control"
+s522="queue_control_params"
+s523="queue_control_param"
+s524="enable_queue"
+s525="queue_type"
+s526="capacity"
+s527="arbitrary_map_entry"
+s528="dhcp_ddns"
+s529="sub_dhcp_ddns"
+s530="dhcp_ddns_params"
+s531="dhcp_ddns_param"
+s532="enable_updates"
+s533="server_ip"
+s534="server_port"
+s535="sender_ip"
+s536="sender_port"
+s537="max_queue_size"
+s538="ncr_protocol"
+s539="ncr_protocol_value"
+s540="ncr_format"
+s541="config_control"
+s542="sub_config_control"
+s543="config_control_params"
+s544="config_control_param"
+s545="config_databases"
+s546="config_fetch_wait_time"
+s547="loggers"
+s548="loggers_entries"
+s549="logger_entry"
+s550="logger_params"
+s551="logger_param"
+s552="debuglevel"
+s553="severity"
+s554="output_options_list"
+s555="output_options_list_content"
+s556="output_entry"
+s557="output_params_list"
+s558="output_params"
+s559="output"
+s560="flush"
+s561="maxsize"
+s562="maxver"
+s563="pattern"
+s564="compatibility"
+s565="compatibility_params"
+s566="compatibility_param"
+s567="lenient_option_parsing"
+s568="ignore_dhcp_server_identifier"
+s569="ignore_rai_link_selection"
+s570="exclude_first_last_24"
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <cc/command_interpreter.h>
+#include <cc/data.h>
+
+#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcp4/json_config_parser.h>
+#include <dhcp4/parser_context.h>
+#include <log/logger_support.h>
+#include <process/daemon.h>
+
+#include "helper_func.h"
+
+#include <array>
+#include <cstdlib>
+#include <string>
+#include <unistd.h>
+#include <vector>
+
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+
+using ControlledDhcpvSrv = ControlledDhcpv4Srv;
+static constexpr Parser4Context::ParserType parserTypes[] = {
+ Parser4Context::PARSER_JSON,
+ Parser4Context::PARSER_INTERFACES,
+ Parser4Context::PARSER_OPTION_DATA,
+ Parser4Context::PARSER_OPTION_DEF,
+ Parser4Context::PARSER_OPTION_DEFS,
+ Parser4Context::PARSER_HOST_RESERVATION,
+ Parser4Context::PARSER_HOOKS_LIBRARY,
+ Parser4Context::PARSER_DHCP_DDNS,
+ Parser4Context::PARSER_CONFIG_CONTROL,
+ Parser4Context::PARSER_HOST_RESERVATION,
+ Parser4Context::PARSER_DHCP4,
+ Parser4Context::SUBPARSER_DHCP4,
+ Parser4Context::PARSER_SUBNET4,
+ Parser4Context::PARSER_POOL4,
+};
+
+static const char *cmds[] = {"config-get",
+ "config-hash-get",
+ "config-write",
+ "config-set",
+ "config-test",
+ "config-reload",
+ "dhcp-disable",
+ "dhcp-enable",
+ "version-get",
+ "build-report",
+ "leases-reclaim",
+ "server-tag-get",
+ "config-backend-pull",
+ "status-get",
+ "statistic-set-max-sample-count-all",
+ "statistic-set-max-sample-age-all",
+ "subnet4-select-test",
+ "subnet4o6-select-test",
+ "lfc-start",
+ "shutdown"};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Set variables
+ FuzzedDataProvider fdp(data, size);
+ Parser4Context ctx;
+ ControlledDhcpv4Srv srv(0, 0);
+
+ // Get random flags
+ const bool checkOnly = fdp.ConsumeBool();
+ const bool extraChecks = fdp.ConsumeBool();
+
+ // Get random type and command
+ Parser4Context::ParserType type =
+ parserTypes[fdp.ConsumeIntegralInRange<int>(0, 13)];
+ std::string cmdStr =
+ std::string(cmds[fdp.ConsumeIntegralInRange<int>(0, 19)]);
+
+ // If no more remaining bytes, early exit
+ if (fdp.remaining_bytes() <= 0) {
+ return 0;
+ }
+
+ // Provide two type of payload with different length to avoid
+ // timeout from parsing trusted configuration file
+ std::string limit_payload = fdp.ConsumeRandomLengthString(25600);
+ std::string full_payload(reinterpret_cast<const char*>(data), size);
+
+ // First target based on the raw payload entire. This makes seeding a lot
+ // easier.
+ try {
+ ElementPtr rawTree = ctx.parseString(limit_payload, Parser4Context::PARSER_JSON);
+
+ // Configure the server with valid tree
+ if (rawTree) {
+ configureDhcp4Server(srv, rawTree, false, extraChecks);
+ ControlledDhcpv4Srv::checkConfig(rawTree);
+ ControlledDhcpv4Srv::processConfig(rawTree);
+ }
+ } catch (const isc::Exception&) {
+ }
+
+ try {
+ ElementPtr tree = ctx.parseString(limit_payload, type);
+
+ // Configure the server with valid tree
+ if (tree) {
+ if (type == Parser4Context::PARSER_JSON ||
+ type == Parser4Context::PARSER_DHCP4) {
+ configureDhcp4Server(srv, tree, checkOnly, extraChecks);
+ ControlledDhcpv4Srv::checkConfig(tree);
+ ControlledDhcpv4Srv::processConfig(tree);
+ }
+ }
+ } catch (const isc::Exception&) {
+ }
+
+ // File base parsing
+ try {
+ std::string path = fuzz::writeTempFile(limit_payload, "json");
+ if (!path.empty()) {
+ ElementPtr fileTree = ctx.parseFile(path, Parser4Context::PARSER_DHCP4);
+ if (fileTree) {
+ configureDhcp4Server(srv, fileTree, checkOnly, extraChecks);
+ ControlledDhcpv4Srv::checkConfig(fileTree);
+ ControlledDhcpv4Srv::processConfig(fileTree);
+ }
+ unlink(path.c_str());
+ }
+ } catch (const isc::Exception&) {
+ }
+
+ // Command parsing
+ try {
+ ElementPtr args = fuzz::parseJSON(full_payload);
+ ElementPtr cmd = Element::create(cmdStr);
+
+ // Configure root element
+ ElementPtr root = Element::createMap();
+ root->set("command", cmd);
+ root->set("arguments", args);
+
+ // Transform to const element
+ ConstElementPtr cmd_const = cmd;
+ ConstElementPtr root_const = root;
+
+ parseCommand(cmd_const, root_const);
+
+ // Response answer parsing
+ int status = 0;
+ parseAnswer(status, fuzz::parseJSON(full_payload));
+ } catch(const isc::Exception&) {}
+
+ // Try fuzzing specific deeper fuzzers directly
+
+ // Subnets6ListConfigParser
+ try {
+ ElementPtr elem = fuzz::parseJSON(full_payload);
+ SrvConfigPtr srv = SrvConfigPtr(new SrvConfig());
+ Subnets6ListConfigParser parser(fdp.ConsumeBool());
+ parser.parse(srv, elem, fdp.ConsumeBool());
+ } catch (const isc::Exception&) {
+ // Known exceptions
+ }
+
+ // RelayInfoParser
+ try {
+ Option::Universe opt = Option::V4;
+ ElementPtr elem = fuzz::parseJSON(full_payload);
+ Network::RelayInfoPtr info = Network::RelayInfoPtr(new Network::RelayInfo());
+ RelayInfoParser parser(opt);
+ parser.parse(info, elem);
+ } catch (const isc::Exception&) {
+ // Known exceptions
+ }
+
+ // PdPoolParser
+ try {
+ ElementPtr elem = fuzz::parseJSON(full_payload);
+ PoolStoragePtr pools(new PoolStorage());
+ PdPoolParser parser = PdPoolParser();
+ parser.parse(pools, elem, fdp.ConsumeBool());
+ } catch (const isc::Exception&) {
+ // Known exceptions
+ }
+
+ // CompatibilityParser
+ try {
+ ElementPtr elem = fuzz::parseJSON(full_payload);
+ SrvConfig srv = SrvConfig();
+ CompatibilityParser parser = CompatibilityParser();
+ parser.parse(elem, srv);
+ } catch (const isc::Exception&) {
+ // Known exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <cc/data.h>
+#include <cc/command_interpreter.h>
+
+#include <dhcp6/parser_context.h>
+#include <dhcp6/json_config_parser.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <process/daemon.h>
+#include <log/logger_support.h>
+
+#include "helper_func.h"
+
+#include <array>
+#include <vector>
+#include <string>
+#include <cstdlib>
+#include <unistd.h>
+
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+
+using ControlledDhcpvSrv = ControlledDhcpv6Srv;
+static constexpr Parser6Context::ParserType parserTypes[] = {
+ Parser6Context::PARSER_JSON, Parser6Context::PARSER_INTERFACES,
+ Parser6Context::PARSER_OPTION_DATA, Parser6Context::PARSER_OPTION_DEF,
+ Parser6Context::PARSER_OPTION_DEFS, Parser6Context::PARSER_HOST_RESERVATION,
+ Parser6Context::PARSER_HOOKS_LIBRARY, Parser6Context::PARSER_DHCP_DDNS,
+ Parser6Context::PARSER_CONFIG_CONTROL, Parser6Context::PARSER_HOST_RESERVATION,
+ Parser6Context::PARSER_DHCP6, Parser6Context::SUBPARSER_DHCP6,
+ Parser6Context::PARSER_SUBNET6, Parser6Context::PARSER_POOL6,
+};
+
+static const char* cmds[] = {
+ "config-get","config-hash-get","config-write","config-set","config-test",
+ "config-reload","dhcp-disable","dhcp-enable","version-get","build-report",
+ "leases-reclaim","server-tag-get","config-backend-pull","status-get",
+ "statistic-set-max-sample-count-all","statistic-set-max-sample-age-all",
+ "subnet6-select-test","subnet6o6-select-test","lfc-start","shutdown"
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (const isc::Exception&) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Set variables
+ FuzzedDataProvider fdp(data, size);
+ Parser6Context ctx;
+ ControlledDhcpv6Srv srv(0, 0);
+
+ // Get random flags
+ const bool checkOnly = fdp.ConsumeBool();
+ const bool extraChecks = fdp.ConsumeBool();
+
+ // Get random type and command
+ Parser6Context::ParserType type = parserTypes[fdp.ConsumeIntegralInRange<int>(0, 13)];
+ std::string cmdStr = std::string(cmds[fdp.ConsumeIntegralInRange<int>(0, 19)]);
+
+ // If no more remaining bytes, early exit
+ if (fdp.remaining_bytes() <= 0) {
+ return 0;
+ }
+
+ // Provide two type of payload with different length to avoid
+ // timeout from parsing trusted configuration file
+ std::string limit_payload = fdp.ConsumeRandomLengthString(25600);
+ std::string full_payload(reinterpret_cast<const char*>(data), size);
+
+ // Perform an evaluation of the raw data.
+ try {
+ // General parsing
+ ElementPtr rawTree = ctx.parseString(limit_payload, Parser6Context::PARSER_JSON);
+
+ // Configure the server with valid tree
+ if (rawTree) {
+ configureDhcp6Server(srv, rawTree, false, true);
+ ControlledDhcpv6Srv::checkConfig(rawTree);
+ ControlledDhcpv6Srv::processConfig(rawTree);
+ }
+ } catch(const isc::Exception&){}
+
+ // Generate random string
+ try {
+ // General parsing
+ ElementPtr tree = ctx.parseString(limit_payload, type);
+
+ // Configure the server with valid tree
+ if (tree) {
+ if (type == Parser6Context::PARSER_JSON || type == Parser6Context::PARSER_DHCP6){
+ configureDhcp6Server(srv, tree, checkOnly, extraChecks);
+ ControlledDhcpv6Srv::checkConfig(tree);
+ ControlledDhcpv6Srv::processConfig(tree);
+ }
+ }
+ } catch(const isc::Exception&){}
+
+ try {
+ // File base parsing
+ std::string path = fuzz::writeTempFile(limit_payload, "json");
+ if (!path.empty()) {
+ ElementPtr fileTree = ctx.parseFile(path, Parser6Context::PARSER_DHCP6);
+ if (fileTree) {
+ configureDhcp6Server(srv, fileTree, checkOnly, extraChecks);
+ ControlledDhcpv6Srv::checkConfig(fileTree);
+ ControlledDhcpv6Srv::processConfig(fileTree);
+ }
+ unlink(path.c_str());
+ }
+ }
+ catch (const isc::Exception&){}
+
+ try{
+ // Command parsing
+ ElementPtr args = fuzz::parseJSON(full_payload);
+ ElementPtr cmd = Element::create(cmdStr);
+
+ // Configure root element
+ ElementPtr root = Element::createMap();
+ root->set("command", cmd);
+ root->set("arguments", args);
+
+ // Transform to const element
+ ConstElementPtr cmd_const = cmd;
+ ConstElementPtr root_const = root;
+
+ parseCommand(cmd_const, root_const);
+
+ // Response answer parsing
+ int status = 0;
+ parseAnswer(status, fuzz::parseJSON(full_payload));
+ } catch (const isc::Exception&) {
+ // Known exceptions
+ }
+
+ // Try fuzzing specific deeper fuzzers directly
+
+ // Subnets6ListConfigParser
+ try {
+ ElementPtr elem = fuzz::parseJSON(full_payload);
+ SrvConfigPtr srv = SrvConfigPtr(new SrvConfig());
+ Subnets6ListConfigParser parser(fdp.ConsumeBool());
+ parser.parse(srv, elem, fdp.ConsumeBool());
+ } catch (const isc::Exception&) {
+ // Known exceptions
+ }
+
+ // RelayInfoParser
+ try {
+ Option::Universe opt = Option::V6;
+ ElementPtr elem = fuzz::parseJSON(full_payload);
+ Network::RelayInfoPtr info = Network::RelayInfoPtr(new Network::RelayInfo());
+ RelayInfoParser parser(opt);
+ parser.parse(info, elem);
+ } catch (const isc::Exception&) {
+ // Known exceptions
+ }
+
+ // Pool6Parser
+ try {
+ ElementPtr elem = fuzz::parseJSON(full_payload);
+ PoolStoragePtr pools(new PoolStorage());
+ Pool6Parser parser = Pool6Parser();
+ parser.parse(pools, elem, AF_INET6, fdp.ConsumeBool());
+ } catch (const isc::Exception&) {
+ // Known exceptions
+ }
+
+ // CompatibilityParser
+ try {
+ ElementPtr elem = fuzz::parseJSON(full_payload);
+ SrvConfig srv = SrvConfig();
+ CompatibilityParser parser = CompatibilityParser();
+ parser.parse(elem, srv);
+ } catch (const isc::Exception&) {
+ // Known exceptions
+ }
+
+ return 0;
+}
+
--- /dev/null
+"\x63\x82\x53\x63"
+"{"
+"}"
+"["
+"]"
+":"
+","
+"Dhcp4"
+"interfaces-config"
+"interfaces"
+"*"
+"rebind-timer"
+"renew-timer"
+"valid-lifetime"
+"subnet4"
+"id"
+"pools"
+"pool"
+"subnet"
+"interface"
+"option-data"
+"name"
+"data"
+"csv-format"
+"always-send"
+"never-send"
+"hosts-database"
+"type"
+"mysql"
+"postgresql"
+"user"
+"password"
+"hooks-libraries"
+"hosts-databases"
+"control-socket"
+"control-sockets"
+"authentication"
+"clients"
+"user-file"
+"password-file"
+"client-classes"
+"test"
+"template-test"
+"option-def"
+"code"
+"uint32"
+"next-server"
+"server-hostname"
+"boot-file-name"
+"user-context"
+"secure"
+"eth0"
+"eth1"
+"10.254.226.0/25"
+"10.254.226.0/24"
+"192.0.2.0/24"
+"192.0.2.1 - 192.0.2.100"
+"default-ip-ttl"
+"ip-forwarding"
+"false"
+"true"
+"FF"
+"interfaces-config\": { \"interfaces\": [ \"*\" ] }"
+"option-data\": ["
+"{ \"name\": "
+"{ \"code\": "
+"{ \"pool\": "
+"subnet4\": [ {"
+"pools\": [ {"
+"id\": 1"
+"interface\": \"eth0\""
+
+"Dhcp6"
+"subnet6"
+"pd-pools"
+"lease-database"
+"preferred-lifetime"
+"min-preferred-lifetime"
+"max-preferred-lifetime"
+"min-valid-lifetime"
+"max-valid-lifetime"
+"rapid-commit"
+"duid"
+"duid-type"
+"interface-id"
+"relay"
+"relay-supplied-options"
+"ia-na"
+"ia-ta"
+"ia-pd"
+"prefix"
+"prefix-len"
+"delegated-len"
+"excluded-prefix"
+"excluded-prefix-len"
+"dns-servers"
+"domain-search"
+"renew-timer\": "
+"rebind-timer\": "
+"valid-lifetime\": "
+"preferred-lifetime\": "
+"rapid-commit\": true"
+"rapid-commit\": false"
+"lease-database\": {"
+"type\": \"memfile\""
+"type\": \"mysql\""
+"type\": \"postgresql\""
+"persist\": true"
+"persist\": false"
+"subnet6\": [ {"
+"pd-pools\": [ {"
+"{ \"prefix\": "
+"{ \"prefix-len\": "
+"{ \"delegated-len\": "
+"{ \"excluded-prefix\": "
+"{ \"excluded-prefix-len\": "
+"{ \"interface-id\": "
+"option-data\": [ {"
+"{ \"name\": \"dns-servers\", \"data\": "
+"{ \"name\": \"domain-search\", \"data\": "
+"{ \"name\": \"client-id\", \"data\": "
+"{ \"name\": \"server-id\", \"data\": "
+"{ \"name\": \"elapsed-time\", \"data\": "
+"{ \"name\": \"oro\", \"data\": "
+"{ \"name\": \"rapid-commit\" "
+"client-classes\": ["
+"{ \"name\": "
+"user-context\": {"
+"hooks-libraries\": ["
+"authentication\": {"
+"interfaces-config\": { \"interfaces\": ["
+"2001:db8::/32"
+"2001:db8:1::/48"
+"2001:db8:1::/64"
+"2001:db8:2::/56"
+"2001:db8:1::10 - 2001:db8:1::ffff"
+"fe80::1"
+"::1"
+"::/0"
+"duid\": \"00:01:00:01:24:60:9b:d0:aa:bb:cc:dd:ee:ff\""
+"server-id\": \"00:02:00:01:aa:bb:cc:dd:ee:ff:00:11:22:33\""
+"client-id\": \"00:03:00:01:11:22:33:44:55:66\""
+"prefix\": \"2001:db8:2::\""
+"prefix-len\": 56"
+"delegated-len\": 64"
+"excluded-prefix\": \"2001:db8:2::\""
+"excluded-prefix-len\": 64"
+"pool\": \"2001:db8:1::/64\""
+"pool\": \"2001:db8:1::10 - 2001:db8:1::ffff\""
+"interface\": \"eth0\""
+"interface\": \"eth1\""
+"true"
+"false"
+"null"
+"0"
+"1"
+"56"
+"64"
+"3600"
+"7200"
+"86400"
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt4o6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option.h>
+#include <dhcp/protocol_util.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcp/option_vendor.h>
+#include <dhcp/option_vendor_class.h>
+#include <log/logger_support.h>
+#include <process/daemon.h>
+#include <util/buffer.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+
+static thread_local FuzzedDataProvider* fdp = nullptr;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Randomly enable validatePath checking
+ fdp = new FuzzedDataProvider(data, size);
+ isc::util::file::PathChecker::enableEnforcement(fdp->ConsumeBool());
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Create temporary configuration file
+ std::string path = fuzz::writeTempConfig(true);
+ if (path.empty()) {
+ // Early exit if configuration file creation failed
+ fuzz::deleteTempFile(path);
+ return 0;
+ }
+
+ OptionCollection options;
+ std::list<uint16_t> deferred;
+ std::unique_ptr<ControlledDhcpv4Srv> srv;
+ std::vector<uint8_t> buf(data, data + size);
+
+ try {
+ // Package parsing
+ Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(data, size));
+ pkt->toText(fdp->ConsumeBool());
+ pkt->getType();
+ pkt->getTransid();
+ pkt->unpack();
+ pkt->pack();
+ pkt->getName();
+ pkt->getName(fdp->ConsumeIntegral<uint8_t>());
+ pkt->getLabel();
+ pkt->getMAC(fdp->ConsumeIntegral<uint16_t>());
+ } catch (...) {}
+
+ // OptionVendorClass parsing
+ try {
+ OptionBuffer buf(data, data + size);
+ OptionVendorClassPtr vendor_class;
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V4,
+ buf.begin(),
+ buf.end()));
+ }catch(...){
+ }
+
+ try {
+ // Package parsing for 4o6
+ Pkt4Ptr pkt4 = Pkt4Ptr(new Pkt4(data, size));
+ Pkt6Ptr pkt6 = Pkt6Ptr(new Pkt6(data, size));
+ Pkt4o6Ptr pkt = Pkt4o6Ptr(new Pkt4o6(pkt4, pkt6));
+ pkt->toText();
+ pkt->getType();
+ pkt->getTransid();
+ pkt->unpack();
+ pkt->pack();
+ pkt->getName();
+ pkt->getName(fdp->ConsumeIntegral<uint8_t>());
+ pkt->getLabel();
+ pkt->getMAC(fdp->ConsumeIntegral<uint16_t>());
+ } catch (...) {}
+
+ try {
+ // Protocol parsing
+ InputBuffer buf(data, size);
+ Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+ decodeEthernetHeader(buf, pkt);
+ decodeIpUdpHeader(buf, pkt);
+ calcChecksum(data, size, fdp->ConsumeIntegral<uint32_t>());
+ } catch (...) {}
+
+ // OptionVendor parsing
+ try{
+ OptionBuffer buf(data, data + size);
+ OptionVendorPtr vendor;
+ vendor.reset(new OptionVendor(Option::V4, buf.begin() + 2, buf.end()));
+ OutputBuffer output(0);
+ vendor->pack(output);
+ }
+ catch (...){}
+
+ try {
+ // Package parsing
+ Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(data, size));
+
+ // Option parsing
+ LibDHCP::unpackOptions4(buf, DHCP4_OPTION_SPACE, options, deferred, false);
+ for (auto& kv : options) {
+ auto opt = kv.second;
+ if (!opt) {
+ continue;
+ }
+ opt->getType();
+ opt->toText();
+ }
+/*
+ // Server initialisation
+ srv.reset(new ControlledDhcpv4Srv());
+ srv->init(path);
+
+ // Process packet
+ if (srv) {
+ srv->processPacket(pkt);
+ srv->processDhcp4Query(pkt, fdp->ConsumeBool());
+ }
+*/
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ }
+
+ // srv.reset();
+
+ // Remove temp configuration file
+ fuzz::deleteTempFile(path);
+ delete fdp;
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/pkt6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <dhcp/option_vendor_class.h>
+#include <log/logger_support.h>
+#include <process/daemon.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+
+static thread_local FuzzedDataProvider* fdp = nullptr;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Randomly enable validatePath checking
+ fdp = new FuzzedDataProvider(data, size);
+ isc::util::file::PathChecker::enableEnforcement(fdp->ConsumeBool());
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Create temporary configuration file
+ std::string path = fuzz::writeTempConfig(true);
+ if (path.empty()) {
+ // Early exit if configuration file creation failed
+ fuzz::deleteTempFile(path);
+ return 0;
+ }
+
+ OptionCollection options;
+ std::unique_ptr<ControlledDhcpv6Srv> srv;
+ std::vector<uint8_t> buf(data, data + size);
+
+ try {
+ Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(data, size));
+ pkt->toText();
+ pkt->getType();
+ pkt->getTransid();
+ pkt->unpack();
+ pkt->pack();
+ pkt->getMAC(fdp->ConsumeIntegral<uint32_t>());
+ pkt->getName(fdp->ConsumeIntegral<uint8_t>());
+ pkt->getLabel();
+ } catch (...) {}
+
+ // OptionVendor parsing
+ try {
+ OptionBuffer buf(data, data + size);
+ OptionVendorClassPtr vendor_class;
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V6,
+ buf.begin(),
+ buf.end()));
+ }catch(...){}
+
+ try {
+ // Package parsing
+ Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(data, size));
+ pkt->toText();
+ pkt->getType();
+ pkt->getTransid();
+
+ // Option parsing
+ LibDHCP::unpackOptions6(buf, DHCP6_OPTION_SPACE, options);
+ for (auto& kv : options) {
+ auto opt = kv.second;
+ if (!opt) {
+ continue;
+ }
+ opt->getType();
+ opt->toText();
+ }
+
+ // Server initialisation
+ srv.reset(new ControlledDhcpv6Srv());
+ srv->init(path);
+
+ // Process packet
+ if (srv) {
+ srv->processPacket(pkt);
+ srv->processDhcp6Query(pkt);
+ }
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ }
+
+ srv.reset();
+
+ // Remove temp configuration file
+ fuzz::deleteTempFile(path);
+ delete fdp;
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+
+extern "C" int lease4_offer(CalloutHandle& handle);
+extern "C" int leases4_committed(CalloutHandle& handle);
+
+namespace isc {
+ namespace dhcp {
+ class MyDhcpv4Srv : public ControlledDhcpv4Srv {
+ public:
+ bool fuzz_accept(const Pkt4Ptr& pkt) {
+ return accept(pkt);
+ }
+
+ static void fuzz_sanityCheck(const Pkt4Ptr& query) {
+ ControlledDhcpv4Srv::sanityCheck(query);
+ }
+
+ void fuzz_classifyPacket(const Pkt4Ptr& pkt) {
+ classifyPacket(pkt);
+ }
+
+ ConstSubnet4Ptr fuzz_selectSubnet(const Pkt4Ptr& query,
+ bool& drop,
+ bool allow_answer_park = true) {
+ return selectSubnet(query, drop, allow_answer_park);
+ }
+ };
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Disable validatePath checking to allow writing configuration file to /tmp
+ isc::util::file::PathChecker::enableEnforcement(false);
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Creating temp config file
+ std::string path = fuzz::writeTempConfig(true);
+ if (path.empty()) {
+ // Early exit if configuration file creation failed
+ fuzz::deleteTempFile(path);
+ return 0;
+ }
+
+ // Creating temp lease file
+ std::string lease_path = fuzz::writeTempLease(true);
+
+ Pkt4Ptr pkt;
+ std::unique_ptr<MyDhcpv4Srv> srv;
+
+ // Package parsing
+ try {
+ // Add fixed magic cookie and correct hardware address
+ std::vector<uint8_t> buf(data, data + size);
+ if (size >= 240) {
+ // Max hardware address length is 20
+ buf[2] = 20;
+
+ // Magic cookie fixed value 0x63825363
+ buf[236] = 0x63;
+ buf[237] = 0x82;
+ buf[238] = 0x53;
+ buf[239] = 0x63;
+ }
+
+ pkt = Pkt4Ptr(new Pkt4(buf.data(), buf.size()));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ FuzzedDataProvider fdp(data, size);
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 8);
+ pkt->setType(static_cast<DHCPMessageType>(typeChoice));
+
+ // Server initialisation
+ try {
+ srv.reset(new MyDhcpv4Srv());
+ srv->init(path);
+ } catch (...) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ if (!srv) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ // Call classifyPacket for packet checking
+ try {
+ srv->fuzz_classifyPacket(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Call accept for packet checking
+ try {
+ srv->fuzz_accept(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Call sanityCheck for packet checking
+ try {
+ MyDhcpv4Srv::fuzz_sanityCheck(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Prepare client context
+ AllocEngine::ClientContext4Ptr ctx(new AllocEngine::ClientContext4());
+
+ // Call earlyGHRLookup
+ try {
+ srv->earlyGHRLookup(pkt, ctx);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Call select subnet
+ try {
+ bool drop = false;
+ if (!ctx) {
+ ctx.reset(new AllocEngine::ClientContext4());
+ }
+ if (ctx) {
+ ctx->subnet_ = srv->fuzz_selectSubnet(pkt, drop, false);
+ }
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Call processLocalizedQuery4
+ try {
+ srv->processLocalizedQuery4(ctx, false);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Prepare callout handle
+ CalloutHandlePtr handle = getCalloutHandle(pkt);
+ Pkt4Ptr rsp;
+
+ // Call lease4_offer
+ try {
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw);
+ handle->setArgument("leases4", leases);
+ if (!ctx) {
+ ctx.reset(new AllocEngine::ClientContext4());
+ }
+ if (ctx) {
+ handle->setArgument("offer_lifetime", ctx->offer_lft_);
+ handle->setArgument("old_lease", ctx->old_lease_);
+ handle->setArgument("host", ctx->currentHost());
+ }
+ handle->setArgument("query4", pkt);
+ handle->setArgument("response4", rsp);
+
+ lease4_offer(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ // Call lease4_committed
+ try {
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw);
+ handle->setArgument("leases4", leases);
+ handle->setArgument("query4", pkt);
+ handle->setArgument("response4", rsp);
+
+ leases4_committed(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ srv.reset();
+
+ // Remove temp files
+ fuzz::deleteTempFile(path);
+ fuzz::deleteTempFile(lease_path);
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+#include <cstdlib>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+
+extern "C" int leases6_committed(CalloutHandle& handle);
+
+namespace isc {
+ namespace dhcp {
+ class MyDhcpv6Srv : public ControlledDhcpv6Srv {
+ public:
+ void fuzz_sanityCheck(const Pkt6Ptr& query) {
+ sanityCheck(query);
+ }
+
+ void fuzz_classifyPacket(const Pkt6Ptr& pkt) {
+ classifyPacket(pkt);
+ }
+
+ ConstSubnet6Ptr fuzz_selectSubnet(const Pkt6Ptr& question, bool& drop) {
+ return selectSubnet(question, drop);
+ }
+ };
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Disable validatePath checking to allow writing configuration file to /tmp
+ isc::util::file::PathChecker::enableEnforcement(false);
+
+ // Force DUID file to /tmp
+ setenv("KEA_DHCP_DATA_DIR", "/tmp", 1);
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Creating temp config file
+ std::string path = fuzz::writeTempConfig(false);
+ if (path.empty()) {
+ // Early exit if configuration file creation failed
+ fuzz::deleteTempFile(path);
+ return 0;
+ }
+
+ // Creating temp lease file
+ std::string lease_path = fuzz::writeTempLease(false);
+
+ Pkt6Ptr pkt;
+ std::unique_ptr<MyDhcpv6Srv> srv;
+
+ // Package parsing
+ try {
+ pkt = Pkt6Ptr(new Pkt6(data, size));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ FuzzedDataProvider fdp(data, size);
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 37);
+ pkt->setType(static_cast<DHCPv6MessageType>(typeChoice));
+
+ // Server initialisation
+ try {
+ srv.reset(new MyDhcpv6Srv());
+ srv->init(path);
+ } catch (...) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ if (!srv) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ // Call classifyPacket for packet checking
+ try {
+ srv->fuzz_classifyPacket(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Call sanityCheck for packet checking
+ try {
+ srv->fuzz_sanityCheck(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Call process functions after the accept and check
+ try {
+ srv->processDhcp6Query(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Prepare client context
+ AllocEngine::ClientContext6 ctx;
+
+ // Call earlyGHRLookup
+ try {
+ srv->earlyGHRLookup(pkt, ctx);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Call select subnet
+ try {
+ bool drop = false;
+ ctx.subnet_ = srv->fuzz_selectSubnet(pkt, drop);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Call processLocalizedQuery6
+ try {
+ srv->processLocalizedQuery6(ctx);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Prepare callout handle
+ CalloutHandlePtr handle = getCalloutHandle(pkt);
+ Pkt6Ptr rsp;
+
+ // Call lease4_committed
+ try {
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease6Collection leases = LeaseMgrFactory::instance().getLease6(hw);
+ handle->setArgument("leases6", leases);
+ handle->setArgument("query6", pkt);
+ handle->setArgument("response6", rsp);
+
+ leases6_committed(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ srv.reset();
+
+ // Remove temp files
+ fuzz::deleteTempFile(path);
+ fuzz::deleteTempFile(lease_path);
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <string>
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+
+#include <dhcpsrv/srv_config.h>
+#include <dhcpsrv/cfg_option_def.h>
+#include <dhcpsrv/cfg_option.h>
+#include <dhcpsrv/cfg_iface.h>
+#include <dhcpsrv/cfg_duid.h>
+#include <dhcpsrv/cfg_expiration.h>
+#include <dhcpsrv/cfg_mac_source.h>
+#include <dhcpsrv/client_class_def.h>
+
+#include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
+#include <dhcpsrv/parsers/ifaces_config_parser.h>
+#include <dhcpsrv/parsers/duid_config_parser.h>
+#include <dhcpsrv/parsers/multi_threading_config_parser.h>
+#include <dhcpsrv/parsers/sanity_checks_parser.h>
+#include <dhcpsrv/parsers/expiration_config_parser.h>
+#include <dhcpsrv/parsers/client_class_def_parser.h>
+#include <dhcpsrv/parsers/host_reservation_parser.h>
+#include <dhcpsrv/parsers/simple_parser4.h>
+#include <dhcpsrv/parsers/simple_parser6.h>
+
+#include "helper_func.h"
+
+using namespace isc;
+using namespace isc::data;
+using namespace isc::dhcp;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+ FuzzedDataProvider fdp(Data, Size);
+ std::string payload = fdp.ConsumeRandomLengthString();
+
+ std::string raw_payload(reinterpret_cast<const char*>(Data), Size);
+ ElementPtr payload_elem = nullptr;
+ try {
+ payload_elem = Element::fromJSON(payload);
+ } catch (...) {
+ return 0;
+ }
+
+
+ try {
+ // Simple Parsing
+ SimpleParser4::setAllDefaults(payload_elem);
+ SimpleParser4::deriveParameters(payload_elem);
+ SimpleParser6::setAllDefaults(payload_elem);
+ SimpleParser6::deriveParameters(payload_elem);
+ } catch (const isc::Exception&) {}
+ try {
+ // Configuration Option definition parsing
+ //ElementPtr elem = fuzz::parseJSON(payload);
+ CfgOptionDefPtr defs(new CfgOptionDef());
+ OptionDefListParser defp4(AF_INET);
+ defp4.parse(defs, payload_elem);
+ //elem = fuzz::parseJSON(fdp.ConsumeRandomLengthString());
+ OptionDefListParser defp6(AF_INET6);
+ defp6.parse(defs, payload_elem);
+ } catch (const isc::Exception&) {}
+ try {
+
+ // Configuration Option data parsing
+ //ElementPtr elem = fuzz::parseJSON(payload);
+ CfgOptionPtr opts(new CfgOption());
+ CfgOptionDefPtr defs(new CfgOptionDef());
+ OptionDataListParser odlp4(AF_INET, defs);
+ odlp4.parse(opts, payload_elem, fdp.ConsumeBool());
+ //elem = fuzz::parseJSON(fdp.ConsumeRandomLengthString());
+ OptionDataListParser odlp6(AF_INET6, defs);
+ odlp6.parse(opts, payload_elem, fdp.ConsumeBool());
+ } catch (const isc::Exception&) {}
+ try {
+
+ // Interfaces configuration parsing
+ //ElementPtr elem = fuzz::parseJSON(payload);
+ CfgIfacePtr ifcfg(new CfgIface());
+ IfacesConfigParser ifparser4(AF_INET, false);
+ ifparser4.parse(ifcfg, payload_elem);
+ } catch (const isc::Exception&) {}
+ try {
+ //ElementPtr elem = fuzz::parseJSON(payload);
+ CfgIfacePtr ifcfg(new CfgIface());
+ IfacesConfigParser ifparser6(AF_INET6, false);
+ ifparser6.parse(ifcfg, payload_elem);
+ } catch (const isc::Exception&) {}
+ try {
+
+ // DUID configuration parsing
+ ElementPtr elem = fuzz::parseJSON(payload);
+ CfgDUIDPtr duid(new CfgDUID());
+ DUIDConfigParser duidp;
+ duidp.parse(duid, elem);
+ } catch (const isc::Exception&) {}
+ try {
+
+ // Configuration expiration parsing
+ //ElementPtr elem = fuzz::parseJSON(payload);
+ CfgExpirationPtr exp(new CfgExpiration());
+ ExpirationConfigParser expp;
+ expp.parse(payload_elem, exp);
+ } catch (const isc::Exception&) {}
+ try {
+
+ // MAC list parsing
+ CfgMACSource macs;
+ MACSourcesListConfigParser macp;
+ //ElementPtr elem = fuzz::parseJSON(payload);
+ macp.parse(macs, payload_elem);
+ } catch (const isc::Exception&) {}
+ try {
+
+ // Multi-Threading configuration parsing
+ SrvConfig srv;
+ //ElementPtr elem = fuzz::parseJSON(payload);
+ MultiThreadingConfigParser mtcp;
+ mtcp.parse(srv, payload_elem);
+ } catch (const isc::Exception&) {}
+
+ try {
+
+ // Sanity Check parsing
+ SrvConfig srv;
+ //ElementPtr elem = fuzz::parseJSON(payload);
+ SanityChecksParser scp;
+ scp.parse(srv, payload_elem);
+ } catch (const isc::Exception&) {}
+ try {
+
+ // Client Class definition parsing
+ // ElementPtr elem = fuzz::parseJSON(payload);
+ ClientClassDictionaryPtr dict(new ClientClassDictionary());
+ ClientClassDefParser ccdp;
+ ccdp.parse(dict, payload_elem, AF_INET);
+ ccdp.parse(dict, payload_elem, AF_INET6);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+
+ try {
+ // SubnetConfigParser parsing
+ //ElementPtr elem = fuzz::parseJSON(payload);
+ Subnet4ConfigParser scf(fdp.ConsumeBool());
+ scf.parse(payload_elem, fdp.ConsumeBool());
+ }
+ catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+
+ // ControlSocketsParser
+ try {
+ ElementPtr elem = fuzz::parseJSON(payload);
+ SrvConfig srv;
+ ControlSocketsParser csp;
+ csp.parse(srv, elem);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ try {
+ // Subnet6ConfigParser parsing
+ ElementPtr elem = fuzz::parseJSON(payload);
+ Subnet6ConfigParser scf(fdp.ConsumeBool());
+ scf.parse(elem, fdp.ConsumeBool());
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ try {
+ // D2ClientConfigParser parsing
+ ElementPtr elem = fuzz::parseJSON(payload);
+ D2ClientConfigParser d2p;
+ d2p.parse(elem);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Host Reservation parsing
+ try {
+ // Host Reservation parsing
+ ElementPtr elem = fuzz::parseJSON(payload);
+ HostReservationParser4 hrp;
+ hrp.parse(SubnetID(10), elem, fdp.ConsumeBool());
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ try {
+ // Host Reservation parsing
+ ElementPtr elem = fuzz::parseJSON(payload);
+ HostReservationParser6 hrp;
+ hrp.parse(SubnetID(10), elem, fdp.ConsumeBool());
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+"{"
+"}"
+"["
+"]"
+":"
+","
+"true"
+"false"
+"null"
+"0"
+"1"
+"2"
+"3"
+"4"
+"5"
+"6"
+"7"
+"8"
+"9"
+"Dhcp4"
+"Dhcp6"
+"interfaces-config"
+"interfaces"
+"*"
+"re-detect"
+"dhcp-socket-type"
+"raw"
+"udp"
+"outbound-interface"
+"same-as-inbound"
+"use-routing"
+"listen-dhcp-init-reboot"
+"lease-database"
+"type"
+"memfile"
+"mysql"
+"postgresql"
+"host"
+"port"
+"name"
+"user"
+"password"
+"persist"
+"lfc-interval"
+"lfc-cleanup-interval"
+"max-row-errors"
+"hooks-libraries"
+"library"
+"parameters"
+"loggers"
+"name"
+"output_options"
+"output"
+"severity"
+"debuglevel"
+"shared-networks"
+"shared-networks6"
+"name"
+"id"
+"subnet4"
+"subnet6"
+"subnet"
+"pools"
+"pool"
+"prefix"
+"prefix-len"
+"excluded-prefix"
+"excluded-prefix-len"
+"interface"
+"relay"
+"ip-address"
+"ip-addresses"
+"option-data"
+"option-def"
+"option-data-fields"
+"client-class"
+"client-classes"
+"require-client-classes"
+"match-client-id"
+"authoritative"
+"valid-lifetime"
+"min-valid-lifetime"
+"max-valid-lifetime"
+"preferred-lifetime"
+"renew-timer"
+"rebind-timer"
+"t1-percent"
+"t2-percent"
+"expired-leases-processing"
+"reclaim-timer-wait-time"
+"hold-reclaimed-time"
+"max-reclaim-leases"
+"max-reclaim-time"
+"unwarned-reclaim-cycles"
+"space"
+"dhcp4"
+"dhcp6"
+"code"
+"encapsulate"
+"array"
+"record-types"
+"record-len"
+"csv-format"
+"always-send"
+"never-send"
+"data"
+"len"
+"hex"
+"string"
+"uint8"
+"uint16"
+"uint32"
+"ipv4-address"
+"ipv6-address"
+"fqdn"
+"boolean"
+"name"
+"routers"
+"domain-name-servers"
+"domain-name"
+"netmask"
+"broadcast-address"
+"time-servers"
+"ntp-servers"
+"tftp-server-name"
+"server-name"
+"boot-file-name"
+"vendor-encapsulated-options"
+"vendor-specific-information"
+"hostname"
+"client-identifier"
+"parameter-request-list"
+"dns-servers"
+"domain-search"
+"ia-na"
+"ia-ta"
+"ia-pd"
+"fqdn"
+"sntp-servers"
+"information-refresh-time"
+"vendor-specific-information"
+"server-id"
+"duid"
+"duid-config"
+"duid-type"
+"LLT"
+"LL"
+"EN"
+"UUID"
+"identifier"
+"time"
+"htype"
+"hw-address"
+"enterprise-id"
+"mac-sources"
+"any"
+"client-id"
+"duid"
+"hwaddr"
+"remote-id"
+"subscriber-id"
+"circuit-id"
+"client-linkaddr"
+"src-addr"
+"multi-threading"
+"enable-multi-threading"
+"thread-pool-size"
+"packet-queue-size"
+"sanity-checks"
+"lease-checks"
+"extended-info-checks"
+"warn-if-queries-too-frequent"
+"hwaddr-duplicates"
+"client-id-duplicates"
+"pd-duplicates"
+"client-classes"
+"test"
+"only-if-required"
+"template-test"
+"evaluate"
+"expression"
+"evaluate-add"
+"option"
+"substring"
+"concat"
+"ifelse"
+"equals"
+"not"
+"and"
+"or"
+"hexstring"
+"pkt4"
+"pkt6"
+"relay4"
+"relay6"
+"vendor"
+"vendor-class"
+"hardware"
+"src"
+"dst"
+"reservations"
+"hostname"
+"ip-addresses"
+"ip-address"
+"duid"
+"hw-address"
+"client-id"
+"flex-id"
+"next-server"
+"server-hostname"
+"boot-file-name"
+"require-client-classes"
+"reservation-mode"
+"disabled"
+"out-of-pool"
+"global"
+"unassigned"
+"rapid-commit"
+"ddns-qualifying-suffix"
+"ddns-override-no-update"
+"ddns-override-client-update"
+"ddns-replace-client-name"
+"lease-database"
+"host-reservation-identifiers"
+"store-extended-info"
+"rebind-timer"
+"{"
+"}"
+"["
+"]"
+":"
+","
+"\""
+"\\"
+"{}"
+"[]"
+"\"\""
+"{\"name\":\"x\"}"
+"{\"id\":1}"
+"{\"code\":1}"
+"{\"data\":\"01:02:03\"}"
+"{\"option-data\":[]}"
+"{\"option-def\":[]}"
+"{\"interfaces\":[\"*\"]}"
+"{\"subnet4\":[{\"subnet\":\"192.0.2.0/24\"}]}"
+"{\"subnet6\":[{\"subnet\":\"2001:db8::/64\"}]}"
+"{\"shared-networks\":[{\"name\":\"net-a\",\"subnet4\":[]}]}"
+"192.0.2.0/24"
+"198.51.100.0/24"
+"203.0.113.0/24"
+"10.0.0.0/8"
+"172.16.0.0/12"
+"0.0.0.0"
+"255.255.255.255"
+"2001:db8::/32"
+"2001:db8::/64"
+"::"
+"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+"prefixes"
+"hostname"
+"ip-addresses"
+"excluded-prefixes"
+"option-data"
+"client-classes"
+"user-context"
+"socket-address"
+"socket-port"
+"cert-required"
+"http-headers"
+"http_header_params"
+"value"
+"http_header"
+"type"
+"basic"
+"auth_params"
+"realm"
+"clients"
+"directory"
+"user-file"
+"password-file"
+"dhcp-queue-control"
+"queue_control_params"
+"enable-queue"
+"queue-type"
+"capacity"
+"constant string"
+"sub_dhcp_ddns"
+"dhcp_ddns_params"
+"enable-updates"
+"server-ip"
+"server-port"
+"sender-ip"
+"max-queue-size"
+"ncr-format"
+"config-control"
+"udp"
+"config-databases"
+"config-fetch-wait-time"
+"lenient-option-parsing"
+"compatibility"
+"test"
+"template-test"
+"option-def"
+"option-data"
+"user-context"
+"only-if-required"
+"only-in-additional-list"
+"next-server"
+"server-hostname"
+"boot-file-name"
+"offer-lifetime"
+"preferred-lifetime"
+"offer-lifetime"
+"adaptive-lease-time-threshold"
+"require-client-classes"
+"evaluate-additional-classes"
+"client-classes"
+"client-class"
+"cache-threshold"
+"cache-max-age"
+"iterative"
+"allocator"
+"pd-allocator"
+"reservations-global"
+"reservations-in-subnet"
+"reservations-out-of-pool"
+"store-extended-info"
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <asiolink/io_address.h>
+#include <dhcp/duid.h>
+#include <dhcpsrv/csv_lease_file4.h>
+#include <dhcpsrv/csv_lease_file6.h>
+#include <dhcpsrv/lease.h>
+#include <dhcpsrv/testutils/lease_file_io.h>
+
+using namespace isc;
+using namespace isc::data;
+using namespace isc::dhcp;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+ char filename[256];
+ sprintf(filename, "/tmp/libfuzzer.%d", getpid());
+
+ FILE *fp = fopen(filename, "wb");
+ if (!fp)
+ return 0;
+ fwrite(Data, Size, 1, fp);
+ fclose(fp);
+
+ try {
+ CSVLeaseFile4 lease_file(filename);
+ lease_file.open(false);
+ Lease4Ptr lease;
+ lease_file.next(lease);
+ lease_file.close();
+ } catch (const std::exception&) {
+ // ignore any errors
+ }
+
+ try {
+ CSVLeaseFile6 lease_file(filename);
+ lease_file.open(false);
+ Lease6Ptr lease;
+ lease_file.next(lease);
+ lease_file.close();
+ } catch (const std::exception&) {
+ // ignore any errors
+ }
+
+ unlink(filename);
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logics Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dns/exceptions.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/opcode.h>
+#include <dns/question.h>
+#include <dns/rcode.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+#include <dns/rrtype.h>
+#include <dns/tsig.h>
+#include <dns/tsigkey.h>
+#include <dns/tsigrecord.h>
+#include <dns/master_lexer.h>
+#include <dns/master_loader.h>
+#include <util/buffer.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+using namespace isc::dns;
+using namespace isc::util;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size < 2) {
+ return 0;
+ }
+
+ FuzzedDataProvider fdp(data, size);
+
+ // Get a choice for which fuzzing path to take
+ uint8_t choice = fdp.ConsumeIntegral<uint8_t>();
+
+ // Reserve some data for different operations
+ std::vector<uint8_t> wire_data = fdp.ConsumeBytes<uint8_t>(fdp.remaining_bytes() / 2);
+ std::string string_data = fdp.ConsumeRemainingBytesAsString();
+
+ // Fuzz DNS Name parsing from string
+ if (choice % 8 == 0 && !string_data.empty()) {
+ try {
+ Name name(string_data);
+ // Try various Name operations
+ try {
+ std::string text = name.toText();
+ OutputBuffer buffer(0);
+ name.toWire(buffer);
+
+ // Try splitting at different positions
+ if (name.getLabelCount() > 0) {
+ Name stripped = name.split(0);
+ Name reversed = name.reverse();
+ }
+
+ // Try comparison operations
+ Name root = Name::ROOT_NAME();
+ name.compare(root);
+
+ } catch (const std::exception&) {
+ // Ignore exceptions from operations
+ }
+ } catch (const std::exception&) {
+ // Ignore exceptions from parsing
+ }
+ }
+
+ // Fuzz DNS Name parsing from wire format
+ if (choice % 8 == 1 && !wire_data.empty()) {
+ try {
+ InputBuffer buffer(&wire_data[0], wire_data.size());
+ Name name(buffer);
+
+ // Try operations on the parsed name
+ try {
+ name.toText();
+ name.getLabelCount();
+ name.getLength();
+ } catch (const std::exception&) {
+ // Ignore exceptions
+ }
+ } catch (const std::exception&) {
+ // Ignore exceptions from parsing
+ }
+ }
+
+ // Fuzz DNS Message parsing from wire
+ if (choice % 8 == 2 && !wire_data.empty()) {
+ try {
+ InputBuffer buffer(&wire_data[0], wire_data.size());
+ Message message(Message::PARSE);
+ message.fromWire(buffer);
+
+ // Try various Message operations
+ try {
+ message.getHeaderFlag(Message::HEADERFLAG_AA);
+ message.getRcode();
+ message.getQid();
+ message.getRRCount(Message::SECTION_ANSWER);
+
+ // Try iterating through sections
+ for (int sec = Message::SECTION_QUESTION;
+ sec <= Message::SECTION_ADDITIONAL;
+ ++sec) {
+ Message::Section section = static_cast<Message::Section>(sec);
+ try {
+ auto it = message.beginSection(section);
+ auto it_end = message.endSection(section);
+ while (it != it_end) {
+ ++it;
+ }
+ } catch (const std::exception&) {
+ // Ignore iteration exceptions
+ }
+ }
+
+ // Try rendering back to wire
+ MessageRenderer renderer;
+ try {
+ message.toWire(renderer);
+ } catch (const std::exception&) {
+ // Ignore rendering exceptions
+ }
+
+ } catch (const std::exception&) {
+ // Ignore operation exceptions
+ }
+ } catch (const std::exception&) {
+ // Ignore parsing exceptions
+ }
+ }
+
+ // Fuzz Question parsing
+ if (choice % 8 == 3 && !wire_data.empty()) {
+ try {
+ InputBuffer buffer(&wire_data[0], wire_data.size());
+ Question question(buffer);
+
+ try {
+ question.toText();
+ question.getName();
+ question.getType();
+ question.getClass();
+
+ OutputBuffer out_buffer(0);
+ question.toWire(out_buffer);
+ } catch (const std::exception&) {
+ // Ignore operation exceptions
+ }
+ } catch (const std::exception&) {
+ // Ignore parsing exceptions
+ }
+ }
+
+ // Fuzz RRset operations
+ if (choice % 8 == 4 && !string_data.empty() && !wire_data.empty()) {
+ try {
+ Name name(string_data);
+ RRsetPtr rrset = RRsetPtr(new RRset(name, RRClass::IN(),
+ RRType::A(), RRTTL(3600)));
+
+ // Try parsing RDATA from wire
+ try {
+ InputBuffer buffer(&wire_data[0], wire_data.size());
+ if (wire_data.size() >= 4) {
+ rdata::ConstRdataPtr rdata =
+ rdata::createRdata(RRType::A(), RRClass::IN(),
+ buffer, wire_data.size());
+ rrset->addRdata(rdata);
+ }
+ } catch (const std::exception&) {
+ // Ignore RDATA parsing exceptions
+ }
+
+ // Try RRset operations
+ try {
+ rrset->toText();
+ rrset->getRdataCount();
+
+ OutputBuffer out_buffer(0);
+ rrset->toWire(out_buffer);
+ } catch (const std::exception&) {
+ // Ignore operation exceptions
+ }
+ } catch (const std::exception&) {
+ // Ignore exceptions
+ }
+ }
+
+ // Fuzz TSIG operations
+ if (choice % 8 == 5 && !string_data.empty() && wire_data.size() >= 16) {
+ try {
+ // Try creating a TSIG key
+ TSIGKey key(string_data + ":secret");
+
+ // Try creating TSIG RDATA and then a TSIG record
+ try {
+ InputBuffer buffer(&wire_data[0], wire_data.size());
+ // Try to parse TSIG RDATA
+ rdata::ConstRdataPtr rdata =
+ rdata::createRdata(RRType::TSIG(), RRClass::ANY(),
+ buffer, wire_data.size());
+ const rdata::any::TSIG& tsig_rdata =
+ dynamic_cast<const rdata::any::TSIG&>(*rdata);
+
+ // Create a TSIGRecord
+ Name key_name(string_data);
+ TSIGRecord tsig(key_name, tsig_rdata);
+ tsig.toText();
+
+ OutputBuffer out_buffer(0);
+ tsig.toWire(out_buffer);
+ } catch (const std::exception&) {
+ // Ignore TSIG parsing exceptions
+ }
+
+ // Try TSIG context operations (sign operation is public)
+ try {
+ TSIGContext ctx(key);
+ // Try signing some data
+ if (!wire_data.empty()) {
+ ConstTSIGRecordPtr tsig_record = ctx.sign(0, &wire_data[0], wire_data.size());
+ }
+ } catch (const std::exception&) {
+ // Ignore context exceptions
+ }
+ } catch (const std::exception&) {
+ // Ignore key creation exceptions
+ }
+ }
+
+ // Fuzz MasterLexer with string input
+ if (choice % 8 == 6 && !string_data.empty()) {
+ try {
+ std::istringstream iss(string_data);
+ MasterLexer lexer;
+ lexer.pushSource(iss);
+
+ // Try tokenizing (loop until we hit EOF token)
+ for (int i = 0; i < 100; ++i) {
+ try {
+ const MasterToken& token = lexer.getNextToken();
+
+ // Stop if we hit EOF
+ if (token.getType() == MasterToken::END_OF_FILE) {
+ break;
+ }
+
+ // Access token properties based on type
+ if (token.getType() == MasterToken::STRING ||
+ token.getType() == MasterToken::QSTRING) {
+ token.getString();
+ token.getStringRegion();
+ } else if (token.getType() == MasterToken::NUMBER) {
+ token.getNumber();
+ } else if (token.getType() == MasterToken::ERROR) {
+ token.getErrorCode();
+ token.getErrorText();
+ }
+ } catch (const std::exception&) {
+ break;
+ }
+ }
+ } catch (const std::exception&) {
+ // Ignore lexer exceptions
+ }
+ }
+
+ // Fuzz Message rendering operations
+ if (choice % 8 == 7 && !string_data.empty()) {
+ try {
+ Message message(Message::RENDER);
+ message.setQid(fdp.ConsumeIntegral<uint16_t>());
+ message.setOpcode(Opcode::QUERY());
+ message.setRcode(Rcode::NOERROR());
+
+ // Try setting various flags
+ message.setHeaderFlag(Message::HEADERFLAG_AA,
+ fdp.ConsumeBool());
+ message.setHeaderFlag(Message::HEADERFLAG_RD,
+ fdp.ConsumeBool());
+ message.setHeaderFlag(Message::HEADERFLAG_RA,
+ fdp.ConsumeBool());
+
+ // Try adding a question
+ try {
+ Name qname(string_data);
+ QuestionPtr question(new Question(qname, RRClass::IN(),
+ RRType::A()));
+ message.addQuestion(question);
+ } catch (const std::exception&) {
+ // Ignore question addition exceptions
+ }
+
+ // Try rendering
+ try {
+ MessageRenderer renderer;
+ message.toWire(renderer);
+ } catch (const std::exception&) {
+ // Ignore rendering exceptions
+ }
+ } catch (const std::exception&) {
+ // Ignore message creation exceptions
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+# DNS fuzzing dictionary
+# Common DNS wire format markers
+"\x00"
+"\x01"
+"\x02"
+"\x03"
+"\x04"
+"\x05"
+"\x0c"
+"\x0d"
+"\x1f"
+"\x20"
+"\x3f"
+"\x40"
+"\x7f"
+"\xff"
+
+# DNS header patterns
+"\x00\x00\x01\x00"
+"\x00\x00\x81\x80"
+"\x00\x01\x00\x00"
+"\x00\x00\x00\x01"
+
+# Common DNS names and labels
+"."
+".."
+"example"
+"example.com"
+"example.org"
+"localhost"
+"www"
+"ns"
+"ns1"
+"ns2"
+"mail"
+"@"
+"*"
+"_"
+
+# DNS escape sequences
+"\\"
+"\\."
+"\\@"
+"\\000"
+"\\255"
+"\\032"
+
+# RR type values (in text)
+"A"
+"AAAA"
+"NS"
+"SOA"
+"MX"
+"CNAME"
+"PTR"
+"TXT"
+"SRV"
+"TSIG"
+"OPT"
+"DNSKEY"
+"DS"
+"RRSIG"
+"NSEC"
+"NSEC3"
+
+# Class values
+"IN"
+"CH"
+"HS"
+"ANY"
+"NONE"
+
+# TSIG algorithm names
+"HMAC-MD5.SIG-ALG.REG.INT"
+"hmac-sha1"
+"hmac-sha256"
+"hmac-sha384"
+"hmac-sha512"
+
+# Long labels (near 63-byte limit)
+"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+# Maximum length domain name components
+"a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.0.1.2.3.4.5.6.7.8.9"
+
+# DNS query/response patterns
+"\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00"
+
+# Common port numbers and values
+"53"
+"5353"
+"853"
+
+# EDNS OPT pseudo-RR
+"\x00\x00\x29"
+
+# Compression pointers
+"\xc0\x00"
+"\xc0\x0c"
+"\xc0\x10"
+"\xc0\x20"
+"\xc0\xff"
+
+# TSIG MAC sizes
+"\x00\x10"
+"\x00\x14"
+"\x00\x20"
--- /dev/null
+// Copyright (C) 2025 Ada Logics Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <exceptions/exceptions.h>
+#include <util/encode/encode.h>
+
+#include <string>
+#include <vector>
+#include <cstddef>
+
+using namespace isc::util::encode;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size < 2) {
+ return 0;
+ }
+
+ FuzzedDataProvider fdp(data, size);
+
+ // Choose which encoding/decoding path to test
+ uint8_t path = fdp.ConsumeIntegralInRange<uint8_t>(0, 11);
+
+ std::vector<uint8_t> binary_data;
+ std::string encoded_str;
+ std::vector<uint8_t> decoded_output;
+
+ switch (path) {
+ case 0: {
+ // Test Base64 encoding from binary data
+ try {
+ size_t bin_size = fdp.ConsumeIntegralInRange<size_t>(0, size);
+ binary_data = fdp.ConsumeBytes<uint8_t>(bin_size);
+ encoded_str = encodeBase64(binary_data);
+ // Verify round-trip
+ decodeBase64(encoded_str, decoded_output);
+ } catch (const isc::Exception&) {
+ // Expected for invalid input
+ }
+ break;
+ }
+
+ case 1: {
+ // Test Base64 decoding from string
+ try {
+ encoded_str = fdp.ConsumeRemainingBytesAsString();
+ decodeBase64(encoded_str, decoded_output);
+ } catch (const isc::Exception&) {
+ // Expected for invalid Base64 strings
+ }
+ break;
+ }
+
+ case 2: {
+ // Test Base32Hex encoding from binary data
+ try {
+ size_t bin_size = fdp.ConsumeIntegralInRange<size_t>(0, size);
+ binary_data = fdp.ConsumeBytes<uint8_t>(bin_size);
+ encoded_str = encodeBase32Hex(binary_data);
+ // Verify round-trip
+ decodeBase32Hex(encoded_str, decoded_output);
+ } catch (const isc::Exception&) {
+ // Expected for invalid input
+ }
+ break;
+ }
+
+ case 3: {
+ // Test Base32Hex decoding from string
+ try {
+ encoded_str = fdp.ConsumeRemainingBytesAsString();
+ decodeBase32Hex(encoded_str, decoded_output);
+ } catch (const isc::Exception&) {
+ // Expected for invalid Base32Hex strings
+ }
+ break;
+ }
+
+ case 4: {
+ // Test Base16 (hex) encoding from binary data
+ try {
+ size_t bin_size = fdp.ConsumeIntegralInRange<size_t>(0, size);
+ binary_data = fdp.ConsumeBytes<uint8_t>(bin_size);
+ encoded_str = encodeHex(binary_data);
+ // Verify round-trip
+ decodeHex(encoded_str, decoded_output);
+ } catch (const isc::Exception&) {
+ // Expected for invalid input
+ }
+ break;
+ }
+
+ case 5: {
+ // Test Base16 (hex) decoding from string
+ try {
+ encoded_str = fdp.ConsumeRemainingBytesAsString();
+ decodeHex(encoded_str, decoded_output);
+ } catch (const isc::Exception&) {
+ // Expected for invalid hex strings
+ }
+ break;
+ }
+
+ case 6: {
+ // Test Base64 with various padding scenarios
+ try {
+ std::string test_str = fdp.ConsumeRandomLengthString();
+ // Add various padding permutations
+ test_str += fdp.ConsumeBool() ? "=" : "";
+ test_str += fdp.ConsumeBool() ? "=" : "";
+ decodeBase64(test_str, decoded_output);
+ } catch (const isc::Exception&) {
+ // Expected for invalid padding
+ }
+ break;
+ }
+
+ case 7: {
+ // Test Base32Hex with various padding scenarios
+ try {
+ std::string test_str = fdp.ConsumeRandomLengthString();
+ // Add various padding permutations
+ for (int i = 0; i < fdp.ConsumeIntegralInRange(0, 6); i++) {
+ test_str += "=";
+ }
+ decodeBase32Hex(test_str, decoded_output);
+ } catch (const isc::Exception&) {
+ // Expected for invalid padding
+ }
+ break;
+ }
+
+ case 8: {
+ // Test mixed case Base64 (should be case-sensitive)
+ try {
+ encoded_str = fdp.ConsumeRemainingBytesAsString();
+ // Mix uppercase and lowercase
+ for (auto& c : encoded_str) {
+ if (fdp.ConsumeBool() && isalpha(c)) {
+ c = (isupper(c)) ? tolower(c) : toupper(c);
+ }
+ }
+ decodeBase64(encoded_str, decoded_output);
+ } catch (const isc::Exception&) {
+ // Expected for case errors
+ }
+ break;
+ }
+
+ case 9: {
+ // Test mixed case Base32Hex (case-insensitive)
+ try {
+ encoded_str = fdp.ConsumeRemainingBytesAsString();
+ // Mix uppercase and lowercase
+ for (auto& c : encoded_str) {
+ if (fdp.ConsumeBool() && isalpha(c)) {
+ c = (isupper(c)) ? tolower(c) : toupper(c);
+ }
+ }
+ decodeBase32Hex(encoded_str, decoded_output);
+ } catch (const isc::Exception&) {
+ // May succeed due to case-insensitivity
+ }
+ break;
+ }
+
+ case 10: {
+ // Test Base16 with mixed case (case-insensitive)
+ try {
+ encoded_str = fdp.ConsumeRemainingBytesAsString();
+ // Mix uppercase and lowercase
+ for (auto& c : encoded_str) {
+ if (fdp.ConsumeBool() && isalpha(c)) {
+ c = (isupper(c)) ? tolower(c) : toupper(c);
+ }
+ }
+ decodeHex(encoded_str, decoded_output);
+ } catch (const isc::Exception&) {
+ // May succeed due to case-insensitivity
+ }
+ break;
+ }
+
+ case 11: {
+ // Test encoding/decoding with whitespace injection
+ try {
+ std::string test_str = fdp.ConsumeRandomLengthString();
+ // Inject whitespace characters
+ size_t insertions = fdp.ConsumeIntegralInRange<size_t>(0, 10);
+ for (size_t i = 0; i < insertions && test_str.size() > 0; i++) {
+ size_t pos = fdp.ConsumeIntegralInRange<size_t>(0, test_str.size());
+ char ws = fdp.PickValueInArray({' ', '\t', '\n', '\r'});
+ test_str.insert(pos, 1, ws);
+ }
+
+ // Try decoding with all encoders
+ try { decodeBase64(test_str, decoded_output); } catch (...) {}
+ try { decodeBase32Hex(test_str, decoded_output); } catch (...) {}
+ try { decodeHex(test_str, decoded_output); } catch (...) {}
+ } catch (const isc::Exception&) {
+ // Expected for whitespace handling
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+"=="
+"option"
+"relay4"
+"relay6"
+"peeraddr"
+"linkaddr"
+"text"
+"hex"
+"exists"
+"pkt"
+"iface"
+"src"
+"dst"
+"len"
+"pkt4"
+"mac"
+"hlen"
+"htype"
+"ciaddr"
+"giaddr"
+"yiaddr"
+"siaddr"
+"pkt6"
+"msgtype"
+"transid"
+"vendor"
+"vendor-class"
+"data"
+"enterprise"
+"substring"
+"lcase"
+"ucase"
+"split"
+"all"
+"concat"
+"ifelse"
+"sifelse"
+"hexstring"
+"addrtotext"
+"int8totext"
+"int16totext"
+"int32totext"
+"uint8totext"
+"uint16totext"
+"uint32totext"
+"not"
+"and"
+"sand"
+"or"
+"sor"
+"member"
+"match"
+"."
+"("
+")"
+"["
+"]"
+","
+"*"
+"+"
+"0x"
+"\""
+"'"
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <eval/eval_context.h>
+#include <eval/evaluate.h>
+#include <eval/dependency.h>
+
+#include <dhcp/pkt4.h>
+#include <dhcp/dhcp4.h>
+
+#include <cstdlib>
+#include <string>
+
+using namespace isc;
+using namespace isc::eval;
+using namespace isc::dhcp;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+ FuzzedDataProvider fdp(Data, Size);
+ EvalContext ctx(Option::V4);
+ auto idx = fdp.ConsumeIntegralInRange<uint8_t>(1, 18);
+ const std::string payload = fdp.ConsumeRemainingBytesAsString();
+
+ try {
+ Pkt4 pkt(idx, 0);
+ // Fuzz boolean parsing
+ if (ctx.parseString(payload, EvalContext::PARSER_BOOL)) {
+ ValueStack vs;
+ Expression& exp_bool = ctx.expression_;
+ ExpressionPtr exp_bool_ptr(new Expression(exp_bool));
+
+ evaluateRaw(exp_bool, pkt, vs);
+ evaluateBool(exp_bool, pkt);
+ evaluateString(exp_bool, pkt);
+ dependOnClass(exp_bool_ptr, payload);
+ }
+ } catch(const isc::Exception&){}
+
+ // Fuzz string parsing
+ try {
+ Pkt4 pkt(idx, 0);
+ if (ctx.parseString(payload, EvalContext::PARSER_STRING)) {
+ ValueStack vs;
+ Expression& exp_str = ctx.expression_;
+ ExpressionPtr exp_str_ptr(new Expression(exp_str));
+
+ evaluateRaw(exp_str, pkt, vs);
+ evaluateBool(exp_str, pkt);
+ evaluateString(exp_str, pkt);
+ dependOnClass(exp_str_ptr, payload);
+ }
+ } catch(const isc::Exception&){}
+
+ location loc;
+ try {
+ // Fuzz converter
+ ctx.convertOptionCode(payload, loc);
+ } catch(const isc::Exception&) {}
+
+ try {
+ ctx.convertOptionName(payload, loc);
+ } catch(const isc::Exception&) {}
+
+ try {
+ ctx.convertNestLevelNumber(payload, loc);
+ } catch(const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <eval/eval_context.h>
+#include <eval/evaluate.h>
+#include <eval/dependency.h>
+
+#include <dhcp/pkt6.h>
+#include <dhcp/dhcp6.h>
+
+#include <cstdlib>
+#include <string>
+
+using namespace isc;
+using namespace isc::eval;
+using namespace isc::dhcp;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+ FuzzedDataProvider fdp(Data, Size);
+ EvalContext ctx(Option::V6);
+
+ auto idx = fdp.ConsumeIntegralInRange<uint8_t>(1, 18);
+ const std::string payload = fdp.ConsumeRemainingBytesAsString();
+ // Fuzz boolean parsing
+ try {
+ Pkt6 pkt(idx, 0);
+ if (ctx.parseString(payload, EvalContext::PARSER_BOOL)) {
+ ValueStack vs;
+ Expression& exp_bool = ctx.expression_;
+ ExpressionPtr exp_bool_ptr(new Expression(exp_bool));
+
+ evaluateRaw(exp_bool, pkt, vs);
+ evaluateBool(exp_bool, pkt);
+ evaluateString(exp_bool, pkt);
+ dependOnClass(exp_bool_ptr, payload);
+ }
+ } catch(const isc::Exception&) {}
+
+ try {
+ // Fuzz string parsing
+ Pkt6 pkt(idx, 0);
+ if (ctx.parseString(payload, EvalContext::PARSER_STRING)) {
+ ValueStack vs;
+ Expression& exp_str = ctx.expression_;
+ ExpressionPtr exp_str_ptr(new Expression(exp_str));
+
+ evaluateRaw(exp_str, pkt, vs);
+ evaluateBool(exp_str, pkt);
+ evaluateString(exp_str, pkt);
+ dependOnClass(exp_str_ptr, payload);
+ }
+ } catch(const isc::Exception&) {}
+
+ location loc;
+ try {
+ // Fuzz converter
+ ctx.convertOptionCode(payload, loc);
+ } catch(const isc::Exception&) {}
+
+ try {
+ ctx.convertOptionName(payload, loc);
+ } catch(const isc::Exception&) {}
+
+ try {
+ ctx.convertNestLevelNumber(payload, loc);
+ } catch(const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+
+extern "C" int ddns4_update(CalloutHandle& handle);
+
+namespace isc {
+ namespace dhcp {
+ class MyDhcpv4Srv : public ControlledDhcpv4Srv {
+ public:
+ void fuzz_classifyPacket(const Pkt4Ptr& pkt) {
+ classifyPacket(pkt);
+ }
+
+ ConstSubnet4Ptr fuzz_selectSubnet(const Pkt4Ptr& query,
+ bool& drop,
+ bool allow_answer_park = true) {
+ return selectSubnet(query, drop, allow_answer_park);
+ }
+ };
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Disable validatePath checking to allow writing configuration file to /tmp
+ isc::util::file::PathChecker::enableEnforcement(false);
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Creating temp config file
+ std::string path = fuzz::writeTempConfig(true);
+ if (path.empty()) {
+ // Early exit if configuration file creation failed
+ fuzz::deleteTempFile(path);
+ return 0;
+ }
+
+ // Creating temp lease file
+ std::string lease_path = fuzz::writeTempLease(true);
+
+ Pkt4Ptr pkt;
+ std::unique_ptr<MyDhcpv4Srv> srv;
+
+ // Package parsing
+ try {
+ // Add fixed magic cookie and correct hardware address
+ std::vector<uint8_t> buf(data, data + size);
+ if (size >= 240) {
+ // Max hardware address length is 20
+ buf[2] = 20;
+
+ // Magic cookie fixed value 0x63825363
+ buf[236] = 0x63;
+ buf[237] = 0x82;
+ buf[238] = 0x53;
+ buf[239] = 0x63;
+ }
+
+ pkt = Pkt4Ptr(new Pkt4(buf.data(), buf.size()));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ FuzzedDataProvider fdp(data, size);
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 8);
+ pkt->setType(static_cast<DHCPMessageType>(typeChoice));
+
+ // Server initialisation
+ try {
+ srv.reset(new MyDhcpv4Srv());
+ srv->init(path);
+ } catch (...) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ if (!srv) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ // Call classifyPacket for packet checking
+ try {
+ srv->fuzz_classifyPacket(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Prepare client context
+ CalloutHandlePtr handle = getCalloutHandle(pkt);
+ AllocEngine::ClientContext4Ptr ctx(new AllocEngine::ClientContext4());
+
+ // Call earlyGHRLookup
+ try {
+ srv->earlyGHRLookup(pkt, ctx);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Call select subnet
+ try {
+ bool drop = false;
+ if (!ctx) {
+ ctx.reset(new AllocEngine::ClientContext4());
+ }
+ if (ctx) {
+ ctx->subnet_ = srv->fuzz_selectSubnet(pkt, drop, false);
+ }
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Fuzz ddns4_update
+ try {
+ Pkt4Ptr rsp;
+ handle = getCalloutHandle(pkt);
+ handle->setArgument("query4", pkt);
+ handle->setArgument("response4", rsp);
+ handle->setArgument("hostname", fdp.ConsumeRandomLengthString(32));
+ handle->setArgument("fwd-update", fdp.ConsumeBool());
+ handle->setArgument("rev-update", fdp.ConsumeBool());
+ if (!ctx) {
+ ctx.reset(new AllocEngine::ClientContext4());
+ }
+ if (ctx) {
+ handle->setArgument("ddns-params", ctx->getDdnsParams());
+ handle->setArgument("subnet4", ctx->subnet_);
+ }
+ ddns4_update(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean handle to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ srv.reset();
+
+ // Remove temp files
+ fuzz::deleteTempFile(path);
+ fuzz::deleteTempFile(lease_path);
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+#include <cstdlib>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+
+extern "C" int ddns6_update(CalloutHandle& handle);
+
+namespace isc {
+ namespace dhcp {
+ class MyDhcpv6Srv : public ControlledDhcpv6Srv {
+ public:
+ void fuzz_classifyPacket(const Pkt6Ptr& pkt) {
+ classifyPacket(pkt);
+ }
+
+ ConstSubnet6Ptr fuzz_selectSubnet(const Pkt6Ptr& question, bool& drop) {
+ return selectSubnet(question, drop);
+ }
+ };
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Disable validatePath checking to allow writing configuration file to /tmp
+ isc::util::file::PathChecker::enableEnforcement(false);
+
+ // Force DUID file to /tmp
+ setenv("KEA_DHCP_DATA_DIR", "/tmp", 1);
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Creating temp config file
+ std::string path = fuzz::writeTempConfig(false);
+ if (path.empty()) {
+ // Early exit if configuration file creation failed
+ fuzz::deleteTempFile(path);
+ return 0;
+ }
+
+ // Creating temp lease file
+ std::string lease_path = fuzz::writeTempLease(false);
+
+ Pkt6Ptr pkt;
+ std::unique_ptr<MyDhcpv6Srv> srv;
+
+ // Package parsing
+ try {
+ pkt = Pkt6Ptr(new Pkt6(data, size));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ FuzzedDataProvider fdp(data, size);
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 37);
+ pkt->setType(static_cast<DHCPv6MessageType>(typeChoice));
+
+ // Server initialisation
+ try {
+ srv.reset(new MyDhcpv6Srv());
+ srv->init(path);
+ } catch (...) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ if (!srv) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ // Call classifyPacket for packet checking
+ try {
+ srv->fuzz_classifyPacket(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Prepare client context
+ CalloutHandlePtr handle = getCalloutHandle(pkt);
+ AllocEngine::ClientContext6 ctx;
+
+ // Call earlyGHRLookup
+ try {
+ srv->earlyGHRLookup(pkt, ctx);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Call select subnet
+ try {
+ bool drop = false;
+ ctx.subnet_ = srv->fuzz_selectSubnet(pkt, drop);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Fuzz ddns6_update
+ try {
+ Pkt6Ptr rsp;
+ handle = getCalloutHandle(pkt);
+ handle->setArgument("query6", pkt);
+ handle->setArgument("response6", rsp);
+ handle->setArgument("hostname", fdp.ConsumeRandomLengthString(32));
+ handle->setArgument("fwd-update", fdp.ConsumeBool());
+ handle->setArgument("rev-update", fdp.ConsumeBool());
+ handle->setArgument("ddns-params", ctx.getDdnsParams());
+ handle->setArgument("subnet6", ctx.subnet_);
+ ddns6_update(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ srv.reset();
+
+ // Remove temp files
+ fuzz::deleteTempFile(path);
+ fuzz::deleteTempFile(lease_path);
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+
+extern "C" int pkt4_receive(CalloutHandle& handle);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ Pkt4Ptr pkt;
+
+ // Package parsing
+ try {
+ // Add fixed magic cookie and correct hardware address
+ std::vector<uint8_t> buf(data, data + size);
+ if (size >= 240) {
+ // Max hardware address length is 20
+ buf[2] = 20;
+
+ // Magic cookie fixed value 0x63825363
+ buf[236] = 0x63;
+ buf[237] = 0x82;
+ buf[238] = 0x53;
+ buf[239] = 0x63;
+ }
+
+ pkt = Pkt4Ptr(new Pkt4(buf.data(), buf.size()));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ FuzzedDataProvider fdp(data, size);
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 8);
+ pkt->setType(static_cast<DHCPMessageType>(typeChoice));
+
+ CalloutHandlePtr handle = getCalloutHandle(pkt);
+
+ // Fuzz pkt4_receive
+ try {
+ handle->setArgument("query4", pkt);
+ pkt4_receive(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean handle to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+#include <cstdlib>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+
+extern "C" int pkt6_receive(CalloutHandle& handle);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Force DUID file to /tmp
+ setenv("KEA_DHCP_DATA_DIR", "/tmp", 1);
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ Pkt6Ptr pkt;
+
+ // Package parsing
+ try {
+ pkt = Pkt6Ptr(new Pkt6(data, size));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ FuzzedDataProvider fdp(data, size);
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 37);
+ pkt->setType(static_cast<DHCPv6MessageType>(typeChoice));
+
+ CalloutHandlePtr handle = getCalloutHandle(pkt);
+
+ // Fuzz pkt6_receive
+ try {
+ handle->setArgument("query6", pkt);
+ pkt6_receive(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean handle to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+
+extern "C" int buffer4_receive(CalloutHandle& handle);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ Pkt4Ptr pkt;
+
+ // Package parsing
+ try {
+ // Add fixed magic cookie and correct hardware address
+ std::vector<uint8_t> buf(data, data + size);
+ if (size >= 240) {
+ // Max hardware address length is 20
+ buf[2] = 20;
+
+ // Magic cookie fixed value 0x63825363
+ buf[236] = 0x63;
+ buf[237] = 0x82;
+ buf[238] = 0x53;
+ buf[239] = 0x63;
+ }
+
+ pkt = Pkt4Ptr(new Pkt4(buf.data(), buf.size()));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ FuzzedDataProvider fdp(data, size);
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 8);
+ pkt->setType(static_cast<DHCPMessageType>(typeChoice));
+
+ CalloutHandlePtr handle = getCalloutHandle(pkt);
+
+ // Fuzz buffer4_receive
+ try {
+ handle = getCalloutHandle(pkt);
+ handle->setArgument("query4", pkt);
+ buffer4_receive(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean handle to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+#include <cstdlib>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+
+extern "C" int buffer6_receive(CalloutHandle& handle);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Force DUID file to /tmp
+ setenv("KEA_DHCP_DATA_DIR", "/tmp", 1);
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ Pkt6Ptr pkt;
+
+ // Package parsing
+ try {
+ pkt = Pkt6Ptr(new Pkt6(data, size));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ FuzzedDataProvider fdp(data, size);
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 37);
+ pkt->setType(static_cast<DHCPv6MessageType>(typeChoice));
+
+ CalloutHandlePtr handle = getCalloutHandle(pkt);
+
+ // Fuzz buffer6_receive
+ try {
+ handle = getCalloutHandle(pkt);
+ handle->setArgument("query6", pkt);
+ buffer6_receive(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean handle to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+
+extern "C" int subnet4_select(CalloutHandle& handle);
+extern "C" int lease4_release(CalloutHandle& handle);
+extern "C" int lease4_decline(CalloutHandle& handle);
+
+namespace isc {
+ namespace dhcp {
+ class MyDhcpv4Srv : public ControlledDhcpv4Srv {
+ public:
+ void fuzz_classifyPacket(const Pkt4Ptr& pkt) {
+ classifyPacket(pkt);
+ }
+ };
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Disable validatePath checking to allow writing configuration file to /tmp
+ isc::util::file::PathChecker::enableEnforcement(false);
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Creating temp config file
+ std::string path = fuzz::writeTempConfig(true);
+ if (path.empty()) {
+ // Early exit if configuration file creation failed
+ fuzz::deleteTempFile(path);
+ return 0;
+ }
+
+ // Creating temp lease file
+ std::string lease_path = fuzz::writeTempLease(true);
+
+ Pkt4Ptr pkt;
+ std::unique_ptr<MyDhcpv4Srv> srv;
+
+ // Package parsing
+ try {
+ // Add fixed magic cookie and correct hardware address
+ std::vector<uint8_t> buf(data, data + size);
+ if (size >= 240) {
+ // Max hardware address length is 20
+ buf[2] = 20;
+
+ // Magic cookie fixed value 0x63825363
+ buf[236] = 0x63;
+ buf[237] = 0x82;
+ buf[238] = 0x53;
+ buf[239] = 0x63;
+ }
+
+ pkt = Pkt4Ptr(new Pkt4(buf.data(), buf.size()));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ FuzzedDataProvider fdp(data, size);
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 8);
+ pkt->setType(static_cast<DHCPMessageType>(typeChoice));
+
+ // Server initialisation
+ try {
+ srv.reset(new MyDhcpv4Srv());
+ srv->init(path);
+ } catch (...) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ if (!srv) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ // Call classifyPacket for packet checking
+ try {
+ srv->fuzz_classifyPacket(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Prepare client context
+ CalloutHandlePtr handle = nullptr;
+ AllocEngine::ClientContext4Ptr ctx(new AllocEngine::ClientContext4());
+
+ // Call earlyGHRLookup
+ try {
+ srv->earlyGHRLookup(pkt, ctx);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Fuzz subnet4_select
+ try {
+ handle = getCalloutHandle(pkt);
+ Pkt4Ptr rsp;
+ CfgMgr& cfgmgr = CfgMgr::instance();
+ handle = getCalloutHandle(pkt);
+ handle->setArgument("query4", pkt);
+ handle->setArgument("subnet4collection",
+ cfgmgr.getCurrentCfg()->getCfgSubnets4()->getAll());
+ if (!ctx) {
+ ctx.reset(new AllocEngine::ClientContext4());
+ }
+ if (ctx) {
+ handle->setArgument("subnet4", ctx->subnet_);
+ }
+ subnet4_select(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean handle to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ // Call lease4_release
+ try {
+ handle = getCalloutHandle(pkt);
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw);
+ handle->setArgument("leases4", leases);
+ handle->setArgument("query4", pkt);
+
+ lease4_release(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ // Call lease4_decline
+ try {
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw);
+ handle->setArgument("leases4", leases);
+ handle->setArgument("query4", pkt);
+
+ lease4_decline(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ srv.reset();
+
+ // Remove temp files
+ fuzz::deleteTempFile(path);
+ fuzz::deleteTempFile(lease_path);
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+#include <cstdlib>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+
+extern "C" int subnet6_select(CalloutHandle& handle);
+extern "C" int lease6_release(CalloutHandle& handle);
+extern "C" int lease6_decline(CalloutHandle& handle);
+
+namespace isc {
+ namespace dhcp {
+ class MyDhcpv6Srv : public ControlledDhcpv6Srv {
+ public:
+ void fuzz_classifyPacket(const Pkt6Ptr& pkt) {
+ classifyPacket(pkt);
+ }
+ };
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Disable validatePath checking to allow writing configuration file to /tmp
+ isc::util::file::PathChecker::enableEnforcement(false);
+
+ // Force DUID file to /tmp
+ setenv("KEA_DHCP_DATA_DIR", "/tmp", 1);
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Creating temp config file
+ std::string path = fuzz::writeTempConfig(false);
+ if (path.empty()) {
+ // Early exit if configuration file creation failed
+ fuzz::deleteTempFile(path);
+ return 0;
+ }
+
+ // Creating temp lease file
+ std::string lease_path = fuzz::writeTempLease(false);
+
+ Pkt6Ptr pkt;
+ std::unique_ptr<MyDhcpv6Srv> srv;
+
+ // Package parsing
+ try {
+ pkt = Pkt6Ptr(new Pkt6(data, size));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ FuzzedDataProvider fdp(data, size);
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 37);
+ pkt->setType(static_cast<DHCPv6MessageType>(typeChoice));
+
+ // Server initialisation
+ try {
+ srv.reset(new MyDhcpv6Srv());
+ srv->init(path);
+ } catch (...) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ if (!srv) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ // Call classifyPacket for packet checking
+ try {
+ srv->fuzz_classifyPacket(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Prepare client context
+ CalloutHandlePtr handle = nullptr;
+ AllocEngine::ClientContext6 ctx;
+
+ // Call earlyGHRLookup
+ try {
+ srv->earlyGHRLookup(pkt, ctx);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Fuzz subnet6_select
+ try {
+ Pkt6Ptr rsp;
+ CfgMgr& cfgmgr = CfgMgr::instance();
+ handle = getCalloutHandle(pkt);
+ handle->setArgument("query6", pkt);
+ handle->setArgument("subnet6", ctx.subnet_);
+ handle->setArgument("subnet6collection",
+ cfgmgr.getCurrentCfg()->getCfgSubnets6()->getAll());
+ subnet6_select(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean handle to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ // Call lease6_decline
+ try {
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease6Collection leases = LeaseMgrFactory::instance().getLease6(hw);
+ handle->setArgument("leases6", leases);
+ handle->setArgument("query6", pkt);
+
+ lease6_decline(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ // Call lease6_release
+ try {
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease6Collection leases = LeaseMgrFactory::instance().getLease6(hw);
+ handle->setArgument("leases6", leases);
+ handle->setArgument("query6", pkt);
+
+ lease6_release(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ srv.reset();
+
+ // Remove temp files
+ fuzz::deleteTempFile(path);
+ fuzz::deleteTempFile(lease_path);
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+
+extern "C" int lease4_release(CalloutHandle& handle);
+extern "C" int lease4_decline(CalloutHandle& handle);
+extern "C" int leases4_committed(CalloutHandle& handle);
+
+namespace isc {
+ namespace dhcp {
+ class MyDhcpv4Srv : public ControlledDhcpv4Srv {
+ public:
+ void fuzz_classifyPacket(const Pkt4Ptr& pkt) {
+ classifyPacket(pkt);
+ }
+ };
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Disable validatePath checking to allow writing configuration file to /tmp
+ isc::util::file::PathChecker::enableEnforcement(false);
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Creating temp config file
+ std::string path = fuzz::writeTempConfig(true);
+ if (path.empty()) {
+ // Early exit if configuration file creation failed
+ fuzz::deleteTempFile(path);
+ return 0;
+ }
+
+ // Creating temp lease file
+ std::string lease_path = fuzz::writeTempLease(true);
+
+ Pkt4Ptr pkt;
+ std::unique_ptr<MyDhcpv4Srv> srv;
+
+ // Package parsing
+ try {
+ // Add fixed magic cookie and correct hardware address
+ std::vector<uint8_t> buf(data, data + size);
+ if (size >= 240) {
+ // Max hardware address length is 20
+ buf[2] = 20;
+
+ // Magic cookie fixed value 0x63825363
+ buf[236] = 0x63;
+ buf[237] = 0x82;
+ buf[238] = 0x53;
+ buf[239] = 0x63;
+ }
+
+ pkt = Pkt4Ptr(new Pkt4(buf.data(), buf.size()));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ FuzzedDataProvider fdp(data, size);
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 8);
+ pkt->setType(static_cast<DHCPMessageType>(typeChoice));
+
+ // Server initialisation
+ try {
+ srv.reset(new MyDhcpv4Srv());
+ srv->init(path);
+ } catch (...) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ if (!srv) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ // Call classifyPacket for packet checking
+ try {
+ srv->fuzz_classifyPacket(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Prepare client context
+ CalloutHandlePtr handle = nullptr;
+ AllocEngine::ClientContext4Ptr ctx(new AllocEngine::ClientContext4());
+
+ // Call earlyGHRLookup
+ try {
+ srv->earlyGHRLookup(pkt, ctx);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Call lease4_release
+ try {
+ handle = getCalloutHandle(pkt);
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw);
+ handle->setArgument("leases4", leases);
+ handle->setArgument("query4", pkt);
+
+ lease4_release(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ // Call lease4_decline
+ try {
+ handle = getCalloutHandle(pkt);
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw);
+ handle->setArgument("leases4", leases);
+ handle->setArgument("query4", pkt);
+
+ lease4_decline(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ // Call lease4_committed
+ try {
+ handle = getCalloutHandle(pkt);
+ Pkt4Ptr rsp;
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw);
+ handle->setArgument("leases4", leases);
+ handle->setArgument("query4", pkt);
+ handle->setArgument("response4", rsp);
+
+ leases4_committed(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ srv.reset();
+
+ // Remove temp files
+ fuzz::deleteTempFile(path);
+ fuzz::deleteTempFile(lease_path);
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+#include <cstdlib>
+
+#include "helper_func.h"
+
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+
+extern "C" int lease6_release(CalloutHandle& handle);
+extern "C" int lease6_decline(CalloutHandle& handle);
+extern "C" int leases6_committed(CalloutHandle& handle);
+extern "C" int addr6_register(CalloutHandle& handle);
+
+namespace isc {
+ namespace dhcp {
+ class MyDhcpv6Srv : public ControlledDhcpv6Srv {
+ public:
+ void fuzz_classifyPacket(const Pkt6Ptr& pkt) {
+ classifyPacket(pkt);
+ }
+ };
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ // Disable validatePath checking to allow writing configuration file to /tmp
+ isc::util::file::PathChecker::enableEnforcement(false);
+
+ // Force DUID file to /tmp
+ setenv("KEA_DHCP_DATA_DIR", "/tmp", 1);
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Creating temp config file
+ std::string path = fuzz::writeTempConfig(false);
+ if (path.empty()) {
+ // Early exit if configuration file creation failed
+ fuzz::deleteTempFile(path);
+ return 0;
+ }
+
+ // Creating temp lease file
+ std::string lease_path = fuzz::writeTempLease(false);
+
+ Pkt6Ptr pkt;
+ std::unique_ptr<MyDhcpv6Srv> srv;
+
+ // Package parsing
+ try {
+ pkt = Pkt6Ptr(new Pkt6(data, size));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ FuzzedDataProvider fdp(data, size);
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 37);
+ pkt->setType(static_cast<DHCPv6MessageType>(typeChoice));
+
+ // Server initialisation
+ try {
+ srv.reset(new MyDhcpv6Srv());
+ srv->init(path);
+ } catch (...) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ if (!srv) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ // Call classifyPacket for packet checking
+ try {
+ srv->fuzz_classifyPacket(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Prepare client context
+ CalloutHandlePtr handle = nullptr;
+ AllocEngine::ClientContext6 ctx;
+
+ // Call earlyGHRLookup
+ try {
+ srv->earlyGHRLookup(pkt, ctx);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Call lease6_decline
+ try {
+ handle = getCalloutHandle(pkt);
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease6Collection leases = LeaseMgrFactory::instance().getLease6(hw);
+ handle->setArgument("leases6", leases);
+ handle->setArgument("query6", pkt);
+
+ lease6_decline(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ // Call lease6_release
+ try {
+ handle = getCalloutHandle(pkt);
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease6Collection leases = LeaseMgrFactory::instance().getLease6(hw);
+ handle->setArgument("leases6", leases);
+ handle->setArgument("query6", pkt);
+
+ lease6_release(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ // Call leases6_committed
+ try {
+ handle = getCalloutHandle(pkt);
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease6Collection leases = LeaseMgrFactory::instance().getLease6(hw);
+ handle->setArgument("leases6", leases);
+ handle->setArgument("deleted_leases6", leases);
+ handle->setArgument("query6", pkt);
+
+ leases6_committed(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ // Call addr6_register
+ try {
+ handle = getCalloutHandle(pkt);
+ Pkt6Ptr rsp;
+ uint8_t mac_addr[6];
+ for (size_t i = 0; i < 6; ++i) {
+ mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
+ Lease6Collection leases = LeaseMgrFactory::instance().getLease6(hw);
+ handle->setArgument("new_leases6", leases);
+ handle->setArgument("response6", rsp);
+ handle->setArgument("query6", pkt);
+
+ IOAddress addr = ctx.query_->getRemoteAddr();
+ handle->setArgument("address6", addr);
+ handle->setArgument("old_leases6",
+ LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr));
+
+ addr6_register(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean up to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ srv.reset();
+
+ // Remove temp files
+ fuzz::deleteTempFile(path);
+ fuzz::deleteTempFile(lease_path);
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dns/name.h>
+#include <dns/tsig.h>
+#include <dns/rdata.h>
+
+#include <gss_tsig_context.h>
+#include <gss_tsig_key.h>
+#include <tkey_exchange.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <vector>
+#include <memory>
+
+using namespace isc;
+using namespace isc::dns;
+using namespace isc::gss_tsig;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ // Prepare basic key crypto information
+ std::string key_name = fdp.ConsumeRandomLengthString(64);
+ if (key_name.empty()) key_name = "fuzz-key";
+ std::string tsig_rdata_txt = fdp.ConsumeRandomLengthString(1024);
+ std::string owner_txt = fdp.ConsumeRandomLengthString(128);
+ const uint16_t qid = fdp.ConsumeIntegral<uint16_t>();
+ const bool do_chunked = fdp.ConsumeBool();
+
+ // Target correct key sign and verify
+ try {
+ std::vector<uint8_t> payload = fdp.ConsumeBytes<uint8_t>(fdp.ConsumeIntegralInRange<size_t>(0, 2048));
+ GssTsigKey key(key_name, payload);
+ GssTsigContext ctx(key);
+
+ std::string wire = fdp.ConsumeRandomLengthString(2048);
+ ctx.sign(qid, wire.data(), wire.size());
+
+ Name owner_name(owner_txt.empty() ? "fuzz." : owner_txt.c_str());
+ rdata::any::TSIG tsig_rdata(tsig_rdata_txt);
+ TSIGRecord record(owner_name, tsig_rdata);
+
+ ctx.verify(&record, wire.data(), wire.size());
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target key exchange
+ try {
+ auto val = fdp.ConsumeIntegralInRange<int>(-5, 10);
+ TKeyExchange::statusToText(static_cast<TKeyExchange::Status>(val));
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/user_chk/user.h>
+#include <dhcp/user_chk/user_data_source.h>
+#include <dhcp/user_chk/user_file.h>
+#include <dhcp/user_chk/user_registry.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+using namespace user_chk;
+
+extern "C" UserRegistryPtr user_registry;
+extern "C" int pkt4_receive(CalloutHandle& handle);
+extern "C" int subnet4_select(CalloutHandle& handle);
+
+namespace isc {
+ namespace dhcp {
+ class MyDhcpv4Srv : public ControlledDhcpv4Srv {
+ public:
+ void fuzz_classifyPacket(const Pkt4Ptr& pkt) {
+ classifyPacket(pkt);
+ }
+ };
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ FuzzedDataProvider fdp(data, size);
+
+ // Disable validatePath checking to allow writing configuration file to /tmp
+ isc::util::file::PathChecker::enableEnforcement(false);
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Creating temp config file
+ std::string path = fuzz::writeTempConfig(true);
+ if (path.empty()) {
+ // Early exit if configuration file creation failed
+ fuzz::deleteTempFile(path);
+ return 0;
+ }
+
+ // Creating temp lease file
+ std::string lease_path = fuzz::writeTempLease(true);
+
+ // Creating temp user file
+ std::string user_path = fuzz::writeTempUserFile();
+
+ // Creating user registry
+ try {
+ user_registry.reset(new UserRegistry());
+ UserDataSourcePtr user_file(new UserFile(user_path));
+ user_registry->setSource(user_file);
+ user_registry->refresh();
+ } catch (std::exception& e) {
+ // Early exit if user registry failed to create.
+ return 0;
+ }
+
+ try {
+ for (int i = 0; i < fdp.ConsumeIntegralInRange<int>(1, 5); i++) {
+ if (fdp.ConsumeBool()) {
+ std::vector<uint8_t> mac = fdp.ConsumeBytes<uint8_t>(6);
+ UserPtr user = UserPtr(new User(UserId::HW_ADDRESS, mac));
+ user_registry->addUser(user);
+ } else {
+ const size_t len = fdp.ConsumeIntegralInRange<size_t>(2, 64);
+ std::vector<uint8_t> duid = fdp.ConsumeBytes<uint8_t>(len);
+ UserPtr user = UserPtr(new User(UserId::DUID, duid));
+ user_registry->addUser(user);
+ }
+ }
+ } catch (...) {
+ // Slient exceptions
+ }
+
+ Pkt4Ptr pkt;
+ std::unique_ptr<MyDhcpv4Srv> srv;
+
+ // Package parsing
+ try {
+ // Add fixed magic cookie and correct hardware address
+ std::vector<uint8_t> buf(data, data + size);
+ if (size >= 240) {
+ // Max hardware address length is 20
+ buf[2] = 20;
+
+ // Magic cookie fixed value 0x63825363
+ buf[236] = 0x63;
+ buf[237] = 0x82;
+ buf[238] = 0x53;
+ buf[239] = 0x63;
+ }
+
+ pkt = Pkt4Ptr(new Pkt4(buf.data(), buf.size()));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 8);
+ pkt->setType(static_cast<DHCPMessageType>(typeChoice));
+
+ // Server initialisation
+ try {
+ srv.reset(new MyDhcpv4Srv());
+ srv->init(path);
+ } catch (...) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ if (!srv) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ CalloutHandlePtr handle = nullptr;
+
+ // Fuzz pkt4_receive
+ try {
+ handle = getCalloutHandle(pkt);
+ handle->setArgument("query4", pkt);
+ pkt4_receive(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean handle to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ // Call classifyPacket for packet checking
+ try {
+ srv->fuzz_classifyPacket(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Prepare client context
+ AllocEngine::ClientContext4Ptr ctx(new AllocEngine::ClientContext4());
+
+ // Call earlyGHRLookup
+ try {
+ srv->earlyGHRLookup(pkt, ctx);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Fuzz subnet4_select
+ try {
+ handle = getCalloutHandle(pkt);
+ Pkt4Ptr rsp;
+ CfgMgr& cfgmgr = CfgMgr::instance();
+ handle = getCalloutHandle(pkt);
+ handle->setArgument("query4", pkt);
+ handle->setArgument("subnet4collection",
+ cfgmgr.getCurrentCfg()->getCfgSubnets4()->getAll());
+ if (!ctx) {
+ ctx.reset(new AllocEngine::ClientContext4());
+ }
+ if (ctx) {
+ handle->setArgument("subnet4", ctx->subnet_);
+ }
+ subnet4_select(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean handle to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ srv.reset();
+
+ // Remove temp files
+ fuzz::deleteTempFile(path);
+ fuzz::deleteTempFile(lease_path);
+ fuzz::deleteTempFile(user_path);
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option.h>
+#include <dhcp/user_chk/user.h>
+#include <dhcp/user_chk/user_data_source.h>
+#include <dhcp/user_chk/user_file.h>
+#include <dhcp/user_chk/user_registry.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <log/logger_support.h>
+#include <util/filesystem.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <memory>
+#include <iostream>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <cstdio>
+#include <cstdlib>
+
+#include "helper_func.h"
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::util;
+using namespace user_chk;
+
+extern "C" UserRegistryPtr user_registry;
+extern "C" int pkt6_receive(CalloutHandle& handle);
+extern "C" int subnet6_select(CalloutHandle& handle);
+
+namespace isc {
+ namespace dhcp {
+ class MyDhcpv6Srv : public ControlledDhcpv6Srv {
+ public:
+ void fuzz_classifyPacket(const Pkt6Ptr& pkt) {
+ classifyPacket(pkt);
+ }
+ };
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 236) {
+ // package size requires at least 236 bytes
+ return 0;
+ }
+
+ FuzzedDataProvider fdp(data, size);
+
+ // Disable validatePath checking to allow writing configuration file to /tmp
+ isc::util::file::PathChecker::enableEnforcement(false);
+
+ // Force DUID file to /tmp
+ setenv("KEA_DHCP_DATA_DIR", "/tmp", 1);
+
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ // Creating temp config file
+ std::string path = fuzz::writeTempConfig(false);
+ if (path.empty()) {
+ // Early exit if configuration file creation failed
+ fuzz::deleteTempFile(path);
+ return 0;
+ }
+
+ // Creating temp lease file
+ std::string lease_path = fuzz::writeTempLease(false);
+
+ // Creating temp user file
+ std::string user_path = fuzz::writeTempUserFile();
+
+ // Creating user registry
+ try {
+ user_registry.reset(new UserRegistry());
+ UserDataSourcePtr user_file(new UserFile(user_path));
+ user_registry->setSource(user_file);
+ user_registry->refresh();
+ } catch (std::exception& e) {
+ // Early exit if user registry failed to create.
+ return 0;
+ }
+
+ try {
+ for (int i = 0; i < fdp.ConsumeIntegralInRange<int>(1, 5); i++) {
+ if (fdp.ConsumeBool()) {
+ std::vector<uint8_t> mac = fdp.ConsumeBytes<uint8_t>(6);
+ UserPtr user = UserPtr(new User(UserId::HW_ADDRESS, mac));
+ user_registry->addUser(user);
+ } else {
+ const size_t len = fdp.ConsumeIntegralInRange<size_t>(2, 64);
+ std::vector<uint8_t> duid = fdp.ConsumeBytes<uint8_t>(len);
+ UserPtr user = UserPtr(new User(UserId::DUID, duid));
+ user_registry->addUser(user);
+ }
+ }
+ } catch (...) {
+ // Slient exceptions
+ }
+
+ Pkt6Ptr pkt;
+ std::unique_ptr<MyDhcpv6Srv> srv;
+
+ // Package parsing
+ try {
+ pkt = Pkt6Ptr(new Pkt6(data, size));
+ pkt->unpack();
+ } catch (...) {
+ // Early exit if package parsing failed.
+ return 0;
+ }
+
+ // Configure random value in packet
+ uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 37);
+ pkt->setType(static_cast<DHCPv6MessageType>(typeChoice));
+
+ // Server initialisation
+ try {
+ srv.reset(new MyDhcpv6Srv());
+ srv->init(path);
+ } catch (...) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ if (!srv) {
+ // Early exit if server initialisation failed.
+ return 0;
+ }
+
+ CalloutHandlePtr handle = nullptr;
+
+ // Fuzz pkt6_receive
+ try {
+ handle = getCalloutHandle(pkt);
+ std::vector<uint8_t> duid = fdp.ConsumeBytes<uint8_t>(fdp.ConsumeIntegralInRange<int>(2, 128));
+ OptionPtr option = OptionPtr(new Option(Option::V4, 1, duid));
+ pkt->addOption(option);
+ handle->setArgument("query6", pkt);
+ pkt6_receive(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean handle to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ // Call classifyPacket for packet checking
+ try {
+ srv->fuzz_classifyPacket(pkt);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Prepare client context
+ AllocEngine::ClientContext6 ctx;
+
+ // Call earlyGHRLookup
+ try {
+ srv->earlyGHRLookup(pkt, ctx);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Fuzz subnet6_select
+ try {
+ handle = getCalloutHandle(pkt);
+ Pkt6Ptr rsp;
+ CfgMgr& cfgmgr = CfgMgr::instance();
+ handle = getCalloutHandle(pkt);
+ handle->setArgument("query6", pkt);
+ handle->setArgument("subnet6", ctx.subnet_);
+ handle->setArgument("subnet6collection",
+ cfgmgr.getCurrentCfg()->getCfgSubnets6()->getAll());
+ subnet6_select(*handle);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ } catch (const boost::exception& e) {
+ // Slient exceptions
+ }
+
+ // Clean handle to avoid mem leak
+ if (handle) {
+ handle->deleteAllArguments();
+ }
+
+ srv.reset();
+
+ // Remove temp files
+ fuzz::deleteTempFile(path);
+ fuzz::deleteTempFile(lease_path);
+ fuzz::deleteTempFile(user_path);
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <log/logger_support.h>
+#include <process/daemon.h>
+
+#include <cc/data.h>
+
+#include <http/basic_auth_config.h>
+#include <http/cfg_http_header.h>
+#include <http/date_time.h>
+#include <http/http_types.h>
+#include <http/request.h>
+#include <http/request_parser.h>
+#include <http/response.h>
+#include <http/response_parser.h>
+#include <http/url.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <vector>
+#include <algorithm>
+
+using namespace isc;
+using namespace isc::data;
+using namespace isc::http;
+
+static constexpr HttpRequest::Method requestMethods[] = {
+ HttpRequest::Method::HTTP_GET, HttpRequest::Method::HTTP_POST,
+ HttpRequest::Method::HTTP_HEAD, HttpRequest::Method::HTTP_PUT,
+ HttpRequest::Method::HTTP_DELETE, HttpRequest::Method::HTTP_OPTIONS,
+ HttpRequest::Method::HTTP_CONNECT, HttpRequest::Method::HTTP_METHOD_UNKNOWN
+};
+
+static constexpr HttpStatusCode statusCodes[] = {
+ HttpStatusCode::OK, HttpStatusCode::CREATED, HttpStatusCode::ACCEPTED,
+ HttpStatusCode::NO_CONTENT, HttpStatusCode::MULTIPLE_CHOICES,
+ HttpStatusCode::MOVED_PERMANENTLY, HttpStatusCode::MOVED_TEMPORARILY,
+ HttpStatusCode::NOT_MODIFIED, HttpStatusCode::BAD_REQUEST,
+ HttpStatusCode::UNAUTHORIZED, HttpStatusCode::FORBIDDEN,
+ HttpStatusCode::NOT_FOUND, HttpStatusCode::REQUEST_TIMEOUT,
+ HttpStatusCode::INTERNAL_SERVER_ERROR, HttpStatusCode::NOT_IMPLEMENTED,
+ HttpStatusCode::BAD_GATEWAY, HttpStatusCode::SERVICE_UNAVAILABLE
+};
+
+template <typename ParserT>
+inline void requestResponseParsing(ParserT& parser, const std::string& payload) {
+ size_t off = 0;
+
+ // Parse the payload until data use up
+ while (off < payload.size()) {
+ size_t remain = payload.size() - off;
+ size_t chunk = 1;
+ chunk = std::min(remain, (off % 32) + 1);
+
+ parser.postBuffer(reinterpret_cast<const uint8_t*>(payload.data()) + off, chunk);
+ off += chunk;
+ parser.poll();
+ if (!parser.needData()) {
+ parser.poll();
+ }
+ }
+
+ // Finishing the parsing
+ if (parser.needData()) {
+ parser.postBuffer(nullptr, 0);
+ parser.poll();
+ }
+
+ parser.getErrorMessage();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+ FuzzedDataProvider fdp(Data, Size);
+
+ HttpRequest request;
+ HttpResponse response;
+
+ // Generate random value
+ const std::string key = fdp.ConsumeBytesAsString(10);
+ const HttpVersion version = fdp.ConsumeBool()?HttpVersion::HTTP_11():HttpVersion::HTTP_10();
+ const HttpRequest::Method requestMethod = requestMethods[fdp.ConsumeIntegralInRange<int>(0, 7)];
+ const HttpStatusCode statusCode = statusCodes[fdp.ConsumeIntegralInRange<int>(0, 16)];
+
+ // Generate payload
+ const std::string payload = fdp.ConsumeRemainingBytesAsString();
+
+ try {
+ // Request parsing
+ HttpRequestParser requestParser(request);
+ requestParser.initModel();
+
+ // Feed payload into request parser and functions
+ requestResponseParsing(requestParser, payload);
+ request.getHttpVersion();
+ request.context();
+ request.getMethod();
+ request.getUri();
+ request.getBasicAuth();
+ request.requireHttpVersion(version);
+ request.requireHttpMethod(requestMethod);
+ request.getHeaderValue(key);
+ request.getHeader(key);
+ request.requireHeader(key);
+ request.finalize();
+ request.reset();
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ try {
+ // Response parsing
+ HttpResponseParser responseParser(response);
+ responseParser.initModel();
+
+ // Feed payload into response parser and functions
+ requestResponseParsing(responseParser, payload);
+ response.getStatusCode();
+ response.getHttpVersion();
+ response.context();
+ response.requireHttpVersion(version);
+ response.getHeaderValue(key);
+ response.getHeader(key);
+ response.requireHeader(key);
+ response.statusCodeToNumber(statusCode);
+ response.statusCodeToString(statusCode);
+ response.isClientError(statusCode);
+ response.isServerError(statusCode);
+ response.create();
+ response.finalize();
+ response.reset();
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ try {
+ // Response JSON parsing
+ HttpResponseJson json(version, statusCode);
+ json.setBodyAsJson(Element::create(payload));
+ json.finalize();
+ json.reset();
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ try {
+ // Configuration headers parsing
+ ConstElementPtr elem = Element::fromJSON(payload);
+ CfgHttpHeaders headers = parseCfgHttpHeaders(elem);
+ copyHttpHeaders(headers, request);
+ copyHttpHeaders(headers, response);
+ CfgHttpHeaderstoElement(headers);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ try{
+ // Basic Authentication Configuration parsing
+ BasicHttpAuthConfig cfg;
+ cfg.parse(Element::fromJSON(payload));
+ cfg.toElement();
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ try{
+ // Parse url
+ Url url(payload);
+ url.isValid();
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ try{
+ // Parse Datetime
+ HttpDateTime::fromAny(payload);
+ HttpDateTime::fromRfc1123(payload);
+ HttpDateTime::fromRfc850(payload);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ try {
+ request.finalize();
+ response.finalize();
+ request.reset();
+ response.reset();
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+"GET"
+"POST"
+"PUT"
+"PATCH"
+"DELETE"
+"HEAD"
+"OPTIONS"
+"TRACE"
+"CONNECT"
+"HTTP/1.0"
+"HTTP/1.1"
+"HTTP/2.0"
+"200 OK"
+"400 Bad Request"
+"401 Unauthorized"
+"403 Forbidden"
+"404 Not Found"
+"500 Internal Server Error"
+"\x0d\x0a"
+"\x0d\x0a\x0d\x0a"
+": "
+":"
+" "
+"Host"
+"User-Agent"
+"Accept"
+"Accept-Encoding"
+"Accept-Language"
+"Connection"
+"Content-Type"
+"Content-Length"
+"Transfer-Encoding"
+"Authorization"
+"Cookie"
+"Set-Cookie"
+"Location"
+"Cache-Control"
+"Pragma"
+"Date"
+"Server"
+"Referer"
+"Upgrade"
+"Upgrade-Insecure-Requests"
+"X-Forwarded-For"
+"X-Forwarded-Proto"
+"X-Real-IP"
+"keep-alive"
+"close"
+"gzip"
+"deflate"
+"br"
+"chunked"
+"application/json"
+"text/plain"
+"text/html"
+"application/octet-stream"
+" / "
+" * "
+" /index.html "
+" /api "
+" HTTP/1.1\x0d\x0a"
+"HTTP/1.1 "
+"HTTP/1.0 "
+"0\x0d\x0a\x0d\x0a"
+"\x0d\x0a0\x0d\x0a\x0d\x0a"
+"1\x0d\x0aA\x0d\x0a0\x0d\x0a\x0d\x0a"
+"4\x0d\x0aWiki\x0d\x0a5\x0d\x0apedia\x0d\x0a0\x0d\x0a\x0d\x0a"
+"{"
+"}"
+"["
+"]"
+","
+"\""
+"\\\""
+"\"http-headers\""
+"\"name\""
+"\"value\""
+"\"Content-Type\""
+"\"Content-Length\""
+"\"Host\""
+"\"User-Agent\""
+"Basic "
+"Bearer "
+"sessionid="
+"Path="
+"Domain="
+"Secure"
+"HttpOnly"
+"SameSite="
+"Transfer-Encoding: chunked\x0d\x0a"
+"Content-Length: 0\x0d\x0a"
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <cstddef>
+#include <cstdint>
+#include <string>
+
+#include <asiolink/io_address.h>
+#include <asiolink/io_error.h>
+
+using isc::asiolink::IOAddress;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::string s(reinterpret_cast<const char*>(data), size);
+
+ try {
+ IOAddress addr(s);
+ addr.toText();
+ addr.isV4();
+ addr.isV6();
+ addr.getFamily();
+ addr.toBytes();
+
+ std::vector<uint8_t> bytes = addr.toBytes();
+ IOAddress::fromBytes(addr.getFamily(), &bytes[0]);
+ } catch (const std::exception&) {
+ // Catch exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <asiolink/io_address.h>
+#include <database/database_connection.h>
+#include <database/server_selector.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/host.h>
+#include <dhcpsrv/cfg_option.h>
+
+#include <log/logger_support.h>
+#include <process/daemon.h>
+#include <exceptions/exceptions.h>
+
+#include <mysql_cb_impl.h>
+#include <mysql_cb_dhcp4.h>
+
+#include <cstdint>
+#include <cstddef>
+#include <string>
+#include <set>
+#include <vector>
+#include <map>
+#include <utility>
+#include <iostream>
+
+using namespace isc::asiolink;
+using namespace isc::db;
+using namespace isc::dhcp;
+using namespace isc::util;
+
+extern "C" void mysqlmock_load_bytes(const uint8_t* data, size_t size);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ FuzzedDataProvider fdp(data, size);
+ mysqlmock_load_bytes(data, size);
+ DbCallback db_cb;
+
+ // Prepare tags
+ std::set<std::string> tags;
+ std::string tag = fdp.ConsumeRandomLengthString(16);
+ if (tag.empty()) {
+ tag = "default-tag";
+ }
+ tags.insert(tag);
+
+ // Prepare DatabaseConnection parameter maps
+ DatabaseConnection::ParameterMap params;
+ std::string dbname = fdp.ConsumeRandomLengthString(16);
+ if (dbname.empty()) {
+ dbname = "kea_fuzz";
+ }
+ params["name"] = dbname;
+ params[fdp.ConsumeRandomLengthString(16)] = fdp.ConsumeRandomLengthString(16);
+ params[fdp.ConsumeRandomLengthString(16)] = fdp.ConsumeRandomLengthString(16);
+
+ // Prepare server selector
+ ServerSelector selector = ServerSelector::UNASSIGNED();
+ try {
+ switch (fdp.ConsumeIntegralInRange<int>(0, 3)) {
+ case 0: selector = ServerSelector::ALL(); break;
+ case 1: selector = ServerSelector::ONE(tag); break;
+ case 2: selector = ServerSelector::MULTIPLE(tags); break;
+ case 3: selector = ServerSelector::ANY(); break;
+ }
+ } catch (const isc::Exception&) {
+ // Silent exceptions use default UNASSIGNED
+ }
+
+ try {
+ MySqlConfigBackendImpl backend("v4", params, db_cb);
+ MySqlConfigBackendDHCPv4 dhcp_backend(params);
+
+ // Target getGlobalParameter4
+ try {
+ dhcp_backend.getGlobalParameter4(selector, fdp.ConsumeRandomLengthString(32));
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ }
+
+ // Target getAllSubnets4
+ try {
+ dhcp_backend.getAllSubnets4(selector);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ }
+
+ // Target createUpdateSubnet4
+ try {
+ IOAddress address("127.0.0.1");
+
+ uint32_t a1 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b1 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c1 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t1(a1, b1, c1);
+
+ uint32_t a2 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b2 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c2 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t2(a2, b2, c2);
+
+ uint32_t a3 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b3 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c3 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t3(a3, b3, c3);
+
+ SubnetID sid = static_cast<SubnetID>(fdp.ConsumeIntegralInRange<uint32_t>(1, UINT32_MAX));
+ Subnet4Ptr subnet(Subnet4::create(address,
+ fdp.ConsumeIntegralInRange(0, 32),
+ t1, t2, t3, sid));
+ dhcp_backend.createUpdateSubnet4(selector, subnet);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ }
+
+ // Target deleteSubnet4
+ try {
+ dhcp_backend.deleteSubnet4(selector, fdp.ConsumeRandomLengthString(32));
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ }
+
+ // Target createUpdateOption4
+ try {
+ OptionBuffer opt_buf;
+ OptionPtr opt(new Option(Option::V4, fdp.ConsumeIntegralInRange<uint16_t>(1, 254), opt_buf));
+ OptionDescriptorPtr opt_desc(new OptionDescriptor(opt, true, true));
+
+ std::string opt_space = "dhcp4";
+ std::string opt_name = fdp.ConsumeRandomLengthString(32);
+ if (opt_name.empty()) {
+ opt_name = "fuzz-opt";
+ }
+
+ dhcp_backend.createUpdateOption4(selector, opt_name, opt_desc);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ }
+ } catch (const isc::Exception&) {
+ // Silent top-level exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <asiolink/io_address.h>
+#include <database/database_connection.h>
+#include <database/server_selector.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/host.h>
+#include <dhcpsrv/cfg_option.h>
+
+#include <log/logger_support.h>
+#include <process/daemon.h>
+#include <exceptions/exceptions.h>
+
+#include <mysql_cb_impl.h>
+#include <mysql_cb_dhcp6.h>
+
+#include <cstdint>
+#include <cstddef>
+#include <string>
+#include <set>
+#include <vector>
+#include <map>
+#include <utility>
+#include <iostream>
+
+using namespace isc::asiolink;
+using namespace isc::db;
+using namespace isc::dhcp;
+using namespace isc::util;
+
+extern "C" void mysqlmock_load_bytes(const uint8_t* data, size_t size);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ FuzzedDataProvider fdp(data, size);
+ mysqlmock_load_bytes(data, size);
+ DbCallback db_cb;
+
+ // Prepare tags
+ std::set<std::string> tags;
+ std::string tag = fdp.ConsumeRandomLengthString(16);
+ if (tag.empty()) {
+ tag = "default-tag";
+ }
+ tags.insert(tag);
+
+ // Prepare DatabaseConnection parameter maps
+ DatabaseConnection::ParameterMap params;
+ std::string dbname = fdp.ConsumeRandomLengthString(16);
+ if (dbname.empty()) {
+ dbname = "kea_fuzz";
+ }
+ params["name"] = dbname;
+ params[fdp.ConsumeRandomLengthString(16)] = fdp.ConsumeRandomLengthString(16);
+ params[fdp.ConsumeRandomLengthString(16)] = fdp.ConsumeRandomLengthString(16);
+
+ // Prepare server selector
+ ServerSelector selector = ServerSelector::UNASSIGNED();
+ try {
+ switch (fdp.ConsumeIntegralInRange<int>(0, 3)) {
+ case 0: selector = ServerSelector::ALL(); break;
+ case 1: selector = ServerSelector::ONE(tag); break;
+ case 2: selector = ServerSelector::MULTIPLE(tags); break;
+ case 3: selector = ServerSelector::ANY(); break;
+ }
+ } catch (const isc::Exception&) {
+ // Silent exceptions use default UNASSIGNED
+ }
+
+ try {
+ MySqlConfigBackendImpl backend("v6", params, db_cb);
+ MySqlConfigBackendDHCPv6 dhcp_backend(params);
+
+ // Target getGlobalParameter6
+ try {
+ dhcp_backend.getGlobalParameter6(selector, fdp.ConsumeRandomLengthString(32));
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ }
+
+ // Target getAllSubnets6
+ try {
+ dhcp_backend.getAllSubnets6(selector);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ }
+
+ // Target createUpdateSubnet6
+ try {
+ IOAddress address("::1");
+
+ uint32_t a1 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b1 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c1 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t1(a1, b1, c1);
+
+ uint32_t a2 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b2 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c2 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t2(a2, b2, c2);
+
+ uint32_t a3 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b3 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c3 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t3(a3, b3, c3);
+
+ uint32_t a4 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b4 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c4 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t4(a4, b4, c4);
+
+ SubnetID sid = static_cast<SubnetID>(fdp.ConsumeIntegralInRange<uint32_t>(1, UINT32_MAX));
+ Subnet6Ptr subnet(Subnet6::create(address,
+ fdp.ConsumeIntegralInRange(0, 32),
+ t1, t2, t3, t4, sid));
+ dhcp_backend.createUpdateSubnet6(selector, subnet);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ }
+
+ // Target deleteSubnet6
+ try {
+ dhcp_backend.deleteSubnet6(selector, fdp.ConsumeRandomLengthString(32));
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ }
+
+ // Target createUpdateOption6
+ try {
+ OptionBuffer opt_buf;
+ OptionPtr opt(new Option(Option::V4, fdp.ConsumeIntegralInRange<uint16_t>(1, 254), opt_buf));
+ OptionDescriptorPtr opt_desc(new OptionDescriptor(opt, true, true));
+
+ std::string opt_space = "dhcp4";
+ std::string opt_name = fdp.ConsumeRandomLengthString(32);
+ if (opt_name.empty()) {
+ opt_name = "fuzz-opt";
+ }
+
+ dhcp_backend.createUpdateOption6(selector, opt_name, opt_desc);
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ }
+ } catch (const isc::Exception&) {
+ // Silent top-level exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <asiolink/io_address.h>
+#include <database/database_connection.h>
+#include <database/server_selector.h>
+#include <dhcp/pgsql/pgsql_cb_impl.h>
+#include <dhcp/pgsql/pgsql_cb_dhcp4.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/host.h>
+#include <pgsql/pgsql_exchange.h>
+
+#include <log/logger_support.h>
+#include <process/daemon.h>
+#include <exceptions/exceptions.h>
+
+#include <cstdint>
+#include <cstddef>
+#include <string>
+#include <vector>
+#include <map>
+#include <utility>
+#include <iostream>
+
+using namespace isc::asiolink;
+using namespace isc::db;
+using namespace isc::dhcp;
+using namespace isc::util;
+
+extern "C" void pgmock_load_bytes(const uint8_t* data, size_t size);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ FuzzedDataProvider fdp(data, size);
+ pgmock_load_bytes(data, size);
+
+ DbCallback db_cb;
+ size_t index = 0;
+
+ // Prepare tags
+ std::set<std::string> tags;
+ std::string tag = fdp.ConsumeRandomLengthString(16);
+ if (tag.size() == 0) {
+ tag = "default-tag";
+ }
+ tags.insert(tag);
+
+ // Preparer DatabaseConnection parameter map
+ DatabaseConnection::ParameterMap params;
+ params["name"] = fdp.ConsumeRandomLengthString(32);
+ params[fdp.ConsumeRandomLengthString(32)] = fdp.ConsumeRandomLengthString(32);
+ params[fdp.ConsumeRandomLengthString(32)] = fdp.ConsumeRandomLengthString(32);
+ params[fdp.ConsumeRandomLengthString(32)] = fdp.ConsumeRandomLengthString(32);
+
+ // Prepare Binding array
+ PsqlBindArray binds;
+ binds.add(fdp.ConsumeRandomLengthString(256));
+ binds.add(fdp.ConsumeRandomLengthString(256));
+ binds.add(fdp.ConsumeRandomLengthString(256));
+
+ // Prepare server selector
+ ServerSelector selector = ServerSelector::UNASSIGNED();
+ try {
+ switch (fdp.ConsumeIntegralInRange<int>(0, 3)) {
+ case 0:
+ selector = ServerSelector::ALL();
+ break;
+ case 1:
+ selector = ServerSelector::ONE(tag);
+ break;
+ case 2:
+ selector = ServerSelector::MULTIPLE(tags);
+ break;
+ case 3:
+ selector = ServerSelector::ANY();
+ break;
+ }
+ } catch (const isc::Exception&) {
+ // Slient exceptions use default unassigned server selector
+ }
+
+ try {
+ // Prepare PgSql backend
+ PgSqlConfigBackendImpl backend(fdp.ConsumeRandomLengthString(32), params, db_cb, index);
+ PgSqlConfigBackendDHCPv4 dhcp_backend(params);
+
+ // Target selectQuery
+ try {
+ backend.selectQuery(0, binds, PgSqlConnection::ConsumeResultRowFun([](PgSqlResult&, int) {}));
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target insertQuery
+ try {
+ backend.insertQuery(0, binds);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target updateDeleteQuery
+ try {
+ backend.updateDeleteQuery(0, binds);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target upper level getGlobalParameter4
+ try {
+ dhcp_backend.getGlobalParameter4(selector, fdp.ConsumeRandomLengthString(32));
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target upper level getAllSubnets4
+ try {
+ dhcp_backend.getAllSubnets4(selector);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target upper level createUpdateSubnet4
+ try {
+ IOAddress address(fdp.ConsumeRandomLengthString(15));
+ uint32_t a1 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b1 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c1 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t1(a1, b1, c1);
+
+ uint32_t a2 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b2 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c2 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t2(a2, b2, c2);
+
+ uint32_t a3 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b3 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c3 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t3(a3, b3, c3);
+
+ SubnetID sid = static_cast<SubnetID>(fdp.ConsumeIntegral<uint32_t>());
+
+ Subnet4Ptr subnet(Subnet4::create(address, fdp.ConsumeIntegralInRange(0, 32), t1, t2, t3, sid));
+ dhcp_backend.createUpdateSubnet4(selector, subnet);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target upper level deleteSubnet4
+ try {
+ dhcp_backend.deleteSubnet4(selector, fdp.ConsumeRandomLengthString(32));
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target upper level createUpdateOption4
+ try {
+ OptionBuffer opt_buf;
+ OptionPtr opt(new Option(Option::V4, 0, opt_buf));
+ OptionDescriptorPtr opt_desc(new OptionDescriptor(opt, true, true));
+
+ std::string opt_space = "dhcp4";
+ std::string opt_name = fdp.ConsumeRandomLengthString(32);
+ if (opt_name.empty()) {
+ opt_name = "fuzz-opt";
+ }
+
+ dhcp_backend.createUpdateOption4(selector, opt_name, opt_desc);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+ } catch (const isc::Exception& e) {
+ // Slient exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <asiolink/io_address.h>
+#include <database/database_connection.h>
+#include <database/server_selector.h>
+#include <dhcp/pgsql/pgsql_cb_impl.h>
+#include <dhcp/pgsql/pgsql_cb_dhcp6.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/host.h>
+#include <pgsql/pgsql_exchange.h>
+
+#include <log/logger_support.h>
+#include <process/daemon.h>
+#include <exceptions/exceptions.h>
+
+#include <cstdint>
+#include <cstddef>
+#include <string>
+#include <vector>
+#include <map>
+#include <utility>
+#include <iostream>
+
+using namespace isc::asiolink;
+using namespace isc::db;
+using namespace isc::dhcp;
+using namespace isc::util;
+
+extern "C" void pgmock_load_bytes(const uint8_t* data, size_t size);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ // Initialise logging
+ setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
+ setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
+ setenv("KEA_PIDFILE_DIR", "/tmp", 0);
+ setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
+ try {
+ isc::log::initLogger("fuzzer");
+ isc::process::Daemon::loggerInit("fuzzer", false);
+ isc::process::Daemon::setDefaultLoggerName("fuzzer");
+ } catch (...) {
+ // Early exit if logging initialisation failed
+ return 0;
+ }
+
+ FuzzedDataProvider fdp(data, size);
+ pgmock_load_bytes(data, size);
+
+ DbCallback db_cb;
+ size_t index = 0;
+
+ // Prepare tags
+ std::set<std::string> tags;
+ std::string tag = fdp.ConsumeRandomLengthString(16);
+ if (tag.size() == 0) {
+ tag = "default-tag";
+ }
+ tags.insert(tag);
+
+ // Preparer DatabaseConnection parameter map
+ DatabaseConnection::ParameterMap params;
+ params[fdp.ConsumeRandomLengthString(32)] = fdp.ConsumeRandomLengthString(32);
+ params[fdp.ConsumeRandomLengthString(32)] = fdp.ConsumeRandomLengthString(32);
+ params[fdp.ConsumeRandomLengthString(32)] = fdp.ConsumeRandomLengthString(32);
+
+ // Prepare Binding array
+ PsqlBindArray binds;
+ binds.add(fdp.ConsumeRandomLengthString(256));
+ binds.add(fdp.ConsumeRandomLengthString(256));
+ binds.add(fdp.ConsumeRandomLengthString(256));
+
+ // Prepare server selector
+ ServerSelector selector = ServerSelector::UNASSIGNED();
+ try {
+ switch (fdp.ConsumeIntegralInRange<int>(0, 3)) {
+ case 0:
+ selector = ServerSelector::ALL();
+ break;
+ case 1:
+ selector = ServerSelector::ONE(tag);
+ break;
+ case 2:
+ selector = ServerSelector::MULTIPLE(tags);
+ break;
+ case 3:
+ selector = ServerSelector::ANY();
+ break;
+ }
+ } catch (const isc::Exception&) {
+ // Slient exceptions use default unassigned server selector
+ }
+
+ try {
+ // Prepare PgSql backend
+ PgSqlConfigBackendImpl backend(fdp.ConsumeRandomLengthString(32), params, db_cb, index);
+ PgSqlConfigBackendDHCPv6 dhcp_backend(params);
+
+ // Target selectQuery
+ try {
+ backend.selectQuery(0, binds, PgSqlConnection::ConsumeResultRowFun([](PgSqlResult&, int) {}));
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target insertQuery
+ try {
+ backend.insertQuery(0, binds);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target updateDeleteQuery
+ try {
+ backend.updateDeleteQuery(0, binds);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target upper level getGlobalParameter6
+ try {
+ dhcp_backend.getGlobalParameter6(selector, fdp.ConsumeRandomLengthString(32));
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target upper level getAllSubnets6
+ try {
+ dhcp_backend.getAllSubnets6(selector);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target upper level createUpdateSubnet6
+ try {
+ IOAddress address(fdp.ConsumeRandomLengthString(64));
+ uint32_t a1 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b1 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c1 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t1(a1, b1, c1);
+
+ uint32_t a2 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b2 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c2 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t2(a2, b2, c2);
+
+ uint32_t a3 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b3 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c3 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t3(a3, b3, c3);
+
+ uint32_t a4 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t b4 = fdp.ConsumeIntegral<uint32_t>();
+ uint32_t c4 = fdp.ConsumeIntegral<uint32_t>();
+ Triplet<uint32_t> t4(a4, b4, c4);
+
+ SubnetID sid = static_cast<SubnetID>(fdp.ConsumeIntegral<uint32_t>());
+
+ Subnet6Ptr subnet(Subnet6::create(address, fdp.ConsumeIntegralInRange(0, 128), t1, t2, t3, t4, sid));
+ dhcp_backend.createUpdateSubnet6(selector, subnet);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target upper level deleteSubnet6
+ try {
+ dhcp_backend.deleteSubnet6(selector, fdp.ConsumeRandomLengthString(32));
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ // Target upper level createUpdateOption6
+ try {
+ OptionBuffer opt_buf;
+ OptionPtr opt(new Option(Option::V6, 0, opt_buf));
+ OptionDescriptorPtr opt_desc(new OptionDescriptor(opt, true, true));
+
+ std::string opt_space = "dhcp4";
+ std::string opt_name = fdp.ConsumeRandomLengthString(32);
+ if (opt_name.empty()) {
+ opt_name = "fuzz-opt";
+ }
+
+ dhcp_backend.createUpdateOption6(selector, opt_name, opt_desc);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <exceptions/exceptions.h>
+#include <util/str.h>
+#include <util/csv_file.h>
+#include <util/encode/utf8.h>
+#include <util/boost_time_utils.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <string>
+#include <vector>
+#include <cstddef>
+
+using namespace boost::posix_time;
+using namespace isc::util;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ const bool isEscape = fdp.ConsumeBool();
+ const std::string delim = fdp.ConsumeBytesAsString(1);
+ const std::string payload = fdp.ConsumeRemainingBytesAsString();
+
+ std::vector<uint8_t> out;
+
+ // Target str tokens
+ try {
+ str::tokens(payload, delim, isEscape);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ // Target str quotedStringToBinary
+ try {
+ str::quotedStringToBinary(payload);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ // Target str decodeFormattedHexString
+ try {
+ str::decodeFormattedHexString(payload, out);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ // Target str decodeColonSeparatedHexString
+ try {
+ str::decodeColonSeparatedHexString(payload, out);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ // Target str decodeSeparatedHexString
+ try {
+ str::decodeSeparatedHexString(payload, delim, out);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ // Target str trim
+ try {
+ str::trim(payload);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ // Target str lowercase/uppercase
+ try {
+ std::string temp = payload;
+ str::lowercase(temp);
+ str::uppercase(temp);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ // Target CSVRow
+ try {
+ CSVRow row(payload, delim[0]);
+ for (int i = 0; i < row.getValuesCount(); i++) {
+ row.readAt(i);
+ row.readAtEscaped(i);
+ }
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ // Target encodeUtf8
+ try {
+ encode::encodeUtf8(payload);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ // Prepare posix_time object
+ ptime pt;
+ try {
+ pt = time_from_string(payload);
+ } catch (...) {
+ // Failed for time_from_string, try from_iso_extended_string
+ try {
+ pt = from_iso_extended_string(payload);
+ } catch (...) {
+ // Failed to create posix_time object, early exit
+ return 0;
+ }
+ }
+
+ // Target ptimeToText
+ try {
+ ptimeToText(pt);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ // Target durationToText
+ try {
+ time_duration td = pt.time_of_day();
+ isc::util::durationToText(td);
+ } catch (const isc::Exception&) {
+ // Slient exceptions
+ } catch (const boost::exception&) {
+ // Slient exceptions
+ }
+
+ return 0;
+}
+
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 "helper_func.h"
+
+#include <cstdlib>
+#include <filesystem>
+#include <fstream>
+#include <unistd.h>
+
+namespace fs = std::filesystem;
+
+using namespace isc::data;
+
+namespace fuzz {
+ std::string writeTempConfig(bool isV4) {
+ return writeTempFile(isV4? JSON_CONFIG4 : JSON_CONFIG6);
+ }
+
+ std::string writeTempLease(bool isV4) {
+ if (isV4) {
+ return writeTempFile(LEASE4, "", "/tmp/kea-leases4.csv");
+ } else {
+ return writeTempFile(LEASE6, "", "/tmp/kea-leases6.csv");
+ }
+ }
+
+ std::string writeTempUserFile() {
+ return writeTempFile(USER, "", "/tmp/users.txt");
+ }
+
+ std::string writeTempFile(const std::string& payload, const char* suffix, const std::string& explicit_path) {
+ std::string path = explicit_path;
+ if (explicit_path.empty()) {
+ const long r = std::rand();
+ const pid_t pid = ::getpid();
+ path = std::string("/tmp/kea_fuzz_") + std::to_string(pid) +
+ "_" + std::to_string(r) + "." + (suffix ? suffix : "tmp");
+ }
+
+ std::ofstream ofs(path.c_str(), std::ios::binary);
+ if (ofs.good()) {
+ ofs.write(payload.data(), static_cast<std::streamsize>(payload.size()));
+ ofs.close();
+ return path;
+ }
+ return std::string();
+ }
+
+ void deleteTempFile(std::string file_path) {
+ if (fs::exists(file_path)) {
+ try {
+ fs::remove(file_path);
+ } catch (...) {
+ // Slient exceptions
+ }
+ }
+ }
+
+ isc::data::ElementPtr parseJSON(const std::string& s) {
+ try {
+ return Element::fromJSON(s);
+ } catch (...) {
+ return Element::createMap();
+ }
+ }
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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/.
+////////////////////////////////////////////////////////////////////////////////
+#pragma once
+
+#include <string>
+#include <cc/data.h>
+
+static const std::string JSON_CONFIG4 = R"CONFIG(
+ {
+ "Dhcp4":{
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600,
+ "name": "/tmp/kea-leases4.csv"
+ },
+ "valid-lifetime": 4000,
+ "subnet4": [{
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "interface": "eth0"
+ }],
+ "loggers": [{
+ "name": "kea-dhcp4",
+ "output-options": [{
+ "output": "stdout"
+ }],
+ "severity": "INFO"
+ }]
+ }
+ })CONFIG";
+
+static const std::string JSON_CONFIG6 = R"CONFIG(
+ {
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+ "option-data": [{
+ "name": "dns-servers",
+ "data": "2001:db8::1, 2001:db8::2"
+ }],
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600,
+ "name": "/tmp/kea-leases6.csv"
+ }
+ }
+ })CONFIG";
+
+static const std::string LEASE4 = R"LEASE(
+address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state,user_context,pool_id
+192.0.2.10,32:30,33:30,40,1642000000,50,1,1,one.example.com,0,,0
+192.0.2.11,,31:32:33,40,1643210000,50,1,1,,1,{ },0
+192.0.2.12,32:32,,40,1643212345,50,1,1,threeˎxampleˌom,2,{ "a": 1, "b": "c" },0
+192.0.2.13,aa:bb:cc:dd:ee:01,01:23:45:67:89:ab,86400,1767225600,1,1,1,host1.example.test,0,{ },0
+)LEASE";
+
+static const std::string LEASE6 = R"LEASE(
+address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,state,user_context,hwtype,hwaddr_source,pool_id
+::10,32:30:33,30,1642000000,40,50,1,60,128,1,1,one.example.com,38:30,0,,90,16,0
+::11,32:31:33,30,1643210000,40,50,1,60,128,1,1,,38:30,1,{ },90,1,0
+::12,32:32:33,30,1643212345,40,50,1,60,128,1,1,threeˎxampleˌom,38:30,2,{ "a": 1, "b": "c" },90,4,0
+2001:db8::100,00:01:00:01:12:34:56:78:aa:bb:cc:dd,86400,1767225600,1,43200,0,1,128,1,1,host1v6.example.test,aa:bb:cc:dd:ee:01,0,{ },1,1,0
+)LEASE";
+
+static const std::string USER = R"USER({ "type" : "HW_ADDR", "id" : "01AC00F03344", "opt1" : "true" }
+{ "type" : "HW_ADDR", "id" : "01:AC:00:F0:33:45", "opt1" : "true" }
+{ "type" : "DUID", "id" : "225060de0a0b", "opt1" : "true" }
+{ "type" : "DUID", "id" : "22:50:60:de:0a:0c", "opt1" : "true" })USER";
+
+namespace fuzz {
+ std::string writeTempConfig(bool isV4);
+ std::string writeTempLease(bool isV4);
+ std::string writeTempUserFile();
+ std::string writeTempFile(const std::string& payload, const char* suffix = "json", const std::string& explicit_path = "");
+ void deleteTempFile(std::string file_path);
+ isc::data::ElementPtr parseJSON(const std::string& s);
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <mariadb/mysql.h>
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <unordered_map>
+
+struct StmtState;
+
+static thread_local FuzzedDataProvider* g_fdp = nullptr;
+static thread_local std::string g_mysql_error;
+static thread_local std::string g_stmt_error;
+static thread_local std::string g_tls_cipher;
+
+struct MockResRow {
+ enum ColKind {
+ CK_UINT64, CK_UINT32, CK_UINT8, CK_STRING, CK_BLOB
+ } kind;
+ std::string s;
+ std::vector<uint8_t> blob;
+ uint64_t u64 = 0;
+ uint32_t u32 = 0;
+ uint8_t u8 = 0;
+};
+
+struct StmtState {
+ MYSQL* mysql;
+ std::string sql;
+ std::vector<std::vector<MockResRow>> rows;
+ size_t fetch_index = 0;
+ MYSQL_BIND* res_binds = nullptr;
+ size_t res_binds_count = 0;
+ bool has_rows = false;
+ bool is_version_stmt = false;
+ unsigned int field_count = 0;
+};
+
+static thread_local std::unordered_map<MYSQL_STMT*, StmtState*> g_stmt_state;
+static thread_local std::vector<MYSQL_STMT*> g_all_stmts;
+static thread_local std::vector<StmtState*> g_live_stmts;
+
+extern "C" void mysqlmock_load_bytes(const uint8_t* data, size_t size) {
+ for (auto* st : g_all_stmts) {
+ free(st);
+ }
+ g_all_stmts.clear();
+ for (auto& kv : g_stmt_state) {
+ delete kv.second;
+ }
+ g_stmt_state.clear();
+ g_stmt_state.rehash(0);
+ delete g_fdp;
+ g_fdp = new FuzzedDataProvider(data, size);
+}
+
+static bool is_like(const std::string& hay, const char* needle) {
+ std::string h = hay;
+ std::string n = needle ? needle : "";
+ std::transform(h.begin(), h.end(), h.begin(), ::tolower);
+ std::transform(n.begin(), n.end(), n.begin(), ::tolower);
+ return h.find(n) != std::string::npos;
+}
+
+static MYSQL_STMT* make_stmt() {
+ auto s = new StmtState();
+ s->mysql = reinterpret_cast<MYSQL*>(0x1);
+ g_live_stmts.push_back(s);
+ return reinterpret_cast<MYSQL_STMT*>(s);
+}
+
+static StmtState* SS(MYSQL_STMT* st) {
+ if (!st) {
+ return nullptr;
+ }
+ auto it = g_stmt_state.find(st);
+ return (it == g_stmt_state.end()) ? nullptr : it->second;
+}
+
+static StmtState* ensure_state(MYSQL_STMT* st) {
+ auto s = SS(st);
+ if (s) return s;
+ s = new StmtState();
+ s->mysql = reinterpret_cast<MYSQL*>(0x1);
+ g_stmt_state[st] = s;
+ g_live_stmts.push_back(s);
+ return s;
+}
+
+static unsigned int infer_field_count_from_sql(const std::string& sql) {
+ std::string s = sql;
+ std::transform(s.begin(), s.end(), s.begin(), ::tolower);
+ auto psel = s.find("select");
+ if (psel == std::string::npos) {
+ return 0;
+ }
+ auto pfrom = s.find(" from ", psel);
+ if (pfrom == std::string::npos) {
+ return 0;
+ }
+ std::string proj = s.substr(psel + 6, pfrom - (psel + 6));
+
+ proj.erase(0, proj.find_first_not_of(" \t\r\n"));
+ proj.erase(proj.find_last_not_of(" \t\r\n")+1);
+ if (proj.rfind("distinct", 0) == 0) {
+ proj.erase(0, 8);
+ proj.erase(0, proj.find_first_not_of(" \t\r\n"));
+ }
+
+ unsigned int cols = 0;
+ int paren = 0;
+ bool in_s = false, in_d = false;
+ for (size_t i = 0; i < proj.size(); ++i) {
+ char c = proj[i];
+ if (!in_d && c=='\'' && (i==0 || proj[i-1] != '\\')) {
+ in_s = !in_s;
+ } else if (!in_s && c=='"' && (i==0 || proj[i-1] != '\\')) {
+ in_d = !in_d;
+ } else if (!in_s && !in_d) {
+ if (c=='(') {
+ paren++;
+ } else if (c==')' && paren>0) {
+ paren--;
+ } else if (c==',' && paren==0) {
+ cols++;
+ }
+ }
+ }
+ if (!proj.empty()) {
+ cols++;
+ }
+ return cols;
+}
+
+static void fill_version_stmt(StmtState* s) {
+ s->is_version_stmt = true;
+ s->has_rows = true;
+ s->rows.clear();
+ s->field_count = 2;
+ std::vector<MockResRow> r;
+ MockResRow c1;
+ c1.kind = MockResRow::CK_UINT32;
+ c1.u32 = 32;
+ MockResRow c2;
+ c2.kind = MockResRow::CK_UINT32;
+ c2.u32 = 0;
+ r.push_back(c1);
+ r.push_back(c2);
+ s->rows.push_back(r);
+}
+
+static void fill_no_rows(StmtState* s, unsigned int cols = 0) {
+ s->is_version_stmt = false;
+ s->has_rows = false;
+ s->rows.clear();
+ s->fetch_index = 0;
+ s->field_count = cols;
+}
+
+static void fill_fuzz_rows(StmtState* s, unsigned int ncols) {
+ s->is_version_stmt = false;
+ s->has_rows = true;
+ s->rows.clear();
+ s->fetch_index = 0;
+ s->field_count = ncols ? ncols : 1;;
+ unsigned int nrows = g_fdp->ConsumeIntegralInRange<unsigned int>(1, 3);
+ for (unsigned int r = 0; r < nrows; ++r) {
+ std::vector<MockResRow> row;
+ row.reserve(ncols);
+ for (unsigned int c = 0; c < ncols; ++c) {
+ int k = g_fdp->ConsumeIntegralInRange<int>(0, 4);
+ MockResRow cell;
+ switch (k) {
+ case 0:
+ cell.kind = MockResRow::CK_UINT64;
+ cell.u64 = g_fdp->ConsumeIntegral<uint64_t>();
+ break;
+ case 1:
+ cell.kind = MockResRow::CK_UINT32;
+ cell.u32 = g_fdp->ConsumeIntegral<uint32_t>();
+ break;
+ case 2:
+ cell.kind = MockResRow::CK_UINT8;
+ cell.u8 = g_fdp->ConsumeIntegral<uint8_t>();
+ break;
+ case 3:
+ cell.kind = MockResRow::CK_STRING;
+ cell.s = g_fdp->ConsumeRandomLengthString(32);
+ break;
+ case 4: {
+ cell.kind = MockResRow::CK_BLOB;
+ size_t n = g_fdp->ConsumeIntegralInRange<size_t>(0, 32);
+ cell.blob = g_fdp->ConsumeBytes<uint8_t>(n);
+ break;
+ }
+ }
+ row.push_back(cell);
+ }
+ s->rows.push_back(std::move(row));
+ }
+}
+
+extern "C" {
+ int mysql_server_init(int argc, char **argv, char **groups) {
+ return 0;
+ }
+
+ MYSQL* mysql_init(MYSQL* in) {
+ return in ? in : reinterpret_cast<MYSQL*>(0x1);
+ }
+
+ void mysql_close(MYSQL*) {}
+
+ void mysql_free_result(MYSQL_RES*) {}
+
+ void mysql_server_end(void) {}
+
+ MYSQL* mysql_real_connect(MYSQL* mysql,
+ const char*,
+ const char*,
+ const char*,
+ const char*,
+ unsigned int,
+ const char*,
+ unsigned long) {
+ return mysql ? mysql : reinterpret_cast<MYSQL*>(0x1);
+ }
+
+ unsigned int mysql_errno(MYSQL*) {
+ return 0;
+ }
+
+ const char* mysql_error(MYSQL*) {
+ g_mysql_error = g_fdp ? g_fdp->ConsumeRandomLengthString(32) : std::string();
+ return g_mysql_error.c_str();
+ }
+
+ MYSQL_STMT* mysql_stmt_init(MYSQL*) {
+ auto* stmt = static_cast<MYSQL_STMT*>(std::calloc(1, sizeof(MYSQL_STMT)));
+ if (!stmt) {
+ return nullptr;
+ }
+ stmt->mysql = reinterpret_cast<MYSQL*>(0x1);
+ g_all_stmts.push_back(stmt);
+ auto* s = ensure_state(stmt);
+ s->mysql = stmt->mysql;
+ return stmt;
+ }
+
+ int mysql_stmt_prepare(MYSQL_STMT* stmt, const char* q, unsigned long len) {
+ if (!stmt) {
+ return 1;
+ }
+ stmt->mysql = reinterpret_cast<MYSQL*>(0x1);
+ auto* s = ensure_state(stmt);
+
+ const uintptr_t pq = reinterpret_cast<uintptr_t>(q);
+ const bool ptr_ok = (q != nullptr) && (pq > 0x10000);
+ const bool len_ok = (len > 0);
+ if (ptr_ok && len_ok) {
+ size_t copy_len = std::min<size_t>(len, 4096);
+ s->sql.assign(q, copy_len);
+ } else {
+ s->sql = "SELECT version, minor FROM schema_version";
+ }
+
+ if (is_like(s->sql, "schema_version") || is_like(s->sql, "select version") || is_like(s->sql, "get_version")) {
+ fill_version_stmt(s);
+ return 0;
+ }
+
+ unsigned int cols = infer_field_count_from_sql(s->sql);
+ if (cols == 0) {
+ cols = g_fdp->ConsumeIntegralInRange<unsigned int>(1, 8);
+ }
+ s->field_count = cols;
+
+ if (g_fdp->ConsumeBool()) {
+ fill_fuzz_rows(s, cols);
+ } else {
+ fill_no_rows(s);
+ }
+ return 0;
+ }
+
+ my_bool mysql_stmt_close(MYSQL_STMT* stmt) {
+ if (stmt) {
+ auto it = g_stmt_state.find(stmt);
+ if (it != g_stmt_state.end()) {
+ delete it->second;
+ g_stmt_state.erase(it);
+ if (g_stmt_state.empty()) {
+ g_stmt_state.rehash(0);
+ }
+ }
+ auto it2 = std::find(g_all_stmts.begin(), g_all_stmts.end(), stmt);
+ if (it2 != g_all_stmts.end()) {
+ g_all_stmts.erase(it2);
+ }
+ free(stmt);
+ }
+ return 0;
+ }
+
+ my_bool mysql_stmt_bind_result(MYSQL_STMT* stmt, MYSQL_BIND* bnd) {
+ auto* s = ensure_state(stmt);
+ s->res_binds = bnd;
+ s->res_binds_count = bnd ? static_cast<size_t>(mysql_stmt_field_count(stmt)) : 0;
+ return 0;
+ }
+
+ int mysql_stmt_execute(MYSQL_STMT*) {
+ return 0;
+ }
+
+ int mysql_stmt_store_result(MYSQL_STMT*) {
+ return 0;
+ }
+
+ my_bool mysql_stmt_free_result(MYSQL_STMT* stmt) {
+ auto s = SS(stmt);
+ if (s) {
+ s->fetch_index = 0;
+ }
+ if (stmt) {
+ stmt->mysql = reinterpret_cast<MYSQL*>(0x1);
+ }
+ return 0;
+ }
+
+ my_ulonglong mysql_stmt_affected_rows(MYSQL_STMT*) {
+ return 0ULL;
+ }
+
+ my_bool mysql_stmt_reset(MYSQL_STMT* stmt) {
+ auto s = SS(stmt);
+ if (s) {
+ s->fetch_index = 0;
+ }
+ if (stmt) {
+ stmt->mysql = reinterpret_cast<MYSQL*>(0x1);
+ }
+ return 0;
+ }
+
+ unsigned int mysql_stmt_errno(MYSQL_STMT*) {
+ return 0;
+ }
+
+ const char* mysql_stmt_error(MYSQL_STMT*) {
+ g_stmt_error = g_fdp ? g_fdp->ConsumeRandomLengthString(32) : std::string();
+ return g_stmt_error.c_str();
+ }
+
+ int mysql_options(MYSQL*, enum mysql_option, const void*) {
+ return 0;
+ }
+
+ my_bool mysql_autocommit(MYSQL*, my_bool) {
+ return 0;
+ }
+
+ my_bool mysql_commit(MYSQL*) {
+ return 0;
+ }
+
+ my_bool mysql_rollback(MYSQL*) {
+ return 0;
+ }
+
+ int mysql_query(MYSQL*, const char*) {
+ return 0;
+ }
+
+ my_bool mysql_stmt_bind_param(MYSQL_STMT*, MYSQL_BIND*) {
+ return 0;
+ }
+
+ unsigned int mysql_stmt_field_count(MYSQL_STMT* stmt) {
+ auto s = SS(stmt);
+ return s ? s->field_count : 0u;
+ }
+
+ MYSQL_RES* mysql_stmt_result_metadata(MYSQL_STMT* stmt) {
+ auto s = SS(stmt);
+ if (!s) {
+ return reinterpret_cast<MYSQL_RES*>(0x1);
+ }
+ if (s->is_version_stmt) {
+ return reinterpret_cast<MYSQL_RES*>(0x1);
+ }
+ return nullptr;
+ }
+
+ my_ulonglong mysql_insert_id(MYSQL*) {
+ return g_fdp ? g_fdp->ConsumeIntegral<my_ulonglong>() : 0ULL;
+ }
+
+ const char* mysql_get_ssl_cipher(MYSQL*) {
+ if (g_fdp && g_fdp->ConsumeBool()) {
+ g_tls_cipher = g_fdp->ConsumeRandomLengthString(64);
+ return g_tls_cipher.c_str();
+ }
+ return "TLS_FAKE_CIPHER_WITH_FAKE_SHA256";
+ }
+
+ int mysql_stmt_fetch(MYSQL_STMT* stmt) {
+ auto s = SS(stmt);
+
+ if (!s || !s->has_rows){
+ return MYSQL_NO_DATA;
+ }
+ if (s->fetch_index >= s->rows.size()) {
+ return MYSQL_NO_DATA;
+ }
+ if (!s->res_binds) {
+ return MYSQL_NO_DATA;
+ }
+
+ const auto& row = s->rows[s->fetch_index++];
+ size_t cols = row.size();
+ if (s->field_count && cols > s->field_count) {
+ cols = s->field_count;
+ }
+ if (cols > s->res_binds_count) {
+ cols = s->res_binds_count;
+ }
+
+ for (size_t i = 0; i < cols; ++i) {
+ const auto& cell = row[i];
+ MYSQL_BIND& b = s->res_binds[i];
+ if (!b.buffer) {
+ continue;
+ }
+ switch (cell.kind) {
+ case MockResRow::CK_UINT32: {
+ uint32_t v = cell.u32;
+ if (b.buffer_length == 0 || b.buffer_length >= sizeof(v)) {
+ memcpy(b.buffer, &v, sizeof(v));
+ if (b.length) *b.length = sizeof(v);
+ }
+ break;
+ }
+ case MockResRow::CK_UINT64: {
+ uint64_t v = cell.u64;
+ if (b.buffer_length == 0 || b.buffer_length >= sizeof(v)) {
+ memcpy(b.buffer, &v, sizeof(v));
+ if (b.length) *b.length = sizeof(v);
+ }
+ break;
+ }
+ case MockResRow::CK_UINT8: {
+ uint8_t v = cell.u8;
+ if (b.buffer_length == 0 || b.buffer_length >= sizeof(v)) {
+ memcpy(b.buffer, &v, sizeof(v));
+ if (b.length) *b.length = sizeof(v);
+ }
+ break;
+ }
+ case MockResRow::CK_STRING: {
+ if (b.buffer_length > 0) {
+ size_t n = std::min<size_t>(b.buffer_length - 1, cell.s.size());
+ memcpy(b.buffer, cell.s.data(), n);
+ reinterpret_cast<char*>(b.buffer)[n] = '\0';
+ if (b.length) *b.length = n;
+ }
+ break;
+ }
+ case MockResRow::CK_BLOB: {
+ if (b.buffer_length > 0) {
+ size_t n = std::min<size_t>(b.buffer_length, cell.blob.size());
+ memcpy(b.buffer, cell.blob.data(), n);
+ if (b.length) *b.length = n;
+ }
+ break;
+ }
+ }
+ }
+ return 0;
+ }
+}
--- /dev/null
+// Copyright (C) 2025 Ada Logcis Ltd.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <cstddef>
+#include <cstdint>
+
+struct pg_conn {};
+using PGconn = pg_conn;
+
+struct pg_result {
+ int status;
+ int ntuples;
+ int nfields;
+ char** field_names;
+ char** values;
+};
+using PGresult = pg_result;
+
+enum {
+ PGRES_EMPTY_QUERY = 0, PGRES_COMMAND_OK = 1,
+ PGRES_TUPLES_OK = 2, PGRES_FATAL_ERROR = 7
+};
+
+static thread_local FuzzedDataProvider* g_fdp = nullptr;
+
+extern "C" void pgmock_load_bytes(const uint8_t* data, size_t size) {
+ delete g_fdp;
+ g_fdp = new FuzzedDataProvider(data, size);
+}
+
+// Helper to duplcate string and drop const for return
+static char* dupstr(const char* s) {
+ if (!s) {
+ return nullptr;
+ }
+
+ size_t n = std::strlen(s) + 1;
+ char* p = static_cast<char*>(std::malloc(n));
+ if (p) {
+ std::memcpy(p, s, n);
+ }
+
+ return p;
+}
+
+// Helper to make a fuzz row result
+static PGresult* make_fuzz_result() {
+ int nfields = g_fdp->ConsumeIntegralInRange<int>(2, 8);
+
+ PGresult* r = static_cast<PGresult*>(std::calloc(1, sizeof(PGresult)));
+ r->status = PGRES_TUPLES_OK;
+ r->ntuples = 1;
+ r->nfields = nfields;
+
+ r->field_names = static_cast<char**>(std::calloc(nfields, sizeof(char*)));
+ r->values = static_cast<char**>(std::calloc((size_t)nfields, sizeof(char*)));
+
+ for (int i = 0; i < nfields; ++i) {
+ r->field_names[i] = dupstr(g_fdp->ConsumeRandomLengthString(32).c_str());
+
+ // Provide random type of data return
+ int kind = g_fdp->ConsumeIntegralInRange<int>(0, 3);
+ if (kind == 0) {
+ unsigned v = g_fdp->ConsumeIntegralInRange<unsigned>(0, 9999u);
+ char buf[32];
+ std::snprintf(buf, sizeof(buf), "%u", v);
+ r->values[i] = dupstr(buf);
+ } else if (kind == 1) {
+ r->values[i] = dupstr(g_fdp->ConsumeBool() ? "1" : "0");
+ } else if (kind == 2) {
+ // IPv4 loopback as text
+ r->values[i] = dupstr("127.0.0.1");
+ } else {
+ // IPv6 loopback as text
+ r->values[i] = dupstr("::1");
+ }
+ }
+ return r;
+}
+
+// Helper to make fixed version query for kea
+static PGresult* make_version_result() {
+ PGresult* r = static_cast<PGresult*>(std::calloc(1, sizeof(PGresult)));
+ r->status = PGRES_TUPLES_OK;
+ r->ntuples = 1;
+ r->nfields = 2;
+
+ r->field_names = static_cast<char**>(std::calloc(2, sizeof(char*)));
+ r->field_names[0] = dupstr("version");
+ r->field_names[1] = dupstr("minor");
+
+ r->values = static_cast<char**>(std::calloc(2, sizeof(char*)));
+ r->values[0] = dupstr("31");
+ r->values[1] = dupstr("0");
+
+ return r;
+}
+
+// Helper to make success reply to update or delete query
+static PGresult* make_command_ok_result() {
+ PGresult* r = static_cast<PGresult*>(std::calloc(1, sizeof(PGresult)));
+ r->status = PGRES_COMMAND_OK;
+ r->ntuples = 0;
+ r->nfields = 0;
+ r->field_names = nullptr;
+ r->values = nullptr;
+
+ return r;
+}
+
+// List of mock functions
+extern "C" {
+ PGconn* PQconnectdb(const char*) {
+ return static_cast<PGconn*>(std::calloc(1, sizeof(PGconn)));
+ }
+
+ int PQstatus(const PGconn* c) {
+ return c ? 0 : 1;
+ }
+
+ void PQfinish(PGconn* c) {
+ std::free(c);
+ }
+
+ char* PQerrorMessage(const PGconn*) {
+ return const_cast<char*>("");
+ }
+
+ PGresult* PQexec(PGconn* , const char* query) {
+ if (g_fdp->ConsumeBool()) {
+ return make_version_result();
+ }
+ return make_fuzz_result();
+ }
+
+ PGresult* PQexecParams(PGconn*, const char* cmd, int, const void*,
+ const char* const*, const int*, const int*, int) {
+ if (g_fdp->ConsumeBool()) {
+ return make_version_result();
+ }
+ return make_fuzz_result();
+ }
+
+ PGresult* PQprepare(PGconn*, const char*, const char*, int, const unsigned int*) {
+ return make_command_ok_result();
+ }
+
+ PGresult* PQexecPrepared(PGconn*, const char* name, int, const char* const*,
+ const int*, const int*, int) {
+ if (g_fdp->ConsumeBool()) {
+ return make_version_result();
+ }
+ return make_fuzz_result();
+ }
+
+ int PQresultStatus(const PGresult* r) {
+ return r ? r->status : PGRES_FATAL_ERROR;
+ }
+
+ int PQntuples(const PGresult* r) {
+ return r ? r->ntuples : 0;
+ }
+
+ int PQnfields(const PGresult* r) {
+ return r ? r->nfields : 0;
+ }
+
+ char* PQfname(const PGresult* r, int i) {
+ if (r && i >= 0 && i < r->nfields) {
+ return r->field_names[i];
+ }
+ return const_cast<char*>("");
+ }
+
+ char* PQgetvalue(const PGresult* r, int row, int col) {
+ if (r && row == 0 && col >= 0 && col < r->nfields) {
+ return r->values[col];
+ }
+ return const_cast<char*>("");
+ }
+
+ int PQgetlength(const PGresult* r, int, int col) {
+ if (r && col >= 0 && col < r->nfields && r->values && r->values[col]) {
+ return static_cast<int>(std::strlen(r->values[col]));
+ }
+ return 0;
+ }
+
+ int PQgetisnull(const PGresult*, int, int) {
+ return 0;
+ }
+
+ int PQbinaryTuples(const PGresult*) {
+ return 0;
+ }
+
+ int PQfformat(const PGresult*, int) {
+ return 0;
+ }
+
+ int PQfsize(const PGresult*, int) {
+ return -1;
+ }
+
+ void PQclear(PGresult* r) {
+ if (!r) {
+ return;
+ }
+
+ if (r->field_names) {
+ for (int i=0; i<r->nfields; ++i) {
+ if (r->field_names[i]) {
+ std::free(r->field_names[i]);
+ }
+ }
+ std::free(r->field_names);
+ r->field_names = nullptr;
+ }
+
+ const int count = (r->ntuples > 0 && r->nfields > 0)
+ ? r->ntuples * r->nfields : 0;
+ if (r->values) {
+ for (int i = 0; i < count; ++i) {
+ if (r->values[i]) {
+ std::free(r->values[i]);
+ }
+ }
+ std::free(r->values);
+ r->values = nullptr;
+ }
+
+ std::free(r);
+ }
+
+ const char* PQcmdTuples(const PGresult*) {
+ return "0";
+ }
+
+ char* PQresultErrorField(const PGresult*, int) {
+ return const_cast<char*>("");
+ }
+
+ unsigned char* PQunescapeBytea(const unsigned char*, size_t* to_length) {
+ if (to_length) {
+ *to_length = 0;
+ }
+
+ unsigned char* p = static_cast<unsigned char*>(std::malloc(1));
+ if (p) {
+ p[0] = 0;
+ }
+
+ return p;
+ }
+
+ void PQfreemem(void* p) {
+ std::free(p);
+ }
+}