1use core::{
19 fmt::{self, Write as _},
20 net::{Ipv4Addr, Ipv6Addr},
21 str::FromStr,
22};
23
24use crate::{
25 class,
26 error::{ErrorKind, Result, WriteError, err},
27 scan::{control_kind, find_byte},
28 text::{skip_ows, trim_ows},
29 write::{CountSink, Sink, reserve},
30};
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
34pub enum Host<'a> {
35 Name(&'a [u8]),
38 V4(Ipv4Addr),
40 V6(Ipv6Addr),
42 Future(&'a [u8]),
44}
45
46impl Host<'_> {
47 #[must_use]
60 pub fn eq_ignore_ascii_case(&self, other: &Host<'_>) -> bool {
61 match (self, other) {
62 (Host::Name(a), Host::Name(b)) | (Host::Future(a), Host::Future(b)) => {
63 a.eq_ignore_ascii_case(b)
64 }
65 (Host::V4(a), Host::V4(b)) => a == b,
66 (Host::V6(a), Host::V6(b)) => a == b,
67 _ => false,
68 }
69 }
70
71 #[must_use]
73 pub fn is_empty(&self) -> bool {
74 matches!(self, Host::Name(n) if n.is_empty())
75 }
76
77 pub fn write_to(&self, out: &mut impl Sink) -> core::result::Result<(), WriteError> {
96 let mut count = CountSink::new();
97 self.put(&mut count)?;
98 reserve(out, count.count())?;
99 self.put(out)
100 }
101
102 fn put(&self, out: &mut impl Sink) -> core::result::Result<(), WriteError> {
103 match self {
104 Host::Name(n) => out.put(n)?,
105 Host::V4(a) => write!(SinkWriter(out), "{a}").map_err(|_| WriteError::Full)?,
106 Host::V6(a) => write!(SinkWriter(out), "[{a}]").map_err(|_| WriteError::Full)?,
107 Host::Future(f) => {
108 out.put(b"[")?;
109 out.put(f)?;
110 out.put(b"]")?;
111 }
112 }
113 Ok(())
114 }
115}
116
117pub(crate) struct SinkWriter<'s, S: Sink>(pub(crate) &'s mut S);
119
120impl<S: Sink> fmt::Write for SinkWriter<'_, S> {
121 fn write_str(&mut self, s: &str) -> fmt::Result {
122 self.0.put(s.as_bytes()).map_err(|_| fmt::Error)
123 }
124}
125
126#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
128pub struct HostPort<'a> {
129 pub host: Host<'a>,
131 pub port: Option<u16>,
133 pub empty_port: bool,
136}
137
138impl HostPort<'_> {
139 pub fn write_to(&self, out: &mut impl Sink) -> core::result::Result<(), WriteError> {
158 let mut count = CountSink::new();
159 self.put(&mut count)?;
160 reserve(out, count.count())?;
161 self.put(out)
162 }
163
164 fn put(&self, out: &mut impl Sink) -> core::result::Result<(), WriteError> {
165 self.host.put(out)?;
166 if let Some(port) = self.port {
167 out.put(b":")?;
168 crate::write::write_decimal_u64(u64::from(port), out)?;
169 }
170 Ok(())
171 }
172}
173
174pub fn parse_host_port(value: &[u8]) -> Result<HostPort<'_>> {
208 let lead = skip_ows(value);
209 host_port(trim_ows(value)).map_err(|e| e.shifted(lead))
210}
211
212pub fn parse_uri_host_port(value: &[u8]) -> Result<HostPort<'_>> {
238 host_port(value)
239}
240
241pub fn parse_host(value: &[u8]) -> Result<Host<'_>> {
257 let hp = parse_host_port(value)?;
258 if hp.port.is_some() || hp.empty_port {
259 let at = skip_ows(value) + value.iter().rposition(|&b| b == b':').unwrap_or(0);
260 return err(ErrorKind::InvalidHost, at.min(value.len()));
261 }
262 Ok(hp.host)
263}
264
265pub(crate) fn host_port(v: &[u8]) -> Result<HostPort<'_>> {
266 host_port_inner(v).map_err(|e| userinfo_first(v, e))
267}
268
269#[cold]
272fn userinfo_first(v: &[u8], e: crate::ParseError) -> crate::ParseError {
273 match find_byte(b'@', v) {
274 Some(at) => crate::ParseError::new(ErrorKind::InvalidHost, at),
275 None => e,
276 }
277}
278
279fn host_port_inner(v: &[u8]) -> Result<HostPort<'_>> {
280 let (host, rest, rest_at) = if v.first() == Some(&b'[') {
281 let Some(close) = find_byte(b']', v) else {
282 return err(ErrorKind::InvalidAddress, v.len());
283 };
284 let inner = v.get(1..close).unwrap_or(&[]);
285 (
286 ip_literal(inner)?,
287 v.get(close + 1..).unwrap_or(&[]),
288 close + 1,
289 )
290 } else {
291 let (host, colon) = reg_name_or_ipv4(v)?;
292 let rest = v.get(colon..).unwrap_or(&[]);
293 if colon == 0 && !rest.is_empty() {
294 return err(ErrorKind::InvalidHost, 0);
295 }
296 (host, rest, colon)
297 };
298 let (port, empty_port) = match rest.split_first() {
299 None => (None, false),
300 Some((b':', digits)) => port(digits).map_err(|e| e.shifted(rest_at + 1))?,
301 Some(_) => return err(ErrorKind::InvalidHost, rest_at),
302 };
303 Ok(HostPort {
304 host,
305 port,
306 empty_port,
307 })
308}
309
310fn port(digits: &[u8]) -> Result<(Option<u16>, bool)> {
311 if digits.is_empty() {
312 return Ok((None, true));
313 }
314 let mut n: u32 = 0;
315 for (i, &d) in digits.iter().enumerate() {
316 if !d.is_ascii_digit() {
317 let kind = if d == b':' {
319 ErrorKind::InvalidHost
320 } else {
321 ErrorKind::InvalidPort
322 };
323 return err(kind, i);
324 }
325 n = n * 10 + u32::from(d - b'0');
326 if n > 65_535 {
327 return err(ErrorKind::InvalidPort, i);
328 }
329 }
330 Ok((u16::try_from(n).ok(), false))
332}
333
334fn ip_literal(inner: &[u8]) -> Result<Host<'_>> {
335 match inner.first() {
337 Some(b'v' | b'V') => {
338 let rest = inner.get(1..).unwrap_or(&[]);
340 let hex = rest.iter().take_while(|&&b| class::is_hexdig(b)).count();
341 let after = rest.get(hex..).unwrap_or(&[]);
342 let tail_ok = match after.split_first() {
343 Some((b'.', tail)) => {
344 !tail.is_empty()
345 && tail.iter().all(|&b| {
346 class::is_unreserved(b) || class::is_sub_delim(b) || b == b':'
347 })
348 }
349 _ => false,
350 };
351 if hex == 0 || !tail_ok {
352 return err(ErrorKind::InvalidAddress, 1);
353 }
354 Ok(Host::Future(inner))
355 }
356 _ => {
357 let text = core::str::from_utf8(inner)
358 .map_err(|_| crate::ParseError::new(ErrorKind::InvalidAddress, 1))?;
359 match Ipv6Addr::from_str(text) {
360 Ok(a) => Ok(Host::V6(a)),
361 Err(_) => err(ErrorKind::InvalidAddress, 1),
362 }
363 }
364 }
365}
366
367fn is_numeric_label(label: &[u8]) -> bool {
370 match label {
371 [] => false,
372 [b'0', b'x' | b'X', hex @ ..] => hex.iter().all(|&b| class::is_hexdig(b)),
373 _ => label.iter().all(u8::is_ascii_digit),
374 }
375}
376
377const REG_NAME: class::Class = class::Class::UNRESERVED.union(class::Class::SUB_DELIM);
379
380fn reg_name_or_ipv4(v: &[u8]) -> Result<(Host<'_>, usize)> {
384 let mut i = 0usize;
385 let mut digits_dots = true;
386 let mut label_start = 0usize;
387 let mut prev_label_start = 0usize;
388 while let Some(&b) = v.get(i) {
389 if REG_NAME.contains(b) {
390 if b == b'.' {
391 prev_label_start = label_start;
392 label_start = i + 1;
393 } else if !b.is_ascii_digit() {
394 digits_dots = false;
395 }
396 i += 1;
397 } else if b == b':' {
398 break;
399 } else if b == b'%' {
400 match (v.get(i + 1), v.get(i + 2)) {
401 (Some(&h), Some(&l)) if class::is_hexdig(h) && class::is_hexdig(l) => i += 3,
402 _ => return err(ErrorKind::InvalidPercentEncoding, i),
403 }
404 digits_dots = false;
405 } else if crate::scan::is_control(b) {
406 return err(control_kind(b), i);
407 } else {
408 return err(ErrorKind::InvalidHost, i);
409 }
410 }
411 let name = v.get(..i).unwrap_or(&[]);
412 let last = if name.last() == Some(&b'.') {
414 name.get(prev_label_start..i - 1).unwrap_or(&[])
415 } else {
416 name.get(label_start..).unwrap_or(&[])
417 };
418 if (digits_dots && !name.is_empty()) || is_numeric_label(last) {
419 return match ipv4(name) {
420 Some(a) => Ok((Host::V4(a), i)),
421 None => err(ErrorKind::InvalidAddress, 0),
422 };
423 }
424 Ok((Host::Name(name), i))
425}
426
427fn ipv4(s: &[u8]) -> Option<Ipv4Addr> {
429 let mut octets = [0u8; 4];
430 let mut parts = s.split(|&b| b == b'.');
431 for slot in &mut octets {
432 let part = parts.next()?;
433 if part.is_empty() || part.len() > 3 || (part.len() > 1 && part.first() == Some(&b'0')) {
434 return None;
435 }
436 let mut n: u16 = 0;
437 for &d in part {
438 if !d.is_ascii_digit() {
439 return None;
440 }
441 n = n * 10 + u16::from(d - b'0');
442 }
443 *slot = u8::try_from(n).ok()?;
444 }
445 if parts.next().is_some() {
446 return None;
447 }
448 Some(Ipv4Addr::from(octets))
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454
455 fn hp(v: &[u8]) -> Result<HostPort<'_>> {
456 parse_host_port(v)
457 }
458
459 #[test]
460 fn test_host_edge_cases() {
461 use ErrorKind::*;
462 assert!(hp(b"").unwrap().host.is_empty());
463 assert_eq!(hp(b"a").unwrap().host, Host::Name(b"a"));
464 let e = hp(b"a:").unwrap();
465 assert_eq!((e.port, e.empty_port), (None, true));
466 assert_eq!(hp(b"a:0").unwrap().port, Some(0));
467 assert_eq!(hp(b"a:65535").unwrap().port, Some(65535));
468 assert_eq!(hp(b"a:65536").unwrap_err().kind(), InvalidPort);
469 assert_eq!(hp(b"a:-1").unwrap_err().kind(), InvalidPort);
470 assert_eq!(hp(b"a:8x").unwrap_err().kind(), InvalidPort);
471 assert_eq!(hp(b"[::1]").unwrap().host, Host::V6(Ipv6Addr::LOCALHOST));
472 assert_eq!(hp(b"[::1]:80").unwrap().port, Some(80));
473 assert_eq!(hp(b"[::1").unwrap_err().kind(), InvalidAddress);
474 assert_eq!(hp(b"[zz]").unwrap_err().kind(), InvalidAddress);
475 assert_eq!(hp(b"[]").unwrap_err().kind(), InvalidAddress);
476 assert_eq!(hp(b"::1").unwrap_err().kind(), InvalidHost);
477 assert_eq!(hp(b"a:1:2").unwrap_err().kind(), InvalidHost);
478 assert_eq!(hp(b"[v1.x]").unwrap().host, Host::Future(b"v1.x"));
479 assert_eq!(hp(b"[v.x]").unwrap_err().kind(), InvalidAddress);
480 assert_eq!(hp(b"[v1.]").unwrap_err().kind(), InvalidAddress);
481 assert_eq!(
482 hp(b"1.2.3.4").unwrap().host,
483 Host::V4(Ipv4Addr::new(1, 2, 3, 4))
484 );
485 assert_eq!(hp(b"[::1]x").unwrap_err().kind(), InvalidHost);
486 assert_eq!(hp(b"%41.com").unwrap().host, Host::Name(b"%41.com"));
487 assert_eq!(hp(b"%4.com").unwrap_err().kind(), InvalidPercentEncoding);
488 assert_eq!(hp(b"a b").unwrap_err().kind(), InvalidHost);
489 assert_eq!(hp(b"a/b").unwrap_err().kind(), InvalidHost);
490 assert_eq!(
491 hp(b"example.com.").unwrap().host,
492 Host::Name(b"example.com.")
493 );
494 assert_eq!(hp(b":80").unwrap_err().kind(), InvalidHost);
495 assert_eq!(hp(b"a\rb").unwrap_err().kind(), BareCr);
496 }
497
498 #[test]
499 fn test_host_partial_ipv4_refused() {
500 for v in [
501 &b"1.2.3"[..],
502 b"01.2.3.4",
503 b"256.0.0.1",
504 b"127.1",
505 b"0x7f.0.0.1",
506 b"1.2.3.4.",
507 b"example.123",
508 b"a.0x10",
509 b"1..2.3",
510 ] {
511 assert_eq!(
512 hp(v).unwrap_err().kind(),
513 ErrorKind::InvalidAddress,
514 "{}",
515 std::string::String::from_utf8_lossy(v)
516 );
517 }
518 assert_eq!(hp(b"123.example").unwrap().host, Host::Name(b"123.example"));
519 assert_eq!(
520 hp(b"0.0.0.0").unwrap().host,
521 Host::V4(Ipv4Addr::UNSPECIFIED)
522 );
523 }
524
525 #[test]
526 fn test_host_userinfo_refused() {
527 let e = hp(b"a.com:80@b.com").unwrap_err();
528 assert_eq!((e.kind(), e.offset()), (ErrorKind::InvalidHost, 8));
529 assert_eq!(
530 hp(b"user@b.com").unwrap_err().kind(),
531 ErrorKind::InvalidHost
532 );
533 assert_eq!(hp(b"[::1]@x").unwrap_err().kind(), ErrorKind::InvalidHost);
534 }
535
536 #[test]
537 fn test_ipv6_text_forms() {
538 for v in [
539 &b"[::]"[..],
540 b"[::1]",
541 b"[2001:db8::1]",
542 b"[2001:DB8:0:0:8:800:200C:417A]",
543 b"[::ffff:192.0.2.1]",
544 b"[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]",
545 b"[1::]",
546 ] {
547 assert!(
548 matches!(hp(v).unwrap().host, Host::V6(_)),
549 "{}",
550 std::string::String::from_utf8_lossy(v)
551 );
552 }
553 for v in [
554 &b"[1:2:3:4:5:6:7:8:9]"[..],
555 b"[12345::]",
556 b"[::1%25eth0]",
557 b"[:::]",
558 b"[1::2::3]",
559 ] {
560 assert_eq!(hp(v).unwrap_err().kind(), ErrorKind::InvalidAddress);
561 }
562 }
563
564 #[test]
565 fn test_host_compare_and_write() {
566 assert!(Host::V6(Ipv6Addr::LOCALHOST).eq_ignore_ascii_case(&hp(b"[0:0::1]").unwrap().host));
567 assert!(!Host::Name(b"a").eq_ignore_ascii_case(&Host::V4(Ipv4Addr::LOCALHOST)));
568 let mut buf = [0u8; 64];
569 let mut out = crate::SliceSink::new(&mut buf);
570 hp(b"1.2.3.4:5").unwrap().write_to(&mut out).unwrap();
571 assert_eq!(out.written(), b"1.2.3.4:5");
572 let mut small = [0u8; 3];
573 let mut out = crate::SliceSink::new(&mut small);
574 assert_eq!(
575 hp(b"[::1]").unwrap().write_to(&mut out),
576 Err(WriteError::Full)
577 );
578 assert!(out.is_empty());
579 assert_eq!(
580 parse_host(b"a:").unwrap_err().kind(),
581 ErrorKind::InvalidHost
582 );
583 }
584}