Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020 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 "Server.h"
19 :
20 : #include <errno.h>
21 : #include <utility>
22 :
23 : #include <lib/dnssd/minimal_mdns/core/DnsHeader.h>
24 : #include <platform/CHIPDeviceLayer.h>
25 :
26 : namespace mdns {
27 : namespace Minimal {
28 : namespace {
29 :
30 : class ShutdownOnError
31 : {
32 : public:
33 18 : ShutdownOnError(ServerBase * s) : mServer(s) {}
34 18 : ~ShutdownOnError()
35 : {
36 18 : if (mServer != nullptr)
37 : {
38 0 : mServer->Shutdown();
39 : }
40 18 : }
41 :
42 18 : CHIP_ERROR ReturnSuccess()
43 : {
44 18 : mServer = nullptr;
45 18 : return CHIP_NO_ERROR;
46 : }
47 :
48 : private:
49 : ServerBase * mServer;
50 : };
51 :
52 : /**
53 : * Extracts the Listening UDP Endpoint from an underlying ServerBase::EndpointInfo
54 : */
55 : class ListenSocketPickerDelegate : public ServerBase::BroadcastSendDelegate
56 : {
57 : public:
58 48 : chip::Inet::UDPEndPointHandle Accept(ServerBase::EndpointInfo * info) override { return info->mListenUdp; }
59 : };
60 :
61 : #if CHIP_MINMDNS_USE_EPHEMERAL_UNICAST_PORT
62 :
63 : /**
64 : * Extracts the Querying UDP Endpoint from an underlying ServerBase::EndpointInfo
65 : */
66 : class QuerySocketPickerDelegate : public ServerBase::BroadcastSendDelegate
67 : {
68 : public:
69 0 : chip::Inet::UDPEndPointHandle Accept(ServerBase::EndpointInfo * info) override { return info->mUnicastQueryUdp; }
70 : };
71 :
72 : #else
73 :
74 : using QuerySocketPickerDelegate = ListenSocketPickerDelegate;
75 :
76 : #endif
77 :
78 : /**
79 : * Validates that an endpoint belongs to a specific interface/ip address type before forwarding the
80 : * endpoint accept logic to another BroadcastSendDelegate.
81 : *
82 : * Usage like:
83 : *
84 : * SomeDelegate *child = ....;
85 : * InterfaceTypeFilterDelegate filter(interfaceId, IPAddressType::IPv6, child);
86 : *
87 : * UDPEndPoint *udp = filter.Accept(endpointInfo);
88 : */
89 : class InterfaceTypeFilterDelegate : public ServerBase::BroadcastSendDelegate
90 : {
91 : public:
92 78 : InterfaceTypeFilterDelegate(chip::Inet::InterfaceId interface, chip::Inet::IPAddressType type,
93 78 : ServerBase::BroadcastSendDelegate * child) :
94 78 : mInterface(interface),
95 78 : mAddressType(type), mChild(child)
96 78 : {}
97 :
98 96 : chip::Inet::UDPEndPointHandle Accept(ServerBase::EndpointInfo * info) override
99 : {
100 96 : if ((info->mInterfaceId != mInterface) && (info->mInterfaceId != chip::Inet::InterfaceId::Null()))
101 : {
102 0 : return nullptr;
103 : }
104 :
105 96 : if ((mAddressType != chip::Inet::IPAddressType::kAny) && (info->mAddressType != mAddressType))
106 : {
107 48 : return nullptr;
108 : }
109 :
110 48 : return mChild->Accept(info);
111 : }
112 :
113 : private:
114 : chip::Inet::InterfaceId mInterface;
115 : chip::Inet::IPAddressType mAddressType;
116 : ServerBase::BroadcastSendDelegate * mChild = nullptr;
117 : };
118 :
119 : } // namespace
120 :
121 : namespace BroadcastIpAddresses {
122 :
123 : // Get standard mDNS Broadcast addresses
124 372 : chip::Inet::IPAddress Get(chip::Inet::IPAddressType addressType)
125 : {
126 : chip::Inet::IPAddress address;
127 : #if INET_CONFIG_ENABLE_IPV4
128 372 : if (addressType == chip::Inet::IPAddressType::kIPv4)
129 : {
130 186 : VerifyOrDie(chip::Inet::IPAddress::FromString("224.0.0.251", address));
131 : }
132 : else
133 : #endif
134 : {
135 186 : VerifyOrDie(chip::Inet::IPAddress::FromString("FF02::FB", address));
136 : }
137 372 : return address;
138 : }
139 :
140 : } // namespace BroadcastIpAddresses
141 :
142 : namespace {
143 :
144 : #if CHIP_ERROR_LOGGING
145 0 : const char * AddressTypeStr(chip::Inet::IPAddressType addressType)
146 : {
147 0 : switch (addressType)
148 : {
149 0 : case chip::Inet::IPAddressType::kIPv6:
150 0 : return "IPv6";
151 : #if INET_CONFIG_ENABLE_IPV4
152 0 : case chip::Inet::IPAddressType::kIPv4:
153 0 : return "IPv4";
154 : #endif // INET_CONFIG_ENABLE_IPV4
155 0 : default:
156 0 : return "UNKNOWN";
157 : }
158 : }
159 : #endif
160 :
161 : } // namespace
162 :
163 109 : ServerBase::~ServerBase()
164 : {
165 109 : Shutdown();
166 109 : }
167 :
168 114 : void ServerBase::Shutdown()
169 : {
170 114 : ShutdownEndpoints();
171 114 : mIsInitialized = false;
172 114 : }
173 :
174 167 : void ServerBase::ShutdownEndpoints()
175 : {
176 167 : mEndpoints.ReleaseAll();
177 167 : }
178 :
179 0 : void ServerBase::ShutdownEndpoint(EndpointInfo & aEndpoint)
180 : {
181 0 : mEndpoints.ReleaseObject(&aEndpoint);
182 0 : }
183 :
184 2 : bool ServerBase::IsListening() const
185 : {
186 2 : bool listening = false;
187 2 : mEndpoints.ForEachActiveObject([&](auto * endpoint) {
188 1 : if (endpoint->mListenUdp)
189 : {
190 1 : listening = true;
191 1 : return chip::Loop::Break;
192 : }
193 0 : return chip::Loop::Continue;
194 : });
195 2 : return listening;
196 : }
197 :
198 18 : CHIP_ERROR ServerBase::Listen(chip::Inet::EndPointManager<chip::Inet::UDPEndPoint> * udpEndPointManager, ListenIterator * it,
199 : uint16_t port)
200 : {
201 18 : ShutdownEndpoints(); // ensure everything starts fresh
202 :
203 18 : chip::Inet::InterfaceId interfaceId = chip::Inet::InterfaceId::Null();
204 : chip::Inet::IPAddressType addressType;
205 :
206 18 : ShutdownOnError autoShutdown(this);
207 :
208 54 : while (it->Next(&interfaceId, &addressType))
209 : {
210 36 : chip::Inet::UDPEndPointHandle listenUdp;
211 36 : ReturnErrorOnFailure(udpEndPointManager->NewEndPoint(listenUdp));
212 :
213 36 : ReturnErrorOnFailure(listenUdp->Bind(addressType, chip::Inet::IPAddress::Any, port, interfaceId));
214 :
215 36 : ReturnErrorOnFailure(listenUdp->Listen(OnUdpPacketReceived, nullptr /*OnReceiveError*/, this));
216 :
217 36 : CHIP_ERROR err = listenUdp->JoinMulticastGroup(interfaceId, BroadcastIpAddresses::Get(addressType));
218 :
219 72 : if (err != CHIP_NO_ERROR)
220 : {
221 : char interfaceName[chip::Inet::InterfaceId::kMaxIfNameLength];
222 0 : TEMPORARY_RETURN_IGNORED interfaceId.GetInterfaceName(interfaceName, sizeof(interfaceName));
223 :
224 : // Log only as non-fatal error. Failure to join will mean we reply to unicast queries only.
225 0 : ChipLogError(DeviceLayer, "MDNS failed to join multicast group on %s for address type %s: %" CHIP_ERROR_FORMAT,
226 : interfaceName, AddressTypeStr(addressType), err.Format());
227 :
228 0 : listenUdp.Release();
229 : }
230 :
231 : #if CHIP_MINMDNS_USE_EPHEMERAL_UNICAST_PORT
232 : // Separate UDP endpoint for unicast queries, bound to 0 (i.e. pick random ephemeral port)
233 : // - helps in not having conflicts on port 5353, will receive unicast replies directly
234 : // - has a *DRAWBACK* of unicast queries being considered LEGACY by mdns since they do
235 : // not originate from 5353 and the answers will include a query section.
236 36 : chip::Inet::UDPEndPointHandle unicastQueryUdp;
237 36 : ReturnErrorOnFailure(udpEndPointManager->NewEndPoint(unicastQueryUdp));
238 36 : ReturnErrorOnFailure(unicastQueryUdp->Bind(addressType, chip::Inet::IPAddress::Any, 0, interfaceId));
239 36 : ReturnErrorOnFailure(unicastQueryUdp->Listen(OnUdpPacketReceived, nullptr /*OnReceiveError*/, this));
240 : #endif
241 :
242 : #if CHIP_MINMDNS_USE_EPHEMERAL_UNICAST_PORT
243 36 : if (listenUdp || unicastQueryUdp)
244 : {
245 : // If allocation fails, the rref will not be consumed, so that the endpoint will also be freed correctly
246 36 : mEndpoints.CreateObject(interfaceId, addressType, std::move(listenUdp), std::move(unicastQueryUdp));
247 : }
248 : #else
249 : if (listenUdp)
250 : {
251 : // If allocation fails, the rref will not be consumed, so that the endpoint will also be freed correctly
252 : mEndpoints.CreateObject(interfaceId, addressType, std::move(listenUdp));
253 : }
254 : #endif
255 :
256 : // If at least one IPv6 interface is used by the mDNS server, notify the application that DNS-SD is ready.
257 36 : if (!mIsInitialized && addressType == chip::Inet::IPAddressType::kIPv6)
258 : {
259 : #if !CHIP_DEVICE_LAYER_NONE
260 2 : chip::DeviceLayer::ChipDeviceEvent event{};
261 2 : event.Type = chip::DeviceLayer::DeviceEventType::kDnssdInitialized;
262 2 : chip::DeviceLayer::PlatformMgr().PostEventOrDie(&event);
263 : #endif
264 2 : mIsInitialized = true;
265 : }
266 36 : }
267 :
268 18 : return autoShutdown.ReturnSuccess();
269 18 : }
270 :
271 0 : CHIP_ERROR ServerBase::DirectSend(chip::System::PacketBufferHandle && data, const chip::Inet::IPAddress & addr, uint16_t port,
272 : chip::Inet::InterfaceId interface)
273 : {
274 0 : CHIP_ERROR err = CHIP_ERROR_NOT_CONNECTED;
275 0 : mEndpoints.ForEachActiveObject([&](auto * info) {
276 0 : if (info->mListenUdp.IsNull())
277 : {
278 0 : return chip::Loop::Continue;
279 : }
280 :
281 0 : if (info->mAddressType != addr.Type())
282 : {
283 0 : return chip::Loop::Continue;
284 : }
285 :
286 0 : chip::Inet::InterfaceId boundIf = info->mListenUdp->GetBoundInterface();
287 :
288 0 : if ((boundIf.IsPresent()) && (boundIf != interface))
289 : {
290 0 : return chip::Loop::Continue;
291 : }
292 :
293 0 : err = info->mListenUdp->SendTo(addr, port, std::move(data));
294 0 : return chip::Loop::Break;
295 : });
296 :
297 0 : return err;
298 : }
299 :
300 0 : CHIP_ERROR ServerBase::BroadcastUnicastQuery(chip::System::PacketBufferHandle && data, uint16_t port)
301 : {
302 0 : QuerySocketPickerDelegate socketPicker;
303 0 : return BroadcastImpl(std::move(data), port, &socketPicker);
304 0 : }
305 :
306 0 : CHIP_ERROR ServerBase::BroadcastUnicastQuery(chip::System::PacketBufferHandle && data, uint16_t port,
307 : chip::Inet::InterfaceId interface, chip::Inet::IPAddressType addressType)
308 : {
309 0 : QuerySocketPickerDelegate socketPicker;
310 0 : InterfaceTypeFilterDelegate filter(interface, addressType, &socketPicker);
311 :
312 0 : return BroadcastImpl(std::move(data), port, &filter);
313 0 : }
314 :
315 78 : CHIP_ERROR ServerBase::BroadcastSend(chip::System::PacketBufferHandle && data, uint16_t port, chip::Inet::InterfaceId interface,
316 : chip::Inet::IPAddressType addressType)
317 : {
318 78 : ListenSocketPickerDelegate socketPicker;
319 78 : InterfaceTypeFilterDelegate filter(interface, addressType, &socketPicker);
320 :
321 78 : return BroadcastImpl(std::move(data), port, &filter);
322 78 : }
323 :
324 0 : CHIP_ERROR ServerBase::BroadcastSend(chip::System::PacketBufferHandle && data, uint16_t port)
325 : {
326 0 : ListenSocketPickerDelegate socketPicker;
327 0 : return BroadcastImpl(std::move(data), port, &socketPicker);
328 0 : }
329 :
330 78 : CHIP_ERROR ServerBase::BroadcastImpl(chip::System::PacketBufferHandle && data, uint16_t port, BroadcastSendDelegate * delegate)
331 : {
332 : // Broadcast requires sending data multiple times, each of which may error
333 : // out, yet broadcast only has a single error code.
334 : //
335 : // The general logic of error handling is:
336 : // - if no send done at all, return error
337 : // - if at least one broadcast succeeds, assume success overall
338 : // + some internal consistency validations for state error.
339 :
340 78 : unsigned successes = 0;
341 78 : unsigned failures = 0;
342 78 : CHIP_ERROR lastError = CHIP_ERROR_NO_ENDPOINT;
343 :
344 78 : if (chip::Loop::Break == mEndpoints.ForEachActiveObject([&](auto * info) {
345 96 : auto udp = delegate->Accept(info);
346 :
347 96 : if (udp.IsNull())
348 : {
349 48 : return chip::Loop::Continue;
350 : }
351 :
352 48 : CHIP_ERROR err = CHIP_NO_ERROR;
353 :
354 : /// The same packet needs to be sent over potentially multiple interfaces.
355 : /// LWIP does not like having a pbuf sent over serparate interfaces, hence we create a copy
356 : /// for sending via `CloneData`
357 : ///
358 : /// TODO: this wastes one copy of the data and that could be optimized away
359 48 : chip::System::PacketBufferHandle tempBuf = data.CloneData();
360 48 : if (tempBuf.IsNull())
361 : {
362 : // Not enough memory available to clone pbuf
363 0 : err = CHIP_ERROR_NO_MEMORY;
364 : }
365 48 : else if (info->mAddressType == chip::Inet::IPAddressType::kIPv6)
366 : {
367 24 : err = udp->SendTo(mIpv6BroadcastAddress, port, std::move(tempBuf), udp->GetBoundInterface());
368 : }
369 : #if INET_CONFIG_ENABLE_IPV4
370 24 : else if (info->mAddressType == chip::Inet::IPAddressType::kIPv4)
371 : {
372 24 : err = udp->SendTo(mIpv4BroadcastAddress, port, std::move(tempBuf), udp->GetBoundInterface());
373 : }
374 : #endif
375 : else
376 : {
377 : // This is a general error of internal consistency: every address has a known type. Fail completely otherwise.
378 0 : lastError = CHIP_ERROR_INCORRECT_STATE;
379 0 : return chip::Loop::Break;
380 : }
381 :
382 96 : if (err == CHIP_NO_ERROR)
383 : {
384 48 : successes++;
385 : }
386 : else
387 : {
388 0 : failures++;
389 0 : lastError = err;
390 : #if CHIP_DETAIL_LOGGING
391 : char ifaceName[chip::Inet::InterfaceId::kMaxIfNameLength];
392 0 : err = info->mInterfaceId.GetInterfaceName(ifaceName, sizeof(ifaceName));
393 0 : if (err != CHIP_NO_ERROR)
394 0 : strcpy(ifaceName, "???");
395 0 : ChipLogDetail(Discovery, "Warning: Attempt to mDNS broadcast failed on %s: %s", ifaceName, lastError.AsString());
396 : #endif
397 : }
398 48 : return chip::Loop::Continue;
399 96 : }))
400 : {
401 0 : return lastError;
402 : }
403 :
404 78 : if (failures != 0)
405 : {
406 : // if we had failures, log if the final status was success or failure, to make log reading
407 : // easier. Some mDNS failures may be expected (e.g. for interfaces unavailable)
408 0 : if (successes != 0)
409 : {
410 0 : ChipLogDetail(Discovery, "mDNS broadcast had only partial success: %u successes and %u failures.", successes, failures);
411 : }
412 : else
413 : {
414 0 : ChipLogProgress(Discovery, "mDNS broadcast full failed in %u separate send attempts.", failures);
415 : }
416 : }
417 :
418 78 : if (!successes)
419 : {
420 30 : return lastError;
421 : }
422 :
423 48 : return CHIP_NO_ERROR;
424 : }
425 :
426 10 : void ServerBase::OnUdpPacketReceived(chip::Inet::UDPEndPoint * endPoint, chip::System::PacketBufferHandle && buffer,
427 : const chip::Inet::IPPacketInfo * info)
428 : {
429 10 : ServerBase * srv = static_cast<ServerBase *>(endPoint->mAppState);
430 10 : if (!srv->mDelegate)
431 : {
432 0 : return;
433 : }
434 :
435 10 : mdns::Minimal::BytesRange data(buffer->Start(), buffer->Start() + buffer->DataLength());
436 10 : if (data.Size() < HeaderRef::kSizeBytes)
437 : {
438 0 : ChipLogError(Discovery, "Packet too small for mDNS data: %d bytes", static_cast<int>(data.Size()));
439 0 : return;
440 : }
441 :
442 10 : if (HeaderRef(const_cast<uint8_t *>(data.Start())).GetFlags().IsQuery())
443 : {
444 : // Only consider queries that are received on the same interface we are listening on.
445 : // Without this, queries show up on all addresses on all interfaces, resulting
446 : // in more replies than one would expect.
447 0 : if (endPoint->GetBoundInterface() == info->Interface)
448 : {
449 0 : srv->mDelegate->OnQuery(data, info);
450 : }
451 : }
452 : else
453 : {
454 10 : srv->mDelegate->OnResponse(data, info);
455 : }
456 : }
457 :
458 : } // namespace Minimal
459 : } // namespace mdns
|