LCOV - code coverage report
Current view: top level - lib/dnssd/minimal_mdns - ResponseSender.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 107 119 89.9 %
Date: 2024-02-15 08:20:41 Functions: 11 11 100.0 %

          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        1021 : bool ResponseSendingState::SendUnicast() const
      44             : {
      45        1021 :     return mQuery->RequestedUnicastAnswer() || (mSource->SrcPort != kMdnsStandardPort);
      46             : }
      47             : 
      48         110 : bool ResponseSendingState::IncludeQuery() const
      49             : {
      50         110 :     return (mSource->SrcPort != kMdnsStandardPort);
      51             : }
      52             : 
      53             : } // namespace Internal
      54             : 
      55        1028 : 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        1078 :     for (auto & responder : mResponders)
      61             :     {
      62        1049 :         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          29 :     mResponders.push_back(queryResponder);
      71          29 :     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         149 : CHIP_ERROR ResponseSender::Respond(uint16_t messageId, const QueryData & query, const chip::Inet::IPPacketInfo * querySource,
     106             :                                    const ResponseConfiguration & configuration)
     107             : {
     108         149 :     mSendState.Reset(messageId, query, querySource);
     109             : 
     110         149 :     if (query.IsAnnounceBroadcast())
     111             :     {
     112             :         // Deny listing large amount of data
     113         114 :         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         516 :     for (auto & responder : mResponders)
     120             :     {
     121             :         {
     122         367 :             if (responder != nullptr)
     123             :             {
     124         367 :                 responder->ResetAdditionals();
     125             :             }
     126             :         }
     127             :     }
     128             : 
     129             :     // send all 'Answer' replies
     130             :     {
     131         149 :         const chip::System::Clock::Timestamp kTimeNow = chip::System::SystemClock().GetMonotonicTimestamp();
     132             : 
     133         149 :         QueryReplyFilter queryReplyFilter(query);
     134         149 :         QueryResponderRecordFilter responseFilter;
     135             : 
     136         149 :         responseFilter.SetReplyFilter(&queryReplyFilter);
     137             : 
     138         149 :         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         114 :             responseFilter.SetIncludeOnlyMulticastBeforeMS(kTimeNow - chip::System::Clock::Seconds32(1));
     145             :         }
     146         516 :         for (auto & responder : mResponders)
     147             :         {
     148         367 :             if (responder == nullptr)
     149             :             {
     150           0 :                 continue;
     151             :             }
     152        1129 :             for (auto it = responder->begin(&responseFilter); it != responder->end(); it++)
     153             :             {
     154         762 :                 it->responder->AddAllResponses(querySource, this, configuration);
     155         762 :                 ReturnErrorOnFailure(mSendState.GetError());
     156             : 
     157         762 :                 responder->MarkAdditionalRepliesFor(it);
     158             : 
     159         762 :                 if (!mSendState.SendUnicast())
     160             :                 {
     161         702 :                     it->lastMulticastTime = kTimeNow;
     162             :                 }
     163             :             }
     164             :         }
     165         149 :     }
     166             : 
     167             :     // send all 'Additional' replies
     168             :     {
     169         149 :         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         149 :         QueryReplyFilter queryReplyFilter(query);
     177             : 
     178         149 :         queryReplyFilter.SetIgnoreNameMatch(true).SetSendingAdditionalItems(true);
     179             : 
     180         149 :         QueryResponderRecordFilter responseFilter;
     181             :         responseFilter
     182         149 :             .SetReplyFilter(&queryReplyFilter) //
     183         149 :             .SetIncludeAdditionalRepliesOnly(true);
     184         474 :         for (auto & responder : mResponders)
     185             :         {
     186         335 :             if (responder == nullptr)
     187             :             {
     188           0 :                 continue;
     189             :             }
     190         719 :             for (auto it = responder->begin(&responseFilter); it != responder->end(); it++)
     191             :             {
     192         394 :                 it->responder->AddAllResponses(querySource, this, configuration);
     193         394 :                 ReturnErrorOnFailure(mSendState.GetError());
     194             :             }
     195             :         }
     196         149 :     }
     197             : 
     198         139 :     return FlushReply();
     199             : }
     200             : 
     201         149 : CHIP_ERROR ResponseSender::FlushReply()
     202             : {
     203         149 :     ReturnErrorCodeIf(!mResponseBuilder.HasPacketBuffer(), CHIP_NO_ERROR); // nothing to flush
     204             : 
     205         110 :     if (mResponseBuilder.HasResponseRecords())
     206             :     {
     207             :         char srcAddressString[chip::Inet::IPAddress::kMaxStringLength];
     208         110 :         VerifyOrDie(mSendState.GetSourceAddress().ToString(srcAddressString) != nullptr);
     209             : 
     210         110 :         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          78 :             ReturnErrorOnFailure(mServer->BroadcastSend(mResponseBuilder.ReleasePacket(), kMdnsStandardPort,
     225             :                                                         mSendState.GetSourceInterfaceId(), mSendState.GetSourceAddress().Type()));
     226             :         }
     227             :     }
     228             : 
     229          80 :     return CHIP_NO_ERROR;
     230             : }
     231             : 
     232         110 : CHIP_ERROR ResponseSender::PrepareNewReplyPacket()
     233             : {
     234         110 :     chip::System::PacketBufferHandle buffer = chip::System::PacketBufferHandle::New(kPacketSizeBytes);
     235         110 :     ReturnErrorCodeIf(buffer.IsNull(), CHIP_ERROR_NO_MEMORY);
     236             : 
     237         110 :     mResponseBuilder.Reset(std::move(buffer));
     238         110 :     mResponseBuilder.Header().SetMessageId(mSendState.GetMessageId());
     239             : 
     240         110 :     if (mSendState.IncludeQuery())
     241             :     {
     242          32 :         mResponseBuilder.AddQuery(*mSendState.GetQuery());
     243             :     }
     244             : 
     245         110 :     return CHIP_NO_ERROR;
     246         110 : }
     247             : 
     248        1156 : bool ResponseSender::ShouldSend(const Responder & responder) const
     249             : {
     250        1156 :     switch (responder.GetQType())
     251             :     {
     252         101 :     case QType::A:
     253         101 :         return !mSendState.GetWasSent(ResponseItemsSent::kIPv4Addresses);
     254         101 :     case QType::AAAA:
     255         101 :         return !mSendState.GetWasSent(ResponseItemsSent::kIPv6Addresses);
     256         736 :     case QType::PTR: {
     257             :         static const QNamePart kDnsSdQueryPath[] = { "_services", "_dns-sd", "_udp", "local" };
     258             : 
     259         736 :         if (responder.GetQName() == FullQName(kDnsSdQueryPath))
     260             :         {
     261         307 :             return !mSendState.GetWasSent(ResponseItemsSent::kServiceListingData);
     262             :         }
     263         429 :         break;
     264             :     }
     265         218 :     default:
     266         218 :         break;
     267             :     }
     268             : 
     269         647 :     return true;
     270             : }
     271             : 
     272         852 : void ResponseSender::ResponsesAdded(const Responder & responder)
     273             : {
     274         852 :     switch (responder.GetQType())
     275             :     {
     276          91 :     case QType::A:
     277          91 :         mSendState.MarkWasSent(ResponseItemsSent::kIPv4Addresses);
     278          91 :         break;
     279          91 :     case QType::AAAA:
     280          91 :         mSendState.MarkWasSent(ResponseItemsSent::kIPv6Addresses);
     281          91 :         break;
     282         670 :     default:
     283         670 :         break;
     284             :     }
     285         852 : }
     286             : 
     287         831 : void ResponseSender::AddResponse(const ResourceRecord & record)
     288             : {
     289         831 :     ReturnOnFailure(mSendState.GetError());
     290             : 
     291         831 :     if (!mResponseBuilder.HasPacketBuffer())
     292             :     {
     293         110 :         mSendState.SetError(PrepareNewReplyPacket());
     294         110 :         ReturnOnFailure(mSendState.GetError());
     295             :     }
     296             : 
     297         831 :     if (!mResponseBuilder.Ok())
     298             :     {
     299           0 :         mSendState.SetError(CHIP_ERROR_INCORRECT_STATE);
     300           0 :         return;
     301             :     }
     302             : 
     303         831 :     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         831 :     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 1.14