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) => out.put(n)?,
127 Host::V4(a) => write!(SinkWriter(out), "{a}").map_err(|_| WriteError::Full)?,
128 Host::V6(a) => write!(SinkWriter(out), "[{a}]").map_err(|_| WriteError::Full)?,
129 Host::Future(f) => {
130 out.put(b"[")?;
131 out.put(f)?;
132 out.put(b"]")?;
133 }
134 }
135 Ok(())
136 }
137}
138
139pub(crate) struct SinkWriter<'s, S: Sink>(pub(crate) &'s mut S);
141
142impl<S: Sink> fmt::Write for SinkWriter<'_, S> {
143 fn write_str(&mut self, s: &str) -> fmt::Result {
144 self.0.put(s.as_bytes()).map_err(|_| fmt::Error)
145 }
146}
147
148#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
161pub struct HostPort<'a> {
162 pub host: Host<'a>,
164 pub port: Option<u16>,
166 pub empty_port: bool,
169}
170
171impl HostPort<'_> {
172 pub fn write_to(&self, out: &mut impl Sink) -> core::result::Result<(), WriteError> {
191 let mut count = CountSink::new();
192 self.put(&mut count)?;
193 reserve(out, count.count())?;
194 self.put(out)
195 }
196
197 fn put(&self, out: &mut impl Sink) -> core::result::Result<(), WriteError> {
198 self.host.put(out)?;
199 if let Some(port) = self.port {
200 out.put(b":")?;
201 crate::write::write_decimal_u64(u64::from(port), out)?;
202 }
203 Ok(())
204 }
205}
206
207pub fn parse_host_port(value: &[u8]) -> Result<HostPort<'_>> {
241 let lead = skip_ows(value);
242 host_port(trim_ows(value)).map_err(|e| e.shifted(lead))
243}
244
245pub fn parse_uri_host_port(value: &[u8]) -> Result<HostPort<'_>> {
271 host_port(value)
272}
273
274pub fn parse_host(value: &[u8]) -> Result<Host<'_>> {
290 let hp = parse_host_port(value)?;
291 if hp.port.is_some() || hp.empty_port {
292 let at = skip_ows(value) + value.iter().rposition(|&b| b == b':').unwrap_or(0);
293 return err(ErrorKind::InvalidHost, at.min(value.len()));
294 }
295 Ok(hp.host)
296}
297
298pub(crate) fn host_port(v: &[u8]) -> Result<HostPort<'_>> {
299 host_port_inner(v).map_err(|e| userinfo_first(v, e))
300}
301
302#[cold]
305fn userinfo_first(v: &[u8], e: crate::ParseError) -> crate::ParseError {
306 match find_byte(b'@', v) {
307 Some(at) => crate::ParseError::new(ErrorKind::InvalidHost, at),
308 None => e,
309 }
310}
311
312fn host_port_inner(v: &[u8]) -> Result<HostPort<'_>> {
313 let (host, rest, rest_at) = if v.first() == Some(&b'[') {
314 let Some(close) = find_byte(b']', v) else {
315 return err(ErrorKind::InvalidAddress, v.len());
316 };
317 let inner = v.get(1..close).unwrap_or(&[]);
318 (
319 ip_literal(inner)?,
320 v.get(close + 1..).unwrap_or(&[]),
321 close + 1,
322 )
323 } else {
324 let (host, colon) = reg_name_or_ipv4(v)?;
325 let rest = v.get(colon..).unwrap_or(&[]);
326 if colon == 0 && !rest.is_empty() {
327 return err(ErrorKind::InvalidHost, 0);
328 }
329 (host, rest, colon)
330 };
331 let (port, empty_port) = match rest.split_first() {
332 None => (None, false),
333 Some((b':', digits)) => port(digits).map_err(|e| e.shifted(rest_at + 1))?,
334 Some(_) => return err(ErrorKind::InvalidHost, rest_at),
335 };
336 Ok(HostPort {
337 host,
338 port,
339 empty_port,
340 })
341}
342
343fn port(digits: &[u8]) -> Result<(Option<u16>, bool)> {
344 if digits.is_empty() {
345 return Ok((None, true));
346 }
347 let mut n: u32 = 0;
348 for (i, &d) in digits.iter().enumerate() {
349 if !d.is_ascii_digit() {
350 let kind = if d == b':' {
352 ErrorKind::InvalidHost
353 } else {
354 ErrorKind::InvalidPort
355 };
356 return err(kind, i);
357 }
358 n = n * 10 + u32::from(d - b'0');
359 if n > 65_535 {
360 return err(ErrorKind::InvalidPort, i);
361 }
362 }
363 Ok((u16::try_from(n).ok(), false))
365}
366
367fn ip_literal(inner: &[u8]) -> Result<Host<'_>> {
368 match inner.first() {
370 Some(b'v' | b'V') => {
371 let rest = inner.get(1..).unwrap_or(&[]);
373 let hex = rest.iter().take_while(|&&b| class::is_hexdig(b)).count();
374 let after = rest.get(hex..).unwrap_or(&[]);
375 let tail_ok = match after.split_first() {
376 Some((b'.', tail)) => {
377 !tail.is_empty()
378 && tail.iter().all(|&b| {
379 class::is_unreserved(b) || class::is_sub_delim(b) || b == b':'
380 })
381 }
382 _ => false,
383 };
384 if hex == 0 || !tail_ok {
385 return err(ErrorKind::InvalidAddress, 1);
386 }
387 Ok(Host::Future(inner))
388 }
389 _ => {
390 let text = core::str::from_utf8(inner)
391 .map_err(|_| crate::ParseError::new(ErrorKind::InvalidAddress, 1))?;
392 match Ipv6Addr::from_str(text) {
393 Ok(a) => Ok(Host::V6(a)),
394 Err(_) => err(ErrorKind::InvalidAddress, 1),
395 }
396 }
397 }
398}
399
400fn is_numeric_label(label: &[u8]) -> bool {
403 match label {
404 [] => false,
405 [b'0', b'x' | b'X', hex @ ..] => hex.iter().all(|&b| class::is_hexdig(b)),
406 _ => label.iter().all(u8::is_ascii_digit),
407 }
408}
409
410const REG_NAME: class::Class = class::Class::UNRESERVED.union(class::Class::SUB_DELIM);
412
413fn reg_name_or_ipv4(v: &[u8]) -> Result<(Host<'_>, usize)> {
417 let mut i = 0usize;
418 let mut digits_dots = true;
419 let mut label_start = 0usize;
420 let mut prev_label_start = 0usize;
421 while let Some(&b) = v.get(i) {
422 if REG_NAME.contains(b) {
423 if b == b'.' {
424 prev_label_start = label_start;
425 label_start = i + 1;
426 } else if !b.is_ascii_digit() {
427 digits_dots = false;
428 }
429 i += 1;
430 } else if b == b':' {
431 break;
432 } else if b == b'%' {
433 match (v.get(i + 1), v.get(i + 2)) {
434 (Some(&h), Some(&l)) if class::is_hexdig(h) && class::is_hexdig(l) => i += 3,
435 _ => return err(ErrorKind::InvalidPercentEncoding, i),
436 }
437 digits_dots = false;
438 } else if crate::scan::is_control(b) {
439 return err(control_kind(b), i);
440 } else {
441 return err(ErrorKind::InvalidHost, i);
442 }
443 }
444 let name = v.get(..i).unwrap_or(&[]);
445 let last = if name.last() == Some(&b'.') {
447 name.get(prev_label_start..i - 1).unwrap_or(&[])
448 } else {
449 name.get(label_start..).unwrap_or(&[])
450 };
451 if (digits_dots && !name.is_empty()) || is_numeric_label(last) {
452 return match ipv4(name) {
453 Some(a) => Ok((Host::V4(a), i)),
454 None => err(ErrorKind::InvalidAddress, 0),
455 };
456 }
457 Ok((Host::Name(name), i))
458}
459
460fn ipv4(s: &[u8]) -> Option<Ipv4Addr> {
462 let mut octets = [0u8; 4];
463 let mut parts = s.split(|&b| b == b'.');
464 for slot in &mut octets {
465 let part = parts.next()?;
466 if part.is_empty() || part.len() > 3 || (part.len() > 1 && part.first() == Some(&b'0')) {
467 return None;
468 }
469 let mut n: u16 = 0;
470 for &d in part {
471 if !d.is_ascii_digit() {
472 return None;
473 }
474 n = n * 10 + u16::from(d - b'0');
475 }
476 *slot = u8::try_from(n).ok()?;
477 }
478 if parts.next().is_some() {
479 return None;
480 }
481 Some(Ipv4Addr::from(octets))
482}
483
484#[cfg(test)]
485mod tests {
486 use super::*;
487
488 fn hp(v: &[u8]) -> Result<HostPort<'_>> {
489 parse_host_port(v)
490 }
491
492 #[test]
493 fn test_host_edge_cases() {
494 use ErrorKind::*;
495 assert!(hp(b"").unwrap().host.is_empty());
496 assert_eq!(hp(b"a").unwrap().host, Host::Name(b"a"));
497 let e = hp(b"a:").unwrap();
498 assert_eq!((e.port, e.empty_port), (None, true));
499 assert_eq!(hp(b"a:0").unwrap().port, Some(0));
500 assert_eq!(hp(b"a:65535").unwrap().port, Some(65535));
501 assert_eq!(hp(b"a:65536").unwrap_err().kind(), InvalidPort);
502 assert_eq!(hp(b"a:-1").unwrap_err().kind(), InvalidPort);
503 assert_eq!(hp(b"a:8x").unwrap_err().kind(), InvalidPort);
504 assert_eq!(hp(b"[::1]").unwrap().host, Host::V6(Ipv6Addr::LOCALHOST));
505 assert_eq!(hp(b"[::1]:80").unwrap().port, Some(80));
506 assert_eq!(hp(b"[::1").unwrap_err().kind(), InvalidAddress);
507 assert_eq!(hp(b"[zz]").unwrap_err().kind(), InvalidAddress);
508 assert_eq!(hp(b"[]").unwrap_err().kind(), InvalidAddress);
509 assert_eq!(hp(b"::1").unwrap_err().kind(), InvalidHost);
510 assert_eq!(hp(b"a:1:2").unwrap_err().kind(), InvalidHost);
511 assert_eq!(hp(b"[v1.x]").unwrap().host, Host::Future(b"v1.x"));
512 assert_eq!(hp(b"[v.x]").unwrap_err().kind(), InvalidAddress);
513 assert_eq!(hp(b"[v1.]").unwrap_err().kind(), InvalidAddress);
514 assert_eq!(
515 hp(b"1.2.3.4").unwrap().host,
516 Host::V4(Ipv4Addr::new(1, 2, 3, 4))
517 );
518 assert_eq!(hp(b"[::1]x").unwrap_err().kind(), InvalidHost);
519 assert_eq!(hp(b"%41.com").unwrap().host, Host::Name(b"%41.com"));
520 assert_eq!(hp(b"%4.com").unwrap_err().kind(), InvalidPercentEncoding);
521 assert_eq!(hp(b"a b").unwrap_err().kind(), InvalidHost);
522 assert_eq!(hp(b"a/b").unwrap_err().kind(), InvalidHost);
523 assert_eq!(
524 hp(b"example.com.").unwrap().host,
525 Host::Name(b"example.com.")
526 );
527 assert_eq!(hp(b":80").unwrap_err().kind(), InvalidHost);
528 assert_eq!(hp(b"a\rb").unwrap_err().kind(), BareCr);
529 }
530
531 #[test]
532 fn test_host_partial_ipv4_refused() {
533 for v in [
534 &b"1.2.3"[..],
535 b"01.2.3.4",
536 b"256.0.0.1",
537 b"127.1",
538 b"0x7f.0.0.1",
539 b"1.2.3.4.",
540 b"example.123",
541 b"a.0x10",
542 b"1..2.3",
543 ] {
544 assert_eq!(
545 hp(v).unwrap_err().kind(),
546 ErrorKind::InvalidAddress,
547 "{}",
548 std::string::String::from_utf8_lossy(v)
549 );
550 }
551 assert_eq!(hp(b"123.example").unwrap().host, Host::Name(b"123.example"));
552 assert_eq!(
553 hp(b"0.0.0.0").unwrap().host,
554 Host::V4(Ipv4Addr::UNSPECIFIED)
555 );
556 }
557
558 #[test]
559 fn test_host_userinfo_refused() {
560 let e = hp(b"a.com:80@b.com").unwrap_err();
561 assert_eq!((e.kind(), e.offset()), (ErrorKind::InvalidHost, 8));
562 assert_eq!(
563 hp(b"user@b.com").unwrap_err().kind(),
564 ErrorKind::InvalidHost
565 );
566 assert_eq!(hp(b"[::1]@x").unwrap_err().kind(), ErrorKind::InvalidHost);
567 }
568
569 #[test]
570 fn test_ipv6_text_forms() {
571 for v in [
572 &b"[::]"[..],
573 b"[::1]",
574 b"[2001:db8::1]",
575 b"[2001:DB8:0:0:8:800:200C:417A]",
576 b"[::ffff:192.0.2.1]",
577 b"[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]",
578 b"[1::]",
579 ] {
580 assert!(
581 matches!(hp(v).unwrap().host, Host::V6(_)),
582 "{}",
583 std::string::String::from_utf8_lossy(v)
584 );
585 }
586 for v in [
587 &b"[1:2:3:4:5:6:7:8:9]"[..],
588 b"[12345::]",
589 b"[::1%25eth0]",
590 b"[:::]",
591 b"[1::2::3]",
592 ] {
593 assert_eq!(hp(v).unwrap_err().kind(), ErrorKind::InvalidAddress);
594 }
595 }
596
597 #[test]
598 fn test_host_compare_and_write() {
599 assert!(Host::V6(Ipv6Addr::LOCALHOST).eq_ignore_ascii_case(&hp(b"[0:0::1]").unwrap().host));
600 assert!(!Host::Name(b"a").eq_ignore_ascii_case(&Host::V4(Ipv4Addr::LOCALHOST)));
601 let mut buf = [0u8; 64];
602 let mut out = crate::SliceSink::new(&mut buf);
603 hp(b"1.2.3.4:5").unwrap().write_to(&mut out).unwrap();
604 assert_eq!(out.written(), b"1.2.3.4:5");
605 let mut small = [0u8; 3];
606 let mut out = crate::SliceSink::new(&mut small);
607 assert_eq!(
608 hp(b"[::1]").unwrap().write_to(&mut out),
609 Err(WriteError::Full)
610 );
611 assert!(out.is_empty());
612 assert_eq!(
613 parse_host(b"a:").unwrap_err().kind(),
614 ErrorKind::InvalidHost
615 );
616 }
617}