1
Fork 0

Rollup merge of #72369 - Lucretiel:socketaddr-parse, r=dtolnay

Bring net/parser.rs up to modern up to date with modern rust patterns

The current implementation of IP address parsing is very unidiomatic; it's full of `if` / `return` / `is_some` / `is_none` instead of `?`, `loop` with manual index tracking; etc. Went through and did and cleanup to try to bring it in line with modern sensibilities.

The obvious concern with making changes like this is "make sure you understand why it's written that way before changing it". Looking through the commit history for this file, there are several much smaller commits that make similar changes (For instance, 3024c1434a, 4f3ab4986e, 79f876495b), and there don't seem to be any commits in the history that indicate that this lack of idiomaticity is related to specific performance needs (ie, there aren't any commits that replace a `for` loop with a `loop` and a manual index count). In fact, the basic shape of the file is essentially unchanged from its initial commit back in 2015.

Made the following changes throughout the IP address parser:
- Replaced all uses of `is_some()` / `is_none()` with `?`.
- "Upgraded" loops wherever possible; ie, replace `while` with `for`, etc.
    - Removed all cases of manual index tracking / incrementing.
- Renamed several single-character variables with more expressive names.
- Replaced several manual control flow segments with equivalent adapters (such as `Option::filter`).
- Removed `read_seq_3`; replaced with simple sequences of `?`.
- Parser now reslices its state when consuming, rather than carrying a separate state and index variable.
- `read_digit` now uses `char::to_digit`.
- Added comments throughout, especially in the complex IPv6 parsing logic.
- Added comprehensive local unit tests for the parser to validate these changes.
This commit is contained in:
Manish Goregaokar 2020-07-01 07:42:32 -07:00 committed by GitHub
commit 33f8ce287a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -10,163 +10,132 @@ use crate::str::FromStr;
struct Parser<'a> { struct Parser<'a> {
// parsing as ASCII, so can use byte array // parsing as ASCII, so can use byte array
s: &'a [u8], state: &'a [u8],
pos: usize,
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
fn new(s: &'a str) -> Parser<'a> { fn new(input: &'a str) -> Parser<'a> {
Parser { s: s.as_bytes(), pos: 0 } Parser { state: input.as_bytes() }
} }
fn is_eof(&self) -> bool { fn is_eof(&self) -> bool {
self.pos == self.s.len() self.state.is_empty()
} }
// Commit only if parser returns Some /// Run a parser, and restore the pre-parse state if it fails
fn read_atomically<T, F>(&mut self, cb: F) -> Option<T> fn read_atomically<T, F>(&mut self, inner: F) -> Option<T>
where where
F: FnOnce(&mut Parser<'_>) -> Option<T>, F: FnOnce(&mut Parser<'_>) -> Option<T>,
{ {
let pos = self.pos; let state = self.state;
let r = cb(self); let result = inner(self);
if r.is_none() { if result.is_none() {
self.pos = pos; self.state = state;
} }
r result
} }
// Commit only if parser read till EOF /// Run a parser, but fail if the entire input wasn't consumed.
fn read_till_eof<T, F>(&mut self, cb: F) -> Option<T> /// Doesn't run atomically.
fn read_till_eof<T, F>(&mut self, inner: F) -> Option<T>
where where
F: FnOnce(&mut Parser<'_>) -> Option<T>, F: FnOnce(&mut Parser<'_>) -> Option<T>,
{ {
self.read_atomically(move |p| cb(p).filter(|_| p.is_eof())) inner(self).filter(|_| self.is_eof())
} }
// Apply 3 parsers sequentially /// Same as read_till_eof, but returns a Result<AddrParseError> on failure
fn read_seq_3<A, B, C, PA, PB, PC>(&mut self, pa: PA, pb: PB, pc: PC) -> Option<(A, B, C)> fn parse_with<T, F>(&mut self, inner: F) -> Result<T, AddrParseError>
where where
PA: FnOnce(&mut Parser<'_>) -> Option<A>, F: FnOnce(&mut Parser<'_>) -> Option<T>,
PB: FnOnce(&mut Parser<'_>) -> Option<B>, {
PC: FnOnce(&mut Parser<'_>) -> Option<C>, self.read_till_eof(inner).ok_or(AddrParseError(()))
}
/// Read the next character from the input
fn read_char(&mut self) -> Option<char> {
self.state.split_first().map(|(&b, tail)| {
self.state = tail;
b as char
})
}
/// Read the next character from the input if it matches the target
fn read_given_char(&mut self, target: char) -> Option<char> {
self.read_atomically(|p| p.read_char().filter(|&c| c == target))
}
/// Helper for reading separators in an indexed loop. Reads the separator
/// character iff index > 0, then runs the parser. When used in a loop,
/// the separator character will only be read on index > 0 (see
/// read_ipv4_addr for an example)
fn read_separator<T, F>(&mut self, sep: char, index: usize, inner: F) -> Option<T>
where
F: FnOnce(&mut Parser<'_>) -> Option<T>,
{ {
self.read_atomically(move |p| { self.read_atomically(move |p| {
let a = pa(p); if index > 0 {
let b = if a.is_some() { pb(p) } else { None }; let _ = p.read_given_char(sep)?;
let c = if b.is_some() { pc(p) } else { None };
match (a, b, c) {
(Some(a), Some(b), Some(c)) => Some((a, b, c)),
_ => None,
} }
inner(p)
}) })
} }
// Read next char // Read a single digit in the given radix. For instance, 0-9 in radix 10;
fn read_char(&mut self) -> Option<char> { // 0-9A-F in radix 16.
if self.is_eof() { fn read_digit(&mut self, radix: u32) -> Option<u32> {
None self.read_atomically(move |p| p.read_char()?.to_digit(radix))
} else {
let r = self.s[self.pos] as char;
self.pos += 1;
Some(r)
}
} }
// Return char and advance iff next char is equal to requested // Read a number off the front of the input in the given radix, stopping
fn read_given_char(&mut self, c: char) -> Option<char> { // at the first non-digit character or eof. Fails if the number has more
self.read_atomically(|p| match p.read_char() { // digits than max_digits, or the value is >= upto, or if there is no number.
Some(next) if next == c => Some(next), fn read_number(&mut self, radix: u32, max_digits: u32, upto: u32) -> Option<u32> {
_ => None, self.read_atomically(move |p| {
let mut result = 0;
let mut digit_count = 0;
while let Some(digit) = p.read_digit(radix) {
result = (result * radix) + digit;
digit_count += 1;
if digit_count > max_digits || result >= upto {
return None;
}
}
if digit_count == 0 { None } else { Some(result) }
}) })
} }
// Read digit /// Read an IPv4 address
fn read_digit(&mut self, radix: u8) -> Option<u8> {
fn parse_digit(c: char, radix: u8) -> Option<u8> {
let c = c as u8;
// assuming radix is either 10 or 16
if c >= b'0' && c <= b'9' {
Some(c - b'0')
} else if radix > 10 && c >= b'a' && c < b'a' + (radix - 10) {
Some(c - b'a' + 10)
} else if radix > 10 && c >= b'A' && c < b'A' + (radix - 10) {
Some(c - b'A' + 10)
} else {
None
}
}
self.read_atomically(|p| p.read_char().and_then(|c| parse_digit(c, radix)))
}
fn read_number_impl(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option<u32> {
let mut r = 0;
let mut digit_count = 0;
loop {
match self.read_digit(radix) {
Some(d) => {
r = r * (radix as u32) + (d as u32);
digit_count += 1;
if digit_count > max_digits || r >= upto {
return None;
}
}
None => {
if digit_count == 0 {
return None;
} else {
return Some(r);
}
}
};
}
}
// Read number, failing if max_digits of number value exceeded
fn read_number(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option<u32> {
self.read_atomically(|p| p.read_number_impl(radix, max_digits, upto))
}
fn read_ipv4_addr_impl(&mut self) -> Option<Ipv4Addr> {
let mut bs = [0; 4];
let mut i = 0;
while i < 4 {
if i != 0 && self.read_given_char('.').is_none() {
return None;
}
bs[i] = self.read_number(10, 3, 0x100).map(|n| n as u8)?;
i += 1;
}
Some(Ipv4Addr::new(bs[0], bs[1], bs[2], bs[3]))
}
// Read IPv4 address
fn read_ipv4_addr(&mut self) -> Option<Ipv4Addr> { fn read_ipv4_addr(&mut self) -> Option<Ipv4Addr> {
self.read_atomically(|p| p.read_ipv4_addr_impl()) self.read_atomically(|p| {
let mut groups = [0; 4];
for (i, slot) in groups.iter_mut().enumerate() {
*slot = p.read_separator('.', i, |p| p.read_number(10, 3, 0x100))? as u8;
}
Some(groups.into())
})
} }
fn read_ipv6_addr_impl(&mut self) -> Option<Ipv6Addr> { /// Read an IPV6 Address
fn ipv6_addr_from_head_tail(head: &[u16], tail: &[u16]) -> Ipv6Addr { fn read_ipv6_addr(&mut self) -> Option<Ipv6Addr> {
assert!(head.len() + tail.len() <= 8); /// Read a chunk of an ipv6 address into `groups`. Returns the number
let mut gs = [0; 8]; /// of groups read, along with a bool indicating if an embedded
gs[..head.len()].copy_from_slice(head); /// trailing ipv4 address was read. Specifically, read a series of
gs[(8 - tail.len())..8].copy_from_slice(tail); /// colon-separated ipv6 groups (0x0000 - 0xFFFF), with an optional
Ipv6Addr::new(gs[0], gs[1], gs[2], gs[3], gs[4], gs[5], gs[6], gs[7]) /// trailing embedded ipv4 address.
} fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) {
let limit = groups.len();
fn read_groups(p: &mut Parser<'_>, groups: &mut [u16; 8], limit: usize) -> (usize, bool) { for (i, slot) in groups.iter_mut().enumerate() {
let mut i = 0; // Try to read a trailing embedded ipv4 address. There must be
while i < limit { // at least two groups left.
if i < limit - 1 { if i < limit - 1 {
let ipv4 = p.read_atomically(|p| { let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr());
if i == 0 || p.read_given_char(':').is_some() {
p.read_ipv4_addr()
} else {
None
}
});
if let Some(v4_addr) = ipv4 { if let Some(v4_addr) = ipv4 {
let octets = v4_addr.octets(); let octets = v4_addr.octets();
groups[i + 0] = ((octets[0] as u16) << 8) | (octets[1] as u16); groups[i + 0] = ((octets[0] as u16) << 8) | (octets[1] as u16);
@ -175,83 +144,85 @@ impl<'a> Parser<'a> {
} }
} }
let group = p.read_atomically(|p| { let group = p.read_separator(':', i, |p| p.read_number(16, 4, 0x10000));
if i == 0 || p.read_given_char(':').is_some() {
p.read_number(16, 4, 0x10000).map(|n| n as u16)
} else {
None
}
});
match group { match group {
Some(g) => groups[i] = g, Some(g) => *slot = g as u16,
None => return (i, false), None => return (i, false),
} }
i += 1;
} }
(i, false) (groups.len(), false)
} }
let mut head = [0; 8]; self.read_atomically(|p| {
let (head_size, head_ipv4) = read_groups(self, &mut head, 8); // Read the front part of the address; either the whole thing, or up
// to the first ::
let mut head = [0; 8];
let (head_size, head_ipv4) = read_groups(p, &mut head);
if head_size == 8 { if head_size == 8 {
return Some(Ipv6Addr::new( return Some(head.into());
head[0], head[1], head[2], head[3], head[4], head[5], head[6], head[7], }
));
}
// IPv4 part is not allowed before `::` // IPv4 part is not allowed before `::`
if head_ipv4 { if head_ipv4 {
return None; return None;
} }
// read `::` if previous code parsed less than 8 groups // read `::` if previous code parsed less than 8 groups
if self.read_given_char(':').is_none() || self.read_given_char(':').is_none() { // `::` indicates one or more groups of 16 bits of zeros
return None; let _ = p.read_given_char(':')?;
} let _ = p.read_given_char(':')?;
let mut tail = [0; 8]; // Read the back part of the address. The :: must contain at least one
// `::` indicates one or more groups of 16 bits of zeros // set of zeroes, so our max length is 7.
let limit = 8 - (head_size + 1); let mut tail = [0; 7];
let (tail_size, _) = read_groups(self, &mut tail, limit); let limit = 8 - (head_size + 1);
Some(ipv6_addr_from_head_tail(&head[..head_size], &tail[..tail_size])) let (tail_size, _) = read_groups(p, &mut tail[..limit]);
}
// Concat the head and tail of the IP address
fn read_ipv6_addr(&mut self) -> Option<Ipv6Addr> { head[(8 - tail_size)..8].copy_from_slice(&tail[..tail_size]);
self.read_atomically(|p| p.read_ipv6_addr_impl())
Some(head.into())
})
} }
/// Read an IP Address, either IPV4 or IPV6.
fn read_ip_addr(&mut self) -> Option<IpAddr> { fn read_ip_addr(&mut self) -> Option<IpAddr> {
self.read_ipv4_addr().map(IpAddr::V4).or_else(|| self.read_ipv6_addr().map(IpAddr::V6)) self.read_ipv4_addr().map(IpAddr::V4).or_else(move || self.read_ipv6_addr().map(IpAddr::V6))
} }
/// Read a : followed by a port in base 10
fn read_port(&mut self) -> Option<u16> {
self.read_atomically(|p| {
let _ = p.read_given_char(':')?;
let port = p.read_number(10, 5, 0x10000)?;
Some(port as u16)
})
}
/// Read an IPV4 address with a port
fn read_socket_addr_v4(&mut self) -> Option<SocketAddrV4> { fn read_socket_addr_v4(&mut self) -> Option<SocketAddrV4> {
let ip_addr = |p: &mut Parser<'_>| p.read_ipv4_addr(); self.read_atomically(|p| {
let colon = |p: &mut Parser<'_>| p.read_given_char(':'); let ip = p.read_ipv4_addr()?;
let port = |p: &mut Parser<'_>| p.read_number(10, 5, 0x10000).map(|n| n as u16); let port = p.read_port()?;
Some(SocketAddrV4::new(ip, port))
self.read_seq_3(ip_addr, colon, port).map(|t| {
let (ip, _, port): (Ipv4Addr, char, u16) = t;
SocketAddrV4::new(ip, port)
}) })
} }
/// Read an IPV6 address with a port
fn read_socket_addr_v6(&mut self) -> Option<SocketAddrV6> { fn read_socket_addr_v6(&mut self) -> Option<SocketAddrV6> {
let ip_addr = |p: &mut Parser<'_>| { self.read_atomically(|p| {
let open_br = |p: &mut Parser<'_>| p.read_given_char('['); let _ = p.read_given_char('[')?;
let ip_addr = |p: &mut Parser<'_>| p.read_ipv6_addr(); let ip = p.read_ipv6_addr()?;
let clos_br = |p: &mut Parser<'_>| p.read_given_char(']'); let _ = p.read_given_char(']')?;
p.read_seq_3(open_br, ip_addr, clos_br).map(|t| t.1)
};
let colon = |p: &mut Parser<'_>| p.read_given_char(':');
let port = |p: &mut Parser<'_>| p.read_number(10, 5, 0x10000).map(|n| n as u16);
self.read_seq_3(ip_addr, colon, port).map(|t| { let port = p.read_port()?;
let (ip, _, port): (Ipv6Addr, char, u16) = t; Some(SocketAddrV6::new(ip, port, 0, 0))
SocketAddrV6::new(ip, port, 0, 0)
}) })
} }
/// Read an IP address with a port
fn read_socket_addr(&mut self) -> Option<SocketAddr> { fn read_socket_addr(&mut self) -> Option<SocketAddr> {
self.read_socket_addr_v4() self.read_socket_addr_v4()
.map(SocketAddr::V4) .map(SocketAddr::V4)
@ -263,10 +234,7 @@ impl<'a> Parser<'a> {
impl FromStr for IpAddr { impl FromStr for IpAddr {
type Err = AddrParseError; type Err = AddrParseError;
fn from_str(s: &str) -> Result<IpAddr, AddrParseError> { fn from_str(s: &str) -> Result<IpAddr, AddrParseError> {
match Parser::new(s).read_till_eof(|p| p.read_ip_addr()) { Parser::new(s).parse_with(|p| p.read_ip_addr())
Some(s) => Ok(s),
None => Err(AddrParseError(())),
}
} }
} }
@ -274,10 +242,7 @@ impl FromStr for IpAddr {
impl FromStr for Ipv4Addr { impl FromStr for Ipv4Addr {
type Err = AddrParseError; type Err = AddrParseError;
fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> { fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> {
match Parser::new(s).read_till_eof(|p| p.read_ipv4_addr()) { Parser::new(s).parse_with(|p| p.read_ipv4_addr())
Some(s) => Ok(s),
None => Err(AddrParseError(())),
}
} }
} }
@ -285,10 +250,7 @@ impl FromStr for Ipv4Addr {
impl FromStr for Ipv6Addr { impl FromStr for Ipv6Addr {
type Err = AddrParseError; type Err = AddrParseError;
fn from_str(s: &str) -> Result<Ipv6Addr, AddrParseError> { fn from_str(s: &str) -> Result<Ipv6Addr, AddrParseError> {
match Parser::new(s).read_till_eof(|p| p.read_ipv6_addr()) { Parser::new(s).parse_with(|p| p.read_ipv6_addr())
Some(s) => Ok(s),
None => Err(AddrParseError(())),
}
} }
} }
@ -296,10 +258,7 @@ impl FromStr for Ipv6Addr {
impl FromStr for SocketAddrV4 { impl FromStr for SocketAddrV4 {
type Err = AddrParseError; type Err = AddrParseError;
fn from_str(s: &str) -> Result<SocketAddrV4, AddrParseError> { fn from_str(s: &str) -> Result<SocketAddrV4, AddrParseError> {
match Parser::new(s).read_till_eof(|p| p.read_socket_addr_v4()) { Parser::new(s).parse_with(|p| p.read_socket_addr_v4())
Some(s) => Ok(s),
None => Err(AddrParseError(())),
}
} }
} }
@ -307,10 +266,7 @@ impl FromStr for SocketAddrV4 {
impl FromStr for SocketAddrV6 { impl FromStr for SocketAddrV6 {
type Err = AddrParseError; type Err = AddrParseError;
fn from_str(s: &str) -> Result<SocketAddrV6, AddrParseError> { fn from_str(s: &str) -> Result<SocketAddrV6, AddrParseError> {
match Parser::new(s).read_till_eof(|p| p.read_socket_addr_v6()) { Parser::new(s).parse_with(|p| p.read_socket_addr_v6())
Some(s) => Ok(s),
None => Err(AddrParseError(())),
}
} }
} }
@ -318,10 +274,7 @@ impl FromStr for SocketAddrV6 {
impl FromStr for SocketAddr { impl FromStr for SocketAddr {
type Err = AddrParseError; type Err = AddrParseError;
fn from_str(s: &str) -> Result<SocketAddr, AddrParseError> { fn from_str(s: &str) -> Result<SocketAddr, AddrParseError> {
match Parser::new(s).read_till_eof(|p| p.read_socket_addr()) { Parser::new(s).parse_with(|p| p.read_socket_addr())
Some(s) => Ok(s),
None => Err(AddrParseError(())),
}
} }
} }
@ -376,3 +329,146 @@ impl Error for AddrParseError {
"invalid IP address syntax" "invalid IP address syntax"
} }
} }
#[cfg(test)]
mod tests {
// FIXME: These tests are all excellent candidates for AFL fuzz testing
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use crate::str::FromStr;
const PORT: u16 = 8080;
const IPV4: Ipv4Addr = Ipv4Addr::new(192, 168, 0, 1);
const IPV4_STR: &str = "192.168.0.1";
const IPV4_STR_PORT: &str = "192.168.0.1:8080";
const IPV6: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc0a8, 0x1);
const IPV6_STR_FULL: &str = "2001:db8:0:0:0:0:c0a8:1";
const IPV6_STR_COMPRESS: &str = "2001:db8::c0a8:1";
const IPV6_STR_V4: &str = "2001:db8::192.168.0.1";
const IPV6_STR_PORT: &str = "[2001:db8::c0a8:1]:8080";
#[test]
fn parse_ipv4() {
let result: Ipv4Addr = IPV4_STR.parse().unwrap();
assert_eq!(result, IPV4);
assert!(Ipv4Addr::from_str(IPV4_STR_PORT).is_err());
assert!(Ipv4Addr::from_str(IPV6_STR_FULL).is_err());
assert!(Ipv4Addr::from_str(IPV6_STR_COMPRESS).is_err());
assert!(Ipv4Addr::from_str(IPV6_STR_V4).is_err());
assert!(Ipv4Addr::from_str(IPV6_STR_PORT).is_err());
}
#[test]
fn parse_ipv6() {
let result: Ipv6Addr = IPV6_STR_FULL.parse().unwrap();
assert_eq!(result, IPV6);
let result: Ipv6Addr = IPV6_STR_COMPRESS.parse().unwrap();
assert_eq!(result, IPV6);
let result: Ipv6Addr = IPV6_STR_V4.parse().unwrap();
assert_eq!(result, IPV6);
assert!(Ipv6Addr::from_str(IPV4_STR).is_err());
assert!(Ipv6Addr::from_str(IPV4_STR_PORT).is_err());
assert!(Ipv6Addr::from_str(IPV6_STR_PORT).is_err());
}
#[test]
fn parse_ip() {
let result: IpAddr = IPV4_STR.parse().unwrap();
assert_eq!(result, IpAddr::from(IPV4));
let result: IpAddr = IPV6_STR_FULL.parse().unwrap();
assert_eq!(result, IpAddr::from(IPV6));
let result: IpAddr = IPV6_STR_COMPRESS.parse().unwrap();
assert_eq!(result, IpAddr::from(IPV6));
let result: IpAddr = IPV6_STR_V4.parse().unwrap();
assert_eq!(result, IpAddr::from(IPV6));
assert!(IpAddr::from_str(IPV4_STR_PORT).is_err());
assert!(IpAddr::from_str(IPV6_STR_PORT).is_err());
}
#[test]
fn parse_socket_v4() {
let result: SocketAddrV4 = IPV4_STR_PORT.parse().unwrap();
assert_eq!(result, SocketAddrV4::new(IPV4, PORT));
assert!(SocketAddrV4::from_str(IPV4_STR).is_err());
assert!(SocketAddrV4::from_str(IPV6_STR_FULL).is_err());
assert!(SocketAddrV4::from_str(IPV6_STR_COMPRESS).is_err());
assert!(SocketAddrV4::from_str(IPV6_STR_V4).is_err());
assert!(SocketAddrV4::from_str(IPV6_STR_PORT).is_err());
}
#[test]
fn parse_socket_v6() {
let result: SocketAddrV6 = IPV6_STR_PORT.parse().unwrap();
assert_eq!(result, SocketAddrV6::new(IPV6, PORT, 0, 0));
assert!(SocketAddrV6::from_str(IPV4_STR).is_err());
assert!(SocketAddrV6::from_str(IPV4_STR_PORT).is_err());
assert!(SocketAddrV6::from_str(IPV6_STR_FULL).is_err());
assert!(SocketAddrV6::from_str(IPV6_STR_COMPRESS).is_err());
assert!(SocketAddrV6::from_str(IPV6_STR_V4).is_err());
}
#[test]
fn parse_socket() {
let result: SocketAddr = IPV4_STR_PORT.parse().unwrap();
assert_eq!(result, SocketAddr::from((IPV4, PORT)));
let result: SocketAddr = IPV6_STR_PORT.parse().unwrap();
assert_eq!(result, SocketAddr::from((IPV6, PORT)));
assert!(SocketAddr::from_str(IPV4_STR).is_err());
assert!(SocketAddr::from_str(IPV6_STR_FULL).is_err());
assert!(SocketAddr::from_str(IPV6_STR_COMPRESS).is_err());
assert!(SocketAddr::from_str(IPV6_STR_V4).is_err());
}
#[test]
fn ipv6_corner_cases() {
let result: Ipv6Addr = "1::".parse().unwrap();
assert_eq!(result, Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0));
let result: Ipv6Addr = "1:1::".parse().unwrap();
assert_eq!(result, Ipv6Addr::new(1, 1, 0, 0, 0, 0, 0, 0));
let result: Ipv6Addr = "::1".parse().unwrap();
assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
let result: Ipv6Addr = "::1:1".parse().unwrap();
assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 1, 1));
let result: Ipv6Addr = "::".parse().unwrap();
assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
let result: Ipv6Addr = "::192.168.0.1".parse().unwrap();
assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc0a8, 0x1));
let result: Ipv6Addr = "::1:192.168.0.1".parse().unwrap();
assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 1, 0xc0a8, 0x1));
let result: Ipv6Addr = "1:1:1:1:1:1:192.168.0.1".parse().unwrap();
assert_eq!(result, Ipv6Addr::new(1, 1, 1, 1, 1, 1, 0xc0a8, 0x1));
}
// Things that might not seem like failures but are
#[test]
fn ipv6_corner_failures() {
// No IP address before the ::
assert!(Ipv6Addr::from_str("1:192.168.0.1::").is_err());
// :: must have at least 1 set of zeroes
assert!(Ipv6Addr::from_str("1:1:1:1::1:1:1:1").is_err());
// Need brackets for a port
assert!(SocketAddrV6::from_str("1:1:1:1:1:1:1:1:8080").is_err());
}
}