From 827be2de0d753afb3e5a00e66afe6e3c3ac79494 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 27 Feb 2016 14:15:19 -0800 Subject: [PATCH 1/6] Add TCP functionality from net2 --- src/libstd/net/tcp.rs | 231 ++++++++++++++++++++++++++++++++++ src/libstd/sys/common/net.rs | 79 ++++++++++++ src/libstd/sys/unix/net.rs | 48 +++++++ src/libstd/sys/windows/c.rs | 28 +++++ src/libstd/sys/windows/net.rs | 54 ++++++++ 5 files changed, 440 insertions(+) diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index f9c38c38458..b8530c98398 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -180,6 +180,117 @@ impl TcpStream { pub fn write_timeout(&self) -> io::Result> { self.0.write_timeout() } + + /// Sets the value of the `TCP_NODELAY` option on this socket. + /// + /// If set, this option disables the Nagle algorithm. This means that + /// segments are always sent as soon as possible, even if there is only a + /// small amount of data. When not set, data is buffered until there is a + /// sufficient amount to send out, thereby avoiding the frequent sending of + /// small packets. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + self.0.set_nodelay(nodelay) + } + + /// Gets the value of the `TCP_NODELAY` option on this socket. + /// + /// For more information about this option, see [`set_nodelay`][link]. + /// + /// [link]: #tymethod.set_nodelay + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn nodelay(&self) -> io::Result { + self.0.nodelay() + } + + /// Sets whether keepalive messages are enabled to be sent on this socket. + /// + /// On Unix, this option will set the `SO_KEEPALIVE` as well as the + /// `TCP_KEEPALIVE` or `TCP_KEEPIDLE` option (depending on your platform). + /// On Windows, this will set the `SIO_KEEPALIVE_VALS` option. + /// + /// If `None` is specified then keepalive messages are disabled, otherwise + /// the duration specified will be the time to remain idle before sending a + /// TCP keepalive probe. + /// + /// Some platforms specify this value in seconds, so sub-second + /// specifications may be omitted. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_keepalive(&self, keepalive: Option) -> io::Result<()> { + self.0.set_keepalive(keepalive) + } + + /// Returns whether keepalive messages are enabled on this socket, and if so + /// the duration of time between them. + /// + /// For more information about this option, see [`set_keepalive`][link]. + /// + /// [link]: #tymethod.set_keepalive + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn keepalive(&self) -> io::Result> { + self.0.keepalive() + } + + /// Sets the value for the `IP_TTL` option on this socket. + /// + /// This value sets the time-to-live field that is used in every packet sent + /// from this socket. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.0.set_ttl(ttl) + } + + /// Gets the value of the `IP_TTL` option for this socket. + /// + /// For more information about this option, see [`set_ttl`][link]. + /// + /// [link]: #tymethod.set_ttl + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn ttl(&self) -> io::Result { + self.0.ttl() + } + + /// Sets the value for the `IPV6_V6ONLY` option on this socket. + /// + /// If this is set to `true` then the socket is restricted to sending and + /// receiving IPv6 packets only. If this is the case, an IPv4 and an IPv6 + /// application can each bind the same port at the same time. + /// + /// If this is set to `false` then the socket can be used to send and + /// receive packets from an IPv4-mapped IPv6 address. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + self.0.set_only_v6(only_v6) + } + + /// Gets the value of the `IPV6_V6ONLY` option for this socket. + /// + /// For more information about this option, see [`set_only_v6`][link]. + /// + /// [link]: #tymethod.set_only_v6 + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn only_v6(&self) -> io::Result { + self.0.only_v6() + } + + /// Get the value of the `SO_ERROR` option on this socket. + /// + /// This will retrieve the stored error in the underlying socket, clearing + /// the field in the process. This can be useful for checking errors between + /// calls. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Moves this TCP stream into or out of nonblocking mode. + /// + /// On Unix this corresponds to calling fcntl, and on Windows this + /// corresponds to calling ioctlsocket. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -278,6 +389,67 @@ impl TcpListener { pub fn incoming(&self) -> Incoming { Incoming { listener: self } } + + /// Sets the value for the `IP_TTL` option on this socket. + /// + /// This value sets the time-to-live field that is used in every packet sent + /// from this socket. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.0.set_ttl(ttl) + } + + /// Gets the value of the `IP_TTL` option for this socket. + /// + /// For more information about this option, see [`set_ttl`][link]. + /// + /// [link]: #tymethod.set_ttl + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn ttl(&self) -> io::Result { + self.0.ttl() + } + + /// Sets the value for the `IPV6_V6ONLY` option on this socket. + /// + /// If this is set to `true` then the socket is restricted to sending and + /// receiving IPv6 packets only. In this case two IPv4 and IPv6 applications + /// can bind the same port at the same time. + /// + /// If this is set to `false` then the socket can be used to send and + /// receive packets from an IPv4-mapped IPv6 address. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + self.0.set_only_v6(only_v6) + } + + /// Gets the value of the `IPV6_V6ONLY` option for this socket. + /// + /// For more information about this option, see [`set_only_v6`][link]. + /// + /// [link]: #tymethod.set_only_v6 + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn only_v6(&self) -> io::Result { + self.0.only_v6() + } + + /// Get the value of the `SO_ERROR` option on this socket. + /// + /// This will retrieve the stored error in the underlying socket, clearing + /// the field in the process. This can be useful for checking errors between + /// calls. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Moves this TCP stream into or out of nonblocking mode. + /// + /// On Unix this corresponds to calling fcntl, and on Windows this + /// corresponds to calling ioctlsocket. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -969,4 +1141,63 @@ mod tests { assert!(start.elapsed() > Duration::from_millis(400)); drop(listener); } + + #[test] + fn nodelay() { + let addr = next_test_ip4(); + let _listener = t!(TcpListener::bind(&addr)); + + let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + + assert_eq!(false, t!(stream.nodelay())); + t!(stream.set_nodelay(true)); + assert_eq!(true, t!(stream.nodelay())); + t!(stream.set_nodelay(false)); + assert_eq!(false, t!(stream.nodelay())); + } + + #[test] + fn keepalive() { + let addr = next_test_ip4(); + let _listener = t!(TcpListener::bind(&addr)); + + let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let dur = Duration::new(15410, 0); + + assert_eq!(None, t!(stream.keepalive())); + t!(stream.set_keepalive(Some(dur))); + assert_eq!(Some(dur), t!(stream.keepalive())); + t!(stream.set_keepalive(None)); + assert_eq!(None, t!(stream.keepalive())); + } + + #[test] + fn ttl() { + let ttl = 100; + + let addr = next_test_ip4(); + let listener = t!(TcpListener::bind(&addr)); + + t!(listener.set_ttl(ttl)); + assert_eq!(ttl, t!(listener.ttl())); + + let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + + t!(stream.set_ttl(ttl)); + assert_eq!(ttl, t!(stream.ttl())); + } + + #[test] + fn set_nonblocking() { + let addr = next_test_ip4(); + let listener = t!(TcpListener::bind(&addr)); + + t!(listener.set_nonblocking(true)); + t!(listener.set_nonblocking(false)); + + let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + + t!(stream.set_nonblocking(true)); + t!(stream.set_nonblocking(false)); + } } diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index 1cb9303a9fc..0ac4056de8e 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -228,6 +228,54 @@ impl TcpStream { pub fn duplicate(&self) -> io::Result { self.inner.duplicate().map(|s| TcpStream { inner: s }) } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c_int) + } + + pub fn nodelay(&self) -> io::Result { + let raw: c_int = try!(getsockopt(&self.inner, c::IPPROTO_TCP, c::TCP_NODELAY)); + Ok(raw != 0) + } + + pub fn set_keepalive(&self, keepalive: Option) -> io::Result<()> { + self.inner.set_keepalive(keepalive) + } + + pub fn keepalive(&self) -> io::Result> { + self.inner.keepalive() + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + } + + pub fn ttl(&self) -> io::Result { + let raw: c_int = try!(getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)); + Ok(raw as u32) + } + + pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY, only_v6 as c_int) + } + + pub fn only_v6(&self) -> io::Result { + let raw: c_int = try!(getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY)); + Ok(raw != 0) + } + + pub fn take_error(&self) -> io::Result> { + let raw: c_int = try!(getsockopt(&self.inner, c::SOL_SOCKET, c::SO_ERROR)); + if raw == 0 { + Ok(None) + } else { + Ok(Some(io::Error::from_raw_os_error(raw as i32))) + } + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.inner.set_nonblocking(nonblocking) + } } impl FromInner for TcpStream { @@ -307,6 +355,37 @@ impl TcpListener { pub fn duplicate(&self) -> io::Result { self.inner.duplicate().map(|s| TcpListener { inner: s }) } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + } + + pub fn ttl(&self) -> io::Result { + let raw: c_int = try!(getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)); + Ok(raw as u32) + } + + pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY, only_v6 as c_int) + } + + pub fn only_v6(&self) -> io::Result { + let raw: c_int = try!(getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY)); + Ok(raw != 0) + } + + pub fn take_error(&self) -> io::Result> { + let raw: c_int = try!(getsockopt(&self.inner, c::SOL_SOCKET, c::SO_ERROR)); + if raw == 0 { + Ok(None) + } else { + Ok(Some(io::Error::from_raw_os_error(raw as i32))) + } + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.inner.set_nonblocking(nonblocking) + } } impl FromInner for TcpListener { diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs index 16c369674f0..7a2ac7257af 100644 --- a/src/libstd/sys/unix/net.rs +++ b/src/libstd/sys/unix/net.rs @@ -35,6 +35,16 @@ use libc::SOCK_CLOEXEC; #[cfg(not(target_os = "linux"))] const SOCK_CLOEXEC: c_int = 0; +#[cfg(any(target_os = "openbsd", taret_os = "freebsd"))] +use libc::SO_KEEPALIVE as TCP_KEEPALIVE; +#[cfg(any(target_os = "macos", taret_os = "ios"))] +use libc::TCP_KEEPALIVE; +#[cfg(not(any(target_os = "openbsd", + target_os = "freebsd", + target_os = "macos", + target_os = "ios")))] +use libc::TCP_KEEPIDLE as TCP_KEEPALIVE; + pub struct Socket(FileDesc); pub fn init() {} @@ -168,6 +178,44 @@ impl Socket { try!(cvt(unsafe { libc::shutdown(self.0.raw(), how) })); Ok(()) } + + pub fn set_keepalive(&self, keepalive: Option) -> io::Result<()> { + try!(setsockopt(self, + libc::SOL_SOCKET, + libc::SO_KEEPALIVE, + keepalive.is_some() as libc::c_int)); + if let Some(dur) = keepalive { + let mut raw = dur.as_secs(); + if dur.subsec_nanos() > 0 { + raw = raw.saturating_add(1); + } + + let raw = if raw > libc::c_int::max_value() as u64 { + libc::c_int::max_value() + } else { + raw as libc::c_int + }; + + try!(setsockopt(self, libc::IPPROTO_TCP, TCP_KEEPALIVE, raw)); + } + + Ok(()) + } + + pub fn keepalive(&self) -> io::Result> { + let raw: c_int = try!(getsockopt(self, libc::SOL_SOCKET, libc::SO_KEEPALIVE)); + if raw == 0 { + return Ok(None); + } + + let raw: c_int = try!(getsockopt(self, libc::IPPROTO_TCP, TCP_KEEPALIVE)); + Ok(Some(Duration::from_secs(raw as u64))) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as libc::c_ulong; + cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(|_| ()) + } } impl AsInner for Socket { diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 5cbfec01bed..cc420763fd7 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -78,6 +78,13 @@ pub type SOCKET = ::os::windows::raw::SOCKET; pub type socklen_t = c_int; pub type ADDRESS_FAMILY = USHORT; +pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = + Option; +pub type LPWSAOVERLAPPED = *mut OVERLAPPED; + pub const TRUE: BOOL = 1; pub const FALSE: BOOL = 0; @@ -114,6 +121,9 @@ pub const FILE_FLAG_OPEN_REPARSE_POINT: DWORD = 0x00200000; pub const FILE_FLAG_BACKUP_SEMANTICS: DWORD = 0x02000000; pub const SECURITY_SQOS_PRESENT: DWORD = 0x00100000; +pub const SIO_KEEPALIVE_VALS: DWORD = 0x98000004; +pub const FIONBIO: c_ulong = 0x8004667e; + #[repr(C)] #[derive(Copy)] pub struct WIN32_FIND_DATAW { @@ -775,6 +785,13 @@ pub struct in6_addr { pub s6_addr: [u8; 16], } +#[repr(C)] +pub struct tcp_keepalive { + pub onoff: c_ulong, + pub keepalivetime: c_ulong, + pub keepaliveinterval: c_ulong, +} + #[cfg(all(target_arch = "x86_64", target_env = "gnu"))] pub enum UNWIND_HISTORY_TABLE {} @@ -833,6 +850,17 @@ extern "system" { lpProtocolInfo: LPWSAPROTOCOL_INFO, g: GROUP, dwFlags: DWORD) -> SOCKET; + pub fn WSAIoctl(s: SOCKET, + dwIoControlCode: DWORD, + lpvInBuffer: LPVOID, + cbInBuffer: DWORD, + lpvOutBuffer: LPVOID, + cbOutBuffer: DWORD, + lpcbBytesReturned: LPDWORD, + lpOverlapped: LPWSAOVERLAPPED, + lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE) + -> c_int; + pub fn ioctlsocket(s: SOCKET, cmd: c_long, argp: *mut u_long) -> c_int; pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION); pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION); pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOLEAN; diff --git a/src/libstd/sys/windows/net.rs b/src/libstd/sys/windows/net.rs index 49ba8e9c659..be13657aaf4 100644 --- a/src/libstd/sys/windows/net.rs +++ b/src/libstd/sys/windows/net.rs @@ -185,6 +185,60 @@ impl Socket { try!(cvt(unsafe { c::shutdown(self.0, how) })); Ok(()) } + + pub fn set_keepalive(&self, keepalive: Option) -> io::Result<()> { + let ms = keepalive.map(sys::dur2timeout).unwrap_or(c::INFINITE); + let ka = c::tcp_keepalive { + onoff: keepalive.is_some() as c::c_ulong, + keepalivetime: ms as c::c_ulong, + keepaliveinterval: ms as c::c_ulong, + }; + sys::cvt(unsafe { + c::WSAIoctl(self.0, + c::SIO_KEEPALIVE_VALS, + &ka as *const _ as *mut _, + mem::size_of_val(&ka) as c::DWORD, + 0 as *mut _, + 0, + 0 as *mut _, + 0 as *mut _, + None) + }).map(|_| ()) + } + + pub fn keepalive(&self) -> io::Result> { + let mut ka = c::tcp_keepalive { + onoff: 0, + keepalivetime: 0, + keepaliveinterval: 0, + }; + try!(sys::cvt(unsafe { + WSAIoctl(self.0, + c::SIO_KEEPALIVE_VALS, + 0 as *mut _, + 0, + &mut ka as *mut _ as *mut _, + mem::size_of_val(&ka) as c::DWORD, + 0 as *mut _, + 0 as *mut _, + None) + })); + + if ka.onoff == 0 { + Ok(None) + } else { + let secs = ka.keepaliveinterval / 1000; + let nsec = (ka.keepaliveinterval % 1000) * 1000000; + Ok(Some(Duration::new(secs as u64, nsec as u32))) + } + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as c::c_ulong; + sys::cvt(unsafe { + c::ioctlsocket(self.0, c::FIONBIO as c::c_int, &mut nonblocking) + }).map(|_| ()) + } } impl Drop for Socket { From 5d6ba17f0308d3b8c96cd89f4c0663bae0f2b9f7 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 27 Feb 2016 15:02:48 -0800 Subject: [PATCH 2/6] Add UDP functionality from net2 --- src/libstd/net/udp.rs | 253 ++++++++++++++++++++++++++++++++++- src/libstd/sys/common/net.rs | 138 ++++++++++++++++++- 2 files changed, 387 insertions(+), 4 deletions(-) diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index 29ac991a547..bce82565fdf 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -10,7 +10,7 @@ use fmt; use io::{self, Error, ErrorKind}; -use net::{ToSocketAddrs, SocketAddr}; +use net::{ToSocketAddrs, SocketAddr, Ipv4Addr, Ipv6Addr}; use sys_common::net as net_imp; use sys_common::{AsInner, FromInner, IntoInner}; use time::Duration; @@ -140,6 +140,221 @@ impl UdpSocket { pub fn write_timeout(&self) -> io::Result> { self.0.write_timeout() } + + /// Sets the value of the `SO_BROADCAST` option for this socket. + /// + /// When enabled, this socket is allowed to send packets to a broadcast + /// address. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { + self.0.set_broadcast(broadcast) + } + + /// Gets the value of the `SO_BROADCAST` option for this socket. + /// + /// For more information about this option, see + /// [`set_broadcast`][link]. + /// + /// [link]: #tymethod.set_broadcast + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn broadcast(&self) -> io::Result { + self.0.broadcast() + } + + /// Sets the value of the `IP_MULTICAST_LOOP` option for this socket. + /// + /// If enabled, multicast packets will be looped back to the local socket. + /// Note that this may not have any affect on IPv6 sockets. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { + self.0.set_multicast_loop_v4(multicast_loop_v4) + } + + /// Gets the value of the `IP_MULTICAST_LOOP` option for this socket. + /// + /// For more information about this option, see + /// [`set_multicast_loop_v4`][link]. + /// + /// [link]: #tymethod.set_multicast_loop_v4 + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn multicast_loop_v4(&self) -> io::Result { + self.0.multicast_loop_v4() + } + + /// Sets the value of the `IP_MULTICAST_TTL` option for this socket. + /// + /// Indicates the time-to-live value of outgoing multicast packets for + /// this socket. The default value is 1 which means that multicast packets + /// don't leave the local network unless explicitly requested. + /// + /// Note that this may not have any affect on IPv6 sockets. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { + self.0.set_multicast_ttl_v4(multicast_ttl_v4) + } + + /// Gets the value of the `IP_MULTICAST_TTL` option for this socket. + /// + /// For more information about this option, see + /// [`set_multicast_ttl_v4`][link]. + /// + /// [link]: #tymethod.set_multicast_ttl_v4 + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn multicast_ttl_v4(&self) -> io::Result { + self.0.multicast_ttl_v4() + } + + /// Sets the value of the `IPV6_MULTICAST_LOOP` option for this socket. + /// + /// Controls whether this socket sees the multicast packets it sends itself. + /// Note that this may not have any affect on IPv4 sockets. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { + self.0.set_multicast_loop_v6(multicast_loop_v6) + } + + /// Gets the value of the `IPV6_MULTICAST_LOOP` option for this socket. + /// + /// For more information about this option, see + /// [`set_multicast_loop_v6`][link]. + /// + /// [link]: #tymethod.set_multicast_loop_v6 + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn multicast_loop_v6(&self) -> io::Result { + self.0.multicast_loop_v6() + } + + /// Sets the value for the `IP_TTL` option on this socket. + /// + /// This value sets the time-to-live field that is used in every packet sent + /// from this socket. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.0.set_ttl(ttl) + } + + /// Gets the value of the `IP_TTL` option for this socket. + /// + /// For more information about this option, see [`set_ttl`][link]. + /// + /// [link]: #tymethod.set_ttl + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn ttl(&self) -> io::Result { + self.0.ttl() + } + + /// Sets the value for the `IPV6_V6ONLY` option on this socket. + /// + /// If this is set to `true` then the socket is restricted to sending and + /// receiving IPv6 packets only. If this is the case, an IPv4 and an IPv6 + /// application can each bind the same port at the same time. + /// + /// If this is set to `false` then the socket can be used to send and + /// receive packets from an IPv4-mapped IPv6 address. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + self.0.set_only_v6(only_v6) + } + + /// Gets the value of the `IPV6_V6ONLY` option for this socket. + /// + /// For more information about this option, see [`set_only_v6`][link]. + /// + /// [link]: #tymethod.set_only_v6 + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn only_v6(&self) -> io::Result { + self.0.only_v6() + } + + /// Executes an operation of the `IP_ADD_MEMBERSHIP` type. + /// + /// This function specifies a new multicast group for this socket to join. + /// The address must be a valid multicast address, and `interface` is the + /// address of the local interface with which the system should join the + /// multicast group. If it's equal to `INADDR_ANY` then an appropriate + /// interface is chosen by the system. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + self.0.join_multicast_v4(multiaddr, interface) + } + + /// Executes an operation of the `IPV6_ADD_MEMBERSHIP` type. + /// + /// This function specifies a new multicast group for this socket to join. + /// The address must be a valid multicast address, and `interface` is the + /// index of the interface to join/leave (or 0 to indicate any interface). + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + self.0.join_multicast_v6(multiaddr, interface) + } + + /// Executes an operation of the `IP_DROP_MEMBERSHIP` type. + /// + /// For more information about this option, see + /// [`join_multicast_v4`][link]. + /// + /// [link]: #tymethod.join_multicast_v4 + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + self.0.leave_multicast_v4(multiaddr, interface) + } + + /// Executes an operation of the `IPV6_DROP_MEMBERSHIP` type. + /// + /// For more information about this option, see + /// [`join_multicast_v6`][link]. + /// + /// [link]: #tymethod.join_multicast_v6 + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + self.0.leave_multicast_v6(multiaddr, interface) + } + + /// Get the value of the `SO_ERROR` option on this socket. + /// + /// This will retrieve the stored error in the underlying socket, clearing + /// the field in the process. This can be useful for checking errors between + /// calls. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Connects this UDP socket to a remote address, allowing the `send` and + /// `recv` syscalls to be used to send data and also applies filters to only + /// receive data from the specified address. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn connect(&self, addr: A) -> io::Result<()> { + super::each_addr(addr, |addr| self.0.connect(addr)) + } + + /// Sends data on the socket to the remote address to which it is connected. + /// + /// The `connect` method will connect this socket to a remote address. This + /// method will fail if the socket is not connected. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn send(&self, buf: &[u8]) -> io::Result { + self.0.send(buf) + } + + /// Receives data on the socket from the remote address to which it is + /// connected. + /// + /// The `connect` method will connect this socket to a remote address. This + /// method will fail if the socket is not connected. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn recv(&self, buf: &mut [u8]) -> io::Result { + self.0.recv(buf) + } + + /// Moves this TCP stream into or out of nonblocking mode. + /// + /// On Unix this corresponds to calling fcntl, and on Windows this + /// corresponds to calling ioctlsocket. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } } impl AsInner for UdpSocket { @@ -400,4 +615,40 @@ mod tests { assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut); assert!(start.elapsed() > Duration::from_millis(400)); } + + #[test] + fn connect_send_recv() { + let addr = next_test_ip4(); + + let socket = t!(UdpSocket::bind(&addr)); + t!(socket.connect(addr)); + + t!(socket.send(b"hello world")); + + let mut buf = [0; 11]; + t!(socket.recv(&mut buf)); + assert_eq!(b"hello world", &buf[..]); + } + + #[test] + fn ttl() { + let ttl = 100; + + let addr = next_test_ip4(); + + let stream = t!(UdpSocket::bind(&addr)); + + t!(stream.set_ttl(ttl)); + assert_eq!(ttl, t!(stream.ttl())); + } + + #[test] + fn set_nonblocking() { + let addr = next_test_ip4(); + + let stream = t!(UdpSocket::bind(&addr)); + + t!(stream.set_nonblocking(true)); + t!(stream.set_nonblocking(false)); + } } diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index 0ac4056de8e..31d3be45372 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -17,7 +17,7 @@ use io::{self, Error, ErrorKind}; use libc::{c_int, c_char, c_void}; use mem; #[allow(deprecated)] -use net::{SocketAddr, Shutdown, IpAddr}; +use net::{SocketAddr, Shutdown, IpAddr, Ipv4Addr, Ipv6Addr}; use ptr; use str::from_utf8; use sys::net::{cvt, cvt_r, cvt_gai, Socket, init, wrlen_t}; @@ -40,8 +40,7 @@ pub fn setsockopt(sock: &Socket, opt: c_int, val: c_int, } pub fn getsockopt(sock: &Socket, opt: c_int, - val: c_int) -> io::Result { - unsafe { + val: c_int) -> io::Result { unsafe { let mut slot: T = mem::zeroed(); let mut len = mem::size_of::() as c::socklen_t; try!(cvt(c::getsockopt(*sock.as_inner(), opt, val, @@ -84,6 +83,16 @@ fn sockaddr_to_addr(storage: &c::sockaddr_storage, } } +#[cfg(target_os = "android")] +fn to_ipv6mr_interface(value: u32) -> c::c_int { + value as c::c_int +} + +#[cfg(not(target_os = "android"))] +fn to_ipv6mr_interface(value: u32) -> c::c_uint { + value as c::c_uint +} + //////////////////////////////////////////////////////////////////////////////// // get_host_addresses //////////////////////////////////////////////////////////////////////////////// @@ -480,6 +489,129 @@ impl UdpSocket { pub fn write_timeout(&self) -> io::Result> { self.inner.timeout(c::SO_SNDTIMEO) } + + pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { + setsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST, broadcast as c_int) + } + + pub fn broadcast(&self) -> io::Result { + let raw: c_int = try!(getsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST)); + Ok(raw != 0) + } + + pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP, multicast_loop_v4 as c_int) + } + + pub fn multicast_loop_v4(&self) -> io::Result { + let raw: c_int = try!(getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP)); + Ok(raw != 0) + } + + pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL, multicast_ttl_v4 as c_int) + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + let raw: c_int = try!(getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL)); + Ok(raw as u32) + } + + pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP, multicast_loop_v6 as c_int) + } + + pub fn multicast_loop_v6(&self) -> io::Result { + let raw: c_int = try!(getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP)); + Ok(raw != 0) + } + + pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) + -> io::Result<()> { + let mreq = c::ip_mreq { + imr_multiaddr: *multiaddr.as_inner(), + imr_interface: *interface.as_inner(), + }; + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq) + } + + pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) + -> io::Result<()> { + let mreq = c::ipv6_mreq { + ipv6mr_multiaddr: *multiaddr.as_inner(), + ipv6mr_interface: to_ipv6mr_interface(interface), + }; + setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_ADD_MEMBERSHIP, mreq) + } + + pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) + -> io::Result<()> { + let mreq = c::ip_mreq { + imr_multiaddr: *multiaddr.as_inner(), + imr_interface: *interface.as_inner(), + }; + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq) + } + + pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) + -> io::Result<()> { + let mreq = c::ipv6_mreq { + ipv6mr_multiaddr: *multiaddr.as_inner(), + ipv6mr_interface: to_ipv6mr_interface(interface), + }; + setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_DROP_MEMBERSHIP, mreq) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + } + + pub fn ttl(&self) -> io::Result { + let raw: c_int = try!(getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)); + Ok(raw as u32) + } + + pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY, only_v6 as c_int) + } + + pub fn only_v6(&self) -> io::Result { + let raw: c_int = try!(getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY)); + Ok(raw != 0) + } + + pub fn take_error(&self) -> io::Result> { + let raw: c_int = try!(getsockopt(&self.inner, c::SOL_SOCKET, c::SO_ERROR)); + if raw == 0 { + Ok(None) + } else { + Ok(Some(io::Error::from_raw_os_error(raw as i32))) + } + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.inner.set_nonblocking(nonblocking) + } + + pub fn recv(&self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + pub fn send(&self, buf: &[u8]) -> io::Result { + let len = cmp::min(buf.len(), ::max_value() as usize) as wrlen_t; + let ret = try!(cvt(unsafe { + c::send(*self.inner.as_inner(), + buf.as_ptr() as *const c_void, + len, + 0) + })); + Ok(ret as usize) + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addrp, len) = addr.into_inner(); + cvt_r(|| unsafe { c::connect(*self.inner.as_inner(), addrp, len) }).map(|_| ()) + } } impl FromInner for UdpSocket { From 728d9115e894bd3c8fc3ae03230ea46f85467c04 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 27 Feb 2016 21:05:32 -0800 Subject: [PATCH 3/6] Fix windows Also back out keepalive support for TCP since the API is perhaps not actually what we want. You can't read the interval on Windows, and we should probably separate the functionality of turning keepalive on and overriding the interval. --- src/libstd/net/tcp.rs | 43 ------------------------ src/libstd/sys/common/net.rs | 23 ++++--------- src/libstd/sys/unix/net.rs | 44 +++--------------------- src/libstd/sys/windows/c.rs | 56 ++++++++++++++++--------------- src/libstd/sys/windows/net.rs | 63 ++++++++--------------------------- 5 files changed, 55 insertions(+), 174 deletions(-) diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index b8530c98398..0073b8f119a 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -203,34 +203,6 @@ impl TcpStream { self.0.nodelay() } - /// Sets whether keepalive messages are enabled to be sent on this socket. - /// - /// On Unix, this option will set the `SO_KEEPALIVE` as well as the - /// `TCP_KEEPALIVE` or `TCP_KEEPIDLE` option (depending on your platform). - /// On Windows, this will set the `SIO_KEEPALIVE_VALS` option. - /// - /// If `None` is specified then keepalive messages are disabled, otherwise - /// the duration specified will be the time to remain idle before sending a - /// TCP keepalive probe. - /// - /// Some platforms specify this value in seconds, so sub-second - /// specifications may be omitted. - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_keepalive(&self, keepalive: Option) -> io::Result<()> { - self.0.set_keepalive(keepalive) - } - - /// Returns whether keepalive messages are enabled on this socket, and if so - /// the duration of time between them. - /// - /// For more information about this option, see [`set_keepalive`][link]. - /// - /// [link]: #tymethod.set_keepalive - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn keepalive(&self) -> io::Result> { - self.0.keepalive() - } - /// Sets the value for the `IP_TTL` option on this socket. /// /// This value sets the time-to-live field that is used in every packet sent @@ -1156,21 +1128,6 @@ mod tests { assert_eq!(false, t!(stream.nodelay())); } - #[test] - fn keepalive() { - let addr = next_test_ip4(); - let _listener = t!(TcpListener::bind(&addr)); - - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); - let dur = Duration::new(15410, 0); - - assert_eq!(None, t!(stream.keepalive())); - t!(stream.set_keepalive(Some(dur))); - assert_eq!(Some(dur), t!(stream.keepalive())); - t!(stream.set_keepalive(None)); - assert_eq!(None, t!(stream.keepalive())); - } - #[test] fn ttl() { let ttl = 100; diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index 31d3be45372..c8738fd1ba5 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -14,7 +14,7 @@ use cmp; use ffi::{CStr, CString}; use fmt; use io::{self, Error, ErrorKind}; -use libc::{c_int, c_char, c_void}; +use libc::{c_int, c_char, c_void, c_uint}; use mem; #[allow(deprecated)] use net::{SocketAddr, Shutdown, IpAddr, Ipv4Addr, Ipv6Addr}; @@ -84,13 +84,13 @@ fn sockaddr_to_addr(storage: &c::sockaddr_storage, } #[cfg(target_os = "android")] -fn to_ipv6mr_interface(value: u32) -> c::c_int { - value as c::c_int +fn to_ipv6mr_interface(value: u32) -> c_int { + value as c_int } #[cfg(not(target_os = "android"))] -fn to_ipv6mr_interface(value: u32) -> c::c_uint { - value as c::c_uint +fn to_ipv6mr_interface(value: u32) -> c_uint { + value as c_uint } //////////////////////////////////////////////////////////////////////////////// @@ -239,20 +239,11 @@ impl TcpStream { } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c_int) + self.inner.set_nodelay(nodelay) } pub fn nodelay(&self) -> io::Result { - let raw: c_int = try!(getsockopt(&self.inner, c::IPPROTO_TCP, c::TCP_NODELAY)); - Ok(raw != 0) - } - - pub fn set_keepalive(&self, keepalive: Option) -> io::Result<()> { - self.inner.set_keepalive(keepalive) - } - - pub fn keepalive(&self) -> io::Result> { - self.inner.keepalive() + self.inner.nodelay() } pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs index 7a2ac7257af..8785da51986 100644 --- a/src/libstd/sys/unix/net.rs +++ b/src/libstd/sys/unix/net.rs @@ -35,16 +35,6 @@ use libc::SOCK_CLOEXEC; #[cfg(not(target_os = "linux"))] const SOCK_CLOEXEC: c_int = 0; -#[cfg(any(target_os = "openbsd", taret_os = "freebsd"))] -use libc::SO_KEEPALIVE as TCP_KEEPALIVE; -#[cfg(any(target_os = "macos", taret_os = "ios"))] -use libc::TCP_KEEPALIVE; -#[cfg(not(any(target_os = "openbsd", - target_os = "freebsd", - target_os = "macos", - target_os = "ios")))] -use libc::TCP_KEEPIDLE as TCP_KEEPALIVE; - pub struct Socket(FileDesc); pub fn init() {} @@ -179,37 +169,13 @@ impl Socket { Ok(()) } - pub fn set_keepalive(&self, keepalive: Option) -> io::Result<()> { - try!(setsockopt(self, - libc::SOL_SOCKET, - libc::SO_KEEPALIVE, - keepalive.is_some() as libc::c_int)); - if let Some(dur) = keepalive { - let mut raw = dur.as_secs(); - if dur.subsec_nanos() > 0 { - raw = raw.saturating_add(1); - } - - let raw = if raw > libc::c_int::max_value() as u64 { - libc::c_int::max_value() - } else { - raw as libc::c_int - }; - - try!(setsockopt(self, libc::IPPROTO_TCP, TCP_KEEPALIVE, raw)); - } - - Ok(()) + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) } - pub fn keepalive(&self) -> io::Result> { - let raw: c_int = try!(getsockopt(self, libc::SOL_SOCKET, libc::SO_KEEPALIVE)); - if raw == 0 { - return Ok(None); - } - - let raw: c_int = try!(getsockopt(self, libc::IPPROTO_TCP, TCP_KEEPALIVE)); - Ok(Some(Duration::from_secs(raw as u64))) + pub fn nodelay(&self) -> io::Result { + let raw: c_int = try!(getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)); + Ok(raw != 0) } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index cc420763fd7..472ffdf9e1d 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -13,7 +13,7 @@ #![allow(bad_style)] #![cfg_attr(test, allow(dead_code))] -use os::raw::{c_int, c_uint, c_ulong, c_long, c_longlong, c_ushort}; +use os::raw::{c_int, c_uint, c_ulong, c_long, c_longlong, c_ushort,}; use os::raw::{c_char, c_ulonglong}; use libc::{wchar_t, size_t, c_void}; use ptr; @@ -78,13 +78,6 @@ pub type SOCKET = ::os::windows::raw::SOCKET; pub type socklen_t = c_int; pub type ADDRESS_FAMILY = USHORT; -pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = - Option; -pub type LPWSAOVERLAPPED = *mut OVERLAPPED; - pub const TRUE: BOOL = 1; pub const FALSE: BOOL = 0; @@ -121,7 +114,6 @@ pub const FILE_FLAG_OPEN_REPARSE_POINT: DWORD = 0x00200000; pub const FILE_FLAG_BACKUP_SEMANTICS: DWORD = 0x02000000; pub const SECURITY_SQOS_PRESENT: DWORD = 0x00100000; -pub const SIO_KEEPALIVE_VALS: DWORD = 0x98000004; pub const FIONBIO: c_ulong = 0x8004667e; #[repr(C)] @@ -233,6 +225,33 @@ pub const SOL_SOCKET: c_int = 0xffff; pub const SO_RCVTIMEO: c_int = 0x1006; pub const SO_SNDTIMEO: c_int = 0x1005; pub const SO_REUSEADDR: c_int = 0x0004; +pub const IPPROTO_IP: c_int = 0; +pub const IPPROTO_TCP: c_int = 6; +pub const IPPROTO_IPV6: c_int = 41; +pub const TCP_NODELAY: c_int = 0x0001; +pub const IP_TTL: c_int = 4; +pub const IPV6_V6ONLY: c_int = 27; +pub const SO_ERROR: c_int = 0x1007; +pub const SO_BROADCAST: c_int = 0x0020; +pub const IP_MULTICAST_LOOP: c_int = 11; +pub const IPV6_MULTICAST_LOOP: c_int = 11; +pub const IP_MULTICAST_TTL: c_int = 10; +pub const IP_ADD_MEMBERSHIP: c_int = 12; +pub const IP_DROP_MEMBERSHIP: c_int = 13; +pub const IPV6_ADD_MEMBERSHIP: c_int = 12; +pub const IPV6_DROP_MEMBERSHIP: c_int = 13; + +#[repr(C)] +pub struct ip_mreq { + pub imr_multiaddr: in_addr, + pub imr_interface: in_addr, +} + +#[repr(C)] +pub struct ipv6_mreq { + pub ipv6mr_multiaddr: in6_addr, + pub ipv6mr_interface: c_uint, +} pub const VOLUME_NAME_DOS: DWORD = 0x0; pub const MOVEFILE_REPLACE_EXISTING: DWORD = 1; @@ -785,13 +804,6 @@ pub struct in6_addr { pub s6_addr: [u8; 16], } -#[repr(C)] -pub struct tcp_keepalive { - pub onoff: c_ulong, - pub keepalivetime: c_ulong, - pub keepaliveinterval: c_ulong, -} - #[cfg(all(target_arch = "x86_64", target_env = "gnu"))] pub enum UNWIND_HISTORY_TABLE {} @@ -850,17 +862,7 @@ extern "system" { lpProtocolInfo: LPWSAPROTOCOL_INFO, g: GROUP, dwFlags: DWORD) -> SOCKET; - pub fn WSAIoctl(s: SOCKET, - dwIoControlCode: DWORD, - lpvInBuffer: LPVOID, - cbInBuffer: DWORD, - lpvOutBuffer: LPVOID, - cbOutBuffer: DWORD, - lpcbBytesReturned: LPDWORD, - lpOverlapped: LPWSAOVERLAPPED, - lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE) - -> c_int; - pub fn ioctlsocket(s: SOCKET, cmd: c_long, argp: *mut u_long) -> c_int; + pub fn ioctlsocket(s: SOCKET, cmd: c_long, argp: *mut c_ulong) -> c_int; pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION); pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION); pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOLEAN; diff --git a/src/libstd/sys/windows/net.rs b/src/libstd/sys/windows/net.rs index be13657aaf4..dfa44a651e6 100644 --- a/src/libstd/sys/windows/net.rs +++ b/src/libstd/sys/windows/net.rs @@ -10,7 +10,7 @@ use cmp; use io; -use libc::{c_int, c_void}; +use libc::{c_int, c_void, c_ulong}; use mem; use net::{SocketAddr, Shutdown}; use num::One; @@ -186,58 +186,23 @@ impl Socket { Ok(()) } - pub fn set_keepalive(&self, keepalive: Option) -> io::Result<()> { - let ms = keepalive.map(sys::dur2timeout).unwrap_or(c::INFINITE); - let ka = c::tcp_keepalive { - onoff: keepalive.is_some() as c::c_ulong, - keepalivetime: ms as c::c_ulong, - keepaliveinterval: ms as c::c_ulong, - }; - sys::cvt(unsafe { - c::WSAIoctl(self.0, - c::SIO_KEEPALIVE_VALS, - &ka as *const _ as *mut _, - mem::size_of_val(&ka) as c::DWORD, - 0 as *mut _, - 0, - 0 as *mut _, - 0 as *mut _, - None) - }).map(|_| ()) - } - - pub fn keepalive(&self) -> io::Result> { - let mut ka = c::tcp_keepalive { - onoff: 0, - keepalivetime: 0, - keepaliveinterval: 0, - }; - try!(sys::cvt(unsafe { - WSAIoctl(self.0, - c::SIO_KEEPALIVE_VALS, - 0 as *mut _, - 0, - &mut ka as *mut _ as *mut _, - mem::size_of_val(&ka) as c::DWORD, - 0 as *mut _, - 0 as *mut _, - None) - })); - - if ka.onoff == 0 { - Ok(None) + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as c_ulong; + let r = unsafe { c::ioctlsocket(self.0, c::FIONBIO as c_int, &mut nonblocking) }; + if r == 0 { + Ok(()) } else { - let secs = ka.keepaliveinterval / 1000; - let nsec = (ka.keepaliveinterval % 1000) * 1000000; - Ok(Some(Duration::new(secs as u64, nsec as u32))) + Err(io::Error::last_os_error()) } } - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as c::c_ulong; - sys::cvt(unsafe { - c::ioctlsocket(self.0, c::FIONBIO as c::c_int, &mut nonblocking) - }).map(|_| ()) + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BYTE) + } + + pub fn nodelay(&self) -> io::Result { + let raw: c::BYTE = try!(net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)); + Ok(raw != 0) } } From 631fa2b8c059ecf22b69941d700d9c9fce0ed47f Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Wed, 2 Mar 2016 22:05:14 -0800 Subject: [PATCH 4/6] Fix comments and OSX build --- src/libstd/net/tcp.rs | 11 +++++++++-- src/libstd/sys/common/net.rs | 24 +++++++++++++++++++++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index 0073b8f119a..f8e3b58bb3e 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -1152,9 +1152,16 @@ mod tests { t!(listener.set_nonblocking(true)); t!(listener.set_nonblocking(false)); - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); - t!(stream.set_nonblocking(true)); t!(stream.set_nonblocking(false)); + t!(stream.set_nonblocking(true)); + + let mut buf = [0]; + match stream.read(&mut buf) { + Ok(_) => panic!("expected error"), + Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} + Err(e) => panic!("unexpected error {}", e), + } } } diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index c8738fd1ba5..d16380a96b2 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -25,6 +25,23 @@ use sys::net::netc as c; use sys_common::{AsInner, FromInner, IntoInner}; use time::Duration; +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "openbsd"))] +use sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; +#[cfg(not(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "openbsd")))] +use sys::net::netc::IPV6_ADD_MEMBERSHIP; +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "openbsd"))] +use sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; +#[cfg(not(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "openbsd")))] +use sys::net::netc::IPV6_DROP_MEMBERSHIP; + //////////////////////////////////////////////////////////////////////////////// // sockaddr and misc bindings //////////////////////////////////////////////////////////////////////////////// @@ -40,7 +57,8 @@ pub fn setsockopt(sock: &Socket, opt: c_int, val: c_int, } pub fn getsockopt(sock: &Socket, opt: c_int, - val: c_int) -> io::Result { unsafe { + val: c_int) -> io::Result { + unsafe { let mut slot: T = mem::zeroed(); let mut len = mem::size_of::() as c::socklen_t; try!(cvt(c::getsockopt(*sock.as_inner(), opt, val, @@ -532,7 +550,7 @@ impl UdpSocket { ipv6mr_multiaddr: *multiaddr.as_inner(), ipv6mr_interface: to_ipv6mr_interface(interface), }; - setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_ADD_MEMBERSHIP, mreq) + setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) } pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) @@ -550,7 +568,7 @@ impl UdpSocket { ipv6mr_multiaddr: *multiaddr.as_inner(), ipv6mr_interface: to_ipv6mr_interface(interface), }; - setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_DROP_MEMBERSHIP, mreq) + setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) } pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { From ee62aab2edc1897d587dc43ff379a69b185e3be7 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 3 Mar 2016 08:11:07 -0800 Subject: [PATCH 5/6] Fix android build --- src/libstd/sys/common/net.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index d16380a96b2..bc6bbc38e14 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -14,7 +14,7 @@ use cmp; use ffi::{CStr, CString}; use fmt; use io::{self, Error, ErrorKind}; -use libc::{c_int, c_char, c_void, c_uint}; +use libc::{c_int, c_char, c_void}; use mem; #[allow(deprecated)] use net::{SocketAddr, Shutdown, IpAddr, Ipv4Addr, Ipv6Addr}; @@ -107,8 +107,8 @@ fn to_ipv6mr_interface(value: u32) -> c_int { } #[cfg(not(target_os = "android"))] -fn to_ipv6mr_interface(value: u32) -> c_uint { - value as c_uint +fn to_ipv6mr_interface(value: u32) -> ::libc::c_uint { + value as ::libc::c_uint } //////////////////////////////////////////////////////////////////////////////// From e4aa513139cd1d25a42f9827ad677c045df3618d Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 3 Mar 2016 09:54:15 -0800 Subject: [PATCH 6/6] Fix netbsd --- src/libstd/sys/common/net.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index bc6bbc38e14..ca4f6e19882 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -27,19 +27,19 @@ use time::Duration; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", - target_os = "openbsd"))] + target_os = "openbsd", target_os = "netbsd"))] use sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", - target_os = "openbsd")))] + target_os = "openbsd", target_os = "netbsd")))] use sys::net::netc::IPV6_ADD_MEMBERSHIP; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", - target_os = "openbsd"))] + target_os = "openbsd", target_os = "netbsd"))] use sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", - target_os = "openbsd")))] + target_os = "openbsd", target_os = "netbsd")))] use sys::net::netc::IPV6_DROP_MEMBERSHIP; ////////////////////////////////////////////////////////////////////////////////