Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,25 @@ make
## Usage

For comprehensive help, use `dooked --help`

### DNS history tracking

When a previous JSON scan is passed back as input, dooked now carries DNS
record history forward:

- `first-seen`: first scan time for a domain/type/value tuple
- `last-seen`: most recent scan time where that tuple was observed
- `seen`: number of scans where that tuple was observed
- `currently_seen`: whether the tuple appeared in the latest scan

This keeps rotating DNS records in the output after they disappear from the
latest scan, so load-balanced targets can be reviewed without losing older
addresses immediately.

Additional comparison flags:

- `--fs`: report records first seen in the current scan
- `--ls N`: report records missing from the current scan that have not been
seen in at least `N` days
- `--lsd MM/DD/YYYY`: report missing records last seen before a US-formatted
date. `MM/DD/YYYY HH:MM:SS` is also accepted.
9 changes: 9 additions & 0 deletions dooked/include/cli_preprocessor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "dns/dns_resolver.hpp"
#include "utils/io_utils.hpp"
#include <ctime>
#include <thread>

// maximum sockets to open regardless of the number of threads
Expand All @@ -24,7 +25,10 @@ struct cli_args_t {
int post_http_request{};
int thread_count{};
int content_length{-1};
int last_seen_days{-1};
bool include_date{false};
bool show_first_seen{false};
std::string last_seen_date{};
};

struct runtime_args_t {
Expand All @@ -36,6 +40,11 @@ struct runtime_args_t {
http_process_e http_request_time_{};
int thread_count{};
int content_length{-1};
int last_seen_days{-1};
bool show_first_seen{false};
std::string last_seen_date{};
std::string scan_time{};
std::time_t scan_time_epoch{};
};

void run_program(cli_args_t const &cli_args);
Expand Down
37 changes: 37 additions & 0 deletions dooked/include/utils/io_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,46 @@ void trim(std::string &);
struct json_data_t {
std::string domain_name{};
std::string rdata{};
std::string first_seen{};
std::string last_seen{};
int ttl{};
int http_code{};
int content_length{};
int seen{};
bool currently_seen{true};
dns_record_type_e type{};

static json_data_t serialize(std::string const &d, int const len,
int const http_code,
json::object_t &json_object) {
auto const string_value = [&json_object](char const *first_key,
char const *second_key) {
auto first_iter = json_object.find(first_key);
if (first_iter != json_object.end() && first_iter->second.is_string()) {
return first_iter->second.get<json::string_t>();
}
auto second_iter = json_object.find(second_key);
if (second_iter != json_object.end() && second_iter->second.is_string()) {
return second_iter->second.get<json::string_t>();
}
return json::string_t{};
};
auto const int_value = [&json_object](char const *key, int const fallback) {
auto iter = json_object.find(key);
if (iter != json_object.end() && iter->second.is_number_integer()) {
return static_cast<int>(iter->second.get<json::number_integer_t>());
}
return fallback;
};
auto const bool_value = [&json_object](char const *key,
bool const fallback) {
auto iter = json_object.find(key);
if (iter != json_object.end() && iter->second.is_boolean()) {
return iter->second.get<json::boolean_t>();
}
return fallback;
};

json_data_t data{};
data.domain_name = d;
data.type =
Expand All @@ -42,6 +74,10 @@ struct json_data_t {
data.ttl = json_object["ttl"].get<json::number_integer_t>();
data.content_length = len;
data.http_code = http_code;
data.first_seen = string_value("first-seen", "first_seen");
data.last_seen = string_value("last-seen", "last_seen");
data.seen = int_value("seen", data.last_seen.empty() ? 0 : 1);
data.currently_seen = bool_value("currently_seen", true);
return data;
}
};
Expand Down Expand Up @@ -82,6 +118,7 @@ void write_json_result_impl(map_container_t<DnsType> const &result_map,
json::object_t res_object;

res_object["program"] = "dooked";
res_object["scanned_at"] = rt_args.scan_time;
res_object["result"] = std::move(list);
(*rt_args.output_file) << json(res_object).dump(2) << "\n";
rt_args.output_file->close();
Expand Down
4 changes: 4 additions & 0 deletions dooked/include/utils/probe_result.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ struct probe_result_t {
std::string rdata{};
dns_record_type_e type{}; // RR TYPE (2 octets)
std::uint32_t ttl{}; // time to live(4 octets)
std::string first_seen{};
std::string last_seen{};
int seen{};
bool currently_seen{true};

friend bool operator==(probe_result_t const &a, probe_result_t const &b) {
return case_insensitive_compare(a.rdata, b.rdata) && (a.type == b.type);
Expand Down
1 change: 1 addition & 0 deletions dooked/include/utils/random_utils.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <array>
#include <cstdint>
#include <string>

namespace dooked {
Expand Down
Loading