d2_cfg_mgr.cc 12.1 KB
Newer Older
1
// Copyright (C) 2014, 2015 Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15 16
#include <config.h>

17 18
#include <d2/d2_log.h>
#include <d2/d2_cfg_mgr.h>
19
#include <util/encode/hex.h>
20

21 22
#include <boost/foreach.hpp>

23 24 25
namespace isc {
namespace d2 {

26 27 28 29 30 31
namespace {

typedef std::vector<uint8_t> ByteAddress;

} // end of unnamed namespace

32 33
// *********************** D2CfgContext  *************************

34
D2CfgContext::D2CfgContext()
35 36
    : d2_params_(new D2Params()),
      forward_mgr_(new DdnsDomainListMgr("forward_mgr")),
37 38
      reverse_mgr_(new DdnsDomainListMgr("reverse_mgr")),
      keys_(new TSIGKeyInfoMap()) {
39 40
}

41
D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs) {
42
    d2_params_ = rhs.d2_params_;
43 44 45 46 47 48 49 50 51
    if (rhs.forward_mgr_) {
        forward_mgr_.reset(new DdnsDomainListMgr(rhs.forward_mgr_->getName()));
        forward_mgr_->setDomains(rhs.forward_mgr_->getDomains());
    }

    if (rhs.reverse_mgr_) {
        reverse_mgr_.reset(new DdnsDomainListMgr(rhs.reverse_mgr_->getName()));
        reverse_mgr_->setDomains(rhs.reverse_mgr_->getDomains());
    }
52

53
    keys_ = rhs.keys_;
54 55 56 57 58 59 60
}

D2CfgContext::~D2CfgContext() {
}

// *********************** D2CfgMgr  *************************

61 62 63 64
const char* D2CfgMgr::IPV4_REV_ZONE_SUFFIX = "in-addr.arpa.";

const char* D2CfgMgr::IPV6_REV_ZONE_SUFFIX = "ip6.arpa.";

65
D2CfgMgr::D2CfgMgr() : DCfgMgrBase(DCfgContextBasePtr(new D2CfgContext())) {
66 67 68 69 70
    // TSIG keys need to parse before the Domains, so we can catch Domains
    // that specify undefined keys. Create the necessary parsing order now.
    addToParseOrder("tsig_keys");
    addToParseOrder("forward_ddns");
    addToParseOrder("reverse_ddns");
71 72 73 74 75
}

D2CfgMgr::~D2CfgMgr() {
}

76 77 78 79 80
DCfgContextBasePtr
D2CfgMgr::createNewContext() {
    return (DCfgContextBasePtr(new D2CfgContext()));
}

81 82 83 84 85 86 87 88 89 90 91 92
bool
D2CfgMgr::forwardUpdatesEnabled() {
    // Forward updates are not enabled if no forward servers are defined.
    return (getD2CfgContext()->getForwardMgr()->size() > 0);
}

bool
D2CfgMgr::reverseUpdatesEnabled() {
    // Reverse updates are not enabled if no revese servers are defined.
    return (getD2CfgContext()->getReverseMgr()->size() > 0);
}

93 94
bool
D2CfgMgr::matchForward(const std::string& fqdn, DdnsDomainPtr& domain) {
95
    if (fqdn.empty()) {
96
        // This is a programmatic error and should not happen.
97
        isc_throw(D2CfgError, "matchForward passed an empty fqdn");
98 99 100
    }

    // Fetch the forward manager from the D2 context.
101
    DdnsDomainListMgrPtr mgr = getD2CfgContext()->getForwardMgr();
102 103 104 105 106 107

    // Call the manager's match method and return the result.
    return (mgr->matchDomain(fqdn, domain));
}

bool
108 109 110
D2CfgMgr::matchReverse(const std::string& ip_address, DdnsDomainPtr& domain) {
    // Note, reverseIpAddress will throw if the ip_address is invalid.
    std::string reverse_address = reverseIpAddress(ip_address);
111 112

    // Fetch the reverse manager from the D2 context.
113
    DdnsDomainListMgrPtr mgr = getD2CfgContext()->getReverseMgr();
114

115 116 117 118 119 120 121 122 123
    return (mgr->matchDomain(reverse_address, domain));
}

std::string
D2CfgMgr::reverseIpAddress(const std::string& address) {
    try {
        // Convert string address into an IOAddress and invoke the
        // appropriate reverse method.
        isc::asiolink::IOAddress ioaddr(address);
124
        if (ioaddr.isV4()) {
125 126 127 128 129 130
            return (reverseV4Address(ioaddr));
        }

        return (reverseV6Address(ioaddr));

    } catch (const isc::Exception& ex) {
131 132
        isc_throw(D2CfgError, "D2CfgMgr cannot reverse address: "
                               << address << " : " << ex.what());
133 134 135 136 137
    }
}

std::string
D2CfgMgr::reverseV4Address(const isc::asiolink::IOAddress& ioaddr) {
138
    if (!ioaddr.isV4()) {
139
        isc_throw(D2CfgError, "D2CfgMgr address is not IPv4 address :"
140
                  << ioaddr);
141 142 143
    }

    // Get the address in byte vector form.
144
    const ByteAddress bytes = ioaddr.toBytes();
145 146 147

    // Walk backwards through vector outputting each octet and a dot.
    std::ostringstream stream;
148

149 150 151 152 153 154 155 156 157
    // We have to set the following variable to get
    // const_reverse_iterator type of rend(), otherwise Solaris GCC
    // complains on operator!= by trying to use the non-const variant.
    const ByteAddress::const_reverse_iterator end = bytes.rend();

    for (ByteAddress::const_reverse_iterator rit = bytes.rbegin();
         rit != end;
         ++rit)
    {
158
        stream << static_cast<unsigned int>(*rit) << ".";
159 160 161 162 163 164 165 166 167
    }

    // Tack on the suffix and we're done.
    stream << IPV4_REV_ZONE_SUFFIX;
    return(stream.str());
}

std::string
D2CfgMgr::reverseV6Address(const isc::asiolink::IOAddress& ioaddr) {
168
    if (!ioaddr.isV6()) {
169
        isc_throw(D2CfgError, "D2Cfg address is not IPv6 address: " << ioaddr);
170 171 172
    }

    // Turn the address into a string of digits.
173 174
    const ByteAddress bytes = ioaddr.toBytes();
    const std::string digits = isc::util::encode::encodeHex(bytes);
175 176 177

    // Walk backwards through string outputting each digits and a dot.
    std::ostringstream stream;
178 179 180 181 182 183 184 185 186 187

    // We have to set the following variable to get
    // const_reverse_iterator type of rend(), otherwise Solaris GCC
    // complains on operator!= by trying to use the non-const variant.
    const std::string::const_reverse_iterator end = digits.rend();

    for (std::string::const_reverse_iterator rit = digits.rbegin();
         rit != end;
         ++rit)
    {
188
        stream << static_cast<char>(*rit) << ".";
189 190 191 192 193
    }

    // Tack on the suffix and we're done.
    stream << IPV6_REV_ZONE_SUFFIX;
    return(stream.str());
194 195
}

196 197 198 199 200
const D2ParamsPtr&
D2CfgMgr::getD2Params() {
    return (getD2CfgContext()->getD2Params());
}

201
std::string
202
D2CfgMgr::getConfigSummary(const uint32_t) {
203 204 205
    return (getD2Params()->getConfigSummary());
}

206 207 208 209 210 211
void
D2CfgMgr::buildParams(isc::data::ConstElementPtr params_config) {
    // Base class build creates parses and invokes build on each parser.
    // This populate the context scalar stores with all of the parameters.
    DCfgMgrBase::buildParams(params_config);

212 213 214 215
    // Fetch and validate the parameters from the context to create D2Params.
    // We validate them here rather than just relying on D2Param constructor
    // so we can spit out config text position info with errors.

216
    // Fetch and validate ip_address.
217
    D2CfgContextPtr context = getD2CfgContext();
218
    isc::dhcp::StringStoragePtr strings = context->getStringStorage();
219 220 221 222 223 224 225 226 227
    asiolink::IOAddress ip_address(D2Params::DFT_IP_ADDRESS);

    std::string ip_address_str = strings->getOptionalParam("ip_address",
                                                            D2Params::
                                                            DFT_IP_ADDRESS);
    try {
        ip_address = asiolink::IOAddress(ip_address_str);
    } catch (const std::exception& ex) {
        isc_throw(D2CfgError, "IP address invalid : \""
228 229
                  << ip_address_str << "\" ("
                  << strings->getPosition("ip_address") << ")");
230
    }
231

232
    if ((ip_address.toText() == "0.0.0.0") || (ip_address.toText() == "::")) {
233 234
        isc_throw(D2CfgError, "IP address cannot be \"" << ip_address << "\" ("
                   << strings->getPosition("ip_address") << ")");
235 236 237
    }

    // Fetch and validate port.
238
    isc::dhcp::Uint32StoragePtr ints = context->getUint32Storage();
239 240
    uint32_t port = ints->getOptionalParam("port", D2Params::DFT_PORT);

241
    if (port == 0) {
242 243
        isc_throw(D2CfgError, "port cannot be 0 ("
                  << ints->getPosition("port") << ")");
244 245 246
    }

    // Fetch and validate dns_server_timeout.
247 248 249 250
    uint32_t dns_server_timeout
        = ints->getOptionalParam("dns_server_timeout",
                                 D2Params::DFT_DNS_SERVER_TIMEOUT);

251
    if (dns_server_timeout < 1) {
252 253
        isc_throw(D2CfgError, "DNS server timeout must be larger than 0 ("
                  << ints->getPosition("dns_server_timeout") << ")");
254 255 256 257 258 259 260 261 262 263 264 265
    }

    // Fetch and validate ncr_protocol.
    dhcp_ddns::NameChangeProtocol ncr_protocol;
    try {
        ncr_protocol = dhcp_ddns::
                       stringToNcrProtocol(strings->
                                           getOptionalParam("ncr_protocol",
                                                            D2Params::
                                                            DFT_NCR_PROTOCOL));
    } catch (const std::exception& ex) {
        isc_throw(D2CfgError, "ncr_protocol : "
266 267
                  << ex.what() << " ("
                  << strings->getPosition("ncr_protocol") << ")");
268 269 270 271 272
    }

    if (ncr_protocol != dhcp_ddns::NCR_UDP) {
        isc_throw(D2CfgError, "ncr_protocol : "
                  << dhcp_ddns::ncrProtocolToString(ncr_protocol)
273 274
                  << " is not yet supported ("
                  << strings->getPosition("ncr_protocol") << ")");
275 276 277 278 279 280 281
    }

    // Fetch and validate ncr_format.
    dhcp_ddns::NameChangeFormat ncr_format;
    try {
        ncr_format = dhcp_ddns::
                     stringToNcrFormat(strings->
282
                                       getOptionalParam("ncr_format",
283 284 285 286
                                                        D2Params::
                                                        DFT_NCR_FORMAT));
    } catch (const std::exception& ex) {
        isc_throw(D2CfgError, "ncr_format : "
287 288
                  << ex.what() << " ("
                  << strings->getPosition("ncr_format") << ")");
289 290 291 292 293
    }

    if (ncr_format != dhcp_ddns::FMT_JSON) {
        isc_throw(D2CfgError, "NCR Format:"
                  << dhcp_ddns::ncrFormatToString(ncr_format)
294 295
                  << " is not yet supported ("
                  << strings->getPosition("ncr_format") << ")");
296 297 298 299
    }

    // Attempt to create the new client config. This ought to fly as
    // we already validated everything.
300 301 302 303 304
    D2ParamsPtr params(new D2Params(ip_address, port, dns_server_timeout,
                                    ncr_protocol, ncr_format));

    context->getD2Params() = params;
}
305

306
isc::dhcp::ParserPtr
307 308
D2CfgMgr::createConfigParser(const std::string& config_id,
                             const isc::data::Element::Position& pos) {
309 310 311 312
    // Get D2 specific context.
    D2CfgContextPtr context = getD2CfgContext();

    // Create parser instance based on element_id.
313 314 315 316 317 318 319 320 321 322
    isc::dhcp::ParserPtr parser;
    if ((config_id.compare("port") == 0) ||
        (config_id.compare("dns_server_timeout") == 0)) {
        parser.reset(new isc::dhcp::Uint32Parser(config_id,
                                                 context->getUint32Storage()));
    } else if ((config_id.compare("ip_address") == 0) ||
        (config_id.compare("ncr_protocol") == 0) ||
        (config_id.compare("ncr_format") == 0)) {
        parser.reset(new isc::dhcp::StringParser(config_id,
                                                 context->getStringStorage()));
323
    } else if (config_id ==  "forward_ddns") {
324 325 326
        parser.reset(new DdnsDomainListMgrParser("forward_mgr",
                                                 context->getForwardMgr(),
                                                 context->getKeys()));
327
    } else if (config_id ==  "reverse_ddns") {
328 329 330
        parser.reset(new DdnsDomainListMgrParser("reverse_mgr",
                                                 context->getReverseMgr(),
                                                 context->getKeys()));
331
    } else if (config_id ==  "tsig_keys") {
332 333
        parser.reset(new TSIGKeyInfoListParser("tsig_key_list",
                                               context->getKeys()));
334 335
    } else {
        isc_throw(NotImplemented,
336 337
                  "parser error: D2CfgMgr parameter not supported : "
                  " (" << config_id << pos << ")");
338 339
    }

340
    return (parser);
341 342 343 344
}

}; // end of isc::dhcp namespace
}; // end of isc namespace