| /* |
| * 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(); |
| } |