diff --git a/adb/Android.mk b/adb/Android.mk index a2ea699bc..8a43e37bc 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -145,6 +145,7 @@ LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS) LOCAL_SRC_FILES := \ $(LIBADB_SRC_FILES) \ adb_auth_host.cpp \ + transport_mdns.cpp \ LOCAL_SRC_FILES_darwin := $(LIBADB_darwin_SRC_FILES) LOCAL_SRC_FILES_linux := $(LIBADB_linux_SRC_FILES) @@ -154,7 +155,7 @@ LOCAL_SANITIZE := $(adb_host_sanitize) # Even though we're building a static library (and thus there's no link step for # this to take effect), this adds the includes to our path. -LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase +LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase libmdnssd LOCAL_STATIC_LIBRARIES_linux := libusb LOCAL_STATIC_LIBRARIES_darwin := libusb @@ -176,7 +177,7 @@ LOCAL_SRC_FILES := \ shell_service_test.cpp \ LOCAL_SANITIZE := $(adb_target_sanitize) -LOCAL_STATIC_LIBRARIES := libadbd libcrypto_utils libcrypto libusb +LOCAL_STATIC_LIBRARIES := libadbd libcrypto_utils libcrypto libusb libmdnssd LOCAL_SHARED_LIBRARIES := liblog libbase libcutils include $(BUILD_NATIVE_TEST) @@ -224,7 +225,8 @@ LOCAL_STATIC_LIBRARIES := \ libcrypto \ libcutils \ libdiagnose_usb \ - libgmock_host \ + libmdnssd \ + libgmock_host LOCAL_STATIC_LIBRARIES_linux := libusb LOCAL_STATIC_LIBRARIES_darwin := libusb @@ -292,6 +294,7 @@ LOCAL_STATIC_LIBRARIES := \ libcrypto \ libdiagnose_usb \ liblog \ + libmdnssd # Don't use libcutils on Windows. LOCAL_STATIC_LIBRARIES_darwin := libcutils @@ -325,6 +328,7 @@ LOCAL_CLANG := true LOCAL_SRC_FILES := \ daemon/main.cpp \ + daemon/mdns.cpp \ services.cpp \ file_sync_service.cpp \ framebuffer_service.cpp \ @@ -372,6 +376,7 @@ LOCAL_STATIC_LIBRARIES := \ libcrypto_utils \ libcrypto \ libminijail \ + libmdnssd \ libdebuggerd_handler \ include $(BUILD_EXECUTABLE) diff --git a/adb/adb_mdns.h b/adb/adb_mdns.h new file mode 100644 index 000000000..2e544d7fe --- /dev/null +++ b/adb/adb_mdns.h @@ -0,0 +1,22 @@ +/* + * 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. + */ + +#ifndef _ADB_MDNS_H_ +#define _ADB_MDNS_H_ + +const char* kADBServiceType = "_adb._tcp"; + +#endif diff --git a/adb/client/main.cpp b/adb/client/main.cpp index 97a54fd6c..606203cb4 100644 --- a/adb/client/main.cpp +++ b/adb/client/main.cpp @@ -117,6 +117,8 @@ int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply init_transport_registration(); + init_mdns_transport_discovery(); + usb_init(); local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp index 6382b6789..7da94ce10 100644 --- a/adb/daemon/main.cpp +++ b/adb/daemon/main.cpp @@ -45,6 +45,8 @@ #include "adb_utils.h" #include "transport.h" +#include "mdns.h" + static const char* root_seclabel = nullptr; static void drop_capabilities_bounding_set_if_needed(struct minijail *j) { @@ -140,6 +142,11 @@ static void drop_privileges(int server_port) { } } +static void setup_port(int port) { + local_init(port); + setup_mdns(port); +} + int adbd_main(int server_port) { umask(0); @@ -188,10 +195,10 @@ int adbd_main(int server_port) { if (sscanf(prop_port.c_str(), "%d", &port) == 1 && port > 0) { D("using port=%d", port); // Listen on TCP port specified by service.adb.tcp.port property. - local_init(port); + setup_port(port); } else if (!is_usb) { // Listen on default port. - local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); + setup_port(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); } D("adbd_main(): pre init_jdwp()"); diff --git a/adb/daemon/mdns.cpp b/adb/daemon/mdns.cpp new file mode 100644 index 000000000..781114351 --- /dev/null +++ b/adb/daemon/mdns.cpp @@ -0,0 +1,95 @@ +/* + * 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 "adb_mdns.h" +#include "sysdeps.h" + +#include +#include +#include +#include +#include + +#include +#include + +using namespace std::chrono_literals; + +static std::mutex& mdns_lock = *new std::mutex(); +static int port; +static DNSServiceRef mdns_ref; +static bool mdns_registered = false; + +static void start_mdns() { + 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 setup_mdns_thread(void* /* unused */) { + start_mdns(); + std::lock_guard lock(mdns_lock); + + std::string hostname = "adb-"; + hostname += android::base::GetProperty("ro.serialno", "unidentified"); + + auto error = DNSServiceRegister(&mdns_ref, 0, 0, hostname.c_str(), + kADBServiceType, nullptr, nullptr, + htobe16((uint16_t)port), 0, nullptr, + mdns_callback, nullptr); + + if (error != kDNSServiceErr_NoError) { + LOG(ERROR) << "Could not register mDNS service (" << error << ")."; + mdns_registered = false; + } + + mdns_registered = true; +} + +static void teardown_mdns() { + std::lock_guard lock(mdns_lock); + + if (mdns_registered) { + DNSServiceRefDeallocate(mdns_ref); + } +} + +void setup_mdns(int port_in) { + port = port_in; + adb_thread_create(setup_mdns_thread, nullptr, nullptr); + + // TODO: Make this more robust against a hard kill. + atexit(teardown_mdns); +} diff --git a/adb/daemon/mdns.h b/adb/daemon/mdns.h new file mode 100644 index 000000000..4c6b1ca02 --- /dev/null +++ b/adb/daemon/mdns.h @@ -0,0 +1,22 @@ +/* + * 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. + */ + +#ifndef _DAEMON_MDNS_H_ +#define _DAEMON_MDNS_H_ + +void setup_mdns(int port); + +#endif // _DAEMON_MDNS_H_ diff --git a/adb/services.cpp b/adb/services.cpp index a48d85512..47f0a03d7 100644 --- a/adb/services.cpp +++ b/adb/services.cpp @@ -377,45 +377,6 @@ static void wait_for_state(int fd, void* data) { D("wait_for_state is done"); } -static void connect_device(const std::string& address, std::string* response) { - if (address.empty()) { - *response = "empty address"; - return; - } - - std::string serial; - std::string host; - int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; - if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) { - return; - } - - std::string error; - int fd = network_connect(host.c_str(), port, SOCK_STREAM, 10, &error); - if (fd == -1) { - *response = android::base::StringPrintf("unable to connect to %s: %s", - serial.c_str(), error.c_str()); - return; - } - - D("client: connected %s remote on fd %d", serial.c_str(), fd); - close_on_exec(fd); - disable_tcp_nagle(fd); - - // Send a TCP keepalive ping to the device every second so we can detect disconnects. - if (!set_tcp_keepalive(fd, 1)) { - D("warning: failed to configure TCP keepalives (%s)", strerror(errno)); - } - - int ret = register_socket_transport(fd, serial.c_str(), port, 0); - if (ret < 0) { - adb_close(fd); - *response = android::base::StringPrintf("already connected to %s", serial.c_str()); - } else { - *response = android::base::StringPrintf("connected to %s", serial.c_str()); - } -} - void connect_emulator(const std::string& port_spec, std::string* response) { std::vector pieces = android::base::Split(port_spec, ","); if (pieces.size() != 2) { diff --git a/adb/sysdeps.h b/adb/sysdeps.h index 654072c35..f195b4eb4 100644 --- a/adb/sysdeps.h +++ b/adb/sysdeps.h @@ -197,6 +197,7 @@ extern int adb_write(int fd, const void* buf, int len); extern int adb_lseek(int fd, int pos, int where); extern int adb_shutdown(int fd); extern int adb_close(int fd); +extern int adb_register_socket(SOCKET s); // See the comments for the !defined(_WIN32) version of unix_close(). static __inline__ int unix_close(int fd) @@ -523,6 +524,12 @@ __inline__ int adb_close(int fd) { #undef close #define close ____xxx_close +// On Windows, ADB has an indirection layer for file descriptors. If we get a +// Win32 SOCKET object from an external library, we have to map it in to that +// indirection layer, which this does. +__inline__ int adb_register_socket(int s) { + return s; +} static __inline__ int adb_read(int fd, void* buf, size_t len) { diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp index a4b5e6978..f997e6b84 100644 --- a/adb/sysdeps_win32.cpp +++ b/adb/sysdeps_win32.cpp @@ -126,10 +126,7 @@ typedef struct FHRec_ SOCKET socket; } u; - int mask; - char name[32]; - } FHRec; #define fh_handle u.handle @@ -577,7 +574,6 @@ extern int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) { static void _fh_socket_init(FH f) { f->fh_socket = INVALID_SOCKET; - f->mask = 0; } static int _fh_socket_close( FH f ) { @@ -598,7 +594,6 @@ static int _fh_socket_close( FH f ) { } f->fh_socket = INVALID_SOCKET; } - f->mask = 0; return 0; } @@ -913,6 +908,12 @@ int network_connect(const std::string& host, int port, int type, int timeout, st return fd; } +int adb_register_socket(SOCKET s) { + FH f = _fh_alloc( &_fh_socket_class ); + f->fh_socket = s; + return _fh_to_int(f); +} + #undef accept int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen) { @@ -1113,18 +1114,22 @@ bool set_file_block_mode(int fd, bool block) { if (!fh || !fh->used) { errno = EBADF; + D("Setting nonblocking on bad file descriptor %d", fd); return false; } if (fh->clazz == &_fh_socket_class) { u_long x = !block; if (ioctlsocket(fh->u.socket, FIONBIO, &x) != 0) { - _socket_set_errno(WSAGetLastError()); + int error = WSAGetLastError(); + _socket_set_errno(error); + D("Setting %d nonblocking failed (%d)", fd, error); return false; } return true; } else { errno = ENOTSOCK; + D("Setting nonblocking on non-socket %d", fd); return false; } } diff --git a/adb/transport.h b/adb/transport.h index 490e51329..4d97fc78b 100644 --- a/adb/transport.h +++ b/adb/transport.h @@ -187,6 +187,7 @@ void kick_transport(atransport* t); void update_transports(void); void init_transport_registration(void); +void init_mdns_transport_discovery(void); std::string list_transports(bool long_listing); atransport* find_transport(const char* serial); void kick_all_tcp_devices(); @@ -194,6 +195,9 @@ void kick_all_tcp_devices(); void register_usb_transport(usb_handle* h, const char* serial, const char* devpath, unsigned writeable); +/* Connect to a network address and register it as a device */ +void connect_device(const std::string& address, std::string* response); + /* cause new transports to be init'd and added to the list */ int register_socket_transport(int s, const char* serial, int port, int local); diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp index b5d0ef070..12b98ba3b 100644 --- a/adb/transport_local.cpp +++ b/adb/transport_local.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -101,6 +102,46 @@ bool local_connect(int port) { return local_connect_arbitrary_ports(port-1, port, &dummy) == 0; } +void connect_device(const std::string& address, std::string* response) { + if (address.empty()) { + *response = "empty address"; + return; + } + + std::string serial; + std::string host; + int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; + if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) { + return; + } + + std::string error; + int fd = network_connect(host.c_str(), port, SOCK_STREAM, 10, &error); + if (fd == -1) { + *response = android::base::StringPrintf("unable to connect to %s: %s", + serial.c_str(), error.c_str()); + return; + } + + D("client: connected %s remote on fd %d", serial.c_str(), fd); + close_on_exec(fd); + disable_tcp_nagle(fd); + + // Send a TCP keepalive ping to the device every second so we can detect disconnects. + if (!set_tcp_keepalive(fd, 1)) { + D("warning: failed to configure TCP keepalives (%s)", strerror(errno)); + } + + int ret = register_socket_transport(fd, serial.c_str(), port, 0); + if (ret < 0) { + adb_close(fd); + *response = android::base::StringPrintf("already connected to %s", serial.c_str()); + } else { + *response = android::base::StringPrintf("connected to %s", serial.c_str()); + } +} + + int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error) { int fd = -1; diff --git a/adb/transport_mdns.cpp b/adb/transport_mdns.cpp new file mode 100644 index 000000000..e49b1c668 --- /dev/null +++ b/adb/transport_mdns.cpp @@ -0,0 +1,280 @@ +/* + * 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. + */ + +#define TRACE_TAG TRANSPORT + +#include "transport.h" + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include +#include + +#include "adb_mdns.h" +#include "adb_trace.h" +#include "fdevent.h" +#include "sysdeps.h" + +static DNSServiceRef service_ref; +static fdevent service_ref_fde; + +// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD() +// directly so that the socket is put through the appropriate compatibility +// layers to work with the rest of ADB's internal APIs. +static inline int adb_DNSServiceRefSockFD(DNSServiceRef ref) { + return adb_register_socket(DNSServiceRefSockFD(ref)); +} +#define DNSServiceRefSockFD ___xxx_DNSServiceRefSockFD + +static void DNSSD_API register_service_ip(DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char* hostname, + const sockaddr* address, + uint32_t ttl, + void* context); + +static void pump_service_ref(int /*fd*/, unsigned ev, void* data) { + DNSServiceRef* ref = reinterpret_cast(data); + + if (ev & FDE_READ) + DNSServiceProcessResult(*ref); +} + +class AsyncServiceRef { + public: + bool Initialized() { + return initialized_; + } + + virtual ~AsyncServiceRef() { + if (! initialized_) { + return; + } + + DNSServiceRefDeallocate(sdRef_); + fdevent_remove(&fde_); + } + + protected: + DNSServiceRef sdRef_; + + void Initialize() { + fdevent_install(&fde_, adb_DNSServiceRefSockFD(sdRef_), + pump_service_ref, &sdRef_); + fdevent_set(&fde_, FDE_READ); + initialized_ = true; + } + + private: + bool initialized_; + fdevent fde_; +}; + +class ResolvedService : public AsyncServiceRef { + public: + virtual ~ResolvedService() = default; + + ResolvedService(std::string name, uint32_t interfaceIndex, + const char* hosttarget, uint16_t port) : + name_(name), + port_(port) { + + /* TODO: We should be able to get IPv6 support by adding + * kDNSServiceProtocol_IPv6 to the flags below. However, when we do + * this, we get served link-local addresses that are usually useless to + * connect to. What's more, we seem to /only/ get those and nothing else. + * If we want IPv6 in the future we'll have to figure out why. + */ + DNSServiceErrorType ret = + DNSServiceGetAddrInfo( + &sdRef_, 0, interfaceIndex, + kDNSServiceProtocol_IPv4, hosttarget, + register_service_ip, reinterpret_cast(this)); + + if (ret != kDNSServiceErr_NoError) { + D("Got %d from DNSServiceGetAddrInfo.", ret); + } else { + Initialize(); + } + } + + void Connect(const sockaddr* address) { + char ip_addr[INET6_ADDRSTRLEN]; + const void* ip_addr_data; + const char* addr_format; + + if (address->sa_family == AF_INET) { + ip_addr_data = + &reinterpret_cast(address)->sin_addr; + addr_format = "%s:%hu"; + } else if (address->sa_family == AF_INET6) { + ip_addr_data = + &reinterpret_cast(address)->sin6_addr; + addr_format = "[%s]:%hu"; + } else { // Should be impossible + D("mDNS resolved non-IP address."); + return; + } + + // Winsock version requires the const cast Because Microsoft. + if (!inet_ntop(address->sa_family, const_cast(ip_addr_data), + ip_addr, INET6_ADDRSTRLEN)) { + D("Could not convert IP address to string."); + return; + } + + std::string response; + connect_device(android::base::StringPrintf(addr_format, ip_addr, port_), + &response); + D("Connect to %s (%s:%hu) : %s", name_.c_str(), ip_addr, port_, + response.c_str()); + } + + private: + std::string name_; + const uint16_t port_; +}; + +static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/, + DNSServiceFlags /*flags*/, + uint32_t /*interfaceIndex*/, + DNSServiceErrorType /*errorCode*/, + const char* /*hostname*/, + const sockaddr* address, + uint32_t /*ttl*/, + void* context) { + D("Got IP for service."); + std::unique_ptr data( + reinterpret_cast(context)); + data->Connect(address); +} + +static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char* fullname, + const char* hosttarget, + uint16_t port, + uint16_t txtLen, + const unsigned char* txtRecord, + void* context); + +class DiscoveredService : public AsyncServiceRef { + public: + DiscoveredService(uint32_t interfaceIndex, const char* serviceName, + const char* regtype, const char* domain) + : serviceName_(serviceName) { + + DNSServiceErrorType ret = + DNSServiceResolve(&sdRef_, 0, interfaceIndex, serviceName, regtype, + domain, register_resolved_mdns_service, + reinterpret_cast(this)); + + if (ret != kDNSServiceErr_NoError) { + D("Got %d from DNSServiceResolve.", ret); + } else { + Initialize(); + } + } + + const char* ServiceName() { + return serviceName_.c_str(); + } + + private: + std::string serviceName_; +}; + +static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char* fullname, + const char* hosttarget, + uint16_t port, + uint16_t /*txtLen*/, + const unsigned char* /*txtRecord*/, + void* context) { + D("Resolved a service."); + std::unique_ptr discovered( + reinterpret_cast(context)); + + if (errorCode != kDNSServiceErr_NoError) { + D("Got error %d resolving service.", errorCode); + return; + } + + + auto resolved = + new ResolvedService(discovered->ServiceName(), + interfaceIndex, hosttarget, ntohs(port)); + + if (! resolved->Initialized()) { + delete resolved; + } + + if (flags) { /* Only ever equals MoreComing or 0 */ + discovered.release(); + } +} + +static void DNSSD_API register_mdns_transport(DNSServiceRef sdRef, + DNSServiceFlags flags, + 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); + fdevent_remove(&service_ref_fde); + return; + } + + auto discovered = new DiscoveredService(interfaceIndex, serviceName, + regtype, domain); + + if (! discovered->Initialized()) { + delete discovered; + } +} + +void init_mdns_transport_discovery(void) { + DNSServiceErrorType errorCode = + DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr, + register_mdns_transport, nullptr); + + if (errorCode != kDNSServiceErr_NoError) { + D("Got %d initiating mDNS browse.", errorCode); + return; + } + + fdevent_install(&service_ref_fde, + adb_DNSServiceRefSockFD(service_ref), + pump_service_ref, + &service_ref); + fdevent_set(&service_ref_fde, FDE_READ); +} diff --git a/adb/transport_mdns_unsupported.cpp b/adb/transport_mdns_unsupported.cpp new file mode 100644 index 000000000..387d34132 --- /dev/null +++ b/adb/transport_mdns_unsupported.cpp @@ -0,0 +1,18 @@ +/* + * 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. + */ + +/* For when mDNS discovery is unsupported */ +void init_mdns_transport_discovery(void) {}