1use crate::header::{HeaderName, Headers};
2use std::fmt;
3use thiserror::Error;
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6pub enum SipMethod {
7 Register,
8 Invite,
9 Ack,
10 Bye,
11 Cancel,
12 Options,
13 Info,
14 Refer,
15 Notify,
16 Update,
17 Prack,
18 Other(String),
19}
20
21impl SipMethod {
22 pub fn from_str(s: &str) -> Self {
23 match s.to_uppercase().as_str() {
24 "REGISTER" => SipMethod::Register,
25 "INVITE" => SipMethod::Invite,
26 "ACK" => SipMethod::Ack,
27 "BYE" => SipMethod::Bye,
28 "CANCEL" => SipMethod::Cancel,
29 "OPTIONS" => SipMethod::Options,
30 "INFO" => SipMethod::Info,
31 "REFER" => SipMethod::Refer,
32 "NOTIFY" => SipMethod::Notify,
33 "UPDATE" => SipMethod::Update,
34 "PRACK" => SipMethod::Prack,
35 other => SipMethod::Other(other.to_string()),
36 }
37 }
38
39 pub fn as_str(&self) -> &str {
40 match self {
41 SipMethod::Register => "REGISTER",
42 SipMethod::Invite => "INVITE",
43 SipMethod::Ack => "ACK",
44 SipMethod::Bye => "BYE",
45 SipMethod::Cancel => "CANCEL",
46 SipMethod::Options => "OPTIONS",
47 SipMethod::Info => "INFO",
48 SipMethod::Refer => "REFER",
49 SipMethod::Notify => "NOTIFY",
50 SipMethod::Update => "UPDATE",
51 SipMethod::Prack => "PRACK",
52 SipMethod::Other(s) => s.as_str(),
53 }
54 }
55}
56
57impl fmt::Display for SipMethod {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 write!(f, "{}", self.as_str())
60 }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub struct StatusCode(pub u16);
65
66impl StatusCode {
67 pub const TRYING: Self = Self(100);
68 pub const RINGING: Self = Self(180);
69 pub const SESSION_PROGRESS: Self = Self(183);
70 pub const OK: Self = Self(200);
71 pub const ACCEPTED: Self = Self(202);
72 pub const BAD_REQUEST: Self = Self(400);
73 pub const UNAUTHORIZED: Self = Self(401);
74 pub const FORBIDDEN: Self = Self(403);
75 pub const NOT_FOUND: Self = Self(404);
76 pub const METHOD_NOT_ALLOWED: Self = Self(405);
77 pub const PROXY_AUTH_REQUIRED: Self = Self(407);
78 pub const REQUEST_TIMEOUT: Self = Self(408);
79 pub const REQUEST_PENDING: Self = Self(491);
80 pub const BUSY_HERE: Self = Self(486);
81 pub const SERVER_ERROR: Self = Self(500);
82 pub const NOT_IMPLEMENTED: Self = Self(501);
83
84 pub fn reason_phrase(&self) -> &'static str {
85 match self.0 {
86 100 => "Trying",
87 180 => "Ringing",
88 183 => "Session Progress",
89 200 => "OK",
90 202 => "Accepted",
91 400 => "Bad Request",
92 401 => "Unauthorized",
93 403 => "Forbidden",
94 404 => "Not Found",
95 405 => "Method Not Allowed",
96 407 => "Proxy Authentication Required",
97 408 => "Request Timeout",
98 486 => "Busy Here",
99 491 => "Request Pending",
100 500 => "Server Internal Error",
101 501 => "Not Implemented",
102 _ => "Unknown",
103 }
104 }
105
106 pub fn is_provisional(&self) -> bool {
107 self.0 >= 100 && self.0 < 200
108 }
109
110 pub fn is_success(&self) -> bool {
111 self.0 >= 200 && self.0 < 300
112 }
113
114 pub fn is_redirect(&self) -> bool {
115 self.0 >= 300 && self.0 < 400
116 }
117
118 pub fn is_error(&self) -> bool {
119 self.0 >= 400
120 }
121}
122
123impl fmt::Display for StatusCode {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 write!(f, "{}", self.0)
126 }
127}
128
129#[derive(Debug, Clone)]
130pub struct SipRequest {
131 pub method: SipMethod,
132 pub uri: String,
133 pub version: String,
134 pub headers: Headers,
135 pub body: Option<String>,
136}
137
138#[derive(Debug, Clone)]
139pub struct SipResponse {
140 pub version: String,
141 pub status: StatusCode,
142 pub reason: String,
143 pub headers: Headers,
144 pub body: Option<String>,
145}
146
147#[derive(Debug, Clone)]
148pub enum SipMessage {
149 Request(SipRequest),
150 Response(SipResponse),
151}
152
153#[derive(Debug, Error)]
154pub enum ParseError {
155 #[error("invalid start line: {0}")]
156 InvalidStartLine(String),
157 #[error("invalid header: {0}")]
158 InvalidHeader(String),
159 #[error("invalid status code: {0}")]
160 InvalidStatusCode(String),
161 #[error("incomplete message")]
162 Incomplete,
163}
164
165impl SipMessage {
166 pub fn parse(input: &str) -> Result<Self, ParseError> {
167 let mut lines = input.lines();
168 let start_line = lines.next().ok_or(ParseError::Incomplete)?;
169 let start_line = start_line.trim();
170
171 if start_line.starts_with("SIP/") {
173 Self::parse_response(start_line, &mut lines)
175 } else {
176 Self::parse_request(start_line, &mut lines)
178 }
179 }
180
181 fn parse_request<'a>(
182 start_line: &str,
183 lines: &mut impl Iterator<Item = &'a str>,
184 ) -> Result<Self, ParseError> {
185 let parts: Vec<&str> = start_line.splitn(3, ' ').collect();
186 if parts.len() != 3 {
187 return Err(ParseError::InvalidStartLine(start_line.to_string()));
188 }
189
190 let method = SipMethod::from_str(parts[0]);
191 let uri = parts[1].to_string();
192 let version = parts[2].to_string();
193
194 let (headers, body) = Self::parse_headers_and_body(lines)?;
195
196 Ok(SipMessage::Request(SipRequest {
197 method,
198 uri,
199 version,
200 headers,
201 body,
202 }))
203 }
204
205 fn parse_response<'a>(
206 start_line: &str,
207 lines: &mut impl Iterator<Item = &'a str>,
208 ) -> Result<Self, ParseError> {
209 let parts: Vec<&str> = start_line.splitn(3, ' ').collect();
210 if parts.len() < 2 {
211 return Err(ParseError::InvalidStartLine(start_line.to_string()));
212 }
213
214 let version = parts[0].to_string();
215 let status_code: u16 = parts[1]
216 .parse()
217 .map_err(|_| ParseError::InvalidStatusCode(parts[1].to_string()))?;
218 let reason = if parts.len() > 2 {
219 parts[2].to_string()
220 } else {
221 StatusCode(status_code).reason_phrase().to_string()
222 };
223
224 let (headers, body) = Self::parse_headers_and_body(lines)?;
225
226 Ok(SipMessage::Response(SipResponse {
227 version,
228 status: StatusCode(status_code),
229 reason,
230 headers,
231 body,
232 }))
233 }
234
235 fn parse_headers_and_body<'a>(
236 lines: &mut impl Iterator<Item = &'a str>,
237 ) -> Result<(Headers, Option<String>), ParseError> {
238 let mut headers = Headers::new();
239 let mut body_lines = Vec::new();
240 let mut in_body = false;
241
242 for line in lines {
243 if in_body {
244 body_lines.push(line);
245 continue;
246 }
247
248 if line.trim().is_empty() {
249 in_body = true;
250 continue;
251 }
252
253 if line.starts_with(' ') || line.starts_with('\t') {
255 continue;
258 }
259
260 if let Some((name, value)) = line.split_once(':') {
261 let name = HeaderName::from_str(name.trim());
262 let value = value.trim().to_string();
263 headers.add(name, value);
264 } else {
265 return Err(ParseError::InvalidHeader(line.to_string()));
266 }
267 }
268
269 let body = if body_lines.is_empty() {
270 None
271 } else {
272 let b = body_lines.join("\r\n");
273 if b.trim().is_empty() {
274 None
275 } else {
276 Some(b)
277 }
278 };
279
280 Ok((headers, body))
281 }
282
283 pub fn to_bytes(&self) -> Vec<u8> {
284 self.to_string().into_bytes()
285 }
286
287 pub fn headers(&self) -> &Headers {
288 match self {
289 SipMessage::Request(req) => &req.headers,
290 SipMessage::Response(res) => &res.headers,
291 }
292 }
293
294 pub fn headers_mut(&mut self) -> &mut Headers {
295 match self {
296 SipMessage::Request(req) => &mut req.headers,
297 SipMessage::Response(res) => &mut res.headers,
298 }
299 }
300
301 pub fn body(&self) -> Option<&str> {
302 match self {
303 SipMessage::Request(req) => req.body.as_deref(),
304 SipMessage::Response(res) => res.body.as_deref(),
305 }
306 }
307
308 pub fn call_id(&self) -> Option<String> {
309 self.headers()
310 .get(&HeaderName::CallId)
311 .map(|v| v.0.clone())
312 }
313
314 pub fn cseq(&self) -> Option<(u32, SipMethod)> {
315 let cseq_val = self.headers().get(&HeaderName::CSeq)?;
316 let parts: Vec<&str> = cseq_val.as_str().splitn(2, ' ').collect();
317 if parts.len() != 2 {
318 return None;
319 }
320 let seq: u32 = parts[0].parse().ok()?;
321 let method = SipMethod::from_str(parts[1]);
322 Some((seq, method))
323 }
324
325 pub fn is_request(&self) -> bool {
326 matches!(self, SipMessage::Request(_))
327 }
328
329 pub fn is_response(&self) -> bool {
330 matches!(self, SipMessage::Response(_))
331 }
332
333 pub fn method(&self) -> Option<&SipMethod> {
334 match self {
335 SipMessage::Request(req) => Some(&req.method),
336 SipMessage::Response(_) => None,
337 }
338 }
339
340 pub fn status(&self) -> Option<&StatusCode> {
341 match self {
342 SipMessage::Response(res) => Some(&res.status),
343 SipMessage::Request(_) => None,
344 }
345 }
346}
347
348impl fmt::Display for SipMessage {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 match self {
351 SipMessage::Request(req) => {
352 writeln!(f, "{} {} {}\r", req.method, req.uri, req.version)?;
353 for header in req.headers.iter() {
354 writeln!(f, "{}\r", header)?;
355 }
356 writeln!(f, "\r")?;
357 if let Some(body) = &req.body {
358 write!(f, "{}", body)?;
359 }
360 }
361 SipMessage::Response(res) => {
362 writeln!(f, "{} {} {}\r", res.version, res.status, res.reason)?;
363 for header in res.headers.iter() {
364 writeln!(f, "{}\r", header)?;
365 }
366 writeln!(f, "\r")?;
367 if let Some(body) = &res.body {
368 write!(f, "{}", body)?;
369 }
370 }
371 }
372 Ok(())
373 }
374}
375
376pub struct RequestBuilder {
378 method: SipMethod,
379 uri: String,
380 headers: Headers,
381 body: Option<String>,
382}
383
384impl RequestBuilder {
385 pub fn new(method: SipMethod, uri: impl Into<String>) -> Self {
386 Self {
387 method,
388 uri: uri.into(),
389 headers: Headers::new(),
390 body: None,
391 }
392 }
393
394 pub fn header(mut self, name: HeaderName, value: impl Into<String>) -> Self {
395 self.headers.add(name, value);
396 self
397 }
398
399 pub fn body(mut self, body: impl Into<String>) -> Self {
400 self.body = Some(body.into());
401 self
402 }
403
404 pub fn build(mut self) -> SipMessage {
405 let content_length = self.body.as_ref().map_or(0, |b| b.len());
407 self.headers
408 .set(HeaderName::ContentLength, content_length.to_string());
409
410 SipMessage::Request(SipRequest {
411 method: self.method,
412 uri: self.uri,
413 version: "SIP/2.0".to_string(),
414 headers: self.headers,
415 body: self.body,
416 })
417 }
418}
419
420pub struct ResponseBuilder {
422 status: StatusCode,
423 headers: Headers,
424 body: Option<String>,
425}
426
427impl ResponseBuilder {
428 pub fn new(status: StatusCode) -> Self {
429 Self {
430 status,
431 headers: Headers::new(),
432 body: None,
433 }
434 }
435
436 pub fn header(mut self, name: HeaderName, value: impl Into<String>) -> Self {
438 self.headers.set(name, value);
439 self
440 }
441
442 pub fn body(mut self, body: impl Into<String>) -> Self {
443 self.body = Some(body.into());
444 self
445 }
446
447 pub fn from_request(request: &SipRequest, status: StatusCode) -> Self {
449 let mut headers = Headers::new();
450
451 for via in request.headers.get_all(&HeaderName::Via) {
453 headers.add(HeaderName::Via, via.as_str());
454 }
455
456 if let Some(from) = request.headers.get(&HeaderName::From) {
458 headers.add(HeaderName::From, from.as_str());
459 }
460
461 if let Some(to) = request.headers.get(&HeaderName::To) {
463 headers.add(HeaderName::To, to.as_str());
464 }
465
466 if let Some(call_id) = request.headers.get(&HeaderName::CallId) {
468 headers.add(HeaderName::CallId, call_id.as_str());
469 }
470
471 if let Some(cseq) = request.headers.get(&HeaderName::CSeq) {
473 headers.add(HeaderName::CSeq, cseq.as_str());
474 }
475
476 Self {
477 status,
478 headers,
479 body: None,
480 }
481 }
482
483 pub fn build(mut self) -> SipMessage {
484 let content_length = self.body.as_ref().map_or(0, |b| b.len());
485 self.headers
486 .set(HeaderName::ContentLength, content_length.to_string());
487
488 SipMessage::Response(SipResponse {
489 version: "SIP/2.0".to_string(),
490 status: self.status,
491 reason: self.status.reason_phrase().to_string(),
492 headers: self.headers,
493 body: self.body,
494 })
495 }
496}
497
498#[cfg(test)]
499mod tests {
500 use super::*;
501
502 const INVITE_REQUEST: &str = "INVITE sip:bob@biloxi.com SIP/2.0\r\n\
503 Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
504 Max-Forwards: 70\r\n\
505 To: Bob <sip:bob@biloxi.com>\r\n\
506 From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
507 Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
508 CSeq: 314159 INVITE\r\n\
509 Contact: <sip:alice@pc33.atlanta.com>\r\n\
510 Content-Type: application/sdp\r\n\
511 Content-Length: 4\r\n\
512 \r\n\
513 test";
514
515 const OK_RESPONSE: &str = "SIP/2.0 200 OK\r\n\
516 Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
517 To: Bob <sip:bob@biloxi.com>;tag=a6c85cf\r\n\
518 From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
519 Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
520 CSeq: 314159 INVITE\r\n\
521 Contact: <sip:bob@192.0.2.4>\r\n\
522 Content-Length: 0\r\n\
523 \r\n";
524
525 #[test]
526 fn test_parse_invite_request() {
527 let msg = SipMessage::parse(INVITE_REQUEST).unwrap();
528 assert!(msg.is_request());
529
530 if let SipMessage::Request(req) = &msg {
531 assert_eq!(req.method, SipMethod::Invite);
532 assert_eq!(req.uri, "sip:bob@biloxi.com");
533 assert_eq!(req.version, "SIP/2.0");
534 assert_eq!(
535 req.headers.get(&HeaderName::CallId).unwrap().as_str(),
536 "a84b4c76e66710@pc33.atlanta.com"
537 );
538 assert_eq!(req.body.as_deref(), Some("test"));
539 }
540 }
541
542 #[test]
543 fn test_parse_ok_response() {
544 let msg = SipMessage::parse(OK_RESPONSE).unwrap();
545 assert!(msg.is_response());
546
547 if let SipMessage::Response(res) = &msg {
548 assert_eq!(res.status, StatusCode::OK);
549 assert_eq!(res.reason, "OK");
550 assert_eq!(res.version, "SIP/2.0");
551 }
552 }
553
554 #[test]
555 fn test_parse_register_request() {
556 let input = "REGISTER sip:registrar.biloxi.com SIP/2.0\r\n\
557 Via: SIP/2.0/UDP bobspc.biloxi.com:5060;branch=z9hG4bKnashds7\r\n\
558 Max-Forwards: 70\r\n\
559 To: Bob <sip:bob@biloxi.com>\r\n\
560 From: Bob <sip:bob@biloxi.com>;tag=456248\r\n\
561 Call-ID: 843817637684230@998sdasdh09\r\n\
562 CSeq: 1826 REGISTER\r\n\
563 Contact: <sip:bob@192.0.2.4>\r\n\
564 Expires: 7200\r\n\
565 Content-Length: 0\r\n\
566 \r\n";
567
568 let msg = SipMessage::parse(input).unwrap();
569 if let SipMessage::Request(req) = &msg {
570 assert_eq!(req.method, SipMethod::Register);
571 assert_eq!(req.uri, "sip:registrar.biloxi.com");
572 } else {
573 panic!("Expected request");
574 }
575 }
576
577 #[test]
578 fn test_cseq_parsing() {
579 let msg = SipMessage::parse(INVITE_REQUEST).unwrap();
580 let (seq, method) = msg.cseq().unwrap();
581 assert_eq!(seq, 314159);
582 assert_eq!(method, SipMethod::Invite);
583 }
584
585 #[test]
586 fn test_call_id() {
587 let msg = SipMessage::parse(INVITE_REQUEST).unwrap();
588 assert_eq!(
589 msg.call_id().unwrap(),
590 "a84b4c76e66710@pc33.atlanta.com"
591 );
592 }
593
594 #[test]
595 fn test_method_enum() {
596 assert_eq!(SipMethod::from_str("INVITE"), SipMethod::Invite);
597 assert_eq!(SipMethod::from_str("invite"), SipMethod::Invite);
598 assert_eq!(SipMethod::from_str("BYE"), SipMethod::Bye);
599 assert_eq!(SipMethod::from_str("REGISTER"), SipMethod::Register);
600 assert_eq!(SipMethod::from_str("INFO"), SipMethod::Info);
601 assert_eq!(
602 SipMethod::from_str("SUBSCRIBE"),
603 SipMethod::Other("SUBSCRIBE".to_string())
604 );
605 }
606
607 #[test]
608 fn test_status_code() {
609 assert!(StatusCode::TRYING.is_provisional());
610 assert!(StatusCode::RINGING.is_provisional());
611 assert!(StatusCode::SESSION_PROGRESS.is_provisional());
612 assert!(!StatusCode::SESSION_PROGRESS.is_success());
613 assert!(!StatusCode::SESSION_PROGRESS.is_error());
614 assert_eq!(StatusCode::SESSION_PROGRESS.0, 183);
615 assert_eq!(StatusCode::SESSION_PROGRESS.reason_phrase(), "Session Progress");
616 assert!(StatusCode::OK.is_success());
617 assert!(StatusCode::UNAUTHORIZED.is_error());
618 assert!(StatusCode::NOT_FOUND.is_error());
619 }
620
621 #[test]
622 fn test_parse_183_session_progress() {
623 let raw = "SIP/2.0 183 Session Progress\r\n\
624 Via: SIP/2.0/UDP 10.0.0.1:5060;branch=z9hG4bK776\r\n\
625 From: <sip:alice@example.com>;tag=abc123\r\n\
626 To: <sip:bob@example.com>;tag=xyz789\r\n\
627 Call-ID: early-media-test@10.0.0.1\r\n\
628 CSeq: 1 INVITE\r\n\
629 Content-Type: application/sdp\r\n\
630 Content-Length: 0\r\n\
631 \r\n";
632 let msg = SipMessage::parse(raw).expect("should parse 183");
633 assert!(msg.is_response());
634 let status = msg.status().expect("should have status");
635 assert_eq!(status.0, 183);
636 assert!(status.is_provisional());
637 assert!(!status.is_success());
638 assert_eq!(status.reason_phrase(), "Session Progress");
639 }
640
641 #[test]
642 fn test_request_builder() {
643 let msg = RequestBuilder::new(SipMethod::Register, "sip:registrar.example.com")
644 .header(
645 HeaderName::Via,
646 "SIP/2.0/UDP 10.0.0.1:5060;branch=z9hG4bK776",
647 )
648 .header(HeaderName::From, "<sip:alice@example.com>;tag=abc123")
649 .header(HeaderName::To, "<sip:alice@example.com>")
650 .header(HeaderName::CallId, "unique-call-id@10.0.0.1")
651 .header(HeaderName::CSeq, "1 REGISTER")
652 .build();
653
654 assert!(msg.is_request());
655 if let SipMessage::Request(req) = &msg {
656 assert_eq!(req.method, SipMethod::Register);
657 assert_eq!(
658 req.headers.get(&HeaderName::ContentLength).unwrap().as_str(),
659 "0"
660 );
661 }
662 }
663
664 #[test]
665 fn test_response_builder_from_request() {
666 let invite = SipMessage::parse(INVITE_REQUEST).unwrap();
667 if let SipMessage::Request(req) = &invite {
668 let response = ResponseBuilder::from_request(req, StatusCode::OK).build();
669 if let SipMessage::Response(res) = &response {
670 assert_eq!(res.status, StatusCode::OK);
671 assert_eq!(
672 res.headers.get(&HeaderName::CallId).unwrap().as_str(),
673 "a84b4c76e66710@pc33.atlanta.com"
674 );
675 assert_eq!(
676 res.headers.get(&HeaderName::CSeq).unwrap().as_str(),
677 "314159 INVITE"
678 );
679 } else {
680 panic!("Expected response");
681 }
682 }
683 }
684
685 #[test]
686 fn test_message_serialization_roundtrip() {
687 let msg = RequestBuilder::new(SipMethod::Invite, "sip:bob@biloxi.com")
688 .header(
689 HeaderName::Via,
690 "SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776",
691 )
692 .header(HeaderName::From, "<sip:alice@atlanta.com>;tag=123")
693 .header(HeaderName::To, "<sip:bob@biloxi.com>")
694 .header(HeaderName::CallId, "test-call-id@pc33")
695 .header(HeaderName::CSeq, "1 INVITE")
696 .build();
697
698 let serialized = msg.to_string();
699 let parsed = SipMessage::parse(&serialized).unwrap();
700 assert!(parsed.is_request());
701 assert_eq!(parsed.call_id().unwrap(), "test-call-id@pc33");
702 }
703
704 #[test]
705 fn test_parse_error_invalid_start_line() {
706 let result = SipMessage::parse("NOT A VALID SIP MESSAGE");
707 assert!(result.is_ok()); }
711
712 #[test]
713 fn test_parse_error_empty() {
714 let result = SipMessage::parse("");
715 assert!(result.is_err());
716 }
717
718 #[test]
719 fn test_parse_401_response() {
720 let input = "SIP/2.0 401 Unauthorized\r\n\
721 Via: SIP/2.0/UDP 10.0.0.1:5060;branch=z9hG4bK776\r\n\
722 From: <sip:alice@example.com>;tag=123\r\n\
723 To: <sip:alice@example.com>;tag=456\r\n\
724 Call-ID: test-call-id\r\n\
725 CSeq: 1 REGISTER\r\n\
726 WWW-Authenticate: Digest realm=\"example.com\", nonce=\"abc123\"\r\n\
727 Content-Length: 0\r\n\
728 \r\n";
729
730 let msg = SipMessage::parse(input).unwrap();
731 if let SipMessage::Response(res) = &msg {
732 assert_eq!(res.status, StatusCode::UNAUTHORIZED);
733 assert!(res
734 .headers
735 .get(&HeaderName::WwwAuthenticate)
736 .is_some());
737 } else {
738 panic!("Expected response");
739 }
740 }
741}