d2_config.cc 21.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// 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.

#include <d2/d2_log.h>
#include <d2/d2_cfg_mgr.h>
#include <dhcpsrv/dhcp_parsers.h>
#include <exceptions/exceptions.h>
#include <asiolink/io_error.h>

#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>

namespace isc {
namespace d2 {

27
28
29
30
31
32
33
34
35
// *********************** TSIGKeyInfo  *************************

TSIGKeyInfo::TSIGKeyInfo(const std::string& name, const std::string& algorithm,
                         const std::string& secret)
    :name_(name), algorithm_(algorithm), secret_(secret) {
}

TSIGKeyInfo::~TSIGKeyInfo() {
}
36
37


38
// *********************** DnsServerInfo  *************************
39

40
41
42
const char* DnsServerInfo::EMPTY_IP_STR = "0.0.0.0";

DnsServerInfo::DnsServerInfo(const std::string& hostname,
43
44
                             isc::asiolink::IOAddress ip_address, uint32_t port,
                             bool enabled)
45
    :hostname_(hostname), ip_address_(ip_address), port_(port),
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    enabled_(enabled) {
}

DnsServerInfo::~DnsServerInfo() {
}

// *********************** DdnsDomain  *************************

DdnsDomain::DdnsDomain(const std::string& name, const std::string& key_name,
                       DnsServerInfoStoragePtr servers)
    : name_(name), key_name_(key_name), servers_(servers) {
}

DdnsDomain::~DdnsDomain() {
}

// *********************** DdnsDomainLstMgr  *************************

64
65
const char* DdnsDomainListMgr::wildcard_domain_name_ = "*";

66
DdnsDomainListMgr::DdnsDomainListMgr(const std::string& name) : name_(name),
67
    domains_(new DdnsDomainMap()) {
68
69
70
71
72
73
}


DdnsDomainListMgr::~DdnsDomainListMgr () {
}

74
75
void
DdnsDomainListMgr::setDomains(DdnsDomainMapPtr domains) {
76
    if (!domains) {
77
        isc_throw(D2CfgError,
78
79
80
81
82
                  "DdnsDomainListMgr::setDomains: Domain list may not be null");
    }

    domains_ = domains;

83
84
85
86
87
88
    // Look for the wild card domain. If present, set the member variable
    // to remember it.  This saves us from having to look for it every time
    // we attempt a match.
    DdnsDomainMap::iterator gotit = domains_->find(wildcard_domain_name_);
    if (gotit != domains_->end()) {
            wildcard_domain_ = gotit->second;
89
90
91
92
93
94
95
96
97
98
99
    }
}

bool
DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
    // First check the case of one domain to rule them all.
    if ((size() == 1) && (wildcard_domain_)) {
        domain = wildcard_domain_;
        return (true);
    }

100
101
102
103
104
105
    // Iterate over the domain map looking for the domain which matches
    // the longest portion of the given fqdn.

    size_t req_len = fqdn.size();
    size_t match_len = 0;
    DdnsDomainMapPair map_pair;
106
    DdnsDomainPtr best_match;
107
108
109
110
111
112
113
    BOOST_FOREACH (map_pair, *domains_) {
        std::string domain_name = map_pair.first;
        size_t dom_len = domain_name.size();

        // If the domain name is longer than the fqdn, then it cant be match.
        if (req_len < dom_len) {
            continue;
114
115
        }

116
117
        // If the lengths are identical and the names match we're done.
        if (req_len == dom_len) {
118
            if (fqdn == domain_name) {
119
120
121
122
123
124
125
126
127
128
                // exact match, done
                domain = map_pair.second;
                return (true);
            }
        } else {
            // The fqdn is longer than the domain name.  Adjust the start
            // point of comparison by the excess in length.  Only do the
            // comparison if the adjustment lands on a boundary. This
            // prevents "onetwo.net" from matching "two.net".
            size_t offset = req_len - dom_len;
129
            if ((fqdn[offset - 1] == '.')  &&
130
               (fqdn.compare(offset, std::string::npos, domain_name) == 0)) {
131
132
133
134
                // Fqdn contains domain name, keep it if its better than
                // any we have matched so far.
                if (dom_len > match_len) {
                    match_len = dom_len;
135
                    best_match = map_pair.second;
136
137
                }
            }
138
139
140
        }
    }

141
    if (!best_match) {
142
143
144
145
146
147
148
149
150
        // There's no match. If they specified a wild card domain use it
        // otherwise there's no domain for this entry.
        if (wildcard_domain_) {
            domain = wildcard_domain_;
            return (true);
        }

        LOG_WARN(dctl_logger, DHCP_DDNS_NO_MATCH).arg(fqdn);
        return (false);
151
    }
152

153
    domain = best_match;
154
    return (true);
155
156
157
158
}

// *************************** PARSERS ***********************************

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// *********************** TSIGKeyInfoParser  *************************

TSIGKeyInfoParser::TSIGKeyInfoParser(const std::string& entry_name,
    TSIGKeyInfoMapPtr keys)
    : entry_name_(entry_name), keys_(keys), local_scalars_() {
    if (!keys_) {
        isc_throw(D2CfgError, "TSIGKeyInfoParser ctor:"
                  " key storage cannot be null");
    }
}

TSIGKeyInfoParser::~TSIGKeyInfoParser() {
}

void
TSIGKeyInfoParser::build(isc::data::ConstElementPtr key_config) {
    isc::dhcp::ConfigPair config_pair;
    // For each element in the key configuration:
    // 1. Create a parser for the element.
    // 2. Invoke the parser's build method passing in the element's
    // configuration.
    // 3. Invoke the parser's commit method to store the element's parsed
    // data to the parser's local storage.
    BOOST_FOREACH (config_pair, key_config->mapValue()) {
        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
        parser->build(config_pair.second);
        parser->commit();
    }
}

isc::dhcp::ParserPtr
TSIGKeyInfoParser::createConfigParser(const std::string& config_id) {
    DhcpConfigParser* parser = NULL;
    // Based on the configuration id of the element, create the appropriate
    // parser. Scalars are set to use the parser's local scalar storage.
    if ((config_id == "name")  ||
        (config_id == "algorithm") ||
        (config_id == "secret")) {
        parser = new isc::dhcp::StringParser(config_id,
                                             local_scalars_.getStringStorage());
    } else {
        isc_throw(NotImplemented,
                  "parser error: TSIGKeyInfo parameter not supported: "
                  << config_id);
    }

    // Return the new parser instance.
    return (isc::dhcp::ParserPtr(parser));
}

void
TSIGKeyInfoParser::commit() {
    std::string name;
    std::string algorithm;
    std::string secret;

    // Fetch the key configuration's parsed scalar values from parser's
    // local storage.
    local_scalars_.getParam("name", name);
    local_scalars_.getParam("algorithm", algorithm);
    local_scalars_.getParam("secret", secret);

221
    // @todo Validation here is very superficial. This will expand as TSIG
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
    // Key use is more fully implemented.

    // Name cannot be blank.
    if (name.empty()) {
        isc_throw(D2CfgError, "TSIG Key Info must specify name");
    }

    // Algorithme cannot be blank.
    if (algorithm.empty()) {
        isc_throw(D2CfgError, "TSIG Key Info must specify algorithm");
    }

    // Secret cannot be blank.
    if (secret.empty()) {
        isc_throw(D2CfgError, "TSIG Key Info must specify secret");
    }

    // Currently, the premise is that key storage is always empty prior to
    // parsing so we are always adding keys never replacing them. Duplicates
    // are not allowed and should be flagged as a configuration error.
    if (keys_->find(name) != keys_->end()) {
        isc_throw(D2CfgError, "Duplicate TSIG key specified:" << name);
    }

    TSIGKeyInfoPtr key_info(new TSIGKeyInfo(name, algorithm, secret));

    // Add the new TSIGKeyInfo to the key storage.
    (*keys_)[name]=key_info;
}

// *********************** TSIGKeyInfoListParser  *************************

TSIGKeyInfoListParser::TSIGKeyInfoListParser(const std::string& list_name,
                                       TSIGKeyInfoMapPtr keys)
    :list_name_(list_name), keys_(keys), parsers_() {
    if (!keys_) {
        isc_throw(D2CfgError, "TSIGKeyInfoListParser ctor:"
                  " key storage cannot be null");
    }
}

TSIGKeyInfoListParser::~TSIGKeyInfoListParser(){
}

void
TSIGKeyInfoListParser::
build(isc::data::ConstElementPtr key_list){
    int i = 0;
    isc::data::ConstElementPtr key_config;
    // For each key element in the key list:
    // 1. Create a parser for the key element.
    // 2. Invoke the parser's build method passing in the key's
    // configuration.
    // 3. Add the parser to a local collection of parsers.
    BOOST_FOREACH(key_config, key_list->listValue()) {
        // Create a name for the parser based on its position in the list.
        std::string entry_name = boost::lexical_cast<std::string>(i++);
        isc::dhcp::ParserPtr parser(new TSIGKeyInfoParser(entry_name,
                                                            keys_));
        parser->build(key_config);
        parsers_.push_back(parser);
    }
}

void
TSIGKeyInfoListParser::commit() {
    // Invoke commit on each server parser. This will cause each one to
    // create it's server instance and commit it to storage.
    BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
        parser->commit();
    }
}

295
296
297
// *********************** DnsServerInfoParser  *************************

DnsServerInfoParser::DnsServerInfoParser(const std::string& entry_name,
298
    DnsServerInfoStoragePtr servers)
299
300
    : entry_name_(entry_name), servers_(servers), local_scalars_() {
    if (!servers_) {
301
        isc_throw(D2CfgError, "DnsServerInfoParser ctor:"
302
303
304
305
306
307
308
                  " server storage cannot be null");
    }
}

DnsServerInfoParser::~DnsServerInfoParser() {
}

309
void
310
311
312
313
DnsServerInfoParser::build(isc::data::ConstElementPtr server_config) {
    isc::dhcp::ConfigPair config_pair;
    // For each element in the server configuration:
    // 1. Create a parser for the element.
314
    // 2. Invoke the parser's build method passing in the element's
315
316
317
318
319
320
321
322
    // configuration.
    // 3. Invoke the parser's commit method to store the element's parsed
    // data to the parser's local storage.
    BOOST_FOREACH (config_pair, server_config->mapValue()) {
        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
        parser->build(config_pair.second);
        parser->commit();
    }
323

324
325
}

326
isc::dhcp::ParserPtr
327
328
329
DnsServerInfoParser::createConfigParser(const std::string& config_id) {
    DhcpConfigParser* parser = NULL;
    // Based on the configuration id of the element, create the appropriate
330
    // parser. Scalars are set to use the parser's local scalar storage.
331
332
    if ((config_id == "hostname")  ||
        (config_id == "ip_address")) {
333
        parser = new isc::dhcp::StringParser(config_id,
334
                                             local_scalars_.getStringStorage());
335
336
    } else if (config_id == "port") {
        parser = new isc::dhcp::Uint32Parser(config_id,
337
338
                                             local_scalars_.getUint32Storage());
    } else {
339
340
        isc_throw(NotImplemented,
                  "parser error: DnsServerInfo parameter not supported: "
341
342
343
344
345
346
347
                  << config_id);
    }

    // Return the new parser instance.
    return (isc::dhcp::ParserPtr(parser));
}

348
void
349
350
351
DnsServerInfoParser::commit() {
    std::string hostname;
    std::string ip_address;
352
    uint32_t port = DnsServerInfo::STANDARD_DNS_PORT;
353

354
355
356
357
358
359
    // Fetch the server configuration's parsed scalar values from parser's
    // local storage.
    local_scalars_.getParam("hostname", hostname, DCfgContextBase::OPTIONAL);
    local_scalars_.getParam("ip_address", ip_address,
                            DCfgContextBase::OPTIONAL);
    local_scalars_.getParam("port", port, DCfgContextBase::OPTIONAL);
360
361

    // The configuration must specify one or the other.
362
363
364
    if (hostname.empty() == ip_address.empty()) {
        isc_throw(D2CfgError, "Dns Server must specify one or the other"
                  " of hostname and IP address");
365
366
    }

367
368
    DnsServerInfoPtr serverInfo;
    if (!hostname.empty()) {
369
370
        // When  hostname is specified, create a valid, blank IOAddress and
        // then create the DnsServerInfo.
371
372
        isc::asiolink::IOAddress io_addr(DnsServerInfo::EMPTY_IP_STR);
        serverInfo.reset(new DnsServerInfo(hostname, io_addr, port));
373
374
375
376
377
    } else {
        try {
            // Create an IOAddress from the IP address string given and then
            // create the DnsServerInfo.
            isc::asiolink::IOAddress io_addr(ip_address);
378
            serverInfo.reset(new DnsServerInfo(hostname, io_addr, port));
379
380
381
382
383
384
        } catch (const isc::asiolink::IOError& ex) {
            isc_throw(D2CfgError, "Invalid IP address:" << ip_address);
        }
    }

    // Add the new DnsServerInfo to the server storage.
385
    servers_->push_back(serverInfo);
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
}

// *********************** DnsServerInfoListParser  *************************

DnsServerInfoListParser::DnsServerInfoListParser(const std::string& list_name,
                                       DnsServerInfoStoragePtr servers)
    :list_name_(list_name), servers_(servers), parsers_() {
    if (!servers_) {
        isc_throw(D2CfgError, "DdnsServerInfoListParser ctor:"
                  " server storage cannot be null");
    }
}

DnsServerInfoListParser::~DnsServerInfoListParser(){
}

402
void
403
404
405
406
DnsServerInfoListParser::
build(isc::data::ConstElementPtr server_list){
    int i = 0;
    isc::data::ConstElementPtr server_config;
407
    // For each server element in the server list:
408
    // 1. Create a parser for the server element.
409
    // 2. Invoke the parser's build method passing in the server's
410
411
412
413
414
    // configuration.
    // 3. Add the parser to a local collection of parsers.
    BOOST_FOREACH(server_config, server_list->listValue()) {
        // Create a name for the parser based on its position in the list.
        std::string entry_name = boost::lexical_cast<std::string>(i++);
415
        isc::dhcp::ParserPtr parser(new DnsServerInfoParser(entry_name,
416
417
418
419
420
421
                                                            servers_));
        parser->build(server_config);
        parsers_.push_back(parser);
    }
}

422
void
423
424
425
426
427
428
429
DnsServerInfoListParser::commit() {
    // Domains must have at least one server.
    if (parsers_.size() == 0) {
        isc_throw (D2CfgError, "Server List must contain at least one server");
    }

    // Invoke commit on each server parser. This will cause each one to
430
    // create it's server instance and commit it to storage.
431
432
433
434
435
436
437
438
    BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
        parser->commit();
    }
}

// *********************** DdnsDomainParser  *************************

DdnsDomainParser::DdnsDomainParser(const std::string& entry_name,
439
440
441
                                   DdnsDomainMapPtr domains,
                                   TSIGKeyInfoMapPtr keys)
    : entry_name_(entry_name), domains_(domains), keys_(keys),
442
443
    local_servers_(new DnsServerInfoStorage()), local_scalars_() {
    if (!domains_) {
444
        isc_throw(D2CfgError,
445
446
447
448
449
450
451
452
                  "DdnsDomainParser ctor, domain storage cannot be null");
    }
}


DdnsDomainParser::~DdnsDomainParser() {
}

453
void
454
455
456
DdnsDomainParser::build(isc::data::ConstElementPtr domain_config) {
    // For each element in the domain configuration:
    // 1. Create a parser for the element.
457
    // 2. Invoke the parser's build method passing in the element's
458
459
460
461
462
463
464
465
466
467
468
    // configuration.
    // 3. Invoke the parser's commit method to store the element's parsed
    // data to the parser's local storage.
    isc::dhcp::ConfigPair config_pair;
    BOOST_FOREACH(config_pair, domain_config->mapValue()) {
        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
        parser->build(config_pair.second);
        parser->commit();
    }
}

469
isc::dhcp::ParserPtr
470
471
472
DdnsDomainParser::createConfigParser(const std::string& config_id) {
    DhcpConfigParser* parser = NULL;
    // Based on the configuration id of the element, create the appropriate
473
    // parser. Scalars are set to use the parser's local scalar storage.
474
    if ((config_id == "name")  ||
475
476
        (config_id == "key_name")) {
        parser = new isc::dhcp::StringParser(config_id,
477
478
479
480
481
482
483
484
                                             local_scalars_.getStringStorage());
    } else if (config_id == "dns_servers") {
       // Server list parser is given in our local server storage. It will pass
       // this down to its server parsers and is where they will write their
       // server instances upon commit.
       parser = new DnsServerInfoListParser(config_id, local_servers_);
    } else {
       isc_throw(NotImplemented,
485
                "parser error: DdnsDomain parameter not supported: "
486
487
488
489
490
491
492
                << config_id);
    }

    // Return the new domain parser instance.
    return (isc::dhcp::ParserPtr(parser));
}

493
void
494
495
496
497
498
DdnsDomainParser::commit() {
    std::string name;
    std::string key_name;

    // Domain name is not optional. The get will throw if its not there.
499
    local_scalars_.getParam("name", name);
500
501

    // Blank domain names are not allowed.
502
    if (name.empty()) {
503
504
505
506
507
508
509
510
        isc_throw(D2CfgError, "Domain name cannot be blank");
    }

    // Currently, the premise is that domain storage is always empty
    // prior to parsing so always adding domains never replacing them.
    // Duplicates are not allowed and should be flagged as a configuration
    // error.
    if (domains_->find(name) != domains_->end()) {
511
        isc_throw(D2CfgError, "Duplicate domain specified:" << name);
512
513
    }

514
515
516
517
518
519
520
521
522
    // Key name is optional. If it is not blank, then validate it against
    // the defined list of keys.
    local_scalars_.getParam("key_name", key_name, DCfgContextBase::OPTIONAL);
    if (!key_name.empty()) {
        if ((!keys_) || (keys_->find(key_name) == keys_->end())) {
            isc_throw(D2CfgError, "DdnsDomain :" << name <<
                     " specifies and undefined key:" << key_name);
        }
    }
523
524
525
526
527
528
529
530
531
532
533

    // Instantiate the new domain and add it to domain storage.
    DdnsDomainPtr domain(new DdnsDomain(name, key_name, local_servers_));

    // Add the new domain to the domain storage.
    (*domains_)[name]=domain;
}

// *********************** DdnsDomainListParser  *************************

DdnsDomainListParser::DdnsDomainListParser(const std::string& list_name,
534
535
536
                                           DdnsDomainMapPtr domains,
                                           TSIGKeyInfoMapPtr keys)
    :list_name_(list_name), domains_(domains), keys_(keys), parsers_() {
537
538
539
540
541
542
543
544
545
    if (!domains_) {
        isc_throw(D2CfgError, "DdnsDomainListParser ctor:"
                  " domain storage cannot be null");
    }
}

DdnsDomainListParser::~DdnsDomainListParser(){
}

546
void
547
548
DdnsDomainListParser::
build(isc::data::ConstElementPtr domain_list){
549
    // For each domain element in the domain list:
550
    // 1. Create a parser for the domain element.
551
    // 2. Invoke the parser's build method passing in the domain's
552
553
554
555
556
557
    // configuration.
    // 3. Add the parser to the local collection of parsers.
    int i = 0;
    isc::data::ConstElementPtr domain_config;
    BOOST_FOREACH(domain_config, domain_list->listValue()) {
        std::string entry_name = boost::lexical_cast<std::string>(i++);
558
559
        isc::dhcp::ParserPtr parser(new DdnsDomainParser(entry_name,
                                                         domains_, keys_));
560
561
562
563
564
        parser->build(domain_config);
        parsers_.push_back(parser);
    }
}

565
void
566
567
DdnsDomainListParser::commit() {
    // Invoke commit on each server parser. This will cause each one to
568
    // create it's server instance and commit it to storage.
569
570
571
572
573
574
575
576
577
    BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
        parser->commit();
    }
}


// *********************** DdnsDomainListMgrParser  *************************

DdnsDomainListMgrParser::DdnsDomainListMgrParser(const std::string& entry_name,
578
579
580
                              DdnsDomainListMgrPtr mgr, TSIGKeyInfoMapPtr keys)
    : entry_name_(entry_name), mgr_(mgr), keys_(keys),
    local_domains_(new DdnsDomainMap()), local_scalars_() {
581
582
583
584
585
586
}


DdnsDomainListMgrParser::~DdnsDomainListMgrParser() {
}

587
void
588
589
590
DdnsDomainListMgrParser::build(isc::data::ConstElementPtr domain_config) {
    // For each element in the domain manager configuration:
    // 1. Create a parser for the element.
591
    // 2. Invoke the parser's build method passing in the element's
592
593
594
595
596
597
598
599
600
601
602
    // configuration.
    // 3. Invoke the parser's commit method to store the element's parsed
    // data to the parser's local storage.
    isc::dhcp::ConfigPair config_pair;
    BOOST_FOREACH(config_pair, domain_config->mapValue()) {
        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
        parser->build(config_pair.second);
        parser->commit();
    }
}

603
isc::dhcp::ParserPtr
604
605
606
607
608
609
DdnsDomainListMgrParser::createConfigParser(const std::string& config_id) {
    DhcpConfigParser* parser = NULL;
    if (config_id == "ddns_domains") {
       // Domain list parser is given our local domain storage. It will pass
       // this down to its domain parsers and is where they will write their
       // domain instances upon commit.
610
       parser = new DdnsDomainListParser(config_id, local_domains_, keys_);
611
612
613
614
615
616
617
618
619
    } else {
       isc_throw(NotImplemented, "parser error: "
                 "DdnsDomainListMgr parameter not supported: " << config_id);
    }

    // Return the new domain parser instance.
    return (isc::dhcp::ParserPtr(parser));
}

620
void
621
622
623
624
625
626
627
628
DdnsDomainListMgrParser::commit() {
    // Add the new domain to the domain storage.
    mgr_->setDomains(local_domains_);
}


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