Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions mdns_cpp/include/mdns_cpp/mdns.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ class mDNS {
void setServiceIP(std::uint32_t ip);
std::uint32_t getServiceIP();

// Restricts outgoing queries to the interface with this IPv4 address (network byte order).
// 0 (the default) queries on every non-loopback interface.
void setQueryInterface(std::uint32_t ip);
std::uint32_t getQueryInterface();

std::string getServiceIPPort(); // IP:Port

void setServiceName(const std::string &name);
Expand All @@ -91,6 +96,9 @@ class mDNS {
private:
void runMainLoop();
int openClientSockets(int *sockets, int max_sockets, int port);
// [filter_ipv4] == 0 opens sockets on every interface, otherwise only on the matching one.
// Interface enumeration into [fInterfaces] always covers every interface.
int openClientSocketsFiltered(int *sockets, int max_sockets, int port, std::uint32_t filter_ipv4);
int openServiceSockets(int *sockets, int max_sockets);

std::string hostname_{"dummy-host"};
Expand All @@ -104,6 +112,7 @@ class mDNS {
bool has_ipv6_{false};

uint32_t service_address_ipv4_{0};
uint32_t query_interface_ipv4_{0};
std::vector<std::pair<std::string, uint32_t>> fInterfaces;
uint8_t service_address_ipv6_[16]{0};

Expand Down
33 changes: 27 additions & 6 deletions mdns_cpp/src/mdns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,14 @@ int mDNS::openServiceSockets(int *sockets, int max_sockets) {
return num_sockets;
}

int mDNS::openClientSockets(int *sockets, int max_sockets, int port) {
int mDNS::openClientSocketsFiltered(int *sockets, int max_sockets, int port, std::uint32_t filter_ipv4) {
// When sending, each socket can only send to one network interface
// Thus we need to open one socket for each interface and address family
int num_sockets = 0;

// Every call re-enumerates all interfaces, so start from a clean list
fInterfaces.clear();

#ifdef _WIN32

IP_ADAPTER_ADDRESSES *adapter_address = nullptr;
Expand Down Expand Up @@ -113,7 +116,8 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) {


has_ipv4_ = 1;
if (num_sockets < max_sockets) {
const bool interface_selected = (filter_ipv4 == 0) || (saddr->sin_addr.s_addr == filter_ipv4);
if (interface_selected && num_sockets < max_sockets) {
saddr->sin_port = htons((unsigned short)port);
int sock = mdns_socket_open_ipv4(saddr);
if (sock >= 0) {
Expand Down Expand Up @@ -142,7 +146,7 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) {
log_addr = 1;
}
has_ipv6_ = 1;
if (num_sockets < max_sockets) {
if ((filter_ipv4 == 0) && num_sockets < max_sockets) {
saddr->sin6_port = htons((unsigned short)port);
int sock = mdns_socket_open_ipv6(saddr);
if (sock >= 0) {
Expand Down Expand Up @@ -185,8 +189,9 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) {
{
int log_addr = 0;
fInterfaces.push_back(std::make_pair(ifa->ifa_name, saddr->sin_addr.s_addr));

if (num_sockets < max_sockets) {

const bool interface_selected = (filter_ipv4 == 0) || (saddr->sin_addr.s_addr == filter_ipv4);
if (interface_selected && num_sockets < max_sockets) {
saddr->sin_port = htons(port);
int sock = mdns_socket_open_ipv4(saddr);
if (sock >= 0) {
Expand All @@ -209,7 +214,7 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) {
log_addr = 1;
}
has_ipv6_ = 1;
if (num_sockets < max_sockets) {
if ((filter_ipv4 == 0) && num_sockets < max_sockets) {
saddr->sin6_port = htons(port);
int sock = mdns_socket_open_ipv6(saddr);
if (sock >= 0) {
Expand All @@ -235,6 +240,19 @@ int mDNS::openClientSockets(int *sockets, int max_sockets, int port) {
return num_sockets;
}

int mDNS::openClientSockets(int *sockets, int max_sockets, int port) {
int num_sockets = openClientSocketsFiltered(sockets, max_sockets, port, query_interface_ipv4_);

if ((query_interface_ipv4_ != 0) && (max_sockets > 0) && (num_sockets == 0)) {
// The selected interface is gone (unplugged, new DHCP lease, ...).
// Fall back to all interfaces instead of silently discovering nothing.
MDNS_LOG << "Selected network interface is not available, falling back to all interfaces\n";
num_sockets = openClientSocketsFiltered(sockets, max_sockets, port, 0);
}

return num_sockets;
}


static int query_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns_entry_type_t entry,
uint16_t query_id, uint16_t rtype, uint16_t rclass, uint32_t ttl, const void *data,
Expand Down Expand Up @@ -386,6 +404,9 @@ std::uint16_t mDNS::getServicePort() { return port_; }
void mDNS::setServiceIP(std::uint32_t ip) { service_address_ipv4_ = ip; has_ipv4_ = true; }
std::uint32_t mDNS::getServiceIP() { return service_address_ipv4_; }

void mDNS::setQueryInterface(std::uint32_t ip) { query_interface_ipv4_ = ip; }
std::uint32_t mDNS::getQueryInterface() { return query_interface_ipv4_; }

std::string mDNS::getServiceIPPort()
{
char buffer[64];
Expand Down
5 changes: 5 additions & 0 deletions src/Include/IMediaRessourceVectorInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -2062,6 +2062,11 @@ class DYNAMIC_ATTRIBUTE IGdtfMacro : public IVWUnknown
*/
virtual VCOMError VCOM_CALLTYPE QueryLocalServices(size_t& out_Count) = 0;
virtual VCOMError VCOM_CALLTYPE GetLocalServiceAt(size_t index, ConnectToLocalServiceArgs& outLocalService) = 0;

using NetworkInterface = std::pair<std::string, uint32_t>;
// Allows the user to query all available network interfaces on the system, and set one of them for the MVR-xchange service to use
virtual VCOMError VCOM_CALLTYPE QueryAllAvailableInterfaces( std::vector<NetworkInterface>& out ) = 0;
virtual VCOMError VCOM_CALLTYPE SetNetworkInterface( const NetworkInterface& inInterface ) = 0;

typedef IMVRxchangeMessage (*IMVRxchangeIncomingMessage)(const IMVRxchangeMessage& args, void* context);
typedef void (*IMVRxchangeReturningMessage)(const IMVRxchangeMessage& outgoingMsg, const IMVRxchangeMessage& returningMsg, void* context);
Expand Down
141 changes: 131 additions & 10 deletions src/mvrxchange/CMVRxchangeService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <unordered_map>
#include <thread>
#include <regex>
#include <cstring>

VectorworksMVR::CMVRxchangeServiceImpl::CMVRxchangeServiceImpl(): fmdns_long_timer(fmdns_IO_Context)
{
Expand Down Expand Up @@ -43,6 +44,61 @@ void FilterServiceName(MVRxchangeString& service)
}


// Parses a dotted quad (an optional ":port" suffix is ignored) into its four octets
static bool ParseIPv4(const std::string& address, std::uint8_t (&outOctets)[4])
{
const std::string ip = address.substr(0, address.find(':'));

size_t start = 0;
for (size_t i = 0; i < 4; i++)
{
const size_t end = (i == 3) ? ip.size() : ip.find('.', start);
if (end == std::string::npos || end == start)
{
return false;
}

const std::string octet = ip.substr(start, end - start);
if (octet.find_first_not_of("0123456789") != std::string::npos)
{
return false;
}

const unsigned long value = std::stoul(octet);
if (value > 255)
{
return false;
}

outOctets[i] = (std::uint8_t)value;
start = end + 1;
}

return true;
}

// mdns_cpp reports the address of an interface but not its prefix length, so the network is
// approximated with classful rules (/8, /16, /24). Hosts outside the network of the selected
// interface are not reachable through it, so their announcements are dropped.
static bool IsAddressOnInterface(std::uint32_t interfaceIp, const std::string& address)
{
std::uint8_t interfaceOctets[4];
std::memcpy(interfaceOctets, &interfaceIp, sizeof(interfaceOctets)); // stored in network byte order, so octet order matches

std::uint8_t addressOctets[4];
if (!ParseIPv4(address, addressOctets))
{
return false;
}

size_t compare = 3;
if (interfaceOctets[0] < 128) { compare = 1; }
else if (interfaceOctets[0] < 192) { compare = 2; }

return std::equal(interfaceOctets, interfaceOctets + compare, addressOctets);
}


VCOMError VectorworksMVR::CMVRxchangeServiceImpl::ConnectToLocalService(const ConnectToLocalServiceArgs& service)
{
this->LeaveLocalService();
Expand All @@ -68,23 +124,38 @@ VCOMError VectorworksMVR::CMVRxchangeServiceImpl::ConnectToLocalService(const Co
txt += (uint8_t)txt2.size();
txt += txt2;

for(std::pair<std::string, uint32_t> e : mdns_cpp::mDNS().getInterfaces())
const NetworkInterface selectedInterface = GetSelectedNetworkInterface();
if ( !selectedInterface.first.empty() || selectedInterface.second != 0 )
{
// Bitmasking IP Address to check if it is 127.x.x.x
// We dont want to start the mDNS Server on loopback addresses
// If two programs on the same device want to connect, they can use one of the other interfaces as well
if((e.second & 4278190080) == 2130706432) {
continue;
}

mdns_cpp::mDNS* s = new mdns_cpp::mDNS();
s->setServiceHostname(std::string(fCurrentService.Service.fBuffer));
s->setServicePort(fServer->GetPort());
s->setServiceIP(e.second);
s->setServiceIP(selectedInterface.second);
s->setServiceName(MVRXChange_Service);
s->setServiceTxtRecord(txt);
s->startService();
fmdns.emplace_back(s); // Pointer is now managed by the unique ptr and deleted upon fmdns going out of scope
fmdns.emplace_back(s);
}
else
{
for(NetworkInterface e : mdns_cpp::mDNS().getInterfaces())
{
// Bitmasking IP Address to check if it is 127.x.x.x
// We dont want to start the mDNS Server on loopback addresses
// If two programs on the same device want to connect, they can use one of the other interfaces as well
if((e.second & 4278190080) == 2130706432) {
continue;
}

mdns_cpp::mDNS* s = new mdns_cpp::mDNS();
s->setServiceHostname(std::string(fCurrentService.Service.fBuffer));
s->setServicePort(fServer->GetPort());
s->setServiceIP(e.second);
s->setServiceName(MVRXChange_Service);
s->setServiceTxtRecord(txt);
s->startService();
fmdns.emplace_back(s); // Pointer is now managed by the unique ptr and deleted upon fmdns going out of scope
}
}

bool doInit = false; // avoid deadlock with temp variable
Expand Down Expand Up @@ -277,6 +348,34 @@ VCOMError VectorworksMVR::CMVRxchangeServiceImpl::Send_message(const SendMessage
return kVCOMError_NoError;
}

VCOMError VectorworksMVR::CMVRxchangeServiceImpl::QueryAllAvailableInterfaces(std::vector<NetworkInterface>& out)
{
out.clear();
for(NetworkInterface e : mdns_cpp::mDNS().getInterfaces())
{
// remove loopback addresses (127.x.x.x) from the list of available interfaces, as they are not useful for mDNS service discovery in a local network context
if((e.second & 4278190080) == 2130706432) {
continue;
}
out.push_back(e);
}

return kVCOMError_NoError;
}

VCOMError VectorworksMVR::CMVRxchangeServiceImpl::SetNetworkInterface( const NetworkInterface& interface )
{
std::lock_guard<std::mutex> lock(fNetworkInterfaceMutex);
fNetworkInterface = interface;
return kVCOMError_NoError;
}

NetworkInterface VectorworksMVR::CMVRxchangeServiceImpl::GetSelectedNetworkInterface()
{
std::lock_guard<std::mutex> lock(fNetworkInterfaceMutex);
return fNetworkInterface;
}

//---------------------------------------------------------------------------
// TCP Server - Local Network mode

Expand Down Expand Up @@ -482,6 +581,9 @@ mdns_cpp::QueryResList CMVRxchangeServiceImpl::mDNS_Filter_Queries(mdns_cpp::Que

std::string serviceAsString(MVRXChange_Service);

const NetworkInterface selectedInterface = GetSelectedNetworkInterface();
const bool hasInterfaceFilter = !selectedInterface.first.empty() || selectedInterface.second != 0;

for (auto &i : input)
{
// filter multiple
Expand Down Expand Up @@ -531,6 +633,22 @@ mdns_cpp::QueryResList CMVRxchangeServiceImpl::mDNS_Filter_Queries(mdns_cpp::Que
continue;
}

if (hasInterfaceFilter)
{
bool reachable = IsAddressOnInterface(selectedInterface.second, i.mdnsAddress);

for (auto it = i.ipV4_address.begin(); !reachable && it != i.ipV4_address.end(); ++it)
{
reachable = IsAddressOnInterface(selectedInterface.second, *it);
}

if (!reachable)
{
// not on the network of the selected interface
continue;
}
}

out.push_back(std::move(i));
}

Expand All @@ -539,7 +657,10 @@ mdns_cpp::QueryResList CMVRxchangeServiceImpl::mDNS_Filter_Queries(mdns_cpp::Que

void CMVRxchangeServiceImpl::mDNS_Client_Task()
{
const NetworkInterface selectedInterface = GetSelectedNetworkInterface();

mdns_cpp::mDNS mdns;
mdns.setQueryInterface(selectedInterface.second);
auto query_res = mdns.executeQuery2(MVRXChange_Service);
std::vector<ConnectToLocalServiceArgs> result;

Expand Down
16 changes: 16 additions & 0 deletions src/mvrxchange/CMVRxchangeService.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace VectorworksMVR
};

using TCPMessageInfo = MVRxchangeNetwork::TCPMessageInfo;
using NetworkInterface = std::pair<std::string, uint32_t>;

//----------------------------------------------------------------------------------------
class CMVRxchangeServiceImpl : public VCOMImpl<IMVRxchangeService>
Expand All @@ -43,6 +44,14 @@ namespace VectorworksMVR
virtual VCOMError VCOM_CALLTYPE OnMessage(OnMessageArgs& messageHandler);
virtual VCOMError VCOM_CALLTYPE Send_message(const SendMessageArgs& messageHandler);

virtual VCOMError VCOM_CALLTYPE QueryAllAvailableInterfaces( std::vector<NetworkInterface>& out );

// Restricts both the advertised service and the service discovery to [interface].
// An unset (default constructed) interface means all non-loopback interfaces.
// The value is applied on the next ConnectToLocalService / QueryLocalServices;
// a running discovery loop is not re-bound.
virtual VCOMError VCOM_CALLTYPE SetNetworkInterface( const NetworkInterface& interface );

private:
void mDNS_Client_Task(); // actual mdns task

Expand All @@ -67,6 +76,12 @@ namespace VectorworksMVR
MVRxchangeNetwork::MVRxchangeServer* fServer;
ConnectToLocalServiceArgs fCurrentService;

std::mutex fNetworkInterfaceMutex;
NetworkInterface fNetworkInterface;

// Thread safe snapshot of [fNetworkInterface]; an unset interface is returned as an empty pair
NetworkInterface GetSelectedNetworkInterface();

void TCP_Start();
void TCP_Stop();
public:
Expand All @@ -75,6 +90,7 @@ namespace VectorworksMVR

std::mutex fMvrGroupMutex;
std::vector<MVRxchangeGroupMember> fMVRGroup;

private:

OnMessageArgs fCallBack;
Expand Down
Empty file.
Loading