]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsname.cc
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <boost/format.hpp>
26 #include "dnswriter.hh"
29 #include <boost/functional/hash.hpp>
32 in DNS label format, with trailing 0. W/o trailing 0, we are 'empty'
33 www.powerdns.com = 3www8powerdns3com0
36 std :: ostream
& operator <<( std :: ostream
& os
, const DNSName
& d
)
38 return os
<< d
. toLogString ();
41 DNSName :: DNSName ( const char * p
)
43 if ( p
[ 0 ]== 0 || ( p
[ 0 ]== '.' && p
[ 1 ]== 0 )) {
44 d_storage
. assign ( 1 , ( char ) 0 );
46 if (! strchr ( p
, ' \\ ' )) {
47 unsigned char lenpos
= 0 ;
48 unsigned char labellen
= 0 ;
49 size_t plen
= strlen ( p
);
50 const char * const pbegin
= p
, * pend
= p
+ plen
;
51 d_storage
. reserve ( plen
+ 1 );
52 for ( auto iter
= pbegin
; iter
!= pend
; ) {
53 lenpos
= d_storage
. size ();
55 throw std :: runtime_error ( "Found . in wrong position in DNSName " + string ( p
));
56 d_storage
. append ( 1 , ( char ) 0 );
59 for (; iter
!= pend
&& * iter
!= '.' ; ++ iter
) {
62 d_storage
. append ( begiter
, iter
);
66 throw std :: range_error ( "label too long to append" );
68 if ( iter
- pbegin
> 254 ) // reserve two bytes, one for length and one for the root label
69 throw std :: range_error ( "name too long to append" );
71 d_storage
[ lenpos
]= labellen
;
73 d_storage
. append ( 1 , ( char ) 0 );
76 d_storage
= segmentDNSNameRaw ( p
);
77 if ( d_storage
. size () > 255 ) {
78 throw std :: range_error ( "name too long" );
85 DNSName :: DNSName ( const char * pos
, int len
, int offset
, bool uncompress
, uint16_t * qtype
, uint16_t * qclass
, unsigned int * consumed
, uint16_t minOffset
)
88 throw std :: range_error ( "Trying to read past the end of the buffer (" + std :: to_string ( offset
)+ " >= " + std :: to_string ( len
)+ ")" );
91 if ( const void * fnd
= memchr ( pos
+ offset
, 0 , len
- offset
)) {
92 d_storage
. reserve ( 2 +( const char *) fnd
-( pos
+ offset
));
96 packetParser ( pos
, len
, offset
, uncompress
, qtype
, qclass
, consumed
, 0 , minOffset
);
99 // this should be the __only__ dns name parser in PowerDNS.
100 void DNSName :: packetParser ( const char * qpos
, int len
, int offset
, bool uncompress
, uint16_t * qtype
, uint16_t * qclass
, unsigned int * consumed
, int depth
, uint16_t minOffset
)
102 const unsigned char * pos
=( const unsigned char *) qpos
;
103 unsigned char labellen
;
104 const unsigned char * opos
= pos
;
107 throw std :: range_error ( "Trying to read past the end of the buffer (" + std :: to_string ( offset
)+ " >= " + std :: to_string ( len
)+ ")" );
108 if ( offset
< ( int ) minOffset
)
109 throw std :: range_error ( "Trying to read before the beginning of the buffer (" + std :: to_string ( offset
)+ " < " + std :: to_string ( minOffset
)+ ")" );
111 const unsigned char * end
= pos
+ len
;
113 while (( labellen
=* pos
++) && pos
< end
) { // "scan and copy"
114 if ( labellen
& 0xc0 ) {
116 throw std :: range_error ( "Found compressed label, instructed not to follow" );
119 int newpos
= ( labellen
<< 8 ) + *( const unsigned char *) pos
;
121 if ( newpos
< offset
) {
122 if ( newpos
< ( int ) minOffset
)
123 throw std :: range_error ( "Invalid label position during decompression (" + std :: to_string ( newpos
)+ " < " + std :: to_string ( minOffset
)+ ")" );
125 throw std :: range_error ( "Abort label decompression after 100 redirects" );
126 packetParser (( const char *) opos
, len
, newpos
, true , 0 , 0 , 0 , depth
, minOffset
);
128 throw std :: range_error ( "Found a forward reference during label decompression" );
132 if ( pos
+ labellen
< end
) {
133 appendRawLabel (( const char *) pos
, labellen
);
136 throw std :: range_error ( "Found an invalid label length in qname" );
139 if ( d_storage
. empty ())
140 d_storage
. append ( 1 , ( char ) 0 ); // we just parsed the root
142 * consumed
= pos
- opos
- offset
;
144 if ( pos
+ labellen
+ 2 > end
) {
145 throw std :: range_error ( "Trying to read qtype past the end of the buffer (" + std :: to_string (( pos
- opos
) + labellen
+ 2 )+ " > " + std :: to_string ( len
)+ ")" );
147 * qtype
=(*( const unsigned char *) pos
)* 256 + *(( const unsigned char *) pos
+ 1 );
151 if ( pos
+ labellen
+ 2 > end
) {
152 throw std :: range_error ( "Trying to read qclass past the end of the buffer (" + std :: to_string (( pos
- opos
) + labellen
+ 2 )+ " > " + std :: to_string ( len
)+ ")" );
154 * qclass
=(*( const unsigned char *) pos
)* 256 + *(( const unsigned char *) pos
+ 1 );
158 std :: string
DNSName :: toString ( const std :: string
& separator
, const bool trailing
) const
161 throw std :: out_of_range ( "Attempt to print an unset dnsname" );
165 return trailing
? separator
: "" ;
168 for ( const auto & s
: getRawLabels ()) {
169 ret
+= escapeLabel ( s
) + separator
;
172 return ret
. substr ( 0 , ret
. size ()-! trailing
);
175 std :: string
DNSName :: toLogString () const
181 return toStringRootDot ();
184 std :: string
DNSName :: toDNSString () const
187 throw std :: out_of_range ( "Attempt to DNSString an unset dnsname" );
189 return std :: string ( d_storage
. c_str (), d_storage
. length ());
192 std :: string
DNSName :: toDNSStringLC () const
194 return toLower ( toDNSString ()); // label lengths are always < 'A'
198 * Get the length of the DNSName on the wire
200 * @return the total wirelength of the DNSName
202 size_t DNSName :: wirelength () const {
203 return d_storage
. length ();
206 // Are WE part of parent
207 bool DNSName :: isPartOf ( const DNSName
& parent
) const
209 if ( parent
. empty () || empty ())
210 throw std :: out_of_range ( "empty dnsnames aren't part of anything" );
212 if ( parent
. d_storage
. size () > d_storage
. size ())
215 // this is slightly complicated since we can't start from the end, since we can't see where a label begins/ends then
216 for ( auto us
= d_storage
. cbegin (); us
< d_storage
. cend (); us
+=* us
+ 1 ) {
217 auto distance
= std :: distance ( us
, d_storage
. cend ());
218 if ( distance
< 0 || static_cast < size_t >( distance
) < parent
. d_storage
. size ()) {
221 if ( static_cast < size_t >( distance
) == parent
. d_storage
. size ()) {
222 auto p
= parent
. d_storage
. cbegin ();
223 for (; us
!= d_storage
. cend (); ++ us
, ++ p
) {
224 if ( dns2_tolower (* p
) != dns2_tolower (* us
))
230 throw std :: out_of_range ( "negative label length in dnsname" );
236 DNSName
DNSName :: makeRelative ( const DNSName
& zone
) const
239 ret
. makeUsRelative ( zone
);
240 return ret
. empty () ? zone
: ret
; // HACK FIXME400
242 void DNSName :: makeUsRelative ( const DNSName
& zone
)
244 if ( isPartOf ( zone
)) {
245 d_storage
. erase ( d_storage
. size ()- zone
. d_storage
. size ());
246 d_storage
. append ( 1 , ( char ) 0 ); // put back the trailing 0
252 DNSName
DNSName :: labelReverse () const
257 return * this ; // we don't create the root automatically below
260 vector
< string
> l
= getRawLabels ();
262 ret
. appendRawLabel ( l
. back ());
269 void DNSName :: appendRawLabel ( const std :: string
& label
)
271 appendRawLabel ( label
. c_str (), label
. length ());
274 void DNSName :: appendRawLabel ( const char * start
, unsigned int length
)
277 throw std :: range_error ( "no such thing as an empty label to append" );
279 throw std :: range_error ( "label too long to append" );
280 if ( d_storage
. size () + length
> 254 ) // reserve one byte for the label length
281 throw std :: range_error ( "name too long to append" );
283 if ( d_storage
. empty ()) {
284 d_storage
. append ( 1 , ( char ) length
);
287 * d_storage
. rbegin ()=( char ) length
;
289 d_storage
. append ( start
, length
);
290 d_storage
. append ( 1 , ( char ) 0 );
293 void DNSName :: prependRawLabel ( const std :: string
& label
)
296 throw std :: range_error ( "no such thing as an empty label to prepend" );
297 if ( label
. size () > 63 )
298 throw std :: range_error ( "label too long to prepend" );
299 if ( d_storage
. size () + label
. size () > 254 ) // reserve one byte for the label length
300 throw std :: range_error ( "name too long to prepend" );
302 if ( d_storage
. empty ())
303 d_storage
. append ( 1 , ( char ) 0 );
305 string_t
prep ( 1 , ( char ) label
. size ());
306 prep
. append ( label
. c_str (), label
. size ());
307 d_storage
= prep
+ d_storage
;
310 bool DNSName :: slowCanonCompare ( const DNSName
& rhs
) const
312 auto ours
= getRawLabels (), rhsLabels
= rhs
. getRawLabels ();
313 return std :: lexicographical_compare ( ours
. rbegin (), ours
. rend (), rhsLabels
. rbegin (), rhsLabels
. rend (), CIStringCompare ());
316 vector
< string
> DNSName :: getRawLabels () const
319 ret
. reserve ( countLabels ());
321 for ( const unsigned char * p
= ( const unsigned char *) d_storage
. c_str (); p
< (( const unsigned char *) d_storage
. c_str ()) + d_storage
. size () && * p
; p
+=* p
+ 1 ) {
322 ret
. push_back ({( const char *) p
+ 1 , ( size_t )* p
}); // XXX FIXME
328 bool DNSName :: chopOff ()
330 if ( d_storage
. empty () || d_storage
[ 0 ]== 0 )
332 d_storage
. erase ( 0 , ( unsigned int ) d_storage
[ 0 ]+ 1 );
336 bool DNSName :: isWildcard () const
338 if ( d_storage
. size () < 2 )
340 auto p
= d_storage
. begin ();
341 return (* p
== 0x01 && *++ p
== '*' );
344 unsigned int DNSName :: countLabels () const
346 unsigned int count
= 0 ;
347 for ( const unsigned char * p
= ( const unsigned char *) d_storage
. c_str (); p
< (( const unsigned char *) d_storage
. c_str ()) + d_storage
. size () && * p
; p
+=* p
+ 1 )
352 void DNSName :: trimToLabels ( unsigned int to
)
354 while ( countLabels () > to
&& chopOff ())
358 bool DNSName :: operator ==( const DNSName
& rhs
) const
360 if ( rhs
. empty () != empty () || rhs
. d_storage
. size () != d_storage
. size ())
363 auto us
= d_storage
. crbegin ();
364 auto p
= rhs
. d_storage
. crbegin ();
365 for (; us
!= d_storage
. crend () && p
!= rhs
. d_storage
. crend (); ++ us
, ++ p
) { // why does this go backward?
366 if ( dns2_tolower (* p
) != dns2_tolower (* us
))
372 size_t hash_value ( DNSName
const & d
)
377 string
DNSName :: escapeLabel ( const std :: string
& label
)
380 ret
. reserve ( label
. size ()); // saves 15% on bulk .COM load
381 for ( uint8_t p
: label
) {
386 else if ( p
> 0x21 && p
< 0x7e )
387 ret
. append ( 1 , ( char ) p
);
389 ret
+= " \\ " + ( boost :: format ( "%03d" ) % ( unsigned int ) p
). str ();