]>
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/algorithm/string.hpp>
36 #include "dnsseckeeper.hh"
38 #include "dnsbackend.hh"
39 #include "pdnsexception.hh"
40 #include "dnspacket.hh"
42 #include "arguments.hh"
43 #include "dnswriter.hh"
44 #include "dnsparser.hh"
45 #include "dnsrecords.hh"
46 #include "dnssecinfra.hh"
48 #include "ednssubnet.hh"
49 #include "gss_context.hh"
50 #include "dns_random.hh"
52 bool DNSPacket :: s_doEDNSSubnetProcessing
;
53 uint16_t DNSPacket :: s_udpTruncationThreshold
;
55 DNSPacket :: DNSPacket ( bool isQuery
)
61 d_haveednssubnet
= false ;
65 memset (& d
, 0 , sizeof ( d
));
67 d_tsig_algo
= TSIG_MD5
;
71 d_tsigtimersonly
= false ;
72 d_haveednssection
= false ;
76 const string
& DNSPacket :: getString ()
84 ComboAddress
DNSPacket :: getRemote () const
89 uint16_t DNSPacket :: getRemotePort () const
91 return d_remote
. sin4
. sin_port
;
94 DNSPacket :: DNSPacket ( const DNSPacket
& orig
)
96 DLOG ( L
<< "DNSPacket copy constructor called!" << endl
);
97 d_socket
= orig
. d_socket
;
98 d_remote
= orig
. d_remote
;
100 d_compress
= orig
. d_compress
;
104 qdomain
= orig
. qdomain
;
105 qdomainwild
= orig
. qdomainwild
;
106 qdomainzone
= orig
. qdomainzone
;
107 d_maxreplylen
= orig
. d_maxreplylen
;
108 d_ednsping
= orig
. d_ednsping
;
109 d_wantsnsid
= orig
. d_wantsnsid
;
110 d_anyLocal
= orig
. d_anyLocal
;
112 d_haveednssubnet
= orig
. d_haveednssubnet
;
113 d_haveednssection
= orig
. d_haveednssection
;
114 d_ednsversion
= orig
. d_ednsversion
;
115 d_ednsrcode
= orig
. d_ednsrcode
;
116 d_dnssecOk
= orig
. d_dnssecOk
;
119 d_tsigkeyname
= orig
. d_tsigkeyname
;
120 d_tsigprevious
= orig
. d_tsigprevious
;
121 d_tsigtimersonly
= orig
. d_tsigtimersonly
;
123 d_tsigsecret
= orig
. d_tsigsecret
;
125 d_havetsig
= orig
. d_havetsig
;
126 d_wrapped
= orig
. d_wrapped
;
128 d_rawpacket
= orig
. d_rawpacket
;
129 d_tsig_algo
= orig
. d_tsig_algo
;
133 void DNSPacket :: setRcode ( int v
)
138 void DNSPacket :: setAnswer ( bool b
)
141 d_rawpacket
. assign ( 12 ,( char ) 0 );
142 memset (( void *)& d
, 0 , sizeof ( d
));
148 void DNSPacket :: setA ( bool b
)
153 void DNSPacket :: setID ( uint16_t id
)
158 void DNSPacket :: setRA ( bool b
)
163 void DNSPacket :: setRD ( bool b
)
168 void DNSPacket :: setOpcode ( uint16_t opcode
)
174 void DNSPacket :: clearRecords ()
179 void DNSPacket :: addRecord ( const DNSZoneRecord
& rr
)
181 // this removes duplicates from the packet in case we are not compressing
182 // for AXFR, no such checking is performed!
183 // cerr<<"addrecord, content=["<<rr.content<<"]"<<endl;
185 for ( auto i
= d_rrs
. begin (); i
!= d_rrs
. end ();++ i
) {
186 if ( rr
. dr
== i
-> dr
) // XXX SUPER SLOW
191 // cerr<<"added to d_rrs"<<endl;
197 vector
< DNSZoneRecord
*> DNSPacket :: getAPRecords ()
199 vector
< DNSZoneRecord
*> arrs
;
201 for ( vector
< DNSZoneRecord
>:: iterator i
= d_rrs
. begin ();
205 if ( i
-> dr
. d_place
!= DNSResourceRecord :: ADDITIONAL
&&
206 ( i
-> dr
. d_type
== QType :: MX
||
207 i
-> dr
. d_type
== QType :: NS
||
208 i
-> dr
. d_type
== QType :: SRV
))
218 vector
< DNSZoneRecord
*> DNSPacket :: getAnswerRecords ()
220 vector
< DNSZoneRecord
*> arrs
;
222 for ( vector
< DNSZoneRecord
>:: iterator i
= d_rrs
. begin ();
226 if ( i
-> dr
. d_place
!= DNSResourceRecord :: ADDITIONAL
)
233 void DNSPacket :: setCompress ( bool compress
)
236 d_rawpacket
. reserve ( 65000 );
240 bool DNSPacket :: couldBeCached ()
242 return d_ednsping
. empty () && ! d_wantsnsid
&& qclass
== QClass :: IN
&& ! d_havetsig
;
245 unsigned int DNSPacket :: getMinTTL ()
247 unsigned int minttl
= UINT_MAX
;
248 for ( const DNSZoneRecord
& rr
: d_rrs
) {
249 if ( rr
. dr
. d_ttl
< minttl
)
250 minttl
= rr
. dr
. d_ttl
;
256 bool DNSPacket :: isEmpty ()
258 return ( d_rrs
. empty ());
261 /** Must be called before attempting to access getData(). This function stuffs all resource
262 * records found in rrs into the data buffer. It also frees resource records queued for us.
264 void DNSPacket :: wrapup ()
271 vector
< DNSZoneRecord
>:: iterator pos
;
273 // we now need to order rrs so that the different sections come at the right place
274 // we want a stable sort, based on the d_place field
276 stable_sort ( d_rrs
. begin (), d_rrs
. end (), []( const DNSZoneRecord
& a
, const DNSZoneRecord
& b
) {
277 return a
. dr
. d_place
< b
. dr
. d_place
;
279 static bool mustNotShuffle
= :: arg (). mustDo ( "no-shuffle" );
281 if (! d_tcp
&& ! mustNotShuffle
) {
286 vector
< uint8_t > packet
;
287 DNSPacketWriter
pw ( packet
, qdomain
, qtype
. getCode (), qclass
);
289 pw
. getHeader ()-> rcode
= d
. rcode
;
290 pw
. getHeader ()-> opcode
= d
. opcode
;
291 pw
. getHeader ()-> aa
= d
. aa
;
292 pw
. getHeader ()-> ra
= d
. ra
;
293 pw
. getHeader ()-> qr
= d
. qr
;
294 pw
. getHeader ()-> id
= d
. id
;
295 pw
. getHeader ()-> rd
= d
. rd
;
296 pw
. getHeader ()-> tc
= d
. tc
;
298 DNSPacketWriter :: optvect_t opts
;
300 const static string mode_server_id
=:: arg ()[ "server-id" ];
301 if ( mode_server_id
!= "disabled" ) {
302 opts
. push_back ( make_pair ( 3 , mode_server_id
));
306 if (! d_ednsping
. empty ()) {
307 opts
. push_back ( make_pair ( 4 , d_ednsping
));
311 if (! d_rrs
. empty () || ! opts
. empty () || d_haveednssubnet
|| d_haveednssection
) {
313 uint8_t maxScopeMask
= 0 ;
314 for ( pos
= d_rrs
. begin (); pos
< d_rrs
. end (); ++ pos
) {
315 // cerr<<"during wrapup, content=["<<pos->content<<"]"<<endl;
316 maxScopeMask
= max ( maxScopeMask
, pos
-> scopeMask
);
318 pw
. startRecord ( pos
-> dr
. d_name
, pos
-> dr
. d_type
, pos
-> dr
. d_ttl
, pos
-> dr
. d_class
, pos
-> dr
. d_place
);
319 pos
-> dr
. d_content
-> toPacket ( pw
);
320 if ( pw
. size () + 20U > ( d_tcp
? 65535 : getMaxReplyLen ())) { // 20 = room for EDNS0
322 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 string
makeEDNSSubnetOptsString ( const EDNSSubnetOpts
& eso
);
337 EDNSSubnetOpts eso
= d_eso
;
338 eso
. scope
= Netmask ( eso
. source
. getNetwork (), maxScopeMask
);
340 string opt
= makeEDNSSubnetOptsString ( eso
);
341 opts
. push_back ( make_pair ( 8 , opt
)); // 'EDNS SUBNET'
344 if (! opts
. empty () || d_haveednssection
|| d_dnssecOk
)
346 pw
. addOpt ( s_udpTruncationThreshold
, d_ednsrcode
, d_dnssecOk
? EDNSOpts :: DNSSECOK
: 0 , opts
);
350 catch ( std :: exception
& e
) {
351 L
<< Logger :: Warning
<< "Exception: " << e
. what ()<< endl
;
356 if ( d_trc
. d_algoName
. countLabels ())
357 addTSIG ( pw
, & d_trc
, d_tsigkeyname
, d_tsigsecret
, d_tsigprevious
, d_tsigtimersonly
);
359 d_rawpacket
. assign (( char *)& packet
[ 0 ], packet
. size ()); // XXX we could do this natively on a vector..
361 // copy RR counts so LPE can read them
362 d
. qdcount
= pw
. getHeader ()-> qdcount
;
363 d
. ancount
= pw
. getHeader ()-> ancount
;
364 d
. nscount
= pw
. getHeader ()-> nscount
;
365 d
. arcount
= pw
. getHeader ()-> arcount
;
368 void DNSPacket :: setQuestion ( int op
, const DNSName
& qd
, int newqtype
)
370 memset (& d
, 0 , sizeof ( d
));
371 d
. id
= dns_random ( 0xffff );
372 d
. rd
= d
. tc
= d
. aa
= false ;
374 d
. qdcount
= 1 ; // is htons'ed later on
375 d
. ancount
= d
. arcount
= d
. nscount
= 0 ;
381 /** convenience function for creating a reply packet from a question packet. Do not forget to delete it after use! */
382 DNSPacket
* DNSPacket :: replyPacket () const
384 DNSPacket
* r
= new DNSPacket ( false );
385 r
-> setSocket ( d_socket
);
386 r
-> d_anyLocal
= d_anyLocal
;
387 r
-> setRemote (& d_remote
);
388 r
-> setAnswer ( true ); // this implies the allocation of the header
389 r
-> setA ( true ); // and we are authoritative
390 r
-> setRA ( 0 ); // no recursion available
391 r
-> setRD ( d
. rd
); // if you wanted to recurse, answer will say you wanted it
393 r
-> setOpcode ( d
. opcode
);
398 r
-> qdomain
= qdomain
;
401 r
-> d_maxreplylen
= d_maxreplylen
;
402 r
-> d_ednsping
= d_ednsping
;
403 r
-> d_wantsnsid
= d_wantsnsid
;
404 r
-> d_dnssecOk
= d_dnssecOk
;
406 r
-> d_haveednssubnet
= d_haveednssubnet
;
407 r
-> d_haveednssection
= d_haveednssection
;
408 r
-> d_ednsversion
= 0 ;
411 if ( d_tsigkeyname
. countLabels ()) {
412 r
-> d_tsigkeyname
= d_tsigkeyname
;
413 r
-> d_tsigprevious
= d_tsigprevious
;
415 r
-> d_tsigsecret
= d_tsigsecret
;
416 r
-> d_tsigtimersonly
= d_tsigtimersonly
;
418 r
-> d_havetsig
= d_havetsig
;
422 void DNSPacket :: spoofQuestion ( const DNSPacket
* qd
)
424 d_wrapped
= true ; // if we do this, don't later on wrapup
427 string :: size_type i
= sizeof ( d
);
430 labellen
= qd
-> d_rawpacket
[ i
];
433 d_rawpacket
. replace ( i
, labellen
, qd
-> d_rawpacket
, i
, labellen
);
438 int DNSPacket :: noparse ( const char * mesg
, size_t length
)
440 d_rawpacket
. assign ( mesg
, length
);
442 L
<< Logger :: Warning
<< "Ignoring packet: too short (" << length
<< " < 12) from "
443 << d_remote
. toStringWithPort ()<< endl
;
449 memcpy (( void *)& d
,( const void *) d_rawpacket
. c_str (), 12 );
453 void DNSPacket :: setTSIGDetails ( const TSIGRecordContent
& tr
, const DNSName
& keyname
, const string
& secret
, const string
& previous
, bool timersonly
)
456 d_trc
. d_origID
= ((( d
. id
& 0xFF )<< 8 ) | (( d
. id
& 0xFF00 )>> 8 ));
457 d_tsigkeyname
= keyname
;
458 d_tsigsecret
= secret
;
459 d_tsigprevious
= previous
;
460 d_tsigtimersonly
= timersonly
;
463 bool DNSPacket :: getTSIGDetails ( TSIGRecordContent
* trc
, DNSName
* keyname
, string
* message
) const
465 MOADNSParser
mdp ( d_isQuery
, d_rawpacket
);
467 if (! mdp
. getTSIGPos ())
471 for ( MOADNSParser :: answers_t :: const_iterator i
= mdp
. d_answers
. begin (); i
!= mdp
. d_answers
. end (); ++ i
) {
472 if ( i
-> first
. d_type
== QType :: TSIG
&& i
-> first
. d_class
== QType :: ANY
) {
473 // cast can fail, f.e. if d_content is an UnknownRecordContent.
474 shared_ptr
< TSIGRecordContent
> content
= std :: dynamic_pointer_cast
< TSIGRecordContent
>( i
-> first
. d_content
);
476 L
<< Logger :: Error
<< "TSIG record has no or invalid content (invalid packet)" << endl
;
480 * keyname
= i
-> first
. d_name
;
487 * message
= makeTSIGMessageFromTSIGPacket ( d_rawpacket
, mdp
. getTSIGPos (), * keyname
, * trc
, "" , false ); // if you change rawpacket to getString it breaks!
492 bool DNSPacket :: getTKEYRecord ( TKEYRecordContent
* tr
, DNSName
* keyname
) const
494 MOADNSParser
mdp ( d_isQuery
, d_rawpacket
);
497 for ( MOADNSParser :: answers_t :: const_iterator i
= mdp
. d_answers
. begin (); i
!= mdp
. d_answers
. end (); ++ i
) {
499 L
<< Logger :: Error
<< "More than one TKEY record found in query" << endl
;
503 if ( i
-> first
. d_type
== QType :: TKEY
) {
504 // cast can fail, f.e. if d_content is an UnknownRecordContent.
505 shared_ptr
< TKEYRecordContent
> content
= std :: dynamic_pointer_cast
< TKEYRecordContent
>( i
-> first
. d_content
);
507 L
<< Logger :: Error
<< "TKEY record has no or invalid content (invalid packet)" << endl
;
511 * keyname
= i
-> first
. d_name
;
519 /** This function takes data from the network, possibly received with recvfrom, and parses
520 it into our class. Results of calling this function multiple times on one packet are
521 unknown. Returns -1 if the packet cannot be parsed.
523 int DNSPacket :: parse ( const char * mesg
, size_t length
)
526 d_rawpacket
. assign ( mesg
, length
);
529 L
<< Logger :: Warning
<< "Ignoring packet: too short from "
530 << getRemote () << endl
;
534 MOADNSParser
mdp ( d_isQuery
, d_rawpacket
);
537 // ANY OPTION WHICH *MIGHT* BE SET DOWN BELOW SHOULD BE CLEARED FIRST!
542 d_havetsig
= mdp
. getTSIGPos ();
543 d_haveednssubnet
= false ;
544 d_haveednssection
= false ;
547 if ( getEDNSOpts ( mdp
, & edo
)) {
548 d_haveednssection
= true ;
549 d_maxreplylen
= std :: min ( edo
. d_packetsize
, s_udpTruncationThreshold
);
550 // cerr<<edo.d_Z<<endl;
551 if ( edo
. d_Z
& 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
== 3 ) { // 'EDNS NSID'
560 else if ( iter
-> first
== 5 ) { // 'EDNS PING'
561 d_ednsping
= iter
-> second
;
563 else if ( s_doEDNSSubnetProcessing
&& ( iter
-> first
== 8 )) { // 'EDNS SUBNET'
564 if ( getEDNSSubnetOptsFromString ( iter
-> second
, & d_eso
)) {
565 //cerr<<"Parsed, source: "<<d_eso.source.toString()<<", scope: "<<d_eso.scope.toString()<<", family = "<<d_eso.scope.getNetwork().sin4.sin_family<<endl;
566 d_haveednssubnet
= true ;
570 // cerr<<"Have an option #"<<iter->first<<": "<<makeHexDump(iter->second)<<endl;
573 d_ednsversion
= edo
. d_version
;
574 d_ednsrcode
= edo
. d_extRCode
;
580 memcpy (( void *)& d
,( const void *) d_rawpacket
. c_str (), 12 );
582 // if(!qdomain.empty()) // strip dot
583 // boost::erase_tail(qdomain, 1);
585 if (! ntohs ( d
. qdcount
)) {
587 L
<< Logger :: Warning
<< "No question section in packet from " << getRemote () << ", error=" << RCode :: to_s ( d
. rcode
)<< endl
;
596 catch ( std :: exception
& e
) {
600 unsigned int DNSPacket :: getMaxReplyLen ()
602 return d_maxreplylen
;
605 void DNSPacket :: setMaxReplyLen ( int bytes
)
610 //! Use this to set where this packet was received from or should be sent to
611 void DNSPacket :: setRemote ( const ComboAddress
* s
)
616 bool DNSPacket :: hasEDNSSubnet ()
618 return d_haveednssubnet
;
621 bool DNSPacket :: hasEDNS ()
623 return d_haveednssection
;
626 Netmask
DNSPacket :: getRealRemote () const
630 return Netmask ( d_remote
);
633 void DNSPacket :: setSocket ( Utility :: sock_t sock
)
638 void DNSPacket :: commitD ()
640 d_rawpacket
. replace ( 0 , 12 ,( char *)& d
, 12 ); // copy in d
643 bool checkForCorrectTSIG ( const DNSPacket
* q
, UeberBackend
* B
, DNSName
* keyname
, string
* secret
, TSIGRecordContent
* trc
)
647 if (! q
-> getTSIGDetails ( trc
, keyname
, & message
)) {
651 uint64_t delta
= std :: abs (( int64_t ) trc
-> d_time
- ( int64_t ) time ( 0 ));
652 if ( delta
> trc
-> d_fudge
) {
653 L
<< Logger :: Error
<< "Packet for '" << q
-> qdomain
<< "' denied: TSIG (key '" <<* keyname
<< "') time delta " << delta
<< " > 'fudge' " << trc
-> d_fudge
<< endl
;
657 DNSName algoName
= trc
-> d_algoName
; // FIXME400
658 if ( algoName
== DNSName ( "hmac-md5.sig-alg.reg.int" ))
659 algoName
= DNSName ( "hmac-md5" );
661 if ( algoName
== DNSName ( "gss-tsig" )) {
662 if (! gss_verify_signature (* keyname
, message
, trc
-> d_mac
)) {
663 L
<< Logger :: Error
<< "Packet for domain '" << q
-> qdomain
<< "' denied: TSIG signature mismatch using '" <<* keyname
<< "' and algorithm '" << trc
-> d_algoName
<< "'" << endl
;
670 if (! B
-> getTSIGKey (* keyname
, & algoName
, & secret64
)) {
671 L
<< Logger :: Error
<< "Packet for domain '" << q
-> qdomain
<< "' denied: can't find TSIG key with name '" <<* keyname
<< "' and algorithm '" << algoName
<< "'" << endl
;
674 if ( trc
-> d_algoName
== DNSName ( "hmac-md5" ))
675 trc
-> d_algoName
+= DNSName ( "sig-alg.reg.int" );
678 if (! getTSIGHashEnum ( trc
-> d_algoName
, algo
)) {
679 L
<< Logger :: Error
<< "Unsupported TSIG HMAC algorithm " << trc
-> d_algoName
. toString () << endl
;
683 B64Decode ( secret64
, * secret
);
684 bool result
= calculateHMAC (* secret
, message
, algo
) == trc
-> d_mac
;
686 L
<< Logger :: Error
<< "Packet for domain '" << q
-> qdomain
<< "' denied: TSIG signature mismatch using '" <<* keyname
<< "' and algorithm '" << trc
-> d_algoName
<< "'" << endl
;
692 const DNSName
& DNSPacket :: getTSIGKeyname () const {
693 return d_tsigkeyname
;