Skip to content
Closed
Show file tree
Hide file tree
Changes from 17 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 @@ -306,6 +313,11 @@ impl Protocol {
#[cfg(target_os = "linux")]
/// 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);
}

impl From<c_int> for Protocol {
Expand Down
163 changes: 163 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;
pub(crate) use libc::{IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP};
Expand Down Expand Up @@ -390,6 +395,8 @@ impl_debug!(
libc::IPPROTO_UDP,
#[cfg(target_os = "linux")]
libc::IPPROTO_MPTCP,
#[cfg(target_os = "linux")]
libc::IPPROTO_DCCP
);

/// Unix-only API.
Expand Down Expand Up @@ -1997,6 +2004,162 @@ impl crate::Socket {
pub fn detach_filter(&self) -> io::Result<()> {
unsafe { setsockopt(self.as_raw(), libc::SOL_SOCKET, libc::SO_DETACH_FILTER, 0) }
}

/// Returns 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 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 the service
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn dccp_set_service(&self, code: u32) -> io::Result<()> {
unsafe {
setsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_SERVICE,
code,
)
}
}

/// Get the current maximum packet size
#[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,
)
}
}

/// Set both the TX and RX CCIDs
/// 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 dccp_set_ccid(&self, ccid: u8) -> io::Result<()> {
unsafe { setsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_CCID, ccid) }
}

/// Get the current CCID if set.
/// The underlying sockopt used here is the DCCP_SOCKOPT_TX_CCID. This means that theoretically only the CCID for TX is returned.
/// However, in practice setting different TX and RX CCIDs is rarely used.
#[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) }
}

/// Enables the server to hold the timewait state when closing the connection.
/// Only available for listening sockets
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn dccp_set_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 partial checksum coverage on the sender side
#[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 partial checksum coverage on the sender side
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn dccp_set_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 partial checksum coverage on the receiver side
#[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 the value of partial checksum coverage on the receiver side
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn dccp_set_recv_cscov(&self, level: u32) -> io::Result<()> {
unsafe {
setsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_RECV_CSCOV,
level,
)
}
}

/// Get the maximum length of the output queue
#[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,
)
}
}

/// Set the maximum length of the output queue
#[cfg(all(feature = "all", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
pub fn dccp_set_qpolicy_txqlen(&self, qlen: u32) -> io::Result<()> {
unsafe {
setsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_QPOLICY_TXQLEN,
qlen,
)
}
}
}

#[cfg_attr(docsrs, doc(cfg(unix)))]
Expand Down
32 changes: 32 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"),
(500.into(), "500"),
];

Expand Down Expand Up @@ -1295,3 +1297,33 @@ fn header_included() {
let got = socket.header_included().expect("failed to get value");
assert_eq!(got, true, "set and get values differ");
}

#[test]
#[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.dccp_set_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.dccp_set_ccid(2).unwrap();
socket_s.dccp_set_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.dccp_set_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());
}