Support SOCK_SEQPACKET
This commit is contained in:
parent
c3572e9548
commit
5ccadb6253
|
@ -99,8 +99,10 @@ impl Connected {
|
|||
pub(super) fn try_read(
|
||||
&self,
|
||||
writer: &mut dyn MultiWrite,
|
||||
is_seqpacket: bool,
|
||||
) -> Result<(usize, Vec<ControlMessage>)> {
|
||||
if writer.is_empty() {
|
||||
let is_empty = writer.is_empty();
|
||||
if is_empty && !is_seqpacket {
|
||||
if self.reader.lock().is_empty() {
|
||||
return_errno_with_message!(Errno::EAGAIN, "the channel is empty");
|
||||
}
|
||||
|
@ -158,9 +160,12 @@ impl Connected {
|
|||
}
|
||||
|
||||
// Read the payload bytes of the current auxiliary data.
|
||||
let read_res = self
|
||||
.inner
|
||||
.read_with(|| reader.read_fallible_with_max_len(writer, aux_len));
|
||||
let read_res = if !is_empty && aux_len > 0 {
|
||||
self.inner
|
||||
.read_with(|| reader.read_fallible_with_max_len(writer, aux_len))
|
||||
} else {
|
||||
Ok(0)
|
||||
};
|
||||
let read_len = match read_res {
|
||||
Ok(read_len) => read_len,
|
||||
Err(_) if read_tot_len > 0 => break aux_prev_data.as_mut().unwrap(),
|
||||
|
@ -168,8 +173,16 @@ impl Connected {
|
|||
};
|
||||
read_tot_len += read_len;
|
||||
|
||||
// Record the current auxiliary data. Break if the read is incomplete.
|
||||
if let Some(front) = aux_front {
|
||||
// Record the current auxiliary data. Break if the read is incomplete or this is a
|
||||
// `SOCK_SEQPACKET` socket.
|
||||
if is_seqpacket {
|
||||
aux_prev_data = Some(all_aux.pop_front().unwrap().data);
|
||||
if read_len < aux_len {
|
||||
warn!("setting MSG_TRUNC is not supported");
|
||||
reader.skip(aux_len - read_len);
|
||||
}
|
||||
break aux_prev_data.as_mut().unwrap();
|
||||
} else if let Some(front) = aux_front {
|
||||
if read_len < aux_len {
|
||||
front.start += read_len;
|
||||
break &mut front.data;
|
||||
|
@ -186,7 +199,7 @@ impl Connected {
|
|||
drop(reader);
|
||||
|
||||
let ctrl_msgs = aux_data.generate_control(is_pass_cred);
|
||||
debug_assert_ne!(read_tot_len, 0);
|
||||
debug_assert!(is_empty || read_tot_len != 0);
|
||||
peer_end
|
||||
.has_aux
|
||||
.store(!all_aux.is_empty(), Ordering::Relaxed);
|
||||
|
@ -198,12 +211,20 @@ impl Connected {
|
|||
&self,
|
||||
reader: &mut dyn MultiRead,
|
||||
aux_data: &mut AuxiliaryData,
|
||||
is_seqpacket: bool,
|
||||
) -> Result<usize> {
|
||||
if reader.is_empty() {
|
||||
let is_empty = reader.is_empty();
|
||||
if is_empty {
|
||||
if self.inner.is_shutdown() {
|
||||
return_errno_with_message!(Errno::EPIPE, "the channel is shut down");
|
||||
}
|
||||
return Ok(0);
|
||||
if !is_seqpacket {
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
|
||||
if is_seqpacket && reader.sum_lens() >= UNIX_STREAM_DEFAULT_BUF_SIZE {
|
||||
return_errno_with_message!(Errno::EMSGSIZE, "the message is too large");
|
||||
}
|
||||
|
||||
let this_end = self.inner.this_end();
|
||||
|
@ -211,9 +232,14 @@ impl Connected {
|
|||
|| self.inner.peer_end().is_pass_cred.load(Ordering::Relaxed);
|
||||
|
||||
// Fast path: There are no auxiliary data to transmit.
|
||||
if aux_data.is_empty() && !need_pass_cred {
|
||||
if aux_data.is_empty() && !is_seqpacket && !need_pass_cred {
|
||||
let mut writer = self.writer.lock();
|
||||
return self.inner.write_with(move || writer.write_fallible(reader));
|
||||
return self.inner.write_with(move || {
|
||||
if is_seqpacket && writer.free_len() < reader.sum_lens() {
|
||||
return Ok(0);
|
||||
}
|
||||
writer.write_fallible(reader)
|
||||
});
|
||||
}
|
||||
|
||||
let mut all_aux = this_end.all_aux.lock();
|
||||
|
@ -223,11 +249,18 @@ impl Connected {
|
|||
this_end.has_aux.store(true, Ordering::Relaxed);
|
||||
|
||||
// Write the payload bytes.
|
||||
let (write_start, write_res) = {
|
||||
let (write_start, write_res) = if !is_empty {
|
||||
let mut writer = self.writer.lock();
|
||||
let write_start = writer.tail();
|
||||
let write_res = self.inner.write_with(move || writer.write_fallible(reader));
|
||||
let write_res = self.inner.write_with(move || {
|
||||
if is_seqpacket && writer.free_len() < reader.sum_lens() {
|
||||
return Ok(0);
|
||||
}
|
||||
writer.write_fallible(reader)
|
||||
});
|
||||
(write_start, write_res)
|
||||
} else {
|
||||
(self.writer.lock().tail(), Ok(0))
|
||||
};
|
||||
let Ok(write_len) = write_res else {
|
||||
this_end
|
||||
|
|
|
@ -82,6 +82,7 @@ impl Init {
|
|||
self,
|
||||
backlog: usize,
|
||||
pollee: Pollee,
|
||||
is_seqpacket: bool,
|
||||
) -> core::result::Result<Listener, (Error, Self)> {
|
||||
let Some(addr) = self.addr else {
|
||||
return Err((
|
||||
|
@ -98,6 +99,7 @@ impl Init {
|
|||
self.is_read_shutdown.into_inner(),
|
||||
self.is_write_shutdown.into_inner(),
|
||||
pollee,
|
||||
is_seqpacket,
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
@ -38,9 +38,10 @@ impl Listener {
|
|||
is_read_shutdown: bool,
|
||||
is_write_shutdown: bool,
|
||||
pollee: Pollee,
|
||||
is_seqpacket: bool,
|
||||
) -> Self {
|
||||
let backlog = BACKLOG_TABLE
|
||||
.add_backlog(addr, pollee, backlog, is_read_shutdown)
|
||||
.add_backlog(addr, pollee, backlog, is_read_shutdown, is_seqpacket)
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
|
@ -53,13 +54,13 @@ impl Listener {
|
|||
self.backlog.addr()
|
||||
}
|
||||
|
||||
pub(super) fn try_accept(&self) -> Result<(Arc<dyn FileLike>, SocketAddr)> {
|
||||
pub(super) fn try_accept(&self, is_seqpacket: bool) -> Result<(Arc<dyn FileLike>, SocketAddr)> {
|
||||
let connected = self.backlog.pop_incoming()?;
|
||||
|
||||
let peer_addr = connected.peer_addr().into();
|
||||
// TODO: Update options for a newly-accepted socket
|
||||
let options = OptionSet::new();
|
||||
let socket = UnixStreamSocket::new_connected(connected, options, false);
|
||||
let socket = UnixStreamSocket::new_connected(connected, options, false, is_seqpacket);
|
||||
|
||||
Ok((socket, peer_addr))
|
||||
}
|
||||
|
@ -123,6 +124,7 @@ impl BacklogTable {
|
|||
pollee: Pollee,
|
||||
backlog: usize,
|
||||
is_shutdown: bool,
|
||||
is_seqpacket: bool,
|
||||
) -> Option<Arc<Backlog>> {
|
||||
let addr_key = addr.to_key();
|
||||
|
||||
|
@ -132,7 +134,13 @@ impl BacklogTable {
|
|||
return None;
|
||||
}
|
||||
|
||||
let new_backlog = Arc::new(Backlog::new(addr, pollee, backlog, is_shutdown));
|
||||
let new_backlog = Arc::new(Backlog::new(
|
||||
addr,
|
||||
pollee,
|
||||
backlog,
|
||||
is_shutdown,
|
||||
is_seqpacket,
|
||||
));
|
||||
backlog_sockets.insert(addr_key, new_backlog.clone());
|
||||
|
||||
Some(new_backlog)
|
||||
|
@ -154,10 +162,17 @@ pub(super) struct Backlog {
|
|||
incoming_conns: SpinLock<Option<VecDeque<Connected>>>,
|
||||
wait_queue: WaitQueue,
|
||||
listener_cred: SocketCred<ReadDupOp>,
|
||||
is_seqpacket: bool,
|
||||
}
|
||||
|
||||
impl Backlog {
|
||||
fn new(addr: UnixSocketAddrBound, pollee: Pollee, backlog: usize, is_shutdown: bool) -> Self {
|
||||
fn new(
|
||||
addr: UnixSocketAddrBound,
|
||||
pollee: Pollee,
|
||||
backlog: usize,
|
||||
is_shutdown: bool,
|
||||
is_seqpacket: bool,
|
||||
) -> Self {
|
||||
let incoming_sockets = if is_shutdown {
|
||||
None
|
||||
} else {
|
||||
|
@ -171,6 +186,7 @@ impl Backlog {
|
|||
incoming_conns: SpinLock::new(incoming_sockets),
|
||||
wait_queue: WaitQueue::new(),
|
||||
listener_cred: SocketCred::<ReadDupOp>::new_current(),
|
||||
is_seqpacket,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,7 +251,21 @@ impl Backlog {
|
|||
init: Init,
|
||||
pollee: Pollee,
|
||||
options: &SocketOptionSet,
|
||||
is_seqpacket: bool,
|
||||
) -> core::result::Result<Connected, (Error, Init)> {
|
||||
if is_seqpacket != self.is_seqpacket {
|
||||
// FIXME: According to the Linux implementation, we should avoid this error by
|
||||
// maintaining two socket tables for SOCK_STREAM sockets and SOCK_SEQPACKET sockets
|
||||
// separately.
|
||||
return Err((
|
||||
Error::with_message(
|
||||
Errno::ECONNREFUSED,
|
||||
"the listening socket has a different socket type",
|
||||
),
|
||||
init,
|
||||
));
|
||||
}
|
||||
|
||||
let mut locked_incoming_conns = self.incoming_conns.lock();
|
||||
|
||||
let Some(incoming_conns) = &mut *locked_incoming_conns else {
|
||||
|
|
|
@ -39,15 +39,18 @@ pub struct UnixStreamSocket {
|
|||
|
||||
pollee: Pollee,
|
||||
is_nonblocking: AtomicBool,
|
||||
|
||||
is_seqpacket: bool,
|
||||
}
|
||||
|
||||
impl UnixStreamSocket {
|
||||
pub(super) fn new_init(init: Init, is_nonblocking: bool) -> Arc<Self> {
|
||||
pub(super) fn new_init(init: Init, is_nonblocking: bool, is_seqpacket: bool) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
state: RwMutex::new(Takeable::new(State::Init(init))),
|
||||
options: RwMutex::new(OptionSet::new()),
|
||||
pollee: Pollee::new(),
|
||||
is_nonblocking: AtomicBool::new(is_nonblocking),
|
||||
is_seqpacket,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -55,6 +58,7 @@ impl UnixStreamSocket {
|
|||
connected: Connected,
|
||||
options: OptionSet,
|
||||
is_nonblocking: bool,
|
||||
is_seqpacket: bool,
|
||||
) -> Arc<Self> {
|
||||
let cloned_pollee = connected.cloned_pollee();
|
||||
Arc::new(Self {
|
||||
|
@ -62,6 +66,7 @@ impl UnixStreamSocket {
|
|||
options: RwMutex::new(options),
|
||||
pollee: cloned_pollee,
|
||||
is_nonblocking: AtomicBool::new(is_nonblocking),
|
||||
is_seqpacket,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -155,11 +160,11 @@ impl OptionSet {
|
|||
}
|
||||
|
||||
impl UnixStreamSocket {
|
||||
pub fn new(is_nonblocking: bool) -> Arc<Self> {
|
||||
Self::new_init(Init::new(), is_nonblocking)
|
||||
pub fn new(is_nonblocking: bool, is_seqpacket: bool) -> Arc<Self> {
|
||||
Self::new_init(Init::new(), is_nonblocking, is_seqpacket)
|
||||
}
|
||||
|
||||
pub fn new_pair(is_nonblocking: bool) -> (Arc<Self>, Arc<Self>) {
|
||||
pub fn new_pair(is_nonblocking: bool, is_seqpacket: bool) -> (Arc<Self>, Arc<Self>) {
|
||||
let cred = SocketCred::<ReadDupOp>::new_current();
|
||||
let options = OptionSet::new();
|
||||
|
||||
|
@ -173,8 +178,8 @@ impl UnixStreamSocket {
|
|||
&options.socket,
|
||||
);
|
||||
(
|
||||
Self::new_connected(conn_a, options, is_nonblocking),
|
||||
Self::new_connected(conn_b, OptionSet::new(), is_nonblocking),
|
||||
Self::new_connected(conn_a, options, is_nonblocking, is_seqpacket),
|
||||
Self::new_connected(conn_b, OptionSet::new(), is_nonblocking, is_seqpacket),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -185,7 +190,7 @@ impl UnixStreamSocket {
|
|||
_flags: SendRecvFlags,
|
||||
) -> Result<usize> {
|
||||
match self.state.read().as_ref() {
|
||||
State::Connected(connected) => connected.try_write(buf, aux_data),
|
||||
State::Connected(connected) => connected.try_write(buf, aux_data, self.is_seqpacket),
|
||||
State::Init(_) | State::Listen(_) => {
|
||||
return_errno_with_message!(Errno::ENOTCONN, "the socket is not connected")
|
||||
}
|
||||
|
@ -198,7 +203,7 @@ impl UnixStreamSocket {
|
|||
_flags: SendRecvFlags,
|
||||
) -> Result<(usize, Vec<ControlMessage>)> {
|
||||
match self.state.read().as_ref() {
|
||||
State::Connected(connected) => connected.try_read(buf),
|
||||
State::Connected(connected) => connected.try_read(buf, self.is_seqpacket),
|
||||
State::Init(_) | State::Listen(_) => {
|
||||
return_errno_with_message!(Errno::EINVAL, "the socket is not connected")
|
||||
}
|
||||
|
@ -232,8 +237,12 @@ impl UnixStreamSocket {
|
|||
}
|
||||
};
|
||||
|
||||
let connected = match backlog.push_incoming(init, self.pollee.clone(), &options.socket)
|
||||
{
|
||||
let connected = match backlog.push_incoming(
|
||||
init,
|
||||
self.pollee.clone(),
|
||||
&options.socket,
|
||||
self.is_seqpacket,
|
||||
) {
|
||||
Ok(connected) => connected,
|
||||
Err((err, init)) => return (State::Init(init), Err(err)),
|
||||
};
|
||||
|
@ -244,7 +253,7 @@ impl UnixStreamSocket {
|
|||
|
||||
fn try_accept(&self) -> Result<(Arc<dyn FileLike>, SocketAddr)> {
|
||||
match self.state.read().as_ref() {
|
||||
State::Listen(listen) => listen.try_accept() as _,
|
||||
State::Listen(listen) => listen.try_accept(self.is_seqpacket) as _,
|
||||
State::Init(_) | State::Connected(_) => {
|
||||
return_errno_with_message!(Errno::EINVAL, "the socket is not listening")
|
||||
}
|
||||
|
@ -326,7 +335,7 @@ impl Socket for UnixStreamSocket {
|
|||
}
|
||||
};
|
||||
|
||||
let listener = match init.listen(backlog, self.pollee.clone()) {
|
||||
let listener = match init.listen(backlog, self.pollee.clone(), self.is_seqpacket) {
|
||||
Ok(listener) => listener,
|
||||
Err((err, init)) => {
|
||||
return (State::Init(init), Err(err));
|
||||
|
@ -391,7 +400,7 @@ impl Socket for UnixStreamSocket {
|
|||
// According to the Linux man pages, `EISCONN` _may_ be returned when the destination
|
||||
// address is specified for a connection-mode socket. In practice, `sendmsg` on UNIX stream
|
||||
// sockets will fail due to that. We follow the same behavior as the Linux implementation.
|
||||
if addr.is_some() {
|
||||
if !self.is_seqpacket && addr.is_some() {
|
||||
match self.state.read().as_ref() {
|
||||
State::Init(_) | State::Listen(_) => return_errno_with_message!(
|
||||
Errno::EOPNOTSUPP,
|
||||
|
|
|
@ -23,11 +23,14 @@ pub fn sys_socket(domain: i32, type_: i32, protocol: i32, ctx: &Context) -> Resu
|
|||
"domain = {:?}, sock_type = {:?}, sock_flags = {:?}",
|
||||
domain, sock_type, sock_flags
|
||||
);
|
||||
|
||||
let is_nonblocking = sock_flags.contains(SockFlags::SOCK_NONBLOCK);
|
||||
let file_like = match (domain, sock_type) {
|
||||
// FIXME: SOCK_SEQPACKET is added to run fcntl_test, not supported yet.
|
||||
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_STREAM | SockType::SOCK_SEQPACKET) => {
|
||||
UnixStreamSocket::new(is_nonblocking) as Arc<dyn FileLike>
|
||||
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_STREAM) => {
|
||||
UnixStreamSocket::new(is_nonblocking, false) as Arc<dyn FileLike>
|
||||
}
|
||||
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_SEQPACKET) => {
|
||||
UnixStreamSocket::new(is_nonblocking, true) as Arc<dyn FileLike>
|
||||
}
|
||||
(CSocketAddrFamily::AF_INET, SockType::SOCK_STREAM) => {
|
||||
let protocol = Protocol::try_from(protocol)?;
|
||||
|
@ -81,6 +84,7 @@ pub fn sys_socket(domain: i32, type_: i32, protocol: i32, ctx: &Context) -> Resu
|
|||
}
|
||||
_ => return_errno_with_message!(Errno::EAFNOSUPPORT, "unsupported domain"),
|
||||
};
|
||||
|
||||
let fd = {
|
||||
let file_table = ctx.thread_local.borrow_file_table();
|
||||
let mut file_table_locked = file_table.unwrap().write();
|
||||
|
@ -91,5 +95,6 @@ pub fn sys_socket(domain: i32, type_: i32, protocol: i32, ctx: &Context) -> Resu
|
|||
};
|
||||
file_table_locked.insert(file_like, fd_flags)
|
||||
};
|
||||
|
||||
Ok(SyscallReturn::Return(fd as _))
|
||||
}
|
||||
|
|
|
@ -19,16 +19,19 @@ pub fn sys_socketpair(
|
|||
let sock_type = SockType::try_from(type_ & SOCK_TYPE_MASK)?;
|
||||
let sock_flags = SockFlags::from_bits_truncate(type_ & !SOCK_TYPE_MASK);
|
||||
let protocol = Protocol::try_from(protocol)?;
|
||||
|
||||
debug!(
|
||||
"domain = {:?}, sock_type = {:?}, sock_flags = {:?}, protocol = {:?}",
|
||||
domain, sock_type, sock_flags, protocol
|
||||
);
|
||||
|
||||
// TODO: deal with all sock_flags and protocol
|
||||
let nonblocking = sock_flags.contains(SockFlags::SOCK_NONBLOCK);
|
||||
let (socket_a, socket_b) = match (domain, sock_type) {
|
||||
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_STREAM) => {
|
||||
UnixStreamSocket::new_pair(nonblocking)
|
||||
UnixStreamSocket::new_pair(nonblocking, false)
|
||||
}
|
||||
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_SEQPACKET) => {
|
||||
UnixStreamSocket::new_pair(nonblocking, true)
|
||||
}
|
||||
_ => return_errno_with_message!(
|
||||
Errno::EAFNOSUPPORT,
|
||||
|
@ -48,8 +51,8 @@ pub fn sys_socketpair(
|
|||
let fd_b = file_table_locked.insert(socket_b, fd_flags);
|
||||
SocketFds(fd_a, fd_b)
|
||||
};
|
||||
|
||||
ctx.user_space().write_val(sv, &socket_fds)?;
|
||||
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
||||
|
|
|
@ -346,6 +346,8 @@ impl<R: Deref<Target = RingBuffer<u8>>> Producer<u8, R> {
|
|||
rb.advance_tail(tail, write_len);
|
||||
Ok(write_len)
|
||||
}
|
||||
|
||||
// There is no counterpart to `Consumer::skip`. It does not make sense for the producer.
|
||||
}
|
||||
|
||||
#[inherit_methods(from = "self.rb")]
|
||||
|
@ -460,6 +462,22 @@ impl<R: Deref<Target = RingBuffer<u8>>> Consumer<u8, R> {
|
|||
rb.advance_head(head, read_len);
|
||||
Ok(read_len)
|
||||
}
|
||||
|
||||
/// Skips `count` bytes in the `RingBuffer`.
|
||||
///
|
||||
/// In other words, `count` bytes are read from the `RingBuffer` and discarded.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if the number of the available bytes to read is less than `count`.
|
||||
pub fn skip(&mut self, count: usize) {
|
||||
let rb = &self.rb;
|
||||
let len = rb.len();
|
||||
assert!(len >= count);
|
||||
|
||||
let head = rb.head();
|
||||
rb.advance_head(head, count);
|
||||
}
|
||||
}
|
||||
|
||||
#[inherit_methods(from = "self.rb")]
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#define SOCK_TYPE SOCK_SEQPACKET
|
||||
#include "unix_streamlike_prologue.h"
|
||||
|
||||
FN_TEST(sendto)
|
||||
{
|
||||
char buf[1] = { 'z' };
|
||||
|
||||
TEST_ERRNO(sendto(sk_unbound, buf, 1, 0, &LISTEN_ADDR, LISTEN_ADDRLEN),
|
||||
ENOTCONN);
|
||||
TEST_ERRNO(sendto(sk_bound, buf, 1, 0, &LISTEN_ADDR2, LISTEN_ADDRLEN2),
|
||||
ENOTCONN);
|
||||
TEST_ERRNO(sendto(sk_listen, buf, 1, 0, &BOUND_ADDR, BOUND_ADDRLEN),
|
||||
ENOTCONN);
|
||||
}
|
||||
END_TEST()
|
||||
|
||||
FN_TEST(send_recv_trunc)
|
||||
{
|
||||
int fildes[2];
|
||||
char buf[1];
|
||||
|
||||
TEST_SUCC(
|
||||
socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, fildes));
|
||||
|
||||
TEST_SUCC(send(fildes[0], "abc", 3, 0));
|
||||
TEST_SUCC(send(fildes[0], "def", 3, 0));
|
||||
TEST_SUCC(send(fildes[0], "hij", 3, 0));
|
||||
|
||||
TEST_RES(recv(fildes[1], buf, 1, 0), _ret == 1 && buf[0] == 'a');
|
||||
TEST_RES(recv(fildes[1], buf, 0, 0), _ret == 0);
|
||||
TEST_RES(recv(fildes[1], buf, 1, 0), _ret == 1 && buf[0] == 'h');
|
||||
|
||||
TEST_SUCC(close(fildes[0]));
|
||||
TEST_SUCC(close(fildes[1]));
|
||||
}
|
||||
END_TEST()
|
||||
|
||||
FN_TEST(send_recv_zero)
|
||||
{
|
||||
int fildes[2];
|
||||
char buf[1];
|
||||
|
||||
TEST_SUCC(
|
||||
socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, fildes));
|
||||
|
||||
buf[0] = 'a';
|
||||
TEST_SUCC(send(fildes[0], buf, 1, 0));
|
||||
buf[0] = 'b';
|
||||
TEST_SUCC(send(fildes[0], buf, 0, 0));
|
||||
buf[0] = 'c';
|
||||
TEST_SUCC(send(fildes[0], buf, 0, 0));
|
||||
buf[0] = 'd';
|
||||
TEST_SUCC(send(fildes[0], buf, 1, 0));
|
||||
|
||||
TEST_RES(recv(fildes[1], buf, 1, 0), _ret == 1 && buf[0] == 'a');
|
||||
TEST_RES(recv(fildes[1], buf, 1, 0), _ret == 0 && buf[0] == 'a');
|
||||
TEST_RES(recv(fildes[1], buf, 1, 0), _ret == 0 && buf[0] == 'a');
|
||||
TEST_RES(recv(fildes[1], buf, 1, 0), _ret == 1 && buf[0] == 'd');
|
||||
|
||||
TEST_SUCC(close(fildes[0]));
|
||||
TEST_SUCC(close(fildes[1]));
|
||||
}
|
||||
END_TEST()
|
||||
|
||||
#include "unix_streamlike_epilogue.h"
|
|
@ -0,0 +1,117 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#define SOCK_TYPE SOCK_STREAM
|
||||
#include "unix_streamlike_prologue.h"
|
||||
|
||||
FN_TEST(sendto)
|
||||
{
|
||||
char buf[1] = { 'z' };
|
||||
|
||||
TEST_ERRNO(sendto(sk_unbound, buf, 1, 0, &LISTEN_ADDR, LISTEN_ADDRLEN),
|
||||
EOPNOTSUPP);
|
||||
TEST_ERRNO(sendto(sk_bound, buf, 1, 0, &LISTEN_ADDR2, LISTEN_ADDRLEN2),
|
||||
EOPNOTSUPP);
|
||||
TEST_ERRNO(sendto(sk_listen, buf, 1, 0, &BOUND_ADDR, BOUND_ADDRLEN),
|
||||
EOPNOTSUPP);
|
||||
TEST_ERRNO(sendto(sk_accepted, buf, 1, 0, &UNNAMED_ADDR,
|
||||
UNNAMED_ADDRLEN),
|
||||
EISCONN);
|
||||
}
|
||||
END_TEST()
|
||||
|
||||
FN_TEST(scm_rights)
|
||||
{
|
||||
int fildes[2];
|
||||
char buf[20] = "abcdefg";
|
||||
char cbuf[CMSG_SPACE(sizeof(int) * 3)];
|
||||
struct iovec iov;
|
||||
struct msghdr mhdr;
|
||||
struct cmsghdr *chdr;
|
||||
int *cdata;
|
||||
int cfds[2];
|
||||
|
||||
TEST_SUCC(socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fildes));
|
||||
|
||||
memset(&mhdr, 0, sizeof(mhdr));
|
||||
mhdr.msg_iov = &iov;
|
||||
mhdr.msg_iovlen = 1;
|
||||
mhdr.msg_control = cbuf;
|
||||
mhdr.msg_controllen = CMSG_SPACE(sizeof(int) * 3);
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = 1;
|
||||
|
||||
chdr = CMSG_FIRSTHDR(&mhdr);
|
||||
chdr->cmsg_level = SOL_SOCKET;
|
||||
chdr->cmsg_type = SCM_RIGHTS;
|
||||
chdr->cmsg_len = CMSG_SPACE(sizeof(int) * 3);
|
||||
|
||||
cdata = (int *)CMSG_DATA(chdr);
|
||||
TEST_SUCC(pipe(cfds));
|
||||
cdata[0] = cfds[0];
|
||||
cdata[1] = cfds[0];
|
||||
cdata[2] = cfds[1];
|
||||
|
||||
// Sending control messages with zero bytes to a stream socket
|
||||
// seems to "succeed". However, no data or control messages can
|
||||
// be transmitted.
|
||||
mhdr.msg_iovlen = 0;
|
||||
TEST_SUCC(sendmsg(fildes[0], &mhdr, 0));
|
||||
mhdr.msg_iovlen = 1;
|
||||
|
||||
// > (1) sendmsg(2) of four bytes, with no ancillary data.
|
||||
// > (2) sendmsg(2) of one byte, with ancillary data.
|
||||
// > (3) sendmsg(2) of four bytes, with no ancillary data.
|
||||
// -- https://man7.org/linux/man-pages/man7/unix.7.html
|
||||
TEST_RES(send(fildes[0], buf, 4, 0), _ret == 4);
|
||||
TEST_RES(sendmsg(fildes[0], &mhdr, 0), _ret == 1);
|
||||
TEST_RES(send(fildes[0], buf, 4, 0), _ret == 4);
|
||||
|
||||
memset(&mhdr, 0, sizeof(mhdr));
|
||||
mhdr.msg_iov = &iov;
|
||||
mhdr.msg_iovlen = 1;
|
||||
mhdr.msg_control = cbuf;
|
||||
mhdr.msg_controllen = CMSG_SPACE(sizeof(int));
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = sizeof(buf);
|
||||
|
||||
memset(cbuf, 0, sizeof(cbuf));
|
||||
|
||||
// > Suppose that the receiver now performs recvmsg(2) calls each with
|
||||
// > a buffer size of 20 bytes. The first call will receive five bytes
|
||||
// > of data, along with the ancillary data sent by the second
|
||||
// > sendmsg(2) call.
|
||||
TEST_RES(recvmsg(fildes[1], &mhdr, 0),
|
||||
_ret == 5 &&
|
||||
mhdr.msg_controllen == CMSG_SPACE(sizeof(int) * 2) &&
|
||||
(chdr = CMSG_FIRSTHDR(&mhdr)) &&
|
||||
chdr->cmsg_level == SOL_SOCKET &&
|
||||
chdr->cmsg_type == SCM_RIGHTS &&
|
||||
chdr->cmsg_len == CMSG_SPACE(sizeof(int) * 2) &&
|
||||
(cdata = (int *)CMSG_DATA(chdr)) &&
|
||||
cdata[0] == cfds[1] + 1 && cdata[1] == cfds[1] + 2);
|
||||
// > The next call will receive the remaining four
|
||||
// > bytes of data.
|
||||
TEST_RES(recv(fildes[1], buf, sizeof(buf), 0), _ret == 4);
|
||||
|
||||
// The purpose of the tests below is to verify that the received file
|
||||
// descriptors are functional.
|
||||
TEST_RES(write(cfds[1], "x", 1), _ret == 1);
|
||||
TEST_RES(read(cdata[0], buf, 1), _ret == 1 && buf[0] == 'x');
|
||||
TEST_RES(write(cfds[1], "y", 1), _ret == 1);
|
||||
TEST_RES(read(cdata[1], buf, 1), _ret == 1 && buf[0] == 'y');
|
||||
|
||||
TEST_SUCC(close(cdata[0]));
|
||||
TEST_SUCC(close(cdata[1]));
|
||||
TEST_SUCC(close(cfds[0]));
|
||||
|
||||
TEST_ERRNO(write(cfds[1], "y", 1), EPIPE);
|
||||
TEST_SUCC(close(cfds[1]));
|
||||
|
||||
TEST_SUCC(close(fildes[0]));
|
||||
TEST_SUCC(close(fildes[1]));
|
||||
}
|
||||
END_TEST()
|
||||
|
||||
#include "unix_streamlike_epilogue.h"
|
|
@ -0,0 +1,19 @@
|
|||
/* SPDX-License-Identifier: MPL-2.0 */
|
||||
|
||||
FN_SETUP(cleanup)
|
||||
{
|
||||
CHECK(close(sk_unbound));
|
||||
|
||||
CHECK(close(sk_bound));
|
||||
|
||||
CHECK(close(sk_listen));
|
||||
|
||||
CHECK(close(sk_connected));
|
||||
|
||||
CHECK(close(sk_accepted));
|
||||
|
||||
CHECK(unlink(BOUND_ADDR.sun_path));
|
||||
|
||||
CHECK(unlink(LISTEN_ADDR.sun_path));
|
||||
}
|
||||
END_SETUP()
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
/* SPDX-License-Identifier: MPL-2.0 */
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
|
@ -31,7 +31,7 @@ FN_TEST(socket_addresses)
|
|||
|
||||
#define MAKE_TEST(path, path_copy_len, path_len_to_kernel, path_buf_len, \
|
||||
path_len_from_kernel, path_from_kernel) \
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM, 0)); \
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_TYPE, 0)); \
|
||||
\
|
||||
memset(&addr, 0, sizeof(addr)); \
|
||||
addr.sun_family = AF_UNIX; \
|
||||
|
@ -86,7 +86,7 @@ FN_TEST(socket_addresses)
|
|||
#undef LONG_PATH
|
||||
#undef MAKE_TEST
|
||||
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM, 0));
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_TYPE, 0));
|
||||
|
||||
TEST_ERRNO(bind(sk, (struct sockaddr *)&addr, -1), EINVAL);
|
||||
TEST_ERRNO(bind(sk, (struct sockaddr *)&addr, PATH_OFFSET - 1), EINVAL);
|
||||
|
@ -120,13 +120,13 @@ static int sk_accepted;
|
|||
|
||||
FN_SETUP(unbound)
|
||||
{
|
||||
sk_unbound = CHECK(socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0));
|
||||
sk_unbound = CHECK(socket(PF_UNIX, SOCK_TYPE | SOCK_NONBLOCK, 0));
|
||||
}
|
||||
END_SETUP()
|
||||
|
||||
FN_SETUP(bound)
|
||||
{
|
||||
sk_bound = CHECK(socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0));
|
||||
sk_bound = CHECK(socket(PF_UNIX, SOCK_TYPE | SOCK_NONBLOCK, 0));
|
||||
|
||||
CHECK(bind(sk_bound, (struct sockaddr *)&BOUND_ADDR, BOUND_ADDRLEN));
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ END_SETUP()
|
|||
|
||||
FN_SETUP(listen)
|
||||
{
|
||||
sk_listen = CHECK(socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0));
|
||||
sk_listen = CHECK(socket(PF_UNIX, SOCK_TYPE | SOCK_NONBLOCK, 0));
|
||||
|
||||
CHECK(bind(sk_listen, (struct sockaddr *)&LISTEN_ADDR, LISTEN_ADDRLEN));
|
||||
|
||||
|
@ -144,7 +144,7 @@ END_SETUP()
|
|||
|
||||
FN_SETUP(connected)
|
||||
{
|
||||
sk_connected = CHECK(socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0));
|
||||
sk_connected = CHECK(socket(PF_UNIX, SOCK_TYPE | SOCK_NONBLOCK, 0));
|
||||
|
||||
CHECK(connect(sk_connected, (struct sockaddr *)&LISTEN_ADDR2,
|
||||
LISTEN_ADDRLEN2));
|
||||
|
@ -242,7 +242,7 @@ FN_TEST(bind_connected)
|
|||
struct sockaddr_un addr;
|
||||
socklen_t addrlen;
|
||||
|
||||
TEST_SUCC(socketpair(PF_UNIX, SOCK_STREAM, 0, fildes));
|
||||
TEST_SUCC(socketpair(PF_UNIX, SOCK_TYPE, 0, fildes));
|
||||
|
||||
TEST_SUCC(bind(fildes[0], (struct sockaddr *)&UNIX_ADDR("\0X"),
|
||||
PATH_OFFSET + 2));
|
||||
|
@ -340,16 +340,6 @@ FN_TEST(send)
|
|||
TEST_ERRNO(send(sk_listen, buf, 0, 0), ENOTCONN);
|
||||
TEST_ERRNO(write(sk_listen, buf, 1), ENOTCONN);
|
||||
TEST_ERRNO(write(sk_listen, buf, 0), ENOTCONN);
|
||||
|
||||
TEST_ERRNO(sendto(sk_unbound, buf, 1, 0, &LISTEN_ADDR, LISTEN_ADDRLEN),
|
||||
EOPNOTSUPP);
|
||||
TEST_ERRNO(sendto(sk_bound, buf, 1, 0, &LISTEN_ADDR2, LISTEN_ADDRLEN2),
|
||||
EOPNOTSUPP);
|
||||
TEST_ERRNO(sendto(sk_listen, buf, 1, 0, &BOUND_ADDR, BOUND_ADDRLEN),
|
||||
EOPNOTSUPP);
|
||||
TEST_ERRNO(sendto(sk_accepted, buf, 1, 0, &UNNAMED_ADDR,
|
||||
UNNAMED_ADDRLEN),
|
||||
EISCONN);
|
||||
}
|
||||
END_TEST()
|
||||
|
||||
|
@ -387,39 +377,39 @@ FN_TEST(blocking_connect)
|
|||
|
||||
// Setup
|
||||
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM, 0));
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_TYPE, 0));
|
||||
TEST_SUCC(
|
||||
bind(sk, (struct sockaddr *)&UNIX_ADDR("\0"), PATH_OFFSET + 1));
|
||||
TEST_SUCC(listen(sk, 2));
|
||||
|
||||
for (i = 0; i < 3; ++i) {
|
||||
sks[i] = TEST_SUCC(
|
||||
socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0));
|
||||
socket(PF_UNIX, SOCK_TYPE | SOCK_NONBLOCK, 0));
|
||||
TEST_SUCC(connect(sks[i], (struct sockaddr *)&UNIX_ADDR("\0"),
|
||||
PATH_OFFSET + 1));
|
||||
}
|
||||
|
||||
#define MAKE_TEST(child, parent, errno) \
|
||||
sks[i] = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)); \
|
||||
TEST_ERRNO(connect(sks[i], (struct sockaddr *)&UNIX_ADDR("\0"), \
|
||||
PATH_OFFSET + 1), \
|
||||
EAGAIN); \
|
||||
TEST_SUCC(close(sks[i])); \
|
||||
\
|
||||
pid = TEST_SUCC(fork()); \
|
||||
if (pid == 0) { \
|
||||
usleep(300 * 1000); \
|
||||
CHECK(child); \
|
||||
exit(0); \
|
||||
} \
|
||||
TEST_SUCC(parent); \
|
||||
\
|
||||
sks[i] = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM, 0)); \
|
||||
TEST_ERRNO(connect(sks[i], (struct sockaddr *)&UNIX_ADDR("\0"), \
|
||||
PATH_OFFSET + 1), \
|
||||
errno); \
|
||||
\
|
||||
TEST_SUCC(close(sks[i])); \
|
||||
#define MAKE_TEST(child, parent, errno) \
|
||||
sks[i] = TEST_SUCC(socket(PF_UNIX, SOCK_TYPE | SOCK_NONBLOCK, 0)); \
|
||||
TEST_ERRNO(connect(sks[i], (struct sockaddr *)&UNIX_ADDR("\0"), \
|
||||
PATH_OFFSET + 1), \
|
||||
EAGAIN); \
|
||||
TEST_SUCC(close(sks[i])); \
|
||||
\
|
||||
pid = TEST_SUCC(fork()); \
|
||||
if (pid == 0) { \
|
||||
usleep(300 * 1000); \
|
||||
CHECK(child); \
|
||||
exit(0); \
|
||||
} \
|
||||
TEST_SUCC(parent); \
|
||||
\
|
||||
sks[i] = TEST_SUCC(socket(PF_UNIX, SOCK_TYPE, 0)); \
|
||||
TEST_ERRNO(connect(sks[i], (struct sockaddr *)&UNIX_ADDR("\0"), \
|
||||
PATH_OFFSET + 1), \
|
||||
errno); \
|
||||
\
|
||||
TEST_SUCC(close(sks[i])); \
|
||||
TEST_SUCC(wait(NULL));
|
||||
|
||||
// Test 1: Accepting a connection resumes the blocked connection request
|
||||
|
@ -474,14 +464,14 @@ FN_TEST(ns_abs)
|
|||
struct sockaddr_un addr;
|
||||
socklen_t addrlen;
|
||||
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM, 0));
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_TYPE, 0));
|
||||
|
||||
TEST_SUCC(bind(sk, (struct sockaddr *)&UNIX_ADDR(""), PATH_OFFSET));
|
||||
addrlen = sizeof(addr);
|
||||
TEST_RES(getsockname(sk, (struct sockaddr *)&addr, &addrlen),
|
||||
addrlen == PATH_OFFSET + 6 && addr.sun_path[0] == '\0');
|
||||
|
||||
sk2 = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM, 0));
|
||||
sk2 = TEST_SUCC(socket(PF_UNIX, SOCK_TYPE, 0));
|
||||
|
||||
TEST_ERRNO(bind(sk2, (struct sockaddr *)&addr, addrlen), EADDRINUSE);
|
||||
TEST_ERRNO(connect(sk2, (struct sockaddr *)&addr, addrlen),
|
||||
|
@ -492,7 +482,7 @@ FN_TEST(ns_abs)
|
|||
TEST_SUCC(close(sk));
|
||||
TEST_SUCC(close(sk2));
|
||||
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM, 0));
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_TYPE, 0));
|
||||
TEST_ERRNO(connect(sk, (struct sockaddr *)&addr, addrlen),
|
||||
ECONNREFUSED);
|
||||
TEST_SUCC(bind(sk, (struct sockaddr *)&addr, addrlen));
|
||||
|
@ -504,7 +494,7 @@ FN_TEST(shutdown_connected)
|
|||
{
|
||||
int fildes[2];
|
||||
|
||||
TEST_SUCC(socketpair(PF_UNIX, SOCK_STREAM, 0, fildes));
|
||||
TEST_SUCC(socketpair(PF_UNIX, SOCK_TYPE, 0, fildes));
|
||||
|
||||
TEST_SUCC(shutdown(fildes[0], SHUT_RD));
|
||||
TEST_SUCC(shutdown(fildes[0], SHUT_WR));
|
||||
|
@ -524,7 +514,7 @@ FN_TEST(poll_unbound)
|
|||
int sk;
|
||||
struct pollfd pfd = { .events = POLLIN | POLLOUT | POLLRDHUP };
|
||||
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM, 0));
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_TYPE, 0));
|
||||
pfd.fd = sk;
|
||||
|
||||
TEST_RES(poll(&pfd, 1, 0), pfd.revents == (POLLOUT | POLLHUP));
|
||||
|
@ -552,7 +542,7 @@ FN_TEST(poll_listen)
|
|||
int sk;
|
||||
struct pollfd pfd = { .events = POLLIN | POLLOUT | POLLRDHUP };
|
||||
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM, 0));
|
||||
sk = TEST_SUCC(socket(PF_UNIX, SOCK_TYPE, 0));
|
||||
pfd.fd = sk;
|
||||
|
||||
TEST_SUCC(
|
||||
|
@ -577,7 +567,7 @@ FN_TEST(poll_connected_close)
|
|||
int fildes[2];
|
||||
struct pollfd pfd = { .events = POLLIN | POLLOUT | POLLRDHUP };
|
||||
|
||||
TEST_SUCC(socketpair(PF_UNIX, SOCK_STREAM, 0, fildes));
|
||||
TEST_SUCC(socketpair(PF_UNIX, SOCK_TYPE, 0, fildes));
|
||||
|
||||
pfd.fd = fildes[1];
|
||||
TEST_RES(poll(&pfd, 1, 0), pfd.revents == POLLOUT);
|
||||
|
@ -597,18 +587,18 @@ FN_TEST(poll_connected_shutdown)
|
|||
int fildes[2];
|
||||
struct pollfd pfd = { .events = POLLIN | POLLOUT | POLLRDHUP };
|
||||
|
||||
#define MAKE_TEST(shut, ev1, ev2) \
|
||||
TEST_SUCC(socketpair(PF_UNIX, SOCK_STREAM, 0, fildes)); \
|
||||
\
|
||||
TEST_SUCC(shutdown(fildes[0], shut)); \
|
||||
\
|
||||
pfd.fd = fildes[0]; \
|
||||
TEST_RES(poll(&pfd, 1, 0), pfd.revents == (ev1)); \
|
||||
\
|
||||
pfd.fd = fildes[1]; \
|
||||
TEST_RES(poll(&pfd, 1, 0), pfd.revents == (ev2)); \
|
||||
\
|
||||
TEST_SUCC(close(fildes[0])); \
|
||||
#define MAKE_TEST(shut, ev1, ev2) \
|
||||
TEST_SUCC(socketpair(PF_UNIX, SOCK_TYPE, 0, fildes)); \
|
||||
\
|
||||
TEST_SUCC(shutdown(fildes[0], shut)); \
|
||||
\
|
||||
pfd.fd = fildes[0]; \
|
||||
TEST_RES(poll(&pfd, 1, 0), pfd.revents == (ev1)); \
|
||||
\
|
||||
pfd.fd = fildes[1]; \
|
||||
TEST_RES(poll(&pfd, 1, 0), pfd.revents == (ev2)); \
|
||||
\
|
||||
TEST_SUCC(close(fildes[0])); \
|
||||
TEST_SUCC(close(fildes[1]));
|
||||
|
||||
MAKE_TEST(SHUT_RD, POLLIN | POLLOUT | POLLRDHUP, POLLOUT);
|
||||
|
@ -630,8 +620,8 @@ FN_TEST(epoll)
|
|||
|
||||
// Setup
|
||||
|
||||
sk2_listen = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM, 0));
|
||||
sk2_connected = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM, 0));
|
||||
sk2_listen = TEST_SUCC(socket(PF_UNIX, SOCK_TYPE, 0));
|
||||
sk2_connected = TEST_SUCC(socket(PF_UNIX, SOCK_TYPE, 0));
|
||||
|
||||
epfd_listen = TEST_SUCC(epoll_create1(0));
|
||||
ev.events = EPOLLIN;
|
||||
|
@ -677,31 +667,13 @@ FN_TEST(epoll)
|
|||
}
|
||||
END_TEST()
|
||||
|
||||
FN_SETUP(cleanup)
|
||||
{
|
||||
CHECK(close(sk_unbound));
|
||||
|
||||
CHECK(close(sk_bound));
|
||||
|
||||
CHECK(close(sk_listen));
|
||||
|
||||
CHECK(close(sk_connected));
|
||||
|
||||
CHECK(close(sk_accepted));
|
||||
|
||||
CHECK(unlink(BOUND_ADDR.sun_path));
|
||||
|
||||
CHECK(unlink(LISTEN_ADDR.sun_path));
|
||||
}
|
||||
END_SETUP()
|
||||
|
||||
// See also `zero_reads_always_succeed` in `pipe_err.c`
|
||||
FN_TEST(zero_recvs_may_fail)
|
||||
{
|
||||
int fildes[2];
|
||||
char buf[1] = { 'z' };
|
||||
|
||||
TEST_SUCC(socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fildes));
|
||||
TEST_SUCC(socketpair(AF_UNIX, SOCK_TYPE | SOCK_NONBLOCK, 0, fildes));
|
||||
|
||||
TEST_ERRNO(recv(fildes[0], buf, 0, 0), EAGAIN);
|
||||
|
||||
|
@ -719,7 +691,7 @@ FN_TEST(zero_sends_may_fail)
|
|||
int fildes[2];
|
||||
char buf[1] = { 'z' };
|
||||
|
||||
TEST_SUCC(socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fildes));
|
||||
TEST_SUCC(socketpair(AF_UNIX, SOCK_TYPE | SOCK_NONBLOCK, 0, fildes));
|
||||
|
||||
TEST_SUCC(send(fildes[1], buf, 0, 0));
|
||||
|
||||
|
@ -729,98 +701,3 @@ FN_TEST(zero_sends_may_fail)
|
|||
TEST_SUCC(close(fildes[1]));
|
||||
}
|
||||
END_TEST()
|
||||
|
||||
FN_TEST(scm_rights)
|
||||
{
|
||||
int fildes[2];
|
||||
char buf[20] = "abcdefg";
|
||||
char cbuf[CMSG_SPACE(sizeof(int) * 3)];
|
||||
struct iovec iov;
|
||||
struct msghdr mhdr;
|
||||
struct cmsghdr *chdr;
|
||||
int *cdata;
|
||||
int cfds[2];
|
||||
|
||||
TEST_SUCC(socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fildes));
|
||||
|
||||
memset(&mhdr, 0, sizeof(mhdr));
|
||||
mhdr.msg_iov = &iov;
|
||||
mhdr.msg_iovlen = 1;
|
||||
mhdr.msg_control = cbuf;
|
||||
mhdr.msg_controllen = CMSG_SPACE(sizeof(int) * 3);
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = 1;
|
||||
|
||||
chdr = CMSG_FIRSTHDR(&mhdr);
|
||||
chdr->cmsg_level = SOL_SOCKET;
|
||||
chdr->cmsg_type = SCM_RIGHTS;
|
||||
chdr->cmsg_len = CMSG_SPACE(sizeof(int) * 3);
|
||||
|
||||
cdata = (int *)CMSG_DATA(chdr);
|
||||
TEST_SUCC(pipe(cfds));
|
||||
cdata[0] = cfds[0];
|
||||
cdata[1] = cfds[0];
|
||||
cdata[2] = cfds[1];
|
||||
|
||||
// Sending control messages with zero bytes to a stream socket
|
||||
// seems to "succeed". However, no data or control messages can
|
||||
// be transmitted.
|
||||
mhdr.msg_iovlen = 0;
|
||||
TEST_SUCC(sendmsg(fildes[0], &mhdr, 0));
|
||||
mhdr.msg_iovlen = 1;
|
||||
|
||||
// > (1) sendmsg(2) of four bytes, with no ancillary data.
|
||||
// > (2) sendmsg(2) of one byte, with ancillary data.
|
||||
// > (3) sendmsg(2) of four bytes, with no ancillary data.
|
||||
// -- https://man7.org/linux/man-pages/man7/unix.7.html
|
||||
TEST_RES(send(fildes[0], buf, 4, 0), _ret == 4);
|
||||
TEST_RES(sendmsg(fildes[0], &mhdr, 0), _ret == 1);
|
||||
TEST_RES(send(fildes[0], buf, 4, 0), _ret == 4);
|
||||
|
||||
memset(&mhdr, 0, sizeof(mhdr));
|
||||
mhdr.msg_iov = &iov;
|
||||
mhdr.msg_iovlen = 1;
|
||||
mhdr.msg_control = cbuf;
|
||||
mhdr.msg_controllen = CMSG_SPACE(sizeof(int));
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = sizeof(buf);
|
||||
|
||||
memset(cbuf, 0, sizeof(cbuf));
|
||||
|
||||
// > Suppose that the receiver now performs recvmsg(2) calls each with
|
||||
// > a buffer size of 20 bytes. The first call will receive five bytes
|
||||
// > of data, along with the ancillary data sent by the second
|
||||
// > sendmsg(2) call.
|
||||
TEST_RES(recvmsg(fildes[1], &mhdr, 0),
|
||||
_ret == 5 &&
|
||||
mhdr.msg_controllen == CMSG_SPACE(sizeof(int) * 2) &&
|
||||
(chdr = CMSG_FIRSTHDR(&mhdr)) &&
|
||||
chdr->cmsg_level == SOL_SOCKET &&
|
||||
chdr->cmsg_type == SCM_RIGHTS &&
|
||||
chdr->cmsg_len == CMSG_SPACE(sizeof(int) * 2) &&
|
||||
(cdata = (int *)CMSG_DATA(chdr)) &&
|
||||
cdata[0] == cfds[1] + 1 && cdata[1] == cfds[1] + 2);
|
||||
// > The next call will receive the remaining four
|
||||
// > bytes of data.
|
||||
TEST_RES(recv(fildes[1], buf, sizeof(buf), 0), _ret == 4);
|
||||
|
||||
// The purpose of the tests below is to verify that the received file
|
||||
// descriptors are functional.
|
||||
TEST_RES(write(cfds[1], "x", 1), _ret == 1);
|
||||
TEST_RES(read(cdata[0], buf, 1), _ret == 1 && buf[0] == 'x');
|
||||
TEST_RES(write(cfds[1], "y", 1), _ret == 1);
|
||||
TEST_RES(read(cdata[1], buf, 1), _ret == 1 && buf[0] == 'y');
|
||||
|
||||
TEST_SUCC(close(cdata[0]));
|
||||
TEST_SUCC(close(cdata[1]));
|
||||
TEST_SUCC(close(cfds[0]));
|
||||
|
||||
TEST_ERRNO(write(cfds[1], "y", 1), EPIPE);
|
||||
TEST_SUCC(close(cfds[1]));
|
||||
|
||||
TEST_SUCC(close(fildes[0]));
|
||||
TEST_SUCC(close(fildes[1]));
|
||||
}
|
||||
END_TEST()
|
|
@ -33,7 +33,8 @@ sleep 0.2
|
|||
./tcp_err
|
||||
./tcp_poll
|
||||
./udp_err
|
||||
./unix_err
|
||||
./unix_stream_err
|
||||
./unix_seqpacket_err
|
||||
|
||||
./netlink_route
|
||||
./rtnl_err
|
||||
|
|
|
@ -37,10 +37,11 @@
|
|||
#include <string.h>
|
||||
|
||||
/** Starts the definition of a setup function. */
|
||||
#define FN_SETUP(name) \
|
||||
void setup_##name(void) __attribute__((constructor(__LINE__ + 200))); \
|
||||
\
|
||||
void setup_##name(void) \
|
||||
#define FN_SETUP(name) \
|
||||
void setup_##name(void) \
|
||||
__attribute__((constructor(__COUNTER__ + 200))); \
|
||||
\
|
||||
void setup_##name(void) \
|
||||
{
|
||||
/** Ends the definition of a setup function. */
|
||||
#define END_SETUP() }
|
||||
|
@ -84,11 +85,12 @@
|
|||
static int __total_failures;
|
||||
|
||||
/** Starts the definition of a test function. */
|
||||
#define FN_TEST(name) \
|
||||
void test_##name(void) __attribute__((constructor(__LINE__ + 200))); \
|
||||
\
|
||||
void test_##name(void) \
|
||||
{ \
|
||||
#define FN_TEST(name) \
|
||||
void test_##name(void) \
|
||||
__attribute__((constructor(__COUNTER__ + 200))); \
|
||||
\
|
||||
void test_##name(void) \
|
||||
{ \
|
||||
int __tests_passed = 0, __tests_failed = 0;
|
||||
|
||||
/** Ends the definition of a test function. */
|
||||
|
|
|
@ -51,7 +51,10 @@ TESTS ?= \
|
|||
signalfd_test \
|
||||
socket_netlink_route_test \
|
||||
socket_unix_pair_test \
|
||||
socket_unix_seqpacket_local_test \
|
||||
socket_unix_stream_test \
|
||||
socket_unix_unbound_seqpacket_test \
|
||||
socket_unix_unbound_stream_test \
|
||||
stat_test \
|
||||
stat_times_test \
|
||||
statfs_test \
|
||||
|
|
|
@ -1,11 +1,3 @@
|
|||
# TODO: Support `SOCK_SEQPACKET` sockets
|
||||
AllUnixDomainSockets/UnixSocketPairTest.BindToBadName/*
|
||||
AllUnixDomainSockets/UnixSocketPairTest.BindToBadFamily/*
|
||||
AllUnixDomainSockets/*/4
|
||||
AllUnixDomainSockets/*/5
|
||||
AllUnixDomainSockets/*/10
|
||||
AllUnixDomainSockets/*/11
|
||||
|
||||
# TODO: Support `SOCK_DGRAM` sockets
|
||||
AllUnixDomainSockets/*/2
|
||||
AllUnixDomainSockets/*/3
|
||||
|
@ -23,5 +15,17 @@ AllUnixDomainSockets/UnixSocketPairTest.NetdeviceIoctlsSucceed/*
|
|||
# TODO: Fix operations on `/proc/*/fd/*`
|
||||
AllUnixDomainSockets/UnixSocketPairTest.SocketReopenFromProcfs/*
|
||||
|
||||
# TODO: Support control messages
|
||||
AllUnixDomainSockets/UnixSocketPairCmsgTest.*
|
||||
# TODO: Report `MSG_(C)TRUNC` after truncating the (control) message
|
||||
AllUnixDomainSockets/UnixSocketPairCmsgTest.BasicFDPassNoSpaceMsgCtrunc/*
|
||||
AllUnixDomainSockets/UnixSocketPairCmsgTest.BasicFDPassNullControlMsgCtrunc/*
|
||||
AllUnixDomainSockets/UnixSocketPairCmsgTest.BasicFDPassNotEnoughSpaceMsgCtrunc/*
|
||||
AllUnixDomainSockets/UnixSocketPairCmsgTest.BasicThreeFDPassTruncationMsgCtrunc/*
|
||||
AllUnixDomainSockets/UnixSocketPairCmsgTest.BasicTwoFDPassUnalignedRecvTruncationMsgTrunc/*
|
||||
AllUnixDomainSockets/UnixSocketPairCmsgTest.CredPassNoSpaceMsgCtrunc/*
|
||||
AllUnixDomainSockets/UnixSocketPairCmsgTest.CredPassTruncatedMsgCtrunc/*
|
||||
|
||||
# TODO: Support `MSG_CMSG_CLOEXEC`
|
||||
AllUnixDomainSockets/UnixSocketPairCmsgTest.CloexecRecvFDPass/*
|
||||
|
||||
# TODO: Support `MSG_PEEK`
|
||||
AllUnixDomainSockets/UnixSocketPairCmsgTest.FDPassPeek/*
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# TODO: Support `MSG_DONTWAIT` and `MSG_PEEK`
|
||||
SeqpacketUnixSockets/NonStreamSocketPairTest.SplitRecv/*
|
||||
SeqpacketUnixSockets/NonStreamSocketPairTest.SinglePeek/*
|
||||
SeqpacketUnixSockets/NonStreamSocketPairTest.RecvmsgTruncPeekDontwaitZeroLen/*
|
||||
|
||||
# TODO: Support `SO_SNDTIMEO`
|
||||
SeqpacketUnixSockets/UnixNonStreamSocketPairTest.SendTimeout/*
|
||||
|
||||
# TODO: Support `MSG_TRUNC`
|
||||
SeqpacketUnixSockets/NonStreamSocketPairTest.MsgTruncTruncation/*
|
||||
SeqpacketUnixSockets/NonStreamSocketPairTest.MsgTruncTruncationRecvmsgMsghdrFlagMsgTrunc/*
|
||||
SeqpacketUnixSockets/NonStreamSocketPairTest.RecvmsgMsgTruncZeroLen/*
|
||||
SeqpacketUnixSockets/NonStreamSocketPairTest.RecvmsgMsgTruncMsgPeekZeroLen/*
|
Loading…
Reference in New Issue