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

jasonish / suricata / 23268535754

18 Mar 2026 09:42PM UTC coverage: 79.084% (-0.2%) from 79.297%
23268535754

push

github

jasonish
github-ci/builds: update ubuntu builds to rust 1.89

264897 of 334955 relevant lines covered (79.08%)

3068071.33 hits per line

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

86.19
/rust/src/http2/detect.rs
1
/* Copyright (C) 2020-2024 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::http2::{
19
    HTTP2Event, HTTP2Frame, HTTP2FrameTypeData, HTTP2State, HTTP2Transaction, HTTP2TransactionState,
20
};
21
use super::parser;
22
use crate::detect::uint::{
23
    detect_match_uint, detect_parse_array_uint_enum, detect_uint_match_at_index,
24
    DetectUintArrayData, DetectUintData, DetectUintIndex, DetectUintMode,
25
};
26
use crate::detect::EnumString;
27
use crate::direction::Direction;
28
use base64::{engine::general_purpose::STANDARD, Engine};
29
use std::ffi::CStr;
30
use std::os::raw::c_void;
31
use std::rc::Rc;
32
use suricata_sys::sys::DetectEngineThreadCtx;
33

34
#[no_mangle]
35
pub unsafe extern "C" fn SCHttp2TxHasFrametype(
11,555✔
36
    tx: *mut std::os::raw::c_void, direction: u8, ctx: *const std::os::raw::c_void,
11,555✔
37
) -> std::os::raw::c_int {
11,555✔
38
    let tx = cast_pointer!(tx, HTTP2Transaction);
11,555✔
39
    let ctx = cast_pointer!(ctx, DetectUintArrayData<u8>);
11,555✔
40
    let frames = if direction & Direction::ToServer as u8 != 0 {
11,555✔
41
        &tx.frames_ts
6,153✔
42
    } else {
43
        &tx.frames_tc
5,402✔
44
    };
45
    return detect_uint_match_at_index::<HTTP2Frame, u8>(
11,555✔
46
        frames,
11,555✔
47
        ctx,
11,555✔
48
        |f| Some(f.header.ftype),
11,555✔
49
        tx.state >= HTTP2TransactionState::HTTP2StateClosed,
11,555✔
50
    );
11,555✔
51
}
11,555✔
52

53
#[no_mangle]
54
pub unsafe extern "C" fn SCHttp2ParseFrametype(
4,434✔
55
    str: *const std::os::raw::c_char,
4,434✔
56
) -> *mut std::os::raw::c_void {
4,434✔
57
    let ft_name: &CStr = CStr::from_ptr(str); //unsafe
4,434✔
58
    if let Ok(s) = ft_name.to_str() {
4,434✔
59
        if let Some(ctx) = detect_parse_array_uint_enum::<u8, parser::HTTP2FrameType>(s) {
4,434✔
60
            let boxed = Box::new(ctx);
3,553✔
61
            // DetectUintArrayData<u8> cannot be cbindgend
3,553✔
62
            return Box::into_raw(boxed) as *mut c_void;
3,553✔
63
        }
881✔
64
    }
×
65
    return std::ptr::null_mut();
881✔
66
}
4,434✔
67

68
fn http2_tx_get_errorcode(f: &HTTP2Frame) -> Option<u32> {
96✔
69
    match &f.data {
96✔
70
        HTTP2FrameTypeData::GOAWAY(goaway) => Some(goaway.errorcode),
32✔
71
        HTTP2FrameTypeData::RSTSTREAM(rst) => Some(rst.errorcode),
×
72
        _ => None,
64✔
73
    }
74
}
96✔
75

76
#[no_mangle]
77
pub unsafe extern "C" fn SCHttp2TxHasErrorCode(
96✔
78
    tx: *mut std::os::raw::c_void, direction: u8, ctx: *const std::os::raw::c_void,
96✔
79
) -> std::os::raw::c_int {
96✔
80
    let tx = cast_pointer!(tx, HTTP2Transaction);
96✔
81
    let ctx = cast_pointer!(ctx, DetectUintArrayData<u32>);
96✔
82
    let frames = if direction & Direction::ToServer as u8 != 0 {
96✔
83
        &tx.frames_ts
48✔
84
    } else {
85
        &tx.frames_tc
48✔
86
    };
87
    return detect_uint_match_at_index::<HTTP2Frame, u32>(
96✔
88
        frames,
96✔
89
        ctx,
96✔
90
        http2_tx_get_errorcode,
96✔
91
        tx.state >= HTTP2TransactionState::HTTP2StateClosed,
96✔
92
    );
96✔
93
}
96✔
94

95
#[no_mangle]
96
pub unsafe extern "C" fn SCHttp2ParseErrorCode(
60✔
97
    str: *const std::os::raw::c_char,
60✔
98
) -> *mut std::os::raw::c_void {
60✔
99
    let ft_name: &CStr = CStr::from_ptr(str); //unsafe
60✔
100
    if let Ok(s) = ft_name.to_str() {
60✔
101
        // special case for backward compatibility, now parsed as HTTP11_REQUIRED
102
        if s.to_uppercase() == "HTTP_1_1_REQUIRED" {
60✔
103
            let ctx = DetectUintArrayData::<u32> {
2✔
104
                du: DetectUintData {
2✔
105
                    arg1: parser::HTTP2ErrorCode::Http11Required.into_u(),
2✔
106
                    arg2: 0,
2✔
107
                    mode: DetectUintMode::DetectUintModeEqual,
2✔
108
                },
2✔
109
                index: DetectUintIndex::Any,
2✔
110
                start: 0,
2✔
111
                end: 0,
2✔
112
            };
2✔
113
            let boxed = Box::new(ctx);
2✔
114
            return Box::into_raw(boxed) as *mut c_void;
2✔
115
        }
58✔
116
        if let Some(ctx) = detect_parse_array_uint_enum::<u32, parser::HTTP2ErrorCode>(s) {
58✔
117
            let boxed = Box::new(ctx);
19✔
118
            return Box::into_raw(boxed) as *mut c_void;
19✔
119
        }
39✔
120
    }
×
121
    return std::ptr::null_mut();
39✔
122
}
60✔
123

124
fn get_http2_priority(frame: &HTTP2Frame) -> Option<u8> {
28✔
125
    return match &frame.data {
28✔
126
        HTTP2FrameTypeData::PRIORITY(prio) => Some(prio.weight),
×
127
        HTTP2FrameTypeData::HEADERS(hd) => {
8✔
128
            if let Some(prio) = hd.priority {
8✔
129
                return Some(prio.weight);
8✔
130
            }
×
131
            None
×
132
        }
133
        _ => None,
20✔
134
    };
135
}
28✔
136

137
fn http2_match_priority(
53✔
138
    tx: &HTTP2Transaction, direction: Direction, ctx: &DetectUintArrayData<u8>,
53✔
139
) -> std::os::raw::c_int {
53✔
140
    let frames = if direction == Direction::ToServer {
53✔
141
        &tx.frames_ts
27✔
142
    } else {
143
        &tx.frames_tc
26✔
144
    };
145
    let eof = tx.state >= HTTP2TransactionState::HTTP2StateClosed;
53✔
146
    return detect_uint_match_at_index::<HTTP2Frame, u8>(frames, ctx, get_http2_priority, eof);
53✔
147
}
53✔
148

149
#[no_mangle]
150
pub unsafe extern "C" fn SCHttp2PriorityMatch(
53✔
151
    tx: *mut std::os::raw::c_void, direction: u8, ctx: *const std::os::raw::c_void,
53✔
152
) -> std::os::raw::c_int {
53✔
153
    let tx = cast_pointer!(tx, HTTP2Transaction);
53✔
154
    let ctx = cast_pointer!(ctx, DetectUintArrayData<u8>);
53✔
155
    return http2_match_priority(tx, direction.into(), ctx);
53✔
156
}
53✔
157

158
fn get_http2_window(frame: &HTTP2Frame) -> Option<u32> {
5,216✔
159
    if let HTTP2FrameTypeData::WINDOWUPDATE(wu) = &frame.data {
5,216✔
160
        return Some(wu.sizeinc);
224✔
161
    }
4,992✔
162
    return None;
4,992✔
163
}
5,216✔
164

165
fn http2_match_window(
8,046✔
166
    tx: &HTTP2Transaction, direction: Direction, ctx: &DetectUintArrayData<u32>,
8,046✔
167
) -> std::os::raw::c_int {
8,046✔
168
    let frames = if direction == Direction::ToServer {
8,046✔
169
        &tx.frames_ts
4,465✔
170
    } else {
171
        &tx.frames_tc
3,581✔
172
    };
173
    let eof = tx.state >= HTTP2TransactionState::HTTP2StateClosed;
8,046✔
174
    return detect_uint_match_at_index::<HTTP2Frame, u32>(frames, ctx, get_http2_window, eof);
8,046✔
175
}
8,046✔
176

177
#[no_mangle]
178
pub unsafe extern "C" fn SCHttp2WindowMatch(
8,046✔
179
    tx: *mut std::os::raw::c_void, direction: u8, ctx: *const std::os::raw::c_void,
8,046✔
180
) -> std::os::raw::c_int {
8,046✔
181
    let tx = cast_pointer!(tx, HTTP2Transaction);
8,046✔
182
    let ctx = cast_pointer!(ctx, DetectUintArrayData<u32>);
8,046✔
183
    return http2_match_window(tx, direction.into(), ctx);
8,046✔
184
}
8,046✔
185

186
#[no_mangle]
187
pub unsafe extern "C" fn SCHttp2DetectSettingsCtxParse(
14,555✔
188
    str: *const std::os::raw::c_char,
14,555✔
189
) -> *mut std::os::raw::c_void {
14,555✔
190
    let ft_name: &CStr = CStr::from_ptr(str); //unsafe
14,555✔
191
    if let Ok(s) = ft_name.to_str() {
14,555✔
192
        if let Ok((_, ctx)) = parser::http2_parse_settingsctx(s) {
14,555✔
193
            let boxed = Box::new(ctx);
8,444✔
194
            return Box::into_raw(boxed) as *mut _;
8,444✔
195
        }
6,111✔
196
    }
×
197
    return std::ptr::null_mut();
6,111✔
198
}
14,555✔
199

200
#[no_mangle]
201
pub unsafe extern "C" fn SCHttp2DetectSettingsCtxFree(ctx: *mut std::os::raw::c_void) {
8,444✔
202
    // Just unbox...
8,444✔
203
    std::mem::drop(Box::from_raw(ctx as *mut parser::DetectHTTP2settingsSigCtx));
8,444✔
204
}
8,444✔
205

206
fn http2_detect_settings_match(
122✔
207
    set: &[parser::HTTP2FrameSettings], ctx: &parser::DetectHTTP2settingsSigCtx,
122✔
208
) -> std::os::raw::c_int {
122✔
209
    for e in set {
382✔
210
        if e.id == ctx.id {
287✔
211
            match &ctx.value {
40✔
212
                None => {
213
                    return 1;
11✔
214
                }
215
                Some(x) => {
29✔
216
                    if detect_match_uint(x, e.value) {
29✔
217
                        return 1;
16✔
218
                    }
13✔
219
                }
220
            }
221
        }
247✔
222
    }
223
    return 0;
95✔
224
}
122✔
225

226
fn http2_detect_settingsctx_match(
8,248✔
227
    ctx: &parser::DetectHTTP2settingsSigCtx, tx: &HTTP2Transaction, direction: Direction,
8,248✔
228
) -> std::os::raw::c_int {
8,248✔
229
    if direction == Direction::ToServer {
8,248✔
230
        for i in 0..tx.frames_ts.len() {
4,559✔
231
            if let HTTP2FrameTypeData::SETTINGS(set) = &tx.frames_ts[i].data {
551✔
232
                if http2_detect_settings_match(set, ctx) != 0 {
77✔
233
                    return 1;
26✔
234
                }
51✔
235
            }
474✔
236
        }
237
    } else {
238
        for i in 0..tx.frames_tc.len() {
3,689✔
239
            if let HTTP2FrameTypeData::SETTINGS(set) = &tx.frames_tc[i].data {
3,315✔
240
                if http2_detect_settings_match(set, ctx) != 0 {
45✔
241
                    return 1;
1✔
242
                }
44✔
243
            }
3,270✔
244
        }
245
    }
246
    return 0;
8,221✔
247
}
8,248✔
248

249
#[no_mangle]
250
pub unsafe extern "C" fn SCHttp2DetectSettingsCtxMatch(
8,248✔
251
    ctx: *const std::os::raw::c_void, tx: *mut std::os::raw::c_void, direction: u8,
8,248✔
252
) -> std::os::raw::c_int {
8,248✔
253
    let ctx = cast_pointer!(ctx, parser::DetectHTTP2settingsSigCtx);
8,248✔
254
    let tx = cast_pointer!(tx, HTTP2Transaction);
8,248✔
255
    return http2_detect_settingsctx_match(ctx, tx, direction.into());
8,248✔
256
}
8,248✔
257

258
fn http2_detect_sizeupdate_match(
×
259
    blocks: &[parser::HTTP2FrameHeaderBlock], ctx: &DetectUintData<u64>,
×
260
) -> std::os::raw::c_int {
×
261
    for block in blocks.iter() {
×
262
        if block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate
×
263
            && detect_match_uint(ctx, block.sizeupdate)
×
264
        {
265
            return 1;
×
266
        }
×
267
    }
268
    return 0;
×
269
}
×
270

271
fn http2_header_blocks(frame: &HTTP2Frame) -> Option<&[parser::HTTP2FrameHeaderBlock]> {
16,730✔
272
    match &frame.data {
16,730✔
273
        HTTP2FrameTypeData::HEADERS(hd) => {
9,091✔
274
            return Some(&hd.blocks);
9,091✔
275
        }
276
        HTTP2FrameTypeData::CONTINUATION(hd) => {
4✔
277
            return Some(&hd.blocks);
4✔
278
        }
279
        HTTP2FrameTypeData::PUSHPROMISE(hd) => {
×
280
            return Some(&hd.blocks);
×
281
        }
282
        _ => {}
7,635✔
283
    }
7,635✔
284
    return None;
7,635✔
285
}
16,730✔
286

287
fn http2_detect_sizeupdatectx_match(
×
288
    ctx: &DetectUintData<u64>, tx: &HTTP2Transaction, direction: Direction,
×
289
) -> std::os::raw::c_int {
×
290
    if direction == Direction::ToServer {
×
291
        for i in 0..tx.frames_ts.len() {
×
292
            if let Some(blocks) = http2_header_blocks(&tx.frames_ts[i]) {
×
293
                if http2_detect_sizeupdate_match(blocks, ctx) != 0 {
×
294
                    return 1;
×
295
                }
×
296
            }
×
297
        }
298
    } else {
299
        for i in 0..tx.frames_tc.len() {
×
300
            if let Some(blocks) = http2_header_blocks(&tx.frames_tc[i]) {
×
301
                if http2_detect_sizeupdate_match(blocks, ctx) != 0 {
×
302
                    return 1;
×
303
                }
×
304
            }
×
305
        }
306
    }
307
    return 0;
×
308
}
×
309

310
#[no_mangle]
311
pub unsafe extern "C" fn SCHttp2DetectSizeUpdateCtxMatch(
×
312
    ctx: *const std::os::raw::c_void, tx: *mut std::os::raw::c_void, direction: u8,
×
313
) -> std::os::raw::c_int {
×
314
    let ctx = cast_pointer!(ctx, DetectUintData<u64>);
×
315
    let tx = cast_pointer!(tx, HTTP2Transaction);
×
316
    return http2_detect_sizeupdatectx_match(ctx, tx, direction.into());
×
317
}
×
318

319
//TODOask better syntax between SCHttp2TxGetHeaderName in argument
320
// and SCHttp2DetectSizeUpdateCtxMatch explicitly casting
321
#[no_mangle]
322
pub unsafe extern "C" fn SCHttp2TxGetHeaderName(
×
323
    _de: *mut DetectEngineThreadCtx, tx: *const c_void, direction: u8, nb: u32,
×
324
    buffer: *mut *const u8, buffer_len: *mut u32,
×
325
) -> bool {
×
326
    let tx = cast_pointer!(tx, HTTP2Transaction);
×
327
    let mut pos = 0_u32;
×
328
    match direction.into() {
×
329
        Direction::ToServer => {
330
            for i in 0..tx.frames_ts.len() {
×
331
                if let Some(blocks) = http2_header_blocks(&tx.frames_ts[i]) {
×
332
                    if nb < pos + blocks.len() as u32 {
×
333
                        let value = &blocks[(nb - pos) as usize].name;
×
334
                        *buffer = value.as_ptr(); //unsafe
×
335
                        *buffer_len = value.len() as u32;
×
336
                        return true;
×
337
                    } else {
×
338
                        pos += blocks.len() as u32;
×
339
                    }
×
340
                }
×
341
            }
342
        }
343
        Direction::ToClient => {
344
            for i in 0..tx.frames_tc.len() {
×
345
                if let Some(blocks) = http2_header_blocks(&tx.frames_tc[i]) {
×
346
                    if nb < pos + blocks.len() as u32 {
×
347
                        let value = &blocks[(nb - pos) as usize].name;
×
348
                        *buffer = value.as_ptr(); //unsafe
×
349
                        *buffer_len = value.len() as u32;
×
350
                        return true;
×
351
                    } else {
×
352
                        pos += blocks.len() as u32;
×
353
                    }
×
354
                }
×
355
            }
356
        }
357
    }
358
    return false;
×
359
}
×
360

361
fn http2_frames_get_header_firstvalue<'a>(
3,537✔
362
    tx: &'a mut HTTP2Transaction, direction: Direction, name: &str,
3,537✔
363
) -> Result<&'a [u8], ()> {
3,537✔
364
    let frames = if direction == Direction::ToServer {
3,537✔
365
        &tx.frames_ts
3,083✔
366
    } else {
367
        &tx.frames_tc
454✔
368
    };
369
    for frame in frames {
4,729✔
370
        if let Some(blocks) = http2_header_blocks(frame) {
2,505✔
371
            for block in blocks.iter() {
2,570✔
372
                if block.name.as_ref() == name.as_bytes() {
2,570✔
373
                    return Ok(&block.value);
1,313✔
374
                }
1,257✔
375
            }
376
        }
1,073✔
377
    }
378
    return Err(());
2,224✔
379
}
3,537✔
380

381
// same as http2_frames_get_header_value but returns a new Vec
382
// instead of using the transaction to store the result slice
383
pub fn http2_frames_get_header_value_vec(
2,502✔
384
    tx: &HTTP2Transaction, direction: Direction, name: &str,
2,502✔
385
) -> Result<Vec<u8>, ()> {
2,502✔
386
    let mut found = 0;
2,502✔
387
    let mut vec = Vec::new();
2,502✔
388
    let frames = if direction == Direction::ToServer {
2,502✔
389
        &tx.frames_ts
140✔
390
    } else {
391
        &tx.frames_tc
2,362✔
392
    };
393
    for frame in frames {
8,264✔
394
        if let Some(blocks) = http2_header_blocks(frame) {
5,762✔
395
            for block in blocks.iter() {
23,627✔
396
                if block.name.as_ref() == name.as_bytes() {
23,627✔
397
                    if found == 0 {
212✔
398
                        vec.extend_from_slice(&block.value);
212✔
399
                        found = 1;
212✔
400
                    } else if found == 1 && Rc::strong_count(&block.name) <= 2 {
212✔
401
                        vec.extend_from_slice(b", ");
×
402
                        vec.extend_from_slice(&block.value);
×
403
                        found = 2;
×
404
                    } else if Rc::strong_count(&block.name) <= 2 {
×
405
                        vec.extend_from_slice(b", ");
×
406
                        vec.extend_from_slice(&block.value);
×
407
                    }
×
408
                }
23,415✔
409
            }
410
        }
2,473✔
411
    }
412
    if found == 0 {
2,502✔
413
        return Err(());
2,290✔
414
    } else {
415
        return Ok(vec);
212✔
416
    }
417
}
2,502✔
418

419
#[derive(Debug, PartialEq)]
420
enum Http2Header<'a> {
421
    Single(&'a [u8]),
422
    Multiple(Vec<u8>),
423
}
424

425
fn http2_frames_get_header_value<'a>(
5,133✔
426
    tx: &'a HTTP2Transaction, direction: Direction, name: &str,
5,133✔
427
) -> Option<Http2Header<'a>> {
5,133✔
428
    let mut found = 0;
5,133✔
429
    let mut vec = Vec::new();
5,133✔
430
    let mut single = None;
5,133✔
431
    let frames = if direction == Direction::ToServer {
5,133✔
432
        &tx.frames_ts
3,339✔
433
    } else {
434
        &tx.frames_tc
1,794✔
435
    };
436
    for frame in frames {
9,602✔
437
        if let Some(blocks) = http2_header_blocks(frame) {
4,469✔
438
            for block in blocks.iter() {
13,759✔
439
                if block.name.as_ref() == name.as_bytes() {
13,759✔
440
                    if found == 0 {
768✔
441
                        single = Some(Http2Header::Single(&block.value));
647✔
442
                        found = 1;
647✔
443
                    } else if found == 1 && Rc::strong_count(&block.name) <= 2 {
647✔
444
                        if let Some(Http2Header::Single(s)) = single {
56✔
445
                            vec.extend_from_slice(s);
56✔
446
                        }
56✔
447
                        vec.extend_from_slice(b", ");
56✔
448
                        vec.extend_from_slice(&block.value);
56✔
449
                        found = 2;
56✔
450
                    } else if Rc::strong_count(&block.name) <= 2 {
65✔
451
                        vec.extend_from_slice(b", ");
3✔
452
                        vec.extend_from_slice(&block.value);
3✔
453
                    }
62✔
454
                }
12,991✔
455
            }
456
        }
2,821✔
457
    }
458
    if found == 0 {
5,133✔
459
        return None;
4,486✔
460
    } else if found == 1 {
647✔
461
        return single;
591✔
462
    } else {
463
        return Some(Http2Header::Multiple(vec));
56✔
464
    }
465
}
5,133✔
466

467
// we mutate the tx to cache req_line
468
fn http2_tx_get_req_line(tx: &mut HTTP2Transaction) {
246✔
469
    if !tx.req_line.is_empty() {
246✔
470
        return;
1✔
471
    }
245✔
472
    let empty = Vec::new();
245✔
473
    let mut req_line = Vec::new();
245✔
474
    let method =
245✔
475
        if let Ok(value) = http2_frames_get_header_firstvalue(tx, Direction::ToServer, ":method") {
245✔
476
            value
51✔
477
        } else {
478
            &empty
194✔
479
        };
480
    req_line.extend(method);
245✔
481
    req_line.push(b' ');
245✔
482

483
    let uri =
245✔
484
        if let Ok(value) = http2_frames_get_header_firstvalue(tx, Direction::ToServer, ":path") {
245✔
485
            value
51✔
486
        } else {
487
            &empty
194✔
488
        };
489
    req_line.extend(uri);
245✔
490
    req_line.extend(b" HTTP/2\r\n");
245✔
491
    tx.req_line.extend(req_line)
245✔
492
}
246✔
493

494
#[no_mangle]
495
pub unsafe extern "C" fn SCHttp2TxGetRequestLine(
246✔
496
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
246✔
497
) -> u8 {
246✔
498
    http2_tx_get_req_line(tx);
246✔
499
    *buffer = tx.req_line.as_ptr(); //unsafe
246✔
500
    *buffer_len = tx.req_line.len() as u32;
246✔
501
    return 1;
246✔
502
}
246✔
503

504
fn http2_tx_get_resp_line(tx: &mut HTTP2Transaction) {
115✔
505
    if !tx.resp_line.is_empty() {
115✔
506
        return;
×
507
    }
115✔
508
    let empty = Vec::new();
115✔
509
    let mut resp_line: Vec<u8> = Vec::new();
115✔
510

511
    let status =
115✔
512
        if let Ok(value) = http2_frames_get_header_firstvalue(tx, Direction::ToClient, ":status") {
115✔
513
            value
12✔
514
        } else {
515
            &empty
103✔
516
        };
517
    resp_line.extend(b"HTTP/2 ");
115✔
518
    resp_line.extend(status);
115✔
519
    resp_line.extend(b"\r\n");
115✔
520
    tx.resp_line.extend(resp_line)
115✔
521
}
115✔
522

523
#[no_mangle]
524
pub unsafe extern "C" fn SCHttp2TxGetResponseLine(
115✔
525
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
115✔
526
) -> u8 {
115✔
527
    http2_tx_get_resp_line(tx);
115✔
528
    *buffer = tx.resp_line.as_ptr(); //unsafe
115✔
529
    *buffer_len = tx.resp_line.len() as u32;
115✔
530
    return 1;
115✔
531
}
115✔
532

533
#[no_mangle]
534
pub unsafe extern "C" fn SCHttp2TxGetUri(
1,965✔
535
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
1,965✔
536
) -> u8 {
1,965✔
537
    if let Ok(value) = http2_frames_get_header_firstvalue(tx, Direction::ToServer, ":path") {
1,965✔
538
        *buffer = value.as_ptr(); //unsafe
702✔
539
        *buffer_len = value.len() as u32;
702✔
540
        return 1;
702✔
541
    }
1,263✔
542
    return 0;
1,263✔
543
}
1,965✔
544

545
#[no_mangle]
546
pub unsafe extern "C" fn SCHttp2TxGetMethod(
628✔
547
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
628✔
548
) -> u8 {
628✔
549
    if let Ok(value) = http2_frames_get_header_firstvalue(tx, Direction::ToServer, ":method") {
628✔
550
        *buffer = value.as_ptr(); //unsafe
364✔
551
        *buffer_len = value.len() as u32;
364✔
552
        return 1;
364✔
553
    }
264✔
554
    return 0;
264✔
555
}
628✔
556

557
#[no_mangle]
558
pub unsafe extern "C" fn SCHttp2TxGetHost(
368✔
559
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, tbuf: *mut c_void,
368✔
560
) -> u8 {
368✔
561
    let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
368✔
562
    if let Some(value) = http2_frames_get_header_value(tx, Direction::ToServer, ":authority") {
368✔
563
        match value {
162✔
564
            Http2Header::Single(v) => {
161✔
565
                *buffer = v.as_ptr(); //unsafe
161✔
566
                *buffer_len = v.len() as u32;
161✔
567
            }
161✔
568
            Http2Header::Multiple(v) => {
1✔
569
                tbuf.data = v;
1✔
570
                *buffer = tbuf.data.as_ptr(); //unsafe
1✔
571
                *buffer_len = tbuf.data.len() as u32;
1✔
572
            }
1✔
573
        }
574
        return 1;
162✔
575
    }
206✔
576
    return 0;
206✔
577
}
368✔
578

579
// returns a tuple with the value and its size
580
fn http2_normalize_host(ve: Http2Header) -> Http2Header {
199✔
581
    let vs = match &ve {
199✔
582
        Http2Header::Single(v) => v,
198✔
583
        Http2Header::Multiple(v) => v.as_slice(),
1✔
584
    };
585
    let (start, end) = match vs.iter().position(|&x| x == b'@') {
2,140✔
586
        Some(i) => match &vs[i + 1..].iter().position(|&x| x == b':') {
19✔
587
            Some(j) => (i + 1, i + 1 + j),
1✔
588
            None => (i + 1, vs.len()),
1✔
589
        },
590
        None => match vs.iter().position(|&x| x == b':') {
2,059✔
591
            Some(i) => (0, i),
10✔
592
            None => (0, vs.len()),
187✔
593
        },
594
    };
595
    return match ve {
199✔
596
        Http2Header::Single(v) => {
198✔
597
            let mut need_transform = false;
198✔
598
            for c in v.iter().take(end).skip(start) {
1,955✔
599
                if c.is_ascii_uppercase() {
1,955✔
600
                    need_transform = true;
7✔
601
                    break;
7✔
602
                }
1,948✔
603
            }
604
            if need_transform {
198✔
605
                let mut vec: Vec<u8> = Vec::with_capacity(end - start);
7✔
606
                for c in v.iter().take(end).skip(start) {
136✔
607
                    vec.push(c.to_ascii_lowercase());
136✔
608
                }
136✔
609
                Http2Header::Multiple(vec)
7✔
610
            } else {
611
                Http2Header::Single(&v[start..end])
191✔
612
            }
613
        }
614
        Http2Header::Multiple(mut v) => {
1✔
615
            if end < v.len() {
1✔
616
                v.truncate(end);
×
617
            }
1✔
618
            if start > 0 {
1✔
619
                v.drain(0..start);
×
620
            }
1✔
621
            for e in &mut v {
40✔
622
                e.make_ascii_lowercase();
39✔
623
            }
39✔
624
            Http2Header::Multiple(v)
1✔
625
        }
626
    };
627
}
199✔
628

629
#[no_mangle]
630
pub unsafe extern "C" fn SCHttp2TxGetHostNorm(
959✔
631
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, tbuf: *mut c_void,
959✔
632
) -> u8 {
959✔
633
    let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
959✔
634
    if let Some(value) = http2_frames_get_header_value(tx, Direction::ToServer, ":authority") {
959✔
635
        match http2_normalize_host(value) {
194✔
636
            Http2Header::Single(v) => {
187✔
637
                *buffer = v.as_ptr(); //unsafe
187✔
638
                *buffer_len = v.len() as u32;
187✔
639
            }
187✔
640
            Http2Header::Multiple(v) => {
7✔
641
                tbuf.data = v;
7✔
642
                *buffer = tbuf.data.as_ptr(); //unsafe
7✔
643
                *buffer_len = tbuf.data.len() as u32;
7✔
644
            }
7✔
645
        }
646
        return 1;
194✔
647
    }
765✔
648
    return 0;
765✔
649
}
959✔
650

651
#[no_mangle]
652
pub unsafe extern "C" fn SCHttp2TxGetUserAgent(
322✔
653
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, tbuf: *mut c_void,
322✔
654
) -> u8 {
322✔
655
    let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
322✔
656
    if let Some(value) = http2_frames_get_header_value(tx, Direction::ToServer, "user-agent") {
322✔
657
        match value {
58✔
658
            Http2Header::Single(v) => {
58✔
659
                *buffer = v.as_ptr(); //unsafe
58✔
660
                *buffer_len = v.len() as u32;
58✔
661
            }
58✔
662
            Http2Header::Multiple(v) => {
×
663
                tbuf.data = v;
×
664
                *buffer = tbuf.data.as_ptr(); //unsafe
×
665
                *buffer_len = tbuf.data.len() as u32;
×
666
            }
×
667
        }
668
        return 1;
58✔
669
    }
264✔
670
    return 0;
264✔
671
}
322✔
672

673
#[no_mangle]
674
pub unsafe extern "C" fn SCHttp2TxGetStatus(
339✔
675
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
339✔
676
) -> u8 {
339✔
677
    if let Ok(value) = http2_frames_get_header_firstvalue(tx, Direction::ToClient, ":status") {
339✔
678
        *buffer = value.as_ptr(); //unsafe
133✔
679
        *buffer_len = value.len() as u32;
133✔
680
        return 1;
133✔
681
    }
206✔
682
    return 0;
206✔
683
}
339✔
684

685
#[no_mangle]
686
pub unsafe extern "C" fn SCHttp2TxGetCookie(
1,236✔
687
    tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
1,236✔
688
    tbuf: *mut c_void,
1,236✔
689
) -> u8 {
1,236✔
690
    let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
1,236✔
691
    if direction == u8::from(Direction::ToServer) {
1,236✔
692
        if let Some(value) = http2_frames_get_header_value(tx, Direction::ToServer, "cookie") {
720✔
693
            match value {
113✔
694
                Http2Header::Single(v) => {
60✔
695
                    *buffer = v.as_ptr(); //unsafe
60✔
696
                    *buffer_len = v.len() as u32;
60✔
697
                }
60✔
698
                Http2Header::Multiple(v) => {
53✔
699
                    tbuf.data = v;
53✔
700
                    *buffer = tbuf.data.as_ptr(); //unsafe
53✔
701
                    *buffer_len = tbuf.data.len() as u32;
53✔
702
                }
53✔
703
            }
704
            return 1;
113✔
705
        }
607✔
706
    } else if let Some(value) = http2_frames_get_header_value(tx, Direction::ToClient, "set-cookie")
516✔
707
    {
708
        match value {
×
709
            Http2Header::Single(v) => {
×
710
                *buffer = v.as_ptr(); //unsafe
×
711
                *buffer_len = v.len() as u32;
×
712
            }
×
713
            Http2Header::Multiple(v) => {
×
714
                tbuf.data = v;
×
715
                *buffer = tbuf.data.as_ptr(); //unsafe
×
716
                *buffer_len = tbuf.data.len() as u32;
×
717
            }
×
718
        }
719
        return 1;
×
720
    }
516✔
721
    return 0;
1,123✔
722
}
1,236✔
723

724
#[no_mangle]
725
pub unsafe extern "C" fn SCHttp2TxGetHeaderValue(
2,247✔
726
    tx: &mut HTTP2Transaction, direction: u8, strname: *const std::os::raw::c_char,
2,247✔
727
    buffer: *mut *const u8, buffer_len: *mut u32, tbuf: *mut c_void,
2,247✔
728
) -> u8 {
2,247✔
729
    let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
2,247✔
730
    let hname: &CStr = CStr::from_ptr(strname); //unsafe
2,247✔
731
    if let Ok(s) = hname.to_str() {
2,247✔
732
        if let Some(value) = http2_frames_get_header_value(tx, direction.into(), &s.to_lowercase())
2,247✔
733
        {
734
            match value {
119✔
735
                Http2Header::Single(v) => {
119✔
736
                    *buffer = v.as_ptr(); //unsafe
119✔
737
                    *buffer_len = v.len() as u32;
119✔
738
                }
119✔
739
                Http2Header::Multiple(v) => {
×
740
                    tbuf.data = v;
×
741
                    *buffer = tbuf.data.as_ptr(); //unsafe
×
742
                    *buffer_len = tbuf.data.len() as u32;
×
743
                }
×
744
            }
745
            return 1;
119✔
746
        }
2,128✔
747
    }
×
748
    return 0;
2,128✔
749
}
2,247✔
750

751
fn http2_escape_header(blocks: &[parser::HTTP2FrameHeaderBlock], i: u32) -> Vec<u8> {
1,268✔
752
    //minimum size + 2 for escapes
1,268✔
753
    let normalsize = blocks[i as usize].value.len() + 2 + blocks[i as usize].name.len();
1,268✔
754
    let mut vec = Vec::with_capacity(normalsize);
1,268✔
755
    vec.extend_from_slice(&blocks[i as usize].name);
1,268✔
756
    vec.extend_from_slice(b": ");
1,268✔
757
    vec.extend_from_slice(&blocks[i as usize].value);
1,268✔
758
    return vec;
1,268✔
759
}
1,268✔
760

761
#[derive(Default)]
762
struct Http2ThreadBuf {
763
    data: Vec<u8>,
764
}
765

766
#[no_mangle]
767
pub unsafe extern "C" fn SCHttp2ThreadBufDataInit(_cfg: *mut c_void) -> *mut c_void {
685,984✔
768
    let boxed = Box::new(Http2ThreadBuf::default());
685,984✔
769
    return Box::into_raw(boxed) as *mut c_void;
685,984✔
770
}
685,984✔
771

772
#[no_mangle]
773
pub unsafe extern "C" fn SCHttp2ThreadBufDataFree(ctx: *mut c_void) {
685,968✔
774
    std::mem::drop(Box::from_raw(ctx as *mut Http2ThreadBuf));
685,968✔
775
}
685,968✔
776

777
#[no_mangle]
778
pub unsafe extern "C" fn SCHttp2TxGetHeaderNames(
633✔
779
    tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
633✔
780
    tbuf: *mut c_void,
633✔
781
) -> u8 {
633✔
782
    let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
633✔
783
    tbuf.data.clear();
633✔
784
    tbuf.data.extend_from_slice(b"\r\n");
633✔
785
    let frames = if direction & Direction::ToServer as u8 != 0 {
633✔
786
        &tx.frames_ts
334✔
787
    } else {
788
        &tx.frames_tc
299✔
789
    };
790
    for frame in frames {
1,691✔
791
        if let Some(blocks) = http2_header_blocks(frame) {
1,058✔
792
            for block in blocks.iter() {
2,782✔
793
                // we do not escape linefeeds in headers names
2,782✔
794
                tbuf.data.extend_from_slice(&block.name);
2,782✔
795
                tbuf.data.extend_from_slice(b"\r\n");
2,782✔
796
            }
2,782✔
797
        }
501✔
798
    }
799
    if tbuf.data.len() > 2 {
633✔
800
        tbuf.data.extend_from_slice(b"\r\n");
270✔
801
        *buffer = tbuf.data.as_ptr(); //unsafe
270✔
802
        *buffer_len = tbuf.data.len() as u32;
270✔
803
        return 1;
270✔
804
    }
363✔
805
    return 0;
363✔
806
}
633✔
807

808
fn http2_header_iscookie(direction: Direction, hname: &[u8]) -> bool {
3,608✔
809
    if let Ok(s) = std::str::from_utf8(hname) {
3,608✔
810
        if direction == Direction::ToServer {
3,606✔
811
            if s.to_lowercase() == "cookie" {
2,531✔
812
                return true;
9✔
813
            }
2,522✔
814
        } else if s.to_lowercase() == "set-cookie" {
1,075✔
815
            return true;
×
816
        }
1,075✔
817
    }
2✔
818
    return false;
3,599✔
819
}
3,608✔
820

821
fn http2_header_trimspaces(value: &[u8]) -> &[u8] {
3,602✔
822
    let mut start = 0;
3,602✔
823
    let mut end = value.len();
3,602✔
824
    while start < value.len() {
3,606✔
825
        if value[start] == b' ' || value[start] == b'\t' {
3,004✔
826
            start += 1;
4✔
827
        } else {
4✔
828
            break;
3,000✔
829
        }
830
    }
831
    while end > start {
3,603✔
832
        if value[end - 1] == b' ' || value[end - 1] == b'\t' {
3,001✔
833
            end -= 1;
1✔
834
        } else {
1✔
835
            break;
3,000✔
836
        }
837
    }
838
    return &value[start..end];
3,602✔
839
}
3,602✔
840

841
#[no_mangle]
842
pub unsafe extern "C" fn SCHttp2TxGetHeaders(
683✔
843
    tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
683✔
844
    tbuf: *mut c_void,
683✔
845
) -> u8 {
683✔
846
    let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
683✔
847
    tbuf.data.clear();
683✔
848
    let frames = if direction & Direction::ToServer as u8 != 0 {
683✔
849
        &tx.frames_ts
418✔
850
    } else {
851
        &tx.frames_tc
265✔
852
    };
853
    for frame in frames {
1,775✔
854
        if let Some(blocks) = http2_header_blocks(frame) {
1,092✔
855
            for block in blocks.iter() {
3,608✔
856
                if !http2_header_iscookie(direction.into(), &block.name) {
3,608✔
857
                    // we do not escape linefeeds nor : in headers names
3,599✔
858
                    tbuf.data.extend_from_slice(&block.name);
3,599✔
859
                    tbuf.data.extend_from_slice(b": ");
3,599✔
860
                    tbuf.data
3,599✔
861
                        .extend_from_slice(http2_header_trimspaces(&block.value));
3,599✔
862
                    tbuf.data.extend_from_slice(b"\r\n");
3,599✔
863
                }
3,599✔
864
            }
865
        }
516✔
866
    }
867
    if !tbuf.data.is_empty() {
683✔
868
        *buffer = tbuf.data.as_ptr(); //unsafe
289✔
869
        *buffer_len = tbuf.data.len() as u32;
289✔
870
        return 1;
289✔
871
    }
394✔
872
    return 0;
394✔
873
}
683✔
874

875
#[no_mangle]
876
pub unsafe extern "C" fn SCHttp2TxGetHeadersRaw(
136✔
877
    tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
136✔
878
    tbuf: *mut c_void,
136✔
879
) -> u8 {
136✔
880
    let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
136✔
881
    tbuf.data.clear();
136✔
882
    let frames = if direction & Direction::ToServer as u8 != 0 {
136✔
883
        &tx.frames_ts
136✔
884
    } else {
885
        &tx.frames_tc
×
886
    };
887
    for frame in frames {
311✔
888
        if let Some(blocks) = http2_header_blocks(frame) {
175✔
889
            for block in blocks.iter() {
1,138✔
890
                // we do not escape linefeeds nor : in headers names
1,138✔
891
                tbuf.data.extend_from_slice(&block.name);
1,138✔
892
                tbuf.data.extend_from_slice(b": ");
1,138✔
893
                tbuf.data.extend_from_slice(&block.value);
1,138✔
894
                tbuf.data.extend_from_slice(b"\r\n");
1,138✔
895
            }
1,138✔
896
        }
77✔
897
    }
898
    if !tbuf.data.is_empty() {
136✔
899
        *buffer = tbuf.data.as_ptr(); //unsafe
48✔
900
        *buffer_len = tbuf.data.len() as u32;
48✔
901
        return 1;
48✔
902
    }
88✔
903
    return 0;
88✔
904
}
136✔
905

906
#[derive(Default)]
907
struct Http2ThreadMultiBuf {
908
    data: Vec<Vec<u8>>,
909
}
910

911
#[no_mangle]
912
pub unsafe extern "C" fn SCHttp2ThreadMultiBufDataInit(_cfg: *mut c_void) -> *mut c_void {
85,748✔
913
    let boxed = Box::new(Http2ThreadMultiBuf::default());
85,748✔
914
    return Box::into_raw(boxed) as *mut c_void;
85,748✔
915
}
85,748✔
916

917
#[no_mangle]
918
pub unsafe extern "C" fn SCHttp2ThreadMultiBufDataFree(ctx: *mut c_void) {
85,746✔
919
    std::mem::drop(Box::from_raw(ctx as *mut Http2ThreadMultiBuf));
85,746✔
920
}
85,746✔
921

922
#[no_mangle]
923
pub unsafe extern "C" fn SCHttp2TxGetHeader(
1,539✔
924
    tbuf: *mut c_void, tx: *const c_void, direction: u8, nb: u32, buffer: *mut *const u8,
1,539✔
925
    buffer_len: *mut u32,
1,539✔
926
) -> bool {
1,539✔
927
    let tbuf = cast_pointer!(tbuf, Http2ThreadMultiBuf);
1,539✔
928
    let tx = cast_pointer!(tx, HTTP2Transaction);
1,539✔
929
    let mut pos = 0_u32;
1,539✔
930
    if nb == 0 {
1,539✔
931
        tbuf.data.clear();
271✔
932
    }
1,268✔
933
    match direction.into() {
1,539✔
934
        Direction::ToServer => {
935
            for i in 0..tx.frames_ts.len() {
1,672✔
936
                if let Some(blocks) = http2_header_blocks(&tx.frames_ts[i]) {
1,667✔
937
                    if nb < pos + blocks.len() as u32 {
1,495✔
938
                        let ehdr = http2_escape_header(blocks, nb - pos);
1,268✔
939
                        tbuf.data.push(ehdr);
1,268✔
940
                        let idx = tbuf.data.len() - 1;
1,268✔
941
                        let value = &tbuf.data[idx];
1,268✔
942
                        *buffer = value.as_ptr(); //unsafe
1,268✔
943
                        *buffer_len = value.len() as u32;
1,268✔
944
                        return true;
1,268✔
945
                    } else {
227✔
946
                        pos += blocks.len() as u32;
227✔
947
                    }
227✔
948
                }
172✔
949
            }
950
        }
951
        Direction::ToClient => {
952
            for i in 0..tx.frames_tc.len() {
4✔
953
                if let Some(blocks) = http2_header_blocks(&tx.frames_tc[i]) {
2✔
954
                    if nb < pos + blocks.len() as u32 {
×
955
                        let ehdr = http2_escape_header(blocks, nb - pos);
×
956
                        tbuf.data.push(ehdr);
×
957
                        let idx = tbuf.data.len() - 1;
×
958
                        let value = &tbuf.data[idx];
×
959
                        *buffer = value.as_ptr(); //unsafe
×
960
                        *buffer_len = value.len() as u32;
×
961
                        return true;
×
962
                    } else {
×
963
                        pos += blocks.len() as u32;
×
964
                    }
×
965
                }
2✔
966
            }
967
        }
968
    }
969
    return false;
271✔
970
}
1,539✔
971

972
fn http2_tx_set_header(state: &mut HTTP2State, name: &[u8], input: &[u8]) {
1,762✔
973
    let head = parser::HTTP2FrameHeader {
1,762✔
974
        length: 0,
1,762✔
975
        ftype: parser::HTTP2FrameType::Headers as u8,
1,762✔
976
        flags: 0,
1,762✔
977
        reserved: 0,
1,762✔
978
        stream_id: 1,
1,762✔
979
    };
1,762✔
980
    let mut blocks = Vec::new();
1,762✔
981
    let b = parser::HTTP2FrameHeaderBlock {
1,762✔
982
        name: Rc::new(name.to_vec()),
1,762✔
983
        value: Rc::new(input.to_vec()),
1,762✔
984
        error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
1,762✔
985
        sizeupdate: 0,
1,762✔
986
    };
1,762✔
987
    blocks.push(b);
1,762✔
988
    let hs = parser::HTTP2FrameHeaders {
1,762✔
989
        padlength: None,
1,762✔
990
        priority: None,
1,762✔
991
        blocks,
1,762✔
992
    };
1,762✔
993
    let txdata = HTTP2FrameTypeData::HEADERS(hs);
1,762✔
994
    let tx = state
1,762✔
995
        .find_or_create_tx(&head, &txdata, Direction::ToServer)
1,762✔
996
        .unwrap();
1,762✔
997
    tx.frames_ts.push(HTTP2Frame {
1,762✔
998
        header: head,
1,762✔
999
        data: txdata,
1,762✔
1000
    });
1,762✔
1001
    //we do not expect more data from client
1,762✔
1002
    tx.state = HTTP2TransactionState::HTTP2StateHalfClosedClient;
1,762✔
1003
}
1,762✔
1004

1005
#[no_mangle]
1006
pub unsafe extern "C" fn SCHttp2TxSetMethod(
244✔
1007
    state: &mut HTTP2State, buffer: *const u8, buffer_len: u32,
244✔
1008
) {
244✔
1009
    let slice = build_slice!(buffer, buffer_len as usize);
244✔
1010
    http2_tx_set_header(state, ":method".as_bytes(), slice)
244✔
1011
}
244✔
1012

1013
#[no_mangle]
1014
pub unsafe extern "C" fn SCHttp2TxSetUri(
243✔
1015
    state: &mut HTTP2State, buffer: *const u8, buffer_len: u32,
243✔
1016
) {
243✔
1017
    let slice = build_slice!(buffer, buffer_len as usize);
243✔
1018
    http2_tx_set_header(state, ":path".as_bytes(), slice)
243✔
1019
}
243✔
1020

1021
fn http2_tx_set_settings(state: &mut HTTP2State, input: &[u8]) {
206✔
1022
    match STANDARD.decode(input) {
206✔
1023
        Ok(dec) => {
182✔
1024
            if dec.len() % 6 != 0 {
182✔
1025
                state.set_event(HTTP2Event::InvalidHttp1Settings);
×
1026
            }
182✔
1027

1028
            let head = parser::HTTP2FrameHeader {
182✔
1029
                length: dec.len() as u32,
182✔
1030
                ftype: parser::HTTP2FrameType::Settings as u8,
182✔
1031
                flags: 0,
182✔
1032
                reserved: 0,
182✔
1033
                stream_id: 0,
182✔
1034
            };
182✔
1035

182✔
1036
            match parser::http2_parse_frame_settings(&dec) {
182✔
1037
                Ok((_, set)) => {
182✔
1038
                    let txdata = HTTP2FrameTypeData::SETTINGS(set);
182✔
1039
                    let tx = state
182✔
1040
                        .find_or_create_tx(&head, &txdata, Direction::ToServer)
182✔
1041
                        .unwrap();
182✔
1042
                    tx.frames_ts.push(HTTP2Frame {
182✔
1043
                        header: head,
182✔
1044
                        data: txdata,
182✔
1045
                    });
182✔
1046
                }
182✔
1047
                Err(_) => {
×
1048
                    state.set_event(HTTP2Event::InvalidHttp1Settings);
×
1049
                }
×
1050
            }
1051
        }
1052
        Err(_) => {
24✔
1053
            state.set_event(HTTP2Event::InvalidHttp1Settings);
24✔
1054
        }
24✔
1055
    }
1056
}
206✔
1057

1058
fn http2_caseinsensitive_cmp(s1: &[u8], s2: &str) -> bool {
1,275✔
1059
    if let Ok(s) = std::str::from_utf8(s1) {
1,275✔
1060
        return s.to_lowercase() == s2;
1,263✔
1061
    }
12✔
1062
    return false;
12✔
1063
}
1,275✔
1064

1065
#[no_mangle]
1066
pub unsafe extern "C" fn SCHttp2TxAddHeader(
1,481✔
1067
    state: &mut HTTP2State, name: *const u8, name_len: u32, value: *const u8, value_len: u32,
1,481✔
1068
) {
1,481✔
1069
    let slice_name = build_slice!(name, name_len as usize);
1,481✔
1070
    let slice_value = build_slice!(value, value_len as usize);
1,481✔
1071
    if slice_name == "HTTP2-Settings".as_bytes() {
1,481✔
1072
        http2_tx_set_settings(state, slice_value)
206✔
1073
    } else if http2_caseinsensitive_cmp(slice_name, "host") {
1,275✔
1074
        http2_tx_set_header(state, ":authority".as_bytes(), slice_value)
228✔
1075
    } else {
1076
        http2_tx_set_header(state, slice_name, slice_value)
1,047✔
1077
    }
1078
}
1,481✔
1079

1080
#[cfg(test)]
1081
mod tests {
1082

1083
    use super::*;
1084

1085
    #[test]
1086
    fn test_http2_normalize_host() {
1✔
1087
        let buf0 = "aBC.com:1234".as_bytes();
1✔
1088
        let r0 = http2_normalize_host(Http2Header::Single(buf0));
1✔
1089
        assert_eq!(r0, Http2Header::Multiple("abc.com".as_bytes().to_vec()));
1✔
1090
        let buf1 = "oisf.net".as_bytes();
1✔
1091
        let r1 = http2_normalize_host(Http2Header::Single(buf1));
1✔
1092
        assert_eq!(r1, Http2Header::Single("oisf.net".as_bytes()));
1✔
1093
        let buf2 = "localhost:3000".as_bytes();
1✔
1094
        let r2 = http2_normalize_host(Http2Header::Single(buf2));
1✔
1095
        assert_eq!(r2, Http2Header::Single("localhost".as_bytes()));
1✔
1096
        let buf3 = "user:pass@localhost".as_bytes();
1✔
1097
        let r3 = http2_normalize_host(Http2Header::Single(buf3));
1✔
1098
        assert_eq!(r3, Http2Header::Single("localhost".as_bytes()));
1✔
1099
        let buf4 = "user:pass@localhost:123".as_bytes();
1✔
1100
        let r4 = http2_normalize_host(Http2Header::Single(buf4));
1✔
1101
        assert_eq!(r4, Http2Header::Single("localhost".as_bytes()));
1✔
1102
    }
1✔
1103

1104
    #[test]
1105
    fn test_http2_header_trimspaces() {
1✔
1106
        let buf0 = "nospaces".as_bytes();
1✔
1107
        let r0 = http2_header_trimspaces(buf0);
1✔
1108
        assert_eq!(r0, "nospaces".as_bytes());
1✔
1109
        let buf1 = " spaces\t".as_bytes();
1✔
1110
        let r1 = http2_header_trimspaces(buf1);
1✔
1111
        assert_eq!(r1, "spaces".as_bytes());
1✔
1112
        let buf2 = " \t".as_bytes();
1✔
1113
        let r2 = http2_header_trimspaces(buf2);
1✔
1114
        assert_eq!(r2, "".as_bytes());
1✔
1115
    }
1✔
1116

1117
    #[test]
1118
    fn test_http2_frames_get_header_value() {
1✔
1119
        let mut tx = HTTP2Transaction::new();
1✔
1120
        let head = parser::HTTP2FrameHeader {
1✔
1121
            length: 0,
1✔
1122
            ftype: parser::HTTP2FrameType::Headers as u8,
1✔
1123
            flags: 0,
1✔
1124
            reserved: 0,
1✔
1125
            stream_id: 1,
1✔
1126
        };
1✔
1127
        let mut blocks = Vec::new();
1✔
1128
        let b = parser::HTTP2FrameHeaderBlock {
1✔
1129
            name: "Host".as_bytes().to_vec().into(),
1✔
1130
            value: "abc.com".as_bytes().to_vec().into(),
1✔
1131
            error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
1✔
1132
            sizeupdate: 0,
1✔
1133
        };
1✔
1134
        blocks.push(b);
1✔
1135
        let b2 = parser::HTTP2FrameHeaderBlock {
1✔
1136
            name: "Host".as_bytes().to_vec().into(),
1✔
1137
            value: "efg.net".as_bytes().to_vec().into(),
1✔
1138
            error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
1✔
1139
            sizeupdate: 0,
1✔
1140
        };
1✔
1141
        blocks.push(b2);
1✔
1142
        let hs = parser::HTTP2FrameHeaders {
1✔
1143
            padlength: None,
1✔
1144
            priority: None,
1✔
1145
            blocks,
1✔
1146
        };
1✔
1147
        let txdata = HTTP2FrameTypeData::HEADERS(hs);
1✔
1148
        tx.frames_ts.push(HTTP2Frame {
1✔
1149
            header: head,
1✔
1150
            data: txdata,
1✔
1151
        });
1✔
1152
        match http2_frames_get_header_value(&tx, Direction::ToServer, "Host") {
1✔
1153
            Some(Http2Header::Multiple(x)) => {
1✔
1154
                assert_eq!(x, "abc.com, efg.net".as_bytes());
1✔
1155
            }
1156
            _ => {
1157
                panic!("Result should have been a multiple header value");
1158
            }
1159
        }
1160
    }
1✔
1161
}
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