Async::DNS SourceAsyncDNSServer

class Server

A DNS server which can be used to resolve queries.

Definitions

def self.default_endpoint(port = 53)

The default endpoint to listen on.

Signature

parameter port Integer

The port to listen on, defaults to 53.

Implementation

def self.default_endpoint(port = 53)
	::IO::Endpoint.composite(
		::IO::Endpoint.udp('localhost', port),
		::IO::Endpoint.tcp('localhost', port)
	)
end

def initialize(endpoint = self.class.default_endpoint, origin: '.')

Instantiate a server with a block.

Implementation

def initialize(endpoint = self.class.default_endpoint, origin: '.')
	@endpoint = endpoint
	@origin = origin
end

attr_accessor :origin

Records are relative to this origin.

def logger

  • deprecated

Signature

deprecated

Use Console instead.

Implementation

def logger
	Console
end

def process(name, resource_class, transaction)

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

Implementation

def process(name, resource_class, transaction)
	transaction.fail!(:NXDomain)
end

def process_query(query, options = {}, &block)

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

Implementation

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)
				
				Console.debug(query) {"Processing question #{question} #{resource_class}..."}
				
				transaction = Transaction.new(self, query, question, resource_class, response, **options)
				
				transaction.process
			rescue Resolv::DNS::OriginError => error
				# This is triggered if the question is not part of the specified @origin:
				Console::Event::Failure.for(error).emit("Skipping question #{question} #{resource_class}!")
			end
		end
	rescue StandardError => error
		Console.error(query) {error}
		
		response.rcode = Resolv::DNS::RCode::ServFail
	end
	
	end_time = Time.now
	Console.debug(query) {"Time to process request: #{end_time - start_time}s"}
	
	return response
end

def run

Setup all specified interfaces and begin accepting incoming connections.

Implementation

def run
	Console.info "Starting Async::DNS server (v#{Async::DNS::VERSION})..."
	
	Async do |task|
		@endpoint.bind do |server|
			Console.info "<> Listening for connections on #{server.local_address.inspect}"
			case server.local_address.socktype
			when Socket::SOCK_DGRAM
				DatagramHandler.new(self, server).run
			when Socket::SOCK_STREAM
				StreamHandler.new(self, server).run
			else
				raise ArgumentError.new("Don't know how to handle #{server}")
			end
		end
	end
end