dns_one 0.5.5 → 0.5.6
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.
- checksums.yaml +4 -4
- data/dns_one.gemspec +1 -1
- data/lib/dns_one.rb +41 -56
- data/lib/dns_one/backend/db.rb +3 -3
- data/lib/dns_one/backend/file.rb +1 -1
- data/lib/dns_one/backend/http_bell.rb +10 -10
- data/lib/dns_one/cli.rb +1 -3
- data/lib/dns_one/global.rb +3 -0
- data/lib/dns_one/req_log/account.rb +85 -0
- data/lib/dns_one/{stat.rb → req_log/db.rb} +9 -8
- data/lib/dns_one/req_log/file.rb +28 -0
- data/lib/dns_one/req_log/req_log.rb +25 -0
- data/lib/dns_one/server.rb +56 -142
- data/lib/dns_one/util.rb +28 -20
- data/lib/dns_one/version.rb +1 -1
- data/lib/dns_one/zone_search.rb +19 -19
- data/util/dns_one.service +0 -4
- data/util/sample_conf.yml +8 -9
- metadata +10 -7
- data/lib/dns_one/log.rb +0 -72
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1712bca4a25d8e72f60b38c83c121087245f583b
|
4
|
+
data.tar.gz: 6f6d4c432b13d708273643c7abed58b2c7b8da04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54a23b46cfba3138ad0c76084f71705a5f8194a1d4c5134a0c623117d52a729bfdb404ec73477014fb5deec93c21893a98b7e4bcee810075c52baa40d6ce9a56
|
7
|
+
data.tar.gz: 55a248615c9999cd3a63aa04288af1b32514867971ade91ac2cd555d49053c7d5c8b5049bfa0dab889967973d5ba6bb8b2914253ce41b1c6d61090a738f5d285
|
data/dns_one.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency "rspec", "~> 3.0"
|
27
27
|
|
28
28
|
spec.add_runtime_dependency "thor", '~> 0.19'
|
29
|
-
spec.add_runtime_dependency "rubydns", '~>
|
29
|
+
spec.add_runtime_dependency "rubydns", '~> 2.0.1'
|
30
30
|
spec.add_runtime_dependency "rexec", '~> 1.6'
|
31
31
|
spec.add_runtime_dependency "pg", '~> 0.21'
|
32
32
|
spec.add_runtime_dependency "activerecord", '~> 5.0'
|
data/lib/dns_one.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
# Core
|
2
|
-
require 'syslog'
|
3
|
-
require 'syslog/logger'
|
4
2
|
require 'ostruct'
|
5
3
|
require 'singleton'
|
6
4
|
require 'fileutils'
|
@@ -20,76 +18,63 @@ require "dns_one/core_ext/string"
|
|
20
18
|
require "dns_one/core_ext/blank"
|
21
19
|
require "dns_one/core_ext/hash"
|
22
20
|
|
23
|
-
require "dns_one/
|
21
|
+
require "dns_one/global"
|
24
22
|
require "dns_one/util"
|
25
23
|
require "dns_one/server"
|
26
|
-
require "dns_one/
|
24
|
+
require "dns_one/req_log/db"
|
27
25
|
|
28
26
|
module DnsOne; class DnsOne
|
29
27
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
Log.w "Cannot change working dir to #{WORK_DIR}. Will continue in #{Dir.pwd}."
|
47
|
-
end
|
28
|
+
DEFAULTS = {
|
29
|
+
conf_file: "/etc/dns_one.yml",
|
30
|
+
work_dir: "/var/local/dns_one",
|
31
|
+
log_file: "/var/log/dns_one/dns_one.log",
|
32
|
+
rubydns_log_file: "/var/log/dns_one/dns_one_rubydns.log",
|
33
|
+
run_as: "dnsone",
|
34
|
+
interfaces: [ [:udp, "0.0.0.0", 53],
|
35
|
+
[:tcp, "0.0.0.0", 53],
|
36
|
+
[:udp, "::", 5300],
|
37
|
+
[:tcp, "::", 5300]
|
38
|
+
],
|
39
|
+
log_req_socket_file: '/tmp/dns_one_log_result.sock'
|
40
|
+
}
|
41
|
+
|
42
|
+
def initialize conf_file: nil
|
43
|
+
@conf = Global.conf = load_conf(conf_file || DEFAULTS[:conf_file])
|
48
44
|
end
|
49
|
-
|
45
|
+
|
50
46
|
def start
|
51
|
-
|
47
|
+
init_loggers
|
48
|
+
chdir
|
49
|
+
Server.new.run
|
52
50
|
end
|
53
51
|
|
54
52
|
private
|
55
|
-
|
56
|
-
def parse_conf conf_file
|
57
|
-
check_conf_file conf_file
|
58
53
|
|
59
|
-
|
60
|
-
conf =
|
54
|
+
def load_conf conf_file
|
55
|
+
conf = DEFAULTS.clone
|
56
|
+
conf.merge! YAML.load_file(conf_file).symbolize_keys
|
57
|
+
Util.hash_to_ostruct_deep conf
|
61
58
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
server: {
|
67
|
-
run_as: conf[:config][:run_as],
|
68
|
-
save_stats: conf[:config][:save_stats],
|
69
|
-
log_result_file: conf[:config][:log_result_file],
|
70
|
-
log_result_socket: conf[:config][:log_result_socket],
|
71
|
-
log_result_socket_file: conf[:config][:log_result_socket_file],
|
72
|
-
},
|
73
|
-
zone_search: {
|
74
|
-
ignore_subdomains: conf[:config][:ignore_subdomains],
|
75
|
-
cache_max: conf[:config][:cache_max],
|
76
|
-
record_sets: conf[:record_sets],
|
77
|
-
backend: conf[:backend]
|
78
|
-
}
|
79
|
-
)
|
59
|
+
rescue => e
|
60
|
+
$stderr.puts e.desc
|
61
|
+
$stderr.puts "Error opening conf file #{conf_file}. Aborting."
|
62
|
+
exit 1
|
80
63
|
end
|
81
64
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
|
65
|
+
def init_loggers
|
66
|
+
Global.logger = Util.init_logger @conf.log_file , Logger::INFO
|
67
|
+
Global.ruby_dns_logger = Util.init_logger @conf.ruby_dns_logger, Logger::WARN
|
68
|
+
end
|
86
69
|
|
87
|
-
|
70
|
+
def chdir
|
71
|
+
d = @conf.work_dir
|
72
|
+
FileUtils.mkdir_p d
|
73
|
+
Dir.chdir d
|
88
74
|
|
89
|
-
|
90
|
-
|
91
|
-
|
75
|
+
rescue => e
|
76
|
+
Global.logger.error e.desc
|
77
|
+
Global.logger.error "Cannot chdir to #{@conf.work_dir}. Will continue in #{Dir.pwd}"
|
92
78
|
end
|
93
79
|
|
94
80
|
end; end
|
95
|
-
|
data/lib/dns_one/backend/db.rb
CHANGED
@@ -31,12 +31,12 @@ module DnsOne; module Backend; class DB < Base
|
|
31
31
|
record_values&.first
|
32
32
|
|
33
33
|
rescue ActiveRecord::StatementInvalid => e
|
34
|
-
|
34
|
+
Global.logger.error "SQL query error. Trying to reconnect #{tries}. Details:\n#{e.desc}"
|
35
35
|
# http://geoff.evason.name/2015/01/18/postgres-ssl-connection-has-been-closed-unexpectedly
|
36
36
|
ActiveRecord::Base.connection.reconnect!
|
37
37
|
find sql, (tries+1)
|
38
38
|
rescue => e
|
39
|
-
|
39
|
+
Global.logger.error "SQL query error. Details:\n#{e.desc}"
|
40
40
|
end
|
41
41
|
|
42
42
|
def build_query dom_name
|
@@ -46,7 +46,7 @@ module DnsOne; module Backend; class DB < Base
|
|
46
46
|
|
47
47
|
def setup_db
|
48
48
|
#require_deps
|
49
|
-
ActiveRecord::Base.logger =
|
49
|
+
ActiveRecord::Base.logger = Global.logger
|
50
50
|
ActiveRecord::Base.establish_connection @conf
|
51
51
|
end
|
52
52
|
|
data/lib/dns_one/backend/file.rb
CHANGED
@@ -24,7 +24,7 @@ module DnsOne; module Backend; class File < Base
|
|
24
24
|
if domain_name and not domain_name.empty?
|
25
25
|
@domain_map[domain_name.strip.downcase] = rec_set_name&.strip || ''
|
26
26
|
else
|
27
|
-
|
27
|
+
Global.logger.warn "Ignoring #{file} line: #{line}"
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
@@ -52,7 +52,7 @@ module DnsOne; module Backend; class HTTPBell < Base
|
|
52
52
|
.map{ |r|
|
53
53
|
id, domain = r.strip.split /\s+/
|
54
54
|
if id !~ /^\d+$/ || domain !~ Util::DOM_REGEX
|
55
|
-
|
55
|
+
Global.logger.warn "invalid line '#{r}'"
|
56
56
|
nil
|
57
57
|
else
|
58
58
|
[id.to_i, domain.downcase]
|
@@ -66,17 +66,17 @@ module DnsOne; module Backend; class HTTPBell < Base
|
|
66
66
|
case point
|
67
67
|
when :start
|
68
68
|
@log_update_t0 = Time.now
|
69
|
-
|
69
|
+
Global.logger.info "update`ing..."
|
70
70
|
when :end
|
71
71
|
dt = '%.2f' % (Time.now - @log_update_t0)
|
72
72
|
dots = '...' if recs.size > LOG_DOM_NUM
|
73
73
|
zones = recs[0, LOG_DOM_NUM].map(&:last).join(', ')
|
74
|
-
|
74
|
+
Global.logger.info "#{recs.size} zone(s) added in #{dt}s: #{zones}#{dots}"
|
75
75
|
else
|
76
|
-
|
76
|
+
Global.logger.error e.desc "Wrong param #{point} for log_update"
|
77
77
|
end
|
78
78
|
rescue => e
|
79
|
-
|
79
|
+
Global.logger.error e.desc
|
80
80
|
end
|
81
81
|
|
82
82
|
def listen_updater_bell
|
@@ -86,19 +86,19 @@ module DnsOne; module Backend; class HTTPBell < Base
|
|
86
86
|
require "socket"
|
87
87
|
dts = TCPServer.new '0.0.0.0', @conf[:http_bell_port]
|
88
88
|
allow_ips = @conf[:http_bell_allow_ips]
|
89
|
-
|
89
|
+
Global.logger.info 'Starting bell listener...'
|
90
90
|
Thread.new do
|
91
91
|
loop do
|
92
92
|
Thread.start(dts.accept) do |client|
|
93
|
-
|
93
|
+
Global.logger.info 'accepted'
|
94
94
|
numeric_address = client.peeraddr[3]
|
95
95
|
if !allow_ips || allow_ips.include?(numeric_address)
|
96
|
-
|
96
|
+
Global.logger.info 'will update'
|
97
97
|
update
|
98
98
|
else
|
99
|
-
|
99
|
+
Global.logger.warn "Ignoring bell ring from #{numeric_address}."
|
100
100
|
end
|
101
|
-
|
101
|
+
Global.logger.info 'closing connection'
|
102
102
|
client.close
|
103
103
|
end
|
104
104
|
end
|
data/lib/dns_one/cli.rb
CHANGED
@@ -0,0 +1,85 @@
|
|
1
|
+
module DnsOne; module Backend; class Account
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
@conf = Global.conf
|
5
|
+
@stat = {}
|
6
|
+
@last_stat = nil
|
7
|
+
@mutex = Mutex.new
|
8
|
+
open_socket
|
9
|
+
reap
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_response ip_address, domain_name, res_class, rcode, resp_log, from_cache
|
13
|
+
@mutex.synchronize {
|
14
|
+
@stat[:requests] ||= 0
|
15
|
+
@stat[:requests] += 1
|
16
|
+
|
17
|
+
@stat[:cache] ||= 0
|
18
|
+
@stat[:cache] += 1 if from_cache
|
19
|
+
|
20
|
+
rcode_uc = Util.const_underscore rcode
|
21
|
+
@stat[:rcode] ||= {}
|
22
|
+
@stat[:rcode][rcode_uc] ||= 0
|
23
|
+
@stat[:rcode][rcode_uc] += 1
|
24
|
+
|
25
|
+
req_resource = Util.last_mod(res_class).downcase
|
26
|
+
@stat[:req_resource] ||= {}
|
27
|
+
@stat[:req_resource][req_resource] ||= 0
|
28
|
+
@stat[:req_resource][req_resource] += 1
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def reap
|
33
|
+
Thread.new do
|
34
|
+
loop do
|
35
|
+
sleep (300 - Time.now.to_f % 300)
|
36
|
+
@mutex.synchronize {
|
37
|
+
@last_stat = @stat.deep_dup
|
38
|
+
reset @stat
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def open_socket
|
45
|
+
conf = @conf
|
46
|
+
mutex = @mutex
|
47
|
+
stat = @stat
|
48
|
+
sock = Thread.new do
|
49
|
+
sleep 1
|
50
|
+
begin
|
51
|
+
Socket.unix_server_loop(Global.conf.log_req_socket_file) do |sock, addr|
|
52
|
+
Thread.new do
|
53
|
+
loop do
|
54
|
+
begin
|
55
|
+
mutex.synchronize {
|
56
|
+
sock.write "#{ last_stat.to_json }\n"
|
57
|
+
}
|
58
|
+
rescue Errno::EPIPE => e
|
59
|
+
break
|
60
|
+
rescue => e
|
61
|
+
Global.logger.error e.desc
|
62
|
+
break
|
63
|
+
end
|
64
|
+
Thread.pass
|
65
|
+
sleep 0.1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
rescue => e
|
70
|
+
Global.logger.error e.desc
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def reset hash
|
76
|
+
hash.each_key do |k|
|
77
|
+
if hash[k].is_a? Hash
|
78
|
+
reset hash[k]
|
79
|
+
else
|
80
|
+
hash[k] = 0
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
|
-
module DnsOne; class
|
1
|
+
module DnsOne; module Backend; class DB
|
2
|
+
|
2
3
|
DB_FNAME = "stat.db"
|
3
4
|
META_STAT_ON = false
|
4
5
|
META_STAT_FILE = '/tmp/dnsone_sql_prof.log'
|
@@ -12,19 +13,19 @@ module DnsOne; class Stat
|
|
12
13
|
ensure_db
|
13
14
|
end
|
14
15
|
|
15
|
-
def
|
16
|
-
|
16
|
+
def on_response ip_address, domain_name, res_class, rcode, resp_log, from_cache
|
17
|
+
Global.logger.debug "saving stat (user: #{ `id -un #{Process.uid}`.strip })"
|
17
18
|
rsql(
|
18
19
|
"INSERT INTO responses (time, rcode, req_resource, cache) VALUES (?, ?, ?, ?)",
|
19
20
|
[
|
20
21
|
Time.now.to_i,
|
21
22
|
Resolv::DNS::RCode.const_get(rcode),
|
22
|
-
|
23
|
-
(
|
23
|
+
res_class::TypeValue,
|
24
|
+
(from_cache ? 1 : 0)
|
24
25
|
]
|
25
26
|
)
|
26
27
|
rescue => e
|
27
|
-
|
28
|
+
Global.logger.error e.desc
|
28
29
|
end
|
29
30
|
|
30
31
|
# select rcode, count(*) from responses where time > strftime('%s', 'now') - 300 group by rcode
|
@@ -120,7 +121,7 @@ module DnsOne; class Stat
|
|
120
121
|
opts = {}
|
121
122
|
opts[:readonly] = true if @conf[:readonly]
|
122
123
|
|
123
|
-
#
|
124
|
+
# Global.logger.info "Opening stat db #{db_file} (cwd: #{Dir.pwd})."
|
124
125
|
@db = SQLite3::Database.new db_file, opts
|
125
126
|
|
126
127
|
if new_db
|
@@ -165,5 +166,5 @@ module DnsOne; class Stat
|
|
165
166
|
@meta_stats_log.info "#{time} #{dur} #{sql}"
|
166
167
|
end
|
167
168
|
|
168
|
-
end; end
|
169
|
+
end; end; end
|
169
170
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module DnsOne; module Backend; class File
|
2
|
+
if @conf.req_log_file
|
3
|
+
path = @conf.req_log_file.is_a?(String) ? @conf.req_log_file :
|
4
|
+
l = Logger.new @conf.ruby_dns_logger, 10, (10 * 2**20)
|
5
|
+
l.level = Logger::WARN
|
6
|
+
Global.ruby_dns_logger = l
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.log_result ip_address, domain_name, res_class, rcode, resp_log, from_cache
|
10
|
+
fields = []
|
11
|
+
|
12
|
+
fields << domain_name
|
13
|
+
fields << Util.last_mod(res_class)
|
14
|
+
fields << rcode
|
15
|
+
fields << resp_log.map{ |rec|
|
16
|
+
Util.last_mod(rec.res_class) +
|
17
|
+
':' +
|
18
|
+
[rec.val].flatten.join(',')
|
19
|
+
}.join(';')
|
20
|
+
fields << ip_address
|
21
|
+
fields << (from_cache ? '1' : '0')
|
22
|
+
|
23
|
+
fields.map!{|v| v.blank? ? '-' : v}
|
24
|
+
|
25
|
+
Global.logger.info "result: #{ fields.join ' ' }"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module DnsOne; module ReqLog; class ReqLog
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
@conf = Global.conf
|
5
|
+
end
|
6
|
+
|
7
|
+
def on_response *args
|
8
|
+
if @conf.log_req_db
|
9
|
+
@db ||= Db.new
|
10
|
+
@db.on_response *args
|
11
|
+
end
|
12
|
+
|
13
|
+
if @conf.log_req_file
|
14
|
+
@file = File.new
|
15
|
+
@file.on_response *args
|
16
|
+
end
|
17
|
+
|
18
|
+
if @conf.log_req_account
|
19
|
+
@account ||= Account.new
|
20
|
+
@account.on_response *args
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end; end; end
|
25
|
+
|
data/lib/dns_one/server.rb
CHANGED
@@ -1,141 +1,69 @@
|
|
1
1
|
|
2
2
|
require "dns_one/zone_search"
|
3
|
+
require "dns_one/req_log/req_log"
|
3
4
|
require 'socket'
|
4
5
|
require 'json'
|
5
6
|
|
6
7
|
module DnsOne; class Server
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
DNS_DAEMON_INTERFACES = [
|
12
|
-
[:udp, "0.0.0.0", 53],
|
13
|
-
[:tcp, "0.0.0.0", 53],
|
14
|
-
[:udp, "::", 5300],
|
15
|
-
[:tcp, "::", 5300]
|
16
|
-
]
|
17
|
-
|
18
|
-
def initialize conf, conf_zone_search
|
19
|
-
@conf = conf
|
20
|
-
@zone_search = ZoneSearch.instance.setup conf_zone_search
|
21
|
-
if conf[:log_result_socket]
|
22
|
-
@log_result = {}
|
23
|
-
@log_result_mutex = Mutex.new
|
24
|
-
end
|
8
|
+
def initialize
|
9
|
+
@zone_search = ZoneSearch.instance.setup
|
10
|
+
@req_log = nil
|
25
11
|
end
|
26
12
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
stat = nil
|
31
|
-
if conf[:log_result_socket]
|
32
|
-
log_result = @log_result
|
33
|
-
log_result_mutex = @log_result_mutex
|
34
|
-
launch_log_result_socket
|
35
|
-
log_result_last_reset = 0
|
13
|
+
def start
|
14
|
+
if RExec.current_user == 'root'
|
15
|
+
RExec.change_user Global.conf.run_as
|
36
16
|
end
|
17
|
+
@req_log ||= ReqLog::ReqLog.new
|
18
|
+
Global.logger.info "Running as #{RExec.current_user}"
|
19
|
+
end
|
37
20
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
run_as = conf[:run_as] || DEFAULT_RUN_AS
|
42
|
-
stat = Stat.new user: run_as if !stat
|
43
|
-
RExec.change_user run_as
|
44
|
-
else
|
45
|
-
stat = Stat.new if !stat
|
46
|
-
end
|
47
|
-
Log.i "Running as #{RExec.current_user}"
|
48
|
-
end
|
49
|
-
|
50
|
-
match(/(.+)/) do |t| # transaction
|
51
|
-
rcode = :NoError
|
52
|
-
resp_log = []
|
53
|
-
|
54
|
-
begin
|
55
|
-
domain_name = t.question.to_s
|
56
|
-
ip_address = t.options[:peer] rescue nil
|
57
|
-
|
58
|
-
records, from_cache = zone_search.query domain_name, t.resource_class, ip_address
|
59
|
-
|
60
|
-
if records
|
61
|
-
if records.empty?
|
62
|
-
t.fail! :NoError
|
63
|
-
else
|
64
|
-
records.each do |rec|
|
65
|
-
resp_log << rec
|
66
|
-
t.respond! *[rec.val].flatten, {resource_class: rec.res_class, section: rec.section}
|
67
|
-
end
|
68
|
-
end
|
69
|
-
else
|
70
|
-
rcode = :NXDomain
|
71
|
-
t.fail! :NXDomain
|
72
|
-
end
|
73
|
-
rescue => e
|
74
|
-
rcode = :ServFail
|
75
|
-
end
|
76
|
-
|
77
|
-
begin
|
78
|
-
if conf[:save_stats]
|
79
|
-
stat.save rcode, t.resource_class, from_cache
|
80
|
-
end
|
21
|
+
def resolve transaction
|
22
|
+
rcode = :NoError
|
23
|
+
resp_log = []
|
81
24
|
|
82
|
-
|
83
|
-
|
84
|
-
|
25
|
+
begin
|
26
|
+
domain_name = transaction.question.to_s
|
27
|
+
ip_address = transaction.options[:peer] rescue nil
|
85
28
|
|
86
|
-
|
87
|
-
log_result_mutex.synchronize {
|
88
|
-
# Reset log_result every 5 min
|
89
|
-
if Time.now.to_i / 300 > log_result_last_reset / 300
|
90
|
-
log_result_last_reset = Time.now.to_i
|
91
|
-
log_result.each_key do |k|
|
92
|
-
if log_result[k].is_a? Hash
|
93
|
-
log_result[k].each_key do |k2|
|
94
|
-
log_result[k][k2] = 0
|
95
|
-
end
|
96
|
-
else
|
97
|
-
log_result[k] = 0
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
29
|
+
records, from_cache = @zone_search.query domain_name, transaction.resource_class, ip_address
|
101
30
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
log_result[:rcode] ||= {}
|
110
|
-
log_result[:rcode][rcode_uc] ||= 0
|
111
|
-
log_result[:rcode][rcode_uc] += 1
|
112
|
-
|
113
|
-
req_resource = Util.last_mod(t.resource_class).downcase
|
114
|
-
log_result[:req_resource] ||= {}
|
115
|
-
log_result[:req_resource][req_resource] ||= 0
|
116
|
-
log_result[:req_resource][req_resource] += 1
|
117
|
-
}
|
31
|
+
if records
|
32
|
+
if records.empty?
|
33
|
+
transaction.fail! :NoError
|
34
|
+
else
|
35
|
+
records.each do |rec|
|
36
|
+
resp_log << rec
|
37
|
+
transaction.respond! *[rec.val].flatten, {resource_class: rec.res_class, section: rec.section}
|
118
38
|
end
|
119
|
-
rescue => e
|
120
|
-
Log.exc e
|
121
39
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
otherwise do |t|
|
127
|
-
t.fail! :NXDomain
|
40
|
+
else
|
41
|
+
rcode = :NXDomain
|
42
|
+
transaction.fail! :NXDomain
|
128
43
|
end
|
44
|
+
rescue => e
|
45
|
+
rcode = :ServFail
|
129
46
|
end
|
47
|
+
|
48
|
+
@req_log.on_response *[
|
49
|
+
ip_address,
|
50
|
+
domain_name,
|
51
|
+
transaction.resource_class,
|
52
|
+
rcode,
|
53
|
+
resp_log,
|
54
|
+
from_cache
|
55
|
+
]
|
56
|
+
|
57
|
+
raise e if e
|
130
58
|
end
|
131
59
|
|
132
60
|
def dns_daemon_interfaces
|
133
61
|
if RExec.current_user == 'root'
|
134
|
-
|
62
|
+
Global.conf.interfaces
|
135
63
|
else
|
136
|
-
ports =
|
64
|
+
ports = Global.conf.interfaces.map do |port|
|
137
65
|
if port[2] <= 1024
|
138
|
-
|
66
|
+
Global.logger.warn "Changing listening port #{port.join ':'} to #{port[2] + 10000} for non-root process."
|
139
67
|
port[2] += 10000
|
140
68
|
end
|
141
69
|
port
|
@@ -144,34 +72,20 @@ module DnsOne; class Server
|
|
144
72
|
end
|
145
73
|
end
|
146
74
|
|
147
|
-
def
|
148
|
-
|
149
|
-
log_result_mutex = @log_result_mutex
|
150
|
-
conf = @conf
|
75
|
+
def run
|
76
|
+
srv = self
|
151
77
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
break
|
164
|
-
rescue => e
|
165
|
-
Log.exc e
|
166
|
-
break
|
167
|
-
end
|
168
|
-
Thread.pass
|
169
|
-
sleep 0.1
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
rescue => e
|
174
|
-
Log.exc e
|
78
|
+
RubyDNS::run_server(dns_daemon_interfaces) do
|
79
|
+
on(:start) do
|
80
|
+
srv.start
|
81
|
+
end
|
82
|
+
|
83
|
+
match(/(.+)/) do |transaction|
|
84
|
+
srv.resolve transaction
|
85
|
+
end
|
86
|
+
|
87
|
+
otherwise do |transaction|
|
88
|
+
transaction.fail! :NXDomain
|
175
89
|
end
|
176
90
|
end
|
177
91
|
end
|
data/lib/dns_one/util.rb
CHANGED
@@ -4,7 +4,7 @@ module DnsOne; class Util
|
|
4
4
|
|
5
5
|
class << self
|
6
6
|
def die msg
|
7
|
-
|
7
|
+
Global.logger.fatal msg
|
8
8
|
exit 1
|
9
9
|
end
|
10
10
|
|
@@ -33,25 +33,6 @@ module DnsOne; class Util
|
|
33
33
|
constant.to_s.split('::').last
|
34
34
|
end
|
35
35
|
|
36
|
-
def log_result ip_address, domain_name, res_class, rcode, resp_log, from_cache
|
37
|
-
fields = []
|
38
|
-
|
39
|
-
fields << domain_name
|
40
|
-
fields << Util.last_mod(res_class)
|
41
|
-
fields << rcode
|
42
|
-
fields << resp_log.map{ |rec|
|
43
|
-
Util.last_mod(rec.res_class) +
|
44
|
-
':' +
|
45
|
-
[rec.val].flatten.join(',')
|
46
|
-
}.join(';')
|
47
|
-
fields << ip_address
|
48
|
-
fields << (from_cache ? '1' : '0')
|
49
|
-
|
50
|
-
fields.map!{|v| v.blank? ? '-' : v}
|
51
|
-
|
52
|
-
Log.i "result: #{ fields.join ' ' }"
|
53
|
-
end
|
54
|
-
|
55
36
|
def const_underscore name
|
56
37
|
name = name.to_s.dup
|
57
38
|
name.gsub!('::', '/')
|
@@ -62,6 +43,33 @@ module DnsOne; class Util
|
|
62
43
|
name
|
63
44
|
end
|
64
45
|
|
46
|
+
def hash_to_ostruct_deep hash
|
47
|
+
os = OpenStruct.new
|
48
|
+
hash.each_pair{ |k, v|
|
49
|
+
if v.is_a? Hash
|
50
|
+
os[k] = hash_to_ostruct_deep v
|
51
|
+
else
|
52
|
+
os[k] = v
|
53
|
+
end
|
54
|
+
}
|
55
|
+
os
|
56
|
+
end
|
57
|
+
|
58
|
+
def init_logger logdev, level = Logger::WARN, shift_age = 10, shift_size = 2**20
|
59
|
+
if logdev.is_a? String
|
60
|
+
begin
|
61
|
+
FileUtils.mkdir_p File.dirname(logdev)
|
62
|
+
File.write logdev, ''
|
63
|
+
rescue => e
|
64
|
+
$stderr.puts "#{e.desc}\nCannot open log file #{logdev}. Will use STDOUT."
|
65
|
+
logdev = $stdout
|
66
|
+
end
|
67
|
+
end
|
68
|
+
l = Logger.new logdev, shift_age, shift_size
|
69
|
+
l.level = level
|
70
|
+
l
|
71
|
+
end
|
72
|
+
|
65
73
|
end
|
66
74
|
|
67
75
|
end; end
|
data/lib/dns_one/version.rb
CHANGED
data/lib/dns_one/zone_search.rb
CHANGED
@@ -11,11 +11,11 @@ module DnsOne; class ZoneSearch
|
|
11
11
|
Name = Resolv::DNS::Name
|
12
12
|
IN = Resolv::DNS::Resource::IN
|
13
13
|
|
14
|
-
def setup
|
15
|
-
@conf = conf
|
14
|
+
def setup
|
15
|
+
@conf = Global.conf
|
16
16
|
check_record_sets
|
17
17
|
@backend = set_backend
|
18
|
-
@cache = Cache.new
|
18
|
+
@cache = Cache.new Global.conf.cache_max
|
19
19
|
@ignore_subdomains_re = build_ignore_subdomains_re
|
20
20
|
|
21
21
|
if @backend.preload_dummy?
|
@@ -30,25 +30,25 @@ module DnsOne; class ZoneSearch
|
|
30
30
|
|
31
31
|
dom_name = dom_name.dup
|
32
32
|
res_class_short = Util.last_mod res_class # :A, :NS, found in conf.yml:record_sets items
|
33
|
-
|
33
|
+
Global.logger.debug "request #{ dom_name }/#{res_class_short} from #{ip_address}..."
|
34
34
|
|
35
35
|
records = []
|
36
36
|
|
37
37
|
rec_set_name, from_cache = find_record_set dom_name
|
38
|
-
|
38
|
+
Global.logger.debug "domain #{ rec_set_name ? "found, rec_set_name = '#{rec_set_name}'" : 'not found' }"
|
39
39
|
return unless rec_set_name
|
40
40
|
|
41
41
|
# use first record set if rec_set_name == ''
|
42
|
-
rec_set_name = @conf
|
42
|
+
rec_set_name = @conf.record_sets.to_h.keys.first if rec_set_name == ''
|
43
43
|
|
44
|
-
rec_set = @conf
|
45
|
-
|
44
|
+
rec_set = @conf.record_sets[rec_set_name]
|
45
|
+
Global.logger.debug "record set #{ rec_set ? 'found' : 'not found' }"
|
46
46
|
return records unless rec_set
|
47
47
|
|
48
48
|
# TODO: move parsing logic to own class
|
49
49
|
|
50
50
|
recs = rec_set[res_class_short.to_sym]
|
51
|
-
|
51
|
+
Global.logger.debug "record(s) #{ recs ? 'found' : 'not found' }"
|
52
52
|
|
53
53
|
# Loop over 1 or more
|
54
54
|
recs = [recs]
|
@@ -72,25 +72,25 @@ module DnsOne; class ZoneSearch
|
|
72
72
|
private
|
73
73
|
|
74
74
|
def build_ignore_subdomains_re
|
75
|
-
if i = @conf
|
75
|
+
if i = @conf.ignore_subdomains.presence
|
76
76
|
s = i.strip.split(/\s+/).map(&:downcase).join '|'
|
77
77
|
/^(#{ s })\./i
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
81
|
def set_backend
|
82
|
-
if file = @conf
|
82
|
+
if file = @conf.backend.file
|
83
83
|
unless ::File.exists? file
|
84
|
-
Util.die "Domain list file #{file} not found."
|
84
|
+
Util.die "Domain list file #{file} not found (pwd = #{Dir.pwd})."
|
85
85
|
end
|
86
86
|
Backend::File.new file
|
87
|
-
elsif @conf
|
88
|
-
unless @conf
|
87
|
+
elsif @conf.backend.http_bell_url
|
88
|
+
unless @conf.backend.http_bell_record_set
|
89
89
|
Util.die "backend.http_bell_record_set not set."
|
90
90
|
end
|
91
|
-
Backend::HTTPBell.new @conf
|
91
|
+
Backend::HTTPBell.new @conf.backend
|
92
92
|
else
|
93
|
-
Backend::DB.new @conf
|
93
|
+
Backend::DB.new @conf.backend
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
@@ -101,7 +101,7 @@ module DnsOne; class ZoneSearch
|
|
101
101
|
enabled_cache = use_cache && @backend.allow_cache
|
102
102
|
|
103
103
|
if enabled_cache and rec_set = @cache.find(dom_name)
|
104
|
-
|
104
|
+
Global.logger.debug "found in cache (#{@cache.stat})"
|
105
105
|
[rec_set, true]
|
106
106
|
else
|
107
107
|
if rec_set = @backend.find(dom_name)
|
@@ -130,11 +130,11 @@ module DnsOne; class ZoneSearch
|
|
130
130
|
end
|
131
131
|
|
132
132
|
def check_record_sets
|
133
|
-
if @conf
|
133
|
+
if @conf.record_sets.blank?
|
134
134
|
Util.die "Record sets cannot be empty. Check file."
|
135
135
|
end
|
136
136
|
|
137
|
-
@conf
|
137
|
+
@conf.record_sets.each_pair do |rec_set_name, records|
|
138
138
|
unless records[:NS] and records[:NS].length >= 1
|
139
139
|
Util.die "Record set #{rec_set_name} is invalid. It must have at least 1 NS record."
|
140
140
|
end
|
data/util/dns_one.service
CHANGED
data/util/sample_conf.yml
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# log_result_socket_file: /tmp/dns_one_log_result.sock
|
2
|
+
run_as: dnsserver # optional, but highly recommended! adduser --system dnsserver
|
3
|
+
ignore_subdomains: www en it es pt ru fr at # optional, defaults to an empty list
|
4
|
+
# cache_max: 100000 # optional, defaults to 10000
|
5
|
+
# log_file: /var/log/dns_one.log # optional, defaults to /var/log/dns_one.log
|
6
|
+
log_req_db: false
|
7
|
+
log_req_file: false
|
8
|
+
log_req_account: true
|
9
|
+
# log_req_socket_file: /tmp/socket.socket
|
11
10
|
|
12
11
|
backend:
|
13
12
|
##############
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dns_one
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Lobato
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-05-
|
11
|
+
date: 2018-05-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 2.0.1
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 2.0.1
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rexec
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -170,10 +170,13 @@ files:
|
|
170
170
|
- lib/dns_one/core_ext/exception.rb
|
171
171
|
- lib/dns_one/core_ext/hash.rb
|
172
172
|
- lib/dns_one/core_ext/string.rb
|
173
|
-
- lib/dns_one/
|
173
|
+
- lib/dns_one/global.rb
|
174
|
+
- lib/dns_one/req_log/account.rb
|
175
|
+
- lib/dns_one/req_log/db.rb
|
176
|
+
- lib/dns_one/req_log/file.rb
|
177
|
+
- lib/dns_one/req_log/req_log.rb
|
174
178
|
- lib/dns_one/server.rb
|
175
179
|
- lib/dns_one/setup.rb
|
176
|
-
- lib/dns_one/stat.rb
|
177
180
|
- lib/dns_one/util.rb
|
178
181
|
- lib/dns_one/version.rb
|
179
182
|
- lib/dns_one/zone_search.rb
|
@@ -200,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
200
203
|
version: '0'
|
201
204
|
requirements: []
|
202
205
|
rubyforge_project:
|
203
|
-
rubygems_version: 2.
|
206
|
+
rubygems_version: 2.5.1
|
204
207
|
signing_key:
|
205
208
|
specification_version: 4
|
206
209
|
summary: DNS server for many zones sharing only one or few records, written in Ruby.
|
data/lib/dns_one/log.rb
DELETED
@@ -1,72 +0,0 @@
|
|
1
|
-
|
2
|
-
class Log < Logger
|
3
|
-
SYSLOG_MIN_SEVERITY = Logger::WARN
|
4
|
-
|
5
|
-
class << self
|
6
|
-
|
7
|
-
# 'def [d|i|w|e|f] msg' for DEBUG INFO WARN ERROR FATAL
|
8
|
-
Logger::Severity::constants.each do |severity|
|
9
|
-
method_name = severity.to_s[0].downcase
|
10
|
-
define_method(method_name) do |msg|
|
11
|
-
log severity, msg
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def setup
|
16
|
-
@syslog = Syslog::Logger.new "dns_one"
|
17
|
-
@log_file = setfile "/var/log/dns_one.log"
|
18
|
-
@logger = Logger.new @log_file
|
19
|
-
@logger.level = Logger::INFO
|
20
|
-
end
|
21
|
-
|
22
|
-
def ruby_dns_logger
|
23
|
-
l = Logger.new setfile("/var/log/dns_one_rubydns.log")
|
24
|
-
l.level = Logger::WARN
|
25
|
-
l
|
26
|
-
end
|
27
|
-
|
28
|
-
def exc exception
|
29
|
-
e exception.desc
|
30
|
-
end
|
31
|
-
|
32
|
-
def logger
|
33
|
-
@logger
|
34
|
-
end
|
35
|
-
|
36
|
-
def log_file_desc
|
37
|
-
case @log_file
|
38
|
-
when STDOUT
|
39
|
-
'STDOUT'
|
40
|
-
when STDERR
|
41
|
-
'STDERR'
|
42
|
-
else
|
43
|
-
@log_file
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def setfile file
|
50
|
-
if File.writable?(file) or File.writable?(File.dirname(file))
|
51
|
-
file
|
52
|
-
else
|
53
|
-
STDERR
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def log severity, msg
|
58
|
-
met_name = severity.downcase
|
59
|
-
|
60
|
-
@logger.send met_name, msg
|
61
|
-
|
62
|
-
if sev_num(severity) >= SYSLOG_MIN_SEVERITY
|
63
|
-
@syslog.send met_name, msg
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def sev_num sev
|
68
|
-
Object.const_get "Logger::#{sev}"
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|