Coverage Report

Created: 2026-08-14 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zeek/src/analyzer/protocol/rpc/Portmap.cc
Line
Count
Source
1
// See the file "COPYING" in the main distribution directory for copyright.
2
3
#include "zeek/analyzer/protocol/rpc/Portmap.h"
4
5
#include "zeek/analyzer/protocol/rpc/XDR.h"
6
#include "zeek/analyzer/protocol/rpc/events.bif.h"
7
8
enum PortmapperProcs : uint8_t {
9
    PMAPPROC_NULL = 0,
10
    PMAPPROC_SET = 1,
11
    PMAPPROC_UNSET = 2,
12
    PMAPPROC_GETPORT = 3,
13
    PMAPPROC_DUMP = 4,
14
    PMAPPROC_CALLIT = 5,
15
};
16
17
namespace zeek::analyzer::rpc {
18
namespace detail {
19
20
607k
bool PortmapperInterp::RPC_BuildCall(RPC_CallInfo* c, const u_char*& buf, int& n) {
21
607k
    if ( c->Program() != 100000 )
22
607k
        Weird("bad_RPC_program");
23
24
607k
    switch ( c->Proc() ) {
25
61.3k
        case PMAPPROC_NULL: break;
26
27
12.2k
        case PMAPPROC_SET: {
28
12.2k
            auto m = ExtractMapping(buf, n);
29
12.2k
            if ( ! m )
30
2.68k
                return false;
31
9.53k
            c->AddVal(std::move(m));
32
9.53k
        } break;
33
34
25.3k
        case PMAPPROC_UNSET: {
35
25.3k
            auto m = ExtractMapping(buf, n);
36
25.3k
            if ( ! m )
37
526
                return false;
38
24.8k
            c->AddVal(std::move(m));
39
24.8k
        } break;
40
41
2.65k
        case PMAPPROC_GETPORT: {
42
2.65k
            auto pr = ExtractPortRequest(buf, n);
43
2.65k
            if ( ! pr )
44
382
                return false;
45
2.27k
            c->AddVal(std::move(pr));
46
2.27k
        } break;
47
48
278k
        case PMAPPROC_DUMP: break;
49
50
141k
        case PMAPPROC_CALLIT: {
51
141k
            auto call_it = ExtractCallItRequest(buf, n);
52
141k
            if ( ! call_it )
53
9.46k
                return false;
54
132k
            c->AddVal(std::move(call_it));
55
132k
        } break;
56
57
86.6k
        default: return false;
58
607k
    }
59
60
508k
    return true;
61
607k
}
62
63
bool PortmapperInterp::RPC_BuildReply(RPC_CallInfo* c, BifEnum::rpc_status status, const u_char*& buf, int& n,
64
373k
                                      double start_time, double last_time, int reply_len) {
65
373k
    EventHandlerPtr event;
66
373k
    ValPtr reply;
67
373k
    int success = (status == BifEnum::RPC_SUCCESS);
68
69
373k
    switch ( c->Proc() ) {
70
59.6k
        case PMAPPROC_NULL: event = success ? pm_request_null : pm_attempt_null; break;
71
72
8.92k
        case PMAPPROC_SET:
73
8.92k
            if ( success ) {
74
1.46k
                uint32_t proc_status = extract_XDR_uint32(buf, n);
75
1.46k
                if ( ! buf )
76
66
                    return false;
77
78
1.39k
                reply = val_mgr->Bool(proc_status);
79
1.39k
                event = pm_request_set;
80
1.39k
            }
81
7.46k
            else
82
7.46k
                event = pm_attempt_set;
83
84
8.86k
            break;
85
86
24.1k
        case PMAPPROC_UNSET:
87
24.1k
            if ( success ) {
88
22.4k
                uint32_t proc_status = extract_XDR_uint32(buf, n);
89
22.4k
                if ( ! buf )
90
198
                    return false;
91
92
22.2k
                reply = val_mgr->Bool(proc_status);
93
22.2k
                event = pm_request_unset;
94
22.2k
            }
95
1.70k
            else
96
1.70k
                event = pm_attempt_unset;
97
98
23.9k
            break;
99
100
23.9k
        case PMAPPROC_GETPORT:
101
1.82k
            if ( success ) {
102
1.42k
                uint32_t port = extract_XDR_uint32(buf, n);
103
1.42k
                if ( ! buf )
104
270
                    return false;
105
106
1.15k
                RecordVal* rv = c->RequestVal()->AsRecordVal();
107
1.15k
                auto is_tcp = rv->GetFieldAs<BoolVal>(2);
108
1.15k
                reply = val_mgr->Port(CheckPort(port), is_tcp ? TRANSPORT_TCP : TRANSPORT_UDP);
109
1.15k
                event = pm_request_getport;
110
1.15k
            }
111
399
            else
112
399
                event = pm_attempt_getport;
113
1.55k
            break;
114
115
266k
        case PMAPPROC_DUMP:
116
266k
            event = success ? pm_request_dump : pm_attempt_dump;
117
266k
            if ( success ) {
118
264k
                static auto pm_mappings = id::find_type<TableType>("pm_mappings");
119
264k
                auto mappings = make_intrusive<TableVal>(pm_mappings);
120
264k
                uint32_t nmap = 0;
121
122
                // Each call in the loop test pulls the next "opted"
123
                // element to see if there are more mappings.
124
343k
                while ( extract_XDR_uint32(buf, n) && buf ) {
125
265k
                    auto m = ExtractMapping(buf, n);
126
265k
                    if ( ! m )
127
186k
                        break;
128
129
79.0k
                    auto index = val_mgr->Count(++nmap);
130
79.0k
                    mappings->Assign(std::move(index), std::move(m));
131
79.0k
                }
132
133
264k
                if ( ! buf )
134
187k
                    return false;
135
136
76.8k
                reply = std::move(mappings);
137
76.8k
                event = pm_request_dump;
138
76.8k
            }
139
2.51k
            else
140
2.51k
                event = pm_attempt_dump;
141
79.4k
            break;
142
143
79.4k
        case PMAPPROC_CALLIT:
144
12.4k
            if ( success ) {
145
10.1k
                uint32_t port = extract_XDR_uint32(buf, n);
146
10.1k
                int reply_n;
147
10.1k
                const u_char* opaque_reply = extract_XDR_opaque(buf, n, reply_n);
148
10.1k
                if ( ! opaque_reply )
149
2.64k
                    return false;
150
151
7.46k
                reply = val_mgr->Port(CheckPort(port), TRANSPORT_UDP);
152
7.46k
                event = pm_request_callit;
153
7.46k
            }
154
2.33k
            else
155
2.33k
                event = pm_attempt_callit;
156
9.79k
            break;
157
158
9.79k
        default: return false;
159
373k
    }
160
161
183k
    Event(event, c->TakeRequestVal(), status, std::move(reply));
162
183k
    return true;
163
373k
}
164
165
303k
RecordValPtr PortmapperInterp::ExtractMapping(const u_char*& buf, int& len) {
166
303k
    static auto pm_mapping = id::find_type<RecordType>("pm_mapping");
167
303k
    auto mapping = make_intrusive<RecordVal>(pm_mapping);
168
169
303k
    mapping->Assign(0, extract_XDR_uint32(buf, len));
170
303k
    mapping->Assign(1, extract_XDR_uint32(buf, len));
171
172
303k
    bool is_tcp = extract_XDR_uint32(buf, len) == IPPROTO_TCP;
173
303k
    uint32_t port = extract_XDR_uint32(buf, len);
174
303k
    mapping->Assign(2, val_mgr->Port(CheckPort(port), is_tcp ? TRANSPORT_TCP : TRANSPORT_UDP));
175
176
303k
    if ( ! buf )
177
190k
        return nullptr;
178
179
113k
    return mapping;
180
303k
}
181
182
2.65k
RecordValPtr PortmapperInterp::ExtractPortRequest(const u_char*& buf, int& len) {
183
2.65k
    static auto pm_port_request = id::find_type<RecordType>("pm_port_request");
184
2.65k
    auto pr = make_intrusive<RecordVal>(pm_port_request);
185
186
2.65k
    pr->Assign(0, extract_XDR_uint32(buf, len));
187
2.65k
    pr->Assign(1, extract_XDR_uint32(buf, len));
188
189
2.65k
    bool is_tcp = extract_XDR_uint32(buf, len) == IPPROTO_TCP;
190
2.65k
    pr->Assign(2, is_tcp);
191
2.65k
    (void)extract_XDR_uint32(buf, len); // consume the bogus port
192
193
2.65k
    if ( ! buf )
194
382
        return nullptr;
195
196
2.27k
    return pr;
197
2.65k
}
198
199
141k
RecordValPtr PortmapperInterp::ExtractCallItRequest(const u_char*& buf, int& len) {
200
141k
    static auto pm_callit_request = id::find_type<RecordType>("pm_callit_request");
201
141k
    auto c = make_intrusive<RecordVal>(pm_callit_request);
202
203
141k
    c->Assign(0, extract_XDR_uint32(buf, len));
204
141k
    c->Assign(1, extract_XDR_uint32(buf, len));
205
141k
    c->Assign(2, extract_XDR_uint32(buf, len));
206
207
141k
    int arg_n;
208
141k
    (void)extract_XDR_opaque(buf, len, arg_n);
209
141k
    c->Assign(3, arg_n);
210
211
141k
    if ( ! buf )
212
9.46k
        return nullptr;
213
214
132k
    return c;
215
141k
}
216
217
312k
uint32_t PortmapperInterp::CheckPort(uint32_t port) {
218
312k
    if ( port >= 65536 ) {
219
69.7k
        if ( pm_bad_port ) {
220
69.7k
            analyzer->EnqueueConnEvent(pm_bad_port, analyzer->ConnVal(), val_mgr->Count(port));
221
69.7k
        }
222
223
69.7k
        port = 0;
224
69.7k
    }
225
226
312k
    return port;
227
312k
}
228
229
183k
void PortmapperInterp::Event(EventHandlerPtr f, ValPtr request, BifEnum::rpc_status status, ValPtr reply) {
230
183k
    if ( ! f )
231
0
        return;
232
233
183k
    Args vl;
234
235
183k
    vl.emplace_back(analyzer->ConnVal());
236
237
183k
    if ( status == BifEnum::RPC_SUCCESS ) {
238
109k
        if ( request )
239
32.2k
            vl.emplace_back(std::move(request));
240
109k
        if ( reply )
241
109k
            vl.emplace_back(std::move(reply));
242
109k
    }
243
73.7k
    else {
244
73.7k
        vl.emplace_back(BifType::Enum::rpc_status->GetEnumVal(status));
245
246
73.7k
        if ( request )
247
11.9k
            vl.emplace_back(std::move(request));
248
73.7k
    }
249
250
183k
    analyzer->EnqueueConnEvent(f, std::move(vl));
251
183k
}
252
253
} // namespace detail
254
255
Portmapper_Analyzer::Portmapper_Analyzer(Connection* conn)
256
3.90k
    : RPC_Analyzer("PORTMAPPER", conn, new detail::PortmapperInterp(this)) {
257
3.90k
    orig_rpc = resp_rpc = nullptr;
258
3.90k
}
259
260
3.90k
void Portmapper_Analyzer::Init() {
261
3.90k
    RPC_Analyzer::Init();
262
263
3.90k
    if ( Conn()->ConnTransport() == TRANSPORT_TCP ) {
264
3.90k
        orig_rpc = new Contents_RPC(Conn(), true, interp);
265
3.90k
        resp_rpc = new Contents_RPC(Conn(), false, interp);
266
3.90k
        AddSupportAnalyzer(orig_rpc);
267
3.90k
        AddSupportAnalyzer(resp_rpc);
268
3.90k
    }
269
3.90k
}
270
271
} // namespace zeek::analyzer::rpc