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