|  | /* | 
|  | * Copyright (C) 2016 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 "mdns.h" | 
|  | #include "adb_mdns.h" | 
|  | #include "sysdeps.h" | 
|  |  | 
|  | #include <dns_sd.h> | 
|  | #include <endian.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include <chrono> | 
|  | #include <mutex> | 
|  | #include <random> | 
|  | #include <thread> | 
|  |  | 
|  | #include <android-base/logging.h> | 
|  | #include <android-base/properties.h> | 
|  |  | 
|  | using namespace std::chrono_literals; | 
|  |  | 
|  | static std::mutex& mdns_lock = *new std::mutex(); | 
|  | static int port; | 
|  | static DNSServiceRef mdns_refs[kNumADBDNSServices]; | 
|  | static bool mdns_registered[kNumADBDNSServices]; | 
|  |  | 
|  | void start_mdnsd() { | 
|  | if (android::base::GetProperty("init.svc.mdnsd", "") == "running") { | 
|  | return; | 
|  | } | 
|  |  | 
|  | android::base::SetProperty("ctl.start", "mdnsd"); | 
|  |  | 
|  | if (! android::base::WaitForProperty("init.svc.mdnsd", "running", 5s)) { | 
|  | LOG(ERROR) << "Could not start mdnsd."; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void mdns_callback(DNSServiceRef /*ref*/, | 
|  | DNSServiceFlags /*flags*/, | 
|  | DNSServiceErrorType errorCode, | 
|  | const char* /*name*/, | 
|  | const char* /*regtype*/, | 
|  | const char* /*domain*/, | 
|  | void* /*context*/) { | 
|  | if (errorCode != kDNSServiceErr_NoError) { | 
|  | LOG(ERROR) << "Encountered mDNS registration error (" | 
|  | << errorCode << ")."; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void register_mdns_service(int index, int port, const std::string service_name) { | 
|  | std::lock_guard<std::mutex> lock(mdns_lock); | 
|  |  | 
|  |  | 
|  | // https://tools.ietf.org/html/rfc6763 | 
|  | // """ | 
|  | // The format of the data within a DNS TXT record is one or more | 
|  | // strings, packed together in memory without any intervening gaps or | 
|  | // padding bytes for word alignment. | 
|  | // | 
|  | // The format of each constituent string within the DNS TXT record is a | 
|  | // single length byte, followed by 0-255 bytes of text data. | 
|  | // """ | 
|  | // | 
|  | // Therefore: | 
|  | // 1. Begin with the string length | 
|  | // 2. No null termination | 
|  |  | 
|  | std::vector<char> txtRecord; | 
|  |  | 
|  | if (kADBDNSServiceTxtRecords[index]) { | 
|  | size_t txtRecordStringLength = strlen(kADBDNSServiceTxtRecords[index]); | 
|  |  | 
|  | txtRecord.resize(1 +                    // length byte | 
|  | txtRecordStringLength  // string bytes | 
|  | ); | 
|  |  | 
|  | txtRecord[0] = (char)txtRecordStringLength; | 
|  | memcpy(txtRecord.data() + 1, kADBDNSServiceTxtRecords[index], txtRecordStringLength); | 
|  | } | 
|  |  | 
|  | auto error = DNSServiceRegister( | 
|  | &mdns_refs[index], 0, 0, service_name.c_str(), kADBDNSServices[index], nullptr, nullptr, | 
|  | htobe16((uint16_t)port), (uint16_t)txtRecord.size(), | 
|  | txtRecord.empty() ? nullptr : txtRecord.data(), mdns_callback, nullptr); | 
|  |  | 
|  | if (error != kDNSServiceErr_NoError) { | 
|  | LOG(ERROR) << "Could not register mDNS service " << kADBDNSServices[index] << ", error (" | 
|  | << error << ")."; | 
|  | mdns_registered[index] = false; | 
|  | } | 
|  |  | 
|  | mdns_registered[index] = true; | 
|  |  | 
|  | LOG(INFO) << "adbd mDNS service " << kADBDNSServices[index] | 
|  | << " registered: " << mdns_registered[index]; | 
|  | } | 
|  |  | 
|  | static void unregister_mdns_service(int index) { | 
|  | std::lock_guard<std::mutex> lock(mdns_lock); | 
|  |  | 
|  | if (mdns_registered[index]) { | 
|  | DNSServiceRefDeallocate(mdns_refs[index]); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void register_base_mdns_transport() { | 
|  | std::string hostname = "adb-"; | 
|  | hostname += android::base::GetProperty("ro.serialno", "unidentified"); | 
|  | register_mdns_service(kADBTransportServiceRefIndex, port, hostname); | 
|  | } | 
|  |  | 
|  | static void setup_mdns_thread() { | 
|  | start_mdnsd(); | 
|  |  | 
|  | // We will now only set up the normal transport mDNS service | 
|  | // instead of registering all the adb secure mDNS services | 
|  | // in the beginning. This is to provide more privacy/security. | 
|  | register_base_mdns_transport(); | 
|  | } | 
|  |  | 
|  | // This also tears down any adb secure mDNS services, if they exist. | 
|  | static void teardown_mdns() { | 
|  | for (int i = 0; i < kNumADBDNSServices; ++i) { | 
|  | unregister_mdns_service(i); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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(); | 
|  |  | 
|  | // TODO: Make this more robust against a hard kill. | 
|  | atexit(teardown_mdns); | 
|  | } | 
|  |  | 
|  | void register_adb_secure_connect_service(int port) { | 
|  | std::thread([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(); | 
|  | } | 
|  |  | 
|  | void unregister_adb_secure_connect_service() { | 
|  | std::thread([]() { unregister_mdns_service(kADBSecureConnectServiceRefIndex); }).detach(); | 
|  | } | 
|  |  | 
|  | bool is_adb_secure_connect_service_registered() { | 
|  | std::lock_guard<std::mutex> lock(mdns_lock); | 
|  | return mdns_registered[kADBSecureConnectServiceRefIndex]; | 
|  | } |