/* * 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 "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 "fdevent/fdevent.h" #include "sysdeps.h" // TODO: Remove this file once openscreen has bonjour client APIs implemented. namespace { DNSServiceRef g_service_refs[kNumADBDNSServices]; fdevent* g_service_ref_fdes[kNumADBDNSServices]; // Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD() // directly so that the socket is put through the appropriate compatibility // layers to work with the rest of ADB's internal APIs. int adb_DNSServiceRefSockFD(DNSServiceRef ref) { return adb_register_socket(DNSServiceRefSockFD(ref)); } #define DNSServiceRefSockFD ___xxx_DNSServiceRefSockFD void DNSSD_API register_service_ip(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t interface_index, DNSServiceErrorType error_code, const char* hostname, const sockaddr* address, uint32_t ttl, void* context); void pump_service_ref(int /*fd*/, unsigned ev, void* data) { DNSServiceRef* ref = reinterpret_cast(data); if (ev & FDE_READ) DNSServiceProcessResult(*ref); } class AsyncServiceRef { public: bool Initialized() const { return initialized_; } void DestroyServiceRef() { if (!initialized_) { return; } // Order matters here! Must destroy the fdevent first since it has a // reference to |sdref_|. fdevent_destroy(fde_); D("DNSServiceRefDeallocate(sdref=%p)", sdref_); DNSServiceRefDeallocate(sdref_); initialized_ = false; } virtual ~AsyncServiceRef() { DestroyServiceRef(); } protected: DNSServiceRef sdref_; void Initialize() { fde_ = fdevent_create(adb_DNSServiceRefSockFD(sdref_), pump_service_ref, &sdref_); if (fde_ == nullptr) { D("Unable to create fdevent"); return; } fdevent_set(fde_, FDE_READ); initialized_ = true; } private: bool initialized_ = false; fdevent* fde_; }; class ResolvedService : public AsyncServiceRef { public: virtual ~ResolvedService() = default; ResolvedService(const std::string& service_name, const std::string& reg_type, uint32_t interface_index, const std::string& host_target, uint16_t port, int version) : service_name_(service_name), reg_type_(reg_type), host_target_(host_target), port_(port), sa_family_(0), service_version_(version) { /* TODO: We should be able to get IPv6 support by adding * kDNSServiceProtocol_IPv6 to the flags below. However, when we do * this, we get served link-local addresses that are usually useless to * connect to. What's more, we seem to /only/ get those and nothing else. * If we want IPv6 in the future we'll have to figure out why. */ DNSServiceErrorType ret = DNSServiceGetAddrInfo( &sdref_, 0, interface_index, kDNSServiceProtocol_IPv4, host_target_.c_str(), register_service_ip, reinterpret_cast(this)); if (ret != kDNSServiceErr_NoError) { D("Got %d from DNSServiceGetAddrInfo.", ret); } else { D("DNSServiceGetAddrInfo(sdref=%p, host_target=%s)", sdref_, host_target_.c_str()); Initialize(); } D("Client version: %d Service version: %d\n", clientVersion_, service_version_); } bool ConnectSecureWifiDevice() { if (!adb_wifi_is_known_host(service_name_)) { LOG(INFO) << "service_name=" << service_name_ << " not in keystore"; return false; } std::string response; connect_device( android::base::StringPrintf("%s.%s", service_name_.c_str(), reg_type_.c_str()), &response); D("Secure connect to %s regtype %s (%s:%hu) : %s", service_name_.c_str(), reg_type_.c_str(), ip_addr_.c_str(), port_, response.c_str()); return true; } bool RegisterIpAddress(const sockaddr* address) { sa_family_ = address->sa_family; const void* ip_addr_data; if (sa_family_ == AF_INET) { ip_addr_data = &reinterpret_cast(address)->sin_addr; addr_format_ = "%s:%hu"; } else if (sa_family_ == AF_INET6) { ip_addr_data = &reinterpret_cast(address)->sin6_addr; addr_format_ = "[%s]:%hu"; } else { // Should be impossible D("mDNS resolved non-IP address."); return false; } // Winsock version requires the const cast mingw defines inet_ntop differently from msvc. char ip_addr[INET6_ADDRSTRLEN] = {}; if (!inet_ntop(sa_family_, const_cast(ip_addr_data), ip_addr, sizeof(ip_addr))) { D("Could not convert IP address to string."); return false; } ip_addr_ = ip_addr; return true; } static void AddToServiceRegistry(std::unique_ptr service) { // Add to the service registry before trying to auto-connect, since socket_spec_connect will // check these registries for the ip address when connecting via mdns instance name. auto service_index = service->service_index(); if (!service_index) { return; } // Remove any services with the same instance name, as it may be a stale registration. RemoveDNSService(service->reg_type(), service->service_name()); ServiceRegistry* services = nullptr; switch (*service_index) { case kADBTransportServiceRefIndex: services = sAdbTransportServices; break; case kADBSecurePairingServiceRefIndex: services = sAdbSecurePairingServices; break; case kADBSecureConnectServiceRefIndex: services = sAdbSecureConnectServices; break; default: LOG(WARNING) << "No registry available for reg_type=[" << service->reg_type() << "]"; return; } services->push_back(std::move(service)); const auto& s = services->back(); auto reg_type = s->reg_type(); auto service_name = s->service_name(); auto ip_addr = s->ip_address(); auto port = s->port(); if (adb_DNSServiceShouldAutoConnect(reg_type, service_name)) { std::string response; D("Attempting to connect service_name=[%s], regtype=[%s] ip_addr=(%s:%hu)", service_name.c_str(), reg_type.c_str(), ip_addr.c_str(), port); if (*service_index == kADBSecureConnectServiceRefIndex) { s->ConnectSecureWifiDevice(); } else { connect_device(android::base::StringPrintf("%s.%s", service_name.c_str(), reg_type.c_str()), &response); D("Connect to %s regtype %s (%s:%hu) : %s", service_name.c_str(), reg_type.c_str(), ip_addr.c_str(), port, response.c_str()); } } else { D("Not immediately connecting to service_name=[%s], regtype=[%s] ip_addr=(%s:%hu)", service_name.c_str(), reg_type.c_str(), ip_addr.c_str(), port); } } std::optional service_index() const { return adb_DNSServiceIndexByName(reg_type_.c_str()); } const std::string& host_target() const { return host_target_; } const std::string& service_name() const { return service_name_; } const std::string& reg_type() const { return reg_type_; } const std::string& ip_address() const { return ip_addr_; } uint16_t port() const { return port_; } using ServiceRegistry = std::vector>; // unencrypted tcp connections static ServiceRegistry* sAdbTransportServices; static ServiceRegistry* sAdbSecurePairingServices; static ServiceRegistry* sAdbSecureConnectServices; static void InitAdbServiceRegistries(); static void ForEachService(const ServiceRegistry& services, const std::string& hostname, adb_secure_foreach_service_callback cb); static bool ConnectByServiceName(const ServiceRegistry& services, const std::string& service_name); static void RemoveDNSService(const std::string& reg_type, const std::string& service_name); private: int clientVersion_ = ADB_SECURE_CLIENT_VERSION; std::string addr_format_; std::string service_name_; std::string reg_type_; std::string host_target_; const uint16_t port_; int sa_family_; std::string ip_addr_; int service_version_; }; // static ResolvedService::ServiceRegistry* ResolvedService::sAdbTransportServices = NULL; // static ResolvedService::ServiceRegistry* ResolvedService::sAdbSecurePairingServices = NULL; // static ResolvedService::ServiceRegistry* ResolvedService::sAdbSecureConnectServices = NULL; // static void ResolvedService::InitAdbServiceRegistries() { if (!sAdbTransportServices) { sAdbTransportServices = new ServiceRegistry; } if (!sAdbSecurePairingServices) { sAdbSecurePairingServices = new ServiceRegistry; } if (!sAdbSecureConnectServices) { sAdbSecureConnectServices = new ServiceRegistry; } } // static void ResolvedService::ForEachService(const ServiceRegistry& services, const std::string& wanted_service_name, adb_secure_foreach_service_callback cb) { InitAdbServiceRegistries(); for (const auto& service : services) { auto service_name = service->service_name(); auto reg_type = service->reg_type(); auto ip = service->ip_address(); auto port = service->port(); if (wanted_service_name.empty()) { cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port); } else if (service_name == wanted_service_name) { cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port); } } } // static bool ResolvedService::ConnectByServiceName(const ServiceRegistry& services, const std::string& service_name) { InitAdbServiceRegistries(); for (const auto& service : services) { auto wanted_name = service->service_name(); if (wanted_name == service_name) { D("Got service_name match [%s]", wanted_name.c_str()); return service->ConnectSecureWifiDevice(); } } D("No registered service_names matched [%s]", service_name.c_str()); return false; } // static void ResolvedService::RemoveDNSService(const std::string& reg_type, const std::string& service_name) { D("%s: reg_type=[%s] service_name=[%s]", __func__, reg_type.c_str(), service_name.c_str()); auto index = adb_DNSServiceIndexByName(reg_type); if (!index) { return; } ServiceRegistry* services; switch (*index) { case kADBTransportServiceRefIndex: services = sAdbTransportServices; break; case kADBSecurePairingServiceRefIndex: services = sAdbSecurePairingServices; break; case kADBSecureConnectServiceRefIndex: services = sAdbSecureConnectServices; break; default: return; } if (services->empty()) { return; } services->erase(std::remove_if(services->begin(), services->end(), [&service_name](std::unique_ptr& service) { return (service_name == service->service_name()); }), services->end()); } void DNSSD_API register_service_ip(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t /*interface_index*/, DNSServiceErrorType error_code, const char* hostname, const sockaddr* address, uint32_t ttl, void* context) { D("%s: sdref=%p flags=0x%08x error_code=%u ttl=%u", __func__, sdref, flags, error_code, ttl); std::unique_ptr data(static_cast(context)); // Only resolve the address once. If the address or port changes, we'll just get another // registration. data->DestroyServiceRef(); if (error_code != kDNSServiceErr_NoError) { D("Got error while looking up ip_addr [%u]", error_code); return; } if (flags & kDNSServiceFlagsAdd) { if (data->RegisterIpAddress(address)) { D("Resolved IP address for [%s]. Adding to service registry.", hostname); ResolvedService::AddToServiceRegistry(std::move(data)); } } } void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t interface_index, DNSServiceErrorType error_code, const char* fullname, const char* host_target, uint16_t port, uint16_t txt_len, const unsigned char* txt_record, void* context); class DiscoveredService : public AsyncServiceRef { public: DiscoveredService(uint32_t interface_index, const char* service_name, const char* regtype, const char* domain) : service_name_(service_name), reg_type_(regtype) { DNSServiceErrorType ret = DNSServiceResolve(&sdref_, 0, interface_index, service_name, regtype, domain, register_resolved_mdns_service, reinterpret_cast(this)); D("DNSServiceResolve for " "interface_index %u " "service_name %s " "regtype %s " "domain %s " ": %d", interface_index, service_name, regtype, domain, ret); if (ret == kDNSServiceErr_NoError) { Initialize(); } } const std::string& service_name() { return service_name_; } const std::string& reg_type() { return reg_type_; } private: std::string service_name_; std::string reg_type_; }; // Returns the version the device wanted to advertise, // or -1 if parsing fails. int ParseVersionFromTxtRecord(uint16_t txt_len, const unsigned char* txt_record) { if (!txt_len) return -1; if (!txt_record) return -1; // https://tools.ietf.org/html/rfc6763 // """ // 6.1. General Format Rules for DNS TXT Records // // A DNS TXT record can be up to 65535 (0xFFFF) bytes long. The total // length is indicated by the length given in the resource record header // in the DNS message. There is no way to tell directly from the data // alone how long it is (e.g., there is no length count at the start, or // terminating NULL byte at the end). // """ // Let's trust the TXT record's length byte // Worst case, it wastes 255 bytes std::vector record_str(txt_len + 1, '\0'); char* str = record_str.data(); memcpy(str, txt_record + 1 /* skip the length byte */, txt_len); // Check if it's the version key static const char* version_key = "v="; size_t version_key_len = strlen(version_key); if (strncmp(version_key, str, version_key_len)) return -1; auto value_start = str + version_key_len; long parsed_number = strtol(value_start, 0, 10); // No valid conversion. Also, 0 // is not a valid version. if (!parsed_number) return -1; // Outside bounds of int. if (parsed_number < INT_MIN || parsed_number > INT_MAX) return -1; // Possibly valid version return static_cast(parsed_number); } void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t interface_index, DNSServiceErrorType error_code, const char* fullname, const char* host_target, uint16_t port, uint16_t txt_len, const unsigned char* txt_record, void* context) { D("Resolved a service."); std::unique_ptr discovered(reinterpret_cast(context)); if (error_code != kDNSServiceErr_NoError) { D("Got error %d resolving service.", error_code); return; } // TODO: Reject certain combinations of invalid or mismatched client and // service versions here before creating anything. // At the moment, there is nothing to reject, so accept everything // as an optimistic default. auto service_version = ParseVersionFromTxtRecord(txt_len, txt_record); auto resolved = new ResolvedService(discovered->service_name(), discovered->reg_type(), interface_index, host_target, ntohs(port), service_version); if (!resolved->Initialized()) { D("Unable to init resolved service"); delete resolved; } if (flags) { /* Only ever equals MoreComing or 0 */ D("releasing discovered service"); discovered.release(); } } void DNSSD_API on_service_browsed(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t interface_index, DNSServiceErrorType error_code, const char* service_name, const char* regtype, const char* domain, void* /*context*/) { if (error_code != kDNSServiceErr_NoError) { D("Got error %d during mDNS browse.", error_code); DNSServiceRefDeallocate(sdref); auto service_index = adb_DNSServiceIndexByName(regtype); if (service_index) { fdevent_destroy(g_service_ref_fdes[*service_index]); } return; } if (flags & kDNSServiceFlagsAdd) { D("%s: Discover found new service_name=[%s] regtype=[%s] domain=[%s]", __func__, service_name, regtype, domain); auto discovered = new DiscoveredService(interface_index, service_name, regtype, domain); if (!discovered->Initialized()) { delete discovered; } } else { D("%s: Discover lost service_name=[%s] regtype=[%s] domain=[%s]", __func__, service_name, regtype, domain); ResolvedService::RemoveDNSService(regtype, service_name); } } void init_mdns_transport_discovery_thread(void) { int error_codes[kNumADBDNSServices]; for (int i = 0; i < kNumADBDNSServices; ++i) { error_codes[i] = DNSServiceBrowse(&g_service_refs[i], 0, 0, kADBDNSServices[i], nullptr, on_service_browsed, nullptr); if (error_codes[i] != kDNSServiceErr_NoError) { D("Got %d browsing for mDNS service %s.", error_codes[i], kADBDNSServices[i]); } else { fdevent_run_on_main_thread([i]() { g_service_ref_fdes[i] = fdevent_create(adb_DNSServiceRefSockFD(g_service_refs[i]), pump_service_ref, &g_service_refs[i]); fdevent_set(g_service_ref_fdes[i], FDE_READ); }); } } } namespace MdnsResponder { bool adb_secure_connect_by_service_name(const std::string& instance_name) { return ResolvedService::ConnectByServiceName(*ResolvedService::sAdbSecureConnectServices, instance_name); } std::string mdns_check() { uint32_t daemon_version; uint32_t sz = sizeof(daemon_version); auto dnserr = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &daemon_version, &sz); if (dnserr != kDNSServiceErr_NoError) { return "ERROR: mdns daemon unavailable"; } return android::base::StringPrintf("mdns daemon version [%u]", daemon_version); } std::string mdns_list_discovered_services() { std::string result; auto cb = [&](const std::string& service_name, const std::string& reg_type, const std::string& ip_addr, uint16_t port) { result += android::base::StringPrintf("%s\t%s\t%s:%u\n", service_name.c_str(), reg_type.c_str(), ip_addr.c_str(), port); }; ResolvedService::ForEachService(*ResolvedService::sAdbTransportServices, "", cb); ResolvedService::ForEachService(*ResolvedService::sAdbSecureConnectServices, "", cb); ResolvedService::ForEachService(*ResolvedService::sAdbSecurePairingServices, "", cb); return result; } std::optional mdns_get_connect_service_info(const std::string& name) { CHECK(!name.empty()); // only adb server creates these registries if (!ResolvedService::sAdbTransportServices && !ResolvedService::sAdbSecureConnectServices) { return std::nullopt; } CHECK(ResolvedService::sAdbTransportServices); CHECK(ResolvedService::sAdbSecureConnectServices); auto mdns_instance = mdns::mdns_parse_instance_name(name); if (!mdns_instance.has_value()) { D("Failed to parse mDNS name [%s]", name.c_str()); return std::nullopt; } std::optional info; auto cb = [&](const std::string& service_name, const std::string& reg_type, const std::string& ip_addr, uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); }; std::string reg_type; if (!mdns_instance->service_name.empty()) { reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.c_str(), mdns_instance->transport_type.c_str()); auto index = adb_DNSServiceIndexByName(reg_type); if (!index) { return std::nullopt; } switch (*index) { case kADBTransportServiceRefIndex: ResolvedService::ForEachService(*ResolvedService::sAdbTransportServices, mdns_instance->instance_name, cb); break; case kADBSecureConnectServiceRefIndex: ResolvedService::ForEachService(*ResolvedService::sAdbSecureConnectServices, mdns_instance->instance_name, cb); break; default: D("Unknown reg_type [%s]", reg_type.c_str()); return std::nullopt; } return info; } for (const auto& service : {ResolvedService::sAdbTransportServices, ResolvedService::sAdbSecureConnectServices}) { ResolvedService::ForEachService(*service, name, cb); if (info.has_value()) { return info; } } return std::nullopt; } std::optional mdns_get_pairing_service_info(const std::string& name) { CHECK(!name.empty()); auto mdns_instance = mdns::mdns_parse_instance_name(name); if (!mdns_instance.has_value()) { D("Failed to parse mDNS pairing name [%s]", name.c_str()); return std::nullopt; } std::optional info; auto cb = [&](const std::string& service_name, const std::string& reg_type, const std::string& ip_addr, uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); }; // Verify it's a pairing service if user explicitly inputs it. if (!mdns_instance->service_name.empty()) { auto reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.c_str(), mdns_instance->transport_type.c_str()); 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.c_str()); return std::nullopt; } } ResolvedService::ForEachService(*ResolvedService::sAdbSecurePairingServices, name, cb); return info; } void mdns_cleanup() {} } // namespace MdnsResponder } // namespace AdbMdnsResponderFuncs StartMdnsResponderDiscovery() { ResolvedService::InitAdbServiceRegistries(); std::thread(init_mdns_transport_discovery_thread).detach(); AdbMdnsResponderFuncs f = { .mdns_check = MdnsResponder::mdns_check, .mdns_list_discovered_services = MdnsResponder::mdns_list_discovered_services, .mdns_get_connect_service_info = MdnsResponder::mdns_get_connect_service_info, .mdns_get_pairing_service_info = MdnsResponder::mdns_get_pairing_service_info, .mdns_cleanup = MdnsResponder::mdns_cleanup, .adb_secure_connect_by_service_name = MdnsResponder::adb_secure_connect_by_service_name, }; return f; }