ronin-dns-proxy 0.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 57b933bc2cd538d5f366174c7916299fe16b1ab737badc4390895ba9565a8e3f
4
+ data.tar.gz: 1cb999659c757a4485f66c0bbcc6d18466b74fea15d2c122e7295fa8d5614fdc
5
+ SHA512:
6
+ metadata.gz: caf2a62c37b77fae8f16278ce797bfb4355c373d316ca57f56e6e28fd3656f2e30b807241c5c6cd3eda487bcf451efa3ceb031a320f8e709f9412c5495fb7512
7
+ data.tar.gz: 2e1596eac004e8f7a9cbd30fba5e88f47fcac56a8418e7225b923e45c210e428680898cdf0065ed000862aa4cfb4f7449766e61ab38635c5f71ae161bd2b901b
data/.document ADDED
@@ -0,0 +1,4 @@
1
+ lib/**/*.rb
2
+ -
3
+ ChangeLog.md
4
+ COPYING.txt
@@ -0,0 +1,46 @@
1
+ name: CI
2
+
3
+ on: [ push, pull_request ]
4
+
5
+ jobs:
6
+ tests:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby:
12
+ - '3.0'
13
+ - '3.1'
14
+ - '3.2'
15
+ - '3.3'
16
+ - truffleruby
17
+ name: Ruby ${{ matrix.ruby }}
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - name: Set up Ruby
21
+ uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: ${{ matrix.ruby }}
24
+ bundler-cache: true
25
+ # - name: Install dependencies
26
+ # run: |
27
+ # sudo apt update -y && \
28
+ # sudo apt install -y --no-install-recommends --no-install-suggests FIXME
29
+ - name: Install dependencies
30
+ run: bundle install --jobs 4 --retry 3
31
+ - name: Run tests
32
+ run: bundle exec rake test
33
+
34
+ # rubocop linting
35
+ rubocop:
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+ - name: Set up Ruby
40
+ uses: ruby/setup-ruby@v1
41
+ with:
42
+ ruby-version: 3.0
43
+ - name: Install dependencies
44
+ run: bundle install --jobs 4 --retry 3
45
+ - name: Run rubocop
46
+ run: bundle exec rubocop --parallel
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /coverage
2
+ /doc
3
+ /pkg
4
+ /man/*.[1-9]
5
+ /vendor/bundle
6
+ /Gemfile.lock
7
+ /.bundle
8
+ /.yardoc
9
+ .DS_Store
10
+ *.db
11
+ *.log
12
+ *.swp
13
+ *~
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ SuggestExtensions: false
4
+ TargetRubyVersion: 3.1
5
+
6
+ inherit_gem:
7
+ rubocop-ronin: rubocop.yml
8
+
9
+ #
10
+ # ronin-dns-proxy specific exceptions
11
+ #
12
+ Style/CaseEquality:
13
+ Exclude:
14
+ - 'lib/ronin/dns/proxy/rule.rb'
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-3.1
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown --title 'Ronin::DNS::Proxy Documentation' --protected
data/COPYING.txt ADDED
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
data/ChangeLog.md ADDED
@@ -0,0 +1,8 @@
1
+ ### 0.1.0 / 2024-XX-XX
2
+
3
+ * Initial release:
4
+ * Supports returning spoofed results to specific DNS queries.
5
+ * Supports matching queries with regular expressions.
6
+ * Supports dynamic DNS server rules.
7
+ * Passing through all other DNS queries.
8
+
data/Gemfile ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ # Ronin dependencies
8
+ # gem 'ronin-support', '~> 1.0', github: "ronin-rb/ronin-support",
9
+ # branch: '1.0.0'
10
+ # gem 'ronin-core', '~> 0.1', github: "ronin-rb/ronin-core",
11
+ # branch: 'main'
12
+
13
+ group :development do
14
+ gem 'rake'
15
+ gem 'rubygems-tasks', '~> 0.2'
16
+
17
+ gem 'rspec', '~> 3.0'
18
+ gem 'simplecov', '~> 0.20'
19
+
20
+ gem 'kramdown', '~> 2.0'
21
+ gem 'kramdown-man', '~> 0.1'
22
+
23
+ gem 'redcarpet', platform: :mri
24
+ gem 'yard', '~> 0.9'
25
+ gem 'yard-spellcheck', require: false
26
+
27
+ gem 'dead_end', require: false
28
+ gem 'sord', require: false, platform: :mri
29
+ gem 'stackprof', require: false, platform: :mri
30
+ gem 'rubocop', require: false, platform: :mri
31
+ gem 'rubocop-ronin', require: false, platform: :mri
32
+ end
data/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # ronin-dns-proxy
2
+
3
+ [![CI](https://github.com/ronin-rb/ronin-dns-proxy/actions/workflows/ruby.yml/badge.svg)](https://github.com/ronin-rb/ronin-dns-proxy/actions/workflows/ruby.yml)
4
+ [![Code Climate](https://codeclimate.com/github/ronin-rb/ronin-dns-proxy.svg)](https://codeclimate.com/github/ronin-rb/ronin-dns-proxy)
5
+
6
+ * [Website](https://ronin-rb.dev/)
7
+ * [Source](https://github.com/ronin-rb/ronin-dns-proxy)
8
+ * [Issues](https://github.com/ronin-rb/ronin-dns-proxy/issues)
9
+ * [Documentation](https://ronin-rb.dev/docs/ronin-dns-proxy)
10
+ * [Discord](https://discord.gg/6WAb3PsVX9) |
11
+ [Mastodon](https://infosec.exchange/@ronin_rb)
12
+
13
+ ## Description
14
+
15
+ ronin-dns-proxy is a configurable DNS proxy server library. It supports
16
+ returning spoofing DNS results or passing DNS queries through to the upstream
17
+ DNS nameserver.
18
+
19
+ ## Features
20
+
21
+ * Supports returning spoofed results to specific DNS queries.
22
+ * Supports matching queries with regular expressions.
23
+ * Supports dynamic DNS server rules.
24
+ * Passing through all other DNS queries.
25
+ * Has 95% documentation coverage.
26
+ * Has 100% test coverage.
27
+
28
+ ## Examples
29
+
30
+ ```ruby
31
+ require 'ronin/dns/proxy'
32
+
33
+ Ronin::DNS::Proxy.run('127.0.0.1', 2346) do |server|
34
+ server.rule :A, 'example.com', '10.0.0.1'
35
+ server.rule :AAAA, 'example.com', 'dead:beef::1'
36
+
37
+ # return multiple values
38
+ server.rule :A, 'ftp.example.com', ['10.0.0.42', '10.0.0.43']
39
+
40
+ # match a query using a regex
41
+ server.rule :TXT, /^spf\./, "v=spf1 include:10.0.0.1 ~all"
42
+
43
+ # return an error for a valid hostname
44
+ server.rule :A, 'updates.example.com', :ServFail
45
+
46
+ # define a dynamic rule
47
+ server.rule(:CNAME, /^www\./) do |type,name,transaction|
48
+ # append '.hax' to the domain name
49
+ names = name.split('.').push('hax')
50
+
51
+ transaction.respond!(names)
52
+ end
53
+
54
+ # return MX records
55
+ server.rule(:MX, 'example.com') do |type,name,transaction|
56
+ transaction.respond!(10, Resolv::DNS::Name.create('email.evil.com' ))
57
+ end
58
+ end
59
+ ```
60
+
61
+ Then try running `host -p 2346 example.com 127.0.0.1` once the server is
62
+ running.
63
+
64
+ ## Requirements
65
+
66
+ * [Ruby] >= 3.0.0
67
+ * [async-dns] ~> 1.0
68
+ * [ronin-support] ~> 1.0
69
+
70
+ ## Install
71
+
72
+ ```shell
73
+ $ gem install ronin-dns-proxy
74
+ ```
75
+
76
+ ### Gemfile
77
+
78
+ ```ruby
79
+ gem 'ronin-dns-proxy', '~> 0.1'
80
+ ```
81
+
82
+ ### gemspec
83
+
84
+ ```ruby
85
+ gem.add_dependency 'ronin-dns-proxy', '~> 0.1'
86
+ ```
87
+
88
+ ## Development
89
+
90
+ 1. [Fork It!](https://github.com/ronin-rb/ronin-dns-proxy/fork)
91
+ 2. Clone It!
92
+ 3. `cd ronin-dns-proxy/`
93
+ 4. `bundle install`
94
+ 5. `git checkout -b my_feature`
95
+ 6. Code It!
96
+ 7. `bundle exec rake spec`
97
+ 8. `git push origin my_feature`
98
+
99
+ ## License
100
+
101
+ Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
102
+
103
+ ronin-dns-proxy is free software: you can redistribute it and/or modify
104
+ it under the terms of the GNU Lesser General Public License as published
105
+ by the Free Software Foundation, either version 3 of the License, or
106
+ (at your option) any later version.
107
+
108
+ ronin-dns-proxy is distributed in the hope that it will be useful,
109
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
110
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
111
+ GNU Lesser General Public License for more details.
112
+
113
+ You should have received a copy of the GNU Lesser General Public License
114
+ along with ronin-dns-proxy. If not, see <https://www.gnu.org/licenses/>.
115
+
116
+ [Ruby]: https://www.ruby-lang.org
117
+ [async-dns]: https://github.com/socketry/async-dns#readme
118
+ [ronin-support]: https://github.com/ronin-rb/ronin-support#readme
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'bundler'
5
+ rescue LoadError => e
6
+ warn e.message
7
+ warn "Run `gem install bundler` to install Bundler"
8
+ exit(-1)
9
+ end
10
+
11
+ begin
12
+ Bundler.setup(:development)
13
+ rescue Bundler::BundlerError => e
14
+ warn e.message
15
+ warn "Run `bundle install` to install missing gems"
16
+ exit e.status_code
17
+ end
18
+
19
+ require 'rake'
20
+
21
+ require 'rubygems/tasks'
22
+ Gem::Tasks.new(sign: {checksum: true, pgp: true})
23
+
24
+ require 'rspec/core/rake_task'
25
+ RSpec::Core::RakeTask.new
26
+ task :test => :spec
27
+ task :default => :spec
28
+
29
+ require 'yard'
30
+ YARD::Rake::YardocTask.new
31
+ task :docs => :yard
32
+
33
+ require 'kramdown/man/task'
34
+ Kramdown::Man::Task.new
data/examples/proxy.rb ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'ronin/dns/proxy'
6
+
7
+ puts "Try running `host -p 2346 example.com 127.0.0.1` once the server is running."
8
+ puts
9
+
10
+ begin
11
+ Ronin::DNS::Proxy.run('127.0.0.1', 2346) do |server|
12
+ server.add_rule :A, 'example.com', '10.0.0.1'
13
+ server.add_rule :AAAA, 'example.com', 'dead:beef::1'
14
+
15
+ # return multiple values
16
+ server.add_rule :A, 'ftp.example.com', ['10.0.0.42', '10.0.0.43']
17
+
18
+ # match a query using a regex
19
+ server.add_rule :TXT, /^spf\./, "v=spf1 include:10.0.0.1 ~all"
20
+
21
+ # return an error for a valid hostname
22
+ server.add_rule :A, 'updates.example.com', :ServFail
23
+
24
+ # define a dynamic rule
25
+ server.add_rule :CNAME, /^www\./, ->(type,name,transaction) {
26
+ # append '.hax' to the domain name
27
+ names = name.split('.').push('hax')
28
+
29
+ transaction.respond!(names)
30
+ }
31
+
32
+ # return MX records
33
+ server.add_rule :MX, 'example.com', ->(type,name,transaction) {
34
+ transaction.respond!(10, Resolv::DNS::Name.create('email.evil.com' ))
35
+ }
36
+ end
37
+ rescue Interrupt
38
+ exit(127)
39
+ end
data/gemspec.yml ADDED
@@ -0,0 +1,29 @@
1
+ name: ronin-dns-proxy
2
+ summary: A configurable DNS proxy server library.
3
+ description: |
4
+ ronin-dns-proxy is a configurable DNS proxy server library. It supports
5
+ returning spoofing DNS results or passing DNS queries through to the upstream
6
+ DNS nameserver.
7
+
8
+ license: LGPL-3.0
9
+ authors: Postmodern
10
+ email: postmodern.mod3@gmail.com
11
+ homepage: https://ronin-rb.dev/
12
+ has_yard: true
13
+
14
+ metadata:
15
+ documentation_uri: https://ronin-rb.dev/docs/ronin-dns-proxy
16
+ source_code_uri: https://github.com/ronin-rb/ronin-dns-proxy
17
+ bug_tracker_uri: https://github.com/ronin-rb/ronin-dns-proxy/issues
18
+ changelog_uri: https://github.com/ronin-rb/ronin-dns-proxy/blob/main/ChangeLog.md
19
+ rubygems_mfa_required: 'true'
20
+
21
+ required_ruby_version: ">= 3.0.0"
22
+
23
+ dependencies:
24
+ async-dns: ~> 1.0
25
+ # Ronin dependencies:
26
+ ronin-support: ~> 1.0
27
+
28
+ development_dependencies:
29
+ bundler: ~> 2.0
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-dns-proxy - A DNS server and proxy library.
4
+ #
5
+ # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
6
+ #
7
+ # ronin-dns-proxy is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-dns-proxy is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-dns-proxy. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ module Ronin
22
+ module DNS
23
+ module Proxy
24
+ #
25
+ # Represents a DNS rule for the DNS {Server}.
26
+ #
27
+ # @api private
28
+ #
29
+ class Rule
30
+
31
+ # The record type to match.
32
+ #
33
+ # @return [:A, :AAAA, :ANY, :CNAME, :HINFO, :LOC, :MINFO, :MX, :NS, :PTR, :SOA, :SRV, :TXT, :WKS]
34
+ attr_reader :type
35
+
36
+ # The record name or regex to match.
37
+ #
38
+ # @return [String, Regexp]
39
+ attr_reader :name
40
+
41
+ # The result to return.
42
+ #
43
+ # @return [String, Array<String>, Symbol, #call]
44
+ attr_reader :result
45
+
46
+ #
47
+ # Initializes the DNS rule.
48
+ #
49
+ # @param [:A, :AAAA, :ANY, :CNAME, :HINFO, :LOC, :MINFO, :MX, :NS, :PTR, :SOA, :SRV, :TXT, :WKS] type
50
+ # The rule's record type.
51
+ #
52
+ # @param [String, Regexp] name
53
+ # The rule's name to match.
54
+ #
55
+ # @param [String, Array<String>, Symbol, #call] result
56
+ # The result to return.
57
+ #
58
+ # @yield [type, name, transaction]
59
+ # If no result argument is given, the given block will be passed the
60
+ # DNS query's type, name, and transaction object.
61
+ #
62
+ # @yieldparam [Symbol] type
63
+ # The query type.
64
+ #
65
+ # @yieldparam [String] name
66
+ # The queried host name.
67
+ #
68
+ # @yieldparam [Async::DNS::Transaction] transaction
69
+ # The DNS query transaction object.
70
+ #
71
+ # @raise [ArgumentError]
72
+ # Must specify a `result` argument or a block.
73
+ #
74
+ def initialize(type,name,result=nil,&block)
75
+ unless (result || block)
76
+ raise(ArgumentError,"must specify a result value or a block")
77
+ end
78
+
79
+ @type = type
80
+ @name = name
81
+ @result = result || block
82
+ end
83
+
84
+ #
85
+ # Determines if the rule matches the query type and query name.
86
+ #
87
+ # @param [:A, :AAAA, :ANY, :CNAME, :HINFO, :LOC, :MINFO, :MX, :NS, :PTR, :SOA, :SRV, :TXT, :WKS] query_type
88
+ # The query's type (ex: `:A`).
89
+ #
90
+ # @param [String] query_name
91
+ # The query's name (ex: 'www.example.com').
92
+ #
93
+ # @return [Boolean]
94
+ # Indicates whether the rule matches the query.
95
+ #
96
+ def matches?(query_type,query_name)
97
+ (@type == query_type) && (@name === query_name)
98
+ end
99
+
100
+ #
101
+ # Invokes the rule with the given query type, query name, and DNS
102
+ # transaction object.
103
+ #
104
+ # @param [:A, :AAAA, :ANY, :CNAME, :HINFO, :LOC, :MINFO, :MX, :NS, :PTR, :SOA, :SRV, :TXT, :WKS] query_type
105
+ # The query's type (ex: `:A`).
106
+ #
107
+ # @param [String] query_name
108
+ # The query's name (ex: 'www.example.com').
109
+ #
110
+ # @return [Async::DNS::Transaction] transaction
111
+ # The DNS query transaction.
112
+ #
113
+ def call(query_type,query_name,transaction)
114
+ if @result.respond_to?(:call)
115
+ @result.call(query_type,query_name,transaction)
116
+ elsif @result.kind_of?(Symbol)
117
+ transaction.fail!(@result)
118
+ elsif @result
119
+ transaction.respond!(@result)
120
+ end
121
+ end
122
+
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-dns-proxy - A DNS server and proxy library.
4
+ #
5
+ # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
6
+ #
7
+ # ronin-dns-proxy is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-dns-proxy is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-dns-proxy. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/dns/proxy/rule'
22
+ require 'ronin/support/network/dns'
23
+
24
+ require 'async/dns'
25
+
26
+ module Ronin
27
+ module DNS
28
+ module Proxy
29
+ #
30
+ # A rule based DNS proxy server.
31
+ #
32
+ class Server < Async::DNS::Server
33
+
34
+ # The host the server will listen on.
35
+ #
36
+ # @return [String]
37
+ attr_reader :host
38
+
39
+ # The port the server will listen on.
40
+ #
41
+ # @return [Integer]
42
+ attr_reader :port
43
+
44
+ # The upstream DNS resolver.
45
+ #
46
+ # @return [Async::DNS::Resolver]
47
+ #
48
+ # @api private
49
+ attr_reader :resolver
50
+
51
+ # The defined rules for the proxy server.
52
+ #
53
+ # @return [Array<Rule>]
54
+ #
55
+ # @api private
56
+ attr_reader :rules
57
+
58
+ #
59
+ # Initializes the DNS server.
60
+ #
61
+ # @param [String] host
62
+ # The interface to listen on.
63
+ #
64
+ # @param [Integer] port
65
+ # The local port to listen on.
66
+ #
67
+ # @param [Array<String>] nameservers
68
+ # The upstream DNS server(s) to pass queries to.
69
+ #
70
+ # @param [Array<(Symbol, String, String), (Symbol, Regexp, String), (Symbol, Regexp, Proc)>] rules
71
+ # Optional rules to populate the server with.
72
+ #
73
+ # @yield [server]
74
+ # If a block is given, it will be passed the newly created server.
75
+ #
76
+ # @example Initializes a new DNS proxy server:
77
+ # server = Ronin::DNS::Proxy.new('127.0.0.1', 2346)
78
+ # server.rule :A, 'example.com', '10.0.0.1'
79
+ # server.rule :AAAA, 'example.com', 'dead:beef::1'
80
+ #
81
+ # @example Initializing a new DNS proxy server with a block:
82
+ # server = Ronin::DNS::Proxy.new('127.0.0.1', 2346) do |server|
83
+ # server.rule :A, 'example.com', '10.0.0.1'
84
+ # server.rule :AAAA, 'example.com', 'dead:beef::1'
85
+ # end
86
+ #
87
+ # @api public
88
+ #
89
+ def initialize(host,port, nameservers: Ronin::Support::Network::DNS.nameservers,
90
+ rules: nil)
91
+ @host = host
92
+ @port = port
93
+
94
+ super([[:udp, host, port]])
95
+
96
+ @resolver = Async::DNS::Resolver.new(
97
+ nameservers.map { |ip| [:udp, ip, 53] }
98
+ )
99
+
100
+ @rules = []
101
+
102
+ if rules
103
+ rules.each do |(record_type,name,result)|
104
+ rule(record_type,name,result)
105
+ end
106
+ end
107
+
108
+ yield self if block_given?
109
+ end
110
+
111
+ #
112
+ # Adds a rule to the server.
113
+ #
114
+ # @param [:A, :AAAA, :ANY, :CNAME, :HINFO, :LOC, :MINFO, :MX, :NS, :PTR, :SOA, :SRV, :TXT, :WKS] record_type
115
+ # The record type that the rule will match against.
116
+ #
117
+ # @param [String, Regexp] name
118
+ # The record name that the rule will match against.
119
+ #
120
+ # @param [String, Array<String>, Symbol, #call] result
121
+ # The result to respond with. It can be a String, or an Array of
122
+ # Strings, or an error code:
123
+ #
124
+ # * `:NoError` - No error occurred.
125
+ # * `:FormErr` - The incoming data was not formatted correctly.
126
+ # * `:ServFail` - The operation caused a server failure (internal error, etc).
127
+ # * `:NXDomain` - Non-eXistant Domain (domain record does not exist).
128
+ # * `:NotImp` - The operation requested is not implemented.
129
+ # * `:Refused` - The operation was refused by the server.
130
+ # * `:NotAuth` - The server is not authoritive for the zone.
131
+ #
132
+ # @yield [type, name, transaction]
133
+ # If no result argument is given, the given block will be passed the
134
+ # DNS query's type, name, and transaction object.
135
+ #
136
+ # @yieldparam [Symbol] type
137
+ # The query type.
138
+ #
139
+ # @yieldparam [String] name
140
+ # The queried host name.
141
+ #
142
+ # @yieldparam [Async::DNS::Transaction] transaction
143
+ # The DNS query transaction object.
144
+ #
145
+ # @example override the IP address for a domain:
146
+ # server.rule :A, 'example.com', '10.0.0.42'
147
+ #
148
+ # @example return multiple IP addresses:
149
+ # server.rule :A, 'example.com', ['10.0.0.42', '10.0.0.43']
150
+ #
151
+ # @example return an error for the given hostname:
152
+ # server.rule :A, 'updates.example.com', :ServFail
153
+ #
154
+ # @example match a query using a regex:
155
+ # server.rule :TXT, /^spf\./, "v=spf1 include:10.0.0.1 ~all"
156
+ #
157
+ # @example define a dynamic rule:
158
+ # server.rule(:CNAME, /^www\./) do |type,name,transaction|
159
+ # # append '.hax' to the domain name
160
+ # names = name.split('.').push('hax')
161
+ #
162
+ # transaction.respond!(names)
163
+ # end
164
+ #
165
+ # @api public
166
+ #
167
+ def rule(record_type,name,result=nil,&block)
168
+ unless (result || block)
169
+ raise(ArgumentError,"must specify a result value or a block")
170
+ end
171
+
172
+ @rules << Rule.new(record_type,name,result,&block)
173
+ end
174
+
175
+ # Mapping of Resolv resource classes to Symbols.
176
+ #
177
+ # @api private
178
+ RECORD_TYPES = {
179
+ Resolv::DNS::Resource::IN::A => :A,
180
+ Resolv::DNS::Resource::IN::AAAA => :AAAA,
181
+ Resolv::DNS::Resource::IN::ANY => :ANY,
182
+ Resolv::DNS::Resource::IN::CNAME => :CNAME,
183
+ Resolv::DNS::Resource::IN::HINFO => :HINFO,
184
+ Resolv::DNS::Resource::IN::LOC => :LOC,
185
+ Resolv::DNS::Resource::IN::MINFO => :MINFO,
186
+ Resolv::DNS::Resource::IN::MX => :MX,
187
+ Resolv::DNS::Resource::IN::NS => :NS,
188
+ Resolv::DNS::Resource::IN::PTR => :PTR,
189
+ Resolv::DNS::Resource::IN::SOA => :SOA,
190
+ Resolv::DNS::Resource::IN::SRV => :SRV,
191
+ Resolv::DNS::Resource::IN::TXT => :TXT,
192
+ Resolv::DNS::Resource::IN::WKS => :WKS
193
+ }
194
+
195
+ #
196
+ # Processes a received query.
197
+ #
198
+ # @param [String] name
199
+ # The query value (ex: `www.example.com`).
200
+ #
201
+ # @param [Class<Resolv::DNS::Resource>] resource_class
202
+ # The resource class (ex: `Resolv::DNS::Resource::IN::A`).
203
+ #
204
+ # @param [Async::DNS::Transaction] transaction
205
+ # The DNS transaction object.
206
+ #
207
+ # @api private
208
+ #
209
+ def process(name,resource_class,transaction)
210
+ query_type = RECORD_TYPES.fetch(resource_class)
211
+
212
+ matched_rule = @rules.find do |rule|
213
+ rule.matches?(query_type,name)
214
+ end
215
+
216
+ if matched_rule
217
+ matched_rule.call(query_type,name,transaction)
218
+ else
219
+ transaction.passthrough!(@resolver)
220
+ end
221
+ end
222
+
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-dns-proxy - A DNS server and proxy library.
4
+ #
5
+ # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
6
+ #
7
+ # ronin-dns-proxy is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-dns-proxy is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-dns-proxy. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ module Ronin
22
+ module DNS
23
+ module Proxy
24
+ # ronin-dns-proxy version
25
+ VERSION = '0.1.0.rc1'
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-dns-proxy - A DNS server and proxy library.
4
+ #
5
+ # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
6
+ #
7
+ # ronin-dns-proxy is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-dns-proxy is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-dns-proxy. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/dns/proxy/server'
22
+
23
+ module Ronin
24
+ module DNS
25
+ #
26
+ # Namespace for `ronin-dns-proxy`.
27
+ #
28
+ module Proxy
29
+ #
30
+ # Starts a new DNS proxy listening on the given host and port.
31
+ #
32
+ # @param [String] host
33
+ # The interface to listen on.
34
+ #
35
+ # @param [Integer] port
36
+ # The local port to listen on.
37
+ #
38
+ # @param [Hash{Symbol => Object}] kwargs
39
+ # Additional keyword arguments for {Server#initialize}.
40
+ #
41
+ # @option kwargs [Array<String>] :nameservers (Ronin::Support::Network::DNS.nameservers)
42
+ # The upstream DNS server(s) to pass queries to.
43
+ #
44
+ # @option kwargs [Array<(Symbol, String, String), (Symbol, Regexp, String), (Symbol, Regexp, Proc)>] rules
45
+ # Optional rules to populate the server with.
46
+ #
47
+ # @yield [server]
48
+ # If a block is given, it will be passed the newly created DNS proxy
49
+ # server object.
50
+ #
51
+ # @yieldparam [Server] server
52
+ # The newly created DNS proxy server.
53
+ #
54
+ # @example
55
+ # require 'ronin/dns/proxy'
56
+ #
57
+ # Ronin::DNS::Proxy.run('127.0.0.1', 2346) do |server|
58
+ # server.rule :A, 'example.com', '10.0.0.1'
59
+ # server.rule :AAAA, 'example.com', 'dead:beef::1'
60
+ # end
61
+ #
62
+ def self.run(host,port,**kwargs,&block)
63
+ server = Server.new(host,port,**kwargs,&block)
64
+ server.run
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gemspec = YAML.load_file('gemspec.yml')
7
+
8
+ gem.name = gemspec.fetch('name')
9
+ gem.version = gemspec.fetch('version') do
10
+ lib_dir = File.join(File.dirname(__FILE__),'lib')
11
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
12
+
13
+ require 'ronin/dns/proxy/version'
14
+ Ronin::DNS::Proxy::VERSION
15
+ end
16
+
17
+ gem.summary = gemspec['summary']
18
+ gem.description = gemspec['description']
19
+ gem.licenses = Array(gemspec['license'])
20
+ gem.authors = Array(gemspec['authors'])
21
+ gem.email = gemspec['email']
22
+ gem.homepage = gemspec['homepage']
23
+ gem.metadata = gemspec['metadata'] if gemspec['metadata']
24
+
25
+ glob = ->(patterns) { gem.files & Dir[*patterns] }
26
+
27
+ gem.files = `git ls-files`.split($/)
28
+ gem.files = glob[gemspec['files']] if gemspec['files']
29
+ gem.files += Array(gemspec['generated_files'])
30
+ # exclude test files from the packages gem
31
+ gem.files -= glob[gemspec['test_files'] || 'spec/{**/}*']
32
+
33
+ gem.executables = gemspec.fetch('executables') do
34
+ glob['bin/*'].map { |path| File.basename(path) }
35
+ end
36
+
37
+ gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
38
+ gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
39
+
40
+ gem.require_paths = Array(gemspec.fetch('require_paths') {
41
+ %w[ext lib].select { |dir| File.directory?(dir) }
42
+ })
43
+
44
+ gem.requirements = gemspec['requirements']
45
+ gem.required_ruby_version = gemspec['required_ruby_version']
46
+ gem.required_rubygems_version = gemspec['required_rubygems_version']
47
+ gem.post_install_message = gemspec['post_install_message']
48
+
49
+ split = ->(string) { string.split(/,\s*/) }
50
+
51
+ if gemspec['dependencies']
52
+ gemspec['dependencies'].each do |name,versions|
53
+ gem.add_dependency(name,split[versions])
54
+ end
55
+ end
56
+
57
+ if gemspec['development_dependencies']
58
+ gemspec['development_dependencies'].each do |name,versions|
59
+ gem.add_development_dependency(name,split[versions])
60
+ end
61
+ end
62
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ronin-dns-proxy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.rc1
5
+ platform: ruby
6
+ authors:
7
+ - Postmodern
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-06-23 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.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.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ronin-support
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ description: |
56
+ ronin-dns-proxy is a configurable DNS proxy server library. It supports
57
+ returning spoofing DNS results or passing DNS queries through to the upstream
58
+ DNS nameserver.
59
+ email: postmodern.mod3@gmail.com
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files:
63
+ - COPYING.txt
64
+ - ChangeLog.md
65
+ - README.md
66
+ files:
67
+ - ".document"
68
+ - ".github/workflows/ruby.yml"
69
+ - ".gitignore"
70
+ - ".rspec"
71
+ - ".rubocop.yml"
72
+ - ".ruby-version"
73
+ - ".yardopts"
74
+ - COPYING.txt
75
+ - ChangeLog.md
76
+ - Gemfile
77
+ - README.md
78
+ - Rakefile
79
+ - examples/proxy.rb
80
+ - gemspec.yml
81
+ - lib/ronin/dns/proxy.rb
82
+ - lib/ronin/dns/proxy/rule.rb
83
+ - lib/ronin/dns/proxy/server.rb
84
+ - lib/ronin/dns/proxy/version.rb
85
+ - ronin-dns-proxy.gemspec
86
+ homepage: https://ronin-rb.dev/
87
+ licenses:
88
+ - LGPL-3.0
89
+ metadata:
90
+ documentation_uri: https://ronin-rb.dev/docs/ronin-dns-proxy
91
+ source_code_uri: https://github.com/ronin-rb/ronin-dns-proxy
92
+ bug_tracker_uri: https://github.com/ronin-rb/ronin-dns-proxy/issues
93
+ changelog_uri: https://github.com/ronin-rb/ronin-dns-proxy/blob/main/ChangeLog.md
94
+ rubygems_mfa_required: 'true'
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 3.0.0
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 3.3.27
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: A configurable DNS proxy server library.
114
+ test_files: []