New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add trait to reduce duplicated code between socket address types, implement netlink addresses #1004
base: main
Are you sure you want to change the base?
Conversation
f694272
to
4c1c746
Compare
As a quick update here, I am looking forward to reviewing this; I've just been busy. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this approach, it allows me to very easily define a socket address not yet present in this crate, like so
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)] // fields are never read directly because they get memcp'd into another struct
#[repr(C)]
struct SocketAddressHci {
hci_family: rustix::net::AddressFamily,
hci_dev: u16,
hci_channel: u16,
}
unsafe impl SocketAddress for SocketAddressHci {
/// SAFETY: `SocketAddressHci` has the same layout as the type in the kernel and can therefore safely be passed in a syscall
type CSockAddr = Self;
fn encode(&self) -> Self::CSockAddr {
self.clone()
}
}
I left some comment about the code structure, most of it more nit than anything else. I am interested to here your opinion on using something like a Bow
for the return value of encode
tho. It would allow impl's to avoid the clone without having to reimplement write_sockaddr
and with_sockaddr
again :)
src/backend/linux_raw/net/mod.rs
Outdated
@@ -6,4 +6,3 @@ pub(crate) mod read_sockaddr; | |||
pub(crate) mod send_recv; | |||
pub(crate) mod sockopt; | |||
pub(crate) mod syscalls; | |||
pub(crate) mod write_sockaddr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the only reason for this removal was because write_sockaddr*
were no longer needed we should remove those functions instead. We can then keep this mod and use the encode_sockaddr*
functions in src/net/socket_address.rs
to define one common impl of SocketAddress
for SockAddrV4
and SockAddrV6
(instead of defining separate impls in src/backend/*/net/addr.rs
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call, replaced the duplicated implementations with ones shared between linux_raw and libc that call into the existing write_sockaddr
functions.
src/net/socket_address.rs
Outdated
/// address. | ||
unsafe fn write_sockaddr(&self, storage: *mut SocketAddrStorage) -> usize { | ||
let encoded = self.encode(); | ||
core::ptr::write(storage.cast(), encoded); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Future Rust moves to enabling the unsafe_fn_in_fn
lint by default, so we should probably mark this operation explicitly as unsafe, even though the compiler does not require it for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this method entirely, but fixed an unsafe_fn_in_fn
elsewhere.
type CSockAddr; | ||
|
||
/// Convert to the C type. | ||
fn encode(&self) -> Self::CSockAddr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it a bit annoying/confusing that this trait uses default fns. We could instead utilize a small custom enum that stores either a reference to or an owned CSockAddr
. Kinda like a Cow
but for nostd, something along the lines of
/// Borrowed Or oWned
pub enum Bow<'a, B> {
/// Stores a reference to the value.
Borrowed(&'a B),
/// Owns the value.
Owned(B),
}
impl<'a, B> Bow<'a, B> {
/// Get a reference to the value stored in this Bow.
pub fn as_ref(&self) -> &B {
match self {
Bow::Borrowed(val) => val,
Bow::Owned(val) => &val,
}
}
}
Then only encode
would be enough and the rest could be defined as helper functions or similar.
Let me know what you think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the write_sockaddr
method, which had a default implementation that was not overridden for any of the types. It was only used in one place, for SocketAddrAny::write
.
The purpose of the with_sockaddr
method is to avoid the copying that you mention, and is overridden in the implementation for SocketAddrUnix
which contains a sockaddr_un
.
Before simplifying this trait further, I'd like to hear what @sunfishcode thinks about the CSockAddr
associated type. If it's undesirable to expose the underlying C types, I think the way to go would be to remove CSockAddr
and encode
, leaving only with_sockaddr
. This is a messier interface than simply returning a value, but a private helper function could be used to implement this in terms of the encode_sockaddr_*
functions for the majority of the address types.
4c1c746
to
013792a
Compare
#918
This adds a trait
SocketAddress
, and implements it forSocketAddrV4
,SocketAddrV6
,SocketAddrUnix
,SocketAddrXdp
,SocketAddr
,SocketAddrAny
, and the newly-addedSocketAddrNetlink
. The syscalls that take asockaddr
are reimplemented in terms of this trait, removing hundreds of lines of duplicated code. It also simplifies the public interface, allowingrustix::net::{bind, connect, sendto, sendmsg_addr}
to take an<A: SocketAddress>(addr: &A)
, rather than needing separate functionsbind_v4
,bind_v6
,bind_unix
,bind_xdp
,bind_any
etc. All of those are left as wrappers around the generic version, but could be deprecated and removed.Open questions
SocketAddress
for the trait. The rest of the code consistently usesSocketAddrX
rather thanAddress
, butSocketAddr
is already taken by the std enum of V4 and V6. Could maybe just berustix::net::Addr
?SocketAddrAny
: So far this only changes the syscalls that take a socket address as an in-pointer. Those that return an address via an out-pointer currently returnSocketAddrAny
. If it is desirable to allow types outside of rustix to implement theSocketAddress
trait, it would be ideal forSocketAddrAny
to wrapsockaddr_storage
instead of being a Rust enum. This would allowSocketAddrAny
be fully generic and allow these syscalls to be used with any address type not known to rustix. Address types could use.into()
/.try_into()
to convert to / fromSocketAddrAny
. This would require a breaking change.*mut c_void
andusize
instead of the underlyingsockaddr_*
types.