Skip to content

TcpListener::bind_to_port and related changes #23161

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/liblibc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2318,6 +2318,7 @@ pub mod consts {
pub const IP_DROP_MEMBERSHIP: c_int = 13;
pub const IPV6_ADD_MEMBERSHIP: c_int = 5;
pub const IPV6_DROP_MEMBERSHIP: c_int = 6;
pub const IPV6_V6ONLY: c_int = 27;
pub const IP_TTL: c_int = 4;
pub const IP_HDRINCL: c_int = 2;

Expand Down Expand Up @@ -3213,6 +3214,7 @@ pub mod consts {
pub const IP_DROP_MEMBERSHIP: c_int = 36;
pub const IPV6_ADD_MEMBERSHIP: c_int = 20;
pub const IPV6_DROP_MEMBERSHIP: c_int = 21;
pub const IPV6_V6ONLY: c_int = 26;

pub const TCP_NODELAY: c_int = 1;
pub const SOL_SOCKET: c_int = 1;
Expand Down Expand Up @@ -3259,6 +3261,7 @@ pub mod consts {
pub const IP_DROP_MEMBERSHIP: c_int = 36;
pub const IPV6_ADD_MEMBERSHIP: c_int = 20;
pub const IPV6_DROP_MEMBERSHIP: c_int = 21;
pub const IPV6_V6ONLY: c_int = 26;

pub const TCP_NODELAY: c_int = 1;
pub const SOL_SOCKET: c_int = 65535;
Expand Down Expand Up @@ -3749,6 +3752,7 @@ pub mod consts {
pub const IP_DROP_MEMBERSHIP: c_int = 13;
pub const IPV6_ADD_MEMBERSHIP: c_int = 12;
pub const IPV6_DROP_MEMBERSHIP: c_int = 13;
pub const IPV6_V6ONLY: c_int = 27;

pub const TCP_NODELAY: c_int = 1;
pub const TCP_KEEPIDLE: c_int = 256;
Expand Down Expand Up @@ -4131,6 +4135,7 @@ pub mod consts {
pub const IP_DROP_MEMBERSHIP: c_int = 13;
pub const IPV6_ADD_MEMBERSHIP: c_int = 12; // don't exist
pub const IPV6_DROP_MEMBERSHIP: c_int = 13; // don't exist
pub const IPV6_V6ONLY: c_int = 27;

pub const TCP_NODELAY: c_int = 0x01;
pub const SOL_SOCKET: c_int = 0xffff;
Expand Down Expand Up @@ -4528,6 +4533,7 @@ pub mod consts {
pub const IP_DROP_MEMBERSHIP: c_int = 13;
pub const IPV6_ADD_MEMBERSHIP: c_int = 12;
pub const IPV6_DROP_MEMBERSHIP: c_int = 13;
pub const IPV6_V6ONLY: c_int = 27;

pub const TCP_NODELAY: c_int = 0x01;
pub const TCP_KEEPALIVE: c_int = 0x10;
Expand Down
72 changes: 70 additions & 2 deletions src/libstd/net/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use prelude::v1::*;
use io::prelude::*;

use io;
use net::{ToSocketAddrs, SocketAddr, Shutdown};
use net::{IpAddr, ToSocketAddrs, SocketAddr, Shutdown};
use sys_common::net2 as net_imp;
use sys_common::AsInner;

Expand Down Expand Up @@ -164,6 +164,23 @@ impl TcpListener {
super::each_addr(addr, net_imp::TcpListener::bind).map(TcpListener)
}

/// Creates a new `TcpListener` that listens on all interfaces and
/// all available protocols (IPv4 and IPv6) on the specified port.
///
/// 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.
///
/// Implementation detail: currently it just binds to `[::]:<port>`
/// and accepts IPv6 sockets. However, it is not guaranteed not to
/// be changed later. For example, on IPv4-only system it could bind
/// on `0.0.0.0:<port>`.
pub fn bind_to_port(port: u16) -> io::Result<TcpListener> {
TcpListener::bind(&(IpAddr::new_v6(0, 0, 0, 0, 0, 0, 0, 0), port))
}

/// Returns the local socket address of this listener.
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
self.0.socket_addr()
Expand Down Expand Up @@ -215,7 +232,7 @@ mod tests {
use io::ErrorKind;
use io::prelude::*;
use net::*;
use net::test::{next_test_ip4, next_test_ip6};
use net::test::{next_test_port, next_test_ip4, next_test_ip6};
use sync::mpsc::channel;
use thread;

Expand Down Expand Up @@ -243,6 +260,57 @@ mod tests {
}
}

#[test]
fn bind_ipv6_connect_ipv4() {
let port = next_test_port();

let acceptor = t!(TcpListener::bind(&("::", port)));

let _t = thread::spawn(move || {
let mut s = t!(acceptor.accept()).0;
t!(s.write(&[10]));
});

let mut buf = [0];
let mut c = t!(TcpStream::connect(&("127.0.0.1", port)));
t!(c.read(&mut buf));
assert!(buf[0] == 10);
}

#[test]
fn bind_to_port_connect_ipv4() {
let port = next_test_port();

let acceptor = t!(TcpListener::bind_to_port(port));

let _t = thread::spawn(move || {
let mut s = t!(acceptor.accept()).0;
t!(s.write(&[10]));
});

let mut buf = [0];
let mut c = t!(TcpStream::connect(&("127.0.0.1", port)));
t!(c.read(&mut buf));
assert!(buf[0] == 10);
}

#[test]
fn bind_to_port_connect_ipv6() {
let port = next_test_port();

let acceptor = t!(TcpListener::bind_to_port(port));

let _t = thread::spawn(move || {
let mut s = t!(acceptor.accept()).0;
t!(s.write(&[10]));
});

let mut buf = [0];
let mut c = t!(TcpStream::connect(&("::1", port)));
t!(c.read(&mut buf));
assert!(buf[0] == 10);
}

#[test]
fn connect_error() {
match TcpStream::connect("0.0.0.0:1") {
Expand Down
10 changes: 6 additions & 4 deletions src/libstd/net/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

static PORT: AtomicUsize = ATOMIC_USIZE_INIT;

pub fn next_test_port() -> u16 {
PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port()
}

pub fn next_test_ip4() -> SocketAddr {
SocketAddr::new(IpAddr::new_v4(127, 0, 0, 1),
PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port())
SocketAddr::new(IpAddr::new_v4(127, 0, 0, 1), next_test_port())
}

pub fn next_test_ip6() -> SocketAddr {
SocketAddr::new(IpAddr::new_v6(0, 0, 0, 0, 0, 0, 0, 1),
PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port())
SocketAddr::new(IpAddr::new_v6(0, 0, 0, 0, 0, 0, 0, 1), next_test_port())
}

// The bots run multiple builds at the same time, and these builds
Expand Down
10 changes: 10 additions & 0 deletions src/libstd/sys/common/net2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,16 @@ impl TcpListener {
1 as c_int));
}

// `IPV6_V6ONLY` is false by default on Linux and OSX, and true
// by default on Windows. `TcpListener::bind` should work the
// same way on all platforms, so setting this option on Windows.
if cfg!(windows) {
if let IpAddr::V6(..) = addr.ip() {
try!(setsockopt(&sock, libc::IPPROTO_IPV6, libc::IPV6_V6ONLY,
0 as c_int));
}
}

// Bind our new socket
let (addrp, len) = addr.into_inner();
try!(cvt(unsafe { libc::bind(*sock.as_inner(), addrp, len) }));
Expand Down