|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 | 11 | use std::cmp;
|
| 12 | +#[cfg(target_os = "linux")] |
| 13 | +use std::ffi::{CStr, CString}; |
12 | 14 | use std::fmt;
|
13 | 15 | use std::io;
|
14 | 16 | use std::io::{ErrorKind, Read, Write};
|
15 | 17 | use std::mem;
|
| 18 | +#[cfg(target_os = "linux")] |
| 19 | +use std::mem::MaybeUninit; |
16 | 20 | use std::net::Shutdown;
|
17 | 21 | use std::net::{self, Ipv4Addr, Ipv6Addr};
|
18 | 22 | use std::ops::Neg;
|
19 | 23 | #[cfg(feature = "unix")]
|
20 | 24 | use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
|
21 | 25 | use std::os::unix::prelude::*;
|
| 26 | +#[cfg(target_os = "linux")] |
| 27 | +use std::ptr; |
| 28 | +#[cfg(target_os = "linux")] |
| 29 | +use std::slice; |
22 | 30 | use std::sync::atomic::{AtomicBool, Ordering};
|
23 | 31 | use std::time::{Duration, Instant};
|
24 | 32 |
|
@@ -563,6 +571,62 @@ impl Socket {
|
563 | 571 | unsafe { self.setsockopt(libc::SOL_SOCKET, libc::SO_MARK, mark as c_int) }
|
564 | 572 | }
|
565 | 573 |
|
| 574 | + #[cfg(target_os = "linux")] |
| 575 | + pub fn device(&self) -> io::Result<Option<CString>> { |
| 576 | + // TODO: replace with `MaybeUninit::uninit_array` once stable. |
| 577 | + let mut buf: [MaybeUninit<u8>; libc::IFNAMSIZ] = |
| 578 | + unsafe { MaybeUninit::<[MaybeUninit<u8>; libc::IFNAMSIZ]>::uninit().assume_init() }; |
| 579 | + let mut len = buf.len() as libc::socklen_t; |
| 580 | + let len = unsafe { |
| 581 | + cvt(libc::getsockopt( |
| 582 | + self.fd, |
| 583 | + libc::SOL_SOCKET, |
| 584 | + libc::SO_BINDTODEVICE, |
| 585 | + buf.as_mut_ptr().cast(), |
| 586 | + &mut len, |
| 587 | + ))? |
| 588 | + }; |
| 589 | + if len == 0 { |
| 590 | + Ok(None) |
| 591 | + } else { |
| 592 | + // Allocate a buffer for `CString` with the length including the |
| 593 | + // null terminator. |
| 594 | + let len = len as usize; |
| 595 | + let mut name = Vec::with_capacity(len); |
| 596 | + |
| 597 | + // TODO: use `MaybeUninit::slice_assume_init_ref` once stable. |
| 598 | + // Safety: `len` bytes are writen by the OS, this includes a null |
| 599 | + // terminator. However we don't copy the null terminator because |
| 600 | + // `CString::from_vec_unchecked` adds its own null terminator. |
| 601 | + let buf = unsafe { slice::from_raw_parts(buf.as_ptr().cast(), len - 1) }; |
| 602 | + name.extend_from_slice(buf); |
| 603 | + |
| 604 | + // Safety: the OS initialised the string for us, which shouldn't |
| 605 | + // include any null bytes. |
| 606 | + Ok(Some(unsafe { CString::from_vec_unchecked(name) })) |
| 607 | + } |
| 608 | + } |
| 609 | + |
| 610 | + #[cfg(target_os = "linux")] |
| 611 | + pub fn bind_device(&self, interface: Option<&CStr>) -> io::Result<()> { |
| 612 | + let (value, len) = if let Some(interface) = interface { |
| 613 | + (interface.as_ptr(), interface.to_bytes_with_nul().len()) |
| 614 | + } else { |
| 615 | + (ptr::null(), 0) |
| 616 | + }; |
| 617 | + |
| 618 | + unsafe { |
| 619 | + cvt(libc::setsockopt( |
| 620 | + self.fd, |
| 621 | + libc::SOL_SOCKET, |
| 622 | + libc::SO_BINDTODEVICE, |
| 623 | + value.cast(), |
| 624 | + len as libc::socklen_t, |
| 625 | + )) |
| 626 | + .map(|_| ()) |
| 627 | + } |
| 628 | + } |
| 629 | + |
566 | 630 | pub fn unicast_hops_v6(&self) -> io::Result<u32> {
|
567 | 631 | unsafe {
|
568 | 632 | let raw: c_int = self.getsockopt(libc::IPPROTO_IPV6, libc::IPV6_UNICAST_HOPS)?;
|
|
0 commit comments