Coverage Report

Created: 2026-02-14 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/rust/src/ntp/ntp.rs
Line
Count
Source
1
/* Copyright (C) 2017-2021 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
// written by Pierre Chifflier  <chifflier@wzdftpd.net>
19
20
extern crate ntp_parser;
21
use self::ntp_parser::*;
22
use crate::core;
23
use crate::core::{AppProto,Flow,ALPROTO_UNKNOWN,ALPROTO_FAILED,Direction};
24
use crate::applayer::{self, *};
25
use std;
26
use std::ffi::CString;
27
28
use nom7::Err;
29
30
#[derive(AppLayerEvent)]
31
pub enum NTPEvent {
32
    UnsolicitedResponse ,
33
    MalformedData,
34
    NotRequest,
35
    NotResponse,
36
}
37
38
#[derive(Default)]
39
pub struct NTPState {
40
    state_data: AppLayerStateData,
41
42
    /// List of transactions for this session
43
    transactions: Vec<NTPTransaction>,
44
45
    /// Events counter
46
    events: u16,
47
48
    /// tx counter for assigning incrementing id's to tx's
49
    tx_id: u64,
50
}
51
52
#[derive(Debug, Default)]
53
pub struct NTPTransaction {
54
    /// The NTP reference ID
55
    pub xid: u32,
56
57
    /// The internal transaction id
58
    id: u64,
59
60
    tx_data: applayer::AppLayerTxData,
61
}
62
63
impl Transaction for NTPTransaction {
64
77.3k
    fn id(&self) -> u64 {
65
77.3k
        self.id
66
77.3k
    }
67
}
68
69
impl NTPState {
70
1.62k
    pub fn new() -> Self {
71
1.62k
        Default::default()
72
1.62k
    }
73
}
74
75
impl State<NTPTransaction> for NTPState {
76
42.9k
    fn get_transaction_count(&self) -> usize {
77
42.9k
        self.transactions.len()
78
42.9k
    }
79
80
38.6k
    fn get_transaction_by_index(&self, index: usize) -> Option<&NTPTransaction> {
81
38.6k
        self.transactions.get(index)
82
38.6k
    }
83
}
84
85
impl NTPState {
86
    /// Parse an NTP request message
87
    ///
88
    /// Returns 0 if successful, or -1 on error
89
44.5k
    fn parse(&mut self, i: &[u8], direction: Direction) -> i32 {
90
44.5k
        match parse_ntp(i) {
91
44.1k
            Ok((_,ref msg)) => {
92
                // SCLogDebug!("parse_ntp: {:?}",msg);
93
44.1k
                let (mode, ref_id) = match msg {
94
37.0k
                    NtpPacket::V3(pkt) => (pkt.mode, pkt.ref_id),
95
7.02k
                    NtpPacket::V4(pkt) => (pkt.mode, pkt.ref_id),
96
                };
97
44.1k
                if mode == NtpMode::SymmetricActive || mode == NtpMode::Client {
98
39.2k
                    let mut tx = self.new_tx(direction);
99
39.2k
                    // use the reference id as identifier
100
39.2k
                    tx.xid = ref_id;
101
39.2k
                    self.transactions.push(tx);
102
39.2k
                }
103
44.1k
                0
104
            },
105
            Err(Err::Incomplete(_)) => {
106
                SCLogDebug!("Insufficient data while parsing NTP data");
107
252
                self.set_event(NTPEvent::MalformedData);
108
252
                -1
109
            },
110
            Err(_) => {
111
                SCLogDebug!("Error while parsing NTP data");
112
153
                self.set_event(NTPEvent::MalformedData);
113
153
                -1
114
            },
115
        }
116
44.5k
    }
117
118
1.62k
    fn free(&mut self) {
119
        // All transactions are freed when the `transactions` object is freed.
120
        // But let's be explicit
121
1.62k
        self.transactions.clear();
122
1.62k
    }
123
124
39.2k
    fn new_tx(&mut self, direction: Direction) -> NTPTransaction {
125
39.2k
        self.tx_id += 1;
126
39.2k
        NTPTransaction::new(direction, self.tx_id)
127
39.2k
    }
128
129
0
    pub fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&NTPTransaction> {
130
0
        self.transactions.iter().find(|&tx| tx.id == tx_id + 1)
131
0
    }
132
133
38.6k
    fn free_tx(&mut self, tx_id: u64) {
134
38.6k
        let tx = self.transactions.iter().position(|tx| tx.id == tx_id + 1);
135
38.6k
        debug_assert!(tx.is_some());
136
38.6k
        if let Some(idx) = tx {
137
38.6k
            let _ = self.transactions.remove(idx);
138
38.6k
        }
139
38.6k
    }
140
141
    /// Set an event. The event is set on the most recent transaction.
142
405
    pub fn set_event(&mut self, event: NTPEvent) {
143
405
        if let Some(tx) = self.transactions.last_mut() {
144
0
            tx.tx_data.set_event(event as u8);
145
0
            self.events += 1;
146
405
        }
147
405
    }
148
}
149
150
impl NTPTransaction {
151
39.2k
    pub fn new(direction: Direction, id: u64) -> NTPTransaction {
152
39.2k
        NTPTransaction {
153
39.2k
            xid: 0,
154
39.2k
            id,
155
39.2k
            tx_data: applayer::AppLayerTxData::for_direction(direction),
156
39.2k
        }
157
39.2k
    }
158
}
159
160
/// Returns *mut NTPState
161
#[no_mangle]
162
1.62k
pub extern "C" fn rs_ntp_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void {
163
1.62k
    let state = NTPState::new();
164
1.62k
    let boxed = Box::new(state);
165
1.62k
    return Box::into_raw(boxed) as *mut _;
166
1.62k
}
167
168
/// Params:
169
/// - state: *mut NTPState as void pointer
170
#[no_mangle]
171
1.62k
pub extern "C" fn rs_ntp_state_free(state: *mut std::os::raw::c_void) {
172
1.62k
    let mut ntp_state = unsafe{ Box::from_raw(state as *mut NTPState) };
173
1.62k
    ntp_state.free();
174
1.62k
}
175
176
#[no_mangle]
177
22.9k
pub unsafe extern "C" fn rs_ntp_parse_request(_flow: *const core::Flow,
178
22.9k
                                       state: *mut std::os::raw::c_void,
179
22.9k
                                       _pstate: *mut std::os::raw::c_void,
180
22.9k
                                       stream_slice: StreamSlice,
181
22.9k
                                       _data: *const std::os::raw::c_void,
182
22.9k
                                       ) -> AppLayerResult {
183
22.9k
    let state = cast_pointer!(state,NTPState);
184
22.9k
    if state.parse(stream_slice.as_slice(), Direction::ToServer) < 0 {
185
259
        return AppLayerResult::err();
186
22.6k
    }
187
22.6k
    AppLayerResult::ok()
188
22.9k
}
189
190
#[no_mangle]
191
21.5k
pub unsafe extern "C" fn rs_ntp_parse_response(_flow: *const core::Flow,
192
21.5k
                                       state: *mut std::os::raw::c_void,
193
21.5k
                                       _pstate: *mut std::os::raw::c_void,
194
21.5k
                                       stream_slice: StreamSlice,
195
21.5k
                                       _data: *const std::os::raw::c_void,
196
21.5k
                                       ) -> AppLayerResult {
197
21.5k
    let state = cast_pointer!(state,NTPState);
198
21.5k
    if state.parse(stream_slice.as_slice(), Direction::ToClient) < 0 {
199
146
        return AppLayerResult::err();
200
21.4k
    }
201
21.4k
    AppLayerResult::ok()
202
21.5k
}
203
204
#[no_mangle]
205
0
pub unsafe extern "C" fn rs_ntp_state_get_tx(state: *mut std::os::raw::c_void,
206
0
                                      tx_id: u64)
207
0
                                      -> *mut std::os::raw::c_void
208
{
209
0
    let state = cast_pointer!(state,NTPState);
210
0
    match state.get_tx_by_id(tx_id) {
211
0
        Some(tx) => tx as *const _ as *mut _,
212
0
        None     => std::ptr::null_mut(),
213
    }
214
0
}
215
216
#[no_mangle]
217
131k
pub unsafe extern "C" fn rs_ntp_state_get_tx_count(state: *mut std::os::raw::c_void)
218
131k
                                            -> u64
219
{
220
131k
    let state = cast_pointer!(state,NTPState);
221
131k
    state.tx_id
222
131k
}
223
224
#[no_mangle]
225
38.6k
pub unsafe extern "C" fn rs_ntp_state_tx_free(state: *mut std::os::raw::c_void,
226
38.6k
                                       tx_id: u64)
227
{
228
38.6k
    let state = cast_pointer!(state,NTPState);
229
38.6k
    state.free_tx(tx_id);
230
38.6k
}
231
232
#[no_mangle]
233
77.3k
pub extern "C" fn rs_ntp_tx_get_alstate_progress(_tx: *mut std::os::raw::c_void,
234
77.3k
                                                 _direction: u8)
235
77.3k
                                                 -> std::os::raw::c_int
236
{
237
77.3k
    1
238
77.3k
}
239
240
static mut ALPROTO_NTP : AppProto = ALPROTO_UNKNOWN;
241
242
#[no_mangle]
243
631
pub extern "C" fn ntp_probing_parser(_flow: *const Flow,
244
631
        _direction: u8,
245
631
        input:*const u8, input_len: u32,
246
631
        _rdir: *mut u8) -> AppProto
247
{
248
631
    if input.is_null() {
249
0
        return ALPROTO_UNKNOWN;
250
631
    }
251
631
    let slice: &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, input_len as usize) };
252
631
    let alproto = unsafe{ ALPROTO_NTP };
253
631
    match parse_ntp(slice) {
254
        Ok((_, _)) => {
255
            // parse_ntp already checks for supported version (3 or 4)
256
462
            return alproto;
257
        },
258
        Err(Err::Incomplete(_)) => {
259
88
            return ALPROTO_UNKNOWN;
260
        },
261
        Err(_) => {
262
81
            return unsafe{ALPROTO_FAILED};
263
        },
264
    }
265
631
}
266
267
export_tx_data_get!(rs_ntp_get_tx_data, NTPTransaction);
268
export_state_data_get!(rs_ntp_get_state_data, NTPState);
269
270
const PARSER_NAME : &[u8] = b"ntp\0";
271
272
#[no_mangle]
273
34
pub unsafe extern "C" fn rs_register_ntp_parser() {
274
34
    let default_port = CString::new("123").unwrap();
275
34
    let parser = RustParser {
276
34
        name               : PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
277
34
        default_port       : default_port.as_ptr(),
278
34
        ipproto            : core::IPPROTO_UDP,
279
34
        probe_ts           : Some(ntp_probing_parser),
280
34
        probe_tc           : Some(ntp_probing_parser),
281
34
        min_depth          : 0,
282
34
        max_depth          : 16,
283
34
        state_new          : rs_ntp_state_new,
284
34
        state_free         : rs_ntp_state_free,
285
34
        tx_free            : rs_ntp_state_tx_free,
286
34
        parse_ts           : rs_ntp_parse_request,
287
34
        parse_tc           : rs_ntp_parse_response,
288
34
        get_tx_count       : rs_ntp_state_get_tx_count,
289
34
        get_tx             : rs_ntp_state_get_tx,
290
34
        tx_comp_st_ts      : 1,
291
34
        tx_comp_st_tc      : 1,
292
34
        tx_get_progress    : rs_ntp_tx_get_alstate_progress,
293
34
        get_eventinfo      : Some(NTPEvent::get_event_info),
294
34
        get_eventinfo_byid : Some(NTPEvent::get_event_info_by_id),
295
34
        localstorage_new   : None,
296
34
        localstorage_free  : None,
297
34
        get_tx_files       : None,
298
34
        get_tx_iterator    : Some(applayer::state_get_tx_iterator::<NTPState, NTPTransaction>),
299
34
        get_tx_data        : rs_ntp_get_tx_data,
300
34
        get_state_data     : rs_ntp_get_state_data,
301
34
        apply_tx_config    : None,
302
34
        flags              : 0,
303
34
        truncate           : None,
304
34
        get_frame_id_by_name: None,
305
34
        get_frame_name_by_id: None,
306
34
    };
307
308
34
    let ip_proto_str = CString::new("udp").unwrap();
309
34
    if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
310
34
        let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
311
        // store the allocated ID for the probe function
312
34
        ALPROTO_NTP = alproto;
313
34
        if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
314
34
            let _ = AppLayerRegisterParser(&parser, alproto);
315
34
        }
316
0
    } else {
317
0
        SCLogDebug!("Protocol detector and parser disabled for NTP.");
318
0
    }
319
34
}
320
321
322
#[cfg(test)]
323
mod tests {
324
    use super::*;
325
326
    #[test]
327
    fn test_ntp_parse_request_valid() {
328
        // A UDP NTP v4 request, in client mode
329
        const REQ : &[u8] = &[
330
            0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
331
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
332
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
333
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
334
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
335
            0x18, 0x57, 0xab, 0xc3, 0x4a, 0x5f, 0x2c, 0xfe
336
        ];
337
338
        let mut state = NTPState::new();
339
        assert_eq!(0, state.parse(REQ, Direction::ToServer));
340
    }
341
}