Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2dbd20c
add support for DCCP socket type and protocol
onestay Sep 20, 2022
6da07c0
add the dccp_service sockopt
onestay Sep 21, 2022
93a7c45
Add support for most DCCP socket options
onestay Sep 21, 2022
8678e52
add a test, testing most of DCCPs socket options + basic read and write
onestay Sep 21, 2022
096743f
fix some spelling
onestay Sep 21, 2022
7a5b733
move all dccp options from socket.rs to sys/unix.rs
onestay Sep 21, 2022
2dcb6c7
add IPPROTO_DCCP to the impl_debug macro and protocol_fmt_debug test
onestay Sep 21, 2022
d49a1fe
cargo fmt + fix clippy complaints
onestay Sep 21, 2022
c49b344
apparently we can't assume that the list of CCIDs will not be empty
onestay Sep 21, 2022
2e2361e
update dccp_available_ccids with a more sensible buffer size
onestay Sep 22, 2022
560a951
rename the best to better fit with the other test names
onestay Sep 22, 2022
18630ab
add correct cfg attribute
onestay Sep 22, 2022
40d24d0
move `dccp_available_ccids` with all the other DCCP functions
onestay Sep 22, 2022
36bebec
fix formatting
onestay Sep 22, 2022
7367fcf
add feature = "all" to imports too
onestay Sep 22, 2022
4ad3329
add feature = "all" to test too
onestay Sep 22, 2022
cb99704
add feature = "all" to protocol_fmt_debug
onestay Sep 22, 2022
f1b9cc8
add better documentation and sort functions
onestay Sep 22, 2022
b51a939
Ignore the DCCP test
onestay Sep 22, 2022
24eba5f
add dccp_server_timewait
onestay Sep 22, 2022
d9e20d4
Merge branch 'master' into dccp
onestay Nov 23, 2022
86c3b4e
remove accidentally pasted text + feature all for debug impl
onestay Nov 23, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,13 @@ impl Type {
/// Used for protocols such as UDP.
pub const DGRAM: Type = Type(sys::SOCK_DGRAM);

/// Type corresponding to `SOCK_DCCP`
///
/// Used for the DCCP protocol
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub const DCCP: Type = Type(sys::SOCK_DCCP);

/// Type corresponding to `SOCK_SEQPACKET`.
#[cfg(feature = "all")]
#[cfg_attr(docsrs, doc(cfg(feature = "all")))]
Expand Down Expand Up @@ -307,6 +314,11 @@ impl Protocol {
/// Protocol corresponding to `MPTCP`.
pub const MPTCP: Protocol = Protocol(sys::IPPROTO_MPTCP);

/// Protocol corresponding to `DCCP`
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub const DCCP: Protocol = Protocol(sys::IPPROTO_DCCP);

#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
/// Protocol corresponding to `SCTP`.
pub const SCTP: Protocol = Protocol(sys::IPPROTO_SCTP);
Expand Down
222 changes: 222 additions & 0 deletions src/sys/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,17 @@ pub(crate) use libc::c_int;
// Used in `Domain`.
pub(crate) use libc::{AF_INET, AF_INET6, AF_UNIX};
// Used in `Type`.
#[cfg(all(feature = "all", target_os = "linux"))]
pub(crate) use libc::SOCK_DCCP;
#[cfg(all(feature = "all", not(target_os = "redox")))]
pub(crate) use libc::SOCK_RAW;
#[cfg(feature = "all")]
pub(crate) use libc::SOCK_SEQPACKET;

pub(crate) use libc::{SOCK_DGRAM, SOCK_STREAM};
// Used in `Protocol`.
#[cfg(all(feature = "all", target_os = "linux"))]
pub(crate) use libc::IPPROTO_DCCP;
#[cfg(target_os = "linux")]
pub(crate) use libc::IPPROTO_MPTCP;
#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
Expand Down Expand Up @@ -392,6 +397,8 @@ impl_debug!(
libc::IPPROTO_UDP,
#[cfg(target_os = "linux")]
libc::IPPROTO_MPTCP,
#[cfg(all(feature = "all", target_os = "linux"))]
libc::IPPROTO_DCCP,
#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
libc::IPPROTO_SCTP,
);
Expand Down Expand Up @@ -2001,6 +2008,221 @@ impl crate::Socket {
pub fn detach_filter(&self) -> io::Result<()> {
unsafe { setsockopt(self.as_raw(), libc::SOL_SOCKET, libc::SO_DETACH_FILTER, 0) }
}

/// Set value for the `DCCP_SOCKOPT_SERVICE` option on this socket.
///
/// Sets the service. The specification mandates use of service codes. If this socket option is not set, the socket will fall back to 0 (which means that no meaningful service code is present). On active sockets this is set before [`connect`].
/// On passive sockets up to 32 service codes can be set before calling [`bind`]
///
/// [`connect`]: crate::Socket::connect
/// [`bind`]: crate::Socket::bind
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn set_dccp_service(&self, code: u32) -> io::Result<()> {
unsafe {
setsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_SERVICE,
code,
)
}
}

/// Get the value of the `DCCP_SOCKOPT_SERVICE` option on this socket.
///
/// For more information about this option see [`set_dccp_service`]
///
/// [`set_dccp_service`]: crate::Socket::set_dccp_service
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn dccp_service(&self) -> io::Result<u32> {
unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_SERVICE) }
}

/// Set value for the `DCCP_SOCKOPT_CCID` option on this socket.
///
/// This option sets both the TX and RX CCIDs at the same time.
///
/// Different TX and RX CCIDs, while in practice allowed, is rarely done and not supported by this library.
// todo: allow passing of multiple CCIDs
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn set_dccp_ccid(&self, ccid: u8) -> io::Result<()> {
unsafe { setsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_CCID, ccid) }
}

/// Get the value of the `DCCP_SOCKOPT_TX_CCID` option on this socket.
///
/// The underlying sockopt used here is the DCCP_SOCKOPT_TX_CCID which means that theoretically only the CCID for TX is returned.
///
/// For more information about this option see [`set_dccp_ccid`].
///
/// [`set_dccp_ccid`]: crate::Socket::set_dccp_ccid
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn dccp_ccid(&self) -> io::Result<u32> {
unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_TX_CCID) }
}

/// Set value for the `DCCP_SOCKOPT_SERVER_TIMEWAIT` option on this socket.
///
/// Enables the server (listening socket) to hold timewait state when closing the connection. This option must be set after accept() returns.
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn set_dccp_server_timewait(&self, hold_timewait: bool) -> io::Result<()> {
unsafe {
setsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_SERVER_TIMEWAIT,
hold_timewait as c_int,
)
}
}

/// Get the value of the `DCCP_SOCKOPT_SERVER_TIMEWAIT` option on this socket.
///
/// For more information see [`set_dccp_server_timewait`]
///
/// [`set_dccp_server_timewait`]: crate::Socket::set_dccp_server_timewait
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn dccp_server_timewait(&self) -> io::Result<bool> {
unsafe {
getsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_SERVER_TIMEWAIT,
)
}
}

/// Set value for the `DCCP_SOCKOPT_SEND_CSCOV` option on this socket.
///
/// Both this option and DCCP_SOCKOPT_RECV_CSCOV are used for setting the partial checksum coverage. The default is that checksums always cover the entire packet and that only fully covered application data is accepted by the receiver. Hence, when using this feature on the sender, it must be enabled at the receiver, too with suitable choice of CsCov.
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn set_dccp_send_cscov(&self, level: u32) -> io::Result<()> {
unsafe {
setsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_SEND_CSCOV,
level,
)
}
}

/// Get the value of the `DCCP_SOCKOPT_SEND_CSCOV` option on this socket.
///
/// For more information on this option see [`set_dccp_send_cscov`].
///
/// [`set_dccp_send_cscov`]: crate::Socket::set_dccp_send_cscov
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn dccp_send_cscov(&self) -> io::Result<u32> {
unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_SEND_CSCOV) }
}

/// Set the value of the `DCCP_SOCKOPT_RECV_CSCOV` option on this socket.
///
/// This option is only useful when combined with [`set_dccp_send_cscov`].
///
/// [`set_dccp_send_cscov`]: crate::Socket::set_dccp_send_cscov
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn set_dccp_recv_cscov(&self, level: u32) -> io::Result<()> {
unsafe {
setsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_RECV_CSCOV,
level,
)
}
}

/// Get the value of the `DCCP_SOCKOPT_RECV_CSCOV` option on this socket.
///
/// For more information on this option see [`set_dccp_recv_cscov`].
///
/// [`set_dccp_recv_cscov`]: crate::Socket::set_dccp_recv_cscov
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn dccp_recv_cscov(&self) -> io::Result<u32> {
unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_RECV_CSCOV) }
}

/// Set value for the `DCCP_SOCKOPT_QPOLICY_TXQLEN` option on this socket.
///
/// This option sets the maximum length of the output queue. A zero value is always interpreted as unbounded queue length.
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn set_dccp_qpolicy_txqlen(&self, qlen: u32) -> io::Result<()> {
unsafe {
setsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_QPOLICY_TXQLEN,
qlen,
)
}
}

/// Get the value of the `DCCP_SOCKOPT_QPOLICY_TXQLEN` on this socket.
///
/// For more information on this option see [`set_dccp_qpolicy_txqlen`].
///
/// [`set_dccp_qpolicy_txqlen`]: crate::Socket::set_dccp_qpolicy_txqlen
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn dccp_qpolicy_txqlen(&self) -> io::Result<u32> {
unsafe {
getsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_QPOLICY_TXQLEN,
)
}
}

/// Get the value of the `DCCP_SOCKOPT_AVAILABLE_CCIDS` option on this socket.
///
/// This option is read-only and returns the list of CCIDs supported by the endpoint.
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn dccp_available_ccids(&self) -> io::Result<Vec<u8>> {
// Creating the buffer with a value of 5 is ~probably~ fine.
// https://www.kernel.org/doc/html/latest/networking/dccp.html says the minimum is 4, however currently there are only 3 CCIDs implemented in the kernel.
// So 5 should cover us for some time. Even if 3 more CCIDs are implemented it would be very unlikely, that all 6 are available.
let mut buf: [MaybeUninit<u8>; 5] = unsafe { MaybeUninit::uninit().assume_init() };
let mut len = buf.len() as libc::socklen_t;
syscall!(getsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_AVAILABLE_CCIDS,
buf.as_mut_ptr().cast(),
&mut len,
))?;
let buf = &buf[..len as usize - 1];
Ok(unsafe { &*(buf as *const [_] as *const [u8]) }.into())
}

/// Get the value of the `DCCP_SOCKOPT_GET_CUR_MPS` option on this socket.
///
/// This option retrieves the current maximum packet size (application payload size) in bytes.
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn dccp_cur_mps(&self) -> io::Result<u32> {
unsafe {
getsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_GET_CUR_MPS,
)
}
}
}

#[cfg_attr(docsrs, doc(cfg(unix)))]
Expand Down
33 changes: 33 additions & 0 deletions tests/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ fn protocol_fmt_debug() {
(Protocol::UDP, "IPPROTO_UDP"),
#[cfg(target_os = "linux")]
(Protocol::MPTCP, "IPPROTO_MPTCP"),
#[cfg(all(feature = "all", target_os = "linux"))]
(Protocol::DCCP, "IPPROTO_DCCP"),
#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
(Protocol::SCTP, "IPPROTO_SCTP"),
(500.into(), "500"),
Expand Down Expand Up @@ -1297,3 +1299,34 @@ fn header_included() {
let got = socket.header_included().expect("failed to get value");
assert_eq!(got, true, "set and get values differ");
}

#[test]
#[ignore = "DCCP support is not enabled in all kernels of majors Linux distros"]
#[cfg(all(feature = "all", target_os = "linux"))]
fn dccp() {
let socket_s = Socket::new(Domain::IPV4, Type::DCCP, Some(Protocol::DCCP)).unwrap();
let addr = "127.0.0.1:8686".parse::<SocketAddr>().unwrap().into();
socket_s.set_dccp_service(45).unwrap();
assert!(socket_s.dccp_service().unwrap() == 45);
assert!(socket_s.dccp_cur_mps().unwrap() > 0);
assert!(socket_s.dccp_available_ccids().is_ok());
assert!(
socket_s.dccp_send_cscov().unwrap() == 0,
"sender cscov should be zero by default"
);
socket_s.set_dccp_ccid(2).unwrap();
socket_s.set_dccp_qpolicy_txqlen(6).unwrap();
assert!(socket_s.dccp_qpolicy_txqlen().unwrap() == 6);
socket_s.bind(&addr).unwrap();
socket_s.listen(10).unwrap();

let mut socket_c = Socket::new(Domain::IPV4, Type::DCCP, Some(Protocol::DCCP)).unwrap();
socket_c.set_dccp_service(45).unwrap();
socket_c.connect(&addr).unwrap();

let (mut socket_s_c, _) = socket_s.accept().unwrap();
let msg = "Hello World!";
assert!(socket_c.write(msg.as_bytes()).unwrap() == msg.len());
let mut recv_buf = [0_u8; 64];
assert!(socket_s_c.read(&mut recv_buf).unwrap() == msg.len());
}