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
|