ddig 0.1.0

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: 1d1bcdd660ab21b14d2e5572b27a50f2cc0fe541c64b9c6c8502f85d89a29b28
4
+ data.tar.gz: 6462363f2b075b9aad78f88a055e1d53af1901054a22c3a010fcdfb0a28e9fde
5
+ SHA512:
6
+ metadata.gz: 785ef898b4f773668131d56ed8596b11de8bfbe9908cc0de7383a2b0cfbb3aaf613dc813b73284382aaa6bbb3d9e89d6e4a45e0d1205aba45361213d6764cc19
7
+ data.tar.gz: c12f638d1899ddb47e6c482b22b315ea8912bce701243fef37a239566653e5e216c8ed01b910105bc06c28843e9d40ef81d653923e1b95a2e386f18c18f2fc72
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Taketo Takashima
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # ddig
2
+
3
+ ddig is DNS lookup utility for Ruby.
4
+
5
+ ## Features
6
+
7
+ - DNS Resolvers
8
+ - UDP (Do53)
9
+ - DoT (DNS over TLS)
10
+ - https://datatracker.ietf.org/doc/html/rfc7858
11
+ - ~~DoH (DNS over HTTPS)~~
12
+ - Not yet Supported
13
+ - https://datatracker.ietf.org/doc/html/rfc8484
14
+ - DDR (Discovery of Designated Resolvers)
15
+ - https://www.rfc-editor.org/rfc/rfc9462.html
16
+
17
+ ## Installation
18
+
19
+ Install the gem and add to the application's Gemfile by executing:
20
+
21
+ $ bundle add ddig
22
+
23
+ If bundler is not being used to manage dependencies, install the gem by executing:
24
+
25
+ $ gem install ddig
26
+
27
+ ## Usage
28
+
29
+ ```ruby
30
+ ddig = Ddig.lookup('dns.google', nameservers: ['8.8.8.8', '2001:4860:4860::8888'])
31
+
32
+ ddig[:do53][:ipv4]
33
+ => #<Ddig::Resolver::Do53:0x00000001207aaeb0 @a=["8.8.4.4", "8.8.8.8"], @aaaa=["2001:4860:4860::8844", "2001:4860:4860::8888"], @hostname="dns.google", @ip=:ipv4, @nameservers=["8.8.8.8"]>
34
+ ddig[:do53][:ipv6]
35
+ => #<Ddig::Resolver::Do53:0x000000012073d2c0 @a=["8.8.4.4", "8.8.8.8"], @aaaa=["2001:4860:4860::8844", "2001:4860:4860::8888"], @hostname="dns.google", @ip=:ipv4, @nameservers=["2001:4860:4860::8888"]>
36
+
37
+ ddig[:ddr]
38
+ => [#<Ddig::Ddr::DesignatedResolver:0x0000000120735480
39
+ @address="8.8.8.8",
40
+ @dohpath=nil,
41
+ @ip=:ipv4,
42
+ @port=853,
43
+ @protocol="dot",
44
+ @target="dns.google",
45
+ @unencrypted_resolver="8.8.8.8",
46
+ @verify_cert=
47
+ #<Ddig::Ddr::VerifyCert:0x00000001207321e0
48
+ @address="8.8.8.8",
49
+ @hostname="dns.google",
50
+ @open_timeout=3,
51
+ @port=853,
52
+ @subject_alt_name=
53
+ ["DNS:dns.google",
54
+ "IP Address:8.8.8.8",
55
+ "IP Address:2001:4860:4860:0:0:0:0:8888",
56
+ ...
57
+ "IP Address:2001:4860:4860:0:0:0:0:64"],
58
+ @unencrypted_resolver="8.8.8.8",
59
+ @verify=true>>,
60
+ #<Ddig::Ddr::DesignatedResolver:0x0000000120733b30
61
+ @address="8.8.8.8",
62
+ @dohpath="/dns-query{?dns}",
63
+ @ip=:ipv4,
64
+ @port=443,
65
+ @protocol="h2",
66
+ @target="dns.google",
67
+ @unencrypted_resolver="8.8.8.8",
68
+ @verify_cert=
69
+ #<Ddig::Ddr::VerifyCert:0x0000000120451bd8
70
+ @address="8.8.8.8",
71
+ @hostname="dns.google",
72
+ @open_timeout=3,
73
+ @port=443,
74
+ @subject_alt_name=
75
+ ["DNS:dns.google",
76
+ "IP Address:8.8.8.8",
77
+ "IP Address:2001:4860:4860:0:0:0:0:8888",
78
+ ...
79
+ "IP Address:2001:4860:4860:0:0:0:0:64"],
80
+ @unencrypted_resolver="8.8.8.8",
81
+ @verify=true>>,
82
+ ...
83
+ ]
84
+ ```
85
+
86
+ ### CLI
87
+
88
+ (TBD)
89
+
90
+ ## Development
91
+
92
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
93
+
94
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
95
+
96
+ ## Contributing
97
+
98
+ Bug reports and pull requests are welcome on GitHub at https://github.com/taketo1113/ddig.
99
+
100
+ ## License
101
+
102
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/exe/ddig ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "ddig/cli"
4
+
5
+ cli = Ddig::Cli.new(ARGV)
6
+ cli.exec
data/lib/ddig/cli.rb ADDED
@@ -0,0 +1,55 @@
1
+ require "optparse"
2
+
3
+ require "ddig"
4
+
5
+ module Ddig
6
+ class Cli
7
+ def initialize(args)
8
+ @args = args
9
+ @options = {
10
+ format: 'text',
11
+ }
12
+
13
+ parse_options
14
+
15
+ if @hostname.nil?
16
+ puts @option_parser
17
+ exit
18
+ end
19
+ end
20
+
21
+ def parse_options
22
+ @option_parser = OptionParser.new do |opts|
23
+ opts.banner = "Usage: ddig [options] hostname"
24
+
25
+ opts.on("--nameserver=ipaddress", "nameserver ip address") { |v| @options[:nameserver] = v }
26
+ opts.on("--format={text|json}", "output format (default: text)") { |v| @options[:format] = v }
27
+
28
+ opts.separator ""
29
+
30
+ opts.on("-v", "--verbose", "run verbosely") { |v| @options[:verbose] = v }
31
+
32
+ opts.on("-h", "--help", "show this help message.") { puts opts; exit }
33
+ opts.on("--version", "show version.") { puts Ddig::VERSION; exit }
34
+ end
35
+ @option_parser.parse!
36
+
37
+ @hostname = @args[0]
38
+ end
39
+
40
+ def exec
41
+ @ddig = Ddig.lookup(@hostname, nameservers: [@options[:nameserver]])
42
+
43
+ if @options[:format] == 'json'
44
+ # TODO: to_json
45
+ puts @ddig
46
+ else
47
+ print_result
48
+ end
49
+ end
50
+
51
+ def print_result
52
+ puts @ddig
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,51 @@
1
+ module Ddig
2
+ class Ddr
3
+ class DesignatedResolver
4
+ attr_reader :unencrypted_resolver, :target, :protocol, :port, :dohpath, :address, :ip
5
+ attr_reader :verify_cert
6
+
7
+ PROTOCOLS = ['http/1.1', 'h2', 'h3', 'dot', 'doq']
8
+
9
+ def initialize(unencrypted_resolver:, target:, protocol: nil, port: nil, dohpath: nil, address: nil, ip: nil)
10
+ @target = target
11
+ @unencrypted_resolver = unencrypted_resolver
12
+ @protocol = protocol
13
+ @port = port
14
+ @dohpath = dohpath
15
+ @address = address
16
+ @ip = ip
17
+
18
+ # check protocol
19
+ unless PROTOCOLS.include?(@protocol)
20
+ raise Error.new("Not Supportted Protocol (protocol: #{@protocol}). Suported protocol is #{PROTOCOLS.join(' / ')}")
21
+ end
22
+
23
+ if @port.nil?
24
+ set_default_port
25
+ end
26
+ end
27
+
28
+ def verify
29
+ @verify_cert = VerifyCert.new(hostname: @target, address: @address, port: @port, unencrypted_resolver: @unencrypted_resolver)
30
+ @verify_cert.verify
31
+ end
32
+
33
+ # Set default port by protocol
34
+ # ref: https://www.rfc-editor.org/rfc/rfc9461.html#section-4.2
35
+ def set_default_port
36
+ case @protocol
37
+ when 'http/1.1'
38
+ @port = 80
39
+ when 'h2', 'h3'
40
+ @port = 443
41
+ when 'dot', 'doq'
42
+ @port = 853
43
+ end
44
+ end
45
+
46
+ def uniq_key
47
+ "#{@unencrypted_resolver}-#{@target}-#{@protocol}-#{@port}-#{@dohpath}-#{@address}-#{@ip}"
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,68 @@
1
+ require "openssl"
2
+ require 'net/protocol'
3
+
4
+ module Ddig
5
+ class Ddr
6
+ class VerifyCert
7
+ attr_reader :hostname, :address, :port, :unencrypted_resolver
8
+ attr_reader :verify, :subject_alt_name, :error_message
9
+
10
+ def initialize(hostname:, address:, port:, unencrypted_resolver:)
11
+ @hostname = hostname
12
+ @address = address
13
+ @port = port
14
+ @unencrypted_resolver = unencrypted_resolver
15
+
16
+ @open_timeout = 3
17
+ end
18
+
19
+ def verify
20
+ begin
21
+ socket = Timeout.timeout(@open_timeout) {
22
+ TCPSocket.open(@address, @port)
23
+ }
24
+
25
+ ctx = OpenSSL::SSL::SSLContext.new
26
+ ctx.set_params
27
+
28
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ctx)
29
+ ssl_socket.sync_close = true
30
+ ssl_socket.hostname = @hostname
31
+
32
+ # connect
33
+ Timeout.timeout(@open_timeout) {
34
+ ssl_socket.connect
35
+ }
36
+
37
+ # verify
38
+ set_subject_alt_name(ssl_socket)
39
+
40
+ unless ssl_socket.post_connection_check(@hostname)
41
+ @verify = false
42
+ return @verify
43
+ end
44
+
45
+ unless ssl_socket.post_connection_check(@unencrypted_resolver)
46
+ @verify = false
47
+ return @verify
48
+ end
49
+
50
+ @verify = true
51
+ return @verify
52
+
53
+ rescue => e
54
+ @verify = false
55
+ @error_message = e.message
56
+
57
+ return @verify
58
+ end
59
+ end
60
+
61
+ def set_subject_alt_name(ssl_socket)
62
+ socket = Net::BufferedIO.new(ssl_socket)
63
+
64
+ @subject_alt_name = socket.io.peer_cert.extensions.select { |ext| ext.to_h['oid'] == 'subjectAltName' }.first.to_h['value'].split(', ')
65
+ end
66
+ end
67
+ end
68
+ end
data/lib/ddig/ddr.rb ADDED
@@ -0,0 +1,140 @@
1
+ require 'resolv'
2
+
3
+ require_relative "ddr/verify_cert"
4
+ require_relative "ddr/designated_resolver"
5
+
6
+ module Ddig
7
+ # DDR client (Discovery of Designated Resolvers)
8
+ class Ddr
9
+ attr_reader :nameservers, :ip
10
+ attr_reader :svcb_records, :designated_resolvers
11
+
12
+ def initialize(nameservers: nil, ip: nil)
13
+ @ip = ip
14
+
15
+ @nameserver = Ddig::Nameserver.new(nameservers: nameservers)
16
+ set_nameservers
17
+
18
+ # discover designated resolvers
19
+ query_svcb_records
20
+ discover_designated_resolvers
21
+ verify_discovery
22
+ end
23
+
24
+ def query_svcb_records
25
+ @svcb_records = []
26
+
27
+ if @nameservers.empty?
28
+ return @svcb_records
29
+ end
30
+
31
+ @nameservers.each do |nameserver|
32
+ svcb_rrset = Resolv::DNS.open(nameserver: nameserver) do |dns|
33
+ dns.getresources('_dns.resolver.arpa', Resolv::DNS::Resource::IN::SVCB)
34
+ end
35
+
36
+ svcb_rrset.sort_by!(&:priority)
37
+
38
+ @svcb_records += svcb_rrset.map { |svcb_record| { unencrypted_resolver: nameserver, svcb_record: svcb_record } }
39
+ end
40
+
41
+ @svcb_records
42
+ end
43
+
44
+ # Discovery Designated Resolvers from SVCB RR Set
45
+ # ref. https://www.rfc-editor.org/rfc/rfc9461.html
46
+ def discover_designated_resolvers
47
+ @designated_resolvers = []
48
+
49
+ @svcb_records.each do |item|
50
+ unencrypted_resolver = item[:unencrypted_resolver]
51
+ svcb_record = item[:svcb_record]
52
+
53
+ target = svcb_record.target.to_s
54
+ priority = svcb_record.priority
55
+ protocols = svcb_record.params[:alpn].protocol_ids
56
+ port = svcb_record.params[:port]&.port
57
+ dohpath = svcb_record.params[:dohpath]&.template
58
+ ipv4hint = svcb_record.params[:ipv4hint]&.addresses
59
+ ipv6hint = svcb_record.params[:ipv6hint]&.addresses
60
+
61
+ # Skip AliasMode of SVCB RR
62
+ if priority.zero?
63
+ next
64
+ end
65
+
66
+ protocols.each do |protocol|
67
+ do53_v4 = ::Ddig::Resolver::Do53.new(hostname: target, nameservers: [unencrypted_resolver], ip: :ipv4).lookup
68
+ do53_v6 = ::Ddig::Resolver::Do53.new(hostname: target, nameservers: [unencrypted_resolver], ip: :ipv6).lookup
69
+
70
+ # ipv4
71
+ unless do53_v4.nil? || do53_v4.a.nil?
72
+ do53_v4.a.each do |address|
73
+ designated_resolver = ::Ddig::Ddr::DesignatedResolver.new(unencrypted_resolver: unencrypted_resolver, target: target, protocol: protocol, port: port, dohpath: dohpath, address: address.to_s, ip: :ipv4)
74
+ @designated_resolvers << designated_resolver
75
+ end
76
+ end
77
+ unless do53_v6.nil? || do53_v6.a.nil?
78
+ do53_v6.a.each do |address|
79
+ designated_resolver = ::Ddig::Ddr::DesignatedResolver.new(unencrypted_resolver: unencrypted_resolver, target: target, protocol: protocol, port: port, dohpath: dohpath, address: address.to_s, ip: :ipv4)
80
+ @designated_resolvers << designated_resolver
81
+ end
82
+ end
83
+
84
+ # ipv6
85
+ unless do53_v4.nil? || do53_v4.aaaa.nil?
86
+ do53_v4.aaaa.each do |address|
87
+ designated_resolver = ::Ddig::Ddr::DesignatedResolver.new(unencrypted_resolver: unencrypted_resolver, target: target, protocol: protocol, port: port, dohpath: dohpath, address: address.to_s, ip: :ipv6)
88
+ @designated_resolvers << designated_resolver
89
+ end
90
+ end
91
+ unless do53_v6.nil? || do53_v6.aaaa.nil?
92
+ do53_v6.aaaa.each do |address|
93
+ designated_resolver = ::Ddig::Ddr::DesignatedResolver.new(unencrypted_resolver: unencrypted_resolver, target: target, protocol: protocol, port: port, dohpath: dohpath, address: address.to_s, ip: :ipv6)
94
+ @designated_resolvers << designated_resolver
95
+ end
96
+ end
97
+
98
+ # ipv4hint
99
+ unless ipv4hint.nil?
100
+ ipv4hint.each do |address|
101
+ ip = :ipv4
102
+ designated_resolver = ::Ddig::Ddr::DesignatedResolver.new(unencrypted_resolver: unencrypted_resolver, target: target, protocol: protocol, port: port, dohpath: dohpath, address: address.to_s, ip: ip)
103
+ @designated_resolvers << designated_resolver
104
+ end
105
+ end
106
+
107
+ # ipv6hint
108
+ unless ipv6hint.nil?
109
+ ipv6hint.each do |address|
110
+ ip = :ipv6
111
+ designated_resolver = ::Ddig::Ddr::DesignatedResolver.new(unencrypted_resolver: unencrypted_resolver, target: target, protocol: protocol, port: port, dohpath: dohpath, address: address.to_s, ip: ip)
112
+ @designated_resolvers << designated_resolver
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ @designated_resolvers.uniq! { |designated_resolver| designated_resolver.uniq_key }
119
+ end
120
+
121
+ def verify_discovery
122
+ @designated_resolvers.map! do |designated_resolver|
123
+ designated_resolver.verify
124
+ designated_resolver
125
+ end
126
+ end
127
+
128
+ def set_nameservers
129
+ @nameservers = @nameserver.servers
130
+
131
+ if @ip == :ipv4
132
+ @nameservers = @nameserver.servers_ipv4
133
+ end
134
+
135
+ if @ip == :ipv6
136
+ @nameservers = @nameserver.servers_ipv6
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,65 @@
1
+ require 'resolv'
2
+
3
+ module Ddig
4
+ class Nameserver
5
+ attr_reader :servers
6
+
7
+ def initialize(nameservers: nil)
8
+ @nameservers = nameservers
9
+
10
+ if @nameservers.nil?
11
+ @servers = default_servers
12
+ elsif @nameservers.is_a?(Array)
13
+ @servers = @nameservers
14
+ else
15
+ @servers = [@nameservers]
16
+ end
17
+
18
+ validation_servers
19
+ end
20
+
21
+ def servers
22
+ if @servers.count.zero?
23
+ raise Ddig::Error.new('nameservers required')
24
+ end
25
+
26
+ @servers
27
+ end
28
+
29
+ def default_servers
30
+ Resolv::DNS::Config.default_config_hash[:nameserver]
31
+ end
32
+
33
+ def servers_ipv4
34
+ @servers.map do |nameserver|
35
+ if IPAddr.new(nameserver).ipv4?
36
+ nameserver
37
+ end
38
+ end.compact
39
+ end
40
+
41
+ def servers_ipv6
42
+ @servers.map do |nameserver|
43
+ if IPAddr.new(nameserver).ipv6?
44
+ nameserver
45
+ end
46
+ end.compact
47
+ end
48
+
49
+ def validation_servers
50
+ @servers.uniq.each do |server|
51
+ addr = IPAddr.new(server) rescue nil
52
+ if addr.nil?
53
+ puts "Warning: nameservers has invalid ip address (nameserver: #{server})"
54
+ @servers.delete(server)
55
+ end
56
+ end
57
+
58
+ if @servers.count.zero?
59
+ return false
60
+ end
61
+
62
+ return true
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,49 @@
1
+ require 'resolv'
2
+
3
+ module Ddig
4
+ module Resolver
5
+ # DNS Resolver of UDP/53
6
+ class Do53
7
+ attr_reader :hostname, :nameservers, :ip
8
+ attr_reader :a, :aaaa
9
+
10
+ def initialize(hostname:, nameservers: nil, ip: nil)
11
+ @hostname = hostname
12
+ @ip = ip
13
+
14
+ @nameserver = Ddig::Nameserver.new(nameservers: nameservers)
15
+ set_nameservers
16
+ end
17
+
18
+ def lookup
19
+ if @nameservers.empty?
20
+ return nil
21
+ end
22
+
23
+ @a = Resolv::DNS.open(nameserver: @nameservers) do |dns|
24
+ ress = dns.getresources(@hostname, Resolv::DNS::Resource::IN::A)
25
+ ress.map { |resource| resource.address.to_s }
26
+ end
27
+
28
+ @aaaa = Resolv::DNS.open(nameserver: @nameservers) do |dns|
29
+ ress = dns.getresources(@hostname, Resolv::DNS::Resource::IN::AAAA)
30
+ ress.map { |resource| resource.address.to_s }
31
+ end
32
+
33
+ self
34
+ end
35
+
36
+ def set_nameservers
37
+ @nameservers = @nameserver.servers
38
+
39
+ if @ip == :ipv4
40
+ @nameservers = @nameserver.servers_ipv4
41
+ end
42
+
43
+ if @ip == :ipv6
44
+ @nameservers = @nameserver.servers_ipv6
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,94 @@
1
+ require 'openssl'
2
+ require 'resolv'
3
+
4
+ module Ddig
5
+ module Resolver
6
+ # DNS over TLS/TCP
7
+ class Dot
8
+ attr_reader :hostname, :server, :server_name, :port
9
+ attr_reader :a, :aaaa
10
+
11
+ def initialize(hostname:, server:, server_name: nil, port: 853)
12
+ @hostname = hostname
13
+ @server = server
14
+ @server_name = server_name
15
+ @port = port
16
+
17
+ @open_timeout = 3
18
+ end
19
+
20
+ def lookup
21
+ if @server.nil?
22
+ return nil
23
+ end
24
+
25
+ @a = get_resources(@hostname, Resolv::DNS::Resource::IN::A).map { |resource| resource.address.to_s if resource.is_a?(Resolv::DNS::Resource::IN::A) }.compact
26
+
27
+ @aaaa = get_resources(@hostname, Resolv::DNS::Resource::IN::AAAA).map { |resource| resource.address.to_s if resource.is_a?(Resolv::DNS::Resource::IN::AAAA) }.compact
28
+
29
+ self
30
+ end
31
+
32
+ def get_resources(hostname, typeclass)
33
+ ssl_socket = get_socket
34
+
35
+ # send query
36
+ message = dns_message(hostname, typeclass)
37
+
38
+ request = [message.encode.length].pack('n') + message.encode
39
+ ssl_socket.write(request)
40
+
41
+ # recive answer
42
+ len = ssl_socket.read(2).unpack1('n')
43
+ response = Resolv::DNS::Message.decode(ssl_socket.read(len))
44
+
45
+ resources = response.answer.map { |name, ttl, resource| resource }
46
+
47
+ resources
48
+ end
49
+
50
+ def get_socket
51
+ begin
52
+ socket = Timeout.timeout(@open_timeout) {
53
+ TCPSocket.open(@server, @port)
54
+ }
55
+
56
+ ctx = OpenSSL::SSL::SSLContext.new
57
+ ctx.set_params
58
+ ctx.alpn_protocols = ['dot']
59
+
60
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ctx)
61
+ ssl_socket.sync_close = true
62
+ unless @server_name.nil?
63
+ ssl_socket.hostname = @server_name
64
+ end
65
+
66
+ # connect
67
+ Timeout.timeout(@open_timeout) {
68
+ ssl_socket.connect
69
+ unless @server_name.nil?
70
+ ssl_socket.post_connection_check(@server_name)
71
+ end
72
+ }
73
+
74
+ ssl_socket
75
+ end
76
+ end
77
+
78
+ def dns_message(hostname, typeclass)
79
+ if hostname.nil?
80
+ return nil
81
+ end
82
+ if typeclass.nil?
83
+ return nil
84
+ end
85
+
86
+ message = Resolv::DNS::Message.new
87
+ message.rd = 1 # recursive query
88
+ message.add_question(hostname, typeclass)
89
+
90
+ message
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ddig
4
+ VERSION = "0.1.0"
5
+ end
data/lib/ddig.rb ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ddig/version"
4
+ require_relative "ddig/nameserver"
5
+ require_relative "ddig/resolver/do53"
6
+ require_relative "ddig/resolver/dot"
7
+ require_relative "ddig/ddr"
8
+
9
+ module Ddig
10
+ class Error < StandardError; end
11
+
12
+ def self.lookup(hostname, nameservers: nil)
13
+ @hostname = hostname
14
+ @nameservers = nameservers
15
+
16
+ @nameserver = Ddig::Nameserver.new(nameservers: @nameservers)
17
+
18
+ @do53_ipv4 = Ddig::Resolver::Do53.new(hostname: @hostname, nameservers: @nameserver.servers, ip: :ipv4).lookup
19
+ @do53_ipv6 = Ddig::Resolver::Do53.new(hostname: @hostname, nameservers: @nameserver.servers, ip: :ipv6).lookup
20
+
21
+ @ddr = Ddig::Ddr.new(nameservers: @nameservers)
22
+
23
+ {
24
+ do53: {
25
+ ipv4: @do53_ipv4,
26
+ ipv6: @do53_ipv6,
27
+ },
28
+ ddr: @ddr.designated_resolvers
29
+ }
30
+ end
31
+ end
data/sig/ddig.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Ddig
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ddig
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Taketo Takashima
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-03-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: resolv
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.3.0
27
+ description: DNS lookup utility for Ruby
28
+ email:
29
+ - t.taketo1113@gmail.com
30
+ executables:
31
+ - ddig
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".rspec"
36
+ - LICENSE.txt
37
+ - README.md
38
+ - Rakefile
39
+ - exe/ddig
40
+ - lib/ddig.rb
41
+ - lib/ddig/cli.rb
42
+ - lib/ddig/ddr.rb
43
+ - lib/ddig/ddr/designated_resolver.rb
44
+ - lib/ddig/ddr/verify_cert.rb
45
+ - lib/ddig/nameserver.rb
46
+ - lib/ddig/resolver/do53.rb
47
+ - lib/ddig/resolver/dot.rb
48
+ - lib/ddig/version.rb
49
+ - sig/ddig.rbs
50
+ homepage: https://github.com/taketo1113/ddig
51
+ licenses:
52
+ - MIT
53
+ metadata:
54
+ homepage_uri: https://github.com/taketo1113/ddig
55
+ source_code_uri: https://github.com/taketo1113/ddig
56
+ changelog_uri: https://github.com/taketo1113/ddig/releases
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: 2.6.0
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubygems_version: 3.5.3
73
+ signing_key:
74
+ specification_version: 4
75
+ summary: DNS lookup utility for Ruby
76
+ test_files: []