Crabby DNS

A simple DNS server in Rust to help contribute further understanding to my team's office hours discussions.

I made significant reference to the following projects and/or documents in building this: - dnsguide - trust-dns - IETF RFC 1035

How to build it

How to get oriented with the crate

How to run it

Right now the Crabby DNS stub resolver implements some of the most basic relevant pieces of IETF RFC 1035, and probably not even that correctly. Only A RRs of class IN are currently supported.

Via the CLI, you specify a query domain name, and optionally a query type and query class. The stub resolver will delegate interface address binding and port selection for the UDP socket to the OS. Your query will be serialized into a DNS protocol message of query type with a header and question section and sent to the configured DNS server. The response will then be listened for (blocking) and deserialized into a DNS protocol message of response type with whatever sections + data the server responded with.

``` $ cargo run -- stub -@ 8.8.8.8 -d google.com Connected to 8.8.8.8:53 from 10.0.2.15:43506 Working on the DNS transaction now...

#

DNS QUESTION MESSAGE

#

Header { id: 0, messagetype: Query, opcode: Query, authoritativeanswer: false, truncation: false, recursiondesired: true, recursionavailable: false, authenticdata: false, checkingdisabled: false, responsecode: NoError, questioncount: 1, answercount: 0, authoritycount: 0, additionalcount: 0, } Question { domain_name: DomainName( "google.com", ), qtype: RRType( A, ), qclass: RRClass( IN, ), }

#

DNS RESPONSE MESSAGE

#

Header { id: 0, messagetype: Response, opcode: Query, authoritativeanswer: false, truncation: false, recursiondesired: true, recursionavailable: true, authenticdata: false, checkingdisabled: false, responsecode: NoError, questioncount: 1, answercount: 1, authoritycount: 0, additionalcount: 0, } Question { domainname: DomainName( "google.com", ), qtype: RRType( A, ), qclass: RRClass( IN, ), } ResourceRecord { domainname: DomainName( "google.com", ), rrtype: A, rrclass: IN, ttl: 102, rrdata_len: 4, rrdata: A( 172.217.3.110, ), } ```

DNS datagram deserializer

``` $ nc -u -l 1053 > query.pkt & [2] 28112

$ dig +retry=0 -p 1053 @127.0.0.1 +noedns google.com

; <<>> DiG 9.16.1-Ubuntu <<>> +retry -p 1053 @127.0.0.1 +noedns google.com ; (1 server found) ;; global options: +cmd ;; connection timed out; no servers could be reached

$ kill 28112 [2]- Terminated nc -u -l 1053 > query.pkt

$ nc -u 8.8.8.8 53 < query.pkt > response.pkt ^C

$ hexdump -C response.pkt 00000000 6e 04 81 80 00 01 00 01 00 00 00 00 06 67 6f 6f |n............goo| 00000010 67 6c 65 03 63 6f 6d 00 00 01 00 01 c0 0c 00 01 |gle.com.........| 00000020 00 01 00 00 00 15 00 04 ac d9 0c ae |............| 0000002c

$ cargo run -- deserialize -f ./response.pkt

#

DNS MESSAGE

#

Header { id: 28164, messagetype: Response, opcode: Query, authoritativeanswer: false, truncation: false, recursiondesired: true, recursionavailable: true, authenticdata: false, checkingdisabled: false, responsecode: NoError, questioncount: 1, answercount: 1, authoritycount: 0, additionalcount: 0, } Question { domainname: DomainName( "google.com", ), qtype: RRType( A, ), qclass: RRClass( IN, ), } ResourceRecord { domainname: DomainName( "google.com", ), rrtype: A, rrclass: IN, ttl: 21, rrdata_len: 4, rrdata: A( 172.217.12.174, ), } ```