bluemonk-net-dns 0.5.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.
@@ -0,0 +1,761 @@
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
+ #
76
+ # =Name
77
+ #
78
+ # Net::DNS::Header::RCode - DNS Header RCode handling class
79
+ #
80
+ # =Synopsis
81
+ #
82
+ # It should be used internally by Net::DNS::Header class. However, it's still
83
+ # possible to instantiate it directly.
84
+ #
85
+ # require 'net/dns/header'
86
+ # rcode = Net::DNS::Header::RCode.new 0
87
+ #
88
+ # =Description
89
+ #
90
+ # The RCode class represents the RCode field in the Header portion of a
91
+ # DNS packet. This field (called Response Code) is used to get informations
92
+ # about the status of a DNS operation, such as a query or an update. These
93
+ # are the values in the original Mockapetris's standard (RFC1035):
94
+ #
95
+ # * 0 No error condition
96
+ # * 1 Format error - The name server was unable to interpret
97
+ # the query.
98
+ # * 2 Server failure - The name server was
99
+ # unable to process this query due to a
100
+ # problem with the name server.
101
+ # * 3 Name Error - Meaningful only for
102
+ # responses from an authoritative name
103
+ # server, this code signifies that the
104
+ # domain name referenced in the query does
105
+ # not exist.
106
+ # * 4 Not Implemented - The name server does
107
+ # not support the requested kind of query.
108
+ # * 5 Refused - The name server refuses to
109
+ # perform the specified operation for
110
+ # policy reasons. For example, a name
111
+ # server may not wish to provide the
112
+ # information to the particular requester,
113
+ # or a name server may not wish to perform
114
+ # a particular operation (e.g., zone
115
+ # transfer) for particular data.
116
+ # * 6-15 Reserved for future use.
117
+ #
118
+ # In the next DNS RFCs, codes 6-15 has been assigned to the following
119
+ # errors:
120
+ #
121
+ # * 6 YXDomain
122
+ # * 7 YXRRSet
123
+ # * 8 NXRRSet
124
+ # * 9 NotAuth
125
+ # * 10 NotZone
126
+ #
127
+ # More RCodes has to come for TSIGs and other operations.
128
+ #
129
+ class RCode
130
+
131
+ # Constant for +rcode+ Response Code No Error
132
+ NOERROR = 0
133
+ # Constant for +rcode+ Response Code Format Error
134
+ FORMAT = 1
135
+ # Constant for +rcode+ Response Code Server Format Error
136
+ SERVER = 2
137
+ # Constant for +rcode+ Response Code Name Error
138
+ NAME = 3
139
+ # Constant for +rcode+ Response Code Not Implemented Error
140
+ NOTIMPLEMENTED = 4
141
+ # Constant for +rcode+ Response Code Refused Error
142
+ REFUSED = 5
143
+
144
+
145
+
146
+ RCodeType = %w[NoError FormErr ServFail NXDomain NotImp
147
+ Refused YXDomain YXRRSet NXRRSet NotAuth NotZone]
148
+
149
+ RCodeErrorString = ["No errors",
150
+ "The name server was unable to interpret the query",
151
+ "The name server was unable to process this query due to problem with the name server",
152
+ "Domain name referenced in the query does not exists",
153
+ "The name server does not support the requested kind of query",
154
+ "The name server refuses to perform the specified operation for policy reasons",
155
+ "",
156
+ "",
157
+ "",
158
+ "",
159
+ ""]
160
+
161
+ attr_reader :code, :type, :explanation
162
+
163
+ def initialize(code)
164
+ if (0..10).include? code
165
+ @code = code
166
+ @type = RCodeType[code]
167
+ @explanation = RCodeErrorString[code]
168
+ else
169
+ raise HeaderArgumentError, "RCode #{code} out of range"
170
+ end
171
+ end
172
+
173
+ def to_s
174
+ @code.to_s
175
+ end
176
+ end
177
+
178
+ # Constant for +opCode+ query
179
+ QUERY = 0
180
+ # Constant for +opCode+ iquery
181
+ IQUERY = 1
182
+ # Constant for +opCode+ status
183
+ STATUS = 2
184
+ # Array with given strings
185
+ OPARR = %w[QUERY IQUERY STATUS]
186
+
187
+ @@id_arr = []
188
+
189
+ # Reader for +id+ attribute
190
+ attr_reader :id
191
+ # Reader for the operational code
192
+ attr_reader :opCode
193
+ # Reader for the rCode instance
194
+ attr_reader :rCode
195
+ # Reader for question section entries number
196
+ attr_reader :qdCount
197
+ # Reader for answer section entries number
198
+ attr_reader :anCount
199
+ # Reader for authority section entries number
200
+ attr_reader :nsCount
201
+ # Reader for addictional section entries number
202
+ attr_reader :arCount
203
+
204
+ # Creates a new Net::DNS::Header object with the desired values,
205
+ # which can be specified as an Hash argument. When called without
206
+ # arguments, defaults are used. If a data string is passed, values
207
+ # are taken from parsing the string.
208
+ #
209
+ # Examples:
210
+ #
211
+ # # Create a new Net::DNS::Header object
212
+ # header = Net::DNS::Header.new
213
+ #
214
+ # # Create a new Net::DNS::Header object passing values
215
+ # header = Net::DNS::Header.new(:opCode => 1, :rd => 0)
216
+ #
217
+ # # Create a new Net::DNS::Header object with binary data
218
+ # header = Net::DNS::Header.new(data)
219
+ #
220
+ # Default values are:
221
+ #
222
+ # :id => auto generated
223
+ # :qr => 0 # Query response flag
224
+ # :aa => 0 # Authoritative answer flag
225
+ # :tc => 0 # Truncated packet flag
226
+ # :ra => 0 # Recursiond available flag
227
+ # :rCode => 0 # Response code (status of the query)
228
+ # :opCode => 0 # Operational code (purpose of the query)
229
+ # :cd => 0 # Checking disable flag
230
+ # :ad => 0 # Only relevant in DNSSEC context
231
+ # :rd => 1 # Recursion desired flag
232
+ # :qdCount => 1 # Number of questions in the dns packet
233
+ # :anCount => 0 # Number of answer RRs in the dns packet
234
+ # :nsCount => 0 # Number of authoritative RRs in the dns packet
235
+ # :arCount => 0 # Number of additional RRs in the dns packet
236
+ #
237
+ # See also each option for a detailed explanation of usage.
238
+ #
239
+ def initialize(arg = {})
240
+ if arg.kind_of? Hash
241
+ new_from_hash(arg)
242
+ else
243
+ raise HeaderArgumentError, "Wrong argument class: #{arg.class}"
244
+ end
245
+ end
246
+
247
+ # Creates a new Net::DNS::Header object from binary data, which is
248
+ # passed as a string object as argument.
249
+ # The configurations parameters are taken from parsing the string.
250
+ #
251
+ # Example:
252
+ #
253
+ # # Create a new Net::DNS::Header object with binary data
254
+ # header = Net::DNS::Header.new(data)
255
+ #
256
+ # header.auth?
257
+ # #=> "true" if it comes from authoritative name server
258
+ #
259
+ def self.parse(arg)
260
+ if arg.kind_of? String
261
+ o = allocate
262
+ o.send(:new_from_binary, arg)
263
+ o
264
+ else
265
+ raise HeaderArgumentError, "Wrong argument class: #{arg.class}"
266
+ end
267
+ end
268
+
269
+ # Inspect method, prints out all the options and relative values.
270
+ #
271
+ # p Net::DNS::Header.new
272
+ # # ;; id = 18123
273
+ # # ;; qr = 0 opCode: 0 aa = 0 tc = 0 rd = 1
274
+ # # ;; ra = 0 ad = 0 cd = 0 rcode = 0
275
+ # # ;; qdCount = 1 anCount = 0 nsCount = 0 arCount = 0
276
+ #
277
+ # This method will maybe be changed in the future to a more pretty
278
+ # way of display output.
279
+ #
280
+ def inspect
281
+ ";; id = #@id\n" +
282
+ if false # @opCode == "UPDATE"
283
+ #do stuff
284
+ else
285
+ ";; qr = #@qr\t" +
286
+ "opCode: #{opCode_str}\t" +
287
+ "aa = #@aa\t" +
288
+ "tc = #@tc\t" +
289
+ "rd = #@rd\n" +
290
+ ";; ra = #@ra\t" +
291
+ "ad = #@ad\t" +
292
+ "cd = #@cd\t" +
293
+ "rcode = #{@rCode.type}\n" +
294
+ ";; qdCount = #@qdCount\t"+
295
+ "anCount = #@anCount\t"+
296
+ "nsCount = #@nsCount\t"+
297
+ "arCount = #@arCount\n"
298
+ end
299
+ end
300
+
301
+ # The Net::DNS::Header#format method prints out the header
302
+ # in a special ascii representation of data, in a way
303
+ # similar to those often found on RFCs.
304
+ #
305
+ # p Net::DNS::Header.new.format
306
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
307
+ # # | 18123 |
308
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
309
+ # # |0| 0 |0|0|1|0|0| 0 | 0 |
310
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
311
+ # # | 1 |
312
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
313
+ # # | 0 |
314
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
315
+ # # | 0 |
316
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
317
+ # # | 0 |
318
+ # # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
319
+ #
320
+ # This can be very usefull for didactical purpouses :)
321
+ #
322
+ def format
323
+ del = ("+-" * 16) + "+\n"
324
+ len = del.length
325
+ str = del + "|" + @id.to_s.center(len-3) + "|\n"
326
+ str += del + "|" + @qr.to_s
327
+ str += "|" + @opCode.to_s.center(7)
328
+ str += "|" + @aa.to_s
329
+ str += "|" + @tc.to_s
330
+ str += "|" + @rd.to_s
331
+ str += "|" + @ra.to_s
332
+ str += "|" + @ad.to_s
333
+ str += "|" + @cd.to_s.center(3)
334
+ str += "|" + @rCode.to_s.center(7) + "|\n"
335
+ str += del + "|" + @qdCount.to_s.center(len-3) + "|\n"
336
+ str += del + "|" + @anCount.to_s.center(len-3) + "|\n"
337
+ str += del + "|" + @nsCount.to_s.center(len-3) + "|\n"
338
+ str += del + "|" + @arCount.to_s.center(len-3) + "|\n" + del
339
+ str
340
+ end
341
+
342
+ # Returns the header data in binary format, appropriate
343
+ # for use in a DNS query packet.
344
+ #
345
+ # hdata = header.data
346
+ # puts "Header is #{hdata.size} bytes"
347
+ #
348
+ def data
349
+ arr = []
350
+ arr.push(@id)
351
+ arr.push((@qr<<7)|(@opCode<<3)|(@aa<<2)|(@tc<<1)|@rd)
352
+ arr.push((@ra<<7)|(@ad<<5)|(@cd<<4)|@rCode.code)
353
+ arr.push(@qdCount)
354
+ arr.push(@anCount)
355
+ arr.push(@nsCount)
356
+ arr.push(@arCount)
357
+ arr.pack("n C2 n4")
358
+ end
359
+
360
+ # Set the ID for the current header. Useful when
361
+ # performing security tests.
362
+ #
363
+ def id=(val)
364
+ if @@id_arr.include? val
365
+ raise HeaderDuplicateID, "ID #{val} already used"
366
+ end
367
+ if (1..65535).include? val
368
+ @id = val
369
+ @@id_arr.push val
370
+ else
371
+ raise HeaderArgumentError, "ID #{val} out of range"
372
+ end
373
+ end
374
+
375
+ # Checks whether the header is a query (+qr+ bit set to 0)
376
+ #
377
+ def query?
378
+ @qr == 0
379
+ end
380
+
381
+ # Set the +qr+ query response flag to be either +true+ or
382
+ # +false+. You can also use the values 0 and 1. This flag
383
+ # indicates if the DNS packet contains a query or an answer,
384
+ # so it should be set to +true+ in DNS answer packets.
385
+ # If +qr+ is +true+, the packet is a response.
386
+ #
387
+ def qr=(val)
388
+ case val
389
+ when true
390
+ @qr = 1
391
+ when false
392
+ @qr = 0
393
+ when 0,1
394
+ @qr = val
395
+ else
396
+ raise HeaderArgumentError, ":qr must be true(or 1) or false(or 0)"
397
+ end
398
+ end
399
+
400
+ # Checks whether the header is a response
401
+ # (+qr+ bit set to 1)
402
+ #
403
+ def response?
404
+ @qr == 1
405
+ end
406
+
407
+ # Returns a string representation of the +opCode+
408
+ #
409
+ # puts "Packet is a #{header.opCode_str}"
410
+ # #=> Packet is a QUERY
411
+ #
412
+ def opCode_str
413
+ OPARR[@opCode]
414
+ end
415
+
416
+ # Set the +opCode+ variable to a new value. This fields indicates
417
+ # the type of the question present in the DNS packet; +val+ can be
418
+ # one of the values QUERY, IQUERY or STATUS.
419
+ #
420
+ # * QUERY is the standard DNS query
421
+ # * IQUERY is the inverse query
422
+ # * STATUS is used to query the nameserver for its status
423
+ #
424
+ # Example:
425
+ #
426
+ # include Net::DNS
427
+ # header = Header.new
428
+ # header.opCode = Header::STATUS
429
+ #
430
+ def opCode=(val)
431
+ if (0..2).include? val
432
+ @opCode = val
433
+ else
434
+ raise HeaderWrongOpcode, "Wrong opCode value (#{val}), must be QUERY, IQUERY or STATUS"
435
+ end
436
+ end
437
+
438
+ # Checks whether the response is authoritative
439
+ #
440
+ # if header.auth?
441
+ # puts "Response is authoritative"
442
+ # else
443
+ # puts "Answer is NOT authoritative"
444
+ # end
445
+ #
446
+ def auth?
447
+ @aa == 1
448
+ end
449
+
450
+ # Set the +aa+ flag (authoritative answer) to either +true+
451
+ # or +false+. You can also use 0 or 1.
452
+ #
453
+ # This flag indicates whether a DNS answer packet contains
454
+ # authoritative data, meaning that is was generated by a
455
+ # nameserver authoritative for the domain of the question.
456
+ #
457
+ # Must only be set to +true+ in DNS answer packets.
458
+ #
459
+ def aa=(val)
460
+ case val
461
+ when true
462
+ @aa = 1
463
+ when false
464
+ @aa = 0
465
+ when 0,1
466
+ @aa = val
467
+ else
468
+ raise HeaderArgumentError, ":aa must be true(or 1) or false(or 0)"
469
+ end
470
+ end
471
+
472
+ # Checks whether the packet was truncated
473
+ #
474
+ # # Sending packet using UDP
475
+ # if header.truncated?
476
+ # puts "Warning, packet has been truncated!"
477
+ # # Sending packet using TCP
478
+ # end
479
+ # # Do something with the answer
480
+ #
481
+ def truncated?
482
+ @tc == 1
483
+ end
484
+
485
+ # Set the +tc+ flag (truncated packet) to either +true+
486
+ # ot +false+. You can also use 0 or 1.
487
+ #
488
+ # The truncated flag is used in response packets to indicate
489
+ # that the amount of data to be trasmitted exceedes the
490
+ # maximum allowed by the protocol in use, tipically UDP, and
491
+ # that the data present in the packet has been truncated.
492
+ # A different protocol (such has TCP) need to be used to
493
+ # retrieve full data.
494
+ #
495
+ # Must only be set in DNS answer packets.
496
+ #
497
+ def tc=(val)
498
+ case val
499
+ when true
500
+ @tc = 1
501
+ when false
502
+ @tc = 0
503
+ when 0,1
504
+ @tc = val
505
+ else
506
+ raise HeaderArgumentError, ":tc must be true(or 1) or false(or 0)"
507
+ end
508
+ end
509
+
510
+ # Checks whether the packet has a recursion bit
511
+ # set, meaning that recursion is desired
512
+ #
513
+ def recursive?
514
+ @rd == 1
515
+ end
516
+
517
+ # Sets the recursion desidered bit.
518
+ # Remember that recursion query support is
519
+ # optional.
520
+ #
521
+ # header.recursive = true
522
+ # hdata = header.data # suitable for sending
523
+ #
524
+ # Consult RFC1034 and RFC1035 for a detailed explanation
525
+ # of how recursion works.
526
+ #
527
+ def recursive=(val)
528
+ case val
529
+ when true
530
+ @rd = 1
531
+ when false
532
+ @rd = 0
533
+ when 1
534
+ @rd = 1
535
+ when 0
536
+ @rd = 0
537
+ else
538
+ raise HeaderWrongRecursive, "Wrong value (#{val}), please specify true (1) or false (0)"
539
+ end
540
+ end
541
+
542
+ # Alias for Header#recursive= to keep compatibility
543
+ # with the Perl version.
544
+ #
545
+ def rd=(val)
546
+ self.recursive = val
547
+ end
548
+
549
+ # Checks whether recursion is available.
550
+ # This flag is usually set by nameservers to indicate
551
+ # that they support recursive-type queries.
552
+ #
553
+ def r_available?
554
+ @ra == 1
555
+ end
556
+
557
+ # Set the +ra+ flag (recursion available) to either +true+ or
558
+ # +false+. You can also use 0 and 1.
559
+ #
560
+ # This flag must only be set in DNS answer packets.
561
+ #
562
+ def ra=(val)
563
+ case val
564
+ when true
565
+ @ra = 1
566
+ when false
567
+ @ra = 0
568
+ when 0,1
569
+ @ra = val
570
+ else
571
+ raise HeaderArgumentError, ":ra must be true(or 1) or false(or 0)"
572
+ end
573
+ end
574
+
575
+ # Checks whether checking is enabled or disabled.
576
+ #
577
+ # Checking is enabled by default.
578
+ #
579
+ def checking?
580
+ @cd == 0
581
+ end
582
+
583
+ # Set the +cd+ flag (checking disabled) to either +true+
584
+ # ot +false+. You can also use 0 or 1.
585
+ #
586
+ def cd=(val)
587
+ case val
588
+ when true
589
+ @cd = 1
590
+ when false
591
+ @cd = 0
592
+ when 0,1
593
+ @cd = val
594
+ else
595
+ raise HeaderArgumentError, ":cd must be true(or 1) or false(or 0)"
596
+ end
597
+ end
598
+
599
+ # Checks whether +ad+ flag has been set.
600
+ #
601
+ # This flag is only relevant in DNSSEC context.
602
+ #
603
+ def verified?
604
+ @ad == 1
605
+ end
606
+
607
+ # Set the +ad+ flag to either +true+
608
+ # ot +false+. You can also use 0 or 1.
609
+ #
610
+ # The AD bit is only set on answers where signatures have
611
+ # been cryptographically verified or the server is
612
+ # authoritative for the data and is allowed to set the bit by policy.
613
+ #
614
+ def ad=(val)
615
+ case val
616
+ when true
617
+ @ad = 1
618
+ when false
619
+ @ad = 0
620
+ when 0,1
621
+ @ad = val
622
+ else
623
+ raise HeaderArgumentError, ":ad must be true(or 1) or false(or 0)"
624
+ end
625
+ end
626
+
627
+ # Returns an error array for the header response code, or
628
+ # +nil+ if no error is generated.
629
+ #
630
+ # error, cause = header.rCode_str
631
+ # puts "Error #{error} cause by: #{cause}" if error
632
+ # #=> Error ForErr caused by: The name server
633
+ # #=> was unable to interpret the query
634
+ #
635
+ def rCode_str
636
+ return rCode.type, rCode.explanation
637
+ end
638
+
639
+ # Checks for errors in the DNS packet
640
+ #
641
+ # unless header.error?
642
+ # puts "No errors in DNS answer packet"
643
+ # end
644
+ #
645
+ def error?
646
+ @rCode.code > 0
647
+ end
648
+
649
+ # Set the rCode value. This should only be done in DNS
650
+ # answer packets.
651
+ #
652
+ def rCode=(val)
653
+ @rCode = RCode.new(val)
654
+ end
655
+
656
+ # Sets the number of entries in a question section
657
+ #
658
+ def qdCount=(val)
659
+ if (0..65535).include? val
660
+ @qdCount = val
661
+ else
662
+ raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"
663
+ end
664
+ end
665
+
666
+ # Sets the number of RRs in an answer section
667
+ #
668
+ def anCount=(val)
669
+ if (0..65535).include? val
670
+ @anCount = val
671
+ else
672
+ raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"
673
+ end
674
+ end
675
+
676
+ # Sets the number of RRs in an authority section
677
+ #
678
+ def nsCount=(val)
679
+ if (0..65535).include? val
680
+ @nsCount = val
681
+ else
682
+ raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"
683
+ end
684
+ end
685
+
686
+ # Sets the number of RRs in an addictional section
687
+ #
688
+ def arCount=(val)
689
+ if (0..65535).include? val
690
+ @arCount = val
691
+ else
692
+ raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"
693
+ end
694
+ end
695
+
696
+ private
697
+
698
+ def new_from_scratch
699
+ @id = genID # generate ad unique id
700
+ @qr = @aa = @tc = @ra = @ad = @cd = 0
701
+ @rCode = RCode.new(0) # no error
702
+ @anCount = @nsCount = @arCount = 0
703
+ @rd = @qdCount = 1
704
+ @opCode = QUERY # standard query, default message
705
+ end
706
+
707
+ def new_from_binary(str)
708
+ unless str.size == Net::DNS::HFIXEDSZ
709
+ raise HeaderArgumentError, "Header binary data has wrong size: #{str.size} bytes"
710
+ end
711
+ arr = str.unpack("n C2 n4")
712
+ @id = arr[0]
713
+ @qr = (arr[1] >> 7) & 0x01
714
+ @opCode = (arr[1] >> 3) & 0x0F
715
+ @aa = (arr[1] >> 2) & 0x01
716
+ @tc = (arr[1] >> 1) & 0x01
717
+ @rd = arr[1] & 0x1
718
+ @ra = (arr[2] >> 7) & 0x01
719
+ @ad = (arr[2] >> 5) & 0x01
720
+ @cd = (arr[2] >> 4) & 0x01
721
+ @rCode = RCode.new(arr[2] & 0xf)
722
+ @qdCount = arr[3]
723
+ @anCount = arr[4]
724
+ @nsCount = arr[5]
725
+ @arCount = arr[6]
726
+ end
727
+
728
+ def new_from_hash(hash)
729
+ new_from_scratch
730
+ hash.each do |key,val|
731
+ eval "self.#{key.to_s} = val"
732
+ end
733
+ end
734
+
735
+ def genID
736
+ while (@@id_arr.include?(q = rand(65535)))
737
+ end
738
+ @@id_arr.push(q)
739
+ q
740
+ end
741
+
742
+ end # class Header
743
+
744
+ end # class DNS
745
+ end # module Net
746
+
747
+
748
+ class HeaderArgumentError < ArgumentError # :nodoc: all
749
+ end
750
+
751
+ class HeaderWrongCount < ArgumentError # :nodoc: all
752
+ end
753
+
754
+ class HeaderWrongRecursive < ArgumentError # :nodoc: all
755
+ end
756
+
757
+ class HeaderWrongOpcode < ArgumentError # :nodoc: all
758
+ end
759
+
760
+ class HeaderDuplicateID < ArgumentError # :nodoc: all
761
+ end