8184770: JDWP support for IPv6
authoramenkov
Wed, 15 May 2019 11:06:33 -0700
changeset 54884 8a6093c186a6
parent 54883 4ee117b890c5
child 54885 e58e454c1409
8184770: JDWP support for IPv6 Reviewed-by: sspitsyn, chegar
src/jdk.jdi/share/classes/com/sun/tools/jdi/SocketListeningConnector.java
src/jdk.jdi/share/classes/com/sun/tools/jdi/SocketTransportService.java
src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c
src/jdk.jdwp.agent/share/native/libdt_socket/sysSocket.h
src/jdk.jdwp.agent/unix/native/libdt_socket/socket_md.c
src/jdk.jdwp.agent/windows/native/libdt_socket/socket_md.c
test/hotspot/jtreg/vmTestbase/nsk/jdi/ListeningConnector/startListening/startlis001.java
test/jdk/com/sun/jdi/BasicJDWPConnectionTest.java
test/jdk/com/sun/jdi/JdwpAllowTest.java
test/jdk/com/sun/jdi/JdwpAttachTest.java
test/jdk/com/sun/jdi/JdwpListenTest.java
test/jdk/com/sun/jdi/JdwpNetProps.java
--- a/src/jdk.jdi/share/classes/com/sun/tools/jdi/SocketListeningConnector.java	Fri May 10 17:13:02 2019 -0700
+++ b/src/jdk.jdi/share/classes/com/sun/tools/jdi/SocketListeningConnector.java	Wed May 15 11:06:33 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -101,7 +101,7 @@
         if (isWildcardPort(args)) {
             String[] address = listener.address().split(":");
             if (address.length > 1) {
-                args.get(ARG_PORT).setValue(address[1]);
+                args.get(ARG_PORT).setValue(address[address.length - 1]);
             }
         }
     }
--- a/src/jdk.jdi/share/classes/com/sun/tools/jdi/SocketTransportService.java	Fri May 10 17:13:02 2019 -0700
+++ b/src/jdk.jdi/share/classes/com/sun/tools/jdi/SocketTransportService.java	Wed May 15 11:06:33 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -79,12 +79,7 @@
                 try {
                     address = InetAddress.getLocalHost();
                 } catch (UnknownHostException uhe) {
-                    byte[] loopback = {0x7f,0x00,0x00,0x01};
-                    try {
-                        address = InetAddress.getByAddress("127.0.0.1", loopback);
-                    } catch (UnknownHostException x) {
-                        throw new InternalError("unable to get local hostname");
-                    }
+                    address = InetAddress.getLoopbackAddress();
                 }
             }
 
@@ -201,6 +196,44 @@
         };
     }
 
+    private static class HostPort {
+        public final String host;
+        public final int port;
+        private HostPort(String host, int port) {
+            this.host = host;
+            this.port = port;
+        }
+
+        /**
+         * Creates an instance for given URN, which can be either <port> or <host>:<port>.
+         * If host is '*', the returned HostPort instance has host set to null.
+         * If <code>host</code> is a literal IPv6 address, it may be in square brackets.
+         */
+        public static HostPort parse(String hostPort) {
+            int splitIndex = hostPort.lastIndexOf(':');
+
+            int port;
+            try {
+                port = Integer.decode(hostPort.substring(splitIndex + 1));
+            } catch (NumberFormatException e) {
+                throw new IllegalArgumentException("unable to parse port number in address");
+            }
+            if (port < 0 || port > 0xFFFF) {
+                throw new IllegalArgumentException("port out of range");
+            }
+
+            if (splitIndex <= 0) {  // empty host means local connection
+                return new HostPort(InetAddress.getLoopbackAddress().getHostAddress(), port);
+            } else if (splitIndex == 1 && hostPort.charAt(0) == '*') {
+                return new HostPort(null, port);
+            } else if (hostPort.charAt(0) == '[' && hostPort.charAt(splitIndex - 1) == ']') {
+                return new HostPort(hostPort.substring(1, splitIndex - 1), port);
+            } else {
+                return new HostPort(hostPort.substring(0, splitIndex), port);
+            }
+        }
+    }
+
     /**
      * Attach to the specified address with optional attach and handshake
      * timeout.
@@ -215,31 +248,14 @@
             throw new IllegalArgumentException("timeout is negative");
         }
 
-        int splitIndex = address.indexOf(':');
-        String host;
-        String portStr;
-        if (splitIndex < 0) {
-            host = "localhost";
-            portStr = address;
-        } else {
-            host = address.substring(0, splitIndex);
-            portStr = address.substring(splitIndex+1);
-        }
-
-        if (host.equals("*")) {
-            host = InetAddress.getLocalHost().getHostName();
-        }
-
-        int port;
-        try {
-            port = Integer.decode(portStr).intValue();
-        } catch (NumberFormatException e) {
-            throw new IllegalArgumentException(
-                "unable to parse port number in address");
-        }
+        HostPort hostPort = HostPort.parse(address);
 
         // open TCP connection to VM
-        InetSocketAddress sa = new InetSocketAddress(host, port);
+        // formally "*" is not correct hostname to attach
+        // but lets connect to localhost
+        InetSocketAddress sa = new InetSocketAddress(hostPort.host == null
+                                                     ? InetAddress.getLoopbackAddress().getHostAddress()
+                                                     : hostPort.host, hostPort.port);
         Socket s = new Socket();
         try {
             s.connect(sa, (int)attachTimeout);
@@ -290,26 +306,8 @@
      */
     public ListenKey startListening(String address) throws IOException {
         // use ephemeral port if address isn't specified.
-        if (address == null || address.length() == 0) {
-            address = "0";
-        }
-
-        int splitIndex = address.indexOf(':');
-        String localaddr = null;
-        if (splitIndex >= 0) {
-            localaddr = address.substring(0, splitIndex);
-            address = address.substring(splitIndex+1);
-        }
-
-        int port;
-        try {
-            port = Integer.decode(address).intValue();
-        } catch (NumberFormatException e) {
-            throw new IllegalArgumentException(
-                    "unable to parse port number in address");
-        }
-
-        return startListening(localaddr, port);
+        HostPort hostPort = HostPort.parse((address == null || address.isEmpty()) ? "0" : address);
+        return startListening(hostPort.host, hostPort.port);
     }
 
     /**
--- a/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c	Fri May 10 17:13:02 2019 -0700
+++ b/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c	Wed May 15 11:06:33 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -47,7 +47,7 @@
  * Service Provider Interface - see src/share/javavm/export/jdwpTransport.h.
  */
 
-static int serverSocketFD;
+static int serverSocketFD = -1;
 static int socketFD = -1;
 static jdwpTransportCallback *callback;
 static JavaVM *jvm;
@@ -78,8 +78,9 @@
 
 /* version >= JDWPTRANSPORT_VERSION_1_1 */
 typedef struct {
-    uint32_t subnet;
-    uint32_t netmask;
+    /* subnet and mask are stored as IPv6 addresses, IPv4 is stored as mapped IPv6 */
+    struct in6_addr subnet;
+    struct in6_addr netmask;
 } AllowedPeerInfo;
 
 #define STR(x) #x
@@ -89,6 +90,9 @@
 static int _peers_cnt = 0;
 
 
+static int allowOnlyIPv4 = 0;                  // reflects "java.net.preferIPv4Stack" sys. property
+static int preferredAddressFamily = AF_INET;   // "java.net.preferIPv6Addresses"
+
 /*
  * Record the last error for this thread.
  */
@@ -137,13 +141,19 @@
 
 /* Set options common to client and server sides */
 static jdwpTransportError
-setOptionsCommon(int fd)
+setOptionsCommon(int domain, int fd)
 {
     jvalue dontcare;
     int err;
 
+    if (domain == AF_INET6) {
+        int off = 0;
+        // make the socket a dual mode socket
+        // this may fail if IPv4 is not supported - it's ok
+        setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&off, sizeof(off));
+    }
+
     dontcare.i = 0;  /* keep compiler happy */
-
     err = dbgsysSetSocketOption(fd, TCP_NODELAY, JNI_TRUE, dontcare);
     if (err < 0) {
         RETURN_IO_ERROR("setsockopt TCPNODELAY failed");
@@ -223,31 +233,6 @@
     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;
@@ -274,199 +259,290 @@
     return n;
 }
 
+static unsigned short getPort(struct sockaddr *sa)
+{
+    return dbgsysNetworkToHostShort(sa->sa_family == AF_INET
+                                    ? (((struct sockaddr_in*)sa)->sin_port)
+                                    : (((struct sockaddr_in6*)sa)->sin6_port));
+}
+
+/*
+ * Result must be released with dbgsysFreeAddrInfo.
+ */
 static jdwpTransportError
-parseAddress(const char *address, struct sockaddr_in *sa) {
-    char *colon;
-    int port;
+parseAddress(const char *address, struct addrinfo **result) {
+    const char *colon;
+    size_t hostLen;
+    char *host = NULL;
+    const char *port;
+    struct addrinfo hints;
+    int res;
 
-    memset((void *)sa, 0, sizeof(struct sockaddr_in));
-    sa->sin_family = AF_INET;
+    *result = NULL;
 
     /* check for host:port or port */
-    colon = strchr(address, ':');
-    port = getPortNumber((colon == NULL) ? address : colon +1);
-    if (port < 0) {
+    colon = strrchr(address, ':');
+    port = (colon == NULL ? address : colon + 1);
+
+    /* ensure the port is valid (getaddrinfo allows port to be empty) */
+    if (getPortNumber(port) < 0) {
         RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "invalid port number specified");
     }
-    sa->sin_port = dbgsysHostToNetworkShort((u_short)port);
+
+    memset (&hints, 0, sizeof(hints));
+    hints.ai_family = allowOnlyIPv4 ? AF_INET : AF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_protocol = IPPROTO_TCP;
+    hints.ai_flags = AI_NUMERICSERV;    // port must be a number
 
-    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) {
+    hostLen = (colon == NULL ? 0 : colon - address);
+    if (hostLen == 0) {
+        /* no hostname - use localhost address (pass NULL to getaddrinfo) */
+    } else  if (*address == '*' && hostLen == 1) {
+        /* *:port - listen on all interfaces
+         * use IPv6 socket (to accept IPv6 and mapped IPv4),
+         * pass hostname == NULL to getaddrinfo.
+         */
+        hints.ai_family = allowOnlyIPv4 ? AF_INET : AF_INET6;
+        hints.ai_flags |= AI_PASSIVE | (allowOnlyIPv4 ? 0 : AI_V4MAPPED | AI_ALL);
+    } else {
+        if (address[0] == '[' && colon[-1] == ']') {
+            address++;
+            hostLen -= 2;
+        }
+        host = (*callback->alloc)((int)hostLen + 1);
+        if (host == 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;
+        strncpy(host, address, hostLen);
+        host[hostLen] = '\0';
+    }
 
-            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);
+    res = dbgsysGetAddrInfo(host, port, &hints, result);
+    if (host != NULL) {
+        (*callback->free)(host);
+    }
+    if (res != 0) {
+        setLastError(res, "getaddrinfo: unknown host");
+        return JDWPTRANSPORT_ERROR_IO_ERROR;
     }
 
     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;
+/*
+ * Input is sockaddr just because all clients have it.
+ */
+static void convertIPv4ToIPv6(const struct sockaddr *addr4, struct in6_addr *addr6) {
+    // Implement in a platform-independent way.
+    // Spec requires in_addr has s_addr member, in6_addr has s6_addr[16] member.
+    struct in_addr *a4 = &(((struct sockaddr_in*)addr4)->sin_addr);
+    memset(addr6, 0, sizeof(*addr6));   // for safety
+
+    // Mapped address contains 80 zero bits, then 16 "1" bits, then IPv4 address (4 bytes).
+    addr6->s6_addr[10] = addr6->s6_addr[11] = 0xFF;
+    memcpy(&(addr6->s6_addr[12]), &(a4->s_addr), 4);
+}
+
+/*
+ * Parses address (IPv4 or IPv6), fills in result by parsed address.
+ * For IPv4 mapped IPv6 is returned in result, isIPv4 is set.
+ */
+static jdwpTransportError
+parseAllowedAddr(const char *buffer, struct in6_addr *result, int *isIPv4) {
+    struct addrinfo hints;
+    struct addrinfo *addrInfo = NULL;
+    int err;
+
+    /*
+     * To parse both IPv4 and IPv6 need to specify AF_UNSPEC family
+     * (with AF_INET6 IPv4 addresses are not parsed even with AI_V4MAPPED and AI_ALL flags).
+     */
+    memset (&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;            // IPv6 or mapped IPv4
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_protocol = IPPROTO_TCP;
+    hints.ai_flags = AI_NUMERICHOST;        // only numeric addresses, no resolution
+
+    err = dbgsysGetAddrInfo(buffer, NULL, &hints, &addrInfo);
+
+    if (err != 0) {
+        setLastError(err, "getaddrinfo: failed to parse address");
+        return JDWPTRANSPORT_ERROR_IO_ERROR;
+    }
 
-    while (1) {
-        if (*s == '.') {
-            ++i;
-            ++s;
-            continue;
+    if (addrInfo->ai_family == AF_INET6) {
+        memcpy(result, &(((struct sockaddr_in6 *)(addrInfo->ai_addr))->sin6_addr), sizeof(*result));
+        *isIPv4 = 0;
+    } else {    // IPv4 address - convert to mapped IPv6
+        struct in6_addr addr6;
+        convertIPv4ToIPv6(addrInfo->ai_addr, &addr6);
+        memcpy(result, &addr6, sizeof(*result));
+        *isIPv4 = 1;
+    }
+
+    dbgsysFreeAddrInfo(addrInfo);
+
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+/*
+ * Parses prefix length from buffer (integer value), fills in result with corresponding net mask.
+ * For IPv4 (isIPv4 is set), maximum prefix length is 32 bit, for IPv6 - 128 bit.
+ */
+static jdwpTransportError
+parseAllowedMask(const char *buffer, int isIPv4, struct in6_addr *result) {
+    int prefixLen = 0;
+    int maxValue = isIPv4 ? 32 : 128;
+
+    do {
+        if (*buffer < '0' || *buffer > '9') {
+            return JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT;
         }
-        if (*s == 0 || *s == '+' || *s == '/') {
-            break;
+        prefixLen = prefixLen * 10 + (*buffer - '0');
+        if (prefixLen > maxValue) {  // avoid overflow
+            return JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT;
         }
-        if (*s < '0' || *s > '9') {
-            return instr;
-        }
-        t[i] = (t[i] * 10) + (*s - '0');
-        ++s;
+        buffer++;
+    } while (*buffer != '\0');
+
+    if (isIPv4) {
+        // IPv4 are stored as mapped IPv6, prefixLen needs to be converted too
+        prefixLen += 96;
+    }
+
+    if (prefixLen == 0) {
+        return JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT;
     }
 
-    *ip = *(uint32_t*)(t);
-    return s;
+    // generate mask for prefix length
+    memset(result, 0, sizeof(*result));
+
+    // prefixLen <= 128, so we won't go over result's size
+    for (int i = 0; prefixLen > 0; i++, prefixLen -= 8) {
+        if (prefixLen >= 8) {
+            // set the whole byte
+            result->s6_addr[i] = 0xFF;
+        } else {
+            // set only "prefixLen" bits
+            result->s6_addr[i] = (char)(0xFF << (8 - prefixLen));
+        }
+    }
+
+    return JDWPTRANSPORT_ERROR_NONE;
 }
 
-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;
+/*
+ * Internal implementation of parseAllowedPeers (requires writable buffer).
+ */
+static jdwpTransportError
+parseAllowedPeersInternal(char *buffer) {
+    char *next;
+    int isIPv4 = 0;
 
-    while (1) {
-        if (*s == 0 || *s == '+') {
-            break;
+    do {
+        char *mask = NULL;
+        char *endOfAddr = strpbrk(buffer, "/+");
+        if (endOfAddr == NULL) {
+            // this is the last address and there is no prefix length
+            next = NULL;
+        } else {
+            next = endOfAddr + 1;
+            if (*endOfAddr == '/') {
+                // mask (prefix length) presents
+                char *endOfMask = strchr(next, '+');
+                mask = next;
+                if (endOfMask == NULL) {
+                    // no more addresses
+                    next = NULL;
+                } else {
+                    next = endOfMask + 1;
+                    *endOfMask = '\0';
+                }
+            }
+            *endOfAddr = '\0';
+        }
+
+        // parse subnet address (IPv4 is stored as mapped IPv6)
+        if (parseAllowedAddr(buffer, &(_peers[_peers_cnt].subnet), &isIPv4) != JDWPTRANSPORT_ERROR_NONE) {
+            _peers_cnt = 0;
+            fprintf(stderr, "Error in allow option: '%s'\n", buffer);
+            RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
+                         "invalid IP address in allow option");
         }
-        if (*s < '0' || *s > '9') {
-            return instr;
+        if (mask != NULL) {
+            if (parseAllowedMask(mask, isIPv4, &(_peers[_peers_cnt].netmask)) != JDWPTRANSPORT_ERROR_NONE) {
+                _peers_cnt = 0;
+                fprintf(stderr, "Error in allow option: '%s'\n", mask);
+                RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
+                             "invalid netmask in allow option");
+            }
+            // for safety update subnet to satisfy the mask
+            for (size_t i = 0; i < sizeof(_peers[_peers_cnt].subnet); i++) {
+                _peers[_peers_cnt].subnet.s6_addr[i] &= _peers[_peers_cnt].netmask.s6_addr[i];
+            }
+        } else {
+            memset(&(_peers[_peers_cnt].netmask), 0xFF, sizeof(_peers[_peers_cnt].netmask));
         }
-        m = (m * 10) + (*s - '0');
-        ++s;
-    }
+        _peers_cnt++;
+        buffer = next;
+    } while (next != NULL);
+
+    return JDWPTRANSPORT_ERROR_NONE;
+}
 
-    if (m == 0 || m > 32) {
-       // Drop invalid input
-       return instr;
+/*
+ * Parses 'allow' argument (fills in list of allowed peers (global _peers variable)).
+ * 'Allow' value consists of tokens separated by '+',
+ * each token contains IP address (IPv4 or IPv6) and optional prefixLength:
+ * '<addr>[/<prefixLength>]'.
+ * Example: '192.168.1.10+192.168.0.0/24'
+ *   - connections are allowed from 192.168.1.10 and subnet 192.168.0.XX.
+ */
+static jdwpTransportError
+parseAllowedPeers(const char *allowed_peers, size_t len) {
+    // Build a list of allowed peers from char string
+    // of format 192.168.0.10+192.168.0.0/24
+
+    // writable copy of the value
+    char *buffer = (*callback->alloc)((int)len + 1);
+    if (buffer == NULL) {
+        RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
     }
+    strncpy(buffer, allowed_peers, len);
+    buffer[len] = '\0';
 
-    *mask = htonl((uint32_t)(~0) << (32 - m));
-    return s;
+    jdwpTransportError err = parseAllowedPeersInternal(buffer);
+
+    (*callback->free)(buffer);
+
+    return err;
 }
 
 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;
+isAddressInSubnet(const struct in6_addr *address, const struct in6_addr *subnet, const struct in6_addr *mask) {
+    for (size_t i = 0; i < sizeof(struct in6_addr); i++) {
+        if ((address->s6_addr[i] & mask->s6_addr[i]) != subnet->s6_addr[i]) {
+            return 0;
         }
     }
-    return JDWPTRANSPORT_ERROR_NONE;
+    return 1;
 }
 
 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)) {
+isPeerAllowed(struct sockaddr_storage *peer) {
+    struct in6_addr tmp;
+    struct in6_addr *addr6;
+    // _peers contains IPv6 subnet and mask (IPv4 is converted to mapped IPv6)
+    if (peer->ss_family == AF_INET) {
+        convertIPv4ToIPv6((struct sockaddr *)peer, &tmp);
+        addr6 = &tmp;
+    } else {
+        addr6 = &(((struct sockaddr_in6 *)peer)->sin6_addr);
+    }
+
+    for (int i = 0; i < _peers_cnt; ++i) {
+        if (isAddressInSubnet(addr6, &(_peers[i].subnet), &(_peers[i].netmask))) {
             return 1;
         }
     }
@@ -490,65 +566,58 @@
     return JDWPTRANSPORT_ERROR_NONE;
 }
 
-
-static jdwpTransportError JNICALL
-socketTransport_startListening(jdwpTransportEnv* env, const char* address,
-                               char** actualAddress)
+/*
+ * Starts listening on the specified addrinfo,
+ * returns listening socket and actual listening port.
+ * If the function fails and returned socket != -1, the socket should be closed.
+ */
+static jdwpTransportError startListening(struct addrinfo *ai, int *socket, 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";
+    *socket = dbgsysSocket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
+    if (*socket < 0) {
+        RETURN_IO_ERROR("socket creation failed");
     }
 
-    err = parseAddress(address, &sa);
-    if (err != JDWPTRANSPORT_ERROR_NONE) {
+    err = setOptionsCommon(ai->ai_family, *socket);
+    if (err) {
         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) {
+    if (getPort(ai->ai_addr) != 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);
+        err = setReuseAddrOption(*socket);
         if (err) {
             return err;
         }
     }
 
-    err = dbgsysBind(serverSocketFD, (struct sockaddr *)&sa, sizeof(sa));
+    err = dbgsysBind(*socket, ai->ai_addr, (socklen_t)ai->ai_addrlen);
     if (err < 0) {
         RETURN_IO_ERROR("bind failed");
     }
 
-    err = dbgsysListen(serverSocketFD, 1);
+    err = dbgsysListen(*socket, 1); // only 1 debugger can attach
     if (err < 0) {
         RETURN_IO_ERROR("listen failed");
     }
 
     {
         char buf[20];
-        socklen_t len = sizeof(sa);
+        struct sockaddr_storage addr;
+        socklen_t len = sizeof(addr);
         jint portNum;
-        err = dbgsysGetSocketName(serverSocketFD,
-                               (struct sockaddr *)&sa, &len);
-        portNum = dbgsysNetworkToHostShort(sa.sin_port);
+        err = dbgsysGetSocketName(*socket, (struct sockaddr *)&addr, &len);
+        if (err != 0) {
+            RETURN_IO_ERROR("getsockname failed");
+        }
+
+        portNum = getPort((struct sockaddr *)&addr);
         sprintf(buf, "%d", portNum);
         *actualAddress = (*callback->alloc)((int)strlen(buf) + 1);
         if (*actualAddress == NULL) {
@@ -562,12 +631,62 @@
 }
 
 static jdwpTransportError JNICALL
+socketTransport_startListening(jdwpTransportEnv* env, const char* address,
+                               char** actualAddress)
+{
+    int err;
+    struct addrinfo *addrInfo = NULL;
+    struct addrinfo *listenAddr = NULL;
+
+    /* no address provided */
+    if ((address == NULL) || (address[0] == '\0')) {
+        address = "0";
+    }
+
+    err = parseAddress(address, &addrInfo);
+    if (err != JDWPTRANSPORT_ERROR_NONE) {
+        return err;
+    }
+
+    /* 1st pass - preferredAddressFamily (by default IPv4), 2nd pass - the rest */
+    for (int pass = 0; pass < 2 && listenAddr == NULL; pass++) {
+        for (struct addrinfo *ai = addrInfo; ai != NULL; ai = ai->ai_next) {
+            if ((pass == 0 && ai->ai_family == preferredAddressFamily) ||
+                (pass == 1 && ai->ai_family != preferredAddressFamily))
+            {
+                listenAddr = ai;
+                break;
+            }
+        }
+    }
+
+    if (listenAddr == NULL) {
+        dbgsysFreeAddrInfo(addrInfo);
+        RETURN_ERROR(JDWPTRANSPORT_ERROR_INTERNAL, "listen failed: wrong address");
+    }
+
+    err = startListening(listenAddr, &serverSocketFD, actualAddress);
+
+    dbgsysFreeAddrInfo(addrInfo);
+
+    if (err != JDWPTRANSPORT_ERROR_NONE) {
+        if (serverSocketFD >= 0) {
+            dbgsysSocketClose(serverSocketFD);
+            serverSocketFD = -1;
+        }
+        return err;
+    }
+
+    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;
+    struct sockaddr_storage clientAddr;
+    socklen_t clientAddrLen;
+    jlong startTime = 0;
 
     /*
      * Use a default handshake timeout if not specified - this avoids an indefinite
@@ -605,11 +724,10 @@
         /*
          * Accept the connection
          */
-        memset((void *)&socket,0,sizeof(struct sockaddr_in));
-        socketLen = sizeof(socket);
+        clientAddrLen = sizeof(clientAddr);
         socketFD = dbgsysAccept(serverSocketFD,
-                                (struct sockaddr *)&socket,
-                                &socketLen);
+                                (struct sockaddr *)&clientAddr,
+                                &clientAddrLen);
         /* set the last error here as could be overridden by configureBlocking */
         if (socketFD < 0) {
             setLastError(JDWPTRANSPORT_ERROR_IO_ERROR, "accept failed");
@@ -632,12 +750,14 @@
          * Verify that peer is allowed to connect.
          */
         if (_peers_cnt > 0) {
-            if (!isPeerAllowed(&socket)) {
+            if (!isPeerAllowed(&clientAddr)) {
                 char ebuf[64] = { 0 };
-                char buf[INET_ADDRSTRLEN] = { 0 };
-                const char* addr_str = inet_ntop(AF_INET, &(socket.sin_addr), buf, INET_ADDRSTRLEN);
+                char addrStr[INET_ADDRSTRLEN] = { 0 };
+                int err2 = getnameinfo((struct sockaddr *)&clientAddr, clientAddrLen,
+                                       addrStr, sizeof(addrStr), NULL, 0,
+                                       NI_NUMERICHOST);
                 sprintf(ebuf, "ERROR: Peer not allowed to connect: %s\n",
-                        (addr_str == NULL) ? "<bad address>" : addr_str);
+                        (err2 != 0) ? "<bad address>" : addrStr);
                 dbgsysSocketClose(socketFD);
                 socketFD = -1;
                 err = JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT;
@@ -686,28 +806,19 @@
     return JDWPTRANSPORT_ERROR_NONE;
 }
 
-static jdwpTransportError JNICALL
-socketTransport_attach(jdwpTransportEnv* env, const char* addressString, jlong attachTimeout,
-                       jlong handshakeTimeout)
-{
-    struct sockaddr_in sa;
+/*
+ * Tries to connect to the specified addrinfo, returns connected socket.
+ * If the function fails and returned socket != -1, the socket should be closed.
+ */
+static jdwpTransportError connectToAddr(struct addrinfo *ai, jlong timeout, int *socket) {
     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) {
+    *socket = dbgsysSocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+    if (*socket < 0) {
         RETURN_IO_ERROR("unable to create socket");
     }
 
-    err = setOptionsCommon(socketFD);
+    err = setOptionsCommon(ai->ai_family, socketFD);
     if (err) {
         return err;
     }
@@ -722,13 +833,13 @@
      * To do a timed connect we make the socket non-blocking
      * and poll with a timeout;
      */
-    if (attachTimeout > 0) {
+    if (timeout > 0) {
         dbgsysConfigureBlocking(socketFD, JNI_FALSE);
     }
 
-    err = dbgsysConnect(socketFD, (struct sockaddr *)&sa, sizeof(sa));
-    if (err == DBG_EINPROGRESS && attachTimeout > 0) {
-        err = dbgsysFinishConnect(socketFD, (long)attachTimeout);
+    err = dbgsysConnect(socketFD, ai->ai_addr, (socklen_t)ai->ai_addrlen);
+    if (err == DBG_EINPROGRESS && timeout > 0) {
+        err = dbgsysFinishConnect(socketFD, (long)timeout);
 
         if (err == DBG_ETIMEOUT) {
             dbgsysConfigureBlocking(socketFD, JNI_TRUE);
@@ -736,10 +847,55 @@
         }
     }
 
-    if (err < 0) {
+    if (err) {
         RETURN_IO_ERROR("connect failed");
     }
 
+    return err;
+}
+
+
+static jdwpTransportError JNICALL
+socketTransport_attach(jdwpTransportEnv* env, const char* addressString, jlong attachTimeout,
+                       jlong handshakeTimeout)
+{
+    int err;
+    struct addrinfo *addrInfo = NULL;
+
+    if (addressString == NULL || addressString[0] == '\0') {
+        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "address is missing");
+    }
+
+    err = parseAddress(addressString, &addrInfo);
+    if (err) {
+        return err;
+    }
+
+    /* 1st pass - preferredAddressFamily (by default IPv4), 2nd pass - the rest */
+    for (int pass = 0; pass < 2 && socketFD < 0; pass++) {
+        for (struct addrinfo *ai = addrInfo; ai != NULL; ai = ai->ai_next) {
+            if ((pass == 0 && ai->ai_family == preferredAddressFamily) ||
+                (pass == 1 && ai->ai_family != preferredAddressFamily))
+            {
+                err = connectToAddr(ai, attachTimeout, &socketFD);
+                if (err == JDWPTRANSPORT_ERROR_NONE) {
+                    break;
+                }
+                if (socketFD >= 0) {
+                    dbgsysSocketClose(socketFD);
+                    socketFD = -1;
+                }
+            }
+        }
+    }
+
+    freeaddrinfo(addrInfo);
+
+    /* err from the last connectToAddr() call */
+    if (err != 0) {
+        return err;
+    }
+
     if (attachTimeout > 0) {
         dbgsysConfigureBlocking(socketFD, JNI_TRUE);
     }
@@ -1010,7 +1166,7 @@
                              "allow option '*' cannot be expanded");
             }
         } else {
-            int err = parseAllowedPeers(allowed_peers);
+            int err = parseAllowedPeers(allowed_peers, len);
             if (err != JDWPTRANSPORT_ERROR_NONE) {
                 return err;
             }
@@ -1019,10 +1175,46 @@
     return JDWPTRANSPORT_ERROR_NONE;
 }
 
+/*
+ * Reads boolean system value, sets *result to
+ *  - trueValue if the property is "true";
+ *  - falseValue if the property is "false".
+ * Doesn't change *result if the property is not set or failed to read.
+ */
+static int readBooleanSysProp(int *result, int trueValue, int falseValue,
+    JNIEnv* jniEnv, jclass sysClass, jmethodID getPropMethod, const char *propName)
+{
+    jstring value;
+    jstring name = (*jniEnv)->NewStringUTF(jniEnv, propName);
+
+    if (name == NULL) {
+        return JNI_ERR;
+    }
+    value = (jstring)(*jniEnv)->CallStaticObjectMethod(jniEnv, sysClass, getPropMethod, name);
+    if ((*jniEnv)->ExceptionCheck(jniEnv)) {
+        return JNI_ERR;
+    }
+    if (value != NULL) {
+        const char *theValue = (*jniEnv)->GetStringUTFChars(jniEnv, value, NULL);
+        if (theValue == NULL) {
+            return JNI_ERR;
+        }
+        if (strcmp(theValue, "true") == 0) {
+            *result = trueValue;
+        } else if (strcmp(theValue, "false") == 0) {
+            *result = falseValue;
+        }
+        (*jniEnv)->ReleaseStringUTFChars(jniEnv, value, theValue);
+    }
+    return JNI_OK;
+}
+
 JNIEXPORT jint JNICALL
 jdwpTransport_OnLoad(JavaVM *vm, jdwpTransportCallback* cbTablePtr,
                      jint version, jdwpTransportEnv** env)
 {
+    JNIEnv* jniEnv = NULL;
+
     if (version < JDWPTRANSPORT_VERSION_1_0 ||
         version > JDWPTRANSPORT_VERSION_1_1) {
         return JNI_EVERSION;
@@ -1055,5 +1247,33 @@
 
     /* initialized TLS */
     tlsIndex = dbgsysTlsAlloc();
+
+    // retrieve network-related system properties
+    do {
+        jclass sysClass;
+        jmethodID getPropMethod;
+        if ((*vm)->GetEnv(vm, (void **)&jniEnv, JNI_VERSION_9) != JNI_OK) {
+            break;
+        }
+        sysClass = (*jniEnv)->FindClass(jniEnv, "java/lang/System");
+        if (sysClass == NULL) {
+            break;
+        }
+        getPropMethod = (*jniEnv)->GetStaticMethodID(jniEnv, sysClass,
+            "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
+        if (getPropMethod == NULL) {
+            break;
+        }
+        readBooleanSysProp(&allowOnlyIPv4, 1, 0,
+            jniEnv, sysClass, getPropMethod, "java.net.preferIPv4Stack");
+        readBooleanSysProp(&preferredAddressFamily, AF_INET6, AF_INET,
+            jniEnv, sysClass, getPropMethod, "java.net.preferIPv6Addresses");
+    } while (0);
+
+    if (jniEnv != NULL && (*jniEnv)->ExceptionCheck(jniEnv)) {
+        (*jniEnv)->ExceptionClear(jniEnv);
+    }
+
+
     return JNI_OK;
 }
--- a/src/jdk.jdwp.agent/share/native/libdt_socket/sysSocket.h	Fri May 10 17:13:02 2019 -0700
+++ b/src/jdk.jdwp.agent/share/native/libdt_socket/sysSocket.h	Wed May 15 11:06:33 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -47,11 +47,11 @@
 int dbgsysListen(int fd, int backlog);
 int dbgsysRecv(int fd, char *buf, size_t nBytes, int flags);
 int dbgsysSend(int fd, char *buf, size_t nBytes, int flags);
-int dbgsysGetAddrInfo(char *hostname, char *service, struct addrinfo *hints, struct addrinfo **results);
+int dbgsysGetAddrInfo(const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **results);
+void dbgsysFreeAddrInfo(struct addrinfo *info);
 int dbgsysSocket(int domain, int type, int protocol);
 int dbgsysBind(int fd, struct sockaddr *name, socklen_t namelen);
 int dbgsysSetSocketOption(int fd, jint cmd, jboolean on, jvalue value);
-uint32_t dbgsysInetAddr(const char* cp);
 uint32_t dbgsysHostToNetworkLong(uint32_t hostlong);
 unsigned short dbgsysHostToNetworkShort(unsigned short hostshort);
 uint32_t dbgsysNetworkToHostLong(uint32_t netlong);
--- a/src/jdk.jdwp.agent/unix/native/libdt_socket/socket_md.c	Fri May 10 17:13:02 2019 -0700
+++ b/src/jdk.jdwp.agent/unix/native/libdt_socket/socket_md.c	Wed May 15 11:06:33 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -127,12 +127,17 @@
 }
 
 int
-dbgsysGetAddrInfo(char *hostname, char *service,
-                  struct addrinfo *hints,
+dbgsysGetAddrInfo(const char *hostname, const char *service,
+                  const struct addrinfo *hints,
                   struct addrinfo **results) {
     return getaddrinfo(hostname, service, hints, results);
 }
 
+void
+dbgsysFreeAddrInfo(struct addrinfo *info) {
+    freeaddrinfo(info);
+}
+
 unsigned short
 dbgsysHostToNetworkShort(unsigned short hostshort) {
     return htons(hostshort);
@@ -164,11 +169,6 @@
 }
 
 uint32_t
-dbgsysInetAddr(const char* cp) {
-    return (uint32_t)inet_addr(cp);
-}
-
-uint32_t
 dbgsysHostToNetworkLong(uint32_t hostlong) {
     return htonl(hostlong);
 }
--- a/src/jdk.jdwp.agent/windows/native/libdt_socket/socket_md.c	Fri May 10 17:13:02 2019 -0700
+++ b/src/jdk.jdwp.agent/windows/native/libdt_socket/socket_md.c	Wed May 15 11:06:33 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -199,10 +199,15 @@
 }
 
 int
-dbgsysGetAddrInfo(char *hostname, char *service,
-                  struct addrinfo *hints,
+dbgsysGetAddrInfo(const char *hostname, const char *service,
+                  const struct addrinfo *hints,
                   struct addrinfo **result) {
-  return getaddrinfo(hostname, service, hints, result);
+    return getaddrinfo(hostname, service, hints, result);
+}
+
+void
+dbgsysFreeAddrInfo(struct addrinfo *info) {
+    freeaddrinfo(info);
 }
 
 unsigned short
@@ -214,7 +219,7 @@
 dbgsysSocket(int domain, int type, int protocol) {
   int fd = (int)socket(domain, type, protocol);
   if (fd != SOCKET_ERROR) {
-      SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE);
+    SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE);
   }
   return fd;
 }
@@ -241,15 +246,6 @@
 
 
 uint32_t
-dbgsysInetAddr(const char* cp) {
-    uint32_t addr;
-    if (inet_pton(AF_INET, cp, &addr) < 1) {
-      return -1;
-    }
-    return addr;
-}
-
-uint32_t
 dbgsysHostToNetworkLong(uint32_t hostlong) {
     return (uint32_t)htonl((u_long)hostlong);
 }
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ListeningConnector/startListening/startlis001.java	Fri May 10 17:13:02 2019 -0700
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ListeningConnector/startListening/startlis001.java	Wed May 15 11:06:33 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, 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
@@ -32,9 +32,12 @@
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 
+import java.util.Arrays;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import nsk.share.*;
 import nsk.share.jpda.*;
@@ -85,7 +88,6 @@
     private int runIt(String argv[], PrintStream out) {
         String port;
         String addr;
-        InetAddress inetAddr = null;
         ArgumentHandler argHandler = new ArgumentHandler(argv);
 
 // pass if CONNECTOR_NAME is not implemented
@@ -97,34 +99,40 @@
 
         long timeout = argHandler.getWaitTime() * 60 * 1000;
 
-/* Check that listening address returned by ListeningConnector.startListening()
-   matches the address which was set via connector's arguments */
+        /* Check that listening address returned by ListeningConnector.startListening()
+         * matches the address which was set via connector's arguments.
+         * Empty host address causes listening for local connections only (loopback interface).
+         * */
+        String hostname = "localhost";
+        List<String> validAddresses = new LinkedList<>();
+        validAddresses.add(hostname);
         try {
-            inetAddr = InetAddress.getLocalHost();
+            Arrays.stream(InetAddress.getAllByName(hostname))
+                    .forEach(address -> validAddresses.add(address.getHostAddress()));
         } catch (UnknownHostException e) {
             log.complain("FAILURE: caught UnknownHostException " +
-                e.getMessage());
+                    e.getMessage());
             totalRes = false;
         }
-        String hostname = inetAddr.getHostName();
-        String ip = inetAddr.getHostAddress();
+
         port = argHandler.getTransportPortIfNotDynamic();
 
         initConnector(port);
         if ((addr = startListen()) == null) {
             log.complain("Test case #1 FAILED: unable to start listening");
             totalRes = false;
-        }
-        else {
+        } else {
+            String validAddrList = validAddresses.stream()
+                    .map(value -> value + ":" + port)
+                    .collect(Collectors.joining(" or "));
             log.display("Test case #1: start listening the address " + addr);
-            log.display("Expected address: "+ hostname + ":" + port +
-                "\n\tor "+ ip + ":" + port);
-            if ( (!addr.startsWith(hostname) && !addr.startsWith(ip)) ||
-                 (port != null && !addr.endsWith(port)) ) {
+            log.display("Expected addresses: " + validAddrList);
+            final String listenAddr = addr;
+            boolean isValid = validAddresses.stream()
+                    .anyMatch(value -> listenAddr.startsWith(value) && (port == null || listenAddr.endsWith(port)));
+            if (!isValid) {
                 log.complain("Test case #1 FAILED: listening address " + addr +
-                    "\ndoes not match expected address:\n" +
-                    hostname + ":" + port + " or " +
-                    ip + ":" + port);
+                    "\ndoes not match expected address:\n" + validAddrList);
                 totalRes = false;
             }
             if (!stopListen()) {
@@ -135,8 +143,8 @@
                log.display("Test case #1 PASSED: listening address matches expected address");
         }
 
-/* Check that an address generated by ListeningConnector.startListening()
-   is valid i.e. debugee VM is accessible via this address */
+        /* Check that an address generated by ListeningConnector.startListening()
+           is valid i.e. debugee VM is accessible via this address */
         initConnector(null);
         if ((addr = startListen()) == null) {
             log.complain("Test case #2 FAILED: unable to start listening");
--- a/test/jdk/com/sun/jdi/BasicJDWPConnectionTest.java	Fri May 10 17:13:02 2019 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,220 +0,0 @@
-/*
- * Copyright (c) 2017, 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.
- *
- * 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.
- */
-
-/*
- * @test
- * @summary Smoke test for JDWP hardening
- * @library /test/lib
- * @run driver BasicJDWPConnectionTest
- */
-
-import java.io.IOException;
-
-import java.net.Socket;
-import java.net.SocketException;
-
-import jdk.test.lib.Utils;
-import jdk.test.lib.apps.LingeredApp;
-
-import java.util.ArrayList;
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-
-public class BasicJDWPConnectionTest {
-
-    public static int handshake(int port) throws IOException {
-        // Connect to the debuggee and handshake
-        int res = -1;
-        Socket s = null;
-        try {
-            s = new Socket("localhost", port);
-            s.getOutputStream().write("JDWP-Handshake".getBytes("UTF-8"));
-            byte[] buffer = new byte[24];
-            res = s.getInputStream().read(buffer);
-        }
-        catch (SocketException ex) {
-            // pass
-        } finally {
-            if (s != null) {
-                s.close();
-            }
-        }
-        return res;
-    }
-
-    public static ArrayList<String> prepareCmd(String allowOpt) {
-         ArrayList<String> cmd = new ArrayList<>();
-
-         String jdwpArgs = "-agentlib:jdwp=transport=dt_socket,server=y," +
-                           "suspend=n,address=*:0" + allowOpt;
-         cmd.add(jdwpArgs);
-         return cmd;
-    }
-
-    private static Pattern listenRegexp = Pattern.compile("Listening for transport \\b(.+)\\b at address: \\b(\\d+)\\b");
-    private static int detectPort(LingeredApp app) {
-        long maxWaitTime = System.currentTimeMillis()
-                + Utils.adjustTimeout(10000);  // 10 seconds adjusted for TIMEOUT_FACTOR
-        while (true) {
-            String s = app.getProcessStdout();
-            Matcher m = listenRegexp.matcher(s);
-            if (m.find()) {
-                // m.group(1) is transport, m.group(2) is port
-                return Integer.parseInt(m.group(2));
-            }
-            if (System.currentTimeMillis() > maxWaitTime) {
-                throw new RuntimeException("Could not detect port from '" + s + "' (timeout)");
-            }
-            try {
-                if (app.getProcess().waitFor(500, TimeUnit.MILLISECONDS)) {
-                    throw new RuntimeException("Could not detect port from '" + s + "' (debuggee is terminated)");
-                }
-            } catch (InterruptedException e) {
-                // ignore
-            }
-        }
-    }
-
-    public static void positiveTest(String testName, String allowOpt)
-        throws InterruptedException, IOException {
-        System.err.println("\nStarting " + testName);
-        ArrayList<String> cmd = prepareCmd(allowOpt);
-
-        LingeredApp a = LingeredApp.startApp(cmd);
-        int res;
-        try {
-            res = handshake(detectPort(a));
-        } finally {
-            a.stopApp();
-        }
-        if (res < 0) {
-            throw new RuntimeException(testName + " FAILED");
-        }
-        System.err.println(testName + " PASSED");
-    }
-
-    public static void negativeTest(String testName, String allowOpt)
-        throws InterruptedException, IOException {
-        System.err.println("\nStarting " + testName);
-        ArrayList<String> cmd = prepareCmd(allowOpt);
-
-        LingeredApp a = LingeredApp.startApp(cmd);
-        int res;
-        try {
-            res = handshake(detectPort(a));
-        } finally {
-            a.stopApp();
-        }
-        if (res > 0) {
-            System.err.println(testName + ": res=" + res);
-            throw new RuntimeException(testName + " FAILED");
-        }
-        System.err.println(testName + ": returned a negative code as expected: " + res);
-        System.err.println(testName + " PASSED");
-    }
-
-    public static void badAllowOptionTest(String testName, String allowOpt)
-        throws InterruptedException, IOException {
-        System.err.println("\nStarting " + testName);
-        ArrayList<String> cmd = prepareCmd(allowOpt);
-
-        LingeredApp a;
-        try {
-            a = LingeredApp.startApp(cmd);
-        } catch (IOException ex) {
-            System.err.println(testName + ": caught expected IOException");
-            System.err.println(testName + " PASSED");
-            return;
-        }
-        // LingeredApp.startApp is expected to fail, but if not, terminate the app
-        a.stopApp();
-        throw new RuntimeException(testName + " FAILED");
-    }
-
-    public static void DefaultTest() throws InterruptedException, IOException {
-        // No allow option is the same as the allow option ',allow=*' is passed
-        String allowOpt = "";
-        positiveTest("DefaultTest", allowOpt);
-    }
-
-    static void ExplicitDefaultTest() throws InterruptedException, IOException {
-        // Explicit permission for connections from everywhere
-        String allowOpt = ",allow=*";
-        positiveTest("ExplicitDefaultTest" ,allowOpt);
-    }
-
-    public static void AllowTest() throws InterruptedException, IOException {
-        String allowOpt = ",allow=127.0.0.1";
-        positiveTest("AllowTest", allowOpt);
-    }
-
-    public static void MultiAllowTest() throws InterruptedException, IOException {
-        String allowOpt = ",allow=127.0.0.1+10.0.0.0/8+172.16.0.0/12+192.168.0.0/24";
-        positiveTest("MultiAllowTest", allowOpt);
-    }
-
-    public static void DenyTest() throws InterruptedException, IOException {
-        // Bad allow address
-        String allowOpt = ",allow=0.0.0.0";
-        negativeTest("DenyTest", allowOpt);
-    }
-
-    public static void MultiDenyTest() throws InterruptedException, IOException {
-        // Wrong separator ';' is used for allow option
-        String allowOpt = ",allow=127.0.0.1;192.168.0.0/24";
-        badAllowOptionTest("MultiDenyTest", allowOpt);
-    }
-
-    public static void EmptyAllowOptionTest() throws InterruptedException, IOException {
-        // Empty allow option
-        String allowOpt = ",allow=";
-        badAllowOptionTest("EmptyAllowOptionTest", allowOpt);
-    }
-
-    public static void ExplicitMultiDefault1Test() throws InterruptedException, IOException {
-        // Bad mix of allow option '*' with address value
-        String allowOpt = ",allow=*+allow=127.0.0.1";
-        badAllowOptionTest("ExplicitMultiDefault1Test", allowOpt);
-    }
-
-    public static void ExplicitMultiDefault2Test() throws InterruptedException, IOException {
-        // Bad mix of allow address value with '*'
-        String allowOpt = ",allow=allow=127.0.0.1+*";
-        badAllowOptionTest("ExplicitMultiDefault2Test", allowOpt);
-    }
-
-    public static void main(String[] args) throws Exception {
-        DefaultTest();
-        ExplicitDefaultTest();
-        AllowTest();
-        MultiAllowTest();
-        DenyTest();
-        MultiDenyTest();
-        EmptyAllowOptionTest();
-        ExplicitMultiDefault1Test();
-        ExplicitMultiDefault2Test();
-        System.err.println("\nTest PASSED");
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/JdwpAllowTest.java	Wed May 15 11:06:33 2019 -0700
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2017, 2019, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Smoke test for JDWP hardening
+ * @library /test/lib
+ * @run driver JdwpAllowTest
+ */
+
+import java.io.IOException;
+
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+
+import jdk.test.lib.Utils;
+import jdk.test.lib.apps.LingeredApp;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+public class JdwpAllowTest {
+
+    public static int handshake(int port) throws IOException {
+        // Connect to the debuggee and handshake
+        int res = -1;
+        Socket s = null;
+        try {
+            s = new Socket(localAddr, port);
+            s.getOutputStream().write("JDWP-Handshake".getBytes("UTF-8"));
+            byte[] buffer = new byte[24];
+            res = s.getInputStream().read(buffer);
+        }
+        catch (SocketException ex) {
+            ex.printStackTrace();
+            // pass
+        } finally {
+            if (s != null) {
+                s.close();
+            }
+        }
+        return res;
+    }
+
+    public static ArrayList<String> prepareCmd(String allowOpt) {
+         ArrayList<String> cmd = new ArrayList<>();
+
+         String jdwpArgs = "-agentlib:jdwp=transport=dt_socket,server=y," +
+                           "suspend=n,address=*:0"
+                            + (allowOpt == null ? "" : ",allow=" + allowOpt);
+         cmd.add(jdwpArgs);
+         return cmd;
+    }
+
+    private static Pattern listenRegexp = Pattern.compile("Listening for transport \\b(.+)\\b at address: \\b(\\d+)\\b");
+    private static int detectPort(LingeredApp app) {
+        long maxWaitTime = System.currentTimeMillis()
+                + Utils.adjustTimeout(10000);  // 10 seconds adjusted for TIMEOUT_FACTOR
+        while (true) {
+            String s = app.getProcessStdout();
+            Matcher m = listenRegexp.matcher(s);
+            if (m.find()) {
+                // m.group(1) is transport, m.group(2) is port
+                return Integer.parseInt(m.group(2));
+            }
+            if (System.currentTimeMillis() > maxWaitTime) {
+                throw new RuntimeException("Could not detect port from '" + s + "' (timeout)");
+            }
+            try {
+                if (app.getProcess().waitFor(500, TimeUnit.MILLISECONDS)) {
+                    throw new RuntimeException("Could not detect port from '" + s + "' (debuggee is terminated)");
+                }
+            } catch (InterruptedException e) {
+                // ignore
+            }
+        }
+    }
+
+    public static void positiveTest(String testName, String allowOpt)
+        throws InterruptedException, IOException {
+        System.err.println("\nStarting " + testName);
+        ArrayList<String> cmd = prepareCmd(allowOpt);
+
+        LingeredApp a = LingeredApp.startApp(cmd);
+        int res;
+        try {
+            res = handshake(detectPort(a));
+        } finally {
+            a.stopApp();
+        }
+        if (res < 0) {
+            throw new RuntimeException(testName + " FAILED");
+        }
+        System.err.println(testName + " PASSED");
+    }
+
+    public static void negativeTest(String testName, String allowOpt)
+        throws InterruptedException, IOException {
+        System.err.println("\nStarting " + testName);
+        ArrayList<String> cmd = prepareCmd(allowOpt);
+
+        LingeredApp a = LingeredApp.startApp(cmd);
+        int res;
+        try {
+            res = handshake(detectPort(a));
+        } finally {
+            a.stopApp();
+        }
+        if (res > 0) {
+            System.err.println(testName + ": res=" + res);
+            throw new RuntimeException(testName + " FAILED");
+        }
+        System.err.println(testName + ": returned a negative code as expected: " + res);
+        System.err.println(testName + " PASSED");
+    }
+
+    public static void badAllowOptionTest(String testName, String allowOpt)
+        throws InterruptedException, IOException {
+        System.err.println("\nStarting " + testName);
+        ArrayList<String> cmd = prepareCmd(allowOpt);
+
+        LingeredApp a;
+        try {
+            a = LingeredApp.startApp(cmd);
+        } catch (IOException ex) {
+            System.err.println(testName + ": caught expected IOException");
+            System.err.println(testName + " PASSED");
+            return;
+        }
+        // LingeredApp.startApp is expected to fail, but if not, terminate the app
+        a.stopApp();
+        throw new RuntimeException(testName + " FAILED");
+    }
+
+    /*
+     * Generate allow address by changing random bit in the local address
+     * and calculate 2 masks (prefix length) - one is matches original local address
+     * and another doesn't.
+     */
+    private static class MaskTest {
+        public final String localAddress;
+        public final String allowAddress;
+        public final int prefixLengthGood;
+        public final int prefixLengthBad;
+
+        public MaskTest(InetAddress addr) throws Exception {
+            localAddress = addr.getHostAddress();
+            byte[] bytes = addr.getAddress();
+            Random r = new Random();
+            // prefix length must be >= 1, so bitToChange must be >= 2
+            int bitToChange = r.nextInt(bytes.length * 8 - 3) + 2;
+            setBit(bytes, bitToChange, !getBit(bytes, bitToChange));
+            // clear rest of the bits for mask address
+            for (int i = bitToChange + 1; i < bytes.length * 8; i++) {
+                setBit(bytes, i, false);
+            }
+            allowAddress = InetAddress.getByAddress(bytes).getHostAddress();
+
+            prefixLengthBad = bitToChange;
+            prefixLengthGood = bitToChange - 1;
+        }
+
+        private static boolean getBit(byte[] bytes, int pos) {
+            return (bytes[pos / 8] & (1 << (7 - (pos % 8)))) != 0;
+        }
+
+        private static void setBit(byte[] bytes, int pos, boolean value) {
+            byte byteValue = (byte)(1 << (7 - (pos % 8)));
+            if (value) {
+                bytes[pos / 8] = (byte)(bytes[pos / 8] | byteValue);
+            } else {
+                bytes[pos / 8] &= (~byteValue);
+            }
+        }
+    }
+
+    private static String localAddr;
+    private static List<MaskTest> maskTests = new LinkedList<>();
+
+    private static void init() throws Exception {
+        InetAddress addrs[] = InetAddress.getAllByName("localhost");
+        if (addrs.length == 0) {
+            throw new RuntimeException("No addresses is returned for 'localhost'");
+        }
+        localAddr = addrs[0].getHostAddress();
+        System.err.println("localhost address: " + localAddr);
+
+        for (int i =  0; i < addrs.length; i++) {
+            maskTests.add(new MaskTest(addrs[i]));
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        init();
+
+        // No allow option is the same as the allow option ',allow=*' is passed
+        positiveTest("DefaultTest", null);
+
+        // Explicit permission for connections from everywhere
+        positiveTest("ExplicitDefaultTest", "*");
+
+        positiveTest("AllowTest", localAddr);
+
+        positiveTest("MultiAllowTest", localAddr + "+10.0.0.0/8+172.16.0.0/12+192.168.0.0/24");
+
+        // Bad allow address
+        negativeTest("DenyTest", "0.0.0.0");
+
+        // Wrong separator ';' is used for allow option
+        badAllowOptionTest("MultiDenyTest", localAddr + ";192.168.0.0/24");
+
+        // Empty allow option
+        badAllowOptionTest("EmptyAllowOptionTest", "");
+
+        // Bad mix of allow option '*' with address value
+        badAllowOptionTest("ExplicitMultiDefault1Test", "*+" + localAddr);
+
+        // Bad mix of allow address value with '*'
+        badAllowOptionTest("ExplicitMultiDefault2Test", localAddr + "+*");
+
+        for (MaskTest test: maskTests) {
+            // override localAddr (to connect to required IPv4 or IPv6 address)
+            localAddr = test.localAddress;
+            positiveTest("PositiveMaskTest(" + test.localAddress + ")",
+                         test.allowAddress + "/" + test.prefixLengthGood);
+            positiveTest("NegativeMaskTest(" + test.localAddress + ")",
+                         test.allowAddress + "/" + test.prefixLengthBad);
+        }
+
+        System.err.println("\nTest PASSED");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/JdwpAttachTest.java	Wed May 15 11:06:33 2019 -0700
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ * 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.
+ */
+
+import com.sun.jdi.Bootstrap;
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.connect.Connector;
+import com.sun.jdi.connect.ListeningConnector;
+import jdk.test.lib.apps.LingeredApp;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/*
+ * @test
+ * @bug 8184770
+ * @summary Tests for JDWP agent attach functionality (including IPv6 support)
+ * @library /test/lib
+ *
+ * @build HelloWorld JdwpAttachTest
+ * @run main/othervm JdwpAttachTest
+ */
+public class JdwpAttachTest {
+
+    public static void main(String[] args) throws Exception {
+        List<InetAddress> addresses = getAddresses();
+
+        boolean ipv4EnclosedTested = false;
+        boolean ipv6EnclosedTested = false;
+        for (InetAddress addr: addresses) {
+            // also test that addresses enclosed in square brackets are supported
+            attachTest(addr.getHostAddress(), addr.getHostAddress());
+            // listening on "*" should accept connections from all addresses
+            attachTest("*", addr.getHostAddress());
+
+            // test that addresses enclosed in square brackets are supported.
+            if (addr instanceof Inet4Address && !ipv4EnclosedTested) {
+                attachTest("[" + addr.getHostAddress() + "]", "[" + addr.getHostAddress() + "]");
+                ipv4EnclosedTested = true;
+            }
+            if (addr instanceof Inet6Address && !ipv6EnclosedTested) {
+                attachTest("[" + addr.getHostAddress() + "]", "[" + addr.getHostAddress() + "]");
+                ipv6EnclosedTested = true;
+            }
+        }
+
+        // by using "localhost" or empty hostname
+        // we should be able to attach to both IPv4 and IPv6 addresses (127.0.0.1 & ::1)
+        InetAddress localAddresses[] = InetAddress.getAllByName("localhost");
+        for (int i = 0; i < localAddresses.length; i++) {
+            attachTest(localAddresses[i].getHostAddress(), "");
+        }
+    }
+
+    private static void attachTest(String listenAddress, String connectAddresses)
+            throws Exception {
+        log("Starting listening at " + listenAddress);
+        ListeningConnector connector = getListenConnector();
+        Map<String, Connector.Argument> args = connector.defaultArguments();
+        setConnectorArg(args, "localAddress", listenAddress);
+        setConnectorArg(args, "port", "0");
+
+        String actualAddress = connector.startListening(args);
+        String actualPort = actualAddress.substring(actualAddress.lastIndexOf(':') + 1);
+        String port = args.get("port").value();
+        // port from connector.startListening must be the same as values from arguments
+        if (!port.equals(actualPort)) {
+            throw new RuntimeException("values from connector.startListening (" + actualPort
+                    + " is not equal to values from arguments (" + port + ")");
+        }
+        log("Listening port: " + port);
+
+        log("Attaching from " + connectAddresses);
+        try {
+            ExecutorService executor = Executors.newSingleThreadExecutor();
+            executor.submit((Callable<Exception>)() -> {
+                VirtualMachine vm = connector.accept(args);
+                log("ACCEPTED.");
+                vm.dispose();
+                return null;
+            });
+            executor.shutdown();
+
+            LingeredApp debuggee = LingeredApp.startApp(
+                    Arrays.asList("-agentlib:jdwp=transport=dt_socket"
+                                +",address=" + connectAddresses + ":" + port
+                                + ",server=n,suspend=n"));
+            debuggee.stopApp();
+
+            executor.awaitTermination(20, TimeUnit.SECONDS);
+        } finally {
+            connector.stopListening(args);
+        }
+    }
+
+    private static List<InetAddress> getAddresses() {
+        List<InetAddress> result = new LinkedList<>();
+        try {
+            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+            while (networkInterfaces.hasMoreElements()) {
+                NetworkInterface iface = networkInterfaces.nextElement();
+                try {
+                    if (iface.isUp()) {
+                        Enumeration<InetAddress> addresses = iface.getInetAddresses();
+                        while (addresses.hasMoreElements()) {
+                            InetAddress addr = addresses.nextElement();
+                            // Java reports link local addresses with named scope,
+                            // but Windows sockets routines support only numeric scope id.
+                            // skip such addresses.
+                            if (addr instanceof Inet6Address) {
+                                Inet6Address addr6 = (Inet6Address)addr;
+                                if (addr6.getScopedInterface() != null) {
+                                    continue;
+                                }
+                            }
+                            log(" - (" + addr.getClass().getSimpleName() + ") " + addr.getHostAddress());
+                            result.add(addr);
+                        }
+                    }
+                } catch (SocketException e) {
+                    log("Interface " + iface.getDisplayName() + ": failed to get addresses");
+                }
+            }
+        } catch (SocketException e) {
+            log("Interface enumeration error: " + e);
+        }
+        return result;
+    }
+
+    private static String LISTEN_CONNECTOR = "com.sun.jdi.SocketListen";
+
+    private static ListeningConnector getListenConnector() {
+        return (ListeningConnector)getConnector(LISTEN_CONNECTOR);
+    }
+
+    private static Connector getConnector(String name) {
+        List<Connector> connectors = Bootstrap.virtualMachineManager().allConnectors();
+        for (Iterator<Connector> iter = connectors.iterator(); iter.hasNext(); ) {
+            Connector connector = iter.next();
+            if (connector.name().equalsIgnoreCase(name)) {
+                return connector;
+            }
+        }
+        throw new IllegalArgumentException("Connector " + name + " not found");
+    }
+
+    private static void setConnectorArg(Map<String, Connector.Argument> args, String name, String value) {
+        Connector.Argument arg = args.get(name);
+        if (arg == null) {
+            throw new IllegalArgumentException("Argument " + name + " is not defined");
+        }
+        arg.setValue(value);
+    }
+
+    private static void log(Object o) {
+        System.out.println(String.valueOf(o));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/JdwpListenTest.java	Wed May 15 11:06:33 2019 -0700
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ * 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.
+ */
+
+import com.sun.jdi.Bootstrap;
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.connect.AttachingConnector;
+import com.sun.jdi.connect.Connector;
+import com.sun.jdi.connect.IllegalConnectorArgumentsException;
+import lib.jdb.Debuggee;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/*
+ * @test
+ * @bug 8184770
+ * @summary Tests for JDWP agent listen functionality (including IPv6 support)
+ * @library /test/lib
+ *
+ * @build HelloWorld JdwpListenTest
+ * @run main/othervm JdwpListenTest
+ */
+public class JdwpListenTest {
+
+    public static void main(String[] args) throws Exception {
+        List<InetAddress> addresses = getAddresses();
+
+        boolean ipv4EnclosedTested = false;
+        boolean ipv6EnclosedTested = false;
+        for (InetAddress listen: addresses) {
+            for (InetAddress attach: addresses) {
+                // can connect only from the same address
+                // IPv6 cannot connect to IPv4 (::1 to 127.0.0.1) and vice versa.
+                listenTest(listen.getHostAddress(), attach.getHostAddress(), attach.equals(listen));
+            }
+            // test that addresses enclosed in square brackets are supported.
+            if (listen instanceof Inet4Address && !ipv4EnclosedTested) {
+                listenTest("[" + listen.getHostAddress() + "]", "[" + listen.getHostAddress() + "]", true);
+                ipv4EnclosedTested = true;
+            }
+            if (listen instanceof Inet6Address && !ipv6EnclosedTested) {
+                listenTest("[" + listen.getHostAddress() + "]", "[" + listen.getHostAddress() + "]", true);
+                ipv6EnclosedTested = true;
+            }
+        }
+        // listen on "*" - should be accessible from any address
+        for (InetAddress attach: addresses) {
+            listenTest("*", attach.getHostAddress(), true);
+        }
+    }
+
+    private static void listenTest(String listenAddress, String connectAddress, boolean expectedResult)
+            throws IOException {
+        log("Starting listening debuggee at " + listenAddress);
+        try (Debuggee debuggee = Debuggee.launcher("HelloWorld").setAddress(listenAddress + ":0").launch()) {
+            log("Debuggee is listening on " + listenAddress + ":" + debuggee.getAddress());
+            log("Connecting from " + connectAddress + ", expected: " + (expectedResult ? "SUCCESS" : "FAILURE"));
+            try {
+                VirtualMachine vm = attach(connectAddress, debuggee.getAddress());
+                vm.dispose();
+                if (!expectedResult) {
+                    throw new RuntimeException("ERROR: attached successfully");
+                }
+            } catch (IOException ex) {
+                if (expectedResult) {
+                    throw new RuntimeException("ERROR: failed to attach", ex);
+                }
+            }
+        }
+    }
+
+    private static List<InetAddress> getAddresses() {
+        List<InetAddress> result = new LinkedList<>();
+        try {
+            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+            while (networkInterfaces.hasMoreElements()) {
+                NetworkInterface iface = networkInterfaces.nextElement();
+                try {
+                    if (iface.isUp()) {
+                        Enumeration<InetAddress> addresses = iface.getInetAddresses();
+                        while (addresses.hasMoreElements()) {
+                            InetAddress addr = addresses.nextElement();
+                            // Java reports link local addresses with named scope,
+                            // but Windows sockets routines support only numeric scope id.
+                            // skip such addresses.
+                            if (addr instanceof Inet6Address) {
+                                Inet6Address addr6 = (Inet6Address)addr;
+                                if (addr6.getScopedInterface() != null) {
+                                    continue;
+                                }
+                            }
+                            log(" - (" + addr.getClass().getSimpleName() + ") " + addr.getHostAddress());
+                            result.add(addr);
+                        }
+                    }
+                } catch (SocketException e) {
+                    log("Interface " + iface.getDisplayName() + ": failed to get addresses");
+                }
+            }
+        } catch (SocketException e) {
+            log("Interface enumeration error: " + e);
+        }
+        return result;
+    }
+
+    private static String ATTACH_CONNECTOR = "com.sun.jdi.SocketAttach";
+    // cache socket attaching connector
+    private static AttachingConnector attachingConnector;
+
+    private static VirtualMachine attach(String address, String port) throws IOException {
+        if (attachingConnector == null) {
+            attachingConnector = (AttachingConnector)getConnector(ATTACH_CONNECTOR);
+        }
+        Map<String, Connector.Argument> args = attachingConnector.defaultArguments();
+        setConnectorArg(args, "hostname", address);
+        setConnectorArg(args, "port", port);
+        try {
+            return attachingConnector.attach(args);
+        } catch (IllegalConnectorArgumentsException e) {
+            // unexpected.. wrap in RuntimeException
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Connector getConnector(String name) {
+        List<Connector> connectors = Bootstrap.virtualMachineManager().allConnectors();
+        for (Iterator<Connector> iter = connectors.iterator(); iter.hasNext(); ) {
+            Connector connector = iter.next();
+            if (connector.name().equalsIgnoreCase(name)) {
+                return connector;
+            }
+        }
+        throw new IllegalArgumentException("Connector " + name + " not found");
+    }
+
+    private static void setConnectorArg(Map<String, Connector.Argument> args, String name, String value) {
+        Connector.Argument arg = args.get(name);
+        if (arg == null) {
+            throw new IllegalArgumentException("Argument " + name + " is not defined");
+        }
+        arg.setValue(value);
+    }
+
+    private static void log(Object o) {
+        System.out.println(String.valueOf(o));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/JdwpNetProps.java	Wed May 15 11:06:33 2019 -0700
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ * 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.
+ */
+
+import com.sun.jdi.Bootstrap;
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.connect.AttachingConnector;
+import com.sun.jdi.connect.Connector;
+import com.sun.jdi.connect.IllegalConnectorArgumentsException;
+import lib.jdb.Debuggee;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/*
+ * @test
+ * @bug 8184770
+ * @summary Tests that JDWP agent honors jdk net properties
+ * @library /test/lib
+ *
+ * @build HelloWorld JdwpNetProps
+ * @run main/othervm JdwpNetProps
+ */
+public class JdwpNetProps {
+
+    public static void main(String[] args) throws Exception {
+        InetAddress addrs[] = InetAddress.getAllByName("localhost");
+        InetAddress ipv4Address = null;
+        InetAddress ipv6Address = null;
+        for (int i =  0; i < addrs.length; i++) {
+            if (addrs[i] instanceof Inet4Address) {
+                ipv4Address = addrs[i];
+            } else if (addrs[i] instanceof Inet6Address) {
+                ipv6Address = addrs[i];
+            }
+        }
+
+        if (ipv4Address != null) {
+            new ListenTest("localhost", ipv4Address)
+                    .preferIPv4Stack(true)
+                    .run(TestResult.Success);
+            new ListenTest("localhost", ipv4Address)
+                    .preferIPv4Stack(false)
+                    .run(TestResult.Success);
+            if (ipv6Address != null) {
+                // - only IPv4, so connection prom IPv6 should fail
+                new ListenTest("localhost", ipv6Address)
+                        .preferIPv4Stack(true)
+                        .preferIPv6Addresses(true)
+                        .run(TestResult.AttachFailed);
+                // - listen on IPv4
+                new ListenTest("localhost", ipv6Address)
+                        .preferIPv6Addresses(false)
+                        .run(TestResult.AttachFailed);
+                // - listen on IPv6
+                new ListenTest("localhost", ipv6Address)
+                        .preferIPv6Addresses(true)
+                        .run(TestResult.Success);
+            }
+        } else {
+            // IPv6-only system - expected to fail on IPv4 address
+            new ListenTest("localhost", ipv6Address)
+                    .preferIPv4Stack(true)
+                    .run(TestResult.ListenFailed);
+        }
+    }
+
+    private enum TestResult {
+        Success,
+        ListenFailed,
+        AttachFailed
+    }
+
+    private static class ListenTest {
+        private final String listenAddress;
+        private final InetAddress connectAddress;
+        private Boolean preferIPv4Stack;
+        private Boolean preferIPv6Addresses;
+        public ListenTest(String listenAddress, InetAddress connectAddress) {
+            this.listenAddress = listenAddress;
+            this.connectAddress = connectAddress;
+        }
+        public ListenTest preferIPv4Stack(Boolean value) {
+            preferIPv4Stack = value;
+            return this;
+        }
+        public ListenTest preferIPv6Addresses(Boolean value) {
+            preferIPv6Addresses = value;
+            return this;
+        }
+
+        public void run(TestResult expectedResult) throws Exception {
+            List<String> options = new LinkedList<>();
+            if (preferIPv4Stack != null) {
+                options.add("-Djava.net.preferIPv4Stack=" + preferIPv4Stack.toString());
+            }
+            if (preferIPv6Addresses != null) {
+                options.add("-Djava.net.preferIPv6Addresses=" + preferIPv6Addresses.toString());
+            }
+            log("Starting listening debuggee at " + listenAddress
+                    + (expectedResult == TestResult.ListenFailed ? ": expected to fail" : ""));
+            Exception error = null;
+            try (Debuggee debuggee = Debuggee.launcher("HelloWorld")
+                    .setAddress(listenAddress + ":0")
+                    .addOptions(options).launch()) {
+                log("Debuggee is listening on " + listenAddress + ":" + debuggee.getAddress());
+                log("Connecting from " + connectAddress.getHostAddress()
+                        + ", expected: " + (expectedResult == TestResult.Success ? "Success" : "Failure"));
+                try {
+                    VirtualMachine vm = attach(connectAddress.getHostAddress(), debuggee.getAddress());
+                    vm.dispose();
+                    if (expectedResult == TestResult.Success) {
+                        log("Attached successfully (as expected)");
+                    } else {
+                        error = new RuntimeException("ERROR: attached successfully");
+                    }
+                } catch (Exception ex) {
+                    if (expectedResult == TestResult.AttachFailed) {
+                        log("Attach failed (as expected)");
+                    } else {
+                        error = new RuntimeException("ERROR: failed to attach", ex);
+                    }
+                }
+            } catch (Exception ex) {
+                if (expectedResult == TestResult.ListenFailed) {
+                    log("Listen failed (as expected)");
+                } else {
+                    error = new RuntimeException("ERROR: listen failed", ex);
+                }
+            }
+            if (error != null) {
+                throw error;
+            }
+        }
+    }
+
+    private static String ATTACH_CONNECTOR = "com.sun.jdi.SocketAttach";
+    // cache socket attaching connector
+    private static AttachingConnector attachingConnector;
+
+    private static VirtualMachine attach(String address, String port) throws IOException {
+        if (attachingConnector == null) {
+            attachingConnector = (AttachingConnector)getConnector(ATTACH_CONNECTOR);
+        }
+        Map<String, Connector.Argument> args = attachingConnector.defaultArguments();
+        setConnectorArg(args, "hostname", address);
+        setConnectorArg(args, "port", port);
+        try {
+            return attachingConnector.attach(args);
+        } catch (IllegalConnectorArgumentsException e) {
+            // unexpected.. wrap in RuntimeException
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Connector getConnector(String name) {
+        List<Connector> connectors = Bootstrap.virtualMachineManager().allConnectors();
+        for (Iterator<Connector> iter = connectors.iterator(); iter.hasNext(); ) {
+            Connector connector = iter.next();
+            if (connector.name().equalsIgnoreCase(name)) {
+                return connector;
+            }
+        }
+        throw new IllegalArgumentException("Connector " + name + " not found");
+    }
+
+    private static void setConnectorArg(Map<String, Connector.Argument> args, String name, String value) {
+        Connector.Argument arg = args.get(name);
+        if (arg == null) {
+            throw new IllegalArgumentException("Argument " + name + " is not defined");
+        }
+        arg.setValue(value);
+    }
+
+    private static void log(Object o) {
+        System.out.println(String.valueOf(o));
+    }
+
+}