399 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			399 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * 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) {
 | |
|         if (interface.GetIpAddressV4() || interface.GetIpAddressV6()) {
 | |
|             config.network_info.push_back({interface});
 | |
|             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;
 | |
| }
 |