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