Skip to content

Commit 8aacd3f

Browse files
Add tests
1 parent 023c3ab commit 8aacd3f

File tree

9 files changed

+430
-88
lines changed

9 files changed

+430
-88
lines changed

config.yml

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
1-
appName: heiDPI
2-
31
logging:
4-
level: ERROR
5-
encoding: utf-8
6-
format: "%(asctime)s %(levelname)s:%(message)s"
7-
datefmt: "%Y-%m-%dT%I:%M:%S"
8-
# filemode: w # a for append, will not override current file
92
# filename: heiDPI.log
103

114
flow_event:
12-
ignore_fields: []
13-
ignore_risks: []
5+
ignore_fields: [
6+
"alias"
7+
]
8+
ignore_risks: [
9+
"5"
10+
]
1411
flow_event_name:
1512
- update
1613
- end
@@ -24,30 +21,26 @@ flow_event:
2421
keys:
2522
- country.names.en
2623
# Additional configurations
27-
- location
24+
# - location
2825
# - city
2926
# - traits
3027
# - postal
31-
# threads: 4
3228

3329
daemon_event:
3430
ignore_fields: []
3531
daemon_event_name:
3632
- init
3733
- status
3834
filename: daemon_event
39-
# threads: 4
4035

4136
packet_event:
4237
ignore_fields: []
4338
packet_event_name:
4439
- packet-flow
4540
filename: packet_event
46-
# threads: 4
4741

4842
error_event:
4943
ignore_fields: []
5044
error_event_name:
5145
- error-flow
5246
filename: error_event
53-
# threads: 4

heidpi-logger/include/Config.hpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@
99
*/
1010
struct LoggingConfig
1111
{
12-
std::string level{"INFO"};
13-
std::string format{"%Y-%m-%dT%H:%M:%S"};
14-
std::string datefmt{"%Y-%m-%dT%H:%M:%S"};
15-
std::string filename{}; // optional log file
12+
std::string filename{};
1613
};
1714

1815
struct EventConfig

heidpi-logger/include/GeoIP.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ class GeoIP
1616

1717
void enrich(const std::string &src_ip, const std::string &dst_ip,
1818
nlohmann::json &out) const;
19+
protected:
20+
virtual nlohmann::json lookup(const std::string &ip) const;
21+
bool loaded{false};
1922

2023
private:
21-
nlohmann::json lookup(const std::string &ip) const;
22-
2324
MMDB_s mmdb{};
24-
bool loaded{false};
2525
std::vector<std::string> keys;
2626
};

heidpi-logger/src/EventProcessor.cpp

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,95 @@
1+
/**
2+
* @file EventProcessor.cpp
3+
* Implementation of event processing logic including timestamping,
4+
* GeoIP enrichment, and output formatting
5+
*/
6+
17
#include "EventProcessor.hpp"
28
#include <fstream>
39
#include <chrono>
410
#include <iomanip>
511
#include <ctime>
612
#include <filesystem>
713

14+
/**
15+
* @class EventProcessor
16+
* Handles event processing pipeline including:
17+
* - Timestamp injection
18+
* - GeoIP enrichment (if enabled)
19+
* - Field/risk filtering
20+
* - Output file writing
21+
*/
822
EventProcessor::EventProcessor(const EventConfig &cfg, const std::string &outDir)
9-
: config(cfg), directory(outDir) {
10-
if (cfg.geoip_enabled && !cfg.geoip_path.empty()) {
23+
: config(cfg), directory(outDir)
24+
{
25+
if (cfg.geoip_enabled && !cfg.geoip_path.empty())
26+
{
1127
geo = std::make_unique<GeoIP>(cfg.geoip_path, cfg.geoip_keys);
12-
} else {
28+
}
29+
else
30+
{
1331
// optional, aber hilfreich zur Diagnose:
1432
Logger::info(std::string("GeoIP disabled for '") + cfg.filename +
1533
"' (enabled=" + (cfg.geoip_enabled ? "true" : "false") +
1634
", path=" + (cfg.geoip_path.empty() ? "<empty>" : cfg.geoip_path) + ")");
1735
}
1836
}
1937

20-
static std::string nowTs() {
38+
/**
39+
* @return Current timestamp in ISO 8601 format (YYYY-MM-DDTHH:MM:SS)
40+
*/
41+
static std::string nowTs()
42+
{
2143
auto now = std::chrono::system_clock::now();
2244
std::time_t tt = std::chrono::system_clock::to_time_t(now);
23-
std::tm tm = *std::localtime(&tt);
24-
char buf[64];
25-
std::strftime(buf, sizeof(buf), "%FT%T", &tm);
26-
return std::string(buf);
45+
std::tm tm = *std::localtime(&tt); // Thread-safe with local variable
46+
std::ostringstream oss;
47+
oss << std::put_time(&tm, "%FT%T"); // ISO 8601 format (2024-03-20T12:34:56)
48+
return oss.str();
2749
}
2850

29-
void EventProcessor::process(const nlohmann::json &j) {
51+
/**
52+
* Processes a raw event JSON object through the full pipeline:
53+
* 1. Injects current timestamp
54+
* 2. Adds GeoIP enrichment (if configured)
55+
* 3. Removes ignored fields
56+
* 4. Filters out ignored risks
57+
* 5. Writes to output file in specified directory
58+
*
59+
* @param j Raw event JSON to process
60+
*/
61+
void EventProcessor::process(const nlohmann::json &j)
62+
{
3063
nlohmann::json out = j;
3164
out["timestamp"] = nowTs();
3265

33-
if (geo) { // statt config.geoip_enabled
66+
if (geo)
67+
{ // statt config.geoip_enabled
3468
std::string src = j.value("src_ip", "");
3569
std::string dst = j.value("dst_ip", "");
3670
geo->enrich(src, dst, out);
3771
}
3872

39-
for (const auto &field : config.ignore_fields) {
73+
for (const auto &field : config.ignore_fields)
74+
{
4075
out.erase(field);
4176
}
4277

43-
if (!config.ignore_risks.empty() && out.contains("ndpi") && out["ndpi"].contains("flow_risk")) {
44-
for (const auto &risk : config.ignore_risks) {
78+
if (!config.ignore_risks.empty() && out.contains("ndpi") && out["ndpi"].contains("flow_risk"))
79+
{
80+
for (const auto &risk : config.ignore_risks)
81+
{
4582
out["ndpi"]["flow_risk"].erase(risk);
4683
}
4784
}
48-
85+
4986
std::filesystem::create_directories(directory);
5087
auto path = std::filesystem::path(directory) / (config.filename + ".json");
5188
std::ofstream ofs(path, std::ios::app);
52-
if (!ofs.is_open()) {
89+
if (!ofs.is_open())
90+
{
5391
Logger::error("Failed to open output file: " + path.string());
5492
return;
5593
}
5694
ofs << out.dump() << std::endl;
5795
}
58-

heidpi-logger/src/GeoIP.cpp

Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
#include "Logger.hpp"
33
#include <sstream>
44

5-
namespace {
6-
nlohmann::json entryToJson(const MMDB_s &db, const MMDB_entry_data_s &entry) {
7-
switch (entry.type) {
5+
namespace
6+
{
7+
nlohmann::json entryToJson(const MMDB_s &db, const MMDB_entry_data_s &entry)
8+
{
9+
switch (entry.type)
10+
{
811
case MMDB_DATA_TYPE_UTF8_STRING:
912
return std::string(entry.utf8_string, entry.data_size);
1013
case MMDB_DATA_TYPE_DOUBLE:
@@ -21,18 +24,22 @@ nlohmann::json entryToJson(const MMDB_s &db, const MMDB_entry_data_s &entry) {
2124
return entry.uint64;
2225
case MMDB_DATA_TYPE_BOOLEAN:
2326
return static_cast<bool>(entry.boolean);
24-
case MMDB_DATA_TYPE_MAP: {
27+
case MMDB_DATA_TYPE_MAP:
28+
{
2529
MMDB_entry_s sub{&db, entry.offset};
2630
MMDB_entry_data_list_s *list = nullptr;
27-
if (MMDB_get_entry_data_list(&sub, &list) == MMDB_SUCCESS && list) {
31+
if (MMDB_get_entry_data_list(&sub, &list) == MMDB_SUCCESS && list)
32+
{
2833
nlohmann::json obj = nlohmann::json::object();
2934
MMDB_entry_data_list_s *ptr = list;
30-
while (ptr && ptr->next) {
35+
while (ptr && ptr->next)
36+
{
3137
auto key = ptr->entry_data;
3238
ptr = ptr->next;
3339
auto val = ptr->entry_data;
3440
ptr = ptr->next;
35-
if (key.type != MMDB_DATA_TYPE_UTF8_STRING) continue;
41+
if (key.type != MMDB_DATA_TYPE_UTF8_STRING)
42+
continue;
3643
std::string k(key.utf8_string, key.data_size);
3744
obj[k] = entryToJson(db, val);
3845
}
@@ -41,13 +48,16 @@ nlohmann::json entryToJson(const MMDB_s &db, const MMDB_entry_data_s &entry) {
4148
}
4249
break;
4350
}
44-
case MMDB_DATA_TYPE_ARRAY: {
51+
case MMDB_DATA_TYPE_ARRAY:
52+
{
4553
MMDB_entry_s sub{&db, entry.offset};
4654
MMDB_entry_data_list_s *list = nullptr;
47-
if (MMDB_get_entry_data_list(&sub, &list) == MMDB_SUCCESS && list) {
55+
if (MMDB_get_entry_data_list(&sub, &list) == MMDB_SUCCESS && list)
56+
{
4857
nlohmann::json arr = nlohmann::json::array();
4958
MMDB_entry_data_list_s *ptr = list;
50-
while (ptr) {
59+
while (ptr)
60+
{
5161
arr.push_back(entryToJson(db, ptr->entry_data));
5262
ptr = ptr->next;
5363
}
@@ -58,61 +68,78 @@ nlohmann::json entryToJson(const MMDB_s &db, const MMDB_entry_data_s &entry) {
5868
}
5969
default:
6070
break;
71+
}
72+
return {};
6173
}
62-
return {};
63-
}
6474
} // namespace
6575

6676
GeoIP::GeoIP(const std::string &path, const std::vector<std::string> &k)
67-
: keys(k) {
77+
: keys(k)
78+
{
6879
int status = MMDB_open(path.c_str(), MMDB_MODE_MMAP, &mmdb);
69-
if (status != MMDB_SUCCESS) {
80+
if (status != MMDB_SUCCESS)
81+
{
7082
Logger::error(std::string("GeoIP open failed: ") + path + " " + MMDB_strerror(status));
7183
loaded = false;
72-
} else {
84+
}
85+
else
86+
{
7387
loaded = true;
7488
}
7589
}
7690

77-
GeoIP::~GeoIP() {
78-
if (loaded) {
91+
GeoIP::~GeoIP()
92+
{
93+
if (loaded)
94+
{
7995
MMDB_close(&mmdb);
8096
}
8197
}
8298

83-
nlohmann::json GeoIP::lookup(const std::string &ip) const {
99+
nlohmann::json GeoIP::lookup(const std::string &ip) const
100+
{
84101
nlohmann::json result;
85-
if (!loaded || ip.empty()) return result;
102+
if (!loaded || ip.empty())
103+
return result;
86104

87105
int gai_error = 0, mmdb_error = 0;
88106
MMDB_lookup_result_s res = MMDB_lookup_string(&mmdb, ip.c_str(), &gai_error, &mmdb_error);
89-
if (gai_error != 0 || mmdb_error != MMDB_SUCCESS || !res.found_entry) {
107+
if (gai_error != 0 || mmdb_error != MMDB_SUCCESS || !res.found_entry)
108+
{
90109
return result;
91110
}
92111

93-
for (const auto &key : keys) {
112+
for (const auto &key : keys)
113+
{
94114
// Split dotted key path into parts
95115
std::vector<std::string> parts;
96116
std::stringstream ss(key);
97117
std::string part;
98-
while (std::getline(ss, part, '.')) parts.push_back(part);
118+
while (std::getline(ss, part, '.'))
119+
parts.push_back(part);
99120

100-
std::vector<const char*> path;
101-
for (const auto &p : parts) path.push_back(p.c_str());
121+
std::vector<const char *> path;
122+
for (const auto &p : parts)
123+
path.push_back(p.c_str());
102124
path.push_back(nullptr);
103125

104126
MMDB_entry_data_s entry{};
105127
int status = MMDB_aget_value(&res.entry, &entry, path.data());
106-
if (status != MMDB_SUCCESS || !entry.has_data) continue;
107-
128+
if (status != MMDB_SUCCESS || !entry.has_data)
129+
continue;
130+
108131
const std::string &field = parts.back();
109132

110133
nlohmann::json value = entryToJson(mmdb, entry);
111134

112-
if (!value.is_null() && !(value.is_object() && value.empty())) {
113-
if (parts.size() == 1) {
135+
if (!value.is_null() && !(value.is_object() && value.empty()))
136+
{
137+
if (parts.size() == 1)
138+
{
114139
result[parts[0]] = value;
115-
} else {
140+
}
141+
else
142+
{
116143
result[field] = value;
117144
}
118145
}
@@ -121,14 +148,18 @@ nlohmann::json GeoIP::lookup(const std::string &ip) const {
121148
}
122149

123150
void GeoIP::enrich(const std::string &src_ip, const std::string &dst_ip,
124-
nlohmann::json &out) const {
125-
if (!loaded) return;
151+
nlohmann::json &out) const
152+
{
153+
if (!loaded)
154+
return;
126155
auto src = lookup(src_ip);
127-
if (!src.empty()) {
156+
if (!src.empty())
157+
{
128158
out["src_geoip2_city"] = src;
129159
}
130160
auto dst = lookup(dst_ip);
131-
if (!dst.empty()) {
161+
if (!dst.empty())
162+
{
132163
out["dst_geoip2_city"] = dst;
133164
}
134165
}

0 commit comments

Comments
 (0)