[mdns] add native mDNS support in OT (#9797)

This commit introduces native mDNS support within the OpenThread
stack, implementing RFC 6762 compliant registration of hosts,
services, and keys. It supports the following functionalities:

- Sending probes to claim names.
- Sending announcements on initial registration and changes.
- Sending "goodbye" announcements when unregistered or upon record
  removal.
- Negative responses (NSEC).
- Support for service sub-types and their addition/removal.
- Support for `_services._dns-sd._udp` queries (all service types).
- Responding to queries (including "QU" questions).
- Delay mechanism when responding to multi-question query messages,
  ensuring unique answers.
- Providing extra records in the Additional Data section if not
  already in the Answer section (e.g., on a PTR query, include SRV
  and host AAAA addresses).
- Implementing Known-Answer Suppression.
- Supporting multi-packet queries with known answers in follow-up
  messages.
- Rate-limiting record multicasts (once per second).
- Rate-limiting probe responses (once per 250ms).
- Detecting conflicts after probes.
- Limiting the size of emitted responses or probes, breaking into
  multiple messages if necessary.
- Detecting self originating messages (sent by mDNS module).
- Support for service browser.
- Support for service resolvers (SRV and TXT records) and IPv4/IPv6
  address resolvers for hostnames.
- Introduces smart cache management:
  - Passively caches service records for active browsers.
  - Passively caches address records for active service resolvers.
- Enables multiple simultaneous browsers/resolvers for the same
  service/host.

This commit introduces public `otMdns` OpenThread APIs and related CLI
commands for the mDNS module.

For platform abstraction, `otPlatMdns` APIs are defined in
`mdns_socket.h` (e.g., to send or receive mDNS messages):

- An implementation of the platform APIs is provided for posix.
- Also under the simulation platform, a simplified implementation of the
  `otPlatMdns` APIs is provided (intended for testing).

This commit also adds a detailed `test_mdns` unit test, validating
various functionalities and covering potential edge cases.
This commit is contained in:
Abtin Keshavarzian
2024-04-09 08:32:01 -07:00
committed by GitHub
parent 6de5cd8256
commit bf41332061
41 changed files with 18547 additions and 3 deletions
+2
View File
@@ -58,6 +58,7 @@
* @defgroup api-dnssd-server DNS-SD Server
* @defgroup api-icmp6 ICMPv6
* @defgroup api-ip6 IPv6
* @defgroup api-mdns Multicast DNS
* @defgroup api-nat64 NAT64
* @defgroup api-srp SRP
* @defgroup api-ping-sender Ping Sender
@@ -181,6 +182,7 @@
* @defgroup plat-memory Memory
* @defgroup plat-messagepool Message Pool
* @defgroup plat-misc Miscellaneous
* @defgroup plat-mdns Multicast DNS
* @defgroup plat-multipan Multipan
* @defgroup plat-otns Network Simulator
* @defgroup plat-radio Radio
+1
View File
@@ -69,6 +69,7 @@
* @defgroup config-mesh-forwarder Mesh Forwarder
* @defgroup config-misc Miscellaneous Constants
* @defgroup config-mle MLE Service
* @defgroup config-mdns Multicast DNS
* @defgroup config-nat64 NAT64
* @defgroup config-netdata-publisher Network Data Publisher
* @defgroup config-network-diagnostic Network Diagnostics
+1
View File
@@ -215,6 +215,7 @@ ot_option(OT_LINK_METRICS_SUBJECT OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENA
ot_option(OT_LINK_RAW OPENTHREAD_CONFIG_LINK_RAW_ENABLE "link raw service")
ot_option(OT_LOG_LEVEL_DYNAMIC OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE "dynamic log level control")
ot_option(OT_MAC_FILTER OPENTHREAD_CONFIG_MAC_FILTER_ENABLE "mac filter")
ot_option(OT_MDNS OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE "multicast DNS (mDNS)")
ot_option(OT_MESH_DIAG OPENTHREAD_CONFIG_MESH_DIAG_ENABLE "mesh diag")
ot_option(OT_MESSAGE_USE_HEAP OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE "heap allocator for message buffers")
ot_option(OT_MLE_LONG_ROUTES OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE "MLE long routes extension (experimental)")
@@ -79,6 +79,7 @@
#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE 1
#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE 1
#define OPENTHREAD_CONFIG_MLR_ENABLE 1
#define OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE 1
#define OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE 0
#define OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE 1
#define OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE 1
@@ -99,5 +100,6 @@
#define OPENTHREAD_CONFIG_DNS_DSO_MOCK_PLAT_APIS_ENABLE 1
#define OPENTHREAD_CONFIG_BORDER_ROUTING_MOCK_PLAT_APIS_ENABLE 1
#define OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_MOCK_PLAT_APIS_ENABLE 1
#define OPENTHREAD_CONFIG_MULTICAST_DNS_MOCK_PLAT_APIS_ENABLE 1
#endif // OT_CORE_CONFIG_CHECK_SIZE_BR_H_
@@ -69,6 +69,7 @@ add_library(openthread-simulation
flash.c
infra_if.c
logging.c
mdns_socket.c
misc.c
multipan.c
radio.c
+569
View File
@@ -0,0 +1,569 @@
/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "platform-simulation.h"
#include <openthread/nat64.h>
#include <openthread/platform/mdns_socket.h>
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
//---------------------------------------------------------------------------------------------------------------------
#if OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX
// Provide a simplified POSIX based implementation of `otPlatMdns`
// platform APIs. This is intended for testing.
#include <openthread/ip6.h>
#include <arpa/inet.h>
#include <errno.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "simul_utils.h"
#include "utils/code_utils.h"
#define MAX_BUFFER_SIZE 1600
#define MDNS_PORT 5353
static bool sEnabled = false;
static uint32_t sInfraIfIndex;
static int sMdnsFd4 = -1;
static int sMdnsFd6 = -1;
/* this is a portability hack */
#ifndef IPV6_ADD_MEMBERSHIP
#ifdef IPV6_JOIN_GROUP
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
#endif
#endif
#ifndef IPV6_DROP_MEMBERSHIP
#ifdef IPV6_LEAVE_GROUP
#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
#endif
#endif
#define VerifyOrDie(aCondition, aErrMsg) \
do \
{ \
if (!(aCondition)) \
{ \
fprintf(stderr, "\n\r" aErrMsg ". errono:%s\n\r", strerror(errno)); \
exit(1); \
} \
} while (false)
static void SetReuseAddrPort(int aFd)
{
int ret;
int yes = 1;
ret = setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes));
VerifyOrDie(ret >= 0, "setsocketopt(SO_REUSEADDR) failed");
ret = setsockopt(aFd, SOL_SOCKET, SO_REUSEPORT, (char *)&yes, sizeof(yes));
VerifyOrDie(ret >= 0, "setsocketopt(SO_REUSEPORT) failed");
}
static void OpenIp4Socket(uint32_t aInfraIfIndex)
{
OT_UNUSED_VARIABLE(aInfraIfIndex);
struct sockaddr_in addr;
int fd;
int ret;
uint8_t u8;
int value;
fd = socket(AF_INET, SOCK_DGRAM, 0);
VerifyOrDie(fd >= 0, "socket() failed");
#ifdef __linux__
{
char nameBuffer[IF_NAMESIZE];
const char *ifname;
ifname = if_indextoname(aInfraIfIndex, nameBuffer);
VerifyOrDie(ifname != NULL, "if_indextoname() failed");
ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname));
VerifyOrDie(ret >= 0, "setsocketopt(SO_BINDTODEVICE) failed");
}
#else
value = aInfraIfIndex;
ret = setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &value, sizeof(value));
#endif
u8 = 255;
ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &u8, sizeof(u8));
VerifyOrDie(ret >= 0, "setsocketopt(IP_MULTICAST_TTL) failed");
value = 255;
ret = setsockopt(fd, IPPROTO_IP, IP_TTL, &value, sizeof(value));
VerifyOrDie(ret >= 0, "setsocketopt(IP_TTL) failed");
u8 = 1;
ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &u8, sizeof(u8));
VerifyOrDie(ret >= 0, "setsocketopt(IP_MULTICAST_LOOP) failed");
SetReuseAddrPort(fd);
{
struct ip_mreqn mreqn;
memset(&mreqn, 0, sizeof(mreqn));
mreqn.imr_multiaddr.s_addr = inet_addr("224.0.0.251");
mreqn.imr_ifindex = aInfraIfIndex;
ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn));
VerifyOrDie(ret >= 0, "setsocketopt(IP_MULTICAST_IF) failed");
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(MDNS_PORT);
ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
VerifyOrDie(ret >= 0, "bind() failed");
sMdnsFd4 = fd;
}
static void JoinOrLeaveIp4MulticastGroup(bool aJoin, uint32_t aInfraIfIndex)
{
struct ip_mreqn mreqn;
int ret;
memset(&mreqn, 0, sizeof(mreqn));
mreqn.imr_multiaddr.s_addr = inet_addr("224.0.0.251");
mreqn.imr_ifindex = aInfraIfIndex;
if (aJoin)
{
// Suggested workaround for netif not dropping
// a previous multicast membership.
setsockopt(sMdnsFd4, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn));
}
ret = setsockopt(sMdnsFd4, IPPROTO_IP, aJoin ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn));
VerifyOrDie(ret >= 0, "setsocketopt(IP_ADD/DROP_MEMBERSHIP) failed");
}
static void OpenIp6Socket(uint32_t aInfraIfIndex)
{
OT_UNUSED_VARIABLE(aInfraIfIndex);
struct sockaddr_in6 addr6;
int fd;
int ret;
int value;
fd = socket(AF_INET6, SOCK_DGRAM, 0);
VerifyOrDie(fd >= 0, "socket() failed");
#ifdef __linux__
{
char nameBuffer[IF_NAMESIZE];
const char *ifname;
ifname = if_indextoname(aInfraIfIndex, nameBuffer);
VerifyOrDie(ifname != NULL, "if_indextoname() failed");
ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname));
VerifyOrDie(ret >= 0, "setsocketopt(SO_BINDTODEVICE) failed");
}
#else
value = aInfraIfIndex;
ret = setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, &value, sizeof(value));
#endif
value = 255;
ret = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &value, sizeof(value));
VerifyOrDie(ret >= 0, "setsocketopt(IPV6_MULTICAST_HOPS) failed");
value = 255;
ret = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &value, sizeof(value));
VerifyOrDie(ret >= 0, "setsocketopt(IPV6_UNICAST_HOPS) failed");
value = 1;
ret = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));
VerifyOrDie(ret >= 0, "setsocketopt(IPV6_V6ONLY) failed");
value = aInfraIfIndex;
ret = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &value, sizeof(value));
VerifyOrDie(ret >= 0, "setsocketopt(IPV6_MULTICAST_IF) failed");
value = 1;
ret = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &value, sizeof(value));
VerifyOrDie(ret >= 0, "setsocketopt(IPV6_MULTICAST_LOOP) failed");
SetReuseAddrPort(fd);
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(MDNS_PORT);
ret = bind(fd, (struct sockaddr *)&addr6, sizeof(addr6));
VerifyOrDie(ret >= 0, "bind() failed");
sMdnsFd6 = fd;
}
static void JoinOrLeaveIp6MulticastGroup(bool aJoin, uint32_t aInfraIfIndex)
{
struct ipv6_mreq mreq6;
int ret;
memset(&mreq6, 0, sizeof(mreq6));
inet_pton(AF_INET6, "ff02::fb", &mreq6.ipv6mr_multiaddr);
mreq6.ipv6mr_interface = (int)aInfraIfIndex;
if (aJoin)
{
// Suggested workaround for netif not dropping
// a previous multicast membership.
setsockopt(sMdnsFd6, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
}
ret = setsockopt(sMdnsFd6, IPPROTO_IPV6, aJoin ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
VerifyOrDie(ret >= 0, "setsocketopt(IP6_ADD/DROP_MEMBERSHIP) failed");
}
otError otPlatMdnsSetListeningEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex)
{
OT_UNUSED_VARIABLE(aInstance);
if (aEnable)
{
otEXPECT(!sEnabled);
OpenIp4Socket(aInfraIfIndex);
JoinOrLeaveIp4MulticastGroup(/* aJoin */ true, aInfraIfIndex);
OpenIp6Socket(aInfraIfIndex);
JoinOrLeaveIp6MulticastGroup(/* aJoin */ true, aInfraIfIndex);
sEnabled = true;
sInfraIfIndex = aInfraIfIndex;
}
else
{
otEXPECT(sEnabled);
JoinOrLeaveIp4MulticastGroup(/* aJoin */ false, aInfraIfIndex);
JoinOrLeaveIp6MulticastGroup(/* aJoin */ false, aInfraIfIndex);
close(sMdnsFd4);
close(sMdnsFd6);
sEnabled = false;
}
exit:
return OT_ERROR_NONE;
}
void otPlatMdnsSendMulticast(otInstance *aInstance, otMessage *aMessage, uint32_t aInfraIfIndex)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aInfraIfIndex);
uint8_t buffer[MAX_BUFFER_SIZE];
uint16_t length;
int bytes;
otEXPECT(sEnabled);
length = otMessageRead(aMessage, 0, buffer, sizeof(buffer));
otMessageFree(aMessage);
{
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("224.0.0.251");
addr.sin_port = htons(MDNS_PORT);
bytes = sendto(sMdnsFd4, buffer, length, 0, (struct sockaddr *)&addr, sizeof(addr));
VerifyOrDie((bytes == length), "sendTo(sMdnsFd4) failed");
}
{
struct sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(MDNS_PORT);
inet_pton(AF_INET6, "ff02::fb", &addr6.sin6_addr);
bytes = sendto(sMdnsFd6, buffer, length, 0, (struct sockaddr *)&addr6, sizeof(addr6));
VerifyOrDie((bytes == length), "sendTo(sMdnsFd6) failed");
}
exit:
return;
}
void otPlatMdnsSendUnicast(otInstance *aInstance, otMessage *aMessage, const otPlatMdnsAddressInfo *aAddress)
{
OT_UNUSED_VARIABLE(aInstance);
otIp4Address ip4Addr;
uint8_t buffer[MAX_BUFFER_SIZE];
uint16_t length;
int bytes;
otEXPECT(sEnabled);
length = otMessageRead(aMessage, 0, buffer, sizeof(buffer));
otMessageFree(aMessage);
if (otIp4FromIp4MappedIp6Address(&aAddress->mAddress, &ip4Addr) == OT_ERROR_NONE)
{
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
memcpy(&addr.sin_addr.s_addr, &ip4Addr, sizeof(otIp4Address));
addr.sin_port = htons(MDNS_PORT);
bytes = sendto(sMdnsFd4, buffer, length, 0, (struct sockaddr *)&addr, sizeof(addr));
VerifyOrDie((bytes == length), "sendTo(sMdnsFd4) failed");
}
else
{
struct sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(MDNS_PORT);
memcpy(&addr6.sin6_addr, &aAddress->mAddress, sizeof(otIp6Address));
bytes = sendto(sMdnsFd6, buffer, length, 0, (struct sockaddr *)&addr6, sizeof(addr6));
VerifyOrDie((bytes == length), "sendTo(sMdnsFd6) failed");
}
exit:
return;
}
void platformMdnsSocketUpdateFdSet(fd_set *aReadFdSet, int *aMaxFd)
{
otEXPECT(sEnabled);
utilsAddFdToFdSet(sMdnsFd4, aReadFdSet, aMaxFd);
utilsAddFdToFdSet(sMdnsFd6, aReadFdSet, aMaxFd);
exit:
return;
}
void platformMdnsSocketProcess(otInstance *aInstance, const fd_set *aReadFdSet)
{
otEXPECT(sEnabled);
if (FD_ISSET(sMdnsFd4, aReadFdSet))
{
uint8_t buffer[MAX_BUFFER_SIZE];
struct sockaddr_in sockaddr;
otPlatMdnsAddressInfo addrInfo;
otMessage *message;
socklen_t len = sizeof(sockaddr);
ssize_t rval;
memset(&sockaddr, 0, sizeof(sockaddr));
rval = recvfrom(sMdnsFd4, (char *)&buffer, sizeof(buffer), 0, (struct sockaddr *)&sockaddr, &len);
VerifyOrDie(rval >= 0, "recvfrom() failed");
message = otIp6NewMessage(aInstance, NULL);
VerifyOrDie(message != NULL, "otIp6NewMessage() failed");
VerifyOrDie(otMessageAppend(message, buffer, (uint16_t)rval) == OT_ERROR_NONE, "otMessageAppend() failed");
memset(&addrInfo, 0, sizeof(addrInfo));
otIp4ToIp4MappedIp6Address((otIp4Address *)(&sockaddr.sin_addr.s_addr), &addrInfo.mAddress);
addrInfo.mPort = MDNS_PORT;
addrInfo.mInfraIfIndex = sInfraIfIndex;
otPlatMdnsHandleReceive(aInstance, message, /* aInUnicast */ false, &addrInfo);
}
if (FD_ISSET(sMdnsFd6, aReadFdSet))
{
uint8_t buffer[MAX_BUFFER_SIZE];
struct sockaddr_in6 sockaddr6;
otPlatMdnsAddressInfo addrInfo;
otMessage *message;
socklen_t len = sizeof(sockaddr6);
ssize_t rval;
memset(&sockaddr6, 0, sizeof(sockaddr6));
rval = recvfrom(sMdnsFd6, (char *)&buffer, sizeof(buffer), 0, (struct sockaddr *)&sockaddr6, &len);
VerifyOrDie(rval >= 0, "recvfrom(sMdnsFd6) failed");
message = otIp6NewMessage(aInstance, NULL);
VerifyOrDie(message != NULL, "otIp6NewMessage() failed");
VerifyOrDie(otMessageAppend(message, buffer, (uint16_t)rval) == OT_ERROR_NONE, "otMessageAppend() failed");
memset(&addrInfo, 0, sizeof(addrInfo));
memcpy(&addrInfo.mAddress, &sockaddr6.sin6_addr, sizeof(otIp6Address));
addrInfo.mPort = MDNS_PORT;
addrInfo.mInfraIfIndex = sInfraIfIndex;
otPlatMdnsHandleReceive(aInstance, message, /* aInUnicast */ false, &addrInfo);
}
exit:
return;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Add weak implementation of `ot` APIs for RCP build. Note that
// `simulation` platform does not get `OPENTHREAD_RADIO` config)
OT_TOOL_WEAK uint16_t otMessageRead(const otMessage *aMessage, uint16_t aOffset, void *aBuf, uint16_t aLength)
{
OT_UNUSED_VARIABLE(aMessage);
OT_UNUSED_VARIABLE(aOffset);
OT_UNUSED_VARIABLE(aBuf);
OT_UNUSED_VARIABLE(aLength);
fprintf(stderr, "\n\rWeak otMessageRead() is incorrectly used\n\r");
exit(1);
return 0;
}
OT_TOOL_WEAK void otMessageFree(otMessage *aMessage)
{
OT_UNUSED_VARIABLE(aMessage);
fprintf(stderr, "\n\rWeak otMessageFree() is incorrectly used\n\r");
exit(1);
}
OT_TOOL_WEAK otMessage *otIp6NewMessage(otInstance *aInstance, const otMessageSettings *aSettings)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aSettings);
fprintf(stderr, "\n\rWeak otIp6NewMessage() is incorrectly used\n\r");
exit(1);
return NULL;
}
OT_TOOL_WEAK otError otMessageAppend(otMessage *aMessage, const void *aBuf, uint16_t aLength)
{
OT_UNUSED_VARIABLE(aMessage);
OT_UNUSED_VARIABLE(aBuf);
OT_UNUSED_VARIABLE(aLength);
fprintf(stderr, "\n\rWeak otMessageFree() is incorrectly used\n\r");
exit(1);
return OT_ERROR_NOT_IMPLEMENTED;
}
OT_TOOL_WEAK void otIp4ToIp4MappedIp6Address(const otIp4Address *aIp4Address, otIp6Address *aIp6Address)
{
OT_UNUSED_VARIABLE(aIp4Address);
OT_UNUSED_VARIABLE(aIp6Address);
fprintf(stderr, "\n\rWeak otIp4ToIp4MappedIp6Address() is incorrectly used\n\r");
exit(1);
}
OT_TOOL_WEAK otError otIp4FromIp4MappedIp6Address(const otIp6Address *aIp6Address, otIp4Address *aIp4Address)
{
OT_UNUSED_VARIABLE(aIp6Address);
OT_UNUSED_VARIABLE(aIp4Address);
fprintf(stderr, "\n\rWeak otIp4FromIp4MappedIp6Address() is incorrectly used\n\r");
exit(1);
return OT_ERROR_NOT_IMPLEMENTED;
}
OT_TOOL_WEAK void otPlatMdnsHandleReceive(otInstance *aInstance,
otMessage *aMessage,
bool aIsUnicast,
const otPlatMdnsAddressInfo *aAddress)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aMessage);
OT_UNUSED_VARIABLE(aIsUnicast);
OT_UNUSED_VARIABLE(aAddress);
fprintf(stderr, "\n\rWeak otPlatMdnsHandleReceive() is incorrectly used\n\r");
exit(1);
}
//---------------------------------------------------------------------------------------------------------------------
#else // OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX
otError otPlatMdnsSetListeningEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aEnable);
OT_UNUSED_VARIABLE(aInfraIfIndex);
return OT_ERROR_NOT_IMPLEMENTED;
}
void otPlatMdnsSendMulticast(otInstance *aInstance, otMessage *aMessage, uint32_t aInfraIfIndex)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aInfraIfIndex);
otMessageFree(aMessage);
}
void otPlatMdnsSendUnicast(otInstance *aInstance, otMessage *aMessage, const otPlatMdnsAddressInfo *aAddress)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aAddress);
otMessageFree(aMessage);
}
#endif // OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX
#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
@@ -103,3 +103,16 @@
#ifndef OPENTHREAD_SIMULATION_MAX_NETWORK_SIZE
#define OPENTHREAD_SIMULATION_MAX_NETWORK_SIZE 33
#endif
/**
* @def OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX
*
* Define as 1 for the simulation platform to provide a simplified implementation of `otPlatMdns` APIs using posix
* socket.
*
* This is intended for testing of the OpenThread Multicast DNS (mDNS) module.
*
*/
#ifndef OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX
#define OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX 0
#endif
@@ -341,6 +341,28 @@ void platformInfraIfProcess(otInstance *aInstance, const fd_set *aReadFdSet, con
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX
/**
* Updates the file descriptor sets with file descriptors used by the mDNS socket.
*
* @param[in,out] aReadFdSet A pointer to the read file descriptors.
* @param[in,out] aMaxFd A pointer to the max file descriptor.
*
*/
void platformMdnsSocketUpdateFdSet(fd_set *aReadFdSet, int *aMaxFd);
/**
* Performs mDNs Socket processing.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aReadFdSet A pointer to the read file descriptors.
*
*/
void platformMdnsSocketProcess(otInstance *aInstance, const fd_set *aReadFdSet);
#endif
/**
* Shuts down the BLE service used by OpenThread.
*
+6
View File
@@ -295,6 +295,9 @@ void otSysProcessDrivers(otInstance *aInstance)
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
platformInfraIfUpdateFdSet(&read_fds, &write_fds, &max_fd);
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX
platformMdnsSocketUpdateFdSet(&read_fds, &max_fd);
#endif
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
platformBleUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd);
@@ -329,6 +332,9 @@ void otSysProcessDrivers(otInstance *aInstance)
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
platformInfraIfProcess(aInstance, &read_fds, &write_fds);
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX
platformMdnsSocketProcess(aInstance, &read_fds);
#endif
if (gTerminate)
{
+2
View File
@@ -73,6 +73,7 @@ source_set("openthread") {
"link_metrics.h",
"link_raw.h",
"logging.h",
"mdns.h",
"mesh_diag.h",
"message.h",
"multi_radio.h",
@@ -97,6 +98,7 @@ source_set("openthread") {
"platform/flash.h",
"platform/infra_if.h",
"platform/logging.h",
"platform/mdns_socket.h",
"platform/memory.h",
"platform/messagepool.h",
"platform/misc.h",
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (403)
#define OPENTHREAD_API_VERSION (404)
/**
* @addtogroup api-instance
+747
View File
@@ -0,0 +1,747 @@
/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* @brief
* This file includes the mDNS related APIs.
*
*/
#ifndef OPENTHREAD_MULTICAST_DNS_H_
#define OPENTHREAD_MULTICAST_DNS_H_
#include <stdint.h>
#include <openthread/error.h>
#include <openthread/instance.h>
#include <openthread/ip6.h>
#include <openthread/platform/dnssd.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup api-mdns
*
* @brief
* This module includes APIs for Multicast DNS (mDNS).
*
* @{
*
* The mDNS APIs are available when the mDNS support `OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE` is enabled and the
* `OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE` is also enabled.
*
*/
/**
* Represents a request ID (`uint32_t` value) for registering a host, a service, or a key service.
*
*/
typedef otPlatDnssdRequestId otMdnsRequestId;
/**
* Represents the callback function to report the outcome of a host, service, or key registration request.
*
* The outcome of a registration request is reported back by invoking this callback with one of the following `aError`
* inputs:
*
* - `OT_ERROR_NONE` indicates registration was successful.
* - `OT_ERROR_DUPLICATED` indicates a name conflict while probing, i.e., name is claimed by another mDNS responder.
*
* See `otMdnsRegisterHost()`, `otMdnsRegisterService()`, and `otMdnsRegisterKey()` for more details about when
* the callback will be invoked.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aRequestId The request ID.
* @param[in] aError Error indicating the outcome of request.
*
*/
typedef otPlatDnssdRegisterCallback otMdnsRegisterCallback;
/**
* Represents the callback function to report a detected name conflict after successful registration of an entry.
*
* If a conflict is detected while registering an entry, it is reported through the provided `otMdnsRegisterCallback`.
* The `otMdnsConflictCallback` is used only when a name conflict is detected after an entry has been successfully
* registered.
*
* A non-NULL @p aServiceType indicates that conflict is for a service entry. In this case @p aName specifies the
* service instance label (treated as as a single DNS label and can potentially include dot `.` character).
*
* A NULL @p aServiceType indicates that conflict is for a host entry. In this case @p Name specifies the host name. It
* does not include the domain name.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aName The host name or the service instance label.
* @param[in] aServiceType The service type (e.g., `_tst._udp`).
*
*/
typedef void (*otMdnsConflictCallback)(otInstance *aInstance, const char *aName, const char *aServiceType);
/**
* Represents an mDNS host.
*
* This type is used to register or unregister a host (`otMdnsRegisterHost()` and `otMdnsUnregisterHost()`).
*
* See the description of each function for more details on how different fields are used in each case.
*
*/
typedef otPlatDnssdHost otMdnsHost;
/**
* Represents an mDNS service.
*
* This type is used to register or unregister a service (`otMdnsRegisterService()` and `otMdnsUnregisterService()`).
*
* See the description of each function for more details on how different fields are used in each case.
*
*/
typedef otPlatDnssdService otMdnsService;
/**
* Represents an mDNS key record.
*
* See `otMdnsRegisterKey()`, `otMdnsUnregisterKey()` for more details about fields in each case.
*
*/
typedef otPlatDnssdKey otMdnsKey;
/**
* Enables or disables the mDNS module.
*
* The mDNS module should be enabled before registration any host, service, or key entries. Disabling mDNS will
* immediately stop all operations and any communication (multicast or unicast tx) and remove any previously registered
* entries without sending any "goodbye" announcements or invoking their callback. Once disabled, all currently active
* browsers and resolvers are stopped.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aEnable Boolean to indicate whether to enable (on `TRUE`) or disable (on `FALSE`).
* @param[in] aInfraIfIndex The network interface index for mDNS operation. Value is ignored when disabling
*
* @retval OT_ERROR_NONE Enabled or disabled the mDNS module successfully.
* @retval OT_ERROR_ALREADY mDNS is already enabled on an enable request or is already disabled on a disable request.
*
*/
otError otMdnsSetEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex);
/**
* Indicates whether the mDNS module is enabled.
*
* @param[in] aInstance The OpenThread instance.
*
* @retval TRUE The mDNS module is enabled
* @retval FALSE The mDNS module is disabled.
*
*/
bool otMdnsIsEnabled(otInstance *aInstance);
/**
* Sets whether the mDNS module is allowed to send questions requesting unicast responses referred to as "QU" questions.
*
* The "QU" questions request unicast responses, in contrast to "QM" questions which request multicast responses.
*
* When allowed, the first probe will be sent as a "QU" question. This API can be used to address platform limitation
* where platform socket cannot accept unicast response received on mDNS port (due to it being already bound).
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aAllow Indicates whether or not to allow "QU" questions.
*
*/
void otMdnsSetQuestionUnicastAllowed(otInstance *aInstance, bool aAllow);
/**
* Indicates whether mDNS module is allowed to send "QU" questions requesting unicast response.
*
* @retval TRUE The mDNS module is allowed to send "QU" questions.
* @retval FALSE The mDNS module is not allowed to send "QU" questions.
*
*/
bool otMdnsIsQuestionUnicastAllowed(otInstance *aInstance);
/**
* Sets the post-registration conflict callback.
*
* If a conflict is detected while registering an entry, it is reported through the provided `otMdnsRegisterCallback`.
* The `otMdnsConflictCallback` is used only when a name conflict is detected after an entry has been successfully
* registered.
*
* @p aCallback can be set to `NULL` if not needed. Subsequent calls will replace any previously set callback.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aCallback The conflict callback.
*
*/
void otMdnsSetConflictCallback(otInstance *aInstance, otMdnsConflictCallback aCallback);
/**
* Registers or updates a host on mDNS.
*
* The fields in @p aHost follow these rules:
*
* - The `mHostName` field specifies the host name to register (e.g., "myhost"). MUST NOT contain the domain name.
* - The `mAddresses` is array of IPv6 addresses to register with the host. `mAddressesLength` provides the number of
* entries in `mAddresses` array.
* - The `mAddresses` array can be empty with zero `mAddressesLength`. In this case, mDNS will treat it as if host is
* unregistered and stops advertising any addresses for this the host name.
* - The `mTtl` specifies the TTL if non-zero. If zero, the mDNS core will choose the default TTL of 120 seconds.
* - Other fields in @p aHost structure are ignored in an `otMdnsRegisterHost()` call.
*
* This function can be called again for the same `mHostName` to update a previously registered host entry, for example,
* to change the list of addresses of the host. In this case, the mDNS module will send "goodbye" announcements for any
* previously registered and now removed addresses and announce any newly added addresses.
*
* The outcome of the registration request is reported back by invoking the provided @p aCallback with @p aRequestId
* as its input and one of the following `aError` inputs:
*
* - `OT_ERROR_NONE` indicates registration was successful.
* - `OT_ERROR_DULICATED` indicates a name conflict while probing, i.e., name is claimed by another mDNS responder.
*
* For caller convenience, the OpenThread mDNS module guarantees that the callback will be invoked after this function
* returns, even in cases of immediate registration success. The @p aCallback can be `NULL` if caller does not want to
* be notified of the outcome.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aHost Information about the host to register.
* @param[in] aRequestId The ID associated with this request.
* @param[in] aCallback The callback function pointer to report the outcome (can be NULL if not needed).
*
* @retval OT_ERROR_NONE Successfully started registration. @p aCallback will report the outcome.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
*
*/
otError otMdnsRegisterHost(otInstance *aInstance,
const otMdnsHost *aHost,
otMdnsRequestId aRequestId,
otMdnsRegisterCallback aCallback);
/**
* Unregisters a host on mDNS.
*
* The fields in @p aHost follow these rules:
*
* - The `mHostName` field specifies the host name to unregister (e.g., "myhost"). MUST NOT contain the domain name.
* - Other fields in @p aHost structure are ignored in an `otMdnsUnregisterHost()` call.
*
* If there is no previously registered host with the same name, no action is performed.
*
* If there is a previously registered host with the same name, the mDNS module will send "goodbye" announcement for
* all previously advertised address records.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aHost Information about the host to unregister.
*
* @retval OT_ERROR_NONE Successfully unregistered host.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
*
*/
otError otMdnsUnregisterHost(otInstance *aInstance, const otMdnsHost *aHost);
/**
* Registers or updates a service on mDNS.
*
* The fields in @p aService follow these rules:
*
* - The `mServiceInstance` specifies the service instance label. It is treated as a single DNS name label. It may
* contain dot `.` character which is allowed in a service instance label.
* - The `mServiceType` specifies the service type (e.g., "_tst._udp"). It is treated as multiple dot `.` separated
* labels. It MUST NOT contain the domain name.
* - The `mHostName` field specifies the host name of the service. MUST NOT contain the domain name.
* - The `mSubTypeLabels` is an array of strings representing sub-types associated with the service. Each array entry
* is a sub-type label. The `mSubTypeLabels can be NULL if there is no sub-type. Otherwise, the array length is
* specified by `mSubTypeLabelsLength`.
* - The `mTxtData` and `mTxtDataLength` specify the encoded TXT data. The `mTxtData` can be NULL or `mTxtDataLength`
* can be zero to specify an empty TXT data. In this case mDNS module will use a single zero byte `[ 0 ]` as the
* TXT data.
* - The `mPort`, `mWeight`, and `mPriority` specify the service's parameters as specified in DNS SRV record.
* - The `mTtl` specifies the TTL if non-zero. If zero, the mDNS module will use the default TTL of 120 seconds.
* - Other fields in @p aService structure are ignored in an `otMdnsRegisterService()` call.
*
* This function can be called again for the same `mServiceInstance` and `mServiceType` to update a previously
* registered service entry, for example, to change the sub-types list, or update any parameter such as port, weight,
* priority, TTL, or host name. The mDNS module will send announcements for any changed info, e.g., will send "goodbye"
* announcements for any removed sub-types and announce any newly added sub-types.
*
* Regarding the invocation of the @p aCallback, this function behaves in the same way as described in
* `otMdnsRegisterHost()`.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aService Information about the service to register.
* @param[in] aRequestId The ID associated with this request.
* @param[in] aCallback The callback function pointer to report the outcome (can be NULL if not needed).
*
* @retval OT_ERROR_NONE Successfully started registration. @p aCallback will report the outcome.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
*
*/
otError otMdnsRegisterService(otInstance *aInstance,
const otMdnsService *aService,
otMdnsRequestId aRequestId,
otMdnsRegisterCallback aCallback);
/**
* Unregisters a service on mDNS module.
*
* The fields in @p aService follow these rules:
* - The `mServiceInstance` specifies the service instance label. It is treated as a single DNS name label. It may
* contain dot `.` character which is allowed in a service instance label.
* - The `mServiceType` specifies the service type (e.g., "_tst._udp"). It is treated as multiple dot `.` separated
* labels. It MUST NOT contain the domain name.
* - Other fields in @p aService structure are ignored in an `otMdnsUnregisterService()` call.
*
* If there is no previously registered service with the same name, no action is performed.
*
* If there is a previously registered service with the same name, the mDNS module will send "goodbye" announcements
* for all related records.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aService Information about the service to unregister.
*
* @retval OT_ERROR_NONE Successfully unregistered service.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
*
*/
otError otMdnsUnregisterService(otInstance *aInstance, const otMdnsService *aService);
/**
* Registers or updates a key record on mDNS module.
*
* The fields in @p aKey follow these rules:
*
* - If the key is associated with a host entry, `mName` specifies the host name and `mServcieType` MUST be NULL.
* - If the key is associated with a service entry, `mName` specifies the service instance label (always treated as
* a single label) and `mServiceType` specifies the service type (e.g., "_tst._udp"). In this case the DNS name for
* key record is `<mName>.<mServiceTye>`.
* - The `mKeyData` field contains the key record's data with `mKeyDataLength` as its length in byes.
* - The `mTtl` specifies the TTL if non-zero. If zero, the mDNS module will use the default TTL of 120 seconds.
* - Other fields in @p aKey structure are ignored in an `otMdnsRegisterKey()` call.
*
* This function can be called again for the same name to updated a previously registered key entry, for example, to
* change the key data or TTL.
*
* Regarding the invocation of the @p aCallback, this function behaves in the same way as described in
* `otMdnsRegisterHost()`.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aKey Information about the key record to register.
* @param[in] aRequestId The ID associated with this request.
* @param[in] aCallback The callback function pointer to report the outcome (can be NULL if not needed).
*
* @retval OT_ERROR_NONE Successfully started registration. @p aCallback will report the outcome.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
*
*/
otError otMdnsRegisterKey(otInstance *aInstance,
const otMdnsKey *aKey,
otMdnsRequestId aRequestId,
otMdnsRegisterCallback aCallback);
/**
* Unregisters a key record on mDNS.
*
* The fields in @p aKey follow these rules:
*
* - If the key is associated with a host entry, `mName` specifies the host name and `mServcieType` MUST be NULL.
* - If the key is associated with a service entry, `mName` specifies the service instance label (always treated as
* a single label) and `mServiceType` specifies the service type (e.g., "_tst._udp"). In this case the DNS name for
* key record is `<mName>.<mServiceTye>`.
* - Other fields in @p aKey structure are ignored in an `otMdnsUnregisterKey()` call.
*
* If there is no previously registered key with the same name, no action is performed.
*
* If there is a previously registered key with the same name, the mDNS module will send "goodbye" announcements for
* the key record.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aKey Information about the key to unregister.
*
* @retval OT_ERROR_NONE Successfully unregistered key
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
*
*/
otError otMdnsUnregisterKey(otInstance *aInstance, const otMdnsKey *aKey);
typedef struct otMdnsBrowseResult otMdnsBrowseResult;
typedef struct otMdnsSrvResult otMdnsSrvResult;
typedef struct otMdnsTxtResult otMdnsTxtResult;
typedef struct otMdnsAddressResult otMdnsAddressResult;
/**
* Represents the callback function used to report a browse result.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aResult The browse result.
*
*/
typedef void (*otMdnsBrowseCallback)(otInstance *aInstance, const otMdnsBrowseResult *aResult);
/**
* Represents the callback function used to report an SRV resolve result.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aResult The SRV resolve result.
*
*/
typedef void (*otMdnsSrvCallback)(otInstance *aInstance, const otMdnsSrvResult *aResult);
/**
* Represents the callback function used to report a TXT resolve result.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aResult The TXT resolve result.
*
*/
typedef void (*otMdnsTxtCallback)(otInstance *aInstance, const otMdnsTxtResult *aResult);
/**
* Represents the callback function use to report a IPv6/IPv4 address resolve result.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aResult The address resolve result.
*
*/
typedef void (*otMdnsAddressCallback)(otInstance *aInstance, const otMdnsAddressResult *aResult);
/**
* Represents a service browser.
*
*/
typedef struct otMdnsBrowser
{
const char *mServiceType; ///< The service type (e.g., "_mt._udp"). MUST NOT include domain name.
const char *mSubTypeLabel; ///< The sub-type label if browsing for sub-type, NULL otherwise.
uint32_t mInfraIfIndex; ///< The infrastructure network interface index.
otMdnsBrowseCallback mCallback; ///< The callback to report result.
} otMdnsBrowser;
/**
* Represents a browse result.
*
*/
struct otMdnsBrowseResult
{
const char *mServiceType; ///< The service type (e.g., "_mt._udp").
const char *mSubTypeLabel; ///< The sub-type label if browsing for sub-type, NULL otherwise.
const char *mServiceInstance; ///< Service instance label.
uint32_t mTtl; ///< TTL in seconds. Zero TTL indicates that service is removed.
uint32_t mInfraIfIndex; ///< The infrastructure network interface index.
};
/**
* Represents an SRV service resolver.
*
*/
typedef struct otMdnsSrvResolver
{
const char *mServiceInstance; ///< The service instance label.
const char *mServiceType; ///< The service type.
uint32_t mInfraIfIndex; ///< The infrastructure network interface index.
otMdnsSrvCallback mCallback; ///< The callback to report result.
} otMdnsSrvResolver;
/**
* Represents an SRV resolver result.
*
*/
struct otMdnsSrvResult
{
const char *mServiceInstance; ///< The service instance name label.
const char *mServiceType; ///< The service type.
const char *mHostName; ///< The host name (e.g., "myhost"). Can be NULL when `mTtl` is zero.
uint16_t mPort; ///< The service port number.
uint16_t mPriority; ///< The service priority.
uint16_t mWeight; ///< The service weight.
uint32_t mTtl; ///< The service TTL in seconds. Zero TTL indicates SRV record is removed.
uint32_t mInfraIfIndex; ///< The infrastructure network interface index.
};
/**
* Represents a TXT service resolver.
*
*/
typedef struct otMdnsTxtResolver
{
const char *mServiceInstance; ///< Service instance label.
const char *mServiceType; ///< Service type.
uint32_t mInfraIfIndex; ///< The infrastructure network interface index.
otMdnsTxtCallback mCallback;
} otMdnsTxtResolver;
/**
* Represents a TXT resolver result.
*
*/
struct otMdnsTxtResult
{
const char *mServiceInstance; ///< The service instance name label.
const char *mServiceType; ///< The service type.
const uint8_t *mTxtData; ///< Encoded TXT data bytes. Can be NULL when `mTtl` is zero.
uint16_t mTxtDataLength; ///< Length of TXT data.
uint32_t mTtl; ///< The TXT data TTL in seconds. Zero TTL indicates record is removed.
uint32_t mInfraIfIndex; ///< The infrastructure network interface index.
};
/**
* Represents an address resolver.
*
*/
typedef struct otMdnsAddressResolver
{
const char *mHostName; ///< The host name (e.g., "myhost"). MUST NOT contain domain name.
uint32_t mInfraIfIndex; ///< The infrastructure network interface index.
otMdnsAddressCallback mCallback; ///< The callback to report result.
} otMdnsAddressResolver;
/**
* Represents a discovered host address and its TTL.
*
*/
typedef struct otMdnsAddressAndTtl
{
otIp6Address mAddress; ///< The IPv6 address. For IPv4 address the IPv4-mapped IPv6 address format is used.
uint32_t mTtl; ///< The TTL in seconds.
} otMdnsAddressAndTtl;
/**
* Represents address resolver result.
*
*/
struct otMdnsAddressResult
{
const char *mHostName; ///< The host name.
const otMdnsAddressAndTtl *mAddresses; ///< Array of host addresses and their TTL. Can be NULL if empty.
uint16_t mAddressesLength; ///< Number of entries in `mAddresses` array.
uint32_t mInfraIfIndex; ///< The infrastructure network interface index.
};
/**
* Starts a service browser.
*
* Initiates a continuous search for the specified `mServiceType` in @p aBrowser. For sub-type services, use
* `mSubTypeLabel` to define the sub-type, for base services, set `mSubTypeLabel` to NULL.
*
* Discovered services are reported through the `mCallback` function in @p aBrowser. Services that have been removed
* are reported with a TTL value of zero. The callback may be invoked immediately with cached information (if available)
* and potentially before this function returns. When cached results are used, the reported TTL value will reflect
* the original TTL from the last received response.
*
* Multiple browsers can be started for the same service, provided they use different callback functions.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aBrowser The browser to be started.
*
* @retval OT_ERROR_NONE Browser started successfully.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
* @retval OT_ERROR_ALREADY An identical browser (same service and callback) is already active.
*
*/
otError otMdnsStartBrowser(otInstance *aInstance, const otMdnsBrowser *aBrowser);
/**
* Stops a service browser.
*
* No action is performed if no matching browser with the same service and callback is currently active.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aBrowser The browser to stop.
*
* @retval OT_ERROR_NONE Browser stopped successfully.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
*
*/
otError otMdnsStopBrowser(otInstance *aInstance, const otMdnsBrowser *aBroswer);
/**
* Starts an SRV record resolver.
*
* Initiates a continuous SRV record resolver for the specified service in @p aResolver.
*
* Discovered information is reported through the `mCallback` function in @p aResolver. When the service is removed
* it is reported with a TTL value of zero. In this case, `mHostName` may be NULL and other result fields (such as
* `mPort`) should be ignored.
*
* The callback may be invoked immediately with cached information (if available) and potentially before this function
* returns. When cached result is used, the reported TTL value will reflect the original TTL from the last received
* response.
*
* Multiple resolvers can be started for the same service, provided they use different callback functions.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aResolver The resolver to be started.
*
* @retval OT_ERROR_NONE Resolver started successfully.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
* @retval OT_ERROR_ALREADY An identical resolver (same service and callback) is already active.
*
*/
otError otMdnsStartSrvResolver(otInstance *aInstance, const otMdnsSrvResolver *aResolver);
/**
* Stops an SRV record resolver.
*
* No action is performed if no matching resolver with the same service and callback is currently active.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aResolver The resolver to stop.
*
* @retval OT_ERROR_NONE Resolver stopped successfully.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
*
*/
otError otMdnsStopSrvResolver(otInstance *aInstance, const otMdnsSrvResolver *aResolver);
/**
* Starts a TXT record resolver.
*
* Initiates a continuous TXT record resolver for the specified service in @p aResolver.
*
* Discovered information is reported through the `mCallback` function in @p aResolver. When the TXT record is removed
* it is reported with a TTL value of zero. In this case, `mTxtData` may be NULL, and other result fields (such as
* `mTxtDataLength`) should be ignored.
*
* The callback may be invoked immediately with cached information (if available) and potentially before this function
* returns. When cached result is used, the reported TTL value will reflect the original TTL from the last received
* response.
*
* Multiple resolvers can be started for the same service, provided they use different callback functions.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aResolver The resolver to be started.
*
* @retval OT_ERROR_NONE Resolver started successfully.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
* @retval OT_ERROR_ALREADY An identical resolver (same service and callback) is already active.
*
*/
otError otMdnsStartTxtResolver(otInstance *aInstance, const otMdnsTxtResolver *aResolver);
/**
* Stops a TXT record resolver.
*
* No action is performed if no matching resolver with the same service and callback is currently active.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aResolver The resolver to stop.
*
* @retval OT_ERROR_NONE Resolver stopped successfully.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
*
*/
otError otMdnsStopTxtResolver(otInstance *aInstance, const otMdnsTxtResolver *aResolver);
/**
* Starts an IPv6 address resolver.
*
* Initiates a continuous IPv6 address resolver for the specified host name in @p aResolver.
*
* Discovered addresses are reported through the `mCallback` function in @ p aResolver. The callback is invoked
* whenever addresses are added or removed, providing an updated list. If all addresses are removed, the callback is
* invoked with an empty list (`mAddresses` will be NULL, and `mAddressesLength` will be zero).
*
* The callback may be invoked immediately with cached information (if available) and potentially before this function
* returns. When cached result is used, the reported TTL values will reflect the original TTL from the last received
* response.
*
* Multiple resolvers can be started for the same host name, provided they use different callback functions.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aResolver The resolver to be started.
*
* @retval OT_ERROR_NONE Resolver started successfully.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
* @retval OT_ERROR_ALREADY An identical resolver (same host and callback) is already active.
*
*/
otError otMdnsStartIp6AddressResolver(otInstance *aInstance, const otMdnsAddressResolver *aResolver);
/**
* Stops an IPv6 address resolver.
*
* No action is performed if no matching resolver with the same host name and callback is currently active.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aResolver The resolver to stop.
*
* @retval OT_ERROR_NONE Resolver stopped successfully.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
*
*/
otError otMdnsStopIp6AddressResolver(otInstance *aInstance, const otMdnsAddressResolver *aResolver);
/**
* Starts an IPv4 address resolver.
*
* Initiates a continuous IPv4 address resolver for the specified host name in @p aResolver.
*
* Discovered addresses are reported through the `mCallback` function in @ p aResolver. The IPv4 addresses are
* represented using the IPv4-mapped IPv6 address format in `mAddresses` array. The callback is invoked whenever
* addresses are added or removed, providing an updated list. If all addresses are removed, the callback is invoked
* with an empty list (`mAddresses` will be NULL, and `mAddressesLength` will be zero).
*
* The callback may be invoked immediately with cached information (if available) and potentially before this function
* returns. When cached result is used, the reported TTL values will reflect the original TTL from the last received
* response.
*
* Multiple resolvers can be started for the same host name, provided they use different callback functions.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aResolver The resolver to be started.
*
* @retval OT_ERROR_NONE Resolver started successfully.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
* @retval OT_ERROR_ALREADY An identical resolver (same host and callback) is already active.
*
*/
otError otMdnsStartIp4AddressResolver(otInstance *aInstance, const otMdnsAddressResolver *aResolver);
/**
* Stops an IPv4 address resolver.
*
* No action is performed if no matching resolver with the same host name and callback is currently active.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aResolver The resolver to stop.
*
* @retval OT_ERROR_NONE Resolver stopped successfully.
* @retval OT_ERROR_INVALID_STATE mDNS module is not enabled.
*
*/
otError otMdnsStopIp4AddressResolver(otInstance *aInstance, const otMdnsAddressResolver *aResolver);
/**
* @}
*
*/
#ifdef __cplusplus
} // extern "C"
#endif
#endif // OPENTHREAD_MULTICAST_DNS_H_
+173
View File
@@ -0,0 +1,173 @@
/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* @brief
* This file includes the platform abstraction for mDNS socket.
*
*/
#ifndef OPENTHREAD_PLATFORM_MULTICAST_DNS_SOCKET_H_
#define OPENTHREAD_PLATFORM_MULTICAST_DNS_SOCKET_H_
#include <stdint.h>
#include <openthread/instance.h>
#include <openthread/ip6.h>
#include <openthread/message.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup plat-mdns
*
* @brief
* This module defines platform APIs for Multicast DNS (mDNS) socket.
*
* @{
*
*/
/**
* Represents a socket address info.
*
*/
typedef struct otPlatMdnsAddressInfo
{
otIp6Address mAddress; ///< IP address. IPv4-mapped IPv6 format should be used to represent IPv4 address.
uint16_t mPort; ///< Port number.
uint32_t mInfraIfIndex; ///< Interface index.
} otPlatMdnsAddressInfo;
/**
* Enables or disables listening for mDNS messages sent to mDNS port 5353.
*
* When listening is enabled, the platform MUST listen for multicast messages sent to UDP destination port 5353 at the
* mDNS link-local multicast address `224.0.0.251` and its IPv6 equivalent `ff02::fb`.
*
* The platform SHOULD also listen for any unicast messages sent to UDP destination port 5353. If this is not possible,
* then OpenThread mDNS module can be configured to not use any "QU" questions requesting unicast response.
*
* While enabled, all received messages MUST be reported back using `otPlatMdnsHandleReceive()` callback.
*
* @param[in] aInstance The OpernThread instance.
* @param[in] aEnable Indicate whether to enable or disable.
* @param[in] aInfraInfIndex The infrastructure network interface index.
*
* @retval OT_ERROR_NONE Successfully enabled/disabled listening for mDNS messages.
* @retval OT_ERROR_FAILED Failed to enable/disable listening for mDNS messages.
*
*/
otError otPlatMdnsSetListeningEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex);
/**
* Sends an mDNS message as multicast.
*
* The platform MUST multicast the prepared mDNS message in @p aMessage as a UDP message using the mDNS well-known port
* number 5353 for both source and destination ports. The message MUST be sent to the mDNS link-local multicast
* address `224.0.0.251` and/or its IPv6 equivalent `ff02::fb`.
*
* @p aMessage contains the mDNS message starting with DNS header at offset zero. It does not include IP or UDP headers.
* This function passes the ownership of @p aMessage to the platform layer and platform implementation MUST free
* @p aMessage once sent and no longer needed.
*
* The platform MUST allow multicast loopback, i.e., the multicast message @p aMessage MUST also be received and
* passed back to OpenThread stack using `otPlatMdnsHandleReceive()` callback. This behavior is essential for the
* OpenThread mDNS stack to process and potentially respond to its own queries, while allowing other mDNS receivers
* to also receive the query and its response.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aMessage The mDNS message to multicast. Ownership is transferred to the platform layer.
* @param[in] aInfraIfIndex The infrastructure network interface index.
*
*/
void otPlatMdnsSendMulticast(otInstance *aInstance, otMessage *aMessage, uint32_t aInfraIfIndex);
/**
* Sends an mDNS message as unicast.
*
* The platform MUST send the prepared mDNS message in @p aMessage as a UDP message using source UDP port 5353 to
* the destination address and port number specified by @p aAddress.
*
* @p aMessage contains the DNS message starting with the DNS header at offset zero. It does not include IP or UDP
* headers. This function passes the ownership of @p aMessage to the platform layer and platform implementation
* MUST free @p aMessage once sent and no longer needed.
*
* The @p aAddress fields are as follows:
*
* - `mAddress` specifies the destination address. IPv4-mapped IPv6 format is used to represent an IPv4 destination.
* - `mPort` specifies the destination port.
* - `mInfraIndex` specifies the interface index.
*
* If the @aAddress matches this devices address, the platform MUST ensure to receive and pass the message back to
* the OpenThread stack using `otPlatMdnsHandleReceive()` for processing.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aMessage The mDNS message to multicast. Ownership is transferred to platform layer.
* @param[in] aAddress The destination address info.
*
*/
void otPlatMdnsSendUnicast(otInstance *aInstance, otMessage *aMessage, const otPlatMdnsAddressInfo *aAddress);
/**
* Callback to notify OpenThread mDNS module of a received message on UDP port 5353.
*
* @p aMessage MUST contain DNS message starting with the DNS header at offset zero. This function passes the
* ownership of @p aMessage from the platform layer to the OpenThread stack. The OpenThread stack will free the
* message once processed.
*
* The @p aAddress fields are as follows:
*
* - `mAddress` specifies the sender's address. IPv4-mapped IPv6 format is used to represent an IPv4 destination.
* - `mPort` specifies the sender's port.
* - `mInfraIndex` specifies the interface index.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aMessage The received mDNS message. Ownership is transferred to the OpenThread stack.
* @param[in] aIsUnicast Indicates whether the received message is unicast or multicast.
* @param[in] aAddress The sender's address info.
*
*/
extern void otPlatMdnsHandleReceive(otInstance *aInstance,
otMessage *aMessage,
bool aIsUnicast,
const otPlatMdnsAddressInfo *aAddress);
/**
* @}
*
*/
#ifdef __cplusplus
} // extern "C"
#endif
#endif // OPENTHREAD_PLATFORM_MULTICAST_DNS_SOCKET_H_
+2
View File
@@ -53,6 +53,8 @@ openthread_cli_sources = [
"cli_link_metrics.hpp",
"cli_mac_filter.cpp",
"cli_mac_filter.hpp",
"cli_mdns.cpp",
"cli_mdns.hpp",
"cli_network_data.cpp",
"cli_network_data.hpp",
"cli_ping.cpp",
+1
View File
@@ -44,6 +44,7 @@ set(COMMON_SOURCES
cli_joiner.cpp
cli_link_metrics.cpp
cli_mac_filter.cpp
cli_mdns.cpp
cli_network_data.cpp
cli_ping.cpp
cli_srp_client.cpp
+10
View File
@@ -119,6 +119,9 @@ Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, voi
#if OPENTHREAD_CLI_DNS_ENABLE
, mDns(aInstance, *this)
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
, mMdns(aInstance, *this)
#endif
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
, mBbr(aInstance, *this)
#endif
@@ -2763,6 +2766,10 @@ exit:
template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[]) { return mDns.Process(aArgs); }
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
template <> otError Interpreter::Process<Cmd("mdns")>(Arg aArgs[]) { return mMdns.Process(aArgs); }
#endif
#if OPENTHREAD_FTD
void Interpreter::OutputEidCacheEntry(const otCacheEntryInfo &aEntry)
{
@@ -8579,6 +8586,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[])
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
CmdEntry("macfilter"),
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
CmdEntry("mdns"),
#endif
#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
CmdEntry("meshdiag"),
#endif
+7
View File
@@ -61,12 +61,14 @@
#include "cli/cli_bbr.hpp"
#include "cli/cli_br.hpp"
#include "cli/cli_commissioner.hpp"
#include "cli/cli_config.h"
#include "cli/cli_dataset.hpp"
#include "cli/cli_dns.hpp"
#include "cli/cli_history.hpp"
#include "cli/cli_joiner.hpp"
#include "cli/cli_link_metrics.hpp"
#include "cli/cli_mac_filter.hpp"
#include "cli/cli_mdns.hpp"
#include "cli/cli_network_data.hpp"
#include "cli/cli_ping.hpp"
#include "cli/cli_srp_client.hpp"
@@ -117,6 +119,7 @@ class Interpreter : public OutputImplementer, public Utils
friend class Dns;
friend class Joiner;
friend class LinkMetrics;
friend class Mdns;
friend class NetworkData;
friend class PingSender;
friend class SrpClient;
@@ -446,6 +449,10 @@ private:
Dns mDns;
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
Mdns mMdns;
#endif
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
Bbr mBbr;
#endif
+776
View File
@@ -0,0 +1,776 @@
/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements CLI for mDNS.
*/
#include <string.h>
#include "cli_mdns.hpp"
#include <openthread/nat64.h>
#include "cli/cli.hpp"
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
namespace ot {
namespace Cli {
template <> otError Mdns::Process<Cmd("enable")>(Arg aArgs[])
{
otError error;
uint32_t infraIfIndex;
SuccessOrExit(error = aArgs[0].ParseAsUint32(infraIfIndex));
VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otMdnsSetEnabled(GetInstancePtr(), true, infraIfIndex));
mInfraIfIndex = infraIfIndex;
exit:
return error;
}
template <> otError Mdns::Process<Cmd("disable")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
error = otMdnsSetEnabled(GetInstancePtr(), false, /* aInfraIfIndex */ 0);
exit:
return error;
}
template <> otError Mdns::Process<Cmd("state")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
OutputEnabledDisabledStatus(otMdnsIsEnabled(GetInstancePtr()));
exit:
return error;
}
template <> otError Mdns::Process<Cmd("unicastquestion")>(Arg aArgs[])
{
return ProcessEnableDisable(aArgs, otMdnsIsQuestionUnicastAllowed, otMdnsSetQuestionUnicastAllowed);
}
void Mdns::OutputHost(const otMdnsHost &aHost)
{
OutputLine("Host %s", aHost.mHostName);
OutputLine(kIndentSize, "%u address:", aHost.mAddressesLength);
for (uint16_t index = 0; index < aHost.mAddressesLength; index++)
{
OutputFormat(kIndentSize, " ");
OutputIp6AddressLine(aHost.mAddresses[index]);
}
OutputLine(kIndentSize, "ttl: %lu", ToUlong(aHost.mTtl));
}
void Mdns::OutputService(const otMdnsService &aService)
{
OutputLine("Service %s for %s", aService.mServiceInstance, aService.mServiceType);
OutputLine(kIndentSize, "host: %s", aService.mHostName);
if (aService.mSubTypeLabelsLength > 0)
{
OutputLine(kIndentSize, "%u sub-type:", aService.mSubTypeLabelsLength);
for (uint16_t index = 0; index < aService.mSubTypeLabelsLength; index++)
{
OutputLine(kIndentSize * 2, "%s", aService.mSubTypeLabels[index]);
}
}
OutputLine(kIndentSize, "port: %u", aService.mPort);
OutputLine(kIndentSize, "priority: %u", aService.mPriority);
OutputLine(kIndentSize, "weight: %u", aService.mWeight);
OutputLine(kIndentSize, "ttl: %lu", ToUlong(aService.mTtl));
if ((aService.mTxtData == nullptr) || (aService.mTxtDataLength == 0))
{
OutputLine(kIndentSize, "txt-data: (empty)");
}
else
{
OutputFormat(kIndentSize, "txt-data: ");
OutputBytesLine(aService.mTxtData, aService.mTxtDataLength);
}
}
void Mdns::OutputKey(const otMdnsKey &aKey)
{
if (aKey.mServiceType != nullptr)
{
OutputLine("Key %s for %s (service)", aKey.mName, aKey.mServiceType);
}
else
{
OutputLine("Key %s (host)", aKey.mName);
}
OutputFormat(kIndentSize, "key-data: ");
OutputBytesLine(aKey.mKeyData, aKey.mKeyDataLength);
OutputLine(kIndentSize, "ttl: %lu", ToUlong(aKey.mTtl));
}
template <> otError Mdns::Process<Cmd("register")>(Arg aArgs[])
{
// mdns [async] [host|service|key] <entry specific args>
otError error = OT_ERROR_NONE;
bool isAsync = false;
if (aArgs[0] == "async")
{
isAsync = true;
aArgs++;
}
if (aArgs[0] == "host")
{
SuccessOrExit(error = ProcessRegisterHost(aArgs + 1));
}
else if (aArgs[0] == "service")
{
SuccessOrExit(error = ProcessRegisterService(aArgs + 1));
}
else if (aArgs[0] == "key")
{
SuccessOrExit(error = ProcessRegisterKey(aArgs + 1));
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
if (isAsync)
{
OutputLine("mDNS request id: %lu", ToUlong(mRequestId));
}
else
{
error = OT_ERROR_PENDING;
mWaitingForCallback = true;
}
exit:
return error;
}
otError Mdns::ProcessRegisterHost(Arg aArgs[])
{
// register host <name> [<zero or more addresses>] [<ttl>]
otError error = OT_ERROR_NONE;
otMdnsHost host;
otIp6Address addresses[kMaxAddresses];
memset(&host, 0, sizeof(host));
VerifyOrExit(!aArgs->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
host.mHostName = aArgs->GetCString();
aArgs++;
host.mAddresses = addresses;
for (; !aArgs->IsEmpty(); aArgs++)
{
otIp6Address address;
uint32_t ttl;
if (aArgs->ParseAsIp6Address(address) == OT_ERROR_NONE)
{
VerifyOrExit(host.mAddressesLength < kMaxAddresses, error = OT_ERROR_NO_BUFS);
addresses[host.mAddressesLength] = address;
host.mAddressesLength++;
}
else if (aArgs->ParseAsUint32(ttl) == OT_ERROR_NONE)
{
host.mTtl = ttl;
VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
OutputHost(host);
mRequestId++;
error = otMdnsRegisterHost(GetInstancePtr(), &host, mRequestId, HandleRegisterationDone);
exit:
return error;
}
otError Mdns::ProcessRegisterService(Arg aArgs[])
{
otError error;
otMdnsService service;
Buffers buffers;
SuccessOrExit(error = ParseServiceArgs(aArgs, service, buffers));
OutputService(service);
mRequestId++;
error = otMdnsRegisterService(GetInstancePtr(), &service, mRequestId, HandleRegisterationDone);
exit:
return error;
}
otError Mdns::ParseServiceArgs(Arg aArgs[], otMdnsService &aService, Buffers &aBuffers)
{
// mdns register service <instance-label> <service-type,sub_types> <host-name> <port> [<prio>] [<weight>] [<ttl>]
// [<txtdata>]
otError error = OT_ERROR_INVALID_ARGS;
char *label;
uint16_t len;
memset(&aService, 0, sizeof(aService));
VerifyOrExit(!aArgs->IsEmpty());
aService.mServiceInstance = aArgs->GetCString();
aArgs++;
// Copy service type into `aBuffer.mString`, then search for
// `,` in the string to parse the list of sub-types (if any).
VerifyOrExit(!aArgs->IsEmpty());
len = aArgs->GetLength();
VerifyOrExit(len + 1 < kStringSize, error = OT_ERROR_NO_BUFS);
memcpy(aBuffers.mString, aArgs->GetCString(), len + 1);
aService.mServiceType = aBuffers.mString;
aService.mSubTypeLabels = aBuffers.mSubTypeLabels;
label = strchr(aBuffers.mString, ',');
if (label != nullptr)
{
while (true)
{
*label++ = '\0';
VerifyOrExit(aService.mSubTypeLabelsLength < kMaxSubTypes, error = OT_ERROR_NO_BUFS);
aBuffers.mSubTypeLabels[aService.mSubTypeLabelsLength] = label;
aService.mSubTypeLabelsLength++;
label = strchr(label, ',');
if (label == nullptr)
{
break;
}
}
}
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
aService.mHostName = aArgs->GetCString();
aArgs++;
SuccessOrExit(aArgs->ParseAsUint16(aService.mPort));
// The rest of `Args` are optional.
error = OT_ERROR_NONE;
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
SuccessOrExit(error = aArgs->ParseAsUint16(aService.mPriority));
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
SuccessOrExit(error = aArgs->ParseAsUint16(aService.mWeight));
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
SuccessOrExit(error = aArgs->ParseAsUint32(aService.mTtl));
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
len = kMaxTxtDataSize;
SuccessOrExit(error = aArgs->ParseAsHexString(len, aBuffers.mTxtData));
aService.mTxtData = aBuffers.mTxtData;
aService.mTxtDataLength = len;
aArgs++;
VerifyOrExit(aArgs->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
exit:
return error;
}
otError Mdns::ProcessRegisterKey(Arg aArgs[])
{
otError error = OT_ERROR_INVALID_ARGS;
otMdnsKey key;
uint16_t len;
uint8_t data[kMaxKeyDataSize];
memset(&key, 0, sizeof(key));
VerifyOrExit(!aArgs->IsEmpty());
key.mName = aArgs->GetCString();
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
if (aArgs->GetCString()[0] == '_')
{
key.mServiceType = aArgs->GetCString();
aArgs++;
VerifyOrExit(!aArgs->IsEmpty());
}
len = kMaxKeyDataSize;
SuccessOrExit(error = aArgs->ParseAsHexString(len, data));
key.mKeyData = data;
key.mKeyDataLength = len;
// ttl is optional
aArgs++;
if (!aArgs->IsEmpty())
{
SuccessOrExit(error = aArgs->ParseAsUint32(key.mTtl));
aArgs++;
VerifyOrExit(aArgs->IsEmpty(), error = kErrorInvalidArgs);
}
OutputKey(key);
mRequestId++;
error = otMdnsRegisterKey(GetInstancePtr(), &key, mRequestId, HandleRegisterationDone);
exit:
return error;
}
void Mdns::HandleRegisterationDone(otInstance *aInstance, otMdnsRequestId aRequestId, otError aError)
{
OT_UNUSED_VARIABLE(aInstance);
Interpreter::GetInterpreter().mMdns.HandleRegisterationDone(aRequestId, aError);
}
void Mdns::HandleRegisterationDone(otMdnsRequestId aRequestId, otError aError)
{
if (mWaitingForCallback && (aRequestId == mRequestId))
{
mWaitingForCallback = false;
Interpreter::GetInterpreter().OutputResult(aError);
}
else
{
OutputLine("mDNS registration for request id %lu outcome: %s", ToUlong(aRequestId),
otThreadErrorToString(aError));
}
}
template <> otError Mdns::Process<Cmd("unregister")>(Arg aArgs[])
{
otError error = OT_ERROR_INVALID_ARGS;
if (aArgs[0] == "host")
{
otMdnsHost host;
memset(&host, 0, sizeof(host));
VerifyOrExit(!aArgs[1].IsEmpty());
host.mHostName = aArgs[1].GetCString();
VerifyOrExit(aArgs[2].IsEmpty());
error = otMdnsUnregisterHost(GetInstancePtr(), &host);
}
else if (aArgs[0] == "service")
{
otMdnsService service;
memset(&service, 0, sizeof(service));
VerifyOrExit(!aArgs[1].IsEmpty());
service.mServiceInstance = aArgs[1].GetCString();
VerifyOrExit(!aArgs[2].IsEmpty());
service.mServiceType = aArgs[2].GetCString();
VerifyOrExit(aArgs[3].IsEmpty());
error = otMdnsUnregisterService(GetInstancePtr(), &service);
}
else if (aArgs[0] == "key")
{
otMdnsKey key;
memset(&key, 0, sizeof(key));
VerifyOrExit(!aArgs[1].IsEmpty());
key.mName = aArgs[1].GetCString();
if (!aArgs[2].IsEmpty())
{
key.mServiceType = aArgs[2].GetCString();
VerifyOrExit(aArgs[3].IsEmpty());
}
error = otMdnsUnregisterKey(GetInstancePtr(), &key);
}
exit:
return error;
}
otError Mdns::ParseStartOrStop(const Arg &aArg, bool &aIsStart)
{
otError error = OT_ERROR_NONE;
if (aArg == "start")
{
aIsStart = true;
}
else if (aArg == "stop")
{
aIsStart = false;
}
else
{
error = OT_ERROR_INVALID_ARGS;
}
return error;
}
template <> otError Mdns::Process<Cmd("browser")>(Arg aArgs[])
{
// mdns browser start|stop <service-type> [<sub-type>]
otError error;
otMdnsBrowser browser;
bool isStart;
ClearAllBytes(browser);
SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
browser.mServiceType = aArgs[1].GetCString();
if (!aArgs[2].IsEmpty())
{
browser.mSubTypeLabel = aArgs[2].GetCString();
VerifyOrExit(aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
}
browser.mInfraIfIndex = mInfraIfIndex;
browser.mCallback = HandleBrowseResult;
if (isStart)
{
error = otMdnsStartBrowser(GetInstancePtr(), &browser);
}
else
{
error = otMdnsStopBrowser(GetInstancePtr(), &browser);
}
exit:
return error;
}
void Mdns::HandleBrowseResult(otInstance *aInstance, const otMdnsBrowseResult *aResult)
{
OT_UNUSED_VARIABLE(aInstance);
Interpreter::GetInterpreter().mMdns.HandleBrowseResult(*aResult);
}
void Mdns::HandleBrowseResult(const otMdnsBrowseResult &aResult)
{
OutputFormat("mDNS browse result for %s", aResult.mServiceType);
if (aResult.mSubTypeLabel)
{
OutputLine(" sub-type %s", aResult.mSubTypeLabel);
}
else
{
OutputNewLine();
}
OutputLine(kIndentSize, "instance: %s", aResult.mServiceInstance);
OutputLine(kIndentSize, "ttl: %lu", ToUlong(aResult.mTtl));
OutputLine(kIndentSize, "if-index: %lu", ToUlong(aResult.mInfraIfIndex));
}
template <> otError Mdns::Process<Cmd("srvresolver")>(Arg aArgs[])
{
// mdns srvresolver start|stop <service-instance> <service-type>
otError error;
otMdnsSrvResolver resolver;
bool isStart;
ClearAllBytes(resolver);
SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
resolver.mServiceInstance = aArgs[1].GetCString();
resolver.mServiceType = aArgs[2].GetCString();
resolver.mInfraIfIndex = mInfraIfIndex;
resolver.mCallback = HandleSrvResult;
if (isStart)
{
error = otMdnsStartSrvResolver(GetInstancePtr(), &resolver);
}
else
{
error = otMdnsStopSrvResolver(GetInstancePtr(), &resolver);
}
exit:
return error;
}
void Mdns::HandleSrvResult(otInstance *aInstance, const otMdnsSrvResult *aResult)
{
OT_UNUSED_VARIABLE(aInstance);
Interpreter::GetInterpreter().mMdns.HandleSrvResult(*aResult);
}
void Mdns::HandleSrvResult(const otMdnsSrvResult &aResult)
{
OutputLine("mDNS SRV result for %s for %s", aResult.mServiceInstance, aResult.mServiceType);
if (aResult.mTtl != 0)
{
OutputLine(kIndentSize, "host: %s", aResult.mHostName);
OutputLine(kIndentSize, "port: %u", aResult.mPort);
OutputLine(kIndentSize, "priority: %u", aResult.mPriority);
OutputLine(kIndentSize, "weight: %u", aResult.mWeight);
}
OutputLine(kIndentSize, "ttl: %lu", ToUlong(aResult.mTtl));
OutputLine(kIndentSize, "if-index: %lu", ToUlong(aResult.mInfraIfIndex));
}
template <> otError Mdns::Process<Cmd("txtresolver")>(Arg aArgs[])
{
// mdns txtresolver start|stop <service-instance> <service-type>
otError error;
otMdnsTxtResolver resolver;
bool isStart;
ClearAllBytes(resolver);
SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
resolver.mServiceInstance = aArgs[1].GetCString();
resolver.mServiceType = aArgs[2].GetCString();
resolver.mInfraIfIndex = mInfraIfIndex;
resolver.mCallback = HandleTxtResult;
if (isStart)
{
error = otMdnsStartTxtResolver(GetInstancePtr(), &resolver);
}
else
{
error = otMdnsStopTxtResolver(GetInstancePtr(), &resolver);
}
exit:
return error;
}
void Mdns::HandleTxtResult(otInstance *aInstance, const otMdnsTxtResult *aResult)
{
OT_UNUSED_VARIABLE(aInstance);
Interpreter::GetInterpreter().mMdns.HandleTxtResult(*aResult);
}
void Mdns::HandleTxtResult(const otMdnsTxtResult &aResult)
{
OutputLine("mDNS TXT result for %s for %s", aResult.mServiceInstance, aResult.mServiceType);
if (aResult.mTtl != 0)
{
OutputFormat(kIndentSize, "txt-data: ");
OutputBytesLine(aResult.mTxtData, aResult.mTxtDataLength);
}
OutputLine(kIndentSize, "ttl: %lu", ToUlong(aResult.mTtl));
OutputLine(kIndentSize, "if-index: %lu", ToUlong(aResult.mInfraIfIndex));
}
template <> otError Mdns::Process<Cmd("ip6resolver")>(Arg aArgs[])
{
// mdns ip6resolver start|stop <host-name>
otError error;
otMdnsAddressResolver resolver;
bool isStart;
ClearAllBytes(resolver);
SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
resolver.mHostName = aArgs[1].GetCString();
resolver.mInfraIfIndex = mInfraIfIndex;
resolver.mCallback = HandleIp6AddressResult;
if (isStart)
{
error = otMdnsStartIp6AddressResolver(GetInstancePtr(), &resolver);
}
else
{
error = otMdnsStopIp6AddressResolver(GetInstancePtr(), &resolver);
}
exit:
return error;
}
void Mdns::HandleIp6AddressResult(otInstance *aInstance, const otMdnsAddressResult *aResult)
{
OT_UNUSED_VARIABLE(aInstance);
Interpreter::GetInterpreter().mMdns.HandleAddressResult(*aResult, kIp6Address);
}
void Mdns::HandleAddressResult(const otMdnsAddressResult &aResult, IpAddressType aType)
{
OutputLine("mDNS %s address result for %s", aType == kIp6Address ? "IPv6" : "IPv4", aResult.mHostName);
OutputLine(kIndentSize, "%u address:", aResult.mAddressesLength);
for (uint16_t index = 0; index < aResult.mAddressesLength; index++)
{
OutputFormat(kIndentSize, " ");
OutputIp6Address(aResult.mAddresses[index].mAddress);
OutputLine(" ttl:%lu", ToUlong(aResult.mAddresses[index].mTtl));
}
OutputLine(kIndentSize, "if-index: %lu", ToUlong(aResult.mInfraIfIndex));
}
template <> otError Mdns::Process<Cmd("ip4resolver")>(Arg aArgs[])
{
// mdns ip4resolver start|stop <host-name>
otError error;
otMdnsAddressResolver resolver;
bool isStart;
ClearAllBytes(resolver);
SuccessOrExit(error = ParseStartOrStop(aArgs[0], isStart));
VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
resolver.mHostName = aArgs[1].GetCString();
resolver.mInfraIfIndex = mInfraIfIndex;
resolver.mCallback = HandleIp4AddressResult;
if (isStart)
{
error = otMdnsStartIp4AddressResolver(GetInstancePtr(), &resolver);
}
else
{
error = otMdnsStopIp4AddressResolver(GetInstancePtr(), &resolver);
}
exit:
return error;
}
void Mdns::HandleIp4AddressResult(otInstance *aInstance, const otMdnsAddressResult *aResult)
{
OT_UNUSED_VARIABLE(aInstance);
Interpreter::GetInterpreter().mMdns.HandleAddressResult(*aResult, kIp4Address);
}
otError Mdns::Process(Arg aArgs[])
{
#define CmdEntry(aCommandString) \
{ \
aCommandString, &Mdns::Process<Cmd(aCommandString)> \
}
static constexpr Command kCommands[] = {
CmdEntry("browser"), CmdEntry("disable"), CmdEntry("enable"), CmdEntry("ip4resolver"),
CmdEntry("ip6resolver"), CmdEntry("register"), CmdEntry("srvresolver"), CmdEntry("state"),
CmdEntry("txtresolver"), CmdEntry("unicastquestion"), CmdEntry("unregister"),
};
#undef CmdEntry
static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
otError error = OT_ERROR_INVALID_COMMAND;
const Command *command;
if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
{
OutputCommandTable(kCommands);
ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
}
command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
VerifyOrExit(command != nullptr);
error = (this->*command->mHandler)(aArgs + 1);
exit:
return error;
}
} // namespace Cli
} // namespace ot
#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
+142
View File
@@ -0,0 +1,142 @@
/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file contains definitions for CLI to DNS (client and resolver).
*/
#ifndef CLI_MDNS_HPP_
#define CLI_MDNS_HPP_
#include "openthread-core-config.h"
#include <openthread/mdns.h>
#include "cli/cli_config.h"
#include "cli/cli_utils.hpp"
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
namespace ot {
namespace Cli {
/**
* Implements the mDNS CLI interpreter.
*
*/
class Mdns : private Utils
{
public:
/**
* Constructor.
*
* @param[in] aInstance The OpenThread Instance.
* @param[in] aOutputImplementer An `OutputImplementer`.
*
*/
Mdns(otInstance *aInstance, OutputImplementer &aOutputImplementer)
: Utils(aInstance, aOutputImplementer)
, mInfraIfIndex(0)
, mRequestId(0)
, mWaitingForCallback(false)
{
}
/**
* Processes a CLI sub-command.
*
* @param[in] aArgs An array of command line arguments.
*
* @retval OT_ERROR_NONE Successfully executed the CLI command.
* @retval OT_ERROR_PENDING The CLI command was successfully started but final result is pending.
* @retval OT_ERROR_INVALID_COMMAND Invalid or unknown CLI command.
* @retval OT_ERROR_INVALID_ARGS Invalid arguments.
* @retval ... Error during execution of the CLI command.
*
*/
otError Process(Arg aArgs[]);
private:
using Command = CommandEntry<Mdns>;
static constexpr uint8_t kIndentSize = 4;
static constexpr uint16_t kMaxAddresses = 16;
static constexpr uint16_t kStringSize = 400;
static constexpr uint16_t kMaxSubTypes = 8;
static constexpr uint16_t kMaxTxtDataSize = 200;
static constexpr uint16_t kMaxKeyDataSize = 200;
enum IpAddressType : uint8_t
{
kIp6Address,
kIp4Address,
};
struct Buffers // Used to populate `otMdnsService` field
{
char mString[kStringSize];
const char *mSubTypeLabels[kMaxSubTypes];
uint8_t mTxtData[kMaxTxtDataSize];
};
template <CommandId kCommandId> otError Process(Arg aArgs[]);
void OutputHost(const otMdnsHost &aHost);
void OutputService(const otMdnsService &aService);
void OutputKey(const otMdnsKey &aKey);
otError ProcessRegisterHost(Arg aArgs[]);
otError ProcessRegisterService(Arg aArgs[]);
otError ProcessRegisterKey(Arg aArgs[]);
void HandleRegisterationDone(otMdnsRequestId aRequestId, otError aError);
void HandleBrowseResult(const otMdnsBrowseResult &aResult);
void HandleSrvResult(const otMdnsSrvResult &aResult);
void HandleTxtResult(const otMdnsTxtResult &aResult);
void HandleAddressResult(const otMdnsAddressResult &aResult, IpAddressType aType);
static otError ParseStartOrStop(const Arg &aArg, bool &aIsStart);
static void HandleRegisterationDone(otInstance *aInstance, otMdnsRequestId aRequestId, otError aError);
static void HandleBrowseResult(otInstance *aInstance, const otMdnsBrowseResult *aResult);
static void HandleSrvResult(otInstance *aInstance, const otMdnsSrvResult *aResult);
static void HandleTxtResult(otInstance *aInstance, const otMdnsTxtResult *aResult);
static void HandleIp6AddressResult(otInstance *aInstance, const otMdnsAddressResult *aResult);
static void HandleIp4AddressResult(otInstance *aInstance, const otMdnsAddressResult *aResult);
static otError ParseServiceArgs(Arg aArgs[], otMdnsService &aService, Buffers &aBuffers);
uint32_t mInfraIfIndex;
otMdnsRequestId mRequestId;
bool mWaitingForCallback;
};
} // namespace Cli
} // namespace ot
#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
#endif // CLI_MDNS_HPP_
+4
View File
@@ -336,6 +336,7 @@ openthread_core_files = [
"api/link_metrics_api.cpp",
"api/link_raw_api.cpp",
"api/logging_api.cpp",
"api/mdns_api.cpp",
"api/mesh_diag_api.cpp",
"api/message_api.cpp",
"api/multi_radio_api.cpp",
@@ -575,6 +576,8 @@ openthread_core_files = [
"net/ip6_mpl.cpp",
"net/ip6_mpl.hpp",
"net/ip6_types.hpp",
"net/mdns.cpp",
"net/mdns.hpp",
"net/nat64_translator.cpp",
"net/nat64_translator.hpp",
"net/nd6.cpp",
@@ -815,6 +818,7 @@ source_set("libopenthread_core_config") {
"config/link_raw.h",
"config/logging.h",
"config/mac.h",
"config/mdns.h",
"config/mesh_diag.h",
"config/mesh_forwarder.h",
"config/misc.h",
+2
View File
@@ -62,6 +62,7 @@ set(COMMON_SOURCES
api/link_metrics_api.cpp
api/link_raw_api.cpp
api/logging_api.cpp
api/mdns_api.cpp
api/mesh_diag_api.cpp
api/message_api.cpp
api/multi_radio_api.cpp
@@ -178,6 +179,7 @@ set(COMMON_SOURCES
net/ip6_filter.cpp
net/ip6_headers.cpp
net/ip6_mpl.cpp
net/mdns.cpp
net/nat64_translator.cpp
net/nd6.cpp
net/nd_agent.cpp
+188
View File
@@ -0,0 +1,188 @@
/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements the OpenThread mDNS API.
*/
#include "openthread-core-config.h"
#include <openthread/mdns.h>
#include "instance/instance.hpp"
#include "net/mdns.hpp"
using namespace ot;
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
otError otMdnsSetEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex)
{
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().SetEnabled(aEnable, aInfraIfIndex);
}
bool otMdnsIsEnabled(otInstance *aInstance) { return AsCoreType(aInstance).Get<Dns::Multicast::Core>().IsEnabled(); }
void otMdnsSetQuestionUnicastAllowed(otInstance *aInstance, bool aAllow)
{
AsCoreType(aInstance).Get<Dns::Multicast::Core>().SetQuestionUnicastAllowed(aAllow);
}
bool otMdnsIsQuestionUnicastAllowed(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().IsQuestionUnicastAllowed();
}
void otMdnsSetConflictCallback(otInstance *aInstance, otMdnsConflictCallback aCallback)
{
AsCoreType(aInstance).Get<Dns::Multicast::Core>().SetConflictCallback(aCallback);
}
otError otMdnsRegisterHost(otInstance *aInstance,
const otMdnsHost *aHost,
otMdnsRequestId aRequestId,
otMdnsRegisterCallback aCallback)
{
AssertPointerIsNotNull(aHost);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().RegisterHost(*aHost, aRequestId, aCallback);
}
otError otMdnsUnregisterHost(otInstance *aInstance, const otMdnsHost *aHost)
{
AssertPointerIsNotNull(aHost);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().UnregisterHost(*aHost);
}
otError otMdnsRegisterService(otInstance *aInstance,
const otMdnsService *aService,
otMdnsRequestId aRequestId,
otMdnsRegisterCallback aCallback)
{
AssertPointerIsNotNull(aService);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().RegisterService(*aService, aRequestId, aCallback);
}
otError otMdnsUnregisterService(otInstance *aInstance, const otMdnsService *aService)
{
AssertPointerIsNotNull(aService);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().UnregisterService(*aService);
}
otError otMdnsRegisterKey(otInstance *aInstance,
const otMdnsKey *aKey,
otMdnsRequestId aRequestId,
otMdnsRegisterCallback aCallback)
{
AssertPointerIsNotNull(aKey);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().RegisterKey(*aKey, aRequestId, aCallback);
}
otError otMdnsUnregisterKey(otInstance *aInstance, const otMdnsKey *aKey)
{
AssertPointerIsNotNull(aKey);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().UnregisterKey(*aKey);
}
otError otMdnsStartBrowser(otInstance *aInstance, const otMdnsBrowser *aBroswer)
{
AssertPointerIsNotNull(aBroswer);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().StartBrowser(*aBroswer);
}
otError otMdnsStopBrowser(otInstance *aInstance, const otMdnsBrowser *aBroswer)
{
AssertPointerIsNotNull(aBroswer);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().StopBrowser(*aBroswer);
}
otError otMdnsStartSrvResolver(otInstance *aInstance, const otMdnsSrvResolver *aResolver)
{
AssertPointerIsNotNull(aResolver);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().StartSrvResolver(*aResolver);
}
otError otMdnsStopSrvResolver(otInstance *aInstance, const otMdnsSrvResolver *aResolver)
{
AssertPointerIsNotNull(aResolver);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().StopSrvResolver(*aResolver);
}
otError otMdnsStartTxtResolver(otInstance *aInstance, const otMdnsTxtResolver *aResolver)
{
AssertPointerIsNotNull(aResolver);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().StartTxtResolver(*aResolver);
}
otError otMdnsStopTxtResolver(otInstance *aInstance, const otMdnsTxtResolver *aResolver)
{
AssertPointerIsNotNull(aResolver);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().StopTxtResolver(*aResolver);
}
otError otMdnsStartIp6AddressResolver(otInstance *aInstance, const otMdnsAddressResolver *aResolver)
{
AssertPointerIsNotNull(aResolver);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().StartIp6AddressResolver(*aResolver);
}
otError otMdnsStopIp6AddressResolver(otInstance *aInstance, const otMdnsAddressResolver *aResolver)
{
AssertPointerIsNotNull(aResolver);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().StopIp6AddressResolver(*aResolver);
}
otError otMdnsStartIp4AddressResolver(otInstance *aInstance, const otMdnsAddressResolver *aResolver)
{
AssertPointerIsNotNull(aResolver);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().StartIp4AddressResolver(*aResolver);
}
otError otMdnsStopIp4AddressResolver(otInstance *aInstance, const otMdnsAddressResolver *aResolver)
{
AssertPointerIsNotNull(aResolver);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().StopIp4AddressResolver(*aResolver);
}
#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
+100
View File
@@ -0,0 +1,100 @@
/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes compile-time configurations for the Multicast DNS (mDNS).
*
*/
#ifndef CONFIG_MULTICAST_DNS_H_
#define CONFIG_MULTICAST_DNS_H_
/**
* @addtogroup config-mdns
*
* @brief
* This module includes configuration variables for the Multicast DNS (mDNS).
*
* @{
*
*/
/**
* @def OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
*
* Define to 1 to enable Multicast DNS (mDNS) support.
*
*/
#ifndef OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
#define OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
*
* Define to 1 to allow public OpenThread APIs to be defined for Multicast DNS (mDNS) module.
*
* The OpenThread mDNS module is mainly intended for use by other OT core modules, so the public APIs are by default
* not provided.
*
*/
#ifndef OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE
#define OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_MULTICAST_DNS_DEFAULT_QUESTION_UNICAST_ALLOWED
*
* Specified the default value for `otMdnsIsQuestionUnicastAllowed()` which indicates whether mDNS core is allowed to
* send "QU" questions (questions requesting unicast response). When allowed, the first probe will be sent as "QU"
* question. The `otMdnsSetQuestionUnicastAllowed()` can be used to change the default value at run-time.
*
*/
#ifndef OPENTHREAD_CONFIG_MULTICAST_DNS_DEFAULT_QUESTION_UNICAST_ALLOWED
#define OPENTHREAD_CONFIG_MULTICAST_DNS_DEFAULT_QUESTION_UNICAST_ALLOWED 1
#endif
/**
* @def OPENTHREAD_CONFIG_MULTICAST_DNS_MOCK_PLAT_APIS_ENABLE
*
* Define to 1 to add mock (empty) implementation of mDNS platform APIs.
*
* This is intended for generating code size report only and should not be used otherwise.
*
*/
#ifndef OPENTHREAD_CONFIG_MULTICAST_DNS_MOCK_PLAT_APIS_ENABLE
#define OPENTHREAD_CONFIG_MULTICAST_DNS_MOCK_PLAT_APIS_ENABLE 0
#endif
/**
* @}
*
*/
#endif // CONFIG_MULTICAST_DNS_H_
+3
View File
@@ -122,6 +122,9 @@ Instance::Instance(void)
#if OPENTHREAD_CONFIG_DNS_DSO_ENABLE
, mDnsDso(*this)
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
, mMdnsCore(*this)
#endif
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
, mSntpClient(*this)
#endif
+9
View File
@@ -92,6 +92,7 @@
#include "net/dnssd_server.hpp"
#include "net/ip6.hpp"
#include "net/ip6_filter.hpp"
#include "net/mdns.hpp"
#include "net/nat64_translator.hpp"
#include "net/nd_agent.hpp"
#include "net/netif.hpp"
@@ -526,6 +527,10 @@ private:
Dns::Dso mDnsDso;
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
Dns::Multicast::Core mMdnsCore;
#endif
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
Sntp::Client mSntpClient;
#endif
@@ -925,6 +930,10 @@ template <> inline Dns::ServiceDiscovery::Server &Instance::Get(void) { return m
template <> inline Dns::Dso &Instance::Get(void) { return mDnsDso; }
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
template <> inline Dns::Multicast::Core &Instance::Get(void) { return mMdnsCore; }
#endif
template <> inline NetworkDiagnostic::Server &Instance::Get(void) { return mNetworkDiagnosticServer; }
#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
+31
View File
@@ -36,6 +36,7 @@
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/num_utils.hpp"
#include "common/numeric_limits.hpp"
#include "common/random.hpp"
#include "common/string.hpp"
#include "instance/instance.hpp"
@@ -1343,5 +1344,35 @@ exit:
return valid;
}
void NsecRecord::TypeBitMap::AddType(uint16_t aType)
{
if ((aType >> 8) == mBlockNumber)
{
uint8_t type = static_cast<uint8_t>(aType & 0xff);
uint8_t index = (type / kBitsPerByte);
uint16_t mask = (0x80 >> (type % kBitsPerByte));
mBitmaps[index] |= mask;
mBitmapLength = Max<uint8_t>(mBitmapLength, index + 1);
}
}
bool NsecRecord::TypeBitMap::ContainsType(uint16_t aType) const
{
bool contains = false;
uint8_t type = static_cast<uint8_t>(aType & 0xff);
uint8_t index = (type / kBitsPerByte);
uint16_t mask = (0x80 >> (type % kBitsPerByte));
VerifyOrExit((aType >> 8) == mBlockNumber);
VerifyOrExit(index < mBitmapLength);
contains = (mBitmaps[index] & mask);
exit:
return contains;
}
} // namespace Dns
} // namespace ot
+99
View File
@@ -1336,6 +1336,7 @@ public:
static constexpr uint16_t kTypeAaaa = 28; ///< IPv6 address record.
static constexpr uint16_t kTypeSrv = 33; ///< SRV locator record.
static constexpr uint16_t kTypeOpt = 41; ///< Option record.
static constexpr uint16_t kTypeNsec = 47; ///< NSEC record.
static constexpr uint16_t kTypeAny = 255; ///< ANY record.
// Resource Record Class Codes.
@@ -2745,6 +2746,104 @@ private:
uint32_t mKeyLeaseInterval;
} OT_TOOL_PACKED_END;
/**
* Implements body format of NSEC record (RFC 3845) for use with mDNS.
*
*/
OT_TOOL_PACKED_BEGIN
class NsecRecord : public ResourceRecord
{
public:
static constexpr uint16_t kType = kTypeNsec; ///< The NSEC record type.
/**
* Represents NSEC Type Bit Map field (RFC 3845 - section 2.1.2)
*
*/
OT_TOOL_PACKED_BEGIN
class TypeBitMap : public Clearable<TypeBitMap>
{
public:
static constexpr uint8_t kMinSize = 2; ///< Minimum size of a valid `TypeBitMap` (with zero length).
static constexpr uint8_t kMaxLength = 32; ///< Maximum BitmapLength value.
/**
* Gets the Window Block Number
*
* @returns The Window Block Number.
*
*/
uint8_t GetBlockNumber(void) const { return mBlockNumber; }
/**
* Sets the Window Block Number
*
* @param[in] aBlockNumber The Window Block Number.
*
*/
void SetBlockNumber(uint8_t aBlockNumber) { mBlockNumber = aBlockNumber; }
/**
* Gets the Bitmap length
*
* @returns The Bitmap length
*
*/
uint8_t GetBitmapLength(void) { return mBitmapLength; }
/**
* Gets the total size (number of bytes) of the `TypeBitMap` field.
*
* @returns The size of the `TypeBitMap`
*
*/
uint16_t GetSize(void) const { return (sizeof(mBlockNumber) + sizeof(mBitmapLength) + mBitmapLength); }
/**
* Adds a resource record type to the Bitmap.
*
* As the types are added to the Bitmap the Bitmap length gets updated accordingly.
*
* The type space is split into 256 window blocks, each representing the low-order 8 bits of the 16-bit type
* value. If @p aType does not match the currently set Window Block Number, no action is performed.
*
* @param[in] aType The resource record type to add.
*
*/
void AddType(uint16_t aType);
/**
* Indicates whether a given resource record type is present in the Bitmap.
*
* If @p aType does not match the currently set Window Block Number, this method returns `false`..
*
* @param[in] aType The resource record type to check.
*
* @retval TRUE The @p aType is present in the Bitmap.
* @retval FALSE The @p aType is not present in the Bitmap.
*
*/
bool ContainsType(uint16_t aType) const;
private:
uint8_t mBlockNumber;
uint8_t mBitmapLength;
uint8_t mBitmaps[kMaxLength];
} OT_TOOL_PACKED_END;
/**
* Initializes the NSEC Resource Record by setting its type and class.
*
* Other record fields (TTL, length remain unchanged/uninitialized.
*
* @param[in] aClass The class of the resource record (default is `kClassInternet`).
*
*/
void Init(uint16_t aClass = kClassInternet) { ResourceRecord::Init(kTypeNsec, aClass); }
} OT_TOOL_PACKED_END;
/**
* Implements Question format.
*
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1
View File
@@ -98,6 +98,7 @@
#include "config/link_raw.h"
#include "config/logging.h"
#include "config/mac.h"
#include "config/mdns.h"
#include "config/mesh_diag.h"
#include "config/mesh_forwarder.h"
#include "config/misc.h"
+1
View File
@@ -135,6 +135,7 @@ add_library(openthread-posix
infra_if.cpp
logging.cpp
mainloop.cpp
mdns_socket.cpp
memory.cpp
misc.cpp
multicast_routing.cpp
+710
View File
@@ -0,0 +1,710 @@
/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "mdns_socket.hpp"
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
#include <arpa/inet.h>
#include <errno.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include "ip6_utils.hpp"
#include "platform-posix.h"
#include "common/code_utils.hpp"
extern "C" otError otPlatMdnsSetListeningEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex)
{
return ot::Posix::MdnsSocket::Get().SetListeningEnabled(aInstance, aEnable, aInfraIfIndex);
}
extern "C" void otPlatMdnsSendMulticast(otInstance *aInstance, otMessage *aMessage, uint32_t aInfraIfIndex)
{
OT_UNUSED_VARIABLE(aInstance);
return ot::Posix::MdnsSocket::Get().SendMulticast(aMessage, aInfraIfIndex);
}
extern "C" void otPlatMdnsSendUnicast(otInstance *aInstance, otMessage *aMessage, const otPlatMdnsAddressInfo *aAddress)
{
OT_UNUSED_VARIABLE(aInstance);
return ot::Posix::MdnsSocket::Get().SendUnicast(aMessage, aAddress);
}
namespace ot {
namespace Posix {
using namespace ot::Posix::Ip6Utils;
const char MdnsSocket::kLogModuleName[] = "MdnsSocket";
MdnsSocket &MdnsSocket::Get(void)
{
static MdnsSocket sInstance;
return sInstance;
}
void MdnsSocket::Init(void)
{
mEnabled = false;
mInfraIfIndex = 0;
mFd6 = -1;
mFd4 = -1;
mPendingIp6Tx = 0;
mPendingIp4Tx = 0;
// mDNS multicast IPv6 address "ff02::fb"
memset(&mMulticastIp6Address, 0, sizeof(otIp6Address));
mMulticastIp6Address.mFields.m8[0] = 0xff;
mMulticastIp6Address.mFields.m8[1] = 0x02;
mMulticastIp6Address.mFields.m8[15] = 0xfb;
// mDNS multicast IPv4 address "224.0.0.251"
memset(&mMulticastIp4Address, 0, sizeof(otIp4Address));
mMulticastIp4Address.mFields.m8[0] = 224;
mMulticastIp4Address.mFields.m8[3] = 251;
memset(&mTxQueue, 0, sizeof(mTxQueue));
}
void MdnsSocket::SetUp(void)
{
otMessageQueueInit(&mTxQueue);
Mainloop::Manager::Get().Add(*this);
}
void MdnsSocket::TearDown(void)
{
Mainloop::Manager::Get().Remove(*this);
if (mEnabled)
{
ClearTxQueue();
mEnabled = false;
}
}
void MdnsSocket::Deinit(void)
{
CloseIp4Socket();
CloseIp6Socket();
}
void MdnsSocket::Update(otSysMainloopContext &aContext)
{
VerifyOrExit(mEnabled);
FD_SET(mFd6, &aContext.mReadFdSet);
FD_SET(mFd4, &aContext.mReadFdSet);
if (mPendingIp6Tx > 0)
{
FD_SET(mFd6, &aContext.mWriteFdSet);
}
if (mPendingIp4Tx > 0)
{
FD_SET(mFd4, &aContext.mWriteFdSet);
}
if (aContext.mMaxFd < mFd6)
{
aContext.mMaxFd = mFd6;
}
if (aContext.mMaxFd < mFd4)
{
aContext.mMaxFd = mFd4;
}
exit:
return;
}
void MdnsSocket::Process(const otSysMainloopContext &aContext)
{
VerifyOrExit(mEnabled);
if (FD_ISSET(mFd6, &aContext.mWriteFdSet))
{
SendQueuedMessages(kIp6Msg);
}
if (FD_ISSET(mFd4, &aContext.mWriteFdSet))
{
SendQueuedMessages(kIp4Msg);
}
if (FD_ISSET(mFd6, &aContext.mReadFdSet))
{
ReceiveMessage(kIp6Msg);
}
if (FD_ISSET(mFd4, &aContext.mReadFdSet))
{
ReceiveMessage(kIp4Msg);
}
exit:
return;
}
otError MdnsSocket::SetListeningEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aEnable != mEnabled);
mInstance = aInstance;
if (aEnable)
{
error = Enable(aInfraIfIndex);
}
else
{
Disable(aInfraIfIndex);
}
exit:
return error;
}
otError MdnsSocket::Enable(uint32_t aInfraIfIndex)
{
otError error;
SuccessOrExit(error = OpenIp4Socket(aInfraIfIndex));
SuccessOrExit(error = JoinOrLeaveIp4MulticastGroup(/* aJoin */ true, aInfraIfIndex));
SuccessOrExit(error = OpenIp6Socket(aInfraIfIndex));
SuccessOrExit(error = JoinOrLeaveIp6MulticastGroup(/* aJoin */ true, aInfraIfIndex));
mEnabled = true;
mInfraIfIndex = aInfraIfIndex;
LogInfo("Enabled");
exit:
if (error != OT_ERROR_NONE)
{
CloseIp4Socket();
CloseIp6Socket();
}
return error;
}
void MdnsSocket::Disable(uint32_t aInfraIfIndex)
{
ClearTxQueue();
IgnoreError(JoinOrLeaveIp4MulticastGroup(/* aJoin */ false, aInfraIfIndex));
IgnoreError(JoinOrLeaveIp6MulticastGroup(/* aJoin */ false, aInfraIfIndex));
CloseIp4Socket();
CloseIp6Socket();
mEnabled = false;
LogInfo("Disabled");
}
void MdnsSocket::SendMulticast(otMessage *aMessage, uint32_t aInfraIfIndex)
{
Metadata metadata;
uint16_t length;
VerifyOrExit(mEnabled);
VerifyOrExit(aInfraIfIndex == mInfraIfIndex);
length = otMessageGetLength(aMessage);
if (length > kMaxMessageLength)
{
LogWarn("Multicast msg length %u is longer than max %u", length, kMaxMessageLength);
ExitNow();
}
metadata.mIp6Address = mMulticastIp6Address;
metadata.mIp6Port = kMdnsPort;
metadata.mIp4Address = mMulticastIp4Address;
metadata.mIp4Port = kMdnsPort;
SuccessOrExit(otMessageAppend(aMessage, &metadata, sizeof(Metadata)));
mPendingIp4Tx++;
mPendingIp6Tx++;
otMessageQueueEnqueue(&mTxQueue, aMessage);
aMessage = NULL;
exit:
if (aMessage != NULL)
{
otMessageFree(aMessage);
}
}
void MdnsSocket::SendUnicast(otMessage *aMessage, const otPlatMdnsAddressInfo *aAddress)
{
bool isIp4 = false;
Metadata metadata;
uint16_t length;
VerifyOrExit(mEnabled);
VerifyOrExit(aAddress->mInfraIfIndex == mInfraIfIndex);
length = otMessageGetLength(aMessage);
if (length > kMaxMessageLength)
{
LogWarn("Unicast msg length %u is longer than max %u", length, kMaxMessageLength);
ExitNow();
}
memset(&metadata, 0, sizeof(Metadata));
if (otIp4FromIp4MappedIp6Address(&aAddress->mAddress, &metadata.mIp4Address) == OT_ERROR_NONE)
{
isIp4 = true;
metadata.mIp4Port = aAddress->mPort;
metadata.mIp6Port = 0;
}
else
{
metadata.mIp6Address = aAddress->mAddress;
metadata.mIp4Port = 0;
metadata.mIp6Port = aAddress->mPort;
}
SuccessOrExit(otMessageAppend(aMessage, &metadata, sizeof(Metadata)));
if (isIp4)
{
mPendingIp4Tx++;
}
else
{
mPendingIp6Tx++;
}
otMessageQueueEnqueue(&mTxQueue, aMessage);
aMessage = NULL;
exit:
if (aMessage != NULL)
{
otMessageFree(aMessage);
}
}
void MdnsSocket::ClearTxQueue(void)
{
otMessage *message;
while ((message = otMessageQueueGetHead(&mTxQueue)) != NULL)
{
otMessageQueueDequeue(&mTxQueue, message);
otMessageFree(message);
}
mPendingIp4Tx = 0;
mPendingIp6Tx = 0;
}
void MdnsSocket::SendQueuedMessages(MsgType aMsgType)
{
switch (aMsgType)
{
case kIp6Msg:
VerifyOrExit(mPendingIp6Tx > 0);
break;
case kIp4Msg:
VerifyOrExit(mPendingIp4Tx > 0);
break;
}
for (otMessage *message = otMessageQueueGetHead(&mTxQueue); message != NULL;
message = otMessageQueueGetNext(&mTxQueue, message))
{
bool isTxPending = false;
uint16_t length;
uint16_t offset;
int bytesSent;
Metadata metadata;
uint8_t buffer[kMaxMessageLength];
struct sockaddr_in6 addr6;
struct sockaddr_in addr;
length = otMessageGetLength(message);
offset = length - sizeof(Metadata);
length -= sizeof(Metadata);
otMessageRead(message, offset, &metadata, sizeof(Metadata));
switch (aMsgType)
{
case kIp6Msg:
isTxPending = (metadata.mIp6Port != 0);
break;
case kIp4Msg:
isTxPending = (metadata.mIp4Port != 0);
break;
}
if (!isTxPending)
{
continue;
}
otMessageRead(message, 0, buffer, length);
switch (aMsgType)
{
case kIp6Msg:
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(metadata.mIp6Port);
CopyIp6AddressTo(metadata.mIp6Address, &addr6.sin6_addr);
bytesSent = sendto(mFd6, buffer, length, 0, reinterpret_cast<struct sockaddr *>(&addr6), sizeof(addr6));
VerifyOrExit(bytesSent == length);
metadata.mIp6Port = 0;
mPendingIp6Tx--;
break;
case kIp4Msg:
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(metadata.mIp4Port);
memcpy(&addr.sin_addr.s_addr, &metadata.mIp4Address, sizeof(otIp4Address));
bytesSent = sendto(mFd4, buffer, length, 0, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr));
VerifyOrExit(bytesSent == length);
metadata.mIp4Port = 0;
mPendingIp4Tx--;
break;
}
if (metadata.CanFreeMessage())
{
otMessageQueueDequeue(&mTxQueue, message);
otMessageFree(message);
}
else
{
otMessageWrite(message, offset, &metadata, sizeof(Metadata));
}
}
exit:
return;
}
void MdnsSocket::ReceiveMessage(MsgType aMsgType)
{
otMessage *message = nullptr;
uint8_t buffer[kMaxMessageLength];
otPlatMdnsAddressInfo addrInfo;
uint16_t length = 0;
struct sockaddr_in6 sockaddr6;
struct sockaddr_in sockaddr;
socklen_t len = sizeof(sockaddr6);
ssize_t rval;
memset(&addrInfo, 0, sizeof(addrInfo));
switch (aMsgType)
{
case kIp6Msg:
len = sizeof(sockaddr6);
memset(&sockaddr6, 0, sizeof(sockaddr6));
rval = recvfrom(mFd6, reinterpret_cast<char *>(&buffer), sizeof(buffer), 0,
reinterpret_cast<struct sockaddr *>(&sockaddr6), &len);
VerifyOrExit(rval >= 0, LogCrit("recvfrom() for IPv6 socket failed, errno: %s", strerror(errno)));
length = static_cast<uint16_t>(rval);
ReadIp6AddressFrom(&sockaddr6.sin6_addr, addrInfo.mAddress);
break;
case kIp4Msg:
len = sizeof(sockaddr);
memset(&sockaddr, 0, sizeof(sockaddr));
rval = recvfrom(mFd4, reinterpret_cast<char *>(&buffer), sizeof(buffer), 0,
reinterpret_cast<struct sockaddr *>(&sockaddr), &len);
VerifyOrExit(rval >= 0, LogCrit("recvfrom() for IPv4 socket failed, errno: %s", strerror(errno)));
length = static_cast<uint16_t>(rval);
otIp4ToIp4MappedIp6Address((otIp4Address *)(&sockaddr.sin_addr.s_addr), &addrInfo.mAddress);
break;
}
VerifyOrExit(length > 0);
message = otIp6NewMessage(mInstance, nullptr);
VerifyOrExit(message != nullptr);
SuccessOrExit(otMessageAppend(message, buffer, length));
addrInfo.mPort = kMdnsPort;
addrInfo.mInfraIfIndex = mInfraIfIndex;
otPlatMdnsHandleReceive(mInstance, message, /* aInUnicast */ false, &addrInfo);
message = nullptr;
exit:
if (message != nullptr)
{
otMessageFree(message);
}
}
//---------------------------------------------------------------------------------------------------------------------
// Socket helpers
otError MdnsSocket::OpenIp4Socket(uint32_t aInfraIfIndex)
{
otError error = OT_ERROR_FAILED;
struct sockaddr_in addr;
int fd;
fd = socket(AF_INET, SOCK_DGRAM, 0);
VerifyOrExit(fd >= 0, LogCrit("Failed to create IPv4 socket"));
#ifdef __linux__
{
char nameBuffer[IF_NAMESIZE];
const char *ifname;
ifname = if_indextoname(aInfraIfIndex, nameBuffer);
VerifyOrExit(ifname != NULL, LogCrit("if_indextoname() failed"));
error = SetSocketOptionValue(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname), "SO_BINDTODEVICE");
SuccessOrExit(error);
}
#else
{
int ifindex = static_cast<int>(aInfraIfIndex);
SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IP, IP_BOUND_IF, ifindex, "IP_BOUND_IF"));
}
#endif
SuccessOrExit(error = SetSocketOption<uint8_t>(fd, IPPROTO_IP, IP_MULTICAST_TTL, 255, "IP_MULTICAST_TTL"));
SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IP, IP_TTL, 255, "IP_TTL"));
SuccessOrExit(error = SetSocketOption<uint8_t>(fd, IPPROTO_IP, IP_MULTICAST_LOOP, 1, "IP_MULTICAST_LOOP"));
SuccessOrExit(error = SetReuseAddrPortOptions(fd));
{
struct ip_mreqn mreqn;
memset(&mreqn, 0, sizeof(mreqn));
mreqn.imr_multiaddr.s_addr = inet_addr("224.0.0.251");
mreqn.imr_ifindex = aInfraIfIndex;
SuccessOrExit(
error = SetSocketOptionValue(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn), "IP_MULTICAST_IF"));
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(kMdnsPort);
if (bind(fd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) < 0)
{
LogCrit("bind() to mDNS port for IPv4 socket failed, errno: %s", strerror(errno));
error = OT_ERROR_FAILED;
ExitNow();
}
mFd4 = fd;
LogInfo("Successfully opened IPv4 socket");
exit:
return error;
}
otError MdnsSocket::JoinOrLeaveIp4MulticastGroup(bool aJoin, uint32_t aInfraIfIndex)
{
struct ip_mreqn mreqn;
memset(&mreqn, 0, sizeof(mreqn));
memcpy(&mreqn.imr_multiaddr.s_addr, &mMulticastIp4Address, sizeof(otIp4Address));
mreqn.imr_ifindex = aInfraIfIndex;
if (aJoin)
{
// Suggested workaround for netif not dropping
// a previous multicast membership.
setsockopt(mFd4, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn));
}
return SetSocketOption(mFd4, IPPROTO_IP, aJoin ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, mreqn,
"IP_ADD/DROP_MEMBERSHIP");
}
void MdnsSocket::CloseIp4Socket(void)
{
if (mFd4 >= 0)
{
close(mFd4);
mFd4 = -1;
}
}
otError MdnsSocket::OpenIp6Socket(uint32_t aInfraIfIndex)
{
otError error = OT_ERROR_FAILED;
struct sockaddr_in6 addr6;
int fd;
int ifindex = static_cast<int>(aInfraIfIndex);
fd = socket(AF_INET6, SOCK_DGRAM, 0);
VerifyOrExit(fd >= 0, LogCrit("Failed to create IPv4 socket"));
#ifdef __linux__
{
char nameBuffer[IF_NAMESIZE];
const char *ifname;
ifname = if_indextoname(aInfraIfIndex, nameBuffer);
VerifyOrExit(ifname != NULL, LogCrit("if_indextoname() failed"));
error = SetSocketOptionValue(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname), "SO_BINDTODEVICE");
SuccessOrExit(error);
}
#else
{
SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IPV6, IPV6_BOUND_IF, ifindex, "IPV6_BOUND_IF"));
}
#endif
SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255, "IPV6_MULTICAST_HOPS"));
SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 255, "IPV6_UNICAST_HOPS"));
SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IPV6, IPV6_V6ONLY, 1, "IPV6_V6ONLY"));
SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, ifindex, "IPV6_MULTICAST_IF"));
SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 1, "IPV6_MULTICAST_LOOP"));
SuccessOrExit(error = SetReuseAddrPortOptions(fd));
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(kMdnsPort);
if (bind(fd, reinterpret_cast<struct sockaddr *>(&addr6), sizeof(addr6)) < 0)
{
LogCrit("bind() to mDNS port for IPv6 socket failed, errno: %s", strerror(errno));
error = OT_ERROR_FAILED;
ExitNow();
}
mFd6 = fd;
LogInfo("Successfully opened IPv6 socket");
exit:
return error;
}
#ifndef IPV6_ADD_MEMBERSHIP
#ifdef IPV6_JOIN_GROUP
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
#endif
#endif
#ifndef IPV6_DROP_MEMBERSHIP
#ifdef IPV6_LEAVE_GROUP
#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
#endif
#endif
otError MdnsSocket::JoinOrLeaveIp6MulticastGroup(bool aJoin, uint32_t aInfraIfIndex)
{
struct ipv6_mreq mreq6;
memset(&mreq6, 0, sizeof(mreq6));
Ip6Utils::CopyIp6AddressTo(mMulticastIp6Address, &mreq6.ipv6mr_multiaddr);
mreq6.ipv6mr_interface = static_cast<int>(aInfraIfIndex);
if (aJoin)
{
// Suggested workaround for netif not dropping
// a previous multicast membership.
setsockopt(mFd6, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
}
return SetSocketOptionValue(mFd6, IPPROTO_IPV6, aJoin ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq6,
sizeof(mreq6), "IP6_ADD/DROP_MEMBERSHIP");
}
void MdnsSocket::CloseIp6Socket(void)
{
if (mFd6 >= 0)
{
close(mFd6);
mFd6 = -1;
}
}
otError MdnsSocket::SetReuseAddrPortOptions(int aFd)
{
otError error;
SuccessOrExit(error = SetSocketOption<int>(aFd, SOL_SOCKET, SO_REUSEADDR, 1, "SO_REUSEADDR"));
SuccessOrExit(error = SetSocketOption<int>(aFd, SOL_SOCKET, SO_REUSEPORT, 1, "SO_REUSEPORT"));
exit:
return error;
}
otError MdnsSocket::SetSocketOptionValue(int aFd,
int aLevel,
int aOption,
const void *aValue,
uint32_t aValueLength,
const char *aOptionName)
{
otError error = OT_ERROR_NONE;
if (setsockopt(aFd, aLevel, aOption, aValue, aValueLength) != 0)
{
error = OT_ERROR_FAILED;
LogCrit("Failed to setsockopt(%s) - errno: %s", aOptionName, strerror(errno));
}
return error;
}
} // namespace Posix
} // namespace ot
#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
+181
View File
@@ -0,0 +1,181 @@
/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef OT_POSIX_PLATFORM_MDNS_SOCKET_HPP_
#define OT_POSIX_PLATFORM_MDNS_SOCKET_HPP_
#include "openthread-posix-config.h"
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
#include <openthread/nat64.h>
#include <openthread/platform/mdns_socket.h>
#include "logger.hpp"
#include "mainloop.hpp"
#include "core/common/non_copyable.hpp"
namespace ot {
namespace Posix {
/**
* Implements platform mDNS socket APIs.
*
*/
class MdnsSocket : public Mainloop::Source, public Logger<MdnsSocket>, private NonCopyable
{
public:
static const char kLogModuleName[]; ///< Module name used for logging.
/**
* Gets the `MdnsSocket` singleton.
*
* @returns The singleton object.
*
*/
static MdnsSocket &Get(void);
/**
* Initializes the `MdnsSocket`.
*
* Called before OpenThread instance is created.
*
*/
void Init(void);
/**
* Sets up the `MdnsSocket`.
*
* Called after OpenThread instance is created.
*
*/
void SetUp(void);
/**
* Tears down the `MdnsSocket`.
*
* Called before OpenThread instance is destructed.
*
*/
void TearDown(void);
/**
* Deinitializes the `MdnsSocket`.
*
* Called after OpenThread instance is destructed.
*
*/
void Deinit(void);
/**
* Updates the fd_set and timeout for mainloop.
*
* @param[in,out] aContext A reference to the mainloop context.
*
*/
void Update(otSysMainloopContext &aContext) override;
/**
* Performs `MdnsSocket` processing.
*
* @param[in] aContext A reference to the mainloop context.
*
*/
void Process(const otSysMainloopContext &aContext) override;
// otPlatMdns APIs
otError SetListeningEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex);
void SendMulticast(otMessage *aMessage, uint32_t aInfraIfIndex);
void SendUnicast(otMessage *aMessage, const otPlatMdnsAddressInfo *aAddress);
private:
static constexpr uint16_t kMaxMessageLength = 2000;
static constexpr uint16_t kMdnsPort = 5353;
enum MsgType : uint8_t
{
kIp6Msg,
kIp4Msg,
};
struct Metadata
{
bool CanFreeMessage(void) const { return (mIp6Port == 0) && (mIp4Port == 0); }
otIp6Address mIp6Address;
uint16_t mIp6Port;
otIp4Address mIp4Address;
uint16_t mIp4Port;
};
bool mEnabled;
uint32_t mInfraIfIndex;
int mFd4;
int mFd6;
uint32_t mPendingIp6Tx;
uint32_t mPendingIp4Tx;
otMessageQueue mTxQueue;
otIp6Address mMulticastIp6Address;
otIp4Address mMulticastIp4Address;
otInstance *mInstance;
otError Enable(uint32_t aInfraIfIndex);
void Disable(uint32_t aInfraIfIndex);
void ClearTxQueue(void);
void SendQueuedMessages(MsgType aMsgType);
void ReceiveMessage(MsgType aMsgType);
otError OpenIp4Socket(uint32_t aInfraIfIndex);
otError JoinOrLeaveIp4MulticastGroup(bool aJoin, uint32_t aInfraIfIndex);
void CloseIp4Socket(void);
otError OpenIp6Socket(uint32_t aInfraIfIndex);
otError JoinOrLeaveIp6MulticastGroup(bool aJoin, uint32_t aInfraIfIndex);
void CloseIp6Socket(void);
static otError SetReuseAddrPortOptions(int aFd);
template <typename ValueType>
static otError SetSocketOption(int aFd, int aLevel, int aOption, const ValueType &aValue, const char *aOptionName)
{
return SetSocketOptionValue(aFd, aLevel, aOption, &aValue, sizeof(ValueType), aOptionName);
}
static otError SetSocketOptionValue(int aFd,
int aLevel,
int aOption,
const void *aValue,
uint32_t aValueLength,
const char *aOptionName);
};
} // namespace Posix
} // namespace ot
#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
#endif // OT_POSIX_PLATFORM_MDNS_SOCKET_HPP_
+16
View File
@@ -54,6 +54,7 @@
#include "posix/platform/firewall.hpp"
#include "posix/platform/infra_if.hpp"
#include "posix/platform/mainloop.hpp"
#include "posix/platform/mdns_socket.hpp"
#include "posix/platform/radio_url.hpp"
#include "posix/platform/udp.hpp"
@@ -145,7 +146,10 @@ void platformInit(otPlatformConfig *aPlatformConfig)
#if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
ot::Posix::InfraNetif::Get().Init();
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
ot::Posix::MdnsSocket::Get().Init();
#endif
gNetifName[0] = '\0';
@@ -197,6 +201,10 @@ void platformSetUp(otPlatformConfig *aPlatformConfig)
ot::Posix::Udp::Get().SetUp();
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
ot::Posix::MdnsSocket::Get().SetUp();
#endif
#if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
ot::Posix::Daemon::Get().SetUp();
#endif
@@ -244,6 +252,10 @@ void platformTearDown(void)
ot::Posix::InfraNetif::Get().TearDown();
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
ot::Posix::MdnsSocket::Get().TearDown();
#endif
exit:
return;
}
@@ -272,6 +284,10 @@ void platformDeinit(void)
ot::Posix::InfraNetif::Get().Deinit();
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
ot::Posix::MdnsSocket::Get().Deinit();
#endif
exit:
return;
}
@@ -39,12 +39,14 @@
#define OPENTHREAD_CONFIG_PLATFORM_INFO "POSIX-toranj"
#define OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE 1
#ifdef __linux__
#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1
#endif
#ifndef OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
#define OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE 1
#define OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE 0
#endif
#define OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE 1
@@ -42,13 +42,14 @@
#define OPENTHREAD_CONFIG_PLATFORM_INFO "SIMULATION-RCP-toranj"
#else
#define OPENTHREAD_CONFIG_PLATFORM_INFO "SIMULATION-toranj"
#endif
#define OPENTHREAD_CONFIG_COAP_API_ENABLE 1
#define OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE 1
#define OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX 1
#define OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE 0
#define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 1
@@ -178,6 +178,10 @@
#define OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE 1
#define OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE 1
#define OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE 1
#define OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE 1
#define OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK 1
+21
View File
@@ -723,6 +723,27 @@ target_link_libraries(ot-test-macros
add_test(NAME ot-test-macros COMMAND ot-test-macros)
add_executable(ot-test-mdns
test_mdns.cpp
)
target_include_directories(ot-test-mdns
PRIVATE
${COMMON_INCLUDES}
)
target_compile_options(ot-test-mdns
PRIVATE
${COMMON_COMPILE_OPTIONS}
)
target_link_libraries(ot-test-mdns
PRIVATE
${COMMON_LIBS}
)
add_test(NAME ot-test-mdns COMMAND ot-test-mdns)
add_executable(ot-test-message
test_message.cpp
)
File diff suppressed because it is too large Load Diff
+29
View File
@@ -555,6 +555,35 @@ otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aTh
return OT_ERROR_NONE;
}
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
OT_TOOL_WEAK otError otPlatMdnsSetListeningEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aEnable);
OT_UNUSED_VARIABLE(aInfraIfIndex);
return OT_ERROR_NOT_IMPLEMENTED;
}
OT_TOOL_WEAK void otPlatMdnsSendMulticast(otInstance *aInstance, otMessage *aMessage, uint32_t aInfraIfIndex)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aMessage);
OT_UNUSED_VARIABLE(aInfraIfIndex);
}
OT_TOOL_WEAK void otPlatMdnsSendUnicast(otInstance *aInstance,
otMessage *aMessage,
const otPlatMdnsAddressInfo *aAddress)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aMessage);
OT_UNUSED_VARIABLE(aAddress);
}
#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
#if OPENTHREAD_CONFIG_DNS_DSO_ENABLE
OT_TOOL_WEAK void otPlatDsoEnableListening(otInstance *aInstance, bool aEnable)
+1
View File
@@ -38,6 +38,7 @@
#include <openthread/platform/dso_transport.h>
#include <openthread/platform/entropy.h>
#include <openthread/platform/logging.h>
#include <openthread/platform/mdns_socket.h>
#include <openthread/platform/misc.h>
#include <openthread/platform/multipan.h>
#include <openthread/platform/radio.h>