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)]
46pub enum Host<'a> {
47 Name(&'a [u8]),
50 V4(Ipv4Addr),
52 V6(Ipv6Addr),
54 Future(&'a [u8]),
56}
57
58impl Host<'_> {
59 #[must_use]
72 pub fn eq_ignore_ascii_case(&self, other: &Host<'_>) -> bool {
73 match (self, other) {
74 (Host::Name(a), Host::Name(b)) | (Host::Future(a), Host::Future(b)) => {
75 a.eq_ignore_ascii_case(b)
76 }
77 (Host::V4(a), Host::V4(b)) => a == b,
78 (Host::V6(a), Host::V6(b)) => a == b,
79 _ => false,
80 }
81 }
82
83 #[must_use]
95 pub fn is_empty(&self) -> bool {
96 matches!(self, Host::Name(n) if n.is_empty())
97 }
98
99 pub fn write_to(&self, out: &mut impl Sink) -> core::result::Result<(), WriteError> {
118 let mut count = CountSink::new();
119 self.put(&mut count)?;
120 reserve(out, count.count())?;
121 self.put(out)
122 }
123
124 fn put(&self, out: &mut impl Sink) -> core::result::Result<(), WriteError> {
125 match self {
126 Host::Name(n) => {
127 check_reg_name(n)?;
128 out.put(n)?;
129 }
130 Host::V4(a) => write!(SinkWriter(out), "{a}").map_err(|_| WriteError::Full)?,
131 Host::V6(a) => write!(SinkWriter(out), "[{a}]").map_err(|_| WriteError::Full)?,
132 Host::Future(f) => {
133 if ip_literal(f).is_err() {
134 return Err(WriteError::forbidden(0));
135 }
136 out.put(b"[")?;
137 out.put(f)?;
138 out.put(b"]")?;
139 }
140 }
141 Ok(())
142 }
143}
144
145fn check_reg_name(n: &[u8]) -> core::result::Result<(), WriteError> {
148 let mut i = 0usize;
149 while let Some(&b) = n.get(i) {
150 match (b, n.get(i + 1), n.get(i + 2)) {
151 (b'%', Some(&h), Some(&l)) if class::is_hexdig(h) && class::is_hexdig(l) => i += 3,
152 _ if REG_NAME.contains(b) => i += 1,
153 _ => return Err(WriteError::forbidden(i)),
154 }
155 }
156 Ok(())
157}
158
159pub(crate) struct SinkWriter<'s, S: Sink>(pub(crate) &'s mut S);
161
162impl<S: Sink> fmt::Write for SinkWriter<'_, S> {
163 fn write_str(&mut self, s: &str) -> fmt::Result {
164 self.0.put(s.as_bytes()).map_err(|_| fmt::Error)
165 }
166}
167
168#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
181pub struct HostPort<'a> {
182 pub host: Host<'a>,
184 pub port: Option<u16>,
186 pub empty_port: bool,
189}
190
191impl HostPort<'_> {
192 pub fn write_to(&self, out: &mut impl Sink) -> core::result::Result<(), WriteError> {
211 let mut count = CountSink::new();
212 self.put(&mut count)?;
213 reserve(out, count.count())?;
214 self.put(out)
215 }
216
217 fn put(&self, out: &mut impl Sink) -> core::result::Result<(), WriteError> {
218 self.host.put(out)?;
219 if let Some(port) = self.port {
220 out.put(b":")?;
221 crate::write::write_decimal_u64(u64::from(port), out)?;
222 }
223 Ok(())
224 }
225}
226
227pub fn parse_host_port(value: &[u8]) -> Result<HostPort<'_>> {
261 let lead = skip_ows(value);
262 host_port(trim_ows(value)).map_err(|e| e.shifted(lead))
263}
264
265pub fn parse_uri_host_port(value: &[u8]) -> Result<HostPort<'_>> {
291 host_port(value)
292}
293
294pub fn parse_host(value: &[u8]) -> Result<Host<'_>> {
310 let hp = parse_host_port(value)?;
311 if hp.port.is_some() || hp.empty_port {
312 let at = value.iter().rposition(|&b| b == b':').unwrap_or(0);
313 return err(ErrorKind::InvalidHost, at);
314 }
315 Ok(hp.host)
316}
317
318pub(crate) fn host_port(v: &[u8]) -> Result<HostPort<'_>> {
319 host_port_inner(v).map_err(|e| userinfo_first(v, e))
320}
321
322#[cold]
325fn userinfo_first(v: &[u8], e: crate::ParseError) -> crate::ParseError {
326 match find_byte(b'@', v) {
327 Some(at) => crate::ParseError::new(ErrorKind::InvalidHost, at),
328 None => e,
329 }
330}
331
332fn host_port_inner(v: &[u8]) -> Result<HostPort<'_>> {
333 let (host, rest, rest_at) = if v.first() == Some(&b'[') {
334 let Some(close) = find_byte(b']', v) else {
335 return err(ErrorKind::InvalidAddress, v.len());
336 };
337 let inner = v.get(1..close).unwrap_or(&[]);
338 (
339 ip_literal(inner)?,
340 v.get(close + 1..).unwrap_or(&[]),
341 close + 1,
342 )
343 } else {
344 let (host, colon) = reg_name_or_ipv4(v)?;
345 let rest = v.get(colon..).unwrap_or(&[]);
346 if colon == 0 && !rest.is_empty() {
347 return err(ErrorKind::InvalidHost, 0);
348 }
349 (host, rest, colon)
350 };
351 let (port, empty_port) = match rest.split_first() {
352 None => (None, false),
353 Some((b':', digits)) => port(digits).map_err(|e| e.shifted(rest_at + 1))?,
354 Some(_) => return err(ErrorKind::InvalidHost, rest_at),
355 };
356 Ok(HostPort {
357 host,
358 port,
359 empty_port,
360 })
361}
362
363fn port(digits: &[u8]) -> Result<(Option<u16>, bool)> {
364 if digits.is_empty() {
365 return Ok((None, true));
366 }
367 let mut n: u32 = 0;
368 for (i, &d) in digits.iter().enumerate() {
369 if !d.is_ascii_digit() {
370 let kind = if d == b':' {
372 ErrorKind::InvalidHost
373 } else {
374 ErrorKind::InvalidPort
375 };
376 return err(kind, i);
377 }
378 n = n * 10 + u32::from(d - b'0');
379 if n > 65_535 {
380 return err(ErrorKind::InvalidPort, i);
381 }
382 }
383 Ok((u16::try_from(n).ok(), false))
385}
386
387fn ip_literal(inner: &[u8]) -> Result<Host<'_>> {
388 match inner.first() {
390 Some(b'v' | b'V') => {
391 let rest = inner.get(1..).unwrap_or(&[]);
393 let hex = rest.iter().take_while(|&&b| class::is_hexdig(b)).count();
394 let after = rest.get(hex..).unwrap_or(&[]);
395 let tail_ok = match after.split_first() {
396 Some((b'.', tail)) => {
397 !tail.is_empty()
398 && tail.iter().all(|&b| {
399 class::is_unreserved(b) || class::is_sub_delim(b) || b == b':'
400 })
401 }
402 _ => false,
403 };
404 if hex == 0 || !tail_ok {
405 return err(ErrorKind::InvalidAddress, 1);
406 }
407 Ok(Host::Future(inner))
408 }
409 _ => {
410 let text = core::str::from_utf8(inner)
411 .map_err(|_| crate::ParseError::new(ErrorKind::InvalidAddress, 1))?;
412 match Ipv6Addr::from_str(text) {
413 Ok(a) => Ok(Host::V6(a)),
414 Err(_) => err(ErrorKind::InvalidAddress, 1),
415 }
416 }
417 }
418}
419
420fn is_numeric_label(label: &[u8]) -> bool {
423 match label {
424 [] => false,
425 [b'0', b'x' | b'X', hex @ ..] => hex.iter().all(|&b| class::is_hexdig(b)),
426 _ => label.iter().all(u8::is_ascii_digit),
427 }
428}
429
430#[derive(Clone, Copy)]
432struct NumericLabel {
433 len: usize,
434 decimal: bool,
435 hex: bool,
436}
437
438impl NumericLabel {
439 const EMPTY: Self = Self {
440 len: 0,
441 decimal: true,
442 hex: true,
443 };
444
445 fn push(&mut self, b: u8) {
446 self.decimal &= b.is_ascii_digit();
447 self.hex &= match self.len {
448 0 => b == b'0',
449 1 => matches!(b, b'x' | b'X'),
450 _ => class::is_hexdig(b),
451 };
452 self.len += 1;
453 }
454
455 const fn is_number(&self) -> bool {
456 self.decimal && self.len > 0 || self.hex && self.len >= 2
457 }
458}
459
460fn decoded_is_numeric(name: &[u8]) -> bool {
464 let (mut label, mut previous) = (NumericLabel::EMPTY, NumericLabel::EMPTY);
465 let (mut digits_dots, mut i) = (true, 0usize);
466 while let Some(&b) = name.get(i) {
467 let byte = match (b, name.get(i + 1), name.get(i + 2)) {
468 (b'%', Some(&h), Some(&l)) => {
469 i += 2;
470 match (class::hex_value(h), class::hex_value(l)) {
471 (Some(h), Some(l)) => (h << 4) | l,
472 _ => b,
473 }
474 }
475 _ => b,
476 };
477 i += 1;
478 digits_dots &= byte.is_ascii_digit() || byte == b'.';
479 if byte == b'.' {
480 previous = label;
481 label = NumericLabel::EMPTY;
482 } else {
483 label.push(byte);
484 }
485 }
486 digits_dots
487 || if label.len == 0 {
488 previous.is_number()
489 } else {
490 label.is_number()
491 }
492}
493
494const REG_NAME: class::Class = class::Class::UNRESERVED.union(class::Class::SUB_DELIM);
496
497fn reg_name_or_ipv4(v: &[u8]) -> Result<(Host<'_>, usize)> {
501 let mut i = 0usize;
502 let mut digits_dots = true;
503 let mut label_start = 0usize;
504 let mut prev_label_start = 0usize;
505 let mut escaped = false;
506 while let Some(&b) = v.get(i) {
507 if REG_NAME.contains(b) {
508 if b == b'.' {
509 prev_label_start = label_start;
510 label_start = i + 1;
511 } else if !b.is_ascii_digit() {
512 digits_dots = false;
513 }
514 i += 1;
515 } else if b == b':' {
516 break;
517 } else if b == b'%' {
518 match (v.get(i + 1), v.get(i + 2)) {
519 (Some(&h), Some(&l)) if class::is_hexdig(h) && class::is_hexdig(l) => i += 3,
520 _ => return err(ErrorKind::InvalidPercentEncoding, i),
521 }
522 digits_dots = false;
523 escaped = true;
524 } else if crate::scan::is_control(b) {
525 return err(control_kind(b), i);
526 } else {
527 return err(ErrorKind::InvalidHost, i);
528 }
529 }
530 let name = v.get(..i).unwrap_or(&[]);
531 if escaped && decoded_is_numeric(name) {
534 return err(ErrorKind::InvalidAddress, 0);
535 }
536 let last = if name.last() == Some(&b'.') {
538 name.get(prev_label_start..i - 1).unwrap_or(&[])
539 } else {
540 name.get(label_start..).unwrap_or(&[])
541 };
542 if (digits_dots && !name.is_empty()) || is_numeric_label(last) {
543 return match ipv4(name) {
544 Some(a) => Ok((Host::V4(a), i)),
545 None => err(ErrorKind::InvalidAddress, 0),
546 };
547 }
548 Ok((Host::Name(name), i))
549}
550
551fn ipv4(s: &[u8]) -> Option<Ipv4Addr> {
553 let mut octets = [0u8; 4];
554 let mut parts = s.split(|&b| b == b'.');
555 for slot in &mut octets {
556 let part = parts.next()?;
557 if part.is_empty() || part.len() > 3 || (part.len() > 1 && part.first() == Some(&b'0')) {
558 return None;
559 }
560 let mut n: u16 = 0;
561 for &d in part {
562 if !d.is_ascii_digit() {
563 return None;
564 }
565 n = n * 10 + u16::from(d - b'0');
566 }
567 *slot = u8::try_from(n).ok()?;
568 }
569 if parts.next().is_some() {
570 return None;
571 }
572 Some(Ipv4Addr::from(octets))
573}
574
575#[cfg(test)]
576mod tests {
577 use super::*;
578
579 fn hp(v: &[u8]) -> Result<HostPort<'_>> {
580 parse_host_port(v)
581 }
582
583 #[test]
584 fn test_host_edge_cases() {
585 use ErrorKind::*;
586 assert!(hp(b"").unwrap().host.is_empty());
587 assert_eq!(hp(b"a").unwrap().host, Host::Name(b"a"));
588 let e = hp(b"a:").unwrap();
589 assert_eq!((e.port, e.empty_port), (None, true));
590 assert_eq!(hp(b"a:0").unwrap().port, Some(0));
591 assert_eq!(hp(b"a:65535").unwrap().port, Some(65535));
592 assert_eq!(hp(b"a:65536").unwrap_err().kind(), InvalidPort);
593 assert_eq!(hp(b"a:-1").unwrap_err().kind(), InvalidPort);
594 assert_eq!(hp(b"a:8x").unwrap_err().kind(), InvalidPort);
595 assert_eq!(hp(b"[::1]").unwrap().host, Host::V6(Ipv6Addr::LOCALHOST));
596 assert_eq!(hp(b"[::1]:80").unwrap().port, Some(80));
597 assert_eq!(hp(b"[::1").unwrap_err().kind(), InvalidAddress);
598 assert_eq!(hp(b"[zz]").unwrap_err().kind(), InvalidAddress);
599 assert_eq!(hp(b"[]").unwrap_err().kind(), InvalidAddress);
600 assert_eq!(hp(b"::1").unwrap_err().kind(), InvalidHost);
601 assert_eq!(hp(b"a:1:2").unwrap_err().kind(), InvalidHost);
602 assert_eq!(hp(b"[v1.x]").unwrap().host, Host::Future(b"v1.x"));
603 assert_eq!(hp(b"[v.x]").unwrap_err().kind(), InvalidAddress);
604 assert_eq!(hp(b"[v1.]").unwrap_err().kind(), InvalidAddress);
605 assert_eq!(
606 hp(b"1.2.3.4").unwrap().host,
607 Host::V4(Ipv4Addr::new(1, 2, 3, 4))
608 );
609 assert_eq!(hp(b"[::1]x").unwrap_err().kind(), InvalidHost);
610 assert_eq!(hp(b"%41.com").unwrap().host, Host::Name(b"%41.com"));
611 assert_eq!(hp(b"%4.com").unwrap_err().kind(), InvalidPercentEncoding);
612 assert_eq!(hp(b"a b").unwrap_err().kind(), InvalidHost);
613 assert_eq!(hp(b"a/b").unwrap_err().kind(), InvalidHost);
614 assert_eq!(
615 hp(b"example.com.").unwrap().host,
616 Host::Name(b"example.com.")
617 );
618 assert_eq!(hp(b":80").unwrap_err().kind(), InvalidHost);
619 assert_eq!(hp(b"a\rb").unwrap_err().kind(), BareCr);
620 }
621
622 #[test]
623 fn test_host_partial_ipv4_refused() {
624 for v in [
625 &b"1.2.3"[..],
626 b"01.2.3.4",
627 b"256.0.0.1",
628 b"127.1",
629 b"0x7f.0.0.1",
630 b"1.2.3.4.",
631 b"example.123",
632 b"a.0x10",
633 b"1..2.3",
634 ] {
635 assert_eq!(
636 hp(v).unwrap_err().kind(),
637 ErrorKind::InvalidAddress,
638 "{}",
639 std::string::String::from_utf8_lossy(v)
640 );
641 }
642 assert_eq!(hp(b"123.example").unwrap().host, Host::Name(b"123.example"));
643 assert_eq!(
644 hp(b"0.0.0.0").unwrap().host,
645 Host::V4(Ipv4Addr::UNSPECIFIED)
646 );
647 }
648
649 #[test]
650 fn test_host_userinfo_refused() {
651 let e = hp(b"a.com:80@b.com").unwrap_err();
652 assert_eq!((e.kind(), e.offset()), (ErrorKind::InvalidHost, 8));
653 assert_eq!(
654 hp(b"user@b.com").unwrap_err().kind(),
655 ErrorKind::InvalidHost
656 );
657 assert_eq!(hp(b"[::1]@x").unwrap_err().kind(), ErrorKind::InvalidHost);
658 }
659
660 #[test]
661 fn test_ipv6_text_forms() {
662 for v in [
663 &b"[::]"[..],
664 b"[::1]",
665 b"[2001:db8::1]",
666 b"[2001:DB8:0:0:8:800:200C:417A]",
667 b"[::ffff:192.0.2.1]",
668 b"[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]",
669 b"[1::]",
670 ] {
671 assert!(
672 matches!(hp(v).unwrap().host, Host::V6(_)),
673 "{}",
674 std::string::String::from_utf8_lossy(v)
675 );
676 }
677 for v in [
678 &b"[1:2:3:4:5:6:7:8:9]"[..],
679 b"[12345::]",
680 b"[::1%25eth0]",
681 b"[:::]",
682 b"[1::2::3]",
683 ] {
684 assert_eq!(hp(v).unwrap_err().kind(), ErrorKind::InvalidAddress);
685 }
686 }
687
688 #[test]
689 fn test_host_compare_and_write() {
690 assert!(Host::V6(Ipv6Addr::LOCALHOST).eq_ignore_ascii_case(&hp(b"[0:0::1]").unwrap().host));
691 assert!(!Host::Name(b"a").eq_ignore_ascii_case(&Host::V4(Ipv4Addr::LOCALHOST)));
692 let mut buf = [0u8; 64];
693 let mut out = crate::SliceSink::new(&mut buf);
694 hp(b"1.2.3.4:5").unwrap().write_to(&mut out).unwrap();
695 assert_eq!(out.written(), b"1.2.3.4:5");
696 let mut small = [0u8; 3];
697 let mut out = crate::SliceSink::new(&mut small);
698 assert_eq!(
699 hp(b"[::1]").unwrap().write_to(&mut out),
700 Err(WriteError::Full)
701 );
702 assert!(out.is_empty());
703 assert_eq!(
704 parse_host(b"a:").unwrap_err().kind(),
705 ErrorKind::InvalidHost
706 );
707 }
708}