diff --git a/internal/platform/implementation/windows/utils.cc b/internal/platform/implementation/windows/utils.cc index 1ab10670..4d57d2ab 100644 --- a/internal/platform/implementation/windows/utils.cc +++ b/internal/platform/implementation/windows/utils.cc @@ -26,6 +26,7 @@ #include #include #include +#include // Third party headers #include "absl/strings/ascii.h" @@ -36,10 +37,18 @@ #include "internal/platform/bluetooth_utils.h" #include "internal/platform/byte_array.h" #include "internal/platform/implementation/crypto.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Connectivity.h" namespace location { namespace nearby { namespace windows { +namespace { + +using ::winrt::Windows::Networking::HostNameType; +using ::winrt::Windows::Networking::Connectivity::NetworkInformation; + +} // namespace std::string uint64_to_mac_address_string(uint64_t bluetoothAddress) { std::string buffer = absl::StrFormat( @@ -100,6 +109,20 @@ std::string wstring_to_string(std::wstring wstr) { return converter.to_bytes(wstr); } +std::vector GetIpv4Addresses() { + std::vector result; + auto host_names = NetworkInformation::GetHostNames(); + for (const auto& host_name : host_names) { + if (host_name.IPInformation() != nullptr && + host_name.IPInformation().NetworkAdapter() != nullptr && + host_name.Type() == HostNameType::Ipv4) { + result.push_back(winrt::to_string(host_name.ToString())); + } + } + + return result; +} + ByteArray Sha256(absl::string_view input, size_t size) { ByteArray hash = location::nearby::Crypto::Sha256(input); return ByteArray{hash.data(), size}; diff --git a/internal/platform/implementation/windows/utils.h b/internal/platform/implementation/windows/utils.h index 5e7a2c8d..7abe01ad 100644 --- a/internal/platform/implementation/windows/utils.h +++ b/internal/platform/implementation/windows/utils.h @@ -44,6 +44,9 @@ std::wstring string_to_wstring(std::string str); std::string wstring_to_string(std::wstring wstr); ByteArray Sha256(absl::string_view input, size_t size); +// Reads the IPv4 addresses +std::vector GetIpv4Addresses(); + namespace Constants { // The Id of the Service Name SDP attribute const uint16_t SdpServiceNameAttributeId = 0x100; diff --git a/internal/platform/implementation/windows/wifi_lan.h b/internal/platform/implementation/windows/wifi_lan.h index 20a70b37..ac27bc9f 100644 --- a/internal/platform/implementation/windows/wifi_lan.h +++ b/internal/platform/implementation/windows/wifi_lan.h @@ -258,18 +258,6 @@ class WifiLanMedium : public api::WifiLanMedium { } private: - // mDNS text attributes - static constexpr std::string_view KEY_ENDPOINT_INFO = "n"; - - // mDNS information for advertising and discovery - static constexpr std::wstring_view MDNS_HOST_NAME = L"Windows.local"; - static constexpr std::string_view MDNS_INSTANCE_NAME_FORMAT = "%s.%slocal"; - static constexpr std::string_view MDNS_DEVICE_SELECTOR_FORMAT = - "System.Devices.AepService.ProtocolId:=\"{4526e8c1-8aac-4153-9b16-" - "55e86ada0e54}\" " - "AND System.Devices.Dnssd.ServiceName:=\"%s\" AND " - "System.Devices.Dnssd.Domain:=\"local\""; - // Nsd status static const int MEDIUM_STATUS_IDLE = 0; static const int MEDIUM_STATUS_ACCEPTING = (1 << 0); @@ -340,6 +328,9 @@ class WifiLanMedium : public api::WifiLanMedium { // Keep the server socket listener pointer WifiLanServerSocket* server_socket_ptr_ = nullptr; + + // Used to keep the service name is advertising. + std::string service_name_; }; } // namespace windows diff --git a/internal/platform/implementation/windows/wifi_lan_medium.cc b/internal/platform/implementation/windows/wifi_lan_medium.cc index b480c020..00f5c78b 100644 --- a/internal/platform/implementation/windows/wifi_lan_medium.cc +++ b/internal/platform/implementation/windows/wifi_lan_medium.cc @@ -22,19 +22,37 @@ #include #include #include +#include +#include // ABSL headers #include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" // Nearby connections headers #include "internal/platform/cancellation_flag_listener.h" +#include "internal/platform/exception.h" #include "internal/platform/implementation/windows/utils.h" #include "internal/platform/logging.h" -#include "internal/platform/exception.h" namespace location { namespace nearby { namespace windows { +namespace { +// mDNS text attributes +constexpr absl::string_view kDeviceEndpointInfo = "n"; +constexpr absl::string_view kDeviceIpv4 = "IPv4"; + +// mDNS information for advertising and discovery +constexpr std::wstring_view kMdnsHostName = L"Windows.local"; +constexpr absl::string_view kMdnsInstanceNameFormat = "%s.%slocal"; +constexpr absl::string_view kMdnsDeviceSelectorFormat = + "System.Devices.AepService.ProtocolId:=\"{4526e8c1-8aac-4153-9b16-" + "55e86ada0e54}\" " + "AND System.Devices.Dnssd.ServiceName:=\"%s\" AND " + "System.Devices.Dnssd.Domain:=\"local\""; + +} // namespace bool WifiLanMedium::IsNetworkConnected() const { // connection_profile will be null when there's no network adapter or @@ -57,7 +75,7 @@ bool WifiLanMedium::StartAdvertising(const NsdServiceInfo& nsd_service_info) { return false; } - if (nsd_service_info.GetTxtRecord(KEY_ENDPOINT_INFO.data()).empty()) { + if (nsd_service_info.GetTxtRecord(kDeviceEndpointInfo.data()).empty()) { NEARBY_LOGS(ERROR) << "cannot start advertising without endpoint info."; return false; } @@ -67,9 +85,11 @@ bool WifiLanMedium::StartAdvertising(const NsdServiceInfo& nsd_service_info) { return false; } - std::string instance_name = absl::StrFormat( - MDNS_INSTANCE_NAME_FORMAT.data(), nsd_service_info.GetServiceName(), - nsd_service_info.GetServiceType()); + service_name_ = nsd_service_info.GetServiceName(); + + std::string instance_name = + absl::StrFormat(kMdnsInstanceNameFormat.data(), service_name_, + nsd_service_info.GetServiceType()); NEARBY_LOGS(INFO) << "mDNS instance name is " << instance_name; @@ -81,7 +101,8 @@ bool WifiLanMedium::StartAdvertising(const NsdServiceInfo& nsd_service_info) { // Add TextRecords from NsdServiceInfo auto text_attributes = dnssd_service_instance_.TextAttributes(); - auto text_records = nsd_service_info.GetTxtRecords(); + absl::flat_hash_map text_records = + nsd_service_info.GetTxtRecords(); auto it = text_records.begin(); while (it != text_records.end()) { text_attributes.Insert(string_to_wstring(it->first), @@ -89,6 +110,16 @@ bool WifiLanMedium::StartAdvertising(const NsdServiceInfo& nsd_service_info) { it++; } + // Add IPv4 address in text attributes. + std::vector ipv4_addresses = GetIpv4Addresses(); + if (!ipv4_addresses.empty()) { + if (ipv4_addresses.size() > 1) { + NEARBY_LOGS(WARNING) << "The device has multiple IPv4 addresses."; + } + text_attributes.Insert(winrt::to_hstring(std::string(kDeviceIpv4)), + winrt::to_hstring(ipv4_addresses[0])); + } + dnssd_regirstraion_result_ = dnssd_service_instance_ .RegisterStreamSocketListenerAsync( server_socket_ptr_->GetSocketListener()) @@ -146,7 +177,7 @@ bool WifiLanMedium::StopAdvertising(const NsdServiceInfo& nsd_service_info) { // Init DNS service instance std::string instance_name = absl::StrFormat( - MDNS_INSTANCE_NAME_FORMAT.data(), nsd_service_info.GetServiceName(), + kMdnsInstanceNameFormat.data(), nsd_service_info.GetServiceName(), nsd_service_info.GetServiceType()); int port = nsd_service_info.GetPort(); dns_service_instance_name_ = @@ -154,7 +185,7 @@ bool WifiLanMedium::StopAdvertising(const NsdServiceInfo& nsd_service_info) { dns_service_instance_.pszInstanceName = (LPWSTR)dns_service_instance_name_->c_str(); - dns_service_instance_.pszHostName = (LPWSTR)MDNS_HOST_NAME.data(); + dns_service_instance_.pszHostName = (LPWSTR)kMdnsHostName.data(); dns_service_instance_.wPort = port; // Init DNS service register request @@ -211,7 +242,7 @@ bool WifiLanMedium::StartDiscovery(const std::string& service_type, } std::string selector = - absl::StrFormat(MDNS_DEVICE_SELECTOR_FORMAT.data(), service_type_trim); + absl::StrFormat(kMdnsDeviceSelectorFormat.data(), service_type_trim); std::vector requestedProperties{ L"System.Devices.IpAddress", @@ -361,7 +392,7 @@ ExceptionOr WifiLanMedium::GetNsdServiceInformation( IMapView properties, bool is_device_found) { NsdServiceInfo nsd_service_info{}; - // Service name information + // Read service name information IInspectable inspectable = properties.TryLookup(L"System.Devices.Dnssd.InstanceName"); if (inspectable == nullptr) { @@ -371,7 +402,7 @@ ExceptionOr WifiLanMedium::GetNsdServiceInformation( } nsd_service_info.SetServiceName(InspectableReader::ReadString(inspectable)); - // Service type information + // Read service type information inspectable = properties.TryLookup(L"System.Devices.Dnssd.ServiceName"); if (inspectable == nullptr) { NEARBY_LOGS(WARNING) @@ -386,56 +417,11 @@ ExceptionOr WifiLanMedium::GetNsdServiceInformation( nsd_service_info.SetServiceType( (InspectableReader::ReadString(inspectable)).append(".")); - if (is_device_found) { - // IP Address information - inspectable = properties.TryLookup(L"System.Devices.IPAddress"); - if (inspectable == nullptr) { - NEARBY_LOGS(WARNING) - << "no IP address information in device information."; - return Exception{Exception::kFailed}; - } - - auto ipaddresses = InspectableReader::ReadStringArray(inspectable); - if (ipaddresses.size() == 0) { - NEARBY_LOGS(WARNING) - << "no IP address information in device information."; - return Exception{Exception::kFailed}; - } - - std::string ip_address; - ip_address.resize(4); - // Gets 4 bytes string - for (std::string& address : ipaddresses) { - uint32_t addr = inet_addr(address.data()); - if (addr == INADDR_NONE) { - continue; - } - - in_addr ipv4_addr; - ipv4_addr.S_un.S_addr = addr; - ip_address[0] = static_cast(ipv4_addr.S_un.S_un_b.s_b1); - ip_address[1] = static_cast(ipv4_addr.S_un.S_un_b.s_b2); - ip_address[2] = static_cast(ipv4_addr.S_un.S_un_b.s_b3); - ip_address[3] = static_cast(ipv4_addr.S_un.S_un_b.s_b4); - break; - } - - // read IP port - inspectable = properties.TryLookup(L"System.Devices.Dnssd.PortNumber"); - if (inspectable == nullptr) { - NEARBY_LOGS(WARNING) << "no IP port information in device information."; - return Exception{Exception::kFailed}; - } - - int port = InspectableReader::ReadUint16(inspectable); - nsd_service_info.SetIPAddress(ip_address); - nsd_service_info.SetPort(port); - } - // read text record + // Read text records inspectable = properties.TryLookup(L"System.Devices.Dnssd.TextAttributes"); if (inspectable == nullptr) { NEARBY_LOGS(WARNING) - << "no text attributes information in device information."; + << "No text attributes information in device information."; return Exception{Exception::kFailed}; } @@ -453,7 +439,64 @@ ExceptionOr WifiLanMedium::GetNsdServiceInformation( nsd_service_info.SetTxtRecord(key, value); } - return ExceptionOr(nsd_service_info); + if (!is_device_found) { + return ExceptionOr(nsd_service_info); + } + + // Read IP Address information + std::string ip_address; + ip_address.resize(4); + + // Find it from text first + std::vector ip_address_candidates; + + std::string ipv4_address = + nsd_service_info.GetTxtRecord(std::string(kDeviceIpv4)); + if (!ipv4_address.empty()) { + ip_address_candidates.push_back(ipv4_address); + } else { + inspectable = properties.TryLookup(L"System.Devices.IPAddress"); + if (inspectable == nullptr) { + NEARBY_LOGS(WARNING) << "No IP address property in device information."; + return Exception{Exception::kFailed}; + } + ip_address_candidates = InspectableReader::ReadStringArray(inspectable); + } + + if (ip_address_candidates.empty()) { + NEARBY_LOGS(WARNING) << "No IP address information in device information."; + return Exception{Exception::kFailed}; + } + + // Gets 4 bytes string + for (std::string& address : ip_address_candidates) { + uint32_t addr = inet_addr(address.data()); + if (addr == INADDR_NONE) { + continue; + } + + in_addr ipv4_addr; + ipv4_addr.S_un.S_addr = addr; + ip_address[0] = static_cast(ipv4_addr.S_un.S_un_b.s_b1); + ip_address[1] = static_cast(ipv4_addr.S_un.S_un_b.s_b2); + ip_address[2] = static_cast(ipv4_addr.S_un.S_un_b.s_b3); + ip_address[3] = static_cast(ipv4_addr.S_un.S_un_b.s_b4); + break; + } + + nsd_service_info.SetIPAddress(ip_address); + + // Read IP port + inspectable = properties.TryLookup(L"System.Devices.Dnssd.PortNumber"); + if (inspectable == nullptr) { + NEARBY_LOGS(WARNING) << "no IP port property in device information."; + return Exception{Exception::kFailed}; + } + + int port = InspectableReader::ReadUint16(inspectable); + nsd_service_info.SetPort(port); + + return ExceptionOr(nsd_service_info); } fire_and_forget WifiLanMedium::Watcher_DeviceAdded( @@ -468,16 +511,28 @@ fire_and_forget WifiLanMedium::Watcher_DeviceAdded( "Don't add WIFI_LAN Medium"; return fire_and_forget{}; } - NsdServiceInfo nsd_service_info = nsd_service_info_except.GetResult(); - NEARBY_LOGS(INFO) << "device added for service name " - << nsd_service_info.GetServiceName(); + NsdServiceInfo nsd_service_info = nsd_service_info_except.GetResult(); std::string endpoint = - nsd_service_info.GetTxtRecord(KEY_ENDPOINT_INFO.data()); + nsd_service_info.GetTxtRecord(kDeviceEndpointInfo.data()); if (endpoint.empty()) { + NEARBY_LOGS(WARNING) << "No endpoint information! " + "Don't add WIFI_LAN Medium"; return fire_and_forget{}; } + // Don't discover itself + if (nsd_service_info.GetServiceName() == service_name_) { + NEARBY_LOGS(WARNING) << "Don't add WIFI_LAN Medium for itself"; + return fire_and_forget{}; + } + + NEARBY_LOGS(INFO) << "device added for service name " + << nsd_service_info.GetServiceName() << ", address: " + << ipaddr_4bytes_to_dotdecimal_string( + nsd_service_info.GetIPAddress()) + << ":" << nsd_service_info.GetPort(); + discovered_service_callback_.service_discovered_cb(nsd_service_info); return fire_and_forget(); @@ -497,8 +552,18 @@ fire_and_forget WifiLanMedium::Watcher_DeviceUpdated( } NsdServiceInfo nsd_service_info = nsd_service_info_except.GetResult(); - NEARBY_LOGS(INFO) << "device updated for service name " - << nsd_service_info.GetServiceName(); + + // Don't discover itself + if (nsd_service_info.GetServiceName() == service_name_) { + NEARBY_LOGS(WARNING) << "Don't update WIFI_LAN Medium for itself"; + return fire_and_forget{}; + } + + NEARBY_LOGS(INFO) << "device updated for service name: " + << nsd_service_info.GetServiceName() << ", address: " + << ipaddr_4bytes_to_dotdecimal_string( + nsd_service_info.GetIPAddress()) + << ":" << nsd_service_info.GetPort(); return fire_and_forget(); } @@ -520,7 +585,7 @@ fire_and_forget WifiLanMedium::Watcher_DeviceRemoved( << nsd_service_info.GetServiceName(); std::string endpoint = - nsd_service_info.GetTxtRecord(KEY_ENDPOINT_INFO.data()); + nsd_service_info.GetTxtRecord(kDeviceEndpointInfo.data()); if (endpoint.empty()) { return fire_and_forget{}; }