From 4d5166a3995231159db18edacf07d020917abfbe Mon Sep 17 00:00:00 2001 From: Ken Tossell Date: Sat, 31 Jan 2015 17:39:12 -0500 Subject: [PATCH] Reorganized `io::net::ip`, creating separate `Ipv[46]Addr` types and adding version-specific methods This adds several address inspection methods, such as `is_loopback`, `is_multicast`, and `is_private`, as well as improving formatting (It adds support for `::`-shortening addresses and fixes :: and ::1) and adding methods to convert between different IP versions. The variants `IpAddr::Ipv4Addr` and `Ipv6Addr` were limiting: There are `is_...` methods that make sense for either v4 or v6 addresses but not for both versions, and there's no way to define methods for only one variant of an enum, so the variants are promoted to independent types. The IpAddr enum now wraps the Ipv4Addr and Ipv6Addr types: enum IpAddr { V4(Ipv4Addr), V6(Ipv6Addr) } Addresses are created as: Ipv4Addr::new(1, 2, 3, 4) -> Ipv4Addr IpAddr::new_v4(1, 2, 3, 4) -> IpvAddr::V4(Ipv4Addr) or using FromStr for any of the three address types. IP addresses can be passed as their core types (IPvXAddr), as IpAddr, or using the trait ToIpAddr, which is implemented for Ipv4Addr, Ipv6Addr and IpAddr. --- src/libstd/old_io/net/addrinfo.rs | 2 +- src/libstd/old_io/net/ip.rs | 594 ++++++++++++++++++++++++++---- src/libstd/old_io/net/udp.rs | 2 +- src/libstd/old_io/test.rs | 4 +- src/libstd/sys/common/net.rs | 24 +- 5 files changed, 542 insertions(+), 84 deletions(-) diff --git a/src/libstd/old_io/net/addrinfo.rs b/src/libstd/old_io/net/addrinfo.rs index e37744f3aa3ec..89901bbc53f77 100644 --- a/src/libstd/old_io/net/addrinfo.rs +++ b/src/libstd/old_io/net/addrinfo.rs @@ -120,7 +120,7 @@ mod test { fn dns_smoke_test() { let ipaddrs = get_host_addresses("localhost").unwrap(); let mut found_local = false; - let local_addr = &Ipv4Addr(127, 0, 0, 1); + let local_addr = &IpAddr::new_v4(127, 0, 0, 1); for addr in ipaddrs.iter() { found_local = found_local || addr == local_addr; } diff --git a/src/libstd/old_io/net/ip.rs b/src/libstd/old_io/net/ip.rs index 565f9d8381801..73602169ab8f7 100644 --- a/src/libstd/old_io/net/ip.rs +++ b/src/libstd/old_io/net/ip.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -15,51 +15,355 @@ #![allow(missing_docs)] -pub use self::IpAddr::*; - use boxed::Box; use fmt; use old_io::{self, IoResult, IoError}; use old_io::net; -use iter::{Iterator, IteratorExt}; +use iter::{Iterator, IteratorExt, range}; use ops::{FnOnce, FnMut}; -use option::Option; -use option::Option::{None, Some}; +use option::Option::{self, None, Some}; use result::Result::{self, Ok, Err}; -use slice::SliceExt; +use slice::{AsSlice, SliceConcatExt, SliceExt}; use str::{FromStr, StrExt}; +use string::String; use vec::Vec; pub type Port = u16; +pub trait ToIpAddr { + /// Convert the address to a generic IpAddr + fn to_ip_addr(&self) -> IpAddr; +} + +#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] +pub struct Ipv4Addr { + octets: [u8; 4] +} + +#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] +pub struct Ipv6Addr { + segments: [u16; 8] +} + +#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] +pub enum Ipv6MulticastScope { + InterfaceLocal, + LinkLocal, + RealmLocal, + AdminLocal, + SiteLocal, + OrganizationLocal, + Global +} + #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] pub enum IpAddr { - Ipv4Addr(u8, u8, u8, u8), - Ipv6Addr(u16, u16, u16, u16, u16, u16, u16, u16) + V4(Ipv4Addr), + V6(Ipv6Addr) +} + +impl IpAddr { + /// Create a new IpAddr that contains an IPv4 address. + /// + /// The result will represent the IP address a.b.c.d + pub fn new_v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr { + Ipv4Addr::new(a, b, c, d).to_ip_addr() + } + + /// Create a new IpAddr that contains an IPv6 address. + /// + /// The result will represent the IP address a:b:c:d:e:f + pub fn new_v6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> IpAddr { + Ipv6Addr::new(a, b, c, d, e, f, g, h).to_ip_addr() + } +} + +impl ToIpAddr for IpAddr { + fn to_ip_addr(&self) -> IpAddr { + *self + } } #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for IpAddr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Ipv4Addr(a, b, c, d) => - write!(fmt, "{}.{}.{}.{}", a, b, c, d), + IpAddr::V4(v4) => v4.fmt(f), + IpAddr::V6(v6) => v6.fmt(f) + } + } +} + +impl Ipv4Addr { + /// Create a new IPv4 address from four eight-bit octets. + /// + /// The result will represent the IP address a.b.c.d + pub fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { + Ipv4Addr { + octets: [a, b, c, d] + } + } + + /// Returns the four eight-bit integers that make up this address + pub fn octets(&self) -> &[u8; 4] { + &self.octets + } + + /// Returns true for the special 'unspecified' address 0.0.0.0 + pub fn is_unspecified(&self) -> bool { + self.octets == [0, 0, 0, 0] + } + + /// Returns true if this is a loopback address (127.0.0.0/8) + pub fn is_loopback(&self) -> bool { + self.octets[0] == 127 + } + + /// Returns true if this is a private address. + /// + /// The private address ranges are defined in RFC1918 and include: + /// + /// - 10.0.0.0/8 + /// - 172.16.0.0/12 + /// - 192.168.0.0/16 + pub fn is_private(&self) -> bool { + match (self.octets[0], self.octets[1]) { + (10, _) => true, + (172, b) if b >= 16 && b <= 31 => true, + (192, 168) => true, + _ => false + } + } + + /// Returns true if the address is link-local (169.254.0.0/16) + pub fn is_link_local(&self) -> bool { + self.octets[0] == 169 && self.octets[1] == 254 + } + + /// Returns true if the address appears to be globally routable. + /// + /// Non-globally-routable networks include the private networks (10.0.0.0/8, + /// 172.16.0.0/12 and 192.168.0.0/16), the loopback network (127.0.0.0/8), + /// and the link-local network (169.254.0.0/16). + pub fn is_global(&self) -> bool { + !self.is_private() && !self.is_loopback() && !self.is_link_local() + } + + /// Returns true if this is a multicast address. + /// + /// Multicast addresses have a most significant octet between 224 and 239. + pub fn is_multicast(&self) -> bool { + self.octets[0] >= 224 && self.octets[0] <= 239 + } + + /// Convert this address to an IPv4-compatible IPv6 address + /// + /// a.b.c.d becomes ::a.b.c.d + pub fn to_ipv6_compatible(&self) -> Ipv6Addr { + Ipv6Addr::new(0, 0, 0, 0, 0, 0, + ((self.octets[0] as u16) << 8) | self.octets[1] as u16, + ((self.octets[2] as u16) << 8) | self.octets[3] as u16) + } + + /// Convert this address to an IPv4-mapped IPv6 address + /// + /// a.b.c.d becomes ::ffff:a.b.c.d + pub fn to_ipv6_mapped(&self) -> Ipv6Addr { + Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, + ((self.octets[0] as u16) << 8) | self.octets[1] as u16, + ((self.octets[2] as u16) << 8) | self.octets[3] as u16) + } + +} + +impl ToIpAddr for Ipv4Addr { + fn to_ip_addr(&self) -> IpAddr { + IpAddr::V4(*self) + } +} + +impl fmt::Display for Ipv4Addr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}.{}.{}.{}", self.octets[0], self.octets[1], self.octets[2], self.octets[3]) + } +} + +impl Ipv6Addr { + /// Create a new IPv6 address from eight 16-bit segments. + /// + /// The result will represent the IP address a:b:c:d:e:f + pub fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { + Ipv6Addr { + segments: [a, b, c, d, e, f, g, h] + } + } + + /// Return the eight 16-bit segments that make up this address + pub fn segments(&self) -> &[u16; 8] { + &self.segments + } + + /// Returns true for the special 'unspecified' address :: + pub fn is_unspecified(&self) -> bool { + self.segments == [0, 0, 0, 0, 0, 0, 0, 0] + } + + /// Returns true if this is a loopback address (::1) + pub fn is_loopback(&self) -> bool { + self.segments == [0, 0, 0, 0, 0, 0, 0, 1] + } + + /// Returns true if the address appears to be globally routable. + /// + /// Non-globally-routable networks include the loopback address; the link-local, + /// site-local, and unique local unicast addresses; and the interface-, link-, + /// realm-, admin- and site-local multicast addresses. + pub fn is_global(&self) -> bool { + match self.multicast_scope() { + Some(Ipv6MulticastScope::Global) => true, + None => self.is_unicast_global(), + _ => false + } + } + /// Returns true if this is a unique local address (IPv6) + /// + /// Unique local addresses are defined in RFC4193 and have the form fc00::/7 + pub fn is_unique_local(&self) -> bool { + (self.segments[0] & 0xfe00) == 0xfc00 + } + + /// Returns true if the address is unicast and link-local (fe80::/10) + pub fn is_unicast_link_local(&self) -> bool { + (self.segments[0] & 0xffc0) == 0xfe80 + } + + /// Returns true if this is a deprecated unicast site-local address (IPv6 fec0::/10) + pub fn is_unicast_site_local(&self) -> bool { + (self.segments[0] & 0xffc0) == 0xfec0 + } + + /// Returns true if the address is a globally routable unicast address + /// + /// Non-globally-routable unicast addresses include the loopback address, the link-local + /// addresses, the deprecated site-local addresses and the unique local addresses. + pub fn is_unicast_global(&self) -> bool { + !self.is_multicast() + && !self.is_loopback() && !self.is_unicast_link_local() + && !self.is_unicast_site_local() && !self.is_unique_local() + } + + /// Returns the address's multicast scope if the address is multicast. + pub fn multicast_scope(&self) -> Option { + if self.is_multicast() { + match self.segments[0] & 0x000f { + 1 => Some(Ipv6MulticastScope::InterfaceLocal), + 2 => Some(Ipv6MulticastScope::LinkLocal), + 3 => Some(Ipv6MulticastScope::RealmLocal), + 4 => Some(Ipv6MulticastScope::AdminLocal), + 5 => Some(Ipv6MulticastScope::SiteLocal), + 8 => Some(Ipv6MulticastScope::OrganizationLocal), + 14 => Some(Ipv6MulticastScope::Global), + _ => None + } + } else { + None + } + } + + /// Returns true if this is a multicast address. + /// + /// Multicast addresses have the form ff00::/8. + pub fn is_multicast(&self) -> bool { + (self.segments[0] & 0xff00) == 0xff00 + } + + /// Convert this address to an IPv4 address. Returns None if this address is neither + /// IPv4-compatible or IPv4-mapped. + /// + /// ::a.b.c.d and ::ffff:a.b.c.d become a.b.c.d + pub fn to_ipv4(&self) -> Option { + match self.segments { + [0, 0, 0, 0, 0, f, g, h] if f == 0 || f == 0xffff => { + Some(Ipv4Addr::new((g >> 8) as u8, g as u8, + (h >> 8) as u8, h as u8)) + }, + _ => None + } + } +} + +impl ToIpAddr for Ipv6Addr { + fn to_ip_addr(&self) -> IpAddr { + IpAddr::V6(*self) + } +} + +impl fmt::Display for Ipv6Addr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self.segments { + // We need special cases for :: and ::1, otherwise they're formatted as ::0.0.0.[01] + [0, 0, 0, 0, 0, 0, 0, 0] => write!(fmt, "::"), + [0, 0, 0, 0, 0, 0, 0, 1] => write!(fmt, "::1"), // Ipv4 Compatible address - Ipv6Addr(0, 0, 0, 0, 0, 0, g, h) => { + [0, 0, 0, 0, 0, 0, g, h] => { write!(fmt, "::{}.{}.{}.{}", (g >> 8) as u8, g as u8, (h >> 8) as u8, h as u8) } - // Ipv4-Mapped address - Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, g, h) => { - write!(fmt, "::FFFF:{}.{}.{}.{}", (g >> 8) as u8, g as u8, + [0, 0, 0, 0, 0, 0xffff, g, h] => { + write!(fmt, "::ffff:{}.{}.{}.{}", (g >> 8) as u8, g as u8, (h >> 8) as u8, h as u8) - } + }, + _ => { + fn find_zero_slice(segments: &[u16; 8]) -> (usize, usize) { + let mut longest_span_len = 0; + let mut longest_span_at = 0; + let mut cur_span_len = 0; + let mut cur_span_at = 0; + + for i in range(0u, 8) { + if segments[i] == 0 { + if cur_span_len == 0 { + cur_span_at = i; + } + + cur_span_len += 1; + + if cur_span_len > longest_span_len { + longest_span_len = cur_span_len; + longest_span_at = cur_span_at; + } + } else { + cur_span_len = 0; + cur_span_at = 0; + } + } + + (longest_span_at, longest_span_len) + } + + let (zeros_at, zeros_len) = find_zero_slice(&self.segments); - Ipv6Addr(a, b, c, d, e, f, g, h) => - write!(fmt, "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", - a, b, c, d, e, f, g, h) + if zeros_len > 1 { + fn fmt_subslice(segments: &[u16]) -> String { + segments + .iter() + .map(|&seg| format!("{:x}", seg)) + .collect::>() + .as_slice() + .connect(":") + } + + write!(fmt, "{}::{}", + fmt_subslice(self.segments.slice_to(zeros_at)), + fmt_subslice(self.segments.slice_from(zeros_at + zeros_len))) + } else { + let &[a, b, c, d, e, f, g, h] = &self.segments; + write!(fmt, "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", + a, b, c, d, e, f, g, h) + } + } } } } @@ -74,8 +378,8 @@ pub struct SocketAddr { impl fmt::Display for SocketAddr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.ip { - Ipv4Addr(..) => write!(f, "{}:{}", self.ip, self.port), - Ipv6Addr(..) => write!(f, "[{}]:{}", self.ip, self.port), + IpAddr::V4(_) => write!(f, "{}:{}", self.ip, self.port), + IpAddr::V6(_) => write!(f, "[{}]:{}", self.ip, self.port), } } } @@ -225,7 +529,7 @@ impl<'a> Parser<'a> { self.read_atomically(|p| p.read_number_impl(radix, max_digits, upto)) } - fn read_ipv4_addr_impl(&mut self) -> Option { + fn read_ipv4_addr_impl(&mut self) -> Option { let mut bs = [0u8; 4]; let mut i = 0; while i < 4 { @@ -240,21 +544,21 @@ impl<'a> Parser<'a> { }; i += 1; } - Some(Ipv4Addr(bs[0], bs[1], bs[2], bs[3])) + Some(Ipv4Addr::new(bs[0], bs[1], bs[2], bs[3])) } // Read IPv4 address - fn read_ipv4_addr(&mut self) -> Option { + fn read_ipv4_addr(&mut self) -> Option { self.read_atomically(|p| p.read_ipv4_addr_impl()) } - fn read_ipv6_addr_impl(&mut self) -> Option { - fn ipv6_addr_from_head_tail(head: &[u16], tail: &[u16]) -> IpAddr { + fn read_ipv6_addr_impl(&mut self) -> Option { + fn ipv6_addr_from_head_tail(head: &[u16], tail: &[u16]) -> Ipv6Addr { assert!(head.len() + tail.len() <= 8); let mut gs = [0u16; 8]; gs.clone_from_slice(head); gs[(8 - tail.len()) .. 8].clone_from_slice(tail); - Ipv6Addr(gs[0], gs[1], gs[2], gs[3], gs[4], gs[5], gs[6], gs[7]) + Ipv6Addr::new(gs[0], gs[1], gs[2], gs[3], gs[4], gs[5], gs[6], gs[7]) } fn read_groups(p: &mut Parser, groups: &mut [u16; 8], limit: uint) -> (uint, bool) { @@ -268,13 +572,11 @@ impl<'a> Parser<'a> { None } }); - match ipv4 { - Some(Ipv4Addr(a, b, c, d)) => { - groups[i + 0] = ((a as u16) << 8) | (b as u16); - groups[i + 1] = ((c as u16) << 8) | (d as u16); - return (i + 2, true); - } - _ => {} + if let Some(v4_addr) = ipv4 { + let octets = v4_addr.octets(); + groups[i + 0] = ((octets[0] as u16) << 8) | (octets[1] as u16); + groups[i + 1] = ((octets[2] as u16) << 8) | (octets[3] as u16); + return (i + 2, true); } } @@ -298,7 +600,7 @@ impl<'a> Parser<'a> { let (head_size, head_ipv4) = read_groups(self, &mut head, 8); if head_size == 8 { - return Some(Ipv6Addr( + return Some(Ipv6Addr::new( head[0], head[1], head[2], head[3], head[4], head[5], head[6], head[7])) } @@ -318,13 +620,13 @@ impl<'a> Parser<'a> { Some(ipv6_addr_from_head_tail(&head[..head_size], &tail[..tail_size])) } - fn read_ipv6_addr(&mut self) -> Option { + fn read_ipv6_addr(&mut self) -> Option { self.read_atomically(|p| p.read_ipv6_addr_impl()) } fn read_ip_addr(&mut self) -> Option { - let ipv4_addr = |&mut: p: &mut Parser| p.read_ipv4_addr(); - let ipv6_addr = |&mut: p: &mut Parser| p.read_ipv6_addr(); + let ipv4_addr = |&mut: p: &mut Parser| p.read_ipv4_addr().map(|v4| IpAddr::V4(v4)); + let ipv6_addr = |&mut: p: &mut Parser| p.read_ipv6_addr().map(|v6| IpAddr::V6(v6)); self.read_or(&mut [box ipv4_addr, box ipv6_addr]) } @@ -335,8 +637,8 @@ impl<'a> Parser<'a> { let open_br = |&: p: &mut Parser| p.read_given_char('['); let ip_addr = |&: p: &mut Parser| p.read_ipv6_addr(); let clos_br = |&: p: &mut Parser| p.read_given_char(']'); - p.read_seq_3::(open_br, ip_addr, clos_br) - .map(|t| match t { (_, ip, _) => ip }) + p.read_seq_3::(open_br, ip_addr, clos_br) + .map(|t| match t { (_, ip, _) => IpAddr::V6(ip) }) }; p.read_or(&mut [box ipv4_p, box ipv6_p]) }; @@ -359,6 +661,26 @@ impl FromStr for IpAddr { } } +impl FromStr for Ipv4Addr { + type Err = ParseError; + fn from_str(s: &str) -> Result { + match Parser::new(s).read_till_eof(|p| p.read_ipv4_addr()) { + Some(s) => Ok(s), + None => Err(ParseError) + } + } +} + +impl FromStr for Ipv6Addr { + type Err = ParseError; + fn from_str(s: &str) -> Result { + match Parser::new(s).read_till_eof(|p| p.read_ipv6_addr()) { + Some(s) => Ok(s), + None => Err(ParseError) + } + } +} + impl FromStr for SocketAddr { type Err = ParseError; fn from_str(s: &str) -> Result { @@ -560,13 +882,13 @@ impl<'a> ToSocketAddr for &'a str { mod test { use prelude::v1::*; use super::*; - use str::FromStr; + use super::Ipv6MulticastScope::*; #[test] fn test_from_str_ipv4() { - assert_eq!(Ok(Ipv4Addr(127, 0, 0, 1)), "127.0.0.1".parse()); - assert_eq!(Ok(Ipv4Addr(255, 255, 255, 255)), "255.255.255.255".parse()); - assert_eq!(Ok(Ipv4Addr(0, 0, 0, 0)), "0.0.0.0".parse()); + assert_eq!(Ok(Ipv4Addr::new(127, 0, 0, 1)), "127.0.0.1".parse()); + assert_eq!(Ok(Ipv4Addr::new(255, 255, 255, 255)), "255.255.255.255".parse()); + assert_eq!(Ok(Ipv4Addr::new(0, 0, 0, 0)), "0.0.0.0".parse()); // out of range let none: Option = "256.0.0.1".parse().ok(); @@ -584,13 +906,13 @@ mod test { #[test] fn test_from_str_ipv6() { - assert_eq!(Ok(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), "0:0:0:0:0:0:0:0".parse()); - assert_eq!(Ok(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), "0:0:0:0:0:0:0:1".parse()); + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "0:0:0:0:0:0:0:0".parse()); + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "0:0:0:0:0:0:0:1".parse()); - assert_eq!(Ok(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), "::1".parse()); - assert_eq!(Ok(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), "::".parse()); + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "::1".parse()); + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "::".parse()); - assert_eq!(Ok(Ipv6Addr(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)), + assert_eq!(Ok(Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)), "2a02:6b8::11:11".parse()); // too long group @@ -612,13 +934,13 @@ mod test { #[test] fn test_from_str_ipv4_in_ipv6() { - assert_eq!(Ok(Ipv6Addr(0, 0, 0, 0, 0, 0, 49152, 545)), + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 545)), "::192.0.2.33".parse()); - assert_eq!(Ok(Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)), + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)), "::FFFF:192.0.2.33".parse()); - assert_eq!(Ok(Ipv6Addr(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)), + assert_eq!(Ok(Ipv6Addr::new(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)), "64:ff9b::192.0.2.33".parse()); - assert_eq!(Ok(Ipv6Addr(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)), + assert_eq!(Ok(Ipv6Addr::new(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)), "2001:db8:122:c000:2:2100:192.0.2.33".parse()); // colon after v4 @@ -634,11 +956,11 @@ mod test { #[test] fn test_from_str_socket_addr() { - assert_eq!(Ok(SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 80 }), + assert_eq!(Ok(SocketAddr { ip: IpAddr::new_v4(77, 88, 21, 11), port: 80 }), "77.88.21.11:80".parse()); - assert_eq!(Ok(SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }), + assert_eq!(Ok(SocketAddr { ip: IpAddr::new_v6(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }), "[2a02:6b8:0:1::1]:53".parse()); - assert_eq!(Ok(SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0x7F00, 1), port: 22 }), + assert_eq!(Ok(SocketAddr { ip: IpAddr::new_v6(0, 0, 0, 0, 0, 0, 0x7F00, 1), port: 22 }), "[::127.0.0.1]:22".parse()); // without port @@ -657,23 +979,157 @@ mod test { #[test] fn ipv6_addr_to_string() { - let a1 = Ipv6Addr(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280); - assert!(a1.to_string() == "::ffff:192.0.2.128" || - a1.to_string() == "::FFFF:192.0.2.128"); - assert_eq!(Ipv6Addr(8, 9, 10, 11, 12, 13, 14, 15).to_string(), + // ipv4-mapped address + let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280); + assert_eq!(a1.to_string(), "::ffff:192.0.2.128"); + + // ipv4-compatible address + let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280); + assert_eq!(a1.to_string(), "::192.0.2.128"); + + // v6 address with no zero segments + assert_eq!(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15).to_string(), "8:9:a:b:c:d:e:f"); + + // reduce a single run of zeros + assert_eq!("ae::ffff:102:304", + Ipv6Addr::new(0xae, 0, 0, 0, 0, 0xffff, 0x0102, 0x0304).to_string()); + + // don't reduce just a single zero segment + assert_eq!("1:2:3:4:5:6:0:8", + Ipv6Addr::new(1, 2, 3, 4, 5, 6, 0, 8).to_string()); + + // 'any' address + assert_eq!("::", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).to_string()); + + // loopback address + assert_eq!("::1", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_string()); + + // ends in zeros + assert_eq!("1::", Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0).to_string()); + + // two runs of zeros, second one is longer + assert_eq!("1:0:0:4::8", Ipv6Addr::new(1, 0, 0, 4, 0, 0, 0, 8).to_string()); + + // two runs of zeros, equal length + assert_eq!("1::4:5:0:0:8", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8).to_string()); + } + + #[test] + fn ipv4_to_ipv6() { + assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678), + Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_mapped()); + assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678), + Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_compatible()); + } + + #[test] + fn ipv6_to_ipv4() { + assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4(), + Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))); + assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), + Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))); + assert_eq!(Ipv6Addr::new(0, 0, 1, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), + None); + } + + #[test] + fn ipv4_properties() { + fn check(octets: &[u8; 4], unspec: bool, loopback: bool, + private: bool, link_local: bool, global: bool, + multicast: bool) { + println!("testing IPv4 address {:?}", octets); + let ip = Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]); + assert_eq!(octets, ip.octets()); + + assert_eq!(ip.is_unspecified(), unspec); + assert_eq!(ip.is_loopback(), loopback); + assert_eq!(ip.is_private(), private); + assert_eq!(ip.is_link_local(), link_local); + assert_eq!(ip.is_global(), global); + assert_eq!(ip.is_multicast(), multicast); + } + + // address unspec loopbk privt linloc global multicast + check(&[0, 0, 0, 0], true, false, false, false, true, false); + check(&[0, 0, 0, 1], false, false, false, false, true, false); + check(&[1, 0, 0, 0], false, false, false, false, true, false); + check(&[10, 9, 8, 7], false, false, true, false, false, false); + check(&[127, 1, 2, 3], false, true, false, false, false, false); + check(&[172, 31, 254, 253], false, false, true, false, false, false); + check(&[169, 254, 253, 242], false, false, false, true, false, false); + check(&[192, 168, 254, 253], false, false, true, false, false, false); + check(&[224, 0, 0, 0], false, false, false, false, true, true); + check(&[239, 255, 255, 255], false, false, false, false, true, true); + check(&[255, 255, 255, 255], false, false, false, false, true, false); + } + + #[test] + fn ipv6_properties() { + fn check(str_addr: &str, unspec: bool, loopback: bool, + unique_local: bool, global: bool, + u_link_local: bool, u_site_local: bool, u_global: bool, + m_scope: Option) { + println!("testing IPv6 address {:?}", str_addr); + let ip: Ipv6Addr = str_addr.parse().ok().unwrap(); + assert_eq!(str_addr, ip.to_string().as_slice()); + + assert_eq!(ip.is_unspecified(), unspec); + assert_eq!(ip.is_loopback(), loopback); + assert_eq!(ip.is_unique_local(), unique_local); + assert_eq!(ip.is_global(), global); + assert_eq!(ip.is_unicast_link_local(), u_link_local); + assert_eq!(ip.is_unicast_site_local(), u_site_local); + assert_eq!(ip.is_unicast_global(), u_global); + assert_eq!(ip.multicast_scope(), m_scope); + assert_eq!(ip.is_multicast(), m_scope.is_some()); + } + + // unspec loopbk uniqlo global unill unisl uniglo mscope + check("::", + true, false, false, true, false, false, true, None); + check("::1", + false, true, false, false, false, false, false, None); + check("::0.0.0.2", + false, false, false, true, false, false, true, None); + check("1::", + false, false, false, true, false, false, true, None); + check("fc00::", + false, false, true, false, false, false, false, None); + check("fdff:ffff::", + false, false, true, false, false, false, false, None); + check("fe80:ffff::", + false, false, false, false, true, false, false, None); + check("febf:ffff::", + false, false, false, false, true, false, false, None); + check("fec0::", + false, false, false, false, false, true, false, None); + check("ff01::", + false, false, false, false, false, false, false, Some(InterfaceLocal)); + check("ff02::", + false, false, false, false, false, false, false, Some(LinkLocal)); + check("ff03::", + false, false, false, false, false, false, false, Some(RealmLocal)); + check("ff04::", + false, false, false, false, false, false, false, Some(AdminLocal)); + check("ff05::", + false, false, false, false, false, false, false, Some(SiteLocal)); + check("ff08::", + false, false, false, false, false, false, false, Some(OrganizationLocal)); + check("ff0e::", + false, false, false, true, false, false, false, Some(Global)); } #[test] fn to_socket_addr_socketaddr() { - let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 12345 }; + let a = SocketAddr { ip: IpAddr::new_v4(77, 88, 21, 11), port: 12345 }; assert_eq!(Ok(a), a.to_socket_addr()); assert_eq!(Ok(vec![a]), a.to_socket_addr_all()); } #[test] fn to_socket_addr_ipaddr_u16() { - let a = Ipv4Addr(77, 88, 21, 11); + let a = IpAddr::new_v4(77, 88, 21, 11); let p = 12345u16; let e = SocketAddr { ip: a, port: p }; assert_eq!(Ok(e), (a, p).to_socket_addr()); @@ -682,29 +1138,29 @@ mod test { #[test] fn to_socket_addr_str_u16() { - let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 }; + let a = SocketAddr { ip: IpAddr::new_v4(77, 88, 21, 11), port: 24352 }; assert_eq!(Ok(a), ("77.88.21.11", 24352u16).to_socket_addr()); assert_eq!(Ok(vec![a]), ("77.88.21.11", 24352u16).to_socket_addr_all()); - let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }; + let a = SocketAddr { ip: IpAddr::new_v6(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }; assert_eq!(Ok(a), ("2a02:6b8:0:1::1", 53).to_socket_addr()); assert_eq!(Ok(vec![a]), ("2a02:6b8:0:1::1", 53).to_socket_addr_all()); - let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 }; + let a = SocketAddr { ip: IpAddr::new_v4(127, 0, 0, 1), port: 23924 }; assert!(("localhost", 23924u16).to_socket_addr_all().unwrap().contains(&a)); } #[test] fn to_socket_addr_str() { - let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 }; + let a = SocketAddr { ip: IpAddr::new_v4(77, 88, 21, 11), port: 24352 }; assert_eq!(Ok(a), "77.88.21.11:24352".to_socket_addr()); assert_eq!(Ok(vec![a]), "77.88.21.11:24352".to_socket_addr_all()); - let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }; + let a = SocketAddr { ip: IpAddr::new_v6(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }; assert_eq!(Ok(a), "[2a02:6b8:0:1::1]:53".to_socket_addr()); assert_eq!(Ok(vec![a]), "[2a02:6b8:0:1::1]:53".to_socket_addr_all()); - let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 }; + let a = SocketAddr { ip: IpAddr::new_v4(127, 0, 0, 1), port: 23924 }; assert!("localhost:23924".to_socket_addr_all().unwrap().contains(&a)); } } diff --git a/src/libstd/old_io/net/udp.rs b/src/libstd/old_io/net/udp.rs index 5f1089bc63b99..d45d9c9704ce0 100644 --- a/src/libstd/old_io/net/udp.rs +++ b/src/libstd/old_io/net/udp.rs @@ -193,7 +193,7 @@ mod test { #[cfg_attr(any(windows, target_os = "android"), ignore)] #[test] fn bind_error() { - let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 }; + let addr = SocketAddr { ip: IpAddr::new_v4(0, 0, 0, 0), port: 1 }; match UdpSocket::bind(addr) { Ok(..) => panic!(), Err(e) => assert_eq!(e.kind, PermissionDenied), diff --git a/src/libstd/old_io/test.rs b/src/libstd/old_io/test.rs index f49e2397d4282..0ec0b572a45d2 100644 --- a/src/libstd/old_io/test.rs +++ b/src/libstd/old_io/test.rs @@ -55,12 +55,12 @@ pub fn next_test_unix() -> Path { /// Get a unique IPv4 localhost:port pair starting at 9600 pub fn next_test_ip4() -> SocketAddr { - SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: next_test_port() } + SocketAddr { ip: IpAddr::new_v4(127, 0, 0, 1), port: next_test_port() } } /// Get a unique IPv6 localhost:port pair starting at 9600 pub fn next_test_ip6() -> SocketAddr { - SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1), port: next_test_port() } + SocketAddr { ip: IpAddr::new_v6(0, 0, 0, 0, 0, 0, 0, 1), port: next_test_port() } } /* diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index 51b6e0a1c1e12..53fde75b122c6 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -15,7 +15,7 @@ use self::InAddr::*; use ffi::CString; use ffi; use old_io::net::addrinfo; -use old_io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr}; +use old_io::net::ip::{SocketAddr, IpAddr}; use old_io::{IoResult, IoError}; use libc::{self, c_char, c_int}; use mem; @@ -56,7 +56,8 @@ pub enum InAddr { pub fn ip_to_inaddr(ip: IpAddr) -> InAddr { match ip { - Ipv4Addr(a, b, c, d) => { + IpAddr::V4(v4) => { + let &[a, b, c, d] = v4.octets(); let ip = ((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | @@ -65,7 +66,8 @@ pub fn ip_to_inaddr(ip: IpAddr) -> InAddr { s_addr: Int::from_be(ip) }) } - Ipv6Addr(a, b, c, d, e, f, g, h) => { + IpAddr::V6(v6) => { + let &[a, b, c, d, e, f, g, h] = v6.segments(); In6Addr(libc::in6_addr { s6_addr: [ htons(a), @@ -109,8 +111,8 @@ pub fn addr_to_sockaddr(addr: SocketAddr, pub fn socket(addr: SocketAddr, ty: libc::c_int) -> IoResult { unsafe { let fam = match addr.ip { - Ipv4Addr(..) => libc::AF_INET, - Ipv6Addr(..) => libc::AF_INET6, + IpAddr::V4(..) => libc::AF_INET, + IpAddr::V6(..) => libc::AF_INET6, }; match libc::socket(fam, ty, 0) { -1 => Err(last_net_error()), @@ -184,7 +186,7 @@ pub fn sockaddr_to_addr(storage: &libc::sockaddr_storage, let c = (ip >> 8) as u8; let d = (ip >> 0) as u8; Ok(SocketAddr { - ip: Ipv4Addr(a, b, c, d), + ip: IpAddr::new_v4(a, b, c, d), port: ntohs(storage.sin_port), }) } @@ -202,7 +204,7 @@ pub fn sockaddr_to_addr(storage: &libc::sockaddr_storage, let g = ntohs(storage.sin6_addr.s6_addr[6]); let h = ntohs(storage.sin6_addr.s6_addr[7]); Ok(SocketAddr { - ip: Ipv6Addr(a, b, c, d, e, f, g, h), + ip: IpAddr::new_v6(a, b, c, d, e, f, g, h), port: ntohs(storage.sin6_port), }) } @@ -907,20 +909,20 @@ impl UdpSocket { pub fn join_multicast(&mut self, multi: IpAddr) -> IoResult<()> { match multi { - Ipv4Addr(..) => { + IpAddr::V4(..) => { self.set_membership(multi, libc::IP_ADD_MEMBERSHIP) } - Ipv6Addr(..) => { + IpAddr::V6(..) => { self.set_membership(multi, libc::IPV6_ADD_MEMBERSHIP) } } } pub fn leave_multicast(&mut self, multi: IpAddr) -> IoResult<()> { match multi { - Ipv4Addr(..) => { + IpAddr::V4(..) => { self.set_membership(multi, libc::IP_DROP_MEMBERSHIP) } - Ipv6Addr(..) => { + IpAddr::V6(..) => { self.set_membership(multi, libc::IPV6_DROP_MEMBERSHIP) } }