1#![deny(missing_docs)]
18
19use packet_dissector_core::dissector::{DispatchHint, DissectResult, Dissector};
20use packet_dissector_core::error::PacketError;
21use packet_dissector_core::field::{FieldDescriptor, FieldType, FieldValue};
22use packet_dissector_core::packet::DissectBuffer;
23use packet_dissector_core::util::{intern_content_type, slice_offset, str_offset, trim_ows};
24
25const MAX_HEADERS: usize = 64;
27
28const MIN_START_LINE_LEN: usize = 15;
33
34const FD_IS_RESPONSE: usize = 0;
40const FD_METHOD: usize = 1;
41const FD_URI: usize = 2;
42const FD_VERSION: usize = 3;
43const FD_STATUS_CODE: usize = 4;
44const FD_REASON_PHRASE: usize = 5;
45const FD_HEADERS: usize = 6;
46const FD_CONTENT_LENGTH: usize = 7;
47const FD_CONTENT_TYPE: usize = 8;
48
49const HC_NAME: usize = 0;
51const HC_VALUE: usize = 1;
52
53static HEADER_CHILDREN: &[FieldDescriptor] = &[
55 FieldDescriptor::new("name", "Name", FieldType::Str),
56 FieldDescriptor::new("value", "Value", FieldType::Str),
57];
58
59static FD_HEADER: FieldDescriptor = FieldDescriptor {
67 name: "header",
68 display_name: "Header",
69 field_type: FieldType::Object,
70 optional: false,
71 children: None,
72 display_fn: None,
73 format_fn: None,
74};
75
76static FIELD_DESCRIPTORS: &[FieldDescriptor] = &[
78 FieldDescriptor::new("is_response", "Is Response", FieldType::U8),
80 FieldDescriptor::new("method", "Method", FieldType::Str).optional(),
82 FieldDescriptor::new("uri", "Request URI", FieldType::Str).optional(),
84 FieldDescriptor::new("version", "Version", FieldType::Str),
86 FieldDescriptor::new("status_code", "Status Code", FieldType::U16).optional(),
88 FieldDescriptor::new("reason_phrase", "Reason Phrase", FieldType::Str).optional(),
90 FieldDescriptor::new("headers", "Headers", FieldType::Array)
92 .optional()
93 .with_children(HEADER_CHILDREN),
94 FieldDescriptor::new("content_length", "Content Length", FieldType::U32).optional(),
96 FieldDescriptor::new("content_type", "Content Type", FieldType::Str).optional(),
98];
99
100pub struct SipDissector;
106
107impl Dissector for SipDissector {
108 fn name(&self) -> &'static str {
109 "Session Initiation Protocol"
110 }
111
112 fn short_name(&self) -> &'static str {
113 "SIP"
114 }
115
116 fn field_descriptors(&self) -> &'static [FieldDescriptor] {
117 FIELD_DESCRIPTORS
118 }
119
120 fn dissect<'pkt>(
121 &self,
122 data: &'pkt [u8],
123 buf: &mut DissectBuffer<'pkt>,
124 offset: usize,
125 ) -> Result<DissectResult, PacketError> {
126 if data.len() < MIN_START_LINE_LEN {
127 return Err(PacketError::Truncated {
128 expected: MIN_START_LINE_LEN,
129 actual: data.len(),
130 });
131 }
132
133 let is_response = data.starts_with(b"SIP/");
135
136 buf.begin_layer("SIP", None, FIELD_DESCRIPTORS, offset..offset);
137
138 buf.push_field(
139 &FIELD_DESCRIPTORS[FD_IS_RESPONSE],
140 FieldValue::U8(u8::from(is_response)),
141 offset..offset + 1,
142 );
143
144 let header_len = if is_response {
145 parse_response(data, offset, buf)?
146 } else {
147 parse_request(data, offset, buf)?
148 };
149
150 let content_length = extract_header_value(buf, "Content-Length")
152 .or_else(|| extract_header_value(buf, "l"))
153 .and_then(|v| v.parse::<u32>().ok());
154 let content_type =
155 extract_header_value(buf, "Content-Type").or_else(|| extract_header_value(buf, "c"));
156
157 if let Some(cl) = content_length {
158 buf.push_field(
159 &FIELD_DESCRIPTORS[FD_CONTENT_LENGTH],
160 FieldValue::U32(cl),
161 offset..offset + header_len,
162 );
163 }
164
165 if let Some(ct) = content_type {
166 buf.push_field(
167 &FIELD_DESCRIPTORS[FD_CONTENT_TYPE],
168 FieldValue::Str(ct),
169 offset..offset + header_len,
170 );
171 }
172
173 let body_len = content_length.unwrap_or(0) as usize;
174 let total = header_len + body_len;
175
176 if total > data.len() {
177 if let Some(layer) = buf.last_layer_mut() {
178 layer.range = offset..offset + header_len;
179 }
180 buf.end_layer();
181 return Err(PacketError::Truncated {
182 expected: total,
183 actual: data.len(),
184 });
185 }
186
187 if body_len > 0 {
189 if let Some(ct) =
190 extract_header_value(buf, "Content-Type").or_else(|| extract_header_value(buf, "c"))
191 {
192 if let Some(interned) = intern_content_type(ct) {
193 if let Some(layer) = buf.last_layer_mut() {
194 layer.range = offset..offset + header_len;
195 }
196 buf.end_layer();
197 return Ok(DissectResult::new(
198 header_len,
199 DispatchHint::ByContentType(interned),
200 ));
201 }
202 }
203 }
204
205 if let Some(layer) = buf.last_layer_mut() {
206 layer.range = offset..offset + total;
207 }
208 buf.end_layer();
209
210 Ok(DissectResult::new(total, DispatchHint::End))
211 }
212}
213
214fn find_line_end(data: &[u8]) -> Option<usize> {
221 data.iter().position(|&b| b == b'\n').map(|i| i + 1)
222}
223
224fn take_start_line(data: &[u8]) -> Result<(&str, usize), PacketError> {
229 let line_end = find_line_end(data).ok_or(PacketError::Truncated {
230 expected: data.len() + 1,
231 actual: data.len(),
232 })?;
233 let line = &data[..line_end];
234 let trimmed = if line.ends_with(b"\r\n") {
235 &line[..line.len() - 2]
236 } else {
237 &line[..line.len() - 1]
238 };
239 let line_str = core::str::from_utf8(trimmed)
240 .map_err(|_| PacketError::InvalidHeader("start-line is not valid UTF-8"))?;
241 Ok((line_str, line_end))
242}
243
244fn parse_remaining_headers<'pkt>(
246 data: &'pkt [u8],
247 offset: usize,
248 line_end: usize,
249 buf: &mut DissectBuffer<'pkt>,
250) -> Result<usize, PacketError> {
251 let header_len = parse_headers(&data[line_end..], offset, line_end, buf)?;
252 Ok(line_end + header_len)
253}
254
255fn parse_request<'pkt>(
261 data: &'pkt [u8],
262 offset: usize,
263 buf: &mut DissectBuffer<'pkt>,
264) -> Result<usize, PacketError> {
265 let (line_str, line_end) = take_start_line(data)?;
266
267 let first_sp = line_str
269 .find(' ')
270 .ok_or(PacketError::InvalidHeader("missing SP in request-line"))?;
271 let method = &line_str[..first_sp];
272 if method.is_empty() {
273 return Err(PacketError::InvalidHeader("empty method in request-line"));
274 }
275 let rest = &line_str[first_sp + 1..];
276
277 let last_sp = rest
278 .rfind(' ')
279 .ok_or(PacketError::InvalidHeader("missing SP before SIP-Version"))?;
280 let uri = &rest[..last_sp];
281 if uri.is_empty() {
282 return Err(PacketError::InvalidHeader(
283 "empty Request-URI in request-line",
284 ));
285 }
286 let version = &rest[last_sp + 1..];
287
288 if !version.starts_with("SIP/") {
289 return Err(PacketError::InvalidHeader("invalid SIP version"));
290 }
291
292 buf.push_field(
293 &FIELD_DESCRIPTORS[FD_METHOD],
294 FieldValue::Str(method),
295 offset..offset + method.len(),
296 );
297
298 let uri_start = first_sp + 1;
299 buf.push_field(
300 &FIELD_DESCRIPTORS[FD_URI],
301 FieldValue::Str(uri),
302 offset + uri_start..offset + uri_start + uri.len(),
303 );
304
305 buf.push_field(
306 &FIELD_DESCRIPTORS[FD_VERSION],
307 FieldValue::Str(version),
308 offset..offset + line_end,
309 );
310
311 parse_remaining_headers(data, offset, line_end, buf)
312}
313
314fn parse_response<'pkt>(
316 data: &'pkt [u8],
317 offset: usize,
318 buf: &mut DissectBuffer<'pkt>,
319) -> Result<usize, PacketError> {
320 let (line_str, line_end) = take_start_line(data)?;
321
322 let first_sp = line_str
324 .find(' ')
325 .ok_or(PacketError::InvalidHeader("missing SP in status-line"))?;
326 let version = &line_str[..first_sp];
327 let rest = &line_str[first_sp + 1..];
328
329 if !version.starts_with("SIP/") {
330 return Err(PacketError::InvalidHeader("invalid SIP version"));
331 }
332
333 buf.push_field(
334 &FIELD_DESCRIPTORS[FD_VERSION],
335 FieldValue::Str(version),
336 offset..offset + line_end,
337 );
338
339 let (code_str, reason) = match rest.find(' ') {
341 Some(sp) => (&rest[..sp], Some(&rest[sp + 1..])),
342 None => (rest, None),
343 };
344
345 if code_str.len() != 3 || !code_str.bytes().all(|b| b.is_ascii_digit()) {
347 return Err(PacketError::InvalidHeader("invalid status code format"));
348 }
349
350 let code: u16 = code_str
351 .parse()
352 .map_err(|_| PacketError::InvalidHeader("invalid status code"))?;
353
354 if !(100..=699).contains(&code) {
356 return Err(PacketError::InvalidHeader("status code out of range"));
357 }
358
359 buf.push_field(
360 &FIELD_DESCRIPTORS[FD_STATUS_CODE],
361 FieldValue::U16(code),
362 offset..offset + line_end,
363 );
364
365 if let Some(reason) = reason {
366 if !reason.is_empty() {
367 buf.push_field(
368 &FIELD_DESCRIPTORS[FD_REASON_PHRASE],
369 FieldValue::Str(reason),
370 offset..offset + line_end,
371 );
372 }
373 }
374
375 parse_remaining_headers(data, offset, line_end, buf)
376}
377
378fn parse_headers<'pkt>(
384 header_data: &'pkt [u8],
385 base_offset: usize,
386 line_end: usize,
387 buf: &mut DissectBuffer<'pkt>,
388) -> Result<usize, PacketError> {
389 let mut headers_buf = [httparse::EMPTY_HEADER; MAX_HEADERS];
390
391 match httparse::parse_headers(header_data, &mut headers_buf) {
392 Ok(httparse::Status::Complete((len, headers))) => {
393 build_header_fields(header_data, base_offset + line_end, headers, buf)?;
394 Ok(len)
395 }
396 Ok(httparse::Status::Partial) => Err(PacketError::Truncated {
397 expected: header_data.len() + 1,
398 actual: header_data.len(),
399 }),
400 Err(_) => Err(PacketError::InvalidHeader("invalid SIP header")),
401 }
402}
403
404fn build_header_fields<'pkt>(
406 data: &'pkt [u8],
407 offset: usize,
408 headers: &[httparse::Header<'pkt>],
409 buf: &mut DissectBuffer<'pkt>,
410) -> Result<(), PacketError> {
411 if headers.is_empty() {
412 return Ok(());
413 }
414
415 let first_header = &headers[0];
418 let last_header = &headers[headers.len() - 1];
419 let first_name_start = str_offset(data, first_header.name)? + offset;
420 let last_value_end = slice_offset(data, last_header.value)? + last_header.value.len() + offset;
421
422 let array_idx = buf.begin_container(
423 &FIELD_DESCRIPTORS[FD_HEADERS],
424 FieldValue::Array(0..0),
425 first_name_start..last_value_end,
426 );
427
428 for header in headers {
429 let name = header.name;
430 let trimmed_value = trim_ows(header.value);
431 let value_str = core::str::from_utf8(trimmed_value)
432 .map_err(|_| PacketError::InvalidHeader("header value is not valid UTF-8"))?;
433
434 let name_start = str_offset(data, name)?;
436 let value_end = slice_offset(data, header.value)? + header.value.len();
437 let header_range = offset + name_start..offset + value_end;
438
439 let obj_idx =
440 buf.begin_container(&FD_HEADER, FieldValue::Object(0..0), header_range.clone());
441 buf.push_field(
442 &HEADER_CHILDREN[HC_NAME],
443 FieldValue::Str(name),
444 header_range.clone(),
445 );
446 buf.push_field(
447 &HEADER_CHILDREN[HC_VALUE],
448 FieldValue::Str(value_str),
449 header_range,
450 );
451 buf.end_container(obj_idx);
452 }
453
454 buf.end_container(array_idx);
455
456 Ok(())
457}
458
459fn extract_header_value<'pkt>(buf: &DissectBuffer<'pkt>, header_name: &str) -> Option<&'pkt str> {
461 let layer = buf.layers().last()?;
462 let start = layer.field_range.start as usize;
463 let fields = &buf.fields()[start..];
464 let headers_field = fields.iter().find(|f| f.name() == "headers")?;
465 let array_range = match &headers_field.value {
466 FieldValue::Array(r) => r,
467 _ => return None,
468 };
469
470 let children = buf.nested_fields(array_range);
471 for field in children {
472 if let FieldValue::Object(ref obj_range) = field.value {
473 let obj_fields = buf.nested_fields(obj_range);
474 let name_field = obj_fields.iter().find(|f| f.name() == "name")?;
475 let value_field = obj_fields.iter().find(|f| f.name() == "value")?;
476 if let FieldValue::Str(n) = &name_field.value {
477 if n.eq_ignore_ascii_case(header_name) {
478 if let FieldValue::Str(v) = &value_field.value {
479 return Some(v);
480 }
481 }
482 }
483 }
484 }
485 None
486}
487
488#[cfg(test)]
489mod tests {
490 use super::*;
491
492 fn dissect(data: &[u8]) -> Result<DissectBuffer<'_>, PacketError> {
516 let dissector = SipDissector;
517 let mut buf = DissectBuffer::new();
518 dissector.dissect(data, &mut buf, 0)?;
519 Ok(buf)
520 }
521
522 fn dissect_err(data: &[u8]) -> PacketError {
523 let dissector = SipDissector;
524 let mut buf = DissectBuffer::new();
525 dissector.dissect(data, &mut buf, 0).unwrap_err()
526 }
527
528 #[test]
529 fn parse_sip_invite_request() {
530 let data = b"INVITE sip:bob@example.net SIP/2.0\r\n\
531 Via: SIP/2.0/UDP pc33.example.com;branch=z9hG4bK776asdhds\r\n\
532 To: Bob <sip:bob@example.net>\r\n\
533 From: Alice <sip:alice@example.com>;tag=1928301774\r\n\
534 Call-ID: a84b4c76e66710@pc33.example.com\r\n\
535 CSeq: 314159 INVITE\r\n\
536 Contact: <sip:alice@pc33.example.com>\r\n\
537 Content-Length: 0\r\n\r\n";
538 let buf = dissect(data).unwrap();
539 let layer = buf.layer_by_name("SIP").unwrap();
540
541 assert_eq!(
542 buf.field_by_name(layer, "is_response").unwrap().value,
543 FieldValue::U8(0)
544 );
545 assert_eq!(
546 buf.field_by_name(layer, "method").unwrap().value,
547 FieldValue::Str("INVITE")
548 );
549 assert_eq!(
550 buf.field_by_name(layer, "uri").unwrap().value,
551 FieldValue::Str("sip:bob@example.net")
552 );
553 assert_eq!(
554 buf.field_by_name(layer, "version").unwrap().value,
555 FieldValue::Str("SIP/2.0")
556 );
557 assert!(buf.field_by_name(layer, "status_code").is_none());
558 }
559
560 #[test]
561 fn parse_sip_register_request() {
562 let data = b"REGISTER sip:registrar.example.net SIP/2.0\r\n\
563 Via: SIP/2.0/UDP bobspc.example.net:5060;branch=z9hG4bKnashds7\r\n\
564 To: Bob <sip:bob@example.net>\r\n\
565 From: Bob <sip:bob@example.net>;tag=456248\r\n\
566 Call-ID: 843817637684230@998sdasdh09\r\n\
567 CSeq: 1826 REGISTER\r\n\
568 Contact: <sip:bob@192.0.2.4>\r\n\
569 Content-Length: 0\r\n\r\n";
570 let buf = dissect(data).unwrap();
571 let layer = buf.layer_by_name("SIP").unwrap();
572
573 assert_eq!(
574 buf.field_by_name(layer, "method").unwrap().value,
575 FieldValue::Str("REGISTER")
576 );
577 assert_eq!(
578 buf.field_by_name(layer, "uri").unwrap().value,
579 FieldValue::Str("sip:registrar.example.net")
580 );
581 }
582
583 #[test]
584 fn parse_sip_200_ok_response() {
585 let data = b"SIP/2.0 200 OK\r\n\
586 Via: SIP/2.0/UDP server10.example.net;branch=z9hG4bKnashds8\r\n\
587 To: Bob <sip:bob@example.net>;tag=2493k59kd\r\n\
588 From: Alice <sip:alice@example.com>;tag=1928301774\r\n\
589 Call-ID: a84b4c76e66710@pc33.example.com\r\n\
590 CSeq: 314159 INVITE\r\n\
591 Contact: <sip:bob@192.0.2.4>\r\n\
592 Content-Length: 0\r\n\r\n";
593 let buf = dissect(data).unwrap();
594 let layer = buf.layer_by_name("SIP").unwrap();
595
596 assert_eq!(
597 buf.field_by_name(layer, "is_response").unwrap().value,
598 FieldValue::U8(1)
599 );
600 assert_eq!(
601 buf.field_by_name(layer, "version").unwrap().value,
602 FieldValue::Str("SIP/2.0")
603 );
604 assert_eq!(
605 buf.field_by_name(layer, "status_code").unwrap().value,
606 FieldValue::U16(200)
607 );
608 assert_eq!(
609 buf.field_by_name(layer, "reason_phrase").unwrap().value,
610 FieldValue::Str("OK")
611 );
612 assert!(buf.field_by_name(layer, "method").is_none());
613 }
614
615 #[test]
616 fn parse_sip_trying_response() {
617 let data = b"SIP/2.0 100 Trying\r\n\
618 Via: SIP/2.0/UDP pc33.example.com;branch=z9hG4bK776asdhds\r\n\
619 To: Bob <sip:bob@example.net>\r\n\
620 From: Alice <sip:alice@example.com>;tag=1928301774\r\n\
621 Call-ID: a84b4c76e66710@pc33.example.com\r\n\
622 CSeq: 314159 INVITE\r\n\
623 Content-Length: 0\r\n\r\n";
624 let buf = dissect(data).unwrap();
625 let layer = buf.layer_by_name("SIP").unwrap();
626
627 assert_eq!(
628 buf.field_by_name(layer, "status_code").unwrap().value,
629 FieldValue::U16(100)
630 );
631 assert_eq!(
632 buf.field_by_name(layer, "reason_phrase").unwrap().value,
633 FieldValue::Str("Trying")
634 );
635 }
636
637 #[test]
638 fn parse_sip_request_with_headers() {
639 let data = b"OPTIONS sip:carol@example.org SIP/2.0\r\n\
640 Via: SIP/2.0/UDP pc33.example.com;branch=z9hG4bKhjhs8ass877\r\n\
641 Max-Forwards: 70\r\n\
642 To: <sip:carol@example.org>\r\n\
643 From: Alice <sip:alice@example.com>;tag=1928301774\r\n\
644 Call-ID: testcallid@pc33.example.com\r\n\
645 CSeq: 63104 OPTIONS\r\n\
646 Contact: <sip:alice@pc33.example.com>\r\n\
647 Accept: application/sdp\r\n\
648 Content-Length: 0\r\n\r\n";
649 let buf = dissect(data).unwrap();
650 let layer = buf.layer_by_name("SIP").unwrap();
651
652 let headers_field = buf.field_by_name(layer, "headers").unwrap();
653 let array_range = match &headers_field.value {
654 FieldValue::Array(r) => r,
655 _ => panic!("expected Array"),
656 };
657 let children = buf.nested_fields(array_range);
658 let objects: Vec<_> = children.iter().filter(|f| f.value.is_object()).collect();
659 assert_eq!(objects.len(), 9);
661
662 if let FieldValue::Object(ref r) = objects[0].value {
663 let f = buf.nested_fields(r);
664 assert_eq!(f[0].value, FieldValue::Str("Via"));
665 }
666
667 if let FieldValue::Object(ref r) = objects[7].value {
669 let f = buf.nested_fields(r);
670 assert_eq!(f[0].value, FieldValue::Str("Accept"));
671 assert_eq!(f[1].value, FieldValue::Str("application/sdp"));
672 }
673 }
674
675 #[test]
676 fn parse_sip_invite_with_sdp_body() {
677 let sdp_body = b"v=0\r\n\
678 o=alice 2890844526 2890844526 IN IP4 host.example.com\r\n\
679 s=-\r\n\
680 c=IN IP4 host.example.com\r\n\
681 t=0 0\r\n\
682 m=audio 49170 RTP/AVP 0\r\n\
683 a=rtpmap:0 PCMU/8000\r\n";
684 let content_length = sdp_body.len();
685 let header = format!(
686 "INVITE sip:bob@example.net SIP/2.0\r\n\
687 Via: SIP/2.0/UDP pc33.example.com;branch=z9hG4bK776asdhds\r\n\
688 To: Bob <sip:bob@example.net>\r\n\
689 From: Alice <sip:alice@example.com>;tag=1928301774\r\n\
690 Call-ID: a84b4c76e66710@pc33.example.com\r\n\
691 CSeq: 314159 INVITE\r\n\
692 Contact: <sip:alice@pc33.example.com>\r\n\
693 Content-Type: application/sdp\r\n\
694 Content-Length: {content_length}\r\n\r\n"
695 );
696 let mut data = Vec::new();
697 data.extend_from_slice(header.as_bytes());
698 data.extend_from_slice(sdp_body);
699
700 let buf = dissect(&data).unwrap();
701 let layer = buf.layer_by_name("SIP").unwrap();
702
703 assert_eq!(
704 buf.field_by_name(layer, "content_length").unwrap().value,
705 FieldValue::U32(content_length as u32)
706 );
707 assert_eq!(
708 buf.field_by_name(layer, "content_type").unwrap().value,
709 FieldValue::Str("application/sdp")
710 );
711 let header_len = header.len();
713 assert_eq!(layer.range, 0..header_len);
714 }
715
716 #[test]
717 fn parse_sip_truncated() {
718 let data = b"INVITE sip:";
719 assert!(matches!(dissect_err(data), PacketError::Truncated { .. }));
720 }
721
722 #[test]
723 fn parse_sip_truncated_headers() {
724 let data = b"INVITE sip:bob@example.net SIP/2.0\r\nVia: SIP/2.0/UDP pc33.example.com";
726 assert!(matches!(dissect_err(data), PacketError::Truncated { .. }));
727 }
728
729 #[test]
730 fn parse_sip_truncated_body() {
731 let data = b"INVITE sip:bob@example.net SIP/2.0\r\nContent-Length: 100\r\n\r\nShort";
732 assert!(matches!(dissect_err(data), PacketError::Truncated { .. }));
733 }
734
735 #[test]
736 fn parse_sip_invalid_request() {
737 let data = b"INVALIDREQUESTLIN\r\n\r\n";
739 assert!(matches!(dissect_err(data), PacketError::InvalidHeader(_)));
740 }
741
742 #[test]
743 fn parse_sip_header_ows_trimming() {
744 let data = b"OPTIONS sip:carol@example.org SIP/2.0\r\n\
745 Via: SIP/2.0/UDP pc33.example.com \r\n\
746 Content-Length: 0\r\n\r\n";
747 let buf = dissect(data).unwrap();
748 let layer = buf.layer_by_name("SIP").unwrap();
749
750 let headers_field = buf.field_by_name(layer, "headers").unwrap();
751 if let FieldValue::Array(ref r) = headers_field.value {
752 let children = buf.nested_fields(r);
753 let obj = children.iter().find(|f| f.value.is_object()).unwrap();
754 if let FieldValue::Object(ref obj_r) = obj.value {
755 let f = buf.nested_fields(obj_r);
756 assert_eq!(f[1].value, FieldValue::Str("SIP/2.0/UDP pc33.example.com"));
757 }
758 }
759 }
760
761 #[test]
762 fn parse_sip_content_type_dispatch() {
763 let sdp = b"v=0\r\n";
764 let cl = sdp.len();
765 let header = format!(
766 "INVITE sip:bob@example.net SIP/2.0\r\n\
767 Content-Type: application/sdp\r\n\
768 Content-Length: {cl}\r\n\r\n"
769 );
770 let mut data = Vec::new();
771 data.extend_from_slice(header.as_bytes());
772 data.extend_from_slice(sdp);
773
774 let dissector = SipDissector;
775 let mut buf = DissectBuffer::new();
776 let result = dissector.dissect(&data, &mut buf, 0).unwrap();
777
778 assert_eq!(result.next, DispatchHint::ByContentType("application/sdp"));
779 }
780
781 #[test]
782 fn parse_sip_content_type_with_params_dispatch() {
783 let body = b"body";
785 let cl = body.len();
786 let header = format!(
787 "INVITE sip:bob@example.net SIP/2.0\r\n\
788 Content-Type: application/sdp; charset=utf-8\r\n\
789 Content-Length: {cl}\r\n\r\n"
790 );
791 let mut data = Vec::new();
792 data.extend_from_slice(header.as_bytes());
793 data.extend_from_slice(body);
794
795 let dissector = SipDissector;
796 let mut buf = DissectBuffer::new();
797 let result = dissector.dissect(&data, &mut buf, 0).unwrap();
798
799 assert_eq!(result.next, DispatchHint::ByContentType("application/sdp"));
800 }
801
802 #[test]
803 fn parse_sip_no_body_dispatch_end() {
804 let data = b"OPTIONS sip:carol@example.org SIP/2.0\r\n\
805 Content-Length: 0\r\n\r\n";
806 let dissector = SipDissector;
807 let mut buf = DissectBuffer::new();
808 let result = dissector.dissect(data, &mut buf, 0).unwrap();
809
810 assert_eq!(result.next, DispatchHint::End);
811 }
812
813 #[test]
814 fn parse_sip_with_offset() {
815 let data = b"OPTIONS sip:carol@example.org SIP/2.0\r\n\
816 Content-Length: 0\r\n\r\n";
817 let dissector = SipDissector;
818 let mut buf = DissectBuffer::new();
819 let result = dissector.dissect(data, &mut buf, 42).unwrap();
820
821 let layer = buf.layer_by_name("SIP").unwrap();
822 assert_eq!(layer.range.start, 42);
823 assert_eq!(layer.range.end, 42 + data.len());
824 assert_eq!(result.bytes_consumed, data.len());
825 }
826
827 #[test]
828 fn parse_sip_compact_content_length() {
829 let data = b"OPTIONS sip:carol@example.org SIP/2.0\r\n\
831 l: 0\r\n\r\n";
832 let buf = dissect(data).unwrap();
833 let layer = buf.layer_by_name("SIP").unwrap();
834
835 assert_eq!(
836 buf.field_by_name(layer, "content_length").unwrap().value,
837 FieldValue::U32(0)
838 );
839 }
840
841 #[test]
842 fn parse_sip_methods_ack() {
843 let data = b"ACK sip:bob@example.net SIP/2.0\r\nContent-Length: 0\r\n\r\n";
844 let buf = dissect(data).unwrap();
845 let layer = buf.layer_by_name("SIP").unwrap();
846 assert_eq!(
847 buf.field_by_name(layer, "method").unwrap().value,
848 FieldValue::Str("ACK")
849 );
850 }
851
852 #[test]
853 fn parse_sip_methods_bye() {
854 let data = b"BYE sip:bob@example.net SIP/2.0\r\nContent-Length: 0\r\n\r\n";
855 let buf = dissect(data).unwrap();
856 let layer = buf.layer_by_name("SIP").unwrap();
857 assert_eq!(
858 buf.field_by_name(layer, "method").unwrap().value,
859 FieldValue::Str("BYE")
860 );
861 }
862
863 #[test]
864 fn parse_sip_methods_cancel() {
865 let data = b"CANCEL sip:bob@example.net SIP/2.0\r\nContent-Length: 0\r\n\r\n";
866 let buf = dissect(data).unwrap();
867 let layer = buf.layer_by_name("SIP").unwrap();
868 assert_eq!(
869 buf.field_by_name(layer, "method").unwrap().value,
870 FieldValue::Str("CANCEL")
871 );
872 }
873
874 #[test]
875 fn parse_sip_response_no_reason() {
876 let data = b"SIP/2.0 200\r\nContent-Length: 0\r\n\r\n";
878 let buf = dissect(data).unwrap();
879 let layer = buf.layer_by_name("SIP").unwrap();
880
881 assert_eq!(
882 buf.field_by_name(layer, "status_code").unwrap().value,
883 FieldValue::U16(200)
884 );
885 assert!(buf.field_by_name(layer, "reason_phrase").is_none());
886 }
887
888 #[test]
889 fn dissector_metadata() {
890 let d = SipDissector;
891 assert_eq!(d.name(), "Session Initiation Protocol");
892 assert_eq!(d.short_name(), "SIP");
893 assert!(!d.field_descriptors().is_empty());
894 }
895
896 #[test]
897 fn header_container_descriptor_distinct_from_inner_name() {
898 let data = b"OPTIONS sip:carol@example.com SIP/2.0\r\n\
902 Via: SIP/2.0/UDP pc.example.com\r\n\
903 Content-Length: 0\r\n\r\n";
904 let buf = dissect(data).unwrap();
905
906 let (idx, field) = buf
907 .fields()
908 .iter()
909 .enumerate()
910 .find(|(_, f)| f.name() == "header")
911 .expect("header container not found");
912 assert!(matches!(field.value, FieldValue::Object(_)));
913 assert_eq!(field.display_name(), "Header");
914 assert_eq!(buf.resolve_container_display_name(idx as u32), None);
915 }
916}