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.
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.