]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnswriter.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.
25 #include <boost/version.hpp>
26 #if BOOST_VERSION >= 105400
27 #include <boost/container/static_vector.hpp>
29 #include "dnswriter.hh"
31 #include "dnsparser.hh"
35 /* d_content: <---- d_stuff ---->
37 dnsheader | qname | qtype | qclass | {recordname| dnsrecordheader | record }
38 ^ d_rollbackmarker ^ d_sor
44 template < typename Container
> GenericDNSPacketWriter
< Container
>:: GenericDNSPacketWriter ( Container
& content
, const DNSName
& qname
, uint16_t qtype
, uint16_t qclass
, uint8_t opcode
)
45 : d_content ( content
), d_qname ( qname
), d_canonic ( false ), d_lowerCase ( false )
50 memset (& dnsheader
, 0 , sizeof ( dnsheader
));
52 dnsheader
. qdcount
= htons ( 1 );
53 dnsheader
. opcode
= opcode
;
55 const uint8_t * ptr
=( const uint8_t *)& dnsheader
;
56 d_content
. reserve ( sizeof ( dnsheader
) + qname
. wirelength () + sizeof ( qtype
) + sizeof ( qclass
));
57 d_content
. resize ( sizeof ( dnsheader
));
58 uint8_t * dptr
=(&* d_content
. begin ());
60 memcpy ( dptr
, ptr
, sizeof ( dnsheader
));
61 d_namepositions
. reserve ( 16 );
62 xfrName ( qname
, false );
66 d_truncatemarker
= d_content
. size ();
71 template < typename Container
> dnsheader
* GenericDNSPacketWriter
< Container
>:: getHeader ()
73 return reinterpret_cast < dnsheader
*>(&* d_content
. begin ());
77 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: startRecord ( const DNSName
& name
, uint16_t qtype
, uint32_t ttl
, uint16_t qclass
, DNSResourceRecord :: Place place
, bool compress
)
79 d_compress
= compress
;
81 d_rollbackmarker
= d_content
. size ();
83 if ( compress
&& ! name
. isRoot () && d_qname
== name
) { // don't do the whole label compression thing if we *know* we can get away with "see question" - except when compressing the root
84 static unsigned char marker
[ 2 ]={ 0xc0 , 0x0c };
85 d_content
. insert ( d_content
. end (), ( const char *) & marker
[ 0 ], ( const char *) & marker
[ 2 ]);
88 xfrName ( name
, compress
);
93 xfr16BitInt ( 0 ); // this will be the record size
94 d_recordplace
= place
;
95 d_sor
= d_content
. size (); // this will remind us where to stuff the record size
98 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: addOpt ( const uint16_t udpsize
, const uint16_t extRCode
, const uint16_t ednsFlags
, const optvect_t
& options
, const uint8_t version
)
104 stuff
. version
= version
;
105 stuff
. extFlags
= htons ( ednsFlags
);
107 /* RFC 6891 section 4 on the Extended RCode wire format
109 * Forms the upper 8 bits of extended 12-bit RCODE (together with the
110 * 4 bits defined in [RFC1035]. Note that EXTENDED-RCODE value 0
111 * indicates that an unextended RCODE is in use (values 0 through 15).
113 // XXX Should be check for extRCode > 1<<12 ?
114 stuff
. extRCode
= extRCode
>> 4 ;
115 if ( extRCode
!= 0 ) { // As this trumps the existing RCODE
116 getHeader ()-> rcode
= extRCode
;
119 static_assert ( sizeof ( EDNS0Record
) == sizeof ( ttl
), "sizeof(EDNS0Record) must match sizeof(ttl)" );
120 memcpy (& ttl
, & stuff
, sizeof ( stuff
));
122 ttl
= ntohl ( ttl
); // will be reversed later on
124 startRecord ( g_rootdnsname
, QType :: OPT
, ttl
, udpsize
, DNSResourceRecord :: ADDITIONAL
, false );
125 for ( auto const & option
: options
) {
126 xfr16BitInt ( option
. first
);
127 xfr16BitInt ( option
. second
. length ());
128 xfrBlob ( option
. second
);
132 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: xfr48BitInt ( uint64_t val
)
134 std :: array
< unsigned char , 6 > bytes
;
135 uint16_t theLeft
= htons (( val
>> 32 )& 0xffff U
);
136 uint32_t theRight
= htonl ( val
& 0xffffffff U
);
137 memcpy (& bytes
[ 0 ], ( void *)& theLeft
, sizeof ( theLeft
));
138 memcpy (& bytes
[ 2 ], ( void *)& theRight
, sizeof ( theRight
));
140 d_content
. insert ( d_content
. end (), bytes
. begin (), bytes
. end ());
143 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: xfrNodeOrLocatorID ( const NodeOrLocatorID
& val
)
145 d_content
. insert ( d_content
. end (), val
. content
, val
. content
+ sizeof ( val
. content
));
148 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: xfr32BitInt ( uint32_t val
)
150 uint32_t rval
= htonl ( val
);
151 uint8_t * ptr
= reinterpret_cast < uint8_t *>(& rval
);
152 d_content
. insert ( d_content
. end (), ptr
, ptr
+ 4 );
155 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: xfr16BitInt ( uint16_t val
)
157 uint16_t rval
= htons ( val
);
158 uint8_t * ptr
= reinterpret_cast < uint8_t *>(& rval
);
159 d_content
. insert ( d_content
. end (), ptr
, ptr
+ 2 );
162 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: xfr8BitInt ( uint8_t val
)
164 d_content
. push_back ( val
);
172 "blah" "blah" -> output 4blah4blah
173 "verylongstringlongerthan256....characters" \xffverylongstring\x23characters (autosplit)
174 "blah\"blah" -> 9blah"blah
179 "blah\"blah" -> blah"blah
181 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: xfrText ( const string
& text
, bool , bool lenField
)
184 d_content
. push_back ( 0 );
187 vector
< string
> segments
= segmentDNSText ( text
);
188 for ( const string
& str
: segments
) {
190 d_content
. push_back ( str
. length ());
191 d_content
. insert ( d_content
. end (), str
. c_str (), str
. c_str () + str
. length ());
195 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: xfrUnquotedText ( const string
& text
, bool lenField
)
198 d_content
. push_back ( 0 );
202 d_content
. push_back ( text
. length ());
203 d_content
. insert ( d_content
. end (), text
. c_str (), text
. c_str () + text
. length ());
207 static constexpr bool l_verbose
= false ;
208 static constexpr uint16_t maxCompressionOffset
= 16384 ;
209 template < typename Container
> uint16_t GenericDNSPacketWriter
< Container
>:: lookupName ( const DNSName
& name
, uint16_t * matchLen
)
211 // iterate over the written labels, see if we find a match
212 const auto & raw
= name
. getStorage ();
214 /* name might be a.root-servers.net, we need to be able to benefit from finding:
215 b.root-servers.net, or even:
218 unsigned int bestpos
= 0 ;
220 #if BOOST_VERSION >= 105400
221 boost :: container :: static_vector
< uint16_t , 34 > nvect
, pvect
;
223 vector
< uint16_t > nvect
, pvect
;
227 for ( auto riter
= raw
. cbegin (); riter
< raw
. cend (); ) {
230 nvect
. push_back ( riter
- raw
. cbegin ());
234 catch ( std :: bad_alloc
& ba
) {
236 cout
<< "Domain " << name
<< " too large to compress" << endl
;
241 cout
<< "Input vector for lookup " << name
<< ": " ;
242 for ( const auto n
: nvect
)
245 cout
<< makeHexDump ( string ( raw
. c_str (), raw
. c_str ()+ raw
. size ()))<< endl
;
249 cout
<< "Have " << d_namepositions
. size ()<< " to ponder" << endl
;
251 for ( auto p
: d_namepositions
) {
253 cout
<< "Pos: " << p
<< ", " << d_content
. size ()<< endl
;
254 DNSName
pname (( const char *)& d_content
[ 0 ], d_content
. size (), p
, true ); // only for debugging
255 cout
<< "Looking at '" << pname
<< "' in packet at position " << p
<< "/" << d_content
. size ()<< ", option " << counter
<< "/" << d_namepositions
. size ()<< endl
;
258 // memcmp here makes things _slower_
261 for ( auto iter
= d_content
. cbegin () + p
; iter
< d_content
. cend ();) {
264 cout
<< "Found label length: " <<( int ) c
<< endl
;
266 uint16_t npos
= 0x100 *( c
& (~ 0xc0 )) + *++ iter
;
267 iter
= d_content
. begin () + npos
;
269 cout
<< "Is compressed label to newpos " << npos
<< ", going there" << endl
;
270 // check against going forward here
275 auto offset
= iter
- d_content
. cbegin ();
276 if ( offset
>= maxCompressionOffset
) break ; // compression pointers cannot point here
277 pvect
. push_back ( offset
);
281 catch ( std :: bad_alloc
& ba
) {
283 cout
<< "Domain " << name
<< " too large to compress" << endl
;
287 cout
<< "Packet vector: " << endl
;
288 for ( const auto n
: pvect
)
292 auto niter
= nvect
. crbegin (), piter
= pvect
. crbegin ();
293 unsigned int cmatchlen
= 1 ;
294 for (; niter
!= nvect
. crend () && piter
!= pvect
. crend (); ++ niter
, ++ piter
) {
295 // niter is an offset in raw, pvect an offset in packet
296 uint8_t nlen
= raw
[* niter
], plen
= d_content
[* piter
];
298 cout
<< "nlnen=" <<( int ) nlen
<< ", plen=" <<( int ) plen
<< endl
;
301 if ( strncasecmp ( raw
. c_str ()+* niter
+ 1 , ( const char *)& d_content
[* piter
]+ 1 , nlen
)) {
303 cout
<< "Mismatch: " << string ( raw
. c_str ()+* niter
+ 1 , raw
. c_str ()+* niter
+ nlen
+ 1 )<< " != " << string (( const char *)& d_content
[* piter
]+ 1 , ( const char *)& d_content
[* piter
]+ nlen
+ 1 )<< endl
;
307 if ( cmatchlen
== raw
. length ()) { // have matched all of it, can't improve
309 cout
<< "Stopping search, matched whole name" << endl
;
310 * matchLen
= cmatchlen
;
314 if ( piter
!= pvect
. crbegin () && * matchLen
< cmatchlen
) {
315 * matchLen
= cmatchlen
;
321 // this is the absolute hottest function in the pdns recursor
322 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: xfrName ( const DNSName
& name
, bool compress
, bool )
325 cout
<< "Wants to write " << name
<< ", compress=" << compress
<< ", canonic=" << d_canonic
<< ", LC=" << d_lowerCase
<< endl
;
326 if ( d_canonic
|| d_lowerCase
) // d_lowerCase implies canonic
329 if ( name
. empty () || name
. isRoot ()) { // for speed
330 d_content
. push_back ( 0 );
336 if ( d_compress
&& compress
&& ( li
= lookupName ( name
, & matchlen
)) && li
< maxCompressionOffset
) {
337 const auto & dns
= name
. getStorage ();
339 cout
<< "Found a substring of " << matchlen
<< " bytes from the back, offset: " << li
<< ", dnslen: " << dns
. size ()<< endl
;
340 // found a substring, if www.powerdns.com matched powerdns.com, we get back matchlen = 13
342 unsigned int pos
= d_content
. size ();
343 if ( pos
< maxCompressionOffset
&& matchlen
!= dns
. size ()) {
345 cout
<< "Inserting pos " << pos
<< " for " << name
<< " for compressed case" << endl
;
346 d_namepositions
. push_back ( pos
);
350 cout
<< "Going to write unique part: '" << makeHexDump ( string ( dns
. c_str (), dns
. c_str () + dns
. size () - matchlen
)) << "'" << endl
;
351 d_content
. insert ( d_content
. end (), ( const unsigned char *) dns
. c_str (), ( const unsigned char *) dns
. c_str () + dns
. size () - matchlen
);
355 d_content
. push_back (( char )( offset
>> 8 ));
356 d_content
. push_back (( char )( offset
& 0xff ));
359 unsigned int pos
= d_content
. size ();
361 cout
<< "Found nothing, we are at pos " << pos
<< ", inserting whole name" << endl
;
362 if ( pos
< maxCompressionOffset
) {
364 cout
<< "Inserting pos " << pos
<< " for " << name
<< " for uncompressed case" << endl
;
365 d_namepositions
. push_back ( pos
);
368 std :: unique_ptr
< DNSName
> lc
;
370 lc
= make_unique
< DNSName
>( name
. makeLowerCase ());
372 const DNSName :: string_t
& raw
= ( lc
? * lc
: name
). getStorage ();
374 cout
<< "Writing out the whole thing " << makeHexDump ( string ( raw
. c_str (), raw
. c_str () + raw
. length ()))<< endl
;
375 d_content
. insert ( d_content
. end (), raw
. c_str (), raw
. c_str () + raw
. size ());
379 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: xfrBlob ( const string
& blob
, int )
381 const uint8_t * ptr
= reinterpret_cast < const uint8_t *>( blob
. c_str ());
382 d_content
. insert ( d_content
. end (), ptr
, ptr
+ blob
. size ());
385 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: xfrBlob ( const std :: vector
< uint8_t >& blob
)
387 d_content
. insert ( d_content
. end (), blob
. begin (), blob
. end ());
390 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: xfrBlobNoSpaces ( const string
& blob
, int )
395 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: xfrHexBlob ( const string
& blob
, bool /* keepReading */ )
400 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: xfrSvcParamKeyVals ( const std :: set
< SvcParam
> & kvs
)
402 for ( auto const & param
: kvs
) {
404 xfr16BitInt ( param
. getKey ());
406 switch ( param
. getKey ())
408 case SvcParam :: mandatory
:
409 xfr16BitInt ( 2 * param
. getMandatory (). size ());
410 for ( auto const & m
: param
. getMandatory ()) {
416 uint16_t totalSize
= param
. getALPN (). size (); // All 1 octet size headers for each value
417 for ( auto const & a
: param
. getALPN ()) {
418 totalSize
+= a
. length ();
420 xfr16BitInt ( totalSize
);
421 for ( auto const & a
: param
. getALPN ()) {
422 xfrUnquotedText ( a
, true ); // will add the 1-byte length field
426 case SvcParam :: no_default_alpn
:
427 xfr16BitInt ( 0 ); // no size :)
430 xfr16BitInt ( 2 ); // size
431 xfr16BitInt ( param
. getPort ());
433 case SvcParam :: ipv4hint
:
434 xfr16BitInt ( param
. getIPHints (). size () * 4 ); // size
435 for ( const auto & a
: param
. getIPHints ()) {
436 xfrCAWithoutPort ( param
. getKey (), a
);
439 case SvcParam :: ipv6hint
:
440 xfr16BitInt ( param
. getIPHints (). size () * 16 ); // size
441 for ( const auto & a
: param
. getIPHints ()) {
442 xfrCAWithoutPort ( param
. getKey (), a
);
446 xfr16BitInt ( param
. getECH (). size ()); // size
447 xfrBlobNoSpaces ( param
. getECH ());
450 xfr16BitInt ( param
. getValue (). size ());
451 xfrBlob ( param
. getValue ());
457 // call __before commit__
458 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: getRecordPayload ( string
& records
)
460 records
. assign ( d_content
. begin () + d_sor
, d_content
. end ());
463 template < typename Container
> uint32_t GenericDNSPacketWriter
< Container
>:: size () const
465 return d_content
. size ();
468 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: rollback ()
470 d_content
. resize ( d_rollbackmarker
);
474 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: truncate ()
476 d_content
. resize ( d_truncatemarker
);
477 dnsheader
* dh
= reinterpret_cast < dnsheader
*>( &* d_content
. begin ());
478 dh
-> ancount
= dh
-> nscount
= dh
-> arcount
= 0 ;
481 template < typename Container
> void GenericDNSPacketWriter
< Container
>:: commit ()
485 uint16_t rlen
= d_content
. size () - d_sor
;
486 d_content
[ d_sor
- 2 ]= rlen
>> 8 ;
487 d_content
[ d_sor
- 1 ]= rlen
& 0xff ;
489 dnsheader
* dh
= reinterpret_cast < dnsheader
*>( &* d_content
. begin ());
490 switch ( d_recordplace
) {
491 case DNSResourceRecord :: QUESTION
:
492 dh
-> qdcount
= htons ( ntohs ( dh
-> qdcount
) + 1 );
494 case DNSResourceRecord :: ANSWER
:
495 dh
-> ancount
= htons ( ntohs ( dh
-> ancount
) + 1 );
497 case DNSResourceRecord :: AUTHORITY
:
498 dh
-> nscount
= htons ( ntohs ( dh
-> nscount
) + 1 );
500 case DNSResourceRecord :: ADDITIONAL
:
501 dh
-> arcount
= htons ( ntohs ( dh
-> arcount
) + 1 );
507 template < typename Container
> size_t GenericDNSPacketWriter
< Container
>:: getSizeWithOpts ( const optvect_t
& options
) const
509 size_t result
= size () + /* root */ 1 + DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
+ DNS_TTL_SIZE
+ DNS_RDLENGTH_SIZE
;
511 for ( auto const & option
: options
) {
513 result
+= option
. second
. size ();
519 template class GenericDNSPacketWriter
< std :: vector
< uint8_t >>;
520 #include "noinitvector.hh"
521 template class GenericDNSPacketWriter
< PacketBuffer
>;