diff --git a/adb/adb.cpp b/adb/adb.cpp index 06e4c5085..a5b2f7b84 100644 --- a/adb/adb.cpp +++ b/adb/adb.cpp @@ -1134,7 +1134,9 @@ bool handle_host_request(const char* service, TransportType type, const char* se std::string host; int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; std::string error; - if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) { + if (address.starts_with("vsock:")) { + serial = address; + } else if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) { SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s", address.c_str(), error.c_str())); return true; diff --git a/adb/daemon/transport_qemu.cpp b/adb/daemon/transport_qemu.cpp index 8ad25729a..e996c1733 100644 --- a/adb/daemon/transport_qemu.cpp +++ b/adb/daemon/transport_qemu.cpp @@ -78,7 +78,7 @@ void qemu_socket_thread(int port) { /* This could be an older version of the emulator, that doesn't * implement adb QEMUD service. Fall back to the old TCP way. */ D("adb service is not available. Falling back to TCP socket."); - std::thread(server_socket_thread, port).detach(); + std::thread(server_socket_thread, android::base::StringPrintf("tcp:%d", port)).detach(); return; } diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp index cc67b6b9f..de4fff98d 100644 --- a/adb/socket_spec.cpp +++ b/adb/socket_spec.cpp @@ -46,6 +46,11 @@ using android::base::StringPrintf; #define ADB_WINDOWS 0 #endif +#if ADB_LINUX +#include +#include "sysdeps/vm_sockets.h" +#endif + // Not static because it is used in commandline.c. int gListenAll = 0; @@ -174,6 +179,62 @@ bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std return true; } return false; + } else if (address.starts_with("vsock:")) { +#if ADB_LINUX + std::string spec_str(address); + std::vector fragments = android::base::Split(spec_str, ":"); + unsigned int port_value = port ? *port : 0; + if (fragments.size() != 2 && fragments.size() != 3) { + *error = android::base::StringPrintf("expected vsock:cid or vsock:port:cid in '%s'", + spec_str.c_str()); + errno = EINVAL; + return false; + } + unsigned int cid = 0; + if (!android::base::ParseUint(fragments[1], &cid)) { + *error = android::base::StringPrintf("could not parse vsock cid in '%s'", + spec_str.c_str()); + errno = EINVAL; + return false; + } + if (fragments.size() == 3 && !android::base::ParseUint(fragments[2], &port_value)) { + *error = android::base::StringPrintf("could not parse vsock port in '%s'", + spec_str.c_str()); + errno = EINVAL; + return false; + } + if (port_value == 0) { + *error = android::base::StringPrintf("vsock port was not provided."); + errno = EINVAL; + return false; + } + fd->reset(socket(AF_VSOCK, SOCK_STREAM, 0)); + if (fd->get() == -1) { + *error = "could not open vsock socket"; + return false; + } + sockaddr_vm addr{}; + addr.svm_family = AF_VSOCK; + addr.svm_port = port_value; + addr.svm_cid = cid; + if (serial) { + *serial = android::base::StringPrintf("vsock:%u:%d", cid, port_value); + } + if (connect(fd->get(), reinterpret_cast(&addr), sizeof(addr))) { + int error_num = errno; + *error = android::base::StringPrintf("could not connect to vsock address '%s'", + spec_str.c_str()); + errno = error_num; + return false; + } + if (port) { + *port = port_value; + } + return true; +#else // ADB_LINUX + *error = "vsock is only supported on linux"; + return false; +#endif // ADB_LINUX } for (const auto& it : kLocalSocketTypes) { @@ -187,6 +248,9 @@ bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std fd->reset(network_local_client(&address[prefix.length()], it.second.socket_namespace, SOCK_STREAM, error)); + if (serial) { + *serial = address; + } return true; } } @@ -196,7 +260,7 @@ bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std return false; } -int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port) { +int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_port) { if (spec.starts_with("tcp:")) { std::string hostname; int port; @@ -215,10 +279,59 @@ int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_ return -1; } - if (result >= 0 && port == 0 && resolved_tcp_port) { - *resolved_tcp_port = adb_socket_get_local_port(result); + if (result >= 0 && resolved_port) { + *resolved_port = adb_socket_get_local_port(result); } return result; + } else if (spec.starts_with("vsock:")) { +#if ADB_LINUX + std::string spec_str(spec); + std::vector fragments = android::base::Split(spec_str, ":"); + if (fragments.size() != 2) { + *error = "given vsock server socket string was invalid"; + return -1; + } + int port; + if (!android::base::ParseInt(fragments[1], &port)) { + *error = "could not parse vsock port"; + errno = EINVAL; + return -1; + } else if (port < 0) { + *error = "vsock port was negative."; + errno = EINVAL; + return -1; + } + unique_fd serverfd(socket(AF_VSOCK, SOCK_STREAM, 0)); + if (serverfd == -1) { + int error_num = errno; + *error = android::base::StringPrintf("could not create vsock server: '%s'", + strerror(error_num)); + errno = error_num; + return -1; + } + sockaddr_vm addr{}; + addr.svm_family = AF_VSOCK; + addr.svm_port = port == 0 ? VMADDR_PORT_ANY : port; + addr.svm_cid = VMADDR_CID_ANY; + socklen_t addr_len = sizeof(addr); + if (bind(serverfd, reinterpret_cast(&addr), addr_len)) { + return -1; + } + if (listen(serverfd, 4)) { + return -1; + } + if (serverfd >= 0 && resolved_port) { + if (getsockname(serverfd, reinterpret_cast(&addr), &addr_len) == 0) { + *resolved_port = addr.svm_port; + } else { + return -1; + } + } + return serverfd.release(); +#else // ADB_LINUX + *error = "vsock is only supported on linux"; + return -1; +#endif // ADB_LINUX } for (const auto& it : kLocalSocketTypes) { diff --git a/adb/sockets.cpp b/adb/sockets.cpp index 28da975f5..5b4c51e3a 100644 --- a/adb/sockets.cpp +++ b/adb/sockets.cpp @@ -609,6 +609,14 @@ bool parse_host_service(std::string_view* out_serial, std::string_view* out_comm return false; } } + if (command.starts_with("vsock:")) { + // vsock serials are vsock:cid:port, which have an extra colon compared to tcp. + size_t next_colon = command.find(':'); + if (next_colon == std::string::npos) { + return false; + } + consume(next_colon + 1); + } bool found_address = false; if (command[0] == '[') { diff --git a/adb/sysdeps/vm_sockets.h b/adb/sysdeps/vm_sockets.h new file mode 100644 index 000000000..75c5f44af --- /dev/null +++ b/adb/sysdeps/vm_sockets.h @@ -0,0 +1,49 @@ +#if __BIONIC__ +#include +#else +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** Copied and modified from bionic/libc/kernel/uapi/linux/vm_sockets.h + *** + **************************************************************************** + ****************************************************************************/ +#ifndef _UAPI_VM_SOCKETS_H +#define _UAPI_VM_SOCKETS_H +#include +#define SO_VM_SOCKETS_BUFFER_SIZE 0 +#define SO_VM_SOCKETS_BUFFER_MIN_SIZE 1 +#define SO_VM_SOCKETS_BUFFER_MAX_SIZE 2 +#define SO_VM_SOCKETS_PEER_HOST_VM_ID 3 +#define SO_VM_SOCKETS_TRUSTED 5 +#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6 +#define SO_VM_SOCKETS_NONBLOCK_TXRX 7 +#define VMADDR_CID_ANY -1U +#define VMADDR_PORT_ANY -1U +#define VMADDR_CID_HYPERVISOR 0 +#define VMADDR_CID_RESERVED 1 +#define VMADDR_CID_HOST 2 +#define VM_SOCKETS_INVALID_VERSION -1U +#define VM_SOCKETS_VERSION_EPOCH(_v) (((_v)&0xFF000000) >> 24) +#define VM_SOCKETS_VERSION_MAJOR(_v) (((_v)&0x00FF0000) >> 16) +#define VM_SOCKETS_VERSION_MINOR(_v) (((_v)&0x0000FFFF)) +struct sockaddr_vm { + __kernel_sa_family_t svm_family; + unsigned short svm_reserved1; + unsigned int svm_port; + unsigned int svm_cid; + unsigned char svm_zero[sizeof(struct sockaddr) - sizeof(sa_family_t) - sizeof(unsigned short) - + sizeof(unsigned int) - sizeof(unsigned int)]; +}; +#define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9) +#ifndef AF_VSOCK +#define AF_VSOCK 40 +#endif +#endif +#endif diff --git a/adb/transport.h b/adb/transport.h index 3baeb1cf7..71e485725 100644 --- a/adb/transport.h +++ b/adb/transport.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -400,7 +401,7 @@ void send_packet(apacket* p, atransport* t); asocket* create_device_tracker(bool long_output); #if !ADB_HOST -void server_socket_thread(int port); +void server_socket_thread(std::string_view spec); #if defined(__ANDROID__) void qemu_socket_thread(int port); diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp index 8885db489..c254d1d62 100644 --- a/adb/transport_local.cpp +++ b/adb/transport_local.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -74,14 +75,15 @@ std::tuple tcp_connect(const std::string& address, unique_fd fd; int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; std::string serial; - if (socket_spec_connect(&fd, "tcp:" + address, &port, &serial, response)) { + std::string prefix_addr = address.starts_with("vsock:") ? address : "tcp:" + address; + if (socket_spec_connect(&fd, prefix_addr, &port, &serial, response)) { close_on_exec(fd); if (!set_tcp_keepalive(fd, 1)) { D("warning: failed to configure TCP keepalives (%s)", strerror(errno)); } return std::make_tuple(std::move(fd), port, serial); } - return std::make_tuple(unique_fd(), 0, ""); + return std::make_tuple(unique_fd(), 0, serial); } void connect_device(const std::string& address, std::string* response) { @@ -95,6 +97,9 @@ void connect_device(const std::string& address, std::string* response) { int port; std::string serial; std::tie(fd, port, serial) = tcp_connect(address, response); + if (fd.get() == -1) { + return; + } auto reconnect = [address](atransport* t) { std::string response; unique_fd fd; @@ -231,16 +236,20 @@ static void client_socket_thread(int) { #else // !ADB_HOST -void server_socket_thread(int port) { +void server_socket_thread(std::string_view spec) { unique_fd serverfd; adb_thread_setname("server socket"); D("transport: server_socket_thread() starting"); + int port; while (serverfd == -1) { - std::string spec = android::base::StringPrintf("tcp:%d", port); std::string error; - serverfd.reset(socket_spec_listen(spec, &error)); - if (serverfd < 0) { + errno = 0; + serverfd.reset(socket_spec_listen(spec, &error, &port)); + if (errno == EAFNOSUPPORT || errno == EINVAL || errno == EPROTONOSUPPORT) { + D("unrecoverable error: '%s'", error.c_str()); + return; + } else if (serverfd < 0) { D("server: cannot bind socket yet: %s", error.c_str()); std::this_thread::sleep_for(1s); continue; @@ -249,7 +258,8 @@ void server_socket_thread(int port) { } while (true) { - D("server: trying to get new connection from %d", port); + std::string spec_str{spec}; + D("server: trying to get new connection from %s", spec_str.c_str()); unique_fd fd(adb_socket_accept(serverfd, nullptr, nullptr)); if (fd >= 0) { D("server: new connection on fd %d", fd.get()); @@ -266,25 +276,25 @@ void server_socket_thread(int port) { #endif void local_init(int port) { - void (*func)(int); - const char* debug_name = ""; - #if ADB_HOST - func = client_socket_thread; - debug_name = "client"; + D("transport: local client init"); + std::thread(client_socket_thread, port).detach(); #elif !defined(__ANDROID__) // Host adbd. - func = server_socket_thread; - debug_name = "server"; + D("transport: local server init"); + std::thread(server_socket_thread, android::base::StringPrintf("tcp:%d", port)).detach(); + std::thread(server_socket_thread, android::base::StringPrintf("vsock:%d", port)).detach(); #else + D("transport: local server init"); // For the adbd daemon in the system image we need to distinguish // between the device, and the emulator. - func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread; - debug_name = "server"; + if (use_qemu_goldfish()) { + std::thread(qemu_socket_thread, port).detach(); + } else { + std::thread(server_socket_thread, android::base::StringPrintf("tcp:%d", port)).detach(); + } + std::thread(server_socket_thread, android::base::StringPrintf("vsock:%d", port)).detach(); #endif // !ADB_HOST - - D("transport: local %s init", debug_name); - std::thread(func, port).detach(); } #if ADB_HOST