text: /*
   * Copyright (c) 1998, 2018, 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 <stdio.h>
  #include <string.h>
  #include <errno.h>
  #include <stdlib.h>
  #include <ctype.h>
  
  #include "jni.h"
  #include "jdwpTransport.h"
  #include "sysSocket.h"
  
  #ifdef _WIN32
   #include <winsock2.h>
   #include <ws2tcpip.h>
  #else
   #include <arpa/inet.h>
   #include <sys/socket.h>
  #endif
  
  /*
   * The Socket Transport Library.
   *
   * This module is an implementation of the Java Debug Wire Protocol Transport
   * Service Provider Interface - see src/share/javavm/export/jdwpTransport.h.
   */
  
  static int serverSocketFD;
  static int socketFD = -1;
  static jdwpTransportCallback *callback;
  static JavaVM *jvm;
  static int tlsIndex;
  static jboolean initialized;
  static struct jdwpTransportNativeInterface_ interface;
  static jdwpTransportEnv single_env = (jdwpTransportEnv)&interface;
  
  #define RETURN_ERROR(err, msg) \
          if (1==1) { \
              setLastError(err, msg); \
              return err; \
          }
  
  #define RETURN_IO_ERROR(msg)    RETURN_ERROR(JDWPTRANSPORT_ERROR_IO_ERROR, msg);
  
  #define RETURN_RECV_ERROR(n) \
          if (n == 0) { \
              RETURN_ERROR(JDWPTRANSPORT_ERROR_IO_ERROR, "premature EOF"); \
          } else { \
              RETURN_IO_ERROR("recv error"); \
          }
  
  #define MAX_DATA_SIZE 1000
  
  static jint recv_fully(int, char *, int);
  static jint send_fully(int, char *, int);
  
  /* version >= JDWPTRANSPORT_VERSION_1_1 */
  typedef struct {
      uint32_t subnet;
      uint32_t netmask;
  } AllowedPeerInfo;
  
  #define STR(x) #x
  #define MAX_PEER_ENTRIES 32
  #define MAX_PEERS_STR STR(MAX_PEER_ENTRIES)
  static AllowedPeerInfo _peers[MAX_PEER_ENTRIES];
  static int _peers_cnt = 0;
  
  
  /*
   * Record the last error for this thread.
   */
  static void
  setLastError(jdwpTransportError err, char *newmsg) {
      char buf[255];
      char *msg;
  
      /* get any I/O first in case any system calls override errno */
      if (err == JDWPTRANSPORT_ERROR_IO_ERROR) {
          dbgsysGetLastIOError(buf, sizeof(buf));
      }
  
      msg = (char *)dbgsysTlsGet(tlsIndex);
      if (msg != NULL) {
          (*callback->free)(msg);
      }
  
      if (err == JDWPTRANSPORT_ERROR_IO_ERROR) {
          char *join_str = ": ";
          int msg_len = (int)strlen(newmsg) + (int)strlen(join_str) +
                        (int)strlen(buf) + 3;
          msg = (*callback->alloc)(msg_len);
          if (msg != NULL) {
              strcpy(msg, newmsg);
              strcat(msg, join_str);
              strcat(msg, buf);
          }
      } else {
          msg = (*callback->alloc)((int)strlen(newmsg)+1);
          if (msg != NULL) {
              strcpy(msg, newmsg);
          }
      }
  
      dbgsysTlsPut(tlsIndex, msg);
  }
  
  /*
   * Return the last error for this thread (may be NULL)
   */
  static char*
  getLastError() {
      return (char *)dbgsysTlsGet(tlsIndex);
  }
  
  /* Set options common to client and server sides */
  static jdwpTransportError
  setOptionsCommon(int fd)
  {
      jvalue dontcare;
      int err;
  
      dontcare.i = 0;  /* keep compiler happy */
  
      err = dbgsysSetSocketOption(fd, TCP_NODELAY, JNI_TRUE, dontcare);
      if (err < 0) {
          RETURN_IO_ERROR("setsockopt TCPNODELAY failed");
      }
  
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  /* Set the SO_REUSEADDR option */
  static jdwpTransportError
  setReuseAddrOption(int fd)
  {
      jvalue dontcare;
      int err;
  
      dontcare.i = 0;  /* keep compiler happy */
  
      err = dbgsysSetSocketOption(fd, SO_REUSEADDR, JNI_TRUE, dontcare);
      if (err < 0) {
          RETURN_IO_ERROR("setsockopt SO_REUSEADDR failed");
      }
  
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  static jdwpTransportError
  handshake(int fd, jlong timeout) {
      const char *hello = "JDWP-Handshake";
      char b[16];
      int rv, helloLen, received;
  
      if (timeout > 0) {
          dbgsysConfigureBlocking(fd, JNI_FALSE);
      }
      helloLen = (int)strlen(hello);
      received = 0;
      while (received < helloLen) {
          int n;
          char *buf;
          if (timeout > 0) {
              rv = dbgsysPoll(fd, JNI_TRUE, JNI_FALSE, (long)timeout);
              if (rv <= 0) {
                  setLastError(0, "timeout during handshake");
                  return JDWPTRANSPORT_ERROR_IO_ERROR;
              }
          }
          buf = b;
          buf += received;
          n = recv_fully(fd, buf, helloLen-received);
          if (n == 0) {
              setLastError(0, "handshake failed - connection prematurally closed");
              return JDWPTRANSPORT_ERROR_IO_ERROR;
          }
          if (n < 0) {
              RETURN_IO_ERROR("recv failed during handshake");
          }
          received += n;
      }
      if (timeout > 0) {
          dbgsysConfigureBlocking(fd, JNI_TRUE);
      }
      if (strncmp(b, hello, received) != 0) {
          char msg[80+2*16];
          b[received] = '\0';
          /*
           * We should really use snprintf here but it's not available on Windows.
           * We can't use jio_snprintf without linking the transport against the VM.
           */
          sprintf(msg, "handshake failed - received >%s< - expected >%s<", b, hello);
          setLastError(0, msg);
          return JDWPTRANSPORT_ERROR_IO_ERROR;
      }
  
      if (send_fully(fd, (char*)hello, helloLen) != helloLen) {
          RETURN_IO_ERROR("send failed during handshake");
      }
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  static uint32_t
  getLocalHostAddress() {
      // Simple routine to guess localhost address.
      // it looks up "localhost" and returns 127.0.0.1 if lookup
      // fails.
      struct addrinfo hints, *res = NULL;
      uint32_t addr;
      int err;
  
      // Use portable way to initialize the structure
      memset((void *)&hints, 0, sizeof(hints));
      hints.ai_family = AF_INET;
  
      err = getaddrinfo("localhost", NULL, &hints, &res);
      if (err < 0 || res == NULL) {
          return dbgsysHostToNetworkLong(INADDR_LOOPBACK);
      }
  
      // getaddrinfo might return more than one address
      // but we are using first one only
      addr = ((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr;
      freeaddrinfo(res);
      return addr;
  }
  
  static int
  getPortNumber(const char *s_port) {
      u_long n;
      char *eptr;
  
      if (*s_port == 0) {
          // bad address - colon with no port number in parameters
          return -1;
      }
  
      n = strtoul(s_port, &eptr, 10);
      if (eptr != s_port + strlen(s_port)) {
          // incomplete conversion - port number contains non-digit
          return -1;
      }
  
      if (n > (u_short) -1) {
          // check that value supplied by user is less than
          // maximum possible u_short value (65535) and
          // will not be truncated later.
          return -1;
      }
  
      return n;
  }
  
  static jdwpTransportError
  parseAddress(const char *address, struct sockaddr_in *sa) {
      char *colon;
      int port;
  
      memset((void *)sa, 0, sizeof(struct sockaddr_in));
      sa->sin_family = AF_INET;
  
      /* check for host:port or port */
      colon = strchr(address, ':');
      port = getPortNumber((colon == NULL) ? address : colon +1);
      if (port < 0) {
          RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "invalid port number specified");
      }
      sa->sin_port = dbgsysHostToNetworkShort((u_short)port);
  
      if (colon == NULL) {
          // bind to localhost only if no address specified
          sa->sin_addr.s_addr = getLocalHostAddress();
      } else if (strncmp(address, "localhost:", 10) == 0) {
          // optimize for common case
          sa->sin_addr.s_addr = getLocalHostAddress();
      } else if (*address == '*' && *(address+1) == ':') {
          // we are explicitly asked to bind server to all available IP addresses
          // has no meaning for client.
          sa->sin_addr.s_addr = dbgsysHostToNetworkLong(INADDR_ANY);
       } else {
          char *buf;
          char *hostname;
          uint32_t addr;
          int ai;
          buf = (*callback->alloc)((int)strlen(address) + 1);
          if (buf == NULL) {
              RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
          }
          strcpy(buf, address);
          buf[colon - address] = '\0';
          hostname = buf;
  
          /*
           * First see if the host is a literal IP address.
           * If not then try to resolve it.
           */
          addr = dbgsysInetAddr(hostname);
          if (addr == 0xffffffff) {
              struct addrinfo hints;
              struct addrinfo *results = NULL;
              memset (&hints, 0, sizeof(hints));
              hints.ai_family = AF_INET;
              hints.ai_socktype = SOCK_STREAM;
              hints.ai_protocol = IPPROTO_TCP;
  
              ai = dbgsysGetAddrInfo(hostname, NULL, &hints, &results);
  
              if (ai != 0) {
                  /* don't use RETURN_IO_ERROR as unknown host is normal */
                  setLastError(0, "getaddrinfo: unknown host");
                  (*callback->free)(buf);
                  return JDWPTRANSPORT_ERROR_IO_ERROR;
              }
  
              /* lookup was successful */
              sa->sin_addr =  ((struct sockaddr_in *)results->ai_addr)->sin_addr;
              freeaddrinfo(results);
          } else {
              sa->sin_addr.s_addr = addr;
          }
  
          (*callback->free)(buf);
      }
  
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  static const char *
  ip_s2u(const char *instr, uint32_t *ip) {
      // Convert string representation of ip to integer
      // in network byte order (big-endian)
      char t[4] = { 0, 0, 0, 0 };
      const char *s = instr;
      int i = 0;
  
      while (1) {
          if (*s == '.') {
              ++i;
              ++s;
              continue;
          }
          if (*s == 0 || *s == '+' || *s == '/') {
              break;
          }
          if (*s < '0' || *s > '9') {
              return instr;
          }
          t[i] = (t[i] * 10) + (*s - '0');
          ++s;
      }
  
      *ip = *(uint32_t*)(t);
      return s;
  }
  
  static const char *
  mask_s2u(const char *instr, uint32_t *mask) {
      // Convert the number of bits to a netmask
      // in network byte order (big-endian)
      unsigned char m = 0;
      const char *s = instr;
  
      while (1) {
          if (*s == 0 || *s == '+') {
              break;
          }
          if (*s < '0' || *s > '9') {
              return instr;
          }
          m = (m * 10) + (*s - '0');
          ++s;
      }
  
      if (m == 0 || m > 32) {
         // Drop invalid input
         return instr;
      }
  
      *mask = htonl((uint32_t)(~0) << (32 - m));
      return s;
  }
  
  static int
  ip_in_subnet(uint32_t subnet, uint32_t mask, uint32_t ipaddr) {
      return (ipaddr & mask) == subnet;
  }
  
  static jdwpTransportError
  parseAllowedPeers(const char *allowed_peers) {
      // Build a list of allowed peers from char string
      // of format 192.168.0.10+192.168.0.0/24
      const char *s = NULL;
      const char *p = allowed_peers;
      uint32_t   ip = 0;
      uint32_t mask = 0xFFFFFFFF;
  
      while (1) {
          s = ip_s2u(p, &ip);
          if (s == p) {
              _peers_cnt = 0;
              fprintf(stderr, "Error in allow option: '%s'\n", s);
              RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
                           "invalid IP address in allow option");
          }
  
          if (*s == '/') {
              // netmask specified
              s = mask_s2u(s + 1, &mask);
              if (*(s - 1) == '/') {
                  // Input is not consumed, something bad happened
                  _peers_cnt = 0;
                  fprintf(stderr, "Error in allow option: '%s'\n", s);
                  RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
                               "invalid netmask in allow option");
              }
          } else {
              // reset netmask
              mask = 0xFFFFFFFF;
          }
  
          if (*s == '+' || *s == 0) {
              if (_peers_cnt >= MAX_PEER_ENTRIES) {
                  fprintf(stderr, "Error in allow option: '%s'\n", allowed_peers);
                  RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
                               "exceeded max number of allowed peers: " MAX_PEERS_STR);
              }
              _peers[_peers_cnt].subnet = ip;
              _peers[_peers_cnt].netmask = mask;
              _peers_cnt++;
              if (*s == 0) {
                  // end of options
                  break;
              }
              // advance to next IP block
              p = s + 1;
          }
      }
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  static int
  isPeerAllowed(struct sockaddr_in *peer) {
      int i;
      for (i = 0; i < _peers_cnt; ++i) {
          int peer_ip = peer->sin_addr.s_addr;
          if (ip_in_subnet(_peers[i].subnet, _peers[i].netmask, peer_ip)) {
              return 1;
          }
      }
  
      return 0;
  }
  
  static jdwpTransportError JNICALL
  socketTransport_getCapabilities(jdwpTransportEnv* env,
          JDWPTransportCapabilities* capabilitiesPtr)
  {
      JDWPTransportCapabilities result;
  
      memset(&result, 0, sizeof(result));
      result.can_timeout_attach = JNI_TRUE;
      result.can_timeout_accept = JNI_TRUE;
      result.can_timeout_handshake = JNI_TRUE;
  
      *capabilitiesPtr = result;
  
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  
  static jdwpTransportError JNICALL
  socketTransport_startListening(jdwpTransportEnv* env, const char* address,
                                 char** actualAddress)
  {
      struct sockaddr_in sa;
      int err;
  
      memset((void *)&sa,0,sizeof(struct sockaddr_in));
      sa.sin_family = AF_INET;
  
      /* no address provided */
      if ((address == NULL) || (address[0] == '\0')) {
          address = "0";
      }
  
      err = parseAddress(address, &sa);
      if (err != JDWPTRANSPORT_ERROR_NONE) {
          return err;
      }
  
      serverSocketFD = dbgsysSocket(AF_INET, SOCK_STREAM, 0);
      if (serverSocketFD < 0) {
          RETURN_IO_ERROR("socket creation failed");
      }
  
      err = setOptionsCommon(serverSocketFD);
      if (err) {
          return err;
      }
      if (sa.sin_port != 0) {
          /*
           * Only need SO_REUSEADDR if we're using a fixed port. If we
           * start seeing EADDRINUSE due to collisions in free ports
           * then we should retry the dbgsysBind() a few times.
           */
          err = setReuseAddrOption(serverSocketFD);
          if (err) {
              return err;
          }
      }
  
      err = dbgsysBind(serverSocketFD, (struct sockaddr *)&sa, sizeof(sa));
      if (err < 0) {
          RETURN_IO_ERROR("bind failed");
      }
  
      err = dbgsysListen(serverSocketFD, 1);
      if (err < 0) {
          RETURN_IO_ERROR("listen failed");
      }
  
      {
          char buf[20];
          socklen_t len = sizeof(sa);
          jint portNum;
          err = dbgsysGetSocketName(serverSocketFD,
                                 (struct sockaddr *)&sa, &len);
          portNum = dbgsysNetworkToHostShort(sa.sin_port);
          sprintf(buf, "%d", portNum);
          *actualAddress = (*callback->alloc)((int)strlen(buf) + 1);
          if (*actualAddress == NULL) {
              RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
          } else {
              strcpy(*actualAddress, buf);
          }
      }
  
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  static jdwpTransportError JNICALL
  socketTransport_accept(jdwpTransportEnv* env, jlong acceptTimeout, jlong handshakeTimeout)
  {
      socklen_t socketLen;
      int err = JDWPTRANSPORT_ERROR_NONE;
      struct sockaddr_in socket;
      jlong startTime = (jlong)0;
  
      /*
       * Use a default handshake timeout if not specified - this avoids an indefinite
       * hang in cases where something other than a debugger connects to our port.
       */
      if (handshakeTimeout == 0) {
          handshakeTimeout = 2000;
      }
  
      do {
          /*
           * If there is an accept timeout then we put the socket in non-blocking
           * mode and poll for a connection.
           */
          if (acceptTimeout > 0) {
              int rv;
              dbgsysConfigureBlocking(serverSocketFD, JNI_FALSE);
              startTime = dbgsysCurrentTimeMillis();
              rv = dbgsysPoll(serverSocketFD, JNI_TRUE, JNI_FALSE, (long)acceptTimeout);
              if (rv <= 0) {
                  /* set the last error here as could be overridden by configureBlocking */
                  if (rv == 0) {
                      setLastError(JDWPTRANSPORT_ERROR_IO_ERROR, "poll failed");
                  }
                  /* restore blocking state */
                  dbgsysConfigureBlocking(serverSocketFD, JNI_TRUE);
                  if (rv == 0) {
                      RETURN_ERROR(JDWPTRANSPORT_ERROR_TIMEOUT, "timed out waiting for connection");
                  } else {
                      return JDWPTRANSPORT_ERROR_IO_ERROR;
                  }
              }
          }
  
          /*
           * Accept the connection
           */
          memset((void *)&socket,0,sizeof(struct sockaddr_in));
          socketLen = sizeof(socket);
          socketFD = dbgsysAccept(serverSocketFD,
                                  (struct sockaddr *)&socket,
                                  &socketLen);
          /* set the last error here as could be overridden by configureBlocking */
          if (socketFD < 0) {
              setLastError(JDWPTRANSPORT_ERROR_IO_ERROR, "accept failed");
          }
          /*
           * Restore the blocking state - note that the accepted socket may be in
           * blocking or non-blocking mode (platform dependent). However as there
           * is a handshake timeout set then it will go into non-blocking mode
           * anyway for the handshake.
           */
          if (acceptTimeout > 0) {
              dbgsysConfigureBlocking(serverSocketFD, JNI_TRUE);
          }
          if (socketFD < 0) {
              return JDWPTRANSPORT_ERROR_IO_ERROR;
          }
  
          /*
           * version >= JDWPTRANSPORT_VERSION_1_1:
           * Verify that peer is allowed to connect.
           */
          if (_peers_cnt > 0) {
              if (!isPeerAllowed(&socket)) {
                  char ebuf[64] = { 0 };
                  char buf[INET_ADDRSTRLEN] = { 0 };
                  const char* addr_str = inet_ntop(AF_INET, &(socket.sin_addr), buf, INET_ADDRSTRLEN);
                  sprintf(ebuf, "ERROR: Peer not allowed to connect: %s\n",
                          (addr_str == NULL) ? "<bad address>" : addr_str);
                  dbgsysSocketClose(socketFD);
                  socketFD = -1;
                  err = JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT;
                  setLastError(err, ebuf);
              }
          }
  
          if (socketFD > 0) {
            /* handshake with the debugger */
            err = handshake(socketFD, handshakeTimeout);
          }
  
          /*
           * If the handshake fails then close the connection. If there if an accept
           * timeout then we must adjust the timeout for the next poll.
           */
          if (err != JDWPTRANSPORT_ERROR_NONE) {
              fprintf(stderr, "Debugger failed to attach: %s\n", getLastError());
              dbgsysSocketClose(socketFD);
              socketFD = -1;
              if (acceptTimeout > 0) {
                  long endTime = dbgsysCurrentTimeMillis();
                  acceptTimeout -= (endTime - startTime);
                  if (acceptTimeout <= 0) {
                      setLastError(JDWPTRANSPORT_ERROR_IO_ERROR,
                          "timeout waiting for debugger to connect");
                      return JDWPTRANSPORT_ERROR_IO_ERROR;
                  }
              }
          }
      } while (socketFD < 0);
  
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  static jdwpTransportError JNICALL
  socketTransport_stopListening(jdwpTransportEnv *env)
  {
      if (serverSocketFD < 0) {
          RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "connection not open");
      }
      if (dbgsysSocketClose(serverSocketFD) < 0) {
          RETURN_IO_ERROR("close failed");
      }
      serverSocketFD = -1;
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  static jdwpTransportError JNICALL
  socketTransport_attach(jdwpTransportEnv* env, const char* addressString, jlong attachTimeout,
                         jlong handshakeTimeout)
  {
      struct sockaddr_in sa;
      int err;
  
      if (addressString == NULL || addressString[0] == '\0') {
          RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "address is missing");
      }
  
      err = parseAddress(addressString, &sa);
      if (err != JDWPTRANSPORT_ERROR_NONE) {
          return err;
      }
  
      socketFD = dbgsysSocket(AF_INET, SOCK_STREAM, 0);
      if (socketFD < 0) {
          RETURN_IO_ERROR("unable to create socket");
      }
  
      err = setOptionsCommon(socketFD);
      if (err) {
          return err;
      }
  
      /*
       * We don't call setReuseAddrOption() for the non-server socket
       * case. If we start seeing EADDRINUSE due to collisions in free
       * ports then we should retry the dbgsysConnect() a few times.
       */
  
      /*
       * To do a timed connect we make the socket non-blocking
       * and poll with a timeout;
       */
      if (attachTimeout > 0) {
          dbgsysConfigureBlocking(socketFD, JNI_FALSE);
      }
  
      err = dbgsysConnect(socketFD, (struct sockaddr *)&sa, sizeof(sa));
      if (err == DBG_EINPROGRESS && attachTimeout > 0) {
          err = dbgsysFinishConnect(socketFD, (long)attachTimeout);
  
          if (err == DBG_ETIMEOUT) {
              dbgsysConfigureBlocking(socketFD, JNI_TRUE);
              RETURN_ERROR(JDWPTRANSPORT_ERROR_TIMEOUT, "connect timed out");
          }
      }
  
      if (err < 0) {
          RETURN_IO_ERROR("connect failed");
      }
  
      if (attachTimeout > 0) {
          dbgsysConfigureBlocking(socketFD, JNI_TRUE);
      }
  
      err = handshake(socketFD, handshakeTimeout);
      if (err) {
          dbgsysSocketClose(socketFD);
          socketFD = -1;
          return err;
      }
  
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  static jboolean JNICALL
  socketTransport_isOpen(jdwpTransportEnv* env)
  {
      if (socketFD >= 0) {
          return JNI_TRUE;
      } else {
          return JNI_FALSE;
      }
  }
  
  static jdwpTransportError JNICALL
  socketTransport_close(jdwpTransportEnv* env)
  {
      int fd = socketFD;
      socketFD = -1;
      if (fd < 0) {
          return JDWPTRANSPORT_ERROR_NONE;
      }
  #ifdef _AIX
      /*
        AIX needs a workaround for I/O cancellation, see:
        http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.basetechref/doc/basetrf1/close.htm
        ...
        The close subroutine is blocked until all subroutines which use the file
        descriptor return to usr space. For example, when a thread is calling close
        and another thread is calling select with the same file descriptor, the
        close subroutine does not return until the select call returns.
        ...
      */
      shutdown(fd, 2);
  #endif
      if (dbgsysSocketClose(fd) < 0) {
          /*
           * close failed - it's pointless to restore socketFD here because
           * any subsequent close will likely fail as well.
           */
          RETURN_IO_ERROR("close failed");
      }
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  static jdwpTransportError JNICALL
  socketTransport_writePacket(jdwpTransportEnv* env, const jdwpPacket *packet)
  {
      jint len, data_len, id;
      /*
       * room for header and up to MAX_DATA_SIZE data bytes
       */
      char header[JDWP_HEADER_SIZE + MAX_DATA_SIZE];
      jbyte *data;
  
      /* packet can't be null */
      if (packet == NULL) {
          RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "packet is NULL");
      }
  
      len = packet->type.cmd.len;         /* includes header */
      data_len = len - JDWP_HEADER_SIZE;
  
      /* bad packet */
      if (data_len < 0) {
          RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "invalid length");
      }
  
      /* prepare the header for transmission */
      len = (jint)dbgsysHostToNetworkLong(len);
      id = (jint)dbgsysHostToNetworkLong(packet->type.cmd.id);
  
      memcpy(header + 0, &len, 4);
      memcpy(header + 4, &id, 4);
      header[8] = packet->type.cmd.flags;
      if (packet->type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) {
          jshort errorCode =
              dbgsysHostToNetworkShort(packet->type.reply.errorCode);
          memcpy(header + 9, &errorCode, 2);
      } else {
          header[9] = packet->type.cmd.cmdSet;
          header[10] = packet->type.cmd.cmd;
      }
  
      data = packet->type.cmd.data;
      /* Do one send for short packets, two for longer ones */
      if (data_len <= MAX_DATA_SIZE) {
          memcpy(header + JDWP_HEADER_SIZE, data, data_len);
          if (send_fully(socketFD, (char *)&header, JDWP_HEADER_SIZE + data_len) !=
              JDWP_HEADER_SIZE + data_len) {
              RETURN_IO_ERROR("send failed");
          }
      } else {
          memcpy(header + JDWP_HEADER_SIZE, data, MAX_DATA_SIZE);
          if (send_fully(socketFD, (char *)&header, JDWP_HEADER_SIZE + MAX_DATA_SIZE) !=
              JDWP_HEADER_SIZE + MAX_DATA_SIZE) {
              RETURN_IO_ERROR("send failed");
          }
          /* Send the remaining data bytes right out of the data area. */
          if (send_fully(socketFD, (char *)data + MAX_DATA_SIZE,
                         data_len - MAX_DATA_SIZE) != data_len - MAX_DATA_SIZE) {
              RETURN_IO_ERROR("send failed");
          }
      }
  
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  static jint
  recv_fully(int f, char *buf, int len)
  {
      int nbytes = 0;
      while (nbytes < len) {
          int res = dbgsysRecv(f, buf + nbytes, len - nbytes, 0);
          if (res < 0) {
              return res;
          } else if (res == 0) {
              break; /* eof, return nbytes which is less than len */
          }
          nbytes += res;
      }
      return nbytes;
  }
  
  jint
  send_fully(int f, char *buf, int len)
  {
      int nbytes = 0;
      while (nbytes < len) {
          int res = dbgsysSend(f, buf + nbytes, len - nbytes, 0);
          if (res < 0) {
              return res;
          } else if (res == 0) {
              break; /* eof, return nbytes which is less than len */
          }
          nbytes += res;
      }
      return nbytes;
  }
  
  static jdwpTransportError JNICALL
  socketTransport_readPacket(jdwpTransportEnv* env, jdwpPacket* packet) {
      jint length, data_len;
      jint n;
  
      /* packet can't be null */
      if (packet == NULL) {
          RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "packet is null");
      }
  
      /* read the length field */
      n = recv_fully(socketFD, (char *)&length, sizeof(jint));
  
      /* check for EOF */
      if (n == 0) {
          packet->type.cmd.len = 0;
          return JDWPTRANSPORT_ERROR_NONE;
      }
      if (n != sizeof(jint)) {
          RETURN_RECV_ERROR(n);
      }
  
      length = (jint)dbgsysNetworkToHostLong(length);
      packet->type.cmd.len = length;
  
  
      n = recv_fully(socketFD,(char *)&(packet->type.cmd.id), sizeof(jint));
      if (n < (int)sizeof(jint)) {
          RETURN_RECV_ERROR(n);
      }
  
      packet->type.cmd.id = (jint)dbgsysNetworkToHostLong(packet->type.cmd.id);
  
      n = recv_fully(socketFD,(char *)&(packet->type.cmd.flags), sizeof(jbyte));
      if (n < (int)sizeof(jbyte)) {
          RETURN_RECV_ERROR(n);
      }
  
      if (packet->type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) {
          n = recv_fully(socketFD,(char *)&(packet->type.reply.errorCode), sizeof(jbyte));
          if (n < (int)sizeof(jshort)) {
              RETURN_RECV_ERROR(n);
          }
  
          /* FIXME - should the error be converted to host order?? */
  
  
      } else {
          n = recv_fully(socketFD,(char *)&(packet->type.cmd.cmdSet), sizeof(jbyte));
          if (n < (int)sizeof(jbyte)) {
              RETURN_RECV_ERROR(n);
          }
  
          n = recv_fully(socketFD,(char *)&(packet->type.cmd.cmd), sizeof(jbyte));
          if (n < (int)sizeof(jbyte)) {
              RETURN_RECV_ERROR(n);
          }
      }
  
      data_len = length - ((sizeof(jint) * 2) + (sizeof(jbyte) * 3));
  
      if (data_len < 0) {
          setLastError(0, "Badly formed packet received - invalid length");
          return JDWPTRANSPORT_ERROR_IO_ERROR;
      } else if (data_len == 0) {
          packet->type.cmd.data = NULL;
      } else {
          packet->type.cmd.data= (*callback->alloc)(data_len);
  
          if (packet->type.cmd.data == NULL) {
              RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
          }
  
          n = recv_fully(socketFD,(char *)packet->type.cmd.data, data_len);
          if (n < data_len) {
              (*callback->free)(packet->type.cmd.data);
              RETURN_RECV_ERROR(n);
          }
      }
  
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  static jdwpTransportError JNICALL
  socketTransport_getLastError(jdwpTransportEnv* env, char** msgP) {
      char *msg = (char *)dbgsysTlsGet(tlsIndex);
      if (msg == NULL) {
          return JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE;
      }
      *msgP = (*callback->alloc)((int)strlen(msg)+1);
      if (*msgP == NULL) {
          return JDWPTRANSPORT_ERROR_OUT_OF_MEMORY;
      }
      strcpy(*msgP, msg);
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  static jdwpTransportError JNICALL
  socketTransport_setConfiguration(jdwpTransportEnv* env, jdwpTransportConfiguration* cfg) {
      const char* allowed_peers = NULL;
  
      if (cfg == NULL) {
          RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
                       "NULL pointer to transport configuration is invalid");
      }
      allowed_peers = cfg->allowed_peers;
      _peers_cnt = 0;
      if (allowed_peers != NULL) {
          size_t len = strlen(allowed_peers);
          if (len == 0) { /* Impossible: parseOptions() would reject it */
              fprintf(stderr, "Error in allow option: '%s'\n", allowed_peers);
              RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
                           "allow option should not be empty");
          } else if (*allowed_peers == '*') {
              if (len != 1) {
                  fprintf(stderr, "Error in allow option: '%s'\n", allowed_peers);
                  RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
                               "allow option '*' cannot be expanded");
              }
          } else {
              int err = parseAllowedPeers(allowed_peers);
              if (err != JDWPTRANSPORT_ERROR_NONE) {
                  return err;
              }
          }
      }
      return JDWPTRANSPORT_ERROR_NONE;
  }
  
  JNIEXPORT jint JNICALL
  jdwpTransport_OnLoad(JavaVM *vm, jdwpTransportCallback* cbTablePtr,
                       jint version, jdwpTransportEnv** env)
  {
      if (version < JDWPTRANSPORT_VERSION_1_0 ||
          version > JDWPTRANSPORT_VERSION_1_1) {
          return JNI_EVERSION;
      }
      if (initialized) {
          /*
           * This library doesn't support multiple environments (yet)
           */
          return JNI_EEXIST;
      }
      initialized = JNI_TRUE;
      jvm = vm;
      callback = cbTablePtr;
  
      /* initialize interface table */
      interface.GetCapabilities = &socketTransport_getCapabilities;
      interface.Attach = &socketTransport_attach;
      interface.StartListening = &socketTransport_startListening;
      interface.StopListening = &socketTransport_stopListening;
      interface.Accept = &socketTransport_accept;
      interface.IsOpen = &socketTransport_isOpen;
      interface.Close = &socketTransport_close;
      interface.ReadPacket = &socketTransport_readPacket;
      interface.WritePacket = &socketTransport_writePacket;
      interface.GetLastError = &socketTransport_getLastError;
      if (version >= JDWPTRANSPORT_VERSION_1_1) {
          interface.SetTransportConfiguration = &socketTransport_setConfiguration;
      }
      *env = &single_env;
  
      /* initialized TLS */
      tlsIndex = dbgsysTlsAlloc();
      return JNI_OK;
  }
  