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