]>
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 DNSPacketWriter :: DNSPacketWriter ( vector
< uint8_t >& 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 uint32_t len
= d_content
. size ();
57 d_content
. resize ( len
+ sizeof ( dnsheader
));
58 uint8_t * dptr
=(&* d_content
. begin ()) + len
;
60 memcpy ( dptr
, ptr
, sizeof ( dnsheader
));
61 d_namepositions
. reserve ( 16 );
62 xfrName ( qname
, false );
66 d_truncatemarker
= d_content
. size ();
71 dnsheader
* DNSPacketWriter :: getHeader ()
73 return reinterpret_cast < dnsheader
*>(&* d_content
. begin ());
77 void DNSPacketWriter :: startRecord ( const DNSName
& name
, uint16_t qtype
, uint32_t ttl
, uint16_t qclass
, DNSResourceRecord :: Place place
, bool compress
)
80 d_rollbackmarker
= d_content
. size ();
82 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
83 static unsigned char marker
[ 2 ]={ 0xc0 , 0x0c };
84 d_content
. insert ( d_content
. end (), ( const char *) & marker
[ 0 ], ( const char *) & marker
[ 2 ]);
87 xfrName ( name
, compress
);
92 xfr16BitInt ( 0 ); // this will be the record size
93 d_recordplace
= place
;
94 d_sor
= d_content
. size (); // this will remind us where to stuff the record size
97 void DNSPacketWriter :: addOpt ( const uint16_t udpsize
, const uint16_t extRCode
, const uint16_t ednsFlags
, const optvect_t
& options
, const uint8_t version
)
103 stuff
. version
= version
;
104 stuff
. extFlags
= htons ( ednsFlags
);
106 /* RFC 6891 section 4 on the Extended RCode wire format
108 * Forms the upper 8 bits of extended 12-bit RCODE (together with the
109 * 4 bits defined in [RFC1035]. Note that EXTENDED-RCODE value 0
110 * indicates that an unextended RCODE is in use (values 0 through 15).
112 // XXX Should be check for extRCode > 1<<12 ?
113 stuff
. extRCode
= extRCode
>> 4 ;
114 if ( extRCode
!= 0 ) { // As this trumps the existing RCODE
115 getHeader ()-> rcode
= extRCode
;
118 static_assert ( sizeof ( EDNS0Record
) == sizeof ( ttl
), "sizeof(EDNS0Record) must match sizeof(ttl)" );
119 memcpy (& ttl
, & stuff
, sizeof ( stuff
));
121 ttl
= ntohl ( ttl
); // will be reversed later on
123 startRecord ( g_rootdnsname
, QType :: OPT
, ttl
, udpsize
, DNSResourceRecord :: ADDITIONAL
, false );
124 for ( auto const & option
: options
) {
125 xfr16BitInt ( option
. first
);
126 xfr16BitInt ( option
. second
. length ());
127 xfrBlob ( option
. second
);
131 void DNSPacketWriter :: xfr48BitInt ( uint64_t val
)
133 unsigned char bytes
[ 6 ];
134 uint16_t theLeft
= htons (( val
>> 32 )& 0xffff U
);
135 uint32_t theRight
= htonl ( val
& 0xffffffff U
);
136 memcpy ( bytes
, ( void *)& theLeft
, sizeof ( theLeft
));
137 memcpy ( bytes
+ 2 , ( void *)& theRight
, sizeof ( theRight
));
139 d_content
. insert ( d_content
. end (), bytes
, bytes
+ sizeof ( bytes
));
143 void DNSPacketWriter :: xfr32BitInt ( uint32_t val
)
145 uint32_t rval
= htonl ( val
);
146 uint8_t * ptr
= reinterpret_cast < uint8_t *>(& rval
);
147 d_content
. insert ( d_content
. end (), ptr
, ptr
+ 4 );
150 void DNSPacketWriter :: xfr16BitInt ( uint16_t val
)
152 uint16_t rval
= htons ( val
);
153 uint8_t * ptr
= reinterpret_cast < uint8_t *>(& rval
);
154 d_content
. insert ( d_content
. end (), ptr
, ptr
+ 2 );
157 void DNSPacketWriter :: xfr8BitInt ( uint8_t val
)
159 d_content
. push_back ( val
);
167 "blah" "blah" -> output 4blah4blah
168 "verylongstringlongerthan256....characters" \xffverylongstring\x23characters (autosplit)
169 "blah\"blah" -> 9blah"blah
174 "blah\"blah" -> blah"blah
176 void DNSPacketWriter :: xfrText ( const string
& text
, bool , bool lenField
)
179 d_content
. push_back ( 0 );
182 vector
< string
> segments
= segmentDNSText ( text
);
183 for ( const string
& str
: segments
) {
185 d_content
. push_back ( str
. length ());
186 d_content
. insert ( d_content
. end (), str
. c_str (), str
. c_str () + str
. length ());
190 void DNSPacketWriter :: xfrUnquotedText ( const string
& text
, bool lenField
)
193 d_content
. push_back ( 0 );
197 d_content
. push_back ( text
. length ());
198 d_content
. insert ( d_content
. end (), text
. c_str (), text
. c_str () + text
. length ());
202 static constexpr bool l_verbose
= false ;
203 uint16_t DNSPacketWriter :: lookupName ( const DNSName
& name
, uint16_t * matchLen
)
205 // iterate over the written labels, see if we find a match
206 const auto & raw
= name
. getStorage ();
208 /* name might be a.root-servers.net, we need to be able to benefit from finding:
209 b.root-servers.net, or even:
212 unsigned int bestpos
= 0 ;
214 #if BOOST_VERSION >= 105400
215 boost :: container :: static_vector
< uint16_t , 34 > nvect
, pvect
;
217 vector
< uint16_t > nvect
, pvect
;
221 for ( auto riter
= raw
. cbegin (); riter
< raw
. cend (); ) {
224 nvect
. push_back ( riter
- raw
. cbegin ());
228 catch ( std :: bad_alloc
& ba
) {
230 cout
<< "Domain " << name
<< " too large to compress" << endl
;
235 cout
<< "Input vector for lookup " << name
<< ": " ;
236 for ( const auto n
: nvect
)
239 cout
<< makeHexDump ( string ( raw
. c_str (), raw
. c_str ()+ raw
. size ()))<< endl
;
243 cout
<< "Have " << d_namepositions
. size ()<< " to ponder" << endl
;
245 for ( auto p
: d_namepositions
) {
247 cout
<< "Pos: " << p
<< ", " << d_content
. size ()<< endl
;
248 DNSName
pname (( const char *)& d_content
[ 0 ], d_content
. size (), p
, true ); // only for debugging
249 cout
<< "Looking at '" << pname
<< "' in packet at position " << p
<< "/" << d_content
. size ()<< ", option " << counter
<< "/" << d_namepositions
. size ()<< endl
;
252 // memcmp here makes things _slower_
255 for ( auto iter
= d_content
. cbegin () + p
; iter
< d_content
. cend ();) {
258 cout
<< "Found label length: " <<( int ) c
<< endl
;
260 uint16_t npos
= 0x100 *( c
& (~ 0xc0 )) + *++ iter
;
261 iter
= d_content
. begin () + npos
;
263 cout
<< "Is compressed label to newpos " << npos
<< ", going there" << endl
;
264 // check against going forward here
269 pvect
. push_back ( iter
- d_content
. cbegin ());
273 catch ( std :: bad_alloc
& ba
) {
275 cout
<< "Domain " << name
<< " too large to compress" << endl
;
279 cout
<< "Packet vector: " << endl
;
280 for ( const auto n
: pvect
)
284 auto niter
= nvect
. crbegin (), piter
= pvect
. crbegin ();
285 unsigned int cmatchlen
= 1 ;
286 for (; niter
!= nvect
. crend () && piter
!= pvect
. crend (); ++ niter
, ++ piter
) {
287 // niter is an offset in raw, pvect an offset in packet
288 uint8_t nlen
= raw
[* niter
], plen
= d_content
[* piter
];
290 cout
<< "nlnen=" <<( int ) nlen
<< ", plen=" <<( int ) plen
<< endl
;
293 if ( strncasecmp ( raw
. c_str ()+* niter
+ 1 , ( const char *)& d_content
[* piter
]+ 1 , nlen
)) {
295 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
;
299 if ( cmatchlen
== raw
. length ()) { // have matched all of it, can't improve
301 cout
<< "Stopping search, matched whole name" << endl
;
302 * matchLen
= cmatchlen
;
306 if ( piter
!= pvect
. crbegin () && * matchLen
< cmatchlen
) {
307 * matchLen
= cmatchlen
;
313 // this is the absolute hottest function in the pdns recursor
314 void DNSPacketWriter :: xfrName ( const DNSName
& name
, bool compress
, bool )
317 cout
<< "Wants to write " << name
<< ", compress=" << compress
<< ", canonic=" << d_canonic
<< ", LC=" << d_lowerCase
<< endl
;
318 if ( d_canonic
|| d_lowerCase
) // d_lowerCase implies canonic
321 if ( name
. empty () || name
. isRoot ()) { // for speed
322 d_content
. push_back ( 0 );
328 if ( compress
&& ( li
= lookupName ( name
, & matchlen
))) {
329 const auto & dns
= name
. getStorage ();
331 cout
<< "Found a substring of " << matchlen
<< " bytes from the back, offset: " << li
<< ", dnslen: " << dns
. size ()<< endl
;
332 // found a substring, if www.powerdns.com matched powerdns.com, we get back matchlen = 13
334 unsigned int pos
= d_content
. size ();
335 if ( pos
< 16384 && matchlen
!= dns
. size ()) {
337 cout
<< "Inserting pos " << pos
<< " for " << name
<< " for compressed case" << endl
;
338 d_namepositions
. push_back ( pos
);
342 cout
<< "Going to write unique part: '" << makeHexDump ( string ( dns
. c_str (), dns
. c_str () + dns
. size () - matchlen
)) << "'" << endl
;
343 d_content
. insert ( d_content
. end (), ( const unsigned char *) dns
. c_str (), ( const unsigned char *) dns
. c_str () + dns
. size () - matchlen
);
347 d_content
. push_back (( char )( offset
>> 8 ));
348 d_content
. push_back (( char )( offset
& 0xff ));
351 unsigned int pos
= d_content
. size ();
353 cout
<< "Found nothing, we are at pos " << pos
<< ", inserting whole name" << endl
;
356 cout
<< "Inserting pos " << pos
<< " for " << name
<< " for uncompressed case" << endl
;
357 d_namepositions
. push_back ( pos
);
360 std :: unique_ptr
< DNSName
> lc
;
362 lc
= make_unique
< DNSName
>( name
. makeLowerCase ());
364 const DNSName :: string_t
& raw
= ( lc
? * lc
: name
). getStorage ();
366 cout
<< "Writing out the whole thing " << makeHexDump ( string ( raw
. c_str (), raw
. c_str () + raw
. length ()))<< endl
;
367 d_content
. insert ( d_content
. end (), raw
. c_str (), raw
. c_str () + raw
. size ());
371 void DNSPacketWriter :: xfrBlob ( const string
& blob
, int )
373 const uint8_t * ptr
= reinterpret_cast < const uint8_t *>( blob
. c_str ());
374 d_content
. insert ( d_content
. end (), ptr
, ptr
+ blob
. size ());
377 void DNSPacketWriter :: xfrBlobNoSpaces ( const string
& blob
, int )
382 void DNSPacketWriter :: xfrHexBlob ( const string
& blob
, bool keepReading
)
387 // call __before commit__
388 void DNSPacketWriter :: getRecordPayload ( string
& records
)
390 records
. assign ( d_content
. begin () + d_sor
, d_content
. end ());
393 uint32_t DNSPacketWriter :: size ()
395 return d_content
. size ();
398 void DNSPacketWriter :: rollback ()
400 d_content
. resize ( d_rollbackmarker
);
404 void DNSPacketWriter :: truncate ()
406 d_content
. resize ( d_truncatemarker
);
407 dnsheader
* dh
= reinterpret_cast < dnsheader
*>( &* d_content
. begin ());
408 dh
-> ancount
= dh
-> nscount
= dh
-> arcount
= 0 ;
411 void DNSPacketWriter :: commit ()
415 uint16_t rlen
= d_content
. size () - d_sor
;
416 d_content
[ d_sor
- 2 ]= rlen
>> 8 ;
417 d_content
[ d_sor
- 1 ]= rlen
& 0xff ;
419 dnsheader
* dh
= reinterpret_cast < dnsheader
*>( &* d_content
. begin ());
420 switch ( d_recordplace
) {
421 case DNSResourceRecord :: QUESTION
:
422 dh
-> qdcount
= htons ( ntohs ( dh
-> qdcount
) + 1 );
424 case DNSResourceRecord :: ANSWER
:
425 dh
-> ancount
= htons ( ntohs ( dh
-> ancount
) + 1 );
427 case DNSResourceRecord :: AUTHORITY
:
428 dh
-> nscount
= htons ( ntohs ( dh
-> nscount
) + 1 );
430 case DNSResourceRecord :: ADDITIONAL
:
431 dh
-> arcount
= htons ( ntohs ( dh
-> arcount
) + 1 );