Class: RubyDNS::RuleBasedServer

Inherits:
Server
  • Object
show all
Defined in:
lib/rubydns/server.rb

Overview

Provides the core of the RubyDNS domain-specific language (DSL). It contains a list of rules which are used to match against incoming DNS questions. These rules are used to generate responses which are either DNS resource records or failures.

Defined Under Namespace

Classes: Rule

Constant Summary

Constants inherited from Server

Server::DEFAULT_INTERFACES

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Server

#run

Constructor Details

#initialize(&block) ⇒ RuleBasedServer

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



211
212
213
214
215
216
217
218
219
220
221
# File 'lib/rubydns/server.rb', line 211

def initialize(&block)
	super()
	
	@events = {}
	@rules = []
	@otherwise = nil
	
	if block_given?
		instance_eval &block
	end
end

Instance Attribute Details

#loggerObject

Returns the value of attribute logger.



223
224
225
# File 'lib/rubydns/server.rb', line 223

def logger
  @logger
end

Instance Method Details

#defer {|fiber| ... } ⇒ Object

Process a block with the current fiber. To resume processing from the block, call fiber.resume. You shouldn't call fiber.resume until after the top level block has returned.

Yields:

  • (fiber)


289
290
291
292
293
294
295
# File 'lib/rubydns/server.rb', line 289

def defer(&block)
	fiber = Fiber.current
	
	yield(fiber)
	
	Fiber.yield
end

#fire(event_name) ⇒ Object

Fire the named event, which must have been registered using on.



245
246
247
248
249
250
251
# File 'lib/rubydns/server.rb', line 245

def fire(event_name)
	callback = @events[event_name]
	
	if callback
		callback.call(self)
	end
end

#match(*pattern, &block) ⇒ Object

This function connects a pattern with a block. A pattern is either a String or a Regex instance. Optionally, a second argument can be provided which is either a String, Symbol or Array of resource record types which the rule matches against.

match("www.google.com") match("gmail.com", IN::MX) match(/g?mail.(com|org|net)/, [IN::MX, IN::A])



231
232
233
# File 'lib/rubydns/server.rb', line 231

def match(*pattern, &block)
	@rules << Rule.new(pattern, block)
end

#next!Object

If you match a rule, but decide within the rule that it isn't the correct one to use, you can call next! to evaluate the next rule - in other words, to continue falling down through the list of rules.



264
265
266
# File 'lib/rubydns/server.rb', line 264

def next!
	throw :next
end

#on(event_name, &block) ⇒ Object

Register a named event which may be invoked later using #fire

on(:start) do |server| RExec.change_user(RUN_AS) end



240
241
242
# File 'lib/rubydns/server.rb', line 240

def on(event_name, &block)
	@events[event_name] = block
end

#otherwise(&block) ⇒ Object

Specify a default block to execute if all other rules fail to match. This block is typially used to pass the request on to another server (i.e. recursive request).

otherwise do |transaction| transaction.passthrough!($R) end



259
260
261
# File 'lib/rubydns/server.rb', line 259

def otherwise(&block)
	@otherwise = block
end

#process(name, resource_class, *args) ⇒ Object

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



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/rubydns/server.rb', line 269

def process(name, resource_class, *args)
	@logger.debug "Searching for #{name} #{resource_class.name}"
	
	@rules.each do |rule|
		@logger.debug "Checking rule #{rule}..."
		
		catch (:next) do
			# If the rule returns true, we assume that it was successful and no further rules need to be evaluated.
			return if rule.call(self, name, resource_class, *args)
		end
	end
	
	if @otherwise
		@otherwise.call(*args)
	else
		@logger.warn "Failed to handle #{name} #{resource_class.name}!"
	end
end

#process_query(query, options = {}, &block) ⇒ Object

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



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/rubydns/server.rb', line 298

def process_query(query, options = {}, &block)
	# 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
	
	Fiber.new do
		transaction = nil
		
		begin
			query.question.each do |question, resource_class|
				@logger.debug "Processing question #{question} #{resource_class}..."
		
				transaction = Transaction.new(self, query, question, resource_class, answer, options)
				
				transaction.process
			end
		rescue
			@logger.error "Exception thrown while processing #{transaction}!"
			RubyDNS.log_exception(@logger, $!)
		
			answer.rcode = Resolv::DNS::RCode::ServFail
		end
	
		yield answer
	end.resume
end