/* * 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 #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #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 got_fatal_{false}; }; struct DiscoveryState { SerialDeletePtr service; std::unique_ptr reporting_client; std::unique_ptr task_runner; std::vector> receivers; InterfaceInfo interface_info; }; // Callback provided to service receiver for updates. void OnServiceReceiverResult(std::vector> infos, std::reference_wrapper info, ServicesUpdatedState state) { LOG(INFO) << "Endpoint state=" << static_cast(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 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(); g_state->reporting_client = std::make_unique(); 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( 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& 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 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 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 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 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 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; }