]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnspacket.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.
28 #include <sys/types.h>
31 #include <boost/tokenizer.hpp>
32 #include <boost/functional/hash.hpp>
33 #include <boost/algorithm/string.hpp>
34 #include <openssl/hmac.h>
37 #include "dnsseckeeper.hh"
39 #include "dnsbackend.hh"
40 #include "ednsoptions.hh"
41 #include "pdnsexception.hh"
42 #include "dnspacket.hh"
44 #include "arguments.hh"
45 #include "dnswriter.hh"
46 #include "dnsparser.hh"
47 #include "dnsrecords.hh"
48 #include "dnssecinfra.hh"
50 #include "ednssubnet.hh"
51 #include "gss_context.hh"
52 #include "dns_random.hh"
55 bool DNSPacket :: s_doEDNSSubnetProcessing
;
56 uint16_t DNSPacket :: s_udpTruncationThreshold
;
58 DNSPacket :: DNSPacket ( bool isQuery
): d_isQuery ( isQuery
)
60 memset (& d
, 0 , sizeof ( d
));
63 const string
& DNSPacket :: getString ()
71 ComboAddress
DNSPacket :: getRemote () const
76 uint16_t DNSPacket :: getRemotePort () const
78 return d_remote
. sin4
. sin_port
;
81 void DNSPacket :: setRcode ( int v
)
86 void DNSPacket :: setAnswer ( bool b
)
89 d_rawpacket
. assign ( 12 ,( char ) 0 );
90 memset (( void *)& d
, 0 , sizeof ( d
));
96 void DNSPacket :: setA ( bool b
)
101 void DNSPacket :: setID ( uint16_t id
)
106 void DNSPacket :: setRA ( bool b
)
111 void DNSPacket :: setRD ( bool b
)
116 void DNSPacket :: setOpcode ( uint16_t opcode
)
121 void DNSPacket :: clearRecords ()
127 void DNSPacket :: addRecord ( DNSZoneRecord
&& rr
)
129 // this removes duplicates from the packet.
130 // in case we are not compressing for AXFR, no such checking is performed!
133 std :: string ser
= const_cast < DNSZoneRecord
&>( rr
). dr
. d_content
-> serialize ( rr
. dr
. d_name
);
134 auto hash
= boost :: hash
< std :: pair
< DNSName
, std :: string
> >()({ rr
. dr
. d_name
, ser
});
135 if ( d_dedup
. count ( hash
)) { // might be a dup
136 for ( auto i
= d_rrs
. begin (); i
!= d_rrs
. end ();++ i
) {
137 if ( rr
. dr
== i
-> dr
) // XXX SUPER SLOW
141 d_dedup
. insert ( hash
);
144 d_rrs
. push_back ( std :: move ( rr
));
147 vector
< DNSZoneRecord
*> DNSPacket :: getAPRecords ()
149 vector
< DNSZoneRecord
*> arrs
;
151 for ( vector
< DNSZoneRecord
>:: iterator i
= d_rrs
. begin ();
155 if ( i
-> dr
. d_place
!= DNSResourceRecord :: ADDITIONAL
&&
156 ( i
-> dr
. d_type
== QType :: MX
||
157 i
-> dr
. d_type
== QType :: NS
||
158 i
-> dr
. d_type
== QType :: SRV
))
166 vector
< DNSZoneRecord
*> DNSPacket :: getAnswerRecords ()
168 vector
< DNSZoneRecord
*> arrs
;
170 for ( vector
< DNSZoneRecord
>:: iterator i
= d_rrs
. begin ();
174 if ( i
-> dr
. d_place
!= DNSResourceRecord :: ADDITIONAL
)
181 void DNSPacket :: setCompress ( bool compress
)
184 d_rawpacket
. reserve ( 65000 );
188 bool DNSPacket :: couldBeCached () const
190 return ! d_wantsnsid
&& qclass
== QClass :: IN
&& ! d_havetsig
;
193 unsigned int DNSPacket :: getMinTTL ()
195 unsigned int minttl
= UINT_MAX
;
196 for ( const DNSZoneRecord
& rr
: d_rrs
) {
197 if ( rr
. dr
. d_ttl
< minttl
)
198 minttl
= rr
. dr
. d_ttl
;
204 bool DNSPacket :: isEmpty ()
206 return ( d_rrs
. empty ());
209 /** Must be called before attempting to access getData(). This function stuffs all resource
210 * records found in rrs into the data buffer. It also frees resource records queued for us.
212 void DNSPacket :: wrapup ()
219 vector
< DNSZoneRecord
>:: iterator pos
;
221 // we now need to order rrs so that the different sections come at the right place
222 // we want a stable sort, based on the d_place field
224 stable_sort ( d_rrs
. begin (), d_rrs
. end (), []( const DNSZoneRecord
& a
, const DNSZoneRecord
& b
) {
225 return a
. dr
. d_place
< b
. dr
. d_place
;
227 static bool mustNotShuffle
= :: arg (). mustDo ( "no-shuffle" );
229 if (! d_tcp
&& ! mustNotShuffle
) {
230 pdns :: shuffle ( d_rrs
);
234 vector
< uint8_t > packet
;
235 DNSPacketWriter
pw ( packet
, qdomain
, qtype
. getCode (), qclass
);
237 pw
. getHeader ()-> rcode
= d
. rcode
;
238 pw
. getHeader ()-> opcode
= d
. opcode
;
239 pw
. getHeader ()-> aa
= d
. aa
;
240 pw
. getHeader ()-> ra
= d
. ra
;
241 pw
. getHeader ()-> qr
= d
. qr
;
242 pw
. getHeader ()-> id
= d
. id
;
243 pw
. getHeader ()-> rd
= d
. rd
;
244 pw
. getHeader ()-> tc
= d
. tc
;
246 DNSPacketWriter :: optvect_t opts
;
248 /* optsize is expected to hold an upper bound of data that will be
249 added after actual record data - i.e. OPT, TSIG, perhaps one day
250 XPF. Because of the way `pw` incrementally writes the packet, we
251 cannot easily 'go back' and remove a few records. So, to prevent
252 going over our maximum size, we keep our (potential) extra data
255 This means that sometimes we'll send TC even if we'd end up with
256 a few bytes to spare, but so be it.
260 if ( d_haveednssection
|| d_dnssecOk
) {
261 /* root label (1), type (2), class (2), ttl (4) + rdlen (2) */
266 const static string mode_server_id
=:: arg ()[ "server-id" ];
267 if ( mode_server_id
!= "disabled" ) {
268 opts
. push_back ( make_pair ( EDNSOptionCode :: NSID
, mode_server_id
));
269 optsize
+= EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
+ mode_server_id
. size ();
273 if ( d_haveednssubnet
)
275 // this is an upper bound
276 optsize
+= EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
+ 2 + 1 + 1 ; // code+len+family+src len+scope len
277 optsize
+= d_eso
. source
. isIPv4 () ? 4 : 16 ;
280 if ( d_trc
. d_algoName
. countLabels ())
282 // TSIG is not OPT, but we count it in optsize anyway
283 optsize
+= d_trc
. d_algoName
. wirelength () + 3 + 1 + 2 ; // algo + time + fudge + maclen
284 optsize
+= EVP_MAX_MD_SIZE
+ 2 + 2 + 2 + 0 ; // mac + origid + ercode + otherdatalen + no other data
286 static_assert ( EVP_MAX_MD_SIZE
<= 64 , "EVP_MAX_MD_SIZE is overly huge on this system, please check" );
289 if (! d_rrs
. empty () || ! opts
. empty () || d_haveednssubnet
|| d_haveednssection
) {
291 uint8_t maxScopeMask
= 0 ;
292 for ( pos
= d_rrs
. begin (); pos
< d_rrs
. end (); ++ pos
) {
293 // cerr<<"during wrapup, content=["<<pos->content<<"]"<<endl;
294 maxScopeMask
= max ( maxScopeMask
, pos
-> scopeMask
);
296 pw
. startRecord ( pos
-> dr
. d_name
, pos
-> dr
. d_type
, pos
-> dr
. d_ttl
, pos
-> dr
. d_class
, pos
-> dr
. d_place
);
297 pos
-> dr
. d_content
-> toPacket ( pw
);
298 if ( pw
. size () + optsize
> ( d_tcp
? 65535 : getMaxReplyLen ())) {
301 pw
. getHeader ()-> tc
= 1 ;
306 // if(!pw.getHeader()->tc) // protect against double commit from addSignature
308 if (! d_rrs
. empty ()) pw
. commit ();
312 if ( d_haveednssubnet
) {
313 EDNSSubnetOpts eso
= d_eso
;
314 eso
. scope
= Netmask ( eso
. source
. getNetwork (), maxScopeMask
);
316 string opt
= makeEDNSSubnetOptsString ( eso
);
317 opts
. push_back ( make_pair ( 8 , opt
)); // 'EDNS SUBNET'
320 if (! opts
. empty () || d_haveednssection
|| d_dnssecOk
)
322 pw
. addOpt ( s_udpTruncationThreshold
, d_ednsrcode
, d_dnssecOk
? EDNSOpts :: DNSSECOK
: 0 , opts
);
326 catch ( std :: exception
& e
) {
327 g_log
<< Logger :: Warning
<< "Exception: " << e
. what ()<< endl
;
332 if ( d_trc
. d_algoName
. countLabels ())
333 addTSIG ( pw
, d_trc
, d_tsigkeyname
, d_tsigsecret
, d_tsigprevious
, d_tsigtimersonly
);
335 d_rawpacket
. assign (( char *)& packet
[ 0 ], packet
. size ()); // XXX we could do this natively on a vector..
337 // copy RR counts so they can be read later
338 d
. qdcount
= pw
. getHeader ()-> qdcount
;
339 d
. ancount
= pw
. getHeader ()-> ancount
;
340 d
. nscount
= pw
. getHeader ()-> nscount
;
341 d
. arcount
= pw
. getHeader ()-> arcount
;
344 void DNSPacket :: setQuestion ( int op
, const DNSName
& qd
, int newqtype
)
346 memset (& d
, 0 , sizeof ( d
));
347 d
. id
= dns_random_uint16 ();
348 d
. rd
= d
. tc
= d
. aa
= false ;
350 d
. qdcount
= 1 ; // is htons'ed later on
351 d
. ancount
= d
. arcount
= d
. nscount
= 0 ;
357 /** convenience function for creating a reply packet from a question packet. */
358 std :: unique_ptr
< DNSPacket
> DNSPacket :: replyPacket () const
360 auto r
= make_unique
< DNSPacket
>( false );
361 r
-> setSocket ( d_socket
);
362 r
-> d_anyLocal
= d_anyLocal
;
363 r
-> setRemote (& d_remote
);
364 r
-> setAnswer ( true ); // this implies the allocation of the header
365 r
-> setA ( true ); // and we are authoritative
366 r
-> setRA ( 0 ); // no recursion available
367 r
-> setRD ( d
. rd
); // if you wanted to recurse, answer will say you wanted it
369 r
-> setOpcode ( d
. opcode
);
374 r
-> qdomain
= qdomain
;
377 r
-> d_maxreplylen
= d_maxreplylen
;
378 r
-> d_wantsnsid
= d_wantsnsid
;
379 r
-> d_dnssecOk
= d_dnssecOk
;
381 r
-> d_haveednssubnet
= d_haveednssubnet
;
382 r
-> d_haveednssection
= d_haveednssection
;
383 r
-> d_ednsversion
= 0 ;
386 if ( d_tsigkeyname
. countLabels ()) {
387 r
-> d_tsigkeyname
= d_tsigkeyname
;
388 r
-> d_tsigprevious
= d_tsigprevious
;
390 r
-> d_tsigsecret
= d_tsigsecret
;
391 r
-> d_tsigtimersonly
= d_tsigtimersonly
;
393 r
-> d_havetsig
= d_havetsig
;
397 void DNSPacket :: spoofQuestion ( const DNSPacket
& qd
)
399 d_wrapped
= true ; // if we do this, don't later on wrapup
402 string :: size_type i
= sizeof ( d
);
405 labellen
= qd
. d_rawpacket
[ i
];
408 d_rawpacket
. replace ( i
, labellen
, qd
. d_rawpacket
, i
, labellen
);
413 int DNSPacket :: noparse ( const char * mesg
, size_t length
)
415 d_rawpacket
. assign ( mesg
, length
);
417 g_log
<< Logger :: Debug
<< "Ignoring packet: too short (" << length
<< " < 12) from "
418 << d_remote
. toStringWithPort ()<< endl
;
423 memcpy (( void *)& d
,( const void *) d_rawpacket
. c_str (), 12 );
427 void DNSPacket :: setTSIGDetails ( const TSIGRecordContent
& tr
, const DNSName
& keyname
, const string
& secret
, const string
& previous
, bool timersonly
)
430 d_trc
. d_origID
= ((( d
. id
& 0xFF )<< 8 ) | (( d
. id
& 0xFF00 )>> 8 ));
431 d_tsigkeyname
= keyname
;
432 d_tsigsecret
= secret
;
433 d_tsigprevious
= previous
;
434 d_tsigtimersonly
= timersonly
;
437 bool DNSPacket :: getTSIGDetails ( TSIGRecordContent
* trc
, DNSName
* keyname
, uint16_t * tsigPosOut
) const
439 MOADNSParser
mdp ( d_isQuery
, d_rawpacket
);
440 uint16_t tsigPos
= mdp
. getTSIGPos ();
445 for ( MOADNSParser :: answers_t :: const_iterator i
= mdp
. d_answers
. begin (); i
!= mdp
. d_answers
. end (); ++ i
) {
446 if ( i
-> first
. d_type
== QType :: TSIG
&& i
-> first
. d_class
== QType :: ANY
) {
447 // cast can fail, f.e. if d_content is an UnknownRecordContent.
448 shared_ptr
< TSIGRecordContent
> content
= std :: dynamic_pointer_cast
< TSIGRecordContent
>( i
-> first
. d_content
);
450 g_log
<< Logger :: Error
<< "TSIG record has no or invalid content (invalid packet)" << endl
;
454 * keyname
= i
-> first
. d_name
;
462 * tsigPosOut
= tsigPos
;
468 bool DNSPacket :: getTKEYRecord ( TKEYRecordContent
* tr
, DNSName
* keyname
) const
470 MOADNSParser
mdp ( d_isQuery
, d_rawpacket
);
473 for ( MOADNSParser :: answers_t :: const_iterator i
= mdp
. d_answers
. begin (); i
!= mdp
. d_answers
. end (); ++ i
) {
475 g_log
<< Logger :: Error
<< "More than one TKEY record found in query" << endl
;
479 if ( i
-> first
. d_type
== QType :: TKEY
) {
480 // cast can fail, f.e. if d_content is an UnknownRecordContent.
481 shared_ptr
< TKEYRecordContent
> content
= std :: dynamic_pointer_cast
< TKEYRecordContent
>( i
-> first
. d_content
);
483 g_log
<< Logger :: Error
<< "TKEY record has no or invalid content (invalid packet)" << endl
;
487 * keyname
= i
-> first
. d_name
;
495 /** This function takes data from the network, possibly received with recvfrom, and parses
496 it into our class. Results of calling this function multiple times on one packet are
497 unknown. Returns -1 if the packet cannot be parsed.
499 int DNSPacket :: parse ( const char * mesg
, size_t length
)
502 d_rawpacket
. assign ( mesg
, length
);
505 g_log
<< Logger :: Debug
<< "Ignoring packet: too short from "
506 << getRemote () << endl
;
510 MOADNSParser
mdp ( d_isQuery
, d_rawpacket
);
513 // ANY OPTION WHICH *MIGHT* BE SET DOWN BELOW SHOULD BE CLEARED FIRST!
517 d_havetsig
= mdp
. getTSIGPos ();
518 d_haveednssubnet
= false ;
519 d_haveednssection
= false ;
521 if ( getEDNSOpts ( mdp
, & edo
)) {
522 d_haveednssection
= true ;
524 "Values lower than 512 MUST be treated as equal to 512."
526 d_ednsRawPacketSizeLimit
= edo
. d_packetsize
;
527 d_maxreplylen
= std :: min ( std :: max ( static_cast < uint16_t >( 512 ), edo
. d_packetsize
), s_udpTruncationThreshold
);
528 // cerr<<edo.d_extFlags<<endl;
529 if ( edo
. d_extFlags
& EDNSOpts :: DNSSECOK
)
532 for ( vector
< pair
< uint16_t , string
> >:: const_iterator iter
= edo
. d_options
. begin ();
533 iter
!= edo
. d_options
. end ();
535 if ( iter
-> first
== EDNSOptionCode :: NSID
) {
538 else if ( s_doEDNSSubnetProcessing
&& ( iter
-> first
== EDNSOptionCode :: ECS
)) { // 'EDNS SUBNET'
539 if ( getEDNSSubnetOptsFromString ( iter
-> second
, & d_eso
)) {
540 //cerr<<"Parsed, source: "<<d_eso.source.toString()<<", scope: "<<d_eso.scope.toString()<<", family = "<<d_eso.scope.getNetwork().sin4.sin_family<<endl;
541 d_haveednssubnet
= true ;
545 // cerr<<"Have an option #"<<iter->first<<": "<<makeHexDump(iter->second)<<endl;
548 d_ednsversion
= edo
. d_version
;
549 d_ednsrcode
= edo
. d_extRCode
;
553 d_ednsRawPacketSizeLimit
=- 1 ;
556 memcpy (( void *)& d
,( const void *) d_rawpacket
. c_str (), 12 );
558 // if(!qdomain.empty()) // strip dot
559 // boost::erase_tail(qdomain, 1);
561 if (! ntohs ( d
. qdcount
)) {
563 g_log
<< Logger :: Debug
<< "No question section in packet from " << getRemote () << ", RCode=" << RCode :: to_s ( d
. rcode
)<< endl
;
571 d_trc
= TSIGRecordContent ();
575 catch ( std :: exception
& e
) {
579 unsigned int DNSPacket :: getMaxReplyLen ()
581 return d_maxreplylen
;
584 void DNSPacket :: setMaxReplyLen ( int bytes
)
589 //! Use this to set where this packet was received from or should be sent to
590 void DNSPacket :: setRemote ( const ComboAddress
* s
)
595 bool DNSPacket :: hasEDNSSubnet () const
597 return d_haveednssubnet
;
600 bool DNSPacket :: hasEDNS () const
602 return d_haveednssection
;
605 Netmask
DNSPacket :: getRealRemote () const
609 return Netmask ( d_remote
);
612 void DNSPacket :: setSocket ( Utility :: sock_t sock
)
617 void DNSPacket :: commitD ()
619 d_rawpacket
. replace ( 0 , 12 ,( char *)& d
, 12 ); // copy in d
622 bool DNSPacket :: checkForCorrectTSIG ( UeberBackend
* B
, DNSName
* keyname
, string
* secret
, TSIGRecordContent
* trc
) const
626 if (! this -> getTSIGDetails ( trc
, keyname
, & tsigPos
)) {
632 tt
. algo
= trc
-> d_algoName
;
633 if ( tt
. algo
== DNSName ( "hmac-md5.sig-alg.reg.int" ))
634 tt
. algo
= DNSName ( "hmac-md5" );
637 if ( tt
. algo
!= DNSName ( "gss-tsig" )) {
638 if (! B
-> getTSIGKey (* keyname
, & tt
. algo
, & secret64
)) {
639 g_log
<< Logger :: Error
<< "Packet for domain '" << this -> qdomain
<< "' denied: can't find TSIG key with name '" <<* keyname
<< "' and algorithm '" << tt
. algo
<< "'" << endl
;
642 B64Decode ( secret64
, * secret
);
649 result
= validateTSIG ( d_rawpacket
, tsigPos
, tt
, * trc
, "" , trc
-> d_mac
, false );
651 catch ( const std :: runtime_error
& err
) {
652 g_log
<< Logger :: Error
<< "Packet for '" << this -> qdomain
<< "' denied: " << err
. what ()<< endl
;
659 const DNSName
& DNSPacket :: getTSIGKeyname () const {
660 return d_tsigkeyname
;