CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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