Skip to content

Commit

Permalink
Merge pull request #258 from microsoft/svcdiscoveryserver
Browse files Browse the repository at this point in the history
Add network operations for registering DNS-SD service on Linux
  • Loading branch information
abeltrano authored May 8, 2024
2 parents ce20f83 + 7dafd55 commit a2cce40
Show file tree
Hide file tree
Showing 27 changed files with 1,211 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,43 @@ struct NetRemoteProtocol
#define xstr(s) str(s)
#define str(s) #s

/**
* @brief Primary service related definitions.
*/

/**
* @brief Default port if none specified.
*/
static constexpr uint32_t PortDefault{ PORT_DEFAULT };

/**
* @brief Port separator for address parsing.
*/
static constexpr std::string_view PortSeparator{ PORT_SEPARATOR };

/**
* @brief Default IP address if none specified.
*/
static constexpr std::string_view IpDefault{ IP_DEFAULT };

/**
* @brief Default address if none specified; includes default port.
*/
static constexpr std::string_view AddressDefault{ IP_DEFAULT PORT_SEPARATOR xstr(PORT_DEFAULT) };

/**
* @brief Discovery service related definitions.
*/

static constexpr auto DnssdServiceName = "netremote";

/**
* @brief Note that all non-TCP protocols must use "udp" as the protocol value, even if the protocol is not "udp".
* See RFC 6763 "DNS-Based Service Discovery", section 4.1.2 "Service Names" and and section 7 "Service Names" for
* additional details and reasoning.
*/
static constexpr auto DnssdServiceProtocol = "udp";

#undef IP_DEFAULT
#undef PORT_DEFAULT
#undef PORT_SEPARATOR
Expand Down
1 change: 1 addition & 0 deletions src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

add_subdirectory(client)
add_subdirectory(dotnet)
add_subdirectory(net)
add_subdirectory(server)
add_subdirectory(service)
add_subdirectory(shared)
Expand Down
23 changes: 23 additions & 0 deletions src/common/net/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

add_library(${PROJECT_NAME}-net INTERFACE "")

set(NET_REMOTE_NET_PUBLIC_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/include)
set(NET_REMOTE_NET_PUBLIC_INCLUDE_SUFFIX microsoft/net)
set(NET_REMOTE_NET_PUBLIC_INCLUDE_PREFIX ${NET_REMOTE_NET_PUBLIC_INCLUDE}/${NET_REMOTE_NET_PUBLIC_INCLUDE_SUFFIX})

target_sources(${PROJECT_NAME}-net
PUBLIC
FILE_SET HEADERS
BASE_DIRS ${NET_REMOTE_NET_PUBLIC_INCLUDE}
FILES
${NET_REMOTE_NET_PUBLIC_INCLUDE_PREFIX}/INetworkOperations.hxx
${NET_REMOTE_NET_PUBLIC_INCLUDE_PREFIX}/IpAddressInformation.hxx
)

install(
TARGETS ${PROJECT_NAME}-net
EXPORT ${PROJECT_NAME}
COMPONENT dev
FILE_SET HEADERS
PUBLIC_HEADER DESTINATION "${NETREMOTE_DIR_INSTALL_PUBLIC_HEADER_BASE}/${NET_REMOTE_NET_PUBLIC_INCLUDE_SUFFIX}"
)
34 changes: 34 additions & 0 deletions src/common/net/include/microsoft/net/INetworkOperations.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

#ifndef NETWORK_OPERATIONS_HXX
#define NETWORK_OPERATIONS_HXX

#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>

#include <microsoft/net/IpAddressInformation.hxx>

namespace Microsoft::Net
{
/**
* @brief Interface for network operations needed by the server.
*/
struct INetworkOperations
{
virtual ~INetworkOperations() = default;

/**
* @brief Obtain information about the specified IP address. The returned map will contain the IP address as the key
* and the information as the value. In the case of a fixed address, the returned map will have a single entry. In
* the case of any "any" address (eg. 0.0.0.0, ::, [::]), the returned map will contain all available addresses.
*
* @param ipAddress The ip address to obtain information for.
* @return std::unordered_map<std::string, IpAddressInformation>
*/
virtual std::unordered_map<std::string, IpAddressInformation>
GetLocalIpAddressInformation(std::string_view ipAddress) const noexcept = 0;
};
} // namespace Microsoft::Net

#endif // NETWORK_OPERATIONS_HXX
73 changes: 73 additions & 0 deletions src/common/net/include/microsoft/net/IpAddressInformation.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

#ifndef IP_ADDRESS_INFORMATION_HXX
#define IP_ADDRESS_INFORMATION_HXX

#include <regex>
#include <string>
#include <string_view>

namespace Microsoft::Net
{
/**
* @brief Determine if a string contains an IPv4 or IPv6 "any" address. The port is optional for both forms, and square
* brackets are optional for the IPv6 form.
*
* @param ipAddressView The view of the string to check.
* @return true If the string contains an IPv4 or IPv6 "any" address.
* @return false If the string does not contain an IPv4 or IPv6 "any" address.
*/
inline bool
IsAnyAddress(std::string_view ipAddressView) noexcept
{
const std::regex RegexIpv4AnyAddressWithPort{ "^0.0.0.0(:\\d+)?$" };
const std::regex RegexIpv6AnyAddressWithPort{ "^\\[?::\\]?(:\\d+)?$" };
const std::string ipAddress(ipAddressView);

return std::regex_match(ipAddress, RegexIpv4AnyAddressWithPort) || std::regex_match(ipAddress, RegexIpv6AnyAddressWithPort);
}

/**
* @brief The type of network interface.
*/
enum class NetworkInterfaceType {
Unknown,
Wifi,
Other,
};

/**
* @brief The IP family of an IP address.
*/
enum class IpFamily {
Unknown,
Ipv4,
Ipv6,
};

/**
* @brief Information about an IP address needed by the server.
*/
struct IpAddressInformation
{
IpFamily Family{ IpFamily::Unknown };
NetworkInterfaceType InterfaceType{ NetworkInterfaceType::Unknown };

auto
operator<=>(const IpAddressInformation&) const = default;
};
} // namespace Microsoft::Net

namespace std
{
template <>
struct hash<Microsoft::Net::IpAddressInformation>
{
std::size_t
operator()(const Microsoft::Net::IpAddressInformation& ipInfo) const noexcept
{
return std::hash<int>{}(static_cast<int>(ipInfo.Family)) ^ std::hash<int>{}(static_cast<int>(ipInfo.InterfaceType));
}
};
} // namespace std

#endif // IP_ADDRESS_INFORMATION_HXX
5 changes: 5 additions & 0 deletions src/common/server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,23 @@ set(NET_REMOTE_SERVER_PUBLIC_INCLUDE_PREFIX ${NET_REMOTE_SERVER_PUBLIC_INCLUDE}/

target_sources(${PROJECT_NAME}-server
PRIVATE
NetRemoteDiscoveryDnssd.cxx
NetRemoteDiscoveryService.cxx
NetRemoteServer.cxx
NetRemoteServerConfiguration.cxx
PUBLIC
FILE_SET HEADERS
BASE_DIRS ${NET_REMOTE_SERVER_PUBLIC_INCLUDE}
FILES
${NET_REMOTE_SERVER_PUBLIC_INCLUDE_PREFIX}/NetRemoteDiscoveryDnssd.hxx
${NET_REMOTE_SERVER_PUBLIC_INCLUDE_PREFIX}/NetRemoteDiscoveryService.hxx
${NET_REMOTE_SERVER_PUBLIC_INCLUDE_PREFIX}/NetRemoteServer.hxx
${NET_REMOTE_SERVER_PUBLIC_INCLUDE_PREFIX}/NetRemoteServerConfiguration.hxx
)

target_link_libraries(${PROJECT_NAME}-server
PUBLIC
${PROJECT_NAME}-net
${PROJECT_NAME}-service
PRIVATE
CLI11::CLI11
Expand Down
15 changes: 15 additions & 0 deletions src/common/server/NetRemoteDiscoveryDnssd.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

#ifndef NET_REMOTE_DISCOVERY_DNSSD_HXX
#define NET_REMOTE_DISCOVERY_DNSSD_HXX

#include <string>

#include <microsoft/net/remote/NetRemoteDiscoveryService.hxx>

namespace Microsoft::Net::Remote
{
std::string
BuildDnssdTxtRecord();
} // namespace Microsoft::Net::Remote

#endif // NET_REMOTE_DISCOVERY_DNSSD_HXX
80 changes: 80 additions & 0 deletions src/common/server/NetRemoteDiscoveryService.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@

#include <cstdint>
#include <format>
#include <iterator>
#include <memory>
#include <stdexcept>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>

#include <microsoft/net/INetworkOperations.hxx>
#include <microsoft/net/IpAddressInformation.hxx>
#include <microsoft/net/remote/NetRemoteDiscoveryService.hxx>

using namespace Microsoft::Net::Remote;
using Microsoft::Net::INetworkOperations;
using Microsoft::Net::IpAddressInformation;

NetRemoteDiscoveryService::NetRemoteDiscoveryService(std::string hostname, uint32_t port, std::unordered_map<std::string, IpAddressInformation> ipAddresses) :
m_hostname(std::move(hostname)),
m_port(port),
m_ipAddresses(std::move(ipAddresses))
{}

std::string_view
NetRemoteDiscoveryService::GetHostname() const noexcept
{
return m_hostname;
}

uint32_t
NetRemoteDiscoveryService::GetPort() const noexcept
{
return m_port;
}

const std::unordered_map<std::string, Microsoft::Net::IpAddressInformation>&
NetRemoteDiscoveryService::GetIpAddresses() const noexcept
{
return m_ipAddresses;
}

NetRemoteDiscoveryServiceBuilder::NetRemoteDiscoveryServiceBuilder(std::unique_ptr<INetRemoteDiscoveryServiceFactory> discoveryServiceFactory, std::unique_ptr<INetworkOperations> networkOperations) :
m_discoveryServiceFactory(std::move(discoveryServiceFactory)),
m_networkOperations(std::move(networkOperations))
{}

NetRemoteDiscoveryServiceBuilder&
NetRemoteDiscoveryServiceBuilder::SetHostname(std::string hostname)
{
m_hostname = std::move(hostname);
return *this;
}

NetRemoteDiscoveryServiceBuilder&
NetRemoteDiscoveryServiceBuilder::SetPort(uint32_t port)
{
m_port = port;
return *this;
}

NetRemoteDiscoveryServiceBuilder&
NetRemoteDiscoveryServiceBuilder::AddIpAddress(std::string ipAddress)
{
auto ipAddressInfo = m_networkOperations->GetLocalIpAddressInformation(ipAddress);
if (std::empty(ipAddressInfo)) {
throw std::invalid_argument(std::format("Invalid IP address: {}", ipAddress));
}

m_ipAddresses.merge(ipAddressInfo);

return *this;
}

std::shared_ptr<NetRemoteDiscoveryService>
NetRemoteDiscoveryServiceBuilder::Build()
{
return m_discoveryServiceFactory->Create(m_hostname, m_port, m_ipAddresses);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

#ifndef NET_REMOTE_DISCOVERY_DNSSD_HXX
#define NET_REMOTE_DISCOVERY_DNSSD_HXX

#include <microsoft/net/remote/NetRemoteDiscoveryService.hxx>

namespace Microsoft::Net::Remote
{
struct DnssdServiceBuilder
{
};
} // namespace Microsoft::Net::Remote

#endif // NET_REMOTE_DISCOVERY_DNSSD_HXX
Loading

0 comments on commit a2cce40

Please sign in to comment.