From 7be8519cdb93df3d8d5abba32ecd0c3c9acbc0ff Mon Sep 17 00:00:00 2001 From: Joshua Duong Date: Thu, 30 Apr 2020 15:45:38 -0700 Subject: [PATCH 1/5] Add mDNS service instance name parser. This will be used for parsing user-provided names to 'adb connect' and 'adb pair' in order to check for matches in the mdns service registry. Bug: 152886765 Test: $ANDROID_HOST_OUT/nativetest64/adb_test/adb_test --gtest_filter=mdns_utils* Change-Id: Ifd74b4394212853c1c193a2ea64937f6a6a0ff24 --- adb/Android.bp | 6 +- adb/client/mdns_utils.cpp | 77 +++++++++++++++ adb/client/mdns_utils.h | 54 ++++++++++ adb/client/mdns_utils_test.cpp | 173 +++++++++++++++++++++++++++++++++ 4 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 adb/client/mdns_utils.cpp create mode 100644 adb/client/mdns_utils.h create mode 100644 adb/client/mdns_utils_test.cpp diff --git a/adb/Android.bp b/adb/Android.bp index 432770cd4..9db151d13 100644 --- a/adb/Android.bp +++ b/adb/Android.bp @@ -218,6 +218,7 @@ cc_library_host_static { "client/usb_dispatch.cpp", "client/transport_local.cpp", "client/transport_mdns.cpp", + "client/mdns_utils.cpp", "client/transport_usb.cpp", "client/pairing/pairing_client.cpp", ], @@ -264,7 +265,10 @@ cc_library_host_static { cc_test_host { name: "adb_test", defaults: ["adb_defaults"], - srcs: libadb_test_srcs, + srcs: libadb_test_srcs + [ + "client/mdns_utils_test.cpp", + ], + static_libs: [ "libadb_crypto_static", "libadb_host", diff --git a/adb/client/mdns_utils.cpp b/adb/client/mdns_utils.cpp new file mode 100644 index 000000000..8666b18e9 --- /dev/null +++ b/adb/client/mdns_utils.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "client/mdns_utils.h" + +#include + +namespace mdns { + +// .. +std::optional mdns_parse_instance_name(std::string_view name) { + CHECK(!name.empty()); + + // Return the whole name if it doesn't fall under .. or + // . + bool has_local_suffix = false; + // Strip the local suffix, if any + { + std::string local_suffix = ".local"; + local_suffix += android::base::EndsWith(name, ".") ? "." : ""; + + if (android::base::ConsumeSuffix(&name, local_suffix)) { + if (name.empty()) { + return std::nullopt; + } + has_local_suffix = true; + } + } + + std::string transport; + // Strip the transport suffix, if any + { + std::string add_dot = (!has_local_suffix && android::base::EndsWith(name, ".")) ? "." : ""; + std::array transport_suffixes{"._tcp", "._udp"}; + + for (const auto& t : transport_suffixes) { + if (android::base::ConsumeSuffix(&name, t + add_dot)) { + if (name.empty()) { + return std::nullopt; + } + transport = t.substr(1); + break; + } + } + + if (has_local_suffix && transport.empty()) { + return std::nullopt; + } + } + + if (!has_local_suffix && transport.empty()) { + return std::make_optional(name, "", ""); + } + + // Split the service name from the instance name + auto pos = name.rfind("."); + if (pos == 0 || pos == std::string::npos || pos == name.size() - 1) { + return std::nullopt; + } + + return std::make_optional(name.substr(0, pos), name.substr(pos + 1), transport); +} + +} // namespace mdns diff --git a/adb/client/mdns_utils.h b/adb/client/mdns_utils.h new file mode 100644 index 000000000..40d095ddc --- /dev/null +++ b/adb/client/mdns_utils.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include +#include + +#include "adb_wifi.h" + +namespace mdns { + +struct MdnsInstance { + std::string instance_name; // "my name" + std::string service_name; // "_adb-tls-connect" + std::string transport_type; // either "_tcp" or "_udp" + + MdnsInstance(std::string_view inst, std::string_view serv, std::string_view trans) + : instance_name(inst), service_name(serv), transport_type(trans) {} +}; + +// This parser is based on https://tools.ietf.org/html/rfc6763#section-4.1 for +// structured service instance names, where the whole name is in the format +// ... +// +// In our case, we ignore portion of the name, which +// we always assume to be ".local", or link-local mDNS. +// +// The string can be in one of the following forms: +// - ...? +// - e.g. "instance._service._tcp.local" (or "...local.") +// - ..? (must contain either "_tcp" or "_udp" at the end) +// - e.g. "instance._service._tcp" (or "..._tcp.) +// - (can contain dots '.') +// - e.g. "myname", "name.", "my.name." +// +// Returns an MdnsInstance with the appropriate fields filled in (instance name is never empty), +// otherwise returns std::nullopt. +std::optional mdns_parse_instance_name(std::string_view name); + +} // namespace mdns diff --git a/adb/client/mdns_utils_test.cpp b/adb/client/mdns_utils_test.cpp new file mode 100644 index 000000000..ec7152957 --- /dev/null +++ b/adb/client/mdns_utils_test.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "client/mdns_utils.h" + +#include + +namespace mdns { + +TEST(mdns_utils, mdns_parse_instance_name) { + // Just the instance name + { + std::string str = "."; + auto res = mdns_parse_instance_name(str); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(str, res->instance_name); + EXPECT_TRUE(res->service_name.empty()); + EXPECT_TRUE(res->transport_type.empty()); + } + { + std::string str = "my.name"; + auto res = mdns_parse_instance_name(str); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(str, res->instance_name); + EXPECT_TRUE(res->service_name.empty()); + EXPECT_TRUE(res->transport_type.empty()); + } + { + std::string str = "my.name."; + auto res = mdns_parse_instance_name(str); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(str, res->instance_name); + EXPECT_TRUE(res->service_name.empty()); + EXPECT_TRUE(res->transport_type.empty()); + } + + // With "_tcp", "_udp" transport type + for (const std::string_view transport : {"._tcp", "._udp"}) { + { + std::string str = android::base::StringPrintf("%s", transport.data()); + auto res = mdns_parse_instance_name(str); + EXPECT_FALSE(res.has_value()); + } + { + std::string str = android::base::StringPrintf("%s.", transport.data()); + auto res = mdns_parse_instance_name(str); + EXPECT_FALSE(res.has_value()); + } + { + std::string str = android::base::StringPrintf("service%s", transport.data()); + auto res = mdns_parse_instance_name(str); + EXPECT_FALSE(res.has_value()); + } + { + std::string str = android::base::StringPrintf(".service%s", transport.data()); + auto res = mdns_parse_instance_name(str); + EXPECT_FALSE(res.has_value()); + } + { + std::string str = android::base::StringPrintf("service.%s", transport.data()); + auto res = mdns_parse_instance_name(str); + EXPECT_FALSE(res.has_value()); + } + { + std::string str = android::base::StringPrintf("my.service%s", transport.data()); + auto res = mdns_parse_instance_name(str); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(res->instance_name, "my"); + EXPECT_EQ(res->service_name, "service"); + EXPECT_EQ(res->transport_type, transport.substr(1)); + } + { + std::string str = android::base::StringPrintf("my.service%s.", transport.data()); + auto res = mdns_parse_instance_name(str); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(res->instance_name, "my"); + EXPECT_EQ(res->service_name, "service"); + EXPECT_EQ(res->transport_type, transport.substr(1)); + } + { + std::string str = android::base::StringPrintf("my..service%s", transport.data()); + auto res = mdns_parse_instance_name(str); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(res->instance_name, "my."); + EXPECT_EQ(res->service_name, "service"); + EXPECT_EQ(res->transport_type, transport.substr(1)); + } + { + std::string str = android::base::StringPrintf("my.name.service%s.", transport.data()); + auto res = mdns_parse_instance_name(str); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(res->instance_name, "my.name"); + EXPECT_EQ(res->service_name, "service"); + EXPECT_EQ(res->transport_type, transport.substr(1)); + } + { + std::string str = android::base::StringPrintf("name.service.%s.", transport.data()); + auto res = mdns_parse_instance_name(str); + EXPECT_FALSE(res.has_value()); + } + + // With ".local" domain + { + std::string str = ".local"; + auto res = mdns_parse_instance_name(str); + EXPECT_FALSE(res.has_value()); + } + { + std::string str = ".local."; + auto res = mdns_parse_instance_name(str); + EXPECT_FALSE(res.has_value()); + } + { + std::string str = "name.local"; + auto res = mdns_parse_instance_name(str); + EXPECT_FALSE(res.has_value()); + } + { + std::string str = android::base::StringPrintf("%s.local", transport.data()); + auto res = mdns_parse_instance_name(str); + EXPECT_FALSE(res.has_value()); + } + { + std::string str = android::base::StringPrintf("service%s.local", transport.data()); + auto res = mdns_parse_instance_name(str); + EXPECT_FALSE(res.has_value()); + } + { + std::string str = android::base::StringPrintf("name.service%s.local", transport.data()); + auto res = mdns_parse_instance_name(str); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(res->instance_name, "name"); + EXPECT_EQ(res->service_name, "service"); + EXPECT_EQ(res->transport_type, transport.substr(1)); + } + { + std::string str = + android::base::StringPrintf("name.service%s.local.", transport.data()); + auto res = mdns_parse_instance_name(str); + ASSERT_TRUE(res.has_value()); + EXPECT_EQ(res->instance_name, "name"); + EXPECT_EQ(res->service_name, "service"); + EXPECT_EQ(res->transport_type, transport.substr(1)); + } + { + std::string str = + android::base::StringPrintf("name.service%s..local.", transport.data()); + auto res = mdns_parse_instance_name(str); + EXPECT_FALSE(res.has_value()); + } + { + std::string str = + android::base::StringPrintf("name.service.%s.local.", transport.data()); + auto res = mdns_parse_instance_name(str); + EXPECT_FALSE(res.has_value()); + } + } +} + +} // namespace mdns From 7ebc59573606cfa0a866699830f1a01581c18300 Mon Sep 17 00:00:00 2001 From: Joshua Duong Date: Fri, 1 May 2020 09:25:12 -0700 Subject: [PATCH 2/5] 'adb connect' by mDNS service name. Bug: 152886765 Test: $ANDROID_HOST_OUT/nativetest64/adb_test/adb_test Test: test_adb.py Change-Id: I7e93ceca7cdf913060bbc5afe824593a9922c6d9 --- adb/adb_wifi.h | 13 ++++++ adb/client/transport_mdns.cpp | 58 +++++++++++++++++++++--- adb/socket_spec.cpp | 20 ++++++++- adb/test_adb.py | 83 ++++++++++++++++++++++++++++++----- 4 files changed, 155 insertions(+), 19 deletions(-) diff --git a/adb/adb_wifi.h b/adb/adb_wifi.h index 3a6b0b133..5113c1552 100644 --- a/adb/adb_wifi.h +++ b/adb/adb_wifi.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include "adb.h" @@ -30,6 +31,18 @@ bool adb_wifi_is_known_host(const std::string& host); std::string mdns_check(); std::string mdns_list_discovered_services(); +struct MdnsInfo { + std::string service_name; + std::string service_type; + std::string addr; + uint16_t port = 0; + + MdnsInfo(std::string_view name, std::string_view type, std::string_view addr, uint16_t port) + : service_name(name), service_type(type), addr(addr), port(port) {} +}; + +std::optional mdns_get_connect_service_info(std::string_view name); + #else // !ADB_HOST struct AdbdAuthContext; diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp index c9993b787..f8c9de381 100644 --- a/adb/client/transport_mdns.cpp +++ b/adb/client/transport_mdns.cpp @@ -38,6 +38,7 @@ #include "adb_trace.h" #include "adb_utils.h" #include "adb_wifi.h" +#include "client/mdns_utils.h" #include "fdevent/fdevent.h" #include "sysdeps.h" @@ -319,7 +320,7 @@ class ResolvedService : public AsyncServiceRef { static void initAdbServiceRegistries(); - static void forEachService(const ServiceRegistry& services, const std::string& hostname, + static void forEachService(const ServiceRegistry& services, std::string_view hostname, adb_secure_foreach_service_callback cb); static bool connectByServiceName(const ServiceRegistry& services, @@ -362,7 +363,7 @@ void ResolvedService::initAdbServiceRegistries() { // static void ResolvedService::forEachService(const ServiceRegistry& services, - const std::string& wanted_service_name, + std::string_view wanted_service_name, adb_secure_foreach_service_callback cb) { initAdbServiceRegistries(); @@ -372,7 +373,7 @@ void ResolvedService::forEachService(const ServiceRegistry& services, auto ip = service->ipAddress(); auto port = service->port(); - if (wanted_service_name == "") { + if (wanted_service_name.empty()) { 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(), reg_type.c_str(), ip.c_str(), port); @@ -396,14 +397,12 @@ bool ResolvedService::connectByServiceName(const ServiceRegistry& services, void adb_secure_foreach_pairing_service(const char* service_name, adb_secure_foreach_service_callback cb) { - ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, - service_name ? service_name : "", cb); + ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, service_name, cb); } void adb_secure_foreach_connect_service(const char* service_name, adb_secure_foreach_service_callback cb) { - ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, - service_name ? service_name : "", cb); + ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, service_name, cb); } bool adb_secure_connect_by_service_name(const char* service_name) { @@ -676,3 +675,48 @@ std::string mdns_list_discovered_services() { ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, "", cb); return result; } + +std::optional mdns_get_connect_service_info(std::string_view name) { + CHECK(!name.empty()); + + auto mdns_instance = mdns::mdns_parse_instance_name(name); + if (!mdns_instance.has_value()) { + D("Failed to parse mDNS name [%s]", name.data()); + return std::nullopt; + } + + std::optional info; + auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr, + uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); }; + + std::string reg_type; + if (!mdns_instance->service_name.empty()) { + reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(), + mdns_instance->transport_type.data()); + int index = adb_DNSServiceIndexByName(reg_type); + switch (index) { + case kADBTransportServiceRefIndex: + ResolvedService::forEachService(*ResolvedService::sAdbTransportServices, + mdns_instance->instance_name, cb); + break; + case kADBSecureConnectServiceRefIndex: + ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, + mdns_instance->instance_name, cb); + break; + default: + D("Unknown reg_type [%s]", reg_type.data()); + return std::nullopt; + } + return info; + } + + for (const auto& service : + {ResolvedService::sAdbTransportServices, ResolvedService::sAdbSecureConnectServices}) { + ResolvedService::forEachService(*service, name, cb); + if (info.has_value()) { + return info; + } + } + + return std::nullopt; +} diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp index d17036cec..b7fd4935f 100644 --- a/adb/socket_spec.cpp +++ b/adb/socket_spec.cpp @@ -30,6 +30,7 @@ #include "adb.h" #include "adb_utils.h" +#include "adb_wifi.h" #include "sysdeps.h" using namespace std::string_literals; @@ -201,7 +202,24 @@ bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std fd->reset(network_loopback_client(port_value, SOCK_STREAM, error)); } else { #if ADB_HOST - fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error)); + // Check if the address is an mdns service we can connect to. + if (auto mdns_info = mdns_get_connect_service_info(address.substr(4)); + mdns_info != std::nullopt) { + fd->reset(network_connect(mdns_info->addr, mdns_info->port, SOCK_STREAM, 0, error)); + if (fd->get() != -1) { + // TODO(joshuaduong): We still show the ip address for the serial. Change it to + // use the mdns instance name, so we can adjust to address changes on + // reconnects. + port_value = mdns_info->port; + if (serial) { + *serial = android::base::StringPrintf("%s.%s", + mdns_info->service_name.c_str(), + mdns_info->service_type.c_str()); + } + } + } else { + fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error)); + } #else // Disallow arbitrary connections in adbd. *error = "adbd does not support arbitrary tcp connections"; diff --git a/adb/test_adb.py b/adb/test_adb.py index 9912f11e8..4b9941147 100755 --- a/adb/test_adb.py +++ b/adb/test_adb.py @@ -25,6 +25,7 @@ import os import random import select import socket +import string import struct import subprocess import sys @@ -628,21 +629,49 @@ def zeroconf_register_service(zeroconf_ctx, info): class MdnsTest(unittest.TestCase): """Tests for adb mdns.""" + @staticmethod + 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:]] + + @staticmethod + def _devices(port): + output = subprocess.check_output(["adb", "-P", str(port), "devices"]) + return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]] + + @contextlib.contextmanager + def _adb_mdns_connect(self, server_port, mdns_instance, serial, should_connect): + """Context manager for an ADB connection. + + This automatically disconnects when done with the connection. + """ + + output = subprocess.check_output(["adb", "-P", str(server_port), "connect", mdns_instance]) + if should_connect: + self.assertEqual(output.strip(), "connected to {}".format(serial).encode("utf8")) + else: + self.assertTrue(output.startswith("failed to resolve host: '{}'" + .format(mdns_instance).encode("utf8"))) + + try: + yield + finally: + # Perform best-effort disconnection. Discard the output. + subprocess.Popen(["adb", "disconnect", serial], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + + @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""" @@ -656,20 +685,52 @@ class MdnsTest(unittest.TestCase): 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(1) - 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))) + for line in MdnsTest._mdns_services(server_port))) """Give adb some time to unregister the service""" - print("Unregistering mdns service...") time.sleep(1) - 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))) + for line in MdnsTest._mdns_services(server_port))) + + @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed") + def test_mdns_connect(self): + """Ensure that `adb connect` by mdns instance name works (for non-pairing services) + """ + from zeroconf import IPVersion, ServiceInfo + + with adb_server() as server_port: + with zeroconf_context(IPVersion.V4Only) as zc: + serv_instance = "fakeadbd-" + ''.join( + random.choice(string.ascii_letters) for i in range(4)) + serv_type = "_" + self.service_name + "._tcp." + serv_ipaddr = socket.inet_aton("127.0.0.1") + should_connect = self.service_name != "adb-tls-pairing" + with fake_adbd() as (port, _): + service_info = ServiceInfo( + serv_type + "local.", + name=serv_instance + "." + serv_type + "local.", + addresses=[serv_ipaddr], + port=port) + with zeroconf_register_service(zc, service_info) as info: + """Give adb some time to register the service""" + time.sleep(1) + self.assertTrue(any((serv_instance in line and serv_type in line) + for line in MdnsTest._mdns_services(server_port))) + full_name = '.'.join([serv_instance, serv_type]) + with self._adb_mdns_connect(server_port, serv_instance, full_name, + should_connect): + if should_connect: + self.assertEqual(MdnsTest._devices(server_port), + [[full_name, "device"]]) + + """Give adb some time to unregister the service""" + time.sleep(1) + self.assertFalse(any((serv_instance in line and serv_type in line) + for line in MdnsTest._mdns_services(server_port))) def main(): """Main entrypoint.""" From beac509301beadcca5eaf954256d91aa8700424b Mon Sep 17 00:00:00 2001 From: Joshua Duong Date: Mon, 4 May 2020 18:42:46 -0700 Subject: [PATCH 3/5] 'adb pair' by mdns instance name. Bug: 152886765 Test: Turn on wireless debugging > "Pair with Pairing Code". On client, > adb pair Change-Id: I8f15c3ea17f3ac02205064f64aca0167bf5d2ad5 --- adb/adb_wifi.h | 1 + adb/client/adb_wifi.cpp | 32 ++++++++++++++++++++------------ adb/client/transport_mdns.cpp | 31 +++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/adb/adb_wifi.h b/adb/adb_wifi.h index 5113c1552..42f414b28 100644 --- a/adb/adb_wifi.h +++ b/adb/adb_wifi.h @@ -42,6 +42,7 @@ struct MdnsInfo { }; std::optional mdns_get_connect_service_info(std::string_view name); +std::optional mdns_get_pairing_service_info(std::string_view name); #else // !ADB_HOST diff --git a/adb/client/adb_wifi.cpp b/adb/client/adb_wifi.cpp index fa7102811..61a9a480c 100644 --- a/adb/client/adb_wifi.cpp +++ b/adb/client/adb_wifi.cpp @@ -179,17 +179,21 @@ bool adb_wifi_is_known_host(const std::string& host) { void adb_wifi_pair_device(const std::string& host, const std::string& password, std::string& response) { - // Check the address for a valid address and port. - std::string parsed_host; - std::string err; - int port = -1; - if (!android::base::ParseNetAddress(host, &parsed_host, &port, nullptr, &err)) { - response = "Failed to parse address for pairing: " + err; - return; - } - if (port <= 0 || port > 65535) { - response = "Invalid port while parsing address [" + host + "]"; - return; + auto mdns_info = mdns_get_pairing_service_info(host); + + if (!mdns_info.has_value()) { + // Check the address for a valid address and port. + std::string parsed_host; + std::string err; + int port = -1; + if (!android::base::ParseNetAddress(host, &parsed_host, &port, nullptr, &err)) { + response = "Failed to parse address for pairing: " + err; + return; + } + if (port <= 0 || port > 65535) { + response = "Invalid port while parsing address [" + host + "]"; + return; + } } auto priv_key = adb_auth_get_user_privkey(); @@ -220,7 +224,11 @@ void adb_wifi_pair_device(const std::string& host, const std::string& password, PairingResultWaiter waiter; std::unique_lock lock(waiter.mutex_); - if (!client->Start(host, waiter.OnResult, &waiter)) { + if (!client->Start(mdns_info.has_value() + ? android::base::StringPrintf("%s:%d", mdns_info->addr.c_str(), + mdns_info->port) + : host, + waiter.OnResult, &waiter)) { response = "Failed: Unable to start pairing client."; return; } diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp index f8c9de381..35e2c6d2a 100644 --- a/adb/client/transport_mdns.cpp +++ b/adb/client/transport_mdns.cpp @@ -720,3 +720,34 @@ std::optional mdns_get_connect_service_info(std::string_view name) { return std::nullopt; } + +std::optional mdns_get_pairing_service_info(std::string_view name) { + CHECK(!name.empty()); + + auto mdns_instance = mdns::mdns_parse_instance_name(name); + if (!mdns_instance.has_value()) { + D("Failed to parse mDNS pairing name [%s]", name.data()); + return std::nullopt; + } + + std::optional info; + auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr, + uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); }; + + // Verify it's a pairing service if user explicitly inputs it. + if (!mdns_instance->service_name.empty()) { + auto reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(), + mdns_instance->transport_type.data()); + int index = adb_DNSServiceIndexByName(reg_type); + switch (index) { + case kADBSecurePairingServiceRefIndex: + break; + default: + D("Not an adb pairing reg_type [%s]", reg_type.data()); + return std::nullopt; + } + } + + ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, name, cb); + return info; +} From 04d207d315c1ddb40df8697b62c4c2ede5d66178 Mon Sep 17 00:00:00 2001 From: Joshua Duong Date: Mon, 4 May 2020 19:44:20 -0700 Subject: [PATCH 4/5] Change adb auto-connect to use mdns instance name. This change will fix reconnects when the ip address and port changes for the same mdns instance name. Bug: 152886765 Test: 'adb pair ' and wait for auto-connect. 'adb devices' | grep push_back(std::unique_ptr(this)); + if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) { + std::string response; + D("Attempting to connect serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)", + serviceName_.c_str(), regType_.c_str(), ip_addr_, port_); + int index = adb_DNSServiceIndexByName(regType_.c_str()); + if (index == kADBSecureConnectServiceRefIndex) { + ConnectSecureWifiDevice(); + } else { + connect_device(android::base::StringPrintf("%s.%s", serviceName_.c_str(), + regType_.c_str()), + &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_); + } + return true; } From dcba5c9257c1850b06bbdbab740062abf651106e Mon Sep 17 00:00:00 2001 From: Joshua Duong Date: Tue, 5 May 2020 10:46:17 -0700 Subject: [PATCH 5/5] adb pair: allow passing password as command-line argument. Bug: 155194845 Test: adb pair Test: adb pair Change-Id: I9813b5812a98524109a4c90cb089eab652da06fa --- adb/client/commandline.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp index f0a287d47..da699c61b 100644 --- a/adb/client/commandline.cpp +++ b/adb/client/commandline.cpp @@ -104,7 +104,8 @@ static void help() { " connect HOST[:PORT] connect to a device via TCP/IP [default port=5555]\n" " disconnect [HOST[:PORT]]\n" " disconnect from given TCP/IP device [default port=5555], or all\n" - " pair HOST[:PORT] pair with a device for secure TCP/IP communication\n" + " pair HOST[:PORT] [PAIRING CODE]\n" + " pair with a device for secure TCP/IP communication\n" " forward --list list all forward socket connections\n" " forward [--no-rebind] LOCAL REMOTE\n" " forward socket connection using:\n" @@ -1728,13 +1729,17 @@ int adb_commandline(int argc, const char** argv) { } else if (!strcmp(argv[0], "abb")) { return adb_abb(argc, argv); } else if (!strcmp(argv[0], "pair")) { - if (argc != 2) error_exit("usage: adb pair [:]"); + if (argc < 2 || argc > 3) error_exit("usage: adb pair HOST[:PORT] [PAIRING CODE]"); std::string password; - printf("Enter pairing code: "); - fflush(stdout); - if (!std::getline(std::cin, password) || password.empty()) { - error_exit("No pairing code provided"); + if (argc == 2) { + printf("Enter pairing code: "); + fflush(stdout); + if (!std::getline(std::cin, password) || password.empty()) { + error_exit("No pairing code provided"); + } + } else { + password = argv[2]; } std::string query = android::base::StringPrintf("host:pair:%s:%s", password.c_str(), argv[1]);