Matter SDK Coverage Report
Current view: top level - lib/dnssd/minimal_mdns - ResponseSender.cpp (source / functions) Coverage Total Hit
Test: SHA:e98a48c2e59f85a25417956e1d105721433aa5d1 Lines: 89.9 % 119 107
Test Date: 2026-01-09 16:53:50 Functions: 100.0 % 11 11

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2020-2021 Project CHIP Authors
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : 
      18              : #include "ResponseSender.h"
      19              : 
      20              : #include "QueryReplyFilter.h"
      21              : 
      22              : #include <minmdns/MinMdnsConfig.h>
      23              : #include <system/SystemClock.h>
      24              : 
      25              : namespace mdns {
      26              : namespace Minimal {
      27              : 
      28              : namespace {
      29              : 
      30              : using namespace mdns::Minimal::Internal;
      31              : 
      32              : constexpr uint16_t kMdnsStandardPort = 5353;
      33              : 
      34              : // Restriction for UDP packets:  https://tools.ietf.org/html/rfc1035#section-4.2.1
      35              : //
      36              : //    Messages carried by UDP are restricted to 512 bytes (not counting the IP
      37              : //    or UDP headers).  Longer messages are truncated and the TC bit is set in
      38              : //    the header.
      39              : constexpr uint16_t kPacketSizeBytes = 512;
      40              : 
      41              : } // namespace
      42              : namespace Internal {
      43              : 
      44         1033 : bool ResponseSendingState::SendUnicast() const
      45              : {
      46         1033 :     return mQuery->RequestedUnicastAnswer() || (mSource->SrcPort != kMdnsStandardPort);
      47              : }
      48              : 
      49          110 : bool ResponseSendingState::IncludeQuery() const
      50              : {
      51          110 :     return (mSource->SrcPort != kMdnsStandardPort);
      52              : }
      53              : 
      54              : } // namespace Internal
      55              : 
      56         1064 : CHIP_ERROR ResponseSender::AddQueryResponder(QueryResponderBase * queryResponder)
      57              : {
      58              :     // If already existing or we find a free slot, just use it
      59              :     // Note that dynamic memory implementations are never expected to be nullptr
      60              :     //
      61         1132 :     for (auto & responder : mResponders)
      62              :     {
      63         1067 :         if (responder == nullptr || responder == queryResponder)
      64              :         {
      65          999 :             responder = queryResponder;
      66          999 :             return CHIP_NO_ERROR;
      67              :         }
      68              :     }
      69              : 
      70              : #if CHIP_CONFIG_MINMDNS_DYNAMIC_OPERATIONAL_RESPONDER_LIST
      71           65 :     mResponders.push_back(queryResponder);
      72           65 :     return CHIP_NO_ERROR;
      73              : #else
      74              :     return CHIP_ERROR_NO_MEMORY;
      75              : #endif
      76              : }
      77              : 
      78            8 : CHIP_ERROR ResponseSender::RemoveQueryResponder(QueryResponderBase * queryResponder)
      79              : {
      80           22 :     for (auto it = mResponders.begin(); it != mResponders.end(); it++)
      81              :     {
      82           22 :         if (*it == queryResponder)
      83              :         {
      84            8 :             *it = nullptr;
      85              : #if CHIP_CONFIG_MINMDNS_DYNAMIC_OPERATIONAL_RESPONDER_LIST
      86            8 :             mResponders.erase(it);
      87              : #endif
      88            8 :             return CHIP_NO_ERROR;
      89              :         }
      90              :     }
      91            0 :     return CHIP_ERROR_NOT_FOUND;
      92              : }
      93              : 
      94            1 : bool ResponseSender::HasQueryResponders() const
      95              : {
      96            1 :     for (auto responder : mResponders)
      97              :     {
      98            0 :         if (responder != nullptr)
      99              :         {
     100            0 :             return true;
     101              :         }
     102              :     }
     103            1 :     return false;
     104              : }
     105              : 
     106          153 : CHIP_ERROR ResponseSender::Respond(uint16_t messageId, const QueryData & query, const chip::Inet::IPPacketInfo * querySource,
     107              :                                    const ResponseConfiguration & configuration)
     108              : {
     109          153 :     mSendState.Reset(messageId, query, querySource);
     110              : 
     111          153 :     if (query.IsAnnounceBroadcast())
     112              :     {
     113              :         // Deny listing large amount of data
     114          118 :         mSendState.MarkWasSent(ResponseItemsSent::kServiceListingData);
     115              :     }
     116              : 
     117              :     // Responder has a stateful 'additional replies required' that is used within the response
     118              :     // loop. 'no additionals required' is set at the start and additionals are marked as the query
     119              :     // reply is built.
     120          528 :     for (auto & responder : mResponders)
     121              :     {
     122              :         {
     123          375 :             if (responder != nullptr)
     124              :             {
     125          375 :                 responder->ResetAdditionals();
     126              :             }
     127              :         }
     128              :     }
     129              : 
     130              :     // send all 'Answer' replies
     131              :     {
     132          153 :         const chip::System::Clock::Timestamp kTimeNow = chip::System::SystemClock().GetMonotonicTimestamp();
     133              : 
     134          153 :         QueryReplyFilter queryReplyFilter(query);
     135          153 :         QueryResponderRecordFilter responseFilter;
     136              : 
     137          153 :         responseFilter.SetReplyFilter(&queryReplyFilter);
     138              : 
     139          153 :         if (!mSendState.SendUnicast())
     140              :         {
     141              :             // According to https://tools.ietf.org/html/rfc6762#section-6  we should multicast at most 1/sec
     142              :             //
     143              :             // TODO: the 'last sent' value does NOT track the interface we used to send, so this may cause
     144              :             //       broadcasts on one interface to throttle broadcasts on another interface.
     145          118 :             responseFilter.SetIncludeOnlyMulticastBeforeMS(kTimeNow - chip::System::Clock::Seconds32(1));
     146              :         }
     147          528 :         for (auto & responder : mResponders)
     148              :         {
     149          375 :             if (responder == nullptr)
     150              :             {
     151            0 :                 continue;
     152              :             }
     153         1145 :             for (auto it = responder->begin(&responseFilter); it != responder->end(); it++)
     154              :             {
     155          770 :                 it->responder->AddAllResponses(querySource, this, configuration);
     156          770 :                 ReturnErrorOnFailure(mSendState.GetError());
     157              : 
     158          770 :                 responder->MarkAdditionalRepliesFor(it);
     159              : 
     160          770 :                 if (!mSendState.SendUnicast())
     161              :                 {
     162          710 :                     it->lastMulticastTime = kTimeNow;
     163              :                 }
     164              :             }
     165              :         }
     166          153 :     }
     167              : 
     168              :     // send all 'Additional' replies
     169              :     {
     170          153 :         if (!query.IsAnnounceBroadcast())
     171              :         {
     172              :             // Initial service broadcast should keep adding data as 'Answers' rather
     173              :             // than addtional data (https://datatracker.ietf.org/doc/html/rfc6762#section-8.3)
     174           35 :             mSendState.SetResourceType(ResourceType::kAdditional);
     175              :         }
     176              : 
     177          153 :         QueryReplyFilter queryReplyFilter(query);
     178              : 
     179          153 :         queryReplyFilter.SetIgnoreNameMatch(true).SetSendingAdditionalItems(true);
     180              : 
     181          153 :         QueryResponderRecordFilter responseFilter;
     182              :         responseFilter
     183          153 :             .SetReplyFilter(&queryReplyFilter) //
     184          153 :             .SetIncludeAdditionalRepliesOnly(true);
     185          484 :         for (auto & responder : mResponders)
     186              :         {
     187          341 :             if (responder == nullptr)
     188              :             {
     189            0 :                 continue;
     190              :             }
     191          675 :             for (auto it = responder->begin(&responseFilter); it != responder->end(); it++)
     192              :             {
     193          344 :                 it->responder->AddAllResponses(querySource, this, configuration);
     194          344 :                 ReturnErrorOnFailure(mSendState.GetError());
     195              :             }
     196              :         }
     197          153 :     }
     198              : 
     199          143 :     return FlushReply();
     200              : }
     201              : 
     202          153 : CHIP_ERROR ResponseSender::FlushReply()
     203              : {
     204          153 :     VerifyOrReturnError(mResponseBuilder.HasPacketBuffer(), CHIP_NO_ERROR); // nothing to flush
     205              : 
     206          110 :     if (mResponseBuilder.HasResponseRecords())
     207              :     {
     208              :         char srcAddressString[chip::Inet::IPAddress::kMaxStringLength];
     209          110 :         VerifyOrDie(mSendState.GetSourceAddress().ToString(srcAddressString) != nullptr);
     210              : 
     211          110 :         if (mSendState.SendUnicast())
     212              :         {
     213              : #if CHIP_MINMDNS_HIGH_VERBOSITY
     214              :             ChipLogDetail(Discovery, "Directly sending mDns reply to peer %s on port %d", srcAddressString,
     215              :                           mSendState.GetSourcePort());
     216              : #endif
     217           32 :             ReturnErrorOnFailure(mServer->DirectSend(mResponseBuilder.ReleasePacket(), mSendState.GetSourceAddress(),
     218              :                                                      mSendState.GetSourcePort(), mSendState.GetSourceInterfaceId()));
     219              :         }
     220              :         else
     221              :         {
     222              : #if CHIP_MINMDNS_HIGH_VERBOSITY
     223              :             ChipLogDetail(Discovery, "Broadcasting mDns reply for query from %s", srcAddressString);
     224              : #endif
     225           78 :             ReturnErrorOnFailure(mServer->BroadcastSend(mResponseBuilder.ReleasePacket(), kMdnsStandardPort,
     226              :                                                         mSendState.GetSourceInterfaceId(), mSendState.GetSourceAddress().Type()));
     227              :         }
     228              :     }
     229              : 
     230           80 :     return CHIP_NO_ERROR;
     231              : }
     232              : 
     233          110 : CHIP_ERROR ResponseSender::PrepareNewReplyPacket()
     234              : {
     235          110 :     chip::System::PacketBufferHandle buffer = chip::System::PacketBufferHandle::New(kPacketSizeBytes);
     236          110 :     VerifyOrReturnError(!buffer.IsNull(), CHIP_ERROR_NO_MEMORY);
     237              : 
     238          110 :     mResponseBuilder.Reset(std::move(buffer));
     239          110 :     mResponseBuilder.Header().SetMessageId(mSendState.GetMessageId());
     240              : 
     241          110 :     if (mSendState.IncludeQuery())
     242              :     {
     243           32 :         mResponseBuilder.AddQuery(*mSendState.GetQuery());
     244              :     }
     245              : 
     246          110 :     return CHIP_NO_ERROR;
     247          110 : }
     248              : 
     249         1114 : bool ResponseSender::ShouldSend(const Responder & responder) const
     250              : {
     251         1114 :     switch (responder.GetQType())
     252              :     {
     253           53 :     case QType::A:
     254           53 :         return !mSendState.GetWasSent(ResponseItemsSent::kIPv4Addresses);
     255          101 :     case QType::AAAA:
     256          101 :         return !mSendState.GetWasSent(ResponseItemsSent::kIPv6Addresses);
     257          744 :     case QType::PTR: {
     258              :         static const QNamePart kDnsSdQueryPath[] = { "_services", "_dns-sd", "_udp", "local" };
     259              : 
     260          744 :         if (responder.GetQName() == FullQName(kDnsSdQueryPath))
     261              :         {
     262          315 :             return !mSendState.GetWasSent(ResponseItemsSent::kServiceListingData);
     263              :         }
     264          429 :         break;
     265              :     }
     266          216 :     default:
     267          216 :         break;
     268              :     }
     269              : 
     270          645 :     return true;
     271              : }
     272              : 
     273          802 : void ResponseSender::ResponsesAdded(const Responder & responder)
     274              : {
     275          802 :     switch (responder.GetQType())
     276              :     {
     277           43 :     case QType::A:
     278           43 :         mSendState.MarkWasSent(ResponseItemsSent::kIPv4Addresses);
     279           43 :         break;
     280           91 :     case QType::AAAA:
     281           91 :         mSendState.MarkWasSent(ResponseItemsSent::kIPv6Addresses);
     282           91 :         break;
     283          668 :     default:
     284          668 :         break;
     285              :     }
     286          802 : }
     287              : 
     288          781 : void ResponseSender::AddResponse(const ResourceRecord & record)
     289              : {
     290          781 :     ReturnOnFailure(mSendState.GetError());
     291              : 
     292          781 :     if (!mResponseBuilder.HasPacketBuffer())
     293              :     {
     294          110 :         TEMPORARY_RETURN_IGNORED mSendState.SetError(PrepareNewReplyPacket());
     295          110 :         ReturnOnFailure(mSendState.GetError());
     296              :     }
     297          781 :     if (!mResponseBuilder.Ok())
     298              :     {
     299            0 :         TEMPORARY_RETURN_IGNORED mSendState.SetError(CHIP_ERROR_INCORRECT_STATE);
     300            0 :         return;
     301              :     }
     302              : 
     303          781 :     mResponseBuilder.AddRecord(mSendState.GetResourceType(), record);
     304              : 
     305              :     // ResponseBuilder AddRecord will only fail if insufficient space is available (or at least this is
     306              :     // the assumption here). It also guarantees that existing data and header are unchanged on
     307              :     // failure, hence we can flush and try again. This allows for split replies.
     308          781 :     if (!mResponseBuilder.Ok())
     309              :     {
     310           10 :         mResponseBuilder.Header().SetFlags(mResponseBuilder.Header().GetFlags().SetTruncated(true));
     311              : 
     312           10 :         ReturnOnFailure(mSendState.SetError(FlushReply()));
     313            0 :         ReturnOnFailure(mSendState.SetError(PrepareNewReplyPacket()));
     314              : 
     315            0 :         mResponseBuilder.AddRecord(mSendState.GetResourceType(), record);
     316            0 :         if (!mResponseBuilder.Ok())
     317              :         {
     318              :             // Very much unexpected: single record addition should fit (our records should not be that big).
     319            0 :             ChipLogError(Discovery, "Failed to add single record to mDNS response.");
     320            0 :             TEMPORARY_RETURN_IGNORED mSendState.SetError(CHIP_ERROR_INTERNAL);
     321              :         }
     322              :     }
     323              : }
     324              : 
     325              : } // namespace Minimal
     326              : } // namespace mdns
        

Generated by: LCOV version 2.0-1