Live data from Hacker News

Rootless Pings in Rust

bou.ke

61–70 of 87 posts

Re: Rootless Pings in Rust

#61
post #59

Earlier quoted context omitted.

> The issue is that the rust library apparently conflates datagram and UDP, when they're not the same thing. It comes down to these two lines (using full items paths for clarity): let socket = socket2::Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::ICMPV4))?; let socket: std::net::UdpSocket = socket.into(); The latter is using this impl: https://docs.rs/socket2/0.6.1/socket2/struct.Socket.html#imp... Basically…

It may be memory safe but it's not using the type system to represent the domain very well. One could imagine a more type-friendly design in which we could write that first line as follows: let socket: Socket = Socket::new()?; Now, the specifics of socket types will be statically checked. Edit: I realized that the issue here is actually the conversion, and that UdpSocket on its own is actually a type-safe representat…

One could, but one probably doesn't want to have separate types for TCP-over-IPv4 vs TCP-over-IPv6 for example, even if they accept/produce different forms of addresses. That'd force a lot of code bloat with monomorphization.

So now one is making one's own enumeration which is different than the OS one and mapping between them, which can get into a mess of all the various protocols Linux and other OSs support, and I'm not sure it's solving a major problem. Opinions vary, but I prefer to use complex types sparingly.

I think there are likely a bunch of other cases where it's useful to choose these values more dynamically too. Networking gets weird!

Re: Rootless Pings in Rust

#62
post #48
post #25

> It turns out you can create a UDP socket with a protocol flag, which allows you to send the ping rootless This is wrong, despite the Rust library in question's naming convention. You're not creating a UDP socket. You're creating an IP (AF_INET), datagram socket (SOCK_DGRAM), using protocol ICMP (IPPROTO_ICMP). The issue is that the rust library apparently conflates datagram and UDP, when they're not the same thing.…

Could you please explain me the difference? As UDP is the "User Datagram Protocol" when I read about datagrams I always think about UDP and though it was just a different way of saying the same thing. Maybe "datagram" is supposed to be the packet itself, but you're still sending it via UDP, right?

[deleted]

Re: Rootless Pings in Rust

#63
post #46
post #25

> It turns out you can create a UDP socket with a protocol flag, which allows you to send the ping rootless This is wrong, despite the Rust library in question's naming convention. You're not creating a UDP socket. You're creating an IP (AF_INET), datagram socket (SOCK_DGRAM), using protocol ICMP (IPPROTO_ICMP). The issue is that the rust library apparently conflates datagram and UDP, when they're not the same thing.…

Thank you. I assumed this was what was happening, but conflating network layer protocols with transport layer ones isn't great. I'm surprised that pedantic zealots, like me in my youth, haven't risen up and flooded rust with issues over it way before this though.

Why are you saying rust needs to be flooded with issues? Rust isn't conflating transport layers and protocols. OP is.

UdpSocket is just able to take ANY file descriptor and try to use it as if it's a UDP socket.

E.g. this compiles, and it's not a bug. It doesn't make any sense, but it's not a bug:

    fn main() {
        let f = std::fs::File::open("/dev/null").unwrap();
        let f: std::os::fd::OwnedFd = f.into();
        let socket: std::net::UdpSocket = f.into();
    }

OP is clearly confused, since there's no need to do this at all. socket2::Socket already has a `send_to()`: https://docs.rs/socket2/latest/socket2/struct.Socket.html#me...

I think OP either banged on this until it compiled, maybe blindly copying from other examples, or it's vibe coded, and shows why AI needs supervision from someone who can actually understand what the code does.

Re: Rootless Pings in Rust

#64
post #48
post #25

> It turns out you can create a UDP socket with a protocol flag, which allows you to send the ping rootless This is wrong, despite the Rust library in question's naming convention. You're not creating a UDP socket. You're creating an IP (AF_INET), datagram socket (SOCK_DGRAM), using protocol ICMP (IPPROTO_ICMP). The issue is that the rust library apparently conflates datagram and UDP, when they're not the same thing.…

Could you please explain me the difference? As UDP is the "User Datagram Protocol" when I read about datagrams I always think about UDP and though it was just a different way of saying the same thing. Maybe "datagram" is supposed to be the packet itself, but you're still sending it via UDP, right?

There's actually a lot of combinations of (domain, type, protocol) that are available. It is not always the case that the protocol implies the type.

In IP land (domains AF_INET and AF_INET6), we have the well known UDP and TCP protocols, of course. UDP is always datagram (SOCK_DGRAM) and TCP is always stream (SOCK_STREAM). Besides datagram-only ICMP, there's also SCTP, which lets you choose between stream and sequential-packet (SOCK_SEQPACKET) types. A sequential-packet socket provides in-order delivery of packet-sized messages, so it sits somewhere between datagram and stream in terms of features.

In AF_UNIX land, there are no protocols (the protocol field is always 0), but all 3 of the aforementioned types are available. You just have to pick the same type on both sides.

Footnotes: SCTP is not widely usable because Windows doesn't natively support it and many routers will drop or reject it instead of forwarding it. Also, AF_UNIX is now supported on Windows, but only with SOCK_STREAM type.

Re: Rootless Pings in Rust

#65
post #59

Earlier quoted context omitted.

It may be memory safe but it's not using the type system to represent the domain very well. One could imagine a more type-friendly design in which we could write that first line as follows: let socket: Socket = Socket::new()?; Now, the specifics of socket types will be statically checked. Edit: I realized that the issue here is actually the conversion, and that UdpSocket on its own is actually a type-safe representat…

One could, but one probably doesn't want to have separate types for TCP-over-IPv4 vs TCP-over-IPv6 for example, even if they accept/produce different forms of addresses. That'd force a lot of code bloat with monomorphization. So now one is making one's own enumeration which is different than the OS one and mapping between them, which can get into a mess of all the various protocols Linux and other OSs support, and I'…

It's precisely because networking gets weird that a good representation at the type level could be useful. But I agree that it'd need to be done carefully to avoid creating usability issues.

Re: Rootless Pings in Rust

#67
post #59

Earlier quoted context omitted.

> The issue is that the rust library apparently conflates datagram and UDP, when they're not the same thing. It comes down to these two lines (using full items paths for clarity): let socket = socket2::Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::ICMPV4))?; let socket: std::net::UdpSocket = socket.into(); The latter is using this impl: https://docs.rs/socket2/0.6.1/socket2/struct.Socket.html#imp... Basically…

It may be memory safe but it's not using the type system to represent the domain very well. One could imagine a more type-friendly design in which we could write that first line as follows: let socket: Socket = Socket::new()?; Now, the specifics of socket types will be statically checked. Edit: I realized that the issue here is actually the conversion, and that UdpSocket on its own is actually a type-safe representat…

> dubiously-safe

No, it's perfectly safe. Except if you expand the scope of "safe" by a lot.

OP turned the socket into an (almost) raw file descriptor, and created an UDP socket from it. Weird, yes, but since it's perfectly memory safe and invalid operations would correctly error, it's not "dubiously-safe". It's safe.

I mean, either your language has the ability to do raw (technically Owned in this case) file descriptors, or it doesn't.

Maybe you'd prefer Rust had a third mode? Safe, `unsafe {}`, and `are_you_sure_you_understand_this {}`, the last one also being 'safe', but just… odd.

Re: Rootless Pings in Rust

#68
post #67
post #59

Earlier quoted context omitted.

It may be memory safe but it's not using the type system to represent the domain very well. One could imagine a more type-friendly design in which we could write that first line as follows: let socket: Socket = Socket::new()?; Now, the specifics of socket types will be statically checked. Edit: I realized that the issue here is actually the conversion, and that UdpSocket on its own is actually a type-safe representat…

> dubiously-safe No, it's perfectly safe. Except if you expand the scope of "safe" by a lot. OP turned the socket into an (almost) raw file descriptor, and created an UDP socket from it. Weird, yes, but since it's perfectly memory safe and invalid operations would correctly error, it's not "dubiously-safe". It's safe. I mean, either your language has the ability to do raw (technically Owned in this case) file descrip…

It's dubiously safe because it allows invalid combinations, i.e. calling UDP-related methods on non-UDP sockets. I'm using "safe" in the general English sense here, "protected from or not exposed to danger or risk."

> invalid operations would correctly error

At runtime, yes. I'm pointing out that Rust makes it possible to do better, and catch such issues at compile time.

Re: Rootless Pings in Rust

#69
post #27

Earlier quoted context omitted.

ICMP is just different protocol from UDP. There's field "Protocol" in IP packet. 0x01 = ICMP, 0x06 = TCP, 0x11 = UDP. I think that this article gets terminology wrong. It's not UDP socket that gets created here, but Datagram socket. Seems to be bad API naming in Rust library.

> It's not UDP socket that gets created here, but Datagram socket A datagram socket is a UDP socket, though. That's what the D stands for.

In related news, all rectangles are squares and all animals are dogs.

Re: Rootless Pings in Rust

#70
post #68
post #67

Earlier quoted context omitted.

> dubiously-safe No, it's perfectly safe. Except if you expand the scope of "safe" by a lot. OP turned the socket into an (almost) raw file descriptor, and created an UDP socket from it. Weird, yes, but since it's perfectly memory safe and invalid operations would correctly error, it's not "dubiously-safe". It's safe. I mean, either your language has the ability to do raw (technically Owned in this case) file descrip…

It's dubiously safe because it allows invalid combinations, i.e. calling UDP-related methods on non-UDP sockets. I'm using "safe" in the general English sense here, "protected from or not exposed to danger or risk." > invalid operations would correctly error At runtime, yes. I'm pointing out that Rust makes it possible to do better, and catch such issues at compile time.

Of course it allows invalid combinations.

This also compiles:

    let f = std::fs::File::open("/dev/null").unwrap();
    let f: std::os::fd::OwnedFd = f.into();
    let socket: std::net::UdpSocket = f.into();
If you convert a high level object into a low level one, and then back up as another type, then what exactly do you expect the language to do about that?

> "protected from or not exposed to danger or risk."

A computer will do what you tell it to do, not what you intend it to do. Opening a file is way more dangerous than risking errors because "this syscall doesn't work on that fd".

There's also always risk that a syscall will fail at runtime, whether the type of fd is correct or not.

It sounds like you would prefer if UdpSocket From should run getsockname() or something to confirm it's of the expected type, but I would prefer not. Indeed, in the general case some perfectly coded `unsafe` code could `dup2()` over the fd, so any checking at UdpSocket creation time is moot; you still don't get the safety you are asking for.

Post reply on HN