# Dog, a command-line DNS client

**URL:** <https://users.rust-lang.org/t/dog-a-command-line-dns-client/51178>\
**Category:** announcements\
**Created:** [November 7, 2020, 3:46pm UTC](https://users.rust-lang.org/t/dog-a-command-line-dns-client/51178 "2020-11-07T15:46:34Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![ogham](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/ogham/32/82_2.png) [@ogham](https://users.rust-lang.org/u/ogham)\
**Post date:** [November 7, 2020, 3:46pm UTC](https://users.rust-lang.org/t/dog-a-command-line-dns-client/51178/1 "2020-11-07T15:46:34Z")

</div>

Hi all,

I'm pleased to announce that [dog, my command-line DNS client](https://dns.lookup.dog/), is good enough to have reached version 0.1.0. It's like `dig`, but a bit different, so, `dog`. The [source code](https://github.com/ogham/dog) is on GitHub (EUPL-1.2 licence).

 ![Screenshot 2020-11-07 at 15.18.40](https://us1.discourse-cdn.com/flex019/uploads/rust_lang/original/3X/8/2/82a53a7254f37e4080fa8962e114813cc5b34c61.png)

Some development notes and shout-outs to the projects and libraries that helped it get made:

- dog uses [its own DNS packet parser](https://github.com/ogham/dog/tree/master/dns/src). The [byteorder](https://docs.rs/byteorder/1.3.4/byteorder/) crate made it _really_ easy to write code to read values from the packet and parse them into a structure.

- Of course, "parse bytes into a structure" is a problem aboslutely _ripe_ for fuzzing, so I threw [cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz) at the parser and it found problems pretty much immediately. There were some overflow panics and some index-out-of-range panics, both of which it was able to reduce down to relatively-simple test cases. Eventually I got it to fuzz for an hour without crashing, and now I'm a lot more confident about the code than I was when I started.

- I'd also like to praise [cargo-mutagen](https://github.com/llogiq/mutagen/), which proved _very_ helpful in detecting untested code. Usually, I'd use a code coverage tool to see if I had any code that wasn't covered by tests, and mutagen worked like a more fine-grained coverage tool. I felt like DNS parsing was a problem with a small enough "code footprint" that I could aim for a high coverage percentage, and mutagen helped me get there.

- Something I found out very quickly writing packet-parsing code is that if you're casting numbers from one type to another, there's a high chance you're writing code that will overflow when given certain invalid input. To combat this, I used [Clippy's](https://rust-lang.github.io/rust-clippy/master/) cast lints (`cast_possible_truncation`, `cast_lossless`, `cast_possible_wrap`, and `cast_sign_loss`) to effectively disable that part of Rust! It was good to not be able to accidentally write code that used `as` anymore.

- It does DNS-over-TLS and HTTPS with the [native-tls](https://crates.io/crates/native-tls) crate, which was great, because I could use TLS without having to worry about whether I was using openssl or a system-specific library. For most of dog's development, I used hyper and hyper-tls for sending and receiving HTTPS responses, but near the end I swapped it out for an implementation that uses [httparse](https://crates.io/crates/httparse) to decode the response data, and manually writes the request HTTP headers and does the `read` and `write` calls itself. This meant all four transport types (UDP, TCP, TLS, HTTPS) work in basically the same way.

- Of course, I used [serde](https://serde.rs/) for the JSON output. I tried separating the code into three crates in a workspace: one for the packet parsing, one for the network transports, and one for the user interface parts. JSON output naturally had to go in the UI crate, but I found I couldn't derive `Serialize` for the DNS record structs without `serde_derive` being a dependency of the packet-parsing crate, which didn't _feel_ right, so I did it manually with `json!`. This gave me more flexibility at the expense of longer code, but I'm not sure what else I could have done here.

Anyway, writing this in Rust was great. I like being able to compile down to a single small executable (a couple hundred kilobytes), I like having unit testing as part of the language, I like being able to use recent language features and still have a pretty-good guarantee that people can compile the code (it requires v1.45.0), and I like the community of people, libraries, and developer tools that helped me along the way. Cheers, everyone.

---

<div class="post-metadata">

**Author:** ![ClementNerma](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/clementnerma/32/4086_2.png) [@ClementNerma](https://users.rust-lang.org/u/ClementNerma)\
**Post date:** [November 7, 2020, 5:44pm UTC](https://users.rust-lang.org/t/dog-a-command-line-dns-client/51178/2 "2020-11-07T17:44:36Z")

</div>

Very nice project!

---

<div class="post-metadata">

**Author:** ![system](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/system/32/51372_2.png) [@system](https://users.rust-lang.org/u/system)\
**Post date:** [February 5, 2021, 5:44pm UTC](https://users.rust-lang.org/t/dog-a-command-line-dns-client/51178/3 "2021-02-05T17:44:42Z")

</div>

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.
