Rust has a very small standard library documented at
Crate std
.
The std.net
library has types for IP addresses and
for some TCP and UDP calls. There are no DNS functions, unlike the
other languages considered here.
The small standard library appears to be a deliberate choice by Rust
designers. There is a huge library of user-contributed 'crates' at
The Rust community’s crate registry
and if you search for DNS there it will return over 200 matches.
The philosophy appears to be that crates.io
is
the defacto standard library and that crates should be pulled from there
rather than cluttering up the "real" standard library.
The philosophy is contentious: see Expansion of standard library for lengthy discussions either way. I'm in favour of a large standard library, with consistency of approach, guarantee of non-breaking updates, security, etc, but that isn't going to happen with Rust. So I'll pick what seem to be the most popular crates here and in the sequel. Of course, that may change over time, or even next month.
Rust has the types IpAddr
, Ipv4Addr
and Ipv6Addr
.
The type Ipv4Addr
has a new
constructor that takes
four 8 bit octets (of type u8
) as in
let addr = Ipv4Addr::new(127, 0, 0, 1);
The type Ipv4Addr
also implements fromStr
.
There may be parsing errors in this, so a Result
type is
created
as in
let addr: Result<Ipv4Addr, AddrParseError> = "103.79.105.27".parse();
match addr {
Ok(a) => println!("address {}", a),
Err(e) => println!("error {}", e)
}
Functions include is_loopback()
,
is_private()
as in
use std::net::Ipv4Addr;
assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_loopback(), true);
assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_loopback(), false);
assert_eq!(Ipv4Addr::new(10, 0, 0, 1).is_private(), true);
assert_eq!(Ipv4Addr::new(10, 10, 10, 10).is_private(), true);
assert_eq!(Ipv4Addr::new(172, 16, 10, 10).is_private(), true);
assert_eq!(Ipv4Addr::new(172, 29, 45, 14).is_private(), true);
assert_eq!(Ipv4Addr::new(172, 32, 0, 2).is_private(), false);
assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true);
assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false);
The type Ipv6Addr
has a new
constructor that takes eight 16-bit segments as in
use std::net::Ipv6Addr;
let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
let newmarch = Ipv6Addr::new(0x2400, 0x3740, 0x200, 0xd900, 0, 0, 0, 0x260);
The type Ipv6Addr
also implements fromStr
.
There may be parsing errors in this, so a Result
type is
created
as in
let addr: Result<Ipv6Addr, AddrParseError> = "2400:3740:200:d900::260".parse();
match addr {
Ok(a) => println!("address {}", a),
Err(e) => println!("error {}", e)
}
Functions include is_global()
, is_unicast_link_local_strict()
.
There is no support for DNS in the standard library. The most popular crate for DNS (as at May, 2020) seems to be trust-dns-resolver .
Creating and managing projects using crates is more complex than simply interpreting or compiling a source file. First you need to create a project
cargo new Resolv
which creates the following directory structure
Resolv
Resolv/.git
Resolv/.git/...
Resolv/.gitignore
Resolv/src
Resolv/src/main.rs
Resolv/Cargo.toml
To install the trust-dns-resolver
crate, keep it ip to date, build it,etc
the following line needs to be added to Resolv/Cargo.toml
in the
Dependencies
section:
trust-dns-resolver = "0.19.5"
Then in the Resolv
direstory, after every change to main.rs
, run
cargo build
I copied the example code into src/main.js
and built it - with two main results:
cargo
point this out when I created the project?
The example program will return a single IP address, and IPv4 address if it exists, otherwise an IPv6 address. Adapted to get a host name from standard input, it is IP.rs illustrates these:
use trust_dns_resolver::Resolver;
use trust_dns_resolver::config::*;
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage {} hostname", args[0]);
std::process::exit(1);
}
let hostname = &args[1];
// Construct a new Resolver with default configuration options
// Default tries IPv4 and then IPv6 (if IPv4 fails)
let opts = ResolverOpts::default();
match opts.ip_strategy {
LookupIpStrategy::Ipv4Only => println!("IPv4 only"),
LookupIpStrategy::Ipv6Only => println!("IPv6 only"),
LookupIpStrategy::Ipv4AndIpv6 => println!("IPv4 and v6"),
LookupIpStrategy::Ipv6thenIpv4 => println!("IPv6 then 4"),
LookupIpStrategy::Ipv4thenIpv6 => println!("IPv4 then 6")
}
let resolver = Resolver::new(ResolverConfig::default(), opts).unwrap();
let response = resolver.lookup_ip(hostname);
match response {
Ok(resp) =>
{
// only one value to return
for a in resp.iter() {
println!("addr {}", a);
}
},
Err(err) =>
{
println!("err {}", err);
std::process::exit(2)
}
}
std::process::exit(0);
}
The changes to get both IPv4 and IPv6 addresses are basically just to the options: IPs.rs illustrates these:
use trust_dns_resolver::Resolver;
use trust_dns_resolver::config::*;
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage {} hostname", args[0]);
std::process::exit(1);
}
let hostname = &args[1];
// Construct a new Resolver with default configuration options
// Default gets IPv4 and then IPv6
let mut opts = ResolverOpts::default();
match opts.ip_strategy {
LookupIpStrategy::Ipv4Only => println!("IPv4 only"),
LookupIpStrategy::Ipv6Only => println!("IPv6 only"),
LookupIpStrategy::Ipv4AndIpv6 => println!("IPv4 and v6"),
LookupIpStrategy::Ipv6thenIpv4 => println!("IPv6 then 4"),
LookupIpStrategy::Ipv4thenIpv6 => println!("IPv4 then 6")
}
// then change to find both
opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
let resolver = Resolver::new(ResolverConfig::default(), opts).unwrap();
let response = resolver.lookup_ip(hostname);
match response {
Ok(resp) =>
{
for a in resp.iter() {
println!("addr {}", a);
}
},
Err(err) =>
{
println!("err {}", err);
std::process::exit(2)
}
}
std::process::exit(0);
}
Again, there isn't anything in the standard library.
The most popular crate that runs under Posix and Windows seems to be
get_if_addrs
.
The dependency entry in Cargo.toml
is
get_if_addrs = "0.5.3"
The program to list all IP addresses for each NIC is simplicity itself: iface.rs illustrates these:
use get_if_addrs;
fn main() {
for iface in get_if_addrs::get_if_addrs().unwrap() {
println!("{:#?}", iface);
}
}
Partial output is
Interface {
name: "wlp2s0",
addr: V4(
Ifv4Addr {
ip: 192.168.2.190,
netmask: 255.255.255.0,
broadcast: Some(
192.168.2.255,
),
},
),
}
Interface {
name: "lo",
addr: V6(
Ifv6Addr {
ip: ::1,
netmask: ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,
broadcast: None,
},
),
}
Interface {
name: "wlp2s0",
addr: V6(
Ifv6Addr {
ip: fd5a:1870:766c::2aa,
netmask: ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,
broadcast: None,
},
),
}
Copyright © Jan Newmarch, jan@newmarch.name
" Network Programming using Java, Go, Python, Rust, JavaScript and Julia"
by
Jan Newmarch
is licensed under a
Creative Commons Attribution-ShareAlike 4.0 International License
.
Based on a work at
https://jan.newmarch.name/NetworkProgramming/
.