Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Adam Osuchowski
Kea
Commits
8e00f359
Commit
8e00f359
authored
Jul 14, 2011
by
JINMEI Tatuya
Browse files
[master] Merge branch 'trac910'
parents
f63ff922
f52ff519
Changes
18
Hide whitespace changes
Inline
Side-by-side
src/lib/dns/message.cc
View file @
8e00f359
...
...
@@ -239,7 +239,28 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
"Message rendering attempted without Opcode set"
);
}
// Reserve the space for TSIG (if needed) so that we can handle truncation
// case correctly later when that happens. orig_xxx variables remember
// some configured parameters of renderer in case they are needed in
// truncation processing below.
const
size_t
tsig_len
=
(
tsig_ctx
!=
NULL
)
?
tsig_ctx
->
getTSIGLength
()
:
0
;
const
size_t
orig_msg_len_limit
=
renderer
.
getLengthLimit
();
const
AbstractMessageRenderer
::
CompressMode
orig_compress_mode
=
renderer
.
getCompressMode
();
if
(
tsig_len
>
0
)
{
if
(
tsig_len
>
orig_msg_len_limit
)
{
isc_throw
(
InvalidParameter
,
"Failed to render DNS message: "
"too small limit for a TSIG ("
<<
orig_msg_len_limit
<<
")"
);
}
renderer
.
setLengthLimit
(
orig_msg_len_limit
-
tsig_len
);
}
// reserve room for the header
if
(
renderer
.
getLengthLimit
()
<
HEADERLEN
)
{
isc_throw
(
InvalidParameter
,
"Failed to render DNS message: "
"too small limit for a Header"
);
}
renderer
.
skip
(
HEADERLEN
);
uint16_t
qdcount
=
...
...
@@ -284,6 +305,22 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
}
}
// If we're adding a TSIG to a truncated message, clear all RRsets
// from the message except for the question before adding the TSIG.
// If even (some of) the question doesn't fit, don't include it.
if
(
tsig_ctx
!=
NULL
&&
renderer
.
isTruncated
())
{
renderer
.
clear
();
renderer
.
setLengthLimit
(
orig_msg_len_limit
-
tsig_len
);
renderer
.
setCompressMode
(
orig_compress_mode
);
renderer
.
skip
(
HEADERLEN
);
qdcount
=
for_each
(
questions_
.
begin
(),
questions_
.
end
(),
RenderSection
<
QuestionPtr
>
(
renderer
,
false
)).
getTotalCount
();
ancount
=
0
;
nscount
=
0
;
arcount
=
0
;
}
// Adjust the counter buffer.
// XXX: these may not be equal to the number of corresponding entries
// in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR
...
...
@@ -315,10 +352,16 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
renderer
.
writeUint16At
(
arcount
,
header_pos
);
// Add TSIG, if necessary, at the end of the message.
// TODO: truncate case consideration
if
(
tsig_ctx
!=
NULL
)
{
tsig_ctx
->
sign
(
qid_
,
renderer
.
getData
(),
renderer
.
getLength
())
->
toWire
(
renderer
);
// Release the reserved space in the renderer.
renderer
.
setLengthLimit
(
orig_msg_len_limit
);
const
int
tsig_count
=
tsig_ctx
->
sign
(
qid_
,
renderer
.
getData
(),
renderer
.
getLength
())
->
toWire
(
renderer
);
if
(
tsig_count
!=
1
)
{
isc_throw
(
Unexpected
,
"Failed to render a TSIG RR"
);
}
// update the ARCOUNT for the TSIG RR. Note that for a sane DNS
// message arcount should never overflow to 0.
...
...
src/lib/dns/message.h
View file @
8e00f359
...
...
@@ -565,6 +565,17 @@ public:
/// \c tsig_ctx will be updated based on the fact it was used for signing
/// and with the latest MAC.
///
/// \exception InvalidMessageOperation The message is not in the Render
/// mode, or either Rcode or Opcode is not set.
/// \exception InvalidParameter The allowable limit of \c renderer is too
/// small for a TSIG or the Header section. Note that this shouldn't
/// happen with parameters as defined in the standard protocols,
/// so it's more likely a program bug.
/// \exception Unexpected Rendering the TSIG RR fails. The implementation
/// internally makes sure this doesn't happen, so if that ever occurs
/// it should mean a bug either in the TSIG context or in the renderer
/// implementation.
///
/// \param renderer See the other version
/// \param tsig_ctx A TSIG context that is to be used for signing the
/// message
...
...
src/lib/dns/python/message_python.cc
View file @
8e00f359
...
...
@@ -703,6 +703,15 @@ Message_toWire(s_Message* self, PyObject* args) {
// python program has a bug.
PyErr_SetString
(
po_TSIGContextError
,
ex
.
what
());
return
(
NULL
);
}
catch
(
const
std
::
exception
&
ex
)
{
// Other exceptions should be rare (most likely an implementation
// bug)
PyErr_SetString
(
po_TSIGContextError
,
ex
.
what
());
return
(
NULL
);
}
catch
(...)
{
PyErr_SetString
(
PyExc_RuntimeError
,
"Unexpected C++ exception in Message.to_wire"
);
return
(
NULL
);
}
}
PyErr_Clear
();
...
...
src/lib/dns/python/tests/message_python_test.py
View file @
8e00f359
...
...
@@ -21,6 +21,7 @@ import unittest
import
os
from
pydnspp
import
*
from
testutil
import
*
from
pyunittests_util
import
fix_current_time
# helper functions for tests taken from c++ unittests
if
"TESTDATA_PATH"
in
os
.
environ
:
...
...
@@ -31,7 +32,7 @@ else:
def
factoryFromFile
(
message
,
file
):
data
=
read_wire_data
(
file
)
message
.
from_wire
(
data
)
pass
return
data
# we don't have direct comparison for rrsets right now (should we?
# should go in the cpp version first then), so also no direct list
...
...
@@ -44,6 +45,15 @@ def compare_rrset_list(list1, list2):
return
False
return
True
# These are used for TSIG + TC tests
LONG_TXT1
=
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde"
;
LONG_TXT2
=
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456"
;
LONG_TXT3
=
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01"
;
LONG_TXT4
=
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0"
;
# a complete message taken from cpp tests, for testing towire and totext
def
create_message
():
message_render
=
Message
(
Message
.
RENDER
)
...
...
@@ -62,16 +72,12 @@ def create_message():
message_render
.
add_rrset
(
Message
.
SECTION_ANSWER
,
rrset
)
return
message_render
def
strip_mutable_tsig_data
(
data
):
# Unfortunately we cannot easily compare TSIG RR because we can't tweak
# current time. As a work around this helper function strips off the time
# dependent part of TSIG RDATA, i.e., the MAC (assuming HMAC-MD5) and
# Time Signed.
return
data
[
0
:
-
32
]
+
data
[
-
26
:
-
22
]
+
data
[
-
6
:]
class
MessageTest
(
unittest
.
TestCase
):
def
setUp
(
self
):
# make sure we don't use faked time unless explicitly do so in tests
fix_current_time
(
None
)
self
.
p
=
Message
(
Message
.
PARSE
)
self
.
r
=
Message
(
Message
.
RENDER
)
...
...
@@ -90,6 +96,10 @@ class MessageTest(unittest.TestCase):
self
.
tsig_key
=
TSIGKey
(
"www.example.com:SFuWd/q99SzF8Yzd1QbB9g=="
)
self
.
tsig_ctx
=
TSIGContext
(
self
.
tsig_key
)
def
tearDown
(
self
):
# reset any faked current time setting (it would affect other tests)
fix_current_time
(
None
)
def
test_init
(
self
):
self
.
assertRaises
(
TypeError
,
Message
,
-
1
)
self
.
assertRaises
(
TypeError
,
Message
,
3
)
...
...
@@ -285,33 +295,112 @@ class MessageTest(unittest.TestCase):
self
.
assertRaises
(
InvalidMessageOperation
,
self
.
r
.
to_wire
,
MessageRenderer
())
def
__common_tsigquery_setup
(
self
):
def
__common_tsigmessage_setup
(
self
,
flags
=
[
Message
.
HEADERFLAG_RD
],
rrtype
=
RRType
(
"A"
),
answer_data
=
None
):
self
.
r
.
set_opcode
(
Opcode
.
QUERY
())
self
.
r
.
set_rcode
(
Rcode
.
NOERROR
())
self
.
r
.
set_header_flag
(
Message
.
HEADERFLAG_RD
)
for
flag
in
flags
:
self
.
r
.
set_header_flag
(
flag
)
if
answer_data
is
not
None
:
rrset
=
RRset
(
Name
(
"www.example.com"
),
RRClass
(
"IN"
),
rrtype
,
RRTTL
(
86400
))
for
rdata
in
answer_data
:
rrset
.
add_rdata
(
Rdata
(
rrtype
,
RRClass
(
"IN"
),
rdata
))
self
.
r
.
add_rrset
(
Message
.
SECTION_ANSWER
,
rrset
)
self
.
r
.
add_question
(
Question
(
Name
(
"www.example.com"
),
RRClass
(
"IN"
),
RRType
(
"A"
)
))
RRClass
(
"IN"
),
rrtype
))
def
__common_tsig_checks
(
self
,
expected_file
):
renderer
=
MessageRenderer
()
self
.
r
.
to_wire
(
renderer
,
self
.
tsig_ctx
)
actual_wire
=
strip_mutable_tsig_data
(
renderer
.
get_data
())
expected_wire
=
strip_mutable_tsig_data
(
read_wire_data
(
expected_file
))
self
.
assertEqual
(
expected_wire
,
actual_wire
)
self
.
assertEqual
(
read_wire_data
(
expected_file
),
renderer
.
get_data
())
def
test_to_wire_with_tsig
(
self
):
fix_current_time
(
0x4da8877a
)
self
.
r
.
set_qid
(
0x2d65
)
self
.
__common_tsig
query
_setup
()
self
.
__common_tsig
message
_setup
()
self
.
__common_tsig_checks
(
"message_toWire2.wire"
)
def
test_to_wire_with_edns_tsig
(
self
):
fix_current_time
(
0x4db60d1f
)
self
.
r
.
set_qid
(
0x6cd
)
self
.
__common_tsig
query
_setup
()
self
.
__common_tsig
message
_setup
()
edns
=
EDNS
()
edns
.
set_udp_size
(
4096
)
self
.
r
.
set_edns
(
edns
)
self
.
__common_tsig_checks
(
"message_toWire3.wire"
)
def
test_to_wire_tsig_truncation
(
self
):
fix_current_time
(
0x4e179212
)
data
=
factoryFromFile
(
self
.
p
,
"message_fromWire17.wire"
)
self
.
assertEqual
(
TSIGError
.
NOERROR
,
self
.
tsig_ctx
.
verify
(
self
.
p
.
get_tsig_record
(),
data
))
self
.
r
.
set_qid
(
0x22c2
)
self
.
__common_tsigmessage_setup
([
Message
.
HEADERFLAG_QR
,
Message
.
HEADERFLAG_AA
,
Message
.
HEADERFLAG_RD
],
RRType
(
"TXT"
),
[
LONG_TXT1
,
LONG_TXT2
])
self
.
__common_tsig_checks
(
"message_toWire4.wire"
)
def
test_to_wire_tsig_truncation2
(
self
):
fix_current_time
(
0x4e179212
)
data
=
factoryFromFile
(
self
.
p
,
"message_fromWire17.wire"
)
self
.
assertEqual
(
TSIGError
.
NOERROR
,
self
.
tsig_ctx
.
verify
(
self
.
p
.
get_tsig_record
(),
data
))
self
.
r
.
set_qid
(
0x22c2
)
self
.
__common_tsigmessage_setup
([
Message
.
HEADERFLAG_QR
,
Message
.
HEADERFLAG_AA
,
Message
.
HEADERFLAG_RD
],
RRType
(
"TXT"
),
[
LONG_TXT1
,
LONG_TXT3
])
self
.
__common_tsig_checks
(
"message_toWire4.wire"
)
def
test_to_wire_tsig_truncation3
(
self
):
self
.
r
.
set_opcode
(
Opcode
.
QUERY
())
self
.
r
.
set_rcode
(
Rcode
.
NOERROR
())
for
i
in
range
(
1
,
68
):
self
.
r
.
add_question
(
Question
(
Name
(
"www.example.com"
),
RRClass
(
"IN"
),
RRType
(
i
)))
renderer
=
MessageRenderer
()
self
.
r
.
to_wire
(
renderer
,
self
.
tsig_ctx
)
self
.
p
.
from_wire
(
renderer
.
get_data
())
self
.
assertTrue
(
self
.
p
.
get_header_flag
(
Message
.
HEADERFLAG_TC
))
self
.
assertEqual
(
66
,
self
.
p
.
get_rr_count
(
Message
.
SECTION_QUESTION
))
self
.
assertNotEqual
(
None
,
self
.
p
.
get_tsig_record
())
def
test_to_wire_tsig_no_truncation
(
self
):
fix_current_time
(
0x4e17b38d
)
data
=
factoryFromFile
(
self
.
p
,
"message_fromWire18.wire"
)
self
.
assertEqual
(
TSIGError
.
NOERROR
,
self
.
tsig_ctx
.
verify
(
self
.
p
.
get_tsig_record
(),
data
))
self
.
r
.
set_qid
(
0xd6e2
)
self
.
__common_tsigmessage_setup
([
Message
.
HEADERFLAG_QR
,
Message
.
HEADERFLAG_AA
,
Message
.
HEADERFLAG_RD
],
RRType
(
"TXT"
),
[
LONG_TXT1
,
LONG_TXT4
])
self
.
__common_tsig_checks
(
"message_toWire5.wire"
)
def
test_to_wire_tsig_length_errors
(
self
):
renderer
=
MessageRenderer
()
renderer
.
set_length_limit
(
84
)
# 84 = expected TSIG length - 1
self
.
__common_tsigmessage_setup
()
self
.
assertRaises
(
TSIGContextError
,
self
.
r
.
to_wire
,
renderer
,
self
.
tsig_ctx
)
renderer
.
clear
()
self
.
r
.
clear
(
Message
.
RENDER
)
renderer
.
set_length_limit
(
86
)
# 86 = expected TSIG length + 1
self
.
__common_tsigmessage_setup
()
self
.
assertRaises
(
TSIGContextError
,
self
.
r
.
to_wire
,
renderer
,
self
.
tsig_ctx
)
# skip the last test of the corresponding C++ test: it requires
# subclassing MessageRenderer, which is (currently) not possible
# for python. In any case, it's very unlikely to happen in practice.
def
test_to_text
(
self
):
message_render
=
create_message
()
...
...
src/lib/dns/python/tests/question_python_test.py
View file @
8e00f359
...
...
@@ -74,7 +74,6 @@ class QuestionTest(unittest.TestCase):
self
.
assertEqual
(
"foo.example.com. IN NS
\n
"
,
str
(
self
.
test_question1
))
self
.
assertEqual
(
"bar.example.com. CH A
\n
"
,
self
.
test_question2
.
to_text
())
def
test_to_wire_buffer
(
self
):
obuffer
=
bytes
()
obuffer
=
self
.
test_question1
.
to_wire
(
obuffer
)
...
...
@@ -82,7 +81,6 @@ class QuestionTest(unittest.TestCase):
wiredata
=
read_wire_data
(
"question_toWire1"
)
self
.
assertEqual
(
obuffer
,
wiredata
)
def
test_to_wire_renderer
(
self
):
renderer
=
MessageRenderer
()
self
.
test_question1
.
to_wire
(
renderer
)
...
...
@@ -91,5 +89,13 @@ class QuestionTest(unittest.TestCase):
self
.
assertEqual
(
renderer
.
get_data
(),
wiredata
)
self
.
assertRaises
(
TypeError
,
self
.
test_question1
.
to_wire
,
1
)
def
test_to_wire_truncated
(
self
):
renderer
=
MessageRenderer
()
renderer
.
set_length_limit
(
self
.
example_name1
.
get_length
())
self
.
assertFalse
(
renderer
.
is_truncated
())
self
.
test_question1
.
to_wire
(
renderer
)
self
.
assertTrue
(
renderer
.
is_truncated
())
self
.
assertEqual
(
0
,
renderer
.
get_length
())
if
__name__
==
'__main__'
:
unittest
.
main
()
src/lib/dns/question.cc
View file @
8e00f359
...
...
@@ -57,10 +57,19 @@ Question::toWire(OutputBuffer& buffer) const {
unsigned
int
Question
::
toWire
(
AbstractMessageRenderer
&
renderer
)
const
{
const
size_t
pos0
=
renderer
.
getLength
();
renderer
.
writeName
(
name_
);
rrtype_
.
toWire
(
renderer
);
rrclass_
.
toWire
(
renderer
);
// Make sure the renderer has a room for the question
if
(
renderer
.
getLength
()
>
renderer
.
getLengthLimit
())
{
renderer
.
trim
(
renderer
.
getLength
()
-
pos0
);
renderer
.
setTruncated
();
return
(
0
);
}
return
(
1
);
// number of "entries"
}
...
...
src/lib/dns/question.h
View file @
8e00f359
...
...
@@ -201,23 +201,23 @@ public:
/// class description).
///
/// The owner name will be compressed if possible, although it's an
/// unlikely event in practice because the
%
Question section a DNS
/// unlikely event in practice because the Question section a DNS
/// message normally doesn't contain multiple question entries and
/// it's located right after the Header section.
/// Nevertheless, \c renderer records the information of the owner name
/// so that it can be pointed by other RRs in other sections (which is
/// more likely to happen).
///
/// In theory, an attempt to render a Question may cause truncation
/// (when the Question section contains a large number of entries),
/// but this implementation doesn't catch that situation.
/// It would make the code unnecessarily complicated (though perhaps
/// slightly) for almost impossible case in practice.
/// An upper layer will handle the pathological case as a general error.
/// It could be possible, though very rare in practice, that
/// an attempt to render a Question may cause truncation
/// (when the Question section contains a large number of entries).
/// In such a case this method avoid the rendering and indicate the
/// truncation in the \c renderer. This method returns 0 in this case.
///
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer and name compression information.
/// \return 1
///
/// \return 1 on success; 0 if it causes truncation
unsigned
int
toWire
(
AbstractMessageRenderer
&
renderer
)
const
;
/// \brief Render the Question in the wire format without name compression.
...
...
src/lib/dns/tests/message_unittest.cc
View file @
8e00f359
...
...
@@ -62,7 +62,6 @@ using namespace isc::dns::rdata;
//
const
uint16_t
Message
::
DEFAULT_MAX_UDPSIZE
;
const
Name
test_name
(
"test.example.com"
);
namespace
isc
{
namespace
util
{
...
...
@@ -79,7 +78,8 @@ const uint16_t TSIGContext::DEFAULT_FUDGE;
namespace
{
class
MessageTest
:
public
::
testing
::
Test
{
protected:
MessageTest
()
:
obuffer
(
0
),
renderer
(
obuffer
),
MessageTest
()
:
test_name
(
"test.example.com"
),
obuffer
(
0
),
renderer
(
obuffer
),
message_parse
(
Message
::
PARSE
),
message_render
(
Message
::
RENDER
),
bogus_section
(
static_cast
<
Message
::
Section
>
(
...
...
@@ -103,8 +103,9 @@ protected:
"FAKEFAKEFAKEFAKE"
));
rrset_aaaa
->
addRRsig
(
rrset_rrsig
);
}
static
Question
factoryFromFile
(
const
char
*
datafile
);
const
Name
test_name
;
OutputBuffer
obuffer
;
MessageRenderer
renderer
;
Message
message_parse
;
...
...
@@ -114,17 +115,18 @@ protected:
RRsetPtr
rrset_aaaa
;
// AAAA RRset with one RDATA with RRSIG
RRsetPtr
rrset_rrsig
;
// RRSIG for the AAAA RRset
TSIGContext
tsig_ctx
;
vector
<
unsigned
char
>
received_data
;
vector
<
unsigned
char
>
expected_data
;
static
void
factoryFromFile
(
Message
&
message
,
const
char
*
datafile
);
void
factoryFromFile
(
Message
&
message
,
const
char
*
datafile
);
};
void
MessageTest
::
factoryFromFile
(
Message
&
message
,
const
char
*
datafile
)
{
std
::
vector
<
unsigned
char
>
data
;
UnitTestUtil
::
readWireData
(
datafile
,
data
);
received_data
.
clear
()
;
UnitTestUtil
::
readWireData
(
datafile
,
received_
data
);
InputBuffer
buffer
(
&
data
[
0
],
data
.
size
());
InputBuffer
buffer
(
&
received_data
[
0
],
received_
data
.
size
());
message
.
fromWire
(
buffer
);
}
...
...
@@ -618,15 +620,43 @@ testGetTime() {
return
(
NOW
);
}
// bit-wise constant flags to configure DNS header flags for test
// messages.
const
unsigned
int
QR_FLAG
=
0x1
;
const
unsigned
int
AA_FLAG
=
0x2
;
const
unsigned
int
RD_FLAG
=
0x4
;
void
commonTSIGToWireCheck
(
Message
&
message
,
MessageRenderer
&
renderer
,
TSIGContext
&
tsig_ctx
,
const
char
*
const
expected_file
)
TSIGContext
&
tsig_ctx
,
const
char
*
const
expected_file
,
unsigned
int
message_flags
=
RD_FLAG
,
RRType
qtype
=
RRType
::
A
(),
const
vector
<
const
char
*>*
answer_data
=
NULL
)
{
message
.
setOpcode
(
Opcode
::
QUERY
());
message
.
setRcode
(
Rcode
::
NOERROR
());
message
.
setHeaderFlag
(
Message
::
HEADERFLAG_RD
,
true
);
if
((
message_flags
&
QR_FLAG
)
!=
0
)
{
message
.
setHeaderFlag
(
Message
::
HEADERFLAG_QR
);
}
if
((
message_flags
&
AA_FLAG
)
!=
0
)
{
message
.
setHeaderFlag
(
Message
::
HEADERFLAG_AA
);
}
if
((
message_flags
&
RD_FLAG
)
!=
0
)
{
message
.
setHeaderFlag
(
Message
::
HEADERFLAG_RD
);
}
message
.
addQuestion
(
Question
(
Name
(
"www.example.com"
),
RRClass
::
IN
(),
RRType
::
A
()));
qtype
));
if
(
answer_data
!=
NULL
)
{
RRsetPtr
ans_rrset
(
new
RRset
(
Name
(
"www.example.com"
),
RRClass
::
IN
(),
qtype
,
RRTTL
(
86400
)));
for
(
vector
<
const
char
*>::
const_iterator
it
=
answer_data
->
begin
();
it
!=
answer_data
->
end
();
++
it
)
{
ans_rrset
->
addRdata
(
createRdata
(
qtype
,
RRClass
::
IN
(),
*
it
));
}
message
.
addRRset
(
Message
::
SECTION_ANSWER
,
ans_rrset
);
}
message
.
toWire
(
renderer
,
tsig_ctx
);
vector
<
unsigned
char
>
expected_data
;
...
...
@@ -670,6 +700,182 @@ TEST_F(MessageTest, toWireWithEDNSAndTSIG) {
}
}
// Some of the following tests involve truncation. We use the query name
// "www.example.com" and some TXT question/answers. The length of the
// header and question will be 33 bytes. If we also try to include a
// TSIG of the same key name (not compressed) with HMAC-MD5, the TSIG RR
// will be 85 bytes.
// A long TXT RDATA. With a fully compressed owner name, the corresponding
// RR will be 268 bytes.
const
char
*
const
long_txt1
=
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde"
;
// With a fully compressed owner name, the corresponding RR will be 212 bytes.
// It should result in truncation even without TSIG (33 + 268 + 212 = 513)
const
char
*
const
long_txt2
=
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456"
;
// With a fully compressed owner name, the corresponding RR will be 127 bytes.
// So, it can fit in the standard 512 bytes with txt1 and without TSIG, but
// adding a TSIG would result in truncation (33 + 268 + 127 + 85 = 513)
const
char
*
const
long_txt3
=
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01"
;
// This is 1 byte shorter than txt3, which will result in a possible longest
// message containing answer RRs and TSIG.
const
char
*
const
long_txt4
=
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0"
;
// Example output generated by
// "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com txt
// QID: 0x22c2
// Time Signed: 0x00004e179212
TEST_F
(
MessageTest
,
toWireTSIGTruncation
)
{
isc
::
util
::
detail
::
gettimeFunction
=
testGetTime
<
0x4e179212
>
;
// Verify a validly signed query so that we can use the TSIG context
factoryFromFile
(
message_parse
,
"message_fromWire17.wire"
);
EXPECT_EQ
(
TSIGError
::
NOERROR
(),
tsig_ctx
.
verify
(
message_parse
.
getTSIGRecord
(),
&
received_data
[
0
],
received_data
.
size
()));
message_render
.
setQid
(
0x22c2
);
vector
<
const
char
*>
answer_data
;
answer_data
.
push_back
(
long_txt1
);
answer_data
.
push_back
(
long_txt2
);
{
SCOPED_TRACE
(
"Message sign with TSIG and TC bit on"
);
commonTSIGToWireCheck
(
message_render
,
renderer
,
tsig_ctx
,
"message_toWire4.wire"
,
QR_FLAG
|
AA_FLAG
|
RD_FLAG
,
RRType
::
TXT
(),
&
answer_data
);
}
}
TEST_F
(
MessageTest
,
toWireTSIGTruncation2
)
{
// Similar to the previous test, but without TSIG it wouldn't cause
// truncation.
isc
::
util
::
detail
::
gettimeFunction
=
testGetTime
<
0x4e179212
>
;
factoryFromFile
(
message_parse
,
"message_fromWire17.wire"
);
EXPECT_EQ
(
TSIGError
::
NOERROR
(),
tsig_ctx
.
verify
(
message_parse
.
getTSIGRecord
(),
&
received_data
[
0
],
received_data
.
size
()));
message_render
.
setQid
(
0x22c2
);
vector
<
const
char
*>
answer_data
;
answer_data
.
push_back
(
long_txt1
);
answer_data
.
push_back
(
long_txt3
);
{
SCOPED_TRACE
(
"Message sign with TSIG and TC bit on (2)"
);
commonTSIGToWireCheck
(
message_render
,
renderer
,
tsig_ctx
,
"message_toWire4.wire"
,
QR_FLAG
|
AA_FLAG
|
RD_FLAG
,
RRType
::
TXT
(),
&
answer_data
);
}
}
TEST_F
(
MessageTest
,
toWireTSIGTruncation3
)
{
// Similar to previous ones, but truncation occurs due to too many
// Questions (very unusual, but not necessarily illegal).
// We are going to create a message starting with a standard
// header (12 bytes) and multiple questions in the Question
// section of the same owner name (changing the RRType, just so
// that it would be the form that would be accepted by the BIND 9
// parser). The first Question is 21 bytes in length, and the subsequent
// ones are 6 bytes. We'll also use a TSIG whose size is 85 bytes.
// Up to 66 questions can fit in the standard 512-byte buffer
// (12 + 21 + 6 * 65 + 85 = 508). If we try to add one more it would
// result in truncation.
message_render
.
setOpcode
(
Opcode
::
QUERY
());
message_render
.
setRcode
(
Rcode
::
NOERROR
());
for
(
int
i
=
1
;
i
<=
67
;
++
i
)
{
message_render
.
addQuestion
(
Question
(
Name
(
"www.example.com"
),
RRClass
::
IN
(),
RRType
(
i
)));
}
message_render
.
toWire
(
renderer
,
tsig_ctx
);
// Check the rendered data by parsing it. We only check it has the
// TC bit on, has the correct number of questions, and has a TSIG RR.
// Checking the signature wouldn't be necessary for this rare case
// scenario.
InputBuffer
buffer
(
renderer
.
getData
(),
renderer
.
getLength
());
message_parse
.
fromWire
(
buffer
);
EXPECT_TRUE
(
message_parse
.
getHeaderFlag
(
Message
::
HEADERFLAG_TC
));
// Note that the number of questions are 66, not 67 as we tried to add.
EXPECT_EQ
(
66
,
message_parse
.
getRRCount
(
Message
::
SECTION_QUESTION
));
EXPECT_TRUE
(
message_parse
.
getTSIGRecord
()
!=
NULL
);
}
TEST_F
(
MessageTest
,
toWireTSIGNoTruncation
)
{
// A boundary case that shouldn't cause truncation: the resulting
// response message with a TSIG will be 512 bytes long.
isc
::
util
::
detail
::
gettimeFunction
=
testGetTime
<
0x4e17b38d
>
;
factoryFromFile
(
message_parse
,
"message_fromWire18.wire"
);
EXPECT_EQ
(
TSIGError
::
NOERROR
(),
tsig_ctx
.
verify
(
message_parse
.
getTSIGRecord
(),
&
received_data
[
0
],
received_data
.
size
()));
message_render
.
setQid
(
0xd6e2
);
vector
<
const
char
*>
answer_data
;
answer_data
.
push_back
(
long_txt1
);
answer_data
.
push_back
(
long_txt4
);
{
SCOPED_TRACE
(
"Message sign with TSIG, no truncation"
);
commonTSIGToWireCheck
(
message_render
,
renderer
,
tsig_ctx
,
"message_toWire5.wire"
,
QR_FLAG
|
AA_FLAG
|
RD_FLAG
,
RRType
::
TXT
(),
&
answer_data
);
}
}