Merge changes from topic "adb-mdns-cmdline"

* changes:
  [adb client] Add "mdns services" command.
  [adb client] Add "adb mdns check" command.
This commit is contained in:
Joshua Duong 2020-04-22 20:04:35 +00:00 committed by Gerrit Code Review
commit 8cab9b6b24
6 changed files with 201 additions and 9 deletions

View file

@ -1076,6 +1076,25 @@ void adb_set_reject_kill_server(bool value) {
g_reject_kill_server = value;
}
static bool handle_mdns_request(std::string_view service, int reply_fd) {
if (!android::base::ConsumePrefix(&service, "mdns:")) {
return false;
}
if (service == "check") {
std::string check = mdns_check();
SendOkay(reply_fd, check);
return true;
}
if (service == "services") {
std::string services_list = mdns_list_discovered_services();
SendOkay(reply_fd, services_list);
return true;
}
return false;
}
HostRequestResult handle_host_request(std::string_view service, TransportType type,
const char* serial, TransportId transport_id, int reply_fd,
asocket* s) {
@ -1320,6 +1339,10 @@ HostRequestResult handle_host_request(std::string_view service, TransportType ty
return HostRequestResult::Handled;
}
if (handle_mdns_request(service, reply_fd)) {
return HostRequestResult::Handled;
}
return HostRequestResult::Unhandled;
}

View file

@ -27,6 +27,9 @@ void adb_wifi_pair_device(const std::string& host, const std::string& password,
std::string& response);
bool adb_wifi_is_known_host(const std::string& host);
std::string mdns_check();
std::string mdns_list_discovered_services();
#else // !ADB_HOST
struct AdbdAuthContext;

View file

@ -90,8 +90,9 @@ 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 service_name, const char* _Nonnull ip_address, uint16_t port)>;
using adb_secure_foreach_service_callback =
std::function<void(const char* _Nonnull service_name, const char* _Nonnull reg_type,
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

View file

@ -127,6 +127,8 @@ static void help() {
" localfilesystem:<unix domain socket name>\n"
" reverse --remove REMOTE remove specific reverse socket connection\n"
" reverse --remove-all remove all reverse socket connections from device\n"
" mdns check check if mdns discovery is available\n"
" mdns services list all discovered services\n"
"\n"
"file transfer:\n"
" push [--sync] [-z ALGORITHM] [-Z] LOCAL... REMOTE\n"
@ -1910,6 +1912,29 @@ int adb_commandline(int argc, const char** argv) {
ReadOrderlyShutdown(fd);
return 0;
} else if (!strcmp(argv[0], "mdns")) {
--argc;
if (argc < 1) error_exit("mdns requires an argument");
++argv;
std::string error;
if (!adb_check_server_version(&error)) {
error_exit("failed to check server version: %s", error.c_str());
}
std::string query = "host:mdns:";
if (!strcmp(argv[0], "check")) {
if (argc != 1) error_exit("mdns %s doesn't take any arguments", argv[0]);
query += "check";
} else if (!strcmp(argv[0], "services")) {
if (argc != 1) error_exit("mdns %s doesn't take any arguments", argv[0]);
query += "services";
printf("List of discovered mdns services\n");
} else {
error_exit("unknown mdns command [%s]", argv[0]);
}
return adb_query_command(query);
}
/* do_sync_*() commands */
else if (!strcmp(argv[0], "ls")) {

View file

@ -216,6 +216,9 @@ class ResolvedService : public AsyncServiceRef {
int adbSecureServiceType = serviceIndex();
switch (adbSecureServiceType) {
case kADBTransportServiceRefIndex:
sAdbTransportServices->push_back(this);
break;
case kADBSecurePairingServiceRefIndex:
sAdbSecurePairingServices->push_back(this);
break;
@ -233,16 +236,21 @@ class ResolvedService : public AsyncServiceRef {
std::string serviceName() const { return serviceName_; }
std::string regType() const { return regType_; }
std::string ipAddress() const { return ip_addr_; }
uint16_t port() const { return port_; }
using ServiceRegistry = std::vector<ResolvedService*>;
// unencrypted tcp connections
static ServiceRegistry* sAdbTransportServices;
static ServiceRegistry* sAdbSecurePairingServices;
static ServiceRegistry* sAdbSecureConnectServices;
static void initAdbSecure();
static void initAdbServiceRegistries();
static void forEachService(const ServiceRegistry& services, const std::string& hostname,
adb_secure_foreach_service_callback cb);
@ -263,6 +271,9 @@ class ResolvedService : public AsyncServiceRef {
int serviceVersion_;
};
// static
std::vector<ResolvedService*>* ResolvedService::sAdbTransportServices = NULL;
// static
std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL;
@ -270,7 +281,10 @@ std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL
std::vector<ResolvedService*>* ResolvedService::sAdbSecureConnectServices = NULL;
// static
void ResolvedService::initAdbSecure() {
void ResolvedService::initAdbServiceRegistries() {
if (!sAdbTransportServices) {
sAdbTransportServices = new ServiceRegistry;
}
if (!sAdbSecurePairingServices) {
sAdbSecurePairingServices = new ServiceRegistry;
}
@ -283,17 +297,18 @@ void ResolvedService::initAdbSecure() {
void ResolvedService::forEachService(const ServiceRegistry& services,
const std::string& wanted_service_name,
adb_secure_foreach_service_callback cb) {
initAdbSecure();
initAdbServiceRegistries();
for (auto service : services) {
auto service_name = service->serviceName();
auto reg_type = service->regType();
auto ip = service->ipAddress();
auto port = service->port();
if (wanted_service_name == "") {
cb(service_name.c_str(), ip.c_str(), port);
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(), ip.c_str(), port);
cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
}
}
}
@ -301,7 +316,7 @@ void ResolvedService::forEachService(const ServiceRegistry& services,
// static
bool ResolvedService::connectByServiceName(const ServiceRegistry& services,
const std::string& service_name) {
initAdbSecure();
initAdbServiceRegistries();
for (auto service : services) {
if (service_name == service->serviceName()) {
D("Got service_name match [%s]", service->serviceName().c_str());
@ -398,6 +413,9 @@ static void adb_RemoveDNSService(const char* regType, const char* serviceName) {
int index = adb_DNSServiceIndexByName(regType);
ResolvedService::ServiceRegistry* services;
switch (index) {
case kADBTransportServiceRefIndex:
services = ResolvedService::sAdbTransportServices;
break;
case kADBSecurePairingServiceRefIndex:
services = ResolvedService::sAdbSecurePairingServices;
break;
@ -542,6 +560,34 @@ void init_mdns_transport_discovery_thread(void) {
}
void init_mdns_transport_discovery(void) {
ResolvedService::initAdbSecure();
ResolvedService::initAdbServiceRegistries();
std::thread(init_mdns_transport_discovery_thread).detach();
}
std::string mdns_check() {
uint32_t daemon_version;
uint32_t sz = sizeof(daemon_version);
auto dnserr = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &daemon_version, &sz);
std::string result = "ERROR: mdns daemon unavailable";
if (dnserr != kDNSServiceErr_NoError) {
return result;
}
result = android::base::StringPrintf("mdns daemon version [%u]", daemon_version);
return result;
}
std::string mdns_list_discovered_services() {
std::string result;
auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
uint16_t port) {
result += android::base::StringPrintf("%s\t%s\t%s:%u\n", service_name, reg_type, ip_addr,
port);
};
ResolvedService::forEachService(*ResolvedService::sAdbTransportServices, "", cb);
ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, "", cb);
ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, "", cb);
return result;
}

View file

@ -32,6 +32,8 @@ import threading
import time
import unittest
import warnings
from importlib import util
from parameterized import parameterized_class
def find_open_port():
# Find an open port.
@ -576,6 +578,98 @@ class PowerTest(unittest.TestCase):
# If the power event was detected, the adb shell command should be broken very quickly.
self.assertLess(end - start, 2)
"""Use 'adb mdns check' to see if mdns discovery is available."""
def is_adb_mdns_available():
with adb_server() as server_port:
output = subprocess.check_output(["adb", "-P", str(server_port),
"mdns", "check"]).strip()
return output.startswith(b"mdns daemon version")
"""Check if we have zeroconf python library installed"""
def is_zeroconf_installed():
zeroconf_spec = util.find_spec("zeroconf")
return zeroconf_spec is not None
@contextlib.contextmanager
def zeroconf_context(ipversion):
from zeroconf import Zeroconf
"""Context manager for a zeroconf instance
This creates a zeroconf instance and returns it.
"""
try:
zeroconf = Zeroconf(ip_version=ipversion)
yield zeroconf
finally:
zeroconf.close()
@contextlib.contextmanager
def zeroconf_register_service(zeroconf_ctx, info):
"""Context manager for a zeroconf service
Registers a service and unregisters it on cleanup. Returns the ServiceInfo
supplied.
"""
try:
zeroconf_ctx.register_service(info)
yield info
finally:
zeroconf_ctx.unregister_service(info)
"""Should match the service names listed in adb_mdns.h"""
@parameterized_class(('service_name',), [
("adb",),
("adb-tls-connect",),
("adb-tls-pairing",),
])
@unittest.skipIf(not is_adb_mdns_available(), "mdns feature not available")
class MdnsTest(unittest.TestCase):
"""Tests for adb mdns."""
@unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
def test_mdns_services_register_unregister(self):
"""Ensure that `adb mdns services` correctly adds and removes a service
"""
from zeroconf import IPVersion, ServiceInfo
def _mdns_services(port):
output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
with adb_server() as server_port:
output = subprocess.check_output(["adb", "-P", str(server_port),
"mdns", "services"]).strip()
self.assertTrue(output.startswith(b"List of discovered mdns services"))
print(f"services={_mdns_services(server_port)}")
"""TODO(joshuaduong): Add ipv6 tests once we have it working in adb"""
"""Register/Unregister a service"""
with zeroconf_context(IPVersion.V4Only) as zc:
serv_instance = "my_fake_test_service"
serv_type = "_" + self.service_name + "._tcp."
serv_ipaddr = socket.inet_aton("1.2.3.4")
serv_port = 12345
service_info = ServiceInfo(
serv_type + "local.",
name=serv_instance + "." + serv_type + "local.",
addresses=[serv_ipaddr],
port=serv_port)
print(f"Registering {serv_instance}.{serv_type} ...")
with zeroconf_register_service(zc, service_info) as info:
"""Give adb some time to register the service"""
time.sleep(0.25)
print(f"services={_mdns_services(server_port)}")
self.assertTrue(any((serv_instance in line and serv_type in line)
for line in _mdns_services(server_port)))
"""Give adb some time to unregister the service"""
print("Unregistering mdns service...")
time.sleep(0.25)
print(f"services={_mdns_services(server_port)}")
self.assertFalse(any((serv_instance in line and serv_type in line)
for line in _mdns_services(server_port)))
def main():
"""Main entrypoint."""