Skip to content
111 changes: 111 additions & 0 deletions core/include/join/protocol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ namespace join

template <class Protocol>
class BasicDatagramNameServer;
template <class Protocol>
class BasicDatagramPeer;

template <class Protocol>
class BasicHttpClient;
Expand Down Expand Up @@ -779,6 +781,115 @@ namespace join
return !(a == b);
}

/**
* @brief Multicast DNS protocol class
*/
class Mdns
{
public:
using Endpoint = BasicInternetEndpoint<Mdns>;
using Socket = BasicDatagramSocket<Mdns>;
using Peer = BasicDatagramPeer<Mdns>;

/**
* @brief construct the mDNS protocol instance.
* @param family IP address family.
*/
constexpr Mdns (int family = AF_INET) noexcept
: _family (family)
{
}

/**
* @brief get protocol suitable for IPv4 address family.
* @return an IPv4 address family suitable protocol.
*/
static inline Mdns& v4 () noexcept
{
static Mdns mdnsv4 (AF_INET);
return mdnsv4;
}

/**
* @brief get protocol suitable for IPv6 address family.
* @return an IPv6 address family suitable protocol.
*/
static inline Mdns& v6 () noexcept
{
static Mdns mdnsv6 (AF_INET6);
return mdnsv6;
}

/**
* @brief get the protocol IP address family.
* @return the protocol IP address family.
*/
constexpr int family () const noexcept
{
return _family;
}

/**
* @brief get the protocol communication semantic.
* @return the protocol communication semantic.
*/
constexpr int type () const noexcept
{
return SOCK_DGRAM;
}

/**
* @brief get the protocol type.
* @return the protocol type.
*/
constexpr int protocol () const noexcept
{
return IPPROTO_UDP;
}

/**
* @brief get multicast address for the given address family.
* @param family IP address family.
* @return multicast IP address.
*/
static IpAddress multicastAddress (int family) noexcept
{
return (family == AF_INET6) ? "ff02::fb" : "224.0.0.251";
}

/// default DNS port.
static constexpr uint16_t defaultPort = 5353;

/// maximum DNS message size.
static constexpr size_t maxMsgSize = 8192;

private:
/// IP address family.
int _family;
};

/**
* @brief check if equals.
* @param a protocol to check.
* @param b protocol to check.
* @return true if equals.
*/
constexpr bool operator== (const Mdns& a, const Mdns& b) noexcept
{
return a.family () == b.family ();
}

/**
* @brief check if not equals.
* @param a protocol to check.
* @param b protocol to check.
* @return true if not equals.
*/
constexpr bool operator!= (const Mdns& a, const Mdns& b) noexcept
{
return !(a == b);
}

/**
* @brief DNS over TLS protocol class.
*/
Expand Down
11 changes: 11 additions & 0 deletions core/tests/protocol_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ using join::Icmp;
using join::Tcp;
using join::Tls;
using join::Dns;
using join::Mdns;
using join::Dot;
using join::Http;
using join::Https;
Expand Down Expand Up @@ -66,6 +67,9 @@ TEST (Protocol, family)
ASSERT_EQ (Dns ().family (), AF_INET);
ASSERT_EQ (Dns::v6 ().family (), AF_INET6);
ASSERT_EQ (Dns::v4 ().family (), AF_INET);
ASSERT_EQ (Mdns ().family (), AF_INET);
ASSERT_EQ (Mdns::v6 ().family (), AF_INET6);
ASSERT_EQ (Mdns::v4 ().family (), AF_INET);
ASSERT_EQ (Dot ().family (), AF_INET);
ASSERT_EQ (Dot::v6 ().family (), AF_INET6);
ASSERT_EQ (Dot::v4 ().family (), AF_INET);
Expand Down Expand Up @@ -97,6 +101,7 @@ TEST (Protocol, type)
ASSERT_EQ (Tcp ().type (), SOCK_STREAM);
ASSERT_EQ (Tls ().type (), SOCK_STREAM);
ASSERT_EQ (Dns ().type (), SOCK_DGRAM);
ASSERT_EQ (Mdns ().type (), SOCK_DGRAM);
ASSERT_EQ (Dot ().type (), SOCK_STREAM);
ASSERT_EQ (Http ().type (), SOCK_STREAM);
ASSERT_EQ (Https ().type (), SOCK_STREAM);
Expand All @@ -119,6 +124,7 @@ TEST (Protocol, protocol)
ASSERT_EQ (Tcp ().protocol (), IPPROTO_TCP);
ASSERT_EQ (Tls ().protocol (), IPPROTO_TCP);
ASSERT_EQ (Dns ().protocol (), IPPROTO_UDP);
ASSERT_EQ (Mdns ().protocol (), IPPROTO_UDP);
ASSERT_EQ (Dot ().protocol (), IPPROTO_TCP);
ASSERT_EQ (Http ().protocol (), IPPROTO_TCP);
ASSERT_EQ (Https ().protocol (), IPPROTO_TCP);
Expand Down Expand Up @@ -159,6 +165,11 @@ TEST (Protocol, equal)
ASSERT_EQ (Dns::v6 (), Dns::v6 ());
ASSERT_NE (Dns::v6 (), Dns::v4 ());

ASSERT_EQ (Mdns::v4 (), Mdns::v4 ());
ASSERT_NE (Mdns::v4 (), Mdns::v6 ());
ASSERT_EQ (Mdns::v6 (), Mdns::v6 ());
ASSERT_NE (Mdns::v6 (), Mdns::v4 ());

ASSERT_EQ (Dot::v4 (), Dot::v4 ());
ASSERT_NE (Dot::v4 (), Dot::v6 ());
ASSERT_EQ (Dot::v6 (), Dot::v6 ());
Expand Down
66 changes: 31 additions & 35 deletions fabric/include/join/arp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,17 +204,45 @@ namespace join

ScopedLock<Mutex> lock (_syncMutex);

_reactor->addHandler (handle (), this);

uint32_t tip;
::memcpy (&tip, &out.arp.ar_tip, sizeof (tip));
auto inserted = _pending.emplace (tip, std::make_unique<PendingRequest> ());
if (!inserted.second)
{
// LCOV_EXCL_START
_reactor->delHandler (handle ());
close ();
lastError = make_error_code (Errc::OperationFailed);
return {};
// LCOV_EXCL_STOP
}

if (write (reinterpret_cast<const char*> (&out), sizeof (Packet)) == -1)
{
// LCOV_EXCL_START
_pending.erase (inserted.first);
_reactor->delHandler (handle ());
close ();
return {};
// LCOV_EXCL_STOP
}

if (!inserted.first->second->cond.timedWait (lock, timeout))
{
_pending.erase (inserted.first);
_reactor->delHandler (handle ());
close ();
lastError = std::make_error_code (std::errc::no_such_device_or_address);
return {};
}

_reactor->addHandler (handle (), this);
MacAddress mac = waitResponse (lock, out.arp.ar_tip, timeout);
MacAddress mac = inserted.first->second->mac;
_pending.erase (inserted.first);
_reactor->delHandler (handle ());

close ();

return mac;
}

Expand Down Expand Up @@ -326,38 +354,6 @@ namespace join
ArpPacket arp;
};

/**
* @brief wait for ARP response.
* @param lock mutex previously locked by the calling thread.
* @param tip target IP.
* @param timeout wait timeout.
* @return the MAC address.
*/
template <typename Rep, typename Period>
MacAddress waitResponse (ScopedLock<Mutex>& lock, uint32_t tip, std::chrono::duration<Rep, Period> timeout)
{
auto inserted = _pending.emplace (tip, std::make_unique<PendingRequest> ());
if (!inserted.second)
{
// LCOV_EXCL_START
lastError = make_error_code (Errc::OperationFailed);
return {};
// LCOV_EXCL_STOP
}

if (!inserted.first->second->cond.timedWait (lock, timeout))
{
_pending.erase (inserted.first);
lastError = std::make_error_code (std::errc::no_such_device_or_address);
return {};
}

MacAddress mac = inserted.first->second->mac;
_pending.erase (inserted.first);

return mac;
}

/**
* @brief method called when data are ready to be read.
* @param fd file descriptor.
Expand Down
Loading
Loading