blob: c1d07c5ba6f6aa001b4d25f2c5573dbe0de9de16 [file]
/*
*
* Copyright (c) 2020 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <inet/IPAddress.h>
#include <inet/InetInterface.h>
#include <inet/InetLayer.h>
#include <inet/UDPEndPoint.h>
#include <lib/core/CHIPError.h>
#include <lib/dnssd/minimal_mdns/core/BytesRange.h>
namespace mdns {
namespace Minimal {
namespace BroadcastIpAddresses {
// Get standard mDNS Broadcast addresses
void GetIpv6Into(chip::Inet::IPAddress & dest);
void GetIpv4Into(chip::Inet::IPAddress & dest);
} // namespace BroadcastIpAddresses
/// Provides a list of intefaces to listen on.
///
/// When listening on IP, both IP address type (IPv4 or IPv6) and interface id
/// are important. In particular, when using link-local IP addresses, the actual
/// interface matters (e.g. FF02::FB will care over which IPv6 interface it is sent)
///
/// For MDNS in particular, you may want:
/// - IPv4 listen on InterfaceId::Null()
/// - IPv6 listen on every specific interface id available (except local loopback and other
/// not usable interfaces like docker)
class ListenIterator
{
public:
virtual ~ListenIterator() {}
// Get the next interface/address type to listen on
virtual bool Next(chip::Inet::InterfaceId * id, chip::Inet::IPAddressType * type) = 0;
};
/// Handles mDNS Server Callbacks
class ServerDelegate
{
public:
virtual ~ServerDelegate() {}
// Callback of when a query is received
virtual void OnQuery(const BytesRange & data, const chip::Inet::IPPacketInfo * info) = 0;
// Callback of when a response is received
virtual void OnResponse(const BytesRange & data, const chip::Inet::IPPacketInfo * info) = 0;
};
// Defines an mDNS server that listens on one or more interfaces.
//
// I can send and receive mDNS packets (requests/replies)
class ServerBase
{
public:
struct EndpointInfo
{
chip::Inet::InterfaceId interfaceId = chip::Inet::InterfaceId::Null();
chip::Inet::IPAddressType addressType;
chip::Inet::UDPEndPoint * udp = nullptr;
};
ServerBase(EndpointInfo * endpointStorage, size_t kStorageSize) : mEndpoints(endpointStorage), mEndpointCount(kStorageSize)
{
for (size_t i = 0; i < mEndpointCount; i++)
{
mEndpoints[i].udp = nullptr;
}
BroadcastIpAddresses::GetIpv6Into(mIpv6BroadcastAddress);
#if INET_CONFIG_ENABLE_IPV4
BroadcastIpAddresses::GetIpv4Into(mIpv4BroadcastAddress);
#endif
}
virtual ~ServerBase();
/// Closes all currently open endpoints
void Shutdown();
/// Listen on the given interfaces/address types.
///
/// Since mDNS uses link-local addresses, one generally wants to listen on all
/// non-loopback interfaces.
CHIP_ERROR Listen(chip::Inet::InetLayer * inetLayer, ListenIterator * it, uint16_t port);
/// Send the specified packet to a destination IP address over the specified address
virtual CHIP_ERROR DirectSend(chip::System::PacketBufferHandle && data, const chip::Inet::IPAddress & addr, uint16_t port,
chip::Inet::InterfaceId interface);
/// Send a specific packet broadcast to all interfaces
virtual CHIP_ERROR BroadcastSend(chip::System::PacketBufferHandle && data, uint16_t port);
/// Send a specific packet broadcast to a specific interface using a specific address type
virtual CHIP_ERROR BroadcastSend(chip::System::PacketBufferHandle && data, uint16_t port, chip::Inet::InterfaceId interface,
chip::Inet::IPAddressType addressType);
ServerBase & SetDelegate(ServerDelegate * d)
{
mDelegate = d;
return *this;
}
/// How many endpoints are availabe to be used by the server.
size_t GetEndpointCount() const { return mEndpointCount; }
/// Get the endpoints that are used by this server
///
/// Entries with non-null UDP are considered usable.
const EndpointInfo * GetEndpoints() const { return mEndpoints; }
/// A server is considered listening if any UDP endpoint is active.
///
/// This is expected to return false after any Shutdown() and will
/// return true IFF lListen was called and the listen iterator successfully
/// found a valid listening interface.
bool IsListening() const;
private:
static void OnUdpPacketReceived(chip::Inet::UDPEndPoint * endPoint, chip::System::PacketBufferHandle && buffer,
const chip::Inet::IPPacketInfo * info);
EndpointInfo * mEndpoints; // possible endpoints, to listen on multiple interfaces
const size_t mEndpointCount; // how many endpoints are allocated
ServerDelegate * mDelegate = nullptr;
// Broadcast IP addresses are cached to not require a string parse every time
// Ideally we should be able to constexpr these
chip::Inet::IPAddress mIpv6BroadcastAddress;
#if INET_CONFIG_ENABLE_IPV4
chip::Inet::IPAddress mIpv4BroadcastAddress;
#endif
};
template <size_t kCount>
class Server : public ServerBase
{
public:
Server() : ServerBase(mAllocatedStorage, kCount) {}
~Server() {}
private:
EndpointInfo mAllocatedStorage[kCount];
};
} // namespace Minimal
} // namespace mdns