]>
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>
32 #include <boost/tokenizer.hpp>
33 #include <boost/functional/hash.hpp>
34 #include <boost/algorithm/string.hpp>
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"
54 bool DNSPacket :: s_doEDNSSubnetProcessing
;
55 uint16_t DNSPacket :: s_udpTruncationThreshold
;
57 DNSPacket :: DNSPacket ( bool isQuery
)
63 d_haveednssubnet
= false ;
67 memset (& d
, 0 , sizeof ( d
));
69 d_tsig_algo
= TSIG_MD5
;
73 d_tsigtimersonly
= false ;
74 d_haveednssection
= false ;
78 const string
& DNSPacket :: getString ()
86 ComboAddress
DNSPacket :: getRemote () const
91 uint16_t DNSPacket :: getRemotePort () const
93 return d_remote
. sin4
. sin_port
;
96 DNSPacket :: DNSPacket ( const DNSPacket
& orig
)
98 DLOG ( g_log
<< "DNSPacket copy constructor called!" << endl
);
99 d_socket
= orig
. d_socket
;
100 d_remote
= orig
. d_remote
;
102 d_compress
= orig
. d_compress
;
106 qdomain
= orig
. qdomain
;
107 qdomainwild
= orig
. qdomainwild
;
108 qdomainzone
= orig
. qdomainzone
;
109 d_maxreplylen
= orig
. d_maxreplylen
;
110 d_wantsnsid
= orig
. d_wantsnsid
;
111 d_anyLocal
= orig
. d_anyLocal
;
113 d_haveednssubnet
= orig
. d_haveednssubnet
;
114 d_haveednssection
= orig
. d_haveednssection
;
115 d_ednsversion
= orig
. d_ednsversion
;
116 d_ednsrcode
= orig
. d_ednsrcode
;
117 d_dnssecOk
= orig
. d_dnssecOk
;
120 d_tsigkeyname
= orig
. d_tsigkeyname
;
121 d_tsigprevious
= orig
. d_tsigprevious
;
122 d_tsigtimersonly
= orig
. d_tsigtimersonly
;
124 d_tsigsecret
= orig
. d_tsigsecret
;
125 d_ednsRawPacketSizeLimit
= orig
. d_ednsRawPacketSizeLimit
;
126 d_havetsig
= orig
. d_havetsig
;
127 d_wrapped
= orig
. d_wrapped
;
129 d_rawpacket
= orig
. d_rawpacket
;
130 d_tsig_algo
= orig
. d_tsig_algo
;
133 d_isQuery
= orig
. d_isQuery
;
134 d_hash
= orig
. d_hash
;
137 void DNSPacket :: setRcode ( int v
)
142 void DNSPacket :: setAnswer ( bool b
)
145 d_rawpacket
. assign ( 12 ,( char ) 0 );
146 memset (( void *)& d
, 0 , sizeof ( d
));
152 void DNSPacket :: setA ( bool b
)
157 void DNSPacket :: setID ( uint16_t id
)
162 void DNSPacket :: setRA ( bool b
)
167 void DNSPacket :: setRD ( bool b
)
172 void DNSPacket :: setOpcode ( uint16_t opcode
)
177 void DNSPacket :: clearRecords ()
183 void DNSPacket :: addRecord ( const DNSZoneRecord
& rr
)
185 // this removes duplicates from the packet.
186 // in case we are not compressing for AXFR, no such checking is performed!
189 std :: string ser
= const_cast < DNSZoneRecord
&>( rr
). dr
. d_content
-> serialize ( rr
. dr
. d_name
);
190 auto hash
= boost :: hash
< std :: pair
< DNSName
, std :: string
> >()({ rr
. dr
. d_name
, ser
});
191 if ( d_dedup
. count ( hash
)) { // might be a dup
192 for ( auto i
= d_rrs
. begin (); i
!= d_rrs
. end ();++ i
) {
193 if ( rr
. dr
== i
-> dr
) // XXX SUPER SLOW
197 d_dedup
. insert ( hash
);
203 vector
< DNSZoneRecord
*> DNSPacket :: getAPRecords ()
205 vector
< DNSZoneRecord
*> arrs
;
207 for ( vector
< DNSZoneRecord
>:: iterator i
= d_rrs
. begin ();
211 if ( i
-> dr
. d_place
!= DNSResourceRecord :: ADDITIONAL
&&
212 ( i
-> dr
. d_type
== QType :: MX
||
213 i
-> dr
. d_type
== QType :: NS
||
214 i
-> dr
. d_type
== QType :: SRV
))
222 vector
< DNSZoneRecord
*> DNSPacket :: getAnswerRecords ()
224 vector
< DNSZoneRecord
*> arrs
;
226 for ( vector
< DNSZoneRecord
>:: iterator i
= d_rrs
. begin ();
230 if ( i
-> dr
. d_place
!= DNSResourceRecord :: ADDITIONAL
)
237 void DNSPacket :: setCompress ( bool compress
)
240 d_rawpacket
. reserve ( 65000 );
244 bool DNSPacket :: couldBeCached ()
246 return ! d_wantsnsid
&& qclass
== QClass :: IN
&& ! d_havetsig
;
249 unsigned int DNSPacket :: getMinTTL ()
251 unsigned int minttl
= UINT_MAX
;
252 for ( const DNSZoneRecord
& rr
: d_rrs
) {
253 if ( rr
. dr
. d_ttl
< minttl
)
254 minttl
= rr
. dr
. d_ttl
;
260 bool DNSPacket :: isEmpty ()
262 return ( d_rrs
. empty ());
265 /** Must be called before attempting to access getData(). This function stuffs all resource
266 * records found in rrs into the data buffer. It also frees resource records queued for us.
268 void DNSPacket :: wrapup ()
275 vector
< DNSZoneRecord
>:: iterator pos
;
277 // we now need to order rrs so that the different sections come at the right place
278 // we want a stable sort, based on the d_place field
280 stable_sort ( d_rrs
. begin (), d_rrs
. end (), []( const DNSZoneRecord
& a
, const DNSZoneRecord
& b
) {
281 return a
. dr
. d_place
< b
. dr
. d_place
;
283 static bool mustNotShuffle
= :: arg (). mustDo ( "no-shuffle" );
285 if (! d_tcp
&& ! mustNotShuffle
) {
290 vector
< uint8_t > packet
;
291 DNSPacketWriter
pw ( packet
, qdomain
, qtype
. getCode (), qclass
);
293 pw
. getHeader ()-> rcode
= d
. rcode
;
294 pw
. getHeader ()-> opcode
= d
. opcode
;
295 pw
. getHeader ()-> aa
= d
. aa
;
296 pw
. getHeader ()-> ra
= d
. ra
;
297 pw
. getHeader ()-> qr
= d
. qr
;
298 pw
. getHeader ()-> id
= d
. id
;
299 pw
. getHeader ()-> rd
= d
. rd
;
300 pw
. getHeader ()-> tc
= d
. tc
;
302 DNSPacketWriter :: optvect_t opts
;
304 const static string mode_server_id
=:: arg ()[ "server-id" ];
305 if ( mode_server_id
!= "disabled" ) {
306 opts
. push_back ( make_pair ( 3 , mode_server_id
));
310 if (! d_rrs
. empty () || ! opts
. empty () || d_haveednssubnet
|| d_haveednssection
) {
312 uint8_t maxScopeMask
= 0 ;
313 for ( pos
= d_rrs
. begin (); pos
< d_rrs
. end (); ++ pos
) {
314 // cerr<<"during wrapup, content=["<<pos->content<<"]"<<endl;
315 maxScopeMask
= max ( maxScopeMask
, pos
-> scopeMask
);
317 pw
. startRecord ( pos
-> dr
. d_name
, pos
-> dr
. d_type
, pos
-> dr
. d_ttl
, pos
-> dr
. d_class
, pos
-> dr
. d_place
);
318 pos
-> dr
. d_content
-> toPacket ( pw
);
319 if ( pw
. size () + 20U > ( d_tcp
? 65535 : getMaxReplyLen ())) { // 20 = room for EDNS0
321 if ( pos
-> dr
. d_place
== DNSResourceRecord :: ANSWER
|| pos
-> dr
. d_place
== DNSResourceRecord :: AUTHORITY
) {
322 pw
. getHeader ()-> tc
= 1 ;
328 // if(!pw.getHeader()->tc) // protect against double commit from addSignature
330 if (! d_rrs
. empty ()) pw
. commit ();
334 if ( d_haveednssubnet
) {
335 EDNSSubnetOpts eso
= d_eso
;
336 eso
. scope
= Netmask ( eso
. source
. getNetwork (), maxScopeMask
);
338 string opt
= makeEDNSSubnetOptsString ( eso
);
339 opts
. push_back ( make_pair ( 8 , opt
)); // 'EDNS SUBNET'
342 if (! opts
. empty () || d_haveednssection
|| d_dnssecOk
)
344 pw
. addOpt ( s_udpTruncationThreshold
, d_ednsrcode
, d_dnssecOk
? EDNSOpts :: DNSSECOK
: 0 , opts
);
348 catch ( std :: exception
& e
) {
349 g_log
<< Logger :: Warning
<< "Exception: " << e
. what ()<< endl
;
354 if ( d_trc
. d_algoName
. countLabels ())
355 addTSIG ( pw
, d_trc
, d_tsigkeyname
, d_tsigsecret
, d_tsigprevious
, d_tsigtimersonly
);
357 d_rawpacket
. assign (( char *)& packet
[ 0 ], packet
. size ()); // XXX we could do this natively on a vector..
359 // copy RR counts so they can be read later
360 d
. qdcount
= pw
. getHeader ()-> qdcount
;
361 d
. ancount
= pw
. getHeader ()-> ancount
;
362 d
. nscount
= pw
. getHeader ()-> nscount
;
363 d
. arcount
= pw
. getHeader ()-> arcount
;
366 void DNSPacket :: setQuestion ( int op
, const DNSName
& qd
, int newqtype
)
368 memset (& d
, 0 , sizeof ( d
));
369 d
. id
= dns_random ( 0xffff );
370 d
. rd
= d
. tc
= d
. aa
= false ;
372 d
. qdcount
= 1 ; // is htons'ed later on
373 d
. ancount
= d
. arcount
= d
. nscount
= 0 ;
379 /** convenience function for creating a reply packet from a question packet. Do not forget to delete it after use! */
380 DNSPacket
* DNSPacket :: replyPacket () const
382 DNSPacket
* r
= new DNSPacket ( false );
383 r
-> setSocket ( d_socket
);
384 r
-> d_anyLocal
= d_anyLocal
;
385 r
-> setRemote (& d_remote
);
386 r
-> setAnswer ( true ); // this implies the allocation of the header
387 r
-> setA ( true ); // and we are authoritative
388 r
-> setRA ( 0 ); // no recursion available
389 r
-> setRD ( d
. rd
); // if you wanted to recurse, answer will say you wanted it
391 r
-> setOpcode ( d
. opcode
);
396 r
-> qdomain
= qdomain
;
399 r
-> d_maxreplylen
= d_maxreplylen
;
400 r
-> d_wantsnsid
= d_wantsnsid
;
401 r
-> d_dnssecOk
= d_dnssecOk
;
403 r
-> d_haveednssubnet
= d_haveednssubnet
;
404 r
-> d_haveednssection
= d_haveednssection
;
405 r
-> d_ednsversion
= 0 ;
408 if ( d_tsigkeyname
. countLabels ()) {
409 r
-> d_tsigkeyname
= d_tsigkeyname
;
410 r
-> d_tsigprevious
= d_tsigprevious
;
412 r
-> d_tsigsecret
= d_tsigsecret
;
413 r
-> d_tsigtimersonly
= d_tsigtimersonly
;
415 r
-> d_havetsig
= d_havetsig
;
419 void DNSPacket :: spoofQuestion ( const DNSPacket
* qd
)
421 d_wrapped
= true ; // if we do this, don't later on wrapup
424 string :: size_type i
= sizeof ( d
);
427 labellen
= qd
-> d_rawpacket
[ i
];
430 d_rawpacket
. replace ( i
, labellen
, qd
-> d_rawpacket
, i
, labellen
);
435 int DNSPacket :: noparse ( const char * mesg
, size_t length
)
437 d_rawpacket
. assign ( mesg
, length
);
439 g_log
<< Logger :: Debug
<< "Ignoring packet: too short (" << length
<< " < 12) from "
440 << d_remote
. toStringWithPort ()<< endl
;
445 memcpy (( void *)& d
,( const void *) d_rawpacket
. c_str (), 12 );
449 void DNSPacket :: setTSIGDetails ( const TSIGRecordContent
& tr
, const DNSName
& keyname
, const string
& secret
, const string
& previous
, bool timersonly
)
452 d_trc
. d_origID
= ((( d
. id
& 0xFF )<< 8 ) | (( d
. id
& 0xFF00 )>> 8 ));
453 d_tsigkeyname
= keyname
;
454 d_tsigsecret
= secret
;
455 d_tsigprevious
= previous
;
456 d_tsigtimersonly
= timersonly
;
459 bool DNSPacket :: getTSIGDetails ( TSIGRecordContent
* trc
, DNSName
* keyname
, uint16_t * tsigPosOut
) const
461 MOADNSParser
mdp ( d_isQuery
, d_rawpacket
);
462 uint16_t tsigPos
= mdp
. getTSIGPos ();
467 for ( MOADNSParser :: answers_t :: const_iterator i
= mdp
. d_answers
. begin (); i
!= mdp
. d_answers
. end (); ++ i
) {
468 if ( i
-> first
. d_type
== QType :: TSIG
&& i
-> first
. d_class
== QType :: ANY
) {
469 // cast can fail, f.e. if d_content is an UnknownRecordContent.
470 shared_ptr
< TSIGRecordContent
> content
= std :: dynamic_pointer_cast
< TSIGRecordContent
>( i
-> first
. d_content
);
472 g_log
<< Logger :: Error
<< "TSIG record has no or invalid content (invalid packet)" << endl
;
476 * keyname
= i
-> first
. d_name
;
484 * tsigPosOut
= tsigPos
;
490 bool DNSPacket :: getTKEYRecord ( TKEYRecordContent
* tr
, DNSName
* keyname
) const
492 MOADNSParser
mdp ( d_isQuery
, d_rawpacket
);
495 for ( MOADNSParser :: answers_t :: const_iterator i
= mdp
. d_answers
. begin (); i
!= mdp
. d_answers
. end (); ++ i
) {
497 g_log
<< Logger :: Error
<< "More than one TKEY record found in query" << endl
;
501 if ( i
-> first
. d_type
== QType :: TKEY
) {
502 // cast can fail, f.e. if d_content is an UnknownRecordContent.
503 shared_ptr
< TKEYRecordContent
> content
= std :: dynamic_pointer_cast
< TKEYRecordContent
>( i
-> first
. d_content
);
505 g_log
<< Logger :: Error
<< "TKEY record has no or invalid content (invalid packet)" << endl
;
509 * keyname
= i
-> first
. d_name
;
517 /** This function takes data from the network, possibly received with recvfrom, and parses
518 it into our class. Results of calling this function multiple times on one packet are
519 unknown. Returns -1 if the packet cannot be parsed.
521 int DNSPacket :: parse ( const char * mesg
, size_t length
)
524 d_rawpacket
. assign ( mesg
, length
);
527 g_log
<< Logger :: Debug
<< "Ignoring packet: too short from "
528 << getRemote () << endl
;
532 MOADNSParser
mdp ( d_isQuery
, d_rawpacket
);
535 // ANY OPTION WHICH *MIGHT* BE SET DOWN BELOW SHOULD BE CLEARED FIRST!
539 d_havetsig
= mdp
. getTSIGPos ();
540 d_haveednssubnet
= false ;
541 d_haveednssection
= false ;
543 if ( getEDNSOpts ( mdp
, & edo
)) {
544 d_haveednssection
= true ;
546 "Values lower than 512 MUST be treated as equal to 512."
548 d_ednsRawPacketSizeLimit
= edo
. d_packetsize
;
549 d_maxreplylen
= std :: min ( std :: max ( static_cast < uint16_t >( 512 ), edo
. d_packetsize
), s_udpTruncationThreshold
);
550 // cerr<<edo.d_extFlags<<endl;
551 if ( edo
. d_extFlags
& EDNSOpts :: DNSSECOK
)
554 for ( vector
< pair
< uint16_t , string
> >:: const_iterator iter
= edo
. d_options
. begin ();
555 iter
!= edo
. d_options
. end ();
557 if ( iter
-> first
== EDNSOptionCode :: NSID
) {
560 else if ( s_doEDNSSubnetProcessing
&& ( iter
-> first
== EDNSOptionCode :: ECS
)) { // 'EDNS SUBNET'
561 if ( getEDNSSubnetOptsFromString ( iter
-> second
, & d_eso
)) {
562 //cerr<<"Parsed, source: "<<d_eso.source.toString()<<", scope: "<<d_eso.scope.toString()<<", family = "<<d_eso.scope.getNetwork().sin4.sin_family<<endl;
563 d_haveednssubnet
= true ;
567 // cerr<<"Have an option #"<<iter->first<<": "<<makeHexDump(iter->second)<<endl;
570 d_ednsversion
= edo
. d_version
;
571 d_ednsrcode
= edo
. d_extRCode
;
575 d_ednsRawPacketSizeLimit
=- 1 ;
578 memcpy (( void *)& d
,( const void *) d_rawpacket
. c_str (), 12 );
580 // if(!qdomain.empty()) // strip dot
581 // boost::erase_tail(qdomain, 1);
583 if (! ntohs ( d
. qdcount
)) {
585 g_log
<< Logger :: Warning
<< "No question section in packet from " << getRemote () << ", error=" << RCode :: to_s ( d
. rcode
)<< endl
;
593 d_trc
= TSIGRecordContent ();
597 catch ( std :: exception
& e
) {
601 unsigned int DNSPacket :: getMaxReplyLen ()
603 return d_maxreplylen
;
606 void DNSPacket :: setMaxReplyLen ( int bytes
)
611 //! Use this to set where this packet was received from or should be sent to
612 void DNSPacket :: setRemote ( const ComboAddress
* s
)
617 bool DNSPacket :: hasEDNSSubnet () const
619 return d_haveednssubnet
;
622 bool DNSPacket :: hasEDNS ()
624 return d_haveednssection
;
627 Netmask
DNSPacket :: getRealRemote () const
631 return Netmask ( d_remote
);
634 void DNSPacket :: setSocket ( Utility :: sock_t sock
)
639 void DNSPacket :: commitD ()
641 d_rawpacket
. replace ( 0 , 12 ,( char *)& d
, 12 ); // copy in d
644 bool DNSPacket :: checkForCorrectTSIG ( UeberBackend
* B
, DNSName
* keyname
, string
* secret
, TSIGRecordContent
* trc
) const
648 if (! this -> getTSIGDetails ( trc
, keyname
, & tsigPos
)) {
654 tt
. algo
= trc
-> d_algoName
;
655 if ( tt
. algo
== DNSName ( "hmac-md5.sig-alg.reg.int" ))
656 tt
. algo
= DNSName ( "hmac-md5" );
659 if ( tt
. algo
!= DNSName ( "gss-tsig" )) {
660 if (! B
-> getTSIGKey (* keyname
, & tt
. algo
, & secret64
)) {
661 g_log
<< Logger :: Error
<< "Packet for domain '" << this -> qdomain
<< "' denied: can't find TSIG key with name '" <<* keyname
<< "' and algorithm '" << tt
. algo
<< "'" << endl
;
664 B64Decode ( secret64
, * secret
);
671 result
= validateTSIG ( d_rawpacket
, tsigPos
, tt
, * trc
, "" , trc
-> d_mac
, false );
673 catch ( const std :: runtime_error
& err
) {
674 g_log
<< Logger :: Error
<< "Packet for '" << this -> qdomain
<< "' denied: " << err
. what ()<< endl
;
681 const DNSName
& DNSPacket :: getTSIGKeyname () const {
682 return d_tsigkeyname
;