diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 22d2dfe65a249..de4c9ed3832a0 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -3,6 +3,7 @@ #[cfg(all(test, not(any(target_os = "emscripten", target_os = "xous"))))] mod tests; +use crate::ffi::CString; use crate::fmt; use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; @@ -603,6 +604,47 @@ impl TcpStream { pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } + + /// Bind the socket to an interface + /// + /// ```no_run + /// #![feature(unix_set_device)] + /// + /// use std::net::TcpStream; + /// + /// fn main() -> std::io::Result<()> { + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_device("eth0")?; + /// Ok(()) + /// } + /// + /// ``` + #[unstable(feature = "unix_set_device", issue = "129182")] + pub fn set_device(&self, ifrname: &str) -> io::Result<()> { + self.0.set_device(ifrname) + } + + /// Get the interface this socket is bound to + /// + /// ```no_run + /// #![feature(unix_set_device)] + /// + /// use std::net::TcpStream; + /// + /// fn main() -> std::io::Result<()> { + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_device("eth0")?; + /// let name = stream.device()?; + /// assert_eq!(Ok("eth0"), name.to_str()); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_set_device", issue = "129182")] + pub fn device(&self) -> io::Result { + self.0.device() + } } // In addition to the `impl`s here, `TcpStream` also has `impl`s for diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 32e9086003d6b..d72a63fa5da4a 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -1,6 +1,7 @@ #[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx", target_os = "xous"))))] mod tests; +use crate::ffi::CString; use crate::fmt; use crate::io::{self, ErrorKind}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; @@ -806,6 +807,45 @@ impl UdpSocket { pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } + + /// Bind the socket to an interface + /// + /// ```no_run + /// #![feature(unix_set_device)] + /// + /// use std::net::UdpSocket; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UdpSocket::bind("127.0.0.1:7878").unwrap(); + /// socket.set_device("eth0")?; + /// Ok(()) + /// } + /// + /// ``` + #[unstable(feature = "unix_set_device", issue = "129182")] + pub fn set_device(&self, ifrname: &str) -> io::Result<()> { + self.0.set_device(ifrname) + } + + /// Get the interface this socket is bound to + /// + /// ```no_run + /// #![feature(unix_set_device)] + /// + /// use std::net::UdpSocket; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UdpSocket::bind("127.0.0.1:7878").unwrap(); + /// socket.set_device("eth0")?; + /// let name = socket.device()?; + /// assert_eq!(Ok("eth0"), name.to_str()); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_set_device", issue = "129182")] + pub fn device(&self) -> io::Result { + self.0.device() + } } // In addition to the `impl`s here, `UdpSocket` also has `impl`s for diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index a605c3d4a2602..50c0260af0255 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -15,6 +15,7 @@ use libc::MSG_NOSIGNAL; #[cfg(any(doc, target_os = "android", target_os = "linux"))] use super::{recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to, SocketAncillary}; use super::{sockaddr_un, SocketAddr}; +use crate::ffi::CString; #[cfg(any(doc, target_os = "android", target_os = "linux"))] use crate::io::{IoSlice, IoSliceMut}; use crate::net::Shutdown; @@ -836,6 +837,41 @@ impl UnixDatagram { self.0.set_mark(mark) } + /// Bind the socket to an interface + /// + /// ```no_run + /// #![feature(unix_set_device)] + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixDatagram::unbound()?; + /// socket.set_device("eth0")?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_set_device", issue = "129182")] + pub fn set_device(&self, ifrname: &str) -> io::Result<()> { + self.0.set_device(ifrname) + } + + /// Get the interface this socket is bound to + /// + /// ```no_run + /// #![feature(unix_set_device)] + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixDatagram::unbound()?; + /// socket.set_device("eth0")?; + /// let name = socket.device()?; + /// assert_eq!(Ok("eth0"), name.to_str()); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_set_device", issue = "129182")] + pub fn device(&self) -> io::Result { + self.0.device() + } /// Returns the value of the `SO_ERROR` option. /// /// # Examples diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs index 416469c003738..a46389f7e666d 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/pal/hermit/net.rs @@ -312,6 +312,14 @@ impl Socket { pub fn as_raw(&self) -> RawFd { self.0.as_raw_fd() } + + pub fn device(&self) -> io::Result { + unimplemented!() + } + + pub fn set_device(&self, _: &str) -> io::Result<()> { + unimplemented!() + } } impl AsInner for Socket { diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/pal/sgx/net.rs index f2e751c51194d..69112cecb15be 100644 --- a/library/std/src/sys/pal/sgx/net.rs +++ b/library/std/src/sys/pal/sgx/net.rs @@ -207,6 +207,14 @@ impl TcpStream { pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { sgx_ineffective(()) } + + pub fn device(&self) -> io::Result { + unimplemented!() + } + + pub fn set_device(&self, _: &str) -> io::Result<()> { + unimplemented!() + } } impl AsInner for TcpStream { diff --git a/library/std/src/sys/pal/solid/net.rs b/library/std/src/sys/pal/solid/net.rs index b6a31395095d9..c5ca7b156fa5e 100644 --- a/library/std/src/sys/pal/solid/net.rs +++ b/library/std/src/sys/pal/solid/net.rs @@ -380,6 +380,14 @@ impl Socket { pub fn as_raw(&self) -> c_int { self.as_raw_fd() } + + pub fn device(&self) -> io::Result { + unimplemented!() + } + + pub fn set_device(&self, _: &str) -> io::Result<()> { + unimplemented!() + } } impl FromInner for Socket { diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index bc0e3f4eeeac8..ccecb8ce0b9e0 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/pal/unix/net.rs @@ -563,6 +563,40 @@ impl Socket { setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) } + #[cfg(any(target_os = "linux", target_os = "haiku", target_os = "vxworks"))] + pub fn device(&self) -> io::Result { + let buf: [libc::c_char; libc::IFNAMSIZ] = + getsockopt(self, libc::SOL_SOCKET, libc::SO_BINDTODEVICE)?; + let s: &[u8] = unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len()) }; + let name = CStr::from_bytes_until_nul(s).unwrap(); + Ok(crate::ffi::CString::new(name.to_bytes()).unwrap()) + } + + #[cfg(not(any(target_os = "linux", target_os = "haiku", target_os = "vxworks")))] + pub fn device(&self) -> io::Result { + unimplemented!() + } + + #[cfg(any(target_os = "linux", target_os = "haiku", target_os = "vxworks"))] + pub fn set_device(&self, ifrname: &str) -> io::Result<()> { + let istr = ifrname.as_bytes(); + + if istr.len() >= libc::IFNAMSIZ { + return Err(io::Error::from_raw_os_error(libc::ENAMETOOLONG)); + } + + let mut buf = [0; libc::IFNAMSIZ]; + for (src, dst) in istr.iter().zip(&mut buf[..libc::IFNAMSIZ - 1]) { + *dst = *src as libc::c_char; + } + setsockopt(self, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, buf) + } + + #[cfg(not(any(target_os = "linux", target_os = "haiku", target_os = "vxworks")))] + pub fn set_device(&self, _: &str) -> io::Result<()> { + unimplemented!() + } + pub fn take_error(&self) -> io::Result> { let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } diff --git a/library/std/src/sys/pal/wasi/net.rs b/library/std/src/sys/pal/wasi/net.rs index b4cf94c8781ec..9db57dc1ae99d 100644 --- a/library/std/src/sys/pal/wasi/net.rs +++ b/library/std/src/sys/pal/wasi/net.rs @@ -194,6 +194,14 @@ impl TcpStream { pub fn into_socket(self) -> Socket { self.inner } + + pub fn device(&self) -> io::Result { + unimplemented!() + } + + pub fn set_device(&self, _: &str) -> io::Result<()> { + unimplemented!() + } } impl FromInner for TcpStream { diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs index ce995f5ed5af7..6bbb96de0c15d 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/pal/windows/net.rs @@ -521,6 +521,14 @@ impl Socket { debug_assert_eq!(mem::align_of::(), mem::align_of::()); unsafe { Self::from_raw_socket(raw as RawSocket) } } + + pub fn device(&self) -> io::Result { + unimplemented!() + } + + pub fn set_device(&self, _: &str) -> io::Result<()> { + unimplemented!() + } } #[unstable(reason = "not public", issue = "none", feature = "fd_read")] diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 25ebeb3502d20..f6c9ef6c00731 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests; -use crate::ffi::{c_int, c_void}; +use crate::ffi::{c_int, c_void, CString}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::sys::common::small_c_string::run_with_cstr; @@ -352,6 +352,14 @@ impl TcpStream { pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.inner.set_nonblocking(nonblocking) } + + pub fn device(&self) -> io::Result { + self.inner.device() + } + + pub fn set_device(&self, ifrname: &str) -> io::Result<()> { + self.inner.set_device(ifrname) + } } impl AsInner for TcpStream { @@ -702,6 +710,14 @@ impl UdpSocket { let (addr, len) = addr?.into_inner(); cvt_r(|| unsafe { c::connect(self.inner.as_raw(), addr.as_ptr(), len) }).map(drop) } + + pub fn device(&self) -> io::Result { + self.inner.device() + } + + pub fn set_device(&self, ifrname: &str) -> io::Result<()> { + self.inner.set_device(ifrname) + } } impl FromInner for UdpSocket {