You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
799 lines
24 KiB
799 lines
24 KiB
/* Copyright (C) 2017 The Android Open Source Project
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This file implements interfaces from the file jdwpTransport.h. This
|
|
* implementation is licensed under the same terms as the file
|
|
* jdwpTransport.h. The copyright and license information for the file
|
|
* jdwpTransport.h follows.
|
|
*
|
|
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
#include "dt_fd_forward.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <android-base/endian.h>
|
|
#include <android-base/logging.h>
|
|
#include <android-base/parseint.h>
|
|
#include <android-base/stringprintf.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/eventfd.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <poll.h>
|
|
|
|
#include <jni.h>
|
|
#include <jdwpTransport.h>
|
|
|
|
#include <base/strlcpy.h>
|
|
|
|
#include "fd_transport.h"
|
|
|
|
namespace dt_fd_forward {
|
|
|
|
// Helper that puts line-number in error message.
|
|
#define DT_IO_ERROR(f) \
|
|
SetLastError(::android::base::StringPrintf("%s:%d - %s: %s", \
|
|
__FILE__, __LINE__, f, strerror(errno)))
|
|
|
|
extern const jdwpTransportNativeInterface_ gTransportInterface;
|
|
|
|
template <typename T> static T HostToNetwork(T in);
|
|
template <typename T> static T NetworkToHost(T in);
|
|
|
|
template<> int8_t HostToNetwork(int8_t in) { return in; }
|
|
template<> int8_t NetworkToHost(int8_t in) { return in; }
|
|
template<> int16_t HostToNetwork(int16_t in) { return htons(in); }
|
|
template<> int16_t NetworkToHost(int16_t in) { return ntohs(in); }
|
|
template<> int32_t HostToNetwork(int32_t in) { return htonl(in); }
|
|
template<> int32_t NetworkToHost(int32_t in) { return ntohl(in); }
|
|
|
|
FdForwardTransport::FdForwardTransport(jdwpTransportCallback* cb)
|
|
: mem_(*cb),
|
|
read_fd_(-1),
|
|
write_fd_(-1),
|
|
wakeup_fd_(eventfd(0, EFD_NONBLOCK)),
|
|
listen_fd_(-1),
|
|
close_notify_fd_(-1),
|
|
state_(TransportState::kClosed),
|
|
current_seq_num_(0) {}
|
|
|
|
FdForwardTransport::~FdForwardTransport() { }
|
|
|
|
bool FdForwardTransport::ChangeState(TransportState old_state, TransportState new_state) {
|
|
if (old_state == state_) {
|
|
state_ = new_state;
|
|
state_cv_.notify_all();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
jdwpTransportError FdForwardTransport::PerformAttach(int listen_fd) {
|
|
jdwpTransportError err = SetupListen(listen_fd);
|
|
if (err != OK) {
|
|
return OK;
|
|
}
|
|
err = Accept();
|
|
StopListening();
|
|
return err;
|
|
}
|
|
|
|
static void SendListenMessage(const android::base::unique_fd& fd) {
|
|
TEMP_FAILURE_RETRY(send(fd, kListenStartMessage, sizeof(kListenStartMessage), MSG_EOR));
|
|
}
|
|
|
|
// Copy from file_utils, so we do not need to depend on libartbase.
|
|
static int DupCloexec(int fd) {
|
|
#if defined(__linux__)
|
|
return fcntl(fd, F_DUPFD_CLOEXEC, 0);
|
|
#else
|
|
return dup(fd);
|
|
#endif
|
|
}
|
|
|
|
jdwpTransportError FdForwardTransport::SetupListen(int listen_fd) {
|
|
std::lock_guard<std::mutex> lk(state_mutex_);
|
|
if (!ChangeState(TransportState::kClosed, TransportState::kListenSetup)) {
|
|
return ERR(ILLEGAL_STATE);
|
|
} else {
|
|
listen_fd_.reset(DupCloexec(listen_fd));
|
|
SendListenMessage(listen_fd_);
|
|
CHECK(ChangeState(TransportState::kListenSetup, TransportState::kListening));
|
|
return OK;
|
|
}
|
|
}
|
|
|
|
static void SendListenEndMessage(const android::base::unique_fd& fd) {
|
|
TEMP_FAILURE_RETRY(send(fd, kListenEndMessage, sizeof(kListenEndMessage), MSG_EOR));
|
|
}
|
|
|
|
jdwpTransportError FdForwardTransport::StopListening() {
|
|
std::lock_guard<std::mutex> lk(state_mutex_);
|
|
if (listen_fd_ != -1) {
|
|
SendListenEndMessage(listen_fd_);
|
|
}
|
|
// Don't close the listen_fd_ since we might need it for later calls to listen.
|
|
if (ChangeState(TransportState::kListening, TransportState::kClosed) ||
|
|
state_ == TransportState::kOpen) {
|
|
listen_fd_.reset();
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
// Last error message.
|
|
thread_local std::string global_last_error_;
|
|
|
|
void FdForwardTransport::SetLastError(const std::string& desc) {
|
|
LOG(ERROR) << desc;
|
|
global_last_error_ = desc;
|
|
}
|
|
|
|
IOResult FdForwardTransport::ReadFullyWithoutChecks(void* data, size_t ndata) {
|
|
uint8_t* bdata = reinterpret_cast<uint8_t*>(data);
|
|
size_t nbytes = 0;
|
|
while (nbytes < ndata) {
|
|
int res = TEMP_FAILURE_RETRY(read(read_fd_, bdata + nbytes, ndata - nbytes));
|
|
if (res < 0) {
|
|
DT_IO_ERROR("Failed read()");
|
|
return IOResult::kError;
|
|
} else if (res == 0) {
|
|
return IOResult::kEOF;
|
|
} else {
|
|
nbytes += res;
|
|
}
|
|
}
|
|
return IOResult::kOk;
|
|
}
|
|
|
|
IOResult FdForwardTransport::ReadUpToMax(void* data, size_t ndata, /*out*/size_t* read_amount) {
|
|
CHECK_GE(read_fd_.get(), 0);
|
|
int avail;
|
|
int res = TEMP_FAILURE_RETRY(ioctl(read_fd_, FIONREAD, &avail));
|
|
if (res < 0) {
|
|
DT_IO_ERROR("Failed ioctl(read_fd_, FIONREAD, &avail)");
|
|
return IOResult::kError;
|
|
}
|
|
size_t to_read = std::min(static_cast<size_t>(avail), ndata);
|
|
*read_amount = to_read;
|
|
if (*read_amount == 0) {
|
|
// Check if the read would cause an EOF.
|
|
struct pollfd pollfd = { read_fd_, POLLRDHUP, 0 };
|
|
res = TEMP_FAILURE_RETRY(poll(&pollfd, /*nfds*/1, /*timeout*/0));
|
|
if (res < 0 || (pollfd.revents & POLLERR) == POLLERR) {
|
|
DT_IO_ERROR("Failed poll on read fd.");
|
|
return IOResult::kError;
|
|
}
|
|
return ((pollfd.revents & (POLLRDHUP | POLLHUP)) == 0) ? IOResult::kOk : IOResult::kEOF;
|
|
}
|
|
|
|
return ReadFullyWithoutChecks(data, to_read);
|
|
}
|
|
|
|
IOResult FdForwardTransport::ReadFully(void* data, size_t ndata) {
|
|
uint64_t seq_num = current_seq_num_;
|
|
size_t nbytes = 0;
|
|
while (nbytes < ndata) {
|
|
size_t read_len;
|
|
struct pollfd pollfds[2];
|
|
{
|
|
std::lock_guard<std::mutex> lk(state_mutex_);
|
|
// Operations in this block must not cause an unbounded pause.
|
|
if (state_ != TransportState::kOpen || seq_num != current_seq_num_) {
|
|
// Async-close occurred!
|
|
return IOResult::kInterrupt;
|
|
} else {
|
|
CHECK_GE(read_fd_.get(), 0);
|
|
}
|
|
IOResult res = ReadUpToMax(reinterpret_cast<uint8_t*>(data) + nbytes,
|
|
ndata - nbytes,
|
|
/*out*/&read_len);
|
|
if (res != IOResult::kOk) {
|
|
return res;
|
|
} else {
|
|
nbytes += read_len;
|
|
}
|
|
|
|
pollfds[0] = { read_fd_, POLLRDHUP | POLLIN, 0 };
|
|
pollfds[1] = { wakeup_fd_, POLLIN, 0 };
|
|
}
|
|
if (read_len == 0) {
|
|
// No more data. Sleep without locks until more is available. We don't actually check for any
|
|
// errors since possible ones are (1) the read_fd_ is closed or wakeup happens which are both
|
|
// fine since the wakeup_fd_ or the poll failing will wake us up.
|
|
int poll_res = TEMP_FAILURE_RETRY(poll(pollfds, 2, -1));
|
|
if (poll_res < 0) {
|
|
DT_IO_ERROR("Failed to poll!");
|
|
}
|
|
// Clear the wakeup_fd regardless.
|
|
uint64_t val;
|
|
int unused = TEMP_FAILURE_RETRY(read(wakeup_fd_, &val, sizeof(val)));
|
|
DCHECK(unused == sizeof(val) || errno == EAGAIN);
|
|
if (poll_res < 0) {
|
|
return IOResult::kError;
|
|
}
|
|
}
|
|
}
|
|
return IOResult::kOk;
|
|
}
|
|
|
|
// A helper that allows us to lock the eventfd 'fd'.
|
|
class ScopedEventFdLock {
|
|
public:
|
|
explicit ScopedEventFdLock(const android::base::unique_fd& fd) : fd_(fd), data_(0) {
|
|
TEMP_FAILURE_RETRY(read(fd_, &data_, sizeof(data_)));
|
|
}
|
|
|
|
~ScopedEventFdLock() {
|
|
TEMP_FAILURE_RETRY(write(fd_, &data_, sizeof(data_)));
|
|
}
|
|
|
|
private:
|
|
const android::base::unique_fd& fd_;
|
|
uint64_t data_;
|
|
};
|
|
|
|
IOResult FdForwardTransport::WriteFullyWithoutChecks(const void* data, size_t ndata) {
|
|
ScopedEventFdLock sefdl(write_lock_fd_);
|
|
const uint8_t* bdata = static_cast<const uint8_t*>(data);
|
|
size_t nbytes = 0;
|
|
while (nbytes < ndata) {
|
|
int res = TEMP_FAILURE_RETRY(write(write_fd_, bdata + nbytes, ndata - nbytes));
|
|
if (res < 0) {
|
|
DT_IO_ERROR("Failed write()");
|
|
return IOResult::kError;
|
|
} else if (res == 0) {
|
|
return IOResult::kEOF;
|
|
} else {
|
|
nbytes += res;
|
|
}
|
|
}
|
|
return IOResult::kOk;
|
|
}
|
|
|
|
IOResult FdForwardTransport::WriteFully(const void* data, size_t ndata) {
|
|
std::lock_guard<std::mutex> lk(state_mutex_);
|
|
if (state_ != TransportState::kOpen) {
|
|
return IOResult::kInterrupt;
|
|
}
|
|
return WriteFullyWithoutChecks(data, ndata);
|
|
}
|
|
|
|
static void SendAcceptMessage(int fd) {
|
|
TEMP_FAILURE_RETRY(send(fd, kAcceptMessage, sizeof(kAcceptMessage), MSG_EOR));
|
|
}
|
|
|
|
static void SendHandshakeCompleteMessage(int fd) {
|
|
TEMP_FAILURE_RETRY(
|
|
send(fd, kHandshakeCompleteMessage, sizeof(kHandshakeCompleteMessage), MSG_EOR));
|
|
}
|
|
|
|
IOResult FdForwardTransport::ReceiveFdsFromSocket(bool* do_handshake) {
|
|
union {
|
|
cmsghdr cm;
|
|
uint8_t buffer[CMSG_SPACE(sizeof(FdSet))];
|
|
} msg_union;
|
|
// This lets us know if we need to do a handshake or not.
|
|
char message[128];
|
|
iovec iov;
|
|
iov.iov_base = message;
|
|
iov.iov_len = sizeof(message);
|
|
|
|
msghdr msg;
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = msg_union.buffer;
|
|
msg.msg_controllen = sizeof(msg_union.buffer);
|
|
|
|
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
|
cmsg->cmsg_len = msg.msg_controllen;
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_RIGHTS;
|
|
memset(reinterpret_cast<int*>(CMSG_DATA(cmsg)), -1, FdSet::kDataLength);
|
|
|
|
int res = TEMP_FAILURE_RETRY(recvmsg(listen_fd_, &msg, 0));
|
|
if (res <= 0) {
|
|
DT_IO_ERROR("Failed to receive fds!");
|
|
return IOResult::kError;
|
|
}
|
|
FdSet out_fds = FdSet::ReadData(CMSG_DATA(cmsg));
|
|
bool failed = false;
|
|
if (out_fds.read_fd_ < 0 ||
|
|
out_fds.write_fd_ < 0 ||
|
|
out_fds.write_lock_fd_ < 0) {
|
|
DT_IO_ERROR("Received fds were invalid!");
|
|
failed = true;
|
|
} else if (strcmp(kPerformHandshakeMessage, message) == 0) {
|
|
*do_handshake = true;
|
|
} else if (strcmp(kSkipHandshakeMessage, message) == 0) {
|
|
*do_handshake = false;
|
|
} else {
|
|
DT_IO_ERROR("Unknown message sent with fds.");
|
|
failed = true;
|
|
}
|
|
|
|
if (failed) {
|
|
if (out_fds.read_fd_ >= 0) {
|
|
close(out_fds.read_fd_);
|
|
}
|
|
if (out_fds.write_fd_ >= 0) {
|
|
close(out_fds.write_fd_);
|
|
}
|
|
if (out_fds.write_lock_fd_ >= 0) {
|
|
close(out_fds.write_lock_fd_);
|
|
}
|
|
return IOResult::kError;
|
|
}
|
|
|
|
read_fd_.reset(out_fds.read_fd_);
|
|
write_fd_.reset(out_fds.write_fd_);
|
|
write_lock_fd_.reset(out_fds.write_lock_fd_);
|
|
|
|
// We got the fds. Send ack.
|
|
close_notify_fd_.reset(DupCloexec(listen_fd_));
|
|
SendAcceptMessage(close_notify_fd_);
|
|
|
|
return IOResult::kOk;
|
|
}
|
|
|
|
// Accept the connection. Note that we match the behavior of other transports which is to just close
|
|
// the connection and try again if we get a bad handshake.
|
|
jdwpTransportError FdForwardTransport::Accept() {
|
|
// TODO Work with timeouts.
|
|
while (true) {
|
|
std::unique_lock<std::mutex> lk(state_mutex_);
|
|
while (!ChangeState(TransportState::kListening, TransportState::kOpening)) {
|
|
if (state_ == TransportState::kClosed ||
|
|
state_ == TransportState::kOpen) {
|
|
return ERR(ILLEGAL_STATE);
|
|
}
|
|
state_cv_.wait(lk);
|
|
}
|
|
|
|
bool do_handshake = false;
|
|
DCHECK_NE(listen_fd_.get(), -1);
|
|
if (ReceiveFdsFromSocket(&do_handshake) != IOResult::kOk) {
|
|
CHECK(ChangeState(TransportState::kOpening, TransportState::kListening));
|
|
return ERR(IO_ERROR);
|
|
}
|
|
|
|
current_seq_num_++;
|
|
|
|
// Moved to the opening state.
|
|
if (do_handshake) {
|
|
// Perform the handshake
|
|
char handshake_recv[sizeof(kJdwpHandshake)];
|
|
memset(handshake_recv, 0, sizeof(handshake_recv));
|
|
IOResult res = ReadFullyWithoutChecks(handshake_recv, sizeof(handshake_recv));
|
|
if (res != IOResult::kOk ||
|
|
strncmp(handshake_recv, kJdwpHandshake, sizeof(kJdwpHandshake)) != 0) {
|
|
DT_IO_ERROR("Failed to read handshake");
|
|
CHECK(ChangeState(TransportState::kOpening, TransportState::kListening));
|
|
CloseFdsLocked();
|
|
// Retry.
|
|
continue;
|
|
}
|
|
res = WriteFullyWithoutChecks(kJdwpHandshake, sizeof(kJdwpHandshake));
|
|
if (res != IOResult::kOk) {
|
|
DT_IO_ERROR("Failed to write handshake");
|
|
CHECK(ChangeState(TransportState::kOpening, TransportState::kListening));
|
|
CloseFdsLocked();
|
|
// Retry.
|
|
continue;
|
|
}
|
|
}
|
|
// Tell everyone we have finished the handshake.
|
|
SendHandshakeCompleteMessage(close_notify_fd_);
|
|
break;
|
|
}
|
|
CHECK(ChangeState(TransportState::kOpening, TransportState::kOpen));
|
|
return OK;
|
|
}
|
|
|
|
void SendClosingMessage(int fd) {
|
|
if (fd >= 0) {
|
|
TEMP_FAILURE_RETRY(send(fd, kCloseMessage, sizeof(kCloseMessage), MSG_EOR));
|
|
}
|
|
}
|
|
|
|
// Actually close the fds associated with this transport.
|
|
void FdForwardTransport::CloseFdsLocked() {
|
|
// We have a different set of fd's now. Increase the seq number.
|
|
current_seq_num_++;
|
|
|
|
// All access to these is locked under the state_mutex_ so we are safe to close these.
|
|
{
|
|
ScopedEventFdLock sefdl(write_lock_fd_);
|
|
if (close_notify_fd_ >= 0) {
|
|
SendClosingMessage(close_notify_fd_);
|
|
}
|
|
close_notify_fd_.reset();
|
|
read_fd_.reset();
|
|
write_fd_.reset();
|
|
close_notify_fd_.reset();
|
|
}
|
|
write_lock_fd_.reset();
|
|
|
|
// Send a wakeup in case we have any in-progress reads/writes.
|
|
uint64_t data = 1;
|
|
TEMP_FAILURE_RETRY(write(wakeup_fd_, &data, sizeof(data)));
|
|
}
|
|
|
|
jdwpTransportError FdForwardTransport::Close() {
|
|
std::lock_guard<std::mutex> lk(state_mutex_);
|
|
jdwpTransportError res =
|
|
ChangeState(TransportState::kOpen, TransportState::kClosed) ? OK : ERR(ILLEGAL_STATE);
|
|
// Send a wakeup after changing the state even if nothing actually happened.
|
|
uint64_t data = 1;
|
|
TEMP_FAILURE_RETRY(write(wakeup_fd_, &data, sizeof(data)));
|
|
if (res == OK) {
|
|
CloseFdsLocked();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// A helper class to read and parse the JDWP packet.
|
|
class PacketReader {
|
|
public:
|
|
PacketReader(FdForwardTransport* transport, jdwpPacket* pkt)
|
|
: transport_(transport),
|
|
pkt_(pkt),
|
|
is_eof_(false),
|
|
is_err_(false) {}
|
|
bool ReadFully() {
|
|
// Zero out.
|
|
memset(pkt_, 0, sizeof(jdwpPacket));
|
|
int32_t len = ReadInt32(); // read len
|
|
if (is_err_) {
|
|
return false;
|
|
} else if (is_eof_) {
|
|
return true;
|
|
} else if (len < 11) {
|
|
transport_->DT_IO_ERROR("Packet with len < 11 received!");
|
|
return false;
|
|
}
|
|
pkt_->type.cmd.len = len;
|
|
pkt_->type.cmd.id = ReadInt32();
|
|
pkt_->type.cmd.flags = ReadByte();
|
|
if (is_err_) {
|
|
return false;
|
|
} else if (is_eof_) {
|
|
return true;
|
|
} else if ((pkt_->type.reply.flags & JDWPTRANSPORT_FLAGS_REPLY) == JDWPTRANSPORT_FLAGS_REPLY) {
|
|
ReadReplyPacket();
|
|
} else {
|
|
ReadCmdPacket();
|
|
}
|
|
return !is_err_;
|
|
}
|
|
|
|
private:
|
|
void ReadReplyPacket() {
|
|
pkt_->type.reply.errorCode = ReadInt16();
|
|
pkt_->type.reply.data = ReadRemaining();
|
|
}
|
|
|
|
void ReadCmdPacket() {
|
|
pkt_->type.cmd.cmdSet = ReadByte();
|
|
pkt_->type.cmd.cmd = ReadByte();
|
|
pkt_->type.cmd.data = ReadRemaining();
|
|
}
|
|
|
|
template <typename T>
|
|
T HandleResult(IOResult res, T val, T fail) {
|
|
switch (res) {
|
|
case IOResult::kError:
|
|
is_err_ = true;
|
|
return fail;
|
|
case IOResult::kOk:
|
|
return val;
|
|
case IOResult::kEOF:
|
|
is_eof_ = true;
|
|
pkt_->type.cmd.len = 0;
|
|
return fail;
|
|
case IOResult::kInterrupt:
|
|
transport_->DT_IO_ERROR("Failed to read, concurrent close!");
|
|
is_err_ = true;
|
|
return fail;
|
|
}
|
|
}
|
|
|
|
jbyte* ReadRemaining() {
|
|
if (is_eof_ || is_err_) {
|
|
return nullptr;
|
|
}
|
|
jbyte* out = nullptr;
|
|
jint rem = pkt_->type.cmd.len - 11;
|
|
CHECK_GE(rem, 0);
|
|
if (rem == 0) {
|
|
return nullptr;
|
|
} else {
|
|
out = reinterpret_cast<jbyte*>(transport_->Alloc(rem));
|
|
IOResult res = transport_->ReadFully(out, rem);
|
|
jbyte* ret = HandleResult(res, out, static_cast<jbyte*>(nullptr));
|
|
if (ret != out) {
|
|
transport_->Free(out);
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
jbyte ReadByte() {
|
|
if (is_eof_ || is_err_) {
|
|
return -1;
|
|
}
|
|
jbyte out;
|
|
IOResult res = transport_->ReadFully(&out, sizeof(out));
|
|
return HandleResult(res, NetworkToHost(out), static_cast<jbyte>(-1));
|
|
}
|
|
|
|
jshort ReadInt16() {
|
|
if (is_eof_ || is_err_) {
|
|
return -1;
|
|
}
|
|
jshort out;
|
|
IOResult res = transport_->ReadFully(&out, sizeof(out));
|
|
return HandleResult(res, NetworkToHost(out), static_cast<jshort>(-1));
|
|
}
|
|
|
|
jint ReadInt32() {
|
|
if (is_eof_ || is_err_) {
|
|
return -1;
|
|
}
|
|
jint out;
|
|
IOResult res = transport_->ReadFully(&out, sizeof(out));
|
|
return HandleResult(res, NetworkToHost(out), -1);
|
|
}
|
|
|
|
FdForwardTransport* transport_;
|
|
jdwpPacket* pkt_;
|
|
bool is_eof_;
|
|
bool is_err_;
|
|
};
|
|
|
|
jdwpTransportError FdForwardTransport::ReadPacket(jdwpPacket* pkt) {
|
|
if (pkt == nullptr) {
|
|
return ERR(ILLEGAL_ARGUMENT);
|
|
}
|
|
PacketReader reader(this, pkt);
|
|
if (reader.ReadFully()) {
|
|
return OK;
|
|
} else {
|
|
return ERR(IO_ERROR);
|
|
}
|
|
}
|
|
|
|
// A class that writes a packet to the transport.
|
|
class PacketWriter {
|
|
public:
|
|
PacketWriter(FdForwardTransport* transport, const jdwpPacket* pkt)
|
|
: transport_(transport), pkt_(pkt), data_() {}
|
|
|
|
bool WriteFully() {
|
|
PushInt32(pkt_->type.cmd.len);
|
|
PushInt32(pkt_->type.cmd.id);
|
|
PushByte(pkt_->type.cmd.flags);
|
|
if ((pkt_->type.reply.flags & JDWPTRANSPORT_FLAGS_REPLY) == JDWPTRANSPORT_FLAGS_REPLY) {
|
|
PushInt16(pkt_->type.reply.errorCode);
|
|
PushData(pkt_->type.reply.data, pkt_->type.reply.len - 11);
|
|
} else {
|
|
PushByte(pkt_->type.cmd.cmdSet);
|
|
PushByte(pkt_->type.cmd.cmd);
|
|
PushData(pkt_->type.cmd.data, pkt_->type.cmd.len - 11);
|
|
}
|
|
IOResult res = transport_->WriteFully(data_.data(), data_.size());
|
|
return res == IOResult::kOk;
|
|
}
|
|
|
|
private:
|
|
void PushInt32(int32_t data) {
|
|
data = HostToNetwork(data);
|
|
PushData(&data, sizeof(data));
|
|
}
|
|
void PushInt16(int16_t data) {
|
|
data = HostToNetwork(data);
|
|
PushData(&data, sizeof(data));
|
|
}
|
|
void PushByte(jbyte data) {
|
|
data_.push_back(HostToNetwork(data));
|
|
}
|
|
|
|
void PushData(void* d, size_t size) {
|
|
uint8_t* bytes = reinterpret_cast<uint8_t*>(d);
|
|
data_.insert(data_.end(), bytes, bytes + size);
|
|
}
|
|
|
|
FdForwardTransport* transport_;
|
|
const jdwpPacket* pkt_;
|
|
std::vector<uint8_t> data_;
|
|
};
|
|
|
|
jdwpTransportError FdForwardTransport::WritePacket(const jdwpPacket* pkt) {
|
|
if (pkt == nullptr) {
|
|
return ERR(ILLEGAL_ARGUMENT);
|
|
}
|
|
PacketWriter writer(this, pkt);
|
|
if (writer.WriteFully()) {
|
|
return OK;
|
|
} else {
|
|
return ERR(IO_ERROR);
|
|
}
|
|
}
|
|
|
|
jboolean FdForwardTransport::IsOpen() {
|
|
return state_ == TransportState::kOpen;
|
|
}
|
|
|
|
void* FdForwardTransport::Alloc(size_t s) {
|
|
return mem_.alloc(s);
|
|
}
|
|
|
|
void FdForwardTransport::Free(void* data) {
|
|
mem_.free(data);
|
|
}
|
|
|
|
jdwpTransportError FdForwardTransport::GetLastError(/*out*/char** err) {
|
|
std::string data = global_last_error_;
|
|
*err = reinterpret_cast<char*>(Alloc(data.size() + 1));
|
|
strlcpy(*err, data.c_str(), data.size() + 1);
|
|
return OK;
|
|
}
|
|
|
|
static FdForwardTransport* AsFdForward(jdwpTransportEnv* env) {
|
|
return reinterpret_cast<FdForwardTransport*>(env);
|
|
}
|
|
|
|
static jdwpTransportError ParseAddress(const std::string& addr,
|
|
/*out*/int* listen_sock) {
|
|
if (!android::base::ParseInt(addr.c_str(), listen_sock) || *listen_sock < 0) {
|
|
LOG(ERROR) << "address format is <fd_num> not " << addr;
|
|
return ERR(ILLEGAL_ARGUMENT);
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
class JdwpTransportFunctions {
|
|
public:
|
|
static jdwpTransportError GetCapabilities(jdwpTransportEnv* env ATTRIBUTE_UNUSED,
|
|
/*out*/ JDWPTransportCapabilities* capabilities_ptr) {
|
|
// We don't support any of the optional capabilities (can_timeout_attach, can_timeout_accept,
|
|
// can_timeout_handshake) so just return a zeroed capabilities ptr.
|
|
// TODO We should maybe support these timeout options.
|
|
memset(capabilities_ptr, 0, sizeof(JDWPTransportCapabilities));
|
|
return OK;
|
|
}
|
|
|
|
// Address is <sock_fd>
|
|
static jdwpTransportError Attach(jdwpTransportEnv* env,
|
|
const char* address,
|
|
jlong attach_timeout ATTRIBUTE_UNUSED,
|
|
jlong handshake_timeout ATTRIBUTE_UNUSED) {
|
|
if (address == nullptr || *address == '\0') {
|
|
return ERR(ILLEGAL_ARGUMENT);
|
|
}
|
|
int listen_fd;
|
|
jdwpTransportError err = ParseAddress(address, &listen_fd);
|
|
if (err != OK) {
|
|
return err;
|
|
}
|
|
return AsFdForward(env)->PerformAttach(listen_fd);
|
|
}
|
|
|
|
static jdwpTransportError StartListening(jdwpTransportEnv* env,
|
|
const char* address,
|
|
/*out*/ char** actual_address) {
|
|
if (address == nullptr || *address == '\0') {
|
|
return ERR(ILLEGAL_ARGUMENT);
|
|
}
|
|
int listen_fd;
|
|
jdwpTransportError err = ParseAddress(address, &listen_fd);
|
|
if (err != OK) {
|
|
return err;
|
|
}
|
|
err = AsFdForward(env)->SetupListen(listen_fd);
|
|
if (err != OK) {
|
|
return err;
|
|
}
|
|
if (actual_address != nullptr) {
|
|
*actual_address = reinterpret_cast<char*>(AsFdForward(env)->Alloc(strlen(address) + 1));
|
|
memcpy(*actual_address, address, strlen(address) + 1);
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
static jdwpTransportError StopListening(jdwpTransportEnv* env) {
|
|
return AsFdForward(env)->StopListening();
|
|
}
|
|
|
|
static jdwpTransportError Accept(jdwpTransportEnv* env,
|
|
jlong accept_timeout ATTRIBUTE_UNUSED,
|
|
jlong handshake_timeout ATTRIBUTE_UNUSED) {
|
|
return AsFdForward(env)->Accept();
|
|
}
|
|
|
|
static jboolean IsOpen(jdwpTransportEnv* env) {
|
|
return AsFdForward(env)->IsOpen();
|
|
}
|
|
|
|
static jdwpTransportError Close(jdwpTransportEnv* env) {
|
|
return AsFdForward(env)->Close();
|
|
}
|
|
|
|
static jdwpTransportError ReadPacket(jdwpTransportEnv* env, jdwpPacket *pkt) {
|
|
return AsFdForward(env)->ReadPacket(pkt);
|
|
}
|
|
|
|
static jdwpTransportError WritePacket(jdwpTransportEnv* env, const jdwpPacket* pkt) {
|
|
return AsFdForward(env)->WritePacket(pkt);
|
|
}
|
|
|
|
static jdwpTransportError GetLastError(jdwpTransportEnv* env, char** error) {
|
|
return AsFdForward(env)->GetLastError(error);
|
|
}
|
|
};
|
|
|
|
// The actual struct holding all the entrypoints into the jdwpTransport interface.
|
|
const jdwpTransportNativeInterface_ gTransportInterface = {
|
|
nullptr, // reserved1
|
|
JdwpTransportFunctions::GetCapabilities,
|
|
JdwpTransportFunctions::Attach,
|
|
JdwpTransportFunctions::StartListening,
|
|
JdwpTransportFunctions::StopListening,
|
|
JdwpTransportFunctions::Accept,
|
|
JdwpTransportFunctions::IsOpen,
|
|
JdwpTransportFunctions::Close,
|
|
JdwpTransportFunctions::ReadPacket,
|
|
JdwpTransportFunctions::WritePacket,
|
|
JdwpTransportFunctions::GetLastError,
|
|
};
|
|
|
|
extern "C"
|
|
JNIEXPORT jint JNICALL jdwpTransport_OnLoad(JavaVM* vm ATTRIBUTE_UNUSED,
|
|
jdwpTransportCallback* cb,
|
|
jint version,
|
|
jdwpTransportEnv** /*out*/env) {
|
|
if (version != JDWPTRANSPORT_VERSION_1_0) {
|
|
LOG(ERROR) << "unknown version " << version;
|
|
return JNI_EVERSION;
|
|
}
|
|
void* data = cb->alloc(sizeof(FdForwardTransport));
|
|
if (data == nullptr) {
|
|
LOG(ERROR) << "Failed to allocate data for transport!";
|
|
return JNI_ENOMEM;
|
|
}
|
|
FdForwardTransport* transport =
|
|
new (data) FdForwardTransport(cb);
|
|
transport->functions = &gTransportInterface;
|
|
*env = transport;
|
|
return JNI_OK;
|
|
}
|
|
|
|
} // namespace dt_fd_forward
|