diff --git a/src/sys/unix.rs b/src/sys/unix.rs index 7a185a5d..b62393df 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -7,11 +7,6 @@ // except according to those terms. use std::cmp::min; -#[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") -))] -use std::ffi::{CStr, CString}; #[cfg(not(target_os = "redox"))] use std::io::IoSlice; use std::marker::PhantomData; @@ -942,10 +937,10 @@ impl crate::Socket { feature = "all", any(target_os = "android", target_os = "fuchsia", target_os = "linux") ))] - pub fn device(&self) -> io::Result> { + pub fn device(&self) -> io::Result>> { // TODO: replace with `MaybeUninit::uninit_array` once stable. let mut buf: [MaybeUninit; libc::IFNAMSIZ] = - unsafe { MaybeUninit::<[MaybeUninit; libc::IFNAMSIZ]>::uninit().assume_init() }; + unsafe { MaybeUninit::uninit().assume_init() }; let mut len = buf.len() as libc::socklen_t; unsafe { syscall!(getsockopt( @@ -959,21 +954,9 @@ impl crate::Socket { if len == 0 { Ok(None) } else { - // Allocate a buffer for `CString` with the length including the - // null terminator. - let len = len as usize; - let mut name = Vec::with_capacity(len); - + let buf = &buf[..len as usize - 1]; // TODO: use `MaybeUninit::slice_assume_init_ref` once stable. - // Safety: `len` bytes are writen by the OS, this includes a null - // terminator. However we don't copy the null terminator because - // `CString::from_vec_unchecked` adds its own null terminator. - let buf = unsafe { slice::from_raw_parts(buf.as_ptr().cast(), len - 1) }; - name.extend_from_slice(buf); - - // Safety: the OS initialised the string for us, which shouldn't - // include any null bytes. - Ok(Some(unsafe { CString::from_vec_unchecked(name) })) + Ok(Some(unsafe { &*(buf as *const [_] as *const [u8]) }.into())) } } @@ -990,9 +973,9 @@ impl crate::Socket { feature = "all", any(target_os = "android", target_os = "fuchsia", target_os = "linux") ))] - pub fn bind_device(&self, interface: Option<&CStr>) -> io::Result<()> { + pub fn bind_device(&self, interface: Option<&[u8]>) -> io::Result<()> { let (value, len) = if let Some(interface) = interface { - (interface.as_ptr(), interface.to_bytes_with_nul().len()) + (interface.as_ptr(), interface.len()) } else { (ptr::null(), 0) }; diff --git a/tests/socket.rs b/tests/socket.rs index 7dad160b..0b5e027e 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -1,5 +1,3 @@ -#[cfg(all(feature = "all", any(target_os = "fuchsia", target_os = "linux")))] -use std::ffi::CStr; #[cfg(any(windows, target_vendor = "apple"))] use std::io; #[cfg(not(target_os = "redox"))] @@ -631,30 +629,36 @@ fn tcp_keepalive() { #[cfg(all(feature = "all", any(target_os = "fuchsia", target_os = "linux")))] #[test] +#[ignore = "setting `SO_BINDTODEVICE` requires the `CAP_NET_RAW` capability (works when running as root)"] fn device() { // Some common network interface on Linux. - const INTERFACES: &[&str] = &["lo\0", "lo0\0", "eth0\0", "wlan0\0"]; + const INTERFACES: &[&str] = &["lo", "lo0", "eth0", "wlan0"]; let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); assert_eq!(socket.device().unwrap(), None); for interface in INTERFACES.iter() { - let interface = CStr::from_bytes_with_nul(interface.as_bytes()).unwrap(); - if let Err(err) = socket.bind_device(Some(interface)) { + if let Err(err) = socket.bind_device(Some(interface.as_bytes())) { // Network interface is not available try another. - if matches!(err.raw_os_error(), Some(libc::ENODEV) | Some(libc::EPERM)) { + if matches!(err.raw_os_error(), Some(libc::ENODEV)) { + eprintln!("error binding to device (`{}`): {}", interface, err); continue; } else { panic!("unexpected error binding device: {}", err); } } - assert_eq!(socket.device().unwrap().as_deref(), Some(interface)); + assert_eq!( + socket.device().unwrap().as_deref(), + Some(interface.as_bytes()) + ); socket.bind_device(None).unwrap(); assert_eq!(socket.device().unwrap(), None); // Just need to do it with one interface. - break; + return; } + + panic!("failed to bind to any device."); } fn any_ipv4() -> SockAddr {