matchd 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop-https---relaxed-ruby-style-rubocop-yml +166 -0
- data/.rubocop.yml +20 -0
- data/.travis.yml +18 -0
- data/DEVELOPMENT.md +5 -0
- data/Gemfile +8 -0
- data/License.txt +373 -0
- data/README.md +473 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/matchd +4 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/setup +8 -0
- data/examples/registry.yml +30 -0
- data/exe/matchd +6 -0
- data/lib/matchd.rb +31 -0
- data/lib/matchd/cli.rb +10 -0
- data/lib/matchd/cli/config.rb +36 -0
- data/lib/matchd/cli/config_file_option.rb +23 -0
- data/lib/matchd/cli/main.rb +45 -0
- data/lib/matchd/config.rb +39 -0
- data/lib/matchd/control.rb +56 -0
- data/lib/matchd/glue.rb +6 -0
- data/lib/matchd/glue/async_endpoint.rb +93 -0
- data/lib/matchd/helpers.rb +14 -0
- data/lib/matchd/registry.rb +66 -0
- data/lib/matchd/response.rb +72 -0
- data/lib/matchd/response/a.rb +12 -0
- data/lib/matchd/response/aaaa.rb +5 -0
- data/lib/matchd/response/cname.rb +12 -0
- data/lib/matchd/response/mx.rb +16 -0
- data/lib/matchd/response/ns.rb +12 -0
- data/lib/matchd/response/ptr.rb +12 -0
- data/lib/matchd/response/soa.rb +28 -0
- data/lib/matchd/response/srv.rb +20 -0
- data/lib/matchd/response/txt.rb +12 -0
- data/lib/matchd/rule.rb +116 -0
- data/lib/matchd/rule/append.rb +23 -0
- data/lib/matchd/rule/fail.rb +18 -0
- data/lib/matchd/rule/invalid.rb +24 -0
- data/lib/matchd/rule/passthrough.rb +50 -0
- data/lib/matchd/rule/respond.rb +16 -0
- data/lib/matchd/server.rb +44 -0
- data/lib/matchd/version.rb +3 -0
- data/matchd.gemspec +31 -0
- metadata +191 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
# Placeholder class for marking keeping invalid Rules around for later
|
2
|
+
# inspection.
|
3
|
+
# It overwrited the entire public interface of {Matchd::Rule} to no-operations
|
4
|
+
class Matchd::Rule::Invalid < Matchd::Rule
|
5
|
+
def initialize(options)
|
6
|
+
@raw = options
|
7
|
+
end
|
8
|
+
attr_reader :raw
|
9
|
+
|
10
|
+
# Noop
|
11
|
+
# @return [FalseClass] Always returns `false` indecating that processing
|
12
|
+
# shall not stop.
|
13
|
+
def visit(*); false end
|
14
|
+
|
15
|
+
# Noop
|
16
|
+
# @return [FalseClass] Always returns `false` indecating that this rule
|
17
|
+
# shall not be executed.
|
18
|
+
def matches?(*); false end
|
19
|
+
|
20
|
+
# Noop
|
21
|
+
# @return [FalseClass] Always returns `false` indecating that processing
|
22
|
+
# shall not stop.
|
23
|
+
def call(*); false end
|
24
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "async/dns"
|
2
|
+
require "async/dns/system"
|
3
|
+
|
4
|
+
class Matchd::Rule::Passthrough < Matchd::Rule
|
5
|
+
# @param [Hash] options
|
6
|
+
# @option options [Array<Array>|Array<Hash>|Async::DNS::Resolver] "resolver" List of passthrough DNS servers. See `Matchd::Glue::AsyncEndpoint` for more formats.
|
7
|
+
def initialize(options)
|
8
|
+
super
|
9
|
+
opts = options.fetch("passthrough")
|
10
|
+
|
11
|
+
if opts.is_a?(Hash) && opts.key?("resolver")
|
12
|
+
@resolver = opts["resolver"]
|
13
|
+
@passthrough_options = Matchd::Helpers.extract_options(%w(force name), opts)
|
14
|
+
else
|
15
|
+
@resolver = opts
|
16
|
+
@passthrough_options = {}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :resolver, :passthrough_options
|
21
|
+
|
22
|
+
def visit!(server, _name, _resource_class, transaction)
|
23
|
+
transaction.passthrough!(passthrough_resolver, passthrough_options) do |response|
|
24
|
+
server.logger.debug ";; Passthrough to upstream resolver"
|
25
|
+
server.logger.debug ";; Question"
|
26
|
+
server.logger.debug(*response.question.map { |q, r| "#{q}\t#{r}" })
|
27
|
+
server.logger.debug ";; Answer"
|
28
|
+
if response.answer.any?
|
29
|
+
response.answer.each do |name_in_answer, ttl, record|
|
30
|
+
server.logger.debug "#{name_in_answer}\t#{ttl}\t#{record.class}\t#{record.address if record.respond_to?(:address)}"
|
31
|
+
end
|
32
|
+
else
|
33
|
+
server.logger.debug ";; Empty"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# @private
|
39
|
+
# Lazy convert the {resolver} into somethinge we can pass to {Async::DNS::Transaction#passthrough!}
|
40
|
+
def passthrough_resolver
|
41
|
+
case resolver
|
42
|
+
when Async::DNS::Resolver
|
43
|
+
resolver
|
44
|
+
when "system", :system, nil
|
45
|
+
Async::DNS::Resolver.new(Async::DNS::System.nameservers)
|
46
|
+
else
|
47
|
+
Async::DNS::Resolver.new(Matchd::Glue::AsyncEndpoint.parse(resolver))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Matchd::Rule::Respond < Matchd::Rule
|
2
|
+
def initialize(options)
|
3
|
+
super
|
4
|
+
@responses = options.fetch("respond")
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :responses
|
8
|
+
|
9
|
+
def visit!(_server, _name, _resource_class, transaction)
|
10
|
+
# Using the original Rule's resource_classes definition as a fallback, if
|
11
|
+
# the response doesn't configure one.
|
12
|
+
Matchd.Response(responses, @resource_classes).each do |resp|
|
13
|
+
resp.call(transaction)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "async/dns"
|
2
|
+
|
3
|
+
module Matchd
|
4
|
+
# Specific implementation of the {Async::DNS::Server} using a list of rules to determine it's response
|
5
|
+
#
|
6
|
+
# Rules will get {#call}'ed in order until the first one matches (returns truthy).
|
7
|
+
# If none matches, a default {Matchd::Rule::Passthrough} will get executed forwarding the query to the resolver
|
8
|
+
# defined via the `:resolver` option.
|
9
|
+
class Server < Async::DNS::Server
|
10
|
+
# @param rules [Matchd::Registry|Array<Matchd.Rule>] A rules registry
|
11
|
+
# @param listen [Array] On which interfaces to listen (`[[<protocol>, <ip>, <port>], ...]`). See `Matchd::Glue::AsyncEndpoint` for more formats.
|
12
|
+
# @param [Hash] options
|
13
|
+
# @option options [Array|Async::DNS::Resolver] :resolver The upstream resolvers (`[[<protocol>, <ip>, <port>], ...]`). See `Matchd::Glue::AsyncEndpoint` for more formats. (default: `Async::DNS::System.nameservers`)
|
14
|
+
# @option options [TrueClass|FalseClass] :forced_passthtough The default Passthrough rule's `force` option
|
15
|
+
def initialize(rules, listen, options = {})
|
16
|
+
@rules = rules
|
17
|
+
@resolver = options.delete(:resolver)
|
18
|
+
@forced_passthtough = options.delete(:forced_passthtough) { true }
|
19
|
+
|
20
|
+
listen = Matchd::Glue::AsyncEndpoint.parse(listen)
|
21
|
+
|
22
|
+
super(listen, *options)
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :rules, :resolver
|
26
|
+
|
27
|
+
def process(name, resource_class, transaction)
|
28
|
+
found = rules.any? do |rule|
|
29
|
+
rule.call(self, name, resource_class, transaction)
|
30
|
+
end
|
31
|
+
|
32
|
+
passthrough!(name, resource_class, transaction) unless found
|
33
|
+
end
|
34
|
+
|
35
|
+
def passthrough!(name, resource_class, transaction)
|
36
|
+
Matchd::Rule::Passthrough.new(
|
37
|
+
"passthrough" => resolver,
|
38
|
+
"match" => name,
|
39
|
+
"resource_class" => resource_class,
|
40
|
+
"force" => @forced_passthtough
|
41
|
+
).visit!(self, name, resource_class, transaction)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/matchd.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path('lib/matchd/version', __dir__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "matchd"
|
5
|
+
spec.version = Matchd::VERSION
|
6
|
+
spec.authors = ["Robert Schulze"]
|
7
|
+
spec.email = ["robert@dotless.de"]
|
8
|
+
spec.license = "MLP-2.0"
|
9
|
+
|
10
|
+
spec.summary = 'A Async::DNS daemon with expanded yaml configuration.'
|
11
|
+
spec.description = <<-DESC
|
12
|
+
Let's you use Async::DNS as a server daemon and configure it using yaml files. No writing ruby code required.
|
13
|
+
DESC
|
14
|
+
spec.homepage = "https://github.com/fnordfish/matchd"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
18
|
+
end
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_runtime_dependency "async-dns", "~> 1.2.0"
|
24
|
+
spec.add_runtime_dependency "daemons"
|
25
|
+
spec.add_runtime_dependency "dry-configurable", "~> 0.7.0"
|
26
|
+
spec.add_runtime_dependency "thor", "~> 0.20.0"
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
29
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
30
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: matchd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert Schulze
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-08-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: async-dns
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: daemons
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dry-configurable
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.7.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.7.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: thor
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.20.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.20.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.16'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.16'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '10.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '10.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.0'
|
111
|
+
description: " Let's you use Async::DNS as a server daemon and configure it using
|
112
|
+
yaml files. No writing ruby code required.\n"
|
113
|
+
email:
|
114
|
+
- robert@dotless.de
|
115
|
+
executables:
|
116
|
+
- matchd
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- ".gitignore"
|
121
|
+
- ".rspec"
|
122
|
+
- ".rubocop-https---relaxed-ruby-style-rubocop-yml"
|
123
|
+
- ".rubocop.yml"
|
124
|
+
- ".travis.yml"
|
125
|
+
- DEVELOPMENT.md
|
126
|
+
- Gemfile
|
127
|
+
- License.txt
|
128
|
+
- README.md
|
129
|
+
- Rakefile
|
130
|
+
- bin/console
|
131
|
+
- bin/matchd
|
132
|
+
- bin/rake
|
133
|
+
- bin/rspec
|
134
|
+
- bin/setup
|
135
|
+
- examples/registry.yml
|
136
|
+
- exe/matchd
|
137
|
+
- lib/matchd.rb
|
138
|
+
- lib/matchd/cli.rb
|
139
|
+
- lib/matchd/cli/config.rb
|
140
|
+
- lib/matchd/cli/config_file_option.rb
|
141
|
+
- lib/matchd/cli/main.rb
|
142
|
+
- lib/matchd/config.rb
|
143
|
+
- lib/matchd/control.rb
|
144
|
+
- lib/matchd/glue.rb
|
145
|
+
- lib/matchd/glue/async_endpoint.rb
|
146
|
+
- lib/matchd/helpers.rb
|
147
|
+
- lib/matchd/registry.rb
|
148
|
+
- lib/matchd/response.rb
|
149
|
+
- lib/matchd/response/a.rb
|
150
|
+
- lib/matchd/response/aaaa.rb
|
151
|
+
- lib/matchd/response/cname.rb
|
152
|
+
- lib/matchd/response/mx.rb
|
153
|
+
- lib/matchd/response/ns.rb
|
154
|
+
- lib/matchd/response/ptr.rb
|
155
|
+
- lib/matchd/response/soa.rb
|
156
|
+
- lib/matchd/response/srv.rb
|
157
|
+
- lib/matchd/response/txt.rb
|
158
|
+
- lib/matchd/rule.rb
|
159
|
+
- lib/matchd/rule/append.rb
|
160
|
+
- lib/matchd/rule/fail.rb
|
161
|
+
- lib/matchd/rule/invalid.rb
|
162
|
+
- lib/matchd/rule/passthrough.rb
|
163
|
+
- lib/matchd/rule/respond.rb
|
164
|
+
- lib/matchd/server.rb
|
165
|
+
- lib/matchd/version.rb
|
166
|
+
- matchd.gemspec
|
167
|
+
homepage: https://github.com/fnordfish/matchd
|
168
|
+
licenses:
|
169
|
+
- MLP-2.0
|
170
|
+
metadata: {}
|
171
|
+
post_install_message:
|
172
|
+
rdoc_options: []
|
173
|
+
require_paths:
|
174
|
+
- lib
|
175
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
|
+
requirements:
|
182
|
+
- - ">="
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
version: '0'
|
185
|
+
requirements: []
|
186
|
+
rubyforge_project:
|
187
|
+
rubygems_version: 2.7.6
|
188
|
+
signing_key:
|
189
|
+
specification_version: 4
|
190
|
+
summary: A Async::DNS daemon with expanded yaml configuration.
|
191
|
+
test_files: []
|