redns 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|