bluemonk-net-dns 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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