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
ISC Open Source Projects
Kea
Commits
89d7de8e
Commit
89d7de8e
authored
Mar 22, 2013
by
JINMEI Tatuya
Browse files
[master] Merge branch 'trac1938'
parents
459cdc4a
9a3d9604
Changes
14
Hide whitespace changes
Inline
Side-by-side
src/bin/auth/auth_messages.mes
View file @
89d7de8e
...
...
@@ -266,9 +266,16 @@ bug ticket for this issue.
This is a debug message issued when the authoritative server has received
a command on the command channel.
% AUTH_RECEIVED_NOTIFY received incoming NOTIFY for zone
name %1, zone class
%
2
% AUTH_RECEIVED_NOTIFY received incoming NOTIFY for zone
%1/%2 from
%
3
This is a debug message reporting that an incoming NOTIFY was received.
% AUTH_RECEIVED_NOTIFY_NOTAUTH received bad NOTIFY for zone %1/%2 from %3
The authoritative server received a NOTIFY message, but the specified zone
doesn't match any of the zones served by the server. The server doesn't
process the message further, and returns a response with the Rcode being
NOTAUTH. Note: RFC 1996 does not specify the server behavior in this case;
responding with Rcode of NOTAUTH follows BIND 9's behavior.
% AUTH_RESPONSE_FAILURE exception while building response to query: %1
This is a debug message, generated by the authoritative server when an
attempt to create a response to a received DNS packet has failed. The
...
...
src/bin/auth/auth_srv.cc
View file @
89d7de8e
...
...
@@ -747,6 +747,8 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
std
::
auto_ptr
<
TSIGContext
>
tsig_context
,
MessageAttributes
&
stats_attrs
)
{
const
IOEndpoint
&
remote_ep
=
io_message
.
getRemoteEndpoint
();
// for logs
// The incoming notify must contain exactly one question for SOA of the
// zone name.
if
(
message
.
getRRCount
(
Message
::
SECTION_QUESTION
)
!=
1
)
{
...
...
@@ -769,8 +771,23 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
// on, but we don't check these conditions. This behavior is compatible
// with BIND 9.
// TODO check with the conf-mgr whether current server is the auth of the
// zone
// See if we have the specified zone in our data sources; if not return
// NOTAUTH, following BIND 9 (this is not specified in RFC 1996).
bool
is_auth
=
false
;
{
auth
::
DataSrcClientsMgr
::
Holder
datasrc_holder
(
datasrc_clients_mgr_
);
const
shared_ptr
<
datasrc
::
ClientList
>
dsrc_clients
=
datasrc_holder
.
findClientList
(
question
->
getClass
());
is_auth
=
dsrc_clients
&&
dsrc_clients
->
find
(
question
->
getName
(),
true
,
false
).
exact_match_
;
}
if
(
!
is_auth
)
{
LOG_DEBUG
(
auth_logger
,
DBG_AUTH_DETAIL
,
AUTH_RECEIVED_NOTIFY_NOTAUTH
)
.
arg
(
question
->
getName
()).
arg
(
question
->
getClass
()).
arg
(
remote_ep
);
makeErrorMessage
(
renderer_
,
message
,
buffer
,
Rcode
::
NOTAUTH
(),
stats_attrs
,
tsig_context
);
return
(
true
);
}
// In the code that follows, we simply ignore the notify if any internal
// error happens rather than returning (e.g.) SERVFAIL. RFC 1996 is
...
...
@@ -782,10 +799,9 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
}
LOG_DEBUG
(
auth_logger
,
DBG_AUTH_DETAIL
,
AUTH_RECEIVED_NOTIFY
)
.
arg
(
question
->
getName
()).
arg
(
question
->
getClass
());
.
arg
(
question
->
getName
()).
arg
(
question
->
getClass
())
.
arg
(
remote_ep
)
;
const
string
remote_ip_address
=
io_message
.
getRemoteEndpoint
().
getAddress
().
toText
();
const
string
remote_ip_address
=
remote_ep
.
getAddress
().
toText
();
static
const
string
command_template_start
=
"{
\"
command
\"
: [
\"
notify
\"
, {
\"
zone_name
\"
:
\"
"
;
static
const
string
command_template_master
=
"
\"
,
\"
master
\"
:
\"
"
;
...
...
src/bin/auth/tests/auth_srv_unittest.cc
View file @
89d7de8e
...
...
@@ -244,6 +244,62 @@ createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
renderer
.
getLength
());
}
void
installDataSrcClientLists
(
AuthSrv
&
server
,
ClientListMapPtr
lists
)
{
// For now, we use explicit swap than reconfigure() because the latter
// involves a separate thread and cannot guarantee the new config is
// available for the subsequent test.
server
.
getDataSrcClientsMgr
().
setDataSrcClientLists
(
lists
);
}
void
updateDatabase
(
AuthSrv
&
server
,
const
char
*
params
)
{
const
ConstElementPtr
config
(
Element
::
fromJSON
(
"{"
"
\"
IN
\"
: [{"
"
\"
type
\"
:
\"
sqlite3
\"
,"
"
\"
params
\"
: "
+
string
(
params
)
+
"}]}"
));
installDataSrcClientLists
(
server
,
configureDataSource
(
config
));
}
// Note: if with_static is set to true, the corresponding test should be
// disabled in case of USE_STATIC_LINK.
void
updateInMemory
(
AuthSrv
&
server
,
const
char
*
origin
,
const
char
*
filename
,
bool
with_static
=
true
)
{
string
spec_txt
=
"{"
"
\"
IN
\"
: [{"
"
\"
type
\"
:
\"
MasterFiles
\"
,"
"
\"
params
\"
: {"
"
\"
"
+
string
(
origin
)
+
"
\"
:
\"
"
+
string
(
filename
)
+
"
\"
"
" },"
"
\"
cache-enable
\"
: true"
"}]"
;
if
(
with_static
)
{
spec_txt
+=
",
\"
CH
\"
: [{"
"
\"
type
\"
:
\"
static
\"
,"
"
\"
params
\"
:
\"
"
+
string
(
STATIC_DSRC_FILE
)
+
"
\"
"
"}]"
;
}
spec_txt
+=
"}"
;
const
ConstElementPtr
config
(
Element
::
fromJSON
(
spec_txt
));
installDataSrcClientLists
(
server
,
configureDataSource
(
config
));
}
// Note: tests using this function should be disabled in case of
// USE_STATIC_LINK.
void
updateBuiltin
(
AuthSrv
&
server
)
{
const
ConstElementPtr
config
(
Element
::
fromJSON
(
"{"
"
\"
CH
\"
: [{"
"
\"
type
\"
:
\"
static
\"
,"
"
\"
params
\"
:
\"
"
+
string
(
STATIC_DSRC_FILE
)
+
"
\"
"
"}]}"
));
installDataSrcClientLists
(
server
,
configureDataSource
(
config
));
}
// We did not configure any client lists. Therefore it should be REFUSED
TEST_F
(
AuthSrvTest
,
noClientList
)
{
UnitTestUtil
::
createRequestMessage
(
request_message
,
Opcode
::
QUERY
(),
...
...
@@ -647,8 +703,10 @@ TEST_F(AuthSrvTest, IXFRDisconnectFail) {
}
TEST_F
(
AuthSrvTest
,
notify
)
{
updateInMemory
(
server
,
"example."
,
CONFIG_INMEMORY_EXAMPLE
,
false
);
UnitTestUtil
::
createRequestMessage
(
request_message
,
Opcode
::
NOTIFY
(),
default_qid
,
Name
(
"example
.com
"
),
default_qid
,
Name
(
"example"
),
RRClass
::
IN
(),
RRType
::
SOA
());
request_message
.
setHeaderFlag
(
Message
::
HEADERFLAG_AA
);
createRequestPacket
(
request_message
,
IPPROTO_UDP
);
...
...
@@ -664,7 +722,7 @@ TEST_F(AuthSrvTest, notify) {
stringValue
());
ConstElementPtr
notify_args
=
notify_session
.
getSentMessage
()
->
get
(
"command"
)
->
get
(
1
);
EXPECT_EQ
(
"example.
com.
"
,
notify_args
->
get
(
"zone_name"
)
->
stringValue
());
EXPECT_EQ
(
"example."
,
notify_args
->
get
(
"zone_name"
)
->
stringValue
());
EXPECT_EQ
(
DEFAULT_REMOTE_ADDRESS
,
notify_args
->
get
(
"master"
)
->
stringValue
());
EXPECT_EQ
(
"IN"
,
notify_args
->
get
(
"zone_class"
)
->
stringValue
());
...
...
@@ -675,7 +733,7 @@ TEST_F(AuthSrvTest, notify) {
// The question must be identical to that of the received notify
ConstQuestionPtr
question
=
*
parse_message
->
beginQuestion
();
EXPECT_EQ
(
Name
(
"example
.com
"
),
question
->
getName
());
EXPECT_EQ
(
Name
(
"example"
),
question
->
getName
());
EXPECT_EQ
(
RRClass
::
IN
(),
question
->
getClass
());
EXPECT_EQ
(
RRType
::
SOA
(),
question
->
getType
());
...
...
@@ -690,10 +748,17 @@ TEST_F(AuthSrvTest, notify) {
checkStatisticsCounters
(
stats_after
,
expect
);
}
#ifdef USE_STATIC_LINK
TEST_F
(
AuthSrvTest
,
DISABLED_notifyForCHClass
)
{
#else
TEST_F
(
AuthSrvTest
,
notifyForCHClass
)
{
// Same as the previous test, but for the CH RRClass.
#endif
// Same as the previous test, but for the CH RRClass (so we install the
// builtin (static) data source.
updateBuiltin
(
server
);
UnitTestUtil
::
createRequestMessage
(
request_message
,
Opcode
::
NOTIFY
(),
default_qid
,
Name
(
"
example.com
"
),
default_qid
,
Name
(
"
bind
"
),
RRClass
::
CH
(),
RRType
::
SOA
());
request_message
.
setHeaderFlag
(
Message
::
HEADERFLAG_AA
);
createRequestPacket
(
request_message
,
IPPROTO_UDP
);
...
...
@@ -773,9 +838,11 @@ TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
}
TEST_F
(
AuthSrvTest
,
notifyWithoutAA
)
{
updateInMemory
(
server
,
"example."
,
CONFIG_INMEMORY_EXAMPLE
,
false
);
// implicitly leave the AA bit off. our implementation will accept it.
UnitTestUtil
::
createRequestMessage
(
request_message
,
Opcode
::
NOTIFY
(),
default_qid
,
Name
(
"example
.com
"
),
default_qid
,
Name
(
"example"
),
RRClass
::
IN
(),
RRType
::
SOA
());
createRequestPacket
(
request_message
,
IPPROTO_UDP
);
server
.
processMessage
(
*
io_message
,
*
parse_message
,
*
response_obuffer
,
...
...
@@ -786,8 +853,10 @@ TEST_F(AuthSrvTest, notifyWithoutAA) {
}
TEST_F
(
AuthSrvTest
,
notifyWithErrorRcode
)
{
updateInMemory
(
server
,
"example."
,
CONFIG_INMEMORY_EXAMPLE
,
false
);
UnitTestUtil
::
createRequestMessage
(
request_message
,
Opcode
::
NOTIFY
(),
default_qid
,
Name
(
"example
.com
"
),
default_qid
,
Name
(
"example"
),
RRClass
::
IN
(),
RRType
::
SOA
());
request_message
.
setHeaderFlag
(
Message
::
HEADERFLAG_AA
);
request_message
.
setRcode
(
Rcode
::
SERVFAIL
());
...
...
@@ -800,10 +869,12 @@ TEST_F(AuthSrvTest, notifyWithErrorRcode) {
}
TEST_F
(
AuthSrvTest
,
notifyWithoutSession
)
{
updateInMemory
(
server
,
"example."
,
CONFIG_INMEMORY_EXAMPLE
,
false
);
server
.
setXfrinSession
(
NULL
);
UnitTestUtil
::
createRequestMessage
(
request_message
,
Opcode
::
NOTIFY
(),
default_qid
,
Name
(
"example
.com
"
),
default_qid
,
Name
(
"example"
),
RRClass
::
IN
(),
RRType
::
SOA
());
request_message
.
setHeaderFlag
(
Message
::
HEADERFLAG_AA
);
createRequestPacket
(
request_message
,
IPPROTO_UDP
);
...
...
@@ -816,10 +887,12 @@ TEST_F(AuthSrvTest, notifyWithoutSession) {
}
TEST_F
(
AuthSrvTest
,
notifySendFail
)
{
updateInMemory
(
server
,
"example."
,
CONFIG_INMEMORY_EXAMPLE
,
false
);
notify_session
.
disableSend
();
UnitTestUtil
::
createRequestMessage
(
request_message
,
Opcode
::
NOTIFY
(),
default_qid
,
Name
(
"example
.com
"
),
default_qid
,
Name
(
"example"
),
RRClass
::
IN
(),
RRType
::
SOA
());
request_message
.
setHeaderFlag
(
Message
::
HEADERFLAG_AA
);
createRequestPacket
(
request_message
,
IPPROTO_UDP
);
...
...
@@ -830,10 +903,12 @@ TEST_F(AuthSrvTest, notifySendFail) {
}
TEST_F
(
AuthSrvTest
,
notifyReceiveFail
)
{
updateInMemory
(
server
,
"example."
,
CONFIG_INMEMORY_EXAMPLE
,
false
);
notify_session
.
disableReceive
();
UnitTestUtil
::
createRequestMessage
(
request_message
,
Opcode
::
NOTIFY
(),
default_qid
,
Name
(
"example
.com
"
),
default_qid
,
Name
(
"example"
),
RRClass
::
IN
(),
RRType
::
SOA
());
request_message
.
setHeaderFlag
(
Message
::
HEADERFLAG_AA
);
createRequestPacket
(
request_message
,
IPPROTO_UDP
);
...
...
@@ -843,10 +918,12 @@ TEST_F(AuthSrvTest, notifyReceiveFail) {
}
TEST_F
(
AuthSrvTest
,
notifyWithBogusSessionMessage
)
{
updateInMemory
(
server
,
"example."
,
CONFIG_INMEMORY_EXAMPLE
,
false
);
notify_session
.
setMessage
(
Element
::
fromJSON
(
"{
\"
foo
\"
: 1}"
));
UnitTestUtil
::
createRequestMessage
(
request_message
,
Opcode
::
NOTIFY
(),
default_qid
,
Name
(
"example
.com
"
),
default_qid
,
Name
(
"example"
),
RRClass
::
IN
(),
RRType
::
SOA
());
request_message
.
setHeaderFlag
(
Message
::
HEADERFLAG_AA
);
createRequestPacket
(
request_message
,
IPPROTO_UDP
);
...
...
@@ -856,11 +933,13 @@ TEST_F(AuthSrvTest, notifyWithBogusSessionMessage) {
}
TEST_F
(
AuthSrvTest
,
notifyWithSessionMessageError
)
{
updateInMemory
(
server
,
"example."
,
CONFIG_INMEMORY_EXAMPLE
,
false
);
notify_session
.
setMessage
(
Element
::
fromJSON
(
"{
\"
result
\"
: [1,
\"
FAIL
\"
]}"
));
UnitTestUtil
::
createRequestMessage
(
request_message
,
Opcode
::
NOTIFY
(),
default_qid
,
Name
(
"example
.com
"
),
default_qid
,
Name
(
"example"
),
RRClass
::
IN
(),
RRType
::
SOA
());
request_message
.
setHeaderFlag
(
Message
::
HEADERFLAG_AA
);
createRequestPacket
(
request_message
,
IPPROTO_UDP
);
...
...
@@ -869,49 +948,50 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
EXPECT_FALSE
(
dnsserv
.
hasAnswer
());
}
void
installDataSrcClientLists
(
AuthSrv
&
server
,
ClientListMapPtr
lists
)
{
// For now, we use explicit swap than reconfigure() because the latter
// involves a separate thread and cannot guarantee the new config is
// available for the subsequent test.
server
.
getDataSrcClientsMgr
().
setDataSrcClientLists
(
lists
);
}
TEST_F
(
AuthSrvTest
,
notifyNotAuth
)
{
// If the server doesn't have authority of the specified zone in NOTIFY,
// it will return NOTAUTH
updateInMemory
(
server
,
"example."
,
CONFIG_INMEMORY_EXAMPLE
,
false
);
void
updateDatabase
(
AuthSrv
&
server
,
const
char
*
params
)
{
const
ConstElementPtr
config
(
Element
::
fromJSON
(
"{"
"
\"
IN
\"
: [{"
"
\"
type
\"
:
\"
sqlite3
\"
,"
"
\"
params
\"
: "
+
string
(
params
)
+
"}]}"
));
installDataSrcClientLists
(
server
,
configureDataSource
(
config
));
UnitTestUtil
::
createRequestMessage
(
request_message
,
Opcode
::
NOTIFY
(),
default_qid
,
Name
(
"example.com"
),
RRClass
::
IN
(),
RRType
::
SOA
());
request_message
.
setHeaderFlag
(
Message
::
HEADERFLAG_AA
);
createRequestPacket
(
request_message
,
IPPROTO_UDP
);
server
.
processMessage
(
*
io_message
,
*
parse_message
,
*
response_obuffer
,
&
dnsserv
);
EXPECT_TRUE
(
dnsserv
.
hasAnswer
());
headerCheck
(
*
parse_message
,
default_qid
,
Rcode
::
NOTAUTH
(),
Opcode
::
NOTIFY
().
getCode
(),
QR_FLAG
/* no AA */
,
1
,
0
,
0
,
0
);
}
void
updateInMemory
(
AuthSrv
&
server
,
const
char
*
origin
,
const
char
*
filename
)
{
const
ConstElementPtr
config
(
Element
::
fromJSON
(
"{"
"
\"
IN
\"
: [{"
"
\"
type
\"
:
\"
MasterFiles
\"
,"
"
\"
params
\"
: {"
"
\"
"
+
string
(
origin
)
+
"
\"
:
\"
"
+
string
(
filename
)
+
"
\"
"
" },"
"
\"
cache-enable
\"
: true"
"}],"
"
\"
CH
\"
: [{"
"
\"
type
\"
:
\"
static
\"
,"
"
\"
params
\"
:
\"
"
+
string
(
STATIC_DSRC_FILE
)
+
"
\"
"
"}]}"
));
installDataSrcClientLists
(
server
,
configureDataSource
(
config
));
TEST_F
(
AuthSrvTest
,
notifyNotAuthSubDomain
)
{
// Similar to the previous case, but checking partial match doesn't confuse
// the processing.
updateInMemory
(
server
,
"example."
,
CONFIG_INMEMORY_EXAMPLE
,
false
);
UnitTestUtil
::
createRequestMessage
(
request_message
,
Opcode
::
NOTIFY
(),
default_qid
,
Name
(
"child.example"
),
RRClass
::
IN
(),
RRType
::
SOA
());
createRequestPacket
(
request_message
,
IPPROTO_UDP
);
server
.
processMessage
(
*
io_message
,
*
parse_message
,
*
response_obuffer
,
&
dnsserv
);
headerCheck
(
*
parse_message
,
default_qid
,
Rcode
::
NOTAUTH
(),
Opcode
::
NOTIFY
().
getCode
(),
QR_FLAG
,
1
,
0
,
0
,
0
);
}
void
updateBuiltin
(
AuthSrv
&
server
)
{
const
ConstElementPtr
config
(
Element
::
fromJSON
(
"{"
"
\"
CH
\"
: [{"
"
\"
type
\"
:
\"
static
\"
,"
"
\"
params
\"
:
\"
"
+
string
(
STATIC_DSRC_FILE
)
+
"
\"
"
"}]}"
));
installDataSrcClientLists
(
server
,
configureDataSource
(
config
));
TEST_F
(
AuthSrvTest
,
notifyNotAuthNoClass
)
{
// Likewise, and there's not even a data source in the specified class.
updateInMemory
(
server
,
"example."
,
CONFIG_INMEMORY_EXAMPLE
,
false
);
UnitTestUtil
::
createRequestMessage
(
request_message
,
Opcode
::
NOTIFY
(),
default_qid
,
Name
(
"example"
),
RRClass
::
CH
(),
RRType
::
SOA
());
createRequestPacket
(
request_message
,
IPPROTO_UDP
);
server
.
processMessage
(
*
io_message
,
*
parse_message
,
*
response_obuffer
,
&
dnsserv
);
headerCheck
(
*
parse_message
,
default_qid
,
Rcode
::
NOTAUTH
(),
Opcode
::
NOTIFY
().
getCode
(),
QR_FLAG
,
1
,
0
,
0
,
0
);
}
// Try giving the server a TSIG signed request and see it can anwer signed as
...
...
src/bin/zonemgr/tests/zonemgr_test.py
View file @
89d7de8e
...
...
@@ -314,16 +314,22 @@ class TestZonemgrRefresh(unittest.TestCase):
sqlite3_ds
.
get_zone_soa
=
old_get_zone_soa
def
test_zone_handle_notify
(
self
):
self
.
zone_refresh
.
zone_handle_notify
(
ZONE_NAME_CLASS1_IN
,
"127.0.0.1"
)
notify_master
=
self
.
zone_refresh
.
_zonemgr_refresh_info
[
ZONE_NAME_CLASS1_IN
][
"notify_master"
]
self
.
assertTrue
(
self
.
zone_refresh
.
zone_handle_notify
(
ZONE_NAME_CLASS1_IN
,
"127.0.0.1"
))
notify_master
=
self
.
zone_refresh
.
\
_zonemgr_refresh_info
[
ZONE_NAME_CLASS1_IN
][
"notify_master"
]
self
.
assertEqual
(
"127.0.0.1"
,
notify_master
)
zone_timeout
=
self
.
zone_refresh
.
_zonemgr_refresh_info
[
ZONE_NAME_CLASS1_IN
][
"next_refresh_time"
]
zone_timeout
=
self
.
zone_refresh
.
\
_zonemgr_refresh_info
[
ZONE_NAME_CLASS1_IN
][
"next_refresh_time"
]
current_time
=
time
.
time
()
self
.
assertTrue
(
zone_timeout
<=
current_time
)
self
.
assertRaises
(
ZonemgrException
,
self
.
zone_refresh
.
zone_handle_notify
,
\
ZONE_NAME_CLASS3_CH
,
"127.0.0.1"
)
self
.
assertRaises
(
ZonemgrException
,
self
.
zone_refresh
.
zone_handle_notify
,
\
ZONE_NAME_CLASS3_IN
,
"127.0.0.1"
)
# If the specified zone does not in the configured secondary list,
# it should return False.
self
.
assertFalse
(
self
.
zone_refresh
.
zone_handle_notify
(
ZONE_NAME_CLASS3_CH
,
"127.0.0.1"
))
self
.
assertFalse
(
self
.
zone_refresh
.
zone_handle_notify
(
ZONE_NAME_CLASS3_IN
,
"127.0.0.1"
))
def
test_zone_refresh_success
(
self
):
soa_rdata
=
'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600'
...
...
@@ -607,6 +613,19 @@ class TestZonemgrRefresh(unittest.TestCase):
config
,
self
.
cc_session
)
class
MyZonemgr
(
Zonemgr
):
class
DummySocket
:
"""This dummy class simply steal send() to record any transmitted data.
"""
def
__init__
(
self
):
self
.
sent_data
=
[]
def
send
(
self
,
data
):
self
.
sent_data
.
append
(
data
)
class
DummyLock
:
def
__enter__
(
self
):
pass
def
__exit__
(
self
,
type
,
value
,
traceback
):
pass
def
__init__
(
self
):
self
.
_db_file
=
TEST_SQLITE3_DBFILE
...
...
@@ -621,6 +640,8 @@ class MyZonemgr(Zonemgr):
"reload_jitter"
:
0.75
,
"secondary_zones"
:
[]
}
self
.
_lock
=
self
.
DummyLock
()
self
.
_master_socket
=
self
.
DummySocket
()
def
_start_zone_refresh_timer
(
self
):
pass
...
...
@@ -672,15 +693,21 @@ class TestZonemgr(unittest.TestCase):
self
.
assertEqual
(
TEST_SQLITE3_DBFILE
,
self
.
zonemgr
.
get_db_file
())
def
test_parse_cmd_params
(
self
):
params1
=
{
"zone_name"
:
"example.com."
,
"zone_class"
:
"CH"
,
"master"
:
"127.0.0.1"
}
params1
=
{
"zone_name"
:
"example.com."
,
"zone_class"
:
"CH"
,
"master"
:
"127.0.0.1"
}
answer1
=
(
ZONE_NAME_CLASS3_CH
,
"127.0.0.1"
)
self
.
assertEqual
(
answer1
,
self
.
zonemgr
.
_parse_cmd_params
(
params1
,
ZONE_NOTIFY_COMMAND
))
self
.
assertEqual
(
answer1
,
self
.
zonemgr
.
_parse_cmd_params
(
params1
,
ZONE_NOTIFY_COMMAND
))
params2
=
{
"zone_name"
:
"example.com."
,
"zone_class"
:
"IN"
}
answer2
=
ZONE_NAME_CLASS3_IN
self
.
assertEqual
(
answer2
,
self
.
zonemgr
.
_parse_cmd_params
(
params2
,
notify_out
.
ZONE_NEW_DATA_READY_CMD
))
self
.
assertRaises
(
ZonemgrException
,
self
.
zonemgr
.
_parse_cmd_params
,
params2
,
ZONE_NOTIFY_COMMAND
)
self
.
assertEqual
(
answer2
,
self
.
zonemgr
.
_parse_cmd_params
(
params2
,
notify_out
.
ZONE_NEW_DATA_READY_CMD
))
self
.
assertRaises
(
ZonemgrException
,
self
.
zonemgr
.
_parse_cmd_params
,
params2
,
ZONE_NOTIFY_COMMAND
)
params1
=
{
"zone_class"
:
"CH"
}
self
.
assertRaises
(
ZonemgrException
,
self
.
zonemgr
.
_parse_cmd_params
,
params2
,
ZONE_NOTIFY_COMMAND
)
self
.
assertRaises
(
ZonemgrException
,
self
.
zonemgr
.
_parse_cmd_params
,
params2
,
ZONE_NOTIFY_COMMAND
)
def
test_config_data_check
(
self
):
# jitter should not be bigger than half of the original value
...
...
@@ -697,6 +724,26 @@ class TestZonemgr(unittest.TestCase):
self
.
zonemgr
.
run
()
self
.
assertTrue
(
self
.
zonemgr
.
_module_cc
.
stopped
)
def
test_command_handler_notify
(
self
):
"""Check the result of NOTIFY command."""
self
.
zonemgr
.
_zone_refresh
=
MyZonemgrRefresh
()
# On successful case, the other thread will be notified via
# _master_socket.
self
.
zonemgr
.
_zone_refresh
.
zone_handle_notify
=
lambda
x
,
y
:
True
self
.
zonemgr
.
command_handler
(
"notify"
,
{
"zone_name"
:
"example."
,
"zone_class"
:
"IN"
,
"master"
:
"192.0.2.1"
})
self
.
assertEqual
([
b
" "
],
self
.
zonemgr
.
_master_socket
.
sent_data
)
# If the specified is not found in the secondary list, it doesn't
# bother to wake the thread (sent_data shouldn't change)
self
.
zonemgr
.
_zone_refresh
.
zone_handle_notify
=
lambda
x
,
y
:
False
self
.
zonemgr
.
command_handler
(
"notify"
,
{
"zone_name"
:
"example."
,
"zone_class"
:
"IN"
,
"master"
:
"192.0.2.1"
})
self
.
assertEqual
([
b
" "
],
self
.
zonemgr
.
_master_socket
.
sent_data
)
if
__name__
==
"__main__"
:
isc
.
log
.
resetUnitTestRootLogger
()
unittest
.
main
()
src/bin/zonemgr/zonemgr.py.in
View file @
89d7de8e
...
...
@@ -191,14 +191,31 @@ class ZonemgrRefresh:
self._set_zone_retry_timer(zone_name_class)
def zone_handle_notify(self, zone_name_class, master):
"""Handle zone notify"""
if (self._zone_not_exist(zone_name_class)):
logger.error(ZONEMGR_UNKNOWN_ZONE_NOTIFIED, zone_name_class[0],
zone_name_class[1], master)
raise ZonemgrException("[b10-zonemgr] Notified zone (%s, %s) "
"doesn't belong to zonemgr" % zone_name_class)
"""Handle an incomding NOTIFY message via the Auth module.
It returns True if the specified zone matches one of the locally
configured list of secondary zones; otherwise returns False.
In the latter case it assumes the server is a primary (master) of the
zone; the Auth module should have rejected the case where it's not
even authoritative for the zone.
Note: to be more robust and less independent from other module's
behavior, it's probably safer to check the authority condition here,
too. But right now it uses SQLite3 specific API (to be deprecated),
so we rather rely on Auth.
Parameters:
zone_name_class (Name, RRClass): the notified zone name and class.
master (str): textual address of the NOTIFY sender.
"""
if self._zone_not_exist(zone_name_class):
logger.debug(DBG_ZONEMGR_BASIC, ZONEMGR_ZONE_NOTIFY_NOT_SECONDARY,
zone_name_class[0], zone_name_class[1], master)
return False
self._set_zone_notifier_master(zone_name_class, master)
self._set_zone_notify_timer(zone_name_class)
return True
def zonemgr_reload_zone(self, zone_name_class):
""" Reload a zone."""
...
...
@@ -423,7 +440,7 @@ class ZonemgrRefresh:
# Ask the thread to stop
self._running = False
self._write_sock.send(b'shutdown') # make self._read_sock readble
self._write_sock.send(b'shutdown') # make self._read_sock read
a
ble
# Wait for it to actually finnish
self._thread.join()
# Wipe out what we do not need
...
...
@@ -630,27 +647,33 @@ class Zonemgr:
""" Handle Auth notify command"""
# master is the source sender of the notify message.
zone_name_class, master = self._parse_cmd_params(args, command)
logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_NOTIFY, zone_name_class[0], zone_name_class[1])
logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_NOTIFY,
zone_name_class[0], zone_name_class[1])
with self._lock:
self._zone_refresh.zone_handle_notify(zone_name_class, master)
# Send notification to zonemgr timer thread
self._master_socket.send(b" ")# make self._slave_socket readble
need_refresh = self._zone_refresh.zone_handle_notify(
zone_name_class, master)
if need_refresh:
# Send notification to zonemgr timer thread by making
# self._slave_socket readable.
self._master_socket.send(b" ")
elif command == notify_out.ZONE_NEW_DATA_READY_CMD:
""" Handle xfrin success command"""
zone_name_class = self._parse_cmd_params(args, command)
logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_XFRIN_SUCCESS, zone_name_class[0], zone_name_class[1])
logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_XFRIN_SUCCESS,
zone_name_class[0], zone_name_class[1])
with self._lock:
self._zone_refresh.zone_refresh_success(zone_name_class)
self._master_socket.send(b" ")# make self._slave_socket readble
self._master_socket.send(b" ")# make self._slave_socket read
a
ble
elif command == notify_out.ZONE_XFRIN_FAILED:
""" Handle xfrin fail command"""
zone_name_class = self._parse_cmd_params(args, command)
logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_XFRIN_FAILED, zone_name_class[0], zone_name_class[1])
logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_XFRIN_FAILED,
zone_name_class[0], zone_name_class[1])
with self._lock:
self._zone_refresh.zone_refresh_fail(zone_name_class)
self._master_socket.send(b" ")# make self._slave_socket readble
self._master_socket.send(b" ")# make self._slave_socket read
a
ble
elif command == "shutdown":
logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_SHUTDOWN)
...
...
src/bin/zonemgr/zonemgr_messages.mes
View file @
89d7de8e
...
...
@@ -138,14 +138,19 @@ zone, or, if this error appears without the administrator giving transfer
commands, it can indicate an error in the program, as it should not have
initiated transfers of unknown zones on its own.
% ZONEMGR_UNKNOWN_ZONE_NOTIFIED notified zone %1/%2 from %3 is not known to the zone manager
A NOTIFY was received but the zone that was the subject of the operation
is not being managed by the zone manager. This may indicate an error
in the program (as the operation should not have been initiated if this
were the case). Please submit a bug report.
% ZONEMGR_UNKNOWN_ZONE_SUCCESS zone %1 (class %2) is not known to the zone manager
An XFRIN operation has succeeded but the zone received is not being
managed by the zone manager. This may indicate an error in the program
(as the operation should not have been initiated if this were the case).
Please submit a bug report.
% ZONEMGR_ZONE_NOTIFY_NOT_SECONDARY notify for zone %1/%2 from %3 received but not in secondaries
A NOTIFY was received but the zone is not listed in the configured
secondary zones of the zone manager. The most common reason for this
is that it's simply received by a primary server of the zone. Another
possibility is a configuration error that it's not configured as a
secondary while it should be. In either case, the zone manager does
not take action in terms of zone management, and the authoritative
server will respond to it like in the secondary case. If this is a
configuration error, it will be noticed by the fact that the zone
isn't updated even after a change is made in the primary server.
src/lib/python/isc/notify/notify_out.py
View file @
89d7de8e
...
...
@@ -570,6 +570,10 @@ class NotifyOut:
logger
.
error
(
NOTIFY_OUT_REPLY_UNCAUGHT_EXCEPTION
,
err
)
return
_BAD_REPLY_PACKET
logger
.
debug
(
logger
.
DBGLVL_TRACE_BASIC
,
NOTIFY_OUT_REPLY_RECEIVED
,
zone_notify_info
.
zone_name
,
zone_notify_info
.
zone_class
,
from_addr
[
0
],
from_addr
[
1
],
msg
.
get_rcode
())
return
_REPLY_OK
def
_get_notify_reply
(
self
,
sock
,
tgt_addr
):
...
...
src/lib/python/isc/notify/notify_out_messages.mes
View file @
89d7de8e
...
...
@@ -60,6 +60,11 @@ given address, but the reply did not have the QR bit set to one.
Since there was a response, no more notifies will be sent to this
server for this notification event.
% NOTIFY_OUT_REPLY_RECEIVED Zone %1/%2: notify response from %3:%4: %5
The notify_out library sent a notify message to the nameserver at
the given address, and received a response. Its Rcode will be shown,