]>
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
) {
323 pw
. getHeader ()-> tc
= 1 ;
329 // if(!pw.getHeader()->tc) // protect against double commit from addSignature
331 if (! d_rrs
. empty ()) pw
. commit ();
335 if ( d_haveednssubnet
) {
336 EDNSSubnetOpts eso
= d_eso
;
337 eso
. scope
= Netmask ( eso
. source
. getNetwork (), maxScopeMask
);
339 string opt
= makeEDNSSubnetOptsString ( eso
);
340 opts
. push_back ( make_pair ( 8 , opt
)); // 'EDNS SUBNET'
343 if (! opts
. empty () || d_haveednssection
|| d_dnssecOk
)
345 pw
. addOpt ( s_udpTruncationThreshold
, d_ednsrcode
, d_dnssecOk
? EDNSOpts :: DNSSECOK
: 0 , opts
);
349 catch ( std :: exception
& e
) {
350 g_log
<< Logger :: Warning
<< "Exception: " << e
. what ()<< endl
;
355 if ( d_trc
. d_algoName
. countLabels ())
356 addTSIG ( pw
, d_trc
, d_tsigkeyname
, d_tsigsecret
, d_tsigprevious
, d_tsigtimersonly
);
358 d_rawpacket
. assign (( char *)& packet
[ 0 ], packet
. size ()); // XXX we could do this natively on a vector..
360 // copy RR counts so they can be read later
361 d
. qdcount
= pw
. getHeader ()-> qdcount
;
362 d
. ancount
= pw
. getHeader ()-> ancount
;
363 d
. nscount
= pw
. getHeader ()-> nscount
;
364 d
. arcount
= pw
. getHeader ()-> arcount
;
367 void DNSPacket :: setQuestion ( int op
, const DNSName
& qd
, int newqtype
)
369 memset (& d
, 0 , sizeof ( d
));
370 d
. id
= dns_random ( 0xffff );
371 d
. rd
= d
. tc
= d
. aa
= false ;
373 d
. qdcount
= 1 ; // is htons'ed later on
374 d
. ancount
= d
. arcount
= d
. nscount
= 0 ;
380 /** convenience function for creating a reply packet from a question packet. Do not forget to delete it after use! */
381 DNSPacket
* DNSPacket :: replyPacket () const
383 DNSPacket
* r
= new DNSPacket ( false );
384 r
-> setSocket ( d_socket
);
385 r
-> d_anyLocal
= d_anyLocal
;
386 r
-> setRemote (& d_remote
);
387 r
-> setAnswer ( true ); // this implies the allocation of the header
388 r
-> setA ( true ); // and we are authoritative
389 r
-> setRA ( 0 ); // no recursion available
390 r
-> setRD ( d
. rd
); // if you wanted to recurse, answer will say you wanted it
392 r
-> setOpcode ( d
. opcode
);
397 r
-> qdomain
= qdomain
;
400 r
-> d_maxreplylen
= d_maxreplylen
;
401 r
-> d_wantsnsid
= d_wantsnsid
;
402 r
-> d_dnssecOk
= d_dnssecOk
;
404 r
-> d_haveednssubnet
= d_haveednssubnet
;
405 r
-> d_haveednssection
= d_haveednssection
;
406 r
-> d_ednsversion
= 0 ;
409 if ( d_tsigkeyname
. countLabels ()) {
410 r
-> d_tsigkeyname
= d_tsigkeyname
;
411 r
-> d_tsigprevious
= d_tsigprevious
;
413 r
-> d_tsigsecret
= d_tsigsecret
;
414 r
-> d_tsigtimersonly
= d_tsigtimersonly
;
416 r
-> d_havetsig
= d_havetsig
;
420 void DNSPacket :: spoofQuestion ( const DNSPacket
* qd
)
422 d_wrapped
= true ; // if we do this, don't later on wrapup
425 string :: size_type i
= sizeof ( d
);
428 labellen
= qd
-> d_rawpacket
[ i
];
431 d_rawpacket
. replace ( i
, labellen
, qd
-> d_rawpacket
, i
, labellen
);
436 int DNSPacket :: noparse ( const char * mesg
, size_t length
)
438 d_rawpacket
. assign ( mesg
, length
);
440 g_log
<< Logger :: Debug
<< "Ignoring packet: too short (" << length
<< " < 12) from "
441 << d_remote
. toStringWithPort ()<< endl
;
446 memcpy (( void *)& d
,( const void *) d_rawpacket
. c_str (), 12 );
450 void DNSPacket :: setTSIGDetails ( const TSIGRecordContent
& tr
, const DNSName
& keyname
, const string
& secret
, const string
& previous
, bool timersonly
)
453 d_trc
. d_origID
= ((( d
. id
& 0xFF )<< 8 ) | (( d
. id
& 0xFF00 )>> 8 ));
454 d_tsigkeyname
= keyname
;
455 d_tsigsecret
= secret
;
456 d_tsigprevious
= previous
;
457 d_tsigtimersonly
= timersonly
;
460 bool DNSPacket :: getTSIGDetails ( TSIGRecordContent
* trc
, DNSName
* keyname
, uint16_t * tsigPosOut
) const
462 MOADNSParser
mdp ( d_isQuery
, d_rawpacket
);
463 uint16_t tsigPos
= mdp
. getTSIGPos ();
468 for ( MOADNSParser :: answers_t :: const_iterator i
= mdp
. d_answers
. begin (); i
!= mdp
. d_answers
. end (); ++ i
) {
469 if ( i
-> first
. d_type
== QType :: TSIG
&& i
-> first
. d_class
== QType :: ANY
) {
470 // cast can fail, f.e. if d_content is an UnknownRecordContent.
471 shared_ptr
< TSIGRecordContent
> content
= std :: dynamic_pointer_cast
< TSIGRecordContent
>( i
-> first
. d_content
);
473 g_log
<< Logger :: Error
<< "TSIG record has no or invalid content (invalid packet)" << endl
;
477 * keyname
= i
-> first
. d_name
;
485 * tsigPosOut
= tsigPos
;
491 bool DNSPacket :: getTKEYRecord ( TKEYRecordContent
* tr
, DNSName
* keyname
) const
493 MOADNSParser
mdp ( d_isQuery
, d_rawpacket
);
496 for ( MOADNSParser :: answers_t :: const_iterator i
= mdp
. d_answers
. begin (); i
!= mdp
. d_answers
. end (); ++ i
) {
498 g_log
<< Logger :: Error
<< "More than one TKEY record found in query" << endl
;
502 if ( i
-> first
. d_type
== QType :: TKEY
) {
503 // cast can fail, f.e. if d_content is an UnknownRecordContent.
504 shared_ptr
< TKEYRecordContent
> content
= std :: dynamic_pointer_cast
< TKEYRecordContent
>( i
-> first
. d_content
);
506 g_log
<< Logger :: Error
<< "TKEY record has no or invalid content (invalid packet)" << endl
;
510 * keyname
= i
-> first
. d_name
;
518 /** This function takes data from the network, possibly received with recvfrom, and parses
519 it into our class. Results of calling this function multiple times on one packet are
520 unknown. Returns -1 if the packet cannot be parsed.
522 int DNSPacket :: parse ( const char * mesg
, size_t length
)
525 d_rawpacket
. assign ( mesg
, length
);
528 g_log
<< Logger :: Debug
<< "Ignoring packet: too short from "
529 << getRemote () << endl
;
533 MOADNSParser
mdp ( d_isQuery
, d_rawpacket
);
536 // ANY OPTION WHICH *MIGHT* BE SET DOWN BELOW SHOULD BE CLEARED FIRST!
540 d_havetsig
= mdp
. getTSIGPos ();
541 d_haveednssubnet
= false ;
542 d_haveednssection
= false ;
544 if ( getEDNSOpts ( mdp
, & edo
)) {
545 d_haveednssection
= true ;
547 "Values lower than 512 MUST be treated as equal to 512."
549 d_ednsRawPacketSizeLimit
= edo
. d_packetsize
;
550 d_maxreplylen
= std :: min ( std :: max ( static_cast < uint16_t >( 512 ), edo
. d_packetsize
), s_udpTruncationThreshold
);
551 // cerr<<edo.d_extFlags<<endl;
552 if ( edo
. d_extFlags
& EDNSOpts :: DNSSECOK
)
555 for ( vector
< pair
< uint16_t , string
> >:: const_iterator iter
= edo
. d_options
. begin ();
556 iter
!= edo
. d_options
. end ();
558 if ( iter
-> first
== EDNSOptionCode :: NSID
) {
561 else if ( s_doEDNSSubnetProcessing
&& ( iter
-> first
== EDNSOptionCode :: ECS
)) { // 'EDNS SUBNET'
562 if ( getEDNSSubnetOptsFromString ( iter
-> second
, & d_eso
)) {
563 //cerr<<"Parsed, source: "<<d_eso.source.toString()<<", scope: "<<d_eso.scope.toString()<<", family = "<<d_eso.scope.getNetwork().sin4.sin_family<<endl;
564 d_haveednssubnet
= true ;
568 // cerr<<"Have an option #"<<iter->first<<": "<<makeHexDump(iter->second)<<endl;
571 d_ednsversion
= edo
. d_version
;
572 d_ednsrcode
= edo
. d_extRCode
;
576 d_ednsRawPacketSizeLimit
=- 1 ;
579 memcpy (( void *)& d
,( const void *) d_rawpacket
. c_str (), 12 );
581 // if(!qdomain.empty()) // strip dot
582 // boost::erase_tail(qdomain, 1);
584 if (! ntohs ( d
. qdcount
)) {
586 g_log
<< Logger :: Warning
<< "No question section in packet from " << getRemote () << ", error=" << RCode :: to_s ( d
. rcode
)<< endl
;
594 d_trc
= TSIGRecordContent ();
598 catch ( std :: exception
& e
) {
602 unsigned int DNSPacket :: getMaxReplyLen ()
604 return d_maxreplylen
;
607 void DNSPacket :: setMaxReplyLen ( int bytes
)
612 //! Use this to set where this packet was received from or should be sent to
613 void DNSPacket :: setRemote ( const ComboAddress
* s
)
618 bool DNSPacket :: hasEDNSSubnet () const
620 return d_haveednssubnet
;
623 bool DNSPacket :: hasEDNS ()
625 return d_haveednssection
;
628 Netmask
DNSPacket :: getRealRemote () const
632 return Netmask ( d_remote
);
635 void DNSPacket :: setSocket ( Utility :: sock_t sock
)
640 void DNSPacket :: commitD ()
642 d_rawpacket
. replace ( 0 , 12 ,( char *)& d
, 12 ); // copy in d
645 bool DNSPacket :: checkForCorrectTSIG ( UeberBackend
* B
, DNSName
* keyname
, string
* secret
, TSIGRecordContent
* trc
) const
649 if (! this -> getTSIGDetails ( trc
, keyname
, & tsigPos
)) {
655 tt
. algo
= trc
-> d_algoName
;
656 if ( tt
. algo
== DNSName ( "hmac-md5.sig-alg.reg.int" ))
657 tt
. algo
= DNSName ( "hmac-md5" );
660 if ( tt
. algo
!= DNSName ( "gss-tsig" )) {
661 if (! B
-> getTSIGKey (* keyname
, & tt
. algo
, & secret64
)) {
662 g_log
<< Logger :: Error
<< "Packet for domain '" << this -> qdomain
<< "' denied: can't find TSIG key with name '" <<* keyname
<< "' and algorithm '" << tt
. algo
<< "'" << endl
;
665 B64Decode ( secret64
, * secret
);
672 result
= validateTSIG ( d_rawpacket
, tsigPos
, tt
, * trc
, "" , trc
-> d_mac
, false );
674 catch ( const std :: runtime_error
& err
) {
675 g_log
<< Logger :: Error
<< "Packet for '" << this -> qdomain
<< "' denied: " << err
. what ()<< endl
;
682 const DNSName
& DNSPacket :: getTSIGKeyname () const {
683 return d_tsigkeyname
;