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 <exceptions/exceptions.h>
10 #include <util/buffer.h>
11 #include <dns/exceptions.h>
12 #include <dns/messagerenderer.h>
13 #include <dns/rdata.h>
14 #include <dns/rdataclass.h>
15 #include <dns/master_lexer.h>
16 #include <dns/master_loader.h>
17 
18 #include <stdint.h>
19 #include <string.h>
20 
21 #include <cerrno>
22 #include <cstring>
23 #include <string>
24 
25 #include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards
26 #include <sys/socket.h> // for AF_INET/AF_INET6
27 
28 using namespace std;
29 using namespace isc::util;
30 
31 // BEGIN_ISC_NAMESPACE
32 // BEGIN_RDATA_NAMESPACE
33 
34 namespace {
35 void
convertToIPv6Addr(const char * src,size_t src_len,void * dst)36 convertToIPv6Addr(const char* src, size_t src_len, void* dst) {
37     // See a_1.cc for this check.
38     if (src_len != strlen(src)) {
39         isc_throw(InvalidRdataText,
40                   "Bad IN/AAAA RDATA text: unexpected nul in string: '"
41                   << src << "'");
42     }
43     const int result = inet_pton(AF_INET6, src, dst);
44     if (result == 0) {
45         isc_throw(InvalidRdataText, "Bad IN/AAAA RDATA text: '" << src << "'");
46     } else if (result < 0) {
47         isc_throw(isc::Unexpected,
48                   "Unexpected failure in parsing IN/AAAA RDATA text: '"
49                   << src << "': " << std::strerror(errno));
50     }
51 }
52 }
53 
54 /// \brief Constructor from string.
55 ///
56 /// The given string must be a valid textual representation of an IPv6
57 /// address as specified in RFC1886.
58 ///
59 /// No extra character should be contained in \c addrstr other than the
60 /// textual address.  These include spaces and the nul character.
61 ///
62 /// \throw InvalidRdata The text extracted by the lexer isn't recognized as
63 /// a valid IPv6 address.
64 /// \throw Unexpected Unexpected system error in conversion (this should be
65 /// very rare).
66 ///
67 /// \param addrstr Textual representation of IPv6 address to be used as the
68 /// RDATA.
AAAA(const std::string & addrstr)69 AAAA::AAAA(const std::string& addrstr) {
70     convertToIPv6Addr(addrstr.c_str(), addrstr.size(), addr_);
71 }
72 
73 /// \brief Constructor with a context of MasterLexer.
74 ///
75 /// The \c lexer should point to the beginning of valid textual representation
76 /// of a class IN AAAA RDATA.
77 ///
78 /// The acceptable form of the textual address is generally the same as the
79 /// string version of the constructor, but this version is slightly more
80 /// flexible.  See the similar constructor of \c in::A class; the same
81 /// notes apply here.
82 ///
83 /// \throw MasterLexer::LexerError General parsing error such as missing field.
84 /// \throw InvalidRdata The text extracted by the lexer isn't recognized as
85 /// a valid IPv6 address.
86 /// \throw Unexpected Unexpected system error in conversion (this should be
87 /// very rare).
88 ///
89 /// \param lexer A \c MasterLexer object parsing a master file for the
90 /// RDATA to be created
AAAA(MasterLexer & lexer,const Name *,MasterLoader::Options,MasterLoaderCallbacks &)91 AAAA::AAAA(MasterLexer& lexer, const Name*,
92            MasterLoader::Options, MasterLoaderCallbacks&)
93 {
94     const MasterToken& token = lexer.getNextToken(MasterToken::STRING);
95     convertToIPv6Addr(token.getStringRegion().beg, token.getStringRegion().len,
96                       addr_);
97 }
98 
99 /// \brief Copy constructor.
AAAA(InputBuffer & buffer,size_t rdata_len)100 AAAA::AAAA(InputBuffer& buffer, size_t rdata_len) {
101     if (rdata_len != sizeof(addr_)) {
102         isc_throw(DNSMessageFORMERR,
103                   "IN/AAAA RDATA construction from wire failed: "
104                   "Invalid length: " << rdata_len);
105     }
106     if (buffer.getLength() - buffer.getPosition() < sizeof(addr_)) {
107         isc_throw(DNSMessageFORMERR,
108                   "IN/AAAA RDATA construction from wire failed: "
109                   "insufficient buffer length: "
110                   << buffer.getLength() - buffer.getPosition());
111     }
112     buffer.readData(&addr_, sizeof(addr_));
113 }
114 
AAAA(const AAAA & other)115 AAAA::AAAA(const AAAA& other) : Rdata() {
116     memcpy(addr_, other.addr_, sizeof(addr_));
117 }
118 
119 /// \brief Return a textual form of the underlying IPv6 address of the RDATA.
120 void
toWire(OutputBuffer & buffer) const121 AAAA::toWire(OutputBuffer& buffer) const {
122     buffer.writeData(&addr_, sizeof(addr_));
123 }
124 
125 void
toWire(AbstractMessageRenderer & renderer) const126 AAAA::toWire(AbstractMessageRenderer& renderer) const {
127     renderer.writeData(&addr_, sizeof(addr_));
128 }
129 
130 string
toText() const131 AAAA::toText() const {
132     char addr_string[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
133 
134     if (inet_ntop(AF_INET6, &addr_, addr_string, sizeof(addr_string))
135         == NULL) {
136         isc_throw(Unexpected,
137                   "Failed to convert IN/AAAA RDATA to textual IPv6 address");
138     }
139 
140     return (string(addr_string));
141 }
142 
143 /// \brief Compare two in::AAAA RDATAs.
144 ///
145 /// In effect, it compares the two RDATA as an unsigned 128-bit integer.
146 int
compare(const Rdata & other) const147 AAAA::compare(const Rdata& other) const {
148     const AAAA& other_a = dynamic_cast<const AAAA&>(other);
149     return (memcmp(&addr_, &other_a.addr_, sizeof(addr_)));
150 }
151 
152 // END_RDATA_NAMESPACE
153 // END_ISC_NAMESPACE
154