Coverage Report

Created: 2026-11-11 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zeek/src/Conn.cc
Line
Count
Source
1
// See the file "COPYING" in the main distribution directory for copyright.
2
3
#include "zeek/Conn.h"
4
5
#include <binpac.h>
6
#include <cctype>
7
#include <memory>
8
9
#include "zeek/Desc.h"
10
#include "zeek/ID.h"
11
#include "zeek/NetVar.h"
12
#include "zeek/Reporter.h"
13
#include "zeek/RunState.h"
14
#include "zeek/Timer.h"
15
#include "zeek/TunnelEncapsulation.h"
16
#include "zeek/WeirdState.h"
17
#include "zeek/analyzer/Analyzer.h"
18
#include "zeek/analyzer/Manager.h"
19
#include "zeek/analyzer/protocol/pia/PIA.h"
20
#include "zeek/iosource/Packet.h"
21
#include "zeek/packet_analysis/protocol/ip/SessionAdapter.h"
22
#include "zeek/packet_analysis/protocol/tcp/TCP.h"
23
#include "zeek/session/Manager.h"
24
25
namespace zeek {
26
27
uint64_t Connection::total_connections = 0;
28
uint64_t Connection::current_connections = 0;
29
zeek::RecordValPtr Connection::conn_id_ctx_singleton;
30
31
66
void Connection::InitPostScript() {
32
66
    if ( id::conn_id_ctx->NumFields() == 0 )
33
66
        conn_id_ctx_singleton = zeek::make_intrusive<zeek::RecordVal>(id::conn_id_ctx);
34
35
    // Double-check that the maximum tunnel changes script variable doesn't exceed the
36
    // maximum value allowed due to it being a uint8_t.
37
66
    if ( zeek::detail::tunnel_max_changes_per_connection > 255 )
38
0
        reporter->Error("Tunnel::max_changes_per_connection is too large: %" PRIu64,
39
0
                        zeek::detail::tunnel_max_changes_per_connection);
40
66
}
41
42
Connection::Connection(zeek::IPBasedConnKeyPtr k, double t, uint32_t flow, const Packet* pkt)
43
399k
    : Session(t, connection_timeout, connection_status_update, detail::connection_status_update_interval),
44
399k
      key(std::move(k)) {
45
399k
    orig_addr = key->SrcAddr();
46
399k
    resp_addr = key->DstAddr();
47
399k
    orig_port = key->SrcPort();
48
399k
    resp_port = key->DstPort();
49
399k
    proto = key->GetTransportProto();
50
51
399k
    orig_flow_label = flow;
52
399k
    resp_flow_label = 0;
53
399k
    saw_first_orig_packet = true;
54
399k
    saw_first_resp_packet = false;
55
56
399k
    if ( pkt->l2_src )
57
1.69k
        memcpy(orig_l2_addr, pkt->l2_src, sizeof(orig_l2_addr));
58
397k
    else
59
397k
        memset(orig_l2_addr, 0, sizeof(orig_l2_addr));
60
61
399k
    if ( pkt->l2_dst )
62
1.69k
        memcpy(resp_l2_addr, pkt->l2_dst, sizeof(resp_l2_addr));
63
397k
    else
64
397k
        memset(resp_l2_addr, 0, sizeof(resp_l2_addr));
65
66
399k
    if ( pkt->vlan )
67
14
        vlan = pkt->vlan->id;
68
69
399k
    if ( pkt->inner_vlan )
70
0
        inner_vlan = pkt->inner_vlan->id;
71
72
399k
    weird = false;
73
74
399k
    suppress_event = 0;
75
76
399k
    finished = false;
77
78
399k
    adapter = nullptr;
79
399k
    primary_PIA = nullptr;
80
81
399k
    ++current_connections;
82
399k
    ++total_connections;
83
84
399k
    encapsulation = pkt->encap;
85
399k
}
86
87
399k
Connection::~Connection() {
88
399k
    if ( ! finished )
89
0
        reporter->InternalError("Done() not called before destruction of Connection");
90
91
399k
    CancelTimers();
92
93
399k
    if ( conn_val )
94
399k
        conn_val->SetOrigin(nullptr);
95
96
399k
    delete adapter;
97
98
399k
    --current_connections;
99
399k
}
100
101
99.9k
void Connection::CheckEncapsulation(const std::shared_ptr<EncapsulationStack>& arg_encap) {
102
99.9k
    auto enqueue_tunnel_changed = [&](const auto& val) {
103
2.86k
        if ( ! tunnel_changed )
104
0
            return;
105
106
2.86k
        if ( zeek::detail::tunnel_max_changes_per_connection != 0 &&
107
2.86k
             tunnel_changes >= zeek::detail::tunnel_max_changes_per_connection ) {
108
1.22k
            Weird("tunnel_max_changes_per_connection_exceeded",
109
1.22k
                  util::fmt("%" PRIu64, zeek::detail::tunnel_max_changes_per_connection));
110
1.22k
            CheckHistory(zeek::session::detail::HIST_ANALYZER_LIMIT_REACHED, 'X');
111
1.22k
            return;
112
1.22k
        }
113
114
1.63k
        tunnel_changes++;
115
1.63k
        EnqueueEvent(tunnel_changed, nullptr, GetVal(), val);
116
1.63k
    };
117
118
99.9k
    if ( encapsulation && arg_encap ) {
119
4.27k
        if ( *encapsulation != *arg_encap ) {
120
2.19k
            enqueue_tunnel_changed(arg_encap->ToVal());
121
2.19k
            encapsulation = std::make_shared<EncapsulationStack>(*arg_encap);
122
2.19k
        }
123
4.27k
    }
124
125
95.7k
    else if ( encapsulation ) {
126
314
        EncapsulationStack empty;
127
314
        enqueue_tunnel_changed(empty.ToVal());
128
314
        encapsulation = nullptr;
129
314
    }
130
131
95.4k
    else if ( arg_encap ) {
132
353
        enqueue_tunnel_changed(arg_encap->ToVal());
133
353
        encapsulation = std::make_shared<EncapsulationStack>(*arg_encap);
134
353
    }
135
99.9k
}
136
137
399k
void Connection::Done() {
138
399k
    finished = true;
139
140
399k
    if ( adapter ) {
141
399k
        if ( ConnTransport() == TRANSPORT_TCP ) {
142
323k
            auto* ta = static_cast<packet_analysis::TCP::TCPSessionAdapter*>(adapter);
143
323k
            assert(ta->IsAnalyzer("TCP"));
144
323k
            analyzer::tcp::TCP_Endpoint* to = ta->Orig();
145
323k
            analyzer::tcp::TCP_Endpoint* tr = ta->Resp();
146
147
323k
            packet_analysis::TCP::TCPAnalyzer::GetStats().StateLeft(to->state, tr->state);
148
323k
        }
149
150
399k
        if ( ! adapter->IsFinished() )
151
399k
            adapter->Done();
152
399k
    }
153
399k
}
154
155
void Connection::NextPacket(double t, bool is_orig, const IP_Hdr* ip, int len, int caplen, const u_char*& data,
156
                            int& record_packet, int& record_content,
157
                            // arguments for reproducing packets
158
0
                            const Packet* pkt) {
159
0
    run_state::current_timestamp = t;
160
0
    run_state::current_pkt = pkt;
161
162
0
    if ( adapter ) {
163
0
        if ( adapter->Skipping() )
164
0
            return;
165
166
0
        record_current_packet = record_packet;
167
0
        record_current_content = record_content;
168
0
        adapter->NextPacket(len, data, is_orig, -1, ip, caplen);
169
0
        record_packet = record_current_packet;
170
0
        record_content = record_current_content;
171
0
    }
172
0
    else
173
0
        last_time = t;
174
175
0
    run_state::current_timestamp = 0;
176
0
    run_state::current_pkt = nullptr;
177
0
}
178
179
180
15.2k
const ConnKey& Connection::Key() const { return *key; }
181
182
511k
session::detail::Key Connection::SessionKey(bool copy) const { return key->SessionKey(); }
183
184
1.02M
uint8_t Connection::KeyProto() const { return key->PackedTuple().proto; }
185
186
208k
bool Connection::IsReuse(double t, const u_char* pkt) { return adapter && adapter->IsReuse(t, pkt); }
187
188
302M
const RecordValPtr& Connection::GetVal() {
189
302M
    if ( ! conn_val ) {
190
399k
        conn_val = make_intrusive<RecordVal>(id::connection);
191
192
399k
        auto id_val = make_intrusive<RecordVal>(id::conn_id);
193
194
399k
        constexpr int ctx_offset = 5;
195
196
        // If the conn_id_ctx type has no fields at all, set it to the singleton instance,
197
        // otherwise the instance is initialized on first access through GetField() below.
198
399k
        if ( conn_id_ctx_singleton ) {
199
399k
            assert(id::conn_id_ctx->NumFields() == 0);
200
399k
            id_val->Assign(ctx_offset, conn_id_ctx_singleton);
201
399k
        }
202
203
399k
        auto ctx = id_val->GetField<zeek::RecordVal>(ctx_offset);
204
205
        // Allow customized ConnKeys to augment conn_id and ctx.
206
399k
        key->PopulateConnIdVal(*id_val, *ctx);
207
208
399k
        auto orig_endp = make_intrusive<RecordVal>(id::endpoint);
209
399k
        orig_endp->Assign(0, 0);
210
399k
        orig_endp->Assign(1, 0);
211
399k
        orig_endp->Assign(4, orig_flow_label);
212
213
399k
        const int l2_len = sizeof(orig_l2_addr);
214
399k
        char null[l2_len]{};
215
216
399k
        if ( memcmp(&orig_l2_addr, &null, l2_len) != 0 ) {
217
965
            auto [mac_bytes, mac_len] = fmt_mac_bytes(orig_l2_addr, l2_len);
218
965
            orig_endp->Assign(5, new String(true, mac_bytes.release(), mac_len));
219
965
        }
220
221
399k
        auto resp_endp = make_intrusive<RecordVal>(id::endpoint);
222
399k
        resp_endp->Assign(0, 0);
223
399k
        resp_endp->Assign(1, 0);
224
399k
        resp_endp->Assign(4, resp_flow_label);
225
226
399k
        if ( memcmp(&resp_l2_addr, &null, l2_len) != 0 ) {
227
971
            auto [mac_bytes, mac_len] = fmt_mac_bytes(resp_l2_addr, l2_len);
228
971
            resp_endp->Assign(5, new String(true, mac_bytes.release(), mac_len));
229
971
        }
230
231
399k
        conn_val->Assign(0, std::move(id_val));
232
399k
        conn_val->Assign(1, std::move(orig_endp));
233
399k
        conn_val->Assign(2, std::move(resp_endp));
234
        // 3 and 4 are set below.
235
        // Do not assign to 5 (service). It is a non-optional set, which will be default-initialized
236
        // using the script-level settings; this easily applies the &ordered attribute to it.
237
        // conn_val->Assign(5, make_intrusive<TableVal>(id::ordered_string_set)); // service
238
399k
        conn_val->Assign(6, val_mgr->EmptyString()); // history
239
240
399k
        if ( ! uid )
241
399k
            uid.Set(zeek::detail::bits_per_uid);
242
243
399k
        conn_val->Assign(7, uid.Base62("C"));
244
245
399k
        if ( encapsulation && encapsulation->Depth() > 0 )
246
1.81k
            conn_val->Assign(8, encapsulation->ToVal());
247
248
399k
        if ( vlan )
249
14
            conn_val->Assign(9, vlan.value());
250
251
399k
        if ( inner_vlan )
252
0
            conn_val->Assign(10, inner_vlan.value());
253
399k
    }
254
255
302M
    if ( adapter )
256
302M
        adapter->UpdateConnVal(conn_val.get());
257
258
302M
    conn_val->AssignTime(3, start_time); // ###
259
302M
    conn_val->AssignInterval(4, last_time - start_time);
260
261
302M
    if ( ! history.empty() ) {
262
33.2M
        auto v = conn_val->GetFieldAs<StringVal>(6);
263
33.2M
        if ( *v != history )
264
431k
            conn_val->Assign(6, history);
265
33.2M
    }
266
267
302M
    conn_val->SetOrigin(this);
268
269
302M
    return conn_val;
270
302M
}
271
272
344k
analyzer::Analyzer* Connection::FindAnalyzer(analyzer::ID id) { return adapter ? adapter->FindChild(id) : nullptr; }
273
274
2.49M
analyzer::Analyzer* Connection::FindAnalyzer(const zeek::Tag& tag) {
275
2.49M
    return adapter ? adapter->FindChild(tag) : nullptr;
276
2.49M
}
277
278
4.42M
analyzer::Analyzer* Connection::FindAnalyzer(const char* name) { return adapter->FindChild(name); }
279
280
void Connection::Match(detail::Rule::PatternType type, const u_char* data, int len, bool is_orig, bool bol, bool eol,
281
40.9M
                       bool clear_state) {
282
40.9M
    if ( primary_PIA )
283
40.9M
        primary_PIA->Match(type, data, len, is_orig, bol, eol, clear_state);
284
40.9M
}
285
286
399k
void Connection::RemovalEvent() {
287
399k
    if ( connection_state_remove )
288
399k
        EnqueueEvent(connection_state_remove, nullptr, GetVal());
289
399k
}
290
291
24.4M
void Connection::Weird(const char* name, const char* addl, const char* source) {
292
24.4M
    weird = true;
293
24.4M
    reporter->Weird(this, name, addl ? addl : "", source ? source : "");
294
24.4M
}
295
296
55.8k
void Connection::LimitReachedWeird(const char* name, size_t limit, size_t limit_max, const char* source) {
297
55.8k
    const char* addl = limit_max > 0 ? util::fmt("%zu > %zu", limit, limit_max) : util::fmt("%zu", limit);
298
55.8k
    Weird(name, addl, source);
299
55.8k
    CheckHistory(zeek::session::detail::HIST_ANALYZER_LIMIT_REACHED, 'X');
300
55.8k
}
301
302
81.1k
void Connection::FlipRoles() {
303
81.1k
    IPAddr tmp_addr = resp_addr;
304
81.1k
    resp_addr = orig_addr;
305
81.1k
    orig_addr = tmp_addr;
306
307
81.1k
    uint32_t tmp_port = resp_port;
308
81.1k
    resp_port = orig_port;
309
81.1k
    orig_port = tmp_port;
310
311
81.1k
    const int l2_len = sizeof(orig_l2_addr);
312
81.1k
    u_char tmp_l2_addr[l2_len];
313
81.1k
    memcpy(tmp_l2_addr, resp_l2_addr, l2_len);
314
81.1k
    memcpy(resp_l2_addr, orig_l2_addr, l2_len);
315
81.1k
    memcpy(orig_l2_addr, tmp_l2_addr, l2_len);
316
317
81.1k
    bool tmp_bool = saw_first_resp_packet;
318
81.1k
    saw_first_resp_packet = saw_first_orig_packet;
319
81.1k
    saw_first_orig_packet = tmp_bool;
320
321
81.1k
    uint32_t tmp_flow = resp_flow_label;
322
81.1k
    resp_flow_label = orig_flow_label;
323
81.1k
    orig_flow_label = tmp_flow;
324
325
81.1k
    if ( conn_val ) {
326
        // Delegate flipping of conn_id and ctx records to the key instance.
327
4.43k
        auto id_val = conn_val->GetField<zeek::RecordVal>(0);
328
4.43k
        auto* ctx = id_val->GetFieldAs<zeek::RecordVal>(5);
329
4.43k
        key->FlipRoles(*id_val, *ctx);
330
331
        // Flip the connection's endpoints
332
4.43k
        const auto& tmp_endp = conn_val->GetField<zeek::RecordVal>(1);
333
4.43k
        conn_val->Assign(1, conn_val->GetField(2));
334
4.43k
        conn_val->Assign(2, tmp_endp);
335
4.43k
    }
336
76.6k
    else {
337
        // Even we haven't yet allocated a connection value, still need to flip the key's
338
        // idea of originator and responder
339
76.6k
        key->FlipRoles();
340
76.6k
    }
341
342
81.1k
    if ( adapter )
343
4.43k
        adapter->FlipRoles();
344
345
81.1k
    analyzer_mgr->ApplyScheduledAnalyzers(this);
346
347
81.1k
    AddHistory('^');
348
349
81.1k
    if ( connection_flipped )
350
81.1k
        EnqueueEvent(connection_flipped, nullptr, GetVal());
351
81.1k
}
352
353
0
void Connection::Describe(ODesc* d) const {
354
0
    session::Session::Describe(d);
355
356
0
    switch ( proto ) {
357
0
        case TRANSPORT_TCP: d->Add("TCP"); break;
358
359
0
        case TRANSPORT_UDP: d->Add("UDP"); break;
360
361
0
        case TRANSPORT_ICMP: d->Add("ICMP"); break;
362
363
0
        case TRANSPORT_UNKNOWN:
364
0
            d->Add("unknown");
365
0
            reporter->InternalWarning("unknown transport in Connection::Describe()");
366
367
0
            break;
368
369
0
        default: reporter->InternalError("unhandled transport type in Connection::Describe");
370
0
    }
371
372
0
    d->SP();
373
0
    d->Add(orig_addr);
374
0
    d->Add(":");
375
0
    d->Add(ntohs(orig_port));
376
377
0
    d->SP();
378
0
    d->AddSP("->");
379
380
0
    d->Add(resp_addr);
381
0
    d->Add(":");
382
0
    d->Add(ntohs(resp_port));
383
384
0
    d->NL();
385
0
}
386
387
143k
void Connection::IDString(ODesc* d) const {
388
143k
    d->Add(orig_addr);
389
143k
    d->AddRaw(":", 1);
390
143k
    d->Add(ntohs(orig_port));
391
143k
    d->AddRaw(" > ", 3);
392
143k
    d->Add(resp_addr);
393
143k
    d->AddRaw(":", 1);
394
143k
    d->Add(ntohs(resp_port));
395
143k
}
396
397
399k
void Connection::SetSessionAdapter(packet_analysis::IP::SessionAdapter* aa, analyzer::pia::PIA* pia) {
398
399k
    adapter = aa;
399
399k
    primary_PIA = pia;
400
399k
}
401
402
294k
void Connection::CheckFlowLabel(bool is_orig, uint32_t flow_label) {
403
294k
    uint32_t& my_flow_label = is_orig ? orig_flow_label : resp_flow_label;
404
405
294k
    if ( my_flow_label != flow_label ) {
406
3.37k
        if ( conn_val ) {
407
3.37k
            RecordVal* endp = conn_val->GetFieldAs<RecordVal>(is_orig ? 1 : 2);
408
3.37k
            endp->Assign(4, flow_label);
409
3.37k
        }
410
411
3.37k
        if ( connection_flow_label_changed && (is_orig ? saw_first_orig_packet : saw_first_resp_packet) ) {
412
1.49k
            EnqueueEvent(connection_flow_label_changed, nullptr, GetVal(), val_mgr->Bool(is_orig),
413
1.49k
                         val_mgr->Count(my_flow_label), val_mgr->Count(flow_label));
414
1.49k
        }
415
416
3.37k
        my_flow_label = flow_label;
417
3.37k
    }
418
419
294k
    if ( is_orig )
420
174k
        saw_first_orig_packet = true;
421
119k
    else
422
119k
        saw_first_resp_packet = true;
423
294k
}
424
425
30.1M
bool Connection::PermitWeird(const char* name, uint64_t threshold, uint64_t rate, double duration) {
426
30.1M
    if ( ! weird_state )
427
274k
        weird_state = std::make_unique<detail::WeirdStateMap>();
428
429
30.1M
    return detail::PermitWeird(*weird_state, name, threshold, rate, duration);
430
30.1M
}
431
432
} // namespace zeek