-
Notifications
You must be signed in to change notification settings - Fork 171
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 :)
4c1c746
to
013792a
Compare
44051ea
to
9b432db
Compare
Given that I also rebased to clean up the messy history for easier review. Full history archived here. |
Hi, any updates on this PR? Looking forward |
This would be very nice to have in 1.0 |
Thanks for the pings here, and your patience! I'll make an effort to review this before 1.0. |
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.
This looks really nice!
I was initially worried about the safety of the f
called by the with_sockaddr
function, however I now see that the safety requirements are covered by the pub unsafe trait
, so it looks good.
And I was initially worried about generic functions in src/backend, to keep code duplication down, however they're in fact replacing manually duplicated code, so that doesn't seem to be a problem.
I agree that it'd be good to continue to evolve this as you discussed. #1171 adds a RawSocketAddr
type which is very close to what you describe for return values, and sounds like a good general direction.
I have have a few relatively minor comments, plus this needs a rebase. If you'd like help with the rebase please let me know.
src/net/socket_address.rs
Outdated
|
||
/// A trait abstracting over the types that can be passed as a `sockaddr`. | ||
/// | ||
/// Safety: implementers of this trait must ensure that `with_sockaddr` calls |
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.
As a minor style nit, this should be a # Safety
section header.
src/net/socket_address.rs
Outdated
#[repr(C)] | ||
pub struct SockAddrRaw { | ||
_data: [u8; 0], | ||
} |
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.
What would you think of renaming SockAddrRaw
to SocketAddrOpaque
?
There are two changes there:
Sock
->Socket
, because if we have some things prefixedSocket
and some prefixedSock
I expect I'll frequently forget which things use which prefixes.Raw ->
Opaque`, which I think better captures that this struct type doesn't describe the data layout.
src/net/mod.rs
Outdated
@@ -10,6 +10,7 @@ | |||
mod send_recv; | |||
mod socket; | |||
mod socket_addr_any; | |||
mod socket_address; |
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.
Could we make this a pub mod
, and perhaps name it addr
, to keep SockAddr
and SockAddrRaw
from cluttering the rest of the net
API? It seems like most users won't ever need to use those directly.
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.
Is it confusing that rustix::net::addr
then only contains those two, and not any of the other address types (SocketAddrUnix
, SocketAddrV4
, SocketAddrV6
, SocketAddr
, SocketAddrAny
, SocketAddrStorage
) that are one level up? Seems like at least SocketAddrStorage
and SocketAddrOpaque
should be next to each other since they represent similar C types.
Maybe move them all into rustix::net::addr
?
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 idea to keep SocketAddrStorage
next to SocketAddrOpaque
.
It seems nice to keep SocketAddrV4
, SocketAddrV6
, SocketAddrUnix
, in rustix::net
where all the rest of the API that uses them are though, because that's the "regular" API.
What if we put SocketAddrOpaque
, SocketAddrArg
, and SocketAddrStorage
into a pub mod
named rustix::net::abstract
?
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.
Sounds good. abstract
might be confused with the abstract namespace for unix sockets, but I don't have a better name to propose.
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.
Also, abstract
is a Rust reserved keyword.
src/net/socket_address.rs
Outdated
/// `f` with a pointer that is readable for the passed length, and points | ||
/// to data that is a valid socket address for the system calls that accept | ||
/// `sockaddr` as a const pointer. | ||
pub unsafe trait 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.
And, what would you think of renaming this to SocketAddrArg
? Sock
-> Socket
for the same reason as above, and Arg
since it is for describing arguments; return values will need to use some other mechanism as you noted in the top comment.
f5ecd5e
to
7214eea
Compare
Rebased and made the requested changes. I named the new module I'll take a look at updating Since it looks like you're merging breaking changes to main for 1.0, should I go ahead and remove the address type specific functions?
|
I haven't thought of a better name yet, but I'll keep thinking 😄 .
👍
Yes, that'd be great! |
7214eea
to
1b82995
Compare
To support extensibility over address types, use sockaddr_storage and Into / TryInto conversions.
Removes: * `bind_any`, `bind_unix`, `bind_v4`, `bind_v6`, `bind_xdp` in favor of `bind`, * `connect_any`, `connect_unix`, `connect_v4`, `connect_v6` in favor of `connect` (leaving address-less `connect_unspec`) * `sendmsg_v4`, `sendmsg_v6`, `sendmsg_unix`, `sendmsg_xdp`, `sendmsg_any` in favor of `sendmsg_addr` (leaving address-less `sendmsg`) * `sendto_any`, `sendto_v4`, `sendto_v6`, `sendto_unix`, `sendto_xdp` in favor of `sendto`
1b82995
to
fbf85a8
Compare
fbf85a8
to
c237ec1
Compare
Removed the address-type-specific variants of |
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.
Cool, overall this looks great!
/// | ||
/// `storage` must point to valid memory for decoding a socket | ||
/// address. `len` bytes must be initialized. | ||
pub unsafe fn read(src: *const SocketAddrOpaque, len: usize) -> Self { |
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.
This is pre-existing, but: it appears every user of this has a socklen_t
rather than a usize
, what would you think about making this argument take a socklen_t
, to save all users from having to .try_into().unwrap()
?
/// | ||
/// `storage` must point to valid memory for decoding a socket | ||
/// address. `len` bytes must be initialized. | ||
pub unsafe fn read_option(src: *const SocketAddrOpaque, len: usize) -> Option<Self> { |
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.
And same here with making len
be a socklen_t
.
SocketAddrAny::V6(v6) => Ok(v6), | ||
_ => unreachable!(), | ||
} | ||
let any = unsafe { SocketAddrAny::read(value.as_ptr().cast(), optlen as usize) }; |
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.
In this code and code like it, instead of doing SocketAddrAny::read
, which does a copy_nonoverlapping
, would it work to change value
above from MaybeUninit<SocketAddrStorage>
to MaybeUninit<SocketAddrAny>
, and have the OS write the data directly into the SocketAddrAny
?
/// `storage` must point to valid memory for decoding a socket | ||
/// address. `len` bytes must be initialized. | ||
pub unsafe fn read(src: *const SocketAddrOpaque, len: usize) -> Self { | ||
assert!(len >= core::mem::size_of::<backend::c::sa_family_t>()); |
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.
This is also pre-existing, but: on BSD-family platforms, the sa_family
field is preceded by a length field, so the invariant ought to be len >= offset_of!(sockaddr, sa_family) + size_of::<sa_family_tt>()
@@ -0,0 +1,83 @@ | |||
//! Socket address types. |
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 we're not able to think of a better name for this module, it'd be good to have a module-level comment describing what's in this module.
} | ||
if addr.len() < size_of::<c::sockaddr_in>() { | ||
return Err(Errno::INVAL); | ||
} |
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.
SocketAddAny::read
panics if the len
is out of range; would it make sense to do that here too? The length should ever be wrong unless there's a bug somewhere.
And similar for the other read_sockaddr_*
functions.
return Err(Errno::INVAL); | ||
} | ||
if len == offsetof_sun_path { | ||
SocketAddrUnix::new(&[][..]) |
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'm looking to figure out all the ways that TryFrom<SocketAddrAny>
can fail, and SocketAddrUnix::new
here can fail with NAMETOOLONG
. However the only time it fails is if the path is longer than sun_path.len()
, which we've already checked here, so I think that can't happen. Would it make sense to make this do Ok(SocketAddrUnix::new(&[][...]).unwrap())
, or add a new infallible SocketAddrUnix
constructor?
And similar for the SocketAddrUnix::new_abstract_name
and SocketAddrUnix::new
calls below?
/// | ||
/// Socket addresses can be converted to `SocketAddrAny` via the [`From`] and | ||
/// [`Into`] traits. `SocketAddrAny` can be converted back to a specific socket | ||
/// address type with [`TryFrom`] and [`TryInto`]. |
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.
Could you document here that the TryFrom
functions fail with Errno::AFNOSUPPORT
if the address family is not the requested one?
An unfortunate thing about using Errno
is that it doesn't describe which errors could actually occur. However, it might be worth it here, because a convenient thing about using Errno
is that these functions will usually be called from within functions that return Result<..., Errno>
, so users can just do ?
on the result and it'll do something reasonable.
Looking at the code, it looks like these TryFrom<SocketAddrAny>
functions can fail with:
AFNOSUPPORT
if the address family isn't the requested oneINVAL
if the length is wrong, though perhaps those should be panics?NAMETOOLONG
if it's Unix-domain and the name doesn't fit insun_path
, but that shouldn't be possible
#918
This adds a trait
SockAddr
, 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(addr: &impl SockAddr)
, rather than needing separate functionsbind_v4
,bind_v6
,bind_unix
,bind_xdp
,bind_any
etc.The
SocketAddrAny
enum is replaced by a struct wrappingSocketAddrStorage
. This allowsSocketAddrAny
be fully generic and allows syscalls returning addresses to be used with any address type not known to rustix. Address types use.into()
/.try_into()
to convert to / fromSocketAddrAny
.