• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 22586342403

02 Mar 2026 04:53PM UTC coverage: 73.68% (-0.007%) from 73.687%
22586342403

Pull #14930

github

web-flow
Merge 1d6dc53fc into 90823fa90
Pull Request #14930: Sslproxy/v16

38328 of 77468 branches covered (49.48%)

Branch coverage included in aggregate %.

336 of 431 new or added lines in 14 files covered. (77.96%)

66 existing lines in 15 files now uncovered.

265724 of 335196 relevant lines covered (79.27%)

5207223.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.63
/rust/src/sslproxy/sslproxy.rs
1
/* Copyright (C) 2025 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
use super::parser;
19
use crate::applayer::*;
20
use crate::core::{ALPROTO_UNKNOWN, IPPROTO_TCP, sc_app_layer_parser_trigger_raw_stream_inspection};
21
use crate::flow::Flow;
22
use crate::direction::Direction;
23
use nom7 as nom;
24
use std;
25
use std::ffi::CString;
26
use std::os::raw::{c_char, c_int, c_void};
27
use std::net::{IpAddr};
28
use suricata_sys::sys::{
29
    AppLayerParserState, AppProto, SCAppLayerParserConfParserEnabled,
30
    SCAppLayerParserStateIssetFlag,
31
    SCAppLayerProtoDetectConfProtoDetectionEnabled,
32
    SCAppLayerProtoDetectPMRegisterPatternCS,
33
    SCAppLayerRequestProtocolChangeUnknown,
34
    SCFlowSetTranslated, SCFlowEnableTranslation
35
};
36

37
pub(super) static mut ALPROTO_SSLPROXY: AppProto = ALPROTO_UNKNOWN;
38

NEW
39
#[derive(AppLayerEvent)]
×
40
enum SSLProxyEvent {
41
    TooManyTransactions,
42
}
43

44
pub struct SSLProxyTransaction {
45
    tx_id: u64,
46
    tx_data: AppLayerTxData,
47
}
48

49
impl Default for SSLProxyTransaction {
50
    fn default() -> Self {
2✔
51
        Self::new()
2✔
52
    }
2✔
53
}
54

55
impl SSLProxyTransaction {
56
    pub fn new() -> SSLProxyTransaction {
2✔
57
        Self {
2✔
58
            tx_id: 0,
2✔
59
            tx_data: AppLayerTxData::new(),
2✔
60
        }
2✔
61
    }
2✔
62
}
63

64
impl Transaction for SSLProxyTransaction {
65
    fn id(&self) -> u64 {
117✔
66
        self.tx_id
117✔
67
    }
117✔
68
}
69

70
#[derive(Default)]
71
pub struct SSLProxyState {
72
    state_data: AppLayerStateData,
73
    tx_id: u64,
74
    transaction: SSLProxyTransaction,
75
}
76

77
impl State<SSLProxyTransaction> for SSLProxyState {
78
    fn get_transaction_count(&self) -> usize {
117✔
79
        1_usize
117✔
80
    }
117✔
81

82
    fn get_transaction_by_index(&self, index: usize) -> Option<&SSLProxyTransaction> {
117✔
83
        if index == 0 {
117✔
84
            return Some(&self.transaction);
117✔
NEW
85
        }
×
NEW
86
        None
×
87
    }
117✔
88
}
89

90
impl SSLProxyState {
91
    pub fn new() -> Self {
2✔
92
        Default::default()
2✔
93
    }
2✔
94

95
    pub fn get_tx(&mut self, tx_id: u64) -> Option<&SSLProxyTransaction> {
2✔
96
        if tx_id == 0 {
2✔
97
            return Some(&self.transaction);
2✔
NEW
98
        }
×
NEW
99
        return None;
×
100
    }
2✔
101

102
    fn parse_request(&mut self, flow: *mut Flow, input: &[u8]) -> AppLayerResult {
2✔
103

2✔
104
        SCLogDebug!("input {}", input.len());
2✔
105
        // We're not interested in empty requests.
2✔
106
        if input.is_empty() {
2✔
NEW
107
            return AppLayerResult::ok();
×
108
        }
2✔
109

2✔
110
        match parser::parse_message(input) {
2✔
111
            Ok((rem, request)) => {
2✔
112
                SCLogDebug!("Request: {:?}", request);
2✔
113
                let mut _tx = self.get_tx(0);
2✔
114

2✔
115
                let proto = 6;
2✔
116
                let sp = request.port2;
2✔
117
                let dp = request.port3;
2✔
118

119
                if let IpAddr::V4(src_ip_v4) = request.ip2 {
2✔
120
                    let src_ip : u32 = src_ip_v4.into();
2✔
121
                    let src_ip = src_ip.to_be();
2✔
122
                    if let IpAddr::V4(dst_ip_v4) = request.ip3 {
2✔
123
                        let dest_ip : u32 = dst_ip_v4.into();
2✔
124
                        let dest_ip = dest_ip.to_be();
2✔
125
                        unsafe {
2✔
126
                            SCAppLayerRequestProtocolChangeUnknown(flow, request.port3);
2✔
127
                            sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToServer as i32);
2✔
128
                            SCFlowSetTranslated(flow, proto, src_ip, sp, dest_ip, dp);
2✔
129
                        }
2✔
130

2✔
131
                        if !rem.is_empty() {
2✔
132
                            SCLogDebug!("returning partial");
133
                            let consumed = (input.len() - rem.len()) + 2;
2✔
134
                            return AppLayerResult::ok_partial_continue(consumed as u32);
2✔
NEW
135
                        }
×
NEW
136
                    }
×
NEW
137
                }
×
138
                SCLogDebug!("malformed proxy line");
NEW
139
                return AppLayerResult::err();
×
140
            }
141
            Err(nom::Err::Incomplete(_)) => {
142
                SCLogDebug!("incomplete");
143
                // Not enough data. This parser doesn't give us a good indication
144
                // of how much data is missing so just ask for one more byte so the
145
                // parse is called as soon as more data is received.
NEW
146
                let consumed = input.len();
×
NEW
147
                let needed = consumed + 1;
×
NEW
148
                return AppLayerResult::incomplete(consumed as u32, needed as u32);
×
149
            }
NEW
150
            Err(_e) => {
×
NEW
151
                SCLogDebug!("e {:?}", _e);
×
NEW
152
                return AppLayerResult::err();
×
153
            }
154
        }
155
    }
2✔
156

NEW
157
    fn parse_response(&mut self, input: &[u8]) -> AppLayerResult {
×
NEW
158
        /* this parser should never get response data */
×
NEW
159
        if !input.is_empty() {
×
NEW
160
            return AppLayerResult::err();
×
NEW
161
        }
×
NEW
162
        return AppLayerResult::ok();
×
NEW
163
    }
×
164
}
165

166
// C exports.
167

168
extern "C" fn sslproxy_state_new(_orig_state: *mut c_void, _orig_proto: AppProto) -> *mut c_void {
2✔
169
    let state = SSLProxyState::new();
2✔
170
    let boxed = Box::new(state);
2✔
171
    return Box::into_raw(boxed) as *mut c_void;
2✔
172
}
2✔
173

174
unsafe extern "C" fn sslproxy_state_free(state: *mut c_void) {
2✔
175
    std::mem::drop(Box::from_raw(state as *mut SSLProxyState));
2✔
176
}
2✔
177

NEW
178
unsafe extern "C" fn sslproxy_state_tx_free(_state: *mut c_void, _tx_id: u64) {
×
NEW
179
}
×
180

181
unsafe extern "C" fn sslproxy_parse_request(
2✔
182
    flow: *mut Flow, state: *mut c_void, pstate: *mut AppLayerParserState,
2✔
183
    stream_slice: StreamSlice, _data: *mut c_void,
2✔
184
) -> AppLayerResult {
2✔
185
    let eof = SCAppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS) > 0;
2✔
186

2✔
187
    if eof {
2✔
188
        // If needed, handle EOF, or pass it into the parser.
NEW
189
        return AppLayerResult::ok();
×
190
    }
2✔
191

2✔
192
    let state = cast_pointer!(state, SSLProxyState);
2✔
193

2✔
194
    let buf = stream_slice.as_slice();
2✔
195
    state.parse_request(flow, buf)
2✔
196
}
2✔
197

NEW
198
unsafe extern "C" fn sslproxy_parse_response(
×
NEW
199
    _flow: *mut Flow, state: *mut c_void, pstate: *mut AppLayerParserState,
×
NEW
200
    stream_slice: StreamSlice, _data: *mut c_void,
×
NEW
201
) -> AppLayerResult {
×
NEW
202
    let _eof = SCAppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC) > 0;
×
NEW
203
    let state = cast_pointer!(state, SSLProxyState);
×
NEW
204

×
NEW
205
    let buf = stream_slice.as_slice();
×
NEW
206
    state.parse_response(buf)
×
NEW
207
}
×
208

NEW
209
unsafe extern "C" fn sslproxy_state_get_tx(state: *mut c_void, tx_id: u64) -> *mut c_void {
×
NEW
210
    let state = cast_pointer!(state, SSLProxyState);
×
NEW
211
    match state.get_tx(tx_id) {
×
NEW
212
        Some(tx) => {
×
NEW
213
            return tx as *const _ as *mut _;
×
214
        }
215
        None => {
NEW
216
            return std::ptr::null_mut();
×
217
        }
218
    }
NEW
219
}
×
220

221
unsafe extern "C" fn sslproxy_state_get_tx_count(state: *mut c_void) -> u64 {
123✔
222
    let state = cast_pointer!(state, SSLProxyState);
123✔
223
    return state.tx_id;
123✔
224
}
123✔
225

NEW
226
unsafe extern "C" fn sslproxy_tx_get_alstate_progress(_tx: *mut c_void, _direction: u8) -> c_int {
×
NEW
227
    return 1;
×
NEW
228
}
×
229

230
export_tx_data_get!(sslproxy_get_tx_data, SSLProxyTransaction);
231
export_state_data_get!(sslproxy_get_state_data, SSLProxyState);
232

233
fn register_pattern_probe(proto: u8) -> i8 {
2,196✔
234
    let methods: Vec<&str> = vec![
2,196✔
235
        "SSLproxy:\0",
2,196✔
236
    ];
2,196✔
237
    let mut r = 0;
2,196✔
238
    unsafe {
239
        for method in methods {
4,392✔
240
            let depth = (method.len() - 1) as u16;
2,196✔
241
            r |= SCAppLayerProtoDetectPMRegisterPatternCS(
2,196✔
242
                proto,
2,196✔
243
                ALPROTO_SSLPROXY,
2,196✔
244
                method.as_ptr() as *const std::os::raw::c_char,
2,196✔
245
                depth,
2,196✔
246
                0,
2,196✔
247
                Direction::ToServer as u8,
2,196✔
248
            );
2,196✔
249
        }
2,196✔
250
    }
251

252
    if r == 0 {
2,196✔
253
        return 0;
2,196✔
254
    } else {
NEW
255
        return -1;
×
256
    }
257
}
2,196✔
258

259
// Parser name as a C style string.
260
const PARSER_NAME: &[u8] = b"sslproxy\0";
261

262
#[no_mangle]
263
pub unsafe extern "C" fn SCRegisterSSLProxyParser() {
2,196✔
264
    let parser = RustParser {
2,196✔
265
        name: PARSER_NAME.as_ptr() as *const c_char,
2,196✔
266
        default_port: std::ptr::null(),
2,196✔
267
        ipproto: IPPROTO_TCP,
2,196✔
268
        probe_ts: None,
2,196✔
269
        probe_tc: None,
2,196✔
270
        min_depth: 0,
2,196✔
271
        max_depth: 16,
2,196✔
272
        state_new: sslproxy_state_new,
2,196✔
273
        state_free: sslproxy_state_free,
2,196✔
274
        tx_free: sslproxy_state_tx_free,
2,196✔
275
        parse_ts: sslproxy_parse_request,
2,196✔
276
        parse_tc: sslproxy_parse_response,
2,196✔
277
        get_tx_count: sslproxy_state_get_tx_count,
2,196✔
278
        get_tx: sslproxy_state_get_tx,
2,196✔
279
        tx_comp_st_ts: 1,
2,196✔
280
        tx_comp_st_tc: 1,
2,196✔
281
        tx_get_progress: sslproxy_tx_get_alstate_progress,
2,196✔
282
        get_eventinfo: Some(SSLProxyEvent::get_event_info),
2,196✔
283
        get_eventinfo_byid: Some(SSLProxyEvent::get_event_info_by_id),
2,196✔
284
        localstorage_new: None,
2,196✔
285
        localstorage_free: None,
2,196✔
286
        get_tx_files: None,
2,196✔
287
        get_tx_iterator: Some(state_get_tx_iterator::<SSLProxyState, SSLProxyTransaction>),
2,196✔
288
        get_tx_data: sslproxy_get_tx_data,
2,196✔
289
        get_state_data: sslproxy_get_state_data,
2,196✔
290
        apply_tx_config: None,
2,196✔
291
        flags: 0, // no GAPS
2,196✔
292
        get_frame_id_by_name: None,
2,196✔
293
        get_frame_name_by_id: None,
2,196✔
294
        get_state_id_by_name: None,
2,196✔
295
        get_state_name_by_id: None,
2,196✔
296
    };
2,196✔
297

2,196✔
298
    let ip_proto_str = CString::new("tcp").unwrap();
2,196✔
299

2,196✔
300
    if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
2,196✔
301
        let alproto = applayer_register_protocol_detection(&parser, 1);
2,196✔
302
        ALPROTO_SSLPROXY = alproto;
2,196✔
303
        if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
2,196✔
304
            let _ = AppLayerRegisterParser(&parser, alproto);
2,196✔
305
        }
2,196✔
306
        if register_pattern_probe(IPPROTO_TCP) < 0 {
2,196✔
NEW
307
            return;
×
308
        }
2,196✔
309

2,196✔
310
        SCFlowEnableTranslation(PARSER_NAME.as_ptr() as *const c_char);
2,196✔
311
        SCLogDebug!("Rust sslproxy parser registered.");
NEW
312
    } else {
×
NEW
313
        SCLogDebug!("Protocol detector and parser disabled for SSLPROXY.");
×
NEW
314
    }
×
315
}
2,196✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc