ddig 0.1.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1d1bcdd660ab21b14d2e5572b27a50f2cc0fe541c64b9c6c8502f85d89a29b28
4
- data.tar.gz: 6462363f2b075b9aad78f88a055e1d53af1901054a22c3a010fcdfb0a28e9fde
3
+ metadata.gz: f89a50e96e5fb9468cb699e1f034c999ee1e063441d70a95e17aa2bb93b57878
4
+ data.tar.gz: fd2c8ede252e8b56756f0e5859f999aa3dadff4016bf005b41dbe388a291ff0d
5
5
  SHA512:
6
- metadata.gz: 785ef898b4f773668131d56ed8596b11de8bfbe9908cc0de7383a2b0cfbb3aaf613dc813b73284382aaa6bbb3d9e89d6e4a45e0d1205aba45361213d6764cc19
7
- data.tar.gz: c12f638d1899ddb47e6c482b22b315ea8912bce701243fef37a239566653e5e216c8ed01b910105bc06c28843e9d40ef81d653923e1b95a2e386f18c18f2fc72
6
+ metadata.gz: 44f6e9dd26e26964dd52d0ce0b9e94e60d66b9a9b1c4d7299ec49b130c20465e3a413058860895d272f89dec80108b8b9e6cde75236ba04c63b3207f536a783e
7
+ data.tar.gz: 19d7a1c4e7660b8cbb0f4aa5d7e4e10ac32a8c1b817e15646161d948e342c700b8c0de36cb931e70ded9cede816edbefb82f82dd57ae86c6026e1b3b6e6e9048
data/README.md CHANGED
@@ -7,10 +7,13 @@ ddig is DNS lookup utility for Ruby.
7
7
  - DNS Resolvers
8
8
  - UDP (Do53)
9
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
10
+ - https://www.rfc-editor.org/rfc/rfc7858.html
11
+ - DoH (DNS over HTTPS)
12
+ - Support only HTTP/1.1
13
+ - https://www.rfc-editor.org/rfc/rfc8484.html
14
+ - ~~DoQ (DNS over QUIC)~~
15
+ - Not Supported
16
+ - https://www.rfc-editor.org/rfc/rfc9250.html
14
17
  - DDR (Discovery of Designated Resolvers)
15
18
  - https://www.rfc-editor.org/rfc/rfc9462.html
16
19
 
@@ -25,6 +28,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
25
28
  $ gem install ddig
26
29
 
27
30
  ## Usage
31
+ ### Ruby
28
32
 
29
33
  ```ruby
30
34
  ddig = Ddig.lookup('dns.google', nameservers: ['8.8.8.8', '2001:4860:4860::8888'])
@@ -83,9 +87,153 @@ ddig[:ddr]
83
87
  ]
84
88
  ```
85
89
 
90
+ - Do53
91
+ ```ruby
92
+ do53 = Ddig::Resolver::Do53.new(hostname: 'dns.google', nameservers: '8.8.8.8').lookup
93
+ => #<Ddig::Resolver::Do53:0x0000000121717b78 @a=["8.8.8.8", "8.8.4.4"], @aaaa=["2001:4860:4860::8844", "2001:4860:4860::8888"], @hostname="dns.google", @ip=nil, @nameserver=#<Ddig::Nameserver:0x00000001211fb108 @nameservers="8.8.8.8", @servers=["8.8.8.8"]>, @nameservers=["8.8.8.8"]>
94
+
95
+ do53.a
96
+ => ["8.8.4.4", "8.8.8.8"]
97
+ do53.aaaa
98
+ => ["2001:4860:4860::8844", "2001:4860:4860::8888"]
99
+ ```
100
+
101
+ - DoT
102
+ ```ruby
103
+ dot = Ddig::Resolver::Dot.new(hostname: 'dns.google', server: '8.8.8.8').lookup
104
+ => #<Ddig::Resolver::Dot:0x000000012145da90 @a=["8.8.8.8", "8.8.4.4"], @aaaa=["2001:4860:4860::8844", "2001:4860:4860::8888"], @hostname="dns.google", @open_timeout=3, @port=853, @server="8.8.8.8", @server_name=nil>
105
+
106
+ dot.a
107
+ => ["8.8.4.4", "8.8.8.8"]
108
+ dot.aaaa
109
+ => ["2001:4860:4860::8844", "2001:4860:4860::8888"]
110
+ ```
111
+
112
+ - DoH (HTTP/1.1)
113
+ ```ruby
114
+ doh = Ddig::Resolver::DohH1.new(hostname: 'dns.google', server: 'dns.google', dohpath: '/dns-query{?dns}').lookup
115
+ => #<Ddig::Resolver::DohH1:0x00000001023ed020 @a=["8.8.4.4", "8.8.8.8"], @aaaa=["2001:4860:4860::8888", "2001:4860:4860::8844"], @address=nil, @dohpath="/dns-query{?dns}", @hostname="dns.google", @open_timeout=10, @port=443, @server="dns.google">
116
+
117
+ doh.a
118
+ => ["8.8.4.4", "8.8.8.8"]
119
+ doh.aaaa
120
+ => ["2001:4860:4860::8844", "2001:4860:4860::8888"]
121
+ ```
122
+
86
123
  ### CLI
124
+ - Usage
125
+ ```
126
+ $ ddig --help
127
+ Usage: ddig [options] hostname
128
+ -d, --dns-type={all|do53|dot} resolve type (default: all)
129
+ --udp use resolve type of udp(do53)
130
+ --dot use resolve type of dot
131
+ --doh-h1 use resolve type of doh (http/1.1)
132
+ --doh-path=doh-path doh service path
133
+ --ddr discover designated resolvers via ddr (discovery of designated resolvers)
134
+ -4, --ipv4 use IPv4 query transport only
135
+ -6, --ipv6 use IPv6 query transport only
136
+ -@ipaddress|doh-hostname, nameserver
137
+ --nameserver
138
+ -p, --port=port port
139
+ --format={text|json} output format (default: text)
140
+
141
+ -v, --verbose run verbosely
142
+ -h, --help show this help message.
143
+ --version show version.
144
+ ```
145
+
146
+ ```sh
147
+ $ ddig --nameserver 8.8.8.8 dns.google
148
+ # Do53 (IPv4)
149
+ dns.google A 8.8.4.4
150
+ dns.google A 8.8.8.8
151
+ dns.google AAAA 2001:4860:4860::8844
152
+ dns.google AAAA 2001:4860:4860::8888
153
+
154
+ # SERVER: 8.8.8.8
155
+
156
+ # DDR
157
+ ## DDR (#0) - dot: dns.google:853 (8.8.4.4), unencrypted_resolver: 8.8.8.8, verify cert: true
158
+ dns.google A 8.8.4.4
159
+ dns.google A 8.8.8.8
160
+ dns.google AAAA 2001:4860:4860::8844
161
+ dns.google AAAA 2001:4860:4860::8888
162
+
163
+ # SERVER(Address): 8.8.4.4
164
+ # PORT: 853
165
+
166
+ ...
167
+
168
+ # SERVER(Address): 2001:4860:4860::8888
169
+ # PORT: 853
170
+
171
+ ## DDR (#6) - h2: dns.google:443 (2001:4860:4860::8888), path: /dns-query{?dns}, unencrypted_resolver: 8.8.8.8, verify cert: true
172
+ dns.google A 8.8.8.8
173
+ dns.google A 8.8.4.4
174
+ dns.google AAAA 2001:4860:4860::8844
175
+ dns.google AAAA 2001:4860:4860::8888
176
+
177
+ # SERVER(Hostname): 2001:4860:4860::8888
178
+ # SERVER(Path): /dns-query{?dns}
179
+ # PORT: 443
180
+ ...
181
+ ```
182
+
183
+ - UDP(Do53)
184
+ ```sh
185
+ $ ddig --udp --nameserver 8.8.8.8 dns.google
186
+ dns.google A 8.8.8.8
187
+ dns.google A 8.8.4.4
188
+ dns.google AAAA 2001:4860:4860::8844
189
+ dns.google AAAA 2001:4860:4860::8888
190
+
191
+ # SERVER: 8.8.8.8
192
+ ```
193
+
194
+ - DoT
195
+ ```sh
196
+ $ ddig --dot --nameserver 8.8.8.8 dns.google
197
+ dns.google A 8.8.8.8
198
+ dns.google A 8.8.4.4
199
+ dns.google AAAA 2001:4860:4860::8888
200
+ dns.google AAAA 2001:4860:4860::8844
87
201
 
88
- (TBD)
202
+ # SERVER(Address): 8.8.8.8
203
+ # PORT: 853
204
+ ```
205
+
206
+ - DoH (HTTP/1.1)
207
+ ```sh
208
+ $ ddig --doh-h1 --nameserver dns.google --doh-path /dns-query{?dns} dns.google
209
+ dns.google A 8.8.8.8
210
+ dns.google A 8.8.4.4
211
+ dns.google AAAA 2001:4860:4860::8888
212
+ dns.google AAAA 2001:4860:4860::8844
213
+
214
+ # SERVER(Hostname): dns.google
215
+ # SERVER(Path): /dns-query{?dns}
216
+ # PORT: 443
217
+ ```
218
+
219
+ - DDR (Discovery of Designated Resolvers)
220
+ ```sh
221
+ $ ddig --ddr --nameserver 8.8.8.8
222
+ dot: dns.google:853 (8.8.8.8), unencrypted_resolver: 8.8.8.8, verify cert: true
223
+ dot: dns.google:853 (8.8.4.4), unencrypted_resolver: 8.8.8.8, verify cert: true
224
+ dot: dns.google:853 (2001:4860:4860::8844), unencrypted_resolver: 8.8.8.8, verify cert: true
225
+ dot: dns.google:853 (2001:4860:4860::8888), unencrypted_resolver: 8.8.8.8, verify cert: true
226
+ h2: dns.google:443 (8.8.8.8), path: /dns-query{?dns}, unencrypted_resolver: 8.8.8.8, verify cert: true
227
+ h2: dns.google:443 (8.8.4.4), path: /dns-query{?dns}, unencrypted_resolver: 8.8.8.8, verify cert: true
228
+ h2: dns.google:443 (2001:4860:4860::8844), path: /dns-query{?dns}, unencrypted_resolver: 8.8.8.8, verify cert: true
229
+ h2: dns.google:443 (2001:4860:4860::8888), path: /dns-query{?dns}, unencrypted_resolver: 8.8.8.8, verify cert: true
230
+ h3: dns.google:443 (8.8.4.4), path: /dns-query{?dns}, unencrypted_resolver: 8.8.8.8, verify cert: true
231
+ h3: dns.google:443 (8.8.8.8), path: /dns-query{?dns}, unencrypted_resolver: 8.8.8.8, verify cert: true
232
+ h3: dns.google:443 (2001:4860:4860::8888), path: /dns-query{?dns}, unencrypted_resolver: 8.8.8.8, verify cert: true
233
+ h3: dns.google:443 (2001:4860:4860::8844), path: /dns-query{?dns}, unencrypted_resolver: 8.8.8.8, verify cert: true
234
+
235
+ # SERVER: 8.8.8.8
236
+ ```
89
237
 
90
238
  ## Development
91
239
 
data/lib/ddig/cli.rb CHANGED
@@ -7,12 +7,13 @@ module Ddig
7
7
  def initialize(args)
8
8
  @args = args
9
9
  @options = {
10
+ dns_type: 'all',
10
11
  format: 'text',
11
12
  }
12
13
 
13
14
  parse_options
14
15
 
15
- if @hostname.nil?
16
+ unless valid_options?
16
17
  puts @option_parser
17
18
  exit
18
19
  end
@@ -22,7 +23,16 @@ module Ddig
22
23
  @option_parser = OptionParser.new do |opts|
23
24
  opts.banner = "Usage: ddig [options] hostname"
24
25
 
25
- opts.on("--nameserver=ipaddress", "nameserver ip address") { |v| @options[:nameserver] = v }
26
+ opts.on("-d", "--dns-type={all|do53|dot|doh_h1}", "resolve type (default: all)") { |v| @options[:dns_type] = v }
27
+ opts.on("--udp", "use resolve type of udp(do53)") { |v| @options[:dns_type] = 'do53' }
28
+ opts.on("--dot", "use resolve type of dot") { |v| @options[:dns_type] = 'dot' }
29
+ opts.on("--doh-h1", "use resolve type of doh (http/1.1)") { |v| @options[:dns_type] = 'doh_h1' }
30
+ opts.on("--doh-path=doh-path", "doh service path") { |v| @options[:doh_path] = v }
31
+ opts.on("--ddr", "discover designated resolvers via ddr (discovery of designated resolvers)") { |v| @options[:ddr] = v }
32
+ opts.on("-4", "--ipv4", "use IPv4 query transport only") { |v| @options[:ipv4] = v }
33
+ opts.on("-6", "--ipv6", "use IPv6 query transport only") { |v| @options[:ipv6] = v }
34
+ opts.on("-@", "--nameserver=ipaddress|doh-hostname", "nameserver") { |v| @options[:nameserver] = v }
35
+ opts.on("-p", "--port=port", "port") { |v| @options[:port] = v }
26
36
  opts.on("--format={text|json}", "output format (default: text)") { |v| @options[:format] = v }
27
37
 
28
38
  opts.separator ""
@@ -37,19 +47,104 @@ module Ddig
37
47
  @hostname = @args[0]
38
48
  end
39
49
 
50
+ def valid_options?
51
+ if @hostname.nil?
52
+ if @options[:ddr]
53
+ return true
54
+ end
55
+
56
+ return false
57
+ end
58
+
59
+ return true
60
+ end
61
+
40
62
  def exec
41
- @ddig = Ddig.lookup(@hostname, nameservers: [@options[:nameserver]])
63
+ if @options[:ipv4] || @options[:ipv6]
64
+ @use_ipv4 = @options[:ipv4] || false
65
+ @use_ipv6 = @options[:ipv6] || false
66
+ end
67
+
68
+ if @options[:ddr]
69
+ resolve_ddr
70
+ exit
71
+ end
72
+
73
+ case @options[:dns_type]
74
+ when "all"
75
+ resolve_all
76
+ when "do53"
77
+ resolve_do53
78
+ when "dot"
79
+ resolve_dot
80
+ when "doh_h1"
81
+ resolve_doh_h1
82
+ end
83
+ end
84
+
85
+ def resolve_all
86
+ @ddig = Ddig.lookup(@hostname, nameservers: @options[:nameserver], use_ipv4: @use_ipv4, use_ipv6: @use_ipv6)
42
87
 
43
88
  if @options[:format] == 'json'
44
89
  # TODO: to_json
45
- puts @ddig
46
90
  else
47
- print_result
91
+ unless @ddig[:do53][:ipv4].nil?
92
+ puts "# Do53 (IPv4)"
93
+ @ddig[:do53][:ipv4].to_cli
94
+ puts
95
+ end
96
+
97
+ unless @ddig[:do53][:ipv6].nil?
98
+ puts "# Do53 (IPv6)"
99
+ @ddig[:do53][:ipv6].to_cli
100
+ puts
101
+ end
102
+
103
+ unless @ddig[:ddr].nil?
104
+ puts "# DDR"
105
+ @ddig[:ddr].each_with_index do |designated_resolver, index|
106
+ puts "## DDR (##{index}) - #{designated_resolver.to_s}"
107
+ designated_resolver.to_cli
108
+ puts
109
+ end
110
+ end
48
111
  end
49
112
  end
50
113
 
51
- def print_result
52
- puts @ddig
114
+ def resolve_do53
115
+ ip = Ddig::Ip.new(use_ipv4: @use_ipv4, use_ipv6: @use_ipv6)
116
+ do53 = Ddig::Resolver::Do53.new(hostname: @hostname, nameservers: @options[:nameserver], ip: ip.ip_type).lookup
117
+
118
+ if do53.nil?
119
+ puts "Error: Could not lookup wit nameserver: #{@options[:nameserver]}"
120
+ exit
121
+ end
122
+
123
+ do53.to_cli
124
+ end
125
+
126
+ def resolve_dot
127
+ dot = Ddig::Resolver::Dot.new(hostname: @hostname, server: @options[:nameserver], port: @options[:port]).lookup
128
+
129
+ dot.to_cli
130
+ end
131
+
132
+ def resolve_doh_h1
133
+ if @options[:nameserver].nil? || @options[:doh_path].nil?
134
+ puts 'ddig: doh needs option of --doh-path=doh-path'
135
+ exit
136
+ end
137
+
138
+ doh = Ddig::Resolver::DohH1.new(hostname: @hostname, server: @options[:nameserver], dohpath: @options[:doh_path], port: @options[:port]).lookup
139
+
140
+ doh.to_cli
141
+ end
142
+
143
+ def resolve_ddr
144
+ ip = Ddig::Ip.new(use_ipv4: @use_ipv4, use_ipv6: @use_ipv6)
145
+ ddr = Ddig::Ddr.new(nameservers: @options[:nameserver], ip: ip.ip_type)
146
+
147
+ ddr.to_cli
53
148
  end
54
149
  end
55
150
  end
@@ -3,6 +3,7 @@ module Ddig
3
3
  class DesignatedResolver
4
4
  attr_reader :unencrypted_resolver, :target, :protocol, :port, :dohpath, :address, :ip
5
5
  attr_reader :verify_cert
6
+ attr_reader :hostname, :a, :aaaa, :resolver, :errors
6
7
 
7
8
  PROTOCOLS = ['http/1.1', 'h2', 'h3', 'dot', 'doq']
8
9
 
@@ -30,6 +31,48 @@ module Ddig
30
31
  @verify_cert.verify
31
32
  end
32
33
 
34
+ def lookup(hostname)
35
+ @hostname = hostname
36
+ @errors = []
37
+
38
+ case @protocol
39
+ when 'dot'
40
+ @resolver = Ddig::Resolver::Dot.new(hostname: @hostname, server: @address, server_name: @target, port: @port).lookup
41
+
42
+ unless @resolver.nil?
43
+ @a = @resolver.a
44
+ @aaaa = @resolver.aaaa
45
+
46
+ return self
47
+ end
48
+
49
+ when 'http/1.1', 'h2', 'h3'
50
+ @resolver = Ddig::Resolver::DohH1.new(hostname: @hostname, server: @address, address: @address, dohpath: @dohpath, port: @port).lookup
51
+
52
+ unless @resolver.nil?
53
+ @a = @resolver.a
54
+ @aaaa = @resolver.aaaa
55
+
56
+ return self
57
+ end
58
+
59
+ when 'doq'
60
+ @errors << "#{@protocol} is not supportted protocol"
61
+ end
62
+ end
63
+
64
+ def to_cli
65
+ @resolver.to_cli
66
+ end
67
+
68
+ def to_s
69
+ if ['http/1.1', 'h2', 'h3'].include?(@protocol)
70
+ "#{@protocol}: #{@target}:#{@port} (#{@address}),\tpath: #{@dohpath},\tunencrypted_resolver: #{@unencrypted_resolver}, \tverify cert: #{@verify_cert.verify}"
71
+ else
72
+ "#{@protocol}: #{@target}:#{@port} (#{@address}),\tunencrypted_resolver: #{@unencrypted_resolver}, \tverify cert: #{@verify_cert.verify}"
73
+ end
74
+ end
75
+
33
76
  # Set default port by protocol
34
77
  # ref: https://www.rfc-editor.org/rfc/rfc9461.html#section-4.2
35
78
  def set_default_port
data/lib/ddig/ddr.rb CHANGED
@@ -68,35 +68,39 @@ module Ddig
68
68
  do53_v6 = ::Ddig::Resolver::Do53.new(hostname: target, nameservers: [unencrypted_resolver], ip: :ipv6).lookup
69
69
 
70
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
71
+ unless @ip == :ipv6
72
+ unless do53_v4.nil? || do53_v4.a.nil?
73
+ do53_v4.a.each do |address|
74
+ designated_resolver = ::Ddig::Ddr::DesignatedResolver.new(unencrypted_resolver: unencrypted_resolver, target: target, protocol: protocol, port: port, dohpath: dohpath, address: address.to_s, ip: :ipv4)
75
+ @designated_resolvers << designated_resolver
76
+ end
75
77
  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
78
+ unless do53_v6.nil? || do53_v6.a.nil?
79
+ do53_v6.a.each do |address|
80
+ designated_resolver = ::Ddig::Ddr::DesignatedResolver.new(unencrypted_resolver: unencrypted_resolver, target: target, protocol: protocol, port: port, dohpath: dohpath, address: address.to_s, ip: :ipv4)
81
+ @designated_resolvers << designated_resolver
82
+ end
81
83
  end
82
84
  end
83
85
 
84
86
  # 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
87
+ unless @ip == :ipv4
88
+ unless do53_v4.nil? || do53_v4.aaaa.nil?
89
+ do53_v4.aaaa.each do |address|
90
+ designated_resolver = ::Ddig::Ddr::DesignatedResolver.new(unencrypted_resolver: unencrypted_resolver, target: target, protocol: protocol, port: port, dohpath: dohpath, address: address.to_s, ip: :ipv6)
91
+ @designated_resolvers << designated_resolver
92
+ end
89
93
  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
94
+ unless do53_v6.nil? || do53_v6.aaaa.nil?
95
+ do53_v6.aaaa.each do |address|
96
+ designated_resolver = ::Ddig::Ddr::DesignatedResolver.new(unencrypted_resolver: unencrypted_resolver, target: target, protocol: protocol, port: port, dohpath: dohpath, address: address.to_s, ip: :ipv6)
97
+ @designated_resolvers << designated_resolver
98
+ end
95
99
  end
96
100
  end
97
101
 
98
102
  # ipv4hint
99
- unless ipv4hint.nil?
103
+ unless ipv4hint.nil? || @ip == :ipv6
100
104
  ipv4hint.each do |address|
101
105
  ip = :ipv4
102
106
  designated_resolver = ::Ddig::Ddr::DesignatedResolver.new(unencrypted_resolver: unencrypted_resolver, target: target, protocol: protocol, port: port, dohpath: dohpath, address: address.to_s, ip: ip)
@@ -105,7 +109,7 @@ module Ddig
105
109
  end
106
110
 
107
111
  # ipv6hint
108
- unless ipv6hint.nil?
112
+ unless ipv6hint.nil? || @ip == :ipv4
109
113
  ipv6hint.each do |address|
110
114
  ip = :ipv6
111
115
  designated_resolver = ::Ddig::Ddr::DesignatedResolver.new(unencrypted_resolver: unencrypted_resolver, target: target, protocol: protocol, port: port, dohpath: dohpath, address: address.to_s, ip: ip)
@@ -125,6 +129,15 @@ module Ddig
125
129
  end
126
130
  end
127
131
 
132
+ def to_cli
133
+ @designated_resolvers.each do |designated_resolver|
134
+ puts designated_resolver.to_s
135
+ end
136
+
137
+ puts
138
+ puts "# SERVER: #{@nameservers.join(', ')}"
139
+ end
140
+
128
141
  def set_nameservers
129
142
  @nameservers = @nameserver.servers
130
143
 
data/lib/ddig/ip.rb ADDED
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ddig
4
+ class Ip
5
+ attr_reader :ip_type
6
+
7
+ def initialize(use_ipv4: nil, use_ipv6: nil)
8
+ @use_ipv4 = use_ipv4
9
+ @use_ipv6 = use_ipv6
10
+
11
+ set_ip_type
12
+ end
13
+
14
+ def set_ip_type
15
+ if @use_ipv4.nil? && self.class.enable_ipv4?
16
+ @use_ipv4 = true
17
+ end
18
+
19
+ if @use_ipv6.nil? && self.class.enable_ipv6?
20
+ @use_ipv6 = true
21
+ end
22
+
23
+ if @use_ipv4 && @use_ipv6
24
+ @ip_type = :all
25
+ elsif @use_ipv4
26
+ @ip_type = :ipv4
27
+ elsif @use_ipv6
28
+ @ip_type = :ipv6
29
+ end
30
+ end
31
+
32
+ def self.enable_ipv4?
33
+ ip_list.any? { |addr| addr.ipv4? }
34
+ end
35
+
36
+ def self.enable_ipv6?
37
+ ip_list.any? { |addr| addr.ipv6? }
38
+ end
39
+
40
+ private
41
+
42
+ def self.ip_list
43
+ Socket.ip_address_list.map do |addrinfo|
44
+ if RUBY_VERSION < '3.1'
45
+ # for ipaddr gem <= v1.2.2, Not support zone identifiers in IPv6 addresses
46
+ addr = IPAddr.new(addrinfo.ip_address.split('%').first)
47
+ else
48
+ addr = IPAddr.new(addrinfo.ip_address)
49
+ end
50
+
51
+ if !addr.loopback? && !addr.link_local?
52
+ addr
53
+ end
54
+ end.compact
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,40 @@
1
+ require 'resolv'
2
+
3
+ module Ddig
4
+ module Resolver
5
+ class DnsMessage
6
+ def self.encode(hostname, typeclass)
7
+ if hostname.nil?
8
+ return nil
9
+ end
10
+ if typeclass.nil?
11
+ return nil
12
+ end
13
+
14
+ message = Resolv::DNS::Message.new
15
+ message.rd = 1 # recursive query
16
+ message.add_question(hostname, typeclass)
17
+
18
+ message.encode
19
+ end
20
+
21
+ def self.decode(payload)
22
+ if payload.nil?
23
+ return nil
24
+ end
25
+
26
+ Resolv::DNS::Message.decode(payload)
27
+ end
28
+
29
+ def self.getresources(payload)
30
+ if payload.nil?
31
+ return []
32
+ end
33
+
34
+ response = self.decode(payload)
35
+
36
+ return response.answer.map { |name, ttl, resource| resource }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -33,6 +33,20 @@ module Ddig
33
33
  self
34
34
  end
35
35
 
36
+ def to_cli
37
+ @a.each do |address|
38
+ rr_type = 'A'
39
+ puts "#{@hostname}\t#{rr_type}\t#{address}"
40
+ end
41
+ @aaaa.each do |address|
42
+ rr_type = 'AAAA'
43
+ puts "#{@hostname}\t#{rr_type}\t#{address}"
44
+ end
45
+
46
+ puts
47
+ puts "# SERVER: #{@nameservers.join(', ')}"
48
+ end
49
+
36
50
  def set_nameservers
37
51
  @nameservers = @nameserver.servers
38
52
 
@@ -0,0 +1,77 @@
1
+ require 'net/http'
2
+ require 'resolv'
3
+ require 'base64'
4
+
5
+ require_relative 'dns_message'
6
+
7
+ module Ddig
8
+ module Resolver
9
+ # DNS over HTTPS (HTTP/1.1)
10
+ class DohH1
11
+ attr_reader :hostname, :server, :address, :dohpath, :port
12
+ attr_reader :a, :aaaa
13
+
14
+ def initialize(hostname:, server:, address: nil, dohpath:, port: 443)
15
+ @hostname = hostname
16
+ @server = server
17
+ @address = address
18
+ @dohpath = dohpath
19
+ @port = port || 443
20
+
21
+ @open_timeout = 10
22
+ end
23
+
24
+ def lookup
25
+ if @server.nil?
26
+ return nil
27
+ end
28
+
29
+ @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
30
+
31
+ @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
32
+
33
+ self
34
+ end
35
+
36
+ def get_resources(hostname, typeclass)
37
+ # send query
38
+ payload = DnsMessage.encode(hostname, typeclass)
39
+
40
+ path_with_query = @dohpath.gsub('{?dns}', '?dns=' + Base64.urlsafe_encode64(payload, padding: false))
41
+
42
+ http_response = Net::HTTP.start(@server, @port, use_ssl: true, ipaddr: @address) do |http|
43
+ header = {}
44
+ header['Accept'] = 'application/dns-message'
45
+ #http.open_timeout = @open_timeout
46
+
47
+ http.get(path_with_query, header)
48
+ end
49
+
50
+ case http_response
51
+ when Net::HTTPSuccess
52
+ # recive answer
53
+ return DnsMessage.getresources(http_response.body)
54
+ else
55
+ http_response.value
56
+ return []
57
+ end
58
+ end
59
+
60
+ def to_cli
61
+ @a.each do |address|
62
+ rr_type = 'A'
63
+ puts "#{@hostname}\t#{rr_type}\t#{address}"
64
+ end
65
+ @aaaa.each do |address|
66
+ rr_type = 'AAAA'
67
+ puts "#{@hostname}\t#{rr_type}\t#{address}"
68
+ end
69
+
70
+ puts
71
+ puts "# SERVER(Hostname): #{@server}"
72
+ puts "# SERVER(Path): #{@dohpath}"
73
+ puts "# PORT: #{@port}"
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,6 +1,8 @@
1
1
  require 'openssl'
2
2
  require 'resolv'
3
3
 
4
+ require_relative 'dns_message'
5
+
4
6
  module Ddig
5
7
  module Resolver
6
8
  # DNS over TLS/TCP
@@ -12,7 +14,7 @@ module Ddig
12
14
  @hostname = hostname
13
15
  @server = server
14
16
  @server_name = server_name
15
- @port = port
17
+ @port = port || 853
16
18
 
17
19
  @open_timeout = 3
18
20
  end
@@ -33,16 +35,14 @@ module Ddig
33
35
  ssl_socket = get_socket
34
36
 
35
37
  # send query
36
- message = dns_message(hostname, typeclass)
38
+ payload = DnsMessage.encode(hostname, typeclass)
39
+ request = [payload.length].pack('n') + payload
37
40
 
38
- request = [message.encode.length].pack('n') + message.encode
39
41
  ssl_socket.write(request)
40
42
 
41
43
  # recive answer
42
44
  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 }
45
+ resources = DnsMessage.getresources(ssl_socket.read(len))
46
46
 
47
47
  resources
48
48
  end
@@ -75,19 +75,20 @@ module Ddig
75
75
  end
76
76
  end
77
77
 
78
- def dns_message(hostname, typeclass)
79
- if hostname.nil?
80
- return nil
78
+ def to_cli
79
+ @a.each do |address|
80
+ rr_type = 'A'
81
+ puts "#{@hostname}\t#{rr_type}\t#{address}"
81
82
  end
82
- if typeclass.nil?
83
- return nil
83
+ @aaaa.each do |address|
84
+ rr_type = 'AAAA'
85
+ puts "#{@hostname}\t#{rr_type}\t#{address}"
84
86
  end
85
87
 
86
- message = Resolv::DNS::Message.new
87
- message.rd = 1 # recursive query
88
- message.add_question(hostname, typeclass)
89
-
90
- message
88
+ puts
89
+ puts "# SERVER(Address): #{@server}"
90
+ #puts "# SERVER(Hostname): #{@server_name}"
91
+ puts "# PORT: #{@port}"
91
92
  end
92
93
  end
93
94
  end
data/lib/ddig/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ddig
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/ddig.rb CHANGED
@@ -2,23 +2,31 @@
2
2
 
3
3
  require_relative "ddig/version"
4
4
  require_relative "ddig/nameserver"
5
+ require_relative "ddig/ip"
5
6
  require_relative "ddig/resolver/do53"
6
7
  require_relative "ddig/resolver/dot"
8
+ require_relative "ddig/resolver/doh_h1"
7
9
  require_relative "ddig/ddr"
8
10
 
9
11
  module Ddig
10
12
  class Error < StandardError; end
11
13
 
12
- def self.lookup(hostname, nameservers: nil)
14
+ def self.lookup(hostname, nameservers: nil, use_ipv4: nil, use_ipv6: nil)
13
15
  @hostname = hostname
14
16
  @nameservers = nameservers
17
+ @use_ipv4 = use_ipv4
18
+ @use_ipv6 = use_ipv6
15
19
 
16
20
  @nameserver = Ddig::Nameserver.new(nameservers: @nameservers)
21
+ @ip = Ddig::Ip.new(use_ipv4: @use_ipv4, use_ipv6: @use_ipv6)
17
22
 
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
23
+ @do53_ipv4 = Ddig::Resolver::Do53.new(hostname: @hostname, nameservers: @nameserver.servers, ip: :ipv4).lookup unless @ip.ip_type == :ipv6
24
+ @do53_ipv6 = Ddig::Resolver::Do53.new(hostname: @hostname, nameservers: @nameserver.servers, ip: :ipv6).lookup unless @ip.ip_type == :ipv4
20
25
 
21
- @ddr = Ddig::Ddr.new(nameservers: @nameservers)
26
+ @ddr = Ddig::Ddr.new(nameservers: @nameservers, ip: @ip.ip_type)
27
+ @ddr.designated_resolvers.each do |designated_resolver|
28
+ designated_resolver.lookup(@hostname)
29
+ end
22
30
 
23
31
  {
24
32
  do53: {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddig
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taketo Takashima
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-03-16 00:00:00.000000000 Z
11
+ date: 2024-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: resolv
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: base64
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'
27
41
  description: DNS lookup utility for Ruby
28
42
  email:
29
43
  - t.taketo1113@gmail.com
@@ -42,8 +56,11 @@ files:
42
56
  - lib/ddig/ddr.rb
43
57
  - lib/ddig/ddr/designated_resolver.rb
44
58
  - lib/ddig/ddr/verify_cert.rb
59
+ - lib/ddig/ip.rb
45
60
  - lib/ddig/nameserver.rb
61
+ - lib/ddig/resolver/dns_message.rb
46
62
  - lib/ddig/resolver/do53.rb
63
+ - lib/ddig/resolver/doh_h1.rb
47
64
  - lib/ddig/resolver/dot.rb
48
65
  - lib/ddig/version.rb
49
66
  - sig/ddig.rbs