From 395709ca6d39ba1e095e404e1d2a169d918b7f0c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 5 Feb 2015 16:50:11 -0800 Subject: [PATCH] std: Add a `net` module for TCP/UDP This commit is an implementation of [RFC 807][rfc] which adds a `std::net` module for basic neworking based on top of `std::io`. This module serves as a replacement for the `std::old_io::net` module and networking primitives in `old_io`. [rfc]: fillmein The major focus of this redesign is to cut back on the level of abstraction to the point that each of the networking types is just a bare socket. To this end functionality such as timeouts and cloning has been removed (although cloning can be done through `duplicate`, it may just yield an error). With this `net` module comes a new implementation of `SocketAddr` and `IpAddr`. This work is entirely based on #20785 and the only changes were to alter the in-memory representation to match the `libc`-expected variants and to move from public fields to accessors. --- src/libstd/lib.rs | 1 + src/libstd/net/addr.rs | 592 +++++++++++++++++++++++++ src/libstd/net/ip.rs | 449 +++++++++++++++++++ src/libstd/net/mod.rs | 99 +++++ src/libstd/net/parser.rs | 330 ++++++++++++++ src/libstd/net/tcp.rs | 792 ++++++++++++++++++++++++++++++++++ src/libstd/net/test.rs | 39 ++ src/libstd/net/udp.rs | 291 +++++++++++++ src/libstd/sys/common/mod.rs | 1 + src/libstd/sys/common/net2.rs | 393 +++++++++++++++++ src/libstd/sys/unix/c.rs | 1 + src/libstd/sys/unix/ext.rs | 14 +- src/libstd/sys/unix/fd.rs | 7 +- src/libstd/sys/unix/mod.rs | 4 +- src/libstd/sys/unix/net.rs | 74 ++++ src/libstd/sys/windows/ext.rs | 11 + src/libstd/sys/windows/mod.rs | 1 + src/libstd/sys/windows/net.rs | 121 ++++++ 18 files changed, 3214 insertions(+), 6 deletions(-) create mode 100644 src/libstd/net/addr.rs create mode 100644 src/libstd/net/ip.rs create mode 100644 src/libstd/net/mod.rs create mode 100644 src/libstd/net/parser.rs create mode 100644 src/libstd/net/tcp.rs create mode 100644 src/libstd/net/test.rs create mode 100644 src/libstd/net/udp.rs create mode 100644 src/libstd/sys/common/net2.rs create mode 100644 src/libstd/sys/unix/net.rs create mode 100644 src/libstd/sys/windows/net.rs diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 967789dd41144..9b7c65bb71999 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -250,6 +250,7 @@ pub mod ffi; pub mod old_io; pub mod io; pub mod fs; +pub mod net; pub mod os; pub mod env; pub mod path; diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs new file mode 100644 index 0000000000000..66d4d34f8eb54 --- /dev/null +++ b/src/libstd/net/addr.rs @@ -0,0 +1,592 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use fmt; +use hash; +use io; +use libc::{self, socklen_t, sa_family_t}; +use mem; +use net::{IpAddr, lookup_host, ntoh, hton}; +use option; +use sys_common::{FromInner, AsInner, IntoInner}; +use vec; + +/// Representation of a socket address for networking applications +/// +/// A socket address consists of at least an (ip, port) pair and may also +/// contain other information depending on the protocol. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct SocketAddr { + repr: Repr, +} + +#[derive(Copy)] +enum Repr { + V4(libc::sockaddr_in), + V6(libc::sockaddr_in6), +} + +impl SocketAddr { + /// Creates a new socket address from the (ip, port) pair. + pub fn new(ip: IpAddr, port: u16) -> SocketAddr { + let repr = match ip { + IpAddr::V4(ref ip) => { + Repr::V4(libc::sockaddr_in { + sin_family: libc::AF_INET as sa_family_t, + sin_port: hton(port), + sin_addr: *ip.as_inner(), + .. unsafe { mem::zeroed() } + }) + } + IpAddr::V6(ref ip) => { + Repr::V6(libc::sockaddr_in6 { + sin6_family: libc::AF_INET6 as sa_family_t, + sin6_port: hton(port), + sin6_addr: *ip.as_inner(), + .. unsafe { mem::zeroed() } + }) + } + }; + SocketAddr { repr: repr } + } + + /// Gets the IP address associated with this socket address. + pub fn ip(&self) -> IpAddr { + match self.repr { + Repr::V4(ref sa) => IpAddr::V4(FromInner::from_inner(sa.sin_addr)), + Repr::V6(ref sa) => IpAddr::V6(FromInner::from_inner(sa.sin6_addr)), + } + } + + /// Gets the port number associated with this socket address + pub fn port(&self) -> u16 { + match self.repr { + Repr::V4(ref sa) => ntoh(sa.sin_port), + Repr::V6(ref sa) => ntoh(sa.sin6_port), + } + } + + fn set_port(&mut self, port: u16) { + match self.repr { + Repr::V4(ref mut sa) => sa.sin_port = hton(port), + Repr::V6(ref mut sa) => sa.sin6_port = hton(port), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for SocketAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.repr { + Repr::V4(_) => write!(f, "{}:{}", self.ip(), self.port()), + Repr::V6(_) => write!(f, "[{}]:{}", self.ip(), self.port()), + } + } +} + +impl FromInner for SocketAddr { + fn from_inner(addr: libc::sockaddr_in) -> SocketAddr { + SocketAddr { repr: Repr::V4(addr) } + } +} + +impl FromInner for SocketAddr { + fn from_inner(addr: libc::sockaddr_in6) -> SocketAddr { + SocketAddr { repr: Repr::V6(addr) } + } +} + +impl<'a> IntoInner<(*const libc::sockaddr, socklen_t)> for &'a SocketAddr { + fn into_inner(self) -> (*const libc::sockaddr, socklen_t) { + match self.repr { + Repr::V4(ref a) => { + (a as *const _ as *const _, mem::size_of_val(a) as socklen_t) + } + Repr::V6(ref a) => { + (a as *const _ as *const _, mem::size_of_val(a) as socklen_t) + } + } + } +} + +impl fmt::Debug for SocketAddr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +impl Clone for Repr { + fn clone(&self) -> Repr { *self } +} + +impl PartialEq for Repr { + fn eq(&self, other: &Repr) -> bool { + match (*self, *other) { + (Repr::V4(ref a), Repr::V4(ref b)) => { + a.sin_port == b.sin_port && + a.sin_addr.s_addr == b.sin_addr.s_addr + } + (Repr::V6(ref a), Repr::V6(ref b)) => { + a.sin6_port == b.sin6_port && + a.sin6_addr.s6_addr == b.sin6_addr.s6_addr && + a.sin6_flowinfo == b.sin6_flowinfo && + a.sin6_scope_id == b.sin6_scope_id + } + _ => false, + } + } +} +impl Eq for Repr {} + +impl hash::Hash for Repr { + fn hash(&self, s: &mut S) { + match *self { + Repr::V4(ref a) => { + (a.sin_family, a.sin_port, a.sin_addr.s_addr).hash(s) + } + Repr::V6(ref a) => { + (a.sin6_family, a.sin6_port, &a.sin6_addr.s6_addr, + a.sin6_flowinfo, a.sin6_scope_id).hash(s) + } + } + } +} + +/// A trait for objects which can be converted or resolved to one or more +/// `SocketAddr` values. +/// +/// This trait is used for generic address resolution when constructing network +/// objects. By default it is implemented for the following types: +/// +/// * `SocketAddr` - `to_socket_addrs` is identity function. +/// +/// * `(IpAddr, u16)` - `to_socket_addrs` constructs `SocketAddr` trivially. +/// +/// * `(&str, u16)` - the string should be either a string representation of an +/// IP address expected by `FromStr` implementation for `IpAddr` or a host +/// name. +/// +/// * `&str` - the string should be either a string representation of a +/// `SocketAddr` as expected by its `FromStr` implementation or a string like +/// `:` pair where `` is a `u16` value. +/// +/// This trait allows constructing network objects like `TcpStream` or +/// `UdpSocket` easily with values of various types for the bind/connection +/// address. It is needed because sometimes one type is more appropriate than +/// the other: for simple uses a string like `"localhost:12345"` is much nicer +/// than manual construction of the corresponding `SocketAddr`, but sometimes +/// `SocketAddr` value is *the* main source of the address, and converting it to +/// some other type (e.g. a string) just for it to be converted back to +/// `SocketAddr` in constructor methods is pointless. +/// +/// Some examples: +/// +/// ```no_run +/// use std::net::{IpAddr, SocketAddr, TcpStream, UdpSocket, TcpListener}; +/// +/// fn main() { +/// let ip = IpAddr::new_v4(127, 0, 0, 1); +/// let port = 12345; +/// +/// // The following lines are equivalent modulo possible "localhost" name +/// // resolution differences +/// let tcp_s = TcpStream::connect(&SocketAddr::new(ip, port)); +/// let tcp_s = TcpStream::connect(&(ip, port)); +/// let tcp_s = TcpStream::connect(&("127.0.0.1", port)); +/// let tcp_s = TcpStream::connect(&("localhost", port)); +/// let tcp_s = TcpStream::connect("127.0.0.1:12345"); +/// let tcp_s = TcpStream::connect("localhost:12345"); +/// +/// // TcpListener::bind(), UdpSocket::bind() and UdpSocket::send_to() +/// // behave similarly +/// let tcp_l = TcpListener::bind("localhost:12345"); +/// +/// let mut udp_s = UdpSocket::bind(&("127.0.0.1", port)).unwrap(); +/// udp_s.send_to(&[7], &(ip, 23451)); +/// } +/// ``` +pub trait ToSocketAddrs { + /// Returned iterator over socket addresses which this type may correspond + /// to. + type Iter: Iterator; + + /// Converts this object to an iterator of resolved `SocketAddr`s. + /// + /// The returned iterator may not actually yield any values depending on the + /// outcome of any resolution performed. + /// + /// Note that this function may block the current thread while resolution is + /// performed. + /// + /// # Errors + /// + /// Any errors encountered during resolution will be returned as an `Err`. + fn to_socket_addrs(&self) -> io::Result; +} + +impl ToSocketAddrs for SocketAddr { + type Iter = option::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + Ok(Some(*self).into_iter()) + } +} + +impl ToSocketAddrs for (IpAddr, u16) { + type Iter = option::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + let (ip, port) = *self; + Ok(Some(SocketAddr::new(ip, port)).into_iter()) + } +} + +fn resolve_socket_addr(s: &str, p: u16) -> io::Result> { + let ips = try!(lookup_host(s)); + let v: Vec<_> = try!(ips.map(|a| { + a.map(|mut a| { a.set_port(p); a }) + }).collect()); + Ok(v.into_iter()) +} + +impl<'a> ToSocketAddrs for (&'a str, u16) { + type Iter = vec::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + let (host, port) = *self; + + // try to parse the host as a regular IpAddr first + match host.parse().ok() { + Some(addr) => return Ok(vec![SocketAddr::new(addr, port)].into_iter()), + None => {} + } + + resolve_socket_addr(host, port) + } +} + +// accepts strings like 'localhost:12345' +impl ToSocketAddrs for str { + type Iter = vec::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + // try to parse as a regular SocketAddr first + match self.parse().ok() { + Some(addr) => return Ok(vec![addr].into_iter()), + None => {} + } + + macro_rules! try_opt { + ($e:expr, $msg:expr) => ( + match $e { + Some(r) => r, + None => return Err(io::Error::new(io::ErrorKind::InvalidInput, + $msg, None)), + } + ) + } + + // split the string by ':' and convert the second part to u16 + let mut parts_iter = self.rsplitn(2, ':'); + let port_str = try_opt!(parts_iter.next(), "invalid socket address"); + let host = try_opt!(parts_iter.next(), "invalid socket address"); + let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); + resolve_socket_addr(host, port) + } +} + +impl<'a, T: ToSocketAddrs + ?Sized> ToSocketAddrs for &'a T { + type Iter = T::Iter; + fn to_socket_addrs(&self) -> io::Result { + (**self).to_socket_addrs() + } +} + +#[cfg(test)] +mod tests { + use prelude::v1::*; + use io; + use net::*; + use net::Ipv6MulticastScope::*; + + #[test] + fn test_from_str_ipv4() { + 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(); + assert_eq!(None, none); + // too short + let none: Option = "255.0.0".parse().ok(); + assert_eq!(None, none); + // too long + let none: Option = "255.0.0.1.2".parse().ok(); + assert_eq!(None, none); + // no number between dots + let none: Option = "255.0..1".parse().ok(); + assert_eq!(None, none); + } + + #[test] + fn test_from_str_ipv6() { + 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::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::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)), + "2a02:6b8::11:11".parse()); + + // too long group + let none: Option = "::00000".parse().ok(); + assert_eq!(None, none); + // too short + let none: Option = "1:2:3:4:5:6:7".parse().ok(); + assert_eq!(None, none); + // too long + let none: Option = "1:2:3:4:5:6:7:8:9".parse().ok(); + assert_eq!(None, none); + // triple colon + let none: Option = "1:2:::6:7:8".parse().ok(); + assert_eq!(None, none); + // two double colons + let none: Option = "1:2::6::8".parse().ok(); + assert_eq!(None, none); + } + + #[test] + fn test_from_str_ipv4_in_ipv6() { + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 545)), + "::192.0.2.33".parse()); + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)), + "::FFFF:192.0.2.33".parse()); + assert_eq!(Ok(Ipv6Addr::new(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)), + "64:ff9b::192.0.2.33".parse()); + 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 + let none: Option = "::127.0.0.1:".parse().ok(); + assert_eq!(None, none); + // not enough groups + let none: Option = "1.2.3.4.5:127.0.0.1".parse().ok(); + assert_eq!(None, none); + // too many groups + let none: Option = "1.2.3.4.5:6:7:127.0.0.1".parse().ok(); + assert_eq!(None, none); + } + + #[test] + fn test_from_str_socket_addr() { + assert_eq!(Ok(SocketAddr::new(IpAddr::new_v4(77, 88, 21, 11), 80)), + "77.88.21.11:80".parse()); + assert_eq!(Ok(SocketAddr::new(IpAddr::new_v6(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53)), + "[2a02:6b8:0:1::1]:53".parse()); + assert_eq!(Ok(SocketAddr::new(IpAddr::new_v6(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22)), + "[::127.0.0.1]:22".parse()); + + // without port + let none: Option = "127.0.0.1".parse().ok(); + assert_eq!(None, none); + // without port + let none: Option = "127.0.0.1:".parse().ok(); + assert_eq!(None, none); + // wrong brackets around v4 + let none: Option = "[127.0.0.1]:22".parse().ok(); + assert_eq!(None, none); + // port out of range + let none: Option = "127.0.0.1:123456".parse().ok(); + assert_eq!(None, none); + } + + #[test] + fn ipv6_addr_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) { + 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) { + let ip: Ipv6Addr = str_addr.parse().ok().unwrap(); + assert_eq!(str_addr, ip.to_string()); + + 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)); + } + + fn tsa(a: A) -> io::Result> { + Ok(try!(a.to_socket_addrs()).collect()) + } + + #[test] + fn to_socket_addr_socketaddr() { + let a = SocketAddr::new(IpAddr::new_v4(77, 88, 21, 11), 12345); + assert_eq!(Ok(vec![a]), tsa(a)); + } + + #[test] + fn to_socket_addr_ipaddr_u16() { + let a = IpAddr::new_v4(77, 88, 21, 11); + let p = 12345u16; + let e = SocketAddr::new(a, p); + assert_eq!(Ok(vec![e]), tsa((a, p))); + } + + #[test] + fn to_socket_addr_str_u16() { + let a = SocketAddr::new(IpAddr::new_v4(77, 88, 21, 11), 24352); + assert_eq!(Ok(vec![a]), tsa(("77.88.21.11", 24352u16))); + + let a = SocketAddr::new(IpAddr::new_v6(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53); + assert_eq!(Ok(vec![a]), tsa(("2a02:6b8:0:1::1", 53))); + + let a = SocketAddr::new(IpAddr::new_v4(127, 0, 0, 1), 23924); + assert!(tsa(("localhost", 23924u16)).unwrap().contains(&a)); + } + + #[test] + fn to_socket_addr_str() { + let a = SocketAddr::new(IpAddr::new_v4(77, 88, 21, 11), 24352); + assert_eq!(Ok(vec![a]), tsa("77.88.21.11:24352")); + + let a = SocketAddr::new(IpAddr::new_v6(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53); + assert_eq!(Ok(vec![a]), tsa("[2a02:6b8:0:1::1]:53")); + + let a = SocketAddr::new(IpAddr::new_v4(127, 0, 0, 1), 23924); + assert!(tsa("localhost:23924").unwrap().contains(&a)); + } +} diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs new file mode 100644 index 0000000000000..08f7a6e2e9636 --- /dev/null +++ b/src/libstd/net/ip.rs @@ -0,0 +1,449 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use cmp::Ordering; +use hash; +use fmt; +use libc; +use sys_common::{AsInner, FromInner}; +use net::{hton, ntoh}; + +/// Representation of an IPv4 address. +#[derive(Copy)] +pub struct Ipv4Addr { + inner: libc::in_addr, +} + +/// Representation of an IPv6 address. +#[derive(Copy)] +pub struct Ipv6Addr { + inner: libc::in6_addr, +} + +#[allow(missing_docs)] +#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] +pub enum Ipv6MulticastScope { + InterfaceLocal, + LinkLocal, + RealmLocal, + AdminLocal, + SiteLocal, + OrganizationLocal, + Global +} + +/// Enumeration of possible IP addresses +#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] +pub enum IpAddr { + /// An IPv4 address. + V4(Ipv4Addr), + /// An IPv6 address. + 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 { + IpAddr::V4(Ipv4Addr::new(a, b, c, d)) + } + + /// 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 { + IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for IpAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + 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 { + inner: libc::in_addr { + s_addr: hton(((a as u32) << 24) | + ((b as u32) << 16) | + ((c as u32) << 8) | + (d as u32)), + } + } + } + + /// Returns the four eight-bit integers that make up this address + pub fn octets(&self) -> [u8; 4] { + let bits = ntoh(self.inner.s_addr); + [(bits >> 24) as u8, (bits >> 16) as u8, (bits >> 8) as u8, bits as u8] + } + + /// Returns true for the special 'unspecified' address 0.0.0.0 + pub fn is_unspecified(&self) -> bool { + self.inner.s_addr == 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 fmt::Display for Ipv4Addr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let octets = self.octets(); + write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) + } +} + +impl fmt::Debug for Ipv4Addr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +impl Clone for Ipv4Addr { + fn clone(&self) -> Ipv4Addr { *self } +} + +impl PartialEq for Ipv4Addr { + fn eq(&self, other: &Ipv4Addr) -> bool { + self.inner.s_addr == other.inner.s_addr + } +} +impl Eq for Ipv4Addr {} + +impl hash::Hash for Ipv4Addr { + fn hash(&self, s: &mut S) { + self.inner.s_addr.hash(s) + } +} + +impl PartialOrd for Ipv4Addr { + fn partial_cmp(&self, other: &Ipv4Addr) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Ipv4Addr { + fn cmp(&self, other: &Ipv4Addr) -> Ordering { + self.inner.s_addr.cmp(&other.inner.s_addr) + } +} + +impl AsInner for Ipv4Addr { + fn as_inner(&self) -> &libc::in_addr { &self.inner } +} +impl FromInner for Ipv4Addr { + fn from_inner(addr: libc::in_addr) -> Ipv4Addr { + Ipv4Addr { inner: addr } + } +} + +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 { + inner: libc::in6_addr { + s6_addr: [hton(a), hton(b), hton(c), hton(d), + hton(e), hton(f), hton(g), hton(h)] + } + } + } + + /// Return the eight 16-bit segments that make up this address + pub fn segments(&self) -> [u16; 8] { + [ntoh(self.inner.s6_addr[0]), + ntoh(self.inner.s6_addr[1]), + ntoh(self.inner.s6_addr[2]), + ntoh(self.inner.s6_addr[3]), + ntoh(self.inner.s6_addr[4]), + ntoh(self.inner.s6_addr[5]), + ntoh(self.inner.s6_addr[6]), + ntoh(self.inner.s6_addr[7])] + } + + /// 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 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 + [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 + [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(0, 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()); + + 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()[..zeros_at]), + fmt_subslice(&self.segments()[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) + } + } + } + } +} + +impl fmt::Debug for Ipv6Addr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +impl Clone for Ipv6Addr { + fn clone(&self) -> Ipv6Addr { *self } +} + +impl PartialEq for Ipv6Addr { + fn eq(&self, other: &Ipv6Addr) -> bool { + self.inner.s6_addr == other.inner.s6_addr + } +} +impl Eq for Ipv6Addr {} + +impl hash::Hash for Ipv6Addr { + fn hash(&self, s: &mut S) { + self.inner.s6_addr.hash(s) + } +} + +impl PartialOrd for Ipv6Addr { + fn partial_cmp(&self, other: &Ipv6Addr) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Ipv6Addr { + fn cmp(&self, other: &Ipv6Addr) -> Ordering { + self.inner.s6_addr.cmp(&other.inner.s6_addr) + } +} + +impl AsInner for Ipv6Addr { + fn as_inner(&self) -> &libc::in6_addr { &self.inner } +} +impl FromInner for Ipv6Addr { + fn from_inner(addr: libc::in6_addr) -> Ipv6Addr { + Ipv6Addr { inner: addr } + } +} diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs new file mode 100644 index 0000000000000..d73c06a2549e1 --- /dev/null +++ b/src/libstd/net/mod.rs @@ -0,0 +1,99 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Networking primitives for TCP/UDP communication +//! +//! > **NOTE**: This module is very much a work in progress and is under active +//! > development. At this time it is still recommended to use the `old_io` +//! > module while the details of this module shake out. + +#![unstable(feature = "net")] + +use prelude::v1::*; + +use io::{self, Error, ErrorKind}; +use num::Int; +use sys_common::net2 as net_imp; + +pub use self::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; +pub use self::addr::{SocketAddr, ToSocketAddrs}; +pub use self::tcp::{TcpStream, TcpListener}; +pub use self::udp::UdpSocket; + +mod ip; +mod addr; +mod tcp; +mod udp; +mod parser; +#[cfg(test)] mod test; + +/// Possible values which can be passed to the `shutdown` method of `TcpStream` +/// and `UdpSocket`. +#[derive(Copy, Clone, PartialEq)] +pub enum Shutdown { + /// Indicates that the reading portion of this stream/socket should be shut + /// down. All currently blocked and future reads will return `Ok(0)`. + Read, + /// Indicates that the writing portion of this stream/socket should be shut + /// down. All currently blocked and future writes will return an error. + Write, + /// Shut down both the reading and writing portions of this stream. + /// + /// See `Shutdown::Read` and `Shutdown::Write` for more information. + Both +} + +fn hton(i: I) -> I { i.to_be() } +fn ntoh(i: I) -> I { Int::from_be(i) } + +fn each_addr(addr: &A, mut f: F) -> io::Result + where F: FnMut(&SocketAddr) -> io::Result +{ + let mut last_err = None; + for addr in try!(addr.to_socket_addrs()) { + match f(&addr) { + Ok(l) => return Ok(l), + Err(e) => last_err = Some(e), + } + } + Err(last_err.unwrap_or_else(|| { + Error::new(ErrorKind::InvalidInput, + "could not resolve to any addresses", None) + })) +} + +/// An iterator over `SocketAddr` values returned from a host lookup operation. +pub struct LookupHost(net_imp::LookupHost); + +impl Iterator for LookupHost { + type Item = io::Result; + fn next(&mut self) -> Option> { self.0.next() } +} + +/// Resolve the host specified by `host` as a number of `SocketAddr` instances. +/// +/// This method may perform a DNS query to resolve `host` and may also inspect +/// system configuration to resolve the specified hostname. +/// +/// # Example +/// +/// ```no_run +/// use std::net; +/// +/// # fn foo() -> std::io::Result<()> { +/// for host in try!(net::lookup_host("rust-lang.org")) { +/// println!("found address: {}", try!(host)); +/// } +/// # Ok(()) +/// # } +/// ``` +pub fn lookup_host(host: &str) -> io::Result { + net_imp::lookup_host(host).map(LookupHost) +} diff --git a/src/libstd/net/parser.rs b/src/libstd/net/parser.rs new file mode 100644 index 0000000000000..e82dc88cddd7b --- /dev/null +++ b/src/libstd/net/parser.rs @@ -0,0 +1,330 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A private parser implementation of IPv4, IPv6, and socket addresses. +//! +//! This module is "publicly exported" through the `FromStr` implementations +//! below. + +use prelude::v1::*; + +use str::FromStr; +use net::{Ipv4Addr, Ipv6Addr, IpAddr, SocketAddr}; + +struct Parser<'a> { + // parsing as ASCII, so can use byte array + s: &'a [u8], + pos: usize, +} + +impl<'a> Parser<'a> { + fn new(s: &'a str) -> Parser<'a> { + Parser { + s: s.as_bytes(), + pos: 0, + } + } + + fn is_eof(&self) -> bool { + self.pos == self.s.len() + } + + // Commit only if parser returns Some + fn read_atomically(&mut self, cb: F) -> Option where + F: FnOnce(&mut Parser) -> Option, + { + let pos = self.pos; + let r = cb(self); + if r.is_none() { + self.pos = pos; + } + r + } + + // Commit only if parser read till EOF + fn read_till_eof(&mut self, cb: F) -> Option where + F: FnOnce(&mut Parser) -> Option, + { + self.read_atomically(move |p| { + match cb(p) { + Some(x) => if p.is_eof() {Some(x)} else {None}, + None => None, + } + }) + } + + // Return result of first successful parser + fn read_or(&mut self, parsers: &mut [Box Option>]) + -> Option { + for pf in parsers.iter_mut() { + match self.read_atomically(|p: &mut Parser| pf(p)) { + Some(r) => return Some(r), + None => {} + } + } + None + } + + // Apply 3 parsers sequentially + fn read_seq_3(&mut self, + pa: PA, + pb: PB, + pc: PC) + -> Option<(A, B, C)> where + PA: FnOnce(&mut Parser) -> Option, + PB: FnOnce(&mut Parser) -> Option, + PC: FnOnce(&mut Parser) -> Option, + { + self.read_atomically(move |p| { + let a = pa(p); + let b = if a.is_some() { pb(p) } else { None }; + 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 + } + }) + } + + // Read next char + fn read_char(&mut self) -> Option { + if self.is_eof() { + None + } 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 + fn read_given_char(&mut self, c: char) -> Option { + self.read_atomically(|p| { + match p.read_char() { + Some(next) if next == c => Some(next), + _ => None, + } + }) + } + + // Read digit + fn read_digit(&mut self, radix: u8) -> Option { + fn parse_digit(c: char, radix: u8) -> Option { + 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 { + let mut r = 0u32; + 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 { + self.read_atomically(|p| p.read_number_impl(radix, max_digits, upto)) + } + + fn read_ipv4_addr_impl(&mut self) -> Option { + let mut bs = [0u8; 4]; + let mut i = 0; + while i < 4 { + if i != 0 && self.read_given_char('.').is_none() { + return None; + } + + let octet = self.read_number(10, 3, 0x100).map(|n| n as u8); + match octet { + Some(d) => bs[i] = d, + None => return None, + }; + i += 1; + } + Some(Ipv4Addr::new(bs[0], bs[1], bs[2], bs[3])) + } + + // Read IPv4 address + 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]) -> 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::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: usize) + -> (usize, bool) { + let mut i = 0; + while i < limit { + if i < limit - 1 { + let ipv4 = p.read_atomically(|p| { + if i == 0 || p.read_given_char(':').is_some() { + p.read_ipv4_addr() + } else { + None + } + }); + 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); + } + } + + let group = p.read_atomically(|p| { + if i == 0 || p.read_given_char(':').is_some() { + p.read_number(16, 4, 0x10000).map(|n| n as u16) + } else { + None + } + }); + match group { + Some(g) => groups[i] = g, + None => return (i, false) + } + i += 1; + } + (i, false) + } + + let mut head = [0u16; 8]; + let (head_size, head_ipv4) = read_groups(self, &mut head, 8); + + if head_size == 8 { + return Some(Ipv6Addr::new( + head[0], head[1], head[2], head[3], + head[4], head[5], head[6], head[7])) + } + + // IPv4 part is not allowed before `::` + if head_ipv4 { + return None + } + + // read `::` if previous code parsed less than 8 groups + if !self.read_given_char(':').is_some() || !self.read_given_char(':').is_some() { + return None; + } + + let mut tail = [0u16; 8]; + let (tail_size, _) = read_groups(self, &mut tail, 8 - head_size); + Some(ipv6_addr_from_head_tail(&head[..head_size], &tail[..tail_size])) + } + + 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 = |p: &mut Parser| p.read_ipv4_addr().map(|v4| IpAddr::V4(v4)); + let ipv6_addr = |p: &mut Parser| p.read_ipv6_addr().map(|v6| IpAddr::V6(v6)); + self.read_or(&mut [Box::new(ipv4_addr), Box::new(ipv6_addr)]) + } + + fn read_socket_addr(&mut self) -> Option { + let ip_addr = |p: &mut Parser| { + let ipv4_p = |p: &mut Parser| p.read_ip_addr(); + let ipv6_p = |p: &mut Parser| { + 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, _) => IpAddr::V6(ip) }) + }; + p.read_or(&mut [Box::new(ipv4_p), Box::new(ipv6_p)]) + }; + 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); + + // host, colon, port + self.read_seq_3::(ip_addr, colon, port) + .map(|t| match t { (ip, _, port) => SocketAddr::new(ip, port) }) + } +} + +impl FromStr for IpAddr { + type Err = ParseError; + fn from_str(s: &str) -> Result { + match Parser::new(s).read_till_eof(|p| p.read_ip_addr()) { + Some(s) => Ok(s), + None => Err(ParseError), + } + } +} + +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 { + match Parser::new(s).read_till_eof(|p| p.read_socket_addr()) { + Some(s) => Ok(s), + None => Err(ParseError), + } + } +} + +#[derive(Debug, Clone, PartialEq, Copy)] +pub struct ParseError; diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs new file mode 100644 index 0000000000000..50eafdfc5c238 --- /dev/null +++ b/src/libstd/net/tcp.rs @@ -0,0 +1,792 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; +use io::prelude::*; + +use io; +use net::{ToSocketAddrs, SocketAddr, Shutdown}; +use sys_common::net2 as net_imp; +use sys_common::AsInner; + +/// A structure which represents a TCP stream between a local socket and a +/// remote socket. +/// +/// The socket will be closed when the value is dropped. +/// +/// # Example +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::net::TcpStream; +/// +/// { +/// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); +/// +/// // ignore the Result +/// let _ = stream.write(&[1]); +/// let _ = stream.read(&mut [0; 128]); // ignore here too +/// } // the stream is closed here +/// ``` +pub struct TcpStream(net_imp::TcpStream); + +/// A structure representing a socket server. +/// +/// # Examples +/// +/// ```no_run +/// use std::net::{TcpListener, TcpStream}; +/// use std::thread::Thread; +/// +/// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); +/// +/// fn handle_client(stream: TcpStream) { +/// // ... +/// } +/// +/// // accept connections and process them, spawning a new thread for each one +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// Thread::spawn(move|| { +/// // connection succeeded +/// handle_client(stream) +/// }); +/// } +/// Err(e) => { /* connection failed */ } +/// } +/// } +/// +/// // close the socket server +/// drop(listener); +/// ``` +pub struct TcpListener(net_imp::TcpListener); + +/// An infinite iterator over the connections from a `TcpListener`. +/// +/// This iterator will infinitely yield `Some` of the accepted connections. It +/// is equivalent to calling `accept` in a loop. +pub struct Incoming<'a> { listener: &'a TcpListener } + +impl TcpStream { + /// Open a TCP connection to a remote host. + /// + /// `addr` is an address of the remote host. Anything which implements + /// `ToSocketAddrs` trait can be supplied for the address; see this trait + /// documentation for concrete examples. + pub fn connect(addr: &A) -> io::Result { + super::each_addr(addr, net_imp::TcpStream::connect).map(TcpStream) + } + + /// Returns the socket address of the remote peer of this TCP connection. + pub fn peer_addr(&self) -> io::Result { + self.0.peer_addr() + } + + /// Returns the socket address of the local half of this TCP connection. + pub fn socket_addr(&self) -> io::Result { + self.0.socket_addr() + } + + /// Shut down the read, write, or both halves of this connection. + /// + /// This function will cause all pending and future I/O on the specified + /// portions to return immediately with an appropriate value (see the + /// documentation of `Shutdown`). + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.0.shutdown(how) + } + + /// Create a new independently owned handle to the underlying socket. + /// + /// The returned `TcpStream` is a reference to the same stream that this + /// object references. Both handles will read and write the same stream of + /// data, and options set on one stream will be propagated to the other + /// stream. + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(TcpStream) + } + + /// Sets the nodelay flag on this connection to the boolean specified + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + self.0.set_nodelay(nodelay) + } + + /// Sets the keepalive timeout to the timeout specified. + /// + /// If the value specified is `None`, then the keepalive flag is cleared on + /// this connection. Otherwise, the keepalive timeout will be set to the + /// specified time, in seconds. + pub fn set_keepalive(&self, seconds: Option) -> io::Result<()> { + self.0.set_keepalive(seconds) + } +} + +impl Read for TcpStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } +} +impl Write for TcpStream { + fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } + fn flush(&mut self) -> io::Result<()> { Ok(()) } +} +impl<'a> Read for &'a TcpStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } +} +impl<'a> Write for &'a TcpStream { + fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } + fn flush(&mut self) -> io::Result<()> { Ok(()) } +} + +impl AsInner for TcpStream { + fn as_inner(&self) -> &net_imp::TcpStream { &self.0 } +} + +impl TcpListener { + /// Creates a new `TcpListener` which will be bound to the specified + /// address. + /// + /// The returned listener is ready for accepting connections. + /// + /// Binding with a port number of 0 will request that the OS assigns a port + /// to this listener. The port allocated can be queried via the + /// `socket_addr` function. + /// + /// The address type can be any implementer of `ToSocketAddrs` trait. See + /// its documentation for concrete examples. + pub fn bind(addr: &A) -> io::Result { + super::each_addr(addr, net_imp::TcpListener::bind).map(TcpListener) + } + + /// Returns the local socket address of this listener. + pub fn socket_addr(&self) -> io::Result { + self.0.socket_addr() + } + + /// Create a new independently owned handle to the underlying socket. + /// + /// The returned `TcpListener` is a reference to the same socket that this + /// object references. Both handles can be used to accept incoming + /// connections and options set on one listener will affect the other. + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(TcpListener) + } + + /// Accept a new incoming connection from this listener. + /// + /// This function will block the calling thread until a new TCP connection + /// is established. When established, the corresponding `TcpStream` and the + /// remote peer's address will be returned. + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + self.0.accept().map(|(a, b)| (TcpStream(a), b)) + } + + /// Returns an iterator over the connections being received on this + /// listener. + /// + /// The returned iterator will never returned `None` and will also not yield + /// the peer's `SocketAddr` structure. + pub fn incoming(&self) -> Incoming { + Incoming { listener: self } + } +} + +impl<'a> Iterator for Incoming<'a> { + type Item = io::Result; + fn next(&mut self) -> Option> { + Some(self.listener.accept().map(|p| p.0)) + } +} + +impl AsInner for TcpListener { + fn as_inner(&self) -> &net_imp::TcpListener { &self.0 } +} + +#[cfg(test)] +mod tests { + use prelude::v1::*; + + use io::ErrorKind; + use io::prelude::*; + use net::*; + use net::test::{next_test_ip4, next_test_ip6}; + use sync::mpsc::channel; + use thread::Thread; + + fn each_ip(f: &mut FnMut(SocketAddr)) { + f(next_test_ip4()); + f(next_test_ip6()); + } + + macro_rules! t { + ($e:expr) => { + match $e { + Ok(t) => t, + Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), + } + } + } + + // FIXME #11530 this fails on android because tests are run as root + #[cfg_attr(any(windows, target_os = "android"), ignore)] + #[test] + fn bind_error() { + match TcpListener::bind("0.0.0.0:1") { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind(), ErrorKind::PermissionDenied), + } + } + + #[test] + fn connect_error() { + match TcpStream::connect("0.0.0.0:1") { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind(), ErrorKind::ConnectionRefused), + } + } + + #[test] + fn listen_localhost() { + let socket_addr = next_test_ip4(); + let listener = t!(TcpListener::bind(&socket_addr)); + + let _t = Thread::scoped(move || { + let mut stream = t!(TcpStream::connect(&("localhost", + socket_addr.port()))); + t!(stream.write(&[144])); + }); + + let mut stream = t!(listener.accept()).0; + let mut buf = [0]; + t!(stream.read(&mut buf)); + assert!(buf[0] == 144); + } + + #[test] + fn connect_ip4_loopback() { + let addr = next_test_ip4(); + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = Thread::scoped(move|| { + let mut stream = t!(TcpStream::connect(&("127.0.0.1", addr.port()))); + t!(stream.write(&[44])); + }); + + let mut stream = t!(acceptor.accept()).0; + let mut buf = [0]; + t!(stream.read(&mut buf)); + assert!(buf[0] == 44); + } + + #[test] + fn connect_ip6_loopback() { + let addr = next_test_ip6(); + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = Thread::scoped(move|| { + let mut stream = t!(TcpStream::connect(&("::1", addr.port()))); + t!(stream.write(&[66])); + }); + + let mut stream = t!(acceptor.accept()).0; + let mut buf = [0]; + t!(stream.read(&mut buf)); + assert!(buf[0] == 66); + } + + #[test] + fn smoke_test_ip6() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let (tx, rx) = channel(); + let _t = Thread::scoped(move|| { + let mut stream = t!(TcpStream::connect(&addr)); + t!(stream.write(&[99])); + tx.send(t!(stream.socket_addr())).unwrap(); + }); + + let (mut stream, addr) = t!(acceptor.accept()); + let mut buf = [0]; + t!(stream.read(&mut buf)); + assert!(buf[0] == 99); + assert_eq!(addr, t!(rx.recv())); + }) + } + + #[test] + fn read_eof_ip4() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = Thread::scoped(move|| { + let _stream = t!(TcpStream::connect(&addr)); + // Close + }); + + let mut stream = t!(acceptor.accept()).0; + let mut buf = [0]; + let nread = t!(stream.read(&mut buf)); + assert_eq!(nread, 0); + let nread = t!(stream.read(&mut buf)); + assert_eq!(nread, 0); + }) + } + + #[test] + fn write_close() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let (tx, rx) = channel(); + let _t = Thread::scoped(move|| { + drop(t!(TcpStream::connect(&addr))); + tx.send(()).unwrap(); + }); + + let mut stream = t!(acceptor.accept()).0; + rx.recv().unwrap(); + let buf = [0]; + match stream.write(&buf) { + Ok(..) => {} + Err(e) => { + assert!(e.kind() == ErrorKind::ConnectionReset || + e.kind() == ErrorKind::BrokenPipe || + e.kind() == ErrorKind::ConnectionAborted, + "unknown error: {}", e); + } + } + }) + } + + #[test] + fn multiple_connect_serial_ip4() { + each_ip(&mut |addr| { + let max = 10; + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = Thread::scoped(move|| { + for _ in 0..max { + let mut stream = t!(TcpStream::connect(&addr)); + t!(stream.write(&[99])); + } + }); + + for stream in acceptor.incoming().take(max) { + let mut stream = t!(stream); + let mut buf = [0]; + t!(stream.read(&mut buf)); + assert_eq!(buf[0], 99); + } + }) + } + + #[test] + fn multiple_connect_interleaved_greedy_schedule() { + static MAX: usize = 10; + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = Thread::scoped(move|| { + let acceptor = acceptor; + for (i, stream) in acceptor.incoming().enumerate().take(MAX) { + // Start another task to handle the connection + let _t = Thread::scoped(move|| { + let mut stream = t!(stream); + let mut buf = [0]; + t!(stream.read(&mut buf)); + assert!(buf[0] == i as u8); + }); + } + }); + + connect(0, addr); + }); + + fn connect(i: usize, addr: SocketAddr) { + if i == MAX { return } + + let t = Thread::scoped(move|| { + let mut stream = t!(TcpStream::connect(&addr)); + // Connect again before writing + connect(i + 1, addr); + t!(stream.write(&[i as u8])); + }); + t.join().ok().unwrap(); + } + } + + #[test] + fn multiple_connect_interleaved_lazy_schedule_ip4() { + static MAX: usize = 10; + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = Thread::scoped(move|| { + for stream in acceptor.incoming().take(MAX) { + // Start another task to handle the connection + let _t = Thread::scoped(move|| { + let mut stream = t!(stream); + let mut buf = [0]; + t!(stream.read(&mut buf)); + assert!(buf[0] == 99); + }); + } + }); + + connect(0, addr); + }); + + fn connect(i: usize, addr: SocketAddr) { + if i == MAX { return } + + let t = Thread::scoped(move|| { + let mut stream = t!(TcpStream::connect(&addr)); + connect(i + 1, addr); + t!(stream.write(&[99])); + }); + t.join().ok().unwrap(); + } + } + + pub fn socket_name(addr: SocketAddr) { + } + + pub fn peer_name(addr: SocketAddr) { + } + + #[test] + fn socket_and_peer_name_ip4() { + each_ip(&mut |addr| { + let listener = t!(TcpListener::bind(&addr)); + let so_name = t!(listener.socket_addr()); + assert_eq!(addr, so_name); + let _t = Thread::scoped(move|| { + t!(listener.accept()); + }); + + let stream = t!(TcpStream::connect(&addr)); + assert_eq!(addr, t!(stream.peer_addr())); + }) + } + + #[test] + fn partial_read() { + each_ip(&mut |addr| { + let (tx, rx) = channel(); + let srv = t!(TcpListener::bind(&addr)); + let _t = Thread::scoped(move|| { + let mut cl = t!(srv.accept()).0; + cl.write(&[10]).unwrap(); + let mut b = [0]; + t!(cl.read(&mut b)); + tx.send(()).unwrap(); + }); + + let mut c = t!(TcpStream::connect(&addr)); + let mut b = [0; 10]; + assert_eq!(c.read(&mut b), Ok(1)); + t!(c.write(&[1])); + rx.recv().unwrap(); + }) + } + + #[test] + fn double_bind() { + each_ip(&mut |addr| { + let _listener = t!(TcpListener::bind(&addr)); + match TcpListener::bind(&addr) { + Ok(..) => panic!(), + Err(e) => { + assert!(e.kind() == ErrorKind::ConnectionRefused || + e.kind() == ErrorKind::Other, + "unknown error: {} {:?}", e, e.kind()); + } + } + }) + } + + #[test] + fn fast_rebind() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = Thread::scoped(move|| { + t!(TcpStream::connect(&addr)); + }); + + t!(acceptor.accept()); + drop(acceptor); + t!(TcpListener::bind(&addr)); + }); + } + + #[test] + fn tcp_clone_smoke() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = Thread::scoped(move|| { + let mut s = t!(TcpStream::connect(&addr)); + let mut buf = [0, 0]; + assert_eq!(s.read(&mut buf), Ok(1)); + assert_eq!(buf[0], 1); + t!(s.write(&[2])); + }); + + let mut s1 = t!(acceptor.accept()).0; + let s2 = t!(s1.try_clone()); + + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + let _t = Thread::scoped(move|| { + let mut s2 = s2; + rx1.recv().unwrap(); + t!(s2.write(&[1])); + tx2.send(()).unwrap(); + }); + tx1.send(()).unwrap(); + let mut buf = [0, 0]; + assert_eq!(s1.read(&mut buf), Ok(1)); + rx2.recv().unwrap(); + }) + } + + #[test] + fn tcp_clone_two_read() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + let (tx1, rx) = channel(); + let tx2 = tx1.clone(); + + let _t = Thread::scoped(move|| { + let mut s = t!(TcpStream::connect(&addr)); + t!(s.write(&[1])); + rx.recv().unwrap(); + t!(s.write(&[2])); + rx.recv().unwrap(); + }); + + let mut s1 = t!(acceptor.accept()).0; + let s2 = t!(s1.try_clone()); + + let (done, rx) = channel(); + let _t = Thread::scoped(move|| { + let mut s2 = s2; + let mut buf = [0, 0]; + t!(s2.read(&mut buf)); + tx2.send(()).unwrap(); + done.send(()).unwrap(); + }); + let mut buf = [0, 0]; + t!(s1.read(&mut buf)); + tx1.send(()).unwrap(); + + rx.recv().unwrap(); + }) + } + + #[test] + fn tcp_clone_two_write() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = Thread::scoped(move|| { + let mut s = t!(TcpStream::connect(&addr)); + let mut buf = [0, 1]; + t!(s.read(&mut buf)); + t!(s.read(&mut buf)); + }); + + let mut s1 = t!(acceptor.accept()).0; + let s2 = t!(s1.try_clone()); + + let (done, rx) = channel(); + let _t = Thread::scoped(move|| { + let mut s2 = s2; + t!(s2.write(&[1])); + done.send(()).unwrap(); + }); + t!(s1.write(&[2])); + + rx.recv().unwrap(); + }) + } + + #[test] + fn shutdown_smoke() { + each_ip(&mut |addr| { + let a = t!(TcpListener::bind(&addr)); + let _t = Thread::scoped(move|| { + let mut c = t!(a.accept()).0; + let mut b = [0]; + assert_eq!(c.read(&mut b), Ok(0)); + t!(c.write(&[1])); + }); + + let mut s = t!(TcpStream::connect(&addr)); + t!(s.shutdown(Shutdown::Write)); + assert!(s.write(&[1]).is_err()); + let mut b = [0, 0]; + assert_eq!(t!(s.read(&mut b)), 1); + assert_eq!(b[0], 1); + }) + } + + #[test] + fn close_readwrite_smoke() { + each_ip(&mut |addr| { + let a = t!(TcpListener::bind(&addr)); + let (tx, rx) = channel::<()>(); + let _t = Thread::scoped(move|| { + let _s = t!(a.accept()); + let _ = rx.recv(); + }); + + let mut b = [0]; + let mut s = t!(TcpStream::connect(&addr)); + let mut s2 = t!(s.try_clone()); + + // closing should prevent reads/writes + t!(s.shutdown(Shutdown::Write)); + assert!(s.write(&[0]).is_err()); + t!(s.shutdown(Shutdown::Read)); + assert_eq!(s.read(&mut b), Ok(0)); + + // closing should affect previous handles + assert!(s2.write(&[0]).is_err()); + assert_eq!(s2.read(&mut b), Ok(0)); + + // closing should affect new handles + let mut s3 = t!(s.try_clone()); + assert!(s3.write(&[0]).is_err()); + assert_eq!(s3.read(&mut b), Ok(0)); + + // make sure these don't die + let _ = s2.shutdown(Shutdown::Read); + let _ = s2.shutdown(Shutdown::Write); + let _ = s3.shutdown(Shutdown::Read); + let _ = s3.shutdown(Shutdown::Write); + drop(tx); + }) + } + + #[test] + fn close_read_wakes_up() { + each_ip(&mut |addr| { + let a = t!(TcpListener::bind(&addr)); + let (tx1, rx) = channel::<()>(); + let _t = Thread::scoped(move|| { + let _s = t!(a.accept()); + let _ = rx.recv(); + }); + + let s = t!(TcpStream::connect(&addr)); + let s2 = t!(s.try_clone()); + let (tx, rx) = channel(); + let _t = Thread::scoped(move|| { + let mut s2 = s2; + assert_eq!(t!(s2.read(&mut [0])), 0); + tx.send(()).unwrap(); + }); + // this should wake up the child task + t!(s.shutdown(Shutdown::Read)); + + // this test will never finish if the child doesn't wake up + rx.recv().unwrap(); + drop(tx1); + }) + } + + #[test] + fn clone_while_reading() { + each_ip(&mut |addr| { + let accept = t!(TcpListener::bind(&addr)); + + // Enqueue a task to write to a socket + let (tx, rx) = channel(); + let (txdone, rxdone) = channel(); + let txdone2 = txdone.clone(); + let _t = Thread::scoped(move|| { + let mut tcp = t!(TcpStream::connect(&addr)); + rx.recv().unwrap(); + t!(tcp.write(&[0])); + txdone2.send(()).unwrap(); + }); + + // Spawn off a reading clone + let tcp = t!(accept.accept()).0; + let tcp2 = t!(tcp.try_clone()); + let txdone3 = txdone.clone(); + let _t = Thread::scoped(move|| { + let mut tcp2 = tcp2; + t!(tcp2.read(&mut [0])); + txdone3.send(()).unwrap(); + }); + + // Try to ensure that the reading clone is indeed reading + for _ in 0..50 { + Thread::yield_now(); + } + + // clone the handle again while it's reading, then let it finish the + // read. + let _ = t!(tcp.try_clone()); + tx.send(()).unwrap(); + rxdone.recv().unwrap(); + rxdone.recv().unwrap(); + }) + } + + #[test] + fn clone_accept_smoke() { + each_ip(&mut |addr| { + let a = t!(TcpListener::bind(&addr)); + let a2 = t!(a.try_clone()); + + let _t = Thread::scoped(move|| { + let _ = TcpStream::connect(&addr); + }); + let _t = Thread::scoped(move|| { + let _ = TcpStream::connect(&addr); + }); + + t!(a.accept()); + t!(a2.accept()); + }) + } + + #[test] + fn clone_accept_concurrent() { + each_ip(&mut |addr| { + let a = t!(TcpListener::bind(&addr)); + let a2 = t!(a.try_clone()); + + let (tx, rx) = channel(); + let tx2 = tx.clone(); + + let _t = Thread::scoped(move|| { + tx.send(t!(a.accept())).unwrap(); + }); + let _t = Thread::scoped(move|| { + tx2.send(t!(a2.accept())).unwrap(); + }); + + let _t = Thread::scoped(move|| { + let _ = TcpStream::connect(&addr); + }); + let _t = Thread::scoped(move|| { + let _ = TcpStream::connect(&addr); + }); + + rx.recv().unwrap(); + rx.recv().unwrap(); + }) + } +} diff --git a/src/libstd/net/test.rs b/src/libstd/net/test.rs new file mode 100644 index 0000000000000..971fb4b69c8ef --- /dev/null +++ b/src/libstd/net/test.rs @@ -0,0 +1,39 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use env; +use net::{SocketAddr, IpAddr}; +use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + +pub fn next_test_ip4() -> SocketAddr { + static PORT: AtomicUsize = ATOMIC_USIZE_INIT; + SocketAddr::new(IpAddr::new_v4(127, 0, 0, 1), + PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port()) +} + +pub fn next_test_ip6() -> SocketAddr { + static PORT: AtomicUsize = ATOMIC_USIZE_INIT; + SocketAddr::new(IpAddr::new_v6(0, 0, 0, 0, 0, 0, 0, 1), + PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port()) +} + +// The bots run multiple builds at the same time, and these builds +// all want to use ports. This function figures out which workspace +// it is running in and assigns a port range based on it. +fn base_port() -> u16 { + let cwd = env::current_dir().unwrap(); + let dirs = ["32-opt", "32-nopt", "64-opt", "64-nopt", "64-opt-vg", + "all-opt", "snap3", "dist"]; + dirs.iter().enumerate().find(|&(i, dir)| { + cwd.as_str().unwrap().contains(dir) + }).map(|p| p.0).unwrap_or(0) as u16 * 1000 + 19600 +} diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs new file mode 100644 index 0000000000000..d162a29790ea1 --- /dev/null +++ b/src/libstd/net/udp.rs @@ -0,0 +1,291 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use io::{self, Error, ErrorKind}; +use net::{ToSocketAddrs, SocketAddr, IpAddr}; +use sys_common::net2 as net_imp; +use sys_common::AsInner; + +/// A User Datagram Protocol socket. +/// +/// This is an implementation of a bound UDP socket. This supports both IPv4 and +/// IPv6 addresses, and there is no corresponding notion of a server because UDP +/// is a datagram protocol. +/// +/// # Example +/// +/// ```no_run +/// use std::net::UdpSocket; +/// +/// # fn foo() -> std::io::Result<()> { +/// let mut socket = try!(UdpSocket::bind("127.0.0.1:34254")); +/// +/// let mut buf = [0; 10]; +/// let (amt, src) = try!(socket.recv_from(&mut buf)); +/// +/// // Send a reply to the socket we received data from +/// let buf = &mut buf[..amt]; +/// buf.reverse(); +/// try!(socket.send_to(buf, &src)); +/// +/// drop(socket); // close the socket +/// # Ok(()) +/// # } +/// ``` +pub struct UdpSocket(net_imp::UdpSocket); + +impl UdpSocket { + /// Creates a UDP socket from the given address. + /// + /// Address type can be any implementor of `ToSocketAddr` trait. See its + /// documentation for concrete examples. + pub fn bind(addr: &A) -> io::Result { + super::each_addr(addr, net_imp::UdpSocket::bind).map(UdpSocket) + } + + /// Receives data from the socket. On success, returns the number of bytes + /// read and the address from whence the data came. + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0.recv_from(buf) + } + + /// Sends data on the socket to the given address. Returns nothing on + /// success. + /// + /// Address type can be any implementor of `ToSocketAddrs` trait. See its + /// documentation for concrete examples. + pub fn send_to(&self, buf: &[u8], addr: &A) + -> io::Result { + match try!(addr.to_socket_addrs()).next() { + Some(addr) => self.0.send_to(buf, &addr), + None => Err(Error::new(ErrorKind::InvalidInput, + "no addresses to send data to", None)), + } + } + + /// Returns the socket address that this socket was created from. + pub fn socket_addr(&self) -> io::Result { + self.0.socket_addr() + } + + /// Create a new independently owned handle to the underlying socket. + /// + /// The returned `UdpSocket` is a reference to the same socket that this + /// object references. Both handles will read and write the same port, and + /// options set on one socket will be propagated to the other. + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(UdpSocket) + } + + /// Sets the broadcast flag on or off + pub fn set_broadcast(&self, on: bool) -> io::Result<()> { + self.0.set_broadcast(on) + } + + /// Set the multicast loop flag to the specified value + /// + /// This lets multicast packets loop back to local sockets (if enabled) + pub fn set_multicast_loop(&self, on: bool) -> io::Result<()> { + self.0.set_multicast_loop(on) + } + + /// Joins a multicast IP address (becomes a member of it) + pub fn join_multicast(&self, multi: &IpAddr) -> io::Result<()> { + self.0.join_multicast(multi) + } + + /// Leaves a multicast IP address (drops membership from it) + pub fn leave_multicast(&self, multi: &IpAddr) -> io::Result<()> { + self.0.leave_multicast(multi) + } + + /// Sets the multicast TTL + pub fn set_multicast_time_to_live(&self, ttl: i32) -> io::Result<()> { + self.0.multicast_time_to_live(ttl) + } + + /// Sets this socket's TTL + pub fn set_time_to_live(&self, ttl: i32) -> io::Result<()> { + self.0.time_to_live(ttl) + } +} + +impl AsInner for UdpSocket { + fn as_inner(&self) -> &net_imp::UdpSocket { &self.0 } +} + +#[cfg(test)] +mod tests { + use prelude::v1::*; + + use io::ErrorKind; + use net::*; + use net::test::{next_test_ip4, next_test_ip6}; + use sync::mpsc::channel; + use thread::Thread; + + fn each_ip(f: &mut FnMut(SocketAddr, SocketAddr)) { + f(next_test_ip4(), next_test_ip4()); + f(next_test_ip6(), next_test_ip6()); + } + + macro_rules! t { + ($e:expr) => { + match $e { + Ok(t) => t, + Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), + } + } + } + + // FIXME #11530 this fails on android because tests are run as root + #[cfg_attr(any(windows, target_os = "android"), ignore)] + #[test] + fn bind_error() { + let addr = SocketAddr::new(IpAddr::new_v4(0, 0, 0, 0), 1); + match UdpSocket::bind(&addr) { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind(), ErrorKind::PermissionDenied), + } + } + + #[test] + fn socket_smoke_test_ip4() { + each_ip(&mut |server_ip, client_ip| { + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + + let _t = Thread::spawn(move|| { + let client = t!(UdpSocket::bind(&client_ip)); + rx1.recv().unwrap(); + t!(client.send_to(&[99], &server_ip)); + tx2.send(()).unwrap(); + }); + + let server = t!(UdpSocket::bind(&server_ip)); + tx1.send(()).unwrap(); + let mut buf = [0]; + let (nread, src) = t!(server.recv_from(&mut buf)); + assert_eq!(nread, 1); + assert_eq!(buf[0], 99); + assert_eq!(src, client_ip); + rx2.recv().unwrap(); + }) + } + + #[test] + fn socket_name_ip4() { + each_ip(&mut |addr, _| { + let server = t!(UdpSocket::bind(&addr)); + assert_eq!(addr, t!(server.socket_addr())); + }) + } + + #[test] + fn udp_clone_smoke() { + each_ip(&mut |addr1, addr2| { + let sock1 = t!(UdpSocket::bind(&addr1)); + let sock2 = t!(UdpSocket::bind(&addr2)); + + let _t = Thread::spawn(move|| { + let mut buf = [0, 0]; + assert_eq!(sock2.recv_from(&mut buf), Ok((1, addr1))); + assert_eq!(buf[0], 1); + t!(sock2.send_to(&[2], &addr1)); + }); + + let sock3 = t!(sock1.try_clone()); + + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + let _t = Thread::spawn(move|| { + rx1.recv().unwrap(); + t!(sock3.send_to(&[1], &addr2)); + tx2.send(()).unwrap(); + }); + tx1.send(()).unwrap(); + let mut buf = [0, 0]; + assert_eq!(sock1.recv_from(&mut buf), Ok((1, addr2))); + rx2.recv().unwrap(); + }) + } + + #[test] + fn udp_clone_two_read() { + each_ip(&mut |addr1, addr2| { + let sock1 = t!(UdpSocket::bind(&addr1)); + let sock2 = t!(UdpSocket::bind(&addr2)); + let (tx1, rx) = channel(); + let tx2 = tx1.clone(); + + let _t = Thread::spawn(move|| { + t!(sock2.send_to(&[1], &addr1)); + rx.recv().unwrap(); + t!(sock2.send_to(&[2], &addr1)); + rx.recv().unwrap(); + }); + + let sock3 = t!(sock1.try_clone()); + + let (done, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut buf = [0, 0]; + t!(sock3.recv_from(&mut buf)); + tx2.send(()).unwrap(); + done.send(()).unwrap(); + }); + let mut buf = [0, 0]; + t!(sock1.recv_from(&mut buf)); + tx1.send(()).unwrap(); + + rx.recv().unwrap(); + }) + } + + #[test] + fn udp_clone_two_write() { + each_ip(&mut |addr1, addr2| { + let sock1 = t!(UdpSocket::bind(&addr1)); + let sock2 = t!(UdpSocket::bind(&addr2)); + + let (tx, rx) = channel(); + let (serv_tx, serv_rx) = channel(); + + let _t = Thread::spawn(move|| { + let mut buf = [0, 1]; + rx.recv().unwrap(); + t!(sock2.recv_from(&mut buf)); + serv_tx.send(()).unwrap(); + }); + + let sock3 = t!(sock1.try_clone()); + + let (done, rx) = channel(); + let tx2 = tx.clone(); + let _t = Thread::spawn(move|| { + match sock3.send_to(&[1], &addr2) { + Ok(..) => { let _ = tx2.send(()); } + Err(..) => {} + } + done.send(()).unwrap(); + }); + match sock1.send_to(&[2], &addr2) { + Ok(..) => { let _ = tx.send(()); } + Err(..) => {} + } + drop(tx); + + rx.recv().unwrap(); + serv_rx.recv().unwrap(); + }) + } +} diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index 80fa5f64597e9..5054f72ea9879 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -24,6 +24,7 @@ pub mod condvar; pub mod helper_thread; pub mod mutex; pub mod net; +pub mod net2; pub mod rwlock; pub mod stack; pub mod thread; diff --git a/src/libstd/sys/common/net2.rs b/src/libstd/sys/common/net2.rs new file mode 100644 index 0000000000000..5af59ec6d2b14 --- /dev/null +++ b/src/libstd/sys/common/net2.rs @@ -0,0 +1,393 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use ffi::CString; +use io::{self, Error, ErrorKind}; +use libc::{self, c_int, c_char, c_void, socklen_t}; +use mem; +use net::{IpAddr, SocketAddr, Shutdown}; +use num::Int; +use sys::c; +use sys::net::{cvt, cvt_r, cvt_gai, Socket, init, wrlen_t}; +use sys_common::{AsInner, FromInner, IntoInner}; + +//////////////////////////////////////////////////////////////////////////////// +// sockaddr and misc bindings +//////////////////////////////////////////////////////////////////////////////// + +fn hton(i: I) -> I { i.to_be() } +fn ntoh(i: I) -> I { Int::from_be(i) } + +fn setsockopt(sock: &Socket, opt: c_int, val: c_int, + payload: T) -> io::Result<()> { + unsafe { + let payload = &payload as *const T as *const c_void; + try!(cvt(libc::setsockopt(*sock.as_inner(), opt, val, payload, + mem::size_of::() as socklen_t))); + Ok(()) + } +} + +#[allow(dead_code)] +fn getsockopt(sock: &Socket, opt: c_int, + val: c_int) -> io::Result { + unsafe { + let mut slot: T = mem::zeroed(); + let mut len = mem::size_of::() as socklen_t; + let ret = try!(cvt(c::getsockopt(*sock.as_inner(), opt, val, + &mut slot as *mut _ as *mut _, + &mut len))); + assert_eq!(ret as usize, mem::size_of::()); + Ok(slot) + } +} + +fn sockname(f: F) -> io::Result + where F: FnOnce(*mut libc::sockaddr, *mut socklen_t) -> c_int +{ + unsafe { + let mut storage: libc::sockaddr_storage = mem::zeroed(); + let mut len = mem::size_of_val(&storage) as socklen_t; + try!(cvt(f(&mut storage as *mut _ as *mut _, &mut len))); + sockaddr_to_addr(&storage, len as usize) + } +} + +fn sockaddr_to_addr(storage: &libc::sockaddr_storage, + len: usize) -> io::Result { + match storage.ss_family as libc::c_int { + libc::AF_INET => { + assert!(len as usize >= mem::size_of::()); + Ok(FromInner::from_inner(unsafe { + *(storage as *const _ as *const libc::sockaddr_in) + })) + } + libc::AF_INET6 => { + assert!(len as usize >= mem::size_of::()); + Ok(FromInner::from_inner(unsafe { + *(storage as *const _ as *const libc::sockaddr_in6) + })) + } + _ => { + Err(Error::new(ErrorKind::InvalidInput, "invalid argument", None)) + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// get_host_addresses +//////////////////////////////////////////////////////////////////////////////// + +extern "system" { + fn getaddrinfo(node: *const c_char, service: *const c_char, + hints: *const libc::addrinfo, + res: *mut *mut libc::addrinfo) -> c_int; + fn freeaddrinfo(res: *mut libc::addrinfo); +} + +pub struct LookupHost { + original: *mut libc::addrinfo, + cur: *mut libc::addrinfo, +} + +impl Iterator for LookupHost { + type Item = io::Result; + fn next(&mut self) -> Option> { + unsafe { + if self.cur.is_null() { return None } + let ret = sockaddr_to_addr(mem::transmute((*self.cur).ai_addr), + (*self.cur).ai_addrlen as usize); + self.cur = (*self.cur).ai_next as *mut libc::addrinfo; + Some(ret) + } + } +} + +impl Drop for LookupHost { + fn drop(&mut self) { + unsafe { freeaddrinfo(self.original) } + } +} + +pub fn lookup_host(host: &str) -> io::Result { + init(); + + let c_host = CString::from_slice(host.as_bytes()); + let mut res = 0 as *mut _; + unsafe { + try!(cvt_gai(getaddrinfo(c_host.as_ptr(), 0 as *const _, 0 as *const _, + &mut res))); + Ok(LookupHost { original: res, cur: res }) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// TCP streams +//////////////////////////////////////////////////////////////////////////////// + +pub struct TcpStream { + inner: Socket, +} + +impl TcpStream { + pub fn connect(addr: &SocketAddr) -> io::Result { + init(); + + let sock = try!(Socket::new(addr, libc::SOCK_STREAM)); + + let (addrp, len) = addr.into_inner(); + try!(cvt_r(|| unsafe { libc::connect(*sock.as_inner(), addrp, len) })); + Ok(TcpStream { inner: sock }) + } + + pub fn socket(&self) -> &Socket { &self.inner } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(&self.inner, libc::IPPROTO_TCP, libc::TCP_NODELAY, + nodelay as c_int) + } + + pub fn set_keepalive(&self, seconds: Option) -> io::Result<()> { + let ret = setsockopt(&self.inner, libc::SOL_SOCKET, libc::SO_KEEPALIVE, + seconds.is_some() as c_int); + match seconds { + Some(n) => ret.and_then(|()| self.set_tcp_keepalive(n)), + None => ret, + } + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + fn set_tcp_keepalive(&self, seconds: u32) -> io::Result<()> { + setsockopt(&self.inner, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, + seconds as c_int) + } + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + fn set_tcp_keepalive(&self, seconds: u32) -> io::Result<()> { + setsockopt(&self.inner, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, + seconds as c_int) + } + #[cfg(not(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly")))] + fn set_tcp_keepalive(&self, _seconds: u32) -> io::Result<()> { + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let ret = try!(cvt(unsafe { + libc::send(*self.inner.as_inner(), + buf.as_ptr() as *const c_void, + buf.len() as wrlen_t, + 0) + })); + Ok(ret as usize) + } + + pub fn peer_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { + libc::getpeername(*self.inner.as_inner(), buf, len) + }) + } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { + libc::getsockname(*self.inner.as_inner(), buf, len) + }) + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + use libc::consts::os::bsd44::SHUT_RDWR; + + let how = match how { + Shutdown::Write => libc::SHUT_WR, + Shutdown::Read => libc::SHUT_RD, + Shutdown::Both => SHUT_RDWR, + }; + try!(cvt(unsafe { libc::shutdown(*self.inner.as_inner(), how) })); + Ok(()) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| TcpStream { inner: s }) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// TCP listeners +//////////////////////////////////////////////////////////////////////////////// + +pub struct TcpListener { + inner: Socket, +} + +impl TcpListener { + pub fn bind(addr: &SocketAddr) -> io::Result { + init(); + + let sock = try!(Socket::new(addr, libc::SOCK_STREAM)); + + // On platforms with Berkeley-derived sockets, this allows + // to quickly rebind a socket, without needing to wait for + // the OS to clean up the previous one. + if !cfg!(windows) { + try!(setsockopt(&sock, libc::SOL_SOCKET, libc::SO_REUSEADDR, + 1 as c_int)); + } + + // Bind our new socket + let (addrp, len) = addr.into_inner(); + try!(cvt(unsafe { libc::bind(*sock.as_inner(), addrp, len) })); + + // Start listening + try!(cvt(unsafe { libc::listen(*sock.as_inner(), 128) })); + Ok(TcpListener { inner: sock }) + } + + pub fn socket(&self) -> &Socket { &self.inner } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { + libc::getsockname(*self.inner.as_inner(), buf, len) + }) + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut len = mem::size_of_val(&storage) as socklen_t; + let sock = try!(self.inner.accept(&mut storage as *mut _ as *mut _, + &mut len)); + let addr = try!(sockaddr_to_addr(&storage, len as usize)); + Ok((TcpStream { inner: sock, }, addr)) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| TcpListener { inner: s }) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// UDP +//////////////////////////////////////////////////////////////////////////////// + +pub struct UdpSocket { + inner: Socket, +} + +impl UdpSocket { + pub fn bind(addr: &SocketAddr) -> io::Result { + init(); + + let sock = try!(Socket::new(addr, libc::SOCK_DGRAM)); + let (addrp, len) = addr.into_inner(); + try!(cvt(unsafe { libc::bind(*sock.as_inner(), addrp, len) })); + Ok(UdpSocket { inner: sock }) + } + + pub fn socket(&self) -> &Socket { &self.inner } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { + libc::getsockname(*self.inner.as_inner(), buf, len) + }) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as socklen_t; + + let n = try!(cvt(unsafe { + libc::recvfrom(*self.inner.as_inner(), + buf.as_mut_ptr() as *mut c_void, + buf.len() as wrlen_t, 0, + &mut storage as *mut _ as *mut _, &mut addrlen) + })); + Ok((n as usize, try!(sockaddr_to_addr(&storage, addrlen as usize)))) + } + + pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result { + let (dstp, dstlen) = dst.into_inner(); + let ret = try!(cvt(unsafe { + libc::sendto(*self.inner.as_inner(), + buf.as_ptr() as *const c_void, buf.len() as wrlen_t, + 0, dstp, dstlen) + })); + Ok(ret as usize) + } + + pub fn set_broadcast(&self, on: bool) -> io::Result<()> { + setsockopt(&self.inner, libc::SOL_SOCKET, libc::SO_BROADCAST, + on as c_int) + } + + pub fn set_multicast_loop(&self, on: bool) -> io::Result<()> { + setsockopt(&self.inner, libc::IPPROTO_IP, + libc::IP_MULTICAST_LOOP, on as c_int) + } + + pub fn join_multicast(&self, multi: &IpAddr) -> io::Result<()> { + match *multi { + IpAddr::V4(..) => { + self.set_membership(multi, libc::IP_ADD_MEMBERSHIP) + } + IpAddr::V6(..) => { + self.set_membership(multi, libc::IPV6_ADD_MEMBERSHIP) + } + } + } + pub fn leave_multicast(&self, multi: &IpAddr) -> io::Result<()> { + match *multi { + IpAddr::V4(..) => { + self.set_membership(multi, libc::IP_DROP_MEMBERSHIP) + } + IpAddr::V6(..) => { + self.set_membership(multi, libc::IPV6_DROP_MEMBERSHIP) + } + } + } + fn set_membership(&self, addr: &IpAddr, opt: c_int) -> io::Result<()> { + match *addr { + IpAddr::V4(ref addr) => { + let mreq = libc::ip_mreq { + imr_multiaddr: *addr.as_inner(), + // interface == INADDR_ANY + imr_interface: libc::in_addr { s_addr: 0x0 }, + }; + setsockopt(&self.inner, libc::IPPROTO_IP, opt, mreq) + } + IpAddr::V6(ref addr) => { + let mreq = libc::ip6_mreq { + ipv6mr_multiaddr: *addr.as_inner(), + ipv6mr_interface: 0, + }; + setsockopt(&self.inner, libc::IPPROTO_IPV6, opt, mreq) + } + } + } + + pub fn multicast_time_to_live(&self, ttl: i32) -> io::Result<()> { + setsockopt(&self.inner, libc::IPPROTO_IP, libc::IP_MULTICAST_TTL, + ttl as c_int) + } + + pub fn time_to_live(&self, ttl: i32) -> io::Result<()> { + setsockopt(&self.inner, libc::IPPROTO_IP, libc::IP_TTL, ttl as c_int) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| UdpSocket { inner: s }) + } +} diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index cf05733cc18af..03803cc30cebd 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -157,6 +157,7 @@ extern { pub fn utimes(filename: *const libc::c_char, times: *const libc::timeval) -> libc::c_int; + pub fn gai_strerror(errcode: libc::c_int) -> *const libc::c_char; } #[cfg(any(target_os = "macos", target_os = "ios"))] diff --git a/src/libstd/sys/unix/ext.rs b/src/libstd/sys/unix/ext.rs index 689bbda832229..1d95f1cce7e1b 100644 --- a/src/libstd/sys/unix/ext.rs +++ b/src/libstd/sys/unix/ext.rs @@ -32,8 +32,8 @@ #![unstable(feature = "std_misc")] use ffi::{OsStr, OsString}; -use fs::{Permissions, OpenOptions}; -use fs; +use fs::{self, Permissions, OpenOptions}; +use net; use libc; use mem; use sys::os_str::Buf; @@ -111,6 +111,16 @@ impl AsRawFd for old_io::net::udp::UdpSocket { } } +impl AsRawFd for net::TcpStream { + fn as_raw_fd(&self) -> Fd { *self.as_inner().socket().as_inner() } +} +impl AsRawFd for net::TcpListener { + fn as_raw_fd(&self) -> Fd { *self.as_inner().socket().as_inner() } +} +impl AsRawFd for net::UdpSocket { + fn as_raw_fd(&self) -> Fd { *self.as_inner().socket().as_inner() } +} + // Unix-specific extensions to `OsString`. pub trait OsStringExt { /// Create an `OsString` from a byte vector. diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index f0943de537809..327d117823ee3 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -15,8 +15,7 @@ use io; use libc::{self, c_int, size_t, c_void}; use mem; use sys::cvt; - -pub type fd_t = c_int; +use sys_common::AsInner; pub struct FileDesc { fd: c_int, @@ -55,6 +54,10 @@ impl FileDesc { } } +impl AsInner for FileDesc { + fn as_inner(&self) -> &c_int { &self.fd } +} + impl Drop for FileDesc { fn drop(&mut self) { // closing stdio file handles makes no sense, so never do it. Also, note diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index b5a24278a206e..96a18a956c6a6 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -18,12 +18,11 @@ use prelude::v1::*; use ffi; -use io::ErrorKind; +use io::{self, ErrorKind}; use libc; use num::{Int, SignedInt}; use num; use old_io::{self, IoResult, IoError}; -use io; use str; use sys_common::mkerr_libc; @@ -47,6 +46,7 @@ pub mod fs; // support for std::old_io pub mod fs2; // support for std::fs pub mod helper_signal; pub mod mutex; +pub mod net; pub mod os; pub mod os_str; pub mod pipe; diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs new file mode 100644 index 0000000000000..54aec7cf4b193 --- /dev/null +++ b/src/libstd/sys/unix/net.rs @@ -0,0 +1,74 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use ffi; +use io; +use libc::{self, c_int, size_t}; +use str; +use sys::c; +use net::{SocketAddr, IpAddr}; +use sys::fd::FileDesc; +use sys_common::AsInner; + +pub use sys::{cvt, cvt_r}; + +pub type wrlen_t = size_t; + +pub struct Socket(FileDesc); + +pub fn init() {} + +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { return Ok(()) } + + let detail = unsafe { + str::from_utf8(ffi::c_str_to_bytes(&c::gai_strerror(err))).unwrap() + .to_string() + }; + Err(io::Error::new(io::ErrorKind::Other, + "failed to lookup address information", Some(detail))) +} + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + let fam = match addr.ip() { + IpAddr::V4(..) => libc::AF_INET, + IpAddr::V6(..) => libc::AF_INET6, + }; + unsafe { + let fd = try!(cvt(libc::socket(fam, ty, 0))); + Ok(Socket(FileDesc::new(fd))) + } + } + + pub fn accept(&self, storage: *mut libc::sockaddr, + len: *mut libc::socklen_t) -> io::Result { + let fd = try!(cvt_r(|| unsafe { + libc::accept(self.0.raw(), storage, len) + })); + Ok(Socket(FileDesc::new(fd))) + } + + pub fn duplicate(&self) -> io::Result { + cvt(unsafe { libc::dup(self.0.raw()) }).map(|fd| { + Socket(FileDesc::new(fd)) + }) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } +} + +impl AsInner for Socket { + fn as_inner(&self) -> &c_int { self.0.as_inner() } +} diff --git a/src/libstd/sys/windows/ext.rs b/src/libstd/sys/windows/ext.rs index dc874c2c7913c..ac1006e653f09 100644 --- a/src/libstd/sys/windows/ext.rs +++ b/src/libstd/sys/windows/ext.rs @@ -21,6 +21,7 @@ pub use sys_common::wtf8::{Wtf8Buf, EncodeWide}; use ffi::{OsStr, OsString}; use fs::{self, OpenOptions}; use libc; +use net; use sys::os_str::Buf; use sys_common::{AsInner, FromInner, AsInnerMut}; @@ -103,6 +104,16 @@ impl AsRawSocket for old_io::net::udp::UdpSocket { } } +impl AsRawSocket for net::TcpStream { + fn as_raw_socket(&self) -> Socket { *self.as_inner().socket().as_inner() } +} +impl AsRawSocket for net::TcpListener { + fn as_raw_socket(&self) -> Socket { *self.as_inner().socket().as_inner() } +} +impl AsRawSocket for net::UdpSocket { + fn as_raw_socket(&self) -> Socket { *self.as_inner().socket().as_inner() } +} + // Windows-specific extensions to `OsString`. pub trait OsStringExt { /// Create an `OsString` from a potentially ill-formed UTF-16 slice of 16-bit code units. diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 140bdb1450102..0fa9aaf432355 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -43,6 +43,7 @@ pub mod fs2; pub mod handle; pub mod helper_signal; pub mod mutex; +pub mod net; pub mod os; pub mod os_str; pub mod pipe; diff --git a/src/libstd/sys/windows/net.rs b/src/libstd/sys/windows/net.rs new file mode 100644 index 0000000000000..4df72f6d4ab7e --- /dev/null +++ b/src/libstd/sys/windows/net.rs @@ -0,0 +1,121 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use io; +use libc::consts::os::extra::INVALID_SOCKET; +use libc::{self, c_int, c_void}; +use mem; +use net::{SocketAddr, IpAddr}; +use num::{SignedInt, Int}; +use rt; +use sync::{Once, ONCE_INIT}; +use sys::c; +use sys_common::AsInner; + +pub type wrlen_t = i32; + +pub struct Socket(libc::SOCKET); + +pub fn init() { + static START: Once = ONCE_INIT; + + START.call_once(|| unsafe { + let mut data: c::WSADATA = mem::zeroed(); + let ret = c::WSAStartup(0x202, // version 2.2 + &mut data); + assert_eq!(ret, 0); + + rt::at_exit(|| { c::WSACleanup(); }) + }); +} + +fn last_error() -> io::Error { + io::Error::from_os_error(unsafe { c::WSAGetLastError() }) +} + +pub fn cvt(t: T) -> io::Result { + let one: T = Int::one(); + if t == -one { + Err(last_error()) + } else { + Ok(t) + } +} + +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { return Ok(()) } + cvt(err).map(|_| ()) +} + +pub fn cvt_r(mut f: F) -> io::Result where F: FnMut() -> T { + cvt(f()) +} + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + let fam = match addr.ip { + IpAddr::V4(..) => libc::AF_INET, + IpAddr::V6(..) => libc::AF_INET6, + }; + match unsafe { libc::socket(fam, ty, 0) } { + INVALID_SOCKET => Err(last_error()), + n => Ok(Socket(n)), + } + } + + pub fn accept(&self, storage: *mut libc::sockaddr, + len: *mut libc::socklen_t) -> io::Result { + match unsafe { libc::accept(self.0, storage, len) } { + INVALID_SOCKET => Err(last_error()), + n => Ok(Socket(n)), + } + } + + pub fn duplicate(&self) -> io::Result { + unsafe { + let mut info: c::WSAPROTOCOL_INFO = mem::zeroed(); + try!(cvt(c::WSADuplicateSocketW(self.0, + c::GetCurrentProcessId(), + &mut info))); + match c::WSASocketW(info.iAddressFamily, + info.iSocketType, + info.iProtocol, + &mut info, 0, 0) { + INVALID_SOCKET => Err(last_error()), + n => Ok(Socket(n)), + } + } + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + // On unix when a socket is shut down all further reads return 0, so we + // do the same on windows to map a shut down socket to returning EOF. + unsafe { + match libc::recv(self.0, buf.as_mut_ptr() as *mut c_void, + buf.len() as i32, 0) { + -1 if c::WSAGetLastError() == c::WSAESHUTDOWN => Ok(0), + -1 => Err(last_error()), + n => Ok(n as usize) + } + } + } +} + +impl Drop for Socket { + fn drop(&mut self) { + unsafe { let _ = libc::closesocket(self.0); } + } +} + +impl AsInner for Socket { + fn as_inner(&self) -> &libc::SOCKET { &self.0 } +}