Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Sebastian Schrader
Kea
Commits
5eb2ff86
Commit
5eb2ff86
authored
Jan 07, 2013
by
JINMEI Tatuya
Browse files
[master] Merge branch 'trac2433'
parents
0de32e7f
96f97334
Changes
9
Hide whitespace changes
Inline
Side-by-side
src/lib/dns/Makefile.am
View file @
5eb2ff86
...
...
@@ -95,6 +95,7 @@ lib_LTLIBRARIES = libb10-dns++.la
libb10_dns___la_LDFLAGS
=
-no-undefined
-version-info
2:0:0
libb10_dns___la_SOURCES
=
libb10_dns___la_SOURCES
+=
dns_fwd.h
libb10_dns___la_SOURCES
+=
edns.h edns.cc
libb10_dns___la_SOURCES
+=
exceptions.h exceptions.cc
libb10_dns___la_SOURCES
+=
master_lexer_inputsource.h master_lexer_inputsource.cc
...
...
@@ -129,6 +130,7 @@ libb10_dns___la_SOURCES += master_loader_callbacks.h master_loader_callbacks.cc
libb10_dns___la_SOURCES
+=
master_loader.h
libb10_dns___la_SOURCES
+=
rrset_collection_base.h
libb10_dns___la_SOURCES
+=
rrset_collection.h rrset_collection.cc
libb10_dns___la_SOURCES
+=
zone_checker.h zone_checker.cc
libb10_dns___la_SOURCES
+=
rdata/generic/detail/char_string.h
libb10_dns___la_SOURCES
+=
rdata/generic/detail/char_string.cc
libb10_dns___la_SOURCES
+=
rdata/generic/detail/nsec_bitmap.h
...
...
@@ -158,6 +160,7 @@ libdns___includedir = $(includedir)/$(PACKAGE_NAME)/dns
libdns___include_HEADERS
=
\
edns.h
\
exceptions.h
\
dns_fwd.h
\
labelsequence.h
\
message.h
\
masterload.h
\
...
...
@@ -175,7 +178,8 @@ libdns___include_HEADERS = \
rrset_collection_base.h
\
rrset_collection.h
\
rrttl.h
\
tsigkey.h
tsigkey.h
\
zone_checker.h
# Purposely not installing these headers:
# name_internal.h: used only internally, and not actually DNS specific
# rdata/*/detail/*.h: these are internal use only
...
...
src/lib/dns/dns_fwd.h
0 → 100644
View file @
5eb2ff86
// Copyright (C) 2012 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.
#ifndef DNS_FWD_H
#define DNS_FWD_H 1
/// \file dns_fwd.h
/// \brief Forward declarations for definitions of libdns++
///
/// This file provides a set of forward declarations for definitions commonly
/// used in libdns++ to help minimize dependency when actual the definition
/// is not necessary.
namespace
isc
{
namespace
dns
{
class
EDNS
;
class
Name
;
class
MasterLoader
;
class
MasterLoaderCallbacks
;
class
Message
;
class
AbstractMessageRenderer
;
class
MessageRenderer
;
class
NSEC3Hash
;
class
NSEC3HashCreator
;
class
Opcode
;
class
Question
;
class
Rcode
;
namespace
rdata
{
class
Rdata
;
}
class
RRCollator
;
class
RRClass
;
class
RRType
;
class
RRTTL
;
class
AbstractRRset
;
class
RdataIterator
;
class
RRsetCollectionBase
;
class
RRsetCollection
;
class
Serial
;
class
TSIGContext
;
class
TSIGError
;
class
TSIGKey
;
class
TSIGKeyRing
;
class
TSIGRecord
;
}
// namespace dns
}
// namespace isc
#endif // DNS_FWD_H
// Local Variables:
// mode: c++
// End:
src/lib/dns/master_loader.cc
View file @
5eb2ff86
...
...
@@ -393,7 +393,7 @@ private:
shared_ptr
<
Name
>
last_name_
;
// Last seen name (for INITAL_WS handling)
const
RRClass
zone_class_
;
MasterLoaderCallbacks
callbacks_
;
AddRRCallback
add_callback_
;
const
AddRRCallback
add_callback_
;
boost
::
scoped_ptr
<
RRTTL
>
default_ttl_
;
// Default TTL of RRs used when
// unspecified. If NULL no default
// is known.
...
...
src/lib/dns/master_loader_callbacks.h
View file @
5eb2ff86
...
...
@@ -100,7 +100,7 @@ public:
/// If the caller of the loader wants to abort, it is possible to throw
/// from the callback, which aborts the load.
void
error
(
const
std
::
string
&
source_name
,
size_t
source_line
,
const
std
::
string
&
reason
)
const
std
::
string
&
reason
)
const
{
error_
(
source_name
,
source_line
,
reason
);
}
...
...
@@ -117,7 +117,7 @@ public:
/// may be false positives), it is possible to throw from inside the
/// callback.
void
warning
(
const
std
::
string
&
source_name
,
size_t
source_line
,
const
std
::
string
&
reason
)
const
std
::
string
&
reason
)
const
{
warning_
(
source_name
,
source_line
,
reason
);
}
...
...
@@ -133,7 +133,7 @@ public:
static
MasterLoaderCallbacks
getNullCallbacks
();
private:
IssueCallback
error_
,
warning_
;
const
IssueCallback
error_
,
warning_
;
};
}
...
...
src/lib/dns/rrcollator.cc
View file @
5eb2ff86
...
...
@@ -42,7 +42,7 @@ public:
const
RdataPtr
&
rdata
);
RRsetPtr
current_rrset_
;
AddRRsetCallback
callback_
;
const
AddRRsetCallback
callback_
;
};
namespace
{
...
...
src/lib/dns/tests/Makefile.am
View file @
5eb2ff86
...
...
@@ -76,6 +76,7 @@ run_unittests_SOURCES += tsigrecord_unittest.cc
run_unittests_SOURCES
+=
character_string_unittest.cc
run_unittests_SOURCES
+=
master_loader_callbacks_test.cc
run_unittests_SOURCES
+=
rrset_collection_unittest.cc
run_unittests_SOURCES
+=
zone_checker_unittest.cc
run_unittests_SOURCES
+=
run_unittests.cc
run_unittests_CPPFLAGS
=
$(AM_CPPFLAGS)
$(GTEST_INCLUDES)
# We shouldn't need to include BOTAN_LIBS here, but there
...
...
src/lib/dns/tests/zone_checker_unittest.cc
0 → 100644
View file @
5eb2ff86
// Copyright (C) 2012 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 <dns/zone_checker.h>
#include <exceptions/exceptions.h>
#include <dns/name.h>
#include <dns/rrclass.h>
#include <dns/rrset.h>
#include <dns/rrtype.h>
#include <dns/rrttl.h>
#include <dns/rdataclass.h>
#include <dns/rrset_collection.h>
#include <gtest/gtest.h>
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
#include <algorithm>
#include <string>
#include <sstream>
#include <vector>
using
isc
::
Unexpected
;
using
namespace
isc
::
dns
;
using
namespace
isc
::
dns
::
rdata
;
namespace
{
const
char
*
const
soa_txt
=
"ns.example.com. root.example.com. 0 0 0 0 0"
;
const
char
*
const
ns_txt1
=
"ns.example.com."
;
const
char
*
const
ns_a_txt1
=
"192.0.2.1"
;
const
char
*
const
ns_txt2
=
"ns2.example.com."
;
const
char
*
const
ns_a_txt2
=
"192.0.2.2"
;
class
ZoneCheckerTest
:
public
::
testing
::
Test
{
protected:
ZoneCheckerTest
()
:
zname_
(
"example.com"
),
zclass_
(
RRClass
::
IN
()),
soa_
(
new
RRset
(
zname_
,
zclass_
,
RRType
::
SOA
(),
RRTTL
(
60
))),
ns_
(
new
RRset
(
zname_
,
zclass_
,
RRType
::
NS
(),
RRTTL
(
60
))),
callbacks_
(
boost
::
bind
(
&
ZoneCheckerTest
::
callback
,
this
,
_1
,
true
),
boost
::
bind
(
&
ZoneCheckerTest
::
callback
,
this
,
_1
,
false
))
{
std
::
stringstream
ss
;
ss
<<
"example.com. 60 IN SOA "
<<
soa_txt
<<
"
\n
"
;
ss
<<
"example.com. 60 IN NS "
<<
ns_txt1
<<
"
\n
"
;
ss
<<
"ns.example.com. 60 IN A "
<<
ns_a_txt1
<<
"
\n
"
;
ss
<<
"ns2.example.com. 60 IN A "
<<
ns_a_txt2
<<
"
\n
"
;
rrsets_
.
reset
(
new
RRsetCollection
(
ss
,
zname_
,
zclass_
));
}
public:
// This one is passed to boost::bind. Some compilers seem to require
// it be public.
void
callback
(
const
std
::
string
&
reason
,
bool
is_error
)
{
if
(
is_error
)
{
errors_
.
push_back
(
reason
);
}
else
{
warns_
.
push_back
(
reason
);
}
}
protected:
// Check stored issue messages with expected ones. Clear vectors so
// the caller can check other cases.
void
checkIssues
()
{
EXPECT_EQ
(
expected_errors_
.
size
(),
errors_
.
size
());
for
(
int
i
=
0
;
i
<
std
::
min
(
expected_errors_
.
size
(),
errors_
.
size
());
++
i
)
{
// The actual message should begin with the expected message.
EXPECT_EQ
(
0
,
errors_
[
0
].
find
(
expected_errors_
[
0
]))
<<
"actual message: "
<<
errors_
[
0
]
<<
" expected: "
<<
expected_errors_
[
0
];
}
EXPECT_EQ
(
expected_warns_
.
size
(),
warns_
.
size
());
for
(
int
i
=
0
;
i
<
std
::
min
(
expected_warns_
.
size
(),
warns_
.
size
());
++
i
)
{
EXPECT_EQ
(
0
,
warns_
[
0
].
find
(
expected_warns_
[
0
]))
<<
"actual message: "
<<
warns_
[
0
]
<<
" expected: "
<<
expected_warns_
[
0
];
}
errors_
.
clear
();
expected_errors_
.
clear
();
warns_
.
clear
();
expected_warns_
.
clear
();
}
const
Name
zname_
;
const
RRClass
zclass_
;
boost
::
scoped_ptr
<
RRsetCollection
>
rrsets_
;
RRsetPtr
soa_
;
RRsetPtr
ns_
;
std
::
vector
<
std
::
string
>
errors_
;
std
::
vector
<
std
::
string
>
warns_
;
std
::
vector
<
std
::
string
>
expected_errors_
;
std
::
vector
<
std
::
string
>
expected_warns_
;
ZoneCheckerCallbacks
callbacks_
;
};
TEST_F
(
ZoneCheckerTest
,
checkGood
)
{
// Checking a valid case. No errors or warnings should be reported.
EXPECT_TRUE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
checkIssues
();
// Multiple NS RRs are okay.
rrsets_
->
removeRRset
(
zname_
,
zclass_
,
RRType
::
NS
());
ns_
->
addRdata
(
generic
::
NS
(
ns_txt1
));
ns_
->
addRdata
(
generic
::
NS
(
ns_txt2
));
rrsets_
->
addRRset
(
ns_
);
EXPECT_TRUE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
checkIssues
();
}
TEST_F
(
ZoneCheckerTest
,
checkSOA
)
{
// If the zone has no SOA it triggers an error.
rrsets_
->
removeRRset
(
zname_
,
zclass_
,
RRType
::
SOA
());
EXPECT_FALSE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
expected_errors_
.
push_back
(
"zone example.com/IN: has 0 SOA records"
);
checkIssues
();
// If null callback is specified, checkZone() only returns the final
// result.
ZoneCheckerCallbacks
noerror_callbacks
(
NULL
,
boost
::
bind
(
&
ZoneCheckerTest
::
callback
,
this
,
_1
,
false
));
EXPECT_FALSE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
noerror_callbacks
));
checkIssues
();
// If there are more than 1 SOA RR, it's also an error.
errors_
.
clear
();
soa_
->
addRdata
(
generic
::
SOA
(
soa_txt
));
soa_
->
addRdata
(
generic
::
SOA
(
"ns2.example.com. . 0 0 0 0 0"
));
rrsets_
->
addRRset
(
soa_
);
EXPECT_FALSE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
expected_errors_
.
push_back
(
"zone example.com/IN: has 2 SOA records"
);
checkIssues
();
// If the SOA RRset is "empty", it's treated as an implementation
// (rather than operational) error and results in an exception.
rrsets_
->
removeRRset
(
zname_
,
zclass_
,
RRType
::
SOA
());
soa_
.
reset
(
new
RRset
(
zname_
,
zclass_
,
RRType
::
SOA
(),
RRTTL
(
60
)));
rrsets_
->
addRRset
(
soa_
);
EXPECT_THROW
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
),
Unexpected
);
checkIssues
();
// no error/warning should be reported
// Likewise, if the SOA RRset contains non SOA Rdata, it should be a bug.
rrsets_
->
removeRRset
(
zname_
,
zclass_
,
RRType
::
SOA
());
soa_
.
reset
(
new
RRset
(
zname_
,
zclass_
,
RRType
::
SOA
(),
RRTTL
(
60
)));
soa_
->
addRdata
(
createRdata
(
RRType
::
NS
(),
zclass_
,
"ns.example.com"
));
rrsets_
->
addRRset
(
soa_
);
EXPECT_THROW
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
),
Unexpected
);
checkIssues
();
// no error/warning should be reported
}
TEST_F
(
ZoneCheckerTest
,
checkNS
)
{
// If the zone has no NS at origin it triggers an error.
rrsets_
->
removeRRset
(
zname_
,
zclass_
,
RRType
::
NS
());
EXPECT_FALSE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
expected_errors_
.
push_back
(
"zone example.com/IN: has no NS records"
);
checkIssues
();
// Check two buggy cases like the SOA tests
ns_
.
reset
(
new
RRset
(
zname_
,
zclass_
,
RRType
::
NS
(),
RRTTL
(
60
)));
rrsets_
->
addRRset
(
ns_
);
EXPECT_THROW
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
),
Unexpected
);
checkIssues
();
// no error/warning should be reported
rrsets_
->
removeRRset
(
zname_
,
zclass_
,
RRType
::
NS
());
ns_
.
reset
(
new
RRset
(
zname_
,
zclass_
,
RRType
::
NS
(),
RRTTL
(
60
)));
ns_
->
addRdata
(
createRdata
(
RRType
::
TXT
(),
zclass_
,
"ns.example.com"
));
rrsets_
->
addRRset
(
ns_
);
EXPECT_THROW
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
),
Unexpected
);
checkIssues
();
// no error/warning should be reported
}
TEST_F
(
ZoneCheckerTest
,
checkNSData
)
{
const
Name
ns_name
(
"ns.example.com"
);
// If a ("in-bailiwick") NS name doesn't have an address record, it's
// reported as a warning.
rrsets_
->
removeRRset
(
ns_name
,
zclass_
,
RRType
::
A
());
EXPECT_TRUE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
expected_warns_
.
push_back
(
"zone example.com/IN: NS has no address"
);
checkIssues
();
// Same check, but disabling warning callback. Same result, but without
// the warning.
ZoneCheckerCallbacks
nowarn_callbacks
(
boost
::
bind
(
&
ZoneCheckerTest
::
callback
,
this
,
_1
,
true
),
NULL
);
EXPECT_TRUE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
nowarn_callbacks
));
checkIssues
();
// A tricky case: if the name matches a wildcard, it should technically
// be considered valid, but this checker doesn't check that far and still
// warns.
RRsetPtr
wild
(
new
RRset
(
Name
(
"*.example.com"
),
zclass_
,
RRType
::
A
(),
RRTTL
(
0
)));
wild
->
addRdata
(
in
::
A
(
"192.0.2.255"
));
rrsets_
->
addRRset
(
wild
);
EXPECT_TRUE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
expected_warns_
.
push_back
(
"zone example.com/IN: NS has no address"
);
checkIssues
();
// If there's a CNAME at the name instead, it's an error.
rrsets_
->
removeRRset
(
Name
(
"*.example.com"
),
zclass_
,
RRType
::
A
());
RRsetPtr
cname
(
new
RRset
(
ns_name
,
zclass_
,
RRType
::
CNAME
(),
RRTTL
(
60
)));
cname
->
addRdata
(
generic
::
CNAME
(
"cname.example.com"
));
rrsets_
->
addRRset
(
cname
);
EXPECT_FALSE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
expected_errors_
.
push_back
(
"zone example.com/IN: NS 'ns.example.com' is "
"a CNAME (illegal per RFC2181)"
);
checkIssues
();
// It doesn't have to be A. An AAAA is enough.
rrsets_
->
removeRRset
(
ns_name
,
zclass_
,
RRType
::
CNAME
());
RRsetPtr
aaaa
(
new
RRset
(
ns_name
,
zclass_
,
RRType
::
AAAA
(),
RRTTL
(
60
)));
aaaa
->
addRdata
(
in
::
AAAA
(
"2001:db8::1"
));
rrsets_
->
addRRset
(
aaaa
);
EXPECT_TRUE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
checkIssues
();
// Coexisting CNAME makes it error (CNAME with other record is itself
// invalid, but it's a different issue in this context)
rrsets_
->
addRRset
(
cname
);
EXPECT_FALSE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
expected_errors_
.
push_back
(
"zone example.com/IN: NS 'ns.example.com' is "
"a CNAME (illegal per RFC2181)"
);
checkIssues
();
// It doesn't matter if the NS name is "out of bailiwick".
rrsets_
->
removeRRset
(
ns_name
,
zclass_
,
RRType
::
CNAME
());
rrsets_
->
removeRRset
(
zname_
,
zclass_
,
RRType
::
NS
());
ns_
.
reset
(
new
RRset
(
zname_
,
zclass_
,
RRType
::
NS
(),
RRTTL
(
60
)));
ns_
->
addRdata
(
generic
::
NS
(
"ns.example.org"
));
rrsets_
->
addRRset
(
ns_
);
EXPECT_TRUE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
checkIssues
();
// Note that if the NS name is the origin name, it should be checked
rrsets_
->
removeRRset
(
zname_
,
zclass_
,
RRType
::
NS
());
ns_
.
reset
(
new
RRset
(
zname_
,
zclass_
,
RRType
::
NS
(),
RRTTL
(
60
)));
ns_
->
addRdata
(
generic
::
NS
(
zname_
));
rrsets_
->
addRRset
(
ns_
);
EXPECT_TRUE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
expected_warns_
.
push_back
(
"zone example.com/IN: NS has no address"
);
checkIssues
();
}
TEST_F
(
ZoneCheckerTest
,
checkNSWithDelegation
)
{
// Tests various cases where there's a zone cut due to delegation between
// the zone origin and the NS name. In each case the NS name doesn't have
// an address record.
const
Name
ns_name
(
"ns.child.example.com"
);
// Zone cut due to delegation in the middle; the check for the address
// record should be skipped.
rrsets_
->
removeRRset
(
zname_
,
zclass_
,
RRType
::
NS
());
ns_
.
reset
(
new
RRset
(
zname_
,
zclass_
,
RRType
::
NS
(),
RRTTL
(
60
)));
ns_
->
addRdata
(
generic
::
NS
(
ns_name
));
rrsets_
->
addRRset
(
ns_
);
RRsetPtr
child_ns
(
new
RRset
(
Name
(
"child.example.com"
),
zclass_
,
RRType
::
NS
(),
RRTTL
(
60
)));
child_ns
->
addRdata
(
generic
::
NS
(
"ns.example.org"
));
rrsets_
->
addRRset
(
child_ns
);
EXPECT_TRUE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
checkIssues
();
// Zone cut at the NS name. Same result.
rrsets_
->
removeRRset
(
child_ns
->
getName
(),
zclass_
,
RRType
::
NS
());
child_ns
.
reset
(
new
RRset
(
ns_name
,
zclass_
,
RRType
::
NS
(),
RRTTL
(
60
)));
child_ns
->
addRdata
(
generic
::
NS
(
"ns.example.org"
));
rrsets_
->
addRRset
(
child_ns
);
EXPECT_TRUE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
checkIssues
();
// Zone cut below the NS name. The check applies.
rrsets_
->
removeRRset
(
child_ns
->
getName
(),
zclass_
,
RRType
::
NS
());
child_ns
.
reset
(
new
RRset
(
Name
(
"another.ns.child.example.com"
),
zclass_
,
RRType
::
NS
(),
RRTTL
(
60
)));
child_ns
->
addRdata
(
generic
::
NS
(
"ns.example.org"
));
rrsets_
->
addRRset
(
child_ns
);
EXPECT_TRUE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
expected_warns_
.
push_back
(
"zone example.com/IN: NS has no address"
);
checkIssues
();
}
TEST_F
(
ZoneCheckerTest
,
checkNSWithDNAME
)
{
// Similar to the above case, but the zone cut is due to DNAME. This is
// an invalid configuration.
const
Name
ns_name
(
"ns.child.example.com"
);
// Zone cut due to DNAME at the zone origin. This is an invalid case.
rrsets_
->
removeRRset
(
zname_
,
zclass_
,
RRType
::
NS
());
ns_
.
reset
(
new
RRset
(
zname_
,
zclass_
,
RRType
::
NS
(),
RRTTL
(
60
)));
ns_
->
addRdata
(
generic
::
NS
(
ns_name
));
rrsets_
->
addRRset
(
ns_
);
RRsetPtr
dname
(
new
RRset
(
zname_
,
zclass_
,
RRType
::
DNAME
(),
RRTTL
(
60
)));
dname
->
addRdata
(
generic
::
DNAME
(
"example.org"
));
rrsets_
->
addRRset
(
dname
);
EXPECT_FALSE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
expected_errors_
.
push_back
(
"zone example.com/IN: NS 'ns.child.example.com'"
" is below a DNAME 'example.com'"
);
checkIssues
();
// Zone cut due to DNAME in the middle. Same result.
rrsets_
->
removeRRset
(
zname_
,
zclass_
,
RRType
::
DNAME
());
dname
.
reset
(
new
RRset
(
Name
(
"child.example.com"
),
zclass_
,
RRType
::
DNAME
(),
RRTTL
(
60
)));
dname
->
addRdata
(
generic
::
DNAME
(
"example.org"
));
rrsets_
->
addRRset
(
dname
);
EXPECT_FALSE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
expected_errors_
.
push_back
(
"zone example.com/IN: NS 'ns.child.example.com'"
" is below a DNAME 'child.example.com'"
);
checkIssues
();
// A tricky case: there's also an NS at the name that has DNAME. It's
// prohibited per RFC6672 so we could say it's "undefined". Nevertheless,
// this implementation prefers the NS and skips further checks.
ns_
.
reset
(
new
RRset
(
Name
(
"child.example.com"
),
zclass_
,
RRType
::
NS
(),
RRTTL
(
60
)));
ns_
->
addRdata
(
generic
::
NS
(
"ns.example.org"
));
rrsets_
->
addRRset
(
ns_
);
EXPECT_TRUE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
checkIssues
();
// Zone cut due to DNAME at the NS name. In this case DNAME doesn't
// affect the NS name, so it should result in "no address record" warning.
rrsets_
->
removeRRset
(
dname
->
getName
(),
zclass_
,
RRType
::
DNAME
());
rrsets_
->
removeRRset
(
ns_
->
getName
(),
zclass_
,
RRType
::
NS
());
dname
.
reset
(
new
RRset
(
ns_name
,
zclass_
,
RRType
::
DNAME
(),
RRTTL
(
60
)));
dname
->
addRdata
(
generic
::
DNAME
(
"example.org"
));
rrsets_
->
addRRset
(
dname
);
EXPECT_TRUE
(
checkZone
(
zname_
,
zclass_
,
*
rrsets_
,
callbacks_
));
expected_warns_
.
push_back
(
"zone example.com/IN: NS has no address"
);
checkIssues
();
}
}
src/lib/dns/zone_checker.cc
0 → 100644
View file @
5eb2ff86
// Copyright (C) 2012 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 <dns/zone_checker.h>
#include <dns/name.h>
#include <dns/rdataclass.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <dns/rrset.h>
#include <dns/rrset_collection_base.h>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
using
boost
::
lexical_cast
;
using
std
::
string
;
namespace
isc
{
namespace
dns
{
namespace
{
std
::
string
zoneText
(
const
Name
&
zone_name
,
const
RRClass
&
zone_class
)
{
return
(
zone_name
.
toText
(
true
)
+
"/"
+
zone_class
.
toText
());
}
void
checkSOA
(
const
Name
&
zone_name
,
const
RRClass
&
zone_class
,
const
RRsetCollectionBase
&
zone_rrsets
,
ZoneCheckerCallbacks
&
callback
)
{
ConstRRsetPtr
rrset
=
zone_rrsets
.
find
(
zone_name
,
zone_class
,
RRType
::
SOA
());
size_t
count
=
0
;
if
(
rrset
)
{
for
(
RdataIteratorPtr
rit
=
rrset
->
getRdataIterator
();
!
rit
->
isLast
();
rit
->
next
(),
++
count
)
{
if
(
dynamic_cast
<
const
rdata
::
generic
::
SOA
*>
(
&
rit
->
getCurrent
())
==
NULL
)
{
isc_throw
(
Unexpected
,
"Zone checker found bad RDATA in SOA"
);
}
}
if
(
count
==
0
)
{
// this should be an implementation bug, not an operational error.
isc_throw
(
Unexpected
,
"Zone checker found an empty SOA RRset"
);
}
}
if
(
count
!=
1
)
{
callback
.
error
(
"zone "
+
zoneText
(
zone_name
,
zone_class
)
+
": has "
+
lexical_cast
<
string
>
(
count
)
+
" SOA records"
);
}
}
// Check if a target name is beyond zone cut, either due to delegation or
// DNAME. Note that DNAME works on the origin but not on the name itself,
// while delegation works on the name itself (but the NS at the origin is not
// delegation).
ConstRRsetPtr
findZoneCut
(
const
Name
&
zone_name
,
const
RRClass
&
zone_class
,
const
RRsetCollectionBase
&
zone_rrsets
,
const
Name
&
target_name
)
{
const
unsigned
int
origin_count
=
zone_name
.
getLabelCount
();
const
unsigned
int
target_count
=
target_name
.
getLabelCount
();
assert
(
origin_count
<=
target_count
);
for
(
unsigned
int
l
=
origin_count
;
l
<=
target_count
;
++
l
)
{
const
Name
&
mid_name
=
(
l
==
target_count
)
?
target_name
:
target_name
.
split
(
target_count
-
l
);
ConstRRsetPtr
found
;
if
(
l
!=
origin_count
&&
(
found
=
zone_rrsets
.
find
(
mid_name
,
zone_class
,
RRType
::
NS
()))
!=
NULL
)
{
return
(
found
);
}
if
(
l
!=
target_count
&&
(
found
=
zone_rrsets
.
find
(
mid_name
,
zone_class
,
RRType
::
DNAME
()))
!=
NULL
)
{
return
(
found
);
}
}
return
(
ConstRRsetPtr
());
}