bytecodealliance / rustix

Safe Rust bindings to POSIX-ish APIs
Other
1.47k stars 153 forks source link

Support abritrary socket adresses #918

Open niklaswimmer opened 11 months ago

niklaswimmer commented 11 months ago

Rustix can currently not be used to bind sockets which are not unix or ip. The only real requirement on a socket address is that it starts with an u16 (the address family) - judging from the types used in the definition of the bind syscall.

My specific use case is binding bluetooth sockets and my current workaround is to just call libc::bind instead (using the fd returned by rustix's socket_with).

I am unsure what a good API would look like, but ideally it would not require me to construct a [u8] array myself and instead work with structs which are then automatically converted into byte arrays when it comes to invoking the syscall. For my use case I wrote a helper module that uses unions to convert some struct into a libc::sockaddr if you want to take a look.

This issue also blocks usage of eyra for any programs that need to work with some non-well-known sockets.

I would be happy to help implementing this API, but as noted above I am not sure about the design and would require some input.

sunfishcode commented 11 months ago

Interesting question.

I think my preference here is to add a Bluetooth arm to SocketAddrAny for bluetooth, containing a SocketAddrBluetooth, similar to the Unix arm, which encapsulates a bluetooth address, and then teach the encode/decode logic to handle it.

That said, at a quick search, I found about 40 sockaddr_* types in Linux's headers. I don't have any idea how commonly used they all are. If rustix is going to end up supporting a lot of them, it may make sense to switch over to just exposing arbitrary bytes and letting users or other crates sort it out.

However, my guess is, for now, it makes sense to stick with typed interfaces and adding address families such as SocketAddrBluetooth when people have a need for them, and we'll see how far we get.

Does that make sense?

nbdd0121 commented 10 months ago

I tried to add SocketAddrNetlink to rustix but it involves an incredible amount of code duplication. Currently there's a copy of connect, bind, sendmsg, sendto for every single type of sockaddr and I don't think that'll be sustainable if more types of addresses are to be added.

sunfishcode commented 10 months ago

The purpose of having connect_v4/connect_v6/connect_unix instead of just connect/connect_any is just to avoid the construction of a SocketAddr/SocketAddrAny in common cases. Compared to V4/V6/Unix, I assume all other socket address types are far less commonly used, so it seems ok to just add entries to SocketAddrAny and use connect_any, and not add new specialized connect_* etc. routines. Does that sound feasible?

nbdd0121 commented 10 months ago

For clarification, I am talking about the syscall side. connect_v4/connect_v6/connect_unix all just encode the sockaddr and perform a syscall.

nbdd0121 commented 10 months ago

Would it make sense to have a SocketAddr trait, that just does encode and decode to sockaddr_*?

trait SocketAddr {
    fn encode(&self) -> SocketAddrStorage;
    fn decode(storage: &SocketAddrStorage) -> Result<Self>;
}

I am not sure why we currently have two methods of encoding/decoding, and duplicate for both backends for each sockaddr (4 redundant copies!).