Merge changes from topic "adbwifi-client-mdns" am: a71ba615ea am: 73447676aa

Change-Id: Ifc9736f4ab3940ad48e16691d3d0cae33adea315
This commit is contained in:
Joshua Duong 2020-02-18 05:45:42 +00:00
commit 560b4a0c94
5 changed files with 422 additions and 77 deletions

View File

@ -17,6 +17,72 @@
#ifndef _ADB_MDNS_H_
#define _ADB_MDNS_H_
#include <android-base/macros.h>
const char* kADBServiceType = "_adb._tcp";
const char* kADBSecurePairingServiceType = "_adb_secure_pairing._tcp";
const char* kADBSecureConnectServiceType = "_adb_secure_connect._tcp";
const int kADBTransportServiceRefIndex = 0;
const int kADBSecurePairingServiceRefIndex = 1;
const int kADBSecureConnectServiceRefIndex = 2;
// Each ADB Secure service advertises with a TXT record indicating the version
// using a key/value pair per RFC 6763 (https://tools.ietf.org/html/rfc6763).
//
// The first key/value pair is always the version of the protocol.
// There may be more key/value pairs added after.
//
// The version is purposely represented as the single letter "v" due to the
// need to minimize DNS traffic. The version starts at 1. With each breaking
// protocol change, the version is incremented by 1.
//
// Newer adb clients/daemons need to recognize and either reject
// or be backward-compatible with older verseions if there is a mismatch.
//
// Relevant sections:
//
// """
// 6.4. Rules for Keys in DNS-SD Key/Value Pairs
//
// The key MUST be at least one character. DNS-SD TXT record strings
// beginning with an '=' character (i.e., the key is missing) MUST be
// silently ignored.
//
// ...
//
// 6.5. Rules for Values in DNS-SD Key/Value Pairs
//
// If there is an '=' in a DNS-SD TXT record string, then everything
// after the first '=' to the end of the string is the value. The value
// can contain any eight-bit values including '='.
// """
#define ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ver) ("v=" #ver)
// Client/service versions are initially defined to be matching,
// but may go out of sync as different clients and services
// try to talk to each other.
#define ADB_SECURE_SERVICE_VERSION 1
#define ADB_SECURE_CLIENT_VERSION ADB_SECURE_SERVICE_VERSION
const char* kADBSecurePairingServiceTxtRecord =
ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ADB_SECURE_SERVICE_VERSION);
const char* kADBSecureConnectServiceTxtRecord =
ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ADB_SECURE_SERVICE_VERSION);
const char* kADBDNSServices[] = {
kADBServiceType,
kADBSecurePairingServiceType,
kADBSecureConnectServiceType,
};
const char* kADBDNSServiceTxtRecords[] = {
nullptr,
kADBSecurePairingServiceTxtRecord,
kADBSecureConnectServiceTxtRecord,
};
const int kNumADBDNSServices = arraysize(kADBDNSServices);
#endif

View File

@ -16,6 +16,7 @@
#pragma once
#include <functional>
#include <optional>
#include <string>
@ -86,3 +87,16 @@ std::optional<std::string> adb_get_server_executable_path();
// Globally acccesible argv/envp, for the purpose of re-execing adb.
extern const char* _Nullable * _Nullable __adb_argv;
extern const char* _Nullable * _Nullable __adb_envp;
// ADB Secure DNS service interface. Used to query what ADB Secure DNS services have been
// resolved, and to run some kind of callback for each one.
using adb_secure_foreach_service_callback = std::function<void(
const char* _Nonnull host_name, const char* _Nonnull ip_address, uint16_t port)>;
// Queries pairing/connect services that have been discovered and resolved.
// If |host_name| is not null, run |cb| only for services
// matching |host_name|. Otherwise, run for all services.
void adb_secure_foreach_pairing_service(const char* _Nullable host_name,
adb_secure_foreach_service_callback cb);
void adb_secure_foreach_connect_service(const char* _Nullable host_name,
adb_secure_foreach_service_callback cb);

View File

@ -25,17 +25,33 @@
#endif
#include <thread>
#include <vector>
#include <android-base/stringprintf.h>
#include <dns_sd.h>
#include "adb_client.h"
#include "adb_mdns.h"
#include "adb_trace.h"
#include "fdevent/fdevent.h"
#include "sysdeps.h"
static DNSServiceRef service_ref;
static fdevent* service_ref_fde;
static DNSServiceRef service_refs[kNumADBDNSServices];
static fdevent* service_ref_fdes[kNumADBDNSServices];
static int adb_DNSServiceIndexByName(const char* regType) {
for (int i = 0; i < kNumADBDNSServices; ++i) {
if (!strncmp(regType, kADBDNSServices[i], strlen(kADBDNSServices[i]))) {
return i;
}
}
return -1;
}
static bool adb_DNSServiceShouldConnect(const char* regType) {
int index = adb_DNSServiceIndexByName(regType);
return index == kADBTransportServiceRefIndex;
}
// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
// directly so that the socket is put through the appropriate compatibility
@ -94,10 +110,16 @@ 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) {
ResolvedService(std::string serviceName, std::string regType, uint32_t interfaceIndex,
const char* hosttarget, uint16_t port, int version)
: serviceName_(serviceName),
regType_(regType),
hosttarget_(hosttarget),
port_(port),
sa_family_(0),
ip_addr_data_(NULL),
serviceVersion_(version) {
memset(ip_addr_, 0, sizeof(ip_addr_));
/* TODO: We should be able to get IPv6 support by adding
* kDNSServiceProtocol_IPv6 to the flags below. However, when we do
@ -116,45 +138,135 @@ class ResolvedService : public AsyncServiceRef {
} else {
Initialize();
}
D("Client version: %d Service version: %d\n", clientVersion_, serviceVersion_);
}
void Connect(const sockaddr* address) {
char ip_addr[INET6_ADDRSTRLEN];
const void* ip_addr_data;
sa_family_ = address->sa_family;
const char* addr_format;
if (address->sa_family == AF_INET) {
ip_addr_data =
&reinterpret_cast<const sockaddr_in*>(address)->sin_addr;
if (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;
} else if (sa_family_ == AF_INET6) {
ip_addr_data_ = &reinterpret_cast<const sockaddr_in6*>(address)->sin6_addr;
addr_format = "[%s]:%hu";
} else { // Should be impossible
} 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)) {
if (!inet_ntop(sa_family_, const_cast<void*>(ip_addr_data_), ip_addr_, sizeof(ip_addr_))) {
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());
// adb secure service needs to do something different from just
// connecting here.
if (adb_DNSServiceShouldConnect(regType_.c_str())) {
std::string response;
connect_device(android::base::StringPrintf(addr_format, ip_addr_, port_), &response);
D("Connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
ip_addr_, port_, response.c_str());
} else {
D("Not immediately connecting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
}
int adbSecureServiceType = serviceIndex();
switch (adbSecureServiceType) {
case kADBSecurePairingServiceRefIndex:
sAdbSecurePairingServices->push_back(this);
break;
case kADBSecureConnectServiceRefIndex:
sAdbSecureConnectServices->push_back(this);
break;
default:
break;
}
}
int serviceIndex() const { return adb_DNSServiceIndexByName(regType_.c_str()); }
std::string hostTarget() const { return hosttarget_; }
std::string ipAddress() const { return ip_addr_; }
uint16_t port() const { return port_; }
using ServiceRegistry = std::vector<ResolvedService*>;
static ServiceRegistry* sAdbSecurePairingServices;
static ServiceRegistry* sAdbSecureConnectServices;
static void initAdbSecure();
static void forEachService(const ServiceRegistry& services, const std::string& hostname,
adb_secure_foreach_service_callback cb);
private:
std::string name_;
int clientVersion_ = ADB_SECURE_CLIENT_VERSION;
std::string serviceName_;
std::string regType_;
std::string hosttarget_;
const uint16_t port_;
int sa_family_;
const void* ip_addr_data_;
char ip_addr_[INET6_ADDRSTRLEN];
int serviceVersion_;
};
// static
std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL;
// static
std::vector<ResolvedService*>* ResolvedService::sAdbSecureConnectServices = NULL;
// static
void ResolvedService::initAdbSecure() {
if (!sAdbSecurePairingServices) {
sAdbSecurePairingServices = new ServiceRegistry;
}
if (!sAdbSecureConnectServices) {
sAdbSecureConnectServices = new ServiceRegistry;
}
}
// static
void ResolvedService::forEachService(const ServiceRegistry& services,
const std::string& wanted_host,
adb_secure_foreach_service_callback cb) {
initAdbSecure();
for (auto service : services) {
auto hostname = service->hostTarget();
auto ip = service->ipAddress();
auto port = service->port();
if (wanted_host == "") {
cb(hostname.c_str(), ip.c_str(), port);
} else if (hostname == wanted_host) {
cb(hostname.c_str(), ip.c_str(), port);
}
}
}
// static
void adb_secure_foreach_pairing_service(const char* host_name,
adb_secure_foreach_service_callback cb) {
ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices,
host_name ? host_name : "", cb);
}
// static
void adb_secure_foreach_connect_service(const char* host_name,
adb_secure_foreach_service_callback cb) {
ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices,
host_name ? host_name : "", cb);
}
static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/,
DNSServiceFlags /*flags*/,
uint32_t /*interfaceIndex*/,
@ -167,6 +279,12 @@ static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/,
std::unique_ptr<ResolvedService> data(
reinterpret_cast<ResolvedService*>(context));
data->Connect(address);
// For ADB Secure services, keep those ResolvedService's around
// for later processing with secure connection establishment.
if (data->serviceIndex() != kADBTransportServiceRefIndex) {
data.release();
}
}
static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
@ -182,18 +300,23 @@ static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
class DiscoveredService : public AsyncServiceRef {
public:
DiscoveredService(uint32_t interfaceIndex, const char* serviceName,
const char* regtype, const char* domain)
: serviceName_(serviceName) {
DiscoveredService(uint32_t interfaceIndex, const char* serviceName, const char* regtype,
const char* domain)
: serviceName_(serviceName), regType_(regtype) {
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 {
D("DNSServiceResolve for "
"interfaceIndex %u "
"serviceName %s "
"regtype %s "
"domain %s "
": %d",
interfaceIndex, serviceName, regtype, domain, ret);
if (ret == kDNSServiceErr_NoError) {
Initialize();
}
}
@ -202,20 +325,62 @@ class DiscoveredService : public AsyncServiceRef {
return serviceName_.c_str();
}
const char* RegType() { return regType_.c_str(); }
private:
std::string serviceName_;
std::string regType_;
};
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) {
// Returns the version the device wanted to advertise,
// or -1 if parsing fails.
static int parse_version_from_txt_record(uint16_t txtLen, const unsigned char* txtRecord) {
if (!txtLen) return -1;
if (!txtRecord) 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<char> recordAsString(txtLen + 1, '\0');
char* str = recordAsString.data();
memcpy(str, txtRecord + 1 /* skip the length byte */, txtLen);
// Check if it's the version key
static const char* versionKey = "v=";
size_t versionKeyLen = strlen(versionKey);
if (strncmp(versionKey, str, versionKeyLen)) return -1;
auto valueStart = str + versionKeyLen;
long parsedNumber = strtol(valueStart, 0, 10);
// No valid conversion. Also, 0
// is not a valid version.
if (!parsedNumber) return -1;
// Outside bounds of long.
if (parsedNumber == LONG_MIN || parsedNumber == LONG_MAX) return -1;
// Possibly valid version
return static_cast<int>(parsedNumber);
}
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));
@ -225,10 +390,14 @@ static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
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 serviceVersion = parse_version_from_txt_record(txtLen, txtRecord);
auto resolved =
new ResolvedService(discovered->ServiceName(),
interfaceIndex, hosttarget, ntohs(port));
auto resolved = new ResolvedService(discovered->ServiceName(), discovered->RegType(),
interfaceIndex, hosttarget, ntohs(port), serviceVersion);
if (! resolved->Initialized()) {
delete resolved;
@ -239,19 +408,18 @@ static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
}
}
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*/) {
static void DNSSD_API on_service_browsed(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_destroy(service_ref_fde);
int serviceIndex = adb_DNSServiceIndexByName(regtype);
if (serviceIndex != -1) {
fdevent_destroy(service_ref_fdes[serviceIndex]);
}
return;
}
@ -262,21 +430,27 @@ static void DNSSD_API register_mdns_transport(DNSServiceRef sdRef,
}
void init_mdns_transport_discovery_thread(void) {
DNSServiceErrorType errorCode = DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
register_mdns_transport, nullptr);
int errorCodes[kNumADBDNSServices];
if (errorCode != kDNSServiceErr_NoError) {
D("Got %d initiating mDNS browse.", errorCode);
return;
for (int i = 0; i < kNumADBDNSServices; ++i) {
errorCodes[i] = DNSServiceBrowse(&service_refs[i], 0, 0, kADBDNSServices[i], nullptr,
on_service_browsed, nullptr);
if (errorCodes[i] != kDNSServiceErr_NoError) {
D("Got %d browsing for mDNS service %s.", errorCodes[i], kADBDNSServices[i]);
}
if (errorCodes[i] == kDNSServiceErr_NoError) {
fdevent_run_on_main_thread([i]() {
service_ref_fdes[i] = fdevent_create(adb_DNSServiceRefSockFD(service_refs[i]),
pump_service_ref, &service_refs[i]);
fdevent_set(service_ref_fdes[i], FDE_READ);
});
}
}
fdevent_run_on_main_thread([]() {
service_ref_fde =
fdevent_create(adb_DNSServiceRefSockFD(service_ref), pump_service_ref, &service_ref);
fdevent_set(service_ref_fde, FDE_READ);
});
}
void init_mdns_transport_discovery(void) {
ResolvedService::initAdbSecure();
std::thread(init_mdns_transport_discovery_thread).detach();
}

View File

@ -14,6 +14,7 @@
* limitations under the License.
*/
#include "mdns.h"
#include "adb_mdns.h"
#include "sysdeps.h"
@ -32,8 +33,8 @@ using namespace std::chrono_literals;
static std::mutex& mdns_lock = *new std::mutex();
static int port;
static DNSServiceRef mdns_ref;
static bool mdns_registered = false;
static DNSServiceRef mdns_refs[kNumADBDNSServices];
static bool mdns_registered[kNumADBDNSServices];
static void start_mdns() {
if (android::base::GetProperty("init.svc.mdnsd", "") == "running") {
@ -60,34 +61,86 @@ static void mdns_callback(DNSServiceRef /*ref*/,
}
}
static void setup_mdns_thread() {
start_mdns();
static void register_mdns_service(int index, int port) {
std::lock_guard<std::mutex> lock(mdns_lock);
std::string hostname = "adb-";
hostname += android::base::GetProperty("ro.serialno", "unidentified");
auto error = DNSServiceRegister(&mdns_ref, 0, 0, hostname.c_str(),
kADBServiceType, nullptr, nullptr,
htobe16((uint16_t)port), 0, nullptr,
mdns_callback, nullptr);
// https://tools.ietf.org/html/rfc6763
// """
// The format of the data within a DNS TXT record is one or more
// strings, packed together in memory without any intervening gaps or
// padding bytes for word alignment.
//
// The format of each constituent string within the DNS TXT record is a
// single length byte, followed by 0-255 bytes of text data.
// """
//
// Therefore:
// 1. Begin with the string length
// 2. No null termination
std::vector<char> txtRecord;
if (kADBDNSServiceTxtRecords[index]) {
size_t txtRecordStringLength = strlen(kADBDNSServiceTxtRecords[index]);
txtRecord.resize(1 + // length byte
txtRecordStringLength // string bytes
);
txtRecord[0] = (char)txtRecordStringLength;
memcpy(txtRecord.data() + 1, kADBDNSServiceTxtRecords[index], txtRecordStringLength);
}
auto error = DNSServiceRegister(
&mdns_refs[index], 0, 0, hostname.c_str(), kADBDNSServices[index], nullptr, nullptr,
htobe16((uint16_t)port), (uint16_t)txtRecord.size(),
txtRecord.empty() ? nullptr : txtRecord.data(), mdns_callback, nullptr);
if (error != kDNSServiceErr_NoError) {
LOG(ERROR) << "Could not register mDNS service (" << error << ").";
return;
LOG(ERROR) << "Could not register mDNS service " << kADBDNSServices[index] << ", error ("
<< error << ").";
mdns_registered[index] = false;
}
mdns_registered = true;
mdns_registered[index] = true;
LOG(INFO) << "adbd mDNS service " << kADBDNSServices[index]
<< " registered: " << mdns_registered[index];
}
static void teardown_mdns() {
static void unregister_mdns_service(int index) {
std::lock_guard<std::mutex> lock(mdns_lock);
if (mdns_registered) {
DNSServiceRefDeallocate(mdns_ref);
if (mdns_registered[index]) {
DNSServiceRefDeallocate(mdns_refs[index]);
}
}
static void register_base_mdns_transport() {
register_mdns_service(kADBTransportServiceRefIndex, port);
}
static void setup_mdns_thread() {
start_mdns();
// We will now only set up the normal transport mDNS service
// instead of registering all the adb secure mDNS services
// in the beginning. This is to provide more privacy/security.
register_base_mdns_transport();
}
// This also tears down any adb secure mDNS services, if they exist.
static void teardown_mdns() {
for (int i = 0; i < kNumADBDNSServices; ++i) {
unregister_mdns_service(i);
}
}
// Public interface/////////////////////////////////////////////////////////////
void setup_mdns(int port_in) {
port = port_in;
std::thread(setup_mdns_thread).detach();
@ -95,3 +148,33 @@ void setup_mdns(int port_in) {
// TODO: Make this more robust against a hard kill.
atexit(teardown_mdns);
}
void register_adb_secure_pairing_service(int port) {
std::thread([port]() {
register_mdns_service(kADBSecurePairingServiceRefIndex, port);
}).detach();
}
void unregister_adb_secure_pairing_service() {
std::thread([]() { unregister_mdns_service(kADBSecurePairingServiceRefIndex); }).detach();
}
bool is_adb_secure_pairing_service_registered() {
std::lock_guard<std::mutex> lock(mdns_lock);
return mdns_registered[kADBSecurePairingServiceRefIndex];
}
void register_adb_secure_connect_service(int port) {
std::thread([port]() {
register_mdns_service(kADBSecureConnectServiceRefIndex, port);
}).detach();
}
void unregister_adb_secure_connect_service() {
std::thread([]() { unregister_mdns_service(kADBSecureConnectServiceRefIndex); }).detach();
}
bool is_adb_secure_connect_service_registered() {
std::lock_guard<std::mutex> lock(mdns_lock);
return mdns_registered[kADBSecureConnectServiceRefIndex];
}

View File

@ -19,4 +19,12 @@
void setup_mdns(int port);
void register_adb_secure_pairing_service(int port);
void unregister_adb_secure_pairing_service(int port);
bool is_adb_secure_pairing_service_registered();
void register_adb_secure_connect_service(int port);
void unregister_adb_secure_connect_service(int port);
bool is_adb_secure_connect_service_registered();
#endif // _DAEMON_MDNS_H_