1use std::fmt;
4use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
5
6use bytes::Bytes;
7use thiserror::Error;
8
9use crate::error::{BuildError, UriError};
10use crate::escape;
11use crate::params::{Param, Params};
12
13#[derive(Debug, Clone)]
18pub enum Scheme {
19 Sip,
21 Sips,
23 Tel,
25 Other(Bytes),
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31pub enum UriTransport {
32 Udp,
34 Tcp,
36 Tls,
38 Ws,
40 Wss,
42 Quic,
44}
45
46impl UriTransport {
47 #[must_use]
49 pub fn default_port(self) -> u16 {
50 match self {
51 Self::Udp | Self::Tcp => 5060,
52 Self::Tls | Self::Quic => 5061,
53 Self::Ws => 80,
54 Self::Wss => 443,
55 }
56 }
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
61#[non_exhaustive]
62pub enum UriTransportError {
63 #[error("the URI is not a SIP or SIPS URI")]
65 NotSip,
66 #[error("the URI names an unsupported transport")]
68 Unsupported,
69 #[error("a SIPS URI cannot select UDP")]
71 SecureDatagram,
72}
73
74impl Scheme {
75 #[must_use]
76 fn parse(raw: &Bytes) -> Self {
77 if escape::eq_ignore_ascii_case(raw, b"sip") {
78 Self::Sip
79 } else if escape::eq_ignore_ascii_case(raw, b"sips") {
80 Self::Sips
81 } else if escape::eq_ignore_ascii_case(raw, b"tel") {
82 Self::Tel
83 } else {
84 Self::Other(raw.clone())
85 }
86 }
87
88 #[must_use]
90 pub fn as_bytes(&self) -> &[u8] {
91 match self {
92 Self::Sip => b"sip",
93 Self::Sips => b"sips",
94 Self::Tel => b"tel",
95 Self::Other(raw) => raw,
96 }
97 }
98
99 #[must_use]
101 pub fn is_secure(&self) -> bool {
102 matches!(self, Self::Sips)
103 }
104
105 #[must_use]
107 pub fn is_sip(&self) -> bool {
108 matches!(self, Self::Sip | Self::Sips)
109 }
110
111 #[must_use]
112 fn equivalent(&self, other: &Self) -> bool {
113 escape::eq_ignore_ascii_case(self.as_bytes(), other.as_bytes())
114 }
115}
116
117#[derive(Debug, Clone)]
124pub struct HostName(Bytes);
125
126impl HostName {
127 pub fn new(name: impl Into<Bytes>) -> Result<Self, BuildError> {
129 let name = name.into();
130 if name.is_empty() || !name.iter().all(|&b| is_host_char(b)) {
131 return Err(BuildError::NotAToken { field: "host" });
132 }
133 Ok(Self(name))
134 }
135
136 pub(crate) fn new_unchecked(name: Bytes) -> Self {
137 Self(name)
138 }
139
140 #[must_use]
142 pub fn as_bytes(&self) -> &[u8] {
143 &self.0
144 }
145}
146
147impl PartialEq<&[u8]> for HostName {
148 fn eq(&self, other: &&[u8]) -> bool {
149 self.0 == *other
150 }
151}
152
153impl PartialEq<&str> for HostName {
154 fn eq(&self, other: &&str) -> bool {
155 self.0 == other.as_bytes()
156 }
157}
158
159#[derive(Debug, Clone)]
161pub enum Host {
162 Name(HostName),
164 Ip(IpAddr),
166}
167
168impl Host {
169 pub fn parse_hostport(raw: &Bytes) -> Result<(Self, Option<u16>), UriError> {
174 parse_hostport(raw)
175 }
176
177 #[must_use]
179 pub fn to_bytes(&self) -> Bytes {
180 match self {
181 Self::Name(name) => name.0.clone(),
182 Self::Ip(ip) => Bytes::from(ip.to_string()),
183 }
184 }
185
186 #[must_use]
192 pub fn equivalent(&self, other: &Self) -> bool {
193 match (self, other) {
194 (Self::Ip(a), Self::Ip(b)) => a == b,
195 (Self::Name(a), Self::Name(b)) => escape::eq_ignore_ascii_case(&a.0, &b.0),
196 _ => false,
197 }
198 }
199}
200
201impl fmt::Display for Host {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 match self {
204 Self::Name(name) => write!(f, "{}", String::from_utf8_lossy(&name.0)),
205 Self::Ip(IpAddr::V6(ip)) => write!(f, "[{ip}]"),
206 Self::Ip(ip) => write!(f, "{ip}"),
207 }
208 }
209}
210
211#[derive(Debug, Clone)]
213struct SipParts {
214 user: Option<Bytes>,
215 raw_user_span: Option<std::ops::Range<usize>>,
218 password: Option<Bytes>,
219 host: Host,
220 port: Option<u16>,
221 params: Params,
222 headers: Params,
223}
224
225#[derive(Debug, Clone)]
226enum Parts {
227 Sip(Box<SipParts>),
228 Opaque(Bytes),
230}
231
232#[derive(Debug, Clone, Copy, PartialEq, Eq)]
237pub struct TelUriParts<'a> {
238 subscriber: &'a [u8],
239 parameters: Option<&'a [u8]>,
240}
241
242impl<'a> TelUriParts<'a> {
243 #[must_use]
245 pub fn subscriber(&self) -> &'a [u8] {
246 self.subscriber
247 }
248
249 #[must_use]
254 pub fn parameters(&self) -> Option<&'a [u8]> {
255 self.parameters
256 }
257
258 #[must_use]
264 pub fn parsed_parameters(&self) -> TelParameters<'a> {
265 TelParameters {
266 remaining: self.parameters,
267 tail_len: self.parameters.map_or(0, <[u8]>::len),
268 }
269 }
270}
271
272#[derive(Debug, Clone)]
274pub struct TelParameters<'a> {
275 remaining: Option<&'a [u8]>,
276 tail_len: usize,
277}
278
279#[derive(Debug, Clone, Copy, PartialEq, Eq)]
281pub struct TelParameter<'a> {
282 name: &'a [u8],
283 value: Option<&'a [u8]>,
284}
285
286impl<'a> TelParameter<'a> {
287 #[must_use]
289 pub fn name(&self) -> &'a [u8] {
290 self.name
291 }
292
293 #[must_use]
295 pub fn value(&self) -> Option<&'a [u8]> {
296 self.value
297 }
298
299 #[must_use]
303 pub fn name_eq(&self, expected: &[u8]) -> bool {
304 valid_tel_parameter_name(expected) && escape::eq_ignore_ascii_case(self.name, expected)
305 }
306}
307
308#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
310#[error("invalid TEL parameter at tail byte {offset}: {kind}")]
311pub struct TelParameterError {
312 offset: usize,
313 kind: TelParameterErrorKind,
314}
315
316impl TelParameterError {
317 #[must_use]
319 pub fn offset(&self) -> usize {
320 self.offset
321 }
322
323 #[must_use]
325 pub fn kind(&self) -> TelParameterErrorKind {
326 self.kind
327 }
328}
329
330#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
332#[non_exhaustive]
333pub enum TelParameterErrorKind {
334 #[error("empty parameter")]
336 Empty,
337 #[error("invalid parameter name")]
339 Name,
340 #[error("invalid parameter value")]
342 Value,
343}
344
345impl<'a> Iterator for TelParameters<'a> {
346 type Item = Result<TelParameter<'a>, TelParameterError>;
347
348 fn next(&mut self) -> Option<Self::Item> {
349 let remaining = self.remaining.take()?;
350 let offset = self.tail_len.saturating_sub(remaining.len());
351 let (segment, rest) = match remaining.iter().position(|&byte| byte == b';') {
352 Some(separator) => (
353 remaining.get(..separator).unwrap_or(&[]),
354 remaining.get(separator.saturating_add(1)..),
355 ),
356 None => (remaining, None),
357 };
358 self.remaining = rest;
359
360 match parse_tel_parameter(segment, offset) {
361 Ok(parameter) => Some(Ok(parameter)),
362 Err(error) => {
363 self.remaining = None;
364 Some(Err(error))
365 }
366 }
367 }
368}
369
370impl std::iter::FusedIterator for TelParameters<'_> {}
371
372#[derive(Debug, Clone)]
384pub struct Uri {
385 scheme: Scheme,
386 parts: Parts,
387 raw: Option<Bytes>,
391 raw_tel_subscriber_span: Option<std::ops::Range<usize>>,
394}
395
396impl Uri {
397 pub fn parse(raw: Bytes) -> Result<Self, UriError> {
402 for &b in &raw {
403 if !(0x21..=0x7e).contains(&b) || matches!(b, b'<' | b'>' | b'"') {
407 return Err(UriError::IllegalCharacter);
408 }
409 }
410
411 let colon = raw
412 .iter()
413 .position(|&b| b == b':')
414 .ok_or(UriError::Scheme)?;
415 let scheme_raw = raw.slice(..colon);
416 if scheme_raw.is_empty() || !scheme_raw.iter().all(|&b| is_scheme_char(b)) {
417 return Err(UriError::Scheme);
418 }
419 let scheme = Scheme::parse(&scheme_raw);
420 let rest = raw.slice(colon + 1..);
421 if !escape::escapes_are_well_formed(&rest) {
422 return Err(UriError::PercentEscape);
423 }
424
425 let raw_tel_subscriber_span = if matches!(scheme, Scheme::Tel) {
426 let body_offset = colon.checked_add(1).ok_or(UriError::TelephoneSubscriber)?;
427 let subscriber = split_tel_body(&rest).subscriber;
428 validate_tel_subscriber(subscriber)?;
429 let subscriber_len = subscriber.len();
430 let end = body_offset
431 .checked_add(subscriber_len)
432 .ok_or(UriError::TelephoneSubscriber)?;
433 Some(body_offset..end)
434 } else {
435 None
436 };
437
438 let parts = if scheme.is_sip() {
439 Parts::Sip(Box::new(parse_sip_parts(&rest, colon + 1)?))
440 } else {
441 Parts::Opaque(rest)
442 };
443
444 Ok(Self {
445 scheme,
446 parts,
447 raw: Some(raw),
448 raw_tel_subscriber_span,
449 })
450 }
451
452 #[must_use]
454 pub fn sip(host: Host) -> Self {
455 Self {
456 scheme: Scheme::Sip,
457 parts: Parts::Sip(Box::new(SipParts {
458 user: None,
459 raw_user_span: None,
460 password: None,
461 host,
462 port: None,
463 params: Params::new(),
464 headers: Params::new(),
465 })),
466 raw: None,
467 raw_tel_subscriber_span: None,
468 }
469 }
470
471 #[must_use]
473 pub fn scheme(&self) -> &Scheme {
474 &self.scheme
475 }
476
477 #[must_use]
478 fn sip_parts(&self) -> Option<&SipParts> {
479 match &self.parts {
480 Parts::Sip(p) => Some(p),
481 Parts::Opaque(_) => None,
482 }
483 }
484
485 #[must_use]
486 fn sip_parts_mut(&mut self) -> Option<&mut SipParts> {
487 match &mut self.parts {
488 Parts::Sip(p) => {
489 self.raw = None;
493 p.raw_user_span = None;
494 Some(p)
495 }
496 Parts::Opaque(_) => None,
497 }
498 }
499
500 #[must_use]
503 pub fn user(&self) -> Option<&[u8]> {
504 self.sip_parts().and_then(|p| p.user.as_deref())
505 }
506
507 #[must_use]
512 pub fn password(&self) -> Option<&[u8]> {
513 self.sip_parts().and_then(|p| p.password.as_deref())
514 }
515
516 #[must_use]
524 pub fn decoded_user(&self) -> Option<Vec<u8>> {
525 self.user().and_then(escape::decode)
526 }
527
528 pub fn replace_user(&mut self, user: impl Into<Bytes>) -> Result<bool, UriError> {
542 if !self.scheme.is_sip() {
543 return Ok(false);
544 }
545
546 let (raw, parts) = (&mut self.raw, &mut self.parts);
547 let Parts::Sip(parts) = parts else {
548 return Ok(false);
549 };
550 if parts.user.is_none() {
551 return Ok(false);
552 }
553 let user = user.into();
554 validate_user(&user)?;
555
556 let rewritten = match (raw.as_ref(), parts.raw_user_span.as_ref()) {
557 (Some(verbatim), Some(span)) => {
558 let end = span.start.checked_add(user.len()).ok_or(UriError::User)?;
559 let value = replace_raw_span(verbatim, span, &user).ok_or(UriError::User)?;
560 Some((value, span.start..end))
561 }
562 (Some(_), None) => return Err(UriError::User),
563 (None, _) => None,
564 };
565
566 parts.user = Some(user);
567 if let Some((value, span)) = rewritten {
568 *raw = Some(value);
569 parts.raw_user_span = Some(span);
570 } else {
571 *raw = None;
572 parts.raw_user_span = None;
573 }
574 Ok(true)
575 }
576
577 pub fn replace_tel_subscriber(
590 &mut self,
591 subscriber: impl Into<Bytes>,
592 ) -> Result<bool, UriError> {
593 if !matches!(self.scheme, Scheme::Tel) {
594 return Ok(false);
595 }
596
597 let subscriber = subscriber.into();
598 validate_tel_subscriber(&subscriber)?;
599
600 let (raw, parts, span) = (
601 &mut self.raw,
602 &mut self.parts,
603 &mut self.raw_tel_subscriber_span,
604 );
605 let Parts::Opaque(body) = parts else {
606 return Err(UriError::TelephoneSubscriber);
607 };
608 let (Some(verbatim), Some(current_span)) = (raw.as_ref(), span.as_ref()) else {
609 return Err(UriError::TelephoneSubscriber);
610 };
611 let start = current_span.start;
612 let end = start
613 .checked_add(subscriber.len())
614 .ok_or(UriError::TelephoneSubscriber)?;
615 let rewritten = replace_raw_span(verbatim, current_span, &subscriber)
616 .ok_or(UriError::TelephoneSubscriber)?;
617 if rewritten.get(start..).is_none() {
618 return Err(UriError::TelephoneSubscriber);
619 }
620 let mut rewritten_body = rewritten.clone();
621 let rewritten_body = rewritten_body.split_off(start);
622
623 *body = rewritten_body;
624 *raw = Some(rewritten);
625 *span = Some(start..end);
626 Ok(true)
627 }
628
629 #[must_use]
631 pub fn host(&self) -> Option<&Host> {
632 self.sip_parts().map(|p| &p.host)
633 }
634
635 #[must_use]
640 pub fn port(&self) -> Option<u16> {
641 self.sip_parts().and_then(|p| p.port)
642 }
643
644 #[must_use]
646 pub fn params(&self) -> Option<&Params> {
647 self.sip_parts().map(|p| &p.params)
648 }
649
650 #[must_use]
652 pub fn headers(&self) -> Option<&Params> {
653 self.sip_parts().map(|p| &p.headers)
654 }
655
656 #[must_use]
658 pub fn opaque(&self) -> Option<&[u8]> {
659 match &self.parts {
660 Parts::Opaque(body) => Some(body),
661 Parts::Sip(_) => None,
662 }
663 }
664
665 #[must_use]
670 pub fn tel_parts(&self) -> Option<TelUriParts<'_>> {
671 match (&self.scheme, &self.parts) {
672 (Scheme::Tel, Parts::Opaque(body)) => Some(split_tel_body(body)),
673 _ => None,
674 }
675 }
676
677 #[must_use]
679 pub fn transport(&self) -> Option<&[u8]> {
680 self.params().and_then(|p| p.value("transport"))
681 }
682
683 pub fn selected_transport(&self) -> Result<UriTransport, UriTransportError> {
685 if !self.scheme.is_sip() {
686 return Err(UriTransportError::NotSip);
687 }
688 let explicit = match self.transport().map(<[u8]>::to_ascii_lowercase) {
689 None => None,
690 Some(value) => Some(match value.as_slice() {
691 b"udp" => UriTransport::Udp,
692 b"tcp" => UriTransport::Tcp,
693 b"tls" => UriTransport::Tls,
694 b"ws" => UriTransport::Ws,
695 b"wss" => UriTransport::Wss,
696 b"quic" => UriTransport::Quic,
697 _ => return Err(UriTransportError::Unsupported),
698 }),
699 };
700 if !self.scheme.is_secure() {
701 return Ok(explicit.unwrap_or(UriTransport::Udp));
702 }
703 match explicit {
704 None | Some(UriTransport::Tcp | UriTransport::Tls) => Ok(UriTransport::Tls),
705 Some(UriTransport::Ws | UriTransport::Wss) => Ok(UriTransport::Wss),
706 Some(UriTransport::Quic) => Ok(UriTransport::Quic),
707 Some(UriTransport::Udp) => Err(UriTransportError::SecureDatagram),
708 }
709 }
710
711 pub fn push_param(&mut self, param: Param) {
716 if let Some(parts) = self.sip_parts_mut() {
717 parts.params.push(param);
718 }
719 }
720
721 pub fn push_header(&mut self, header: Param) -> bool {
727 let Some(parts) = self.sip_parts_mut() else {
728 return false;
729 };
730 parts.headers.push(header);
731 true
732 }
733
734 pub fn remove_header(&mut self, name: &str) -> bool {
736 self.sip_parts_mut()
737 .is_some_and(|parts| parts.headers.remove(name))
738 }
739
740 pub fn remove_param(&mut self, name: &str) -> bool {
744 self.sip_parts_mut()
745 .is_some_and(|parts| parts.params.remove(name))
746 }
747
748 #[must_use]
752 pub fn has_headers(&self) -> bool {
753 self.headers().is_some_and(|h| !h.is_empty())
754 }
755
756 pub fn write_to(&self, out: &mut Vec<u8>) {
760 if let Some(raw) = &self.raw {
761 out.extend_from_slice(raw);
762 return;
763 }
764 out.extend_from_slice(self.scheme.as_bytes());
765 out.push(b':');
766 match &self.parts {
767 Parts::Opaque(body) => out.extend_from_slice(body),
768 Parts::Sip(p) => {
769 if let Some(user) = &p.user {
770 out.extend_from_slice(user);
771 if let Some(password) = &p.password {
772 out.push(b':');
773 out.extend_from_slice(password);
774 }
775 out.push(b'@');
776 }
777 match &p.host {
778 Host::Ip(IpAddr::V6(ip)) => {
779 out.push(b'[');
780 out.extend_from_slice(ip.to_string().as_bytes());
781 out.push(b']');
782 }
783 host => out.extend_from_slice(&host.to_bytes()),
784 }
785 if let Some(port) = p.port {
786 out.push(b':');
787 out.extend_from_slice(port.to_string().as_bytes());
788 }
789 p.params.write_to(out, b';');
790 p.headers.write_to(out, b'?');
791 }
792 }
793 }
794
795 #[must_use]
797 pub fn to_bytes(&self) -> Bytes {
798 let mut out = Vec::new();
799 self.write_to(&mut out);
800 Bytes::from(out)
801 }
802
803 #[must_use]
807 pub fn equivalent(&self, other: &Self) -> bool {
808 if !self.scheme.equivalent(&other.scheme) {
810 return false;
811 }
812
813 let (a, b) = match (&self.parts, &other.parts) {
814 (Parts::Sip(a), Parts::Sip(b)) => (a, b),
815 (Parts::Opaque(a), Parts::Opaque(b)) => {
816 if matches!(self.scheme, Scheme::Tel) {
819 return tel_equivalent(a, b);
820 }
821 return escape::normalize_for_comparison(a) == escape::normalize_for_comparison(b);
825 }
826 _ => return false,
827 };
828
829 if !opt_bytes_equivalent(a.user.as_deref(), b.user.as_deref(), true) {
832 return false;
833 }
834 if !opt_bytes_equivalent(a.password.as_deref(), b.password.as_deref(), true) {
835 return false;
836 }
837 if !a.host.equivalent(&b.host) {
838 return false;
839 }
840 if a.port != b.port {
843 return false;
844 }
845
846 for name in ["user", "ttl", "method", "maddr", "transport"] {
849 if !a.params.param_equivalent(&b.params, name) {
850 return false;
851 }
852 }
853 if !a.params.common_params_agree(&b.params) || !b.params.common_params_agree(&a.params) {
855 return false;
856 }
857
858 header_multiset(&a.headers) == header_multiset(&b.headers)
867 }
868}
869
870impl fmt::Display for Uri {
871 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
872 write!(f, "{}", String::from_utf8_lossy(&self.to_bytes()))
873 }
874}
875
876fn header_multiset(headers: &Params) -> std::collections::BTreeMap<Vec<u8>, Vec<Vec<u8>>> {
881 let mut grouped: std::collections::BTreeMap<Vec<u8>, Vec<Vec<u8>>> =
882 std::collections::BTreeMap::new();
883 for param in headers.iter() {
884 let name = escape::normalize_for_comparison(param.name()).to_ascii_lowercase();
885 let value = param
886 .value()
887 .map(|value| escape::normalize_for_comparison(value).to_ascii_lowercase())
888 .unwrap_or_default();
889 grouped.entry(name).or_default().push(value);
890 }
891 for values in grouped.values_mut() {
892 values.sort_unstable();
893 }
894 grouped
895}
896
897#[must_use]
904fn tel_equivalent(a: &[u8], b: &[u8]) -> bool {
905 let parts_a = split_tel_body(a);
906 let parts_b = split_tel_body(b);
907
908 if !escape::eq_ignore_ascii_case(
909 &strip_visual_separators(parts_a.subscriber),
910 &strip_visual_separators(parts_b.subscriber),
911 ) {
912 return false;
913 }
914
915 tel_param_multiset(parts_a.parameters.unwrap_or_default())
916 == tel_param_multiset(parts_b.parameters.unwrap_or_default())
917}
918
919#[must_use]
921fn split_tel_body(body: &[u8]) -> TelUriParts<'_> {
922 match body.iter().position(|&b| b == b';') {
923 Some(semi) => TelUriParts {
924 subscriber: body.get(..semi).unwrap_or(&[]),
925 parameters: Some(body.get(semi + 1..).unwrap_or(&[])),
926 },
927 None => TelUriParts {
928 subscriber: body,
929 parameters: None,
930 },
931 }
932}
933
934fn parse_tel_parameter(
935 segment: &[u8],
936 offset: usize,
937) -> Result<TelParameter<'_>, TelParameterError> {
938 if segment.is_empty() {
939 return Err(TelParameterError {
940 offset,
941 kind: TelParameterErrorKind::Empty,
942 });
943 }
944 let (name, value, value_offset) = match segment.iter().position(|&byte| byte == b'=') {
945 Some(equals) => (
946 segment.get(..equals).unwrap_or(&[]),
947 Some(segment.get(equals.saturating_add(1)..).unwrap_or(&[])),
948 equals.saturating_add(1),
949 ),
950 None => (segment, None, segment.len()),
951 };
952 if name.is_empty() {
953 return Err(TelParameterError {
954 offset,
955 kind: TelParameterErrorKind::Name,
956 });
957 }
958 if let Some(invalid) = name
959 .iter()
960 .position(|&byte| !is_tel_parameter_name_char(byte))
961 {
962 return Err(TelParameterError {
963 offset: offset.checked_add(invalid).unwrap_or(offset),
964 kind: TelParameterErrorKind::Name,
965 });
966 }
967 if let Some(value) = value {
968 if value.is_empty() {
969 return Err(TelParameterError {
970 offset: offset.checked_add(value_offset).unwrap_or(offset),
971 kind: TelParameterErrorKind::Value,
972 });
973 }
974 if let Some(invalid) = invalid_tel_parameter_value_byte(value) {
975 let value_start = offset.checked_add(value_offset).unwrap_or(offset);
976 return Err(TelParameterError {
977 offset: value_start.checked_add(invalid).unwrap_or(value_start),
978 kind: TelParameterErrorKind::Value,
979 });
980 }
981 }
982 Ok(TelParameter { name, value })
983}
984
985#[must_use]
986fn valid_tel_parameter_name(name: &[u8]) -> bool {
987 !name.is_empty() && name.iter().copied().all(is_tel_parameter_name_char)
988}
989
990#[must_use]
991fn is_tel_parameter_name_char(byte: u8) -> bool {
992 byte.is_ascii_alphanumeric() || byte == b'-'
993}
994
995#[must_use]
996fn invalid_tel_parameter_value_byte(value: &[u8]) -> Option<usize> {
997 let mut index = 0;
998 while let Some(&byte) = value.get(index) {
999 if byte == b'%' {
1000 let first = value.get(index.saturating_add(1));
1001 let second = value.get(index.saturating_add(2));
1002 if !first.is_some_and(u8::is_ascii_hexdigit)
1003 || !second.is_some_and(u8::is_ascii_hexdigit)
1004 {
1005 return Some(index);
1006 }
1007 index = index.saturating_add(3);
1008 continue;
1009 }
1010 if !(byte.is_ascii_alphanumeric()
1011 || matches!(
1012 byte,
1013 b'-' | b'.'
1014 | b'_'
1015 | b'!'
1016 | b'~'
1017 | b'*'
1018 | b'\''
1019 | b'('
1020 | b')'
1021 | b'['
1022 | b']'
1023 | b'/'
1024 | b':'
1025 | b'&'
1026 | b'+'
1027 | b'$'
1028 ))
1029 {
1030 return Some(index);
1031 }
1032 index = index.saturating_add(1);
1033 }
1034 None
1035}
1036
1037#[must_use]
1039fn strip_visual_separators(number: &[u8]) -> Vec<u8> {
1040 number
1041 .iter()
1042 .copied()
1043 .filter(|b| !matches!(b, b'-' | b'.' | b'(' | b')'))
1044 .collect()
1045}
1046
1047fn tel_param_multiset(params: &[u8]) -> std::collections::BTreeMap<Vec<u8>, Vec<Vec<u8>>> {
1053 let mut grouped: std::collections::BTreeMap<Vec<u8>, Vec<Vec<u8>>> =
1054 std::collections::BTreeMap::new();
1055 if params.is_empty() {
1056 return grouped;
1057 }
1058 for segment in params.split(|&b| b == b';') {
1059 let (name, value) = match segment.iter().position(|&b| b == b'=') {
1060 Some(eq) => (
1061 segment.get(..eq).unwrap_or(&[]),
1062 segment.get(eq + 1..).unwrap_or(&[]),
1063 ),
1064 None => (segment, &[][..]),
1065 };
1066 let name = escape::normalize_for_comparison(name).to_ascii_lowercase();
1067 let mut value = escape::normalize_for_comparison(value).to_ascii_lowercase();
1068 if name == b"ext" || (name == b"phone-context" && value.first() == Some(&b'+')) {
1069 value = strip_visual_separators(&value);
1070 }
1071 grouped.entry(name).or_default().push(value);
1072 }
1073 for values in grouped.values_mut() {
1074 values.sort_unstable();
1075 }
1076 grouped
1077}
1078
1079#[must_use]
1082fn opt_bytes_equivalent(a: Option<&[u8]>, b: Option<&[u8]>, case_sensitive: bool) -> bool {
1083 match (a, b) {
1084 (None, None) => true,
1085 (Some(x), Some(y)) => {
1086 let (x, y) = (
1087 escape::normalize_for_comparison(x),
1088 escape::normalize_for_comparison(y),
1089 );
1090 if case_sensitive {
1091 x == y
1092 } else {
1093 escape::eq_ignore_ascii_case(&x, &y)
1094 }
1095 }
1096 _ => false,
1097 }
1098}
1099
1100#[must_use]
1101fn is_scheme_char(b: u8) -> bool {
1102 b.is_ascii_alphanumeric() || matches!(b, b'+' | b'-' | b'.')
1103}
1104
1105fn validate_user(user: &[u8]) -> Result<(), UriError> {
1107 if !escape::escapes_are_well_formed(user) {
1108 return Err(UriError::PercentEscape);
1109 }
1110 if user.is_empty() || !user.iter().copied().all(is_user_char) {
1111 return Err(UriError::User);
1112 }
1113 Ok(())
1114}
1115
1116fn validate_tel_subscriber(subscriber: &[u8]) -> Result<(), UriError> {
1118 let valid = if let Some(rest) = subscriber.strip_prefix(b"+") {
1119 rest.iter().copied().all(is_global_phone_digit) && rest.iter().any(u8::is_ascii_digit)
1120 } else {
1121 subscriber.iter().copied().all(is_local_phone_digit)
1122 && subscriber.iter().copied().any(is_local_phone_symbol)
1123 };
1124 if valid {
1125 Ok(())
1126 } else {
1127 Err(UriError::TelephoneSubscriber)
1128 }
1129}
1130
1131#[must_use]
1132fn is_global_phone_digit(byte: u8) -> bool {
1133 byte.is_ascii_digit() || is_visual_separator(byte)
1134}
1135
1136#[must_use]
1137fn is_local_phone_digit(byte: u8) -> bool {
1138 is_local_phone_symbol(byte) || is_visual_separator(byte)
1139}
1140
1141#[must_use]
1142fn is_local_phone_symbol(byte: u8) -> bool {
1143 byte.is_ascii_hexdigit() || matches!(byte, b'*' | b'#')
1144}
1145
1146#[must_use]
1147fn is_visual_separator(byte: u8) -> bool {
1148 matches!(byte, b'-' | b'.' | b'(' | b')')
1149}
1150
1151#[must_use]
1152fn is_user_char(b: u8) -> bool {
1153 b.is_ascii_alphanumeric()
1154 || matches!(
1155 b,
1156 b'-' | b'_'
1157 | b'.'
1158 | b'!'
1159 | b'~'
1160 | b'*'
1161 | b'\''
1162 | b'('
1163 | b')'
1164 | b'&'
1165 | b'='
1166 | b'+'
1167 | b'$'
1168 | b','
1169 | b';'
1170 | b'?'
1171 | b'/'
1172 | b'%'
1173 )
1174}
1175
1176#[must_use]
1182fn is_host_char(b: u8) -> bool {
1183 b.is_ascii_alphanumeric() || matches!(b, b'-' | b'.' | b'_')
1184}
1185
1186fn parse_sip_parts(rest: &Bytes, body_offset: usize) -> Result<SipParts, UriError> {
1187 let (userinfo, after) = match rest.iter().position(|&b| b == b'@') {
1191 Some(at) => (Some(rest.slice(..at)), rest.slice(at + 1..)),
1192 None => (None, rest.clone()),
1193 };
1194
1195 let (user, password) = match userinfo {
1196 None => (None, None),
1197 Some(info) => match info.iter().position(|&b| b == b':') {
1198 Some(c) => (Some(info.slice(..c)), Some(info.slice(c + 1..))),
1200 None => (Some(info), None),
1201 },
1202 };
1203 if let Some(user) = &user {
1204 validate_user(user)?;
1205 }
1206 let raw_user_span = user
1207 .as_ref()
1208 .and_then(|value| body_offset.checked_add(value.len()))
1209 .map(|end| body_offset..end);
1210
1211 for field in [user.as_ref(), password.as_ref()].into_iter().flatten() {
1212 if !escape::escapes_are_well_formed(field) {
1213 return Err(UriError::PercentEscape);
1214 }
1215 }
1216
1217 let (before_headers, headers_raw) = match after.iter().position(|&b| b == b'?') {
1221 Some(q) => (after.slice(..q), Some(after.slice(q + 1..))),
1222 None => (after.clone(), None),
1223 };
1224 let (hostport, params_raw) = match before_headers.iter().position(|&b| b == b';') {
1225 Some(s) => (
1226 before_headers.slice(..s),
1227 Some(before_headers.slice(s + 1..)),
1228 ),
1229 None => (before_headers.clone(), None),
1230 };
1231
1232 let (host, port) = parse_hostport(&hostport)?;
1233
1234 let params = match params_raw {
1235 Some(raw) => parse_params(&raw, b';')?,
1236 None => Params::new(),
1237 };
1238 let headers = match headers_raw {
1239 Some(raw) => parse_params(&raw, b'&')?,
1240 None => Params::new(),
1241 };
1242
1243 Ok(SipParts {
1244 user,
1245 raw_user_span,
1246 password,
1247 host,
1248 port,
1249 params,
1250 headers,
1251 })
1252}
1253
1254fn replace_raw_span(
1256 raw: &Bytes,
1257 span: &std::ops::Range<usize>,
1258 replacement: &[u8],
1259) -> Option<Bytes> {
1260 let before = raw.get(..span.start)?;
1261 let after = raw.get(span.end..)?;
1262 let capacity = before
1263 .len()
1264 .checked_add(replacement.len())?
1265 .checked_add(after.len())?;
1266 let mut rewritten = Vec::with_capacity(capacity);
1267 rewritten.extend_from_slice(before);
1268 rewritten.extend_from_slice(replacement);
1269 rewritten.extend_from_slice(after);
1270 Some(Bytes::from(rewritten))
1271}
1272
1273fn parse_hostport(hostport: &Bytes) -> Result<(Host, Option<u16>), UriError> {
1274 if hostport.is_empty() {
1275 return Err(UriError::EmptyHost);
1276 }
1277
1278 if hostport.first() == Some(&b'[') {
1279 let close = hostport
1280 .iter()
1281 .position(|&b| b == b']')
1282 .ok_or(UriError::Ipv6Reference)?;
1283 let inner = hostport.slice(1..close);
1284 let text = std::str::from_utf8(&inner).map_err(|_| UriError::Host)?;
1285 let ip = parse_ipv6_reference(text)?;
1286 let tail = hostport.slice(close + 1..);
1287 let port = parse_port_suffix(&tail)?;
1288 return Ok((Host::Ip(IpAddr::V6(ip)), port));
1289 }
1290
1291 let (host_raw, port) = match hostport.iter().position(|&b| b == b':') {
1292 Some(c) => (hostport.slice(..c), parse_port(&hostport.slice(c + 1..))?),
1293 None => (hostport.clone(), None),
1294 };
1295
1296 if host_raw.is_empty() {
1297 return Err(UriError::EmptyHost);
1298 }
1299 if !host_raw.iter().all(|&b| is_host_char(b)) {
1300 return Err(UriError::Host);
1301 }
1302
1303 let host = std::str::from_utf8(&host_raw)
1304 .ok()
1305 .and_then(|s| s.parse::<Ipv4Addr>().ok())
1306 .map_or_else(
1307 || Host::Name(HostName::new_unchecked(host_raw.clone())),
1308 |ip| Host::Ip(IpAddr::V4(ip)),
1309 );
1310
1311 Ok((host, port))
1312}
1313
1314fn parse_ipv6_reference(text: &str) -> Result<Ipv6Addr, UriError> {
1347 if let Ok(ip) = text.parse::<Ipv6Addr>() {
1348 return Ok(ip);
1349 }
1350
1351 let (hexpart, embedded) = text.split_once(":::").ok_or(UriError::Host)?;
1356 if embedded.parse::<Ipv4Addr>().is_err() {
1357 return Err(UriError::Host);
1358 }
1359
1360 let mut corrected = String::with_capacity(text.len());
1361 corrected.push_str(hexpart);
1362 corrected.push_str("::");
1363 corrected.push_str(embedded);
1364 corrected.parse::<Ipv6Addr>().map_err(|_| UriError::Host)
1365}
1366
1367fn parse_port_suffix(tail: &Bytes) -> Result<Option<u16>, UriError> {
1368 if tail.is_empty() {
1369 return Ok(None);
1370 }
1371 if tail.first() != Some(&b':') {
1372 return Err(UriError::Host);
1373 }
1374 parse_port(&tail.slice(1..))
1375}
1376
1377fn parse_port(raw: &Bytes) -> Result<Option<u16>, UriError> {
1378 if raw.is_empty() || !raw.iter().all(u8::is_ascii_digit) {
1379 return Err(UriError::Port);
1380 }
1381 if raw.len() > 5 {
1384 return Err(UriError::Port);
1385 }
1386 let mut value: u32 = 0;
1387 for &b in raw {
1388 value = value * 10 + u32::from(b - b'0');
1389 }
1390 u16::try_from(value).map(Some).map_err(|_| UriError::Port)
1391}
1392
1393fn parse_params(raw: &Bytes, separator: u8) -> Result<Params, UriError> {
1394 let mut params = Params::new();
1395 if raw.is_empty() {
1398 return Err(UriError::EmptyParameterName);
1399 }
1400 let mut start = 0usize;
1401 let cut = |from: usize, to: usize, params: &mut Params| -> Result<(), UriError> {
1402 let field = raw.slice(from..to);
1403 if field.is_empty() {
1407 return Err(UriError::EmptyParameterName);
1408 }
1409 let param = match field.iter().position(|&b| b == b'=') {
1410 Some(eq) => {
1411 let name = field.slice(..eq);
1412 if name.is_empty() {
1413 return Err(UriError::EmptyParameterName);
1414 }
1415 Param::new(name, field.slice(eq + 1..))
1416 }
1417 None => Param::flag(field),
1418 };
1419 if !escape::escapes_are_well_formed(param.name())
1420 || param
1421 .value()
1422 .is_some_and(|v| !escape::escapes_are_well_formed(v))
1423 {
1424 return Err(UriError::PercentEscape);
1425 }
1426 if separator == b';'
1431 && params
1432 .iter()
1433 .any(|existing| crate::params::names_equivalent(existing.name(), param.name()))
1434 {
1435 return Err(UriError::DuplicateParameterName);
1436 }
1437 params.push(param);
1438 Ok(())
1439 };
1440
1441 for (i, &b) in raw.iter().enumerate() {
1442 if b == separator {
1443 cut(start, i, &mut params)?;
1444 start = i + 1;
1445 }
1446 }
1447 cut(start, raw.len(), &mut params)?;
1448 Ok(params)
1449}
1450
1451#[cfg(test)]
1452#[allow(
1453 clippy::unwrap_used,
1454 clippy::expect_used,
1455 clippy::panic,
1456 clippy::indexing_slicing
1457)]
1458mod tests {
1459 use super::*;
1460
1461 fn uri(s: &str) -> Uri {
1462 Uri::parse(Bytes::from(s.to_owned())).unwrap_or_else(|e| panic!("{s:?} should parse: {e}"))
1463 }
1464
1465 #[test]
1468 fn uri_equivalence_rfc3261_19_1_4() {
1469 let equivalent: &[(&str, &str)] = &[
1470 (
1471 "sip:%61lice@atlanta.com;transport=TCP",
1472 "sip:alice@AtLanTa.CoM;Transport=tcp",
1473 ),
1474 ("sip:carol@chicago.com", "sip:carol@chicago.com;newparam=5"),
1475 ("sip:carol@chicago.com", "sip:carol@chicago.com;security=on"),
1476 (
1477 "sip:carol@chicago.com;newparam=5",
1478 "sip:carol@chicago.com;security=on",
1479 ),
1480 (
1481 "sip:biloxi.com;transport=tcp;method=REGISTER?to=sip:bob%40biloxi.com",
1482 "sip:biloxi.com;method=REGISTER;transport=tcp?to=sip:bob%40biloxi.com",
1483 ),
1484 (
1485 "sip:alice@atlanta.com?subject=project%20x&priority=urgent",
1486 "sip:alice@atlanta.com?priority=urgent&subject=project%20x",
1487 ),
1488 ];
1489 for (a, b) in equivalent {
1490 assert!(
1491 uri(a).equivalent(&uri(b)),
1492 "RFC 3261 19.1.4 says these are equivalent:\n {a}\n {b}"
1493 );
1494 assert!(uri(b).equivalent(&uri(a)), "equivalence must be symmetric");
1495 }
1496
1497 let different: &[(&str, &str, &str)] = &[
1498 (
1499 "SIP:ALICE@AtLanTa.CoM;Transport=udp",
1500 "sip:alice@AtLanTa.CoM;Transport=UDP",
1501 "different usernames",
1502 ),
1503 (
1504 "sip:bob@biloxi.com",
1505 "sip:bob@biloxi.com:5060",
1506 "can resolve to different ports",
1507 ),
1508 (
1509 "sip:bob@biloxi.com",
1510 "sip:bob@biloxi.com;transport=udp",
1511 "can resolve to different transports",
1512 ),
1513 (
1514 "sip:bob@biloxi.com",
1515 "sip:bob@biloxi.com:6000;transport=tcp",
1516 "different port and transport",
1517 ),
1518 (
1519 "sip:carol@chicago.com",
1520 "sip:carol@chicago.com?Subject=next%20meeting",
1521 "different header component",
1522 ),
1523 (
1524 "sip:bob@phone21.boxesbybob.com",
1525 "sip:bob@192.0.2.4",
1526 "a hostname never matches an IP address",
1527 ),
1528 ];
1529 for (a, b, why) in different {
1530 assert!(
1531 !uri(a).equivalent(&uri(b)),
1532 "RFC 3261 19.1.4 says these differ ({why}):\n {a}\n {b}"
1533 );
1534 }
1535 }
1536
1537 #[test]
1538 fn sip_and_sips_are_never_equivalent() {
1539 assert!(!uri("sip:a@b.com").equivalent(&uri("sips:a@b.com")));
1540 }
1541
1542 #[test]
1546 fn tel_uri_equivalence_rfc3966_4_1() {
1547 let equivalent: &[(&str, &str)] = &[
1548 ("tel:+1-201-555-0123", "tel:+12015550123"),
1550 (
1551 "tel:7042;phone-context=example.com",
1552 "tel:7042;PHONE-CONTEXT=EXAMPLE.COM",
1553 ),
1554 (
1556 "tel:863-1234;phone-context=+1-914-555",
1557 "tel:8631234;phone-context=+1914555",
1558 ),
1559 (
1561 "tel:7042;ext=1;phone-context=example.com",
1562 "tel:7042;phone-context=example.com;ext=1",
1563 ),
1564 ];
1565 for (a, b) in equivalent {
1566 assert!(
1567 uri(a).equivalent(&uri(b)),
1568 "RFC 3966 4.1 says these are equivalent:\n {a}\n {b}"
1569 );
1570 assert!(uri(b).equivalent(&uri(a)), "equivalence must be symmetric");
1571 }
1572
1573 let different: &[(&str, &str, &str)] = &[
1574 (
1575 "tel:+12015550123",
1576 "tel:12015550123",
1577 "a global number never matches a local one",
1578 ),
1579 (
1580 "tel:7042;phone-context=example.com",
1581 "tel:7042",
1582 "a parameter present in only one is a difference",
1583 ),
1584 (
1585 "tel:+1-201-555-0123",
1586 "tel:+1-201-555-0124",
1587 "different numbers",
1588 ),
1589 (
1590 "tel:7042;phone-context=example.com",
1591 "tel:7042;phone-context=example.org",
1592 "different phone-context domains",
1593 ),
1594 ];
1595 for (a, b, why) in different {
1596 assert!(
1597 !uri(a).equivalent(&uri(b)),
1598 "RFC 3966 4.1 says these differ ({why}):\n {a}\n {b}"
1599 );
1600 }
1601 }
1602
1603 #[test]
1608 fn escaped_parameter_names_are_the_same_parameter() {
1609 assert!(uri("sip:h;transport=udp").equivalent(&uri("sip:h;%74ransport=udp")));
1610 assert_eq!(uri("sip:h;%74ransport=tcp").transport(), Some(&b"tcp"[..]));
1611
1612 assert!(!uri("sip:h").equivalent(&uri("sip:h;m%61ddr=239.1.1.1")));
1614 assert!(!uri("sip:h;m%61ddr=239.1.1.1").equivalent(&uri("sip:h")));
1615
1616 assert!(!uri("sip:h;foo=1").equivalent(&uri("sip:h;%66oo=2")));
1618 }
1619
1620 #[test]
1623 fn equivalence_is_not_transitive() {
1624 let plain = uri("sip:carol@chicago.com");
1625 let on = uri("sip:carol@chicago.com;security=on");
1626 let off = uri("sip:carol@chicago.com;security=off");
1627 assert!(plain.equivalent(&on));
1628 assert!(plain.equivalent(&off));
1629 assert!(!on.equivalent(&off));
1630 }
1631
1632 #[test]
1633 fn parses_userinfo_with_password() {
1634 let u = uri("sip:alice:secret@atlanta.com");
1635 assert_eq!(u.user(), Some(&b"alice"[..]));
1636 assert_eq!(u.password(), Some(&b"secret"[..]));
1637 }
1638
1639 #[test]
1642 fn user_part_may_contain_parameter_and_header_delimiters() {
1643 let u = uri(
1644 "sip:1_unusual.URI~(to-be!sure)&isn't+it$/crazy?,/;;*:&it+has=1,weird!*pas$wo~d_too.(doesn't-it)@example.com",
1645 );
1646 assert_eq!(
1647 u.user(),
1648 Some(&b"1_unusual.URI~(to-be!sure)&isn't+it$/crazy?,/;;*"[..])
1649 );
1650 assert_eq!(
1651 u.password(),
1652 Some(&b"&it+has=1,weird!*pas$wo~d_too.(doesn't-it)"[..])
1653 );
1654 assert!(matches!(u.host(), Some(Host::Name(h)) if *h == "example.com"));
1655 assert!(u.params().is_some_and(Params::is_empty));
1656 assert!(!u.has_headers());
1657 }
1658
1659 #[test]
1662 fn semicolons_in_user_part_are_not_parameters() {
1663 let u = uri("sip:user;par=u%40example.net@example.com");
1664 assert_eq!(u.user(), Some(&b"user;par=u%40example.net"[..]));
1665 assert!(u.params().is_some_and(Params::is_empty));
1666 }
1667
1668 #[test]
1671 fn decodes_escaped_null_in_user_part() {
1672 let u = uri("sip:null-%00-null@example.com");
1673 assert_eq!(u.user(), Some(&b"null-%00-null"[..]));
1674 assert_eq!(u.decoded_user(), Some(b"null-\x00-null".to_vec()));
1675 assert_eq!(
1677 u.to_bytes(),
1678 Bytes::from_static(b"sip:null-%00-null@example.com")
1679 );
1680 }
1681
1682 #[test]
1683 fn parses_ipv6_reference_with_and_without_port() {
1684 let u = uri("sip:alice@[2001:db8::1]");
1685 assert!(matches!(u.host(), Some(Host::Ip(IpAddr::V6(_)))));
1686 assert_eq!(u.port(), None);
1687
1688 let u = uri("sip:alice@[2001:db8::1]:5061");
1689 assert_eq!(u.port(), Some(5061));
1690 }
1691
1692 #[test]
1695 fn tolerates_three_colons_before_an_embedded_ipv4_address() {
1696 let buggy = uri("sip:user@[2001:db8:::192.0.2.1]");
1697 let correct = uri("sip:user@[2001:db8::192.0.2.1]");
1698 let expected = "2001:db8::192.0.2.1"
1699 .parse::<IpAddr>()
1700 .expect("a valid RFC 4291 address");
1701
1702 for (u, what) in [(&buggy, "three-colon"), (&correct, "two-colon")] {
1703 match u.host() {
1704 Some(Host::Ip(ip)) => assert_eq!(*ip, expected, "{what} form"),
1705 other => panic!("{what} form should be an IPv6 literal, got {other:?}"),
1706 }
1707 }
1708
1709 assert_eq!(
1711 buggy.to_bytes(),
1712 Bytes::from_static(b"sip:user@[2001:db8:::192.0.2.1]")
1713 );
1714
1715 let (host, port) =
1719 Host::parse_hostport(&Bytes::from_static(b"[2001:db8:::192.0.2.1]:5060"))
1720 .expect("a Via sent-by holds an IPv6reference too");
1721 assert!(matches!(host, Host::Ip(ip) if ip == expected));
1722 assert_eq!(port, Some(5060));
1723
1724 for (input, want) in [
1729 ("sip:user@[:::192.0.2.1]", "::192.0.2.1"),
1730 ("sip:user@[1:2:3:4:5:::192.0.2.1]", "1:2:3:4:5::192.0.2.1"),
1731 ] {
1732 let want = want.parse::<IpAddr>().expect("a valid RFC 4291 address");
1733 match uri(input).host() {
1734 Some(Host::Ip(ip)) => assert_eq!(*ip, want, "{input}"),
1735 other => panic!("{input} should be an IPv6 literal, got {other:?}"),
1736 }
1737 }
1738 }
1739
1740 #[test]
1743 fn three_colons_anywhere_but_before_an_embedded_ipv4_address_stay_rejected() {
1744 let rejected: &[(&str, UriError)] = &[
1748 ("sip:user@[2001:db8:::10]", UriError::Host),
1750 ("sip:user@[2001:db8:::]", UriError::Host),
1751 ("sip:user@[:::]", UriError::Host),
1752 ("sip:user@[2001:db8:::192.0.2]", UriError::Host),
1754 ("sip:user@[2001:db8:::192.0.2.1.5]", UriError::Host),
1755 ("sip:user@[2001:db8:::192.0.2.256]", UriError::Host),
1756 ("sip:user@[2001:db8:::0192.0.2.1]", UriError::Host),
1757 ("sip:user@[2001:db8::::192.0.2.1]", UriError::Host),
1759 ("sip:user@[::::192.0.2.1]", UriError::Host),
1760 (
1763 "sip:user@[2001:db8:::192.0.2.1:::192.0.2.2]",
1764 UriError::Host,
1765 ),
1766 ("sip:user@[2001:db8::1:::192.0.2.1]", UriError::Host),
1767 ("sip:user@[2001:::db8:192.0.2.1]", UriError::Host),
1769 ("sip:user@2001:db8:::192.0.2.1", UriError::Port),
1774 ("sip:user@2001:db8::192.0.2.1", UriError::Port),
1775 ];
1776 for (input, expected) in rejected {
1777 let got = Uri::parse(Bytes::from((*input).to_owned()));
1778 assert_eq!(
1779 got.as_ref().err(),
1780 Some(expected),
1781 "{input:?} is not RFC 5118 §4.10's construct and must stay {expected:?}, \
1782 got {got:?}"
1783 );
1784 }
1785 }
1786
1787 #[test]
1788 fn parses_ipv4_literal_as_an_address() {
1789 let u = uri("sip:bob@192.0.2.4");
1790 assert!(matches!(u.host(), Some(Host::Ip(IpAddr::V4(_)))));
1791 }
1792
1793 #[test]
1794 fn scheme_and_parameter_select_one_fail_closed_transport_and_default_port() {
1795 let cases = [
1796 ("sip:h;transport=tcp", UriTransport::Tcp, 5060),
1797 ("sip:h;transport=ws", UriTransport::Ws, 80),
1798 ("sips:h;transport=tcp", UriTransport::Tls, 5061),
1799 ("sips:h;transport=tls", UriTransport::Tls, 5061),
1800 ("sips:h;transport=ws", UriTransport::Wss, 443),
1801 ("sips:h;transport=wss", UriTransport::Wss, 443),
1802 ];
1803 for (input, expected, port) in cases {
1804 let selected = uri(input).selected_transport().expect("supported mapping");
1805 assert_eq!(selected, expected, "{input}");
1806 assert_eq!(selected.default_port(), port, "{input}");
1807 }
1808 assert_eq!(
1809 uri("sips:h;transport=udp").selected_transport(),
1810 Err(UriTransportError::SecureDatagram)
1811 );
1812 }
1813
1814 #[test]
1815 fn unknown_schemes_are_kept_opaque() {
1816 let u = uri("nobodyKnowsThisScheme:totally-bogus-stuff");
1819 assert!(matches!(u.scheme(), Scheme::Other(_)));
1820 assert_eq!(u.opaque(), Some(&b"totally-bogus-stuff"[..]));
1821 assert!(u.host().is_none());
1822 }
1823
1824 #[test]
1825 fn rejects_malformed_uris() {
1826 let cases: &[(&str, UriError)] = &[
1827 ("sip:alice@example .com", UriError::IllegalCharacter),
1828 ("sip:alice@exa\tmple.com", UriError::IllegalCharacter),
1829 ("<sip:alice@example.com>", UriError::IllegalCharacter),
1830 ("alice@example.com", UriError::Scheme),
1831 (":alice@example.com", UriError::Scheme),
1832 ("sip:", UriError::EmptyHost),
1833 ("sip:alice@", UriError::EmptyHost),
1834 ("sip:alice@host:70000", UriError::Port),
1835 ("sip:alice@host:", UriError::Port),
1836 ("sip:alice@host:12x", UriError::Port),
1837 ("sip:alice@[2001:db8::1", UriError::Ipv6Reference),
1838 ("sip:alice%zz@host", UriError::PercentEscape),
1839 ];
1840 for (input, expected) in cases {
1841 let got = Uri::parse(Bytes::from((*input).to_owned()));
1842 assert_eq!(
1843 got.as_ref().err(),
1844 Some(expected),
1845 "{input:?} should be rejected as {expected:?}, got {got:?}"
1846 );
1847 }
1848 }
1849
1850 #[test]
1851 fn port_five_digits_is_bounded_before_conversion() {
1852 assert!(Uri::parse(Bytes::from_static(b"sip:h:99999")).is_err());
1854 assert!(Uri::parse(Bytes::from_static(b"sip:h:999999")).is_err());
1855 assert_eq!(uri("sip:h:65535").port(), Some(65535));
1856 }
1857
1858 #[test]
1859 fn a_parsed_uri_round_trips_byte_exactly() {
1860 for input in [
1861 "sip:vivekg@chair-dnrc.example.com;unknownparam",
1862 "SIP:ALICE@AtLanTa.CoM;Transport=udp",
1863 "sip:biloxi.com;transport=tcp;method=REGISTER?to=sip:bob%40biloxi.com",
1864 "sip:user;par=u%40example.net@example.com",
1865 "sips:alice@[2001:db8::1]:5061;maddr=239.255.255.1;ttl=15",
1866 "nobodyKnowsThisScheme:totally-bogus-stuff",
1867 ] {
1868 assert_eq!(
1869 uri(input).to_bytes(),
1870 Bytes::from(input.to_owned()),
1871 "{input} must survive a round trip unchanged"
1872 );
1873 }
1874 }
1875
1876 #[test]
1877 fn a_constructed_uri_serializes_from_its_parts() {
1878 let mut u = Uri::sip(Host::Name(
1879 HostName::new(Bytes::from_static(b"example.com")).expect("a valid host"),
1880 ));
1881 u.push_param(Param::new(
1882 Bytes::from_static(b"transport"),
1883 Bytes::from_static(b"tcp"),
1884 ));
1885 assert_eq!(
1886 u.to_bytes(),
1887 Bytes::from_static(b"sip:example.com;transport=tcp")
1888 );
1889 }
1890
1891 #[test]
1895 fn removing_a_uri_parameter_reports_whether_there_was_one() {
1896 let mut u = uri("sip:alice@example.com;transport=tcp;lr");
1897 assert!(
1898 u.remove_param("TRANSPORT"),
1899 "§19.1.4 compares names case-insensitively"
1900 );
1901 assert_eq!(
1902 u.to_bytes(),
1903 Bytes::from_static(b"sip:alice@example.com;lr")
1904 );
1905 assert!(!u.remove_param("transport"), "it was already gone");
1906 assert!(u.remove_param("lr"));
1907 assert_eq!(u.to_bytes(), Bytes::from_static(b"sip:alice@example.com"));
1908 let mut opaque = uri("tel:+15551234");
1911 assert!(!opaque.remove_param("transport"));
1912 assert_eq!(opaque.to_bytes(), Bytes::from_static(b"tel:+15551234"));
1913 }
1914
1915 #[test]
1916 fn mutating_a_parsed_uri_drops_its_verbatim_form() {
1917 let mut u = uri("sip:alice@Example.COM");
1918 u.push_param(Param::flag(Bytes::from_static(b"lr")));
1919 assert_eq!(
1922 u.to_bytes(),
1923 Bytes::from_static(b"sip:alice@Example.COM;lr")
1924 );
1925 }
1926
1927 #[test]
1931 fn duplicate_uri_parameter_names_are_rejected() {
1932 for input in [
1933 "sip:h;a=1;a=2",
1934 "sip:h;a=1;a=1",
1935 "sip:h;lr;lr",
1936 "sip:h;transport=udp;TRANSPORT=tcp",
1938 "sip:h;transport=udp;%74ransport=tcp",
1939 ] {
1940 assert!(
1941 Uri::parse(Bytes::from(input.to_owned())).is_err(),
1942 "{input} should be rejected"
1943 );
1944 }
1945 assert!(Uri::parse(Bytes::from_static(b"sip:a?f=a&f=b")).is_ok());
1947 }
1948
1949 #[test]
1953 fn empty_parameter_segments_are_rejected() {
1954 for input in ["sip:host;;a=1", "sip:host;a=1;", "sip:host;", "sip:host;;"] {
1955 assert_eq!(
1956 Uri::parse(Bytes::from(input.to_owned())).err(),
1957 Some(UriError::EmptyParameterName),
1958 "{input} should be rejected"
1959 );
1960 }
1961 assert_eq!(uri("sip:host;a=1").params().map(Params::len), Some(1));
1963 }
1964}