Class: RubyDNS::Server

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

Overview

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.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(&block) ⇒ Server

Instantiate a server with a block

server = Server.new do
  match(/server.mydomain.com/, :A) do |transaction|
    transaction.respond!("1.2.3.4")
  end
end


33
34
35
36
37
38
39
40
41
42
43
# File 'lib/rubydns/server.rb', line 33

def initialize(&block)
	@events = {}
	@rules = []
	@otherwise = nil

	@logger = Logger.new($stderr)

	if block_given?
		instance_eval &block
	end
end

Instance Attribute Details

#loggerObject

Returns the value of attribute logger.



45
46
47
# File 'lib/rubydns/server.rb', line 45

def logger
  @logger
end

Instance Method Details

#fire(event_name) ⇒ Object

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



81
82
83
84
85
86
87
# File 'lib/rubydns/server.rb', line 81

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", :MX)
match(/g?mail.(com|org|net)/, [:MX, :A])


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/rubydns/server.rb', line 56

def match (*pattern, &block)
	# Normalize pattern
	case pattern[1]
	when nil
		# Do nothing
	when String
		pattern[1] = pattern[1].upcase
	when Symbol
		pattern[1] = pattern[1].to_s.upcase
	when Array
		pattern[1] = pattern[1].collect { |v| v.to_s.upcase }
	end

	@rules << [pattern, Proc.new(&block)]
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


76
77
78
# File 'lib/rubydns/server.rb', line 76

def on(event_name, &block)
	@events[event_name] = Proc.new(&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


97
98
99
# File 'lib/rubydns/server.rb', line 97

def otherwise(&block)
	@otherwise = Proc.new(&block)
end

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

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.



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/rubydns/server.rb', line 106

def process(name, record_type, *args)
	@logger.debug "Searching for #{name} #{record_type}"

	@rules.each do |rule|
		@logger.debug "Checking rule #{rule[0].inspect}..."
		
		pattern = rule[0]

		# Match failed against record_type?
		case pattern[1]
		when String
			next if pattern[1] != record_type
			@logger.debug "Resource type #{record_type} matched"
		when Array
			next if pattern[1].include?(record_type)
			@logger.debug "Resource type #{record_type} matched #{pattern[1].inspect}"
		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
		end
	end

	if @otherwise
		@otherwise.call(*args)
	else
		@logger.warn "Failed to handle #{name} #{record_type}!"
	end
end

#receive_data(data, &block) ⇒ Object

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



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/rubydns/server.rb', line 159

def receive_data(data, &block)
	query = Resolv::DNS::Message::decode(data)

	# 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

	query.each_question do |question, resource_class|    # There may be multiple questions per query
		transaction = Transaction.new(self, query, question, resource_class, answer)

		begin
			transaction.process
		rescue
			@logger.error "Exception thrown while processing #{transaction}!"
			@logger.error "#{$!.class}: #{$!.message}"
			$!.backtrace.each { |at| @logger.error at }

			answer.rcode = Resolv::DNS::RCode::ServFail
		end
	end

	if block_given?
		yield answer
	else
		return answer
	end
end