Update 2009-09-10: If you’re interested in a DNS server written in Ruby, check out RubyDNS. Thanks to Samuel for his comment below announcing its release.
A few weeks ago, I started playing with Ruby’s Resolv library, which abstracts the DNS protocol. I’m playing with creating a DNS server in Ruby. It turns out to be pretty easy to get a very basic DNS server going in Ruby, And, for the curious, here it is.
require 'socket'
require 'resolv'
hosts = [
{:name => "example.com", :type => "A", :data => "192.168.0.1"}
]
# Bind to UDP port 53 to receive requests
$port = 53
server = UDPSocket.open
server.bind(nil, $port)
while true
# Receive and parse query
data = server.recvfrom(10000)
query = Resolv::DNS::Message::decode(data[0])
# Setup answer
answer = Resolv::DNS::Message::new(query.id)
answer.qr = 1 # 0 = Query, 1 = Response
answer.opcode = query.opcode # Type of Query; copy from query
answer.aa = 1 # Is this an authoritative response: 0 = No, 1 = Yes
answer.rd = query.rd # Is Recursion Desired, copied from query
answer.ra = 0 # Does name server support recursion: 0 = No, 1 = Yes
answer.rcode = 0 # Response code: 0 = No errors
query.each_question do |question, typeclass| # There may be multiple questions per query
name = question.to_s # The domain name looked for in the query.
record_type = typeclass.name.split("::").last # For example "A", "MX"
ttl = 16000
record = hosts.find{|host| host[:name] == name && host[:type] == record_type }
unless record.nil?
# Setup answer to this question
answer.add_answer(name + ".",ttl,typeclass.new(record[:data]))
answer.encode
end
end
# Send the response
server.send(answer.encode, 0, data[1][2], data[1][1])
end
To try this out, run the above script (may require sudo, since it binds to port 53; I haven’t figured out a way to test this at a different port) and then, in irb:
require 'resolv'
d = Resolv::DNS::new(:nameserver => "localhost")
puts d.getaddress("example.com")
This would only respond to A record requests for example.com, and the array of hashes is not particularly scalable. One could, for example, use ActiveRecord to access the records from a database, which is something I’m currently working on.