I've recently found that DNS is surprisingly simple protocol, you can implement simple query/response with very few lines of code. So if you want to better understand it, you might want to code a simple client, may be even recursive one.

It seems simple on the surface, then you get into the various record types, and edge cases to support.

You just need a decent library to parse DNS packets. [1] Yes, it's in C, but it should be easy to interface to other languages given it only has two functions.

[1] https://github.com/spc476/SPCDNS

I chose to write my own: https://github.com/bluejekyll/trust-dns

And, it in theory can be used in other languages via C FFI as well, since it’s in Rust.