Skip to main content

parse_net/
host.rs

1//! `Host = uri-host [ ":" port ]` (RFC 9110 section 7.2, citing RFC 3986
2//! section 3.2.2), with real IPv4 and IPv6 validation.
3//!
4//! # Examples
5//!
6//! ```
7//! use core::net::Ipv6Addr;
8//! use parse_net::{self as pn, Host, HostPort};
9//!
10//! assert_eq!(pn::parse_host_port(b"[::1]:8080")?,
11//!            HostPort { host: Host::V6(Ipv6Addr::LOCALHOST), port: Some(8080), empty_port: false });
12//! assert_eq!(pn::parse_host_port(b"Example.COM.")?.host, Host::Name(b"Example.COM."));
13//! assert_eq!(pn::parse_host_port(b"127.1").unwrap_err().kind(), pn::ErrorKind::InvalidAddress);
14//! assert_eq!(pn::parse_host_port(b"a.com:80@b.com").unwrap_err().kind(), pn::ErrorKind::InvalidHost);
15//! # Ok::<(), pn::ParseError>(())
16//! ```
17
18use 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/// A host: a registered name, an IP address, or a future IP literal.
33///
34/// # Examples
35///
36/// ```
37/// use core::net::Ipv4Addr;
38/// use parse_net::{parse_host_port, Host};
39///
40/// assert_eq!(parse_host_port(b"192.0.2.1")?.host, Host::V4(Ipv4Addr::new(192, 0, 2, 1)));
41/// assert_eq!(parse_host_port(b"example.com")?.host, Host::Name(b"example.com"));
42/// assert_eq!(parse_host_port(b"[v1.fe]")?.host, Host::Future(b"v1.fe"));
43/// # Ok::<(), parse_net::ParseError>(())
44/// ```
45#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
46pub enum Host<'a> {
47    /// A registered name (`reg-name`), as sent: case and a trailing dot are
48    /// kept, `%XX` triples are not decoded. Empty when the field was empty.
49    Name(&'a [u8]),
50    /// A dotted-decimal IPv4 address.
51    V4(Ipv4Addr),
52    /// A bracketed IPv6 address.
53    V6(Ipv6Addr),
54    /// A bracketed `IPvFuture` literal (`vX.yyy`), without the brackets.
55    Future(&'a [u8]),
56}
57
58impl Host<'_> {
59    /// Whether two hosts are the same: names compared ASCII
60    /// case-insensitively, IP literals by value.
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// use parse_net::host::parse_host;
66    ///
67    /// assert!(parse_host(b"Example.com")?.eq_ignore_ascii_case(&parse_host(b"example.COM")?));
68    /// assert!(parse_host(b"[::1]")?.eq_ignore_ascii_case(&parse_host(b"[0:0::1]")?));
69    /// # Ok::<(), parse_net::ParseError>(())
70    /// ```
71    #[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    /// Whether this is the empty name of an empty `Host` field.
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// use parse_net::parse_host_port;
89    ///
90    /// assert!(parse_host_port(b"")?.host.is_empty());
91    /// assert!(!parse_host_port(b"example.com")?.host.is_empty());
92    /// # Ok::<(), parse_net::ParseError>(())
93    /// ```
94    #[must_use]
95    pub fn is_empty(&self) -> bool {
96        matches!(self, Host::Name(n) if n.is_empty())
97    }
98
99    /// Writes the host: names as given, IPv6 bracketed in its RFC 5952 form.
100    ///
101    /// # Errors
102    ///
103    /// [`WriteError::Full`]; nothing is written when the sink reports its
104    /// room.
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// use parse_net::{host::parse_host, SliceSink};
110    ///
111    /// let mut buf = [0u8; 64];
112    /// let mut out = SliceSink::new(&mut buf);
113    /// parse_host(b"[2001:DB8:0:0:0:0:0:1]")?.write_to(&mut out).expect("room");
114    /// assert_eq!(out.written(), b"[2001:db8::1]");
115    /// # Ok::<(), parse_net::ParseError>(())
116    /// ```
117    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
139/// Lets `core::fmt` write into a [`Sink`].
140pub(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/// A host and an optional port.
149///
150/// # Examples
151///
152/// ```
153/// use parse_net::{parse_host_port, Host};
154///
155/// let hp = parse_host_port(b"example.com:")?;
156/// assert_eq!((hp.host, hp.port, hp.empty_port), (Host::Name(b"example.com"), None, true));
157/// assert_eq!(parse_host_port(b"example.com:8080")?.port, Some(8080));
158/// # Ok::<(), parse_net::ParseError>(())
159/// ```
160#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
161pub struct HostPort<'a> {
162    /// The host.
163    pub host: Host<'a>,
164    /// The port, when digits followed the `:`.
165    pub port: Option<u16>,
166    /// Whether a `:` with no digits followed the host (`example.com:`),
167    /// which RFC 3986 allows (`port = *DIGIT`).
168    pub empty_port: bool,
169}
170
171impl HostPort<'_> {
172    /// Writes the host, then `:port` when there is a port.
173    ///
174    /// # Errors
175    ///
176    /// [`WriteError::Full`]; nothing is written when the sink reports its
177    /// room.
178    ///
179    /// # Examples
180    ///
181    /// ```
182    /// use parse_net::{parse_host_port, SliceSink};
183    ///
184    /// let mut buf = [0u8; 64];
185    /// let mut out = SliceSink::new(&mut buf);
186    /// parse_host_port(b"[::1]:8080")?.write_to(&mut out).expect("room");
187    /// assert_eq!(out.written(), b"[::1]:8080");
188    /// # Ok::<(), parse_net::ParseError>(())
189    /// ```
190    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
207/// Parses a `Host` value: `uri-host [ ":" port ]`.
208///
209/// A registered name (`unreserved / pct-encoded / sub-delims`), a
210/// dotted-decimal IPv4 address with no leading zeros, a bracketed IPv6
211/// address in any RFC 4291 text form, or a bracketed IPvFuture literal,
212/// optionally followed by `:` and a port up to 65535. An empty port is
213/// accepted with `empty_port` set. An empty value is accepted as an empty
214/// name (RFC 9112 section 3.2: the target has no authority); the caller
215/// decides what that means.
216///
217/// A name made only of digits and dots, or whose last label is a number
218/// (`127.1`, `0x7f.1`, `example.123`), must be a canonical IPv4 address:
219/// RFC 3986 reads such hosts as names and the WHATWG URL standard as IPv4
220/// addresses, and refusing them removes the disagreement.
221///
222/// # Errors
223///
224/// [`ErrorKind::InvalidHost`] for userinfo (any `@`, checked first), a byte
225/// outside the name class, a port with no host, or unbracketed IPv6;
226/// [`ErrorKind::InvalidAddress`] for a malformed IP literal or a numeric
227/// name that is not canonical IPv4; [`ErrorKind::InvalidPort`];
228/// [`ErrorKind::InvalidPercentEncoding`]; a control kind for a control byte.
229///
230/// # Examples
231///
232/// ```
233/// use parse_net::{parse_host_port, ErrorKind};
234///
235/// let hp = parse_host_port(b"example.com:8443")?;
236/// assert_eq!(hp.port, Some(8443));
237/// assert_eq!(parse_host_port(b"a:65536").unwrap_err().kind(), ErrorKind::InvalidPort);
238/// # Ok::<(), parse_net::ParseError>(())
239/// ```
240pub 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
245/// Parses `host [ ":" port ]` exactly as given, for a URI authority or a
246/// request target: unlike [`parse_host_port`], no OWS is stripped, so any
247/// whitespace is refused.
248///
249/// Use this wherever the bytes are not a field value: an authority-form or
250/// absolute-form request target, a URL, a `Forwarded` node. Stripping
251/// whitespace there would accept `CONNECT  example.com:443` (two spaces),
252/// which other parsers refuse.
253///
254/// # Errors
255///
256/// As [`parse_host_port`]; whitespace is refused like any other byte outside
257/// the grammar ([`ErrorKind::InvalidHost`] in the host, [`ErrorKind::InvalidPort`]
258/// after a port).
259///
260/// # Examples
261///
262/// ```
263/// use parse_net::{host::parse_uri_host_port, parse_host_port};
264///
265/// assert!(parse_host_port(b" example.com:443").is_ok());
266/// assert!(parse_uri_host_port(b" example.com:443").is_err());
267/// assert_eq!(parse_uri_host_port(b"example.com:443")?.port, Some(443));
268/// # Ok::<(), parse_net::ParseError>(())
269/// ```
270pub fn parse_uri_host_port(value: &[u8]) -> Result<HostPort<'_>> {
271    host_port(value)
272}
273
274/// Parses a host with no port.
275///
276/// # Errors
277///
278/// As [`parse_host_port`], and [`ErrorKind::InvalidHost`] for a port.
279///
280/// # Examples
281///
282/// ```
283/// use parse_net::{host::parse_host, Host};
284///
285/// assert_eq!(parse_host(b"192.0.2.1")?, Host::V4([192, 0, 2, 1].into()));
286/// assert!(parse_host(b"a.example:80").is_err());
287/// # Ok::<(), parse_net::ParseError>(())
288/// ```
289pub 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/// Userinfo is the attack (`a.com:80@b.com`): whatever else is wrong with the
303/// value, an `@` anywhere is reported as `InvalidHost` at the `@`.
304#[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            // A second ":" means unbracketed IPv6.
351            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    // `n` is at most 65535.
364    Ok((u16::try_from(n).ok(), false))
365}
366
367fn ip_literal(inner: &[u8]) -> Result<Host<'_>> {
368    // Offsets inside the brackets are reported from the "[".
369    match inner.first() {
370        Some(b'v' | b'V') => {
371            // IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
372            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
400/// Whether `label` is a number in the WHATWG sense: decimal digits, or `0x`
401/// followed by hex digits.
402fn 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
410/// `unreserved / sub-delims`: the reg-name bytes other than `pct-encoded`.
411const REG_NAME: class::Class = class::Class::UNRESERVED.union(class::Class::SUB_DELIM);
412
413/// Reads the name at the start of `v` up to a `:` or the end, in one pass:
414/// validates it as a reg-name and notes whether it could be read as IPv4.
415/// Returns the host and the offset where the name ended.
416fn 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    // The last label, ignoring one trailing dot.
446    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
460/// Canonical dotted decimal: four parts, 0 to 255, no leading zeros.
461fn 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}