iri_string/parser/validate/
authority.rs1use core::mem;
4
5use crate::parser::char;
6use crate::parser::str::{
7 find_split_hole, get_wrapped_inner, rfind_split_hole, satisfy_chars_with_pct_encoded,
8 strip_ascii_char_prefix,
9};
10use crate::spec::Spec;
11use crate::validate::{Error, ErrorKind};
12
13pub(crate) fn validate_userinfo<S: Spec>(i: &str) -> Result<(), Error> {
15 let is_valid = satisfy_chars_with_pct_encoded(
16 i,
17 char::is_ascii_userinfo_ipvfutureaddr,
18 char::is_nonascii_userinfo::<S>,
19 );
20 if is_valid {
21 Ok(())
22 } else {
23 Err(Error::with_kind(ErrorKind::InvalidUserInfo))
24 }
25}
26
27#[must_use]
31fn is_dec_octet(i: &str) -> bool {
32 matches!(
33 i.as_bytes(),
34 [b'0'..=b'9']
35 | [b'1'..=b'9', b'0'..=b'9']
36 | [b'1', b'0'..=b'9', b'0'..=b'9']
37 | [b'2', b'0'..=b'4', b'0'..=b'9']
38 | [b'2', b'5', b'0'..=b'5']
39 )
40}
41
42fn validate_ipv4address(i: &str) -> Result<(), Error> {
44 fn validate_ipv4address_impl(i: &str) -> Result<(), ()> {
46 let (first, rest) = find_split_hole(i, b'.').ok_or(())?;
47 if !is_dec_octet(first) {
48 return Err(());
49 }
50 let (second, rest) = find_split_hole(rest, b'.').ok_or(())?;
51 if !is_dec_octet(second) {
52 return Err(());
53 }
54 let (third, fourth) = find_split_hole(rest, b'.').ok_or(())?;
55 if is_dec_octet(third) && is_dec_octet(fourth) {
56 Ok(())
57 } else {
58 Err(())
59 }
60 }
61
62 validate_ipv4address_impl(i).map_err(|_| Error::with_kind(ErrorKind::InvalidHost))
63}
64
65#[derive(Clone, Copy)]
67enum V6AddrPart {
68 H16Omit,
70 H16Cont,
72 H16End,
74 V4,
76 Omit,
78}
79
80fn split_v6_addr_part(i: &str) -> Result<(&str, V6AddrPart), Error> {
82 debug_assert!(!i.is_empty());
83 match find_split_hole(i, b':') {
84 Some((prefix, rest)) => {
85 if prefix.len() >= 5 {
86 return Err(Error::with_kind(ErrorKind::InvalidHost));
87 }
88
89 if prefix.is_empty() {
90 return match strip_ascii_char_prefix(rest, b':') {
91 Some(rest) => Ok((rest, V6AddrPart::Omit)),
92 None => Err(Error::with_kind(ErrorKind::InvalidHost)),
93 };
94 }
95
96 debug_assert!((1..=4).contains(&prefix.len()));
98 if !prefix.bytes().all(|b| b.is_ascii_hexdigit()) {
99 return Err(Error::with_kind(ErrorKind::InvalidHost));
100 }
101 match strip_ascii_char_prefix(rest, b':') {
102 Some(rest) => Ok((rest, V6AddrPart::H16Omit)),
103 None => Ok((rest, V6AddrPart::H16Cont)),
104 }
105 }
106 None => {
107 if i.len() >= 5 {
108 validate_ipv4address(i)?;
110 return Ok(("", V6AddrPart::V4));
111 }
112 if i.bytes().all(|b| b.is_ascii_hexdigit()) {
113 Ok(("", V6AddrPart::H16End))
114 } else {
115 Err(Error::with_kind(ErrorKind::InvalidHost))
116 }
117 }
118 }
119}
120
121fn validate_ipv6address(mut i: &str) -> Result<(), Error> {
123 let mut h16_count = 0;
124 let mut is_omitted = false;
125 while !i.is_empty() {
126 let (rest, part) = split_v6_addr_part(i)?;
127 match part {
128 V6AddrPart::H16Omit => {
129 h16_count += 1;
130 if mem::replace(&mut is_omitted, true) {
131 return Err(Error::with_kind(ErrorKind::InvalidHost));
133 }
134 }
135 V6AddrPart::H16Cont => {
136 h16_count += 1;
137 if rest.is_empty() {
138 return Err(Error::with_kind(ErrorKind::InvalidHost));
140 }
141 }
142 V6AddrPart::H16End => {
143 h16_count += 1;
144 break;
145 }
146 V6AddrPart::V4 => {
147 debug_assert!(rest.is_empty());
148 h16_count += 2;
149 break;
150 }
151 V6AddrPart::Omit => {
152 if mem::replace(&mut is_omitted, true) {
153 return Err(Error::with_kind(ErrorKind::InvalidHost));
155 }
156 }
157 }
158 if h16_count > 8 {
159 return Err(Error::with_kind(ErrorKind::InvalidHost));
160 }
161 i = rest;
162 }
163 let is_valid = if is_omitted {
164 h16_count < 8
165 } else {
166 h16_count == 8
167 };
168 if is_valid {
169 Ok(())
170 } else {
171 Err(Error::with_kind(ErrorKind::InvalidHost))
172 }
173}
174
175pub(crate) fn validate_authority<S: Spec>(i: &str) -> Result<(), Error> {
177 let (i, _userinfo) = match find_split_hole(i, b'@') {
179 Some((maybe_userinfo, i)) => {
180 validate_userinfo::<S>(maybe_userinfo)?;
181 (i, Some(maybe_userinfo))
182 }
183 None => (i, None),
184 };
185 let (maybe_host, _port) = match rfind_split_hole(i, b':') {
188 Some((maybe_host, maybe_port)) => {
189 if maybe_port.bytes().all(|b| b.is_ascii_digit()) {
190 (maybe_host, Some(maybe_port))
191 } else {
192 (i, None)
193 }
194 }
195 None => (i, None),
196 };
197 validate_host::<S>(maybe_host)
199}
200
201pub(crate) fn validate_host<S: Spec>(i: &str) -> Result<(), Error> {
203 match get_wrapped_inner(i, b'[', b']') {
204 Some(maybe_addr) => {
205 if let Some(maybe_addr_rest) = strip_ascii_char_prefix(maybe_addr, b'v')
208 .or_else(|| strip_ascii_char_prefix(maybe_addr, b'V'))
209 {
210 let (maybe_ver, maybe_addr) = find_split_hole(maybe_addr_rest, b'.')
212 .ok_or(Error::with_kind(ErrorKind::InvalidHost))?;
213 if maybe_ver.is_empty() || !maybe_ver.bytes().all(|b| b.is_ascii_hexdigit()) {
215 return Err(Error::with_kind(ErrorKind::InvalidHost));
216 }
217 if !maybe_addr.is_empty()
219 && maybe_addr.is_ascii()
220 && maybe_addr
221 .bytes()
222 .all(char::is_ascii_userinfo_ipvfutureaddr)
223 {
224 Ok(())
225 } else {
226 Err(Error::with_kind(ErrorKind::InvalidHost))
227 }
228 } else {
229 validate_ipv6address(maybe_addr)
231 }
232 }
233 None => {
234 let is_valid = satisfy_chars_with_pct_encoded(
237 i,
238 char::is_ascii_regname,
239 char::is_nonascii_regname::<S>,
240 );
241 if is_valid {
242 Ok(())
243 } else {
244 Err(Error::with_kind(ErrorKind::InvalidHost))
245 }
246 }
247 }
248}
249
250#[cfg(test)]
251#[cfg(feature = "alloc")]
252mod tests {
253 use super::*;
254
255 use alloc::format;
256
257 macro_rules! assert_validate {
258 ($parser:expr, $($input:expr),* $(,)?) => {{
259 $({
260 let input = $input;
261 let input: &str = input.as_ref();
262 assert!($parser(input).is_ok(), "input={:?}", input);
263 })*
264 }};
265 }
266
267 #[test]
268 fn test_ipv6address() {
269 use core::cmp::Ordering;
270
271 assert_validate!(validate_ipv6address, "a:bB:cCc:dDdD:e:F:a:B");
272 assert_validate!(validate_ipv6address, "1:1:1:1:1:1:1:1");
273 assert_validate!(validate_ipv6address, "1:1:1:1:1:1:1.1.1.1");
274 assert_validate!(validate_ipv6address, "2001:db8::7");
275
276 let make_sub = |n: usize| {
278 let mut s = "1:".repeat(n);
279 s.pop();
280 s
281 };
282 for len_pref in 0..=7 {
283 let prefix = make_sub(len_pref);
284 for len_suf in 1..=(7 - len_pref) {
285 assert_validate!(
286 validate_ipv6address,
287 &format!("{}::{}", prefix, make_sub(len_suf))
288 );
289 match len_suf.cmp(&2) {
290 Ordering::Greater => assert_validate!(
291 validate_ipv6address,
292 &format!("{}::{}:1.1.1.1", prefix, make_sub(len_suf - 2))
293 ),
294 Ordering::Equal => {
295 assert_validate!(validate_ipv6address, &format!("{}::1.1.1.1", prefix))
296 }
297 Ordering::Less => {}
298 }
299 }
300 }
301 }
302}