devdnsd 1.7.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis-gemfile +14 -0
- data/.travis.yml +4 -4
- data/Gemfile +13 -1
- data/README.md +6 -3
- data/Rakefile +1 -8
- data/bin/devdnsd +15 -13
- data/devdnsd.gemspec +6 -13
- data/doc/DevDNSd/Application.html +1096 -1651
- data/doc/DevDNSd/ApplicationMethods/Server.html +517 -0
- data/doc/DevDNSd/ApplicationMethods/System/ClassMethods.html +372 -0
- data/doc/DevDNSd/ApplicationMethods/System.html +956 -0
- data/doc/DevDNSd/ApplicationMethods.html +125 -0
- data/doc/DevDNSd/Configuration.html +7 -6
- data/doc/DevDNSd/Errors/InvalidRule.html +3 -3
- data/doc/DevDNSd/Errors.html +3 -3
- data/doc/DevDNSd/Rule.html +1685 -218
- data/doc/DevDNSd/Version.html +5 -5
- data/doc/DevDNSd.html +6 -6
- data/doc/_index.html +43 -4
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +47 -40
- data/doc/frames.html +1 -1
- data/doc/index.html +47 -40
- data/doc/method_list.html +69 -45
- data/doc/top-level-namespace.html +3 -3
- data/lib/devdnsd/application.rb +382 -290
- data/lib/devdnsd/configuration.rb +9 -9
- data/lib/devdnsd/errors.rb +1 -1
- data/lib/devdnsd/rule.rb +84 -48
- data/lib/devdnsd/version.rb +3 -3
- data/lib/devdnsd.rb +5 -2
- data/locales/en.yml +48 -0
- data/locales/it.yml +48 -0
- data/spec/coverage_helper.rb +6 -9
- data/spec/devdnsd/application_spec.rb +62 -42
- data/spec/devdnsd/configuration_spec.rb +1 -1
- data/spec/devdnsd/rule_spec.rb +7 -7
- data/spec/spec_helper.rb +1 -2
- data/utils/tester.rb +13 -27
- metadata +17 -142
- data/Gemfile.lock +0 -101
data/lib/devdnsd/application.rb
CHANGED
@@ -1,352 +1,420 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
#
|
3
|
-
# This file is part of the devdnsd gem. Copyright (C)
|
3
|
+
# This file is part of the devdnsd gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
|
4
4
|
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
5
5
|
#
|
6
6
|
|
7
7
|
# A small DNS server to enable local .dev domain resolution.
|
8
8
|
module DevDNSd
|
9
|
-
#
|
10
|
-
|
11
|
-
#
|
12
|
-
|
9
|
+
# Methods for the {Application Application} class.
|
10
|
+
module ApplicationMethods
|
11
|
+
# System management methods.
|
12
|
+
module System
|
13
|
+
extend ActiveSupport::Concern
|
14
|
+
|
15
|
+
# Class methods.
|
16
|
+
module ClassMethods
|
17
|
+
# Returns the name of the daemon.
|
18
|
+
#
|
19
|
+
# @return [String] The name of the daemon.
|
20
|
+
def daemon_name
|
21
|
+
File.basename(self.instance.config.pid_file, ".pid")
|
22
|
+
end
|
13
23
|
|
14
|
-
|
15
|
-
|
24
|
+
# Returns the standard location of the PID file.
|
25
|
+
#
|
26
|
+
# @return [String] The standard location of the PID file.
|
27
|
+
def pid_directory
|
28
|
+
File.dirname(self.instance.config.pid_file)
|
29
|
+
end
|
16
30
|
|
17
|
-
|
18
|
-
|
31
|
+
# Returns the complete path of the PID file.
|
32
|
+
#
|
33
|
+
# @return [String] The complete path of the PID file.
|
34
|
+
def pid_fn
|
35
|
+
self.instance.config.pid_file
|
36
|
+
end
|
37
|
+
end
|
19
38
|
|
20
|
-
|
21
|
-
|
39
|
+
# Gets the path for the resolver file.
|
40
|
+
#
|
41
|
+
# @param tld [String] The TLD to manage.
|
42
|
+
# @return [String] The path for the resolver file.
|
43
|
+
def resolver_path(tld = nil)
|
44
|
+
tld ||= @config.tld
|
45
|
+
"/etc/resolver/#{tld}"
|
46
|
+
end
|
22
47
|
|
23
|
-
|
24
|
-
|
48
|
+
# Gets the path for the launch agent file.
|
49
|
+
#
|
50
|
+
# @param name [String] The base name for the agent.
|
51
|
+
# @return [String] The path for the launch agent file.
|
52
|
+
def launch_agent_path(name = "it.cowtech.devdnsd")
|
53
|
+
ENV["HOME"] + "/Library/LaunchAgents/#{name}.plist"
|
54
|
+
end
|
25
55
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
56
|
+
# Executes a shell command.
|
57
|
+
#
|
58
|
+
# @param command [String] The command to execute.
|
59
|
+
# @return [Boolean] `true` if command succeeded, `false` otherwise.
|
60
|
+
def execute_command(command)
|
61
|
+
system(command)
|
62
|
+
end
|
32
63
|
|
33
|
-
#
|
34
|
-
|
35
|
-
@
|
64
|
+
# Updates DNS cache.
|
65
|
+
#
|
66
|
+
# @return [Boolean] `true` if command succeeded, `false` otherwise.
|
67
|
+
def dns_update
|
68
|
+
@logger.info(self.i18n.dns_update)
|
69
|
+
self.execute_command("dscacheutil -flushcache")
|
70
|
+
end
|
36
71
|
|
37
|
-
#
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
:log_file => application.options["log-file"].value,
|
45
|
-
:log_level => application.options["log-level"].value
|
46
|
-
}.reject {|k,v| v.nil? }
|
47
|
-
|
48
|
-
@config = DevDNSd::Configuration.new(application.options["configuration"].value, overrides, @logger)
|
49
|
-
|
50
|
-
@logger = nil
|
51
|
-
@logger = self.get_logger
|
52
|
-
rescue Bovem::Errors::InvalidConfiguration, DevDNSd::Errors::InvalidRule => e
|
53
|
-
@logger ? @logger.fatal(e.message) : Bovem::Logger.create("STDERR").fatal("Cannot log to #{config.log_file}. Exiting...")
|
54
|
-
raise ::SystemExit
|
72
|
+
# Checks if we are running on MacOS X.
|
73
|
+
#
|
74
|
+
# System services are only available on that platform.
|
75
|
+
#
|
76
|
+
# @return [Boolean] `true` if the current platform is MacOS X, `false` otherwise.
|
77
|
+
def is_osx?
|
78
|
+
::RbConfig::CONFIG['host_os'] =~ /^darwin/
|
55
79
|
end
|
56
80
|
|
57
|
-
|
58
|
-
|
81
|
+
# Starts the server in background.
|
82
|
+
#
|
83
|
+
# @return [Boolean] `true` if action succedeed, `false` otherwise.
|
84
|
+
def action_start
|
85
|
+
self.get_logger.info(self.i18n.starting)
|
59
86
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
File.basename(self.instance.config.pid_file, ".pid")
|
65
|
-
end
|
87
|
+
if !Process.respond_to?(:fork) then
|
88
|
+
self.logger.warn(self.i18n.no_fork)
|
89
|
+
@config.foreground = true
|
90
|
+
end
|
66
91
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
def self.pid_directory
|
71
|
-
File.dirname(self.instance.config.pid_file)
|
72
|
-
end
|
92
|
+
@config.foreground ? self.perform_server : RExec::Daemon::Controller.start(self.class)
|
93
|
+
true
|
94
|
+
end
|
73
95
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
96
|
+
# Stops the server in background.
|
97
|
+
#
|
98
|
+
# @return [Boolean] `true` if action succedeed, `false` otherwise.
|
99
|
+
def action_stop
|
100
|
+
RExec::Daemon::Controller.stop(self.class)
|
101
|
+
true
|
102
|
+
end
|
80
103
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
end
|
104
|
+
# Installs the application into the autolaunch.
|
105
|
+
#
|
106
|
+
# @return [Boolean] `true` if action succedeed, `false` otherwise.
|
107
|
+
def action_install
|
108
|
+
manage_installation(self.launch_agent_path, self.resolver_path, :create_resolver, :create_agent, :load_agent)
|
109
|
+
end
|
88
110
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
111
|
+
# Uninstalls the application from the autolaunch.
|
112
|
+
#
|
113
|
+
# @return [Boolean] `true` if action succedeed, `false` otherwise.
|
114
|
+
def action_uninstall
|
115
|
+
manage_installation(self.launch_agent_path, self.resolver_path, :delete_resolver, :unload_agent, :delete_agent)
|
116
|
+
end
|
95
117
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
118
|
+
private
|
119
|
+
# Manages a OSX agent.
|
120
|
+
#
|
121
|
+
# @param launch_agent [String] The agent path.
|
122
|
+
# @param resolver_path [String] The resolver path.
|
123
|
+
# @param first_operation [Symbol] The first operation to execute.
|
124
|
+
# @param second_operation [Symbol] The second operation to execute.
|
125
|
+
# @param third_operation [Symbol] The third operation to execute.
|
126
|
+
# @return [Boolean] `true` if operation succedeed, `false` otherwise.
|
127
|
+
def manage_installation(launch_agent, resolver_path, first_operation, second_operation, third_operation)
|
128
|
+
rv = true
|
129
|
+
|
130
|
+
rv = check_agent_available
|
131
|
+
rv = send(first_operation, launch_agent, resolver_path) if rv
|
132
|
+
rv = send(second_operation, launch_agent, resolver_path) if rv
|
133
|
+
rv = send(third_operation, launch_agent, resolver_path) if rv
|
134
|
+
self.dns_update
|
135
|
+
rv
|
136
|
+
end
|
104
137
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
138
|
+
# Deletes a file
|
139
|
+
#
|
140
|
+
# @param file [String] The file to delete.
|
141
|
+
# @param before_message [Symbol] The message to show before deleting.
|
142
|
+
# @param error_message [Symbol] The message to show in case of errors.
|
143
|
+
# @return [Boolean] `true` if the file have been deleted, `false` otherwise.
|
144
|
+
def delete_file(file, before_message, error_message)
|
145
|
+
begin
|
146
|
+
self.logger.info(self.i18n.send(before_message, file))
|
147
|
+
::File.delete(file)
|
148
|
+
true
|
149
|
+
rescue => e
|
150
|
+
self.logger.warn(self.i18n.send(error_message))
|
151
|
+
false
|
152
|
+
end
|
153
|
+
end
|
112
154
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
155
|
+
# Checks if agent is enabled (that is, we are on OSX).
|
156
|
+
#
|
157
|
+
# @return [Boolean] `true` if the agent is enabled, `false` otherwise.
|
158
|
+
def check_agent_available
|
159
|
+
rv = true
|
160
|
+
if !self.is_osx? then
|
161
|
+
logger.fatal(self.i18n.no_agent)
|
162
|
+
rv = false
|
163
|
+
end
|
120
164
|
|
121
|
-
|
122
|
-
|
123
|
-
# @return [Boolean] `true` if command succeeded, `false` otherwise.
|
124
|
-
def dns_update
|
125
|
-
@logger.info("Flushing DNS cache and resolvers ...")
|
126
|
-
self.execute_command("dscacheutil -flushcache")
|
127
|
-
end
|
165
|
+
rv
|
166
|
+
end
|
128
167
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
resource_classes = DevDNSd::Application::ANY_CLASSES & rule.resource_class.ensure_array
|
143
|
-
resource_classes = resource_classes & [transaction.resource_class] if transaction.resource_class != DevDNSd::Application::ANY_REQUEST
|
144
|
-
|
145
|
-
if resource_classes.present? then
|
146
|
-
resource_classes.each do |resource_class| # Now for every class
|
147
|
-
matches = rule.match_host(match_data[0])
|
148
|
-
DevDNSd::Application.instance.process_rule(rule, resource_class, rule.is_regexp? ? matches : nil, transaction) if matches
|
149
|
-
end
|
150
|
-
end
|
151
|
-
rescue ::Exception => e
|
152
|
-
raise e
|
153
|
-
end
|
168
|
+
# Creates a OSX resolver.
|
169
|
+
#
|
170
|
+
# @param launch_agent [String] The agent path.
|
171
|
+
# @param resolver_path [String] The resolver path.
|
172
|
+
# @return [Boolean] `true` if operation succedeed, `false` otherwise.
|
173
|
+
def create_resolver(launch_agent, resolver_path)
|
174
|
+
begin
|
175
|
+
self.logger.info(self.i18n.resolver_creating(resolver_path))
|
176
|
+
write_resolver(resolver_path)
|
177
|
+
true
|
178
|
+
rescue
|
179
|
+
self.logger.error(self.i18n.resolver_creating_error)
|
180
|
+
false
|
154
181
|
end
|
155
182
|
end
|
156
183
|
|
157
|
-
#
|
158
|
-
|
159
|
-
|
184
|
+
# Writes a OSX resolver.
|
185
|
+
#
|
186
|
+
# @param resolver_path [String] The resolver path.
|
187
|
+
def write_resolver(resolver_path)
|
188
|
+
::File.open(resolver_path, "w") {|f|
|
189
|
+
f.write("nameserver 127.0.0.1\n")
|
190
|
+
f.write("port #{@config.port}")
|
191
|
+
f.flush
|
192
|
+
}
|
160
193
|
end
|
161
194
|
|
162
|
-
#
|
163
|
-
|
164
|
-
|
195
|
+
# Deletes a OSX resolver.
|
196
|
+
#
|
197
|
+
# @param launch_agent [String] The agent path.
|
198
|
+
# @param resolver_path [String] The resolver path.
|
199
|
+
# @return [Boolean] `true` if operation succedeed, `false` otherwise.
|
200
|
+
def delete_resolver(launch_agent, resolver_path)
|
201
|
+
delete_file(resolver_path, :resolver_deleting, :resolver_deleting_error)
|
165
202
|
end
|
166
203
|
|
167
|
-
|
168
|
-
|
204
|
+
# Creates a OSX system agent.
|
205
|
+
#
|
206
|
+
# @param launch_agent [String] The agent path.
|
207
|
+
# @param resolver_path [String] The resolver path.
|
208
|
+
# @return [Boolean] `true` if operation succedeed, `false` otherwise.
|
209
|
+
def create_agent(launch_agent, resolver_path)
|
210
|
+
begin
|
211
|
+
self.logger.info(self.i18n.agent_creating(launch_agent))
|
212
|
+
write_agent(launch_agent)
|
213
|
+
self.execute_command("plutil -convert binary1 \"#{launch_agent}\"")
|
214
|
+
true
|
215
|
+
rescue
|
216
|
+
self.logger.error(self.i18n.agent_creating_error)
|
217
|
+
false
|
218
|
+
end
|
169
219
|
end
|
170
|
-
end
|
171
|
-
end
|
172
220
|
|
173
|
-
# Processes a DNS rule.
|
174
|
-
#
|
175
|
-
# @param rule [Rule] The rule to process.
|
176
|
-
# @param type [Class] The type of request.
|
177
|
-
# @param match_data [MatchData|nil] If the rule pattern was a Regexp, then this holds the match data, otherwise `nil` is passed.
|
178
|
-
# @param transaction [Transaction] The current DNS transaction (http://rubydoc.info/gems/rubydns/RubyDNS/Transaction).
|
179
|
-
# @return A reply for the request if matched, otherwise `false` or `nil`.
|
180
|
-
def process_rule(rule, type, match_data, transaction)
|
181
|
-
is_regex = rule.match.is_a?(::Regexp)
|
182
|
-
type = DevDNSd::Rule.resource_class_to_symbol(type)
|
183
|
-
|
184
|
-
DevDNSd::Application.instance.logger.debug("Found match on #{rule.match} with type #{type}.")
|
185
|
-
|
186
|
-
if !rule.block.nil? then
|
187
|
-
reply = rule.block.call(match_data, type, transaction)
|
188
|
-
else
|
189
|
-
reply = rule.reply
|
190
|
-
end
|
191
|
-
|
192
|
-
if is_regex && reply && match_data[0] then
|
193
|
-
reply = match_data[0].gsub(rule.match, reply.gsub("$", "\\"))
|
194
|
-
end
|
195
221
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
222
|
+
# Writes a OSX system agent.
|
223
|
+
#
|
224
|
+
# @param launch_agent [String] The agent path.
|
225
|
+
def write_agent(launch_agent)
|
226
|
+
::File.open(launch_agent, "w") {|f|
|
227
|
+
f.write({"KeepAlive" => true, "Label" => "it.cowtech.devdnsd", "Program" => (::Pathname.new(Dir.pwd) + $0).to_s, "ProgramArguments" => ($ARGV ? $ARGV[0, $ARGV.length - 1] : []), "RunAtLoad" => true}.to_json)
|
228
|
+
f.flush
|
229
|
+
}
|
230
|
+
end
|
202
231
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
232
|
+
# Deletes a OSX system agent.
|
233
|
+
#
|
234
|
+
# @param launch_agent [String] The agent path.
|
235
|
+
# @param resolver_path [String] The resolver path.
|
236
|
+
# @return [Boolean] `true` if operation succedeed, `false` otherwise.
|
237
|
+
def delete_agent(launch_agent, resolver_path)
|
238
|
+
delete_file(launch_agent, :agent_deleting, :agent_deleting_error)
|
208
239
|
end
|
209
240
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
241
|
+
# Loads a OSX system agent.
|
242
|
+
#
|
243
|
+
# @param launch_agent [String] The agent path.
|
244
|
+
# @param resolver_path [String] The resolver path.
|
245
|
+
# @return [Boolean] `true` if operation succedeed, `false` otherwise.
|
246
|
+
def load_agent(launch_agent, resolver_path)
|
247
|
+
begin
|
248
|
+
self.logger.info(self.i18n.agent_loading(launch_agent))
|
249
|
+
self.execute_command("launchctl load -w \"#{launch_agent}\" > /dev/null 2>&1")
|
250
|
+
true
|
251
|
+
rescue
|
252
|
+
self.logger.error(self.i18n.agent_loading_error)
|
253
|
+
false
|
254
|
+
end
|
214
255
|
end
|
215
256
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
257
|
+
# Unoads a OSX system agent.
|
258
|
+
#
|
259
|
+
# @param launch_agent [String] The agent path.
|
260
|
+
# @param resolver_path [String] The resolver path.
|
261
|
+
# @return [Boolean] `true` if operation succedeed, `false` otherwise.
|
262
|
+
def unload_agent(launch_agent, resolver_path)
|
263
|
+
begin
|
264
|
+
self.logger.info(self.i18n.agent_unloading(launch_agent))
|
265
|
+
self.execute_command("launchctl unload -w \"#{launch_agent}\" > /dev/null 2>&1")
|
266
|
+
true
|
267
|
+
rescue => e
|
268
|
+
self.logger.warn(self.i18n.agent_unloading_error)
|
269
|
+
false
|
270
|
+
end
|
271
|
+
end
|
223
272
|
end
|
224
273
|
|
225
|
-
#
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
274
|
+
# Methods to process requests.
|
275
|
+
module Server
|
276
|
+
# Starts the DNS server.
|
277
|
+
#
|
278
|
+
# @return [Object] The result of stop callbacks.
|
279
|
+
def perform_server
|
280
|
+
application = self
|
281
|
+
RubyDNS::run_server(listen: [[:udp, @config.address, @config.port.to_integer]]) do
|
282
|
+
self.logger = application.logger
|
283
|
+
|
284
|
+
match(/.+/, DevDNSd::Application::ANY_CLASSES) do |match_data, transaction|
|
285
|
+
transaction.append_question!
|
286
|
+
application.config.rules.each { |rule| application.process_rule_in_classes(rule, match_data, transaction) } # During debugging, wrap the inside of the block with a begin rescue and PRINT the exception because RubyDNS hides it.
|
287
|
+
end
|
232
288
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
289
|
+
# Default DNS handler and event handlers
|
290
|
+
otherwise { |transaction| transaction.failure!(:NXDomain) }
|
291
|
+
self.on(:start) { application.on_start }
|
292
|
+
self.on(:stop) { application.on_stop }
|
293
|
+
end
|
237
294
|
end
|
238
295
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
if !self.is_osx? then
|
258
|
-
logger.fatal("Install DevDNSd as a local resolver is only available on MacOSX.")
|
259
|
-
return false
|
296
|
+
# Processes a DNS rule.
|
297
|
+
#
|
298
|
+
# @param rule [Rule] The rule to process.
|
299
|
+
# @param type [Symbol] The type of the query.
|
300
|
+
# @param match_data [MatchData|nil] If the rule pattern was a Regexp, then this holds the match data, otherwise `nil` is passed.
|
301
|
+
# @param transaction [RubyDNS::Transaction] The current DNS transaction (http://rubydoc.info/gems/rubydns/RubyDNS/Transaction).
|
302
|
+
def process_rule(rule, type, match_data, transaction)
|
303
|
+
reply, type = perform_process_rule(rule, type, match_data, transaction)
|
304
|
+
self.logger.debug(reply ? self.i18n.reply(reply, type) : self.i18n.no_reply)
|
305
|
+
|
306
|
+
if reply then
|
307
|
+
transaction.respond!(*finalize_reply(reply, rule, type))
|
308
|
+
elsif reply == false then
|
309
|
+
false
|
310
|
+
else
|
311
|
+
nil
|
312
|
+
end
|
260
313
|
end
|
261
314
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
#
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
315
|
+
# Processes a rule against a set of DNS resource classes.
|
316
|
+
#
|
317
|
+
# @param rule [Rule] The rule to process.
|
318
|
+
# @param match_data [MatchData|nil] If the rule pattern was a Regexp, then this holds the match data, otherwise `nil` is passed.
|
319
|
+
# @param transaction [RubyDNS::Transaction] The current DNS transaction (http://rubydoc.info/gems/rubydns/RubyDNS/Transaction).
|
320
|
+
def process_rule_in_classes(rule, match_data, transaction)
|
321
|
+
# Get the subset of handled class that is valid for the rule
|
322
|
+
resource_classes = DevDNSd::Application::ANY_CLASSES & rule.resource_class.ensure_array
|
323
|
+
resource_classes = resource_classes & [transaction.resource_class] if transaction.resource_class != DevDNSd::Application::ANY_REQUEST
|
324
|
+
|
325
|
+
if resource_classes.present? then
|
326
|
+
resource_classes.each do |resource_class| # Now for every class
|
327
|
+
matches = rule.match_host(match_data[0])
|
328
|
+
self.process_rule(rule, resource_class, rule.is_regexp? ? matches : nil, transaction) if matches
|
329
|
+
end
|
330
|
+
end
|
277
331
|
end
|
278
332
|
|
279
|
-
|
280
|
-
|
333
|
+
private
|
334
|
+
# Performs the processing of a rule.
|
335
|
+
#
|
336
|
+
# @param rule [Rule] The rule to process.
|
337
|
+
# @param type [Symbol] The type of query.
|
338
|
+
# @param match_data [MatchData|nil] If the rule pattern was a Regexp, then this holds the match data, otherwise `nil` is passed.
|
339
|
+
# @param transaction [RubyDNS::Transaction] The current DNS transaction (http://rubydoc.info/gems/rubydns/RubyDNS/Transaction).
|
340
|
+
# @return [Array] The type and reply to the query.
|
341
|
+
def perform_process_rule(rule, type, match_data, transaction)
|
342
|
+
type = DevDNSd::Rule.resource_class_to_symbol(type)
|
343
|
+
reply = !rule.block.nil? ? rule.block.call(match_data, type, transaction) : rule.reply
|
344
|
+
reply = match_data[0].gsub(rule.match, reply.gsub("$", "\\")) if rule.match.is_a?(::Regexp) && reply && match_data[0]
|
345
|
+
|
346
|
+
self.logger.debug(self.i18n.match(rule.match, type))
|
347
|
+
[reply, type]
|
348
|
+
end
|
281
349
|
|
282
|
-
|
350
|
+
# Finalizes a query to return to the client.
|
351
|
+
#
|
352
|
+
# @param reply [String] The reply to send to the client.
|
353
|
+
# @param rule [Rule] The rule to process.
|
354
|
+
# @param type [Symbol] The type of query.
|
355
|
+
def finalize_reply(reply, rule, type)
|
356
|
+
rv = []
|
357
|
+
rv << rule.options.delete(:preference).to_integer(10) if type == :MX
|
358
|
+
rv << ([:A, :AAAA].include?(type) ? reply : Resolv::DNS::Name.create(reply))
|
359
|
+
rv << rule.options.merge({resource_class: DevDNSd::Rule.symbol_to_resource_class(type, @locale)})
|
360
|
+
rv
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
283
364
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
365
|
+
# The main DevDNSd application.
|
366
|
+
#
|
367
|
+
# @attribute [r] config
|
368
|
+
# @return [Configuration] The {Configuration Configuration} of this application.
|
369
|
+
# @attribute [r] command
|
370
|
+
# @return [Mamertes::Command] The Mamertes command.
|
371
|
+
# @attribute logger
|
372
|
+
# @return [Bovem::Logger] The logger for this application.
|
373
|
+
# @attribute [r] locale
|
374
|
+
# @return [Symbol|nil] The current application locale.
|
375
|
+
class Application < RExec::Daemon::Base
|
376
|
+
# Class for ANY DNS request.
|
377
|
+
ANY_REQUEST = Resolv::DNS::Resource::IN::ANY
|
294
378
|
|
295
|
-
|
296
|
-
|
297
|
-
self.execute_command("launchctl load -w \"#{launch_agent}\" > /dev/null 2>&1")
|
298
|
-
rescue => e
|
299
|
-
logger.error("Cannot load the launch agent.")
|
300
|
-
return false
|
301
|
-
end
|
379
|
+
# List of classes handled in case of DNS request with resource class ANY.
|
380
|
+
ANY_CLASSES = [Resolv::DNS::Resource::IN::A, Resolv::DNS::Resource::IN::AAAA, Resolv::DNS::Resource::IN::ANY, Resolv::DNS::Resource::IN::CNAME, Resolv::DNS::Resource::IN::HINFO, Resolv::DNS::Resource::IN::MINFO, Resolv::DNS::Resource::IN::MX, Resolv::DNS::Resource::IN::NS, Resolv::DNS::Resource::IN::PTR, Resolv::DNS::Resource::IN::SOA, Resolv::DNS::Resource::IN::TXT]
|
302
381
|
|
303
|
-
|
382
|
+
include Lazier::I18n
|
383
|
+
include DevDNSd::ApplicationMethods::System
|
384
|
+
include DevDNSd::ApplicationMethods::Server
|
304
385
|
|
305
|
-
|
306
|
-
|
386
|
+
attr_reader :config
|
387
|
+
attr_reader :command
|
388
|
+
attr_accessor :logger
|
389
|
+
attr_reader :locale
|
307
390
|
|
308
|
-
#
|
391
|
+
# Creates a new application.
|
309
392
|
#
|
310
|
-
# @
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
logger.fatal("Install DevDNSd as a local resolver is only available on MacOSX.")
|
316
|
-
return false
|
317
|
-
end
|
318
|
-
|
319
|
-
resolver_file = self.resolver_path
|
320
|
-
launch_agent = self.launch_agent_path
|
393
|
+
# @param command [Mamertes::Command] The current Mamertes command.
|
394
|
+
# @param locale [Symbol] The locale to use for the application.
|
395
|
+
def initialize(command, locale)
|
396
|
+
self.i18n_setup(:devdnsd, ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/"))
|
397
|
+
self.i18n = locale
|
321
398
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
::File.delete(resolver_file)
|
326
|
-
rescue => e
|
327
|
-
logger.warn("Cannot delete the resolver file.")
|
328
|
-
return false
|
329
|
-
end
|
399
|
+
@locale = locale
|
400
|
+
@command = command
|
401
|
+
options = @command.application.get_options.reject {|k,v| v.nil? }
|
330
402
|
|
331
|
-
#
|
332
|
-
|
333
|
-
|
334
|
-
rescue => e
|
335
|
-
logger.warn("Cannot unload the launch agent.")
|
336
|
-
end
|
403
|
+
# Setup logger
|
404
|
+
Bovem::Logger.start_time = Time.now
|
405
|
+
@logger = Bovem::Logger.create(Bovem::Logger.get_real_file(options["log_file"]) || Bovem::Logger.default_file, Logger::INFO)
|
337
406
|
|
338
|
-
#
|
339
|
-
|
340
|
-
logger.info("Deleting the launch agent #{launch_agent} ...")
|
341
|
-
::File.delete(launch_agent)
|
342
|
-
rescue => e
|
343
|
-
logger.warn("Cannot delete the launch agent.")
|
344
|
-
return false
|
345
|
-
end
|
407
|
+
# Open configuration
|
408
|
+
read_configuration(options)
|
346
409
|
|
347
|
-
self
|
410
|
+
self
|
411
|
+
end
|
348
412
|
|
349
|
-
|
413
|
+
# Gets the current logger of the application.
|
414
|
+
#
|
415
|
+
# @return [Logger] The current logger of the application.
|
416
|
+
def get_logger
|
417
|
+
@logger ||= Bovem::Logger.create(@config.foreground ? Bovem::Logger.default_file : @config.log_file, @config.log_level, @log_formatter)
|
350
418
|
end
|
351
419
|
|
352
420
|
# This method is called when the server starts. By default is a no-op.
|
@@ -364,11 +432,12 @@ module DevDNSd
|
|
364
432
|
# Returns a unique (singleton) instance of the application.
|
365
433
|
#
|
366
434
|
# @param command [Mamertes::Command] The current Mamertes command.
|
435
|
+
# @param locale [Symbol] The locale to use for the application.
|
367
436
|
# @param force [Boolean] If to force recreation of the instance.
|
368
437
|
# @return [Application] The unique (singleton) instance of the application.
|
369
|
-
def self.instance(command = nil, force = false)
|
438
|
+
def self.instance(command = nil, locale = nil, force = false)
|
370
439
|
@instance = nil if force
|
371
|
-
@instance ||= DevDNSd::Application.new(command) if command
|
440
|
+
@instance ||= DevDNSd::Application.new(command, locale) if command
|
372
441
|
@instance
|
373
442
|
end
|
374
443
|
|
@@ -381,7 +450,30 @@ module DevDNSd
|
|
381
450
|
|
382
451
|
# Stops the application.
|
383
452
|
def self.quit
|
384
|
-
::EventMachine.stop
|
453
|
+
::EventMachine.stop rescue nil
|
454
|
+
end
|
455
|
+
|
456
|
+
# Check if the current implementation supports DevDNSd.
|
457
|
+
def self.check_ruby_implementation
|
458
|
+
if defined?(Rubinius) || defined?(JRuby) then
|
459
|
+
Kernel.puts(Lazier::Localizer.new(:devdnsd, ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/")).i18n.no_jruby_rubinius)
|
460
|
+
Kernel.exit(0)
|
461
|
+
end
|
385
462
|
end
|
463
|
+
|
464
|
+
private
|
465
|
+
# Reads configuration.
|
466
|
+
#
|
467
|
+
# @param options [Hash] The configuration to read.
|
468
|
+
def read_configuration(options)
|
469
|
+
begin
|
470
|
+
@config = DevDNSd::Configuration.new(options["configuration"], options, @logger)
|
471
|
+
@logger = nil
|
472
|
+
@logger = self.get_logger
|
473
|
+
rescue Bovem::Errors::InvalidConfiguration => e
|
474
|
+
@logger ? @logger.fatal(e.message) : Bovem::Logger.create("STDERR").fatal(self.i18n.logging_failed(log_file))
|
475
|
+
raise ::SystemExit
|
476
|
+
end
|
477
|
+
end
|
386
478
|
end
|
387
479
|
end
|