/* Copyright (C) 2020 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 2 along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ use super::http2::{ HTTP2Event, HTTP2Frame, HTTP2FrameTypeData, HTTP2State, HTTP2Transaction, HTTP2TransactionState, }; use super::parser; use crate::core::STREAM_TOSERVER; use std::ffi::CStr; use std::str::FromStr; fn http2_tx_has_frametype( tx: &mut HTTP2Transaction, direction: u8, value: u8, ) -> std::os::raw::c_int { if direction & STREAM_TOSERVER != 0 { for i in 0..tx.frames_ts.len() { if tx.frames_ts[i].header.ftype as u8 == value { return 1; } } } else { for i in 0..tx.frames_tc.len() { if tx.frames_tc[i].header.ftype as u8 == value { return 1; } } } return 0; } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_has_frametype( tx: *mut std::os::raw::c_void, direction: u8, value: u8, ) -> std::os::raw::c_int { let tx = cast_pointer!(tx, HTTP2Transaction); return http2_tx_has_frametype(tx, direction, value); } #[no_mangle] pub unsafe extern "C" fn rs_http2_parse_frametype( str: *const std::os::raw::c_char, ) -> std::os::raw::c_int { let ft_name: &CStr = CStr::from_ptr(str); //unsafe if let Ok(s) = ft_name.to_str() { if let Ok(x) = parser::HTTP2FrameType::from_str(s) { return x as i32; } } return -1; } fn http2_tx_has_errorcode( tx: &mut HTTP2Transaction, direction: u8, code: u32, ) -> std::os::raw::c_int { if direction & STREAM_TOSERVER != 0 { for i in 0..tx.frames_ts.len() { match tx.frames_ts[i].data { HTTP2FrameTypeData::GOAWAY(goaway) => { if goaway.errorcode == code { return 1; } } HTTP2FrameTypeData::RSTSTREAM(rst) => { if rst.errorcode == code { return 1; } } _ => {} } } } else { for i in 0..tx.frames_tc.len() { match tx.frames_tc[i].data { HTTP2FrameTypeData::GOAWAY(goaway) => { if goaway.errorcode as u32 == code { return 1; } } HTTP2FrameTypeData::RSTSTREAM(rst) => { if rst.errorcode as u32 == code { return 1; } } _ => {} } } } return 0; } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_has_errorcode( tx: *mut std::os::raw::c_void, direction: u8, code: u32, ) -> std::os::raw::c_int { let tx = cast_pointer!(tx, HTTP2Transaction); return http2_tx_has_errorcode(tx, direction, code); } #[no_mangle] pub unsafe extern "C" fn rs_http2_parse_errorcode( str: *const std::os::raw::c_char, ) -> std::os::raw::c_int { let ft_name: &CStr = CStr::from_ptr(str); //unsafe if let Ok(s) = ft_name.to_str() { if let Ok(x) = parser::HTTP2ErrorCode::from_str(s) { return x as i32; } } return -1; } fn http2_tx_get_next_priority( tx: &mut HTTP2Transaction, direction: u8, nb: u32, ) -> std::os::raw::c_int { let mut pos = 0 as u32; if direction & STREAM_TOSERVER != 0 { for i in 0..tx.frames_ts.len() { match &tx.frames_ts[i].data { HTTP2FrameTypeData::PRIORITY(prio) => { if pos == nb { return prio.weight as i32; } else { pos = pos + 1; } } HTTP2FrameTypeData::HEADERS(hd) => { if let Some(prio) = hd.priority { if pos == nb { return prio.weight as i32; } else { pos = pos + 1; } } } _ => {} } } } else { for i in 0..tx.frames_tc.len() { match &tx.frames_tc[i].data { HTTP2FrameTypeData::PRIORITY(prio) => { if pos == nb { return prio.weight as i32; } else { pos = pos + 1; } } HTTP2FrameTypeData::HEADERS(hd) => { if let Some(prio) = hd.priority { if pos == nb { return prio.weight as i32; } else { pos = pos + 1; } } } _ => {} } } } return -1; } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_next_priority( tx: *mut std::os::raw::c_void, direction: u8, nb: u32, ) -> std::os::raw::c_int { let tx = cast_pointer!(tx, HTTP2Transaction); return http2_tx_get_next_priority(tx, direction, nb); } fn http2_tx_get_next_window( tx: &mut HTTP2Transaction, direction: u8, nb: u32, ) -> std::os::raw::c_int { let mut pos = 0 as u32; if direction & STREAM_TOSERVER != 0 { for i in 0..tx.frames_ts.len() { match tx.frames_ts[i].data { HTTP2FrameTypeData::WINDOWUPDATE(wu) => { if pos == nb { return wu.sizeinc as i32; } else { pos = pos + 1; } } _ => {} } } } else { for i in 0..tx.frames_tc.len() { match tx.frames_tc[i].data { HTTP2FrameTypeData::WINDOWUPDATE(wu) => { if pos == nb { return wu.sizeinc as i32; } else { pos = pos + 1; } } _ => {} } } } return -1; } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_next_window( tx: *mut std::os::raw::c_void, direction: u8, nb: u32, ) -> std::os::raw::c_int { let tx = cast_pointer!(tx, HTTP2Transaction); return http2_tx_get_next_window(tx, direction, nb); } #[no_mangle] pub unsafe extern "C" fn rs_http2_detect_settingsctx_parse( str: *const std::os::raw::c_char, ) -> *mut std::os::raw::c_void { let ft_name: &CStr = CStr::from_ptr(str); //unsafe if let Ok(s) = ft_name.to_str() { if let Ok((_, ctx)) = parser::http2_parse_settingsctx(s) { let boxed = Box::new(ctx); return Box::into_raw(boxed) as *mut _; } } return std::ptr::null_mut(); } #[no_mangle] pub unsafe extern "C" fn rs_http2_detect_settingsctx_free(ctx: *mut std::os::raw::c_void) { // Just unbox... std::mem::drop(Box::from_raw(ctx as *mut parser::DetectHTTP2settingsSigCtx)); } fn http2_detect_settings_match( set: &Vec, ctx: &parser::DetectHTTP2settingsSigCtx, ) -> std::os::raw::c_int { for i in 0..set.len() { if set[i].id == ctx.id { match &ctx.value { None => { return 1; } Some(x) => match x.mode { parser::DetectUintMode::DetectUintModeEqual => { if set[i].value == x.value { return 1; } } parser::DetectUintMode::DetectUintModeLt => { if set[i].value <= x.value { return 1; } } parser::DetectUintMode::DetectUintModeGt => { if set[i].value >= x.value { return 1; } } parser::DetectUintMode::DetectUintModeRange => { if set[i].value <= x.value && set[i].value >= x.valrange { return 1; } } }, } } } return 0; } fn http2_detect_settingsctx_match( ctx: &mut parser::DetectHTTP2settingsSigCtx, tx: &mut HTTP2Transaction, direction: u8, ) -> std::os::raw::c_int { if direction & STREAM_TOSERVER != 0 { for i in 0..tx.frames_ts.len() { match &tx.frames_ts[i].data { HTTP2FrameTypeData::SETTINGS(set) => { if http2_detect_settings_match(set, ctx) != 0 { return 1; } } _ => {} } } } else { for i in 0..tx.frames_tc.len() { match &tx.frames_tc[i].data { HTTP2FrameTypeData::SETTINGS(set) => { if http2_detect_settings_match(set, ctx) != 0 { return 1; } } _ => {} } } } return 0; } #[no_mangle] pub unsafe extern "C" fn rs_http2_detect_settingsctx_match( ctx: *const std::os::raw::c_void, tx: *mut std::os::raw::c_void, direction: u8, ) -> std::os::raw::c_int { let ctx = cast_pointer!(ctx, parser::DetectHTTP2settingsSigCtx); let tx = cast_pointer!(tx, HTTP2Transaction); return http2_detect_settingsctx_match(ctx, tx, direction); } #[no_mangle] pub unsafe extern "C" fn rs_detect_u64_parse( str: *const std::os::raw::c_char, ) -> *mut std::os::raw::c_void { let ft_name: &CStr = CStr::from_ptr(str); //unsafe if let Ok(s) = ft_name.to_str() { if let Ok((_, ctx)) = parser::detect_parse_u64(s) { let boxed = Box::new(ctx); return Box::into_raw(boxed) as *mut _; } } return std::ptr::null_mut(); } #[no_mangle] pub unsafe extern "C" fn rs_detect_u64_free(ctx: *mut std::os::raw::c_void) { // Just unbox... std::mem::drop(Box::from_raw(ctx as *mut parser::DetectU64Data)); } fn http2_detect_sizeupdate_match( hd: &parser::HTTP2FrameHeaders, ctx: &parser::DetectU64Data, ) -> std::os::raw::c_int { for i in 0..hd.blocks.len() { match ctx.mode { parser::DetectUintMode::DetectUintModeEqual => { if hd.blocks[i].sizeupdate == ctx.value && hd.blocks[i].error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { return 1; } } parser::DetectUintMode::DetectUintModeLt => { if hd.blocks[i].sizeupdate <= ctx.value && hd.blocks[i].error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { return 1; } } parser::DetectUintMode::DetectUintModeGt => { if hd.blocks[i].sizeupdate >= ctx.value && hd.blocks[i].error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { return 1; } } parser::DetectUintMode::DetectUintModeRange => { if hd.blocks[i].sizeupdate <= ctx.value && hd.blocks[i].sizeupdate >= ctx.valrange && hd.blocks[i].error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { return 1; } } } } return 0; } fn http2_detect_sizeupdatectx_match( ctx: &mut parser::DetectU64Data, tx: &mut HTTP2Transaction, direction: u8, ) -> std::os::raw::c_int { if direction & STREAM_TOSERVER != 0 { for i in 0..tx.frames_ts.len() { match &tx.frames_ts[i].data { HTTP2FrameTypeData::HEADERS(hd) => { if http2_detect_sizeupdate_match(hd, ctx) != 0 { return 1; } } _ => {} } } } else { for i in 0..tx.frames_tc.len() { match &tx.frames_tc[i].data { HTTP2FrameTypeData::HEADERS(hd) => { if http2_detect_sizeupdate_match(hd, ctx) != 0 { return 1; } } _ => {} } } } return 0; } #[no_mangle] pub unsafe extern "C" fn rs_http2_detect_sizeupdatectx_match( ctx: *const std::os::raw::c_void, tx: *mut std::os::raw::c_void, direction: u8, ) -> std::os::raw::c_int { let ctx = cast_pointer!(ctx, parser::DetectU64Data); let tx = cast_pointer!(tx, HTTP2Transaction); return http2_detect_sizeupdatectx_match(ctx, tx, direction); } //TODOask better syntax between rs_http2_tx_get_header_name in argument // and rs_http2_detect_sizeupdatectx_match explicitly casting #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_header_name( tx: &mut HTTP2Transaction, direction: u8, nb: u32, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { let mut pos = 0 as u32; if direction & STREAM_TOSERVER != 0 { for i in 0..tx.frames_ts.len() { match &tx.frames_ts[i].data { HTTP2FrameTypeData::HEADERS(hd) => { if nb < pos + hd.blocks.len() as u32 { let value = &hd.blocks[(nb - pos) as usize].name; *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; } else { pos = pos + hd.blocks.len() as u32; } } _ => {} } } } else { for i in 0..tx.frames_tc.len() { match &tx.frames_tc[i].data { HTTP2FrameTypeData::HEADERS(hd) => { if nb < pos + hd.blocks.len() as u32 { let value = &hd.blocks[(nb - pos) as usize].name; *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; } else { pos = pos + hd.blocks.len() as u32; } } _ => {} } } } return 0; } fn http2_blocks_get_header_value<'a>( blocks: &'a Vec, name: &str, ) -> Result<&'a [u8], ()> { for j in 0..blocks.len() { if blocks[j].name == name.as_bytes().to_vec() { return Ok(&blocks[j].value); } } return Err(()); } fn http2_frames_get_header_value<'a>( frames: &'a Vec, name: &str, ) -> Result<&'a [u8], ()> { for i in 0..frames.len() { match &frames[i].data { HTTP2FrameTypeData::HEADERS(hd) => { if let Ok(value) = http2_blocks_get_header_value(&hd.blocks, name) { return Ok(value); } } HTTP2FrameTypeData::PUSHPROMISE(hd) => { if let Ok(value) = http2_blocks_get_header_value(&hd.blocks, name) { return Ok(value); } } HTTP2FrameTypeData::CONTINUATION(hd) => { if let Ok(value) = http2_blocks_get_header_value(&hd.blocks, name) { return Ok(value); } } _ => {} } } return Err(()); } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_uri( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { if let Ok(value) = http2_frames_get_header_value(&tx.frames_ts, ":path") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; } return 0; } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_method( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { if let Ok(value) = http2_frames_get_header_value(&tx.frames_ts, ":method") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; } return 0; } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_host( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { if let Ok(value) = http2_frames_get_header_value(&tx.frames_ts, ":authority") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; } return 0; } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_useragent( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { if let Ok(value) = http2_frames_get_header_value(&tx.frames_ts, "user-agent") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; } return 0; } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_status( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { if let Ok(value) = http2_frames_get_header_value(&tx.frames_tc, ":status") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; } return 0; } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_cookie( tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { if direction == STREAM_TOSERVER { if let Ok(value) = http2_frames_get_header_value(&tx.frames_ts, "cookie") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; } } else { if let Ok(value) = http2_frames_get_header_value(&tx.frames_tc, "set-cookie") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; } } return 0; } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_header_value( tx: &mut HTTP2Transaction, direction: u8, strname: *const std::os::raw::c_char, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { let hname: &CStr = CStr::from_ptr(strname); //unsafe if let Ok(s) = hname.to_str() { let frames = if direction == STREAM_TOSERVER { &tx.frames_ts } else { &tx.frames_tc }; if let Ok(value) = http2_frames_get_header_value(frames, &s.to_lowercase()) { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; } } return 0; } fn http2_escape_header(hd: &parser::HTTP2FrameHeaders, i: u32) -> Vec { //minimum size + 2 for escapes let normalsize = hd.blocks[i as usize].value.len() + 2 + hd.blocks[i as usize].name.len() + 2; let mut vec = Vec::with_capacity(normalsize); for j in 0..hd.blocks[i as usize].name.len() { vec.push(hd.blocks[i as usize].name[j]); if hd.blocks[i as usize].name[j] == ':' as u8 { vec.push(':' as u8); } } vec.push(':' as u8); vec.push(' ' as u8); for j in 0..hd.blocks[i as usize].value.len() { vec.push(hd.blocks[i as usize].value[j]); if hd.blocks[i as usize].value[j] == ':' as u8 { vec.push(':' as u8); } } return vec; } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_header( tx: &mut HTTP2Transaction, direction: u8, nb: u32, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { let mut pos = 0 as u32; if direction & STREAM_TOSERVER != 0 { for i in 0..tx.frames_ts.len() { match &tx.frames_ts[i].data { HTTP2FrameTypeData::HEADERS(hd) => { if nb < pos + hd.blocks.len() as u32 { let ehdr = http2_escape_header(hd, nb - pos); tx.escaped.push(ehdr); let idx = tx.escaped.len() - 1; let value = &tx.escaped[idx]; *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; } else { pos = pos + hd.blocks.len() as u32; } } _ => {} } } } else { for i in 0..tx.frames_tc.len() { match &tx.frames_tc[i].data { HTTP2FrameTypeData::HEADERS(hd) => { if nb < pos + hd.blocks.len() as u32 { let ehdr = http2_escape_header(hd, nb - pos); tx.escaped.push(ehdr); let idx = tx.escaped.len() - 1; let value = &tx.escaped[idx]; *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; } else { pos = pos + hd.blocks.len() as u32; } } _ => {} } } } return 0; } fn http2_tx_set_header(state: &mut HTTP2State, name: &[u8], input: &[u8]) { let head = parser::HTTP2FrameHeader { length: 0, ftype: parser::HTTP2FrameType::HEADERS as u8, flags: 0, reserved: 0, stream_id: 1, }; let mut blocks = Vec::new(); let b = parser::HTTP2FrameHeaderBlock { name: name.to_vec(), value: input.to_vec(), error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, sizeupdate: 0, }; blocks.push(b); let hs = parser::HTTP2FrameHeaders { padlength: None, priority: None, blocks: blocks, }; let txdata = HTTP2FrameTypeData::HEADERS(hs); let tx = state.find_or_create_tx(&head, &txdata, STREAM_TOSERVER); tx.frames_ts.push(HTTP2Frame { header: head, data: txdata, }); //we do not expect more data from client tx.state = HTTP2TransactionState::HTTP2StateHalfClosedClient; } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_set_method( state: &mut HTTP2State, buffer: *const u8, buffer_len: u32, ) { let slice = build_slice!(buffer, buffer_len as usize); http2_tx_set_header(state, ":method".as_bytes(), slice) } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_set_uri( state: &mut HTTP2State, buffer: *const u8, buffer_len: u32, ) { let slice = build_slice!(buffer, buffer_len as usize); http2_tx_set_header(state, ":path".as_bytes(), slice) } #[derive(Debug, PartialEq)] pub enum Http2Base64Error { InvalidBase64, } impl std::error::Error for Http2Base64Error {} impl std::fmt::Display for Http2Base64Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "invalid base64") } } fn http2_base64_map(input: u8) -> Result { match input { 43 => Ok(62), // + 47 => Ok(63), // / 48 => Ok(52), // 0 49 => Ok(53), // 1 50 => Ok(54), // 2 51 => Ok(55), // 3 52 => Ok(56), // 4 53 => Ok(57), // 5 54 => Ok(58), // 6 55 => Ok(59), // 7 56 => Ok(60), // 8 57 => Ok(61), // 9 65 => Ok(0), // A 66 => Ok(1), // B 67 => Ok(2), // C 68 => Ok(3), // D 69 => Ok(4), // E 70 => Ok(5), // F 71 => Ok(6), // G 72 => Ok(7), // H 73 => Ok(8), // I 74 => Ok(9), // J 75 => Ok(10), // K 76 => Ok(11), // L 77 => Ok(12), // M 78 => Ok(13), // N 79 => Ok(14), // O 80 => Ok(15), // P 81 => Ok(16), // Q 82 => Ok(17), // R 83 => Ok(18), // S 84 => Ok(19), // T 85 => Ok(20), // U 86 => Ok(21), // V 87 => Ok(22), // W 88 => Ok(23), // X 89 => Ok(24), // Y 90 => Ok(25), // Z 97 => Ok(26), // a 98 => Ok(27), // b 99 => Ok(28), // c 100 => Ok(29), // d 101 => Ok(30), // e 102 => Ok(31), // f 103 => Ok(32), // g 104 => Ok(33), // h 105 => Ok(34), // i 106 => Ok(35), // j 107 => Ok(36), // k 108 => Ok(37), // l 109 => Ok(38), // m 110 => Ok(39), // n 111 => Ok(40), // o 112 => Ok(41), // p 113 => Ok(42), // q 114 => Ok(43), // r 115 => Ok(44), // s 116 => Ok(45), // t 117 => Ok(46), // u 118 => Ok(47), // v 119 => Ok(48), // w 120 => Ok(49), // x 121 => Ok(50), // y 122 => Ok(51), // z _ => Err(Http2Base64Error::InvalidBase64), } } fn http2_decode_base64(input: &[u8]) -> Result, Http2Base64Error> { if input.len() % 4 != 0 { return Err(Http2Base64Error::InvalidBase64); } let mut r = vec![0; (input.len() * 3) / 4]; for i in 0..input.len() / 4 { let i1 = http2_base64_map(input[4 * i])?; let i2 = http2_base64_map(input[4 * i + 1])?; let i3 = http2_base64_map(input[4 * i + 2])?; let i4 = http2_base64_map(input[4 * i + 3])?; r[3 * i] = (i1 << 2) | (i2 >> 4); r[3 * i + 1] = (i2 << 4) | (i3 >> 2); r[3 * i + 2] = (i3 << 6) | i4; } return Ok(r); } fn http2_tx_set_settings(state: &mut HTTP2State, input: &[u8]) { match http2_decode_base64(input) { Ok(dec) => { if dec.len() % 6 != 0 { state.set_event(HTTP2Event::InvalidHTTP1Settings); } let head = parser::HTTP2FrameHeader { length: dec.len() as u32, ftype: parser::HTTP2FrameType::SETTINGS as u8, flags: 0, reserved: 0, stream_id: 0, }; match parser::http2_parse_frame_settings(&dec) { Ok((_, set)) => { let txdata = HTTP2FrameTypeData::SETTINGS(set); let tx = state.find_or_create_tx(&head, &txdata, STREAM_TOSERVER); tx.frames_ts.push(HTTP2Frame { header: head, data: txdata, }); } Err(_) => { state.set_event(HTTP2Event::InvalidHTTP1Settings); } } } Err(_) => { state.set_event(HTTP2Event::InvalidHTTP1Settings); } } } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_add_header( state: &mut HTTP2State, name: *const u8, name_len: u32, value: *const u8, value_len: u32, ) { let slice_name = build_slice!(name, name_len as usize); let slice_value = build_slice!(value, value_len as usize); if slice_name == "HTTP2-Settings".as_bytes() { http2_tx_set_settings(state, slice_value) } else { http2_tx_set_header(state, slice_name, slice_value) } }