mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-25 23:14:12 +00:00
rollup merge of #22015: alexcrichton/netv2
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.
This commit is contained in:
commit
a1056360ec
@ -253,6 +253,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;
|
||||
|
592
src/libstd/net/addr.rs
Normal file
592
src/libstd/net/addr.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<libc::sockaddr_in> for SocketAddr {
|
||||
fn from_inner(addr: libc::sockaddr_in) -> SocketAddr {
|
||||
SocketAddr { repr: Repr::V4(addr) }
|
||||
}
|
||||
}
|
||||
|
||||
impl FromInner<libc::sockaddr_in6> 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<S: hash::Hasher + hash::Writer> hash::Hash<S> 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
|
||||
/// `<host_name>:<port>` pair where `<port>` 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<Item=SocketAddr>;
|
||||
|
||||
/// 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<Self::Iter>;
|
||||
}
|
||||
|
||||
impl ToSocketAddrs for SocketAddr {
|
||||
type Iter = option::IntoIter<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
|
||||
Ok(Some(*self).into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSocketAddrs for (IpAddr, u16) {
|
||||
type Iter = option::IntoIter<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
|
||||
let (ip, port) = *self;
|
||||
Ok(Some(SocketAddr::new(ip, port)).into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_socket_addr(s: &str, p: u16) -> io::Result<vec::IntoIter<SocketAddr>> {
|
||||
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<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
|
||||
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<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
|
||||
// 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<T::Iter> {
|
||||
(**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<IpAddr> = "256.0.0.1".parse().ok();
|
||||
assert_eq!(None, none);
|
||||
// too short
|
||||
let none: Option<IpAddr> = "255.0.0".parse().ok();
|
||||
assert_eq!(None, none);
|
||||
// too long
|
||||
let none: Option<IpAddr> = "255.0.0.1.2".parse().ok();
|
||||
assert_eq!(None, none);
|
||||
// no number between dots
|
||||
let none: Option<IpAddr> = "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<IpAddr> = "::00000".parse().ok();
|
||||
assert_eq!(None, none);
|
||||
// too short
|
||||
let none: Option<IpAddr> = "1:2:3:4:5:6:7".parse().ok();
|
||||
assert_eq!(None, none);
|
||||
// too long
|
||||
let none: Option<IpAddr> = "1:2:3:4:5:6:7:8:9".parse().ok();
|
||||
assert_eq!(None, none);
|
||||
// triple colon
|
||||
let none: Option<IpAddr> = "1:2:::6:7:8".parse().ok();
|
||||
assert_eq!(None, none);
|
||||
// two double colons
|
||||
let none: Option<IpAddr> = "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<IpAddr> = "::127.0.0.1:".parse().ok();
|
||||
assert_eq!(None, none);
|
||||
// not enough groups
|
||||
let none: Option<IpAddr> = "1.2.3.4.5:127.0.0.1".parse().ok();
|
||||
assert_eq!(None, none);
|
||||
// too many groups
|
||||
let none: Option<IpAddr> = "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<SocketAddr> = "127.0.0.1".parse().ok();
|
||||
assert_eq!(None, none);
|
||||
// without port
|
||||
let none: Option<SocketAddr> = "127.0.0.1:".parse().ok();
|
||||
assert_eq!(None, none);
|
||||
// wrong brackets around v4
|
||||
let none: Option<SocketAddr> = "[127.0.0.1]:22".parse().ok();
|
||||
assert_eq!(None, none);
|
||||
// port out of range
|
||||
let none: Option<SocketAddr> = "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<Ipv6MulticastScope>) {
|
||||
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: ToSocketAddrs>(a: A) -> io::Result<Vec<SocketAddr>> {
|
||||
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));
|
||||
}
|
||||
}
|
449
src/libstd/net/ip.rs
Normal file
449
src/libstd/net/ip.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<S: hash::Hasher + hash::Writer> hash::Hash<S> 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<Ordering> {
|
||||
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<libc::in_addr> for Ipv4Addr {
|
||||
fn as_inner(&self) -> &libc::in_addr { &self.inner }
|
||||
}
|
||||
impl FromInner<libc::in_addr> 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<Ipv6MulticastScope> {
|
||||
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<Ipv4Addr> {
|
||||
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::<Vec<String>>()
|
||||
.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<S: hash::Hasher + hash::Writer> hash::Hash<S> 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<Ordering> {
|
||||
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<libc::in6_addr> for Ipv6Addr {
|
||||
fn as_inner(&self) -> &libc::in6_addr { &self.inner }
|
||||
}
|
||||
impl FromInner<libc::in6_addr> for Ipv6Addr {
|
||||
fn from_inner(addr: libc::in6_addr) -> Ipv6Addr {
|
||||
Ipv6Addr { inner: addr }
|
||||
}
|
||||
}
|
99
src/libstd/net/mod.rs
Normal file
99
src/libstd/net/mod.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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: Int>(i: I) -> I { i.to_be() }
|
||||
fn ntoh<I: Int>(i: I) -> I { Int::from_be(i) }
|
||||
|
||||
fn each_addr<A: ToSocketAddrs + ?Sized, F, T>(addr: &A, mut f: F) -> io::Result<T>
|
||||
where F: FnMut(&SocketAddr) -> io::Result<T>
|
||||
{
|
||||
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<SocketAddr>;
|
||||
fn next(&mut self) -> Option<io::Result<SocketAddr>> { 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<LookupHost> {
|
||||
net_imp::lookup_host(host).map(LookupHost)
|
||||
}
|
330
src/libstd/net/parser.rs
Normal file
330
src/libstd/net/parser.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T, F>(&mut self, cb: F) -> Option<T> where
|
||||
F: FnOnce(&mut Parser) -> Option<T>,
|
||||
{
|
||||
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<T, F>(&mut self, cb: F) -> Option<T> where
|
||||
F: FnOnce(&mut Parser) -> Option<T>,
|
||||
{
|
||||
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<T>(&mut self, parsers: &mut [Box<FnMut(&mut Parser) -> Option<T>>])
|
||||
-> Option<T> {
|
||||
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<A, B, C, PA, PB, PC>(&mut self,
|
||||
pa: PA,
|
||||
pb: PB,
|
||||
pc: PC)
|
||||
-> Option<(A, B, C)> where
|
||||
PA: FnOnce(&mut Parser) -> Option<A>,
|
||||
PB: FnOnce(&mut Parser) -> Option<B>,
|
||||
PC: FnOnce(&mut Parser) -> Option<C>,
|
||||
{
|
||||
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<char> {
|
||||
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<char> {
|
||||
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<u8> {
|
||||
fn parse_digit(c: char, radix: u8) -> Option<u8> {
|
||||
let c = c as u8;
|
||||
// assuming radix is either 10 or 16
|
||||
if c >= b'0' && c <= b'9' {
|
||||
Some(c - b'0')
|
||||
} else if radix > 10 && c >= b'a' && c < b'a' + (radix - 10) {
|
||||
Some(c - b'a' + 10)
|
||||
} else if radix > 10 && c >= b'A' && c < b'A' + (radix - 10) {
|
||||
Some(c - b'A' + 10)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
self.read_atomically(|p| {
|
||||
p.read_char().and_then(|c| parse_digit(c, radix))
|
||||
})
|
||||
}
|
||||
|
||||
fn read_number_impl(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option<u32> {
|
||||
let mut r = 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<u32> {
|
||||
self.read_atomically(|p| p.read_number_impl(radix, max_digits, upto))
|
||||
}
|
||||
|
||||
fn read_ipv4_addr_impl(&mut self) -> Option<Ipv4Addr> {
|
||||
let mut bs = [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<Ipv4Addr> {
|
||||
self.read_atomically(|p| p.read_ipv4_addr_impl())
|
||||
}
|
||||
|
||||
fn read_ipv6_addr_impl(&mut self) -> Option<Ipv6Addr> {
|
||||
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<Ipv6Addr> {
|
||||
self.read_atomically(|p| p.read_ipv6_addr_impl())
|
||||
}
|
||||
|
||||
fn read_ip_addr(&mut self) -> Option<IpAddr> {
|
||||
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<SocketAddr> {
|
||||
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::<char, Ipv6Addr, char, _, _, _>(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::<IpAddr, char, u16, _, _, _>(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<IpAddr, ParseError> {
|
||||
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<Ipv4Addr, ParseError> {
|
||||
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<Ipv6Addr, ParseError> {
|
||||
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<SocketAddr, ParseError> {
|
||||
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;
|
792
src/libstd/net/tcp.rs
Normal file
792
src/libstd/net/tcp.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<A: ToSocketAddrs + ?Sized>(addr: &A) -> io::Result<TcpStream> {
|
||||
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<SocketAddr> {
|
||||
self.0.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this TCP connection.
|
||||
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
|
||||
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<TcpStream> {
|
||||
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<u32>) -> io::Result<()> {
|
||||
self.0.set_keepalive(seconds)
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for TcpStream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) }
|
||||
}
|
||||
impl Write for TcpStream {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 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<usize> { self.0.read(buf) }
|
||||
}
|
||||
impl<'a> Write for &'a TcpStream {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) }
|
||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||
}
|
||||
|
||||
impl AsInner<net_imp::TcpStream> 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<A: ToSocketAddrs + ?Sized>(addr: &A) -> io::Result<TcpListener> {
|
||||
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<SocketAddr> {
|
||||
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<TcpListener> {
|
||||
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<TcpStream>;
|
||||
fn next(&mut self) -> Option<io::Result<TcpStream>> {
|
||||
Some(self.listener.accept().map(|p| p.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsInner<net_imp::TcpListener> 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();
|
||||
})
|
||||
}
|
||||
}
|
39
src/libstd/net/test.rs
Normal file
39
src/libstd/net/test.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
|
||||
}
|
291
src/libstd/net/udp.rs
Normal file
291
src/libstd/net/udp.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<A: ToSocketAddrs + ?Sized>(addr: &A) -> io::Result<UdpSocket> {
|
||||
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<A: ToSocketAddrs + ?Sized>(&self, buf: &[u8], addr: &A)
|
||||
-> io::Result<usize> {
|
||||
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<SocketAddr> {
|
||||
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<UdpSocket> {
|
||||
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<net_imp::UdpSocket> 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();
|
||||
})
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
393
src/libstd/sys/common/net2.rs
Normal file
393
src/libstd/sys/common/net2.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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: Int>(i: I) -> I { i.to_be() }
|
||||
fn ntoh<I: Int>(i: I) -> I { Int::from_be(i) }
|
||||
|
||||
fn setsockopt<T>(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::<T>() as socklen_t)));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn getsockopt<T: Copy>(sock: &Socket, opt: c_int,
|
||||
val: c_int) -> io::Result<T> {
|
||||
unsafe {
|
||||
let mut slot: T = mem::zeroed();
|
||||
let mut len = mem::size_of::<T>() 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::<T>());
|
||||
Ok(slot)
|
||||
}
|
||||
}
|
||||
|
||||
fn sockname<F>(f: F) -> io::Result<SocketAddr>
|
||||
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<SocketAddr> {
|
||||
match storage.ss_family as libc::c_int {
|
||||
libc::AF_INET => {
|
||||
assert!(len as usize >= mem::size_of::<libc::sockaddr_in>());
|
||||
Ok(FromInner::from_inner(unsafe {
|
||||
*(storage as *const _ as *const libc::sockaddr_in)
|
||||
}))
|
||||
}
|
||||
libc::AF_INET6 => {
|
||||
assert!(len as usize >= mem::size_of::<libc::sockaddr_in6>());
|
||||
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<SocketAddr>;
|
||||
fn next(&mut self) -> Option<io::Result<SocketAddr>> {
|
||||
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<LookupHost> {
|
||||
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<TcpStream> {
|
||||
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<u32>) -> 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<usize> {
|
||||
self.inner.read(buf)
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
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<SocketAddr> {
|
||||
sockname(|buf, len| unsafe {
|
||||
libc::getpeername(*self.inner.as_inner(), buf, len)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
|
||||
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<TcpStream> {
|
||||
self.inner.duplicate().map(|s| TcpStream { inner: s })
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TCP listeners
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct TcpListener {
|
||||
inner: Socket,
|
||||
}
|
||||
|
||||
impl TcpListener {
|
||||
pub fn bind(addr: &SocketAddr) -> io::Result<TcpListener> {
|
||||
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<SocketAddr> {
|
||||
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<TcpListener> {
|
||||
self.inner.duplicate().map(|s| TcpListener { inner: s })
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// UDP
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct UdpSocket {
|
||||
inner: Socket,
|
||||
}
|
||||
|
||||
impl UdpSocket {
|
||||
pub fn bind(addr: &SocketAddr) -> io::Result<UdpSocket> {
|
||||
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<SocketAddr> {
|
||||
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<usize> {
|
||||
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<UdpSocket> {
|
||||
self.inner.duplicate().map(|s| UdpSocket { inner: s })
|
||||
}
|
||||
}
|
@ -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"))]
|
||||
|
@ -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.
|
||||
|
@ -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<c_int> 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
|
||||
|
@ -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;
|
||||
|
74
src/libstd/sys/unix/net.rs
Normal file
74
src/libstd/sys/unix/net.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Socket> {
|
||||
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<Socket> {
|
||||
let fd = try!(cvt_r(|| unsafe {
|
||||
libc::accept(self.0.raw(), storage, len)
|
||||
}));
|
||||
Ok(Socket(FileDesc::new(fd)))
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<Socket> {
|
||||
cvt(unsafe { libc::dup(self.0.raw()) }).map(|fd| {
|
||||
Socket(FileDesc::new(fd))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsInner<c_int> for Socket {
|
||||
fn as_inner(&self) -> &c_int { self.0.as_inner() }
|
||||
}
|
@ -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.
|
||||
|
@ -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;
|
||||
|
121
src/libstd/sys/windows/net.rs
Normal file
121
src/libstd/sys/windows/net.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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: SignedInt>(t: T) -> io::Result<T> {
|
||||
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<T: SignedInt, F>(mut f: F) -> io::Result<T> where F: FnMut() -> T {
|
||||
cvt(f())
|
||||
}
|
||||
|
||||
impl Socket {
|
||||
pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
|
||||
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<Socket> {
|
||||
match unsafe { libc::accept(self.0, storage, len) } {
|
||||
INVALID_SOCKET => Err(last_error()),
|
||||
n => Ok(Socket(n)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<Socket> {
|
||||
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<usize> {
|
||||
// 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<libc::SOCKET> for Socket {
|
||||
fn as_inner(&self) -> &libc::SOCKET { &self.0 }
|
||||
}
|
Loading…
Reference in New Issue
Block a user