1 /* Copyright (C) 2020 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::core::{STREAM_TOCLIENT, STREAM_TOSERVER};
23 use std::ffi::CStr;
24 use std::mem::transmute;
25 use std::str::FromStr;
26
http2_tx_has_frametype( tx: &mut HTTP2Transaction, direction: u8, value: u8, ) -> std::os::raw::c_int27 fn http2_tx_has_frametype(
28 tx: &mut HTTP2Transaction, direction: u8, value: u8,
29 ) -> std::os::raw::c_int {
30 if direction & STREAM_TOSERVER != 0 {
31 for i in 0..tx.frames_ts.len() {
32 if tx.frames_ts[i].header.ftype as u8 == value {
33 return 1;
34 }
35 }
36 } else {
37 for i in 0..tx.frames_tc.len() {
38 if tx.frames_tc[i].header.ftype as u8 == value {
39 return 1;
40 }
41 }
42 }
43 return 0;
44 }
45
46 #[no_mangle]
rs_http2_tx_has_frametype( tx: *mut std::os::raw::c_void, direction: u8, value: u8, ) -> std::os::raw::c_int47 pub extern "C" fn rs_http2_tx_has_frametype(
48 tx: *mut std::os::raw::c_void, direction: u8, value: u8,
49 ) -> std::os::raw::c_int {
50 let tx = cast_pointer!(tx, HTTP2Transaction);
51 return http2_tx_has_frametype(tx, direction, value);
52 }
53
54 #[no_mangle]
rs_http2_parse_frametype( str: *const std::os::raw::c_char, ) -> std::os::raw::c_int55 pub unsafe extern "C" fn rs_http2_parse_frametype(
56 str: *const std::os::raw::c_char,
57 ) -> std::os::raw::c_int {
58 let ft_name: &CStr = CStr::from_ptr(str); //unsafe
59 if let Ok(s) = ft_name.to_str() {
60 if let Ok(x) = parser::HTTP2FrameType::from_str(s) {
61 return x as i32;
62 }
63 }
64 return -1;
65 }
66
http2_tx_has_errorcode( tx: &mut HTTP2Transaction, direction: u8, code: u32, ) -> std::os::raw::c_int67 fn http2_tx_has_errorcode(
68 tx: &mut HTTP2Transaction, direction: u8, code: u32,
69 ) -> std::os::raw::c_int {
70 if direction & STREAM_TOSERVER != 0 {
71 for i in 0..tx.frames_ts.len() {
72 match tx.frames_ts[i].data {
73 HTTP2FrameTypeData::GOAWAY(goaway) => {
74 if goaway.errorcode == code {
75 return 1;
76 }
77 }
78 HTTP2FrameTypeData::RSTSTREAM(rst) => {
79 if rst.errorcode == code {
80 return 1;
81 }
82 }
83 _ => {}
84 }
85 }
86 } else {
87 for i in 0..tx.frames_tc.len() {
88 match tx.frames_tc[i].data {
89 HTTP2FrameTypeData::GOAWAY(goaway) => {
90 if goaway.errorcode as u32 == code {
91 return 1;
92 }
93 }
94 HTTP2FrameTypeData::RSTSTREAM(rst) => {
95 if rst.errorcode as u32 == code {
96 return 1;
97 }
98 }
99 _ => {}
100 }
101 }
102 }
103 return 0;
104 }
105
106 #[no_mangle]
rs_http2_tx_has_errorcode( tx: *mut std::os::raw::c_void, direction: u8, code: u32, ) -> std::os::raw::c_int107 pub extern "C" fn rs_http2_tx_has_errorcode(
108 tx: *mut std::os::raw::c_void, direction: u8, code: u32,
109 ) -> std::os::raw::c_int {
110 let tx = cast_pointer!(tx, HTTP2Transaction);
111 return http2_tx_has_errorcode(tx, direction, code);
112 }
113
114 #[no_mangle]
rs_http2_parse_errorcode( str: *const std::os::raw::c_char, ) -> std::os::raw::c_int115 pub unsafe extern "C" fn rs_http2_parse_errorcode(
116 str: *const std::os::raw::c_char,
117 ) -> std::os::raw::c_int {
118 let ft_name: &CStr = CStr::from_ptr(str); //unsafe
119 if let Ok(s) = ft_name.to_str() {
120 if let Ok(x) = parser::HTTP2ErrorCode::from_str(s) {
121 return x as i32;
122 }
123 }
124 return -1;
125 }
126
http2_tx_get_next_priority( tx: &mut HTTP2Transaction, direction: u8, nb: u32, ) -> std::os::raw::c_int127 fn http2_tx_get_next_priority(
128 tx: &mut HTTP2Transaction, direction: u8, nb: u32,
129 ) -> std::os::raw::c_int {
130 let mut pos = 0 as u32;
131 if direction & STREAM_TOSERVER != 0 {
132 for i in 0..tx.frames_ts.len() {
133 match &tx.frames_ts[i].data {
134 HTTP2FrameTypeData::PRIORITY(prio) => {
135 if pos == nb {
136 return prio.weight as i32;
137 } else {
138 pos = pos + 1;
139 }
140 }
141 HTTP2FrameTypeData::HEADERS(hd) => {
142 if let Some(prio) = hd.priority {
143 if pos == nb {
144 return prio.weight as i32;
145 } else {
146 pos = pos + 1;
147 }
148 }
149 }
150 _ => {}
151 }
152 }
153 } else {
154 for i in 0..tx.frames_tc.len() {
155 match &tx.frames_tc[i].data {
156 HTTP2FrameTypeData::PRIORITY(prio) => {
157 if pos == nb {
158 return prio.weight as i32;
159 } else {
160 pos = pos + 1;
161 }
162 }
163 HTTP2FrameTypeData::HEADERS(hd) => {
164 if let Some(prio) = hd.priority {
165 if pos == nb {
166 return prio.weight as i32;
167 } else {
168 pos = pos + 1;
169 }
170 }
171 }
172 _ => {}
173 }
174 }
175 }
176 return -1;
177 }
178
179 #[no_mangle]
rs_http2_tx_get_next_priority( tx: *mut std::os::raw::c_void, direction: u8, nb: u32, ) -> std::os::raw::c_int180 pub extern "C" fn rs_http2_tx_get_next_priority(
181 tx: *mut std::os::raw::c_void, direction: u8, nb: u32,
182 ) -> std::os::raw::c_int {
183 let tx = cast_pointer!(tx, HTTP2Transaction);
184 return http2_tx_get_next_priority(tx, direction, nb);
185 }
186
http2_tx_get_next_window( tx: &mut HTTP2Transaction, direction: u8, nb: u32, ) -> std::os::raw::c_int187 fn http2_tx_get_next_window(
188 tx: &mut HTTP2Transaction, direction: u8, nb: u32,
189 ) -> std::os::raw::c_int {
190 let mut pos = 0 as u32;
191 if direction & STREAM_TOSERVER != 0 {
192 for i in 0..tx.frames_ts.len() {
193 match tx.frames_ts[i].data {
194 HTTP2FrameTypeData::WINDOWUPDATE(wu) => {
195 if pos == nb {
196 return wu.sizeinc as i32;
197 } else {
198 pos = pos + 1;
199 }
200 }
201 _ => {}
202 }
203 }
204 } else {
205 for i in 0..tx.frames_tc.len() {
206 match tx.frames_tc[i].data {
207 HTTP2FrameTypeData::WINDOWUPDATE(wu) => {
208 if pos == nb {
209 return wu.sizeinc as i32;
210 } else {
211 pos = pos + 1;
212 }
213 }
214 _ => {}
215 }
216 }
217 }
218 return -1;
219 }
220
221 #[no_mangle]
rs_http2_tx_get_next_window( tx: *mut std::os::raw::c_void, direction: u8, nb: u32, ) -> std::os::raw::c_int222 pub extern "C" fn rs_http2_tx_get_next_window(
223 tx: *mut std::os::raw::c_void, direction: u8, nb: u32,
224 ) -> std::os::raw::c_int {
225 let tx = cast_pointer!(tx, HTTP2Transaction);
226 return http2_tx_get_next_window(tx, direction, nb);
227 }
228
229 #[no_mangle]
rs_http2_parse_settingsid( str: *const std::os::raw::c_char, ) -> std::os::raw::c_int230 pub unsafe extern "C" fn rs_http2_parse_settingsid(
231 str: *const std::os::raw::c_char,
232 ) -> std::os::raw::c_int {
233 let ft_name: &CStr = CStr::from_ptr(str); //unsafe
234 if let Ok(s) = ft_name.to_str() {
235 if let Ok(x) = parser::HTTP2SettingsId::from_str(s) {
236 return x as i32;
237 }
238 }
239 return -1;
240 }
241
242 #[no_mangle]
rs_http2_detect_settingsctx_parse( str: *const std::os::raw::c_char, ) -> *mut std::os::raw::c_void243 pub unsafe extern "C" fn rs_http2_detect_settingsctx_parse(
244 str: *const std::os::raw::c_char,
245 ) -> *mut std::os::raw::c_void {
246 let ft_name: &CStr = CStr::from_ptr(str); //unsafe
247 if let Ok(s) = ft_name.to_str() {
248 if let Ok((_, ctx)) = parser::http2_parse_settingsctx(s) {
249 let boxed = Box::new(ctx);
250 return transmute(boxed); //unsafe
251 }
252 }
253 return std::ptr::null_mut();
254 }
255
256 #[no_mangle]
rs_http2_detect_settingsctx_free(ctx: *mut std::os::raw::c_void)257 pub unsafe extern "C" fn rs_http2_detect_settingsctx_free(ctx: *mut std::os::raw::c_void) {
258 // Just unbox...
259 let _ctx: Box<parser::DetectHTTP2settingsSigCtx> = transmute(ctx);
260 }
261
http2_detect_settings_match( set: &[parser::HTTP2FrameSettings], ctx: &parser::DetectHTTP2settingsSigCtx, ) -> std::os::raw::c_int262 fn http2_detect_settings_match(
263 set: &[parser::HTTP2FrameSettings], ctx: &parser::DetectHTTP2settingsSigCtx,
264 ) -> std::os::raw::c_int {
265 for i in 0..set.len() {
266 if set[i].id == ctx.id {
267 match &ctx.value {
268 None => {
269 return 1;
270 }
271 Some(x) => match x.mode {
272 parser::DetectUintMode::DetectUintModeEqual => {
273 if set[i].value == x.value {
274 return 1;
275 }
276 }
277 parser::DetectUintMode::DetectUintModeLt => {
278 if set[i].value <= x.value {
279 return 1;
280 }
281 }
282 parser::DetectUintMode::DetectUintModeGt => {
283 if set[i].value >= x.value {
284 return 1;
285 }
286 }
287 parser::DetectUintMode::DetectUintModeRange => {
288 if set[i].value <= x.value && set[i].value >= x.valrange {
289 return 1;
290 }
291 }
292 },
293 }
294 }
295 }
296 return 0;
297 }
298
http2_detect_settingsctx_match( ctx: &mut parser::DetectHTTP2settingsSigCtx, tx: &mut HTTP2Transaction, direction: u8, ) -> std::os::raw::c_int299 fn http2_detect_settingsctx_match(
300 ctx: &mut parser::DetectHTTP2settingsSigCtx, tx: &mut HTTP2Transaction, direction: u8,
301 ) -> std::os::raw::c_int {
302 if direction & STREAM_TOSERVER != 0 {
303 for i in 0..tx.frames_ts.len() {
304 match &tx.frames_ts[i].data {
305 HTTP2FrameTypeData::SETTINGS(set) => {
306 if http2_detect_settings_match(&set, ctx) != 0 {
307 return 1;
308 }
309 }
310 _ => {}
311 }
312 }
313 } else {
314 for i in 0..tx.frames_tc.len() {
315 match &tx.frames_tc[i].data {
316 HTTP2FrameTypeData::SETTINGS(set) => {
317 if http2_detect_settings_match(&set, ctx) != 0 {
318 return 1;
319 }
320 }
321 _ => {}
322 }
323 }
324 }
325 return 0;
326 }
327
328 #[no_mangle]
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_int329 pub extern "C" fn rs_http2_detect_settingsctx_match(
330 ctx: *const std::os::raw::c_void, tx: *mut std::os::raw::c_void, direction: u8,
331 ) -> std::os::raw::c_int {
332 let ctx = cast_pointer!(ctx, parser::DetectHTTP2settingsSigCtx);
333 let tx = cast_pointer!(tx, HTTP2Transaction);
334 return http2_detect_settingsctx_match(ctx, tx, direction);
335 }
336
337 #[no_mangle]
rs_detect_u64_parse( str: *const std::os::raw::c_char, ) -> *mut std::os::raw::c_void338 pub unsafe extern "C" fn rs_detect_u64_parse(
339 str: *const std::os::raw::c_char,
340 ) -> *mut std::os::raw::c_void {
341 let ft_name: &CStr = CStr::from_ptr(str); //unsafe
342 if let Ok(s) = ft_name.to_str() {
343 if let Ok((_, ctx)) = parser::detect_parse_u64(s) {
344 let boxed = Box::new(ctx);
345 return transmute(boxed); //unsafe
346 }
347 }
348 return std::ptr::null_mut();
349 }
350
351 #[no_mangle]
rs_detect_u64_free(ctx: *mut std::os::raw::c_void)352 pub unsafe extern "C" fn rs_detect_u64_free(ctx: *mut std::os::raw::c_void) {
353 // Just unbox...
354 let _ctx: Box<parser::DetectU64Data> = transmute(ctx);
355 }
356
http2_detect_sizeupdate_match( blocks: &[parser::HTTP2FrameHeaderBlock], ctx: &parser::DetectU64Data, ) -> std::os::raw::c_int357 fn http2_detect_sizeupdate_match(
358 blocks: &[parser::HTTP2FrameHeaderBlock], ctx: &parser::DetectU64Data,
359 ) -> std::os::raw::c_int {
360 for block in blocks.iter() {
361 match ctx.mode {
362 parser::DetectUintMode::DetectUintModeEqual => {
363 if block.sizeupdate == ctx.value
364 && block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate
365 {
366 return 1;
367 }
368 }
369 parser::DetectUintMode::DetectUintModeLt => {
370 if block.sizeupdate <= ctx.value
371 && block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate
372 {
373 return 1;
374 }
375 }
376 parser::DetectUintMode::DetectUintModeGt => {
377 if block.sizeupdate >= ctx.value
378 && block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate
379 {
380 return 1;
381 }
382 }
383 parser::DetectUintMode::DetectUintModeRange => {
384 if block.sizeupdate <= ctx.value
385 && block.sizeupdate >= ctx.valrange
386 && block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate
387 {
388 return 1;
389 }
390 }
391 }
392 }
393 return 0;
394 }
395
http2_header_blocks(frame: &HTTP2Frame) -> Option<&[parser::HTTP2FrameHeaderBlock]>396 fn http2_header_blocks(frame: &HTTP2Frame) -> Option<&[parser::HTTP2FrameHeaderBlock]> {
397 match &frame.data {
398 HTTP2FrameTypeData::HEADERS(hd) => {
399 return Some(&hd.blocks);
400 }
401 HTTP2FrameTypeData::CONTINUATION(hd) => {
402 return Some(&hd.blocks);
403 }
404 HTTP2FrameTypeData::PUSHPROMISE(hd) => {
405 return Some(&hd.blocks);
406 }
407 _ => {}
408 }
409 return None;
410 }
411
http2_detect_sizeupdatectx_match( ctx: &mut parser::DetectU64Data, tx: &mut HTTP2Transaction, direction: u8, ) -> std::os::raw::c_int412 fn http2_detect_sizeupdatectx_match(
413 ctx: &mut parser::DetectU64Data, tx: &mut HTTP2Transaction, direction: u8,
414 ) -> std::os::raw::c_int {
415 if direction & STREAM_TOSERVER != 0 {
416 for i in 0..tx.frames_ts.len() {
417 if let Some(blocks) = http2_header_blocks(&tx.frames_ts[i]) {
418 if http2_detect_sizeupdate_match(blocks, ctx) != 0 {
419 return 1;
420 }
421 }
422 }
423 } else {
424 for i in 0..tx.frames_tc.len() {
425 if let Some(blocks) = http2_header_blocks(&tx.frames_tc[i]) {
426 if http2_detect_sizeupdate_match(blocks, ctx) != 0 {
427 return 1;
428 }
429 }
430 }
431 }
432 return 0;
433 }
434
435 #[no_mangle]
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_int436 pub extern "C" fn rs_http2_detect_sizeupdatectx_match(
437 ctx: *const std::os::raw::c_void, tx: *mut std::os::raw::c_void, direction: u8,
438 ) -> std::os::raw::c_int {
439 let ctx = cast_pointer!(ctx, parser::DetectU64Data);
440 let tx = cast_pointer!(tx, HTTP2Transaction);
441 return http2_detect_sizeupdatectx_match(ctx, tx, direction);
442 }
443
444 //TODOask better syntax between rs_http2_tx_get_header_name in argument
445 // and rs_http2_detect_sizeupdatectx_match explicitly casting
446 #[no_mangle]
rs_http2_tx_get_header_name( tx: &mut HTTP2Transaction, direction: u8, nb: u32, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8447 pub unsafe extern "C" fn rs_http2_tx_get_header_name(
448 tx: &mut HTTP2Transaction, direction: u8, nb: u32, buffer: *mut *const u8, buffer_len: *mut u32,
449 ) -> u8 {
450 let mut pos = 0 as u32;
451 if direction & STREAM_TOSERVER != 0 {
452 for i in 0..tx.frames_ts.len() {
453 if let Some(blocks) = http2_header_blocks(&tx.frames_ts[i]) {
454 if nb < pos + blocks.len() as u32 {
455 let value = &blocks[(nb - pos) as usize].name;
456 *buffer = value.as_ptr(); //unsafe
457 *buffer_len = value.len() as u32;
458 return 1;
459 } else {
460 pos = pos + blocks.len() as u32;
461 }
462 }
463 }
464 } else {
465 for i in 0..tx.frames_tc.len() {
466 if let Some(blocks) = http2_header_blocks(&tx.frames_tc[i]) {
467 if nb < pos + blocks.len() as u32 {
468 let value = &blocks[(nb - pos) as usize].name;
469 *buffer = value.as_ptr(); //unsafe
470 *buffer_len = value.len() as u32;
471 return 1;
472 } else {
473 pos = pos + blocks.len() as u32;
474 }
475 }
476 }
477 }
478 return 0;
479 }
480
http2_frames_get_header_firstvalue<'a>( tx: &'a mut HTTP2Transaction, direction: u8, name: &str, ) -> Result<&'a [u8], ()>481 fn http2_frames_get_header_firstvalue<'a>(
482 tx: &'a mut HTTP2Transaction, direction: u8, name: &str,
483 ) -> Result<&'a [u8], ()> {
484 let frames = if direction == STREAM_TOSERVER {
485 &tx.frames_ts
486 } else {
487 &tx.frames_tc
488 };
489 for i in 0..frames.len() {
490 if let Some(blocks) = http2_header_blocks(&frames[i]) {
491 for block in blocks.iter() {
492 if block.name == name.as_bytes().to_vec() {
493 return Ok(&block.value);
494 }
495 }
496 }
497 }
498 return Err(());
499 }
500
http2_frames_get_header_value<'a>( tx: &'a mut HTTP2Transaction, direction: u8, name: &str, ) -> Result<&'a [u8], ()>501 fn http2_frames_get_header_value<'a>(
502 tx: &'a mut HTTP2Transaction, direction: u8, name: &str,
503 ) -> Result<&'a [u8], ()> {
504 let mut found = 0;
505 let mut vec = Vec::new();
506 let mut single: Result<&[u8], ()> = Err(());
507 let frames = if direction == STREAM_TOSERVER {
508 &tx.frames_ts
509 } else {
510 &tx.frames_tc
511 };
512 for i in 0..frames.len() {
513 if let Some(blocks) = http2_header_blocks(&frames[i]) {
514 for block in blocks.iter() {
515 if block.name == name.as_bytes().to_vec() {
516 if found == 0 {
517 single = Ok(&block.value);
518 found = 1;
519 } else if found == 1 {
520 if let Ok(s) = single {
521 vec.extend_from_slice(s);
522 }
523 vec.extend_from_slice(&[b',', b' ']);
524 vec.extend_from_slice(&block.value);
525 found = 2;
526 } else {
527 vec.extend_from_slice(&[b',', b' ']);
528 vec.extend_from_slice(&block.value);
529 }
530 }
531 }
532 }
533 }
534 if found == 0 {
535 return Err(());
536 } else if found == 1 {
537 return single;
538 } else {
539 tx.escaped.push(vec);
540 let idx = tx.escaped.len() - 1;
541 let value = &tx.escaped[idx];
542 return Ok(&value);
543 }
544 }
545
546 #[no_mangle]
rs_http2_tx_get_uri( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8547 pub unsafe extern "C" fn rs_http2_tx_get_uri(
548 tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
549 ) -> u8 {
550 if let Ok(value) = http2_frames_get_header_firstvalue(tx, STREAM_TOSERVER, ":path") {
551 *buffer = value.as_ptr(); //unsafe
552 *buffer_len = value.len() as u32;
553 return 1;
554 }
555 return 0;
556 }
557
558 #[no_mangle]
rs_http2_tx_get_method( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8559 pub unsafe extern "C" fn rs_http2_tx_get_method(
560 tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
561 ) -> u8 {
562 if let Ok(value) = http2_frames_get_header_firstvalue(tx, STREAM_TOSERVER, ":method") {
563 *buffer = value.as_ptr(); //unsafe
564 *buffer_len = value.len() as u32;
565 return 1;
566 }
567 return 0;
568 }
569
570 #[no_mangle]
rs_http2_tx_get_host( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8571 pub unsafe extern "C" fn rs_http2_tx_get_host(
572 tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
573 ) -> u8 {
574 if let Ok(value) = http2_frames_get_header_value(tx, STREAM_TOSERVER, ":authority") {
575 *buffer = value.as_ptr(); //unsafe
576 *buffer_len = value.len() as u32;
577 return 1;
578 }
579 return 0;
580 }
581
http2_lower(value: &[u8]) -> Option<Vec<u8>>582 fn http2_lower(value: &[u8]) -> Option<Vec<u8>> {
583 for i in 0..value.len() {
584 if value[i].is_ascii_uppercase() {
585 // we got at least one upper character, need to transform
586 let mut vec: Vec<u8> = Vec::with_capacity(value.len());
587 vec.extend_from_slice(value);
588 for j in i..vec.len() {
589 vec[j].make_ascii_lowercase();
590 }
591 return Some(vec);
592 }
593 }
594 return None;
595 }
596
597 // returns a tuple with the value and its size
http2_normalize_host(value: &[u8]) -> (Option<Vec<u8>>, usize)598 fn http2_normalize_host(value: &[u8]) -> (Option<Vec<u8>>, usize) {
599 match value.iter().position(|&x| x == ':' as u8) {
600 Some(i) => {
601 return (http2_lower(&value[..i]), i);
602 }
603 None => {
604 return (http2_lower(value), value.len());
605 }
606 }
607 }
608
609 #[no_mangle]
rs_http2_tx_get_host_norm( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8610 pub unsafe extern "C" fn rs_http2_tx_get_host_norm(
611 tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
612 ) -> u8 {
613 if let Ok(value) = http2_frames_get_header_value(tx, STREAM_TOSERVER, ":authority") {
614 let r = http2_normalize_host(value);
615 // r is a tuple with the value and its size
616 // this is useful when we only take a substring (before the port)
617 match r.0 {
618 Some(normval) => {
619 // In case we needed some normalization,
620 // the transaction needs to take ownership of this normalized host
621 tx.escaped.push(normval);
622 let idx = tx.escaped.len() - 1;
623 let resvalue = &tx.escaped[idx];
624 *buffer = resvalue.as_ptr(); //unsafe
625 *buffer_len = r.1 as u32;
626 return 1;
627 }
628 None => {
629 *buffer = value.as_ptr(); //unsafe
630 *buffer_len = r.1 as u32;
631 return 1;
632 }
633 }
634 }
635 return 0;
636 }
637
638 #[no_mangle]
rs_http2_tx_get_useragent( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8639 pub unsafe extern "C" fn rs_http2_tx_get_useragent(
640 tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
641 ) -> u8 {
642 if let Ok(value) = http2_frames_get_header_value(tx, STREAM_TOSERVER, "user-agent") {
643 *buffer = value.as_ptr(); //unsafe
644 *buffer_len = value.len() as u32;
645 return 1;
646 }
647 return 0;
648 }
649
650 #[no_mangle]
rs_http2_tx_get_status( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8651 pub unsafe extern "C" fn rs_http2_tx_get_status(
652 tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
653 ) -> u8 {
654 if let Ok(value) = http2_frames_get_header_firstvalue(tx, STREAM_TOCLIENT, ":status") {
655 *buffer = value.as_ptr(); //unsafe
656 *buffer_len = value.len() as u32;
657 return 1;
658 }
659 return 0;
660 }
661
662 #[no_mangle]
rs_http2_tx_get_cookie( tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8663 pub unsafe extern "C" fn rs_http2_tx_get_cookie(
664 tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
665 ) -> u8 {
666 if direction == STREAM_TOSERVER {
667 if let Ok(value) = http2_frames_get_header_value(tx, STREAM_TOSERVER, "cookie") {
668 *buffer = value.as_ptr(); //unsafe
669 *buffer_len = value.len() as u32;
670 return 1;
671 }
672 } else {
673 if let Ok(value) = http2_frames_get_header_value(tx, STREAM_TOCLIENT, "set-cookie") {
674 *buffer = value.as_ptr(); //unsafe
675 *buffer_len = value.len() as u32;
676 return 1;
677 }
678 }
679 return 0;
680 }
681
682 #[no_mangle]
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, ) -> u8683 pub unsafe extern "C" fn rs_http2_tx_get_header_value(
684 tx: &mut HTTP2Transaction, direction: u8, strname: *const std::os::raw::c_char,
685 buffer: *mut *const u8, buffer_len: *mut u32,
686 ) -> u8 {
687 let hname: &CStr = CStr::from_ptr(strname); //unsafe
688 if let Ok(s) = hname.to_str() {
689 if let Ok(value) = http2_frames_get_header_value(tx, direction, &s.to_lowercase()) {
690 *buffer = value.as_ptr(); //unsafe
691 *buffer_len = value.len() as u32;
692 return 1;
693 }
694 }
695 return 0;
696 }
697
http2_escape_header(blocks: &[parser::HTTP2FrameHeaderBlock], i: u32) -> Vec<u8>698 fn http2_escape_header(blocks: &[parser::HTTP2FrameHeaderBlock], i: u32) -> Vec<u8> {
699 //minimum size + 2 for escapes
700 let normalsize = blocks[i as usize].value.len() + 2 + blocks[i as usize].name.len() + 2;
701 let mut vec = Vec::with_capacity(normalsize);
702 for j in 0..blocks[i as usize].name.len() {
703 vec.push(blocks[i as usize].name[j]);
704 if blocks[i as usize].name[j] == ':' as u8 {
705 vec.push(':' as u8);
706 }
707 }
708 vec.extend_from_slice(&[b':', b' ']);
709 for j in 0..blocks[i as usize].value.len() {
710 vec.push(blocks[i as usize].value[j]);
711 if blocks[i as usize].value[j] == ':' as u8 {
712 vec.push(':' as u8);
713 }
714 }
715 return vec;
716 }
717
718 #[no_mangle]
rs_http2_tx_get_header_names( tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8719 pub unsafe extern "C" fn rs_http2_tx_get_header_names(
720 tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
721 ) -> u8 {
722 let mut vec = vec![b'\r', b'\n'];
723 let frames = if direction & STREAM_TOSERVER != 0 {
724 &tx.frames_ts
725 } else {
726 &tx.frames_tc
727 };
728 for i in 0..frames.len() {
729 if let Some(blocks) = http2_header_blocks(&frames[i]) {
730 for block in blocks.iter() {
731 // we do not escape linefeeds in headers names
732 vec.extend_from_slice(&block.name);
733 vec.extend_from_slice(&[b'\r', b'\n']);
734 }
735 }
736 }
737 if vec.len() > 2 {
738 vec.extend_from_slice(&[b'\r', b'\n']);
739 tx.escaped.push(vec);
740 let idx = tx.escaped.len() - 1;
741 let value = &tx.escaped[idx];
742 *buffer = value.as_ptr(); //unsafe
743 *buffer_len = value.len() as u32;
744 return 1;
745 }
746 return 0;
747 }
748
http2_header_iscookie(direction: u8, hname: &[u8]) -> bool749 fn http2_header_iscookie(direction: u8, hname: &[u8]) -> bool {
750 if let Ok(s) = std::str::from_utf8(hname) {
751 if direction & STREAM_TOSERVER != 0 {
752 if s.to_lowercase() == "cookie" {
753 return true;
754 }
755 } else {
756 if s.to_lowercase() == "set-cookie" {
757 return true;
758 }
759 }
760 }
761 return false;
762 }
763
http2_header_trimspaces(value: &[u8]) -> &[u8]764 fn http2_header_trimspaces(value: &[u8]) -> &[u8] {
765 let mut start = 0;
766 let mut end = value.len();
767 while start < value.len() {
768 if value[start] == b' ' || value[start] == b'\t' {
769 start += 1;
770 } else {
771 break;
772 }
773 }
774 while end > start {
775 if value[end - 1] == b' ' || value[end - 1] == b'\t' {
776 end -= 1;
777 } else {
778 break;
779 }
780 }
781 return &value[start..end];
782 }
783
784 #[no_mangle]
rs_http2_tx_get_headers( tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8785 pub unsafe extern "C" fn rs_http2_tx_get_headers(
786 tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
787 ) -> u8 {
788 let mut vec = Vec::new();
789 let frames = if direction & STREAM_TOSERVER != 0 {
790 &tx.frames_ts
791 } else {
792 &tx.frames_tc
793 };
794 for i in 0..frames.len() {
795 if let Some(blocks) = http2_header_blocks(&frames[i]) {
796 for block in blocks.iter() {
797 if !http2_header_iscookie(direction, &block.name) {
798 // we do not escape linefeeds nor : in headers names
799 vec.extend_from_slice(&block.name);
800 vec.extend_from_slice(&[b':', b' ']);
801 vec.extend_from_slice(http2_header_trimspaces(&block.value));
802 vec.extend_from_slice(&[b'\r', b'\n']);
803 }
804 }
805 }
806 }
807 if vec.len() > 0 {
808 tx.escaped.push(vec);
809 let idx = tx.escaped.len() - 1;
810 let value = &tx.escaped[idx];
811 *buffer = value.as_ptr(); //unsafe
812 *buffer_len = value.len() as u32;
813 return 1;
814 }
815 return 0;
816 }
817
818 #[no_mangle]
rs_http2_tx_get_headers_raw( tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8819 pub unsafe extern "C" fn rs_http2_tx_get_headers_raw(
820 tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
821 ) -> u8 {
822 let mut vec = Vec::new();
823 let frames = if direction & STREAM_TOSERVER != 0 {
824 &tx.frames_ts
825 } else {
826 &tx.frames_tc
827 };
828 for i in 0..frames.len() {
829 if let Some(blocks) = http2_header_blocks(&frames[i]) {
830 for block in blocks.iter() {
831 // we do not escape linefeeds nor : in headers names
832 vec.extend_from_slice(&block.name);
833 vec.extend_from_slice(&[b':', b' ']);
834 vec.extend_from_slice(&block.value);
835 vec.extend_from_slice(&[b'\r', b'\n']);
836 }
837 }
838 }
839 if vec.len() > 0 {
840 tx.escaped.push(vec);
841 let idx = tx.escaped.len() - 1;
842 let value = &tx.escaped[idx];
843 *buffer = value.as_ptr(); //unsafe
844 *buffer_len = value.len() as u32;
845 return 1;
846 }
847 return 0;
848 }
849
850 #[no_mangle]
rs_http2_tx_get_header( tx: &mut HTTP2Transaction, direction: u8, nb: u32, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8851 pub unsafe extern "C" fn rs_http2_tx_get_header(
852 tx: &mut HTTP2Transaction, direction: u8, nb: u32, buffer: *mut *const u8, buffer_len: *mut u32,
853 ) -> u8 {
854 let mut pos = 0 as u32;
855 if direction & STREAM_TOSERVER != 0 {
856 for i in 0..tx.frames_ts.len() {
857 if let Some(blocks) = http2_header_blocks(&tx.frames_ts[i]) {
858 if nb < pos + blocks.len() as u32 {
859 let ehdr = http2_escape_header(&blocks, nb - pos);
860 tx.escaped.push(ehdr);
861 let idx = tx.escaped.len() - 1;
862 let value = &tx.escaped[idx];
863 *buffer = value.as_ptr(); //unsafe
864 *buffer_len = value.len() as u32;
865 return 1;
866 } else {
867 pos = pos + blocks.len() as u32;
868 }
869 }
870 }
871 } else {
872 for i in 0..tx.frames_tc.len() {
873 if let Some(blocks) = http2_header_blocks(&tx.frames_tc[i]) {
874 if nb < pos + blocks.len() as u32 {
875 let ehdr = http2_escape_header(&blocks, nb - pos);
876 tx.escaped.push(ehdr);
877 let idx = tx.escaped.len() - 1;
878 let value = &tx.escaped[idx];
879 *buffer = value.as_ptr(); //unsafe
880 *buffer_len = value.len() as u32;
881 return 1;
882 } else {
883 pos = pos + blocks.len() as u32;
884 }
885 }
886 }
887 }
888
889 return 0;
890 }
891
http2_tx_set_header(state: &mut HTTP2State, name: &[u8], input: &[u8])892 fn http2_tx_set_header(state: &mut HTTP2State, name: &[u8], input: &[u8]) {
893 let head = parser::HTTP2FrameHeader {
894 length: 0,
895 ftype: parser::HTTP2FrameType::HEADERS as u8,
896 flags: 0,
897 reserved: 0,
898 stream_id: 1,
899 };
900 let mut blocks = Vec::new();
901 let b = parser::HTTP2FrameHeaderBlock {
902 name: name.to_vec(),
903 value: input.to_vec(),
904 error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
905 sizeupdate: 0,
906 };
907 blocks.push(b);
908 let hs = parser::HTTP2FrameHeaders {
909 padlength: None,
910 priority: None,
911 blocks: blocks,
912 };
913 let txdata = HTTP2FrameTypeData::HEADERS(hs);
914 let tx = state.find_or_create_tx(&head, &txdata, STREAM_TOSERVER);
915 tx.frames_ts.push(HTTP2Frame {
916 header: head,
917 data: txdata,
918 });
919 //we do not expect more data from client
920 tx.state = HTTP2TransactionState::HTTP2StateHalfClosedClient;
921 }
922
923 #[no_mangle]
rs_http2_tx_set_method( state: &mut HTTP2State, buffer: *const u8, buffer_len: u32, )924 pub extern "C" fn rs_http2_tx_set_method(
925 state: &mut HTTP2State, buffer: *const u8, buffer_len: u32,
926 ) {
927 let slice = build_slice!(buffer, buffer_len as usize);
928 http2_tx_set_header(state, ":method".as_bytes(), slice)
929 }
930
931 #[no_mangle]
rs_http2_tx_set_uri(state: &mut HTTP2State, buffer: *const u8, buffer_len: u32)932 pub extern "C" fn rs_http2_tx_set_uri(state: &mut HTTP2State, buffer: *const u8, buffer_len: u32) {
933 let slice = build_slice!(buffer, buffer_len as usize);
934 http2_tx_set_header(state, ":path".as_bytes(), slice)
935 }
936
937 #[derive(Debug, PartialEq)]
938 pub enum Http2Base64Error {
939 InvalidBase64,
940 }
941
942 impl std::error::Error for Http2Base64Error {}
943
944 impl std::fmt::Display for Http2Base64Error {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result945 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
946 write!(f, "invalid base64")
947 }
948 }
949
http2_base64_map(input: u8) -> Result<u8, Http2Base64Error>950 fn http2_base64_map(input: u8) -> Result<u8, Http2Base64Error> {
951 match input {
952 43 => Ok(62), // +
953 47 => Ok(63), // /
954 48 => Ok(52), // 0
955 49 => Ok(53), // 1
956 50 => Ok(54), // 2
957 51 => Ok(55), // 3
958 52 => Ok(56), // 4
959 53 => Ok(57), // 5
960 54 => Ok(58), // 6
961 55 => Ok(59), // 7
962 56 => Ok(60), // 8
963 57 => Ok(61), // 9
964 65 => Ok(0), // A
965 66 => Ok(1), // B
966 67 => Ok(2), // C
967 68 => Ok(3), // D
968 69 => Ok(4), // E
969 70 => Ok(5), // F
970 71 => Ok(6), // G
971 72 => Ok(7), // H
972 73 => Ok(8), // I
973 74 => Ok(9), // J
974 75 => Ok(10), // K
975 76 => Ok(11), // L
976 77 => Ok(12), // M
977 78 => Ok(13), // N
978 79 => Ok(14), // O
979 80 => Ok(15), // P
980 81 => Ok(16), // Q
981 82 => Ok(17), // R
982 83 => Ok(18), // S
983 84 => Ok(19), // T
984 85 => Ok(20), // U
985 86 => Ok(21), // V
986 87 => Ok(22), // W
987 88 => Ok(23), // X
988 89 => Ok(24), // Y
989 90 => Ok(25), // Z
990 97 => Ok(26), // a
991 98 => Ok(27), // b
992 99 => Ok(28), // c
993 100 => Ok(29), // d
994 101 => Ok(30), // e
995 102 => Ok(31), // f
996 103 => Ok(32), // g
997 104 => Ok(33), // h
998 105 => Ok(34), // i
999 106 => Ok(35), // j
1000 107 => Ok(36), // k
1001 108 => Ok(37), // l
1002 109 => Ok(38), // m
1003 110 => Ok(39), // n
1004 111 => Ok(40), // o
1005 112 => Ok(41), // p
1006 113 => Ok(42), // q
1007 114 => Ok(43), // r
1008 115 => Ok(44), // s
1009 116 => Ok(45), // t
1010 117 => Ok(46), // u
1011 118 => Ok(47), // v
1012 119 => Ok(48), // w
1013 120 => Ok(49), // x
1014 121 => Ok(50), // y
1015 122 => Ok(51), // z
1016 _ => Err(Http2Base64Error::InvalidBase64),
1017 }
1018 }
1019
http2_decode_base64(input: &[u8]) -> Result<Vec<u8>, Http2Base64Error>1020 fn http2_decode_base64(input: &[u8]) -> Result<Vec<u8>, Http2Base64Error> {
1021 if input.len() % 4 != 0 {
1022 return Err(Http2Base64Error::InvalidBase64);
1023 }
1024 let mut r = vec![0; (input.len() * 3) / 4];
1025 for i in 0..input.len() / 4 {
1026 let i1 = http2_base64_map(input[4 * i])?;
1027 let i2 = http2_base64_map(input[4 * i + 1])?;
1028 let i3 = http2_base64_map(input[4 * i + 2])?;
1029 let i4 = http2_base64_map(input[4 * i + 3])?;
1030 r[3 * i] = (i1 << 2) | (i2 >> 4);
1031 r[3 * i + 1] = (i2 << 4) | (i3 >> 2);
1032 r[3 * i + 2] = (i3 << 6) | i4;
1033 }
1034 return Ok(r);
1035 }
1036
http2_tx_set_settings(state: &mut HTTP2State, input: &[u8])1037 fn http2_tx_set_settings(state: &mut HTTP2State, input: &[u8]) {
1038 match http2_decode_base64(input) {
1039 Ok(dec) => {
1040 if dec.len() % 6 != 0 {
1041 state.set_event(HTTP2Event::InvalidHTTP1Settings);
1042 }
1043
1044 let head = parser::HTTP2FrameHeader {
1045 length: dec.len() as u32,
1046 ftype: parser::HTTP2FrameType::SETTINGS as u8,
1047 flags: 0,
1048 reserved: 0,
1049 stream_id: 0,
1050 };
1051
1052 match parser::http2_parse_frame_settings(&dec) {
1053 Ok((_, set)) => {
1054 let txdata = HTTP2FrameTypeData::SETTINGS(set);
1055 let tx = state.find_or_create_tx(&head, &txdata, STREAM_TOSERVER);
1056 tx.frames_ts.push(HTTP2Frame {
1057 header: head,
1058 data: txdata,
1059 });
1060 }
1061 Err(_) => {
1062 state.set_event(HTTP2Event::InvalidHTTP1Settings);
1063 }
1064 }
1065 }
1066 Err(_) => {
1067 state.set_event(HTTP2Event::InvalidHTTP1Settings);
1068 }
1069 }
1070 }
1071
http2_caseinsensitive_cmp(s1: &[u8], s2: &str) -> bool1072 fn http2_caseinsensitive_cmp(s1: &[u8], s2: &str) -> bool {
1073 if let Ok(s) = std::str::from_utf8(s1) {
1074 return s.to_lowercase() == s2;
1075 }
1076 return false;
1077 }
1078
1079 #[no_mangle]
rs_http2_tx_add_header( state: &mut HTTP2State, name: *const u8, name_len: u32, value: *const u8, value_len: u32, )1080 pub extern "C" fn rs_http2_tx_add_header(
1081 state: &mut HTTP2State, name: *const u8, name_len: u32, value: *const u8, value_len: u32,
1082 ) {
1083 let slice_name = build_slice!(name, name_len as usize);
1084 let slice_value = build_slice!(value, value_len as usize);
1085 if slice_name == "HTTP2-Settings".as_bytes() {
1086 http2_tx_set_settings(state, slice_value)
1087 } else if http2_caseinsensitive_cmp(slice_name, "host") {
1088 http2_tx_set_header(state, ":authority".as_bytes(), slice_value)
1089 } else {
1090 http2_tx_set_header(state, slice_name, slice_value)
1091 }
1092 }
1093
1094 #[cfg(test)]
1095 mod tests {
1096
1097 use super::*;
1098
1099 #[test]
test_http2_normalize_host()1100 fn test_http2_normalize_host() {
1101 let buf0 = "aBC.com:1234".as_bytes();
1102 let r0 = http2_normalize_host(buf0);
1103 match r0.0 {
1104 Some(r) => {
1105 assert_eq!(r, "abc.com".as_bytes().to_vec());
1106 }
1107 None => {
1108 panic!("Result should not have been None");
1109 }
1110 }
1111 let buf1 = "oisf.net".as_bytes();
1112 let r1 = http2_normalize_host(buf1);
1113 match r1.0 {
1114 Some(r) => {
1115 panic!("Result should not have been None, not {:?}", r);
1116 }
1117 None => {}
1118 }
1119 assert_eq!(r1.1, "oisf.net".len());
1120 let buf2 = "localhost:3000".as_bytes();
1121 let r2 = http2_normalize_host(buf2);
1122 match r2.0 {
1123 Some(r) => {
1124 panic!("Result should not have been None, not {:?}", r);
1125 }
1126 None => {}
1127 }
1128 assert_eq!(r2.1, "localhost".len());
1129 }
1130
1131 #[test]
test_http2_header_trimspaces()1132 fn test_http2_header_trimspaces() {
1133 let buf0 = "nospaces".as_bytes();
1134 let r0 = http2_header_trimspaces(buf0);
1135 assert_eq!(r0, "nospaces".as_bytes());
1136 let buf1 = " spaces\t".as_bytes();
1137 let r1 = http2_header_trimspaces(buf1);
1138 assert_eq!(r1, "spaces".as_bytes());
1139 let buf2 = " \t".as_bytes();
1140 let r2 = http2_header_trimspaces(buf2);
1141 assert_eq!(r2, "".as_bytes());
1142 }
1143
1144 #[test]
test_http2_frames_get_header_value()1145 fn test_http2_frames_get_header_value() {
1146 let mut tx = HTTP2Transaction::new();
1147 let head = parser::HTTP2FrameHeader {
1148 length: 0,
1149 ftype: parser::HTTP2FrameType::HEADERS as u8,
1150 flags: 0,
1151 reserved: 0,
1152 stream_id: 1,
1153 };
1154 let mut blocks = Vec::new();
1155 let b = parser::HTTP2FrameHeaderBlock {
1156 name: "Host".as_bytes().to_vec(),
1157 value: "abc.com".as_bytes().to_vec(),
1158 error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
1159 sizeupdate: 0,
1160 };
1161 blocks.push(b);
1162 let b2 = parser::HTTP2FrameHeaderBlock {
1163 name: "Host".as_bytes().to_vec(),
1164 value: "efg.net".as_bytes().to_vec(),
1165 error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
1166 sizeupdate: 0,
1167 };
1168 blocks.push(b2);
1169 let hs = parser::HTTP2FrameHeaders {
1170 padlength: None,
1171 priority: None,
1172 blocks: blocks,
1173 };
1174 let txdata = HTTP2FrameTypeData::HEADERS(hs);
1175 tx.frames_ts.push(HTTP2Frame {
1176 header: head,
1177 data: txdata,
1178 });
1179 match http2_frames_get_header_value(&mut tx, STREAM_TOSERVER, "Host") {
1180 Ok(x) => {
1181 assert_eq!(x, "abc.com, efg.net".as_bytes());
1182 }
1183 Err(e) => {
1184 panic!("Result should not have been an error: {:?}", e);
1185 }
1186 }
1187 }
1188 }
1189