1 // Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 #include <config.h>
8
9 #include <stdint.h>
10
11 #include <cassert>
12
13 #include <boost/lexical_cast.hpp>
14
15 #include <exceptions/exceptions.h>
16
17 #include <dns/edns.h>
18 #include <dns/exceptions.h>
19 #include <dns/message.h>
20 #include <dns/messagerenderer.h>
21 #include <dns/name.h>
22 #include <dns/rdata.h>
23 #include <dns/rdataclass.h>
24 #include <dns/rrclass.h>
25 #include <dns/rrttl.h>
26 #include <dns/rrtype.h>
27
28 using namespace std;
29 using boost::lexical_cast;
30 using namespace isc::dns::rdata;
31 using namespace isc::util;
32
33 namespace isc {
34 namespace dns {
35
36 namespace {
37 // This diagram shows the wire-format representation of the TTL field of
38 // OPT RR and its relationship with implementation specific parameters.
39 //
40 // 0 7 15 31
41 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42 // | EXTENDED-RCODE| VERSION |D| Z |
43 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44 // <= VERSION_SHIFT (16 bits)
45 // <= EXTRCODE_SHIFT (24 bits)
46 //EXTFLAG_DO:0 0 0 ....................... 0 1 0 0 0 0.....................0
47 //VER_MASK: 0 0 0 ........0 1 1 1 1 1 1 1 1 0 0 ..........................0
48
49 const unsigned int VERSION_SHIFT = 16;
50 const unsigned int EXTRCODE_SHIFT = 24;
51 const uint32_t VERSION_MASK = 0x00ff0000;
52 const uint32_t EXTFLAG_DO = 0x00008000;
53 }
54
EDNS(const uint8_t version)55 EDNS::EDNS(const uint8_t version) :
56 version_(version),
57 udp_size_(Message::DEFAULT_MAX_UDPSIZE),
58 dnssec_aware_(false)
59 {
60 if (version_ > SUPPORTED_VERSION) {
61 isc_throw(isc::InvalidParameter,
62 "failed to construct EDNS: unsupported version: " <<
63 static_cast<unsigned int>(version_));
64 }
65 }
66
EDNS(const Name & name,const RRClass & rrclass,const RRType & rrtype,const RRTTL & ttl,const Rdata &)67 EDNS::EDNS(const Name& name, const RRClass& rrclass, const RRType& rrtype,
68 const RRTTL& ttl, const Rdata&) :
69 version_((ttl.getValue() & VERSION_MASK) >> VERSION_SHIFT)
70 {
71 if (rrtype != RRType::OPT()) {
72 isc_throw(isc::InvalidParameter,
73 "EDNS is being created with incompatible RR type: "
74 << rrtype);
75 }
76
77 if (version_ > EDNS::SUPPORTED_VERSION) {
78 isc_throw(DNSMessageBADVERS, "unsupported EDNS version: " <<
79 static_cast<unsigned int>(version_));
80 }
81
82 if (name != Name::ROOT_NAME()) {
83 isc_throw(DNSMessageFORMERR, "invalid owner name for EDNS OPT RR: " <<
84 name);
85 }
86
87 dnssec_aware_ = ((ttl.getValue() & EXTFLAG_DO) != 0);
88 udp_size_ = rrclass.getCode();
89 }
90
91 string
toText() const92 EDNS::toText() const {
93 string ret = "; EDNS: version: ";
94
95 ret += lexical_cast<string>(static_cast<int>(getVersion()));
96 ret += ", flags:";
97 if (getDNSSECAwareness()) {
98 ret += " do";
99 }
100 ret += "; udp: " + lexical_cast<string>(getUDPSize()) + "\n";
101
102 return (ret);
103 }
104
105 namespace {
106 /// Helper function to define unified implementation for the public versions
107 /// of toWire().
108 template <typename Output>
109 int
toWireCommon(Output & output,const uint8_t version,const uint16_t udp_size,const bool dnssec_aware,const uint8_t extended_rcode)110 toWireCommon(Output& output, const uint8_t version,
111 const uint16_t udp_size, const bool dnssec_aware,
112 const uint8_t extended_rcode)
113 {
114 // Render EDNS OPT RR
115 uint32_t extrcode_flags = extended_rcode << EXTRCODE_SHIFT;
116 extrcode_flags |= (version << VERSION_SHIFT) & VERSION_MASK;
117 if (dnssec_aware) {
118 extrcode_flags |= EXTFLAG_DO;
119 }
120
121 // Construct an RRset corresponding to the EDNS.
122 // We don't support any options for now, so the OPT RR can be empty.
123 RRsetPtr edns_rrset(new RRset(Name::ROOT_NAME(), RRClass(udp_size),
124 RRType::OPT(), RRTTL(extrcode_flags)));
125 edns_rrset->addRdata(ConstRdataPtr(new generic::OPT()));
126
127 edns_rrset->toWire(output);
128
129 return (1);
130 }
131 }
132
133 unsigned int
toWire(AbstractMessageRenderer & renderer,const uint8_t extended_rcode) const134 EDNS::toWire(AbstractMessageRenderer& renderer,
135 const uint8_t extended_rcode) const
136 {
137 // If adding the OPT RR would exceed the size limit, don't do it.
138 // 11 = len(".") + type(2byte) + class(2byte) + TTL(4byte) + RDLEN(2byte)
139 // (RDATA is empty in this simple implementation)
140 if (renderer.getLength() + 11 > renderer.getLengthLimit()) {
141 return (0);
142 }
143
144 return (toWireCommon(renderer, version_, udp_size_, dnssec_aware_,
145 extended_rcode));
146 }
147
148 unsigned int
toWire(isc::util::OutputBuffer & buffer,const uint8_t extended_rcode) const149 EDNS::toWire(isc::util::OutputBuffer& buffer,
150 const uint8_t extended_rcode) const
151 {
152 return (toWireCommon(buffer, version_, udp_size_, dnssec_aware_,
153 extended_rcode));
154 }
155
156 EDNS*
createEDNSFromRR(const Name & name,const RRClass & rrclass,const RRType & rrtype,const RRTTL & ttl,const Rdata & rdata,uint8_t & extended_rcode)157 createEDNSFromRR(const Name& name, const RRClass& rrclass,
158 const RRType& rrtype, const RRTTL& ttl,
159 const Rdata& rdata,
160 uint8_t& extended_rcode)
161 {
162 // Create a new EDNS object first for exception guarantee.
163 EDNS* edns = new EDNS(name, rrclass, rrtype, ttl, rdata);
164
165 // At this point we can update extended_rcode safely.
166 extended_rcode = ttl.getValue() >> EXTRCODE_SHIFT;
167
168 return (edns);
169 }
170
171 ostream&
operator <<(std::ostream & os,const EDNS & edns)172 operator<<(std::ostream& os, const EDNS& edns) {
173 os << edns.toText();
174 return (os);
175 }
176
177 } // end of namespace dns
178 } // end of namespace isc
179