diff options
author | jfrijters <jfrijters> | 2011-08-25 09:01:20 +0400 |
---|---|---|
committer | jfrijters <jfrijters> | 2011-08-25 09:01:20 +0400 |
commit | 0fe529afda3c3b1c2dc488f5037e63ae3e85e340 (patch) | |
tree | be159a54f772757c9a1cd19a851e9cd8e958aa37 /openjdk/java/net | |
parent | adf4ee1fb7f69255a2bf107152711997af66c494 (diff) |
Prepare for forking.
Diffstat (limited to 'openjdk/java/net')
-rw-r--r-- | openjdk/java/net/DualStackPlainDatagramSocketImpl.java | 265 | ||||
-rw-r--r-- | openjdk/java/net/DualStackPlainDatagramSocketImpl_c.java | 496 | ||||
-rw-r--r-- | openjdk/java/net/DualStackPlainSocketImpl.java | 276 | ||||
-rw-r--r-- | openjdk/java/net/DualStackPlainSocketImpl_c.java | 478 |
4 files changed, 1515 insertions, 0 deletions
diff --git a/openjdk/java/net/DualStackPlainDatagramSocketImpl.java b/openjdk/java/net/DualStackPlainDatagramSocketImpl.java new file mode 100644 index 00000000..7533dea1 --- /dev/null +++ b/openjdk/java/net/DualStackPlainDatagramSocketImpl.java @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.net; + +import java.io.IOException; +import java.io.FileDescriptor; +import sun.misc.SharedSecrets; +import sun.misc.JavaIOFileDescriptorAccess; + +/** + * This class defines the plain DatagramSocketImpl that is used on + * Windows platforms greater than or equal to Windows Vista. These + * platforms have a dual layer TCP/IP stack and can handle both IPv4 + * and IPV6 through a single file descriptor. + * <p> + * Note: Multicasting on a dual layer TCP/IP stack is always done with + * TwoStacksPlainDatagramSocketImpl. This is to overcome the lack + * of behavior defined for multicasting over a dual layer socket by the RFC. + * + * @author Chris Hegarty + */ + +class DualStackPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl +{ + static JavaIOFileDescriptorAccess fdAccess = SharedSecrets.getJavaIOFileDescriptorAccess(); + + protected void datagramSocketCreate() throws SocketException { + if (fd == null) + throw new SocketException("Socket closed"); + + int newfd = socketCreate(false /* v6Only */); + + fdAccess.set(fd, newfd); + } + + protected synchronized void bind0(int lport, InetAddress laddr) + throws SocketException { + int nativefd = checkAndReturnNativeFD(); + + if (laddr == null) + throw new NullPointerException("argument address"); + + socketBind(nativefd, laddr, lport); + if (lport == 0) { + localPort = socketLocalPort(nativefd); + } else { + localPort = lport; + } + } + + protected synchronized int peek(InetAddress address) throws IOException { + int nativefd = checkAndReturnNativeFD(); + + if (address == null) + throw new NullPointerException("Null address in peek()"); + + // Use peekData() + DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1); + int peekPort = peekData(peekPacket); + address = peekPacket.getAddress(); + return peekPort; + } + + protected synchronized int peekData(DatagramPacket p) throws IOException { + int nativefd = checkAndReturnNativeFD(); + + if (p == null) + throw new NullPointerException("packet"); + if (p.getData() == null) + throw new NullPointerException("packet buffer"); + + return socketReceiveOrPeekData(nativefd, p, timeout, connected, true /*peek*/); + } + + protected synchronized void receive0(DatagramPacket p) throws IOException { + int nativefd = checkAndReturnNativeFD(); + + if (p == null) + throw new NullPointerException("packet"); + if (p.getData() == null) + throw new NullPointerException("packet buffer"); + + socketReceiveOrPeekData(nativefd, p, timeout, connected, false /*receive*/); + } + + protected void send(DatagramPacket p) throws IOException { + int nativefd = checkAndReturnNativeFD(); + + if (p == null) + throw new NullPointerException("null packet"); + + if (p.getAddress() == null ||p.getData() ==null) + throw new NullPointerException("null address || null buffer"); + + socketSend(nativefd, p.getData(), p.getOffset(), p.getLength(), + p.getAddress(), p.getPort(), connected); + } + + protected void connect0(InetAddress address, int port) throws SocketException { + int nativefd = checkAndReturnNativeFD(); + + if (address == null) + throw new NullPointerException("address"); + + socketConnect(nativefd, address, port); + } + + protected void disconnect0(int family /*unused*/) { + if (fd == null || !fd.valid()) + return; // disconnect doesn't throw any exceptions + + socketDisconnect(fdAccess.get(fd)); + } + + protected void datagramSocketClose() { + if (fd == null || !fd.valid()) + return; // close doesn't throw any exceptions + + socketClose(fdAccess.get(fd)); + fdAccess.set(fd, -1); + } + + protected void socketSetOption(int opt, Object val) throws SocketException { + int nativefd = checkAndReturnNativeFD(); + + int optionValue = 0; + + switch(opt) { + case IP_TOS : + case SO_RCVBUF : + case SO_SNDBUF : + optionValue = ((Integer)val).intValue(); + break; + case SO_REUSEADDR : + case SO_BROADCAST : + optionValue = ((Boolean)val).booleanValue() ? 1 : 0; + break; + default: /* shouldn't get here */ + throw new SocketException("Option not supported"); + } + + socketSetIntOption(nativefd, opt, optionValue); + } + + protected Object socketGetOption(int opt) throws SocketException { + int nativefd = checkAndReturnNativeFD(); + + // SO_BINDADDR is not a socket option. + if (opt == SO_BINDADDR) { + return socketLocalAddress(nativefd); + } + + int value = socketGetIntOption(nativefd, opt); + Object returnValue = null; + + switch (opt) { + case SO_REUSEADDR : + case SO_BROADCAST : + returnValue = (value == 0) ? Boolean.FALSE : Boolean.TRUE; + break; + case IP_TOS : + case SO_RCVBUF : + case SO_SNDBUF : + returnValue = new Integer(value); + break; + default: /* shouldn't get here */ + throw new SocketException("Option not supported"); + } + + return returnValue; + } + + /* Multicast specific methods. + * Multicasting on a dual layer TCP/IP stack is always done with + * TwoStacksPlainDatagramSocketImpl. This is to overcome the lack + * of behavior defined for multicasting over a dual layer socket by the RFC. + */ + protected void join(InetAddress inetaddr, NetworkInterface netIf) + throws IOException { + throw new IOException("Method not implemented!"); + } + + protected void leave(InetAddress inetaddr, NetworkInterface netIf) + throws IOException { + throw new IOException("Method not implemented!"); + } + + protected void setTimeToLive(int ttl) throws IOException { + throw new IOException("Method not implemented!"); + } + + protected int getTimeToLive() throws IOException { + throw new IOException("Method not implemented!"); + } + + + protected void setTTL(byte ttl) throws IOException { + throw new IOException("Method not implemented!"); + } + + protected byte getTTL() throws IOException { + throw new IOException("Method not implemented!"); + } + /* END Multicast specific methods */ + + private int checkAndReturnNativeFD() throws SocketException { + if (fd == null || !fd.valid()) + throw new SocketException("Socket closed"); + + return fdAccess.get(fd); + } + + /* Native methods */ + + private static native void initIDs(); + + private static native int socketCreate(boolean v6Only); + + private static native void socketBind(int fd, InetAddress localAddress, int localport) + throws SocketException; + + private static native void socketConnect(int fd, InetAddress address, int port) + throws SocketException; + + private static native void socketDisconnect(int fd); + + private static native void socketClose(int fd); + + private static native int socketLocalPort(int fd) throws SocketException; + + private static native Object socketLocalAddress(int fd) throws SocketException; + + private static native int socketReceiveOrPeekData(int fd, DatagramPacket packet, + int timeout, boolean connected, boolean peek) throws IOException; + + private static native void socketSend(int fd, byte[] data, int offset, int length, + InetAddress address, int port, boolean connected) throws IOException; + + private static native void socketSetIntOption(int fd, int cmd, + int optionValue) throws SocketException; + + private static native int socketGetIntOption(int fd, int cmd) throws SocketException; +} diff --git a/openjdk/java/net/DualStackPlainDatagramSocketImpl_c.java b/openjdk/java/net/DualStackPlainDatagramSocketImpl_c.java new file mode 100644 index 00000000..6521e1e5 --- /dev/null +++ b/openjdk/java/net/DualStackPlainDatagramSocketImpl_c.java @@ -0,0 +1,496 @@ +/* + * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +#include <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; +} diff --git a/openjdk/java/net/DualStackPlainSocketImpl.java b/openjdk/java/net/DualStackPlainSocketImpl.java new file mode 100644 index 00000000..4073b9d5 --- /dev/null +++ b/openjdk/java/net/DualStackPlainSocketImpl.java @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.net; + +import java.io.IOException; +import java.io.FileDescriptor; +import sun.misc.SharedSecrets; +import sun.misc.JavaIOFileDescriptorAccess; + +/** + * This class defines the plain SocketImpl that is used on Windows platforms + * greater or equal to Windows Vista. These platforms have a dual + * layer TCP/IP stack and can handle both IPv4 and IPV6 through a + * single file descriptor. + * + * @author Chris Hegarty + */ + +class DualStackPlainSocketImpl extends AbstractPlainSocketImpl +{ + static JavaIOFileDescriptorAccess fdAccess = SharedSecrets.getJavaIOFileDescriptorAccess(); + + public DualStackPlainSocketImpl() {} + + public DualStackPlainSocketImpl(FileDescriptor fd) { + this.fd = fd; + } + + void socketCreate(boolean stream) throws IOException { + if (fd == null) + throw new SocketException("Socket closed"); + + int newfd = socket0(stream, false /*v6 Only*/); + + fdAccess.set(fd, newfd); + } + + void socketConnect(InetAddress address, int port, int timeout) + throws IOException { + int nativefd = checkAndReturnNativeFD(); + + if (address == null) + throw new NullPointerException("inet address argument is null."); + + int connectResult; + if (timeout <= 0) { + connectResult = connect0(nativefd, address, port); + } else { + configureBlocking(nativefd, false); + try { + connectResult = connect0(nativefd, address, port); + if (connectResult == WOULDBLOCK) { + waitForConnect(nativefd, timeout); + } + } finally { + configureBlocking(nativefd, true); + } + } + /* + * We need to set the local port field. If bind was called + * previous to the connect (by the client) then localport field + * will already be set. + */ + if (localport == 0) + localport = localPort0(nativefd); + } + + void socketBind(InetAddress address, int port) throws IOException { + int nativefd = checkAndReturnNativeFD(); + + if (address == null) + throw new NullPointerException("inet address argument is null."); + + bind0(nativefd, address, port); + if (port == 0) { + localport = localPort0(nativefd); + } else { + localport = port; + } + + this.address = address; + } + + void socketListen(int backlog) throws IOException { + int nativefd = checkAndReturnNativeFD(); + + listen0(nativefd, backlog); + } + + void socketAccept(SocketImpl s) throws IOException { + int nativefd = checkAndReturnNativeFD(); + + if (s == null) + throw new NullPointerException("socket is null"); + + int newfd = -1; + InetSocketAddress[] isaa = new InetSocketAddress[1]; + if (timeout <= 0) { + newfd = accept0(nativefd, isaa); + } else { + configureBlocking(nativefd, false); + try { + waitForNewConnection(nativefd, timeout); + newfd = accept0(nativefd, isaa); + if (newfd != -1) { + configureBlocking(newfd, true); + } + } finally { + configureBlocking(nativefd, true); + } + } + /* Update (SocketImpl)s' fd */ + fdAccess.set(s.fd, newfd); + /* Update socketImpls remote port, address and localport */ + InetSocketAddress isa = isaa[0]; + s.port = isa.getPort(); + s.address = isa.getAddress(); + s.localport = localport; + } + + int socketAvailable() throws IOException { + int nativefd = checkAndReturnNativeFD(); + return available0(nativefd); + } + + void socketClose0(boolean useDeferredClose/*unused*/) throws IOException { + if (fd == null) + throw new SocketException("Socket closed"); + + if (!fd.valid()) + return; + + close0(fdAccess.get(fd)); + fdAccess.set(fd, -1); + } + + void socketShutdown(int howto) throws IOException { + int nativefd = checkAndReturnNativeFD(); + shutdown0(nativefd, howto); + } + + void socketSetOption(int opt, boolean on, Object value) + throws SocketException { + int nativefd = checkAndReturnNativeFD(); + + if (opt == SO_TIMEOUT) { // timeout implemented through select. + return; + } + + int optionValue = 0; + + switch(opt) { + case TCP_NODELAY : + case SO_OOBINLINE : + case SO_KEEPALIVE : + case SO_REUSEADDR : + optionValue = on ? 1 : 0; + break; + case SO_SNDBUF : + case SO_RCVBUF : + case IP_TOS : + optionValue = ((Integer)value).intValue(); + break; + case SO_LINGER : + if (on) { + optionValue = ((Integer)value).intValue(); + } else { + optionValue = -1; + } + break; + default :/* shouldn't get here */ + throw new SocketException("Option not supported"); + } + + setIntOption(nativefd, opt, optionValue); + } + + int socketGetOption(int opt, Object iaContainerObj) throws SocketException { + int nativefd = checkAndReturnNativeFD(); + + // SO_BINDADDR is not a socket option. + if (opt == SO_BINDADDR) { + localAddress(nativefd, (InetAddressContainer)iaContainerObj); + return 0; // return value doesn't matter. + } + + int value = getIntOption(nativefd, opt); + + switch (opt) { + case TCP_NODELAY : + case SO_OOBINLINE : + case SO_KEEPALIVE : + case SO_REUSEADDR : + return (value == 0) ? -1 : 1; + } + return value; + } + + void socketSendUrgentData(int data) throws IOException { + int nativefd = checkAndReturnNativeFD(); + sendOOB(nativefd, data); + } + + private int checkAndReturnNativeFD() throws SocketException { + if (fd == null || !fd.valid()) + throw new SocketException("Socket closed"); + + return fdAccess.get(fd); + } + + static final int WOULDBLOCK = -2; // Nothing available (non-blocking) + + static { + initIDs(); + } + + /* Native methods */ + + static native void initIDs(); + + static native int socket0(boolean stream, boolean v6Only) throws IOException; + + static native void bind0(int fd, InetAddress localAddress, int localport) + throws IOException; + + static native int connect0(int fd, InetAddress remote, int remotePort) + throws IOException; + + static native void waitForConnect(int fd, int timeout) throws IOException; + + static native int localPort0(int fd) throws IOException; + + static native void localAddress(int fd, InetAddressContainer in) throws SocketException; + + static native void listen0(int fd, int backlog) throws IOException; + + static native int accept0(int fd, InetSocketAddress[] isaa) throws IOException; + + static native void waitForNewConnection(int fd, int timeout) throws IOException; + + static native int available0(int fd) throws IOException; + + static native void close0(int fd) throws IOException; + + static native void shutdown0(int fd, int howto) throws IOException; + + static native void setIntOption(int fd, int cmd, int optionValue) throws SocketException; + + static native int getIntOption(int fd, int cmd) throws SocketException; + + static native void sendOOB(int fd, int data) throws IOException; + + static native void configureBlocking(int fd, boolean blocking) throws IOException; +} diff --git a/openjdk/java/net/DualStackPlainSocketImpl_c.java b/openjdk/java/net/DualStackPlainSocketImpl_c.java new file mode 100644 index 00000000..5f22e887 --- /dev/null +++ b/openjdk/java/net/DualStackPlainSocketImpl_c.java @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +#include <windows.h> +#include <winsock2.h> +#include "jni.h" +#include "net_util.h" +#include "java_net_DualStackPlainSocketImpl.h" + +#define SET_BLOCKING 0 +#define SET_NONBLOCKING 1 + +static jclass isa_class; /* java.net.InetSocketAddress */ +static jmethodID isa_ctorID; /* InetSocketAddress(InetAddress, int) */ + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: initIDs + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_initIDs + (JNIEnv *env, jclass clazz) { + + jclass cls = (*env)->FindClass(env, "java/net/InetSocketAddress"); + isa_class = (*env)->NewGlobalRef(env, cls); + isa_ctorID = (*env)->GetMethodID(env, cls, "<init>", + "(Ljava/net/InetAddress;I)V"); + + // implement read timeout with select. + isRcvTimeoutSupported = 0; +} + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: socket0 + * Signature: (ZZ)I + */ +JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_socket0 + (JNIEnv *env, jclass clazz, jboolean stream, jboolean v6Only /*unused*/) { + int fd, rv, opt=0; + + fd = NET_Socket(AF_INET6, (stream ? SOCK_STREAM : SOCK_DGRAM), 0); + if (fd == INVALID_SOCKET) { + NET_ThrowNew(env, WSAGetLastError(), "create"); + return -1; + } + + rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, sizeof(opt)); + if (rv == SOCKET_ERROR) { + NET_ThrowNew(env, WSAGetLastError(), "create"); + } + + SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE); + + return fd; +} + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: bind0 + * Signature: (ILjava/net/InetAddress;I)V + */ +JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_bind0 + (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 = NET_Bind(fd, (struct sockaddr *)&sa, sa_len); + + if (rv == SOCKET_ERROR) + NET_ThrowNew(env, WSAGetLastError(), "JVM_Bind"); +} + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: connect0 + * Signature: (ILjava/net/InetAddress;I)I + */ +JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_connect0 + (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 -1; + } + + rv = connect(fd, (struct sockaddr *)&sa, sa_len); + if (rv == SOCKET_ERROR) { + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) { + return java_net_DualStackPlainSocketImpl_WOULDBLOCK; + } else if (err == WSAEADDRNOTAVAIL) { + JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException", + "connect: Address is invalid on local machine, or port is not valid on remote machine"); + } else { + NET_ThrowNew(env, err, "connect"); + } + return -1; // return value not important. + } + return rv; +} + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: waitForConnect + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_waitForConnect + (JNIEnv *env, jclass clazz, jint fd, jint timeout) { + int rv, retry; + int optlen = sizeof(rv); + fd_set wr, ex; + struct timeval t; + + FD_ZERO(&wr); + FD_ZERO(&ex); + FD_SET(fd, &wr); + FD_SET(fd, &ex); + t.tv_sec = timeout / 1000; + t.tv_usec = (timeout % 1000) * 1000; + + /* + * Wait for timeout, connection established or + * connection failed. + */ + rv = select(fd+1, 0, &wr, &ex, &t); + + /* + * Timeout before connection is established/failed so + * we throw exception and shutdown input/output to prevent + * socket from being used. + * The socket should be closed immediately by the caller. + */ + if (rv == 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + "connect timed out"); + shutdown( fd, SD_BOTH ); + return; + } + + /* + * Socket is writable or error occured. On some Windows editions + * the socket will appear writable when the connect fails so we + * check for error rather than writable. + */ + if (!FD_ISSET(fd, &ex)) { + return; /* connection established */ + } + + /* + * Connection failed. The logic here is designed to work around + * bug on Windows NT whereby using getsockopt to obtain the + * last error (SO_ERROR) indicates there is no error. The workaround + * on NT is to allow winsock to be scheduled and this is done by + * yielding and retrying. As yielding is problematic in heavy + * load conditions we attempt up to 3 times to get the error reason. + */ + for (retry=0; retry<3; retry++) { + NET_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, + (char*)&rv, &optlen); + if (rv) { + break; + } + Sleep(0); + } + + if (rv == 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Unable to establish connection"); + } else { + NET_ThrowNew(env, rv, "connect"); + } +} + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: localPort0 + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_localPort0 + (JNIEnv *env, jclass clazz, jint fd) { + SOCKETADDRESS sa; + int len = sizeof(sa); + + if (getsockname(fd, (struct sockaddr *)&sa, &len) == SOCKET_ERROR) { + if (WSAGetLastError() == WSAENOTSOCK) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + } else { + NET_ThrowNew(env, WSAGetLastError(), "getsockname failed"); + } + return -1; + } + return (int) ntohs((u_short)GET_PORT(&sa)); +} + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: localAddress + * Signature: (ILjava/net/InetAddressContainer;)V + */ +JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_localAddress + (JNIEnv *env, jclass clazz, jint fd, jobject iaContainerObj) { + int port; + SOCKETADDRESS sa; + int len = sizeof(sa); + jobject iaObj; + jclass iaContainerClass; + jfieldID iaFieldID; + + if (getsockname(fd, (struct sockaddr *)&sa, &len) == SOCKET_ERROR) { + NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name"); + return; + } + iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port); + CHECK_NULL(iaObj); + + iaContainerClass = (*env)->GetObjectClass(env, iaContainerObj); + iaFieldID = (*env)->GetFieldID(env, iaContainerClass, "addr", "Ljava/net/InetAddress;"); + CHECK_NULL(iaFieldID); + (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj); +} + + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: listen0 + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_listen0 + (JNIEnv *env, jclass clazz, jint fd, jint backlog) { + if (listen(fd, backlog) == SOCKET_ERROR) { + NET_ThrowNew(env, WSAGetLastError(), "listen failed"); + } +} + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: accept0 + * Signature: (I[Ljava/net/InetSocketAddress;)I + */ +JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_accept0 + (JNIEnv *env, jclass clazz, jint fd, jobjectArray isaa) { + int newfd, port=0; + jobject isa; + jobject ia; + SOCKETADDRESS sa; + int len = sizeof(sa); + + memset((char *)&sa, 0, len); + newfd = accept(fd, (struct sockaddr *)&sa, &len); + + if (newfd == INVALID_SOCKET) { + if (WSAGetLastError() == -2) { + JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", + "operation interrupted"); + } else { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "socket closed"); + } + return -1; + } + + ia = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port); + isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port); + (*env)->SetObjectArrayElement(env, isaa, 0, isa); + + return newfd; +} + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: waitForNewConnection + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_waitForNewConnection + (JNIEnv *env, jclass clazz, jint fd, jint timeout) { + int rv; + + rv = NET_Timeout(fd, timeout); + if (rv == 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + "Accept timed out"); + } else if (rv == -1) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed"); + } else if (rv == -2) { + JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", + "operation interrupted"); + } +} + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: available0 + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_available0 + (JNIEnv *env, jclass clazz, jint fd) { + jint available = -1; + + if ((ioctlsocket(fd, FIONREAD, &available)) == SOCKET_ERROR) { + NET_ThrowNew(env, WSAGetLastError(), "socket available"); + } + + return available; +} + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: close0 + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_close0 + (JNIEnv *env, jclass clazz, jint fd) { + NET_SocketClose(fd); +} + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: shutdown0 + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_shutdown0 + (JNIEnv *env, jclass clazz, jint fd, jint howto) { + shutdown(fd, howto); +} + + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: setIntOption + * Signature: (III)V + */ +JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_setIntOption + (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value) { + + int level, opt; + struct linger linger; + char *parg; + int arglen; + + if (NET_MapSocketOption(cmd, &level, &opt) < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "Invalid option"); + return; + } + + if (opt == java_net_SocketOptions_SO_LINGER) { + parg = (char *)&linger; + arglen = sizeof(linger); + if (value >= 0) { + linger.l_onoff = 1; + linger.l_linger = (unsigned short)value; + } else { + linger.l_onoff = 0; + linger.l_linger = 0; + } + } else { + parg = (char *)&value; + arglen = sizeof(value); + } + + if (NET_SetSockOpt(fd, level, opt, parg, arglen) < 0) { + NET_ThrowNew(env, WSAGetLastError(), "setsockopt"); + } +} + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: getIntOption + * Signature: (II)I + */ +JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_getIntOption + (JNIEnv *env, jclass clazz, jint fd, jint cmd) { + + int level, opt; + int result=0; + struct linger linger; + char *arg; + int arglen; + + if (NET_MapSocketOption(cmd, &level, &opt) < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "Unsupported socket option"); + return -1; + } + + if (opt == java_net_SocketOptions_SO_LINGER) { + arg = (char *)&linger; + arglen = sizeof(linger); + } else { + arg = (char *)&result; + arglen = sizeof(result); + } + + if (NET_GetSockOpt(fd, level, opt, arg, &arglen) < 0) { + NET_ThrowNew(env, WSAGetLastError(), "getsockopt"); + return -1; + } + + if (opt == java_net_SocketOptions_SO_LINGER) + return linger.l_onoff ? linger.l_linger : -1; + else + return result; +} + + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: sendOOB + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_sendOOB + (JNIEnv *env, jclass clazz, jint fd, jint data) { + jint n; + unsigned char d = (unsigned char) data & 0xff; + + n = send(fd, (char *)&data, 1, MSG_OOB); + if (n == JVM_IO_ERR) { + NET_ThrowNew(env, WSAGetLastError(), "send"); + } else if (n == JVM_IO_INTR) { + JNU_ThrowByName(env, "java/io/InterruptedIOException", 0); + } +} + +/* + * Class: java_net_DualStackPlainSocketImpl + * Method: configureBlocking + * Signature: (IZ)V + */ +JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_configureBlocking + (JNIEnv *env, jclass clazz, jint fd, jboolean blocking) { + u_long arg; + int result; + + if (blocking == JNI_TRUE) { + arg = SET_BLOCKING; // 0 + } else { + arg = SET_NONBLOCKING; // 1 + } + + result = ioctlsocket(fd, FIONBIO, &arg); + if (result == SOCKET_ERROR) { + NET_ThrowNew(env, WSAGetLastError(), "configureBlocking"); + } +} |