dns_mock 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +89 -0
- data/.codeclimate.yml +13 -0
- data/.github/BRANCH_NAMING_CONVENTION.md +36 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +28 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
- data/.github/ISSUE_TEMPLATE/issue_report.md +28 -0
- data/.github/ISSUE_TEMPLATE/question.md +22 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +49 -0
- data/.gitignore +10 -0
- data/.overcommit.yml +32 -0
- data/.reek.yml +46 -0
- data/.rspec +2 -0
- data/.rubocop.yml +273 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +9 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +46 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +127 -0
- data/LICENSE.txt +21 -0
- data/README.md +79 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/dns_mock.gemspec +48 -0
- data/lib/dns_mock.rb +19 -0
- data/lib/dns_mock/core.rb +56 -0
- data/lib/dns_mock/error/argument_type.rb +11 -0
- data/lib/dns_mock/error/helper.rb +22 -0
- data/lib/dns_mock/error/port_in_use.rb +11 -0
- data/lib/dns_mock/error/random_free_port.rb +11 -0
- data/lib/dns_mock/error/record_context.rb +11 -0
- data/lib/dns_mock/error/record_context_type.rb +15 -0
- data/lib/dns_mock/error/record_host_type.rb +11 -0
- data/lib/dns_mock/error/record_not_found.rb +11 -0
- data/lib/dns_mock/error/record_type.rb +11 -0
- data/lib/dns_mock/record/builder/a.rb +9 -0
- data/lib/dns_mock/record/builder/aaaa.rb +9 -0
- data/lib/dns_mock/record/builder/base.rb +26 -0
- data/lib/dns_mock/record/builder/cname.rb +13 -0
- data/lib/dns_mock/record/builder/mx.rb +22 -0
- data/lib/dns_mock/record/builder/ns.rb +9 -0
- data/lib/dns_mock/record/builder/soa.rb +19 -0
- data/lib/dns_mock/record/builder/txt.rb +9 -0
- data/lib/dns_mock/record/factory/a.rb +15 -0
- data/lib/dns_mock/record/factory/aaaa.rb +15 -0
- data/lib/dns_mock/record/factory/base.rb +54 -0
- data/lib/dns_mock/record/factory/cname.rb +15 -0
- data/lib/dns_mock/record/factory/mx.rb +15 -0
- data/lib/dns_mock/record/factory/ns.rb +15 -0
- data/lib/dns_mock/record/factory/soa.rb +15 -0
- data/lib/dns_mock/record/factory/txt.rb +15 -0
- data/lib/dns_mock/response/answer.rb +32 -0
- data/lib/dns_mock/response/message.rb +29 -0
- data/lib/dns_mock/server.rb +82 -0
- data/lib/dns_mock/server/random_available_port.rb +48 -0
- data/lib/dns_mock/server/records_dictionary_builder.rb +52 -0
- data/lib/dns_mock/version.rb +5 -0
- metadata +334 -0
data/lib/dns_mock.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'dns_mock/core'
|
4
|
+
|
5
|
+
module DnsMock
|
6
|
+
class << self
|
7
|
+
def start_server(server = DnsMock::Server, records: {}, port: nil)
|
8
|
+
server.new(records: records, port: port)
|
9
|
+
end
|
10
|
+
|
11
|
+
def running_servers
|
12
|
+
::ObjectSpace.each_object(DnsMock::Server).select(&:alive?)
|
13
|
+
end
|
14
|
+
|
15
|
+
def stop_running_servers!
|
16
|
+
running_servers.all?(&:stop!)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'resolv'
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
module DnsMock
|
7
|
+
AVAILABLE_DNS_RECORD_TYPES = %i[a aaaa cname mx ns soa txt].freeze
|
8
|
+
|
9
|
+
module Error
|
10
|
+
require_relative '../dns_mock/error/argument_type'
|
11
|
+
require_relative '../dns_mock/error/port_in_use'
|
12
|
+
require_relative '../dns_mock/error/random_free_port'
|
13
|
+
require_relative '../dns_mock/error/record_context_type'
|
14
|
+
require_relative '../dns_mock/error/record_context'
|
15
|
+
require_relative '../dns_mock/error/record_host_type'
|
16
|
+
require_relative '../dns_mock/error/record_not_found'
|
17
|
+
require_relative '../dns_mock/error/record_type'
|
18
|
+
require_relative '../dns_mock/error/helper'
|
19
|
+
end
|
20
|
+
|
21
|
+
module Record
|
22
|
+
module Factory
|
23
|
+
require_relative '../dns_mock/record/factory/base'
|
24
|
+
require_relative '../dns_mock/record/factory/a'
|
25
|
+
require_relative '../dns_mock/record/factory/aaaa'
|
26
|
+
require_relative '../dns_mock/record/factory/cname'
|
27
|
+
require_relative '../dns_mock/record/factory/mx'
|
28
|
+
require_relative '../dns_mock/record/factory/ns'
|
29
|
+
require_relative '../dns_mock/record/factory/soa'
|
30
|
+
require_relative '../dns_mock/record/factory/txt'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module Record
|
35
|
+
module Builder
|
36
|
+
require_relative '../dns_mock/record/builder/base'
|
37
|
+
require_relative '../dns_mock/record/builder/a'
|
38
|
+
require_relative '../dns_mock/record/builder/aaaa'
|
39
|
+
require_relative '../dns_mock/record/builder/cname'
|
40
|
+
require_relative '../dns_mock/record/builder/mx'
|
41
|
+
require_relative '../dns_mock/record/builder/ns'
|
42
|
+
require_relative '../dns_mock/record/builder/soa'
|
43
|
+
require_relative '../dns_mock/record/builder/txt'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module Response
|
48
|
+
require_relative '../dns_mock/response/answer'
|
49
|
+
require_relative '../dns_mock/response/message'
|
50
|
+
end
|
51
|
+
|
52
|
+
require_relative '../dns_mock/version'
|
53
|
+
require_relative '../dns_mock/server/records_dictionary_builder'
|
54
|
+
require_relative '../dns_mock/server/random_available_port'
|
55
|
+
require_relative '../dns_mock/server'
|
56
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DnsMock
|
4
|
+
module Error
|
5
|
+
module Helper
|
6
|
+
def raise_record_context_type_error(record_type, record_context, expected_type)
|
7
|
+
current_type, record_type = record_context.class, record_type.upcase
|
8
|
+
raise_unless(DnsMock::Error::RecordContextType.new(current_type, record_type, expected_type), current_type.eql?(expected_type))
|
9
|
+
end
|
10
|
+
|
11
|
+
def raise_record_type_error(record_type, condition)
|
12
|
+
raise_unless(DnsMock::Error::RecordType.new(record_type), condition)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def raise_unless(error_instance, condition)
|
18
|
+
raise error_instance unless condition
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DnsMock
|
4
|
+
module Error
|
5
|
+
PortInUse = ::Class.new(::RuntimeError) do
|
6
|
+
def initialize(hostname, port)
|
7
|
+
super("Impossible to bind UDP DNS mock server on #{hostname}:#{port}. Address already in use")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DnsMock
|
4
|
+
module Error
|
5
|
+
RecordContextType = ::Class.new(::ArgumentError) do
|
6
|
+
def initialize(record_context_type, record_type, expected_record_context_type)
|
7
|
+
super(
|
8
|
+
"#{record_context_type} is invalid record context type for " \
|
9
|
+
"#{record_type} record. Should be a " \
|
10
|
+
"#{expected_record_context_type}"
|
11
|
+
)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DnsMock
|
4
|
+
module Error
|
5
|
+
RecordHostType = ::Class.new(::ArgumentError) do
|
6
|
+
def initialize(hostname, hostname_class)
|
7
|
+
super("Hostname #{hostname} type is #{hostname_class}. Should be a String")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DnsMock
|
4
|
+
module Error
|
5
|
+
RecordNotFound = ::Class.new(::StandardError) do
|
6
|
+
def initialize(record_type, hostname)
|
7
|
+
super("#{record_type.upcase} record not found for #{hostname} in predefined records dictionary")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DnsMock
|
4
|
+
module Record
|
5
|
+
module Builder
|
6
|
+
class Base
|
7
|
+
def self.call(target_factory, records_data)
|
8
|
+
new(target_factory, records_data).build
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(target_factory, records_data)
|
12
|
+
@target_factory = target_factory
|
13
|
+
@records_data = records_data
|
14
|
+
end
|
15
|
+
|
16
|
+
def build
|
17
|
+
records_data.map { |record_data| target_factory.new(record_data: record_data).create }
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :target_factory, :records_data
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DnsMock
|
4
|
+
module Record
|
5
|
+
module Builder
|
6
|
+
class Mx < DnsMock::Record::Builder::Base
|
7
|
+
RECORD_PREFERENCE_STEP = 10
|
8
|
+
|
9
|
+
def build
|
10
|
+
records_data.map.with_index(1) do |record_data, record_preference|
|
11
|
+
target_factory.new(
|
12
|
+
record_data: [
|
13
|
+
record_preference * DnsMock::Record::Builder::Mx::RECORD_PREFERENCE_STEP,
|
14
|
+
record_data
|
15
|
+
]
|
16
|
+
).create
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DnsMock
|
4
|
+
module Record
|
5
|
+
module Builder
|
6
|
+
class Soa < DnsMock::Record::Builder::Base
|
7
|
+
FACTORY_ARGS_ORDER = %i[mname rname serial refresh retry expire minimum].freeze
|
8
|
+
|
9
|
+
def build
|
10
|
+
records_data.map do |record_data|
|
11
|
+
target_factory.new(
|
12
|
+
record_data: record_data.values_at(*DnsMock::Record::Builder::Soa::FACTORY_ARGS_ORDER)
|
13
|
+
).create
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DnsMock
|
4
|
+
module Record
|
5
|
+
module Factory
|
6
|
+
class Base
|
7
|
+
extend DnsMock::Error::Helper
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_reader :target_class
|
11
|
+
|
12
|
+
def record_type(record_type)
|
13
|
+
@target_class = ::Resolv::DNS::Resource::IN.const_get(
|
14
|
+
record_type_check(record_type).upcase
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def record_type_check(defined_record_type)
|
21
|
+
raise_record_type_error(defined_record_type, DnsMock::AVAILABLE_DNS_RECORD_TYPES.include?(defined_record_type))
|
22
|
+
defined_record_type
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(dns_name = ::Resolv::DNS::Name, record_data:)
|
27
|
+
@dns_name = dns_name
|
28
|
+
@record_data = record_data
|
29
|
+
end
|
30
|
+
|
31
|
+
def instance_params; end
|
32
|
+
|
33
|
+
def create
|
34
|
+
self.class.target_class.public_send(:new, *instance_params)
|
35
|
+
rescue => error
|
36
|
+
raise DnsMock::Error::RecordContext.new(error.message, record_type)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :dns_name, :record_data
|
42
|
+
|
43
|
+
def record_type
|
44
|
+
self.class.name.split('::').last.upcase
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_dns_name(hostname)
|
48
|
+
raise ::ArgumentError, "cannot interpret as DNS name: #{hostname}" unless hostname.is_a?(::String)
|
49
|
+
dns_name.create("#{hostname}.")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DnsMock
|
4
|
+
module Record
|
5
|
+
module Factory
|
6
|
+
Cname = ::Class.new(DnsMock::Record::Factory::Base) do
|
7
|
+
record_type :cname
|
8
|
+
|
9
|
+
def instance_params
|
10
|
+
[create_dns_name(record_data)]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DnsMock
|
4
|
+
module Record
|
5
|
+
module Factory
|
6
|
+
Mx = ::Class.new(DnsMock::Record::Factory::Base) do
|
7
|
+
record_type :mx
|
8
|
+
|
9
|
+
def instance_params
|
10
|
+
[record_data.first, create_dns_name(record_data.last)]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|