net-dns 0.1

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.
data/AUTHORS ADDED
@@ -0,0 +1,10 @@
1
+ # $Id: AUTHORS,v 1.2 2005/06/17 10:09:57 bluemonk Exp $
2
+
3
+
4
+ AUTHORS
5
+
6
+ Net::DNS core developement:
7
+ Marco Ceresa <ceresa@ieee.org>
8
+
9
+ Beta testing:
10
+
data/INSTALL ADDED
File without changes
data/README ADDED
@@ -0,0 +1,55 @@
1
+ Net::DNS README
2
+ ============
3
+
4
+ This is a port of the Perl Net::DNS module, written by Michael Fuhr
5
+ and now currently maintained by Olaf Kolkman (www.net-dns.org). It
6
+ keeps the same interfaces and function names, although has a bit
7
+ improved OO and some other stuff.
8
+ It can be used to query DNS servers for various kind of records, perform
9
+ zone transfer and dynamic updates. It has even a class for acting as a
10
+ nameserver.
11
+ This version is quite incomplete. You can use it as a resolver.
12
+
13
+
14
+ Requirements
15
+ ------------
16
+
17
+ * Ruby 1.6
18
+
19
+
20
+ Install
21
+ -------
22
+
23
+ De-compress archive and enter its top directory.
24
+ Then type:
25
+
26
+ ($ su)
27
+ # ruby setup.rb
28
+
29
+ These simple step installs this program under the default
30
+ location of Ruby libraries. You can also install files into
31
+ your favorite directory by supplying setup.rb some options.
32
+ Try "ruby setup.rb --help".
33
+
34
+
35
+ Usage
36
+ -----
37
+
38
+ Have a look on the manual pages.
39
+ In doc/ you will find many useful documents too.
40
+
41
+
42
+ License
43
+ -------
44
+
45
+ Net::DNS is distributed under the same license Ruby is.
46
+
47
+
48
+ Author
49
+ ------
50
+
51
+ See AUTHORS
52
+
53
+
54
+ # $Id: README,v 1.2 2005/06/17 15:11:18 bluemonk Exp $
55
+
data/Rakefile ADDED
@@ -0,0 +1,65 @@
1
+ require 'rake/clean'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/contrib/rubyforgepublisher'
5
+ require 'rake/gempackagetask'
6
+
7
+ require 'rubygems'
8
+
9
+ desc "Run the tests"
10
+ Rake::TestTask.new do |t|
11
+ t.libs << "test"
12
+ t.test_files = FileList['test/net/dns/**/test*.rb']
13
+ t.verbose = true
14
+ end
15
+
16
+ desc "Install the library"
17
+ task :install do
18
+ sh "sudo ruby setup.rb"
19
+ end
20
+
21
+ desc "Build documentation"
22
+ Rake::RDocTask.new do |rd|
23
+ rd.rdoc_files.include("lib/net/dns/**/*.rb")
24
+ rd.options << "-S"
25
+ end
26
+
27
+ #
28
+ # Gem specifications
29
+ #
30
+ SPEC = Gem::Specification.new do |s|
31
+ s.name = "net-dns"
32
+ s.version = "0.1"
33
+ s.author = "Marco Ceresa"
34
+ s.email = "ceresa@gmail.com"
35
+ s.homepage = "http://net-dns.rubyforge.org/"
36
+ s.platform = Gem::Platform::RUBY
37
+ s.summary = "Pure Ruby DNS library"
38
+ candidates = Dir.glob("**/*")
39
+ s.files = candidates.delete_if do |item|
40
+ item.include?("CVS") || item.include?("rdoc") || item.include?("pkg")
41
+ end
42
+ s.has_rdoc = true
43
+ s.extra_rdoc_files = ["README"]
44
+ s.description = <<EOF
45
+ A pure Ruby DNS library, similar to the Perl Net::DNS library
46
+ EOF
47
+ end
48
+
49
+ #
50
+ # Build packages
51
+ #
52
+ desc "Build packages"
53
+ Rake::GemPackageTask.new(SPEC) do |pkg|
54
+ pkg.need_zip = true
55
+ pkg.need_tar = true
56
+ end
57
+
58
+ #
59
+ # RubyForge publisher
60
+ #
61
+ desc "Upload project on RubyForge"
62
+ task :upload do
63
+ rubyforge = Rake::RubyForgePublisher.new("net-dns","bluemonk")
64
+ rubyforge.upload
65
+ end
data/THANKS ADDED
File without changes
data/demo/check_soa.rb ADDED
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # $Id: check_soa.rb,v 1.7 2006/07/30 16:53:57 bluemonk Exp $
4
+
5
+
6
+
7
+ require 'net/dns/resolver'
8
+
9
+ #------------------------------------------------------------------------------
10
+ # Get the domain from the command line.
11
+ #------------------------------------------------------------------------------
12
+
13
+ raise ArgumentError, "Usage: check_soa.rb domain\n" unless ARGV.size == 1
14
+
15
+ domain = ARGV[0]
16
+
17
+ #------------------------------------------------------------------------------
18
+ # Find all the nameservers for the domain.
19
+ #------------------------------------------------------------------------------
20
+
21
+ res = Net::DNS::Resolver.new(:defname => false, :retry => 2)
22
+
23
+ #res.defname=false
24
+ #res.retry=2
25
+
26
+
27
+ ns_req = res.query(domain, Net::DNS::NS);
28
+ raise ArgumentError, "No nameservers found for domain: " + res.errorstring +
29
+ "\n" unless ns_req and ns_req.header.anCount > 0
30
+
31
+
32
+ # Send out non-recursive queries
33
+ res.recurse=false
34
+ # Do not buffer standard out
35
+ #| = 1;
36
+
37
+
38
+ #------------------------------------------------------------------------------
39
+ # Check the SOA record on each nameserver.
40
+ #------------------------------------------------------------------------------
41
+
42
+ ns_req.each_nameserver do |ns|
43
+
44
+ #----------------------------------------------------------------------
45
+ # Set the resolver to query this nameserver.
46
+ #----------------------------------------------------------------------
47
+
48
+ # In order to lookup the IP(s) of the nameserver, we need a Resolver
49
+ # object that is set to our local, recursive nameserver. So we create
50
+ # a new object just to do that.
51
+
52
+ local_res = Net::DNS::Resolver.new
53
+
54
+ a_req = local_res.query(ns, Net::DNS::A)
55
+
56
+
57
+ unless a_req
58
+ puts "Can not find address for ns: " + res.errorstring + "\n"
59
+ next
60
+ end
61
+
62
+
63
+ a_req.each_address do |ip|
64
+
65
+ #----------------------------------------------------------------------
66
+ # Ask this IP.
67
+ #----------------------------------------------------------------------
68
+ res.nameservers=ip
69
+
70
+ print "#{ns} (#{ip}): "
71
+
72
+ #----------------------------------------------------------------------
73
+ # Get the SOA record.
74
+ #----------------------------------------------------------------------
75
+
76
+ soa_req = res.send(domain, Net::DNS::SOA, Net::DNS::IN)
77
+
78
+ if soa_req == nil
79
+ puts res.errorstring, "\n"
80
+ next
81
+ end
82
+
83
+ #----------------------------------------------------------------------
84
+ # Is this nameserver authoritative for the domain?
85
+ #----------------------------------------------------------------------
86
+
87
+ unless soa_req.header.auth?
88
+ print "isn't authoritative for domain\n"
89
+ next
90
+ end
91
+
92
+ #----------------------------------------------------------------------
93
+ # We should have received exactly one answer.
94
+ #----------------------------------------------------------------------
95
+
96
+ unless soa_req.header.anCount == 1
97
+ print "expected 1 answer, got " + soa_req.header.anCount.to_s + "\n"
98
+ next
99
+ end
100
+
101
+ #----------------------------------------------------------------------
102
+ # Did we receive an SOA record?
103
+ #----------------------------------------------------------------------
104
+
105
+ unless soa_req.answer[0].class == Net::DNS::RR::SOA
106
+ print "expected SOA, got " + Net::DNS::RR::RRTypes.to_str(soa_req.answer[0].type) + "\n"
107
+ next
108
+ end
109
+
110
+ #----------------------------------------------------------------------
111
+ # Print the serial number.
112
+ #----------------------------------------------------------------------
113
+
114
+ print "has serial number " + soa_req.answer[0].serial.to_s + "\n"
115
+
116
+ end
117
+ end
118
+
119
+
120
+
data/demo/threads.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'net/dns/resolver'
2
+
3
+ a = ["ibm.com", "sun.com", "redhat.com"]
4
+
5
+ threads = []
6
+
7
+ for dom in a
8
+ threads << Thread.new(dom) do |domain|
9
+ res = Net::DNS::Resolver.new
10
+ res.query(domain, Net::DNS::NS).each_nameserver do |ns|
11
+ puts "Domain #{domain} has nameserver #{ns}"
12
+ end
13
+ puts ""
14
+ end
15
+ end
16
+
17
+ threads.each do |t|
18
+ t.join
19
+ end
20
+
21
+
data/gemspec ADDED
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ SPEC = Gem::Specification.new do |s|
3
+ s.name = "net-dns"
4
+ s.version = "0.1"
5
+ s.author = "Marco Ceresa"
6
+ s.email = "ceresa@gmail.com"
7
+ s.homepage = "http://net-dns.rubyforge.org/"
8
+ s.platform = Gem::Platform::RUBY
9
+ s.summary = "Pure Ruby DNS library"
10
+ candidates = Dir.glob("{bin,docs,lib,tests}/**/*")
11
+ s.files = candidates.delete_if do |item|
12
+ item.include?("CVS") || item.include?("rdoc")
13
+ end
14
+ s.has_rdoc = true
15
+ s.extra_rdoc_files = ["README"]
16
+ end
@@ -0,0 +1,117 @@
1
+ ##
2
+ #
3
+ # dns.rb
4
+ #
5
+ # $id$
6
+ #
7
+ ##
8
+
9
+ module Net # :nodoc:
10
+ module DNS
11
+
12
+ # Version of the library
13
+ VERSION = 0.1
14
+
15
+ # Packet size in bytes
16
+ PACKETSZ = 512
17
+
18
+ # Size of the header
19
+ HFIXEDSZ = 12
20
+
21
+ # Size of the question portion (type and class)
22
+ QFIXEDSZ = 4
23
+
24
+ # Size of an RR portion (type,class,lenght and ttl)
25
+ RRFIXEDSZ = 10
26
+
27
+ # Size of an int 32 bit
28
+ INT32SZ = 4
29
+
30
+ # Size of a short int
31
+ INT16SZ = 2
32
+
33
+ module QueryTypes
34
+
35
+ SIGZERO = 0
36
+ A = 1
37
+ NS = 2
38
+ MD = 3
39
+ MF = 4
40
+ CNAME = 5
41
+ SOA = 6
42
+ MB = 7
43
+ MG = 8
44
+ MR = 9
45
+ NULL = 10
46
+ WKS = 11
47
+ PTR = 12
48
+ HINFO = 13
49
+ MINFO = 14
50
+ MX = 15
51
+ TXT = 16
52
+ RP = 17
53
+ AFSDB = 18
54
+ X25 = 19
55
+ ISDN = 20
56
+ RT = 21
57
+ NSAP = 22
58
+ NSAPPTR = 23
59
+ SIG = 24
60
+ KEY = 25
61
+ PX = 26
62
+ GPOS = 27
63
+ AAAA = 28
64
+ LOC = 29
65
+ NXT = 30
66
+ EID = 31
67
+ NIMLOC = 32
68
+ SRV = 33
69
+ ATMA = 34
70
+ NAPTR = 35
71
+ KX = 36
72
+ CERT = 37
73
+ DNAME = 39
74
+ OPT = 41
75
+ DS = 43
76
+ SSHFP = 44
77
+ RRSIG = 46
78
+ NSEC = 47
79
+ DNSKEY = 48
80
+ UINFO = 100
81
+ UID = 101
82
+ GID = 102
83
+ UNSPEC = 103
84
+ TKEY = 249
85
+ TSIG = 250
86
+ IXFR = 251
87
+ AXFR = 252
88
+ MAILB = 253
89
+ MAILA = 254
90
+ ANY = 255
91
+
92
+ end
93
+
94
+ module QueryClasses
95
+
96
+ # Internet class
97
+ IN = 1
98
+
99
+ # Chaos class
100
+ CH = 3
101
+
102
+ # Hesiod class
103
+ HS = 4
104
+
105
+ # None class
106
+ NONE = 254
107
+
108
+ # Any class
109
+ ANY = 255
110
+
111
+ end
112
+
113
+ include QueryTypes
114
+ include QueryClasses
115
+
116
+ end # module DNS
117
+ end # module Net
@@ -0,0 +1,692 @@
1
+ #---
2
+ # $Id: Header.rb,v 1.5 2006/07/30 16:54:28 bluemonk Exp $
3
+ #+++
4
+
5
+ require 'net/dns/dns'
6
+
7
+ module Net # :nodoc:
8
+ module DNS
9
+
10
+ #
11
+ # =Name
12
+ #
13
+ # Net::DNS::Header - DNS packet header class
14
+ #
15
+ # =Synopsis
16
+ #
17
+ # require 'net/dns/header'
18
+ #
19
+ # =Description
20
+ #
21
+ # The Net::DNS::Header class represents the header portion of a
22
+ # DNS packet. An Header object is created whenever a new packet
23
+ # is parsed or as user request.
24
+ #
25
+ # header = Net::DNS::Header.new
26
+ # # ;; id = 18123
27
+ # # ;; qr = 0 opCode: 0 aa = 0 tc = 0 rd = 1
28
+ # # ;; ra = 0 ad = 0 cd = 0 rcode = 0
29
+ # # ;; qdCount = 1 anCount = 0 nsCount = 0 arCount = 0
30
+ #
31
+ # header.format
32
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33
+ # # | 18123 |
34
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35
+ # # |0| 0 |0|0|1|0|0| 0 | 0 |
36
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37
+ # # | 1 |
38
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39
+ # # | 0 |
40
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41
+ # # | 0 |
42
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43
+ # # | 0 |
44
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45
+ #
46
+ # # packet is an instance of Net::DNS::Packet
47
+ # header = packet.header
48
+ # puts "Answer is #{header.auth? ? '' : 'non'} authoritative"
49
+ #
50
+ # A lot of methods were written to keep a compatibility layer with
51
+ # the Perl version of the library, as long as methods name which are
52
+ # more or less the same.
53
+ #
54
+ # =Error classes
55
+ #
56
+ # Some error classes has been defined for the Net::DNS::Header class,
57
+ # which are listed here to keep a light and browsable main documentation.
58
+ # We have:
59
+ #
60
+ # * HeaderArgumentError: canonical argument error
61
+ # * HeaderWrongCount: a wrong +count+ parameter has been passed
62
+ # * HeaderWrongRecursive: a wrong +recursive+ parameter has been passed
63
+ # * HeaderWrongOpcode: a not valid +opCode+ has been specified
64
+ # * HeaderDuplicateID: the requested ID is already in use
65
+ #
66
+ # =Copyright
67
+ #
68
+ # Copyright (c) 2006 Marco Ceresa
69
+ #
70
+ # All rights reserved. This program is free software; you may redistribute
71
+ # it and/or modify it under the same terms as Ruby itself.
72
+ #
73
+ class Header
74
+
75
+ # Constant for +opCode+ query
76
+ QUERY = 0
77
+ # Constant for +opCode+ iquery
78
+ IQUERY = 1
79
+ # Constant for +opCode+ status
80
+ STATUS = 2
81
+ # Array with given strings
82
+ OPARR = %w[QUERY IQUERY STATUS]
83
+
84
+ # Constant for +rcode+ Response Code No Error
85
+ NOERROR = 0
86
+ # Constant for +rcode+ Response Code Format Error
87
+ FORMAT = 1
88
+ # Constant for +rcode+ Response Code Server Format Error
89
+ SERVER = 2
90
+ # Constant for +rcode+ Response Code Name Error
91
+ NAME = 3
92
+ # Constant for +rcode+ Response Code Not Implemented Error
93
+ NOTIMPLEMENTED = 4
94
+ # Constant for +rcode+ Response Code Refused Error
95
+ REFUSED = 5
96
+ # Array with given strings
97
+ CODEARR = %w[NOERROR FORMAT SERVER NAME NOTIMPLEMENTED REFUSED]
98
+
99
+ @@id_arr = []
100
+
101
+ # Reader for +id+ attribute
102
+ attr_reader :id
103
+ # Reader for the operational code
104
+ attr_reader :opCode
105
+ # Reader for question section entries number
106
+ attr_reader :qdCount
107
+ # Reader for answer section entries number
108
+ attr_reader :anCount
109
+ # Reader for authority section entries number
110
+ attr_reader :nsCount
111
+ # Reader for addictional section entries number
112
+ attr_reader :arCount
113
+
114
+ # Creates a new Net::DNS::Header object with the desired values,
115
+ # which can be specified as an Hash argument. When called without
116
+ # arguments, defaults are used. If a data string is passed, values
117
+ # are taken from parsing the string.
118
+ #
119
+ # Examples:
120
+ #
121
+ # # Create a new Net::DNS::Header object
122
+ # header = Net::DNS::Header.new
123
+ #
124
+ # # Create a new Net::DNS::Header object passing values
125
+ # header = Net::DNS::Header.new(:opCode => 1, :rd => 0)
126
+ #
127
+ # # Create a new Net::DNS::Header object with binary data
128
+ # header = Net::DNS::Header.new(data)
129
+ #
130
+ # Default values are:
131
+ #
132
+ # :id => auto generated
133
+ # :qr => 0 # Query response flag
134
+ # :aa => 0 # Authoritative answer flag
135
+ # :tc => 0 # Truncated packet flag
136
+ # :ra => 0 # Recursiond available flag
137
+ # :rCode => 0 # Response code (status of the query)
138
+ # :opCode => 0 # Operational code (purpose of the query)
139
+ # :cd => 0 # Checking disable flag
140
+ # :ad => 0 # Only relevant in DNSSEC context
141
+ # :rd => 1 # Recursion desired flag
142
+ # :qdCount => 1 # Number of questions in the dns packet
143
+ # :anCount => 0 # Number of answer RRs in the dns packet
144
+ # :nsCount => 0 # Number of authoritative RRs in the dns packet
145
+ # :arCount => 0 # Number of additional RRs in the dns packet
146
+ #
147
+ # See also each option for a detailed explanation of usage.
148
+ #
149
+ def initialize(arg = {})
150
+ if arg.kind_of? Hash
151
+ new_from_hash(arg)
152
+ else
153
+ raise HeaderArgumentError, "Wrong argument class: #{arg.class}"
154
+ end
155
+ end
156
+
157
+ # Creates a new Net::DNS::Header object from binary data, which is
158
+ # passed as a string object as argument.
159
+ # The configurations parameters are taken from parsing the string.
160
+ #
161
+ # Example:
162
+ #
163
+ # # Create a new Net::DNS::Header object with binary data
164
+ # header = Net::DNS::Header.new(data)
165
+ #
166
+ # header.auth?
167
+ # #=> "true" if it comes from authoritative name server
168
+ #
169
+ def self.parse(arg)
170
+ if arg.kind_of? String
171
+ o = allocate
172
+ o.send(:new_from_binary, arg)
173
+ o
174
+ else
175
+ raise HeaderArgumentError, "Wrong argument class: #{arg.class}"
176
+ end
177
+ end
178
+
179
+
180
+ # Inspect method, prints out all the options and relative values.
181
+ #
182
+ # p Net::DNS::Header.new
183
+ # # ;; id = 18123
184
+ # # ;; qr = 0 opCode: 0 aa = 0 tc = 0 rd = 1
185
+ # # ;; ra = 0 ad = 0 cd = 0 rcode = 0
186
+ # # ;; qdCount = 1 anCount = 0 nsCount = 0 arCount = 0
187
+ #
188
+ # This method will maybe be changed in the future to a more pretty
189
+ # way of display output.
190
+ #
191
+ def inspect
192
+ ";; id = #@id\n" +
193
+ if false # @opCode == "UPDATE"
194
+ #do stuff
195
+ else
196
+ ";; qr = #@qr\t" +
197
+ "opCode: #{opCode_str}\t" +
198
+ "aa = #@aa\t" +
199
+ "tc = #@tc\t" +
200
+ "rd = #@rd\n" +
201
+ ";; ra = #@ra\t" +
202
+ "ad = #@ad\t" +
203
+ "cd = #@cd\t" +
204
+ "rcode = #{rCode_str[0] || "NOERROR"}\n" +
205
+ ";; qdCount = #@qdCount\t"+
206
+ "anCount = #@anCount\t"+
207
+ "nsCount = #@nsCount\t"+
208
+ "arCount = #@arCount\n"
209
+ end
210
+ end
211
+
212
+ # The Net::DNS::Header#format method prints out the header
213
+ # in a special ascii representation of data, in a way
214
+ # similar to those often found on RFCs.
215
+ #
216
+ # p Net::DNS::Header.new.format
217
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
218
+ # # | 18123 |
219
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
220
+ # # |0| 0 |0|0|1|0|0| 0 | 0 |
221
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
222
+ # # | 1 |
223
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
224
+ # # | 0 |
225
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
226
+ # # | 0 |
227
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
228
+ # # | 0 |
229
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
230
+ #
231
+ # This can be very usefull for didactical purpouses :)
232
+ #
233
+ def format
234
+ del = ("+-" * 16) + "+\n"
235
+ len = del.length
236
+ str = del + "|" + @id.to_s.center(len-3) + "|\n"
237
+ str += del + "|" + @qr.to_s
238
+ str += "|" + @opCode.to_s.center(7)
239
+ str += "|" + @aa.to_s
240
+ str += "|" + @tc.to_s
241
+ str += "|" + @rd.to_s
242
+ str += "|" + @ra.to_s
243
+ str += "|" + @ad.to_s
244
+ str += "|" + @cd.to_s.center(3)
245
+ str += "|" + @rCode.to_s.center(7) + "|\n"
246
+ str += del + "|" + @qdCount.to_s.center(len-3) + "|\n"
247
+ str += del + "|" + @anCount.to_s.center(len-3) + "|\n"
248
+ str += del + "|" + @nsCount.to_s.center(len-3) + "|\n"
249
+ str += del + "|" + @arCount.to_s.center(len-3) + "|\n" + del
250
+ str
251
+ end
252
+
253
+ # Returns the header data in binary format, appropriate
254
+ # for use in a DNS query packet.
255
+ #
256
+ # hdata = header.data
257
+ # puts "Header is #{hdata.size} bytes"
258
+ #
259
+ def data
260
+ arr = []
261
+ arr.push(@id)
262
+ arr.push((@qr<<7)|(@opCode<<3)|(@aa<<2)|(@tc<<1)|@rd)
263
+ arr.push((@ra<<7)|(@ad<<5)|(@cd<<4)|@rCode)
264
+ arr.push(@qdCount)
265
+ arr.push(@anCount)
266
+ arr.push(@nsCount)
267
+ arr.push(@arCount)
268
+ arr.pack("n C2 n4")
269
+ end
270
+
271
+ # Set the ID for the current header. Useful when
272
+ # performing security tests.
273
+ #
274
+ def id=(val)
275
+ if @@id_arr.include? val
276
+ raise HeaderDuplicateID, "ID #{val} already used"
277
+ end
278
+ if (1..65535).include? val
279
+ @id = val
280
+ @@id_arr.push val
281
+ else
282
+ raise HeaderArgumentError, "ID #{val} out of range"
283
+ end
284
+ end
285
+
286
+ # Checks whether the header is a query (+qr+ bit set to 0)
287
+ #
288
+ def query?
289
+ @qr == 0
290
+ end
291
+
292
+ # Set the +qr+ query response flag to be either +true+ or
293
+ # +false+. You can also use the values 0 and 1. This flag
294
+ # indicates if the DNS packet contains a query or an answer,
295
+ # so it should be set to +true+ in DNS answer packets.
296
+ # If +qr+ is +true+, the packet is a response.
297
+ #
298
+ def qr=(val)
299
+ case val
300
+ when true
301
+ @qr = 1
302
+ when false
303
+ @qr = 0
304
+ when 0,1
305
+ @qr = val
306
+ else
307
+ raise HeaderArgumentError, ":qr must be true(or 1) or false(or 0)"
308
+ end
309
+ end
310
+
311
+ # Checks whether the header is a response
312
+ # (+qr+ bit set to 1)
313
+ #
314
+ def response?
315
+ @qr == 1
316
+ end
317
+
318
+ # Returns a string representation of the +opCode+
319
+ #
320
+ # puts "Packet is a #{header.opCode_str}"
321
+ # #=> Packet is a QUERY
322
+ #
323
+ def opCode_str
324
+ OPARR[@opCode]
325
+ end
326
+
327
+ # Set the +opCode+ variable to a new value. This fields indicates
328
+ # the type of the question present in the DNS packet; +val+ can be
329
+ # one of the values QUERY, IQUERY or STATUS.
330
+ #
331
+ # * QUERY is the standard DNS query
332
+ # * IQUERY is the inverse query
333
+ # * STATUS is used to query the nameserver for its status
334
+ #
335
+ # Example:
336
+ #
337
+ # include Net::DNS
338
+ # header = Header.new
339
+ # header.opCode = Header::STATUS
340
+ #
341
+ def opCode=(val)
342
+ if (0..2).include? val
343
+ @opCode = val
344
+ else
345
+ raise HeaderWrongOpcode, "Wrong opCode value (#{val}), must be QUERY, IQUERY or STATUS"
346
+ end
347
+ end
348
+
349
+ # Checks whether the response is authoritative
350
+ #
351
+ # if header.auth?
352
+ # puts "Response is authoritative"
353
+ # else
354
+ # puts "Answer is NOT authoritative"
355
+ # end
356
+ #
357
+ def auth?
358
+ @aa == 1
359
+ end
360
+
361
+ # Set the +aa+ flag (authoritative answer) to either +true+
362
+ # or +false+. You can also use 0 or 1.
363
+ #
364
+ # This flag indicates whether a DNS answer packet contains
365
+ # authoritative data, meaning that is was generated by a
366
+ # nameserver authoritative for the domain of the question.
367
+ #
368
+ # Must only be set to +true+ in DNS answer packets.
369
+ #
370
+ def aa=(val)
371
+ case val
372
+ when true
373
+ @aa = 1
374
+ when false
375
+ @aa = 0
376
+ when 0,1
377
+ @aa = val
378
+ else
379
+ raise HeaderArgumentError, ":aa must be true(or 1) or false(or 0)"
380
+ end
381
+ end
382
+
383
+ # Checks whether the packet was truncated
384
+ #
385
+ # # Sending packet using UDP
386
+ # if header.truncated?
387
+ # puts "Warning, packet has been truncated!"
388
+ # # Sending packet using TCP
389
+ # end
390
+ # # Do something with the answer
391
+ #
392
+ def truncated?
393
+ @tc == 1
394
+ end
395
+
396
+ # Set the +tc+ flag (truncated packet) to either +true+
397
+ # ot +false+. You can also use 0 or 1.
398
+ #
399
+ # The truncated flag is used in response packets to indicate
400
+ # that the amount of data to be trasmitted exceedes the
401
+ # maximum allowed by the protocol in use, tipically UDP, and
402
+ # that the data present in the packet has been truncated.
403
+ # A different protocol (such has TCP) need to be used to
404
+ # retrieve full data.
405
+ #
406
+ # Must only be set in DNS answer packets.
407
+ #
408
+ def tc=(val)
409
+ case val
410
+ when true
411
+ @tc = 1
412
+ when false
413
+ @tc = 0
414
+ when 0,1
415
+ @tc = val
416
+ else
417
+ raise HeaderArgumentError, ":tc must be true(or 1) or false(or 0)"
418
+ end
419
+ end
420
+
421
+ # Checks whether the packet has a recursion bit
422
+ # set, meaning that recursion is desired
423
+ #
424
+ def recursive?
425
+ @rd == 1
426
+ end
427
+
428
+ # Sets the recursion desidered bit.
429
+ # Remember that recursion query support is
430
+ # optional.
431
+ #
432
+ # header.recursive = true
433
+ # hdata = header.data # suitable for sending
434
+ #
435
+ # Consult RFC1034 and RFC1035 for a detailed explanation
436
+ # of how recursion works.
437
+ #
438
+ def recursive=(val)
439
+ case val
440
+ when true
441
+ @rd = 1
442
+ when false
443
+ @rd = 0
444
+ when 1
445
+ @rd = 1
446
+ when 0
447
+ @rd = 0
448
+ else
449
+ raise HeaderWrongRecursive, "Wrong value (#{val}), please specify true (1) or false (0)"
450
+ end
451
+ end
452
+
453
+ # Alias for Header#recursive= to keep compatibility
454
+ # with the Perl version.
455
+ #
456
+ def rd=(val)
457
+ self.recursive = val
458
+ end
459
+
460
+ # Checks whether recursion is available.
461
+ # This flag is usually set by nameservers to indicate
462
+ # that they support recursive-type queries.
463
+ #
464
+ def r_available?
465
+ @ra == 1
466
+ end
467
+
468
+ # Set the +ra+ flag (recursion available) to either +true+ or
469
+ # +false+. You can also use 0 and 1.
470
+ #
471
+ # This flag must only be set in DNS answer packets.
472
+ #
473
+ def ra=(val)
474
+ case val
475
+ when true
476
+ @ra = 1
477
+ when false
478
+ @ra = 0
479
+ when 0,1
480
+ @ra = val
481
+ else
482
+ raise HeaderArgumentError, ":ra must be true(or 1) or false(or 0)"
483
+ end
484
+ end
485
+
486
+ # Checks whether checking is enabled or disabled.
487
+ #
488
+ # Checking is enabled by default.
489
+ #
490
+ def checking?
491
+ @cd == 0
492
+ end
493
+
494
+ # Set the +cd+ flag (checking disabled) to either +true+
495
+ # ot +false+. You can also use 0 or 1.
496
+ #
497
+ def cd=(val)
498
+ case val
499
+ when true
500
+ @cd = 1
501
+ when false
502
+ @cd = 0
503
+ when 0,1
504
+ @cd = val
505
+ else
506
+ raise HeaderArgumentError, ":cd must be true(or 1) or false(or 0)"
507
+ end
508
+ end
509
+
510
+ # Checks whether +ad+ flag has been set.
511
+ #
512
+ # This flag is only relevant in DNSSEC context.
513
+ #
514
+ def verified?
515
+ @ad == 1
516
+ end
517
+
518
+ # Set the +ad+ flag to either +true+
519
+ # ot +false+. You can also use 0 or 1.
520
+ #
521
+ # The AD bit is only set on answers where signatures have
522
+ # been cryptographically verified or the server is
523
+ # authoritative for the data and is allowed to set the bit by policy.
524
+ #
525
+ def ad=(val)
526
+ case val
527
+ when true
528
+ @ad = 1
529
+ when false
530
+ @ad = 0
531
+ when 0,1
532
+ @ad = val
533
+ else
534
+ raise HeaderArgumentError, ":ad must be true(or 1) or false(or 0)"
535
+ end
536
+ end
537
+
538
+ # Returns an error array for the header response code, or
539
+ # +nil+ if no error is generated.
540
+ #
541
+ # error, cause = header.rcode_str
542
+ # puts "Error #{error} cause by: #{cause}" if error
543
+ # #=> Error Format Error caused by: The name server
544
+ # #=> was unable to interpret the query
545
+ #
546
+ def rCode_str
547
+ case @rCode
548
+ when NOERROR
549
+ return nil,nil
550
+ when FORMAT
551
+ return "Format Error", "The name server was unable to interpret the query"
552
+ when SERVER
553
+ return "Server Failure",
554
+ "The name server was unable to process this query due to problem with the name server"
555
+ when NAME
556
+ return "Name Error", "Domain name referenced in the query does not exists"
557
+ when NOTIMPLEMENTED
558
+ return "Not Implemented",
559
+ "The name server does not support the requested kind of query"
560
+ when REFUSED
561
+ return "Refused",
562
+ "The name server refuses to perform the specified operation for policy reasons"
563
+ end
564
+ end
565
+
566
+ # Return the value of rCode variable
567
+ #
568
+ # if header.rcode == Net::DNS::Header::NOERROR
569
+ # puts "No errors in DNS answer packet"
570
+ # end
571
+ #
572
+ def rCode
573
+ @rCode
574
+ end
575
+
576
+ # Set the rCode value. This should only be done in DNS
577
+ # answer packets.
578
+ #
579
+ def rCode=(val)
580
+ if (0..5).include? val
581
+ @rCode = val
582
+ else
583
+ raise HeaderArgumentError, ":rCode must be one of #{CODEARR.join(" ")}"
584
+ end
585
+ end
586
+
587
+ # Sets the number of entries in a question section
588
+ #
589
+ def qdCount=(val)
590
+ if (0..65535).include? val
591
+ @qdCount = val
592
+ else
593
+ raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"
594
+ end
595
+ end
596
+
597
+ # Sets the number of RRs in an answer section
598
+ #
599
+ def anCount=(val)
600
+ if (0..65535).include? val
601
+ @anCount = val
602
+ else
603
+ raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"
604
+ end
605
+ end
606
+
607
+ # Sets the number of RRs in an authority section
608
+ #
609
+ def nsCount=(val)
610
+ if (0..65535).include? val
611
+ @nsCount = val
612
+ else
613
+ raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"
614
+ end
615
+ end
616
+
617
+ # Sets the number of RRs in an addictional section
618
+ #
619
+ def arCount=(val)
620
+ if (0..65535).include? val
621
+ @arCount = val
622
+ else
623
+ raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"
624
+ end
625
+ end
626
+
627
+ private
628
+
629
+ def new_from_scratch
630
+ @id = genID # generate ad unique id
631
+ @qr = @aa = @tc = @ra = @ad = @cd = 0
632
+ @rCode = NOERROR
633
+ @anCount = @nsCount = @arCount = 0
634
+ @rd = @qdCount = 1
635
+ @opCode = QUERY # standard query, default message
636
+ end
637
+
638
+ def new_from_binary(str)
639
+ unless str.size == Net::DNS::HFIXEDSZ
640
+ raise HeaderArgumentError, "Header binary data has wrong size: #{str.size} bytes"
641
+ end
642
+ arr = str.unpack("n C2 n4")
643
+ @id = arr[0]
644
+ @qr = (arr[1] >> 7) & 0x01
645
+ @opCode = (arr[1] >> 3) & 0x0F
646
+ @aa = (arr[1] >> 2) & 0x01
647
+ @tc = (arr[1] >> 1) & 0x01
648
+ @rd = arr[1] & 0x1
649
+ @ra = (arr[2] >> 7) & 0x01
650
+ @ad = (arr[2] >> 5) & 0x01
651
+ @cd = (arr[2] >> 4) & 0x01
652
+ @rCode = arr[2] & 0xf
653
+ @qdCount = arr[3]
654
+ @anCount = arr[4]
655
+ @nsCount = arr[5]
656
+ @arCount = arr[6]
657
+ end
658
+
659
+ def new_from_hash(hash)
660
+ new_from_scratch
661
+ hash.each do |key,val|
662
+ eval "self.#{key.to_s} = val"
663
+ end
664
+ end
665
+
666
+ def genID
667
+ while (@@id_arr.include?(q = rand(65535)))
668
+ end
669
+ @@id_arr.push(q)
670
+ q
671
+ end
672
+
673
+ end # class Header
674
+
675
+ end # class DNS
676
+ end # module Net
677
+
678
+
679
+ class HeaderArgumentError < ArgumentError # :nodoc: all
680
+ end
681
+
682
+ class HeaderWrongCount < ArgumentError # :nodoc: all
683
+ end
684
+
685
+ class HeaderWrongRecursive < ArgumentError # :nodoc: all
686
+ end
687
+
688
+ class HeaderWrongOpcode < ArgumentError # :nodoc: all
689
+ end
690
+
691
+ class HeaderDuplicateID < ArgumentError # :nodoc: all
692
+ end