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
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,47 @@ make
## Usage

For comprehensive help, use `dooked --help`

### Runtime regex checks

Use `--checks <file>` or `--check-config <file>` to load custom alert checks
from a JSON configuration file while dooked is running:

```
dooked -i domains.txt -o results --checks checks.json
```

The config may be either a JSON array or an object with a `checks` array. Each
check requires `field`, `regex`, and `alert`. `pattern` is accepted as an alias
for `regex`, `message` is accepted as an alias for `alert`, and checks are
case-sensitive unless `ignore_case: true` or `case_sensitive: false` is set.

```json
{
"checks": [
{
"field": "domain",
"regex": "(dev|test)",
"alert": "domain contains an environment marker",
"ignore_case": true
},
{
"field": "content",
"regex": "Copyright 2020",
"alert": "outdated copyright banner"
},
{
"field": "rdata",
"regex": "v=spf1",
"alert": "SPF TXT record found"
}
]
}
```

Supported fields are domain aliases (`domain`, `domain_name`), DNS record fields
(`type`, `info`, `rdata`, `ttl`), HTTP fields (`content_length`, `http_code`,
`http_status`, `code_string`), and response body aliases (`body`,
`response_body`, `page_content`, `content`). Response bodies are kept only in
memory for matching, capped at the first 64 KiB, and are not written to the JSON
result file.
11 changes: 11 additions & 0 deletions dooked/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ message("${PROJECT_NAME}: CURR BIN DIR: ${CMAKE_CURRENT_BINARY_DIR}")

# Source Files
set(SRC_FILES
./source/checks/regex_checks.cpp
./source/dns/dns.cpp
./source/dns/dns_resolver.cpp
./source/http/resolver.cpp
Expand All @@ -80,6 +81,7 @@ source_group("Sources" FILES ${SRC_FILES})

# Header Files
set(HEADERS_FILES
./include/checks/regex_checks.hpp
./include/dns/dns.hpp
./include/dns/dns_resolver.hpp
./include/http/resolver.hpp
Expand All @@ -103,6 +105,15 @@ add_executable(${PROJECT_NAME}
${SRC_FILES} ${HEADERS_FILES}
)

option(DOOKED_BUILD_TESTS "Build dooked test targets" OFF)
if(DOOKED_BUILD_TESTS)
add_executable(regex_checks_test
./test/regex_checks_test.cpp
./source/checks/regex_checks.cpp
./source/utils/constants.cpp
)
endif()

if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O3")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
Expand Down
21 changes: 21 additions & 0 deletions dooked/examples/regex-checks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"checks": [
{
"field": "domain",
"regex": "(dev|test)",
"alert": "domain contains an environment marker",
"ignore_case": true
},
{
"field": "content",
"regex": "Copyright 2020",
"alert": "outdated copyright banner"
},
{
"field": "rdata",
"regex": "v=spf1",
"alert": "SPF TXT record found",
"ignore_case": true
}
]
}
44 changes: 44 additions & 0 deletions dooked/include/checks/regex_checks.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

#include "utils/containers.hpp"
#include "utils/probe_result.hpp"
#include <iosfwd>
#include <optional>
#include <regex>
#include <string>
#include <vector>

namespace dooked {

enum class regex_check_field_e {
domain,
type,
rdata,
ttl,
content_length,
http_code,
code_string,
body
};

struct regex_check_t {
regex_check_field_e field{};
std::string field_name{};
std::string pattern{};
std::string alert{};
bool ignore_case{};
std::regex compiled_pattern{};
};

using regex_check_list_t = std::vector<regex_check_t>;

std::optional<regex_check_list_t>
parse_regex_checks_config(std::istream &input, std::string &error_message);

std::optional<regex_check_list_t>
load_regex_checks(std::string const &filename, std::string &error_message);

void run_regex_checks(map_container_t<probe_result_t> const &result_map,
regex_check_list_t const &checks);

} // namespace dooked
3 changes: 3 additions & 0 deletions dooked/include/cli_preprocessor.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "checks/regex_checks.hpp"
#include "dns/dns_resolver.hpp"
#include "utils/io_utils.hpp"
#include <thread>
Expand All @@ -19,6 +20,7 @@ struct cli_args_t {
std::string resolver_filename{};
std::string output_filename{};
std::string input_filename{};
std::string check_config_filename{};

int file_type{};
int post_http_request{};
Expand All @@ -34,6 +36,7 @@ struct runtime_args_t {
std::unique_ptr<std::ofstream> output_file{};
std::string output_filename{};
http_process_e http_request_time_{};
regex_check_list_t regex_checks{};
int thread_count{};
int content_length{-1};
};
Expand Down
13 changes: 9 additions & 4 deletions dooked/include/utils/containers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <mutex>
#include <optional>
#include <queue>
#include <string>
#include <vector>

namespace dooked {
Expand All @@ -31,6 +32,7 @@ template <typename T> class circular_queue_t {
struct http_response_t {
int content_length_{};
int http_status_{};
std::string body_{};
};

template <typename ValueType> struct http_dns_response_t {
Expand All @@ -52,9 +54,11 @@ template <typename ValueType> class map_container_t {
}

void insert_impl(std::string const &name, int const len,
int const http_status) {
int const http_status,
std::string const &body) {
map_[name].http_result_.content_length_ = len;
map_[name].http_result_.http_status_ = http_status;
map_[name].http_result_.body_ = body;
}

public:
Expand All @@ -74,12 +78,13 @@ template <typename ValueType> class map_container_t {
append_impl(key, value);
}

void insert(std::string const &name, int const len, int const http_status) {
void insert(std::string const &name, int const len, int const http_status,
std::string const &body = {}) {
if (!opt_mutex_) {
return insert_impl(name, len, http_status);
return insert_impl(name, len, http_status, body);
}
std::lock_guard<std::mutex> lock_g{*opt_mutex_};
insert_impl(name, len, http_status);
insert_impl(name, len, http_status, body);
}
// only used by main thread, after all "computations" has been
// done. There's no need for locks here.
Expand Down
1 change: 1 addition & 0 deletions dooked/include/utils/exceptions.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <stdexcept>
#include <string>

namespace dooked {

Expand Down
Loading