redns 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +16 -0
- data/README.rdoc +10 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/lib/redns/address.rb +44 -0
- data/lib/redns/buffer.rb +138 -0
- data/lib/redns/fragment.rb +105 -0
- data/lib/redns/message.rb +144 -0
- data/lib/redns/name.rb +79 -0
- data/lib/redns/question.rb +38 -0
- data/lib/redns/record/mx.rb +28 -0
- data/lib/redns/record/null.rb +30 -0
- data/lib/redns/record/soa.rb +65 -0
- data/lib/redns/record.rb +7 -0
- data/lib/redns/resource.rb +86 -0
- data/lib/redns/support.rb +19 -0
- data/lib/redns.rb +84 -0
- data/redns.gemspec +77 -0
- data/test/helper.rb +13 -0
- data/test/test_redns.rb +4 -0
- data/test/test_redns_address.rb +23 -0
- data/test/test_redns_buffer.rb +180 -0
- data/test/test_redns_fragment.rb +55 -0
- data/test/test_redns_message.rb +35 -0
- data/test/test_redns_name.rb +76 -0
- data/test/test_redns_question.rb +24 -0
- data/test/test_redns_resource.rb +184 -0
- data/test/test_redns_support.rb +16 -0
- metadata +101 -0
data/.document
ADDED
data/.gitignore
ADDED
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "redns"
|
8
|
+
gem.summary = %Q{Ruby Reactor-Ready DNS Library}
|
9
|
+
gem.description = %Q{ReDNS is a pure Ruby DNS library with drivers for reactor-model engines such as EventMachine}
|
10
|
+
gem.email = "github@tadman.ca"
|
11
|
+
gem.homepage = "http://github.com/tadman/redns"
|
12
|
+
gem.authors = %w[ tadman ]
|
13
|
+
end
|
14
|
+
Jeweler::GemcutterTasks.new
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
21
|
+
test.libs << 'lib' << 'test'
|
22
|
+
test.pattern = 'test/**/test_*.rb'
|
23
|
+
test.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rcov/rcovtask'
|
28
|
+
Rcov::RcovTask.new do |test|
|
29
|
+
test.libs << 'test'
|
30
|
+
test.pattern = 'test/**/test_*.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
task :rcov do
|
35
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
task :test => :check_dependencies
|
40
|
+
|
41
|
+
task :default => :test
|
42
|
+
|
43
|
+
require 'rake/rdoctask'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
46
|
+
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = "redns #{version}"
|
49
|
+
rdoc.rdoc_files.include('README*')
|
50
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class ReDNS::Address < ReDNS::Fragment
|
2
|
+
# == Attributes ===========================================================
|
3
|
+
|
4
|
+
attribute :address, :default => '0.0.0.0'
|
5
|
+
|
6
|
+
# == Class Methods ========================================================
|
7
|
+
|
8
|
+
# == Instance Methods =====================================================
|
9
|
+
|
10
|
+
def initialize(contents = nil)
|
11
|
+
case (contents)
|
12
|
+
when ReDNS::Buffer
|
13
|
+
# Ensure that this String subclass is handled using the default
|
14
|
+
# method, not intercepted and treated as an actual String
|
15
|
+
super(contents)
|
16
|
+
when String
|
17
|
+
super(:address => contents)
|
18
|
+
else
|
19
|
+
super(contents)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def empty?
|
24
|
+
self.address == '0.0.0.0'
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
self.address
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_a
|
32
|
+
[ to_s ]
|
33
|
+
end
|
34
|
+
|
35
|
+
def serialize(buffer = ReDNS::Buffer.new)
|
36
|
+
buffer.append(inet_aton(self.address))
|
37
|
+
buffer
|
38
|
+
end
|
39
|
+
|
40
|
+
def deserialize(buffer)
|
41
|
+
self.address = inet_ntoa(buffer)
|
42
|
+
self
|
43
|
+
end
|
44
|
+
end
|
data/lib/redns/buffer.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
class ReDNS::Buffer < String
|
2
|
+
# == Constants ============================================================
|
3
|
+
|
4
|
+
# == Properties ===========================================================
|
5
|
+
|
6
|
+
alias_method :total_length, :length
|
7
|
+
alias_method :total_size, :size
|
8
|
+
|
9
|
+
attr_reader :offset
|
10
|
+
attr_reader :size
|
11
|
+
alias_method :length, :size
|
12
|
+
|
13
|
+
# == Class Methods ========================================================
|
14
|
+
|
15
|
+
# == Instance Methods =====================================================
|
16
|
+
|
17
|
+
# Create a buffer with arbitrary String contents. The offset parameter
|
18
|
+
# indicates where to start reading, which defaults to character 0 at the
|
19
|
+
# start of the string. The size parameter is used to limit how much of
|
20
|
+
# the content is used
|
21
|
+
|
22
|
+
def initialize(contents = nil, offset = nil, size = nil)
|
23
|
+
if (contents.respond_to?(:serialize))
|
24
|
+
super('')
|
25
|
+
|
26
|
+
@offset = 0
|
27
|
+
@size = total_length
|
28
|
+
|
29
|
+
contents.serialize(self)
|
30
|
+
else
|
31
|
+
super(contents || '')
|
32
|
+
|
33
|
+
@offset = offset ? offset.to_i : 0
|
34
|
+
@size = size ? size.to_i : nil
|
35
|
+
end
|
36
|
+
|
37
|
+
advance(0)
|
38
|
+
end
|
39
|
+
|
40
|
+
def unpack(format)
|
41
|
+
return [ ] if (@size <= 0)
|
42
|
+
|
43
|
+
raise ReDNS::Exception::BufferUnderrun if (@offset > total_length)
|
44
|
+
|
45
|
+
data = to_s.unpack(format)
|
46
|
+
advance(data.pack(format).length)
|
47
|
+
data
|
48
|
+
end
|
49
|
+
|
50
|
+
def pack(contents, format)
|
51
|
+
append(contents.pack(format))
|
52
|
+
end
|
53
|
+
|
54
|
+
def read(chars = 1)
|
55
|
+
raise ReDNS::Exception::BufferUnderrun if (@offset + chars > total_length)
|
56
|
+
|
57
|
+
result = to_str[@offset, chars]
|
58
|
+
advance(chars)
|
59
|
+
result
|
60
|
+
end
|
61
|
+
|
62
|
+
def write(contents, length = nil)
|
63
|
+
insertion = length ? contents[0, length] : contents
|
64
|
+
|
65
|
+
self[@offset, 0] = insertion
|
66
|
+
|
67
|
+
advance(insertion.length)
|
68
|
+
end
|
69
|
+
|
70
|
+
def append(contents, length = nil)
|
71
|
+
insertion = length ? contents[0, length] : contents
|
72
|
+
|
73
|
+
self << insertion
|
74
|
+
|
75
|
+
@size += insertion.length
|
76
|
+
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
def advance(chars = 1)
|
81
|
+
if (chars < 0)
|
82
|
+
rewind(-chars)
|
83
|
+
else
|
84
|
+
@offset += chars
|
85
|
+
|
86
|
+
if (@offset > total_length)
|
87
|
+
@offset = total_length
|
88
|
+
end
|
89
|
+
|
90
|
+
max_length = (total_length - @offset)
|
91
|
+
|
92
|
+
if (!@size or @size > max_length)
|
93
|
+
@size = max_length
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
@size ||= total_length - @offset
|
98
|
+
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
def rewind(chars = nil)
|
103
|
+
if (chars)
|
104
|
+
if (chars < 0)
|
105
|
+
advance(-chars)
|
106
|
+
else
|
107
|
+
@offset -= chars
|
108
|
+
@size += chars
|
109
|
+
|
110
|
+
if (@offset < 0)
|
111
|
+
@size += @offset
|
112
|
+
@offset = 0
|
113
|
+
end
|
114
|
+
end
|
115
|
+
else
|
116
|
+
@size += @offset
|
117
|
+
@offset = 0
|
118
|
+
end
|
119
|
+
|
120
|
+
self
|
121
|
+
end
|
122
|
+
|
123
|
+
def restore_state
|
124
|
+
offset = @offset
|
125
|
+
size = @size
|
126
|
+
yield(self) if (block_given?)
|
127
|
+
@offset = offset
|
128
|
+
@size = size
|
129
|
+
end
|
130
|
+
|
131
|
+
def to_s
|
132
|
+
to_str[@offset, @size]
|
133
|
+
end
|
134
|
+
|
135
|
+
def inspect
|
136
|
+
"\#<#{self.class} #{to_s.inspect}>"
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
class ReDNS::Fragment
|
2
|
+
# This represents a piece of data serialized in a particular DNS format,
|
3
|
+
# which includes both queries, responses, and the individual records that
|
4
|
+
# they contain.
|
5
|
+
|
6
|
+
# == Properties ===========================================================
|
7
|
+
|
8
|
+
attr_reader :attributes
|
9
|
+
|
10
|
+
# == Extensions ===========================================================
|
11
|
+
|
12
|
+
include ReDNS::Support
|
13
|
+
|
14
|
+
# == Class Methods ========================================================
|
15
|
+
|
16
|
+
# Declares an attribute of this fragment.
|
17
|
+
# Options:
|
18
|
+
# * :default = The default value to use if the attribute is not assigned
|
19
|
+
# * :convert = The conversion method to call on incoming values, which can
|
20
|
+
# be a Symbol, a Class, or a Proc.
|
21
|
+
# * :boolean = Converted to a boolean value, also introduces name? method
|
22
|
+
# * :primary = Indicates this is the primary attribute to be assigned when
|
23
|
+
# onstructed with only a String.
|
24
|
+
|
25
|
+
def self.attribute(name, options = nil)
|
26
|
+
name = name.to_sym
|
27
|
+
|
28
|
+
attribute_config = options || { }
|
29
|
+
|
30
|
+
default = attribute_config[:default]
|
31
|
+
|
32
|
+
if (attribute_config[:boolean])
|
33
|
+
default ||= false
|
34
|
+
|
35
|
+
attribute_config[:convert] ||= lambda { |v| !!v }
|
36
|
+
end
|
37
|
+
|
38
|
+
convert = attribute_config[:convert] || attribute_config[:class]
|
39
|
+
|
40
|
+
define_method(name) do
|
41
|
+
case (@attributes[name])
|
42
|
+
when nil
|
43
|
+
new_default = default.respond_to?(:call) ? default.call : default
|
44
|
+
|
45
|
+
@attributes[name] = convert ? convert.call(new_default) : new_default
|
46
|
+
else
|
47
|
+
@attributes[name]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if (attribute_config[:boolean])
|
52
|
+
alias_method :"#{name}?", name
|
53
|
+
end
|
54
|
+
|
55
|
+
case (convert)
|
56
|
+
when Symbol
|
57
|
+
convert_sym = convert
|
58
|
+
convert = lambda { |o| o.send(convert_sym) }
|
59
|
+
when Class
|
60
|
+
convert_class = convert
|
61
|
+
convert = lambda { |o| o.is_a?(convert_class) ? o : convert_class.new(o) }
|
62
|
+
end
|
63
|
+
|
64
|
+
define_method(:"#{name}=") do |value|
|
65
|
+
if (convert)
|
66
|
+
value = convert.call(value)
|
67
|
+
end
|
68
|
+
|
69
|
+
@attributes[name] = value
|
70
|
+
end
|
71
|
+
|
72
|
+
if (attribute_config[:primary])
|
73
|
+
alias_method :primary_attribute=, :"#{name}="
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# == Instance Methods =====================================================
|
78
|
+
|
79
|
+
def initialize(contents = nil)
|
80
|
+
@attributes = { }
|
81
|
+
|
82
|
+
case (contents)
|
83
|
+
when Hash
|
84
|
+
assign(contents)
|
85
|
+
when ReDNS::Buffer
|
86
|
+
deserialize(contents)
|
87
|
+
when String
|
88
|
+
if (respond_to?(:primary_attribute=))
|
89
|
+
self.primary_attribute = contents
|
90
|
+
end
|
91
|
+
else
|
92
|
+
# FUTURE: Raise exception on invalid or unexpected type, but for now
|
93
|
+
# allow the subclass to try.
|
94
|
+
end
|
95
|
+
|
96
|
+
yield(self) if (block_given?)
|
97
|
+
end
|
98
|
+
|
99
|
+
protected
|
100
|
+
def assign(attributes)
|
101
|
+
attributes.each do |k, v|
|
102
|
+
send(:"#{k}=", v)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
class ReDNS::Message < ReDNS::Fragment
|
2
|
+
# == Constants ============================================================
|
3
|
+
|
4
|
+
SECTIONS = [ :questions, :answers, :nameservers, :additional_records ]
|
5
|
+
|
6
|
+
# == Attributes ===========================================================
|
7
|
+
|
8
|
+
attribute :random_id, :default => lambda { rand(0x10000) }
|
9
|
+
|
10
|
+
attribute :id, :convert => lambda { |v| v.to_i % 0x10000 }, :default => 1
|
11
|
+
attribute :query, :boolean => true, :default => true
|
12
|
+
attribute :opcode, :default => :query
|
13
|
+
attribute :authorative, :boolean => true, :default => false
|
14
|
+
attribute :truncated, :boolean => true, :default => false
|
15
|
+
attribute :recursion_desired, :boolean => true, :default => false
|
16
|
+
attribute :recursion_available, :boolean => true, :default => false
|
17
|
+
attribute :response_code, :default => :noerror
|
18
|
+
|
19
|
+
attribute :questions_count, :convert => :to_i, :default => 0
|
20
|
+
attribute :answers_count, :convert => :to_i, :default => 0
|
21
|
+
attribute :nameservers_count, :convert => :to_i, :default => 0
|
22
|
+
attribute :additional_records_count, :convert => :to_i, :default => 0
|
23
|
+
|
24
|
+
attribute :questions, :default => lambda { [ ] }
|
25
|
+
attribute :answers, :default => lambda { [ ] }
|
26
|
+
attribute :nameservers, :default => lambda { [ ] }
|
27
|
+
attribute :additional_records, :default => lambda { [ ] }
|
28
|
+
|
29
|
+
# == Class Methods ========================================================
|
30
|
+
|
31
|
+
# == Instance Methods =====================================================
|
32
|
+
|
33
|
+
def increment_id!
|
34
|
+
@attributes[:id] += 1
|
35
|
+
end
|
36
|
+
|
37
|
+
def response?
|
38
|
+
!self.query?
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
flags = [ ]
|
43
|
+
flags << 'qr' if (response?)
|
44
|
+
flags << 'aa' if (authorative?)
|
45
|
+
flags << 'tc' if (truncated?)
|
46
|
+
flags << 'rd' if (recursion_desired?)
|
47
|
+
flags << 'ra' if (recursion_available?)
|
48
|
+
|
49
|
+
";; HEADER:\n;; opcode: #{opcode.to_s.upcase} status: #{response_code.to_s.upcase} id: #{id} \n" +
|
50
|
+
";; flags: #{flags.join(' ')}; QUERY: #{questions.length}, ANSWER: #{answers.length}, AUTHORITY: #{nameservers.length}, ADDITIONAL: #{additional_records.length}" +
|
51
|
+
"\n" +
|
52
|
+
";; QUESTION SECTION:\n" +
|
53
|
+
questions.collect(&:to_s).join("\n") + "\n" +
|
54
|
+
";; ANSWER SECTION:\n" +
|
55
|
+
answers.collect(&:to_s).join("\n") + "\n" +
|
56
|
+
";; NAMESERVER SECTION:\n" +
|
57
|
+
nameservers.collect(&:to_s).join("\n") + "\n" +
|
58
|
+
";; ADDITIONAL SECTION:\n" +
|
59
|
+
additional_records.collect(&:to_s).join("\n") + "\n"
|
60
|
+
end
|
61
|
+
|
62
|
+
def length
|
63
|
+
to_dns.length
|
64
|
+
end
|
65
|
+
|
66
|
+
def empty?
|
67
|
+
questions.empty? and
|
68
|
+
answers.empty? and
|
69
|
+
nameservers.empty? and
|
70
|
+
additional_records.empty?
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_yaml
|
74
|
+
@attributes.to_yaml
|
75
|
+
end
|
76
|
+
|
77
|
+
def serialize(buffer = ReDNS::Buffer.new)
|
78
|
+
buffer.pack(
|
79
|
+
[
|
80
|
+
self.id,
|
81
|
+
(
|
82
|
+
(self.query? ? 0 : 0x8000) |
|
83
|
+
(ReDNS::OPCODE[self.opcode] || ReDNS::OPCODE[:unknown]) << 12 |
|
84
|
+
(self.authorative? ? 0x0400 : 0) |
|
85
|
+
(self.truncated? ? 0x0200 : 0) |
|
86
|
+
(self.ecursion_desired? ? 0x0100 : 0) |
|
87
|
+
(self.recursion_available? ? 0x0080 : 0) |
|
88
|
+
(ReDNS::RCODE[self.reponse_code] || ReDNS::RCODE[:noerror])
|
89
|
+
),
|
90
|
+
self.questions.length,
|
91
|
+
self.answers.length,
|
92
|
+
self.nameservers.length,
|
93
|
+
self.additional_records.length
|
94
|
+
],
|
95
|
+
'nnnnnn'
|
96
|
+
)
|
97
|
+
|
98
|
+
[ :questions, :answers, :nameservers, :additional_records ].each do |section|
|
99
|
+
@attributes[:group].each do |part|
|
100
|
+
part.serialize(buffer)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
buffer
|
105
|
+
end
|
106
|
+
|
107
|
+
def deserialize(buffer)
|
108
|
+
data = buffer.unpack("nnnnnn")
|
109
|
+
|
110
|
+
self.id = data.shift
|
111
|
+
|
112
|
+
flags = data.shift
|
113
|
+
self.query = flags & 0x8000
|
114
|
+
self.opcode = (flags & 0x7800) >> 12
|
115
|
+
self.authorative = flags & 0x0400
|
116
|
+
self.truncated = flags & 0x0200
|
117
|
+
self.recursion_desired = flags & 0x0100
|
118
|
+
|
119
|
+
self.recursion_available = flags & 0x0080
|
120
|
+
self.response_code = flags & 0x000F
|
121
|
+
|
122
|
+
SECTIONS.each do |section|
|
123
|
+
@attributes[:"#{section}_count"] = data.shift
|
124
|
+
end
|
125
|
+
|
126
|
+
SECTIONS.each do |section|
|
127
|
+
collection = @attributes[section] = [ ]
|
128
|
+
|
129
|
+
decode_class =
|
130
|
+
case (section)
|
131
|
+
when :question
|
132
|
+
ReDNS::Question
|
133
|
+
else
|
134
|
+
ReDNS::Resource
|
135
|
+
end
|
136
|
+
|
137
|
+
@attributes[:"#{section}_count"].times do
|
138
|
+
collection << decode_class.new(buffer)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
self
|
143
|
+
end
|
144
|
+
end
|
data/lib/redns/name.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
class ReDNS::Name < ReDNS::Fragment
|
2
|
+
# == Attributes ===========================================================
|
3
|
+
|
4
|
+
attribute :name, :default => '.'
|
5
|
+
|
6
|
+
# == Instance Methods =====================================================
|
7
|
+
|
8
|
+
def initialize(contents = nil)
|
9
|
+
case (contents)
|
10
|
+
when ReDNS::Buffer
|
11
|
+
# Ensure that this String subclass is handled using the default
|
12
|
+
# method, not intercepted and treated as an actual String
|
13
|
+
super(contents)
|
14
|
+
when String
|
15
|
+
super(:name => contents)
|
16
|
+
self.name << '.' unless (self.name.match(/\.$/))
|
17
|
+
else
|
18
|
+
super(contents)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
self.name
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_a
|
27
|
+
[ self.name ]
|
28
|
+
end
|
29
|
+
|
30
|
+
def length
|
31
|
+
to_s.length
|
32
|
+
end
|
33
|
+
|
34
|
+
def empty?
|
35
|
+
name == '.'
|
36
|
+
end
|
37
|
+
|
38
|
+
def serialize(buffer = ReDNS::Buffer.new)
|
39
|
+
buffer.append(
|
40
|
+
self.name.split(/\./).collect { |l| [ l.length, l ].pack("ca*") }.join('')
|
41
|
+
)
|
42
|
+
|
43
|
+
buffer.append("\0")
|
44
|
+
|
45
|
+
buffer
|
46
|
+
end
|
47
|
+
|
48
|
+
def deserialize(buffer)
|
49
|
+
self.name = ''
|
50
|
+
|
51
|
+
return_to_offset = nil
|
52
|
+
|
53
|
+
while (c = buffer.unpack('C')[0])
|
54
|
+
if (c & 0xC0 == 0xC0)
|
55
|
+
# This is part of a pointer to another section, so advance to that
|
56
|
+
# point and read from there, but preserve the position where the
|
57
|
+
# pointer was found to leave the buffer in that final state.
|
58
|
+
|
59
|
+
pointer = (c & 0x3F << 8) | buffer.unpack('C')[0]
|
60
|
+
|
61
|
+
return_to_offset = buffer.offset
|
62
|
+
buffer.rewind
|
63
|
+
buffer.advance(pointer)
|
64
|
+
elsif (c == 0)
|
65
|
+
break
|
66
|
+
else
|
67
|
+
name << buffer.read(c)
|
68
|
+
name << '.'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if (return_to_offset)
|
73
|
+
buffer.rewind
|
74
|
+
buffer.advance(return_to_offset)
|
75
|
+
end
|
76
|
+
|
77
|
+
self
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class ReDNS::Question < ReDNS::Fragment
|
2
|
+
# == Constants ============================================================
|
3
|
+
|
4
|
+
SECTIONS = [ :questions, :answers, :nameservers, :additional_records ]
|
5
|
+
|
6
|
+
# == Attributes ===========================================================
|
7
|
+
|
8
|
+
attribute :name, :class => ReDNS::Name, :default => lambda { ReDNS::Name.new }
|
9
|
+
|
10
|
+
attribute :qclass, :default => :in
|
11
|
+
attribute :qtype, :default => :any
|
12
|
+
|
13
|
+
# == Class Methods ========================================================
|
14
|
+
|
15
|
+
# == Instance Methods =====================================================
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"#{name} #{qclass.to_s.upcase} #{qtype.to_s.upcase}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def serialize(buffer = ReDNS::Buffer.new)
|
22
|
+
name.serialize(buffer)
|
23
|
+
buffer.pack([ self.qtype_rfc, self.qclass_rfc ], "nn")
|
24
|
+
|
25
|
+
buffer
|
26
|
+
end
|
27
|
+
|
28
|
+
def deserialize(buffer)
|
29
|
+
self.name = DNS::Name.new(buffer)
|
30
|
+
|
31
|
+
data = buffer.unpack('nn')
|
32
|
+
|
33
|
+
self.qtype = ReDNS::RR_TYPE_LABEL[data.shift]
|
34
|
+
self.qclass = ReDNS::RR_CLASS_LABEL[data.shift]
|
35
|
+
|
36
|
+
self
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class ReDNS::Record::MX < ReDNS::Fragment
|
2
|
+
# == Attributes ===========================================================
|
3
|
+
|
4
|
+
attribute :name, :default => lambda { ReDNS::Name.new }, :class => ReDNS::Name
|
5
|
+
attribute :preference, :default => 0, :convert => :to_i
|
6
|
+
|
7
|
+
# == Class Methods ========================================================
|
8
|
+
|
9
|
+
# == Instance Methods =====================================================
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
"#{preference} #{name}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_a
|
16
|
+
[ name.to_s, preference ]
|
17
|
+
end
|
18
|
+
|
19
|
+
def serialize(buffer = ReDNS::Buffer.new)
|
20
|
+
buffer.pack([ self.preference ], 'n')
|
21
|
+
self.name.serialize(buffer)
|
22
|
+
end
|
23
|
+
|
24
|
+
def deserialize(buffer)
|
25
|
+
self.preference = buffer.unpack('n')[0]
|
26
|
+
self.name = ReDNS::Name.new(buffer)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class ReDNS::Record::Null < ReDNS::Fragment
|
2
|
+
# == Attributes ===========================================================
|
3
|
+
|
4
|
+
attribute :contents, :default => '', :primary => true
|
5
|
+
|
6
|
+
# == Class Methods ========================================================
|
7
|
+
|
8
|
+
# == Instance Methods =====================================================
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"#{self.contents}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_a
|
15
|
+
[ self.contents ]
|
16
|
+
end
|
17
|
+
|
18
|
+
def serialize(buffer = ReDNS::Buffer.new)
|
19
|
+
buffer.append(self.contents)
|
20
|
+
|
21
|
+
buffer
|
22
|
+
end
|
23
|
+
|
24
|
+
def deserialize(buffer)
|
25
|
+
self.contents = buffer.to_s
|
26
|
+
buffer.advance(self.contents.length)
|
27
|
+
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|