Skip to content

Commit 293b0a2

Browse files
committed
std::net: adding new option todevice.
Allows to bind a socket to an network interface, to exclusively listen from it. For packet sockets, regular bind is more appropriate.
1 parent 569d7e3 commit 293b0a2

File tree

3 files changed

+132
-0
lines changed

3 files changed

+132
-0
lines changed

library/std/src/os/unix/net/datagram.rs

+56
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use libc::MSG_NOSIGNAL;
1515
#[cfg(any(doc, target_os = "android", target_os = "linux"))]
1616
use super::{recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to, SocketAncillary};
1717
use super::{sockaddr_un, SocketAddr};
18+
#[cfg(any(doc, target_os = "linux", target_os = "haiku", target_os = "vxworks",))]
19+
use crate::ffi::CStr;
1820
#[cfg(any(doc, target_os = "android", target_os = "linux"))]
1921
use crate::io::{IoSlice, IoSliceMut};
2022
use crate::net::Shutdown;
@@ -836,6 +838,60 @@ impl UnixDatagram {
836838
self.0.set_mark(mark)
837839
}
838840

841+
/// Bind the socket to an interface
842+
///
843+
#[cfg_attr(
844+
any(target_os = "linux", target_os = "haiku", target_os = "vxworks"),
845+
doc = "```no_run"
846+
)]
847+
#[cfg_attr(
848+
not(any(target_os = "linux", target_os = "haiku", target_os = "vxworks")),
849+
doc = "```ignore"
850+
)]
851+
/// #![feature(unix_set_todevice)]
852+
/// use std::io;
853+
/// use std::os::unix::net::UnixDatagram;
854+
///
855+
/// fn main() -> std::io::Result<()> {
856+
/// let socket = UnixDatagram::unbound()?;
857+
/// socket.set_todevice(c"eth0");
858+
/// Ok(())
859+
/// }
860+
/// ```
861+
#[cfg(any(doc, target_os = "linux", target_os = "haiku", target_os = "vxworks",))]
862+
#[unstable(feature = "unix_set_todevice", issue = "none")]
863+
pub fn set_todevice(&self, ifrname: &CStr) -> io::Result<()> {
864+
self.0.set_todevice(ifrname)
865+
}
866+
867+
/// Get the interface this socket is bound to
868+
///
869+
#[cfg_attr(
870+
any(target_os = "linux", target_os = "haiku", target_os = "vxworks"),
871+
doc = "```no_run"
872+
)]
873+
#[cfg_attr(
874+
not(any(target_os = "linux", target_os = "haiku", target_os = "vxworks")),
875+
doc = "```ignore"
876+
)]
877+
/// #![feature(unix_set_todevice)]
878+
/// use std::io;
879+
/// use std::net::UdpSocket;
880+
/// use std::time::Duration;
881+
///
882+
/// fn main() -> std::io::Result<()> {
883+
/// let socket = UdpSocket::bind("127.0.0.1:34254")?;
884+
/// socket.set_todevice(c"eth0");
885+
/// let name = socket.to_device();
886+
/// assert_eq!(Ok("eth0"), name.to_str());
887+
/// Ok(())
888+
/// }
889+
/// ```
890+
#[cfg(any(doc, target_os = "linux", target_os = "haiku", target_os = "vxworks",))]
891+
#[unstable(feature = "unix_set_todevice", issue = "none")]
892+
pub fn todevice(&self) -> io::Result<&CStr> {
893+
self.0.todevice()
894+
}
839895
/// Returns the value of the `SO_ERROR` option.
840896
///
841897
/// # Examples

library/std/src/os/unix/net/stream.rs

+58
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use super::{peer_cred, UCred};
1212
#[cfg(any(doc, target_os = "android", target_os = "linux"))]
1313
use super::{recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to, SocketAncillary};
1414
use super::{sockaddr_un, SocketAddr};
15+
#[cfg(any(doc, target_os = "linux", target_os = "haiku", target_os = "vxworks",))]
16+
use crate::ffi::CStr;
1517
use crate::fmt;
1618
use crate::io::{self, IoSlice, IoSliceMut};
1719
use crate::net::Shutdown;
@@ -405,6 +407,62 @@ impl UnixStream {
405407
self.0.set_mark(mark)
406408
}
407409

410+
/// Bind the socket to an interface
411+
///
412+
#[cfg_attr(
413+
any(target_os = "linux", target_os = "haiku", target_os = "vxworks"),
414+
doc = "```no_run"
415+
)]
416+
#[cfg_attr(
417+
not(any(target_os = "linux", target_os = "haiku", target_os = "vxworks")),
418+
doc = "```ignore"
419+
)]
420+
/// #![feature(unix_set_todevice)]
421+
/// use std::io;
422+
/// use std::net::UdpSocket;
423+
/// use std::time::Duration;
424+
///
425+
/// fn main() -> std::io::Result<()> {
426+
/// let socket = UdpSocket::bind("127.0.0.1:34254")?;
427+
/// socket.set_todevice(c"eth0");
428+
/// Ok(())
429+
/// }
430+
/// ```
431+
#[cfg(any(doc, target_os = "linux", target_os = "haiku", target_os = "vxworks",))]
432+
#[unstable(feature = "unix_set_todevice", issue = "none")]
433+
pub fn set_todevice(&self, ifrname: &CStr) -> io::Result<()> {
434+
self.0.set_todevice(ifrname)
435+
}
436+
437+
/// Get the interface this socket is bound to
438+
///
439+
#[cfg_attr(
440+
any(target_os = "linux", target_os = "haiku", target_os = "vxworks"),
441+
doc = "```no_run"
442+
)]
443+
#[cfg_attr(
444+
not(any(target_os = "linux", target_os = "haiku", target_os = "vxworks")),
445+
doc = "```ignore"
446+
)]
447+
/// #![feature(unix_set_todevice)]
448+
/// use std::io;
449+
/// use std::net::UdpSocket;
450+
/// use std::time::Duration;
451+
///
452+
/// fn main() -> std::io::Result<()> {
453+
/// let socket = UdpSocket::bind("127.0.0.1:34254")?;
454+
/// socket.set_todevice(c"eth0");
455+
/// let name = socket.to_device();
456+
/// assert_eq!(Ok("eth0"), name.to_str());
457+
/// Ok(())
458+
/// }
459+
/// ```
460+
#[cfg(any(doc, target_os = "linux", target_os = "haiku", target_os = "vxworks",))]
461+
#[unstable(feature = "unix_set_todevice", issue = "none")]
462+
pub fn todevice(&self) -> io::Result<&CStr> {
463+
self.0.todevice()
464+
}
465+
408466
/// Returns the value of the `SO_ERROR` option.
409467
///
410468
/// # Examples

library/std/src/sys/pal/unix/net.rs

+18
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,24 @@ impl Socket {
563563
setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int)
564564
}
565565

566+
#[cfg(any(target_os = "linux", target_os = "haiku", target_os = "vxworks"))]
567+
pub fn todevice(&self) -> io::Result<&CStr> {
568+
let buf: [libc::c_char; libc::IFNAMSIZ] =
569+
getsockopt(self, libc::SOL_SOCKET, libc::SO_BINDTODEVICE)?;
570+
let s: &[u8] = unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len()) };
571+
let ifrname = CStr::from_bytes_with_nul(s).unwrap();
572+
Ok(ifrname)
573+
}
574+
575+
#[cfg(any(target_os = "linux", target_os = "haiku", target_os = "vxworks"))]
576+
pub fn set_todevice(&self, ifrname: &CStr) -> io::Result<()> {
577+
let mut buf = [0; libc::IFNAMSIZ];
578+
for (src, dst) in ifrname.to_bytes().iter().zip(&mut buf[..libc::IFNAMSIZ - 1]) {
579+
*dst = *src as libc::c_char;
580+
}
581+
setsockopt(self, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, buf)
582+
}
583+
566584
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
567585
let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?;
568586
if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }

0 commit comments

Comments
 (0)