summaryrefslogtreecommitdiffstats
path: root/src/output/dnssim.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/output/dnssim.lua')
-rw-r--r--src/output/dnssim.lua433
1 files changed, 0 insertions, 433 deletions
diff --git a/src/output/dnssim.lua b/src/output/dnssim.lua
deleted file mode 100644
index 25193c4..0000000
--- a/src/output/dnssim.lua
+++ /dev/null
@@ -1,433 +0,0 @@
--- Copyright (c) 2018-2021, CZ.NIC, z.s.p.o.
--- All rights reserved.
---
--- This file is part of dnsjit.
---
--- dnsjit is free software: you can redistribute it and/or modify
--- it under the terms of the GNU General Public License as published by
--- the Free Software Foundation, either version 3 of the License, or
--- (at your option) any later version.
---
--- dnsjit is distributed in the hope that it will be useful,
--- but WITHOUT ANY WARRANTY; without even the implied warranty of
--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--- GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License
--- along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
-
--- dnsjit.output.dnssim
--- Simulate independent DNS clients over various transports
--- output = require("dnsjit.output.dnssim").new()
--- .SS Usage
--- output:udp()
--- output:target("::1", 53)
--- recv, rctx = output:receive()
--- -- pass in objects using recv(rctx, obj)
--- -- repeatedly call output:run_nowait() until it returns 0
--- .SS DNS-over-TLS example configuration
--- output:tls("NORMAL:-VERS-ALL:+VERS-TLS1.3") -- enforce TLS 1.3
--- .SS DNS-over-HTTPS/2 example configuration
--- output:https2({ method = "POST", uri_path = "/doh" })
---
--- Output module for simulating traffic from huge number of independent,
--- individual DNS clients.
--- Uses libuv for asynchronous communication.
--- There may only be a single DnsSim in a thread.
--- Use
--- .I dnsjit.core.thread
--- to have multiple DnsSim instances.
--- .P
--- With proper use of this component, it is possible to simulate hundreds of
--- thousands of clients when using a high-performance server.
--- This also applies for state-full transports.
--- The complete set-up is quite complex and requires other components.
--- See DNS Shotgun
--- .RI ( https://gitlab.nic.cz/knot/shotgun )
--- for dnsjit scripts ready for use for high-performance
--- benchmarking.
-module(...,package.seeall)
-
-require("dnsjit.output.dnssim_h")
-local bit = require("bit")
-local object = require("dnsjit.core.objects")
-local ffi = require("ffi")
-local C = ffi.C
-
-local DnsSim = {}
-
-local _DNSSIM_VERSION = 20210129
-local _DNSSIM_JSON_VERSION = 20200527
-
--- Create a new DnsSim output for up to max_clients.
-function DnsSim.new(max_clients)
- local self = {
- obj = C.output_dnssim_new(max_clients),
- max_clients = max_clients,
- }
- ffi.gc(self.obj, C.output_dnssim_free)
- return setmetatable(self, { __index = DnsSim })
-end
-
-local function _check_version(version, req_version)
- if req_version == nil then
- return version
- end
- local min_version = tonumber(req_version)
- if min_version == nil then
- C.output_dnssim_log():fatal("invalid version number: "..req_version)
- return nil
- end
- if version >= min_version then
- return version
- end
- return nil
-end
-
--- Check that version of dnssim is at minimum the one passed as
--- .B req_version
--- and return the actual version number.
--- Return nil if the condition is not met.
---
--- If no
--- .B req_version
--- is specified no check is done and only the version number is returned.
-function DnsSim.check_version(req_version)
- return _check_version(_DNSSIM_VERSION, req_version)
-end
-
--- Check that version of dnssim's JSON data format is at minimum the one passed as
--- .B req_version
--- and return the actual version number.
--- Return nil if the condition is not met.
---
--- If no
--- .B req_version
--- is specified no check is done and only the version number is returned.
-function DnsSim.check_json_version(req_version)
- return _check_version(_DNSSIM_JSON_VERSION, req_version)
-end
-
--- Return the Log object to control logging of this instance or module.
--- Optionally, set the instance's log name.
--- Unique name should be used for each instance.
-function DnsSim:log(name)
- if self == nil then
- return C.output_dnssim_log()
- end
- if name ~= nil then
- C.output_dnssim_log_name(self.obj, name)
- end
- return self.obj._log
-end
-
--- Set the target IPv4/IPv6 address where queries will be sent to.
-function DnsSim:target(ip, port)
- local nport = tonumber(port)
- if nport == nil then
- self.obj._log:fatal("invalid port: "..port)
- return -1
- end
- if nport <= 0 or nport > 65535 then
- self.obj._log:fatal("invalid port number: "..nport)
- return -1
- end
- return C.output_dnssim_target(self.obj, ip, nport)
-end
-
--- Specify source IPv4/IPv6 address for sending queries.
--- Can be set multiple times.
--- Addresses are selected round-robin when sending.
-function DnsSim:bind(ip)
- return C.output_dnssim_bind(self.obj, ip)
-end
-
--- Set the preferred transport to UDP.
---
--- When the optional argument
--- .B tcp_fallback
--- is set to true, individual queries are re-tried over TCP when TC bit is set in the answer.
--- Defaults to
--- .B false
--- (aka only UDP is used).
-function DnsSim:udp(tcp_fallback)
- if tcp_fallback == true then
- C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_UDP)
- else
- C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_UDP_ONLY)
- end
-end
-
--- Set the transport to TCP.
-function DnsSim:tcp()
- C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_TCP)
-end
-
--- Set the transport to TLS.
---
--- The optional argument
--- .B tls_priority
--- is a GnuTLS priority string, which can be used to select TLS versions, cipher suites etc.
--- For example:
---
--- .RB "- """ NORMAL:%NO_TICKETS """"
--- will use defaults without TLS session resumption.
---
--- .RB "- """ SECURE128:-VERS-ALL:+VERS-TLS1.3 """"
--- will use only TLS 1.3 with 128-bit secure ciphers.
---
--- Refer to:
--- .I https://gnutls.org/manual/html_node/Priority-Strings.html
-function DnsSim:tls(tls_priority)
- if tls_priority ~= nil then
- C.output_dnssim_tls_priority(self.obj, tls_priority)
- end
- C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_TLS)
-end
-
--- Set the transport to HTTP/2 over TLS.
---
--- .B http2_options
--- is a lua table which supports the following keys:
---
--- .B method:
--- .B GET
--- (default)
--- or
--- .B POST
---
--- .B uri_path:
--- where queries will be sent.
--- Defaults to
--- .B /dns-query
---
--- .B zero_out_msgid:
--- when
--- .B true
--- (default), query ID is always set to 0
---
--- See tls() method for
--- .B tls_priority
--- documentation.
-function DnsSim:https2(http2_options, tls_priority)
- if tls_priority ~= nil then
- C.output_dnssim_tls_priority(self.obj, tls_priority)
- end
-
- uri_path = "/dns-query"
- zero_out_msgid = true
- method = "GET"
-
- if http2_options ~= nil then
- if type(http2_options) ~= "table" then
- self.obj._log:fatal("http2_options must be a table")
- else
- if http2_options["uri_path"] ~= nil then
- uri_path = http2_options["uri_path"]
- end
- if http2_options["zero_out_msgid"] ~= nil and http2_options["zero_out_msgid"] ~= true then
- zero_out_msgid = false
- end
- if http2_options["method"] ~= nil then
- method = http2_options["method"]
- end
- end
- end
-
- C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_HTTPS2)
- C.output_dnssim_h2_uri_path(self.obj, uri_path)
- C.output_dnssim_h2_method(self.obj, method)
- C.output_dnssim_h2_zero_out_msgid(self.obj, zero_out_msgid)
-end
-
--- Set timeout for the individual requests in seconds (default 2s).
---
--- .BR Beware :
--- increasing this value while the target resolver isn't very responsive
--- (cold cache, heavy load) may degrade DnsSim's performance and skew
--- the results.
-function DnsSim:timeout(seconds)
- if seconds == nil then
- seconds = 2
- end
- timeout_ms = math.floor(seconds * 1000)
- C.output_dnssim_timeout_ms(self.obj, timeout_ms)
-end
-
--- Set TCP connection idle timeout for connection reuse according to RFC7766,
--- Section 6.2.3 (defaults to 10s).
--- When set to zero, connections are closed immediately after there are no
--- more pending queries.
-function DnsSim:idle_timeout(seconds)
- if seconds == nil then
- seconds = 10
- end
- self.obj.idle_timeout_ms = math.floor(seconds * 1000)
-end
-
--- Set TCP connection handshake timeout (defaults to 5s).
--- During heavy load, the server may no longer accept new connections.
--- This parameter ensures such connection attempts are aborted after the
--- timeout expires.
-function DnsSim:handshake_timeout(seconds)
- if seconds == nil then
- seconds = 5
- end
- self.obj.handshake_timeout_ms = math.floor(seconds * 1000)
-end
-
--- Run the libuv loop once without blocking when there is no I/O.
--- This should be called repeatedly until 0 is returned and no more data
--- is expected to be received by DnsSim.
-function DnsSim:run_nowait()
- return C.output_dnssim_run_nowait(self.obj)
-end
-
--- Set this to true if DnsSim should free the memory of passed-in objects
--- (useful when using
--- .I dnsjit.filter.copy
--- to pass objects from different thread).
-function DnsSim:free_after_use(free_after_use)
- self.obj.free_after_use = free_after_use
-end
-
--- Number of input packets discarded due to various reasons.
--- To investigate causes, run with increased logging level.
-function DnsSim:discarded()
- return tonumber(self.obj.discarded)
-end
-
--- Number of valid requests (input packets) processed.
-function DnsSim:requests()
- return tonumber(self.obj.stats_sum.requests)
-end
-
--- Number of requests that received an answer
-function DnsSim:answers()
- return tonumber(self.obj.stats_sum.answers)
-end
-
--- Number of requests that received a NOERROR response
-function DnsSim:noerror()
- return tonumber(self.obj.stats_sum.rcode_noerror)
-end
-
--- Configure statistics to be collected every N seconds.
-function DnsSim:stats_collect(seconds)
- if seconds == nil then
- self.obj._log:fatal("number of seconds must be set for stats_collect()")
- end
- interval_ms = math.floor(seconds * 1000)
- C.output_dnssim_stats_collect(self.obj, interval_ms)
-end
-
--- Stop the collection of statistics.
-function DnsSim:stats_finish()
- C.output_dnssim_stats_finish(self.obj)
-end
-
--- Export the results to a JSON file.
-function DnsSim:export(filename)
- local file = io.open(filename, "w")
- if file == nil then
- self.obj._log:fatal("export failed: no filename")
- return
- end
-
- local function write_stats(file, stats)
- file:write(
- "{ ",
- '"since_ms":', tonumber(stats.since_ms), ',',
- '"until_ms":', tonumber(stats.until_ms), ',',
- '"requests":', tonumber(stats.requests), ',',
- '"ongoing":', tonumber(stats.ongoing), ',',
- '"answers":', tonumber(stats.answers), ',',
- '"conn_active":', tonumber(stats.conn_active), ',',
- '"conn_handshakes":', tonumber(stats.conn_handshakes), ',',
- '"conn_resumed":', tonumber(stats.conn_resumed), ',',
- '"conn_handshakes_failed":', tonumber(stats.conn_handshakes_failed), ',',
- '"rcode_noerror":', tonumber(stats.rcode_noerror), ',',
- '"rcode_formerr":', tonumber(stats.rcode_formerr), ',',
- '"rcode_servfail":', tonumber(stats.rcode_servfail), ',',
- '"rcode_nxdomain":', tonumber(stats.rcode_nxdomain), ',',
- '"rcode_notimp":', tonumber(stats.rcode_notimp), ',',
- '"rcode_refused":', tonumber(stats.rcode_refused), ',',
- '"rcode_yxdomain":', tonumber(stats.rcode_yxdomain), ',',
- '"rcode_yxrrset":', tonumber(stats.rcode_yxrrset), ',',
- '"rcode_nxrrset":', tonumber(stats.rcode_nxrrset), ',',
- '"rcode_notauth":', tonumber(stats.rcode_notauth), ',',
- '"rcode_notzone":', tonumber(stats.rcode_notzone), ',',
- '"rcode_badvers":', tonumber(stats.rcode_badvers), ',',
- '"rcode_badkey":', tonumber(stats.rcode_badkey), ',',
- '"rcode_badtime":', tonumber(stats.rcode_badtime), ',',
- '"rcode_badmode":', tonumber(stats.rcode_badmode), ',',
- '"rcode_badname":', tonumber(stats.rcode_badname), ',',
- '"rcode_badalg":', tonumber(stats.rcode_badalg), ',',
- '"rcode_badtrunc":', tonumber(stats.rcode_badtrunc), ',',
- '"rcode_badcookie":', tonumber(stats.rcode_badcookie), ',',
- '"rcode_other":', tonumber(stats.rcode_other), ',',
- '"latency":[')
- file:write(tonumber(stats.latency[0]))
- for i=1,tonumber(self.obj.timeout_ms) do
- file:write(',', tonumber(stats.latency[i]))
- end
- file:write("]}")
- end
-
- file:write(
- "{ ",
- '"version":', _DNSSIM_JSON_VERSION, ',',
- '"merged":false,',
- '"stats_interval_ms":', tonumber(self.obj.stats_interval_ms), ',',
- '"timeout_ms":', tonumber(self.obj.timeout_ms), ',',
- '"idle_timeout_ms":', tonumber(self.obj.idle_timeout_ms), ',',
- '"handshake_timeout_ms":', tonumber(self.obj.handshake_timeout_ms), ',',
- '"discarded":', self:discarded(), ',',
- '"stats_sum":')
- write_stats(file, self.obj.stats_sum)
- file:write(
- ',',
- '"stats_periodic":[')
-
- local stats = self.obj.stats_first
- write_stats(file, stats)
-
- while (stats.next ~= nil) do
- stats = stats.next
- file:write(',')
- write_stats(file, stats)
- end
-
- file:write(']}')
- file:close()
- self.obj._log:notice("results exported to "..filename)
-end
-
--- Return the C function and context for receiving objects.
--- Only
--- .I dnsjit.filter.core.object.ip
--- or
--- .I dnsjit.filter.core.object.ip6
--- objects are supported.
--- The component expects a 32bit integer (in host order) ranging from 0
--- to max_clients written to first 4 bytes of destination IP.
--- See
--- .IR dnsjit.filter.ipsplit .
-function DnsSim:receive()
- local receive = C.output_dnssim_receiver()
- return receive, self.obj
-end
-
--- Deprecated: use udp() instead.
---
--- Set the transport to UDP (without any TCP fallback).
-function DnsSim:udp_only()
- C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_UDP_ONLY)
-end
-
--- dnsjit.filter.copy (3),
--- dnsjit.filter.ipsplit (3),
--- dnsjit.filter.core.object.ip (3),
--- dnsjit.filter.core.object.ip6 (3),
--- https://gitlab.nic.cz/knot/shotgun
-return DnsSim