lib/rubydns/server.rb in rubydns-0.5.0 vs lib/rubydns/server.rb in rubydns-0.5.1
- old
+ new
@@ -24,10 +24,65 @@
# This class provides the core of the 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.
class Server
+ class Rule
+ def initialize(pattern, callback)
+ @pattern = pattern
+ @callback = callback
+ end
+
+ def match(name, resource_class)
+ # If the pattern doesn't specify any resource classes, we implicitly pass this test:
+ return true if @pattern.size < 2
+
+ # Otherwise, we try to match against some specific resource classes:
+ if Class === @pattern[1]
+ @pattern[1] == resource_class
+ else
+ @pattern[1].include?(resource_class) rescue false
+ end
+ end
+
+ def call(server, name, resource_class, *args)
+ unless match(name, resource_class)
+ server.logger.debug "Resource class #{resource_class} failed to match #{@pattern[1].inspect}!"
+
+ return false
+ end
+
+ # Match succeeded against name?
+ case @pattern[0]
+ when Regexp
+ match_data = @pattern[0].match(name)
+ if match_data
+ server.logger.debug "Regexp pattern matched with #{match_data.inspect}."
+ return @callback[match_data, *args]
+ end
+ when String
+ if @pattern[0] == name
+ server.logger.debug "String pattern matched."
+ return @callback[*args]
+ end
+ else
+ if (@pattern[0].call(name, resource_class) rescue false)
+ server.logger.debug "Callable pattern matched."
+ return @callback[*args]
+ end
+ end
+
+ server.logger.debug "No pattern matched."
+ # We failed to match the pattern.
+ return false
+ end
+
+ def to_s
+ @pattern.inspect
+ end
+ end
+
# Instantiate a server with a block
#
# server = Server.new do
# match(/server.mydomain.com/, IN::A) do |transaction|
# transaction.respond!("1.2.3.4")
@@ -56,19 +111,19 @@
# match("www.google.com")
# match("gmail.com", IN::MX)
# match(/g?mail.(com|org|net)/, [IN::MX, IN::A])
#
def match(*pattern, &block)
- @rules << [pattern, Proc.new(&block)]
+ @rules << Rule.new(pattern, block)
end
# Register a named event which may be invoked later using #fire
# on(:start) do |server|
# RExec.change_user(RUN_AS)
# end
def on(event_name, &block)
- @events[event_name] = Proc.new(&block)
+ @events[event_name] = block
end
# Fire the named event, which must have been registered using on.
def fire(event_name)
callback = @events[event_name]
@@ -85,72 +140,32 @@
# otherwise do |transaction|
# transaction.passthrough!($R)
# end
#
def otherwise(&block)
- @otherwise = Proc.new(&block)
+ @otherwise = block
end
+ def next!
+ throw :next
+ end
+
# Give a name and a record type, try to match a rule and use it for
# processing the given arguments.
#
# If a rule returns false, it is considered that the rule failed and
# futher matching is carried out.
def process(name, resource_class, *args)
@logger.debug "Searching for #{name} #{resource_class.name}"
@rules.each do |rule|
- @logger.debug "Checking rule #{rule[0].inspect}..."
-
- pattern = rule[0]
+ @logger.debug "Checking rule #{rule}..."
- # Match failed against resource_class?
- case pattern[1]
- when Class
- next unless pattern[1] == resource_class
- @logger.debug "Resource class #{resource_class.name} matched"
- when Array
- next unless pattern[1].include?(resource_class)
- @logger.debug "Resource class #{resource_class} matched #{pattern[1].inspect}"
+ catch (:next) do
+ # If the rule returns true, we assume that it was successful and no further rules need to be evaluated.
+ return true if rule.call(self, name, resource_class, *args)
end
-
- # Match succeeded against name?
- case pattern[0]
- when Regexp
- match_data = pattern[0].match(name)
- if match_data
- @logger.debug "Query #{name} matched #{pattern[0].to_s} with result #{match_data.inspect}"
- if rule[1].call(match_data, *args)
- @logger.debug "Rule returned successfully"
- return
- end
- else
- @logger.debug "Query #{name} failed to match against #{pattern[0].to_s}"
- end
- when String
- if pattern[0] == name
- @logger.debug "Query #{name} matched #{pattern[0]}"
- if rule[1].call(*args)
- @logger.debug "Rule returned successfully"
- return
- end
- else
- @logger.debug "Query #{name} failed to match against #{pattern[0]}"
- end
- else
- if pattern[0].respond_to? :call
- if pattern[0].call(name)
- @logger.debug "Query #{name} matched #{pattern[0]}"
- if rule[1].call(*args)
- @logger.debug "Rule returned successfully"
- return
- end
- else
- @logger.debug "Query #{name} failed to match against #{pattern[0]}"
- end
- end
- end
end
if @otherwise
@otherwise.call(*args)
else
@@ -158,11 +173,11 @@
end
end
# Process an incoming DNS message. Returns a serialized message to be
# sent back to the client.
- def process_query(query, &block)
+ 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
@@ -184,22 +199,29 @@
next_link = chain.last
chain << lambda do
@logger.debug "Processing question #{question} #{resource_class}..."
- transaction = Transaction.new(self, query, question, resource_class, answer)
+ transaction = Transaction.new(self, query, question, resource_class, answer, options)
# Call the next link in the chain:
transaction.callback do
# 3/ ... which calls the previous item in the chain, i.e. the next question to be answered:
next_link.call
end
# If there was an error, log it and fail:
transaction.errback do |response|
- @logger.error "Exception thrown while processing #{transaction}!"
- @logger.error "#{response.class}: #{response.message}"
- response.backtrace.each { |at| @logger.error at }
+ if Exception === response
+ @logger.error "Exception thrown while processing #{transaction}!"
+ @logger.error "#{response.class}: #{response.message}"
+ if response.backtrace
+ Array(response.backtrace).each { |at| @logger.error at }
+ end
+ else
+ @logger.error "Failure while processing #{transaction}!"
+ @logger.error "#{response.inspect}"
+ end
answer.rcode = Resolv::DNS::RCode::ServFail
chain.first.call
end