Register MDNS service using Win32 API

PiperOrigin-RevId: 714283635
This commit is contained in:
Guogang Li
2025-01-10 17:38:50 -08:00
committed by Copybara-Service
parent 694a722d2c
commit 877a2d3e5b
3 changed files with 281 additions and 0 deletions
@@ -105,6 +105,7 @@ cc_library(
"wifi_hotspot_native.h",
"wifi_intel.h",
"wifi_lan.h",
"wifi_lan_mdns.h",
],
copts = ["-DNO_INTEL_PIE"],
visibility = ["//visibility:private"],
@@ -204,6 +205,7 @@ cc_library(
"wifi_hotspot_server_socket.cc",
"wifi_hotspot_socket.cc",
"wifi_intel.cc",
"wifi_lan_mdns.cc",
"wifi_lan_medium.cc",
"wifi_lan_server_socket.cc",
"wifi_lan_socket.cc",
@@ -0,0 +1,209 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/windows/wifi_lan_mdns.h"
// clang-format off
#include <windows.h>
#include <windns.h>
// clang-format on
#include <cstring>
#include <memory>
#include <optional>
#include <string>
#include "absl/container/flat_hash_map.h"
#include "absl/strings/str_format.h"
#include "absl/synchronization/mutex.h"
#include "absl/synchronization/notification.h"
#include "absl/time/time.h"
#include "internal/platform/implementation/windows/string_utils.h"
#include "internal/platform/logging.h"
namespace nearby::windows {
namespace {
// mDNS information for advertising and discovery
const char kMdnsHostName[] = "%s.local";
const char kMdnsInstanceNameFormat[] = "%s.%slocal";
// Timeout for starting mDNS service
constexpr absl::Duration kDnsServiceTimeout = absl::Seconds(3);
} // namespace
WifiLanMdns::~WifiLanMdns() {
if (is_service_started_) {
StopMdnsService();
}
}
bool WifiLanMdns::StartMdnsService(
const std::string& service_name, const std::string& service_type, int port,
absl::flat_hash_map<std::string, std::string> text_records) {
absl::MutexLock lock(&mutex_);
LOG(INFO) << "StartMdnsService: " << service_name << " " << service_type
<< " " << port;
if (is_service_started_) {
LOG(WARNING) << "The mDNS service is already started.";
return false;
}
memset(&dns_service_instance_, 0, sizeof(dns_service_instance_));
memset(&dns_service_register_request_, 0,
sizeof(dns_service_register_request_));
// Composite the service request.
std::string instance_name =
absl::StrFormat(kMdnsInstanceNameFormat, service_name, service_type);
dns_service_instance_name_ = std::make_unique<std::wstring>(
string_utils::StringToWideString(instance_name));
std::optional<std::string> computer_name = GetComputerName();
if (!computer_name.has_value()) {
LOG(ERROR) << "Failed to get computer name.";
return false;
}
std::string host_name = absl::StrFormat(kMdnsHostName, *computer_name);
host_name_ = std::make_unique<std::wstring>(
string_utils::StringToWideString(host_name));
dns_service_instance_.pszInstanceName =
(LPWSTR)dns_service_instance_name_->c_str();
dns_service_instance_.pszHostName = (LPWSTR)host_name_->c_str();
dns_service_instance_.wPort = port;
// Allocate memory for filling text records, it should be freed in
// stopping mDNS service.
if (!text_records.empty()) {
dns_service_instance_.dwPropertyCount = text_records.size();
for (const auto& [key, value] : text_records) {
text_keys_.push_back(string_utils::StringToWideString(key));
text_values_.push_back(string_utils::StringToWideString(value));
}
keys_ = new PWSTR[text_records.size()];
values_ = new PWSTR[text_records.size()];
for (int i = 0; i < text_records.size(); ++i) {
keys_[i] = text_keys_[i].data();
values_[i] = text_values_[i].data();
}
dns_service_instance_.keys = keys_;
dns_service_instance_.values = values_;
}
// Init DNS service register request
dns_service_register_request_.Version = DNS_QUERY_REQUEST_VERSION1;
dns_service_register_request_.InterfaceIndex =
0; // all interfaces will be considered
dns_service_register_request_.unicastEnabled = false;
dns_service_register_request_.hCredentials = nullptr;
dns_service_register_request_.pServiceInstance = &dns_service_instance_;
dns_service_register_request_.pQueryContext = this; // callback use it
dns_service_register_request_.pRegisterCompletionCallback =
WifiLanMdns::DnsServiceRegisterComplete;
dns_service_notification_ = std::make_unique<absl::Notification>();
DWORD status = DnsServiceRegister(&dns_service_register_request_, nullptr);
if (status != DNS_REQUEST_PENDING) {
NEARBY_LOGS(ERROR) << "Failed to start mDNS advertising for service type ="
<< service_type;
return false;
}
if (!dns_service_notification_->WaitForNotificationWithTimeout(
kDnsServiceTimeout)) {
LOG(ERROR) << "Failed to start mDNS advertising for service type ="
<< service_type;
return false;
}
dns_service_notification_ = nullptr;
is_service_started_ = true;
LOG(INFO) << "Succeeded to start mDNS advertising for service type "
<< service_type;
return true;
}
bool WifiLanMdns::StopMdnsService() {
absl::MutexLock lock(&mutex_);
LOG(INFO) << "StopMdnsService is called";
if (!is_service_started_) {
LOG(WARNING) << "The mDNS service is not started.";
return false;
}
dns_service_notification_ = std::make_unique<absl::Notification>();
DWORD status = DnsServiceDeRegister(&dns_service_register_request_, nullptr);
if (status != DNS_REQUEST_PENDING) {
NEARBY_LOGS(ERROR) << "Failed to stop mDNS advertising.";
return false;
}
if (!dns_service_notification_->WaitForNotificationWithTimeout(
kDnsServiceTimeout)) {
LOG(ERROR) << "Failed to start mDNS advertising.";
return false;
}
dns_service_notification_ = nullptr;
if (dns_service_instance_.keys != nullptr) {
delete[] dns_service_instance_.keys;
delete[] dns_service_instance_.values;
dns_service_instance_.keys = nullptr;
dns_service_instance_.values = nullptr;
}
is_service_started_ = false;
LOG(INFO) << "Succeeded to stop mDNS advertising.";
return true;
}
void WifiLanMdns::NotifyStatusUpdated(DWORD status) {
VLOG(1) << "NotifyStatusUpdated: " << status;
if (dns_service_notification_ != nullptr) {
dns_service_notification_->Notify();
}
}
std::optional<std::string> WifiLanMdns::GetComputerName() {
char computer_name[MAX_COMPUTERNAME_LENGTH + 1];
DWORD size = sizeof(computer_name);
// Get the computer name.
if (::GetComputerNameA(computer_name, &size)) {
return std::string(computer_name, size);
} else {
return std::nullopt;
}
}
void WifiLanMdns::DnsServiceRegisterComplete(DWORD Status, PVOID pQueryContext,
PDNS_SERVICE_INSTANCE pInstance) {
LOG(INFO) << "DnsServiceRegisterComplete: " << Status;
WifiLanMdns* mdns = static_cast<WifiLanMdns*>(pQueryContext);
mdns->NotifyStatusUpdated(Status);
}
} // namespace nearby::windows
@@ -0,0 +1,70 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_WIFI_LAN_MDNS_H_
#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_WIFI_LAN_MDNS_H_
// clang-format off
#include <windows.h>
#include <windns.h>
// clang-format on
#include <memory>
#include <optional>
#include <string>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/synchronization/mutex.h"
#include "absl/synchronization/notification.h"
namespace nearby::windows {
class WifiLanMdns {
public:
WifiLanMdns() = default;
~WifiLanMdns();
bool StartMdnsService(
const std::string& service_name, const std::string& service_type,
int port, absl::flat_hash_map<std::string, std::string> text_records);
bool StopMdnsService();
void NotifyStatusUpdated(DWORD status);
private:
static void DnsServiceRegisterComplete(DWORD Status, PVOID pQueryContext,
PDNS_SERVICE_INSTANCE pInstance);
std::optional<std::string> GetComputerName();
absl::Mutex mutex_;
std::unique_ptr<absl::Notification> dns_service_notification_ = nullptr;
bool is_service_started_ ABSL_GUARDED_BY(mutex_) = false;
std::unique_ptr<std::wstring> dns_service_instance_name_
ABSL_GUARDED_BY(mutex_);
std::unique_ptr<std::wstring> host_name_ ABSL_GUARDED_BY(mutex_);
DNS_SERVICE_INSTANCE
dns_service_instance_ ABSL_GUARDED_BY(mutex_);
DNS_SERVICE_REGISTER_REQUEST dns_service_register_request_
ABSL_GUARDED_BY(mutex_);
std::vector<std::wstring> text_keys_ ABSL_GUARDED_BY(mutex_);
std::vector<std::wstring> text_values_ ABSL_GUARDED_BY(mutex_);
PWSTR* keys_ ABSL_GUARDED_BY(mutex_) = nullptr;
PWSTR* values_ ABSL_GUARDED_BY(mutex_) = nullptr;
};
} // namespace nearby::windows
#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_WIFI_LAN_MDNS_H_