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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +3 -0
  4. data/.rubocop-https---relaxed-ruby-style-rubocop-yml +166 -0
  5. data/.rubocop.yml +20 -0
  6. data/.travis.yml +18 -0
  7. data/DEVELOPMENT.md +5 -0
  8. data/Gemfile +8 -0
  9. data/License.txt +373 -0
  10. data/README.md +473 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/matchd +4 -0
  14. data/bin/rake +29 -0
  15. data/bin/rspec +29 -0
  16. data/bin/setup +8 -0
  17. data/examples/registry.yml +30 -0
  18. data/exe/matchd +6 -0
  19. data/lib/matchd.rb +31 -0
  20. data/lib/matchd/cli.rb +10 -0
  21. data/lib/matchd/cli/config.rb +36 -0
  22. data/lib/matchd/cli/config_file_option.rb +23 -0
  23. data/lib/matchd/cli/main.rb +45 -0
  24. data/lib/matchd/config.rb +39 -0
  25. data/lib/matchd/control.rb +56 -0
  26. data/lib/matchd/glue.rb +6 -0
  27. data/lib/matchd/glue/async_endpoint.rb +93 -0
  28. data/lib/matchd/helpers.rb +14 -0
  29. data/lib/matchd/registry.rb +66 -0
  30. data/lib/matchd/response.rb +72 -0
  31. data/lib/matchd/response/a.rb +12 -0
  32. data/lib/matchd/response/aaaa.rb +5 -0
  33. data/lib/matchd/response/cname.rb +12 -0
  34. data/lib/matchd/response/mx.rb +16 -0
  35. data/lib/matchd/response/ns.rb +12 -0
  36. data/lib/matchd/response/ptr.rb +12 -0
  37. data/lib/matchd/response/soa.rb +28 -0
  38. data/lib/matchd/response/srv.rb +20 -0
  39. data/lib/matchd/response/txt.rb +12 -0
  40. data/lib/matchd/rule.rb +116 -0
  41. data/lib/matchd/rule/append.rb +23 -0
  42. data/lib/matchd/rule/fail.rb +18 -0
  43. data/lib/matchd/rule/invalid.rb +24 -0
  44. data/lib/matchd/rule/passthrough.rb +50 -0
  45. data/lib/matchd/rule/respond.rb +16 -0
  46. data/lib/matchd/server.rb +44 -0
  47. data/lib/matchd/version.rb +3 -0
  48. data/matchd.gemspec +31 -0
  49. 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
@@ -0,0 +1,3 @@
1
+ module Matchd
2
+ VERSION = "0.1.0".freeze
3
+ end
@@ -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: []