Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 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
10 changes: 10 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ impl Type {
/// Used for protocols such as UDP.
pub const DGRAM: Type = Type(sys::SOCK_DGRAM);

#[cfg(target_os = "linux")]
/// Type corresponding to `SOCK_DCCP`
///
/// Used for the DCCP protocol
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 +312,10 @@ impl Protocol {
#[cfg(target_os = "linux")]
/// Protocol corresponding to `MPTCP`.
pub const MPTCP: Protocol = Protocol(sys::IPPROTO_MPTCP);

#[cfg(target_os = "linux")]
/// Protocol corresponding to `DCCP`
pub const DCCP: Protocol = Protocol(sys::IPPROTO_DCCP);
}

impl From<c_int> for Protocol {
Expand Down
147 changes: 146 additions & 1 deletion src/sys/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +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(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(target_os = "linux")]
pub(crate) use libc::IPPROTO_MPTCP;
pub(crate) use libc::{IPPROTO_DCCP, IPPROTO_MPTCP};
pub(crate) use libc::{IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP};
// Used in `SockAddr`.
pub(crate) use libc::{
Expand Down Expand Up @@ -390,6 +393,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 @@ -1506,6 +1511,22 @@ impl crate::Socket {
}
}

/// Returns list of CCIDs supported by the endpoint
#[cfg(target_os = "linux")]
pub fn dccp_available_ccids(&self) -> io::Result<Vec<u8>> {
let mut buf: [MaybeUninit<u8>; 10] = 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())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do better than allocating 10 bytes? Maybe a small vec like type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quiet sure what to do here. I've changed the buf to length 5 instead of 10. The code of this function was basically copied from the device function.

socket2/src/sys/unix.rs

Lines 1488 to 1507 in 4c43681

pub fn device(&self) -> io::Result<Option<Vec<u8>>> {
// TODO: replace with `MaybeUninit::uninit_array` once stable.
let mut buf: [MaybeUninit<u8>; libc::IFNAMSIZ] =
unsafe { MaybeUninit::uninit().assume_init() };
let mut len = buf.len() as libc::socklen_t;
syscall!(getsockopt(
self.as_raw(),
libc::SOL_SOCKET,
libc::SO_BINDTODEVICE,
buf.as_mut_ptr().cast(),
&mut len,
))?;
if len == 0 {
Ok(None)
} else {
let buf = &buf[..len as usize - 1];
// TODO: use `MaybeUninit::slice_assume_init_ref` once stable.
Ok(Some(unsafe { &*(buf as *const [_] as *const [u8]) }.into()))
}
}

}

/// Sets the value for the `SO_BINDTODEVICE` option on this socket.
///
/// If a socket is bound to an interface, only packets received from that
Expand Down Expand Up @@ -1997,6 +2018,130 @@ impl crate::Socket {
pub fn detach_filter(&self) -> io::Result<()> {
unsafe { setsockopt(self.as_raw(), libc::SOL_SOCKET, libc::SO_DETACH_FILTER, 0) }
}

#[cfg(target_os = "linux")]
/// Get the service
pub fn dccp_service(&self) -> io::Result<u32> {
unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_SERVICE) }
}

#[cfg(target_os = "linux")]
/// Set the service
pub fn dccp_set_service(&self, code: u32) -> io::Result<()> {
unsafe {
setsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_SERVICE,
code,
)
}
}

#[cfg(target_os = "linux")]
/// Get the current maximum packet size
pub fn dccp_cur_mps(&self) -> io::Result<u32> {
unsafe {
getsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_GET_CUR_MPS,
)
}
}

#[cfg(target_os = "linux")]
/// 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
pub fn dccp_set_ccid(&self, ccid: u8) -> io::Result<()> {
unsafe { setsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_CCID, ccid) }
}

#[cfg(target_os = "linux")]
/// 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.
pub fn dccp_ccid(&self) -> io::Result<u32> {
unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_TX_CCID) }
}

#[cfg(target_os = "linux")]
/// Enables the server to hold the timewait state when closing the connection.
/// Only available for listening sockets
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,
)
}
}

#[cfg(target_os = "linux")]
/// Get the value of partial checksum coverage on the sender side
pub fn dccp_send_cscov(&self) -> io::Result<u32> {
unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_SEND_CSCOV) }
}

#[cfg(target_os = "linux")]
/// Set the value of partial checksum coverage on the sender side
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,
)
}
}

#[cfg(target_os = "linux")]
/// Get the value of partial checksum coverage on the receiver side
pub fn dccp_recv_cscov(&self) -> io::Result<u32> {
unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_RECV_CSCOV) }
}

#[cfg(target_os = "linux")]
/// Set the value of partial checksum coverage on the receiver side
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,
)
}
}

#[cfg(target_os = "linux")]
/// Get the maximum length of the output queue
pub fn dccp_qpolicy_txqlen(&self) -> io::Result<u32> {
unsafe {
getsockopt(
self.as_raw(),
libc::SOL_DCCP,
libc::DCCP_SOCKOPT_QPOLICY_TXQLEN,
)
}
}

#[cfg(target_os = "linux")]
/// Set the maximum length of the output queue
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
31 changes: 31 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(target_os = "linux")]
(Protocol::DCCP, "IPPROTO_DCCP"),
(500.into(), "500"),
];

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

#[test]
#[cfg(target_os = "linux")]
fn test_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_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());
}