Skip to content

Commit

Permalink
Add connect_unspec (#903)
Browse files Browse the repository at this point in the history
  • Loading branch information
badeend authored Oct 26, 2023
1 parent b01b482 commit b0a83ce
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/backend/libc/net/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,19 @@ pub(crate) fn connect_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io:
}
}

#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn connect_unspec(sockfd: BorrowedFd<'_>) -> io::Result<()> {
debug_assert_eq!(c::AF_UNSPEC, 0);
let addr = MaybeUninit::<c::sockaddr_storage>::zeroed();
unsafe {
ret(c::connect(
borrowed_fd(sockfd),
as_ptr(&addr).cast(),
size_of::<c::sockaddr_storage>() as c::socklen_t,
))
}
}

#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn listen(sockfd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()> {
unsafe { ret(c::listen(borrowed_fd(sockfd), backlog)) }
Expand Down
28 changes: 28 additions & 0 deletions src/backend/linux_raw/net/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,34 @@ pub(crate) fn connect_unix(fd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Res
}
}

#[inline]
pub(crate) fn connect_unspec(fd: BorrowedFd<'_>) -> io::Result<()> {
debug_assert_eq!(c::AF_UNSPEC, 0);
let addr = MaybeUninit::<c::sockaddr_storage>::zeroed();

#[cfg(not(target_arch = "x86"))]
unsafe {
ret(syscall_readonly!(
__NR_connect,
fd,
by_ref(&addr),
size_of::<c::sockaddr_storage, _>()
))
}
#[cfg(target_arch = "x86")]
unsafe {
ret(syscall_readonly!(
__NR_socketcall,
x86_sys(SYS_CONNECT),
slice_just_addr::<ArgReg<'_, SocketArg>, _>(&[
fd.into(),
by_ref(&addr),
size_of::<c::sockaddr_storage, _>(),
])
))
}
}

#[inline]
pub(crate) fn listen(fd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()> {
#[cfg(not(target_arch = "x86"))]
Expand Down
35 changes: 35 additions & 0 deletions src/net/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,41 @@ pub fn connect_unix<Fd: AsFd>(sockfd: Fd, addr: &SocketAddrUnix) -> io::Result<(
backend::net::syscalls::connect_unix(sockfd.as_fd(), addr)
}

/// `connect(sockfd, {.sa_family = AF_UNSPEC}, sizeof(struct sockaddr))`
/// — Dissolve the socket's association.
///
/// On UDP sockets, BSD platforms report AFNOSUPPORT or INVAL even if the disconnect was successful.
///
/// # References
/// - [Beej's Guide to Network Programming]
/// - [POSIX]
/// - [Linux]
/// - [Apple]
/// - [Winsock2]
/// - [FreeBSD]
/// - [NetBSD]
/// - [OpenBSD]
/// - [DragonFly BSD]
/// - [illumos]
/// - [glibc]
///
/// [Beej's Guide to Network Programming]: https://beej.us/guide/bgnet/html/split/system-calls-or-bust.html#connect
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html
/// [Linux]: https://man7.org/linux/man-pages/man2/connect.2.html
/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/connect.2.html
/// [Winsock2]: https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect
/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=connect&sektion=2
/// [NetBSD]: https://man.netbsd.org/connect.2
/// [OpenBSD]: https://man.openbsd.org/connect.2
/// [DragonFly BSD]: https://man.dragonflybsd.org/?command=connect&section=2
/// [illumos]: https://illumos.org/man/3SOCKET/connect
/// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Connecting.html
#[inline]
#[doc(alias = "connect")]
pub fn connect_unspec<Fd: AsFd>(sockfd: Fd) -> io::Result<()> {
backend::net::syscalls::connect_unspec(sockfd.as_fd())
}

/// `listen(fd, backlog)`—Enables listening for incoming connections.
///
/// # References
Expand Down
88 changes: 88 additions & 0 deletions tests/net/connect_bind_send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,94 @@ fn net_v6_connect() {
assert_eq!(request, &response[..n]);
}

/// Test `connect_unspec`.
#[test]
fn net_v4_connect_unspec() {
const SOME_PORT: u16 = 47;
let localhost_addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, SOME_PORT);

let socket = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap();

rustix::net::connect_v4(&socket, &localhost_addr).expect("connect_v4");
assert_eq!(getsockname_v4(&socket).unwrap().ip(), &Ipv4Addr::LOCALHOST);
assert_eq!(getpeername_v4(&socket).unwrap(), localhost_addr);

match rustix::net::connect_unspec(&socket) {
// BSD platforms return an error even if the socket was disconnected successfully.
#[cfg(bsd)]
Err(rustix::io::Errno::INVAL | rustix::io::Errno::AFNOSUPPORT) => {}
r => r.expect("connect_unspec"),
}
assert_eq!(
getsockname_v4(&socket).unwrap().ip(),
&Ipv4Addr::UNSPECIFIED
);
assert_eq!(getpeername_v4(&socket), Err(rustix::io::Errno::NOTCONN));

rustix::net::connect_v4(&socket, &localhost_addr).expect("connect_v4");
assert_eq!(getsockname_v4(&socket).unwrap().ip(), &Ipv4Addr::LOCALHOST);
assert_eq!(getpeername_v4(&socket).unwrap(), localhost_addr);

fn getsockname_v4<Fd: rustix::fd::AsFd>(sockfd: Fd) -> rustix::io::Result<SocketAddrV4> {
match rustix::net::getsockname(sockfd)? {
SocketAddrAny::V4(addr_v4) => Ok(addr_v4),
_ => Err(rustix::io::Errno::AFNOSUPPORT),
}
}

fn getpeername_v4<Fd: rustix::fd::AsFd>(sockfd: Fd) -> rustix::io::Result<SocketAddrV4> {
match rustix::net::getpeername(sockfd)? {
Some(SocketAddrAny::V4(addr_v4)) => Ok(addr_v4),
None => Err(rustix::io::Errno::NOTCONN),
_ => Err(rustix::io::Errno::AFNOSUPPORT),
}
}
}

/// Test `connect_unspec`.
#[test]
fn net_v6_connect_unspec() {
const SOME_PORT: u16 = 47;
let localhost_addr = SocketAddrV6::new(Ipv6Addr::LOCALHOST, SOME_PORT, 0, 0);

let socket = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap();

rustix::net::connect_v6(&socket, &localhost_addr).expect("connect_v6");
assert_eq!(getsockname_v6(&socket).unwrap().ip(), &Ipv6Addr::LOCALHOST);
assert_eq!(getpeername_v6(&socket).unwrap(), localhost_addr);

match rustix::net::connect_unspec(&socket) {
// BSD platforms return an error even if the socket was disconnected successfully.
#[cfg(bsd)]
Err(rustix::io::Errno::INVAL | rustix::io::Errno::AFNOSUPPORT) => {}
r => r.expect("connect_unspec"),
}
assert_eq!(
getsockname_v6(&socket).unwrap().ip(),
&Ipv6Addr::UNSPECIFIED
);
assert_eq!(getpeername_v6(&socket), Err(rustix::io::Errno::NOTCONN));

rustix::net::connect_v6(&socket, &localhost_addr).expect("connect_v6");
assert_eq!(getsockname_v6(&socket).unwrap().ip(), &Ipv6Addr::LOCALHOST);
assert_eq!(getpeername_v6(&socket).unwrap(), localhost_addr);

fn getsockname_v6<Fd: rustix::fd::AsFd>(sockfd: Fd) -> rustix::io::Result<SocketAddrV6> {
match rustix::net::getsockname(sockfd)? {
SocketAddrAny::V6(addr_v6) => Ok(addr_v6),
_ => Err(rustix::io::Errno::AFNOSUPPORT),
}
}

fn getpeername_v6<Fd: rustix::fd::AsFd>(sockfd: Fd) -> rustix::io::Result<SocketAddrV6> {
match rustix::net::getpeername(sockfd)? {
Some(SocketAddrAny::V6(addr_v6)) => Ok(addr_v6),
None => Err(rustix::io::Errno::NOTCONN),
_ => Err(rustix::io::Errno::AFNOSUPPORT),
}
}
}

/// Test `bind_any`.
#[test]
fn net_v4_bind_any() {
Expand Down

0 comments on commit b0a83ce

Please sign in to comment.