Merge changes from topic "adb-mdns"
* changes: adb pair: allow passing password as command-line argument. Change adb auto-connect to use mdns instance name. 'adb pair' by mdns instance name. 'adb connect' by mDNS service name. Add mDNS service instance name parser.
This commit is contained in:
commit
7ce4a267f5
10 changed files with 549 additions and 59 deletions
|
@ -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",
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "adb.h"
|
||||
|
@ -30,6 +31,19 @@ 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<MdnsInfo> mdns_get_connect_service_info(std::string_view name);
|
||||
std::optional<MdnsInfo> mdns_get_pairing_service_info(std::string_view name);
|
||||
|
||||
#else // !ADB_HOST
|
||||
|
||||
struct AdbdAuthContext;
|
||||
|
|
|
@ -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<std::mutex> 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;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
@ -1735,13 +1736,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 <host>[:<port>]");
|
||||
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]);
|
||||
|
|
77
adb/client/mdns_utils.cpp
Normal file
77
adb/client/mdns_utils.cpp
Normal file
|
@ -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 <android-base/strings.h>
|
||||
|
||||
namespace mdns {
|
||||
|
||||
// <Instance>.<Service>.<Domain>
|
||||
std::optional<MdnsInstance> mdns_parse_instance_name(std::string_view name) {
|
||||
CHECK(!name.empty());
|
||||
|
||||
// Return the whole name if it doesn't fall under <Instance>.<Service>.<Domain> or
|
||||
// <Instance>.<Service>
|
||||
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<std::string, 2> 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<MdnsInstance>(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<MdnsInstance>(name.substr(0, pos), name.substr(pos + 1), transport);
|
||||
}
|
||||
|
||||
} // namespace mdns
|
54
adb/client/mdns_utils.h
Normal file
54
adb/client/mdns_utils.h
Normal file
|
@ -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 <optional>
|
||||
#include <string_view>
|
||||
|
||||
#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
|
||||
// <Instance>.<Service>.<Domain>.
|
||||
//
|
||||
// In our case, we ignore <Domain> 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:
|
||||
// - <Instance>.<Service>.<Domain>.?
|
||||
// - e.g. "instance._service._tcp.local" (or "...local.")
|
||||
// - <Instance>.<Service>.? (must contain either "_tcp" or "_udp" at the end)
|
||||
// - e.g. "instance._service._tcp" (or "..._tcp.)
|
||||
// - <Instance> (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<MdnsInstance> mdns_parse_instance_name(std::string_view name);
|
||||
|
||||
} // namespace mdns
|
173
adb/client/mdns_utils_test.cpp
Normal file
173
adb/client/mdns_utils_test.cpp
Normal file
|
@ -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 <gtest/gtest.h>
|
||||
|
||||
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
|
|
@ -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"
|
||||
|
||||
|
@ -221,7 +222,7 @@ class ResolvedService : public AsyncServiceRef {
|
|||
}
|
||||
|
||||
std::string response;
|
||||
connect_device(android::base::StringPrintf(addr_format_.c_str(), ip_addr_, port_),
|
||||
connect_device(android::base::StringPrintf("%s.%s", serviceName_.c_str(), regType_.c_str()),
|
||||
&response);
|
||||
D("Secure connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
|
||||
ip_addr_, port_, response.c_str());
|
||||
|
@ -248,26 +249,8 @@ class ResolvedService : public AsyncServiceRef {
|
|||
return false;
|
||||
}
|
||||
|
||||
// adb secure service needs to do something different from just
|
||||
// connecting here.
|
||||
if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
|
||||
std::string response;
|
||||
D("Attempting to 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(addr_format_.c_str(), 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_);
|
||||
}
|
||||
|
||||
// Add to the service registry before trying to auto-connect, since socket_spec_connect will
|
||||
// check these registries for the ip address when connecting via mdns instance name.
|
||||
int adbSecureServiceType = serviceIndex();
|
||||
ServiceRegistry* services = nullptr;
|
||||
switch (adbSecureServiceType) {
|
||||
|
@ -294,6 +277,25 @@ class ResolvedService : public AsyncServiceRef {
|
|||
}
|
||||
services->push_back(std::unique_ptr<ResolvedService>(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;
|
||||
}
|
||||
|
||||
|
@ -319,7 +321,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 +364,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 +374,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 +398,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 +676,79 @@ std::string mdns_list_discovered_services() {
|
|||
ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, "", cb);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<MdnsInfo> 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<MdnsInfo> 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;
|
||||
}
|
||||
|
||||
std::optional<MdnsInfo> 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<MdnsInfo> 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;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "adb.h"
|
||||
#include "adb_utils.h"
|
||||
#include "adb_wifi.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
@ -195,7 +196,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";
|
||||
|
|
|
@ -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."""
|
||||
|
|
Loading…
Reference in a new issue