]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsparser.cc
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2005 - 2011 PowerDNS.COM BV
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "dnsparser.hh"
24 #include "dnswriter.hh"
25 #include <boost/lexical_cast.hpp>
26 #include <boost/algorithm/string.hpp>
27 #include <boost/format.hpp>
29 #include "namespaces.hh"
31 class UnknownRecordContent
: public DNSRecordContent
34 UnknownRecordContent ( const DNSRecord
& dr
, PacketReader
& pr
)
35 : DNSRecordContent ( dr
. d_type
), d_dr ( dr
)
37 pr
. copyRecord ( d_record
, dr
. d_clen
);
40 UnknownRecordContent ( const string
& zone
) : DNSRecordContent ( 0 )
42 d_record
. insert ( d_record
. end (), zone
. begin (), zone
. end ());
45 string
getZoneRepresentation () const
48 str
<< " \\ # " <<( unsigned int ) d_record
. size ()<< " " ;
50 for ( size_t n
= 0 ; n
< d_record
. size (); ++ n
) {
51 snprintf ( hex
, sizeof ( hex
)- 1 , "%02x" , d_record
. at ( n
));
57 void toPacket ( DNSPacketWriter
& pw
)
59 string
tmp (( char *)&* d_record
. begin (), d_record
. size ());
61 stringtok ( parts
, tmp
);
62 if ( parts
. size ()!= 3 && !( parts
. size ()== 2 && equals ( parts
[ 1 ], "0" )) )
63 throw MOADNSException ( "Unknown record was stored incorrectly, need 3 fields, got " + lexical_cast
< string
>( parts
. size ())+ ": " + tmp
);
64 const string
& relevant
=( parts
. size () > 2 ) ? parts
[ 2 ] : "" ;
65 unsigned int total
= atoi ( parts
[ 1 ]. c_str ());
66 if ( relevant
. size ()!= 2 * total
)
67 throw MOADNSException (( boost :: format ( "invalid unknown record length for label %s: size not equal to length field (%d != %d)" ) % d_dr
. d_label
. c_str () % relevant
. size () % ( 2 * total
)). str ());
70 for ( unsigned int n
= 0 ; n
< total
; ++ n
) {
72 sscanf ( relevant
. c_str ()+ 2 * n
, "%02x" , & c
);
73 out
. append ( 1 , ( char ) c
);
79 vector
< uint8_t > d_record
;
82 static const string
EncodeDNSLabel ( const string
& input
)
84 if ( input
. length () == 1 && input
[ 0 ]== '.' ) // otherwise we encode .. (long story)
88 bool unescapedSomething
= labeltokUnescape ( parts
, input
);
91 if (! unescapedSomething
) {
92 for ( labelparts_t :: const_iterator i
= parts
. begin (); i
!= parts
. end (); ++ i
) {
93 ret
. append ( 1 , i
-> second
- i
-> first
);
94 ret
. append ( input
. c_str () + i
-> first
, i
-> second
- i
-> first
);
98 for ( labelparts_t :: const_iterator i
= parts
. begin (); i
!= parts
. end (); ++ i
) {
99 string
part ( input
. c_str () + i
-> first
, i
-> second
- i
-> first
);
100 boost :: replace_all ( part
, " \\\\ " , " \\ " );
101 boost :: replace_all ( part
, " \\ ." , "." );
103 ret
. append ( 1 , part
. length ());
112 shared_ptr
< DNSRecordContent
> DNSRecordContent :: unserialize ( const string
& qname
, uint16_t qtype
, const string
& serialized
)
115 memset (& dnsheader
, 0 , sizeof ( dnsheader
));
116 dnsheader
. qdcount
= htons ( 1 );
117 dnsheader
. ancount
= htons ( 1 );
119 vector
< uint8_t > packet
; // build pseudo packet
121 /* will look like: dnsheader, 5 bytes, encoded qname, dns record header, serialized data */
123 string encoded
= EncodeDNSLabel ( qname
);
125 packet
. resize ( sizeof ( dnsheader
) + 5 + encoded
. size () + sizeof ( struct dnsrecordheader
) + serialized
. size ());
129 memcpy (& packet
[ 0 ], & dnsheader
, sizeof ( dnsheader
)); pos
+= sizeof ( dnsheader
);
131 char tmp
[ 6 ]= "\x0" "\x0\x1" "\x0\x1" ; // root question for ns_t_a
132 memcpy (& packet
[ pos
], & tmp
, 5 ); pos
+= 5 ;
134 memcpy (& packet
[ pos
], encoded
. c_str (), encoded
. size ()); pos
+=( uint16_t ) encoded
. size ();
136 struct dnsrecordheader drh
;
137 drh
. d_type
= htons ( qtype
);
138 drh
. d_class
= htons ( 1 );
140 drh
. d_clen
= htons ( serialized
. size ());
142 memcpy (& packet
[ pos
], & drh
, sizeof ( drh
)); pos
+= sizeof ( drh
);
143 memcpy (& packet
[ pos
], serialized
. c_str (), serialized
. size ()); pos
+=( uint16_t ) serialized
. size ();
145 MOADNSParser
mdp (( char *)&* packet
. begin (), ( unsigned int ) packet
. size ());
146 shared_ptr
< DNSRecordContent
> ret
= mdp
. d_answers
. begin ()-> first
. d_content
;
147 ret
-> header
. d_type
= ret
-> d_qtype
;
148 ret
-> label
= mdp
. d_answers
. begin ()-> first
. d_label
;
149 ret
-> header
. d_ttl
= mdp
. d_answers
. begin ()-> first
. d_ttl
;
153 DNSRecordContent
* DNSRecordContent :: mastermake ( const DNSRecord
& dr
,
156 uint16_t searchclass
= ( dr
. d_type
== QType :: OPT
) ? 1 : dr
. d_class
; // class is invalid for OPT
158 typemap_t :: const_iterator i
= getTypemap (). find ( make_pair ( searchclass
, dr
. d_type
));
159 if ( i
== getTypemap (). end () || ! i
-> second
) {
160 return new UnknownRecordContent ( dr
, pr
);
163 return i
-> second ( dr
, pr
);
166 DNSRecordContent
* DNSRecordContent :: mastermake ( uint16_t qtype
, uint16_t qclass
,
167 const string
& content
)
169 zmakermap_t :: const_iterator i
= getZmakermap (). find ( make_pair ( qclass
, qtype
));
170 if ( i
== getZmakermap (). end ()) {
171 return new UnknownRecordContent ( content
);
174 return i
-> second ( content
);
177 DNSRecordContent
* DNSRecordContent :: mastermake ( const DNSRecord
& dr
, PacketReader
& pr
, uint16_t oc
) {
178 // For opcode UPDATE and where the DNSRecord is an answer record, we don't care about content, because this is
179 // not used within the prerequisite section of RFC2136, so - we can simply use unknownrecordcontent.
180 // For section 3.2.3, we do need content so we need to get it properly. But only for the correct Qclasses.
181 if ( oc
== Opcode :: Update
&& dr
. d_place
== DNSRecord :: Answer
&& dr
. d_class
!= 1 )
182 return new UnknownRecordContent ( dr
, pr
);
184 uint16_t searchclass
= ( dr
. d_type
== QType :: OPT
) ? 1 : dr
. d_class
; // class is invalid for OPT
186 typemap_t :: const_iterator i
= getTypemap (). find ( make_pair ( searchclass
, dr
. d_type
));
187 if ( i
== getTypemap (). end () || ! i
-> second
) {
188 return new UnknownRecordContent ( dr
, pr
);
191 return i
-> second ( dr
, pr
);
195 DNSRecordContent :: typemap_t
& DNSRecordContent :: getTypemap ()
197 static DNSRecordContent :: typemap_t typemap
;
201 DNSRecordContent :: n2typemap_t
& DNSRecordContent :: getN2Typemap ()
203 static DNSRecordContent :: n2typemap_t n2typemap
;
207 DNSRecordContent :: t2namemap_t
& DNSRecordContent :: getT2Namemap ()
209 static DNSRecordContent :: t2namemap_t t2namemap
;
214 DNSRecordContent :: zmakermap_t
& DNSRecordContent :: getZmakermap ()
216 static DNSRecordContent :: zmakermap_t zmakermap
;
220 void MOADNSParser :: init ( const char * packet
, unsigned int len
)
222 if ( len
< sizeof ( dnsheader
))
223 throw MOADNSException ( "Packet shorter than minimal header" );
225 memcpy (& d_header
, packet
, sizeof ( dnsheader
));
227 if ( d_header
. opcode
!= Opcode :: Query
&& d_header
. opcode
!= Opcode :: Notify
&& d_header
. opcode
!= Opcode :: Update
)
228 throw MOADNSException ( "Can't parse non-query packet with opcode=" + lexical_cast
< string
>( d_header
. opcode
));
230 d_header
. qdcount
= ntohs ( d_header
. qdcount
);
231 d_header
. ancount
= ntohs ( d_header
. ancount
);
232 d_header
. nscount
= ntohs ( d_header
. nscount
);
233 d_header
. arcount
= ntohs ( d_header
. arcount
);
235 uint16_t contentlen
= len
- sizeof ( dnsheader
);
237 d_content
. resize ( contentlen
);
238 copy ( packet
+ sizeof ( dnsheader
), packet
+ len
, d_content
. begin ());
242 PacketReader
pr ( d_content
);
243 bool validPacket
= false ;
245 d_qtype
= d_qclass
= 0 ; // sometimes replies come in with no question, don't present garbage then
247 for ( n
= 0 ; n
< d_header
. qdcount
; ++ n
) {
248 d_qname
= pr
. getLabel ();
249 d_qtype
= pr
. get16BitInt ();
250 d_qclass
= pr
. get16BitInt ();
253 struct dnsrecordheader ah
;
254 vector
< unsigned char > record
;
256 for ( n
= 0 ; n
< ( unsigned int )( d_header
. ancount
+ d_header
. nscount
+ d_header
. arcount
); ++ n
) {
259 if ( n
< d_header
. ancount
)
260 dr
. d_place
= DNSRecord :: Answer
;
261 else if ( n
< d_header
. ancount
+ d_header
. nscount
)
262 dr
. d_place
= DNSRecord :: Nameserver
;
264 dr
. d_place
= DNSRecord :: Additional
;
266 unsigned int recordStartPos
= pr
. d_pos
;
268 string label
= pr
. getLabel ();
270 pr
. getDnsrecordheader ( ah
);
273 dr
. d_class
= ah
. d_class
;
278 dr
. d_content
= boost :: shared_ptr
< DNSRecordContent
>( DNSRecordContent :: mastermake ( dr
, pr
, d_header
. opcode
));
279 d_answers
. push_back ( make_pair ( dr
, pr
. d_pos
));
281 if ( dr
. d_type
== QType :: TSIG
&& dr
. d_class
== 0xff )
282 d_tsigPos
= recordStartPos
+ sizeof ( struct dnsheader
);
286 if ( pr
. d_pos
!= contentlen
) {
287 throw MOADNSException ( "Packet (" + d_qname
+ "|#" + lexical_cast
< string
>( d_qtype
)+ ") has trailing garbage (" + lexical_cast
< string
>( pr
. d_pos
) + " < " +
288 lexical_cast
< string
>( contentlen
) + ")" );
292 catch ( std :: out_of_range
& re
) {
293 if ( validPacket
&& d_header
. tc
) { // don't sweat it over truncated packets, but do adjust an, ns and arcount
294 if ( n
< d_header
. ancount
) {
295 d_header
. ancount
= n
; d_header
. nscount
= d_header
. arcount
= 0 ;
297 else if ( n
< d_header
. ancount
+ d_header
. nscount
) {
298 d_header
. nscount
= n
- d_header
. ancount
; d_header
. arcount
= 0 ;
301 d_header
. arcount
= n
- d_header
. ancount
- d_header
. nscount
;
305 throw MOADNSException ( "Error parsing packet of " + lexical_cast
< string
>( len
)+ " bytes (rd=" +
306 lexical_cast
< string
>( d_header
. rd
)+
307 "), out of bounds: " + string ( re
. what ()));
313 void PacketReader :: getDnsrecordheader ( struct dnsrecordheader
& ah
)
316 unsigned char * p
= reinterpret_cast < unsigned char *>(& ah
);
318 for ( n
= 0 ; n
< sizeof ( dnsrecordheader
); ++ n
)
319 p
[ n
]= d_content
. at ( d_pos
++);
321 ah
. d_type
= ntohs ( ah
. d_type
);
322 ah
. d_class
= ntohs ( ah
. d_class
);
323 ah
. d_clen
= ntohs ( ah
. d_clen
);
324 ah
. d_ttl
= ntohl ( ah
. d_ttl
);
326 d_startrecordpos
= d_pos
; // needed for getBlob later on
327 d_recordlen
= ah
. d_clen
;
331 void PacketReader :: copyRecord ( vector
< unsigned char >& dest
, uint16_t len
)
337 for ( uint16_t n
= 0 ; n
< len
;++ n
) {
338 dest
. at ( n
)= d_content
. at ( d_pos
++);
342 void PacketReader :: copyRecord ( unsigned char * dest
, uint16_t len
)
344 if ( d_pos
+ len
> d_content
. size ())
345 throw std :: out_of_range ( "Attempt to copy outside of packet" );
347 memcpy ( dest
, & d_content
. at ( d_pos
), len
);
351 void PacketReader :: xfr48BitInt ( uint64_t & ret
)
354 ret
+= d_content
. at ( d_pos
++);
356 ret
+= d_content
. at ( d_pos
++);
358 ret
+= d_content
. at ( d_pos
++);
360 ret
+= d_content
. at ( d_pos
++);
362 ret
+= d_content
. at ( d_pos
++);
364 ret
+= d_content
. at ( d_pos
++);
367 uint32_t PacketReader :: get32BitInt ()
370 ret
+= d_content
. at ( d_pos
++);
372 ret
+= d_content
. at ( d_pos
++);
374 ret
+= d_content
. at ( d_pos
++);
376 ret
+= d_content
. at ( d_pos
++);
382 uint16_t PacketReader :: get16BitInt ()
384 return get16BitInt ( d_content
, d_pos
);
387 uint16_t PacketReader :: get16BitInt ( const vector
< unsigned char >& content
, uint16_t & pos
)
390 ret
+= content
. at ( pos
++);
392 ret
+= content
. at ( pos
++);
397 uint8_t PacketReader :: get8BitInt ()
399 return d_content
. at ( d_pos
++);
402 string
PacketReader :: getLabel ( unsigned int recurs
)
406 getLabelFromContent ( d_content
, d_pos
, ret
, recurs
++);
410 static string
txtEscape ( const string
& name
)
415 for ( string :: const_iterator i
= name
. begin (); i
!= name
. end ();++ i
) {
416 if (( unsigned char ) * i
> 127 || ( unsigned char ) * i
< 32 ) {
417 snprintf ( ebuf
, sizeof ( ebuf
), " \\ %03u" , ( unsigned char )* i
);
420 else if (* i
== '"' || * i
== ' \\ ' ){
430 // exceptions thrown here do not result in logging in the main pdns auth server - just so you know!
431 string
PacketReader :: getText ( bool multi
)
435 while ( d_pos
< d_startrecordpos
+ d_recordlen
) {
439 unsigned char labellen
= d_content
. at ( d_pos
++);
442 if ( labellen
) { // no need to do anything for an empty string
443 string
val (& d_content
. at ( d_pos
), & d_content
. at ( d_pos
+ labellen
- 1 )+ 1 );
444 ret
. append ( txtEscape ( val
)); // the end is one beyond the packet
456 void PacketReader :: getLabelFromContent ( const vector
< uint8_t >& content
, uint16_t & frompos
, string
& ret
, int recurs
)
458 if ( recurs
> 1000 ) // the forward reference-check below should make this test 100% obsolete
459 throw MOADNSException ( "Loop" );
462 unsigned char labellen
= content
. at ( frompos
++);
469 if (( labellen
& 0xc0 ) == 0xc0 ) {
470 uint16_t offset
= 256 *( labellen
& ~ 0xc0 ) + ( unsigned int ) content
. at ( frompos
++) - sizeof ( dnsheader
);
471 // cout<<"This is an offset, need to go to: "<<offset<<endl;
473 if ( offset
>= frompos
- 2 )
474 throw MOADNSException ( "forward reference during label decompression" );
475 return getLabelFromContent ( content
, offset
, ret
, ++ recurs
);
478 // XXX FIXME THIS MIGHT BE VERY SLOW!
479 ret
. reserve ( ret
. size () + labellen
+ 2 );
480 for ( string :: size_type n
= 0 ; n
< labellen
; ++ n
, frompos
++) {
481 if ( content
. at ( frompos
)== '.' || content
. at ( frompos
)== ' \\ ' ) {
483 ret
. append ( 1 , content
[ frompos
]);
485 else if ( content
. at ( frompos
)== ' ' ) {
489 ret
. append ( 1 , content
[ frompos
]);
496 void PacketReader :: xfrBlob ( string
& blob
)
498 if ( d_recordlen
&& !( d_pos
== ( d_startrecordpos
+ d_recordlen
)))
499 blob
. assign (& d_content
. at ( d_pos
), & d_content
. at ( d_startrecordpos
+ d_recordlen
- 1 ) + 1 );
503 d_pos
= d_startrecordpos
+ d_recordlen
;
506 void PacketReader :: xfrBlob ( string
& blob
, int length
)
509 blob
. assign (& d_content
. at ( d_pos
), & d_content
. at ( d_pos
+ length
- 1 ) + 1 );
518 void PacketReader :: xfrHexBlob ( string
& blob
, bool keepReading
)
523 string
simpleCompress ( const string
& elabel
, const string
& root
)
526 // FIXME: this relies on the semi-canonical escaped output from getLabelFromContent
527 boost :: replace_all ( label
, " \\ ." , "." );
528 boost :: replace_all ( label
, " \\ 032" , " " );
529 boost :: replace_all ( label
, " \\\\ " , " \\ " );
530 typedef vector
< pair
< unsigned int , unsigned int > > parts_t
;
532 vstringtok ( parts
, label
, "." );
534 ret
. reserve ( label
. size ()+ 4 );
535 for ( parts_t :: const_iterator i
= parts
. begin (); i
!= parts
. end (); ++ i
) {
536 if (! root
. empty () && ! strncasecmp ( root
. c_str (), label
. c_str () + i
-> first
, 1 + label
. length () - i
-> first
)) { // also match trailing 0, hence '1 +'
537 const unsigned char rootptr
[ 2 ]={ 0xc0 , 0x11 };
538 ret
. append (( const char *) rootptr
, 2 );
541 ret
. append ( 1 , ( char )( i
-> second
- i
-> first
));
542 ret
. append ( label
. c_str () + i
-> first
, i
-> second
- i
-> first
);
544 ret
. append ( 1 , ( char ) 0 );
549 void simpleExpandTo ( const string
& label
, unsigned int frompos
, string
& ret
)
551 unsigned int labellen
= 0 ;
552 while (( labellen
= label
. at ( frompos
++))) {
553 ret
. append ( label
. c_str ()+ frompos
, labellen
);
559 /** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs
560 * If you survive that, feel free to read from the pointer */
561 class DNSPacketMangler
564 explicit DNSPacketMangler ( std :: string
& packet
)
565 : d_packet ( packet
), d_notyouroffset ( 12 ), d_offset ( d_notyouroffset
)
571 while (( len
= get8BitInt ())) {
572 if ( len
>= 0xc0 ) { // extended label
579 void skipBytes ( uint16_t bytes
)
583 uint16_t get16BitInt ()
585 const char * p
= d_packet
. c_str () + d_offset
;
588 memcpy (& ret
, ( void *) p
, 2 );
594 const char * p
= d_packet
. c_str () + d_offset
;
601 int toskip
= get16BitInt ();
604 void decreaseAndSkip32BitInt ( uint32_t decrease
)
606 const char * p
= ( const char *) d_packet
. c_str () + d_offset
;
610 memcpy (& tmp
, ( void *) p
, sizeof ( tmp
));
614 d_packet
. replace ( d_offset
- 4 , sizeof ( tmp
), ( const char *)& tmp
, sizeof ( tmp
));
617 void moveOffset ( uint16_t by
)
619 d_notyouroffset
+= by
;
620 if ( d_notyouroffset
> d_packet
. length ())
621 throw std :: out_of_range ( "dns packet out of range: " + lexical_cast
< string
>( d_notyouroffset
) + " > "
622 + lexical_cast
< string
>( d_packet
. length ()) );
624 std :: string
& d_packet
;
626 uint32_t d_notyouroffset
; // only 'moveOffset' can touch this
627 const uint32_t & d_offset
; // look.. but don't touch
631 // method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
632 void ageDNSPacket ( std :: string
& packet
, uint32_t seconds
)
634 if ( packet
. length () < sizeof ( dnsheader
))
639 memcpy (( void *)& dh
, ( const dnsheader
*) packet
. c_str (), sizeof ( dh
));
640 int numrecords
= ntohs ( dh
. ancount
) + ntohs ( dh
. nscount
) + ntohs ( dh
. arcount
);
641 DNSPacketMangler
dpm ( packet
);
644 for ( n
= 0 ; n
< ntohs ( dh
. qdcount
) ; ++ n
) {
646 dpm
. skipBytes ( 4 ); // qtype, qclass
648 // cerr<<"Skipped "<<n<<" questions, now parsing "<<numrecords<<" records"<<endl;
649 for ( n
= 0 ; n
< numrecords
; ++ n
) {
652 uint16_t dnstype
= dpm
. get16BitInt ();
653 /* uint16_t dnsclass = */ dpm
. get16BitInt ();
655 if ( dnstype
== QType :: OPT
) // not aging that one with a stick
658 dpm
. decreaseAndSkip32BitInt ( seconds
);