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
|