Articles tagged #rust
Consuming Ethernet frames with the nom crate
Now that we’ve found the best way to find the “default network interface”… what can we do with that interface?
Well, listen for network traffic of course!
use rawsock::open_best_library;
use std::time::Instant;
fn main() -> Result<(), Error> {
let lib = open_best_library()?;
let iface_name = format!(r#"\Device\NPF_{}"#, netinfo::default_nic_guid()?);
let iface = lib.open_interface(&iface_name)?;
println!("Listening for packets...");
// doing some low-cost logging over here
let start = Instant::now();
iface.loop_infinite_dyn(&mut |packet| {
println!(
"{:?} | received {} bytes",
start.elapsed(),
packet.len()
);
})?;
Ok(())
}
Binding C APIs with variable-length structs and UTF-16
Okay, I lied.
I’m deciding - right this instant - that using wmic is cheating too. Oh, it was fair game when we were learning about Windows, but we’re past that now.
We know there’s IPv4 routing tables, and we know network interfaces have indices (yes, they do change when you disable/enable one, so ill-timed configuration changes may make our program blow up).
Finding the default network interface through WMI
Let’s set aside our sup
project for a while.
Don’t get me wrong - it’s a perfectly fine project, and, were we simply rewriting “ping” for Windows in Rust, we could (almost) stop there.
We’re currently using the operating system’s facility to speak ICMP, which is great for a bunch of reasons: we can be sure that whatever flaws there are in the implementation, all “native” Windows programs suffer from it as well.
The builder pattern, and a macro that keeps FFI code DRY
Our ping API is simple, but it’s also very limited:
pub fn ping(dest: ipv4::Addr) -> Result<(), String>
// called as:
ping(ipv4::Addr([8, 8, 8, 8])).unwrap();
It doesn’t allow specifying the TTL (time to live) of packets, it doesn’t allow specifying the timeout, it doesn’t let one specify the data to send along, and it doesn’t give us any kind of information on the reply.
A simple ping library, parsing strings into IPv4 address
We’ve just spent a lot of time abstracting over LoadLibrary, but we still have all the gory details of the Win32 ICMP API straight in our main.rs file! That won’t do.
This time will be much quicker, since we already learned about carefully designing an API, hiding the low-level bits and so on.
Let’s add an icmp
module to our program. Actually, we’ve been dealing with
an IPAddr
all this time, it also sounds like it could use its own package:
Designing and implementing a safer API on top of LoadLibrary
It’s refactor time!
Our complete program is now about a hundred lines, counting blank lines (see the end of part 3 for a complete listing).
While this is pretty good for a zero-dependency project (save for
pretty-hex
), we can do better.
First off, concerns are mixed up. In the same file, we:
- Expose
LoadLibraryA
/GetProcAddress
- Expose the Win32 ICMP API
FFI-safe types in Rust, newtypes and MaybeUninit
Windows dynamic libraries, calling conventions, and transmute
So, how does ping.exe
actually send a ping? It seems unrealistic that
ping.exe
itself implements all the protocols involved in sending a ping.
So it must be calling some sort of library. Also, since it ends up
talking to the outside world via a NIC (network interface controller),
the kernel is probably involved at some point.
In reading files the hard way - part 2, we learned about dynamic libraries (like libc), and the Linux kernel, and how syscalls allowed us to ask the Linux kernel to do our bidding. For this series, we’re going to have to look at the Windows equivalents.
Go back to the homepage.