| /* |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <lib/dnssd/minimal_mdns/records/ResourceRecord.h> |
| #include <lib/support/UnitTestRegistration.h> |
| |
| #include <nlunit-test.h> |
| |
| namespace { |
| |
| using namespace chip; |
| using namespace chip::Encoding::BigEndian; |
| using namespace mdns::Minimal; |
| |
| const QNamePart kNames[] = { "foo", "bar" }; |
| |
| class FakeResourceRecord : public ResourceRecord |
| { |
| public: |
| FakeResourceRecord(const char * data) : ResourceRecord(QType::ANY, kNames), mData(data) {} |
| |
| protected: |
| bool WriteData(RecordWriter & out) const override { return out.PutString(mData).Fit(); } |
| |
| private: |
| const char * mData; |
| }; |
| |
| void CanWriteSimpleRecord(nlTestSuite * inSuite, void * inContext) |
| { |
| uint8_t headerBuffer[HeaderRef::kSizeBytes]; |
| uint8_t dataBuffer[128]; |
| |
| HeaderRef header(headerBuffer); |
| header.Clear(); |
| |
| BufferWriter output(dataBuffer, sizeof(dataBuffer)); |
| RecordWriter writer(&output); |
| |
| FakeResourceRecord record("somedata"); |
| |
| record.SetTtl(0x11223344); |
| |
| const uint8_t expectedOutput[] = { |
| // |
| 3, 'f', 'o', 'o', // QNAME part: foo |
| 3, 'b', 'a', 'r', // QNAME part: bar |
| 0, // QNAME ends |
| 0, 255, // QType ANY (totally fake) |
| 0, 1, // QClass IN |
| 0x11, 0x22, 0x33, 0x44, // TTL |
| 0, 8, // data size |
| 's', 'o', 'm', 'e', 'd', 'a', 't', 'a', |
| }; |
| |
| NL_TEST_ASSERT(inSuite, record.Append(header, ResourceType::kAnswer, writer)); |
| NL_TEST_ASSERT(inSuite, header.GetAnswerCount() == 1); |
| NL_TEST_ASSERT(inSuite, header.GetAuthorityCount() == 0); |
| NL_TEST_ASSERT(inSuite, header.GetAdditionalCount() == 0); |
| NL_TEST_ASSERT(inSuite, output.Needed() == sizeof(expectedOutput)); |
| NL_TEST_ASSERT(inSuite, memcmp(dataBuffer, expectedOutput, sizeof(expectedOutput)) == 0); |
| } |
| |
| void CanWriteMultipleRecords(nlTestSuite * inSuite, void * inContext) |
| { |
| uint8_t headerBuffer[HeaderRef::kSizeBytes]; |
| uint8_t dataBuffer[128]; |
| |
| HeaderRef header(headerBuffer); |
| header.Clear(); |
| |
| BufferWriter output(dataBuffer, sizeof(dataBuffer)); |
| RecordWriter writer(&output); |
| |
| FakeResourceRecord record1("somedata"); |
| FakeResourceRecord record2("moredata"); |
| FakeResourceRecord record3("xyz"); |
| |
| record1.SetTtl(0x11223344); |
| record2.SetTtl(0); |
| record3.SetTtl(0xFF); |
| |
| const uint8_t expectedOutput[] = { |
| 3, 'f', 'o', 'o', // QNAME part: foo |
| 3, 'b', 'a', 'r', // QNAME part: bar |
| 0, // QNAME ends |
| 0, 255, // QType ANY (totally fake) |
| 0, 1, // QClass IN |
| 0x11, 0x22, 0x33, 0x44, // TTL |
| 0, 8, // data size |
| 's', 'o', 'm', 'e', 'd', 'a', 't', 'a', // |
| 0xC0, 0x00, // PTR: foo.bar |
| 0, 255, // QType ANY (totally fake) |
| 0, 1, // QClass IN |
| 0, 0, 0, 0, // TTL |
| 0, 8, // data size |
| 'm', 'o', 'r', 'e', 'd', 'a', 't', 'a', // |
| 0xC0, 0x00, // PTR: foo.bar |
| 0, 255, // QType ANY (totally fake) |
| 0, 1, // QClass IN |
| 0, 0, 0, 0xFF, // TTL |
| 0, 3, // data size |
| 'x', 'y', 'z', |
| }; |
| |
| NL_TEST_ASSERT(inSuite, record1.Append(header, ResourceType::kAnswer, writer)); |
| NL_TEST_ASSERT(inSuite, header.GetAnswerCount() == 1); |
| NL_TEST_ASSERT(inSuite, header.GetAuthorityCount() == 0); |
| NL_TEST_ASSERT(inSuite, header.GetAdditionalCount() == 0); |
| |
| NL_TEST_ASSERT(inSuite, record2.Append(header, ResourceType::kAuthority, writer)); |
| NL_TEST_ASSERT(inSuite, header.GetAnswerCount() == 1); |
| NL_TEST_ASSERT(inSuite, header.GetAuthorityCount() == 1); |
| NL_TEST_ASSERT(inSuite, header.GetAdditionalCount() == 0); |
| |
| NL_TEST_ASSERT(inSuite, record3.Append(header, ResourceType::kAdditional, writer)); |
| NL_TEST_ASSERT(inSuite, header.GetAnswerCount() == 1); |
| NL_TEST_ASSERT(inSuite, header.GetAuthorityCount() == 1); |
| NL_TEST_ASSERT(inSuite, header.GetAdditionalCount() == 1); |
| |
| NL_TEST_ASSERT(inSuite, output.Needed() == sizeof(expectedOutput)); |
| NL_TEST_ASSERT(inSuite, memcmp(dataBuffer, expectedOutput, sizeof(expectedOutput)) == 0); |
| } |
| |
| void RecordOrderIsEnforced(nlTestSuite * inSuite, void * inContext) |
| { |
| uint8_t headerBuffer[HeaderRef::kSizeBytes]; |
| uint8_t dataBuffer[128]; |
| |
| HeaderRef header(headerBuffer); |
| |
| BufferWriter output(dataBuffer, sizeof(dataBuffer)); |
| RecordWriter writer(&output); |
| |
| FakeResourceRecord record("somedata"); |
| |
| header.Clear(); |
| header.SetAuthorityCount(1); |
| NL_TEST_ASSERT(inSuite, record.Append(header, ResourceType::kAnswer, writer) == false); |
| |
| header.Clear(); |
| header.SetAdditionalCount(1); |
| NL_TEST_ASSERT(inSuite, record.Append(header, ResourceType::kAnswer, writer) == false); |
| NL_TEST_ASSERT(inSuite, record.Append(header, ResourceType::kAuthority, writer) == false); |
| } |
| |
| void ErrorsOutOnSmallBuffers(nlTestSuite * inSuite, void * inContext) |
| { |
| uint8_t headerBuffer[HeaderRef::kSizeBytes]; |
| uint8_t dataBuffer[123]; |
| |
| HeaderRef header(headerBuffer); |
| |
| FakeResourceRecord record("somedata"); |
| |
| const uint8_t expectedOutput[] = { |
| // |
| 3, 'f', 'o', 'o', // QNAME part: foo |
| 3, 'b', 'a', 'r', // QNAME part: bar |
| 0, // QNAME ends |
| 0, 255, // QType ANY (totally fake) |
| 0, 1, // QClass IN |
| 0, 0, 0, 120, // TTL |
| 0, 8, // data size |
| 's', 'o', 'm', 'e', 'd', 'a', 't', 'a', |
| }; |
| |
| header.Clear(); |
| uint8_t clearHeader[HeaderRef::kSizeBytes]; |
| |
| memcpy(clearHeader, headerBuffer, HeaderRef::kSizeBytes); |
| |
| for (size_t i = 0; i < sizeof(expectedOutput); i++) |
| { |
| memset(dataBuffer, 0, sizeof(dataBuffer)); |
| BufferWriter output(dataBuffer, i); |
| RecordWriter writer(&output); |
| |
| NL_TEST_ASSERT(inSuite, record.Append(header, ResourceType::kAnswer, writer) == false); |
| |
| // header untouched |
| NL_TEST_ASSERT(inSuite, memcmp(headerBuffer, clearHeader, HeaderRef::kSizeBytes) == 0); |
| } |
| |
| memset(dataBuffer, 0, sizeof(dataBuffer)); |
| BufferWriter output(dataBuffer, sizeof(expectedOutput)); |
| RecordWriter writer(&output); |
| |
| NL_TEST_ASSERT(inSuite, record.Append(header, ResourceType::kAnswer, writer)); |
| NL_TEST_ASSERT(inSuite, output.Needed() == sizeof(expectedOutput)); |
| NL_TEST_ASSERT(inSuite, memcmp(dataBuffer, expectedOutput, sizeof(expectedOutput)) == 0); |
| NL_TEST_ASSERT(inSuite, memcmp(headerBuffer, clearHeader, HeaderRef::kSizeBytes) != 0); |
| } |
| |
| void RecordCount(nlTestSuite * inSuite, void * inContext) |
| { |
| constexpr int kAppendCount = 10; |
| uint8_t headerBuffer[HeaderRef::kSizeBytes]; |
| uint8_t dataBuffer[123]; |
| |
| HeaderRef header(headerBuffer); |
| header.Clear(); |
| |
| FakeResourceRecord record("somedata"); |
| |
| for (int i = 0; i < kAppendCount; i++) |
| { |
| BufferWriter output(dataBuffer, sizeof(dataBuffer)); |
| RecordWriter writer(&output); |
| |
| NL_TEST_ASSERT(inSuite, record.Append(header, ResourceType::kAnswer, writer)); |
| NL_TEST_ASSERT(inSuite, header.GetAnswerCount() == i + 1); |
| NL_TEST_ASSERT(inSuite, header.GetAuthorityCount() == 0); |
| NL_TEST_ASSERT(inSuite, header.GetAdditionalCount() == 0); |
| } |
| |
| for (int i = 0; i < kAppendCount; i++) |
| { |
| BufferWriter output(dataBuffer, sizeof(dataBuffer)); |
| RecordWriter writer(&output); |
| |
| NL_TEST_ASSERT(inSuite, record.Append(header, ResourceType::kAuthority, writer)); |
| NL_TEST_ASSERT(inSuite, header.GetAnswerCount() == kAppendCount); |
| NL_TEST_ASSERT(inSuite, header.GetAuthorityCount() == i + 1); |
| NL_TEST_ASSERT(inSuite, header.GetAdditionalCount() == 0); |
| } |
| |
| for (int i = 0; i < kAppendCount; i++) |
| { |
| BufferWriter output(dataBuffer, sizeof(dataBuffer)); |
| RecordWriter writer(&output); |
| |
| NL_TEST_ASSERT(inSuite, record.Append(header, ResourceType::kAdditional, writer)); |
| NL_TEST_ASSERT(inSuite, header.GetAnswerCount() == kAppendCount); |
| NL_TEST_ASSERT(inSuite, header.GetAuthorityCount() == kAppendCount); |
| NL_TEST_ASSERT(inSuite, header.GetAdditionalCount() == i + 1); |
| } |
| } |
| void CacheFlushBit(nlTestSuite * inSuite, void * inContext) |
| { |
| FakeResourceRecord record("somedata"); |
| // No cache flush bit by default. |
| NL_TEST_ASSERT(inSuite, record.GetClass() == QClass::IN); |
| NL_TEST_ASSERT(inSuite, record.GetCacheFlush() == false); |
| |
| // Check we can set flush bit and the class marker reflects that. |
| record.SetCacheFlush(true); |
| NL_TEST_ASSERT(inSuite, record.GetClass() == QClass::IN_FLUSH); |
| NL_TEST_ASSERT(inSuite, record.GetCacheFlush() == true); |
| |
| // Check we can unset. |
| record.SetCacheFlush(false); |
| NL_TEST_ASSERT(inSuite, record.GetClass() == QClass::IN); |
| NL_TEST_ASSERT(inSuite, record.GetCacheFlush() == false); |
| } |
| |
| const nlTest sTests[] = { |
| NL_TEST_DEF("CanWriteSimpleRecord", CanWriteSimpleRecord), // |
| NL_TEST_DEF("CanWriteMultipleRecords", CanWriteMultipleRecords), // |
| NL_TEST_DEF("RecordOrderIsEnforced", RecordOrderIsEnforced), // |
| NL_TEST_DEF("ErrorsOutOnSmallBuffers", ErrorsOutOnSmallBuffers), // |
| NL_TEST_DEF("RecordCount", RecordCount), // |
| NL_TEST_DEF("CacheFlushBit", CacheFlushBit), // |
| NL_TEST_SENTINEL() // |
| }; |
| |
| } // namespace |
| |
| int TestResourceRecord() |
| { |
| nlTestSuite theSuite = { "ResourceRecord", sTests, nullptr, nullptr }; |
| nlTestRunner(&theSuite, nullptr); |
| return nlTestRunnerStats(&theSuite); |
| } |
| |
| CHIP_REGISTER_TEST_SUITE(TestResourceRecord) |