class Async::DNS::Server

Constants

DEFAULT_ENDPOINTS

The default server interfaces

Attributes

logger[RW]
origin[RW]

Records are relative to this origin:

Public Class Methods

new(endpoints = DEFAULT_ENDPOINTS, origin: '.', logger: Console.logger) click to toggle source

Instantiate a server with a block

server = Server.new do
        match(/server.mydomain.com/, IN::A) do |transaction|
                transaction.respond!("1.2.3.4")
        end
end
# File lib/async/dns/server.rb, line 39
def initialize(endpoints = DEFAULT_ENDPOINTS, origin: '.', logger: Console.logger)
        @endpoints = endpoints
        @origin = origin
        @logger = logger
end

Public Instance Methods

fire(event_name) click to toggle source

Fire the named event as part of running the server.

# File lib/async/dns/server.rb, line 51
def fire(event_name)
end
process(name, resource_class, transaction) click to toggle source

Give a name and a record type, try to match a rule and use it for processing the given arguments.

# File lib/async/dns/server.rb, line 55
def process(name, resource_class, transaction)
        raise NotImplementedError.new
end
process_query(query, options = {}, &block) click to toggle source

Process an incoming DNS message. Returns a serialized message to be sent back to the client.

# File lib/async/dns/server.rb, line 60
def process_query(query, options = {}, &block)
        start_time = Time.now
        
        # Setup response
        response = Resolv::DNS::Message::new(query.id)
        response.qr = 1                 # 0 = Query, 1 = Response
        response.opcode = query.opcode  # Type of Query; copy from query
        response.aa = 1                 # Is this an authoritative response: 0 = No, 1 = Yes
        response.rd = query.rd          # Is Recursion Desired, copied from query
        response.ra = 0                 # Does name server support recursion: 0 = No, 1 = Yes
        response.rcode = 0              # Response code: 0 = No errors
        
        transaction = nil
        
        begin
                query.question.each do |question, resource_class|
                        begin
                                question = question.without_origin(@origin)
                                
                                @logger.debug(query) {"Processing question #{question} #{resource_class}..."}
                                
                                transaction = Transaction.new(self, query, question, resource_class, response, options)
                                
                                transaction.process
                        rescue Resolv::DNS::OriginError
                                # This is triggered if the question is not part of the specified @origin:
                                @logger.debug(query) {"Skipping question #{question} #{resource_class} because #{$!}"}
                        end
                end
        rescue StandardError => error
                @logger.error(query) {error}
                
                response.rcode = Resolv::DNS::RCode::ServFail
        end
        
        end_time = Time.now
        @logger.debug(query) {"Time to process request: #{end_time - start_time}s"}
        
        return response
end
run(*args) click to toggle source

Setup all specified interfaces and begin accepting incoming connections.

# File lib/async/dns/server.rb, line 102
def run(*args)
        @logger.info "Starting Async::DNS server (v#{Async::DNS::VERSION})..."
        
        Async::Reactor.run do |task|
                fire(:setup)
                
                Async::IO::Endpoint.each(@endpoints) do |endpoint|
                        task.async do
                                endpoint.bind do |socket|
                                        case socket.type
                                        when Socket::SOCK_DGRAM
                                                @logger.info "<> Listening for datagrams on #{socket.local_address.inspect}"
                                                DatagramHandler.new(self, socket).run
                                        when Socket::SOCK_STREAM
                                                @logger.info "<> Listening for connections on #{socket.local_address.inspect}"
                                                StreamHandler.new(self, socket).run
                                        else
                                                raise ArgumentError.new("Don't know how to handle #{address}")
                                        end
                                end
                        end
                end
                
                fire(:start)
        end
end