| /* | |
| * Copyright (C) 2016 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 <thread> | |
| #include <android-base/stringprintf.h> | |
| #include <dns_sd.h> | |
| #include "adb_mdns.h" | |
| #include "adb_trace.h" | |
| #include "fdevent.h" | |
| #include "sysdeps.h" | |
| static DNSServiceRef service_ref; | |
| static fdevent service_ref_fde; | |
| // 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. | |
| static inline int adb_DNSServiceRefSockFD(DNSServiceRef ref) { | |
| return adb_register_socket(DNSServiceRefSockFD(ref)); | |
| } | |
| #define DNSServiceRefSockFD ___xxx_DNSServiceRefSockFD | |
| static void DNSSD_API register_service_ip(DNSServiceRef sdRef, | |
| DNSServiceFlags flags, | |
| uint32_t interfaceIndex, | |
| DNSServiceErrorType errorCode, | |
| const char* hostname, | |
| const sockaddr* address, | |
| uint32_t ttl, | |
| void* context); | |
| static void pump_service_ref(int /*fd*/, unsigned ev, void* data) { | |
| DNSServiceRef* ref = reinterpret_cast<DNSServiceRef*>(data); | |
| if (ev & FDE_READ) | |
| DNSServiceProcessResult(*ref); | |
| } | |
| class AsyncServiceRef { | |
| public: | |
| bool Initialized() { | |
| return initialized_; | |
| } | |
| virtual ~AsyncServiceRef() { | |
| if (! initialized_) { | |
| return; | |
| } | |
| DNSServiceRefDeallocate(sdRef_); | |
| fdevent_remove(&fde_); | |
| } | |
| protected: | |
| DNSServiceRef sdRef_; | |
| void Initialize() { | |
| fdevent_install(&fde_, adb_DNSServiceRefSockFD(sdRef_), | |
| pump_service_ref, &sdRef_); | |
| fdevent_set(&fde_, FDE_READ); | |
| initialized_ = true; | |
| } | |
| private: | |
| bool initialized_; | |
| fdevent fde_; | |
| }; | |
| class ResolvedService : public AsyncServiceRef { | |
| public: | |
| virtual ~ResolvedService() = default; | |
| ResolvedService(std::string name, uint32_t interfaceIndex, | |
| const char* hosttarget, uint16_t port) : | |
| name_(name), | |
| port_(port) { | |
| /* 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, interfaceIndex, | |
| kDNSServiceProtocol_IPv4, hosttarget, | |
| register_service_ip, reinterpret_cast<void*>(this)); | |
| if (ret != kDNSServiceErr_NoError) { | |
| D("Got %d from DNSServiceGetAddrInfo.", ret); | |
| } else { | |
| Initialize(); | |
| } | |
| } | |
| void Connect(const sockaddr* address) { | |
| char ip_addr[INET6_ADDRSTRLEN]; | |
| const void* ip_addr_data; | |
| const char* addr_format; | |
| if (address->sa_family == AF_INET) { | |
| ip_addr_data = | |
| &reinterpret_cast<const sockaddr_in*>(address)->sin_addr; | |
| addr_format = "%s:%hu"; | |
| } else if (address->sa_family == AF_INET6) { | |
| ip_addr_data = | |
| &reinterpret_cast<const sockaddr_in6*>(address)->sin6_addr; | |
| addr_format = "[%s]:%hu"; | |
| } else { // Should be impossible | |
| D("mDNS resolved non-IP address."); | |
| return; | |
| } | |
| // Winsock version requires the const cast Because Microsoft. | |
| if (!inet_ntop(address->sa_family, const_cast<void*>(ip_addr_data), | |
| ip_addr, INET6_ADDRSTRLEN)) { | |
| D("Could not convert IP address to string."); | |
| return; | |
| } | |
| std::string response; | |
| connect_device(android::base::StringPrintf(addr_format, ip_addr, port_), | |
| &response); | |
| D("Connect to %s (%s:%hu) : %s", name_.c_str(), ip_addr, port_, | |
| response.c_str()); | |
| } | |
| private: | |
| std::string name_; | |
| const uint16_t port_; | |
| }; | |
| static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/, | |
| DNSServiceFlags /*flags*/, | |
| uint32_t /*interfaceIndex*/, | |
| DNSServiceErrorType /*errorCode*/, | |
| const char* /*hostname*/, | |
| const sockaddr* address, | |
| uint32_t /*ttl*/, | |
| void* context) { | |
| D("Got IP for service."); | |
| std::unique_ptr<ResolvedService> data( | |
| reinterpret_cast<ResolvedService*>(context)); | |
| data->Connect(address); | |
| } | |
| static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef, | |
| DNSServiceFlags flags, | |
| uint32_t interfaceIndex, | |
| DNSServiceErrorType errorCode, | |
| const char* fullname, | |
| const char* hosttarget, | |
| uint16_t port, | |
| uint16_t txtLen, | |
| const unsigned char* txtRecord, | |
| void* context); | |
| class DiscoveredService : public AsyncServiceRef { | |
| public: | |
| DiscoveredService(uint32_t interfaceIndex, const char* serviceName, | |
| const char* regtype, const char* domain) | |
| : serviceName_(serviceName) { | |
| DNSServiceErrorType ret = | |
| DNSServiceResolve(&sdRef_, 0, interfaceIndex, serviceName, regtype, | |
| domain, register_resolved_mdns_service, | |
| reinterpret_cast<void*>(this)); | |
| if (ret != kDNSServiceErr_NoError) { | |
| D("Got %d from DNSServiceResolve.", ret); | |
| } else { | |
| Initialize(); | |
| } | |
| } | |
| const char* ServiceName() { | |
| return serviceName_.c_str(); | |
| } | |
| private: | |
| std::string serviceName_; | |
| }; | |
| static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef, | |
| DNSServiceFlags flags, | |
| uint32_t interfaceIndex, | |
| DNSServiceErrorType errorCode, | |
| const char* fullname, | |
| const char* hosttarget, | |
| uint16_t port, | |
| uint16_t /*txtLen*/, | |
| const unsigned char* /*txtRecord*/, | |
| void* context) { | |
| D("Resolved a service."); | |
| std::unique_ptr<DiscoveredService> discovered( | |
| reinterpret_cast<DiscoveredService*>(context)); | |
| if (errorCode != kDNSServiceErr_NoError) { | |
| D("Got error %d resolving service.", errorCode); | |
| return; | |
| } | |
| auto resolved = | |
| new ResolvedService(discovered->ServiceName(), | |
| interfaceIndex, hosttarget, ntohs(port)); | |
| if (! resolved->Initialized()) { | |
| delete resolved; | |
| } | |
| if (flags) { /* Only ever equals MoreComing or 0 */ | |
| discovered.release(); | |
| } | |
| } | |
| static void DNSSD_API register_mdns_transport(DNSServiceRef sdRef, | |
| DNSServiceFlags flags, | |
| uint32_t interfaceIndex, | |
| DNSServiceErrorType errorCode, | |
| const char* serviceName, | |
| const char* regtype, | |
| const char* domain, | |
| void* /*context*/) { | |
| D("Registering a transport."); | |
| if (errorCode != kDNSServiceErr_NoError) { | |
| D("Got error %d during mDNS browse.", errorCode); | |
| DNSServiceRefDeallocate(sdRef); | |
| fdevent_remove(&service_ref_fde); | |
| return; | |
| } | |
| auto discovered = new DiscoveredService(interfaceIndex, serviceName, | |
| regtype, domain); | |
| if (! discovered->Initialized()) { | |
| delete discovered; | |
| } | |
| } | |
| void init_mdns_transport_discovery_thread(void) { | |
| DNSServiceErrorType errorCode = DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr, | |
| register_mdns_transport, nullptr); | |
| if (errorCode != kDNSServiceErr_NoError) { | |
| D("Got %d initiating mDNS browse.", errorCode); | |
| return; | |
| } | |
| fdevent_run_on_main_thread([]() { | |
| fdevent_install(&service_ref_fde, adb_DNSServiceRefSockFD(service_ref), pump_service_ref, | |
| &service_ref); | |
| fdevent_set(&service_ref_fde, FDE_READ); | |
| }); | |
| } | |
| void init_mdns_transport_discovery(void) { | |
| std::thread(init_mdns_transport_discovery_thread).detach(); | |
| } |