You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
410 lines
14 KiB
410 lines
14 KiB
/*
|
|
* Copyright (C) 2020 The Android Open Source Project
|
|
*
|
|
* 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
|
|
*
|
|
* http://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.
|
|
*/
|
|
|
|
#define TRACE_TAG TRANSPORT
|
|
|
|
#include "transport.h"
|
|
|
|
#ifdef _WIN32
|
|
#include <winsock2.h>
|
|
#else
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
#include <memory>
|
|
#include <thread>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#include <android-base/stringprintf.h>
|
|
#include <android-base/strings.h>
|
|
|
|
#include <discovery/common/config.h>
|
|
#include <discovery/common/reporting_client.h>
|
|
#include <discovery/public/dns_sd_service_factory.h>
|
|
#include <platform/api/network_interface.h>
|
|
#include <platform/api/serial_delete_ptr.h>
|
|
#include <platform/base/error.h>
|
|
#include <platform/base/interface_info.h>
|
|
|
|
#include "adb_client.h"
|
|
#include "adb_mdns.h"
|
|
#include "adb_trace.h"
|
|
#include "adb_utils.h"
|
|
#include "adb_wifi.h"
|
|
#include "client/mdns_utils.h"
|
|
#include "client/openscreen/mdns_service_watcher.h"
|
|
#include "client/openscreen/platform/task_runner.h"
|
|
#include "fdevent/fdevent.h"
|
|
#include "sysdeps.h"
|
|
|
|
namespace {
|
|
|
|
using namespace mdns;
|
|
using namespace openscreen;
|
|
using ServicesUpdatedState = mdns::ServiceReceiver::ServicesUpdatedState;
|
|
|
|
struct DiscoveryState;
|
|
DiscoveryState* g_state = nullptr;
|
|
// TODO: remove once openscreen has bonjour client APIs.
|
|
bool g_using_bonjour = false;
|
|
AdbMdnsResponderFuncs g_adb_mdnsresponder_funcs;
|
|
|
|
class DiscoveryReportingClient : public discovery::ReportingClient {
|
|
public:
|
|
void OnFatalError(Error error) override {
|
|
// The multicast port 5353 may fail to bind because of another process already binding
|
|
// to it (bonjour). So let's fallback to bonjour client APIs.
|
|
// TODO: Remove this once openscreen implements the bonjour client APIs.
|
|
LOG(ERROR) << "Encountered fatal discovery error: " << error;
|
|
got_fatal_ = true;
|
|
}
|
|
|
|
void OnRecoverableError(Error error) override {
|
|
LOG(ERROR) << "Encountered recoverable discovery error: " << error;
|
|
}
|
|
|
|
bool GotFatalError() const { return got_fatal_; }
|
|
|
|
private:
|
|
std::atomic<bool> got_fatal_{false};
|
|
};
|
|
|
|
struct DiscoveryState {
|
|
SerialDeletePtr<discovery::DnsSdService> service;
|
|
std::unique_ptr<DiscoveryReportingClient> reporting_client;
|
|
std::unique_ptr<AdbOspTaskRunner> task_runner;
|
|
std::vector<std::unique_ptr<ServiceReceiver>> receivers;
|
|
InterfaceInfo interface_info;
|
|
};
|
|
|
|
// Callback provided to service receiver for updates.
|
|
void OnServiceReceiverResult(std::vector<std::reference_wrapper<const ServiceInfo>> infos,
|
|
std::reference_wrapper<const ServiceInfo> info,
|
|
ServicesUpdatedState state) {
|
|
LOG(INFO) << "Endpoint state=" << static_cast<int>(state)
|
|
<< " instance_name=" << info.get().instance_name
|
|
<< " service_name=" << info.get().service_name << " addr=" << info.get().v4_address
|
|
<< " addrv6=" << info.get().v6_address << " total_serv=" << infos.size();
|
|
|
|
switch (state) {
|
|
case ServicesUpdatedState::EndpointCreated:
|
|
case ServicesUpdatedState::EndpointUpdated:
|
|
if (adb_DNSServiceShouldAutoConnect(info.get().service_name,
|
|
info.get().instance_name) &&
|
|
info.get().v4_address) {
|
|
auto index = adb_DNSServiceIndexByName(info.get().service_name);
|
|
if (!index) {
|
|
return;
|
|
}
|
|
|
|
// Don't try to auto-connect if not in the keystore.
|
|
if (*index == kADBSecureConnectServiceRefIndex &&
|
|
!adb_wifi_is_known_host(info.get().instance_name)) {
|
|
LOG(INFO) << "instance_name=" << info.get().instance_name << " not in keystore";
|
|
return;
|
|
}
|
|
std::string response;
|
|
LOG(INFO) << "Attempting to auto-connect to instance=" << info.get().instance_name
|
|
<< " service=" << info.get().service_name << " addr4=%s"
|
|
<< info.get().v4_address << ":" << info.get().port;
|
|
connect_device(
|
|
android::base::StringPrintf("%s.%s", info.get().instance_name.c_str(),
|
|
info.get().service_name.c_str()),
|
|
&response);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::optional<discovery::Config> GetConfigForAllInterfaces() {
|
|
auto interface_infos = GetNetworkInterfaces();
|
|
|
|
discovery::Config config;
|
|
for (const auto interface : interface_infos) {
|
|
discovery::Config::NetworkInfo::AddressFamilies supported_address_families =
|
|
discovery::Config::NetworkInfo::kNoAddressFamily;
|
|
if (interface.GetIpAddressV4()) {
|
|
supported_address_families |= discovery::Config::NetworkInfo::kUseIpV4;
|
|
}
|
|
if (interface.GetIpAddressV6()) {
|
|
supported_address_families |= discovery::Config::NetworkInfo::kUseIpV6;
|
|
}
|
|
if (supported_address_families == discovery::Config::NetworkInfo::kNoAddressFamily) {
|
|
LOG(INFO) << "Interface [" << interface << "] doesn't support ipv4 or ipv6."
|
|
<< " Ignoring interface.";
|
|
continue;
|
|
}
|
|
config.network_info.push_back({interface, supported_address_families});
|
|
LOG(VERBOSE) << "Listening on interface [" << interface << "]";
|
|
}
|
|
|
|
if (config.network_info.empty()) {
|
|
LOG(INFO) << "No available network interfaces for mDNS discovery";
|
|
return std::nullopt;
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
void StartDiscovery() {
|
|
CHECK(!g_state);
|
|
g_state = new DiscoveryState();
|
|
g_state->task_runner = std::make_unique<AdbOspTaskRunner>();
|
|
g_state->reporting_client = std::make_unique<DiscoveryReportingClient>();
|
|
|
|
g_state->task_runner->PostTask([]() {
|
|
auto config = GetConfigForAllInterfaces();
|
|
if (!config) {
|
|
return;
|
|
}
|
|
|
|
g_state->service = discovery::CreateDnsSdService(g_state->task_runner.get(),
|
|
g_state->reporting_client.get(), *config);
|
|
// Register a receiver for each service type
|
|
for (int i = 0; i < kNumADBDNSServices; ++i) {
|
|
auto receiver = std::make_unique<ServiceReceiver>(
|
|
g_state->service.get(), kADBDNSServices[i], OnServiceReceiverResult);
|
|
receiver->StartDiscovery();
|
|
g_state->receivers.push_back(std::move(receiver));
|
|
|
|
if (g_state->reporting_client->GotFatalError()) {
|
|
for (auto& r : g_state->receivers) {
|
|
if (r->is_running()) {
|
|
r->StopDiscovery();
|
|
}
|
|
}
|
|
g_using_bonjour = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (g_using_bonjour) {
|
|
LOG(INFO) << "Fallback to MdnsResponder client for discovery";
|
|
g_adb_mdnsresponder_funcs = StartMdnsResponderDiscovery();
|
|
}
|
|
});
|
|
}
|
|
|
|
void ForEachService(const std::unique_ptr<ServiceReceiver>& receiver,
|
|
std::string_view wanted_instance_name, adb_secure_foreach_service_callback cb) {
|
|
if (!receiver->is_running()) {
|
|
return;
|
|
}
|
|
auto services = receiver->GetServices();
|
|
for (const auto& s : services) {
|
|
if (wanted_instance_name.empty() || s.get().instance_name == wanted_instance_name) {
|
|
std::stringstream ss;
|
|
ss << s.get().v4_address;
|
|
cb(s.get().instance_name.c_str(), s.get().service_name.c_str(), ss.str().c_str(),
|
|
s.get().port);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ConnectAdbSecureDevice(const MdnsInfo& info) {
|
|
if (!adb_wifi_is_known_host(info.service_name)) {
|
|
LOG(INFO) << "serviceName=" << info.service_name << " not in keystore";
|
|
return false;
|
|
}
|
|
|
|
std::string response;
|
|
connect_device(android::base::StringPrintf("%s.%s", info.service_name.c_str(),
|
|
info.service_type.c_str()),
|
|
&response);
|
|
D("Secure connect to %s regtype %s (%s:%hu) : %s", info.service_name.c_str(),
|
|
info.service_type.c_str(), info.addr.c_str(), info.port, response.c_str());
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void mdns_cleanup() {
|
|
if (g_using_bonjour) {
|
|
return g_adb_mdnsresponder_funcs.mdns_cleanup();
|
|
}
|
|
}
|
|
|
|
void init_mdns_transport_discovery(void) {
|
|
// TODO(joshuaduong): Use openscreen discovery by default for all platforms.
|
|
const char* mdns_osp = getenv("ADB_MDNS_OPENSCREEN");
|
|
if (mdns_osp && strcmp(mdns_osp, "1") == 0) {
|
|
LOG(INFO) << "Openscreen mdns discovery enabled";
|
|
StartDiscovery();
|
|
} else {
|
|
// Original behavior is to use Bonjour client.
|
|
g_using_bonjour = true;
|
|
g_adb_mdnsresponder_funcs = StartMdnsResponderDiscovery();
|
|
}
|
|
}
|
|
|
|
bool adb_secure_connect_by_service_name(const std::string& instance_name) {
|
|
if (g_using_bonjour) {
|
|
return g_adb_mdnsresponder_funcs.adb_secure_connect_by_service_name(instance_name);
|
|
}
|
|
|
|
if (!g_state || g_state->receivers.empty()) {
|
|
LOG(INFO) << "Mdns not enabled";
|
|
return false;
|
|
}
|
|
|
|
std::optional<MdnsInfo> info;
|
|
auto cb = [&](const std::string& instance_name, const std::string& service_name,
|
|
const std::string& ip_addr,
|
|
uint16_t port) { info.emplace(instance_name, service_name, ip_addr, port); };
|
|
ForEachService(g_state->receivers[kADBSecureConnectServiceRefIndex], instance_name, cb);
|
|
if (info.has_value()) {
|
|
return ConnectAdbSecureDevice(*info);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string mdns_check() {
|
|
if (!g_state && !g_using_bonjour) {
|
|
return "ERROR: mdns discovery disabled";
|
|
}
|
|
|
|
if (g_using_bonjour) {
|
|
return g_adb_mdnsresponder_funcs.mdns_check();
|
|
}
|
|
|
|
return "mdns daemon version [Openscreen discovery 0.0.0]";
|
|
}
|
|
|
|
std::string mdns_list_discovered_services() {
|
|
if (g_using_bonjour) {
|
|
return g_adb_mdnsresponder_funcs.mdns_list_discovered_services();
|
|
}
|
|
|
|
if (!g_state || g_state->receivers.empty()) {
|
|
return "";
|
|
}
|
|
|
|
std::string result;
|
|
auto cb = [&](const std::string& instance_name, const std::string& service_name,
|
|
const std::string& ip_addr, uint16_t port) {
|
|
result += android::base::StringPrintf("%s\t%s\t%s:%u\n", instance_name.data(),
|
|
service_name.data(), ip_addr.data(), port);
|
|
};
|
|
|
|
for (const auto& receiver : g_state->receivers) {
|
|
ForEachService(receiver, "", cb);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::optional<MdnsInfo> mdns_get_connect_service_info(const std::string& name) {
|
|
CHECK(!name.empty());
|
|
|
|
if (g_using_bonjour) {
|
|
return g_adb_mdnsresponder_funcs.mdns_get_connect_service_info(name);
|
|
}
|
|
|
|
if (!g_state || g_state->receivers.empty()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto mdns_instance = mdns::mdns_parse_instance_name(name);
|
|
if (!mdns_instance.has_value()) {
|
|
D("Failed to parse mDNS name [%s]", name.data());
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<MdnsInfo> info;
|
|
auto cb = [&](const std::string& instance_name, const std::string& service_name,
|
|
const std::string& ip_addr,
|
|
uint16_t port) { info.emplace(instance_name, service_name, ip_addr, port); };
|
|
|
|
std::string reg_type;
|
|
// Service name was provided.
|
|
if (!mdns_instance->service_name.empty()) {
|
|
reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(),
|
|
mdns_instance->transport_type.data());
|
|
const auto index = adb_DNSServiceIndexByName(reg_type);
|
|
if (!index) {
|
|
return std::nullopt;
|
|
}
|
|
switch (*index) {
|
|
case kADBTransportServiceRefIndex:
|
|
case kADBSecureConnectServiceRefIndex:
|
|
ForEachService(g_state->receivers[*index], mdns_instance->instance_name, cb);
|
|
break;
|
|
default:
|
|
D("Not a connectable service name [%s]", reg_type.data());
|
|
return std::nullopt;
|
|
}
|
|
return info;
|
|
}
|
|
|
|
// No mdns service name provided. Just search for the instance name in all adb connect services.
|
|
// Prefer the secured connect service over the other.
|
|
ForEachService(g_state->receivers[kADBSecureConnectServiceRefIndex], name, cb);
|
|
if (!info.has_value()) {
|
|
ForEachService(g_state->receivers[kADBTransportServiceRefIndex], name, cb);
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
std::optional<MdnsInfo> mdns_get_pairing_service_info(const std::string& name) {
|
|
CHECK(!name.empty());
|
|
|
|
if (g_using_bonjour) {
|
|
return g_adb_mdnsresponder_funcs.mdns_get_pairing_service_info(name);
|
|
}
|
|
|
|
if (!g_state || g_state->receivers.empty()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto mdns_instance = mdns::mdns_parse_instance_name(name);
|
|
if (!mdns_instance.has_value()) {
|
|
D("Failed to parse mDNS name [%s]", name.data());
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<MdnsInfo> info;
|
|
auto cb = [&](const std::string& instance_name, const std::string& service_name,
|
|
const std::string& ip_addr,
|
|
uint16_t port) { info.emplace(instance_name, service_name, ip_addr, port); };
|
|
|
|
std::string reg_type;
|
|
// Verify it's a pairing service if user explicitly inputs it.
|
|
if (!mdns_instance->service_name.empty()) {
|
|
reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(),
|
|
mdns_instance->transport_type.data());
|
|
const auto index = adb_DNSServiceIndexByName(reg_type);
|
|
if (!index) {
|
|
return std::nullopt;
|
|
}
|
|
switch (*index) {
|
|
case kADBSecurePairingServiceRefIndex:
|
|
break;
|
|
default:
|
|
D("Not an adb pairing reg_type [%s]", reg_type.data());
|
|
return std::nullopt;
|
|
}
|
|
return info;
|
|
}
|
|
|
|
ForEachService(g_state->receivers[kADBSecurePairingServiceRefIndex], name, cb);
|
|
|
|
return info;
|
|
}
|