[adbwifi] Add adbwifi_libs, TLS connection, and MDNS implementation.
Bug: 111434128, 119493510, 119494503 Test: Enable wireless debugging in Settings UI, click "pair with pairing code" to generate pairing code. On client, 'adb pair <ip_address>', enter pairing code at prompt and hit enter. Pairing should complete. 'adb logcat'. Change-Id: I86527bd3fc52e30a8e08ec5843dc3e100abf91fa Exempt-From-Owner-Approval: approved already
This commit is contained in:
parent
16d5bc6ed5
commit
d85f5c0130
24 changed files with 2122 additions and 70 deletions
|
@ -225,9 +225,11 @@ cc_library_host_static {
|
|||
|
||||
srcs: libadb_srcs + [
|
||||
"client/auth.cpp",
|
||||
"client/adb_wifi.cpp",
|
||||
"client/usb_libusb.cpp",
|
||||
"client/usb_dispatch.cpp",
|
||||
"client/transport_mdns.cpp",
|
||||
"client/pairing/pairing_client.cpp",
|
||||
],
|
||||
|
||||
generated_headers: ["platform_tools_version"],
|
||||
|
@ -257,6 +259,8 @@ cc_library_host_static {
|
|||
static_libs: [
|
||||
"libadb_crypto",
|
||||
"libadb_protos",
|
||||
"libadb_pairing_connection",
|
||||
"libadb_tls_connection",
|
||||
"libbase",
|
||||
"libcrypto_utils",
|
||||
"libcrypto",
|
||||
|
@ -266,6 +270,7 @@ cc_library_host_static {
|
|||
"libutils",
|
||||
"liblog",
|
||||
"libcutils",
|
||||
"libprotobuf-cpp-lite",
|
||||
],
|
||||
}
|
||||
|
||||
|
@ -274,8 +279,12 @@ cc_test_host {
|
|||
defaults: ["adb_defaults"],
|
||||
srcs: libadb_test_srcs,
|
||||
static_libs: [
|
||||
"libadb_crypto",
|
||||
"libadb_crypto_static",
|
||||
"libadb_host",
|
||||
"libadb_pairing_auth_static",
|
||||
"libadb_pairing_connection_static",
|
||||
"libadb_protos_static",
|
||||
"libadb_tls_connection_static",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libcrypto_utils",
|
||||
|
@ -283,6 +292,8 @@ cc_test_host {
|
|||
"liblog",
|
||||
"libmdnssd",
|
||||
"libdiagnose_usb",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libssl",
|
||||
"libusb",
|
||||
],
|
||||
|
||||
|
@ -314,12 +325,16 @@ cc_benchmark {
|
|||
},
|
||||
|
||||
static_libs: [
|
||||
"libadb_crypto_static",
|
||||
"libadb_tls_connection_static",
|
||||
"libadbd_auth",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libcrypto_utils",
|
||||
"libcrypto_static",
|
||||
"libdiagnose_usb",
|
||||
"liblog",
|
||||
"libssl",
|
||||
"libusb",
|
||||
],
|
||||
}
|
||||
|
@ -354,6 +369,10 @@ cc_binary_host {
|
|||
static_libs: [
|
||||
"libadb_crypto",
|
||||
"libadb_host",
|
||||
"libadb_pairing_auth",
|
||||
"libadb_pairing_connection",
|
||||
"libadb_protos",
|
||||
"libadb_tls_connection",
|
||||
"libandroidfw",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
|
@ -365,6 +384,7 @@ cc_binary_host {
|
|||
"liblz4",
|
||||
"libmdnssd",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libssl",
|
||||
"libusb",
|
||||
"libutils",
|
||||
"liblog",
|
||||
|
@ -415,6 +435,7 @@ cc_library_static {
|
|||
srcs: libadb_srcs + libadb_linux_srcs + libadb_posix_srcs + [
|
||||
"daemon/auth.cpp",
|
||||
"daemon/jdwp_service.cpp",
|
||||
"daemon/adb_wifi.cpp",
|
||||
],
|
||||
|
||||
local_include_dirs: [
|
||||
|
@ -430,6 +451,9 @@ cc_library_static {
|
|||
|
||||
shared_libs: [
|
||||
"libadb_crypto",
|
||||
"libadb_pairing_connection",
|
||||
"libadb_protos",
|
||||
"libadb_tls_connection",
|
||||
"libadbd_auth",
|
||||
"libasyncio",
|
||||
"libbase",
|
||||
|
@ -484,6 +508,10 @@ cc_library {
|
|||
],
|
||||
|
||||
shared_libs: [
|
||||
"libadb_crypto",
|
||||
"libadb_pairing_connection",
|
||||
"libadb_protos",
|
||||
"libadb_tls_connection",
|
||||
"libadbd_auth",
|
||||
"libasyncio",
|
||||
"libbase",
|
||||
|
@ -532,6 +560,9 @@ cc_library {
|
|||
],
|
||||
|
||||
shared_libs: [
|
||||
"libadb_crypto",
|
||||
"libadb_pairing_connection",
|
||||
"libadb_tls_connection",
|
||||
"libadbd_auth",
|
||||
"libadbd_services",
|
||||
"libasyncio",
|
||||
|
@ -580,9 +611,14 @@ cc_binary {
|
|||
"libmdnssd",
|
||||
"libminijail",
|
||||
"libselinux",
|
||||
"libssl",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libadb_crypto",
|
||||
"libadb_pairing_connection",
|
||||
"libadb_protos",
|
||||
"libadb_tls_connection",
|
||||
"libadbd_auth",
|
||||
"libcrypto",
|
||||
],
|
||||
|
@ -659,6 +695,9 @@ cc_test {
|
|||
static_libs: [
|
||||
"libadbd",
|
||||
"libadbd_auth",
|
||||
"libadb_crypto_static",
|
||||
"libadb_pairing_connection_static",
|
||||
"libadb_tls_connection_static",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libcrypto_utils",
|
||||
|
@ -773,8 +812,12 @@ cc_test_host {
|
|||
"fastdeploy/deploypatchgenerator/patch_utils_test.cpp",
|
||||
],
|
||||
static_libs: [
|
||||
"libadb_crypto",
|
||||
"libadb_crypto_static",
|
||||
"libadb_host",
|
||||
"libadb_pairing_auth_static",
|
||||
"libadb_pairing_connection_static",
|
||||
"libadb_protos_static",
|
||||
"libadb_tls_connection_static",
|
||||
"libandroidfw",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
|
@ -785,6 +828,7 @@ cc_test_host {
|
|||
"liblog",
|
||||
"libmdnssd",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libssl",
|
||||
"libusb",
|
||||
"libutils",
|
||||
"libziparchive",
|
||||
|
|
|
@ -38,6 +38,7 @@ void adb_auth_init();
|
|||
int adb_auth_keygen(const char* filename);
|
||||
int adb_auth_pubkey(const char* filename);
|
||||
std::string adb_auth_get_userkey();
|
||||
bssl::UniquePtr<EVP_PKEY> adb_auth_get_user_privkey();
|
||||
std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys();
|
||||
|
||||
void send_auth_response(const char* token, size_t token_size, atransport* t);
|
||||
|
|
37
adb/adb_wifi.h
Normal file
37
adb/adb_wifi.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 <string>
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
#if ADB_HOST
|
||||
|
||||
void adb_wifi_init(void);
|
||||
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);
|
||||
|
||||
#else // !ADB_HOST
|
||||
|
||||
struct AdbdAuthContext;
|
||||
|
||||
void adbd_wifi_init(AdbdAuthContext* ctx);
|
||||
void adbd_wifi_secure_connect(atransport* t);
|
||||
|
||||
#endif
|
|
@ -91,12 +91,15 @@ 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)>;
|
||||
const char* _Nonnull service_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,
|
||||
void adb_secure_foreach_pairing_service(const char* _Nullable service_name,
|
||||
adb_secure_foreach_service_callback cb);
|
||||
void adb_secure_foreach_connect_service(const char* _Nullable host_name,
|
||||
void adb_secure_foreach_connect_service(const char* _Nullable service_name,
|
||||
adb_secure_foreach_service_callback cb);
|
||||
// Tries to connect to a |service_name| if found. Returns true if found and
|
||||
// connected, false otherwise.
|
||||
bool adb_secure_connect_by_service_name(const char* _Nonnull service_name);
|
||||
|
|
246
adb/client/adb_wifi.cpp
Normal file
246
adb/client/adb_wifi.cpp
Normal file
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 "adb_wifi.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
|
||||
#include <adb/crypto/key.h>
|
||||
#include <adb/crypto/x509_generator.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/parsenetaddress.h>
|
||||
#include "client/pairing/pairing_client.h"
|
||||
|
||||
#include "adb_auth.h"
|
||||
#include "adb_known_hosts.pb.h"
|
||||
#include "adb_utils.h"
|
||||
#include "client/adb_client.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
using adbwifi::pairing::PairingClient;
|
||||
using namespace adb::crypto;
|
||||
|
||||
struct PairingResultWaiter {
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cv_;
|
||||
std::optional<bool> is_valid_;
|
||||
PeerInfo peer_info_;
|
||||
|
||||
static void OnResult(const PeerInfo* peer_info, void* opaque) {
|
||||
CHECK(opaque);
|
||||
auto* p = reinterpret_cast<PairingResultWaiter*>(opaque);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(p->mutex_);
|
||||
if (peer_info) {
|
||||
memcpy(&(p->peer_info_), peer_info, sizeof(PeerInfo));
|
||||
}
|
||||
p->is_valid_ = (peer_info != nullptr);
|
||||
}
|
||||
p->cv_.notify_one();
|
||||
}
|
||||
}; // PairingResultWaiter
|
||||
|
||||
void adb_wifi_init() {}
|
||||
|
||||
static std::vector<uint8_t> stringToUint8(const std::string& str) {
|
||||
auto* p8 = reinterpret_cast<const uint8_t*>(str.data());
|
||||
return std::vector<uint8_t>(p8, p8 + str.length());
|
||||
}
|
||||
|
||||
// Tries to replace the |old_file| with |new_file|.
|
||||
// On success, then |old_file| has been removed and replaced with the
|
||||
// contents of |new_file|, |new_file| will be removed, and only |old_file| will
|
||||
// remain.
|
||||
// On failure, both files will be unchanged.
|
||||
// |new_file| must exist, but |old_file| does not need to exist.
|
||||
bool SafeReplaceFile(std::string_view old_file, std::string_view new_file) {
|
||||
std::string to_be_deleted(old_file);
|
||||
to_be_deleted += ".tbd";
|
||||
|
||||
bool old_renamed = true;
|
||||
if (adb_rename(old_file.data(), to_be_deleted.c_str()) != 0) {
|
||||
// Don't exit here. This is not necessarily an error, because |old_file|
|
||||
// may not exist.
|
||||
PLOG(INFO) << "Failed to rename " << old_file;
|
||||
old_renamed = false;
|
||||
}
|
||||
|
||||
if (adb_rename(new_file.data(), old_file.data()) != 0) {
|
||||
PLOG(ERROR) << "Unable to rename file (" << new_file << " => " << old_file << ")";
|
||||
if (old_renamed) {
|
||||
// Rename the .tbd file back to it's original name
|
||||
adb_rename(to_be_deleted.c_str(), old_file.data());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
adb_unlink(to_be_deleted.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string get_user_known_hosts_path() {
|
||||
return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb_known_hosts.pb";
|
||||
}
|
||||
|
||||
bool load_known_hosts_from_file(const std::string& path, adb::proto::AdbKnownHosts& known_hosts) {
|
||||
// Check for file existence.
|
||||
struct stat buf;
|
||||
if (stat(path.c_str(), &buf) == -1) {
|
||||
LOG(INFO) << "Known hosts file [" << path << "] does not exist...";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
if (!file) {
|
||||
PLOG(ERROR) << "Unable to open [" << path << "].";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!known_hosts.ParseFromIstream(&file)) {
|
||||
PLOG(ERROR) << "Failed to parse [" << path << "]. Deleting it as it may be corrupted.";
|
||||
adb_unlink(path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write_known_host_to_file(std::string& known_host) {
|
||||
std::string path = get_user_known_hosts_path();
|
||||
if (path.empty()) {
|
||||
PLOG(ERROR) << "Error getting user known hosts filename";
|
||||
return false;
|
||||
}
|
||||
|
||||
adb::proto::AdbKnownHosts known_hosts;
|
||||
load_known_hosts_from_file(path, known_hosts);
|
||||
auto* host_info = known_hosts.add_host_infos();
|
||||
host_info->set_guid(known_host);
|
||||
|
||||
std::unique_ptr<TemporaryFile> temp_file(new TemporaryFile(adb_get_android_dir_path()));
|
||||
if (temp_file->fd == -1) {
|
||||
PLOG(ERROR) << "Failed to open [" << temp_file->path << "] for writing";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!known_hosts.SerializeToFileDescriptor(temp_file->fd)) {
|
||||
LOG(ERROR) << "Unable to write out adb_knowns_hosts";
|
||||
return false;
|
||||
}
|
||||
temp_file->DoNotRemove();
|
||||
std::string temp_file_name(temp_file->path);
|
||||
temp_file.reset();
|
||||
|
||||
// Replace the existing adb_known_hosts with the new one
|
||||
if (!SafeReplaceFile(path, temp_file_name.c_str())) {
|
||||
LOG(ERROR) << "Failed to replace old adb_known_hosts";
|
||||
adb_unlink(temp_file_name.c_str());
|
||||
return false;
|
||||
}
|
||||
chmod(path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool adb_wifi_is_known_host(const std::string& host) {
|
||||
std::string path = get_user_known_hosts_path();
|
||||
if (path.empty()) {
|
||||
PLOG(ERROR) << "Error getting user known hosts filename";
|
||||
return false;
|
||||
}
|
||||
|
||||
adb::proto::AdbKnownHosts known_hosts;
|
||||
if (!load_known_hosts_from_file(path, known_hosts)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& host_info : known_hosts.host_infos()) {
|
||||
if (host == host_info.guid()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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 priv_key = adb_auth_get_user_privkey();
|
||||
auto x509_cert = GenerateX509Certificate(priv_key.get());
|
||||
if (!x509_cert) {
|
||||
LOG(ERROR) << "Unable to create X509 certificate for pairing";
|
||||
return;
|
||||
}
|
||||
auto cert_str = X509ToPEMString(x509_cert.get());
|
||||
auto priv_str = Key::ToPEMString(priv_key.get());
|
||||
|
||||
// Send our public key on pairing success
|
||||
PeerInfo system_info = {};
|
||||
system_info.type = ADB_RSA_PUB_KEY;
|
||||
std::string public_key = adb_auth_get_userkey();
|
||||
CHECK_LE(public_key.size(), sizeof(system_info.data) - 1); // -1 for null byte
|
||||
memcpy(system_info.data, public_key.data(), public_key.size());
|
||||
|
||||
auto pswd8 = stringToUint8(password);
|
||||
auto cert8 = stringToUint8(cert_str);
|
||||
auto priv8 = stringToUint8(priv_str);
|
||||
|
||||
auto client = PairingClient::Create(pswd8, system_info, cert8, priv8);
|
||||
if (client == nullptr) {
|
||||
response = "Failed: unable to create pairing client.";
|
||||
return;
|
||||
}
|
||||
|
||||
PairingResultWaiter waiter;
|
||||
std::unique_lock<std::mutex> lock(waiter.mutex_);
|
||||
if (!client->Start(host, waiter.OnResult, &waiter)) {
|
||||
response = "Failed: Unable to start pairing client.";
|
||||
return;
|
||||
}
|
||||
waiter.cv_.wait(lock, [&]() { return waiter.is_valid_.has_value(); });
|
||||
if (!*(waiter.is_valid_)) {
|
||||
response = "Failed: Wrong password or connection was dropped.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (waiter.peer_info_.type != ADB_DEVICE_GUID) {
|
||||
response = "Failed: Successfully paired but server returned unknown response=";
|
||||
response += waiter.peer_info_.type;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string device_guid = reinterpret_cast<const char*>(waiter.peer_info_.data);
|
||||
response = "Successfully paired to " + host + " [guid=" + device_guid + "]";
|
||||
|
||||
// Write to adb_known_hosts
|
||||
write_known_host_to_file(device_guid);
|
||||
// Try to auto-connect.
|
||||
adb_secure_connect_by_service_name(device_guid.c_str());
|
||||
}
|
|
@ -279,6 +279,28 @@ static bool pubkey_from_privkey(std::string* out, const std::string& path) {
|
|||
return CalculatePublicKey(out, privkey.get());
|
||||
}
|
||||
|
||||
bssl::UniquePtr<EVP_PKEY> adb_auth_get_user_privkey() {
|
||||
std::string path = get_user_key_path();
|
||||
if (path.empty()) {
|
||||
PLOG(ERROR) << "Error getting user key filename";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<RSA> rsa_privkey = read_key_file(path);
|
||||
if (!rsa_privkey) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
|
||||
if (!pkey) {
|
||||
LOG(ERROR) << "Failed to allocate key";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EVP_PKEY_set1_RSA(pkey.get(), rsa_privkey.get());
|
||||
return pkey;
|
||||
}
|
||||
|
||||
std::string adb_auth_get_userkey() {
|
||||
std::string path = get_user_key_path();
|
||||
if (path.empty()) {
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
@ -97,8 +98,10 @@ static void help() {
|
|||
" version show version num\n"
|
||||
"\n"
|
||||
"networking:\n"
|
||||
" connect HOST[:PORT] connect to a device via TCP/IP\n"
|
||||
" disconnect [[HOST]:PORT] disconnect from given TCP/IP device, or all\n"
|
||||
" 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"
|
||||
" forward --list list all forward socket connections\n"
|
||||
" forward [--no-rebind] LOCAL REMOTE\n"
|
||||
" forward socket connection using:\n"
|
||||
|
@ -1638,6 +1641,19 @@ int adb_commandline(int argc, const char** argv) {
|
|||
return adb_query_command(query);
|
||||
} 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>]");
|
||||
|
||||
std::string password;
|
||||
printf("Enter pairing code: ");
|
||||
fflush(stdout);
|
||||
if (!std::getline(std::cin, password) || password.empty()) {
|
||||
error_exit("No pairing code provided");
|
||||
}
|
||||
std::string query =
|
||||
android::base::StringPrintf("host:pair:%s:%s", password.c_str(), argv[1]);
|
||||
|
||||
return adb_query_command(query);
|
||||
} else if (!strcmp(argv[0], "emu")) {
|
||||
return adb_send_emulator_command(argc, argv, serial);
|
||||
} else if (!strcmp(argv[0], "shell")) {
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "adb_client.h"
|
||||
#include "adb_listeners.h"
|
||||
#include "adb_utils.h"
|
||||
#include "adb_wifi.h"
|
||||
#include "commandline.h"
|
||||
#include "sysdeps/chrono.h"
|
||||
#include "transport.h"
|
||||
|
@ -118,6 +119,7 @@ int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply
|
|||
init_transport_registration();
|
||||
init_reconnect_handler();
|
||||
|
||||
adb_wifi_init();
|
||||
if (!getenv("ADB_MDNS") || strcmp(getenv("ADB_MDNS"), "0") != 0) {
|
||||
init_mdns_transport_discovery();
|
||||
}
|
||||
|
|
172
adb/client/pairing/pairing_client.cpp
Normal file
172
adb/client/pairing/pairing_client.cpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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/pairing/pairing_client.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <iomanip>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parsenetaddress.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <cutils/sockets.h>
|
||||
#include "sysdeps.h"
|
||||
|
||||
namespace adbwifi {
|
||||
namespace pairing {
|
||||
|
||||
using android::base::unique_fd;
|
||||
|
||||
namespace {
|
||||
|
||||
struct ConnectionDeleter {
|
||||
void operator()(PairingConnectionCtx* p) { pairing_connection_destroy(p); }
|
||||
}; // ConnectionDeleter
|
||||
using ConnectionPtr = std::unique_ptr<PairingConnectionCtx, ConnectionDeleter>;
|
||||
|
||||
class PairingClientImpl : public PairingClient {
|
||||
public:
|
||||
virtual ~PairingClientImpl();
|
||||
|
||||
explicit PairingClientImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
|
||||
const Data& priv_key);
|
||||
|
||||
// Starts the pairing client. This call is non-blocking. Upon pairing
|
||||
// completion, |cb| will be called with the PeerInfo on success,
|
||||
// or an empty value on failure.
|
||||
//
|
||||
// Returns true if PairingClient was successfully started. Otherwise,
|
||||
// return false.
|
||||
virtual bool Start(std::string_view ip_addr, pairing_client_result_cb cb,
|
||||
void* opaque) override;
|
||||
|
||||
static void OnPairingResult(const PeerInfo* peer_info, int fd, void* opaque);
|
||||
|
||||
private:
|
||||
// Setup and start the PairingConnection
|
||||
bool StartConnection();
|
||||
|
||||
enum class State {
|
||||
Ready,
|
||||
Running,
|
||||
Stopped,
|
||||
};
|
||||
|
||||
State state_ = State::Ready;
|
||||
Data pswd_;
|
||||
PeerInfo peer_info_;
|
||||
Data cert_;
|
||||
Data priv_key_;
|
||||
std::string host_;
|
||||
int port_;
|
||||
|
||||
ConnectionPtr connection_;
|
||||
pairing_client_result_cb cb_;
|
||||
void* opaque_ = nullptr;
|
||||
}; // PairingClientImpl
|
||||
|
||||
PairingClientImpl::PairingClientImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
|
||||
const Data& priv_key)
|
||||
: pswd_(pswd), peer_info_(peer_info), cert_(cert), priv_key_(priv_key) {
|
||||
CHECK(!pswd_.empty() && !cert_.empty() && !priv_key_.empty());
|
||||
|
||||
state_ = State::Ready;
|
||||
}
|
||||
|
||||
PairingClientImpl::~PairingClientImpl() {
|
||||
// Make sure to kill the PairingConnection before terminating the fdevent
|
||||
// looper.
|
||||
if (connection_ != nullptr) {
|
||||
connection_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool PairingClientImpl::Start(std::string_view ip_addr, pairing_client_result_cb cb, void* opaque) {
|
||||
CHECK(!ip_addr.empty());
|
||||
cb_ = cb;
|
||||
opaque_ = opaque;
|
||||
|
||||
if (state_ != State::Ready) {
|
||||
LOG(ERROR) << "PairingClient already running or finished";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to parse the host address
|
||||
std::string err;
|
||||
CHECK(android::base::ParseNetAddress(std::string(ip_addr), &host_, &port_, nullptr, &err));
|
||||
CHECK(port_ > 0 && port_ <= 65535);
|
||||
|
||||
if (!StartConnection()) {
|
||||
LOG(ERROR) << "Unable to start PairingClient connection";
|
||||
state_ = State::Stopped;
|
||||
return false;
|
||||
}
|
||||
|
||||
state_ = State::Running;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PairingClientImpl::StartConnection() {
|
||||
std::string err;
|
||||
const int timeout = 10; // seconds
|
||||
unique_fd fd(network_connect(host_, port_, SOCK_STREAM, timeout, &err));
|
||||
if (fd.get() == -1) {
|
||||
LOG(ERROR) << "Failed to start pairing connection client [" << err << "]";
|
||||
return false;
|
||||
}
|
||||
int off = 1;
|
||||
adb_setsockopt(fd.get(), IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
|
||||
|
||||
connection_ = ConnectionPtr(
|
||||
pairing_connection_client_new(pswd_.data(), pswd_.size(), &peer_info_, cert_.data(),
|
||||
cert_.size(), priv_key_.data(), priv_key_.size()));
|
||||
CHECK(connection_);
|
||||
|
||||
if (!pairing_connection_start(connection_.get(), fd.release(), OnPairingResult, this)) {
|
||||
LOG(ERROR) << "PairingClient failed to start the PairingConnection";
|
||||
state_ = State::Stopped;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void PairingClientImpl::OnPairingResult(const PeerInfo* peer_info, int /* fd */, void* opaque) {
|
||||
auto* p = reinterpret_cast<PairingClientImpl*>(opaque);
|
||||
p->cb_(peer_info, p->opaque_);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
std::unique_ptr<PairingClient> PairingClient::Create(const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& cert, const Data& priv_key) {
|
||||
CHECK(!pswd.empty());
|
||||
CHECK(!cert.empty());
|
||||
CHECK(!priv_key.empty());
|
||||
|
||||
return std::unique_ptr<PairingClient>(new PairingClientImpl(pswd, peer_info, cert, priv_key));
|
||||
}
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adbwifi
|
68
adb/client/pairing/pairing_client.h
Normal file
68
adb/client/pairing/pairing_client.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "adb/pairing/pairing_connection.h"
|
||||
|
||||
namespace adbwifi {
|
||||
namespace pairing {
|
||||
|
||||
typedef void (*pairing_client_result_cb)(const PeerInfo*, void*);
|
||||
|
||||
// PairingClient is the client side of the PairingConnection protocol. It will
|
||||
// attempt to connect to a PairingServer specified at |host| and |port|, and
|
||||
// allocate a new PairingConnection for processing.
|
||||
//
|
||||
// See pairing_connection_test.cpp for example usage.
|
||||
//
|
||||
class PairingClient {
|
||||
public:
|
||||
using Data = std::vector<uint8_t>;
|
||||
|
||||
virtual ~PairingClient() = default;
|
||||
|
||||
// Starts the pairing client. This call is non-blocking. Upon completion,
|
||||
// if the pairing was successful, then |cb| will be called with the PeerInfo
|
||||
// containing the info of the trusted peer. Otherwise, |cb| will be
|
||||
// called with an empty value. Start can only be called once in the lifetime
|
||||
// of this object.
|
||||
//
|
||||
// Returns true if PairingClient was successfully started. Otherwise,
|
||||
// returns false.
|
||||
virtual bool Start(std::string_view ip_addr, pairing_client_result_cb cb, void* opaque) = 0;
|
||||
|
||||
// Creates a new PairingClient instance. May return null if unable
|
||||
// to create an instance. |pswd|, |certificate|, |priv_key| and
|
||||
// |ip_addr| cannot be empty. |peer_info| must contain non-empty strings for
|
||||
// the guid and name fields.
|
||||
static std::unique_ptr<PairingClient> Create(const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& certificate, const Data& priv_key);
|
||||
|
||||
protected:
|
||||
PairingClient() = default;
|
||||
}; // class PairingClient
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adbwifi
|
473
adb/client/pairing/tests/pairing_connection_test.cpp
Normal file
473
adb/client/pairing/tests/pairing_connection_test.cpp
Normal file
|
@ -0,0 +1,473 @@
|
|||
/*
|
||||
* Copyright 2019 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "AdbWifiPairingConnectionTest"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <adbwifi/pairing/pairing_server.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "adb/client/pairing/tests/pairing_client.h"
|
||||
|
||||
namespace adbwifi {
|
||||
namespace pairing {
|
||||
|
||||
static const std::string kTestServerCert =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIBljCCAT2gAwIBAgIBATAKBggqhkjOPQQDAjAzMQswCQYDVQQGEwJVUzEQMA4G\n"
|
||||
"A1UECgwHQW5kcm9pZDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTE5MTEwNzAyMDkx\n"
|
||||
"NVoXDTI5MTEwNDAyMDkxNVowMzELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJv\n"
|
||||
"aWQxEjAQBgNVBAMMCWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA\n"
|
||||
"BCXRovy3RhtK0Khle48vUmkcuI0OF7K8o9sVPE4oVnp24l+cCYr3BtrgifoHPgj4\n"
|
||||
"vq7n105qzK7ngBHH+LBmYIijQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/\n"
|
||||
"BAQDAgGGMB0GA1UdDgQWBBQi4eskzqVG3SCX2CwJF/aTZqUcuTAKBggqhkjOPQQD\n"
|
||||
"AgNHADBEAiBPYvLOCIvPDtq3vMF7A2z7t7JfcCmbC7g8ftEVJucJBwIgepf+XjTb\n"
|
||||
"L7RCE16p7iVkpHUrWAOl7zDxqD+jaji5MkQ=\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
static const std::string kTestServerPrivKey =
|
||||
"-----BEGIN PRIVATE KEY-----\n"
|
||||
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgSCaskWPtutIgh8uQ\n"
|
||||
"UBH6ZIea5Kxm7m6kkGNkd8FYPSOhRANCAAQl0aL8t0YbStCoZXuPL1JpHLiNDhey\n"
|
||||
"vKPbFTxOKFZ6duJfnAmK9wba4In6Bz4I+L6u59dOasyu54ARx/iwZmCI\n"
|
||||
"-----END PRIVATE KEY-----\n";
|
||||
|
||||
static const std::string kTestClientCert =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIBlzCCAT2gAwIBAgIBATAKBggqhkjOPQQDAjAzMQswCQYDVQQGEwJVUzEQMA4G\n"
|
||||
"A1UECgwHQW5kcm9pZDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTE5MTEwOTAxNTAy\n"
|
||||
"OFoXDTI5MTEwNjAxNTAyOFowMzELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJv\n"
|
||||
"aWQxEjAQBgNVBAMMCWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA\n"
|
||||
"BGW+RuoEIzbt42zAuZzbXaC0bvh8n4OLFDnqkkW6kWA43GYg/mUMVc9vg/nuxyuM\n"
|
||||
"aT0KqbTaLhm+NjCXVRnxBrajQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/\n"
|
||||
"BAQDAgGGMB0GA1UdDgQWBBTjCaC8/NXgdBz9WlMVCNwhx7jn0jAKBggqhkjOPQQD\n"
|
||||
"AgNIADBFAiB/xp2boj7b1KK2saS6BL59deo/TvfgZ+u8HPq4k4VP3gIhAMXswp9W\n"
|
||||
"XdlziccQdj+0KpbUojDKeHOr4fIj/+LxsWPa\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
static const std::string kTestClientPrivKey =
|
||||
"-----BEGIN PRIVATE KEY-----\n"
|
||||
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgFw/CWY1f6TSB70AF\n"
|
||||
"yVe8n6QdYFu8HW5t/tij2SrXx42hRANCAARlvkbqBCM27eNswLmc212gtG74fJ+D\n"
|
||||
"ixQ56pJFupFgONxmIP5lDFXPb4P57scrjGk9Cqm02i4ZvjYwl1UZ8Qa2\n"
|
||||
"-----END PRIVATE KEY-----\n";
|
||||
|
||||
class AdbWifiPairingConnectionTest : public testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() override {}
|
||||
|
||||
virtual void TearDown() override {}
|
||||
|
||||
void initPairing(const std::vector<uint8_t> server_pswd,
|
||||
const std::vector<uint8_t> client_pswd) {
|
||||
std::vector<uint8_t> cert;
|
||||
std::vector<uint8_t> key;
|
||||
// Include the null-byte as well.
|
||||
cert.assign(reinterpret_cast<const uint8_t*>(kTestServerCert.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestServerCert.data()) +
|
||||
kTestServerCert.size() + 1);
|
||||
key.assign(reinterpret_cast<const uint8_t*>(kTestServerPrivKey.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestServerPrivKey.data()) +
|
||||
kTestServerPrivKey.size() + 1);
|
||||
server_ = PairingServer::create(server_pswd, server_info_, cert, key, kDefaultPairingPort);
|
||||
cert.assign(reinterpret_cast<const uint8_t*>(kTestClientCert.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestClientCert.data()) +
|
||||
kTestClientCert.size() + 1);
|
||||
key.assign(reinterpret_cast<const uint8_t*>(kTestClientPrivKey.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestClientPrivKey.data()) +
|
||||
kTestClientPrivKey.size() + 1);
|
||||
client_ = PairingClient::create(client_pswd, client_info_, cert, key, "127.0.0.1");
|
||||
}
|
||||
|
||||
std::unique_ptr<PairingServer> createServer(const std::vector<uint8_t> pswd) {
|
||||
std::vector<uint8_t> cert;
|
||||
std::vector<uint8_t> key;
|
||||
// Include the null-byte as well.
|
||||
cert.assign(reinterpret_cast<const uint8_t*>(kTestServerCert.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestServerCert.data()) +
|
||||
kTestServerCert.size() + 1);
|
||||
key.assign(reinterpret_cast<const uint8_t*>(kTestServerPrivKey.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestServerPrivKey.data()) +
|
||||
kTestServerPrivKey.size() + 1);
|
||||
return PairingServer::create(pswd, server_info_, cert, key, kDefaultPairingPort);
|
||||
}
|
||||
|
||||
std::unique_ptr<PairingClient> createClient(const std::vector<uint8_t> pswd) {
|
||||
std::vector<uint8_t> cert;
|
||||
std::vector<uint8_t> key;
|
||||
// Include the null-byte as well.
|
||||
cert.assign(reinterpret_cast<const uint8_t*>(kTestClientCert.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestClientCert.data()) +
|
||||
kTestClientCert.size() + 1);
|
||||
key.assign(reinterpret_cast<const uint8_t*>(kTestClientPrivKey.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestClientPrivKey.data()) +
|
||||
kTestClientPrivKey.size() + 1);
|
||||
return PairingClient::create(pswd, client_info_, cert, key, "127.0.0.1");
|
||||
}
|
||||
|
||||
std::unique_ptr<PairingServer> server_;
|
||||
const PeerInfo server_info_ = {
|
||||
.name = "my_server_name",
|
||||
.guid = "my_server_guid",
|
||||
};
|
||||
std::unique_ptr<PairingClient> client_;
|
||||
const PeerInfo client_info_ = {
|
||||
.name = "my_client_name",
|
||||
.guid = "my_client_guid",
|
||||
};
|
||||
};
|
||||
|
||||
TEST_F(AdbWifiPairingConnectionTest, ServerCreation) {
|
||||
// All parameters bad
|
||||
auto server = PairingServer::create({}, {}, {}, {}, -1);
|
||||
EXPECT_EQ(nullptr, server);
|
||||
// Bad password
|
||||
server = PairingServer::create({}, server_info_, {0x01}, {0x01}, -1);
|
||||
EXPECT_EQ(nullptr, server);
|
||||
// Bad peer_info
|
||||
server = PairingServer::create({0x01}, {}, {0x01}, {0x01}, -1);
|
||||
EXPECT_EQ(nullptr, server);
|
||||
// Bad certificate
|
||||
server = PairingServer::create({0x01}, server_info_, {}, {0x01}, -1);
|
||||
EXPECT_EQ(nullptr, server);
|
||||
// Bad private key
|
||||
server = PairingServer::create({0x01}, server_info_, {0x01}, {}, -1);
|
||||
EXPECT_EQ(nullptr, server);
|
||||
// Bad port
|
||||
server = PairingServer::create({0x01}, server_info_, {0x01}, {0x01}, -1);
|
||||
EXPECT_EQ(nullptr, server);
|
||||
// Valid params
|
||||
server = PairingServer::create({0x01}, server_info_, {0x01}, {0x01}, 7776);
|
||||
EXPECT_NE(nullptr, server);
|
||||
}
|
||||
|
||||
TEST_F(AdbWifiPairingConnectionTest, ClientCreation) {
|
||||
// All parameters bad
|
||||
auto client = PairingClient::create({}, client_info_, {}, {}, "");
|
||||
EXPECT_EQ(nullptr, client);
|
||||
// Bad password
|
||||
client = PairingClient::create({}, client_info_, {0x01}, {0x01}, "127.0.0.1");
|
||||
EXPECT_EQ(nullptr, client);
|
||||
// Bad peer_info
|
||||
client = PairingClient::create({0x01}, {}, {0x01}, {0x01}, "127.0.0.1");
|
||||
EXPECT_EQ(nullptr, client);
|
||||
// Bad certificate
|
||||
client = PairingClient::create({0x01}, client_info_, {}, {0x01}, "127.0.0.1");
|
||||
EXPECT_EQ(nullptr, client);
|
||||
// Bad private key
|
||||
client = PairingClient::create({0x01}, client_info_, {0x01}, {}, "127.0.0.1");
|
||||
EXPECT_EQ(nullptr, client);
|
||||
// Bad ip address
|
||||
client = PairingClient::create({0x01}, client_info_, {0x01}, {0x01}, "");
|
||||
EXPECT_EQ(nullptr, client);
|
||||
// Valid params
|
||||
client = PairingClient::create({0x01}, client_info_, {0x01}, {0x01}, "127.0.0.1");
|
||||
EXPECT_NE(nullptr, client);
|
||||
}
|
||||
|
||||
TEST_F(AdbWifiPairingConnectionTest, SmokeValidPairing) {
|
||||
std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
|
||||
initPairing(pswd, pswd);
|
||||
|
||||
// Start the server first, to open the port for connections
|
||||
std::mutex server_mutex;
|
||||
std::condition_variable server_cv;
|
||||
std::unique_lock<std::mutex> server_lock(server_mutex);
|
||||
|
||||
auto server_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
ASSERT_NE(nullptr, peer_info);
|
||||
ASSERT_NE(nullptr, cert);
|
||||
EXPECT_FALSE(cert->empty());
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
// Verify the peer_info and cert
|
||||
ASSERT_EQ(strlen(peer_info->name), strlen(client_info_.name));
|
||||
EXPECT_EQ(::memcmp(peer_info->name, client_info_.name, strlen(client_info_.name)), 0);
|
||||
ASSERT_EQ(strlen(peer_info->guid), strlen(client_info_.guid));
|
||||
EXPECT_EQ(::memcmp(peer_info->guid, client_info_.guid, strlen(client_info_.guid)), 0);
|
||||
ASSERT_EQ(cert->size(), kTestClientCert.size() + 1);
|
||||
EXPECT_EQ(::memcmp(cert->data(), kTestClientCert.data(), kTestClientCert.size() + 1), 0);
|
||||
|
||||
std::lock_guard<std::mutex> lock(server_mutex);
|
||||
server_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(server_->start(server_callback, nullptr));
|
||||
|
||||
// Start the client
|
||||
bool got_valid_pairing = false;
|
||||
std::mutex client_mutex;
|
||||
std::condition_variable client_cv;
|
||||
std::unique_lock<std::mutex> client_lock(client_mutex);
|
||||
auto client_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
ASSERT_NE(nullptr, peer_info);
|
||||
ASSERT_NE(nullptr, cert);
|
||||
EXPECT_FALSE(cert->empty());
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
// Verify the peer_info and cert
|
||||
ASSERT_EQ(strlen(peer_info->name), strlen(server_info_.name));
|
||||
EXPECT_EQ(::memcmp(peer_info->name, server_info_.name, strlen(server_info_.name)), 0);
|
||||
ASSERT_EQ(strlen(peer_info->guid), strlen(server_info_.guid));
|
||||
EXPECT_EQ(::memcmp(peer_info->guid, server_info_.guid, strlen(server_info_.guid)), 0);
|
||||
ASSERT_EQ(cert->size(), kTestServerCert.size() + 1);
|
||||
EXPECT_EQ(::memcmp(cert->data(), kTestServerCert.data(), kTestServerCert.size() + 1), 0);
|
||||
|
||||
got_valid_pairing = (peer_info != nullptr && cert != nullptr && !cert->empty());
|
||||
std::lock_guard<std::mutex> lock(client_mutex);
|
||||
client_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(client_->start(client_callback, nullptr));
|
||||
client_cv.wait(client_lock);
|
||||
|
||||
// Kill server if the pairing failed, since server only shuts down when
|
||||
// it gets a valid pairing.
|
||||
if (!got_valid_pairing) {
|
||||
server_lock.unlock();
|
||||
server_.reset();
|
||||
} else {
|
||||
server_cv.wait(server_lock);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(AdbWifiPairingConnectionTest, CancelPairing) {
|
||||
std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
|
||||
std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
|
||||
initPairing(pswd, pswd2);
|
||||
|
||||
// Start the server first, to open the port for connections
|
||||
std::mutex server_mutex;
|
||||
std::condition_variable server_cv;
|
||||
std::unique_lock<std::mutex> server_lock(server_mutex);
|
||||
|
||||
bool server_got_valid_pairing = true;
|
||||
auto server_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
// Pairing will be cancelled, which should initiate this callback with
|
||||
// empty values.
|
||||
ASSERT_EQ(nullptr, peer_info);
|
||||
ASSERT_EQ(nullptr, cert);
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
std::lock_guard<std::mutex> lock(server_mutex);
|
||||
server_cv.notify_one();
|
||||
server_got_valid_pairing = false;
|
||||
};
|
||||
ASSERT_TRUE(server_->start(server_callback, nullptr));
|
||||
|
||||
// Start the client (should fail because of different passwords).
|
||||
bool got_valid_pairing = false;
|
||||
std::mutex client_mutex;
|
||||
std::condition_variable client_cv;
|
||||
std::unique_lock<std::mutex> client_lock(client_mutex);
|
||||
auto client_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
ASSERT_EQ(nullptr, peer_info);
|
||||
ASSERT_EQ(nullptr, cert);
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
got_valid_pairing = (peer_info != nullptr && cert != nullptr && !cert->empty());
|
||||
std::lock_guard<std::mutex> lock(client_mutex);
|
||||
client_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(client_->start(client_callback, nullptr));
|
||||
client_cv.wait(client_lock);
|
||||
|
||||
server_lock.unlock();
|
||||
// This should trigger the callback to be on the same thread.
|
||||
server_.reset();
|
||||
EXPECT_FALSE(server_got_valid_pairing);
|
||||
}
|
||||
|
||||
TEST_F(AdbWifiPairingConnectionTest, MultipleClientsAllFail) {
|
||||
std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
|
||||
std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
|
||||
|
||||
auto server = createServer(pswd);
|
||||
ASSERT_NE(nullptr, server);
|
||||
// Start the server first, to open the port for connections
|
||||
std::mutex server_mutex;
|
||||
std::condition_variable server_cv;
|
||||
std::unique_lock<std::mutex> server_lock(server_mutex);
|
||||
|
||||
bool server_got_valid_pairing = true;
|
||||
auto server_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
// Pairing will be cancelled, which should initiate this callback with
|
||||
// empty values.
|
||||
ASSERT_EQ(nullptr, peer_info);
|
||||
ASSERT_EQ(nullptr, cert);
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
std::lock_guard<std::mutex> lock(server_mutex);
|
||||
server_cv.notify_one();
|
||||
server_got_valid_pairing = false;
|
||||
};
|
||||
ASSERT_TRUE(server->start(server_callback, nullptr));
|
||||
|
||||
// Start multiple clients, all with bad passwords
|
||||
std::vector<std::unique_ptr<PairingClient>> clients;
|
||||
int num_clients_done = 0;
|
||||
int test_num_clients = 5;
|
||||
std::mutex client_mutex;
|
||||
std::condition_variable client_cv;
|
||||
std::unique_lock<std::mutex> client_lock(client_mutex);
|
||||
while (clients.size() < test_num_clients) {
|
||||
auto client = createClient(pswd2);
|
||||
ASSERT_NE(nullptr, client);
|
||||
auto callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
ASSERT_EQ(nullptr, peer_info);
|
||||
ASSERT_EQ(nullptr, cert);
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(client_mutex);
|
||||
num_clients_done++;
|
||||
}
|
||||
client_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(client->start(callback, nullptr));
|
||||
clients.push_back(std::move(client));
|
||||
}
|
||||
|
||||
client_cv.wait(client_lock, [&]() { return (num_clients_done == test_num_clients); });
|
||||
EXPECT_EQ(num_clients_done, test_num_clients);
|
||||
|
||||
server_lock.unlock();
|
||||
// This should trigger the callback to be on the same thread.
|
||||
server.reset();
|
||||
EXPECT_FALSE(server_got_valid_pairing);
|
||||
}
|
||||
|
||||
TEST_F(AdbWifiPairingConnectionTest, MultipleClientsOnePass) {
|
||||
// Send multiple clients with bad passwords, but send the last one with the
|
||||
// correct password.
|
||||
std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
|
||||
std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
|
||||
|
||||
auto server = createServer(pswd);
|
||||
ASSERT_NE(nullptr, server);
|
||||
// Start the server first, to open the port for connections
|
||||
std::mutex server_mutex;
|
||||
std::condition_variable server_cv;
|
||||
std::unique_lock<std::mutex> server_lock(server_mutex);
|
||||
|
||||
bool server_got_valid_pairing = false;
|
||||
auto server_callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
// Pairing will be cancelled, which should initiate this callback with
|
||||
// empty values.
|
||||
|
||||
ASSERT_NE(nullptr, peer_info);
|
||||
ASSERT_NE(nullptr, cert);
|
||||
EXPECT_FALSE(cert->empty());
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
// Verify the peer_info and cert
|
||||
ASSERT_EQ(strlen(peer_info->name), strlen(client_info_.name));
|
||||
EXPECT_EQ(::memcmp(peer_info->name, client_info_.name, strlen(client_info_.name)), 0);
|
||||
ASSERT_EQ(strlen(peer_info->guid), strlen(client_info_.guid));
|
||||
EXPECT_EQ(::memcmp(peer_info->guid, client_info_.guid, strlen(client_info_.guid)), 0);
|
||||
ASSERT_EQ(cert->size(), kTestClientCert.size() + 1);
|
||||
EXPECT_EQ(::memcmp(cert->data(), kTestClientCert.data(), kTestClientCert.size() + 1), 0);
|
||||
|
||||
std::lock_guard<std::mutex> lock(server_mutex);
|
||||
server_got_valid_pairing = true;
|
||||
server_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(server->start(server_callback, nullptr));
|
||||
|
||||
// Start multiple clients, all with bad passwords (except for the last one)
|
||||
std::vector<std::unique_ptr<PairingClient>> clients;
|
||||
int num_clients_done = 0;
|
||||
int test_num_clients = 5;
|
||||
std::mutex client_mutex;
|
||||
std::condition_variable client_cv;
|
||||
std::unique_lock<std::mutex> client_lock(client_mutex);
|
||||
bool got_valid_pairing = false;
|
||||
while (clients.size() < test_num_clients) {
|
||||
std::unique_ptr<PairingClient> client;
|
||||
if (clients.size() == test_num_clients - 1) {
|
||||
// Make this one have the valid password
|
||||
client = createClient(pswd);
|
||||
ASSERT_NE(nullptr, client);
|
||||
auto callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
ASSERT_NE(nullptr, peer_info);
|
||||
ASSERT_NE(nullptr, cert);
|
||||
EXPECT_FALSE(cert->empty());
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
// Verify the peer_info and cert
|
||||
ASSERT_EQ(strlen(peer_info->name), strlen(server_info_.name));
|
||||
EXPECT_EQ(::memcmp(peer_info->name, server_info_.name, strlen(server_info_.name)),
|
||||
0);
|
||||
ASSERT_EQ(strlen(peer_info->guid), strlen(server_info_.guid));
|
||||
EXPECT_EQ(::memcmp(peer_info->guid, server_info_.guid, strlen(server_info_.guid)),
|
||||
0);
|
||||
ASSERT_EQ(cert->size(), kTestServerCert.size() + 1);
|
||||
EXPECT_EQ(
|
||||
::memcmp(cert->data(), kTestServerCert.data(), kTestServerCert.size() + 1),
|
||||
0);
|
||||
got_valid_pairing = (peer_info != nullptr && cert != nullptr && !cert->empty());
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(client_mutex);
|
||||
num_clients_done++;
|
||||
}
|
||||
client_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(client->start(callback, nullptr));
|
||||
} else {
|
||||
client = createClient(pswd2);
|
||||
ASSERT_NE(nullptr, client);
|
||||
auto callback = [&](const PeerInfo* peer_info, const std::vector<uint8_t>* cert,
|
||||
void* opaque) {
|
||||
ASSERT_EQ(nullptr, peer_info);
|
||||
ASSERT_EQ(nullptr, cert);
|
||||
EXPECT_EQ(nullptr, opaque);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(client_mutex);
|
||||
num_clients_done++;
|
||||
}
|
||||
client_cv.notify_one();
|
||||
};
|
||||
ASSERT_TRUE(client->start(callback, nullptr));
|
||||
}
|
||||
clients.push_back(std::move(client));
|
||||
}
|
||||
|
||||
client_cv.wait(client_lock, [&]() { return (num_clients_done == test_num_clients); });
|
||||
EXPECT_EQ(num_clients_done, test_num_clients);
|
||||
|
||||
// Kill server if the pairing failed, since server only shuts down when
|
||||
// it gets a valid pairing.
|
||||
if (!got_valid_pairing) {
|
||||
server_lock.unlock();
|
||||
server_.reset();
|
||||
} else {
|
||||
server_cv.wait(server_lock);
|
||||
}
|
||||
EXPECT_TRUE(server_got_valid_pairing);
|
||||
}
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adbwifi
|
426
adb/client/pairing/tests/pairing_server.cpp
Normal file
426
adb/client/pairing/tests/pairing_server.cpp
Normal file
|
@ -0,0 +1,426 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 "adbwifi/pairing/pairing_server.h"
|
||||
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <iomanip>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <adbwifi/pairing/pairing_connection.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parsenetaddress.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <cutils/sockets.h>
|
||||
|
||||
namespace adbwifi {
|
||||
namespace pairing {
|
||||
|
||||
using android::base::ScopedLockAssertion;
|
||||
using android::base::unique_fd;
|
||||
|
||||
namespace {
|
||||
|
||||
// The implimentation has two background threads running: one to handle and
|
||||
// accept any new pairing connection requests (socket accept), and the other to
|
||||
// handle connection events (connection started, connection finished).
|
||||
class PairingServerImpl : public PairingServer {
|
||||
public:
|
||||
virtual ~PairingServerImpl();
|
||||
|
||||
// All parameters must be non-empty.
|
||||
explicit PairingServerImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
|
||||
const Data& priv_key, int port);
|
||||
|
||||
// Starts the pairing server. This call is non-blocking. Upon completion,
|
||||
// if the pairing was successful, then |cb| will be called with the PublicKeyHeader
|
||||
// containing the info of the trusted peer. Otherwise, |cb| will be
|
||||
// called with an empty value. Start can only be called once in the lifetime
|
||||
// of this object.
|
||||
//
|
||||
// Returns true if PairingServer was successfully started. Otherwise,
|
||||
// returns false.
|
||||
virtual bool start(PairingConnection::ResultCallback cb, void* opaque) override;
|
||||
|
||||
private:
|
||||
// Setup the server socket to accept incoming connections
|
||||
bool setupServer();
|
||||
// Force stop the server thread.
|
||||
void stopServer();
|
||||
|
||||
// handles a new pairing client connection
|
||||
bool handleNewClientConnection(int fd) EXCLUDES(conn_mutex_);
|
||||
|
||||
// ======== connection events thread =============
|
||||
std::mutex conn_mutex_;
|
||||
std::condition_variable conn_cv_;
|
||||
|
||||
using FdVal = int;
|
||||
using ConnectionPtr = std::unique_ptr<PairingConnection>;
|
||||
using NewConnectionEvent = std::tuple<unique_fd, ConnectionPtr>;
|
||||
// <fd, PeerInfo.name, PeerInfo.guid, certificate>
|
||||
using ConnectionFinishedEvent = std::tuple<FdVal, std::optional<std::string>,
|
||||
std::optional<std::string>, std::optional<Data>>;
|
||||
using ConnectionEvent = std::variant<NewConnectionEvent, ConnectionFinishedEvent>;
|
||||
// Queue for connections to write into. We have a separate queue to read
|
||||
// from, in order to minimize the time the server thread is blocked.
|
||||
std::deque<ConnectionEvent> conn_write_queue_ GUARDED_BY(conn_mutex_);
|
||||
std::deque<ConnectionEvent> conn_read_queue_;
|
||||
// Map of fds to their PairingConnections currently running.
|
||||
std::unordered_map<FdVal, ConnectionPtr> connections_;
|
||||
|
||||
// Two threads launched when starting the pairing server:
|
||||
// 1) A server thread that waits for incoming client connections, and
|
||||
// 2) A connection events thread that synchonizes events from all of the
|
||||
// clients, since each PairingConnection is running in it's own thread.
|
||||
void startConnectionEventsThread();
|
||||
void startServerThread();
|
||||
|
||||
std::thread conn_events_thread_;
|
||||
void connectionEventsWorker();
|
||||
std::thread server_thread_;
|
||||
void serverWorker();
|
||||
bool is_terminate_ GUARDED_BY(conn_mutex_) = false;
|
||||
|
||||
enum class State {
|
||||
Ready,
|
||||
Running,
|
||||
Stopped,
|
||||
};
|
||||
State state_ = State::Ready;
|
||||
Data pswd_;
|
||||
PeerInfo peer_info_;
|
||||
Data cert_;
|
||||
Data priv_key_;
|
||||
int port_ = -1;
|
||||
|
||||
PairingConnection::ResultCallback cb_;
|
||||
void* opaque_ = nullptr;
|
||||
bool got_valid_pairing_ = false;
|
||||
|
||||
static const int kEpollConstSocket = 0;
|
||||
// Used to break the server thread from epoll_wait
|
||||
static const int kEpollConstEventFd = 1;
|
||||
unique_fd epoll_fd_;
|
||||
unique_fd server_fd_;
|
||||
unique_fd event_fd_;
|
||||
}; // PairingServerImpl
|
||||
|
||||
PairingServerImpl::PairingServerImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
|
||||
const Data& priv_key, int port)
|
||||
: pswd_(pswd), peer_info_(peer_info), cert_(cert), priv_key_(priv_key), port_(port) {
|
||||
CHECK(!pswd_.empty() && !cert_.empty() && !priv_key_.empty() && port_ > 0);
|
||||
CHECK('\0' == peer_info.name[kPeerNameLength - 1] &&
|
||||
'\0' == peer_info.guid[kPeerGuidLength - 1] && strlen(peer_info.name) > 0 &&
|
||||
strlen(peer_info.guid) > 0);
|
||||
}
|
||||
|
||||
PairingServerImpl::~PairingServerImpl() {
|
||||
// Since these connections have references to us, let's make sure they
|
||||
// destruct before us.
|
||||
if (server_thread_.joinable()) {
|
||||
stopServer();
|
||||
server_thread_.join();
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(conn_mutex_);
|
||||
is_terminate_ = true;
|
||||
}
|
||||
conn_cv_.notify_one();
|
||||
if (conn_events_thread_.joinable()) {
|
||||
conn_events_thread_.join();
|
||||
}
|
||||
|
||||
// Notify the cb_ if it hasn't already.
|
||||
if (!got_valid_pairing_ && cb_ != nullptr) {
|
||||
cb_(nullptr, nullptr, opaque_);
|
||||
}
|
||||
}
|
||||
|
||||
bool PairingServerImpl::start(PairingConnection::ResultCallback cb, void* opaque) {
|
||||
cb_ = cb;
|
||||
opaque_ = opaque;
|
||||
|
||||
if (state_ != State::Ready) {
|
||||
LOG(ERROR) << "PairingServer already running or stopped";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setupServer()) {
|
||||
LOG(ERROR) << "Unable to start PairingServer";
|
||||
state_ = State::Stopped;
|
||||
return false;
|
||||
}
|
||||
|
||||
state_ = State::Running;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PairingServerImpl::stopServer() {
|
||||
if (event_fd_.get() == -1) {
|
||||
return;
|
||||
}
|
||||
uint64_t value = 1;
|
||||
ssize_t rc = write(event_fd_.get(), &value, sizeof(value));
|
||||
if (rc == -1) {
|
||||
// This can happen if the server didn't start.
|
||||
PLOG(ERROR) << "write to eventfd failed";
|
||||
} else if (rc != sizeof(value)) {
|
||||
LOG(FATAL) << "write to event returned short (" << rc << ")";
|
||||
}
|
||||
}
|
||||
|
||||
bool PairingServerImpl::setupServer() {
|
||||
epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
|
||||
if (epoll_fd_ == -1) {
|
||||
PLOG(ERROR) << "failed to create epoll fd";
|
||||
return false;
|
||||
}
|
||||
|
||||
event_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
|
||||
if (event_fd_ == -1) {
|
||||
PLOG(ERROR) << "failed to create eventfd";
|
||||
return false;
|
||||
}
|
||||
|
||||
server_fd_.reset(socket_inaddr_any_server(port_, SOCK_STREAM));
|
||||
if (server_fd_.get() == -1) {
|
||||
PLOG(ERROR) << "Failed to start pairing connection server";
|
||||
return false;
|
||||
} else if (fcntl(server_fd_.get(), F_SETFD, FD_CLOEXEC) != 0) {
|
||||
PLOG(ERROR) << "Failed to make server socket cloexec";
|
||||
return false;
|
||||
} else if (fcntl(server_fd_.get(), F_SETFD, O_NONBLOCK) != 0) {
|
||||
PLOG(ERROR) << "Failed to make server socket nonblocking";
|
||||
return false;
|
||||
}
|
||||
|
||||
startConnectionEventsThread();
|
||||
startServerThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void PairingServerImpl::startServerThread() {
|
||||
server_thread_ = std::thread([this]() { serverWorker(); });
|
||||
}
|
||||
|
||||
void PairingServerImpl::startConnectionEventsThread() {
|
||||
conn_events_thread_ = std::thread([this]() { connectionEventsWorker(); });
|
||||
}
|
||||
|
||||
void PairingServerImpl::serverWorker() {
|
||||
{
|
||||
struct epoll_event event;
|
||||
event.events = EPOLLIN;
|
||||
event.data.u64 = kEpollConstSocket;
|
||||
CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, server_fd_.get(), &event));
|
||||
}
|
||||
|
||||
{
|
||||
struct epoll_event event;
|
||||
event.events = EPOLLIN;
|
||||
event.data.u64 = kEpollConstEventFd;
|
||||
CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, event_fd_.get(), &event));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
struct epoll_event events[2];
|
||||
int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), events, 2, -1));
|
||||
if (rc == -1) {
|
||||
PLOG(ERROR) << "epoll_wait failed";
|
||||
return;
|
||||
} else if (rc == 0) {
|
||||
LOG(ERROR) << "epoll_wait returned 0";
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rc; ++i) {
|
||||
struct epoll_event& event = events[i];
|
||||
switch (event.data.u64) {
|
||||
case kEpollConstSocket:
|
||||
handleNewClientConnection(server_fd_.get());
|
||||
break;
|
||||
case kEpollConstEventFd:
|
||||
uint64_t dummy;
|
||||
int rc = TEMP_FAILURE_RETRY(read(event_fd_.get(), &dummy, sizeof(dummy)));
|
||||
if (rc != sizeof(dummy)) {
|
||||
PLOG(FATAL) << "failed to read from eventfd (rc=" << rc << ")";
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PairingServerImpl::connectionEventsWorker() {
|
||||
for (;;) {
|
||||
// Transfer the write queue to the read queue.
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(conn_mutex_);
|
||||
ScopedLockAssertion assume_locked(conn_mutex_);
|
||||
|
||||
if (is_terminate_) {
|
||||
// We check |is_terminate_| twice because condition_variable's
|
||||
// notify() only wakes up a thread if it is in the wait state
|
||||
// prior to notify(). Furthermore, we aren't holding the mutex
|
||||
// when processing the events in |conn_read_queue_|.
|
||||
return;
|
||||
}
|
||||
if (conn_write_queue_.empty()) {
|
||||
// We need to wait for new events, or the termination signal.
|
||||
conn_cv_.wait(lock, [this]() REQUIRES(conn_mutex_) {
|
||||
return (is_terminate_ || !conn_write_queue_.empty());
|
||||
});
|
||||
}
|
||||
if (is_terminate_) {
|
||||
// We're done.
|
||||
return;
|
||||
}
|
||||
// Move all events into the read queue.
|
||||
conn_read_queue_ = std::move(conn_write_queue_);
|
||||
conn_write_queue_.clear();
|
||||
}
|
||||
|
||||
// Process all events in the read queue.
|
||||
while (conn_read_queue_.size() > 0) {
|
||||
auto& event = conn_read_queue_.front();
|
||||
if (auto* p = std::get_if<NewConnectionEvent>(&event)) {
|
||||
// Ignore if we are already at the max number of connections
|
||||
if (connections_.size() >= internal::kMaxConnections) {
|
||||
conn_read_queue_.pop_front();
|
||||
continue;
|
||||
}
|
||||
auto [ufd, connection] = std::move(*p);
|
||||
int fd = ufd.release();
|
||||
bool started = connection->start(
|
||||
fd,
|
||||
[fd](const PeerInfo* peer_info, const Data* cert, void* opaque) {
|
||||
auto* p = reinterpret_cast<PairingServerImpl*>(opaque);
|
||||
|
||||
ConnectionFinishedEvent event;
|
||||
if (peer_info != nullptr && cert != nullptr) {
|
||||
event = std::make_tuple(fd, std::string(peer_info->name),
|
||||
std::string(peer_info->guid), Data(*cert));
|
||||
} else {
|
||||
event = std::make_tuple(fd, std::nullopt, std::nullopt,
|
||||
std::nullopt);
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(p->conn_mutex_);
|
||||
p->conn_write_queue_.push_back(std::move(event));
|
||||
}
|
||||
p->conn_cv_.notify_one();
|
||||
},
|
||||
this);
|
||||
if (!started) {
|
||||
LOG(ERROR) << "PairingServer unable to start a PairingConnection fd=" << fd;
|
||||
ufd.reset(fd);
|
||||
} else {
|
||||
connections_[fd] = std::move(connection);
|
||||
}
|
||||
} else if (auto* p = std::get_if<ConnectionFinishedEvent>(&event)) {
|
||||
auto [fd, name, guid, cert] = std::move(*p);
|
||||
if (name.has_value() && guid.has_value() && cert.has_value() && !name->empty() &&
|
||||
!guid->empty() && !cert->empty()) {
|
||||
// Valid pairing. Let's shutdown the server and close any
|
||||
// pairing connections in progress.
|
||||
stopServer();
|
||||
connections_.clear();
|
||||
|
||||
CHECK_LE(name->size(), kPeerNameLength);
|
||||
CHECK_LE(guid->size(), kPeerGuidLength);
|
||||
PeerInfo info = {};
|
||||
strncpy(info.name, name->data(), name->size());
|
||||
strncpy(info.guid, guid->data(), guid->size());
|
||||
|
||||
cb_(&info, &*cert, opaque_);
|
||||
|
||||
got_valid_pairing_ = true;
|
||||
return;
|
||||
}
|
||||
// Invalid pairing. Close the invalid connection.
|
||||
if (connections_.find(fd) != connections_.end()) {
|
||||
connections_.erase(fd);
|
||||
}
|
||||
}
|
||||
conn_read_queue_.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PairingServerImpl::handleNewClientConnection(int fd) {
|
||||
unique_fd ufd(TEMP_FAILURE_RETRY(accept4(fd, nullptr, nullptr, SOCK_CLOEXEC)));
|
||||
if (ufd == -1) {
|
||||
PLOG(WARNING) << "adb_socket_accept failed fd=" << fd;
|
||||
return false;
|
||||
}
|
||||
auto connection = PairingConnection::create(PairingConnection::Role::Server, pswd_, peer_info_,
|
||||
cert_, priv_key_);
|
||||
if (connection == nullptr) {
|
||||
LOG(ERROR) << "PairingServer unable to create a PairingConnection fd=" << fd;
|
||||
return false;
|
||||
}
|
||||
// send the new connection to the connection thread for further processing
|
||||
NewConnectionEvent event = std::make_tuple(std::move(ufd), std::move(connection));
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(conn_mutex_);
|
||||
conn_write_queue_.push_back(std::move(event));
|
||||
}
|
||||
conn_cv_.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
std::unique_ptr<PairingServer> PairingServer::create(const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& cert, const Data& priv_key,
|
||||
int port) {
|
||||
if (pswd.empty() || cert.empty() || priv_key.empty() || port <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
// Make sure peer_info has a non-empty, null-terminated string for guid and
|
||||
// name.
|
||||
if ('\0' != peer_info.name[kPeerNameLength - 1] ||
|
||||
'\0' != peer_info.guid[kPeerGuidLength - 1] || strlen(peer_info.name) == 0 ||
|
||||
strlen(peer_info.guid) == 0) {
|
||||
LOG(ERROR) << "The GUID/short name fields are empty or not null-terminated";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (port != kDefaultPairingPort) {
|
||||
LOG(WARNING) << "Starting server with non-default pairing port=" << port;
|
||||
}
|
||||
|
||||
return std::unique_ptr<PairingServer>(
|
||||
new PairingServerImpl(pswd, peer_info, cert, priv_key, port));
|
||||
}
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adbwifi
|
70
adb/client/pairing/tests/pairing_server.h
Normal file
70
adb/client/pairing/tests/pairing_server.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <adbwifi/pairing/pairing_connection.h>
|
||||
|
||||
namespace adbwifi {
|
||||
namespace pairing {
|
||||
|
||||
// PairingServer is the server side of the PairingConnection protocol. It will
|
||||
// listen for incoming PairingClient connections, and allocate a new
|
||||
// PairingConnection per client for processing. PairingServer can handle multiple
|
||||
// connections, but the first one to establish the pairing will be the only one
|
||||
// to succeed. All others will be disconnected.
|
||||
//
|
||||
// See pairing_connection_test.cpp for example usage.
|
||||
//
|
||||
class PairingServer {
|
||||
public:
|
||||
using Data = std::vector<uint8_t>;
|
||||
|
||||
virtual ~PairingServer() = default;
|
||||
|
||||
// Starts the pairing server. This call is non-blocking. Upon completion,
|
||||
// if the pairing was successful, then |cb| will be called with the PeerInfo
|
||||
// containing the info of the trusted peer. Otherwise, |cb| will be
|
||||
// called with an empty value. Start can only be called once in the lifetime
|
||||
// of this object.
|
||||
//
|
||||
// Returns true if PairingServer was successfully started. Otherwise,
|
||||
// returns false.
|
||||
virtual bool start(PairingConnection::ResultCallback cb, void* opaque) = 0;
|
||||
|
||||
// Creates a new PairingServer instance. May return null if unable
|
||||
// to create an instance. |pswd|, |certificate| and |priv_key| cannot
|
||||
// be empty. |port| is the port PairingServer will listen to PairingClient
|
||||
// connections on. |peer_info| must contain non-empty strings for the guid
|
||||
// and name fields.
|
||||
static std::unique_ptr<PairingServer> create(const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& certificate, const Data& priv_key,
|
||||
int port);
|
||||
|
||||
protected:
|
||||
PairingServer() = default;
|
||||
}; // class PairingServer
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adbwifi
|
|
@ -24,15 +24,19 @@
|
|||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <dns_sd.h>
|
||||
|
||||
#include "adb_client.h"
|
||||
#include "adb_mdns.h"
|
||||
#include "adb_trace.h"
|
||||
#include "adb_utils.h"
|
||||
#include "adb_wifi.h"
|
||||
#include "fdevent/fdevent.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
|
@ -48,9 +52,17 @@ static int adb_DNSServiceIndexByName(const char* regType) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
static bool adb_DNSServiceShouldConnect(const char* regType) {
|
||||
static bool adb_DNSServiceShouldConnect(const char* regType, const char* serviceName) {
|
||||
int index = adb_DNSServiceIndexByName(regType);
|
||||
return index == kADBTransportServiceRefIndex;
|
||||
if (index == kADBTransportServiceRefIndex) {
|
||||
// Ignore adb-EMULATOR* service names, as it interferes with the
|
||||
// emulator ports that are already connected.
|
||||
if (android::base::StartsWith(serviceName, "adb-EMULATOR")) {
|
||||
LOG(INFO) << "Ignoring emulator transport service [" << serviceName << "]";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (index == kADBTransportServiceRefIndex || index == kADBSecureConnectServiceRefIndex);
|
||||
}
|
||||
|
||||
// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
|
||||
|
@ -88,8 +100,10 @@ class AsyncServiceRef {
|
|||
return;
|
||||
}
|
||||
|
||||
DNSServiceRefDeallocate(sdRef_);
|
||||
// Order matters here! Must destroy the fdevent first since it has a
|
||||
// reference to |sdRef_|.
|
||||
fdevent_destroy(fde_);
|
||||
DNSServiceRefDeallocate(sdRef_);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -97,6 +111,10 @@ class AsyncServiceRef {
|
|||
|
||||
void Initialize() {
|
||||
fde_ = fdevent_create(adb_DNSServiceRefSockFD(sdRef_), pump_service_ref, &sdRef_);
|
||||
if (fde_ == nullptr) {
|
||||
D("Unable to create fdevent");
|
||||
return;
|
||||
}
|
||||
fdevent_set(fde_, FDE_READ);
|
||||
initialized_ = true;
|
||||
}
|
||||
|
@ -142,16 +160,29 @@ class ResolvedService : public AsyncServiceRef {
|
|||
D("Client version: %d Service version: %d\n", clientVersion_, serviceVersion_);
|
||||
}
|
||||
|
||||
bool ConnectSecureWifiDevice() {
|
||||
if (!adb_wifi_is_known_host(serviceName_)) {
|
||||
LOG(INFO) << "serviceName=" << serviceName_ << " not in keystore";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string response;
|
||||
connect_device(android::base::StringPrintf(addr_format_.c_str(), ip_addr_, port_),
|
||||
&response);
|
||||
D("Secure connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
|
||||
ip_addr_, port_, response.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
void Connect(const sockaddr* address) {
|
||||
sa_family_ = address->sa_family;
|
||||
const char* addr_format;
|
||||
|
||||
if (sa_family_ == AF_INET) {
|
||||
ip_addr_data_ = &reinterpret_cast<const sockaddr_in*>(address)->sin_addr;
|
||||
addr_format = "%s:%hu";
|
||||
addr_format_ = "%s:%hu";
|
||||
} else if (sa_family_ == AF_INET6) {
|
||||
ip_addr_data_ = &reinterpret_cast<const sockaddr_in6*>(address)->sin6_addr;
|
||||
addr_format = "[%s]:%hu";
|
||||
addr_format_ = "[%s]:%hu";
|
||||
} else { // Should be impossible
|
||||
D("mDNS resolved non-IP address.");
|
||||
return;
|
||||
|
@ -165,11 +196,19 @@ class ResolvedService : public AsyncServiceRef {
|
|||
|
||||
// adb secure service needs to do something different from just
|
||||
// connecting here.
|
||||
if (adb_DNSServiceShouldConnect(regType_.c_str())) {
|
||||
if (adb_DNSServiceShouldConnect(regType_.c_str(), serviceName_.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());
|
||||
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_);
|
||||
|
@ -192,6 +231,8 @@ class ResolvedService : public AsyncServiceRef {
|
|||
|
||||
std::string hostTarget() const { return hosttarget_; }
|
||||
|
||||
std::string serviceName() const { return serviceName_; }
|
||||
|
||||
std::string ipAddress() const { return ip_addr_; }
|
||||
|
||||
uint16_t port() const { return port_; }
|
||||
|
@ -206,8 +247,12 @@ class ResolvedService : public AsyncServiceRef {
|
|||
static void forEachService(const ServiceRegistry& services, const std::string& hostname,
|
||||
adb_secure_foreach_service_callback cb);
|
||||
|
||||
static bool connectByServiceName(const ServiceRegistry& services,
|
||||
const std::string& service_name);
|
||||
|
||||
private:
|
||||
int clientVersion_ = ADB_SECURE_CLIENT_VERSION;
|
||||
std::string addr_format_;
|
||||
std::string serviceName_;
|
||||
std::string regType_;
|
||||
std::string hosttarget_;
|
||||
|
@ -236,35 +281,52 @@ void ResolvedService::initAdbSecure() {
|
|||
|
||||
// static
|
||||
void ResolvedService::forEachService(const ServiceRegistry& services,
|
||||
const std::string& wanted_host,
|
||||
const std::string& wanted_service_name,
|
||||
adb_secure_foreach_service_callback cb) {
|
||||
initAdbSecure();
|
||||
|
||||
for (auto service : services) {
|
||||
auto hostname = service->hostTarget();
|
||||
auto service_name = service->serviceName();
|
||||
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);
|
||||
if (wanted_service_name == "") {
|
||||
cb(service_name.c_str(), ip.c_str(), port);
|
||||
} else if (service_name == wanted_service_name) {
|
||||
cb(service_name.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);
|
||||
bool ResolvedService::connectByServiceName(const ServiceRegistry& services,
|
||||
const std::string& service_name) {
|
||||
initAdbSecure();
|
||||
for (auto service : services) {
|
||||
if (service_name == service->serviceName()) {
|
||||
D("Got service_name match [%s]", service->serviceName().c_str());
|
||||
return service->ConnectSecureWifiDevice();
|
||||
}
|
||||
}
|
||||
D("No registered serviceNames matched [%s]", service_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
void adb_secure_foreach_connect_service(const char* host_name,
|
||||
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);
|
||||
}
|
||||
|
||||
void adb_secure_foreach_connect_service(const char* service_name,
|
||||
adb_secure_foreach_service_callback cb) {
|
||||
ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices,
|
||||
host_name ? host_name : "", cb);
|
||||
service_name ? service_name : "", cb);
|
||||
}
|
||||
|
||||
bool adb_secure_connect_by_service_name(const char* service_name) {
|
||||
return ResolvedService::connectByServiceName(*ResolvedService::sAdbSecureConnectServices,
|
||||
service_name);
|
||||
}
|
||||
|
||||
static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/,
|
||||
|
@ -332,6 +394,26 @@ class DiscoveredService : public AsyncServiceRef {
|
|||
std::string regType_;
|
||||
};
|
||||
|
||||
static void adb_RemoveDNSService(const char* regType, const char* serviceName) {
|
||||
int index = adb_DNSServiceIndexByName(regType);
|
||||
ResolvedService::ServiceRegistry* services;
|
||||
switch (index) {
|
||||
case kADBSecurePairingServiceRefIndex:
|
||||
services = ResolvedService::sAdbSecurePairingServices;
|
||||
break;
|
||||
case kADBSecureConnectServiceRefIndex:
|
||||
services = ResolvedService::sAdbSecureConnectServices;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
std::string sName(serviceName);
|
||||
std::remove_if(services->begin(), services->end(), [&sName](ResolvedService* service) {
|
||||
return (sName == service->serviceName());
|
||||
});
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
@ -400,10 +482,12 @@ static void DNSSD_API register_resolved_mdns_service(
|
|||
interfaceIndex, hosttarget, ntohs(port), serviceVersion);
|
||||
|
||||
if (! resolved->Initialized()) {
|
||||
D("Unable to init resolved service");
|
||||
delete resolved;
|
||||
}
|
||||
|
||||
if (flags) { /* Only ever equals MoreComing or 0 */
|
||||
D("releasing discovered service");
|
||||
discovered.release();
|
||||
}
|
||||
}
|
||||
|
@ -412,7 +496,6 @@ static void DNSSD_API on_service_browsed(DNSServiceRef sdRef, DNSServiceFlags fl
|
|||
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);
|
||||
|
@ -423,9 +506,17 @@ static void DNSSD_API on_service_browsed(DNSServiceRef sdRef, DNSServiceFlags fl
|
|||
return;
|
||||
}
|
||||
|
||||
auto discovered = new DiscoveredService(interfaceIndex, serviceName, regtype, domain);
|
||||
if (!discovered->Initialized()) {
|
||||
delete discovered;
|
||||
if (flags & kDNSServiceFlagsAdd) {
|
||||
D("%s: Discover found new serviceName=[%s] regtype=[%s] domain=[%s]", __func__, serviceName,
|
||||
regtype, domain);
|
||||
auto discovered = new DiscoveredService(interfaceIndex, serviceName, regtype, domain);
|
||||
if (!discovered->Initialized()) {
|
||||
delete discovered;
|
||||
}
|
||||
} else {
|
||||
D("%s: Discover lost serviceName=[%s] regtype=[%s] domain=[%s]", __func__, serviceName,
|
||||
regtype, domain);
|
||||
adb_RemoveDNSService(regtype, serviceName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,10 +64,6 @@ cc_library {
|
|||
"com.android.adbd",
|
||||
"test_com.android.adbd",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libadb_protos",
|
||||
],
|
||||
}
|
||||
|
||||
// For running atest (b/147158681)
|
||||
|
|
227
adb/daemon/adb_wifi.cpp
Normal file
227
adb/daemon/adb_wifi.cpp
Normal file
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#if !ADB_HOST
|
||||
|
||||
#define TRACE_TAG ADB_WIRELESS
|
||||
|
||||
#include "adb_wifi.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <optional>
|
||||
|
||||
#include <adbd_auth.h>
|
||||
#include <android-base/properties.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "daemon/mdns.h"
|
||||
#include "sysdeps.h"
|
||||
#include "transport.h"
|
||||
|
||||
using namespace android::base;
|
||||
|
||||
namespace {
|
||||
|
||||
static AdbdAuthContext* auth_ctx;
|
||||
|
||||
static void adb_disconnected(void* unused, atransport* t);
|
||||
static struct adisconnect adb_disconnect = {adb_disconnected, nullptr};
|
||||
|
||||
static void adb_disconnected(void* unused, atransport* t) {
|
||||
LOG(INFO) << "ADB wifi device disconnected";
|
||||
adbd_auth_tls_device_disconnected(auth_ctx, kAdbTransportTypeWifi, t->auth_id);
|
||||
}
|
||||
|
||||
// TODO(b/31559095): need bionic host so that we can use 'prop_info' returned
|
||||
// from WaitForProperty
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
class TlsServer {
|
||||
public:
|
||||
explicit TlsServer(int port);
|
||||
virtual ~TlsServer();
|
||||
bool Start();
|
||||
uint16_t port() { return port_; };
|
||||
|
||||
private:
|
||||
void OnFdEvent(int fd, unsigned ev);
|
||||
static void StaticOnFdEvent(int fd, unsigned ev, void* opaque);
|
||||
|
||||
fdevent* fd_event_ = nullptr;
|
||||
uint16_t port_;
|
||||
}; // TlsServer
|
||||
|
||||
TlsServer::TlsServer(int port) : port_(port) {}
|
||||
|
||||
TlsServer::~TlsServer() {
|
||||
fdevent* fde = fd_event_;
|
||||
fdevent_run_on_main_thread([fde]() {
|
||||
if (fde != nullptr) {
|
||||
fdevent_destroy(fde);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool TlsServer::Start() {
|
||||
std::condition_variable cv;
|
||||
std::mutex mutex;
|
||||
std::optional<bool> success;
|
||||
auto callback = [&](bool result) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
success = result;
|
||||
}
|
||||
cv.notify_one();
|
||||
};
|
||||
|
||||
std::string err;
|
||||
unique_fd fd(network_inaddr_any_server(port_, SOCK_STREAM, &err));
|
||||
if (fd.get() == -1) {
|
||||
LOG(ERROR) << "Failed to start TLS server [" << err << "]";
|
||||
return false;
|
||||
}
|
||||
close_on_exec(fd.get());
|
||||
int port = socket_get_local_port(fd.get());
|
||||
if (port <= 0 || port > 65535) {
|
||||
LOG(ERROR) << "Invalid port for tls server";
|
||||
return false;
|
||||
}
|
||||
port_ = static_cast<uint16_t>(port);
|
||||
LOG(INFO) << "adbwifi started on port " << port_;
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
fdevent_run_on_main_thread([&]() {
|
||||
fd_event_ = fdevent_create(fd.release(), &TlsServer::StaticOnFdEvent, this);
|
||||
if (fd_event_ == nullptr) {
|
||||
LOG(ERROR) << "Failed to create fd event for TlsServer.";
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
callback(true);
|
||||
});
|
||||
|
||||
cv.wait(lock, [&]() { return success.has_value(); });
|
||||
if (!*success) {
|
||||
LOG(INFO) << "TlsServer fdevent_create failed";
|
||||
return false;
|
||||
}
|
||||
fdevent_set(fd_event_, FDE_READ);
|
||||
LOG(INFO) << "TlsServer running on port " << port_;
|
||||
|
||||
return *success;
|
||||
}
|
||||
|
||||
// static
|
||||
void TlsServer::StaticOnFdEvent(int fd, unsigned ev, void* opaque) {
|
||||
auto server = reinterpret_cast<TlsServer*>(opaque);
|
||||
server->OnFdEvent(fd, ev);
|
||||
}
|
||||
|
||||
void TlsServer::OnFdEvent(int fd, unsigned ev) {
|
||||
if ((ev & FDE_READ) == 0 || fd != fd_event_->fd.get()) {
|
||||
LOG(INFO) << __func__ << ": No read [ev=" << ev << " fd=" << fd << "]";
|
||||
return;
|
||||
}
|
||||
|
||||
unique_fd new_fd(adb_socket_accept(fd, nullptr, nullptr));
|
||||
if (new_fd >= 0) {
|
||||
LOG(INFO) << "New TLS connection [fd=" << new_fd.get() << "]";
|
||||
close_on_exec(new_fd.get());
|
||||
disable_tcp_nagle(new_fd.get());
|
||||
std::string serial = android::base::StringPrintf("host-%d", new_fd.get());
|
||||
// TODO: register a tls transport
|
||||
// register_socket_transport(std::move(new_fd), std::move(serial), port_, 1,
|
||||
// [](atransport*) { return ReconnectResult::Abort; });
|
||||
}
|
||||
}
|
||||
|
||||
TlsServer* sTlsServer = nullptr;
|
||||
const char kWifiPortProp[] = "service.adb.tls.port";
|
||||
|
||||
const char kWifiEnabledProp[] = "persist.adb.tls_server.enable";
|
||||
|
||||
static void enable_wifi_debugging() {
|
||||
start_mdnsd();
|
||||
|
||||
if (sTlsServer != nullptr) {
|
||||
delete sTlsServer;
|
||||
}
|
||||
sTlsServer = new TlsServer(0);
|
||||
if (!sTlsServer->Start()) {
|
||||
LOG(ERROR) << "Failed to start TlsServer";
|
||||
delete sTlsServer;
|
||||
sTlsServer = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Start mdns connect service for discovery
|
||||
register_adb_secure_connect_service(sTlsServer->port());
|
||||
LOG(INFO) << "adb wifi started on port " << sTlsServer->port();
|
||||
SetProperty(kWifiPortProp, std::to_string(sTlsServer->port()));
|
||||
}
|
||||
|
||||
static void disable_wifi_debugging() {
|
||||
if (sTlsServer != nullptr) {
|
||||
delete sTlsServer;
|
||||
sTlsServer = nullptr;
|
||||
}
|
||||
if (is_adb_secure_connect_service_registered()) {
|
||||
unregister_adb_secure_connect_service();
|
||||
}
|
||||
kick_all_tcp_tls_transports();
|
||||
LOG(INFO) << "adb wifi stopped";
|
||||
SetProperty(kWifiPortProp, "");
|
||||
}
|
||||
|
||||
// Watches for the #kWifiEnabledProp property to toggle the TlsServer
|
||||
static void start_wifi_enabled_observer() {
|
||||
std::thread([]() {
|
||||
bool wifi_enabled = false;
|
||||
while (true) {
|
||||
std::string toggled_val = wifi_enabled ? "0" : "1";
|
||||
LOG(INFO) << "Waiting for " << kWifiEnabledProp << "=" << toggled_val;
|
||||
if (WaitForProperty(kWifiEnabledProp, toggled_val)) {
|
||||
wifi_enabled = !wifi_enabled;
|
||||
LOG(INFO) << kWifiEnabledProp << " changed to " << toggled_val;
|
||||
if (wifi_enabled) {
|
||||
enable_wifi_debugging();
|
||||
} else {
|
||||
disable_wifi_debugging();
|
||||
}
|
||||
}
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
#endif //__ANDROID__
|
||||
|
||||
} // namespace
|
||||
|
||||
void adbd_wifi_init(AdbdAuthContext* ctx) {
|
||||
auth_ctx = ctx;
|
||||
#if defined(__ANDROID__)
|
||||
start_wifi_enabled_observer();
|
||||
#endif //__ANDROID__
|
||||
}
|
||||
|
||||
void adbd_wifi_secure_connect(atransport* t) {
|
||||
t->AddDisconnect(&adb_disconnect);
|
||||
handle_online(t);
|
||||
send_connect(t);
|
||||
LOG(INFO) << __func__ << ": connected " << t->serial;
|
||||
t->auth_id = adbd_auth_tls_device_connected(auth_ctx, kAdbTransportTypeWifi, t->auth_key.data(),
|
||||
t->auth_key.size());
|
||||
}
|
||||
#endif /* !HOST */
|
|
@ -35,10 +35,12 @@
|
|||
#include <openssl/obj_mac.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_auth.h"
|
||||
#include "adb_io.h"
|
||||
#include "adb_wifi.h"
|
||||
#include "fdevent/fdevent.h"
|
||||
#include "transport.h"
|
||||
#include "types.h"
|
||||
|
@ -159,11 +161,20 @@ static void adbd_auth_key_authorized(void* arg, uint64_t id) {
|
|||
});
|
||||
}
|
||||
|
||||
static void adbd_key_removed(const char* public_key, size_t len) {
|
||||
// The framework removed the key from its keystore. We need to disconnect all
|
||||
// devices using that key. Search by t->auth_key
|
||||
std::string_view auth_key(public_key, len);
|
||||
kick_all_transports_by_auth_key(auth_key);
|
||||
}
|
||||
|
||||
void adbd_auth_init(void) {
|
||||
AdbdAuthCallbacksV1 cb;
|
||||
cb.version = 1;
|
||||
cb.key_authorized = adbd_auth_key_authorized;
|
||||
cb.key_removed = adbd_key_removed;
|
||||
auth_ctx = adbd_auth_new(&cb);
|
||||
adbd_wifi_init(auth_ctx);
|
||||
std::thread([]() {
|
||||
adb_thread_setname("adbd auth");
|
||||
adbd_auth_run(auth_ctx);
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include "adb_auth.h"
|
||||
#include "adb_listeners.h"
|
||||
#include "adb_utils.h"
|
||||
#include "adb_wifi.h"
|
||||
#include "socket_spec.h"
|
||||
#include "transport.h"
|
||||
|
||||
|
@ -196,6 +197,7 @@ static void setup_adb(const std::vector<std::string>& addrs) {
|
|||
if (port == -1) {
|
||||
port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
|
||||
}
|
||||
LOG(INFO) << "Setup mdns on port= " << port;
|
||||
setup_mdns(port);
|
||||
#endif
|
||||
for (const auto& addr : addrs) {
|
||||
|
@ -317,9 +319,10 @@ int main(int argc, char** argv) {
|
|||
|
||||
while (true) {
|
||||
static struct option opts[] = {
|
||||
{"root_seclabel", required_argument, nullptr, 's'},
|
||||
{"device_banner", required_argument, nullptr, 'b'},
|
||||
{"version", no_argument, nullptr, 'v'},
|
||||
{"root_seclabel", required_argument, nullptr, 's'},
|
||||
{"device_banner", required_argument, nullptr, 'b'},
|
||||
{"version", no_argument, nullptr, 'v'},
|
||||
{"logpostfsdata", no_argument, nullptr, 'l'},
|
||||
};
|
||||
|
||||
int option_index = 0;
|
||||
|
@ -341,6 +344,9 @@ int main(int argc, char** argv) {
|
|||
printf("Android Debug Bridge Daemon version %d.%d.%d\n", ADB_VERSION_MAJOR,
|
||||
ADB_VERSION_MINOR, ADB_SERVER_VERSION);
|
||||
return 0;
|
||||
case 'l':
|
||||
LOG(ERROR) << "post-fs-data triggered";
|
||||
return 0;
|
||||
default:
|
||||
// getopt already prints "adbd: invalid option -- %c" for us.
|
||||
return 1;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
@ -36,7 +37,7 @@ static int port;
|
|||
static DNSServiceRef mdns_refs[kNumADBDNSServices];
|
||||
static bool mdns_registered[kNumADBDNSServices];
|
||||
|
||||
static void start_mdns() {
|
||||
void start_mdnsd() {
|
||||
if (android::base::GetProperty("init.svc.mdnsd", "") == "running") {
|
||||
return;
|
||||
}
|
||||
|
@ -61,11 +62,9 @@ static void mdns_callback(DNSServiceRef /*ref*/,
|
|||
}
|
||||
}
|
||||
|
||||
static void register_mdns_service(int index, int port) {
|
||||
static void register_mdns_service(int index, int port, const std::string service_name) {
|
||||
std::lock_guard<std::mutex> lock(mdns_lock);
|
||||
|
||||
std::string hostname = "adb-";
|
||||
hostname += android::base::GetProperty("ro.serialno", "unidentified");
|
||||
|
||||
// https://tools.ietf.org/html/rfc6763
|
||||
// """
|
||||
|
@ -95,7 +94,7 @@ static void register_mdns_service(int index, int port) {
|
|||
}
|
||||
|
||||
auto error = DNSServiceRegister(
|
||||
&mdns_refs[index], 0, 0, hostname.c_str(), kADBDNSServices[index], nullptr, nullptr,
|
||||
&mdns_refs[index], 0, 0, service_name.c_str(), kADBDNSServices[index], nullptr, nullptr,
|
||||
htobe16((uint16_t)port), (uint16_t)txtRecord.size(),
|
||||
txtRecord.empty() ? nullptr : txtRecord.data(), mdns_callback, nullptr);
|
||||
|
||||
|
@ -120,11 +119,13 @@ static void unregister_mdns_service(int index) {
|
|||
}
|
||||
|
||||
static void register_base_mdns_transport() {
|
||||
register_mdns_service(kADBTransportServiceRefIndex, port);
|
||||
std::string hostname = "adb-";
|
||||
hostname += android::base::GetProperty("ro.serialno", "unidentified");
|
||||
register_mdns_service(kADBTransportServiceRefIndex, port, hostname);
|
||||
}
|
||||
|
||||
static void setup_mdns_thread() {
|
||||
start_mdns();
|
||||
start_mdnsd();
|
||||
|
||||
// We will now only set up the normal transport mDNS service
|
||||
// instead of registering all the adb secure mDNS services
|
||||
|
@ -139,9 +140,57 @@ static void teardown_mdns() {
|
|||
}
|
||||
}
|
||||
|
||||
static std::string RandomAlphaNumString(size_t len) {
|
||||
std::string ret;
|
||||
std::random_device rd;
|
||||
std::mt19937 mt(rd());
|
||||
// Generate values starting with zero and then up to enough to cover numeric
|
||||
// digits, small letters and capital letters (26 each).
|
||||
std::uniform_int_distribution<uint8_t> dist(0, 61);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
uint8_t val = dist(mt);
|
||||
if (val < 10) {
|
||||
ret += '0' + val;
|
||||
} else if (val < 36) {
|
||||
ret += 'A' + (val - 10);
|
||||
} else {
|
||||
ret += 'a' + (val - 36);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::string GenerateDeviceGuid() {
|
||||
// The format is adb-<serial_no>-<six-random-alphanum>
|
||||
std::string guid = "adb-";
|
||||
|
||||
std::string serial = android::base::GetProperty("ro.serialno", "");
|
||||
if (serial.empty()) {
|
||||
// Generate 16-bytes of random alphanum string
|
||||
serial = RandomAlphaNumString(16);
|
||||
}
|
||||
guid += serial + '-';
|
||||
// Random six-char suffix
|
||||
guid += RandomAlphaNumString(6);
|
||||
return guid;
|
||||
}
|
||||
|
||||
static std::string ReadDeviceGuid() {
|
||||
std::string guid = android::base::GetProperty("persist.adb.wifi.guid", "");
|
||||
if (guid.empty()) {
|
||||
guid = GenerateDeviceGuid();
|
||||
CHECK(!guid.empty());
|
||||
android::base::SetProperty("persist.adb.wifi.guid", guid);
|
||||
}
|
||||
return guid;
|
||||
}
|
||||
|
||||
// Public interface/////////////////////////////////////////////////////////////
|
||||
|
||||
void setup_mdns(int port_in) {
|
||||
// Make sure the adb wifi guid is generated.
|
||||
std::string guid = ReadDeviceGuid();
|
||||
CHECK(!guid.empty());
|
||||
port = port_in;
|
||||
std::thread(setup_mdns_thread).detach();
|
||||
|
||||
|
@ -149,24 +198,14 @@ void setup_mdns(int port_in) {
|
|||
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);
|
||||
auto service_name = ReadDeviceGuid();
|
||||
if (service_name.empty()) {
|
||||
return;
|
||||
}
|
||||
LOG(INFO) << "Registering secure_connect service (" << service_name << ")";
|
||||
register_mdns_service(kADBSecureConnectServiceRefIndex, port, service_name);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,12 +19,9 @@
|
|||
|
||||
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);
|
||||
void unregister_adb_secure_connect_service();
|
||||
bool is_adb_secure_connect_service_registered();
|
||||
|
||||
void start_mdnsd();
|
||||
#endif // _DAEMON_MDNS_H_
|
||||
|
|
|
@ -48,6 +48,12 @@ class FdeventTest : public ::testing::Test {
|
|||
protected:
|
||||
unique_fd dummy;
|
||||
|
||||
~FdeventTest() {
|
||||
if (thread_.joinable()) {
|
||||
TerminateThread();
|
||||
}
|
||||
}
|
||||
|
||||
static void SetUpTestCase() {
|
||||
#if !defined(_WIN32)
|
||||
ASSERT_NE(SIG_ERR, signal(SIGPIPE, SIG_IGN));
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
|
@ -34,6 +35,7 @@
|
|||
#include "adb_io.h"
|
||||
#include "adb_unique_fd.h"
|
||||
#include "adb_utils.h"
|
||||
#include "adb_wifi.h"
|
||||
#include "services.h"
|
||||
#include "socket_spec.h"
|
||||
#include "sysdeps.h"
|
||||
|
@ -193,6 +195,12 @@ static void connect_service(unique_fd fd, std::string host) {
|
|||
// Send response for emulator and device
|
||||
SendProtocolString(fd.get(), response);
|
||||
}
|
||||
|
||||
static void pair_service(unique_fd fd, std::string host, std::string password) {
|
||||
std::string response;
|
||||
adb_wifi_pair_device(host, password, response);
|
||||
SendProtocolString(fd.get(), response);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADB_HOST
|
||||
|
@ -248,6 +256,16 @@ asocket* host_service_to_socket(std::string_view name, std::string_view serial,
|
|||
unique_fd fd = create_service_thread(
|
||||
"connect", std::bind(connect_service, std::placeholders::_1, host));
|
||||
return create_local_socket(std::move(fd));
|
||||
} else if (android::base::ConsumePrefix(&name, "pair:")) {
|
||||
const char* divider = strchr(name.data(), ':');
|
||||
if (!divider) {
|
||||
return nullptr;
|
||||
}
|
||||
std::string password(name.data(), divider);
|
||||
std::string host(divider + 1);
|
||||
unique_fd fd = create_service_thread(
|
||||
"pair", std::bind(pair_service, std::placeholders::_1, host, password));
|
||||
return create_local_socket(std::move(fd));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -88,6 +88,8 @@ extern int adb_mkdir(const std::string& path, int mode);
|
|||
#undef mkdir
|
||||
#define mkdir ___xxx_mkdir
|
||||
|
||||
extern int adb_rename(const char* oldpath, const char* newpath);
|
||||
|
||||
// See the comments for the !defined(_WIN32) versions of adb_*().
|
||||
extern int adb_open(const char* path, int options);
|
||||
extern int adb_creat(const char* path, int mode);
|
||||
|
@ -101,6 +103,9 @@ extern int adb_close(int fd);
|
|||
extern int adb_register_socket(SOCKET s);
|
||||
extern HANDLE adb_get_os_handle(borrowed_fd fd);
|
||||
|
||||
extern int adb_gethostname(char* name, size_t len);
|
||||
extern int adb_getlogin_r(char* buf, size_t bufsize);
|
||||
|
||||
// See the comments for the !defined(_WIN32) version of unix_close().
|
||||
static __inline__ int unix_close(int fd) {
|
||||
return close(fd);
|
||||
|
@ -461,6 +466,14 @@ __inline__ int adb_register_socket(int s) {
|
|||
return s;
|
||||
}
|
||||
|
||||
static __inline__ int adb_gethostname(char* name, size_t len) {
|
||||
return gethostname(name, len);
|
||||
}
|
||||
|
||||
static __inline__ int adb_getlogin_r(char* buf, size_t bufsize) {
|
||||
return getlogin_r(buf, bufsize);
|
||||
}
|
||||
|
||||
static __inline__ int adb_read(borrowed_fd fd, void* buf, size_t len) {
|
||||
return TEMP_FAILURE_RETRY(read(fd.get(), buf, len));
|
||||
}
|
||||
|
@ -637,6 +650,10 @@ static __inline__ int adb_mkdir(const std::string& path, int mode) {
|
|||
#undef mkdir
|
||||
#define mkdir ___xxx_mkdir
|
||||
|
||||
static __inline__ int adb_rename(const char* oldpath, const char* newpath) {
|
||||
return rename(oldpath, newpath);
|
||||
}
|
||||
|
||||
static __inline__ int adb_is_absolute_host_path(const char* path) {
|
||||
return path[0] == '/';
|
||||
}
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <winsock2.h> /* winsock.h *must* be included before windows.h. */
|
||||
#include <lmcons.h>
|
||||
#include <windows.h>
|
||||
#include <winsock2.h> /* winsock.h *must* be included before windows.h. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
@ -1009,6 +1010,55 @@ int adb_register_socket(SOCKET s) {
|
|||
return _fh_to_int(f);
|
||||
}
|
||||
|
||||
static bool isBlankStr(const char* str) {
|
||||
for (; *str != '\0'; ++str) {
|
||||
if (!isblank(*str)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int adb_gethostname(char* name, size_t len) {
|
||||
const char* computerName = adb_getenv("COMPUTERNAME");
|
||||
if (computerName && !isBlankStr(computerName)) {
|
||||
strncpy(name, computerName, len);
|
||||
name[len - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
wchar_t buffer[MAX_COMPUTERNAME_LENGTH + 1];
|
||||
DWORD size = sizeof(buffer);
|
||||
if (!GetComputerNameW(buffer, &size)) {
|
||||
return -1;
|
||||
}
|
||||
std::string name_utf8;
|
||||
if (!android::base::WideToUTF8(buffer, &name_utf8)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(name, name_utf8.c_str(), len);
|
||||
name[len - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adb_getlogin_r(char* buf, size_t bufsize) {
|
||||
wchar_t buffer[UNLEN + 1];
|
||||
DWORD len = sizeof(buffer);
|
||||
if (!GetUserNameW(buffer, &len)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string login;
|
||||
if (!android::base::WideToUTF8(buffer, &login)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(buf, login.c_str(), bufsize);
|
||||
buf[bufsize - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef accept
|
||||
int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr, socklen_t* addrlen) {
|
||||
FH serverfh = _fh_from_int(serverfd, __func__);
|
||||
|
@ -2342,6 +2392,20 @@ int adb_mkdir(const std::string& path, int mode) {
|
|||
return _wmkdir(path_wide.c_str());
|
||||
}
|
||||
|
||||
int adb_rename(const char* oldpath, const char* newpath) {
|
||||
std::wstring oldpath_wide, newpath_wide;
|
||||
if (!android::base::UTF8ToWide(oldpath, &oldpath_wide)) {
|
||||
return -1;
|
||||
}
|
||||
if (!android::base::UTF8ToWide(newpath, &newpath_wide)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// MSDN just says the return value is non-zero on failure, make sure it
|
||||
// returns -1 on failure so that it behaves the same as other systems.
|
||||
return _wrename(oldpath_wide.c_str(), newpath_wide.c_str()) ? -1 : 0;
|
||||
}
|
||||
|
||||
// Version of utime() that takes a UTF-8 path.
|
||||
int adb_utime(const char* path, struct utimbuf* u) {
|
||||
std::wstring path_wide;
|
||||
|
|
Loading…
Reference in a new issue