gitlab-net-dns 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.gitlab-ci.yml +20 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +3 -0
  6. data/.rubocop_defaults.yml +359 -0
  7. data/.rubocop_todo.yml +207 -0
  8. data/.travis.yml +13 -0
  9. data/CHANGELOG.md +113 -0
  10. data/Gemfile +9 -0
  11. data/LICENSE.txt +56 -0
  12. data/README.md +172 -0
  13. data/Rakefile +38 -0
  14. data/THANKS.rdoc +24 -0
  15. data/bin/console +14 -0
  16. data/demo/check_soa.rb +104 -0
  17. data/demo/threads.rb +18 -0
  18. data/gitlab-net-dns.gemspec +24 -0
  19. data/lib/net/dns.rb +104 -0
  20. data/lib/net/dns/header.rb +705 -0
  21. data/lib/net/dns/names.rb +120 -0
  22. data/lib/net/dns/packet.rb +560 -0
  23. data/lib/net/dns/question.rb +185 -0
  24. data/lib/net/dns/resolver.rb +1214 -0
  25. data/lib/net/dns/resolver/socks.rb +148 -0
  26. data/lib/net/dns/resolver/timeouts.rb +70 -0
  27. data/lib/net/dns/rr.rb +356 -0
  28. data/lib/net/dns/rr/a.rb +114 -0
  29. data/lib/net/dns/rr/aaaa.rb +94 -0
  30. data/lib/net/dns/rr/classes.rb +130 -0
  31. data/lib/net/dns/rr/cname.rb +74 -0
  32. data/lib/net/dns/rr/hinfo.rb +96 -0
  33. data/lib/net/dns/rr/mr.rb +70 -0
  34. data/lib/net/dns/rr/mx.rb +82 -0
  35. data/lib/net/dns/rr/ns.rb +70 -0
  36. data/lib/net/dns/rr/null.rb +50 -0
  37. data/lib/net/dns/rr/ptr.rb +77 -0
  38. data/lib/net/dns/rr/soa.rb +75 -0
  39. data/lib/net/dns/rr/srv.rb +41 -0
  40. data/lib/net/dns/rr/txt.rb +58 -0
  41. data/lib/net/dns/rr/types.rb +191 -0
  42. data/lib/net/dns/version.rb +8 -0
  43. data/spec/fixtures/resolv.conf +4 -0
  44. data/spec/spec_helper.rb +14 -0
  45. data/spec/unit/resolver/dns_timeout_spec.rb +36 -0
  46. data/spec/unit/resolver/tcp_timeout_spec.rb +46 -0
  47. data/spec/unit/resolver/udp_timeout_spec.rb +46 -0
  48. data/test/test_helper.rb +13 -0
  49. data/test/unit/header_test.rb +164 -0
  50. data/test/unit/names_test.rb +21 -0
  51. data/test/unit/packet_test.rb +47 -0
  52. data/test/unit/question_test.rb +81 -0
  53. data/test/unit/resolver_test.rb +114 -0
  54. data/test/unit/rr/a_test.rb +106 -0
  55. data/test/unit/rr/aaaa_test.rb +102 -0
  56. data/test/unit/rr/classes_test.rb +83 -0
  57. data/test/unit/rr/cname_test.rb +90 -0
  58. data/test/unit/rr/hinfo_test.rb +111 -0
  59. data/test/unit/rr/mr_test.rb +99 -0
  60. data/test/unit/rr/mx_test.rb +106 -0
  61. data/test/unit/rr/ns_test.rb +80 -0
  62. data/test/unit/rr/types_test.rb +71 -0
  63. data/test/unit/rr_test.rb +127 -0
  64. metadata +172 -0
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems' if RUBY_VERSION.to_s < "1.9.0"
4
+ require 'net/dns'
5
+
6
+ #------------------------------------------------------------------------------
7
+ # Get the domain from the command line.
8
+ #------------------------------------------------------------------------------
9
+
10
+ raise ArgumentError, "Usage: check_soa.rb domain\n" unless ARGV.size == 1
11
+
12
+ domain = ARGV[0]
13
+
14
+ #------------------------------------------------------------------------------
15
+ # Find all the nameservers for the domain.
16
+ #------------------------------------------------------------------------------
17
+
18
+ res = Net::DNS::Resolver.new(defname: false, retry: 2)
19
+
20
+ ns_req = res.query(domain, Net::DNS::NS)
21
+ unless ns_req && (ns_req.header.anCount > 0)
22
+ raise ArgumentError, "No nameservers found for domain: #{res.errorstring}"
23
+ end
24
+
25
+ # Send out non-recursive queries
26
+ res.recurse = false
27
+ # Do not buffer standard out
28
+ # | = 1;
29
+
30
+ #------------------------------------------------------------------------------
31
+ # Check the SOA record on each nameserver.
32
+ #------------------------------------------------------------------------------
33
+
34
+ ns_req.each_nameserver do |ns|
35
+ #----------------------------------------------------------------------
36
+ # Set the resolver to query this nameserver.
37
+ #----------------------------------------------------------------------
38
+
39
+ # In order to lookup the IP(s) of the nameserver, we need a Resolver
40
+ # object that is set to our local, recursive nameserver. So we create
41
+ # a new object just to do that.
42
+
43
+ local_res = Net::DNS::Resolver.new
44
+
45
+ a_req = local_res.query(ns, Net::DNS::A)
46
+
47
+ unless a_req
48
+ puts "Can not find address for ns: " + res.errorstring + "\n"
49
+ next
50
+ end
51
+
52
+ a_req.each_address do |ip|
53
+ #----------------------------------------------------------------------
54
+ # Ask this IP.
55
+ #----------------------------------------------------------------------
56
+ res.nameservers = ip
57
+
58
+ print "#{ns} (#{ip}): "
59
+
60
+ #----------------------------------------------------------------------
61
+ # Get the SOA record.
62
+ #----------------------------------------------------------------------
63
+
64
+ soa_req = res.send(domain, Net::DNS::SOA, Net::DNS::IN)
65
+
66
+ if soa_req.nil?
67
+ puts res.errorstring, "\n"
68
+ next
69
+ end
70
+
71
+ #----------------------------------------------------------------------
72
+ # Is this nameserver authoritative for the domain?
73
+ #----------------------------------------------------------------------
74
+
75
+ unless soa_req.header.auth?
76
+ print "isn't authoritative for domain\n"
77
+ next
78
+ end
79
+
80
+ #----------------------------------------------------------------------
81
+ # We should have received exactly one answer.
82
+ #----------------------------------------------------------------------
83
+
84
+ unless soa_req.header.anCount == 1
85
+ print "expected 1 answer, got " + soa_req.header.anCount.to_s + "\n"
86
+ next
87
+ end
88
+
89
+ #----------------------------------------------------------------------
90
+ # Did we receive an SOA record?
91
+ #----------------------------------------------------------------------
92
+
93
+ unless soa_req.answer[0].class == Net::DNS::RR::SOA
94
+ print "expected SOA, got " + Net::DNS::RR::RRTypes.to_str(soa_req.answer[0].type) + "\n"
95
+ next
96
+ end
97
+
98
+ #----------------------------------------------------------------------
99
+ # Print the serial number.
100
+ #----------------------------------------------------------------------
101
+
102
+ print "has serial number " + soa_req.answer[0].serial.to_s + "\n"
103
+ end
104
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems' if RUBY_VERSION.to_s < "1.9.0"
2
+ require 'net/dns'
3
+
4
+ a = ["ibm.com", "sun.com", "redhat.com"]
5
+
6
+ threads = []
7
+
8
+ a.each do |dom|
9
+ threads << Thread.new(dom) do |domain|
10
+ res = Net::DNS::Resolver.new
11
+ res.query(domain, Net::DNS::NS).each_nameserver do |ns|
12
+ puts "Domain #{domain} has nameserver #{ns}"
13
+ end
14
+ puts ""
15
+ end
16
+ end
17
+
18
+ threads.each(&:join)
@@ -0,0 +1,24 @@
1
+ $LOAD_PATH.push File.expand_path('lib', __dir__)
2
+ require "net/dns/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "gitlab-net-dns"
6
+ s.version = Net::DNS::VERSION
7
+ s.authors = ["Marco Ceresa", "Simone Carletti"]
8
+ s.email = ["ceresa@gmail.com", "weppos@weppos.net"]
9
+ s.homepage = "https://gitlab.com/gitlab-org/gitlab-net-dns"
10
+ s.summary = "Pure Ruby DNS library."
11
+ s.description = "Net::DNS is a pure Ruby DNS library, with a clean OO interface and an extensible API."
12
+ s.licenses = ["Ruby"]
13
+
14
+ s.required_ruby_version = ">= 2.1"
15
+
16
+ s.require_paths = ["lib"]
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.extra_rdoc_files = %w[LICENSE.txt]
20
+
21
+ s.add_development_dependency "mocha"
22
+ s.add_development_dependency "rake"
23
+ s.add_development_dependency "yard"
24
+ end
@@ -0,0 +1,104 @@
1
+ require_relative 'dns/version'
2
+ require_relative 'dns/resolver'
3
+ require_relative 'dns/rr'
4
+
5
+ module Net
6
+ module DNS
7
+ # Packet size in bytes
8
+ PACKETSZ = 512
9
+
10
+ # Size of the header
11
+ HFIXEDSZ = 12
12
+
13
+ # Size of the question portion (type and class)
14
+ QFIXEDSZ = 4
15
+
16
+ # Size of an RR portion (type,class,lenght and ttl)
17
+ RRFIXEDSZ = 10
18
+
19
+ # Size of an int 32 bit
20
+ INT32SZ = 4
21
+
22
+ # Size of a short int
23
+ INT16SZ = 2
24
+
25
+ module QueryTypes
26
+ SIGZERO = 0
27
+ A = 1
28
+ NS = 2
29
+ MD = 3
30
+ MF = 4
31
+ CNAME = 5
32
+ SOA = 6
33
+ MB = 7
34
+ MG = 8
35
+ MR = 9
36
+ NULL = 10
37
+ WKS = 11
38
+ PTR = 12
39
+ HINFO = 13
40
+ MINFO = 14
41
+ MX = 15
42
+ TXT = 16
43
+ RP = 17
44
+ AFSDB = 18
45
+ X25 = 19
46
+ ISDN = 20
47
+ RT = 21
48
+ NSAP = 22
49
+ NSAPPTR = 23
50
+ SIG = 24
51
+ KEY = 25
52
+ PX = 26
53
+ GPOS = 27
54
+ AAAA = 28
55
+ LOC = 29
56
+ NXT = 30
57
+ EID = 31
58
+ NIMLOC = 32
59
+ SRV = 33
60
+ ATMA = 34
61
+ NAPTR = 35
62
+ KX = 36
63
+ CERT = 37
64
+ DNAME = 39
65
+ OPT = 41
66
+ DS = 43
67
+ SSHFP = 44
68
+ RRSIG = 46
69
+ NSEC = 47
70
+ DNSKEY = 48
71
+ UINFO = 100
72
+ UID = 101
73
+ GID = 102
74
+ UNSPEC = 103
75
+ TKEY = 249
76
+ TSIG = 250
77
+ IXFR = 251
78
+ AXFR = 252
79
+ MAILB = 253
80
+ MAILA = 254
81
+ ANY = 255
82
+ end
83
+
84
+ module QueryClasses
85
+ # Internet class
86
+ IN = 1
87
+
88
+ # Chaos class
89
+ CH = 3
90
+
91
+ # Hesiod class
92
+ HS = 4
93
+
94
+ # None class
95
+ NONE = 254
96
+
97
+ # Any class
98
+ ANY = 255
99
+ end
100
+
101
+ include QueryTypes
102
+ include QueryClasses
103
+ end
104
+ end
@@ -0,0 +1,705 @@
1
+ module Net
2
+ module DNS
3
+ # DNS packet header class
4
+ #
5
+ # The Net::DNS::Header class represents the header portion of a
6
+ # DNS packet. An Header object is created whenever a new packet
7
+ # is parsed or as user request.
8
+ #
9
+ # header = Net::DNS::Header.new
10
+ # # ;; id = 18123
11
+ # # ;; qr = 0 opCode: 0 aa = 0 tc = 0 rd = 1
12
+ # # ;; ra = 0 ad = 0 cd = 0 rcode = 0
13
+ # # ;; qdCount = 1 anCount = 0 nsCount = 0 arCount = 0
14
+ #
15
+ # header.format
16
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17
+ # # | 18123 |
18
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19
+ # # |0| 0 |0|0|1|0|0| 0 | 0 |
20
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21
+ # # | 1 |
22
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23
+ # # | 0 |
24
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25
+ # # | 0 |
26
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
27
+ # # | 0 |
28
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
29
+ #
30
+ # # packet is an instance of Net::DNS::Packet
31
+ # header = packet.header
32
+ # puts "Answer is #{header.auth? ? '' : 'non'} authoritative"
33
+ #
34
+ # A lot of methods were written to keep a compatibility layer with
35
+ # the Perl version of the library, as long as methods name which are
36
+ # more or less the same.
37
+ #
38
+ class Header
39
+ # A wrong +count+ parameter has been passed.
40
+ class WrongCountError < ArgumentError
41
+ end
42
+
43
+ # A wrong +recursive+ parameter has been passed.
44
+ class WrongRecursiveError < ArgumentError
45
+ end
46
+
47
+ # An invalid +opCode+ has been specified.
48
+ class WrongOpcodeError < ArgumentError
49
+ end
50
+
51
+ # Base error class.
52
+ class Error < StandardError
53
+ end
54
+
55
+ # DNS Header RCode handling class
56
+ #
57
+ # It should be used internally by Net::DNS::Header class. However, it's still
58
+ # possible to instantiate it directly.
59
+ #
60
+ # require 'net/dns/header'
61
+ # rcode = Net::DNS::Header::RCode.new 0
62
+ #
63
+ # The RCode class represents the RCode field in the Header portion of a
64
+ # DNS packet. This field (called Response Code) is used to get informations
65
+ # about the status of a DNS operation, such as a query or an update. These
66
+ # are the values in the original Mockapetris's standard (RFC1035):
67
+ #
68
+ # * 0 No error condition
69
+ # * 1 Format error - The name server was unable to interpret
70
+ # the query.
71
+ # * 2 Server failure - The name server was
72
+ # unable to process this query due to a
73
+ # problem with the name server.
74
+ # * 3 Name Error - Meaningful only for
75
+ # responses from an authoritative name
76
+ # server, this code means that the
77
+ # domain name referenced in the query does
78
+ # not exist.
79
+ # * 4 Not Implemented - The name server does
80
+ # not support the requested kind of query.
81
+ # * 5 Refused - The name server refuses to
82
+ # perform the specified operation for
83
+ # policy reasons. For example, a name
84
+ # server may not wish to provide the
85
+ # information to the particular requester,
86
+ # or a name server may not wish to perform
87
+ # a particular operation (e.g., zone
88
+ # transfer) for particular data.
89
+ # * 6-15 Reserved for future use.
90
+ #
91
+ # In the next DNS RFCs, codes 6-15 has been assigned to the following
92
+ # errors:
93
+ #
94
+ # * 6 YXDomain
95
+ # * 7 YXRRSet
96
+ # * 8 NXRRSet
97
+ # * 9 NotAuth
98
+ # * 10 NotZone
99
+ #
100
+ # More RCodes has to come for TSIGs and other operations.
101
+ #
102
+ class RCode
103
+ # Constant for +rcode+ Response Code No Error
104
+ NOERROR = 0
105
+ # Constant for +rcode+ Response Code Format Error
106
+ FORMAT = 1
107
+ # Constant for +rcode+ Response Code Server Format Error
108
+ SERVER = 2
109
+ # Constant for +rcode+ Response Code Name Error
110
+ NAME = 3
111
+ # Constant for +rcode+ Response Code Not Implemented Error
112
+ NOTIMPLEMENTED = 4
113
+ # Constant for +rcode+ Response Code Refused Error
114
+ REFUSED = 5
115
+
116
+ RCodeType = %w[NoError FormErr ServFail NXDomain NotImp
117
+ Refused YXDomain YXRRSet NXRRSet NotAuth NotZone].freeze
118
+
119
+ RCodeErrorString = ["No errors",
120
+ "The name server was unable to interpret the query",
121
+ "The name server was unable to process this query due to problem with the name server",
122
+ "Domain name referenced in the query does not exists",
123
+ "The name server does not support the requested kind of query",
124
+ "The name server refuses to perform the specified operation for policy reasons",
125
+ "",
126
+ "",
127
+ "",
128
+ "",
129
+ "",].freeze
130
+
131
+ attr_reader :code, :type, :explanation
132
+
133
+ def initialize(code)
134
+ if (0..10).cover? code
135
+ @code = code
136
+ @type = RCodeType[code]
137
+ @explanation = RCodeErrorString[code]
138
+ else
139
+ raise ArgumentError, "RCode `#{code}' out of range"
140
+ end
141
+ end
142
+
143
+ def to_s
144
+ @code.to_s
145
+ end
146
+ end
147
+
148
+ # Constant for +opCode+ query
149
+ QUERY = 0
150
+ # Constant for +opCode+ iquery
151
+ IQUERY = 1
152
+ # Constant for +opCode+ status
153
+ STATUS = 2
154
+ # Array with given strings
155
+ OPARR = %w[QUERY IQUERY STATUS].freeze
156
+
157
+ # Reader for +id+ attribute
158
+ attr_reader :id
159
+ # Reader for the operational code
160
+ attr_reader :opCode
161
+ # Reader for the rCode instance
162
+ attr_reader :rCode
163
+ # Reader for question section entries number
164
+ attr_reader :qdCount
165
+ # Reader for answer section entries number
166
+ attr_reader :anCount
167
+ # Reader for authority section entries number
168
+ attr_reader :nsCount
169
+ # Reader for addictional section entries number
170
+ attr_reader :arCount
171
+
172
+ # Creates a new Net::DNS::Header object with the desired values,
173
+ # which can be specified as an Hash argument. When called without
174
+ # arguments, defaults are used. If a data string is passed, values
175
+ # are taken from parsing the string.
176
+ #
177
+ # Examples:
178
+ #
179
+ # # Create a new Net::DNS::Header object
180
+ # header = Net::DNS::Header.new
181
+ #
182
+ # # Create a new Net::DNS::Header object passing values
183
+ # header = Net::DNS::Header.new(:opCode => 1, :rd => 0)
184
+ #
185
+ # # Create a new Net::DNS::Header object with binary data
186
+ # header = Net::DNS::Header.new(data)
187
+ #
188
+ # Default values are:
189
+ #
190
+ # :id => auto generated
191
+ # :qr => 0 # Query response flag
192
+ # :aa => 0 # Authoritative answer flag
193
+ # :tc => 0 # Truncated packet flag
194
+ # :ra => 0 # Recursiond available flag
195
+ # :rCode => 0 # Response code (status of the query)
196
+ # :opCode => 0 # Operational code (purpose of the query)
197
+ # :cd => 0 # Checking disable flag
198
+ # :ad => 0 # Only relevant in DNSSEC context
199
+ # :rd => 1 # Recursion desired flag
200
+ # :qdCount => 1 # Number of questions in the dns packet
201
+ # :anCount => 0 # Number of answer RRs in the dns packet
202
+ # :nsCount => 0 # Number of authoritative RRs in the dns packet
203
+ # :arCount => 0 # Number of additional RRs in the dns packet
204
+ #
205
+ # See also each option for a detailed explanation of usage.
206
+ #
207
+ def initialize(arg = {})
208
+ if arg.is_a? Hash
209
+ new_from_hash(arg)
210
+ else
211
+ raise ArgumentError, "Wrong argument class `#{arg.class}'"
212
+ end
213
+ end
214
+
215
+ # Creates a new Net::DNS::Header object from binary data, which is
216
+ # passed as a string object as argument.
217
+ # The configurations parameters are taken from parsing the string.
218
+ #
219
+ # Example:
220
+ #
221
+ # # Create a new Net::DNS::Header object with binary data
222
+ # header = Net::DNS::Header.new(data)
223
+ #
224
+ # header.auth?
225
+ # #=> "true" if it comes from authoritative name server
226
+ #
227
+ def self.parse(arg)
228
+ if arg.is_a? String
229
+ o = allocate
230
+ o.send(:new_from_binary, arg)
231
+ o
232
+ else
233
+ raise ArgumentError, "Wrong argument class `#{arg.class}'"
234
+ end
235
+ end
236
+
237
+ # Inspect method, prints out all the options and relative values.
238
+ #
239
+ # p Net::DNS::Header.new
240
+ # # ;; id = 18123
241
+ # # ;; qr = 0 opCode: 0 aa = 0 tc = 0 rd = 1
242
+ # # ;; ra = 0 ad = 0 cd = 0 rcode = 0
243
+ # # ;; qdCount = 1 anCount = 0 nsCount = 0 arCount = 0
244
+ #
245
+ # This method will maybe be changed in the future to a more pretty
246
+ # way of display output.
247
+ #
248
+ def inspect
249
+ ";; id = #{@id}\n" +
250
+ if false # @opCode == "UPDATE"
251
+ # do stuff
252
+ else
253
+ ";; qr = #{@qr}\t" \
254
+ "opCode: #{opCode_str}\t" \
255
+ "aa = #{@aa}\t" \
256
+ "tc = #{@tc}\t" \
257
+ "rd = #{@rd}\n" \
258
+ ";; ra = #{@ra}\t" \
259
+ "ad = #{@ad}\t" \
260
+ "cd = #{@cd}\t" \
261
+ "rcode = #{@rCode.type}\n" \
262
+ ";; qdCount = #{@qdCount}\t" \
263
+ "anCount = #{@anCount}\t" \
264
+ "nsCount = #{@nsCount}\t" \
265
+ "arCount = #{@arCount}\n"
266
+ end
267
+ end
268
+
269
+ # The Net::DNS::Header#format method prints out the header
270
+ # in a special ascii representation of data, in a way
271
+ # similar to those often found on RFCs.
272
+ #
273
+ # p Net::DNS::Header.new.format
274
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
275
+ # # | 18123 |
276
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
277
+ # # |0| 0 |0|0|1|0|0| 0 | 0 |
278
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
279
+ # # | 1 |
280
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
281
+ # # | 0 |
282
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
283
+ # # | 0 |
284
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
285
+ # # | 0 |
286
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
287
+ #
288
+ # This can be very usefull for didactical purpouses :)
289
+ #
290
+ def format
291
+ del = ("+-" * 16) + "+\n"
292
+ len = del.length
293
+ str = del + "|" + @id.to_s.center(len - 3) + "|\n"
294
+ str += del + "|" + @qr.to_s
295
+ str += "|" + @opCode.to_s.center(7)
296
+ str += "|" + @aa.to_s
297
+ str += "|" + @tc.to_s
298
+ str += "|" + @rd.to_s
299
+ str += "|" + @ra.to_s
300
+ str += "|" + @ad.to_s
301
+ str += "|" + @cd.to_s.center(3)
302
+ str += "|" + @rCode.to_s.center(7) + "|\n"
303
+ str += del + "|" + @qdCount.to_s.center(len - 3) + "|\n"
304
+ str += del + "|" + @anCount.to_s.center(len - 3) + "|\n"
305
+ str += del + "|" + @nsCount.to_s.center(len - 3) + "|\n"
306
+ str += del + "|" + @arCount.to_s.center(len - 3) + "|\n" + del
307
+ str
308
+ end
309
+
310
+ # Returns the header data in binary format, appropriate
311
+ # for use in a DNS query packet.
312
+ #
313
+ # hdata = header.data
314
+ # puts "Header is #{hdata.size} bytes"
315
+ #
316
+ def data
317
+ arr = []
318
+ arr.push(@id)
319
+ arr.push((@qr << 7) | (@opCode << 3) | (@aa << 2) | (@tc << 1) | @rd)
320
+ arr.push((@ra << 7) | (@ad << 5) | (@cd << 4) | @rCode.code)
321
+ arr.push(@qdCount)
322
+ arr.push(@anCount)
323
+ arr.push(@nsCount)
324
+ arr.push(@arCount)
325
+ arr.pack("n C2 n4")
326
+ end
327
+
328
+ # Set the ID for the current header. Useful when
329
+ # performing security tests.
330
+ #
331
+ def id=(val)
332
+ if (0..65_535).cover? val
333
+ @id = val
334
+ else
335
+ raise ArgumentError, "ID `#{val}' out of range"
336
+ end
337
+ end
338
+
339
+ # Checks whether the header is a query (+qr+ bit set to 0)
340
+ #
341
+ def query?
342
+ @qr == 0
343
+ end
344
+
345
+ # Set the +qr+ query response flag to be either +true+ or
346
+ # +false+. You can also use the values 0 and 1. This flag
347
+ # indicates if the DNS packet contains a query or an answer,
348
+ # so it should be set to +true+ in DNS answer packets.
349
+ # If +qr+ is +true+, the packet is a response.
350
+ #
351
+ def qr=(val)
352
+ case val
353
+ when true
354
+ @qr = 1
355
+ when false
356
+ @qr = 0
357
+ when 0, 1
358
+ @qr = val
359
+ else
360
+ raise ArgumentError, ":qr must be true(or 1) or false(or 0)"
361
+ end
362
+ end
363
+
364
+ # Checks whether the header is a response
365
+ # (+qr+ bit set to 1)
366
+ #
367
+ def response?
368
+ @qr == 1
369
+ end
370
+
371
+ # Returns a string representation of the +opCode+
372
+ #
373
+ # puts "Packet is a #{header.opCode_str}"
374
+ # #=> Packet is a QUERY
375
+ #
376
+ def opCode_str
377
+ OPARR[@opCode]
378
+ end
379
+
380
+ # Set the +opCode+ variable to a new value. This fields indicates
381
+ # the type of the question present in the DNS packet; +val+ can be
382
+ # one of the values QUERY, IQUERY or STATUS.
383
+ #
384
+ # * QUERY is the standard DNS query
385
+ # * IQUERY is the inverse query
386
+ # * STATUS is used to query the nameserver for its status
387
+ #
388
+ # Example:
389
+ #
390
+ # include Net::DNS
391
+ # header = Header.new
392
+ # header.opCode = Header::STATUS
393
+ #
394
+ def opCode=(val)
395
+ if (0..2).cover? val
396
+ @opCode = val
397
+ else
398
+ raise WrongOpcodeError, "Wrong opCode value (#{val}), must be QUERY, IQUERY or STATUS"
399
+ end
400
+ end
401
+
402
+ # Checks whether the response is authoritative
403
+ #
404
+ # if header.auth?
405
+ # puts "Response is authoritative"
406
+ # else
407
+ # puts "Answer is NOT authoritative"
408
+ # end
409
+ #
410
+ def auth?
411
+ @aa == 1
412
+ end
413
+
414
+ # Set the +aa+ flag (authoritative answer) to either +true+
415
+ # or +false+. You can also use 0 or 1.
416
+ #
417
+ # This flag indicates whether a DNS answer packet contains
418
+ # authoritative data, meaning that is was generated by a
419
+ # nameserver authoritative for the domain of the question.
420
+ #
421
+ # Must only be set to +true+ in DNS answer packets.
422
+ #
423
+ def aa=(val)
424
+ case val
425
+ when true
426
+ @aa = 1
427
+ when false
428
+ @aa = 0
429
+ when 0, 1
430
+ @aa = val
431
+ else
432
+ raise ArgumentError, ":aa must be true(or 1) or false(or 0)"
433
+ end
434
+ end
435
+
436
+ # Checks whether the packet was truncated
437
+ #
438
+ # # Sending packet using UDP
439
+ # if header.truncated?
440
+ # puts "Warning, packet has been truncated!"
441
+ # # Sending packet using TCP
442
+ # end
443
+ # # Do something with the answer
444
+ #
445
+ def truncated?
446
+ @tc == 1
447
+ end
448
+
449
+ # Set the +tc+ flag (truncated packet) to either +true+
450
+ # ot +false+. You can also use 0 or 1.
451
+ #
452
+ # The truncated flag is used in response packets to indicate
453
+ # that the amount of data to be trasmitted exceedes the
454
+ # maximum allowed by the protocol in use, tipically UDP, and
455
+ # that the data present in the packet has been truncated.
456
+ # A different protocol (such has TCP) need to be used to
457
+ # retrieve full data.
458
+ #
459
+ # Must only be set in DNS answer packets.
460
+ #
461
+ def tc=(val)
462
+ case val
463
+ when true
464
+ @tc = 1
465
+ when false
466
+ @tc = 0
467
+ when 0, 1
468
+ @tc = val
469
+ else
470
+ raise ArgumentError, ":tc must be true(or 1) or false(or 0)"
471
+ end
472
+ end
473
+
474
+ # Checks whether the packet has a recursion bit
475
+ # set, meaning that recursion is desired
476
+ #
477
+ def recursive?
478
+ @rd == 1
479
+ end
480
+
481
+ # Sets the recursion desidered bit.
482
+ # Remember that recursion query support is
483
+ # optional.
484
+ #
485
+ # header.recursive = true
486
+ # hdata = header.data # suitable for sending
487
+ #
488
+ # Consult RFC1034 and RFC1035 for a detailed explanation
489
+ # of how recursion works.
490
+ #
491
+ def recursive=(val)
492
+ case val
493
+ when true
494
+ @rd = 1
495
+ when false
496
+ @rd = 0
497
+ when 1
498
+ @rd = 1
499
+ when 0
500
+ @rd = 0
501
+ else
502
+ raise WrongRecursiveError, "Wrong value (#{val}), please specify true (1) or false (0)"
503
+ end
504
+ end
505
+
506
+ # Alias for Header#recursive= to keep compatibility
507
+ # with the Perl version.
508
+ #
509
+ def rd=(val)
510
+ self.recursive = val
511
+ end
512
+
513
+ # Checks whether recursion is available.
514
+ # This flag is usually set by nameservers to indicate
515
+ # that they support recursive-type queries.
516
+ #
517
+ def r_available?
518
+ @ra == 1
519
+ end
520
+
521
+ # Set the +ra+ flag (recursion available) to either +true+ or
522
+ # +false+. You can also use 0 and 1.
523
+ #
524
+ # This flag must only be set in DNS answer packets.
525
+ #
526
+ def ra=(val)
527
+ case val
528
+ when true
529
+ @ra = 1
530
+ when false
531
+ @ra = 0
532
+ when 0, 1
533
+ @ra = val
534
+ else
535
+ raise ArgumentError, ":ra must be true(or 1) or false(or 0)"
536
+ end
537
+ end
538
+
539
+ # Checks whether checking is enabled or disabled.
540
+ #
541
+ # Checking is enabled by default.
542
+ #
543
+ def checking?
544
+ @cd == 0
545
+ end
546
+
547
+ # Set the +cd+ flag (checking disabled) to either +true+
548
+ # ot +false+. You can also use 0 or 1.
549
+ #
550
+ def cd=(val)
551
+ case val
552
+ when true
553
+ @cd = 1
554
+ when false
555
+ @cd = 0
556
+ when 0, 1
557
+ @cd = val
558
+ else
559
+ raise ArgumentError, ":cd must be true(or 1) or false(or 0)"
560
+ end
561
+ end
562
+
563
+ # Checks whether +ad+ flag has been set.
564
+ #
565
+ # This flag is only relevant in DNSSEC context.
566
+ #
567
+ def verified?
568
+ @ad == 1
569
+ end
570
+
571
+ # Set the +ad+ flag to either +true+
572
+ # ot +false+. You can also use 0 or 1.
573
+ #
574
+ # The AD bit is only set on answers where signatures have
575
+ # been cryptographically verified or the server is
576
+ # authoritative for the data and is allowed to set the bit by policy.
577
+ #
578
+ def ad=(val)
579
+ case val
580
+ when true
581
+ @ad = 1
582
+ when false
583
+ @ad = 0
584
+ when 0, 1
585
+ @ad = val
586
+ else
587
+ raise ArgumentError, ":ad must be true(or 1) or false(or 0)"
588
+ end
589
+ end
590
+
591
+ # Returns an error array for the header response code, or
592
+ # +nil+ if no error is generated.
593
+ #
594
+ # error, cause = header.rCode_str
595
+ # puts "Error #{error} cause by: #{cause}" if error
596
+ # #=> Error ForErr caused by: The name server
597
+ # #=> was unable to interpret the query
598
+ #
599
+ def rCode_str
600
+ [rCode.type, rCode.explanation]
601
+ end
602
+
603
+ # Checks for errors in the DNS packet
604
+ #
605
+ # unless header.error?
606
+ # puts "No errors in DNS answer packet"
607
+ # end
608
+ #
609
+ def error?
610
+ @rCode.code > 0
611
+ end
612
+
613
+ # Set the rCode value. This should only be done in DNS
614
+ # answer packets.
615
+ #
616
+ def rCode=(val)
617
+ @rCode = RCode.new(val)
618
+ end
619
+
620
+ # Sets the number of entries in a question section
621
+ #
622
+ def qdCount=(val)
623
+ if (0..65_535).cover? val
624
+ @qdCount = val
625
+ else
626
+ raise WrongCountError, "Wrong number of count (#{val}), must be 0-65535"
627
+ end
628
+ end
629
+
630
+ # Sets the number of RRs in an answer section
631
+ #
632
+ def anCount=(val)
633
+ if (0..65_535).cover? val
634
+ @anCount = val
635
+ else
636
+ raise WrongCountError, "Wrong number of count (#{val}), must be 0-65535"
637
+ end
638
+ end
639
+
640
+ # Sets the number of RRs in an authority section
641
+ #
642
+ def nsCount=(val)
643
+ if (0..65_535).cover? val
644
+ @nsCount = val
645
+ else
646
+ raise WrongCountError, "Wrong number of count (#{val}), must be 0-65535"
647
+ end
648
+ end
649
+
650
+ # Sets the number of RRs in an addictional section
651
+ #
652
+ def arCount=(val)
653
+ if (0..65_535).cover? val
654
+ @arCount = val
655
+ else
656
+ raise WrongCountError, "Wrong number of count: `#{val}' must be 0-65535"
657
+ end
658
+ end
659
+
660
+ private
661
+
662
+ def new_from_scratch
663
+ @id = genID # generate ad unique id
664
+ @qr = @aa = @tc = @ra = @ad = @cd = 0
665
+ @rCode = RCode.new(0) # no error
666
+ @anCount = @nsCount = @arCount = 0
667
+ @rd = @qdCount = 1
668
+ @opCode = QUERY # standard query, default message
669
+ end
670
+
671
+ def new_from_binary(str)
672
+ unless str.size == Net::DNS::HFIXEDSZ
673
+ raise ArgumentError, "Header binary data has wrong size: `#{str.size}' bytes"
674
+ end
675
+
676
+ arr = str.unpack("n C2 n4")
677
+ @id = arr[0]
678
+ @qr = (arr[1] >> 7) & 0x01
679
+ @opCode = (arr[1] >> 3) & 0x0F
680
+ @aa = (arr[1] >> 2) & 0x01
681
+ @tc = (arr[1] >> 1) & 0x01
682
+ @rd = arr[1] & 0x1
683
+ @ra = (arr[2] >> 7) & 0x01
684
+ @ad = (arr[2] >> 5) & 0x01
685
+ @cd = (arr[2] >> 4) & 0x01
686
+ @rCode = RCode.new(arr[2] & 0xf)
687
+ @qdCount = arr[3]
688
+ @anCount = arr[4]
689
+ @nsCount = arr[5]
690
+ @arCount = arr[6]
691
+ end
692
+
693
+ def new_from_hash(hash)
694
+ new_from_scratch
695
+ hash.each do |key, val|
696
+ eval "self.#{key} = val"
697
+ end
698
+ end
699
+
700
+ def genID
701
+ rand(65_535)
702
+ end
703
+ end
704
+ end
705
+ end