crystal-dns

DNS for Crystal

DNS for Crystal

crystal-dns implements the DNS protocol and provides a resolver. It can also be used to implement a name server, but there might be some missing pieces.

This is beta quality software. The API is not stable and some features are missing. Further testing is needed. No support for DNSSEC and DNS-over-TLS at this time.

crystal-dns was developed for Everbase, our Crystal-powered API that makes you a more productive developer. Check it out!

Installation

Add this to your project's shard.yml:

dependencies:
  dns:
    gitlab: jgillich/crystal-dns

Resolver Usage

resolver = DNS::Resolver.new
response = resolver.query("example.com", DNS::RecordType::AAAA)
response.answers.each do |answer|
  puts "got ipv6 address #{answer.data}"
end
resolver.close

Advanced Usage

If you are already familiar with DNS, most of this library should be self-explanatory. The main information carrier in DNS is a Message. The format is identical for both queries and responses, but some fields vary. In a nutshell, a message contains:

  • A header. This includes information about the message (is it a query or a response, number of results, etc)
  • A question. It consists of a domain name, a record type (A, CNAME, MX etc) and a record class (in most cases IN for internet). Technically DNS allows for more than one question, but very few name servers support this.
  • A number of records with a data payload. If your question's record type is A, the payload is an IPv4 address.

First we need a Socket to communicate over. This can be either a client or a server, but for this example we'll talk to a server.

require "socket"
require "dns"

socket = UDPSocket.new
socket.connect("8.8.8.8", 53)

Now we build a message with the format from above:

header = DNS::Header.new(op_code: DNS::OpCode::Query, recursion_desired: true)
message = DNS::Message.new(header: header)
message.questions << DNS::Question.new(name: DNS::Name.new("www.example.com"), query_type: DNS::RecordType::A)

This is a valid DNS query that will return the IPv4 address for the domain www.example.com.

Message implements #to_io(IO, IO::ByteFormat) and .from_io(IO, IO::ByteFormat), but they do not work well with a Socket. We need IO#seek to resolve pointers, but sockets do not implement this method. There are also some slight variations in the data format between UDP and TCP. So instead, we provide #to_socket(Socket, IO::ByteFormat) and Message.from_socket(Socket, IO::ByteFormat).

Let's use them to send our message:

message.to_socket socket

Done! Now we can read the response:

response = DNS::Message.from_socket socket
response.answers.each do |answer|
  puts "got ipv4 address #{answer.data}"
end

Links

Repository

crystal-dns

Owner
Statistic
  • 9
  • 2
  • 3
  • 4
  • 0
  • over 3 years ago
  • December 16, 2019
License

MIT License

Links
Synced at

Fri, 22 Nov 2024 08:40:48 GMT

Languages