Class: Dnsruby::Message

Inherits:
Object
  • Object
show all
Defined in:
lib/Dnsruby/message.rb

Overview

Defines a DNS packet.

RFC 1035 Section 4.1, RFC 2136 Section 2, RFC 2845

Sections

Message objects have five sections:

  • The header section, a Dnsruby::Header object.

    msg.header=Header.new(...)
    header = msg.header
    
  • The question section, an array of Dnsruby::Question objects.

    msg.add_question(Question.new(domain, type, klass))
    msg.each_question do |question|  ....   end
    
  • The answer section, an array of Dnsruby::RR objects.

    msg.add_answer(RR.create({:name    => "a2.example.com",
    

:type => “A”, :address => “10.0.0.2”}))

msg.each_answer {|answer| ... }
  • The authority section, an array of Dnsruby::RR objects.

    msg.add_authority(rr)
    msg.each_authority {|rr| ... }
    
  • The additional section, an array of Dnsruby::RR objects.

    msg.add_additional(rr)
    msg.each_additional {|rr| ... }
    

In addition, each_resource iterates the answer, additional and authority sections :

msg.each_resource {|rr| ... }

Packet format encoding

Dnsruby::Message#encode
Dnsruby::Message::decode(data)

Direct Known Subclasses

Update

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Message

Create a new Message. Takes optional name, type and class

type defaults to A, and klass defaults to IN

Dnsruby::Message.new("example.com") # defaults to A, IN
Dnsruby::Message.new("example.com", 'AAAA')
Dnsruby::Message.new("example.com", Dnsruby::Types.PTR, "HS")


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/Dnsruby/message.rb', line 70

def initialize(*args)
  @header = Header.new()
  @question = []
  @answer = []
  @authority = []
  @additional = []
  type = Types.A
  klass = Classes.IN
  if (args.length > 0)
    name = args[0]
    if (args.length > 1)
      type = Types.new(args[1])
      if (args.length > 2)
        klass = Classes.new(args[2])
      end
    end
    add_question(name, type, klass)
  end
end

Instance Attribute Details

#additionalObject (readonly)

The additional section, an array of Dnsruby::RR objects.



98
99
100
# File 'lib/Dnsruby/message.rb', line 98

def additional
  @additional
end

#answerObject (readonly) Also known as: pre

The answer section, an array of Dnsruby::RR objects.



94
95
96
# File 'lib/Dnsruby/message.rb', line 94

def answer
  @answer
end

#answerfromObject

If this Message is a response from a server, then answerfrom contains the address of the server



103
104
105
# File 'lib/Dnsruby/message.rb', line 103

def answerfrom
  @answerfrom
end

#answersizeObject

If this Message is a response from a server, then answersize contains the size of the response



106
107
108
# File 'lib/Dnsruby/message.rb', line 106

def answersize
  @answersize
end

#authorityObject (readonly) Also known as: update

The authority section, an array of Dnsruby::RR objects.



96
97
98
# File 'lib/Dnsruby/message.rb', line 96

def authority
  @authority
end

#headerObject

The header section, a Dnsruby::Header object.



100
101
102
# File 'lib/Dnsruby/message.rb', line 100

def header
  @header
end

#querytsigObject (readonly)

Returns the value of attribute querytsig.



109
110
111
# File 'lib/Dnsruby/message.rb', line 109

def querytsig
  @querytsig
end

#questionObject (readonly) Also known as: zone

The question section, an array of Dnsruby::Question objects.



91
92
93
# File 'lib/Dnsruby/message.rb', line 91

def question
  @question
end

#tsigerrorObject (readonly)

Returns the value of attribute tsigerror.



110
111
112
# File 'lib/Dnsruby/message.rb', line 110

def tsigerror
  @tsigerror
end

#tsigkeyObject

Returns the value of attribute tsigkey.



108
109
110
# File 'lib/Dnsruby/message.rb', line 108

def tsigkey
  @tsigkey
end

#tsigstartObject

Returns the value of attribute tsigstart.



112
113
114
# File 'lib/Dnsruby/message.rb', line 112

def tsigstart
  @tsigstart
end

#tsigstateObject

Returns the value of attribute tsigstate.



113
114
115
# File 'lib/Dnsruby/message.rb', line 113

def tsigstate
  @tsigstate
end

Class Method Details

.decode(m) ⇒ Object

Decode the encoded message



284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/Dnsruby/message.rb', line 284

def Message.decode(m)
  o = Message.new()
  MessageDecoder.new(m) {|msg|
    o.header = Header.new(msg)
    o.header.qdcount.times {
      question = msg.get_question
      o.question << question
    }
    o.header.ancount.times {
      rr = msg.get_rr
      o.answer << rr
    }
    o.header.nscount.times {
      rr = msg.get_rr
      o.authority << rr
    }
    o.header.arcount.times { |count|
      rr = msg.get_rr
      if (rr.type == Types.TSIG)
        if (count!=o.header.arcount-1)
          TheLog.Error("Incoming message has TSIG record before last record")
        end
        # @TODO@ Sort TSIG stuff out!
        # o.tsigstart = pos
      end
      o.additional << rr
    }
  }
  return o
end

Instance Method Details

#==(other) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
# File 'lib/Dnsruby/message.rb', line 115

def ==(other)
  ret = false
  if (other.kind_of?Message)
    ret = @header == other.header &&
      @question == other.question &&
      @answer == other.answer &&
      @authority == other.authority &&
      @additional == other.additional
  end
  return ret
end

#add_additional(rr) ⇒ Object

:nodoc: all



174
175
176
177
178
179
# File 'lib/Dnsruby/message.rb', line 174

def add_additional(rr) #:nodoc: all
  if (!@additional.include?rr)
    @additional << rr
    @header.arcount = @additional.length
  end
end

#add_answer(rr) ⇒ Object Also known as: add_pre

:nodoc: all



148
149
150
151
152
153
# File 'lib/Dnsruby/message.rb', line 148

def add_answer(rr) #:nodoc: all
  if (!@answer.include?rr)
    @answer << rr
    @header.ancount = @answer.length
  end
end

#add_authority(rr) ⇒ Object Also known as: add_update

:nodoc: all



161
162
163
164
165
166
# File 'lib/Dnsruby/message.rb', line 161

def add_authority(rr) #:nodoc: all
  if (!@authority.include?rr)
    @authority << rr
    @header.nscount = @authority.length
  end
end

#add_question(question, type = Types.A, klass = Classes.IN) ⇒ Object Also known as: add_zone

Add a new Question to the Message. Takes either a Question, or a name, and an optional type and class.

msg.add_question(Question.new(“example.com”, ‘MX’)) msg.add_question(“example.com”) # defaults to Types.A, Classes.IN msg.add_question(“example.com”, Types.LOC)



133
134
135
136
137
138
139
# File 'lib/Dnsruby/message.rb', line 133

def add_question(question, type=Types.A, klass=Classes.IN)
  if (!question.kind_of?Question) 
    question = Question.new(question, type, klass)
  end
  @question << question
  @header.qdcount = @question.length
end

#each_additionalObject



181
182
183
184
185
# File 'lib/Dnsruby/message.rb', line 181

def each_additional
  @additional.each {|rec|
    yield rec
  }
end

#each_answerObject Also known as: each_pre



155
156
157
158
159
# File 'lib/Dnsruby/message.rb', line 155

def each_answer
  @answer.each {|rec|
    yield rec
  }
end

#each_authorityObject Also known as: each_update



168
169
170
171
172
# File 'lib/Dnsruby/message.rb', line 168

def each_authority
  @authority.each {|rec|
    yield rec
  }
end

#each_questionObject Also known as: each_zone



141
142
143
144
145
# File 'lib/Dnsruby/message.rb', line 141

def each_question
  @question.each {|rec|
    yield rec
  }
end

#each_resourceObject

Calls each_answer, each_authority, each_additional



188
189
190
191
192
# File 'lib/Dnsruby/message.rb', line 188

def each_resource
  each_answer {|rec| yield rec}
  each_authority {|rec| yield rec}
  each_additional {|rec| yield rec}
end

#encodeObject

Return the encoded form of the message



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/Dnsruby/message.rb', line 255

def encode
  return MessageEncoder.new {|msg|
    header = @header
    if (@tsigkey)
      header.arcount += 1
    end
    header.encode(msg)
    @question.each {|q|
      msg.put_name(q.qname)
      msg.put_pack('nn', q.qtype.code, q.qclass.code)
    }
    [@answer, @authority, @additional].each {|rr|
      rr.each {|r|
        # name, ttl, data = r
        name = r.name
        ttl = r.ttl
        msg.put_name(name)
        msg.put_pack('nnN', r.type.code, r.klass.code, ttl)
        msg.put_length16 {r.encode_rdata(msg)}
      }
    }
    if (@tsigkey)
      tsigrec = @tsigkey.generate(this, msg, tsigerror, querytsig)
      tsigrec.encode(msg, Section.ADDITIONAL, c)
    end
  }.to_s
end

#is_signedObject

Was this message signed by a TSIG?



203
204
205
206
207
# File 'lib/Dnsruby/message.rb', line 203

def is_signed
  return (tsigstate == :Signed ||
      tsigstate == :Verified ||
      tsigstate == :Failed)
end

#is_verifiedObject

If this message was signed by a TSIG, was the TSIG verified?



210
211
212
# File 'lib/Dnsruby/message.rb', line 210

def is_verified
  return (tsigstate == :Verified)
end

#to_sObject



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/Dnsruby/message.rb', line 214

def to_s
  retval = "";
  
  if (@answerfrom != nil && @answerfrom != "")
    retval = retval + ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n";
  end
  
  retval = retval + ";; HEADER SECTION\n";
  retval = retval + @header.to_s;
  
  retval = retval + "\n";
  section = (@header.opcode == OpCode.UPDATE) ? "ZONE" : "QUESTION";
  retval = retval +  ";; #{section} SECTION (#{@header.qdcount}  record#{@header.qdcount == 1 ? '' : 's'})\n";
  each_question { |qr|
    retval = retval + ";; #{qr.to_s}\n";
  }
  
  retval = retval + "\n";
  section = (@header.opcode == OpCode.UPDATE) ? "PREREQUISITE" : "ANSWER";
  retval = retval + ";; #{section} SECTION (#{@header.ancount}  record#{@header.ancount == 1 ? '' : 's'})\n";
  each_answer { |rr|
    retval = retval + rr.to_s + "\n";
  }
  
  retval = retval + "\n";
  section = (@header.opcode == OpCode.UPDATE) ? "UPDATE" : "AUTHORITY";
  retval = retval + ";; #{section} SECTION (#{@header.nscount}  record#{@header.nscount == 1 ? '' : 's'})\n";
  each_authority { |rr|
    retval = retval + rr.to_s + "\n";
  }
  
  retval = retval + "\n";
  retval = retval + ";; ADDITIONAL SECTION (#{@header.arcount}  record#{@header.arcount == 1 ? '' : 's'})\n";
  each_additional { |rr|
    retval = retval + rr.to_s+ "\n";
  }
  
  return retval;
end

#tsigObject

Returns the TSIG record from the ADDITIONAL section, if one is present.



195
196
197
198
199
200
# File 'lib/Dnsruby/message.rb', line 195

def tsig
  if (@additional.last.type == Types.TSIG)
    return @additional.last
  end
  return nil
end