changeset 11710:719acb12beed

8150234: Windows 10 App Containers disallow access to ICMP calls Reviewed-by: chegar
author robm
date Thu, 09 Jun 2016 22:47:46 +0100
parents 3f8696411cf9
children 96393e490afd
files src/windows/native/java/net/Inet4AddressImpl.c src/windows/native/java/net/Inet6AddressImpl.c
diffstat 2 files changed, 139 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/src/windows/native/java/net/Inet4AddressImpl.c	Wed Jun 08 16:19:38 2016 +0000
+++ b/src/windows/native/java/net/Inet4AddressImpl.c	Thu Jun 09 22:47:46 2016 +0100
@@ -292,7 +292,6 @@
 }
 
 
-
 static BOOL
 WindowsVersionCheck(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) {
     OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
@@ -316,7 +315,7 @@
 }
 
 static jboolean
-wxp_ping4(JNIEnv *env,
+tcp_ping4(JNIEnv *env,
           jbyteArray addrArray,
           jint timeout,
           jbyteArray ifArray,
@@ -471,23 +470,17 @@
 ping4(JNIEnv *env,
       unsigned long src_addr,
       unsigned long dest_addr,
-      jint timeout)
+      jint timeout,
+      HANDLE hIcmpFile)
 {
     // See https://msdn.microsoft.com/en-us/library/aa366050%28VS.85%29.aspx
 
-    HANDLE hIcmpFile;
     DWORD dwRetVal = 0;
     char SendData[32] = {0};
     LPVOID ReplyBuffer = NULL;
     DWORD ReplySize = 0;
     jboolean ret = JNI_FALSE;
 
-    hIcmpFile = IcmpCreateFile();
-    if (hIcmpFile == INVALID_HANDLE_VALUE) {
-        NET_ThrowNew(env, WSAGetLastError(), "Unable to open handle");
-        return JNI_FALSE;
-    }
-
     ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData);
     ReplyBuffer = (VOID*) malloc(ReplySize);
     if (ReplyBuffer == NULL) {
@@ -553,6 +546,7 @@
         jint dest_addr = 0;
         jbyte caddr[4];
         int sz;
+        HANDLE hIcmpFile;
 
         /**
          * Convert IP address from byte array to integer
@@ -583,8 +577,20 @@
             src_addr = htonl(src_addr);
         }
 
-        return ping4(env, src_addr, dest_addr, timeout);
+        hIcmpFile = IcmpCreateFile();
+        if (hIcmpFile == INVALID_HANDLE_VALUE) {
+            int err = WSAGetLastError();
+            if (err == ERROR_ACCESS_DENIED) {
+                // fall back to TCP echo if access is denied to ICMP
+                return tcp_ping4(env, addrArray, timeout, ifArray, ttl);
+            } else {
+                NET_ThrowNew(env, err, "Unable to create ICMP file handle");
+                return JNI_FALSE;
+            }
+        } else {
+            return ping4(env, src_addr, dest_addr, timeout, hIcmpFile);
+        }
     } else {
-        wxp_ping4(env, addrArray, timeout, ifArray, ttl);
+        tcp_ping4(env, addrArray, timeout, ifArray, ttl);
     }
 }
--- a/src/windows/native/java/net/Inet6AddressImpl.c	Wed Jun 08 16:19:38 2016 +0000
+++ b/src/windows/native/java/net/Inet6AddressImpl.c	Thu Jun 09 22:47:46 2016 +0100
@@ -360,6 +360,109 @@
 
 #ifdef AF_INET6
 
+/**
+ * ping implementation using tcp port 7 (echo)
+ */
+static jboolean
+tcp_ping6(JNIEnv *env,
+          jint timeout,
+          jint ttl,
+          struct sockaddr_in6 him6,
+          struct sockaddr_in6* netif,
+          int len)
+{
+    jint fd;
+    WSAEVENT hEvent;
+    int connect_rv = -1;
+
+    fd = NET_Socket(AF_INET6, SOCK_STREAM, 0);
+    if (fd == SOCKET_ERROR) {
+        /* note: if you run out of fds, you may not be able to load
+         * the exception class, and get a NoClassDefFoundError
+         * instead.
+         */
+        NET_ThrowNew(env, errno, "Can't create socket");
+        return JNI_FALSE;
+    }
+
+    /**
+     * A TTL was specified, let's set the socket option.
+     */
+    if (ttl > 0) {
+      setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (const char *)&ttl, sizeof(ttl));
+    }
+
+    /**
+     * A network interface was specified, let's bind to it.
+     */
+    if (netif != NULL) {
+      if (NET_Bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in6)) < 0) {
+        NET_ThrowNew(env, WSAGetLastError(), "Can't bind socket to interface");
+        closesocket(fd);
+        return JNI_FALSE;
+      }
+    }
+
+    /**
+     * Make the socket non blocking.
+     */
+    hEvent = WSACreateEvent();
+    WSAEventSelect(fd, hEvent, FD_READ|FD_CONNECT|FD_CLOSE);
+
+    /* no need to use NET_Connect as non-blocking */
+    him6.sin6_port = htons((short) 7); /* Echo port */
+    connect_rv = connect(fd, (struct sockaddr *)&him6, len);
+
+    /**
+     * connection established or refused immediately, either way it means
+     * we were able to reach the host!
+     */
+    if (connect_rv == 0 || WSAGetLastError() == WSAECONNREFUSED) {
+        WSACloseEvent(hEvent);
+        closesocket(fd);
+        return JNI_TRUE;
+    } else {
+        int optlen;
+
+        switch (WSAGetLastError()) {
+        case WSAEHOSTUNREACH:   /* Host Unreachable */
+        case WSAENETUNREACH:    /* Network Unreachable */
+        case WSAENETDOWN:       /* Network is down */
+        case WSAEPFNOSUPPORT:   /* Protocol Family unsupported */
+          WSACloseEvent(hEvent);
+          closesocket(fd);
+          return JNI_FALSE;
+        }
+
+        if (WSAGetLastError() != WSAEWOULDBLOCK) {
+            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
+                                         "connect failed");
+            WSACloseEvent(hEvent);
+            closesocket(fd);
+            return JNI_FALSE;
+        }
+
+        timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);
+
+        if (timeout >= 0) {
+          /* has connection been established? */
+          optlen = sizeof(connect_rv);
+          if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
+                         &optlen) <0) {
+            connect_rv = WSAGetLastError();
+          }
+
+          if (connect_rv == 0 || connect_rv == WSAECONNREFUSED) {
+            WSACloseEvent(hEvent);
+            closesocket(fd);
+            return JNI_TRUE;
+          }
+        }
+    }
+    WSACloseEvent(hEvent);
+    closesocket(fd);
+    return JNI_FALSE;
+}
 
 /**
  * ping implementation.
@@ -371,9 +474,9 @@
 ping6(JNIEnv *env,
       struct sockaddr_in6* src,
       struct sockaddr_in6* dest,
-      jint timeout)
+      jint timeout,
+      HANDLE hIcmpFile)
 {
-    HANDLE hIcmpFile;
     DWORD dwRetVal = 0;
     char SendData[32] = {0};
     LPVOID ReplyBuffer = NULL;
@@ -381,12 +484,6 @@
     IP_OPTION_INFORMATION ipInfo = {255, 0, 0, 0, NULL};
     struct sockaddr_in6 sa6Source;
 
-    hIcmpFile = Icmp6CreateFile();
-    if (hIcmpFile == INVALID_HANDLE_VALUE) {
-        NET_ThrowNew(env, WSAGetLastError(), "Unable to open handle");
-        return JNI_FALSE;
-    }
-
     ReplySize = sizeof(ICMPV6_ECHO_REPLY) + sizeof(SendData);
     ReplyBuffer = (VOID*) malloc(ReplySize);
     if (ReplyBuffer == NULL) {
@@ -445,7 +542,7 @@
     struct sockaddr_in6* netif = NULL;
     struct sockaddr_in6 inf6;
     int len = 0;
-    int connect_rv = -1;
+    HANDLE hIcmpFile;
 
     /*
      * If IPv6 is not enable, then we can't reach an IPv6 address, can we?
@@ -489,7 +586,21 @@
       inf6.sin6_scope_id = if_scope;
       netif = &inf6;
     }
-    return ping6(env, netif, &him6, timeout);
+
+    hIcmpFile = Icmp6CreateFile();
+    if (hIcmpFile == INVALID_HANDLE_VALUE) {
+        int err = WSAGetLastError();
+        if (err == ERROR_ACCESS_DENIED) {
+            // fall back to TCP echo if access is denied to ICMP
+            return tcp_ping6(env, timeout, ttl, him6, netif, len);
+        } else {
+            NET_ThrowNew(env, err, "Unable to create ICMP file handle");
+            return JNI_FALSE;
+        }
+    } else {
+        return ping6(env, netif, &him6, timeout, hIcmpFile);
+    }
+
 #endif /* AF_INET6 */
     return JNI_FALSE;
 }