diff options
Diffstat (limited to 'vm/jdwp/JdwpSocket.cpp')
| -rw-r--r-- | vm/jdwp/JdwpSocket.cpp | 910 |
1 files changed, 0 insertions, 910 deletions
diff --git a/vm/jdwp/JdwpSocket.cpp b/vm/jdwp/JdwpSocket.cpp deleted file mode 100644 index eaea607be..000000000 --- a/vm/jdwp/JdwpSocket.cpp +++ /dev/null @@ -1,910 +0,0 @@ -/* - * Copyright (C) 2008 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. - */ -/* - * JDWP TCP socket network code. - */ -#include "jdwp/JdwpPriv.h" -#include "jdwp/JdwpHandler.h" -#include "Bits.h" - -#include <stdlib.h> -#include <unistd.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <arpa/inet.h> -#include <netdb.h> - -#define kBasePort 8000 -#define kMaxPort 8040 - -#define kInputBufferSize 8192 - -#define kMagicHandshake "JDWP-Handshake" -#define kMagicHandshakeLen (sizeof(kMagicHandshake)-1) - -// fwd -static void netShutdown(JdwpNetState* state); -static void netFree(JdwpNetState* state); - - -/* - * JDWP network state. - * - * We only talk to one debugger at a time. - */ -struct JdwpNetState : public JdwpNetStateBase { - short listenPort; - int listenSock; /* listen for connection from debugger */ - int wakePipe[2]; /* break out of select */ - - struct in_addr remoteAddr; - unsigned short remotePort; - - bool awaitingHandshake; /* waiting for "JDWP-Handshake" */ - - /* pending data from the network; would be more efficient as circular buf */ - unsigned char inputBuffer[kInputBufferSize]; - int inputCount; - - JdwpNetState() - { - listenPort = 0; - listenSock = -1; - wakePipe[0] = -1; - wakePipe[1] = -1; - - awaitingHandshake = false; - - inputCount = 0; - } -}; - -static JdwpNetState* netStartup(short port); - -/* - * Set up some stuff for transport=dt_socket. - */ -static bool prepareSocket(JdwpState* state, const JdwpStartupParams* pParams) -{ - unsigned short port; - - if (pParams->server) { - if (pParams->port != 0) { - /* try only the specified port */ - port = pParams->port; - state->netState = netStartup(port); - } else { - /* scan through a range of ports, binding to the first available */ - for (port = kBasePort; port <= kMaxPort; port++) { - state->netState = netStartup(port); - if (state->netState != NULL) - break; - } - } - if (state->netState == NULL) { - ALOGE("JDWP net startup failed (req port=%d)", pParams->port); - return false; - } - } else { - port = pParams->port; // used in a debug msg later - state->netState = netStartup(-1); - } - - if (pParams->suspend) - ALOGI("JDWP will wait for debugger on port %d", port); - else - ALOGD("JDWP will %s on port %d", - pParams->server ? "listen" : "connect", port); - - return true; -} - - -/* - * Are we still waiting for the handshake string? - */ -static bool awaitingHandshake(JdwpState* state) -{ - return state->netState->awaitingHandshake; -} - -/* - * Initialize JDWP stuff. - * - * Allocates a new state structure. If "port" is non-negative, this also - * tries to bind to a listen port. If "port" is less than zero, we assume - * we're preparing for an outbound connection, and return without binding - * to anything. - * - * This may be called several times if we're probing for a port. - * - * Returns 0 on success. - */ -static JdwpNetState* netStartup(short port) -{ - int one = 1; - JdwpNetState* netState = new JdwpNetState; - - if (port < 0) - return netState; - - assert(port != 0); - - netState->listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (netState->listenSock < 0) { - ALOGE("Socket create failed: %s", strerror(errno)); - goto fail; - } - - /* allow immediate re-use */ - if (setsockopt(netState->listenSock, SOL_SOCKET, SO_REUSEADDR, &one, - sizeof(one)) < 0) - { - ALOGE("setsockopt(SO_REUSEADDR) failed: %s", strerror(errno)); - goto fail; - } - - union { - struct sockaddr_in addrInet; - struct sockaddr addrPlain; - } addr; - addr.addrInet.sin_family = AF_INET; - addr.addrInet.sin_port = htons(port); - inet_aton("127.0.0.1", &addr.addrInet.sin_addr); - - if (bind(netState->listenSock, &addr.addrPlain, sizeof(addr)) != 0) { - ALOGV("attempt to bind to port %u failed: %s", port, strerror(errno)); - goto fail; - } - - netState->listenPort = port; - LOGVV("+++ bound to port %d", netState->listenPort); - - if (listen(netState->listenSock, 5) != 0) { - ALOGE("Listen failed: %s", strerror(errno)); - goto fail; - } - - return netState; - -fail: - netShutdown(netState); - netFree(netState); - return NULL; -} - -/* - * Shut down JDWP listener. Don't free state. - * - * Note that "netState" may be partially initialized if "startup" failed. - * - * This may be called from a non-JDWP thread as part of shutting the - * JDWP thread down. - * - * (This is currently called several times during startup as we probe - * for an open port.) - */ -static void netShutdown(JdwpNetState* netState) -{ - if (netState == NULL) - return; - - int listenSock = netState->listenSock; - int clientSock = netState->clientSock; - - /* clear these out so it doesn't wake up and try to reuse them */ - netState->listenSock = netState->clientSock = -1; - - /* "shutdown" dislodges blocking read() and accept() calls */ - if (listenSock >= 0) { - shutdown(listenSock, SHUT_RDWR); - close(listenSock); - } - if (clientSock >= 0) { - shutdown(clientSock, SHUT_RDWR); - close(clientSock); - } - - /* if we might be sitting in select, kick us loose */ - if (netState->wakePipe[1] >= 0) { - ALOGV("+++ writing to wakePipe"); - TEMP_FAILURE_RETRY(write(netState->wakePipe[1], "", 1)); - } -} -static void netShutdownExtern(JdwpState* state) -{ - netShutdown(state->netState); -} - -/* - * Free JDWP state. - * - * Call this after shutting the network down with netShutdown(). - */ -static void netFree(JdwpNetState* netState) -{ - if (netState == NULL) - return; - assert(netState->listenSock == -1); - assert(netState->clientSock == -1); - - if (netState->wakePipe[0] >= 0) { - close(netState->wakePipe[0]); - netState->wakePipe[0] = -1; - } - if (netState->wakePipe[1] >= 0) { - close(netState->wakePipe[1]); - netState->wakePipe[1] = -1; - } - - delete netState; -} -static void netFreeExtern(JdwpState* state) -{ - netFree(state->netState); -} - -/* - * Returns "true" if we're connected to a debugger. - */ -static bool isConnected(JdwpState* state) -{ - return (state->netState != NULL && - state->netState->clientSock >= 0); -} - -/* - * Returns "true" if the fd is ready, "false" if not. - */ -#if 0 -static bool isFdReadable(int sock) -{ - fd_set readfds; - struct timeval tv; - int count; - - FD_ZERO(&readfds); - FD_SET(sock, &readfds); - - tv.tv_sec = 0; - tv.tv_usec = 0; - count = select(sock+1, &readfds, NULL, NULL, &tv); - if (count <= 0) - return false; - - if (FD_ISSET(sock, &readfds)) /* make sure it's our fd */ - return true; - - ALOGE("WEIRD: odd behavior in select (count=%d)", count); - return false; -} -#endif - -#if 0 -/* - * Check to see if we have a pending connection from the debugger. - * - * Returns true on success (meaning a connection is available). - */ -static bool checkConnection(JdwpState* state) -{ - JdwpNetState* netState = state->netState; - - assert(netState->listenSock >= 0); - /* not expecting to be called when debugger is actively connected */ - assert(netState->clientSock < 0); - - if (!isFdReadable(netState->listenSock)) - return false; - return true; -} -#endif - -/* - * Disable the TCP Nagle algorithm, which delays transmission of outbound - * packets until the previous transmissions have been acked. JDWP does a - * lot of back-and-forth with small packets, so this may help. - */ -static int setNoDelay(int fd) -{ - int cc, on = 1; - - cc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); - assert(cc == 0); - return cc; -} - -/* - * Accept a connection. This will block waiting for somebody to show up. - * If that's not desirable, use checkConnection() to make sure something - * is pending. - */ -static bool acceptConnection(JdwpState* state) -{ - JdwpNetState* netState = state->netState; - union { - struct sockaddr_in addrInet; - struct sockaddr addrPlain; - } addr; - socklen_t addrlen; - int sock; - - if (netState->listenSock < 0) - return false; /* you're not listening! */ - - assert(netState->clientSock < 0); /* must not already be talking */ - - addrlen = sizeof(addr); - do { - sock = accept(netState->listenSock, &addr.addrPlain, &addrlen); - if (sock < 0 && errno != EINTR) { - // When we call shutdown() on the socket, accept() returns with - // EINVAL. Don't gripe about it. - if (errno == EINVAL) - LOGVV("accept failed: %s", strerror(errno)); - else - ALOGE("accept failed: %s", strerror(errno)); - return false; - } - } while (sock < 0); - - netState->remoteAddr = addr.addrInet.sin_addr; - netState->remotePort = ntohs(addr.addrInet.sin_port); - ALOGV("+++ accepted connection from %s:%u", - inet_ntoa(netState->remoteAddr), netState->remotePort); - - netState->clientSock = sock; - netState->awaitingHandshake = true; - netState->inputCount = 0; - - ALOGV("Setting TCP_NODELAY on accepted socket"); - setNoDelay(netState->clientSock); - - if (pipe(netState->wakePipe) < 0) { - ALOGE("pipe failed"); - return false; - } - - return true; -} - -/* - * Create a connection to a waiting debugger. - */ -static bool establishConnection(JdwpState* state) -{ - union { - struct sockaddr_in addrInet; - struct sockaddr addrPlain; - } addr; - struct hostent* pEntry; - int h_errno; - - assert(state != NULL && state->netState != NULL); - assert(!state->params.server); - assert(state->params.host[0] != '\0'); - assert(state->params.port != 0); - - /* - * Start by resolving the host name. - */ -//#undef HAVE_GETHOSTBYNAME_R -//#warning "forcing non-R" -#ifdef HAVE_GETHOSTBYNAME_R - struct hostent he; - char auxBuf[128]; - int cc = gethostbyname_r(state->params.host, &he, auxBuf, sizeof(auxBuf), - &pEntry, &h_errno); - if (cc != 0) { - ALOGW("gethostbyname_r('%s') failed: %s", - state->params.host, strerror(errno)); - return false; - } - -#else - h_errno = 0; - pEntry = gethostbyname(state->params.host); - if (pEntry == NULL) { - ALOGW("gethostbyname('%s') failed: %s", - state->params.host, strerror(h_errno)); - return false; - } -#endif - - /* copy it out ASAP to minimize risk of multithreaded annoyances */ - memcpy(&addr.addrInet.sin_addr, pEntry->h_addr, pEntry->h_length); - addr.addrInet.sin_family = pEntry->h_addrtype; - - addr.addrInet.sin_port = htons(state->params.port); - - ALOGI("Connecting out to '%s' %d", - inet_ntoa(addr.addrInet.sin_addr), ntohs(addr.addrInet.sin_port)); - - /* - * Create a socket. - */ - JdwpNetState* netState; - netState = state->netState; - netState->clientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (netState->clientSock < 0) { - ALOGE("Unable to create socket: %s", strerror(errno)); - return false; - } - - /* - * Try to connect. - */ - if (connect(netState->clientSock, &addr.addrPlain, sizeof(addr)) != 0) { - ALOGE("Unable to connect to %s:%d: %s", - inet_ntoa(addr.addrInet.sin_addr), ntohs(addr.addrInet.sin_port), - strerror(errno)); - close(netState->clientSock); - netState->clientSock = -1; - return false; - } - - ALOGI("Connection established to %s (%s:%d)", - state->params.host, inet_ntoa(addr.addrInet.sin_addr), - ntohs(addr.addrInet.sin_port)); - netState->awaitingHandshake = true; - netState->inputCount = 0; - - setNoDelay(netState->clientSock); - - if (pipe(netState->wakePipe) < 0) { - ALOGE("pipe failed"); - return false; - } - - return true; -} - -/* - * Close the connection to the debugger. - * - * Reset the state so we're ready to receive a new connection. - */ -static void closeConnection(JdwpState* state) -{ - JdwpNetState* netState; - - assert(state != NULL && state->netState != NULL); - - netState = state->netState; - if (netState->clientSock < 0) - return; - - ALOGV("+++ closed connection to %s:%u", - inet_ntoa(netState->remoteAddr), netState->remotePort); - - close(netState->clientSock); - netState->clientSock = -1; - - return; -} - -/* - * Figure out if we have a full packet in the buffer. - */ -static bool haveFullPacket(JdwpNetState* netState) -{ - long length; - - if (netState->awaitingHandshake) - return (netState->inputCount >= (int) kMagicHandshakeLen); - - if (netState->inputCount < 4) - return false; - - length = get4BE(netState->inputBuffer); - return (netState->inputCount >= length); -} - -/* - * Consume bytes from the buffer. - * - * This would be more efficient with a circular buffer. However, we're - * usually only going to find one packet, which is trivial to handle. - */ -static void consumeBytes(JdwpNetState* netState, int count) -{ - assert(count > 0); - assert(count <= netState->inputCount); - - if (count == netState->inputCount) { - netState->inputCount = 0; - return; - } - - memmove(netState->inputBuffer, netState->inputBuffer + count, - netState->inputCount - count); - netState->inputCount -= count; -} - -/* - * Dump the contents of a packet to stdout. - */ -#if 0 -static void dumpPacket(const unsigned char* packetBuf) -{ - const unsigned char* buf = packetBuf; - u4 length, id; - u1 flags, cmdSet, cmd; - u2 error; - bool reply; - int dataLen; - - cmd = cmdSet = 0xcc; - - length = read4BE(&buf); - id = read4BE(&buf); - flags = read1(&buf); - if ((flags & kJDWPFlagReply) != 0) { - reply = true; - error = read2BE(&buf); - } else { - reply = false; - cmdSet = read1(&buf); - cmd = read1(&buf); - } - - dataLen = length - (buf - packetBuf); - - ALOGV("--- %s: dataLen=%u id=0x%08x flags=0x%02x cmd=%d/%d", - reply ? "reply" : "req", - dataLen, id, flags, cmdSet, cmd); - if (dataLen > 0) - dvmPrintHexDumpDbg(buf, dataLen, LOG_TAG); -} -#endif - -/* - * Handle a packet. Returns "false" if we encounter a connection-fatal error. - */ -static bool handlePacket(JdwpState* state) -{ - JdwpNetState* netState = state->netState; - const unsigned char* buf = netState->inputBuffer; - JdwpReqHeader hdr; - u4 length, id; - u1 flags, cmdSet, cmd; - u2 error; - bool reply; - int dataLen; - - cmd = cmdSet = 0; // shut up gcc - - /*dumpPacket(netState->inputBuffer);*/ - - length = read4BE(&buf); - id = read4BE(&buf); - flags = read1(&buf); - if ((flags & kJDWPFlagReply) != 0) { - reply = true; - error = read2BE(&buf); - } else { - reply = false; - cmdSet = read1(&buf); - cmd = read1(&buf); - } - - assert((int) length <= netState->inputCount); - dataLen = length - (buf - netState->inputBuffer); - - if (!reply) { - ExpandBuf* pReply = expandBufAlloc(); - - hdr.length = length; - hdr.id = id; - hdr.cmdSet = cmdSet; - hdr.cmd = cmd; - dvmJdwpProcessRequest(state, &hdr, buf, dataLen, pReply); - if (expandBufGetLength(pReply) > 0) { - ssize_t cc = netState->writePacket(pReply); - - if (cc != (ssize_t) expandBufGetLength(pReply)) { - ALOGE("Failed sending reply to debugger: %s", strerror(errno)); - expandBufFree(pReply); - return false; - } - } else { - ALOGW("No reply created for set=%d cmd=%d", cmdSet, cmd); - } - expandBufFree(pReply); - } else { - ALOGV("reply?!"); - assert(false); - } - - ALOGV("----------"); - - consumeBytes(netState, length); - return true; -} - -/* - * Process incoming data. If no data is available, this will block until - * some arrives. - * - * If we get a full packet, handle it. - * - * To take some of the mystery out of life, we want to reject incoming - * connections if we already have a debugger attached. If we don't, the - * debugger will just mysteriously hang until it times out. We could just - * close the listen socket, but there's a good chance we won't be able to - * bind to the same port again, which would confuse utilities. - * - * Returns "false" on error (indicating that the connection has been severed), - * "true" if things are still okay. - */ -static bool processIncoming(JdwpState* state) -{ - JdwpNetState* netState = state->netState; - int readCount; - - assert(netState->clientSock >= 0); - - if (!haveFullPacket(netState)) { - /* read some more, looping until we have data */ - errno = 0; - while (1) { - int selCount; - fd_set readfds; - int maxfd; - int fd; - - maxfd = netState->listenSock; - if (netState->clientSock > maxfd) - maxfd = netState->clientSock; - if (netState->wakePipe[0] > maxfd) - maxfd = netState->wakePipe[0]; - - if (maxfd < 0) { - ALOGV("+++ all fds are closed"); - return false; - } - - FD_ZERO(&readfds); - - /* configure fds; note these may get zapped by another thread */ - fd = netState->listenSock; - if (fd >= 0) - FD_SET(fd, &readfds); - fd = netState->clientSock; - if (fd >= 0) - FD_SET(fd, &readfds); - fd = netState->wakePipe[0]; - if (fd >= 0) { - FD_SET(fd, &readfds); - } else { - ALOGI("NOTE: entering select w/o wakepipe"); - } - - /* - * Select blocks until it sees activity on the file descriptors. - * Closing the local file descriptor does not count as activity, - * so we can't rely on that to wake us up (it works for read() - * and accept(), but not select()). - * - * We can do one of three things: (1) send a signal and catch - * EINTR, (2) open an additional fd ("wakePipe") and write to - * it when it's time to exit, or (3) time out periodically and - * re-issue the select. We're currently using #2, as it's more - * reliable than #1 and generally better than #3. Wastes two fds. - */ - selCount = select(maxfd+1, &readfds, NULL, NULL, NULL); - if (selCount < 0) { - if (errno == EINTR) - continue; - ALOGE("select failed: %s", strerror(errno)); - goto fail; - } - - if (netState->wakePipe[0] >= 0 && - FD_ISSET(netState->wakePipe[0], &readfds)) - { - if (netState->listenSock >= 0) - ALOGE("Exit wake set, but not exiting?"); - else - ALOGD("Got wake-up signal, bailing out of select"); - goto fail; - } - if (netState->listenSock >= 0 && - FD_ISSET(netState->listenSock, &readfds)) - { - ALOGI("Ignoring second debugger -- accepting and dropping"); - union { - struct sockaddr_in addrInet; - struct sockaddr addrPlain; - } addr; - socklen_t addrlen; - int tmpSock; - tmpSock = accept(netState->listenSock, &addr.addrPlain, - &addrlen); - if (tmpSock < 0) - ALOGI("Weird -- accept failed"); - else - close(tmpSock); - } - if (netState->clientSock >= 0 && - FD_ISSET(netState->clientSock, &readfds)) - { - readCount = read(netState->clientSock, - netState->inputBuffer + netState->inputCount, - sizeof(netState->inputBuffer) - netState->inputCount); - if (readCount < 0) { - /* read failed */ - if (errno != EINTR) - goto fail; - ALOGD("+++ EINTR hit"); - return true; - } else if (readCount == 0) { - /* EOF hit -- far end went away */ - ALOGD("+++ peer disconnected"); - goto fail; - } else - break; - } - } - - netState->inputCount += readCount; - if (!haveFullPacket(netState)) - return true; /* still not there yet */ - } - - /* - * Special-case the initial handshake. For some bizarre reason we're - * expected to emulate bad tty settings by echoing the request back - * exactly as it was sent. Note the handshake is always initiated by - * the debugger, no matter who connects to whom. - * - * Other than this one case, the protocol [claims to be] stateless. - */ - if (netState->awaitingHandshake) { - int cc; - - if (memcmp(netState->inputBuffer, - kMagicHandshake, kMagicHandshakeLen) != 0) - { - ALOGE("ERROR: bad handshake '%.14s'", netState->inputBuffer); - goto fail; - } - - errno = 0; - cc = TEMP_FAILURE_RETRY(write(netState->clientSock, netState->inputBuffer, - kMagicHandshakeLen)); - if (cc != kMagicHandshakeLen) { - ALOGE("Failed writing handshake bytes: %s (%d of %d)", - strerror(errno), cc, (int) kMagicHandshakeLen); - goto fail; - } - - consumeBytes(netState, kMagicHandshakeLen); - netState->awaitingHandshake = false; - ALOGV("+++ handshake complete"); - return true; - } - - /* - * Handle this packet. - */ - return handlePacket(state); - -fail: - closeConnection(state); - return false; -} - -/* - * Send a request. - * - * The entire packet must be sent with a single write() call to avoid - * threading issues. - * - * Returns "true" if it was sent successfully. - */ -static bool sendRequest(JdwpState* state, ExpandBuf* pReq) -{ - JdwpNetState* netState = state->netState; - - /*dumpPacket(expandBufGetBuffer(pReq));*/ - if (netState->clientSock < 0) { - /* can happen with some DDMS events */ - ALOGV("NOT sending request -- no debugger is attached"); - return false; - } - - errno = 0; - ssize_t cc = netState->writePacket(pReq); - - if (cc != (ssize_t) expandBufGetLength(pReq)) { - ALOGE("Failed sending req to debugger: %s (%d of %d)", - strerror(errno), (int) cc, (int) expandBufGetLength(pReq)); - return false; - } - - return true; -} - -/* - * Send a request that was split into multiple buffers. - * - * The entire packet must be sent with a single writev() call to avoid - * threading issues. - * - * Returns "true" if it was sent successfully. - */ -static bool sendBufferedRequest(JdwpState* state, const struct iovec* iov, - int iovcnt) -{ - JdwpNetState* netState = state->netState; - - if (netState->clientSock < 0) { - /* can happen with some DDMS events */ - ALOGV("NOT sending request -- no debugger is attached"); - return false; - } - - size_t expected = 0; - int i; - for (i = 0; i < iovcnt; i++) - expected += iov[i].iov_len; - - ssize_t actual = netState->writeBufferedPacket(iov, iovcnt); - - if ((size_t)actual != expected) { - ALOGE("Failed sending b-req to debugger: %s (%d of %zu)", - strerror(errno), (int) actual, expected); - return false; - } - - return true; -} - - -/* - * Our functions. - * - * We can't generally share the implementations with other transports, - * even if they're also socket-based, because our JdwpNetState will be - * different from theirs. - */ -static const JdwpTransport socketTransport = { - prepareSocket, - acceptConnection, - establishConnection, - closeConnection, - netShutdownExtern, - netFreeExtern, - isConnected, - awaitingHandshake, - processIncoming, - sendRequest, - sendBufferedRequest, -}; - -/* - * Return our set. - */ -const JdwpTransport* dvmJdwpSocketTransport() -{ - return &socketTransport; -} |
