dnsruby 1.55 → 1.56.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +96 -0
  3. data/Rakefile +30 -29
  4. data/demo/axfr.rb +93 -93
  5. data/demo/check_soa.rb +99 -99
  6. data/demo/check_zone.rb +59 -59
  7. data/demo/digdlv.rb +43 -43
  8. data/demo/digroot.rb +34 -34
  9. data/demo/example_recurse.rb +14 -14
  10. data/demo/mresolv.rb +30 -30
  11. data/demo/mx.rb +31 -31
  12. data/demo/rubydig.rb +37 -37
  13. data/demo/to_resolve.txt +3088 -3088
  14. data/demo/trace_dns.rb +46 -46
  15. data/lib/dnsruby.rb +161 -526
  16. data/lib/dnsruby/DNS.rb +305 -0
  17. data/lib/{Dnsruby/Cache.rb → dnsruby/cache.rb} +152 -152
  18. data/lib/{Dnsruby → dnsruby}/code_mapper.rb +48 -52
  19. data/lib/dnsruby/code_mappers.rb +295 -0
  20. data/lib/{Dnsruby/Config.rb → dnsruby/config.rb} +454 -454
  21. data/lib/{Dnsruby → dnsruby}/dnssec.rb +91 -91
  22. data/lib/{Dnsruby/Hosts.rb → dnsruby/hosts.rb} +125 -125
  23. data/lib/{Dnsruby → dnsruby}/ipv4.rb +26 -26
  24. data/lib/{Dnsruby → dnsruby}/ipv6.rb +42 -42
  25. data/lib/{Dnsruby → dnsruby}/key_cache.rb +29 -29
  26. data/lib/dnsruby/message/decoder.rb +164 -0
  27. data/lib/dnsruby/message/encoder.rb +75 -0
  28. data/lib/dnsruby/message/header.rb +249 -0
  29. data/lib/dnsruby/message/message.rb +629 -0
  30. data/lib/dnsruby/message/question.rb +86 -0
  31. data/lib/dnsruby/message/section.rb +96 -0
  32. data/lib/{Dnsruby → dnsruby}/name.rb +141 -141
  33. data/lib/dnsruby/packet_sender.rb +661 -0
  34. data/lib/{Dnsruby/Recursor.rb → dnsruby/recursor.rb} +235 -233
  35. data/lib/dnsruby/resolv.rb +113 -0
  36. data/lib/dnsruby/resolver.rb +1192 -0
  37. data/lib/dnsruby/resource/A.rb +56 -0
  38. data/lib/dnsruby/resource/AAAA.rb +54 -0
  39. data/lib/{Dnsruby → dnsruby}/resource/AFSDB.rb +68 -68
  40. data/lib/{Dnsruby → dnsruby}/resource/CERT.rb +105 -105
  41. data/lib/{Dnsruby → dnsruby}/resource/DHCID.rb +54 -54
  42. data/lib/dnsruby/resource/DLV.rb +27 -0
  43. data/lib/{Dnsruby → dnsruby}/resource/DNSKEY.rb +372 -372
  44. data/lib/{Dnsruby → dnsruby}/resource/DS.rb +255 -255
  45. data/lib/{Dnsruby → dnsruby}/resource/HINFO.rb +71 -71
  46. data/lib/{Dnsruby → dnsruby}/resource/HIP.rb +29 -29
  47. data/lib/{Dnsruby → dnsruby}/resource/IN.rb +30 -30
  48. data/lib/{Dnsruby → dnsruby}/resource/IPSECKEY.rb +31 -31
  49. data/lib/{Dnsruby → dnsruby}/resource/ISDN.rb +62 -62
  50. data/lib/{Dnsruby → dnsruby}/resource/KX.rb +65 -65
  51. data/lib/{Dnsruby → dnsruby}/resource/LOC.rb +263 -263
  52. data/lib/{Dnsruby → dnsruby}/resource/MINFO.rb +69 -69
  53. data/lib/{Dnsruby → dnsruby}/resource/MX.rb +65 -65
  54. data/lib/{Dnsruby → dnsruby}/resource/NAPTR.rb +98 -98
  55. data/lib/{Dnsruby → dnsruby}/resource/NSAP.rb +171 -171
  56. data/lib/dnsruby/resource/NSEC.rb +275 -0
  57. data/lib/dnsruby/resource/NSEC3.rb +332 -0
  58. data/lib/dnsruby/resource/NSEC3PARAM.rb +135 -0
  59. data/lib/dnsruby/resource/OPT.rb +272 -0
  60. data/lib/{Dnsruby → dnsruby}/resource/PX.rb +70 -70
  61. data/lib/{Dnsruby → dnsruby}/resource/RP.rb +75 -75
  62. data/lib/dnsruby/resource/RR.rb +421 -0
  63. data/lib/dnsruby/resource/RRSIG.rb +275 -0
  64. data/lib/dnsruby/resource/RRSet.rb +190 -0
  65. data/lib/{Dnsruby → dnsruby}/resource/RT.rb +67 -67
  66. data/lib/{Dnsruby → dnsruby}/resource/SOA.rb +94 -94
  67. data/lib/dnsruby/resource/SPF.rb +29 -0
  68. data/lib/dnsruby/resource/SRV.rb +112 -0
  69. data/lib/{Dnsruby → dnsruby}/resource/SSHFP.rb +14 -14
  70. data/lib/dnsruby/resource/TKEY.rb +163 -0
  71. data/lib/dnsruby/resource/TSIG.rb +593 -0
  72. data/lib/{Dnsruby → dnsruby}/resource/TXT.rb +191 -191
  73. data/lib/dnsruby/resource/X25.rb +55 -0
  74. data/lib/{Dnsruby → dnsruby}/resource/domain_name.rb +25 -25
  75. data/lib/{Dnsruby → dnsruby}/resource/generic.rb +80 -80
  76. data/lib/dnsruby/resource/resource.rb +25 -0
  77. data/lib/{Dnsruby → dnsruby}/select_thread.rb +148 -148
  78. data/lib/{Dnsruby/SingleResolver.rb → dnsruby/single_resolver.rb} +60 -60
  79. data/lib/{Dnsruby → dnsruby}/single_verifier.rb +344 -344
  80. data/lib/dnsruby/the_log.rb +44 -0
  81. data/lib/dnsruby/update.rb +278 -0
  82. data/lib/dnsruby/validator_thread.rb +124 -0
  83. data/lib/dnsruby/version.rb +3 -0
  84. data/lib/{Dnsruby → dnsruby}/zone_reader.rb +93 -93
  85. data/lib/{Dnsruby → dnsruby}/zone_transfer.rb +377 -377
  86. data/test/spec_helper.rb +16 -0
  87. data/test/tc_axfr.rb +31 -34
  88. data/test/tc_cache.rb +32 -32
  89. data/test/tc_dlv.rb +28 -28
  90. data/test/tc_dns.rb +73 -76
  91. data/test/tc_dnskey.rb +31 -32
  92. data/test/tc_dnsruby.rb +50 -44
  93. data/test/tc_ds.rb +36 -36
  94. data/test/tc_escapedchars.rb +252 -255
  95. data/test/tc_hash.rb +17 -21
  96. data/test/tc_header.rb +48 -57
  97. data/test/tc_hip.rb +19 -22
  98. data/test/tc_ipseckey.rb +18 -21
  99. data/test/tc_keith.rb +300 -0
  100. data/test/tc_message.rb +87 -0
  101. data/test/tc_misc.rb +83 -87
  102. data/test/tc_name.rb +81 -84
  103. data/test/tc_naptr.rb +18 -21
  104. data/test/tc_nsec.rb +55 -55
  105. data/test/tc_nsec3.rb +23 -24
  106. data/test/tc_nsec3param.rb +20 -21
  107. data/test/tc_packet.rb +90 -93
  108. data/test/tc_packet_unique_push.rb +48 -51
  109. data/test/tc_question.rb +30 -33
  110. data/test/tc_queue.rb +16 -17
  111. data/test/tc_recur.rb +16 -17
  112. data/test/tc_res_config.rb +38 -41
  113. data/test/tc_res_env.rb +29 -32
  114. data/test/tc_res_file.rb +26 -29
  115. data/test/tc_res_opt.rb +62 -65
  116. data/test/tc_resolver.rb +287 -242
  117. data/test/tc_rr-opt.rb +70 -63
  118. data/test/tc_rr-txt.rb +68 -71
  119. data/test/tc_rr-unknown.rb +45 -48
  120. data/test/tc_rr.rb +76 -70
  121. data/test/tc_rrset.rb +21 -22
  122. data/test/tc_rrsig.rb +19 -20
  123. data/test/tc_single_resolver.rb +294 -297
  124. data/test/tc_soak.rb +199 -202
  125. data/test/tc_soak_base.rb +29 -34
  126. data/test/tc_sshfp.rb +20 -23
  127. data/test/tc_tcp.rb +32 -35
  128. data/test/tc_tkey.rb +41 -44
  129. data/test/tc_tsig.rb +81 -84
  130. data/test/tc_update.rb +108 -111
  131. data/test/tc_validator.rb +29 -29
  132. data/test/tc_verifier.rb +81 -82
  133. data/test/ts_dnsruby.rb +16 -15
  134. data/test/ts_offline.rb +62 -63
  135. data/test/ts_online.rb +115 -115
  136. metadata +155 -90
  137. data/README +0 -59
  138. data/lib/Dnsruby/DNS.rb +0 -305
  139. data/lib/Dnsruby/PacketSender.rb +0 -656
  140. data/lib/Dnsruby/Resolver.rb +0 -1189
  141. data/lib/Dnsruby/TheLog.rb +0 -44
  142. data/lib/Dnsruby/message.rb +0 -1230
  143. data/lib/Dnsruby/resource/A.rb +0 -56
  144. data/lib/Dnsruby/resource/AAAA.rb +0 -54
  145. data/lib/Dnsruby/resource/DLV.rb +0 -27
  146. data/lib/Dnsruby/resource/NSEC.rb +0 -298
  147. data/lib/Dnsruby/resource/NSEC3.rb +0 -340
  148. data/lib/Dnsruby/resource/NSEC3PARAM.rb +0 -135
  149. data/lib/Dnsruby/resource/OPT.rb +0 -213
  150. data/lib/Dnsruby/resource/RRSIG.rb +0 -275
  151. data/lib/Dnsruby/resource/SPF.rb +0 -29
  152. data/lib/Dnsruby/resource/SRV.rb +0 -112
  153. data/lib/Dnsruby/resource/TKEY.rb +0 -163
  154. data/lib/Dnsruby/resource/TSIG.rb +0 -593
  155. data/lib/Dnsruby/resource/X25.rb +0 -55
  156. data/lib/Dnsruby/resource/resource.rb +0 -678
  157. data/lib/Dnsruby/update.rb +0 -278
  158. data/lib/Dnsruby/validator_thread.rb +0 -124
@@ -0,0 +1,29 @@
1
+ # --
2
+ # Copyright 2007 Nominet UK
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+ module Dnsruby
17
+ class RR
18
+ # DNS SPF resource record
19
+
20
+ # This is a clone of the TXT record. This class therfore completely inherits
21
+ # all properties of the Dnsruby::Resource::TXT class.
22
+ #
23
+ # Please see the Dnsruby::Resource::TXT documentation for details
24
+ # RFC 1035 Section 3.3.14, draft-schlitt-ospf-classic-02.txt
25
+ class SPF < TXT
26
+ TypeValue = Types::SPF #:nodoc: all
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,112 @@
1
+ # --
2
+ # Copyright 2007 Nominet UK
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+ module Dnsruby
17
+ class RR
18
+ module IN
19
+ # SRV resource record defined in RFC 2782
20
+ #
21
+ # These records identify the hostname and port that a service is
22
+ # available at.
23
+ #
24
+ # The format is:
25
+ # _Service._Proto.Name TTL Class SRV Priority Weight Port Target
26
+ #
27
+ # The fields specific to SRV are defined in RFC 2782
28
+ class SRV < RR
29
+ ClassHash[[TypeValue = Types::SRV, ClassValue = ClassValue]] = self #:nodoc: all
30
+
31
+ # The priority of this target host.
32
+ # A client MUST attempt
33
+ # to contact the target host with the lowest-numbered priority it can
34
+ # reach; target hosts with the same priority SHOULD be tried in an
35
+ # order defined by the weight field. The range is 0-65535. Note that
36
+ # it is not widely implemented and should be set to zero.
37
+ attr_accessor :priority
38
+
39
+ # A server selection mechanism.
40
+ # The weight field specifies
41
+ # a relative weight for entries with the same priority. Larger weights
42
+ # SHOULD be given a proportionately higher probability of being
43
+ # selected. The range of this number is 0-65535. Domain administrators
44
+ # SHOULD use Weight 0 when there isn't any server selection to do, to
45
+ # make the RR easier to read for humans (less noisy). Note that it is
46
+ # not widely implemented and should be set to zero.
47
+ attr_accessor :weight
48
+
49
+ # The port on this target host of this service. The range is 0-65535.
50
+ attr_accessor :port
51
+
52
+ # The domain name of the target host. A target of "." means
53
+ # that the service is decidedly not available at this domain.
54
+ attr_accessor :target
55
+
56
+ def from_data(data) #:nodoc: all
57
+ @priority, @weight, @port, @target = data
58
+ end
59
+
60
+ def from_hash(hash)
61
+ if hash[:priority]
62
+ @priority = hash[:priority].to_i
63
+ end
64
+ if hash[:weight]
65
+ @weight = hash[:weight].to_i
66
+ end
67
+ if hash[:port]
68
+ @port = hash[:port].to_i
69
+ end
70
+ if hash[:target]
71
+ @target= Name.create(hash[:target])
72
+ end
73
+ end
74
+
75
+ def from_string(input)
76
+ if (input.length > 0)
77
+ names = input.split(" ")
78
+ @priority = names[0].to_i
79
+ @weight = names[1].to_i
80
+ @port = names[2].to_i
81
+ if (names[3])
82
+ @target = Name.create(names[3])
83
+ end
84
+ end
85
+ end
86
+
87
+ def rdata_to_string
88
+ if (@target!=nil)
89
+ return "#{@priority} #{@weight} #{@port} #{@target.to_s(true)}"
90
+ else
91
+ return ""
92
+ end
93
+ end
94
+
95
+ def encode_rdata(msg, canonical=false) #:nodoc: all
96
+ msg.put_pack("n", @priority)
97
+ msg.put_pack("n", @weight)
98
+ msg.put_pack("n", @port)
99
+ msg.put_name(@target,canonical)
100
+ end
101
+
102
+ def self.decode_rdata(msg) #:nodoc: all
103
+ priority, = msg.get_unpack("n")
104
+ weight, = msg.get_unpack("n")
105
+ port, = msg.get_unpack("n")
106
+ target = msg.get_name
107
+ return self.new([priority, weight, port, target])
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -1,18 +1,18 @@
1
- #--
2
- #Copyright 2007 Nominet UK
3
- #
4
- #Licensed under the Apache License, Version 2.0 (the "License");
5
- #you may not use this file except in compliance with the License.
6
- #You may obtain a copy of the License at
7
- #
1
+ # --
2
+ # Copyright 2007 Nominet UK
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
8
  # http://www.apache.org/licenses/LICENSE-2.0
9
- #
10
- #Unless required by applicable law or agreed to in writing, software
11
- #distributed under the License is distributed on an "AS IS" BASIS,
12
- #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- #See the License for the specific language governing permissions and
14
- #limitations under the License.
15
- #++
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
16
  module Dnsruby
17
17
  class RR
18
18
  class SSHFP < RR
@@ -0,0 +1,163 @@
1
+ # --
2
+ # Copyright 2007 Nominet UK
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+ module Dnsruby
17
+
18
+ class Modes < CodeMapper
19
+ # The key is assigned by the server (unimplemented)
20
+ SERVERASSIGNED = 1
21
+
22
+ # The key is computed using a Diffie-Hellman key exchange
23
+ DIFFIEHELLMAN = 2
24
+
25
+ # The key is computed using GSS_API (unimplemented)
26
+ GSSAPI = 3
27
+
28
+ # The key is assigned by the resolver (unimplemented)
29
+ RESOLVERASSIGNED = 4
30
+
31
+ # The key should be deleted
32
+ DELETE = 5
33
+ update()
34
+ end
35
+
36
+ class RR
37
+ # RFC2930
38
+ class TKEY < RR
39
+ TypeValue = Types::TKEY #:nodoc: all
40
+ ClassValue = nil #:nodoc: all
41
+ ClassHash[[TypeValue, Classes::ANY]] = self #:nodoc: all
42
+
43
+ attr_reader :key_size
44
+ attr_accessor :key
45
+ # Gets or sets the domain name that specifies the name of the algorithm.
46
+ # The default algorithm is gss.microsoft.com
47
+ #
48
+ # rr.algorithm=(algorithm_name)
49
+ # print "algorithm = ", rr.algorithm, "\n"
50
+ #
51
+ attr_accessor :algorithm
52
+ # Gets or sets the inception time as the number of seconds since 1 Jan 1970
53
+ # 00:00:00 UTC.
54
+ #
55
+ # The default inception time is the current time.
56
+ #
57
+ # rr.inception=(time)
58
+ # print "inception = ", rr.inception, "\n"
59
+ #
60
+ attr_accessor :inception
61
+ # Gets or sets the expiration time as the number of seconds since 1 Jan 1970
62
+ # 00:00:00 UTC.
63
+ #
64
+ # The default expiration time is the current time plus 1 day.
65
+ #
66
+ # rr.expiration=(time)
67
+ # print "expiration = ", rr.expiration, "\n"
68
+ #
69
+ attr_accessor :expiration
70
+ # Sets the key mode (see rfc2930). The default is 3 which corresponds to GSSAPI
71
+ #
72
+ # rr.mode=(3)
73
+ # print "mode = ", rr.mode, "\n"
74
+ #
75
+ attr_accessor :mode
76
+ # Returns the RCODE covering TKEY processing. See RFC 2930 for details.
77
+ #
78
+ # print "error = ", rr.error, "\n"
79
+ #
80
+ attr_accessor :error
81
+ # Returns the length of the Other Data. Should be zero.
82
+ #
83
+ # print "other size = ", rr.other_size, "\n"
84
+ #
85
+ attr_reader :other_size
86
+ # Returns the Other Data. This field should be empty.
87
+ #
88
+ # print "other data = ", rr.other_data, "\n"
89
+ #
90
+ attr_reader :other_data
91
+
92
+ def other_data=(od)
93
+ @other_data=od
94
+ @other_size=@other_data.length
95
+ end
96
+
97
+ def initialize
98
+ @algorithm = "gss.microsoft.com"
99
+ @inception = Time.now
100
+ @expiration = Time.now + 24*60*60
101
+ @mode = Modes.GSSAPI
102
+ @error = 0
103
+ @other_size = 0
104
+ @other_data = ""
105
+
106
+ # RFC 2845 Section 2.3
107
+ @klass = Classes.ANY
108
+ # RFC 2845 Section 2.3
109
+ @ttl = 0
110
+ end
111
+
112
+ def from_hash(hash)
113
+ super(hash)
114
+ if (algorithm)
115
+ @algorithm = Name.create(hash[:algorithm])
116
+ end
117
+ end
118
+
119
+ def from_data(data) #:nodoc: all
120
+ @algorithm, @inception, @expiration, @mode, @error, @key_size, @key, @other_size, @other_data = data
121
+ end
122
+
123
+ # Create the RR from a standard string
124
+ def from_string(string) #:nodoc: all
125
+ Dnsruby.log.error("Dnsruby::RR::TKEY#from_string called, but no text format defined for TKEY")
126
+ end
127
+
128
+ def rdata_to_string
129
+ rdatastr=""
130
+
131
+ if (@algorithm!=nil)
132
+ error = @error
133
+ error = "UNDEFINED" unless error!=nil
134
+ rdatastr = "#{@algorithm.to_s(true)} #{error}"
135
+ if (@other_size != nil && @other_size >0 && @other_data!=nil)
136
+ rdatastr += " #{@other_data}"
137
+ end
138
+ end
139
+
140
+ return rdatastr
141
+ end
142
+
143
+ def encode_rdata(msg, canonical=false) #:nodoc: all
144
+ msg.put_name(@algorithm, canonical)
145
+ msg.put_pack("NNnn", @inception, @expiration, @mode, @error)
146
+ msg.put_pack("n", @key.length)
147
+ msg.put_bytes(@key)
148
+ msg.put_pack("n", @other_data.length)
149
+ msg.put_bytes(@other_data)
150
+ end
151
+
152
+ def self.decode_rdata(msg) #:nodoc: all
153
+ alg=msg.get_name
154
+ inc, exp, mode, error = msg.get_unpack("NNnn")
155
+ key_size, =msg.get_unpack("n")
156
+ key=msg.get_bytes(key_size)
157
+ other_size, =msg.get_unpack("n")
158
+ other=msg.get_bytes(other_size)
159
+ return self.new([alg, inc, exp, mode, error, key_size, key, other_size, other])
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,593 @@
1
+ # --
2
+ # Copyright 2007 Nominet UK
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+ # require 'base64'
17
+ begin
18
+ require 'openssl'
19
+ rescue LoadError
20
+ print "OpenSSL not found - ignoring\n"
21
+ end
22
+ module Dnsruby
23
+ class RR
24
+ # TSIG implements RFC2845.
25
+ #
26
+ # "This protocol allows for transaction level authentication using
27
+ # shared secrets and one way hashing. It can be used to authenticate
28
+ # dynamic updates as coming from an approved client, or to authenticate
29
+ # responses as coming from an approved recursive name server."
30
+ #
31
+ # A Dnsruby::RR::TSIG can represent the data present in a TSIG RR.
32
+ # However, it can also represent the data (specified in RFC2845) used
33
+ # to sign or verify a DNS message.
34
+ #
35
+ #
36
+ # Example code :
37
+ # res = Dnsruby::Resolver.new("ns0.validation-test-servers.nominet.org.uk")
38
+ #
39
+ # # Now configure the resolver with the TSIG key for signing/verifying
40
+ # KEY_NAME="rubytsig"
41
+ # KEY = "8n6gugn4aJ7MazyNlMccGKH1WxD2B3UvN/O/RA6iBupO2/03u9CTa3Ewz3gBWTSBCH3crY4Kk+tigNdeJBAvrw=="
42
+ # res.tsig=KEY_NAME, KEY
43
+ #
44
+ # update = Dnsruby::Update.new("validation-test-servers.nominet.org.uk")
45
+ # # Generate update record name, and test it has been made. Then delete it and check it has been deleted
46
+ # update_name = generate_update_name
47
+ # update.absent(update_name)
48
+ # update.add(update_name, 'TXT', 100, "test signed update")
49
+ #
50
+ # # Resolver will automatically sign message and verify response
51
+ # response = res.send_message(update)
52
+ # assert(response.verified?) # Check that the response has been verified
53
+ class TSIG < RR
54
+ HMAC_MD5 = Name.create("HMAC-MD5.SIG-ALG.REG.INT.")
55
+ HMAC_SHA1 = Name.create("hmac-sha1.")
56
+ HMAC_SHA256 = Name.create("hmac-sha256.")
57
+
58
+ DEFAULT_FUDGE = 300
59
+
60
+ DEFAULT_ALGORITHM = HMAC_MD5
61
+
62
+ # Generates a TSIG record and adds it to the message.
63
+ # Takes an optional original_request argument for the case where this is
64
+ # a response to a query (RFC2845 3.4.1)
65
+ #
66
+ # Message#tsigstate will be set to :Signed.
67
+ def apply(message, original_request=nil)
68
+ if (!message.signed?)
69
+ tsig_rr = generate(message, original_request)
70
+ message.add_additional(tsig_rr)
71
+ message.tsigstate = :Signed
72
+ @query = message
73
+ tsig_rr.query = message
74
+ end
75
+ end
76
+
77
+ def query=q#:nodoc: all
78
+ @query = q
79
+ end
80
+
81
+
82
+ # Generates a TSIG record
83
+ def generate(msg, original_request = nil, data="", msg_bytes=nil, tsig_rr=self)#:nodoc: all
84
+ time_signed=@time_signed
85
+ if (!time_signed)
86
+ time_signed=Time.now.to_i
87
+ end
88
+ if (tsig_rr.time_signed)
89
+ time_signed = tsig_rr.time_signed
90
+ end
91
+
92
+ if (original_request)
93
+ # # Add the request MAC if present (used to validate responses).
94
+ # hmac.update(pack("H*", request_mac))
95
+ mac_bytes = MessageEncoder.new {|m|
96
+ m.put_pack('n', original_request.tsig.mac_size)
97
+ m.put_bytes(original_request.tsig.mac)
98
+ }.to_s
99
+ data += mac_bytes
100
+ # Original ID - should we set message ID to original ID?
101
+ if (tsig_rr != self)
102
+ msg.header.id = tsig_rr.original_id
103
+ else
104
+ msg.header.id = original_request.header.id
105
+ end
106
+ end
107
+
108
+ if (!msg_bytes)
109
+ msg_bytes = msg.encode
110
+ data += msg_bytes
111
+ else
112
+ # If msg_bytes came in, we need somehow to remove the TSIG RR
113
+ # It is the last record, so we can strip it if we know where it starts
114
+ # We must also poke the header ARcount to decrement it
115
+ msg_bytes = Header.decrement_arcount_encoded(msg_bytes)
116
+ data += msg_bytes[0, msg.tsigstart]
117
+ end
118
+
119
+ data += sig_data(tsig_rr, time_signed)
120
+
121
+ mac = calculate_mac(tsig_rr.algorithm, data)
122
+
123
+ mac_size = mac.length
124
+
125
+ new_tsig_rr = Dnsruby::RR.create({
126
+ :name => tsig_rr.name,
127
+ :type => Types.TSIG,
128
+ :ttl => tsig_rr.ttl,
129
+ :klass => tsig_rr.klass,
130
+ :algorithm => tsig_rr.algorithm,
131
+ :fudge => tsig_rr.fudge,
132
+ :key => @key,
133
+ :mac => mac,
134
+ :mac_size => mac_size,
135
+ :error => tsig_rr.error,
136
+ :time_signed => time_signed,
137
+ :original_id => msg.header.id
138
+ })
139
+ return new_tsig_rr
140
+
141
+ end
142
+
143
+ def calculate_mac(algorithm, data)
144
+ mac=nil
145
+ # + if (key_size > max_digest_len) {
146
+ # + EVP_DigestInit(&ectx, digester);
147
+ # + EVP_DigestUpdate(&ectx, (const void*) key_bytes, key_size);
148
+ # + EVP_DigestFinal(&ectx, key_bytes, NULL);
149
+ # + key_size = max_digest_len;
150
+ # + }
151
+ key = @key.gsub(" ", "")
152
+ # key = Base64::decode64(key)
153
+ key = key.unpack("m*")[0]
154
+ if (algorithm.to_s.downcase == HMAC_MD5.to_s.downcase)
155
+ mac = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, data)
156
+ elsif (algorithm == HMAC_SHA1)
157
+ mac = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, key, data)
158
+ elsif (algorithm == HMAC_SHA256)
159
+ mac = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, key, data)
160
+ else
161
+ # Should we allow client to pass in their own signing function?
162
+ raise VerifyError.new("Algorithm #{algorithm} unsupported by TSIG")
163
+ end
164
+ return mac
165
+ end
166
+
167
+ # Private method to return the TSIG RR data to be signed
168
+ def sig_data(tsig_rr, time_signed=@time_signed) #:nodoc: all
169
+ return MessageEncoder.new { |msg|
170
+ msg.put_name(tsig_rr.name.downcase, true)
171
+ msg.put_pack('nN', tsig_rr.klass.code, tsig_rr.ttl)
172
+ msg.put_name(tsig_rr.algorithm.downcase, true)
173
+
174
+ time_high = (time_signed >> 32)
175
+ time_low = (time_signed & 0xFFFFFFFF)
176
+ msg.put_pack('nN', time_high, time_low)
177
+ msg.put_pack('n', tsig_rr.fudge)
178
+ msg.put_pack('n', tsig_rr.error)
179
+ msg.put_pack('n', tsig_rr.other_size)
180
+ msg.put_bytes(tsig_rr.other_data)
181
+ }.to_s
182
+ end
183
+
184
+ # Verify a response. This method will be called by Dnsruby::SingleResolver
185
+ # before passing a response to the client code.
186
+ # The TSIG record will be removed from packet before passing to client, and
187
+ # the Message#tsigstate and Message#tsigerror will be set accordingly.
188
+ # Message#tsigstate will be set to one of :
189
+ # * :Failed
190
+ # * :Verified
191
+ def verify(query, response, response_bytes, buf="")
192
+ # 4.6. Client processing of answer
193
+ #
194
+ # When a client receives a response from a server and expects to see a
195
+ # TSIG, it first checks if the TSIG RR is present in the response.
196
+ # Otherwise, the response is treated as having a format error and
197
+ # discarded. The client then extracts the TSIG, adjusts the ARCOUNT,
198
+ # and calculates the keyed digest in the same way as the server. If
199
+ # the TSIG does not validate, that response MUST be discarded, unless
200
+ # the RCODE is 9 (NOTAUTH), in which case the client SHOULD attempt to
201
+ # verify the response as if it were a TSIG Error response, as specified
202
+ # in [4.3]. A message containing an unsigned TSIG record or a TSIG
203
+ # record which fails verification SHOULD not be considered an
204
+ # acceptable response; the client SHOULD log an error and continue to
205
+ # wait for a signed response until the request times out.
206
+
207
+ # So, this verify method should simply remove the TSIG RR and calculate
208
+ # the MAC (using original request MAC if required).
209
+ # Should set tsigstate on packet appropriately, and return error.
210
+ # Side effect is packet is stripped of TSIG.
211
+ # Resolver (or client) can then decide what to do...
212
+
213
+ msg_tsig_rr = response.tsig
214
+ if (!verify_common(response))
215
+ return false
216
+ end
217
+
218
+ new_msg_tsig_rr = generate(response, query, buf, response_bytes, msg_tsig_rr)
219
+
220
+ if (msg_tsig_rr.mac == new_msg_tsig_rr.mac)
221
+ response.tsigstate = :Verified
222
+ response.tsigerror = RCode.NOERROR
223
+ return true
224
+ else
225
+ response.tsigstate = :Failed
226
+ response.tsigerror = RCode.BADSIG
227
+ return false
228
+ end
229
+ end
230
+
231
+ def verify_common(response)#:nodoc: all
232
+ tsig_rr = response.tsig
233
+
234
+ if (!tsig_rr)
235
+ response.tsigerror = RCode.FORMERR
236
+ response.tsigstate = :Failed
237
+ return false
238
+ end
239
+
240
+ response.additional.delete(tsig_rr)
241
+ response.header.arcount-=1
242
+
243
+ # First, check the TSIG error in the RR
244
+ if (tsig_rr.error != RCode.NOERROR)
245
+ response.tsigstate = :Failed
246
+ response.tsigerror = tsig_rr.error
247
+ return false
248
+ end
249
+
250
+ if ((tsig_rr.name != @name) || (tsig_rr.algorithm.downcase != @algorithm.downcase))
251
+ Dnsruby.log.error("BADKEY failure")
252
+ response.tsigstate = :Failed
253
+ response.tsigerror = RCode.BADKEY
254
+ return false
255
+ end
256
+
257
+ # Check time_signed (RFC2845, 4.5.2) - only really necessary for server
258
+ if (Time.now.to_i > tsig_rr.time_signed + tsig_rr.fudge ||
259
+ Time.now.to_i < tsig_rr.time_signed - tsig_rr.fudge)
260
+ Dnsruby.log.error("TSIG failed with BADTIME")
261
+ response.tsigstate = :Failed
262
+ response.tsigerror = RCode.BADTIME
263
+ return false
264
+ end
265
+
266
+ return true
267
+ end
268
+
269
+ # Checks TSIG signatures across sessions of multiple DNS envelopes.
270
+ # This method is called each time a new envelope comes in. The envelope
271
+ # is checked - if a TSIG is present, them the stream so far is verified,
272
+ # and the response#tsigstate set to :Verified. If a TSIG is not present,
273
+ # and does not need to be present, then the message is added to the digest
274
+ # stream and the response#tsigstate is set to :Intermediate.
275
+ # If there is an error with the TSIG verification, then the response#tsigstate
276
+ # is set to :Failed.
277
+ # Like verify, this method will only be called by the Dnsruby::SingleResolver
278
+ # class. Client code need not call this method directly.
279
+ def verify_envelope(response, response_bytes)
280
+ # RFC2845 Section 4.4
281
+ # -----
282
+ # A DNS TCP session can include multiple DNS envelopes. This is, for
283
+ # example, commonly used by zone transfer. Using TSIG on such a
284
+ # connection can protect the connection from hijacking and provide data
285
+ # integrity. The TSIG MUST be included on the first and last DNS
286
+ # envelopes. It can be optionally placed on any intermediary
287
+ # envelopes. It is expensive to include it on every envelopes, but it
288
+ # MUST be placed on at least every 100'th envelope. The first envelope
289
+ # is processed as a standard answer, and subsequent messages have the
290
+ # following digest components:
291
+ #
292
+ # * Prior Digest (running)
293
+ # * DNS Messages (any unsigned messages since the last TSIG)
294
+ # * TSIG Timers (current message)
295
+ #
296
+ # This allows the client to rapidly detect when the session has been
297
+ # altered; at which point it can close the connection and retry. If a
298
+ # client TSIG verification fails, the client MUST close the connection.
299
+ # If the client does not receive TSIG records frequently enough (as
300
+ # specified above) it SHOULD assume the connection has been hijacked
301
+ # and it SHOULD close the connection. The client SHOULD treat this the
302
+ # same way as they would any other interrupted transfer (although the
303
+ # exact behavior is not specified).
304
+ # -----
305
+ #
306
+ # Each time a new envelope comes in, this method is called on the QUERY TSIG RR.
307
+ # It will set the response tsigstate to :Verified :Intermediate or :Failed
308
+ # as appropriate.
309
+
310
+ # Keep digest going of messages as they come in (and mark them intermediate)
311
+ # When TSIG comes in, work out what key should be and check. If OK, mark
312
+ # verified. Can reset digest then.
313
+ if (!@buf)
314
+ @num_envelopes = 0
315
+ @last_signed = 0
316
+ end
317
+ @num_envelopes += 1
318
+ if (!response.tsig)
319
+ if ((@num_envelopes > 1) && (@num_envelopes - @last_signed < 100))
320
+ Dnsruby.log.debug("Receiving intermediate envelope in TSIG TCP session")
321
+ response.tsigstate = :Intermediate
322
+ response.tsigerror = RCode.NOERROR
323
+ @buf = @buf + response_bytes
324
+ return
325
+ else
326
+ response.tsigstate = :Failed
327
+ Dnsruby.log.error("Expecting signed packet")
328
+ return false
329
+ end
330
+ end
331
+ @last_signed = @num_envelopes
332
+
333
+ # We have a TSIG - process it!
334
+ tsig = response.tsig
335
+ if (@num_envelopes == 1)
336
+ Dnsruby.log.debug("First response in TSIG TCP session - verifying normally")
337
+ # Process it as a standard answer
338
+ ok = verify(@query, response, response_bytes)
339
+ if (ok)
340
+ mac_bytes = MessageEncoder.new {|m|
341
+ m.put_pack('n', tsig.mac_size)
342
+ m.put_bytes(tsig.mac)
343
+ }.to_s
344
+ @buf = mac_bytes
345
+ end
346
+ return ok
347
+ end
348
+ Dnsruby.log.debug("Processing TSIG on TSIG TCP session")
349
+
350
+ if (!verify_common(response))
351
+ return false
352
+ end
353
+
354
+ # Now add the current message data - remember to frig the arcount
355
+ response_bytes = Header.decrement_arcount_encoded(response_bytes)
356
+ @buf += response_bytes[0, response.tsigstart]
357
+
358
+ # Let's add the timers
359
+ timers_data = MessageEncoder.new { |msg|
360
+ time_high = (tsig.time_signed >> 32)
361
+ time_low = (tsig.time_signed & 0xFFFFFFFF)
362
+ msg.put_pack('nN', time_high, time_low)
363
+ msg.put_pack('n', tsig.fudge)
364
+ }.to_s
365
+ @buf += timers_data
366
+
367
+ mac = calculate_mac(tsig.algorithm, @buf)
368
+
369
+ if (mac != tsig.mac)
370
+ Dnsruby.log.error("TSIG Verify error on TSIG TCP session")
371
+ response.tsigstate = :Failed
372
+ return false
373
+ end
374
+ mac_bytes = MessageEncoder.new {|m|
375
+ m.put_pack('n', mac.length)
376
+ m.put_bytes(mac)
377
+ }.to_s
378
+ @buf=mac_bytes
379
+
380
+ response.tsigstate = :Verified
381
+ response.tsigerror = RCode.NOERROR
382
+ return true
383
+ end
384
+
385
+
386
+ TypeValue = Types::TSIG #:nodoc: all
387
+ ClassValue = nil #:nodoc: all
388
+ ClassHash[[TypeValue, Classes::ANY]] = self #:nodoc: all
389
+
390
+ # Gets or sets the domain name that specifies the name of the algorithm.
391
+ # The only algorithms currently supported are hmac-md5 and hmac-sha1.
392
+ #
393
+ # rr.algorithm=(algorithm_name)
394
+ # print "algorithm = ", rr.algorithm, "\n"
395
+ #
396
+ attr_reader :algorithm
397
+
398
+ # Gets or sets the signing time as the number of seconds since 1 Jan 1970
399
+ # 00:00:00 UTC.
400
+ #
401
+ # The default signing time is the current time.
402
+ #
403
+ # rr.time_signed=(time)
404
+ # print "time signed = ", rr.time_signed, "\n"
405
+ #
406
+ attr_accessor :time_signed
407
+
408
+ # Gets or sets the "fudge", i.e., the seconds of error permitted in the
409
+ # signing time.
410
+ #
411
+ # The default fudge is 300 seconds.
412
+ #
413
+ # rr.fudge=(60)
414
+ # print "fudge = ", rr.fudge, "\n"
415
+ #
416
+ attr_reader :fudge
417
+
418
+ # Returns the number of octets in the message authentication code (MAC).
419
+ # The programmer must call a Net::DNS::Packet object's data method
420
+ # before this will return anything meaningful.
421
+ #
422
+ # print "MAC size = ", rr.mac_size, "\n"
423
+ #
424
+ attr_accessor :mac_size
425
+
426
+ # Returns the message authentication code (MAC) as a string of hex
427
+ # characters. The programmer must call a Net::DNS::Packet object's
428
+ # data method before this will return anything meaningful.
429
+ #
430
+ # print "MAC = ", rr.mac, "\n"
431
+ #
432
+ attr_accessor :mac
433
+
434
+ # Gets or sets the original message ID.
435
+ #
436
+ # rr.original_id(12345)
437
+ # print "original ID = ", rr.original_id, "\n"
438
+ #
439
+ attr_accessor :original_id
440
+
441
+ # Returns the RCODE covering TSIG processing. Common values are
442
+ # NOERROR, BADSIG, BADKEY, and BADTIME. See RFC 2845 for details.
443
+ #
444
+ # print "error = ", rr.error, "\n"
445
+ #
446
+ attr_accessor :error
447
+
448
+ # Returns the length of the Other Data. Should be zero unless the
449
+ # error is BADTIME.
450
+ #
451
+ # print "other len = ", rr.other_size, "\n"
452
+ #
453
+ attr_accessor :other_size
454
+
455
+ # Returns the Other Data. This field should be empty unless the
456
+ # error is BADTIME, in which case it will contain the server's
457
+ # time as the number of seconds since 1 Jan 1970 00:00:00 UTC.
458
+ #
459
+ # print "other data = ", rr.other_data, "\n"
460
+ #
461
+ attr_accessor :other_data
462
+
463
+ # Stores the secret key used for signing/verifying messages.
464
+ attr_accessor :key
465
+
466
+ def init_defaults
467
+ # @TODO@ Have new() method which takes key_name and key?
468
+ @algorithm = DEFAULT_ALGORITHM
469
+ @fudge = DEFAULT_FUDGE
470
+ @mac_size = 0
471
+ @mac = ""
472
+ @original_id = rand(65536)
473
+ @error = 0
474
+ @other_size = 0
475
+ @other_data = ""
476
+ @time_signed = nil
477
+ @buf = nil
478
+
479
+ # RFC 2845 Section 2.3
480
+ @klass = Classes.ANY
481
+
482
+ @ttl = 0 # RFC 2845 Section 2.3
483
+ end
484
+
485
+ def from_data(data) #:nodoc: all
486
+ @algorithm, @time_signed, @fudge, @mac_size, @mac, @original_id, @error, @other_size, @other_data = data
487
+ end
488
+
489
+ def name=(n)
490
+ if (n.instance_of?String)
491
+ n = Name.create(n)
492
+ end
493
+ if (!n.absolute?)
494
+ @name = Name.create(n.to_s + ".")
495
+ else
496
+ @name = n
497
+ end
498
+ end
499
+
500
+ # Create the RR from a standard string
501
+ def from_string(str) #:nodoc: all
502
+ parts = str.split("[:/]")
503
+ if (parts.length < 2 || parts.length > 3)
504
+ raise ArgumentException.new("Invalid TSIG key specification")
505
+ end
506
+ if (parts.length == 3)
507
+ return TSIG.new(parts[0], parts[1], parts[2]);
508
+ else
509
+ return TSIG.new(HMAC_MD5, parts[0], parts[1]);
510
+ end
511
+ end
512
+
513
+ # Set the algorithm to use to generate the HMAC
514
+ # Supported values are :
515
+ # * hmac-md5
516
+ # * hmac-sha1
517
+ # * hmac-sha256
518
+ def algorithm=(alg)
519
+ if (alg.class == String)
520
+ if (alg.downcase=="hmac-md5")
521
+ @algorithm = HMAC_MD5;
522
+ elsif (alg.downcase=="hmac-sha1")
523
+ @algorithm = HMAC_SHA1;
524
+ elsif (alg.downcase=="hmac-sha256")
525
+ @algorithm = HMAC_SHA256;
526
+ else
527
+ raise ArgumentError.new("Invalid TSIG algorithm")
528
+ end
529
+ elsif (alg.class == Name)
530
+ if (alg!=HMAC_MD5 && alg!=HMAC_SHA1 && alg!=HMAC_SHA256)
531
+ raise ArgumentException.new("Invalid TSIG algorithm")
532
+ end
533
+ @algorithm=alg
534
+ else
535
+ raise ArgumentError.new("#{alg.class} not valid type for Dnsruby::RR::TSIG#algorithm= - use String or Name")
536
+ end
537
+ Dnsruby.log.debug{"Using #{@algorithm.to_s} algorithm"}
538
+ end
539
+
540
+ def fudge=(f)
541
+ if (f < 0 || f > 0x7FFF)
542
+ @fudge = DEFAULT_FUDGE
543
+ else
544
+ @fudge = f
545
+ end
546
+ end
547
+
548
+ def rdata_to_string
549
+ rdatastr=""
550
+ if (@algorithm!=nil)
551
+ error = @error
552
+ error = "UNDEFINED" unless error!=nil
553
+ rdatastr = "#{@original_id} #{@time_signed} #{@algorithm.to_s(true)} #{error}";
554
+ if (@other_size > 0 && @other_data!=nil)
555
+ rdatastr += " #{@other_data}"
556
+ end
557
+ rdatastr += " " + mac.unpack("H*").to_s
558
+ end
559
+
560
+ return rdatastr
561
+ end
562
+
563
+ def encode_rdata(msg, canonical=false) #:nodoc: all
564
+ # Name needs to be added with no compression - done in Dnsruby::Message#encode
565
+ msg.put_name(@algorithm.downcase, true)
566
+ time_high = (@time_signed >> 32)
567
+ time_low = (@time_signed & 0xFFFFFFFF)
568
+ msg.put_pack('nN', time_high, time_low)
569
+ msg.put_pack('n', @fudge)
570
+ msg.put_pack('n', @mac_size)
571
+ msg.put_bytes(@mac)
572
+ msg.put_pack('n', @original_id)
573
+ msg.put_pack('n', @error)
574
+ msg.put_pack('n', @other_size)
575
+ msg.put_bytes(@other_data)
576
+ end
577
+
578
+ def self.decode_rdata(msg) #:nodoc: all
579
+ alg=msg.get_name
580
+ time_high, time_low = msg.get_unpack("nN")
581
+ time_signed = (time_high << 32) + time_low
582
+ fudge, = msg.get_unpack("n")
583
+ mac_size, = msg.get_unpack("n")
584
+ mac = msg.get_bytes(mac_size)
585
+ original_id, = msg.get_unpack("n")
586
+ error, = msg.get_unpack("n")
587
+ other_size, = msg.get_unpack("n")
588
+ other_data = msg.get_bytes(other_size)
589
+ return self.new([alg, time_signed, fudge, mac_size, mac, original_id, error, other_size, other_data])
590
+ end
591
+ end
592
+ end
593
+ end