--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/windows/native/java/net/DualStackPlainDatagramSocketImpl.c Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,496 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+#include <windows.h>
+#include <winsock2.h>
+#include "jni.h"
+#include "net_util.h"
+#include "java_net_DualStackPlainDatagramSocketImpl.h"
+
+/*
+ * This function "purges" all outstanding ICMP port unreachable packets
+ * outstanding on a socket and returns JNI_TRUE if any ICMP messages
+ * have been purged. The rational for purging is to emulate normal BSD
+ * behaviour whereby receiving a "connection reset" status resets the
+ * socket.
+ */
+static jboolean purgeOutstandingICMP(JNIEnv *env, jint fd)
+{
+ jboolean got_icmp = JNI_FALSE;
+ char buf[1];
+ fd_set tbl;
+ struct timeval t = { 0, 0 };
+ struct sockaddr_in rmtaddr;
+ int addrlen = sizeof(rmtaddr);
+
+ /*
+ * Peek at the queue to see if there is an ICMP port unreachable. If there
+ * is then receive it.
+ */
+ FD_ZERO(&tbl);
+ FD_SET(fd, &tbl);
+ while(1) {
+ if (select(/*ignored*/fd+1, &tbl, 0, 0, &t) <= 0) {
+ break;
+ }
+ if (recvfrom(fd, buf, 1, MSG_PEEK,
+ (struct sockaddr *)&rmtaddr, &addrlen) != JVM_IO_ERR) {
+ break;
+ }
+ if (WSAGetLastError() != WSAECONNRESET) {
+ /* some other error - we don't care here */
+ break;
+ }
+
+ recvfrom(fd, buf, 1, 0, (struct sockaddr *)&rmtaddr, &addrlen);
+ got_icmp = JNI_TRUE;
+ }
+
+ return got_icmp;
+}
+
+/*
+ * Class: java_net_DualStackPlainDatagramSocketImpl
+ * Method: socketCreate
+ * Signature: (Z)I
+ */
+JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketCreate
+ (JNIEnv *env, jclass clazz, jboolean v6Only /*unused*/) {
+ int fd, rv, opt=0, t=TRUE;
+ DWORD x1, x2; /* ignored result codes */
+
+ fd = (int) socket(AF_INET6, SOCK_DGRAM, 0);
+ if (fd == INVALID_SOCKET) {
+ NET_ThrowNew(env, WSAGetLastError(), "Socket creation failed");
+ return -1;
+ }
+
+ rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, sizeof(opt));
+ if (rv == SOCKET_ERROR) {
+ NET_ThrowNew(env, WSAGetLastError(), "Socket creation failed");
+ return -1;
+ }
+
+ SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE);
+ NET_SetSockOpt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&t, sizeof(BOOL));
+
+ /* SIO_UDP_CONNRESET fixes a "bug" introduced in Windows 2000, which
+ * returns connection reset errors on unconnected UDP sockets (as well
+ * as connected sockets). The solution is to only enable this feature
+ * when the socket is connected.
+ */
+ t = FALSE;
+ WSAIoctl(fd ,SIO_UDP_CONNRESET ,&t ,sizeof(t) ,&x1 ,sizeof(x1) ,&x2 ,0 ,0);
+
+ return fd;
+}
+
+/*
+ * Class: java_net_DualStackPlainDatagramSocketImpl
+ * Method: socketBind
+ * Signature: (ILjava/net/InetAddress;I)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketBind
+ (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) {
+ SOCKETADDRESS sa;
+ int rv;
+ int sa_len = sizeof(sa);
+
+ if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&sa,
+ &sa_len, JNI_TRUE) != 0) {
+ return;
+ }
+
+ rv = bind(fd, (struct sockaddr *)&sa, sa_len);
+
+ if (rv == SOCKET_ERROR) {
+ if (WSAGetLastError() == WSAEACCES) {
+ WSASetLastError(WSAEADDRINUSE);
+ }
+ NET_ThrowNew(env, WSAGetLastError(), "Cannot bind");
+ }
+}
+
+/*
+ * Class: java_net_DualStackPlainDatagramSocketImpl
+ * Method: socketConnect
+ * Signature: (ILjava/net/InetAddress;I)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketConnect
+ (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) {
+ SOCKETADDRESS sa;
+ int rv;
+ int sa_len = sizeof(sa);
+ DWORD x1, x2; /* ignored result codes */
+ int t = TRUE;
+
+ if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&sa,
+ &sa_len, JNI_TRUE) != 0) {
+ return;
+ }
+
+ rv = connect(fd, (struct sockaddr *)&sa, sa_len);
+ if (rv == SOCKET_ERROR) {
+ NET_ThrowNew(env, WSAGetLastError(), "connect");
+ return;
+ }
+
+ /* see comment in socketCreate */
+ WSAIoctl(fd, SIO_UDP_CONNRESET, &t, sizeof(t), &x1, sizeof(x1), &x2, 0, 0);
+}
+
+/*
+ * Class: java_net_DualStackPlainDatagramSocketImpl
+ * Method: socketDisconnect
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketDisconnect
+ (JNIEnv *env, jclass clazz, jint fd ) {
+ SOCKETADDRESS sa;
+ int sa_len = sizeof(sa);
+ DWORD x1, x2; /* ignored result codes */
+ int t = FALSE;
+
+ memset(&sa, 0, sa_len);
+ connect(fd, (struct sockaddr *)&sa, sa_len);
+
+ /* see comment in socketCreate */
+ WSAIoctl(fd, SIO_UDP_CONNRESET, &t, sizeof(t), &x1, sizeof(x1), &x2, 0, 0);
+}
+
+/*
+ * Class: java_net_DualStackPlainDatagramSocketImpl
+ * Method: socketClose
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketClose
+ (JNIEnv *env, jclass clazz , jint fd) {
+ NET_SocketClose(fd);
+}
+
+
+/*
+ * Class: java_net_DualStackPlainDatagramSocketImpl
+ * Method: socketLocalPort
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketLocalPort
+ (JNIEnv *env, jclass clazz, jint fd) {
+ SOCKETADDRESS sa;
+ int len = sizeof(sa);
+
+ if (getsockname(fd, (struct sockaddr *)&sa, &len) == SOCKET_ERROR) {
+ NET_ThrowNew(env, WSAGetLastError(), "JVM_GetSockName");
+ return -1;
+ }
+ return (int) ntohs((u_short)GET_PORT(&sa));
+}
+
+/*
+ * Class: java_net_DualStackPlainDatagramSocketImpl
+ * Method: socketLocalAddress
+ * Signature: (I)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketLocalAddress
+ (JNIEnv *env , jclass clazz, jint fd) {
+ SOCKETADDRESS sa;
+ int len = sizeof(sa);
+ jobject iaObj;
+ int port;
+
+ if (getsockname(fd, (struct sockaddr *)&sa, &len) == SOCKET_ERROR) {
+ NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name");
+ return NULL;
+ }
+
+ iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port);
+ return iaObj;
+}
+
+/*
+ * Class: java_net_DualStackPlainDatagramSocketImpl
+ * Method: socketReceiveOrPeekData
+ * Signature: (ILjava/net/DatagramPacket;IZZ)I
+ */
+JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketReceiveOrPeekData
+ (JNIEnv *env, jclass clazz, jint fd, jobject dpObj,
+ jint timeout, jboolean connected, jboolean peek) {
+ SOCKETADDRESS sa;
+ int sa_len = sizeof(sa);
+ int port, rv, flags=0;
+ char BUF[MAX_BUFFER_LEN];
+ char *fullPacket;
+ BOOL retry;
+ jlong prevTime = 0;
+
+ jint packetBufferOffset, packetBufferLen;
+ jbyteArray packetBuffer;
+
+ /* if we are only peeking. Called from peekData */
+ if (peek) {
+ flags = MSG_PEEK;
+ }
+
+ packetBuffer = (*env)->GetObjectField(env, dpObj, dp_bufID);
+ packetBufferOffset = (*env)->GetIntField(env, dpObj, dp_offsetID);
+ packetBufferLen = (*env)->GetIntField(env, dpObj, dp_bufLengthID);
+
+ if (packetBufferLen > MAX_BUFFER_LEN) {
+ /* Note: the buffer needn't be greater than 65,536 (0xFFFF)
+ * the max size of an IP packet. Anything bigger is truncated anyway.
+ */
+ if (packetBufferLen > MAX_PACKET_LEN) {
+ packetBufferLen = MAX_PACKET_LEN;
+ }
+ fullPacket = (char *)malloc(packetBufferLen);
+ if (!fullPacket) {
+ JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
+ return -1;
+ }
+ } else {
+ fullPacket = &(BUF[0]);
+ }
+
+ do {
+ retry = FALSE;
+
+ if (timeout) {
+ if (prevTime == 0) {
+ prevTime = JVM_CurrentTimeMillis(env, 0);
+ }
+ rv = NET_Timeout(fd, timeout);
+ if (rv <= 0) {
+ if (rv == 0) {
+ JNU_ThrowByName(env,JNU_JAVANETPKG "SocketTimeoutException",
+ "Receive timed out");
+ } else if (rv == JVM_IO_ERR) {
+ JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
+ "Socket closed");
+ } else if (rv == JVM_IO_INTR) {
+ JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
+ "operation interrupted");
+ }
+ if (packetBufferLen > MAX_BUFFER_LEN) {
+ free(fullPacket);
+ }
+ return -1;
+ }
+ }
+
+ /* receive the packet */
+ rv = recvfrom(fd, fullPacket, packetBufferLen, flags,
+ (struct sockaddr *)&sa, &sa_len);
+
+ if (rv == SOCKET_ERROR && (WSAGetLastError() == WSAECONNRESET)) {
+ /* An icmp port unreachable - we must receive this as Windows
+ * does not reset the state of the socket until this has been
+ * received.
+ */
+ purgeOutstandingICMP(env, fd);
+
+ if (connected) {
+ JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
+ "ICMP Port Unreachable");
+ if (packetBufferLen > MAX_BUFFER_LEN)
+ free(fullPacket);
+ return -1;
+ } else if (timeout) {
+ /* Adjust timeout */
+ jlong newTime = JVM_CurrentTimeMillis(env, 0);
+ timeout -= (jint)(newTime - prevTime);
+ if (timeout <= 0) {
+ JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
+ "Receive timed out");
+ if (packetBufferLen > MAX_BUFFER_LEN)
+ free(fullPacket);
+ return -1;
+ }
+ prevTime = newTime;
+ }
+ retry = TRUE;
+ }
+ } while (retry);
+
+ port = (int) ntohs ((u_short) GET_PORT((SOCKETADDRESS *)&sa));
+
+ /* truncate the data if the packet's length is too small */
+ if (rv > packetBufferLen) {
+ rv = packetBufferLen;
+ }
+ if (rv < 0) {
+ if (WSAGetLastError() == WSAEMSGSIZE) {
+ /* it is because the buffer is too small. It's UDP, it's
+ * unreliable, it's all good. discard the rest of the
+ * data..
+ */
+ rv = packetBufferLen;
+ } else {
+ /* failure */
+ (*env)->SetIntField(env, dpObj, dp_lengthID, 0);
+ }
+ }
+
+ if (rv == -1) {
+ JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");
+ } else if (rv == -2) {
+ JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
+ "operation interrupted");
+ } else if (rv < 0) {
+ NET_ThrowCurrent(env, "Datagram receive failed");
+ } else {
+ jobject packetAddress;
+ /*
+ * Check if there is an InetAddress already associated with this
+ * packet. If so, we check if it is the same source address. We
+ * can't update any existing InetAddress because it is immutable
+ */
+ packetAddress = (*env)->GetObjectField(env, dpObj, dp_addressID);
+ if (packetAddress != NULL) {
+ if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&sa,
+ packetAddress)) {
+ /* force a new InetAddress to be created */
+ packetAddress = NULL;
+ }
+ }
+ if (packetAddress == NULL) {
+ packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa,
+ &port);
+ /* stuff the new Inetaddress into the packet */
+ (*env)->SetObjectField(env, dpObj, dp_addressID, packetAddress);
+ }
+
+ /* populate the packet */
+ (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, rv,
+ (jbyte *)fullPacket);
+ (*env)->SetIntField(env, dpObj, dp_portID, port);
+ (*env)->SetIntField(env, dpObj, dp_lengthID, rv);
+ }
+
+ if (packetBufferLen > MAX_BUFFER_LEN) {
+ free(fullPacket);
+ }
+ return port;
+}
+
+/*
+ * Class: java_net_DualStackPlainDatagramSocketImpl
+ * Method: socketSend
+ * Signature: (I[BIILjava/net/InetAddress;IZ)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketSend
+ (JNIEnv *env, jclass clazz, jint fd, jbyteArray data, jint offset, jint length,
+ jobject iaObj, jint port, jboolean connected) {
+ SOCKETADDRESS sa;
+ int sa_len = sizeof(sa);
+ SOCKETADDRESS *sap = &sa;
+ char BUF[MAX_BUFFER_LEN];
+ char *fullPacket;
+ int rv;
+
+ if (connected) {
+ sap = 0; /* arg to JVM_Sendto () null in this case */
+ sa_len = 0;
+ } else {
+ if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&sa,
+ &sa_len, JNI_TRUE) != 0) {
+ return;
+ }
+ }
+
+ if (length > MAX_BUFFER_LEN) {
+ /* Note: the buffer needn't be greater than 65,536 (0xFFFF)
+ * the max size of an IP packet. Anything bigger is truncated anyway.
+ */
+ if (length > MAX_PACKET_LEN) {
+ length = MAX_PACKET_LEN;
+ }
+ fullPacket = (char *)malloc(length);
+ if (!fullPacket) {
+ JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
+ return;
+ }
+ } else {
+ fullPacket = &(BUF[0]);
+ }
+
+ (*env)->GetByteArrayRegion(env, data, offset, length,
+ (jbyte *)fullPacket);
+ rv = sendto(fd, fullPacket, length, 0, (struct sockaddr *)sap, sa_len);
+ if (rv == SOCKET_ERROR) {
+ if (rv == JVM_IO_ERR) {
+ NET_ThrowNew(env, WSAGetLastError(), "Datagram send failed");
+ } else if (rv == JVM_IO_INTR) {
+ JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
+ "operation interrupted");
+ }
+ }
+
+ if (length > MAX_BUFFER_LEN) {
+ free(fullPacket);
+ }
+}
+
+/*
+ * Class: java_net_DualStackPlainDatagramSocketImpl
+ * Method: socketSetIntOption
+ * Signature: (III)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketSetIntOption
+ (JNIEnv *env, jclass clazz, jint fd , jint cmd, jint value) {
+ int level, opt;
+
+ if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
+ JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+ "Invalid option");
+ return;
+ }
+
+ if (NET_SetSockOpt(fd, level, opt, (char *)&value, sizeof(value)) < 0) {
+ NET_ThrowNew(env, WSAGetLastError(), "setsockopt");
+ }
+}
+
+/*
+ * Class: java_net_DualStackPlainDatagramSocketImpl
+ * Method: socketGetIntOption
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketGetIntOption
+ (JNIEnv *env, jclass clazz, jint fd, jint cmd) {
+ int level, opt, result=0;
+ int result_len = sizeof(result);
+
+ if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
+ JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+ "Invalid option");
+ return -1;
+ }
+
+ if (NET_GetSockOpt(fd, level, opt, (void *)&result, &result_len) < 0) {
+ NET_ThrowNew(env, WSAGetLastError(), "getsockopt");
+ return -1;
+ }
+
+ return result;
+}