Matter SDK Coverage Report
Current view: top level - lib/dnssd/minimal_mdns - ResponseSender.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 89.9 % 119 107
Test Date: 2025-01-17 19:00:11 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 <system/SystemClock.h>
      23              : 
      24              : namespace mdns {
      25              : namespace Minimal {
      26              : 
      27              : namespace {
      28              : 
      29              : using namespace mdns::Minimal::Internal;
      30              : 
      31              : constexpr uint16_t kMdnsStandardPort = 5353;
      32              : 
      33              : // Restriction for UDP packets:  https://tools.ietf.org/html/rfc1035#section-4.2.1
      34              : //
      35              : //    Messages carried by UDP are restricted to 512 bytes (not counting the IP
      36              : //    or UDP headers).  Longer messages are truncated and the TC bit is set in
      37              : //    the header.
      38              : constexpr uint16_t kPacketSizeBytes = 512;
      39              : 
      40              : } // namespace
      41              : namespace Internal {
      42              : 
      43          943 : bool ResponseSendingState::SendUnicast() const
      44              : {
      45          943 :     return mQuery->RequestedUnicastAnswer() || (mSource->SrcPort != kMdnsStandardPort);
      46              : }
      47              : 
      48          104 : bool ResponseSendingState::IncludeQuery() const
      49              : {
      50          104 :     return (mSource->SrcPort != kMdnsStandardPort);
      51              : }
      52              : 
      53              : } // namespace Internal
      54              : 
      55         1030 : CHIP_ERROR ResponseSender::AddQueryResponder(QueryResponderBase * queryResponder)
      56              : {
      57              :     // If already existing or we find a free slot, just use it
      58              :     // Note that dynamic memory implementations are never expected to be nullptr
      59              :     //
      60         1081 :     for (auto & responder : mResponders)
      61              :     {
      62         1050 :         if (responder == nullptr || responder == queryResponder)
      63              :         {
      64          999 :             responder = queryResponder;
      65          999 :             return CHIP_NO_ERROR;
      66              :         }
      67              :     }
      68              : 
      69              : #if CHIP_CONFIG_MINMDNS_DYNAMIC_OPERATIONAL_RESPONDER_LIST
      70           31 :     mResponders.push_back(queryResponder);
      71           31 :     return CHIP_NO_ERROR;
      72              : #else
      73              :     return CHIP_ERROR_NO_MEMORY;
      74              : #endif
      75              : }
      76              : 
      77            8 : CHIP_ERROR ResponseSender::RemoveQueryResponder(QueryResponderBase * queryResponder)
      78              : {
      79           22 :     for (auto it = mResponders.begin(); it != mResponders.end(); it++)
      80              :     {
      81           22 :         if (*it == queryResponder)
      82              :         {
      83            8 :             *it = nullptr;
      84              : #if CHIP_CONFIG_MINMDNS_DYNAMIC_OPERATIONAL_RESPONDER_LIST
      85            8 :             mResponders.erase(it);
      86              : #endif
      87            8 :             return CHIP_NO_ERROR;
      88              :         }
      89              :     }
      90            0 :     return CHIP_ERROR_NOT_FOUND;
      91              : }
      92              : 
      93            1 : bool ResponseSender::HasQueryResponders() const
      94              : {
      95            1 :     for (auto responder : mResponders)
      96              :     {
      97            0 :         if (responder != nullptr)
      98              :         {
      99            0 :             return true;
     100              :         }
     101              :     }
     102            1 :     return false;
     103              : }
     104              : 
     105          135 : CHIP_ERROR ResponseSender::Respond(uint16_t messageId, const QueryData & query, const chip::Inet::IPPacketInfo * querySource,
     106              :                                    const ResponseConfiguration & configuration)
     107              : {
     108          135 :     mSendState.Reset(messageId, query, querySource);
     109              : 
     110          135 :     if (query.IsAnnounceBroadcast())
     111              :     {
     112              :         // Deny listing large amount of data
     113          100 :         mSendState.MarkWasSent(ResponseItemsSent::kServiceListingData);
     114              :     }
     115              : 
     116              :     // Responder has a stateful 'additional replies required' that is used within the response
     117              :     // loop. 'no additionals required' is set at the start and additionals are marked as the query
     118              :     // reply is built.
     119          474 :     for (auto & responder : mResponders)
     120              :     {
     121              :         {
     122          339 :             if (responder != nullptr)
     123              :             {
     124          339 :                 responder->ResetAdditionals();
     125              :             }
     126              :         }
     127              :     }
     128              : 
     129              :     // send all 'Answer' replies
     130              :     {
     131          135 :         const chip::System::Clock::Timestamp kTimeNow = chip::System::SystemClock().GetMonotonicTimestamp();
     132              : 
     133          135 :         QueryReplyFilter queryReplyFilter(query);
     134          135 :         QueryResponderRecordFilter responseFilter;
     135              : 
     136          135 :         responseFilter.SetReplyFilter(&queryReplyFilter);
     137              : 
     138          135 :         if (!mSendState.SendUnicast())
     139              :         {
     140              :             // According to https://tools.ietf.org/html/rfc6762#section-6  we should multicast at most 1/sec
     141              :             //
     142              :             // TODO: the 'last sent' value does NOT track the interface we used to send, so this may cause
     143              :             //       broadcasts on one interface to throttle broadcasts on another interface.
     144          100 :             responseFilter.SetIncludeOnlyMulticastBeforeMS(kTimeNow - chip::System::Clock::Seconds32(1));
     145              :         }
     146          474 :         for (auto & responder : mResponders)
     147              :         {
     148          339 :             if (responder == nullptr)
     149              :             {
     150            0 :                 continue;
     151              :             }
     152         1043 :             for (auto it = responder->begin(&responseFilter); it != responder->end(); it++)
     153              :             {
     154          704 :                 it->responder->AddAllResponses(querySource, this, configuration);
     155          704 :                 ReturnErrorOnFailure(mSendState.GetError());
     156              : 
     157          704 :                 responder->MarkAdditionalRepliesFor(it);
     158              : 
     159          704 :                 if (!mSendState.SendUnicast())
     160              :                 {
     161          644 :                     it->lastMulticastTime = kTimeNow;
     162              :                 }
     163              :             }
     164              :         }
     165          135 :     }
     166              : 
     167              :     // send all 'Additional' replies
     168              :     {
     169          135 :         if (!query.IsAnnounceBroadcast())
     170              :         {
     171              :             // Initial service broadcast should keep adding data as 'Answers' rather
     172              :             // than addtional data (https://datatracker.ietf.org/doc/html/rfc6762#section-8.3)
     173           35 :             mSendState.SetResourceType(ResourceType::kAdditional);
     174              :         }
     175              : 
     176          135 :         QueryReplyFilter queryReplyFilter(query);
     177              : 
     178          135 :         queryReplyFilter.SetIgnoreNameMatch(true).SetSendingAdditionalItems(true);
     179              : 
     180          135 :         QueryResponderRecordFilter responseFilter;
     181              :         responseFilter
     182          135 :             .SetReplyFilter(&queryReplyFilter) //
     183          135 :             .SetIncludeAdditionalRepliesOnly(true);
     184          430 :         for (auto & responder : mResponders)
     185              :         {
     186          305 :             if (responder == nullptr)
     187              :             {
     188            0 :                 continue;
     189              :             }
     190          663 :             for (auto it = responder->begin(&responseFilter); it != responder->end(); it++)
     191              :             {
     192          368 :                 it->responder->AddAllResponses(querySource, this, configuration);
     193          368 :                 ReturnErrorOnFailure(mSendState.GetError());
     194              :             }
     195              :         }
     196          135 :     }
     197              : 
     198          125 :     return FlushReply();
     199              : }
     200              : 
     201          135 : CHIP_ERROR ResponseSender::FlushReply()
     202              : {
     203          135 :     VerifyOrReturnError(mResponseBuilder.HasPacketBuffer(), CHIP_NO_ERROR); // nothing to flush
     204              : 
     205          104 :     if (mResponseBuilder.HasResponseRecords())
     206              :     {
     207              :         char srcAddressString[chip::Inet::IPAddress::kMaxStringLength];
     208          104 :         VerifyOrDie(mSendState.GetSourceAddress().ToString(srcAddressString) != nullptr);
     209              : 
     210          104 :         if (mSendState.SendUnicast())
     211              :         {
     212              : #if CHIP_MINMDNS_HIGH_VERBOSITY
     213              :             ChipLogDetail(Discovery, "Directly sending mDns reply to peer %s on port %d", srcAddressString,
     214              :                           mSendState.GetSourcePort());
     215              : #endif
     216           32 :             ReturnErrorOnFailure(mServer->DirectSend(mResponseBuilder.ReleasePacket(), mSendState.GetSourceAddress(),
     217              :                                                      mSendState.GetSourcePort(), mSendState.GetSourceInterfaceId()));
     218              :         }
     219              :         else
     220              :         {
     221              : #if CHIP_MINMDNS_HIGH_VERBOSITY
     222              :             ChipLogDetail(Discovery, "Broadcasting mDns reply for query from %s", srcAddressString);
     223              : #endif
     224           72 :             ReturnErrorOnFailure(mServer->BroadcastSend(mResponseBuilder.ReleasePacket(), kMdnsStandardPort,
     225              :                                                         mSendState.GetSourceInterfaceId(), mSendState.GetSourceAddress().Type()));
     226              :         }
     227              :     }
     228              : 
     229           74 :     return CHIP_NO_ERROR;
     230              : }
     231              : 
     232          104 : CHIP_ERROR ResponseSender::PrepareNewReplyPacket()
     233              : {
     234          104 :     chip::System::PacketBufferHandle buffer = chip::System::PacketBufferHandle::New(kPacketSizeBytes);
     235          104 :     VerifyOrReturnError(!buffer.IsNull(), CHIP_ERROR_NO_MEMORY);
     236              : 
     237          104 :     mResponseBuilder.Reset(std::move(buffer));
     238          104 :     mResponseBuilder.Header().SetMessageId(mSendState.GetMessageId());
     239              : 
     240          104 :     if (mSendState.IncludeQuery())
     241              :     {
     242           32 :         mResponseBuilder.AddQuery(*mSendState.GetQuery());
     243              :     }
     244              : 
     245          104 :     return CHIP_NO_ERROR;
     246          104 : }
     247              : 
     248         1072 : bool ResponseSender::ShouldSend(const Responder & responder) const
     249              : {
     250         1072 :     switch (responder.GetQType())
     251              :     {
     252           95 :     case QType::A:
     253           95 :         return !mSendState.GetWasSent(ResponseItemsSent::kIPv4Addresses);
     254           95 :     case QType::AAAA:
     255           95 :         return !mSendState.GetWasSent(ResponseItemsSent::kIPv6Addresses);
     256          678 :     case QType::PTR: {
     257              :         static const QNamePart kDnsSdQueryPath[] = { "_services", "_dns-sd", "_udp", "local" };
     258              : 
     259          678 :         if (responder.GetQName() == FullQName(kDnsSdQueryPath))
     260              :         {
     261          279 :             return !mSendState.GetWasSent(ResponseItemsSent::kServiceListingData);
     262              :         }
     263          399 :         break;
     264              :     }
     265          204 :     default:
     266          204 :         break;
     267              :     }
     268              : 
     269          603 :     return true;
     270              : }
     271              : 
     272          796 : void ResponseSender::ResponsesAdded(const Responder & responder)
     273              : {
     274          796 :     switch (responder.GetQType())
     275              :     {
     276           85 :     case QType::A:
     277           85 :         mSendState.MarkWasSent(ResponseItemsSent::kIPv4Addresses);
     278           85 :         break;
     279           85 :     case QType::AAAA:
     280           85 :         mSendState.MarkWasSent(ResponseItemsSent::kIPv6Addresses);
     281           85 :         break;
     282          626 :     default:
     283          626 :         break;
     284              :     }
     285          796 : }
     286              : 
     287          775 : void ResponseSender::AddResponse(const ResourceRecord & record)
     288              : {
     289          775 :     ReturnOnFailure(mSendState.GetError());
     290              : 
     291          775 :     if (!mResponseBuilder.HasPacketBuffer())
     292              :     {
     293          104 :         mSendState.SetError(PrepareNewReplyPacket());
     294          104 :         ReturnOnFailure(mSendState.GetError());
     295              :     }
     296              : 
     297          775 :     if (!mResponseBuilder.Ok())
     298              :     {
     299            0 :         mSendState.SetError(CHIP_ERROR_INCORRECT_STATE);
     300            0 :         return;
     301              :     }
     302              : 
     303          775 :     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          775 :     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 :             mSendState.SetError(CHIP_ERROR_INTERNAL);
     321              :         }
     322              :     }
     323              : }
     324              : 
     325              : } // namespace Minimal
     326              : } // namespace mdns
        

Generated by: LCOV version 2.0-1