1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define TRACE_TAG TRANSPORT
18
19#include "transport.h"
20
21#ifdef _WIN32
22#include <winsock2.h>
23#else
24#include <arpa/inet.h>
25#endif
26
27#include <thread>
28
29#include <android-base/stringprintf.h>
30#include <dns_sd.h>
31
32#include "adb_mdns.h"
33#include "adb_trace.h"
34#include "fdevent.h"
35#include "sysdeps.h"
36
37static DNSServiceRef service_ref;
38static fdevent service_ref_fde;
39
40// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
41// directly so that the socket is put through the appropriate compatibility
42// layers to work with the rest of ADB's internal APIs.
43static inline int adb_DNSServiceRefSockFD(DNSServiceRef ref) {
44    return adb_register_socket(DNSServiceRefSockFD(ref));
45}
46#define DNSServiceRefSockFD ___xxx_DNSServiceRefSockFD
47
48static void DNSSD_API register_service_ip(DNSServiceRef sdRef,
49                                          DNSServiceFlags flags,
50                                          uint32_t interfaceIndex,
51                                          DNSServiceErrorType errorCode,
52                                          const char* hostname,
53                                          const sockaddr* address,
54                                          uint32_t ttl,
55                                          void* context);
56
57static void pump_service_ref(int /*fd*/, unsigned ev, void* data) {
58    DNSServiceRef* ref = reinterpret_cast<DNSServiceRef*>(data);
59
60    if (ev & FDE_READ)
61        DNSServiceProcessResult(*ref);
62}
63
64class AsyncServiceRef {
65  public:
66    bool Initialized() {
67        return initialized_;
68    }
69
70    virtual ~AsyncServiceRef() {
71        if (! initialized_) {
72            return;
73        }
74
75        DNSServiceRefDeallocate(sdRef_);
76        fdevent_remove(&fde_);
77    }
78
79  protected:
80    DNSServiceRef sdRef_;
81
82    void Initialize() {
83        fdevent_install(&fde_, adb_DNSServiceRefSockFD(sdRef_),
84                        pump_service_ref, &sdRef_);
85        fdevent_set(&fde_, FDE_READ);
86        initialized_ = true;
87    }
88
89  private:
90    bool initialized_;
91    fdevent fde_;
92};
93
94class ResolvedService : public AsyncServiceRef {
95  public:
96    virtual ~ResolvedService() = default;
97
98    ResolvedService(std::string name, uint32_t interfaceIndex,
99                    const char* hosttarget, uint16_t port) :
100            name_(name),
101            port_(port) {
102
103        /* TODO: We should be able to get IPv6 support by adding
104         * kDNSServiceProtocol_IPv6 to the flags below. However, when we do
105         * this, we get served link-local addresses that are usually useless to
106         * connect to. What's more, we seem to /only/ get those and nothing else.
107         * If we want IPv6 in the future we'll have to figure out why.
108         */
109        DNSServiceErrorType ret =
110            DNSServiceGetAddrInfo(
111                &sdRef_, 0, interfaceIndex,
112                kDNSServiceProtocol_IPv4, hosttarget,
113                register_service_ip, reinterpret_cast<void*>(this));
114
115        if (ret != kDNSServiceErr_NoError) {
116            D("Got %d from DNSServiceGetAddrInfo.", ret);
117        } else {
118            Initialize();
119        }
120    }
121
122    void Connect(const sockaddr* address) {
123        char ip_addr[INET6_ADDRSTRLEN];
124        const void* ip_addr_data;
125        const char* addr_format;
126
127        if (address->sa_family == AF_INET) {
128            ip_addr_data =
129                &reinterpret_cast<const sockaddr_in*>(address)->sin_addr;
130            addr_format = "%s:%hu";
131        } else if (address->sa_family == AF_INET6) {
132            ip_addr_data =
133                &reinterpret_cast<const sockaddr_in6*>(address)->sin6_addr;
134            addr_format = "[%s]:%hu";
135        } else { // Should be impossible
136            D("mDNS resolved non-IP address.");
137            return;
138        }
139
140        // Winsock version requires the const cast Because Microsoft.
141        if (!inet_ntop(address->sa_family, const_cast<void*>(ip_addr_data),
142                       ip_addr, INET6_ADDRSTRLEN)) {
143            D("Could not convert IP address to string.");
144            return;
145        }
146
147        std::string response;
148        connect_device(android::base::StringPrintf(addr_format, ip_addr, port_),
149                       &response);
150        D("Connect to %s (%s:%hu) : %s", name_.c_str(), ip_addr, port_,
151          response.c_str());
152    }
153
154  private:
155    std::string name_;
156    const uint16_t port_;
157};
158
159static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/,
160                                          DNSServiceFlags /*flags*/,
161                                          uint32_t /*interfaceIndex*/,
162                                          DNSServiceErrorType /*errorCode*/,
163                                          const char* /*hostname*/,
164                                          const sockaddr* address,
165                                          uint32_t /*ttl*/,
166                                          void* context) {
167    D("Got IP for service.");
168    std::unique_ptr<ResolvedService> data(
169        reinterpret_cast<ResolvedService*>(context));
170    data->Connect(address);
171}
172
173static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
174                                                     DNSServiceFlags flags,
175                                                     uint32_t interfaceIndex,
176                                                     DNSServiceErrorType errorCode,
177                                                     const char* fullname,
178                                                     const char* hosttarget,
179                                                     uint16_t port,
180                                                     uint16_t txtLen,
181                                                     const unsigned char* txtRecord,
182                                                     void* context);
183
184class DiscoveredService : public AsyncServiceRef {
185  public:
186    DiscoveredService(uint32_t interfaceIndex, const char* serviceName,
187                      const char* regtype, const char* domain)
188        : serviceName_(serviceName) {
189
190        DNSServiceErrorType ret =
191            DNSServiceResolve(&sdRef_, 0, interfaceIndex, serviceName, regtype,
192                              domain, register_resolved_mdns_service,
193                              reinterpret_cast<void*>(this));
194
195        if (ret != kDNSServiceErr_NoError) {
196            D("Got %d from DNSServiceResolve.", ret);
197        } else {
198            Initialize();
199        }
200    }
201
202    const char* ServiceName() {
203        return serviceName_.c_str();
204    }
205
206  private:
207    std::string serviceName_;
208};
209
210static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
211                                                     DNSServiceFlags flags,
212                                                     uint32_t interfaceIndex,
213                                                     DNSServiceErrorType errorCode,
214                                                     const char* fullname,
215                                                     const char* hosttarget,
216                                                     uint16_t port,
217                                                     uint16_t /*txtLen*/,
218                                                     const unsigned char* /*txtRecord*/,
219                                                     void* context) {
220    D("Resolved a service.");
221    std::unique_ptr<DiscoveredService> discovered(
222        reinterpret_cast<DiscoveredService*>(context));
223
224    if (errorCode != kDNSServiceErr_NoError) {
225        D("Got error %d resolving service.", errorCode);
226        return;
227    }
228
229
230    auto resolved =
231        new ResolvedService(discovered->ServiceName(),
232                            interfaceIndex, hosttarget, ntohs(port));
233
234    if (! resolved->Initialized()) {
235        delete resolved;
236    }
237
238    if (flags) { /* Only ever equals MoreComing or 0 */
239        discovered.release();
240    }
241}
242
243static void DNSSD_API register_mdns_transport(DNSServiceRef sdRef,
244                                              DNSServiceFlags flags,
245                                              uint32_t interfaceIndex,
246                                              DNSServiceErrorType errorCode,
247                                              const char* serviceName,
248                                              const char* regtype,
249                                              const char* domain,
250                                              void*  /*context*/) {
251    D("Registering a transport.");
252    if (errorCode != kDNSServiceErr_NoError) {
253        D("Got error %d during mDNS browse.", errorCode);
254        DNSServiceRefDeallocate(sdRef);
255        fdevent_remove(&service_ref_fde);
256        return;
257    }
258
259    auto discovered = new DiscoveredService(interfaceIndex, serviceName,
260                                            regtype, domain);
261
262    if (! discovered->Initialized()) {
263        delete discovered;
264    }
265}
266
267void init_mdns_transport_discovery_thread(void) {
268    DNSServiceErrorType errorCode = DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
269                                                     register_mdns_transport, nullptr);
270
271    if (errorCode != kDNSServiceErr_NoError) {
272        D("Got %d initiating mDNS browse.", errorCode);
273        return;
274    }
275
276    fdevent_run_on_main_thread([]() {
277        fdevent_install(&service_ref_fde, adb_DNSServiceRefSockFD(service_ref), pump_service_ref,
278                        &service_ref);
279        fdevent_set(&service_ref_fde, FDE_READ);
280    });
281}
282
283void init_mdns_transport_discovery(void) {
284    std::thread(init_mdns_transport_discovery_thread).detach();
285}
286