blob: 3b1994dfc4e124ba0b213f0698ed0aa55595dc6a [file] [log] [blame]
/*
* Copyright (c) 1997, 2015, 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 <ws2tcpip.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys/types.h>
#ifndef IPTOS_TOS_MASK
#define IPTOS_TOS_MASK 0x1e
#endif
#ifndef IPTOS_PREC_MASK
#define IPTOS_PREC_MASK 0xe0
#endif
#include "java_net_TwoStacksPlainDatagramSocketImpl.h"
#include "java_net_SocketOptions.h"
#include "java_net_NetworkInterface.h"
#include "NetworkInterface.h"
#include "jvm.h"
#include "jni_util.h"
#include "net_util.h"
#define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000)
#define IN_MULTICAST(i) IN_CLASSD(i)
/************************************************************************
* TwoStacksPlainDatagramSocketImpl
*/
static jfieldID IO_fd_fdID;
static jfieldID pdsi_trafficClassID;
jfieldID pdsi_fdID;
jfieldID pdsi_fd1ID;
jfieldID pdsi_fduseID;
jfieldID pdsi_lastfdID;
jfieldID pdsi_timeoutID;
jfieldID pdsi_localPortID;
jfieldID pdsi_connected;
static jclass ia4_clazz;
static jmethodID ia4_ctor;
static CRITICAL_SECTION sizeCheckLock;
/* Windows OS version is XP or better */
static int xp_or_later = 0;
/* Windows OS version is Windows 2000 or better */
static int w2k_or_later = 0;
/*
* Notes about UDP/IPV6 on Windows (XP and 2003 server):
*
* fd always points to the IPv4 fd, and fd1 points to the IPv6 fd.
* Both fds are used when we bind to a wild-card address. When a specific
* address is used, only one of them is used.
*/
/*
* Returns a java.lang.Integer based on 'i'
*/
jobject createInteger(JNIEnv *env, int i) {
static jclass i_class;
static jmethodID i_ctrID;
static jfieldID i_valueID;
if (i_class == NULL) {
jclass c = (*env)->FindClass(env, "java/lang/Integer");
CHECK_NULL_RETURN(c, NULL);
i_ctrID = (*env)->GetMethodID(env, c, "<init>", "(I)V");
CHECK_NULL_RETURN(i_ctrID, NULL);
i_class = (*env)->NewGlobalRef(env, c);
CHECK_NULL_RETURN(i_class, NULL);
}
return ( (*env)->NewObject(env, i_class, i_ctrID, i) );
}
/*
* Returns a java.lang.Boolean based on 'b'
*/
jobject createBoolean(JNIEnv *env, int b) {
static jclass b_class;
static jmethodID b_ctrID;
static jfieldID b_valueID;
if (b_class == NULL) {
jclass c = (*env)->FindClass(env, "java/lang/Boolean");
CHECK_NULL_RETURN(c, NULL);
b_ctrID = (*env)->GetMethodID(env, c, "<init>", "(Z)V");
CHECK_NULL_RETURN(b_ctrID, NULL);
b_class = (*env)->NewGlobalRef(env, c);
CHECK_NULL_RETURN(b_class, NULL);
}
return( (*env)->NewObject(env, b_class, b_ctrID, (jboolean)(b!=0)) );
}
static int getFD(JNIEnv *env, jobject this) {
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
if (fdObj == NULL) {
return -1;
}
return (*env)->GetIntField(env, fdObj, IO_fd_fdID);
}
static int getFD1(JNIEnv *env, jobject this) {
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fd1ID);
if (fdObj == NULL) {
return -1;
}
return (*env)->GetIntField(env, fdObj, IO_fd_fdID);
}
/*
* This function returns JNI_TRUE if the datagram size exceeds the underlying
* provider's ability to send to the target address. The following OS
* oddities have been observed :-
*
* 1. On Windows 95/98 if we try to send a datagram > 12k to an application
* on the same machine then the send will fail silently.
*
* 2. On Windows ME if we try to send a datagram > supported by underlying
* provider then send will not return an error.
*
* 3. On Windows NT/2000 if we exceeds the maximum size then send will fail
* with WSAEADDRNOTAVAIL.
*
* 4. On Windows 95/98 if we exceed the maximum size when sending to
* another machine then WSAEINVAL is returned.
*
*/
jboolean exceedSizeLimit(JNIEnv *env, jint fd, jint addr, jint size)
{
#define DEFAULT_MSG_SIZE 65527
static jboolean initDone;
static jboolean is95or98;
static int maxmsg;
typedef struct _netaddr { /* Windows 95/98 only */
unsigned long addr;
struct _netaddr *next;
} netaddr;
static netaddr *addrList;
netaddr *curr;
/*
* First time we are called we must determine which OS this is and also
* get the maximum size supported by the underlying provider.
*
* In addition on 95/98 we must enumerate our IP addresses.
*/
if (!initDone) {
EnterCriticalSection(&sizeCheckLock);
if (initDone) {
/* another thread got there first */
LeaveCriticalSection(&sizeCheckLock);
} else {
OSVERSIONINFO ver;
int len;
/*
* Step 1: Determine which OS this is.
*/
ver.dwOSVersionInfoSize = sizeof(ver);
GetVersionEx(&ver);
is95or98 = JNI_FALSE;
if (ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
ver.dwMajorVersion == 4 &&
(ver.dwMinorVersion == 0 || ver.dwMinorVersion == 10)) {
is95or98 = JNI_TRUE;
}
/*
* Step 2: Determine the maximum datagram supported by the
* underlying provider. On Windows 95 if winsock hasn't been
* upgraded (ie: unsupported configuration) then we assume
* the default 64k limit.
*/
len = sizeof(maxmsg);
if (NET_GetSockOpt(fd, SOL_SOCKET, SO_MAX_MSG_SIZE, (char *)&maxmsg, &len) < 0) {
maxmsg = DEFAULT_MSG_SIZE;
}
/*
* Step 3: On Windows 95/98 then enumerate the IP addresses on
* this machine. This is neccesary because we need to check if the
* datagram is being sent to an application on the same machine.
*/
if (is95or98) {
char hostname[255];
struct hostent *hp;
if (gethostname(hostname, sizeof(hostname)) == -1) {
LeaveCriticalSection(&sizeCheckLock);
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Unable to obtain hostname");
return JNI_TRUE;
}
hp = (struct hostent *)gethostbyname(hostname);
if (hp != NULL) {
struct in_addr **addrp = (struct in_addr **) hp->h_addr_list;
while (*addrp != (struct in_addr *) 0) {
curr = (netaddr *)malloc(sizeof(netaddr));
if (curr == NULL) {
while (addrList != NULL) {
curr = addrList->next;
free(addrList);
addrList = curr;
}
LeaveCriticalSection(&sizeCheckLock);
JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
return JNI_TRUE;
}
curr->addr = htonl((*addrp)->S_un.S_addr);
curr->next = addrList;
addrList = curr;
addrp++;
}
}
}
/*
* Step 4: initialization is done so set flag and unlock cs
*/
initDone = JNI_TRUE;
LeaveCriticalSection(&sizeCheckLock);
}
}
/*
* Now examine the size of the datagram :-
*
* (a) If exceeds size of service provider return 'false' to indicate that
* we exceed the limit.
* (b) If not 95/98 then return 'true' to indicate that the size is okay.
* (c) On 95/98 if the size is <12k we are okay.
* (d) On 95/98 if size > 12k then check if the destination is the current
* machine.
*/
if (size > maxmsg) { /* step (a) */
return JNI_TRUE;
}
if (!is95or98) { /* step (b) */
return JNI_FALSE;
}
if (size <= 12280) { /* step (c) */
return JNI_FALSE;
}
/* step (d) */
if ((addr & 0x7f000000) == 0x7f000000) {
return JNI_TRUE;
}
curr = addrList;
while (curr != NULL) {
if (curr->addr == addr) {
return JNI_TRUE;
}
curr = curr->next;
}
return JNI_FALSE;
}
/*
* Return JNI_TRUE if this Windows edition supports ICMP Port Unreachable
*/
__inline static jboolean supportPortUnreachable() {
static jboolean initDone;
static jboolean portUnreachableSupported;
if (!initDone) {
OSVERSIONINFO ver;
ver.dwOSVersionInfoSize = sizeof(ver);
GetVersionEx(&ver);
if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT && ver.dwMajorVersion >= 5) {
portUnreachableSupported = JNI_TRUE;
} else {
portUnreachableSupported = JNI_FALSE;
}
initDone = JNI_TRUE;
}
return portUnreachableSupported;
}
/*
* 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, jobject this, 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);
memset((char *)&rmtaddr, 0, sizeof(rmtaddr));
/*
* A no-op if this OS doesn't support it.
*/
if (!supportPortUnreachable()) {
return JNI_FALSE;
}
/*
* 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_TwoStacksPlainDatagramSocketImpl
* Method: init
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_init(JNIEnv *env, jclass cls) {
OSVERSIONINFO ver;
int version;
ver.dwOSVersionInfoSize = sizeof(ver);
GetVersionEx(&ver);
version = ver.dwMajorVersion * 10 + ver.dwMinorVersion;
xp_or_later = (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) && (version >= 51);
w2k_or_later = (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) && (version >= 50);
/* get fieldIDs */
pdsi_fdID = (*env)->GetFieldID(env, cls, "fd", "Ljava/io/FileDescriptor;");
CHECK_NULL(pdsi_fdID);
pdsi_fd1ID = (*env)->GetFieldID(env, cls, "fd1", "Ljava/io/FileDescriptor;");
CHECK_NULL(pdsi_fd1ID);
pdsi_timeoutID = (*env)->GetFieldID(env, cls, "timeout", "I");
CHECK_NULL(pdsi_timeoutID);
pdsi_fduseID = (*env)->GetFieldID(env, cls, "fduse", "I");
CHECK_NULL(pdsi_fduseID);
pdsi_lastfdID = (*env)->GetFieldID(env, cls, "lastfd", "I");
CHECK_NULL(pdsi_lastfdID);
pdsi_trafficClassID = (*env)->GetFieldID(env, cls, "trafficClass", "I");
CHECK_NULL(pdsi_trafficClassID);
pdsi_localPortID = (*env)->GetFieldID(env, cls, "localPort", "I");
CHECK_NULL(pdsi_localPortID);
pdsi_connected = (*env)->GetFieldID(env, cls, "connected", "Z");
CHECK_NULL(pdsi_connected);
cls = (*env)->FindClass(env, "java/io/FileDescriptor");
CHECK_NULL(cls);
IO_fd_fdID = NET_GetFileDescriptorID(env);
CHECK_NULL(IO_fd_fdID);
ia4_clazz = (*env)->FindClass(env, "java/net/Inet4Address");
CHECK_NULL(ia4_clazz);
ia4_clazz = (*env)->NewGlobalRef(env, ia4_clazz);
CHECK_NULL(ia4_clazz);
ia4_ctor = (*env)->GetMethodID(env, ia4_clazz, "<init>", "()V");
CHECK_NULL(ia4_ctor);
InitializeCriticalSection(&sizeCheckLock);
}
JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this,
jint port, jobject addressObj,
jboolean exclBind) {
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID);
int fd, fd1, family;
int ipv6_supported = ipv6_available();
SOCKETADDRESS lcladdr;
int lcladdrlen = sizeof(lcladdr);
int address;
memset((char *)&lcladdr, 0, sizeof(lcladdr));
family = getInetAddress_family(env, addressObj);
if (family == IPv6 && !ipv6_supported) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Protocol family not supported");
return;
}
if (IS_NULL(fdObj) || (ipv6_supported && IS_NULL(fd1Obj))) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");
return;
} else {
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
if (ipv6_supported) {
fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID);
}
}
if (IS_NULL(addressObj)) {
JNU_ThrowNullPointerException(env, "argument address");
return;
} else {
address = getInetAddress_addr(env, addressObj);
}
if (NET_InetAddressToSockaddr(env, addressObj, port, (struct sockaddr *)&lcladdr, &lcladdrlen, JNI_FALSE) != 0) {
return;
}
if (ipv6_supported) {
struct ipv6bind v6bind;
v6bind.addr = &lcladdr;
v6bind.ipv4_fd = fd;
v6bind.ipv6_fd = fd1;
if (NET_BindV6(&v6bind, exclBind) != -1) {
/* check if the fds have changed */
if (v6bind.ipv4_fd != fd) {
fd = v6bind.ipv4_fd;
if (fd == -1) {
/* socket is closed. */
(*env)->SetObjectField(env, this, pdsi_fdID, NULL);
} else {
/* socket was re-created */
(*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
}
}
if (v6bind.ipv6_fd != fd1) {
fd1 = v6bind.ipv6_fd;
if (fd1 == -1) {
/* socket is closed. */
(*env)->SetObjectField(env, this, pdsi_fd1ID, NULL);
} else {
/* socket was re-created */
(*env)->SetIntField(env, fd1Obj, IO_fd_fdID, fd1);
}
}
} else {
/* NET_BindV6() closes both sockets upon a failure */
(*env)->SetObjectField(env, this, pdsi_fdID, NULL);
(*env)->SetObjectField(env, this, pdsi_fd1ID, NULL);
NET_ThrowCurrent (env, "Cannot bind");
return;
}
} else {
if (NET_WinBind(fd, (struct sockaddr *)&lcladdr, lcladdrlen, exclBind) == -1) {
if (WSAGetLastError() == WSAEACCES) {
WSASetLastError(WSAEADDRINUSE);
}
NET_ThrowCurrent(env, "Cannot bind");
return;
}
}
if (port == 0) {
if (fd == -1) {
/* must be an IPV6 only socket. */
fd = fd1;
}
if (getsockname(fd, (struct sockaddr *)&lcladdr, &lcladdrlen) == -1) {
NET_ThrowCurrent(env, "JVM_GetSockName");
return;
}
port = ntohs((u_short) GET_PORT (&lcladdr));
}
(*env)->SetIntField(env, this, pdsi_localPortID, port);
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: connect0
* Signature: (Ljava/net/InetAddress;I)V
*/
JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_connect0(JNIEnv *env, jobject this,
jobject address, jint port) {
/* The object's field */
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID);
/* The fdObj'fd */
jint fd=-1, fd1=-1, fdc;
/* The packetAddress address, family and port */
jint addr, family;
SOCKETADDRESS rmtaddr;
int rmtaddrlen;
int ipv6_supported = ipv6_available();
if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
return;
}
if (!IS_NULL(fdObj)) {
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
}
if (!IS_NULL(fd1Obj)) {
fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID);
}
if (IS_NULL(address)) {
JNU_ThrowNullPointerException(env, "address");
return;
}
addr = getInetAddress_addr(env, address);
family = getInetAddress_family(env, address);
if (family == IPv6 && !ipv6_supported) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Protocol family not supported");
return;
}
fdc = family == IPv4? fd: fd1;
if (xp_or_later) {
/* SIO_UDP_CONNRESET fixes a bug introduced in Windows 2000, which
* returns connection reset errors on connected UDP sockets (as well
* as connected sockets). The solution is to only enable this feature
* when the socket is connected
*/
DWORD x1, x2; /* ignored result codes */
int res, t = TRUE;
res = WSAIoctl(fdc,SIO_UDP_CONNRESET,&t,sizeof(t),&x1,sizeof(x1),&x2,0,0);
}
if (NET_InetAddressToSockaddr(env, address, port,(struct sockaddr *)&rmtaddr, &rmtaddrlen, JNI_FALSE) != 0) {
return;
}
if (connect(fdc, (struct sockaddr *)&rmtaddr, sizeof(rmtaddr)) == -1) {
NET_ThrowCurrent(env, "connect");
return;
}
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: disconnect0
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_disconnect0(JNIEnv *env, jobject this, jint family) {
/* The object's field */
jobject fdObj;
/* The fdObj'fd */
jint fd, len;
SOCKETADDRESS addr;
if (family == IPv4) {
fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
len = sizeof (struct sockaddr_in);
} else {
fdObj = (*env)->GetObjectField(env, this, pdsi_fd1ID);
len = sizeof (struct SOCKADDR_IN6);
}
if (IS_NULL(fdObj)) {
/* disconnect doesn't throw any exceptions */
return;
}
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
memset((char *)&addr, 0, len);
connect(fd, (struct sockaddr *)&addr, len);
/*
* use SIO_UDP_CONNRESET
* to disable ICMP port unreachable handling here.
*/
if (xp_or_later) {
DWORD x1 = 0, x2 = 0; /* ignored result codes */
int t = FALSE;
WSAIoctl(fd,SIO_UDP_CONNRESET,&t,sizeof(t),&x1,sizeof(x1),&x2,0,0);
}
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: send
* Signature: (Ljava/net/DatagramPacket;)V
*/
JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_send(JNIEnv *env, jobject this,
jobject packet) {
char BUF[MAX_BUFFER_LEN];
char *fullPacket;
jobject fdObj;
jint fd;
jobject iaObj;
jint address;
jint family;
jint packetBufferOffset, packetBufferLen, packetPort;
jbyteArray packetBuffer;
jboolean connected;
SOCKETADDRESS rmtaddr;
SOCKETADDRESS *addrp = &rmtaddr;
int addrlen = 0;
memset((char *)&rmtaddr, 0, sizeof(rmtaddr));
if (IS_NULL(packet)) {
JNU_ThrowNullPointerException(env, "null packet");
return;
}
iaObj = (*env)->GetObjectField(env, packet, dp_addressID);
packetPort = (*env)->GetIntField(env, packet, dp_portID);
packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
packetBuffer = (jbyteArray)(*env)->GetObjectField(env, packet, dp_bufID);
connected = (*env)->GetBooleanField(env, this, pdsi_connected);
if (IS_NULL(iaObj) || IS_NULL(packetBuffer)) {
JNU_ThrowNullPointerException(env, "null address || null buffer");
return;
}
family = getInetAddress_family(env, iaObj);
if (family == IPv4) {
fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
} else {
if (!ipv6_available()) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Protocol not allowed");
return;
}
fdObj = (*env)->GetObjectField(env, this, pdsi_fd1ID);
}
if (IS_NULL(fdObj)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
return;
}
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
packetBufferLen = (*env)->GetIntField(env, packet, dp_lengthID);
/* Note: the buffer needn't be greater than 65,536 (0xFFFF)...
* the maximum size of an IP packet. Anything bigger is truncated anyway.
*/
if (packetBufferLen > MAX_PACKET_LEN) {
packetBufferLen = MAX_PACKET_LEN;
}
if (connected) {
addrp = 0; /* arg to JVM_Sendto () null in this case */
addrlen = 0;
} else {
if (NET_InetAddressToSockaddr(env, iaObj, packetPort, (struct sockaddr *)&rmtaddr, &addrlen, JNI_FALSE) != 0) {
return;
}
}
if (packetBufferLen > MAX_BUFFER_LEN) {
/*
* On 95/98 if we try to send a datagram >12k to an application
* on the same machine then this will fail silently. Thus we
* catch this situation here so that we can throw an exception
* when this arises.
* On ME if we try to send a datagram with a size greater than
* that supported by the service provider then no error is
* returned.
*/
if (!w2k_or_later) { /* avoid this check on Win 2K or better. Does not work with IPv6.
* Check is not necessary on these OSes */
if (connected) {
address = getInetAddress_addr(env, iaObj);
} else {
address = ntohl(rmtaddr.him4.sin_addr.s_addr);
}
if (exceedSizeLimit(env, fd, address, packetBufferLen)) {
if (!((*env)->ExceptionOccurred(env))) {
NET_ThrowNew(env, WSAEMSGSIZE, "Datagram send failed");
}
return;
}
}
/* When JNI-ifying the JDK's IO routines, we turned
* reads and writes of byte arrays of size greater
* than 2048 bytes into several operations of size 2048.
* This saves a malloc()/memcpy()/free() for big
* buffers. This is OK for file IO and TCP, but that
* strategy violates the semantics of a datagram protocol.
* (one big send) != (several smaller sends). So here
* we *must* alloc the buffer. Note it needn't be bigger
* than 65,536 (0xFFFF) the max size of an IP packet.
* anything bigger is truncated anyway.
*/
fullPacket = (char *)malloc(packetBufferLen);
if (!fullPacket) {
JNU_ThrowOutOfMemoryError(env, "Send buf native heap allocation failed");
return;
}
} else {
fullPacket = &(BUF[0]);
}
(*env)->GetByteArrayRegion(env, packetBuffer, packetBufferOffset, packetBufferLen,
(jbyte *)fullPacket);
switch (sendto(fd, fullPacket, packetBufferLen, 0,
(struct sockaddr *)addrp, addrlen)) {
case JVM_IO_ERR:
NET_ThrowCurrent(env, "Datagram send failed");
break;
case JVM_IO_INTR:
JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
"operation interrupted");
}
if (packetBufferLen > MAX_BUFFER_LEN) {
free(fullPacket);
}
}
/*
* check which socket was last serviced when there was data on both sockets.
* Only call this if sure that there is data on both sockets.
*/
static int checkLastFD (JNIEnv *env, jobject this, int fd, int fd1) {
int nextfd, lastfd = (*env)->GetIntField(env, this, pdsi_lastfdID);
if (lastfd == -1) {
/* arbitrary. Choose fd */
(*env)->SetIntField(env, this, pdsi_lastfdID, fd);
return fd;
} else {
if (lastfd == fd) {
nextfd = fd1;
} else {
nextfd = fd;
}
(*env)->SetIntField(env, this, pdsi_lastfdID, nextfd);
return nextfd;
}
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: peek
* Signature: (Ljava/net/InetAddress;)I
*/
JNIEXPORT jint JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_peek(JNIEnv *env, jobject this,
jobject addressObj) {
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
jint fd;
/* The address and family fields of addressObj */
jint address, family;
int n;
struct sockaddr_in remote_addr;
jint remote_addrsize = sizeof (remote_addr);
char buf[1];
BOOL retry;
jlong prevTime = 0;
if (IS_NULL(fdObj)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
return -1;
} else {
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
if (fd < 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"socket closed");
return -1;
}
}
if (IS_NULL(addressObj)) {
JNU_ThrowNullPointerException(env, "Null address in peek()");
} else {
address = getInetAddress_addr(env, addressObj);
/* We only handle IPv4 for now. Will support IPv6 once its in the os */
family = AF_INET;
}
do {
retry = FALSE;
/*
* If a timeout has been specified then we select on the socket
* waiting for a read event or a timeout.
*/
if (timeout) {
int ret;
prevTime = JVM_CurrentTimeMillis(env, 0);
ret = NET_Timeout (fd, timeout);
if (ret == 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
"Peek timed out");
return ret;
} else if (ret == JVM_IO_ERR) {
NET_ThrowCurrent(env, "timeout in datagram socket peek");
return ret;
} else if (ret == JVM_IO_INTR) {
JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
"operation interrupted");
return ret;
}
}
/* now try the peek */
n = recvfrom(fd, buf, 1, MSG_PEEK,
(struct sockaddr *)&remote_addr, &remote_addrsize);
if (n == JVM_IO_ERR) {
if (WSAGetLastError() == WSAECONNRESET) {
jboolean connected;
/*
* 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, this, fd);
connected = (*env)->GetBooleanField(env, this, pdsi_connected);
if (connected) {
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
"ICMP Port Unreachable");
return 0;
}
/*
* If a timeout was specified then we need to adjust it because
* we may have used up some of the timeout befor the icmp port
* unreachable arrived.
*/
if (timeout) {
jlong newTime = JVM_CurrentTimeMillis(env, 0);
timeout -= (jint)(newTime - prevTime);
if (timeout <= 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
"Receive timed out");
return 0;
}
prevTime = newTime;
}
/* Need to retry the recv */
retry = TRUE;
}
}
} while (retry);
if (n == JVM_IO_ERR && WSAGetLastError() != WSAEMSGSIZE) {
NET_ThrowCurrent(env, "Datagram peek failed");
return 0;
}
if (n == JVM_IO_INTR) {
JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", 0);
return 0;
}
setInetAddress_addr(env, addressObj, ntohl(remote_addr.sin_addr.s_addr));
setInetAddress_family(env, addressObj, IPv4);
/* return port */
return ntohs(remote_addr.sin_port);
}
JNIEXPORT jint JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_peekData(JNIEnv *env, jobject this,
jobject packet) {
char BUF[MAX_BUFFER_LEN];
char *fullPacket;
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID);
jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
jbyteArray packetBuffer;
jint packetBufferOffset, packetBufferLen;
int fd, fd1, fduse, nsockets=0, errorCode;
int port;
int checkBoth = 0;
int n;
SOCKETADDRESS remote_addr;
jint remote_addrsize=sizeof(remote_addr);
BOOL retry;
jlong prevTime = 0;
if (!IS_NULL(fdObj)) {
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
if (fd < 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"socket closed");
return -1;
}
nsockets = 1;
}
if (!IS_NULL(fd1Obj)) {
fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID);
if (fd1 < 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"socket closed");
return -1;
}
nsockets ++;
}
switch (nsockets) {
case 0:
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"socket closed");
return -1;
case 1:
if (!IS_NULL(fdObj)) {
fduse = fd;
} else {
fduse = fd1;
}
break;
case 2:
checkBoth = TRUE;
break;
}
if (IS_NULL(packet)) {
JNU_ThrowNullPointerException(env, "packet");
return -1;
}
packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
if (IS_NULL(packetBuffer)) {
JNU_ThrowNullPointerException(env, "packet buffer");
return -1;
}
packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID);
if (packetBufferLen > MAX_BUFFER_LEN) {
/* When JNI-ifying the JDK's IO routines, we turned
* read's and write's of byte arrays of size greater
* than 2048 bytes into several operations of size 2048.
* This saves a malloc()/memcpy()/free() for big
* buffers. This is OK for file IO and TCP, but that
* strategy violates the semantics of a datagram protocol.
* (one big send) != (several smaller sends). So here
* we *must* alloc the buffer. Note it needn't be bigger
* than 65,536 (0xFFFF) the max size of an IP packet.
* anything bigger is truncated anyway.
*/
fullPacket = (char *)malloc(packetBufferLen);
if (!fullPacket) {
JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
return -1;
}
} else {
fullPacket = &(BUF[0]);
}
do {
int ret;
retry = FALSE;
/*
* If a timeout has been specified then we select on the socket
* waiting for a read event or a timeout.
*/
if (checkBoth) {
int t = timeout == 0 ? -1: timeout;
prevTime = JVM_CurrentTimeMillis(env, 0);
ret = NET_Timeout2 (fd, fd1, t, &fduse);
/* all subsequent calls to recv() or select() will use the same fd
* for this call to peek() */
if (ret <= 0) {
if (ret == 0) {
JNU_ThrowByName(env,JNU_JAVANETPKG "SocketTimeoutException",
"Peek timed out");
} else if (ret == JVM_IO_ERR) {
NET_ThrowCurrent(env, "timeout in datagram socket peek");
} else if (ret == JVM_IO_INTR) {
JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
"operation interrupted");
}
if (packetBufferLen > MAX_BUFFER_LEN) {
free(fullPacket);
}
return -1;
}
if (ret == 2) {
fduse = checkLastFD (env, this, fd, fd1);
}
checkBoth = FALSE;
} else if (timeout) {
if (prevTime == 0) {
prevTime = JVM_CurrentTimeMillis(env, 0);
}
ret = NET_Timeout (fduse, timeout);
if (ret <= 0) {
if (ret == 0) {
JNU_ThrowByName(env,JNU_JAVANETPKG "SocketTimeoutException",
"Receive timed out");
} else if (ret == JVM_IO_ERR) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
} else if (ret == JVM_IO_INTR) {
JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
"operation interrupted");
}
if (packetBufferLen > MAX_BUFFER_LEN) {
free(fullPacket);
}
return -1;
}
}
/* receive the packet */
n = recvfrom(fduse, fullPacket, packetBufferLen, MSG_PEEK,
(struct sockaddr *)&remote_addr, &remote_addrsize);
port = (int) ntohs ((u_short) GET_PORT((SOCKETADDRESS *)&remote_addr));
if (n == JVM_IO_ERR) {
if (WSAGetLastError() == WSAECONNRESET) {
jboolean connected;
/*
* 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, this, fduse);
connected = (*env)->GetBooleanField(env, this, pdsi_connected);
if (connected) {
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
"ICMP Port Unreachable");
if (packetBufferLen > MAX_BUFFER_LEN) {
free(fullPacket);
}
return -1;
}
/*
* If a timeout was specified then we need to adjust it because
* we may have used up some of the timeout befor the icmp port
* unreachable arrived.
*/
if (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);
/* truncate the data if the packet's length is too small */
if (n > packetBufferLen) {
n = packetBufferLen;
}
if (n < 0) {
errorCode = WSAGetLastError();
/* check to see if it's because the buffer was too small */
if (errorCode == 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..
*/
n = packetBufferLen;
} else {
/* failure */
(*env)->SetIntField(env, packet, dp_lengthID, 0);
}
}
if (n == -1) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");
} else if (n == -2) {
JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
"operation interrupted");
} else if (n < 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, packet, dp_addressID);
if (packetAddress != NULL) {
if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)
&remote_addr, packetAddress)) {
/* force a new InetAddress to be created */
packetAddress = NULL;
}
}
if (packetAddress == NULL) {
packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *)
&remote_addr, &port);
/* stuff the new Inetaddress in the packet */
(*env)->SetObjectField(env, packet, dp_addressID, packetAddress);
}
/* populate the packet */
(*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n,
(jbyte *)fullPacket);
(*env)->SetIntField(env, packet, dp_portID, port);
(*env)->SetIntField(env, packet, dp_lengthID, n);
}
/* make sure receive() picks up the right fd */
(*env)->SetIntField(env, this, pdsi_fduseID, fduse);
if (packetBufferLen > MAX_BUFFER_LEN) {
free(fullPacket);
}
return port;
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: receive
* Signature: (Ljava/net/DatagramPacket;)V
*/
JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_receive0(JNIEnv *env, jobject this,
jobject packet) {
char BUF[MAX_BUFFER_LEN];
char *fullPacket;
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID);
jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
jbyteArray packetBuffer;
jint packetBufferOffset, packetBufferLen;
int ipv6_supported = ipv6_available();
/* as a result of the changes for ipv6, peek() or peekData()
* must be called prior to receive() so that fduse can be set.
*/
int fd, fd1, fduse, errorCode;
int n, nsockets=0;
SOCKETADDRESS remote_addr;
jint remote_addrsize=sizeof(remote_addr);
BOOL retry;
jlong prevTime = 0, selectTime=0;
jboolean connected;
if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
return;
}
if (!IS_NULL(fdObj)) {
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
nsockets ++;
}
if (!IS_NULL(fd1Obj)) {
fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID);
nsockets ++;
}
if (nsockets == 2) { /* need to choose one of them */
/* was fduse set in peek? */
fduse = (*env)->GetIntField(env, this, pdsi_fduseID);
if (fduse == -1) {
/* not set in peek(), must select on both sockets */
int ret, t = (timeout == 0) ? -1: timeout;
ret = NET_Timeout2 (fd, fd1, t, &fduse);
if (ret == 2) {
fduse = checkLastFD (env, this, fd, fd1);
} else if (ret <= 0) {
if (ret == 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
"Receive timed out");
} else if (ret == JVM_IO_ERR) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
} else if (ret == JVM_IO_INTR) {
JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
"operation interrupted");
}
return;
}
}
} else if (!ipv6_supported) {
fduse = fd;
} else if (IS_NULL(fdObj)) {
/* ipv6 supported: and this socket bound to an IPV6 only address */
fduse = fd1;
} else {
/* ipv6 supported: and this socket bound to an IPV4 only address */
fduse = fd;
}
if (IS_NULL(packet)) {
JNU_ThrowNullPointerException(env, "packet");
return;
}
packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
if (IS_NULL(packetBuffer)) {
JNU_ThrowNullPointerException(env, "packet buffer");
return;
}
packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID);
if (packetBufferLen > MAX_BUFFER_LEN) {
/* When JNI-ifying the JDK's IO routines, we turned
* read's and write's of byte arrays of size greater
* than 2048 bytes into several operations of size 2048.
* This saves a malloc()/memcpy()/free() for big
* buffers. This is OK for file IO and TCP, but that
* strategy violates the semantics of a datagram protocol.
* (one big send) != (several smaller sends). So here
* we *must* alloc the buffer. Note it needn't be bigger
* than 65,536 (0xFFFF) the max size of an IP packet.
* anything bigger is truncated anyway.
*/
fullPacket = (char *)malloc(packetBufferLen);
if (!fullPacket) {
JNU_ThrowOutOfMemoryError(env, "Receive buf native heap allocation failed");
return;
}
} else {
fullPacket = &(BUF[0]);
}
/*
* If this Windows edition supports ICMP port unreachable and if we
* are not connected then we need to know if a timeout has been specified
* and if so we need to pick up the current time. These are required in
* order to implement the semantics of timeout, viz :-
* timeout set to t1 but ICMP port unreachable arrives in t2 where
* t2 < t1. In this case we must discard the ICMP packets and then
* wait for the next packet up to a maximum of t1 minus t2.
*/
connected = (*env)->GetBooleanField(env, this, pdsi_connected);
if (supportPortUnreachable() && !connected && timeout &&!ipv6_supported) {
prevTime = JVM_CurrentTimeMillis(env, 0);
}
if (timeout && nsockets == 1) {
int ret;
ret = NET_Timeout(fduse, timeout);
if (ret <= 0) {
if (ret == 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
"Receive timed out");
} else if (ret == JVM_IO_ERR) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
} else if (ret == JVM_IO_INTR) {
JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
"operation interrupted");
}
if (packetBufferLen > MAX_BUFFER_LEN) {
free(fullPacket);
}
return;
}
}
/*
* Loop only if we discarding ICMP port unreachable packets
*/
do {
retry = FALSE;
/* receive the packet */
n = recvfrom(fduse, fullPacket, packetBufferLen, 0,
(struct sockaddr *)&remote_addr, &remote_addrsize);
if (n == JVM_IO_ERR) {
if (WSAGetLastError() == WSAECONNRESET) {
/*
* An icmp port unreachable has been received - consume any other
* outstanding packets.
*/
purgeOutstandingICMP(env, this, fduse);
/*
* If connected throw a PortUnreachableException
*/
if (connected) {
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
"ICMP Port Unreachable");
if (packetBufferLen > MAX_BUFFER_LEN) {
free(fullPacket);
}
return;
}
/*
* If a timeout was specified then we need to adjust it because
* we may have used up some of the timeout before the icmp port
* unreachable arrived.
*/
if (timeout) {
int ret;
jlong newTime = JVM_CurrentTimeMillis(env, 0);
timeout -= (jint)(newTime - prevTime);
prevTime = newTime;
if (timeout <= 0) {
ret = 0;
} else {
ret = NET_Timeout(fduse, timeout);
}
if (ret <= 0) {
if (ret == 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
"Receive timed out");
} else if (ret == JVM_IO_ERR) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
} else if (ret == JVM_IO_INTR) {
JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
"operation interrupted");
}
if (packetBufferLen > MAX_BUFFER_LEN) {
free(fullPacket);
}
return;
}
}
/*
* An ICMP port unreachable was received but we are
* not connected so ignore it.
*/
retry = TRUE;
}
}
} while (retry);
/* truncate the data if the packet's length is too small */
if (n > packetBufferLen) {
n = packetBufferLen;
}
if (n < 0) {
errorCode = WSAGetLastError();
/* check to see if it's because the buffer was too small */
if (errorCode == 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..
*/
n = packetBufferLen;
} else {
/* failure */
(*env)->SetIntField(env, packet, dp_lengthID, 0);
}
}
if (n == -1) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");
} else if (n == -2) {
JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
"operation interrupted");
} else if (n < 0) {
NET_ThrowCurrent(env, "Datagram receive failed");
} else {
int port = 0;
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, packet, dp_addressID);
if (packetAddress != NULL) {
if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&remote_addr, packetAddress)) {
/* force a new InetAddress to be created */
packetAddress = NULL;
}
}
if (packetAddress == NULL) {
packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *)&remote_addr, &port);
/* stuff the new Inetaddress in the packet */
(*env)->SetObjectField(env, packet, dp_addressID, packetAddress);
} else {
/* only get the new port number */
port = NET_GetPortFromSockaddr((struct sockaddr *)&remote_addr);
}
/* populate the packet */
(*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n,
(jbyte *)fullPacket);
(*env)->SetIntField(env, packet, dp_portID, port);
(*env)->SetIntField(env, packet, dp_lengthID, n);
}
if (packetBufferLen > MAX_BUFFER_LEN) {
free(fullPacket);
}
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: datagramSocketCreate
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_datagramSocketCreate(JNIEnv *env,
jobject this) {
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID);
int fd, fd1;
int t = TRUE;
DWORD x1, x2; /* ignored result codes */
int ipv6_supported = ipv6_available();
int arg = -1;
if (IS_NULL(fdObj) || (ipv6_supported && IS_NULL(fd1Obj))) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
return;
} else {
fd = (int) socket (AF_INET, SOCK_DGRAM, 0);
}
if (fd == JVM_IO_ERR) {
NET_ThrowCurrent(env, "Socket creation failed");
return;
}
SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE);
(*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
NET_SetSockOpt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&t, sizeof(BOOL));
if (ipv6_supported) {
/* SIO_UDP_CONNRESET fixes a bug introduced in Windows 2000, which
* returns connection reset errors un connected 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);
t = TRUE;
fd1 = socket (AF_INET6, SOCK_DGRAM, 0);
if (fd1 == JVM_IO_ERR) {
NET_ThrowCurrent(env, "Socket creation failed");
return;
}
NET_SetSockOpt(fd1, SOL_SOCKET, SO_BROADCAST, (char*)&t, sizeof(BOOL));
t = FALSE;
WSAIoctl(fd1,SIO_UDP_CONNRESET,&t,sizeof(t),&x1,sizeof(x1),&x2,0,0);
(*env)->SetIntField(env, fd1Obj, IO_fd_fdID, fd1);
SetHandleInformation((HANDLE)(UINT_PTR)fd1, HANDLE_FLAG_INHERIT, FALSE);
} else {
/* drop the second fd */
(*env)->SetObjectField(env, this, pdsi_fd1ID, NULL);
}
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: datagramSocketClose
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_datagramSocketClose(JNIEnv *env,
jobject this) {
/*
* REMIND: PUT A LOCK AROUND THIS CODE
*/
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID);
int ipv6_supported = ipv6_available();
int fd=-1, fd1=-1;
if (IS_NULL(fdObj) && (!ipv6_supported || IS_NULL(fd1Obj))) {
return;
}
if (!IS_NULL(fdObj)) {
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
if (fd != -1) {
(*env)->SetIntField(env, fdObj, IO_fd_fdID, -1);
NET_SocketClose(fd);
}
}
if (ipv6_supported && fd1Obj != NULL) {
fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID);
if (fd1 == -1) {
return;
}
(*env)->SetIntField(env, fd1Obj, IO_fd_fdID, -1);
NET_SocketClose(fd1);
}
}
/*
* check the addresses attached to the NetworkInterface object
* and return the first one (of the requested family Ipv4 or Ipv6)
* in *iaddr
*/
static int getInetAddrFromIf (JNIEnv *env, int family, jobject nif, jobject *iaddr)
{
jobjectArray addrArray;
static jfieldID ni_addrsID=0;
jsize len;
jobject addr;
int i;
if (ni_addrsID == NULL ) {
jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
CHECK_NULL_RETURN (c, -1);
ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
"[Ljava/net/InetAddress;");
CHECK_NULL_RETURN (ni_addrsID, -1);
}
addrArray = (*env)->GetObjectField(env, nif, ni_addrsID);
len = (*env)->GetArrayLength(env, addrArray);
/*
* Check that there is at least one address bound to this
* interface.
*/
if (len < 1) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"bad argument for IP_MULTICAST_IF2: No IP addresses bound to interface");
return -1;
}
for (i=0; i<len; i++) {
int fam;
addr = (*env)->GetObjectArrayElement(env, addrArray, i);
fam = getInetAddress_family(env, addr);
if (fam == family) {
*iaddr = addr;
return 0;
}
}
return -1;
}
static int getInet4AddrFromIf (JNIEnv *env, jobject nif, struct in_addr *iaddr)
{
jobject addr;
int ret = getInetAddrFromIf (env, IPv4, nif, &addr);
if (ret == -1) {
return -1;
}
iaddr->s_addr = htonl(getInetAddress_addr(env, addr));
return 0;
}
/* Get the multicasting index from the interface */
static int getIndexFromIf (JNIEnv *env, jobject nif) {
static jfieldID ni_indexID;
if (ni_indexID == NULL) {
jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
CHECK_NULL_RETURN(c, -1);
ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
CHECK_NULL_RETURN(ni_indexID, -1);
}
return (*env)->GetIntField(env, nif, ni_indexID);
}
static int isAdapterIpv6Enabled(JNIEnv *env, int index) {
netif *ifList, *curr;
int ipv6Enabled = 0;
if (getAllInterfacesAndAddresses (env, &ifList) < 0) {
return ipv6Enabled;
}
/* search by index */
curr = ifList;
while (curr != NULL) {
if (index == curr->index) {
break;
}
curr = curr->next;
}
/* if found ipv6Index != 0 then interface is configured with IPV6 */
if ((curr != NULL) && (curr->ipv6Index !=0)) {
ipv6Enabled = 1;
}
/* release the interface list */
free_netif(ifList);
return ipv6Enabled;
}
/*
* Sets the multicast interface.
*
* SocketOptions.IP_MULTICAST_IF (argument is an InetAddress) :-
* IPv4: set outgoing multicast interface using
* IPPROTO_IP/IP_MULTICAST_IF
*
* IPv6: Get the interface to which the
* InetAddress is bound
* and do same as SockOptions.IF_MULTICAST_IF2
*
* SockOptions.IF_MULTICAST_IF2 (argument is a NetworkInterface ) :-
* For each stack:
* IPv4: Obtain IP address bound to network interface
* (NetworkInterface.addres[0])
* set outgoing multicast interface using
* IPPROTO_IP/IP_MULTICAST_IF
*
* IPv6: Obtain NetworkInterface.index
* Set outgoing multicast interface using
* IPPROTO_IPV6/IPV6_MULTICAST_IF
*
*/
static void setMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1,
jint opt, jobject value)
{
int ipv6_supported = ipv6_available();
if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
/*
* value is an InetAddress.
* On IPv4 system use IP_MULTICAST_IF socket option
* On IPv6 system get the NetworkInterface that this IP
* address is bound to and use the IPV6_MULTICAST_IF
* option instead of IP_MULTICAST_IF
*/
if (ipv6_supported) {
static jclass ni_class;
if (ni_class == NULL) {
jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
CHECK_NULL(c);
ni_class = (*env)->NewGlobalRef(env, c);
CHECK_NULL(ni_class);
}
value = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, value);
if (value == NULL) {
if (!(*env)->ExceptionOccurred(env)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"bad argument for IP_MULTICAST_IF"
": address not bound to any interface");
}
return;
}
opt = java_net_SocketOptions_IP_MULTICAST_IF2;
} else {
struct in_addr in;
in.s_addr = htonl(getInetAddress_addr(env, value));
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
(const char*)&in, sizeof(in)) < 0) {
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"Error setting socket option");
}
return;
}
}
if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
/*
* value is a NetworkInterface.
* On IPv6 system get the index of the interface and use the
* IPV6_MULTICAST_IF socket option
* On IPv4 system extract addr[0] and use the IP_MULTICAST_IF
* option. For IPv6 both must be done.
*/
if (ipv6_supported) {
static jfieldID ni_indexID;
struct in_addr in;
int index;
if (ni_indexID == NULL) {
jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
CHECK_NULL(c);
ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
CHECK_NULL(ni_indexID);
}
index = (*env)->GetIntField(env, value, ni_indexID);
if ( isAdapterIpv6Enabled(env, index) != 0 ) {
if (setsockopt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_IF,
(const char*)&index, sizeof(index)) < 0) {
if (errno == EINVAL && index > 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"IPV6_MULTICAST_IF failed (interface has IPv4 "
"address only?)");
} else {
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"Error setting socket option");
}
return;
}
}
/* If there are any IPv4 addresses on this interface then
* repeat the operation on the IPv4 fd */
if (getInet4AddrFromIf (env, value, &in) < 0) {
return;
}
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
(const char*)&in, sizeof(in)) < 0) {
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"Error setting socket option");
}
return;
} else {
struct in_addr in;
if (getInet4AddrFromIf (env, value, &in) < 0) {
if ((*env)->ExceptionOccurred(env)) {
return;
}
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"no InetAddress instances of requested type");
return;
}
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
(const char*)&in, sizeof(in)) < 0) {
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"Error setting socket option");
}
return;
}
}
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: socketNativeSetOption
* Signature: (ILjava/lang/Object;)V
*/
JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_socketNativeSetOption(JNIEnv *env,jobject this,
jint opt,jobject value) {
int fd=-1, fd1=-1;
int levelv4 = 0, levelv6 = 0, optnamev4 = 0, optnamev6 = 0, optlen = 0;
union {
int i;
char c;
} optval = { 0 };
int ipv6_supported = ipv6_available();
fd = getFD(env, this);
if (ipv6_supported) {
fd1 = getFD1(env, this);
}
if (fd < 0 && fd1 < 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");
return;
}
if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) ||
(opt == java_net_SocketOptions_IP_MULTICAST_IF2)) {
setMulticastInterface(env, this, fd, fd1, opt, value);
return;
}
/*
* Map the Java level socket option to the platform specific
* level(s) and option name(s).
*/
if (fd1 != -1) {
if (NET_MapSocketOptionV6(opt, &levelv6, &optnamev6)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
return;
}
}
if (fd != -1) {
if (NET_MapSocketOption(opt, &levelv4, &optnamev4)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
return;
}
}
switch (opt) {
case java_net_SocketOptions_SO_SNDBUF :
case java_net_SocketOptions_SO_RCVBUF :
case java_net_SocketOptions_IP_TOS :
{
jclass cls;
jfieldID fid;
cls = (*env)->FindClass(env, "java/lang/Integer");
CHECK_NULL(cls);
fid = (*env)->GetFieldID(env, cls, "value", "I");
CHECK_NULL(fid);
optval.i = (*env)->GetIntField(env, value, fid);
optlen = sizeof(optval.i);
}
break;
case java_net_SocketOptions_SO_REUSEADDR:
case java_net_SocketOptions_SO_BROADCAST:
case java_net_SocketOptions_IP_MULTICAST_LOOP:
{
jclass cls;
jfieldID fid;
jboolean on;
cls = (*env)->FindClass(env, "java/lang/Boolean");
CHECK_NULL(cls);
fid = (*env)->GetFieldID(env, cls, "value", "Z");
CHECK_NULL(fid);
on = (*env)->GetBooleanField(env, value, fid);
optval.i = (on ? 1 : 0);
/*
* setLoopbackMode (true) disables IP_MULTICAST_LOOP rather
* than enabling it.
*/
if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP) {
optval.i = !optval.i;
}
optlen = sizeof(optval.i);
}
break;
default :
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket option not supported by PlainDatagramSocketImp");
break;
}
if (fd1 != -1) {
if (NET_SetSockOpt(fd1, levelv6, optnamev6, (void *)&optval, optlen) < 0) {
NET_ThrowCurrent(env, "setsockopt IPv6");
return;
}
}
if (fd != -1) {
if (NET_SetSockOpt(fd, levelv4, optnamev4, (void *)&optval, optlen) < 0) {
NET_ThrowCurrent(env, "setsockopt");
return;
}
}
}
/*
*
* called by getMulticastInterface to retrieve a NetworkInterface
* configured for IPv4.
* The ipv4Mode parameter, is a closet boolean, which allows for a NULL return,
* or forces the creation of a NetworkInterface object with null data.
* It relates to its calling context in getMulticastInterface.
* ipv4Mode == 1, the context is IPV4 processing only.
* ipv4Mode == 0, the context is IPV6 processing
*
*/
static jobject getIPv4NetworkInterface (JNIEnv *env, jobject this, int fd, jint opt, int ipv4Mode) {
static jclass inet4_class;
static jmethodID inet4_ctrID;
static jclass ni_class; static jmethodID ni_ctrID;
static jfieldID ni_indexID;
static jfieldID ni_addrsID;
jobjectArray addrArray;
jobject addr;
jobject ni;
struct in_addr in;
struct in_addr *inP = &in;
int len = sizeof(struct in_addr);
if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
(char *)inP, &len) < 0) {
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"Error getting socket option");
return NULL;
}
/*
* Construct and populate an Inet4Address
*/
if (inet4_class == NULL) {
jclass c = (*env)->FindClass(env, "java/net/Inet4Address");
CHECK_NULL_RETURN(c, NULL);
inet4_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
CHECK_NULL_RETURN(inet4_ctrID, NULL);
inet4_class = (*env)->NewGlobalRef(env, c);
CHECK_NULL_RETURN(inet4_class, NULL);
}
addr = (*env)->NewObject(env, inet4_class, inet4_ctrID, 0);
CHECK_NULL_RETURN(addr, NULL);
setInetAddress_addr(env, addr, ntohl(in.s_addr));
/*
* For IP_MULTICAST_IF return InetAddress
*/
if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
return addr;
}
/*
* For IP_MULTICAST_IF2 we get the NetworkInterface for
* this address and return it
*/
if (ni_class == NULL) {
jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
CHECK_NULL_RETURN(c, NULL);
ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
CHECK_NULL_RETURN(ni_ctrID, NULL);
ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
CHECK_NULL_RETURN(ni_indexID, NULL);
ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
"[Ljava/net/InetAddress;");
CHECK_NULL_RETURN(ni_addrsID, NULL);
ni_class = (*env)->NewGlobalRef(env, c);
CHECK_NULL_RETURN(ni_class, NULL);
}
ni = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, addr);
if (ni) {
return ni;
}
if (ipv4Mode) {
ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
CHECK_NULL_RETURN(ni, NULL);
(*env)->SetIntField(env, ni, ni_indexID, -1);
addrArray = (*env)->NewObjectArray(env, 1, inet4_class, NULL);
CHECK_NULL_RETURN(addrArray, NULL);
(*env)->SetObjectArrayElement(env, addrArray, 0, addr);
(*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
} else {
ni = NULL;
}
return ni;
}
/*
* Return the multicast interface:
*
* SocketOptions.IP_MULTICAST_IF
* IPv4: Query IPPROTO_IP/IP_MULTICAST_IF
* Create InetAddress
* IP_MULTICAST_IF returns struct ip_mreqn on 2.2
* kernel but struct in_addr on 2.4 kernel
* IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF or
* obtain from impl is Linux 2.2 kernel
* If index == 0 return InetAddress representing
* anyLocalAddress.
* If index > 0 query NetworkInterface by index
* and returns addrs[0]
*
* SocketOptions.IP_MULTICAST_IF2
* IPv4: Query IPPROTO_IP/IP_MULTICAST_IF
* Query NetworkInterface by IP address and
* return the NetworkInterface that the address
* is bound too.
* IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
* (except Linux .2 kernel)
* Query NetworkInterface by index and
* return NetworkInterface.
*/
jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1, jint opt) {
jboolean isIPV4 = !ipv6_available() || fd1 == -1;
/*
* IPv4 implementation
*/
if (isIPV4) {
jobject netObject = NULL; // return is either an addr or a netif
netObject = getIPv4NetworkInterface(env, this, fd, opt, 1);
return netObject;
}
/*
* IPv6 implementation
*/
if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) ||
(opt == java_net_SocketOptions_IP_MULTICAST_IF2)) {
static jclass ni_class;
static jmethodID ni_ctrID;
static jfieldID ni_indexID;
static jfieldID ni_addrsID;
static jclass ia_class;
static jmethodID ia_anyLocalAddressID;
int index;
int len = sizeof(index);
jobjectArray addrArray;
jobject addr;
jobject ni;
{
if (getsockopt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_IF,
(char*)&index, &len) < 0) {
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"Error getting socket option");
return NULL;
}
}
if (ni_class == NULL) {
jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
CHECK_NULL_RETURN(c, NULL);
ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
CHECK_NULL_RETURN(ni_ctrID, NULL);
ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
CHECK_NULL_RETURN(ni_indexID, NULL);
ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
"[Ljava/net/InetAddress;");
CHECK_NULL_RETURN(ni_addrsID, NULL);
ia_class = (*env)->FindClass(env, "java/net/InetAddress");
CHECK_NULL_RETURN(ia_class, NULL);
ia_class = (*env)->NewGlobalRef(env, ia_class);
CHECK_NULL_RETURN(ia_class, NULL);
ia_anyLocalAddressID = (*env)->GetStaticMethodID(env,
ia_class,
"anyLocalAddress",
"()Ljava/net/InetAddress;");
CHECK_NULL_RETURN(ia_anyLocalAddressID, NULL);
ni_class = (*env)->NewGlobalRef(env, c);
CHECK_NULL_RETURN(ni_class, NULL);
}
/*
* If multicast to a specific interface then return the
* interface (for IF2) or the any address on that interface
* (for IF).
*/
if (index > 0) {
ni = Java_java_net_NetworkInterface_getByIndex0(env, ni_class,
index);
if (ni == NULL) {
char errmsg[255];
sprintf(errmsg,
"IPV6_MULTICAST_IF returned index to unrecognized interface: %d",
index);
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", errmsg);
return NULL;
}
/*
* For IP_MULTICAST_IF2 return the NetworkInterface
*/
if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
return ni;
}
/*
* For IP_MULTICAST_IF return addrs[0]
*/
addrArray = (*env)->GetObjectField(env, ni, ni_addrsID);
if ((*env)->GetArrayLength(env, addrArray) < 1) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"IPV6_MULTICAST_IF returned interface without IP bindings");
return NULL;
}
addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
return addr;
} else if (index == 0) { // index == 0 typically means IPv6 not configured on the interfaces
// falling back to treat interface as configured for IPv4
jobject netObject = NULL;
netObject = getIPv4NetworkInterface(env, this, fd, opt, 0);
if (netObject != NULL) {
return netObject;
}
}
/*
* Multicast to any address - return anyLocalAddress
* or a NetworkInterface with addrs[0] set to anyLocalAddress
*/
addr = (*env)->CallStaticObjectMethod(env, ia_class, ia_anyLocalAddressID,
NULL);
if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
return addr;
}
ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
CHECK_NULL_RETURN(ni, NULL);
(*env)->SetIntField(env, ni, ni_indexID, -1);
addrArray = (*env)->NewObjectArray(env, 1, ia_class, NULL);
CHECK_NULL_RETURN(addrArray, NULL);
(*env)->SetObjectArrayElement(env, addrArray, 0, addr);
(*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
return ni;
}
return NULL;
}
/*
* Returns relevant info as a jint.
*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: socketGetOption
* Signature: (I)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_socketGetOption(JNIEnv *env, jobject this,
jint opt) {
int fd=-1, fd1=-1;
int level, optname, optlen;
union {
int i;
} optval = {0};
int ipv6_supported = ipv6_available();
fd = getFD(env, this);
if (ipv6_supported) {
fd1 = getFD1(env, this);
}
if (fd < 0 && fd1 < 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
return NULL;
}
/*
* Handle IP_MULTICAST_IF separately
*/
if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
return getMulticastInterface(env, this, fd, fd1, opt);
}
/*
* Map the Java level socket option to the platform specific
* level and option name.
*/
if (NET_MapSocketOption(opt, &level, &optname)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
return NULL;
}
if (fd == -1) {
if (NET_MapSocketOptionV6(opt, &level, &optname)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
return NULL;
}
fd = fd1; /* must be IPv6 only */
}
optlen = sizeof(optval.i);
if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) {
char tmpbuf[255];
int size = 0;
char errmsg[255 + 31];
getErrorString(errno, tmpbuf, sizeof(tmpbuf));
sprintf(errmsg, "error getting socket option: %s", tmpbuf);
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", errmsg);
return NULL;
}
switch (opt) {
case java_net_SocketOptions_SO_BROADCAST:
case java_net_SocketOptions_SO_REUSEADDR:
return createBoolean(env, optval.i);
case java_net_SocketOptions_IP_MULTICAST_LOOP:
/* getLoopbackMode() returns true if IP_MULTICAST_LOOP is disabled */
return createBoolean(env, !optval.i);
case java_net_SocketOptions_SO_SNDBUF:
case java_net_SocketOptions_SO_RCVBUF:
case java_net_SocketOptions_IP_TOS:
return createInteger(env, optval.i);
default :
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket option not supported by TwoStacksPlainDatagramSocketImpl");
return NULL;
}
}
/*
* Returns local address of the socket.
*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: socketLocalAddress
* Signature: (I)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_socketLocalAddress(JNIEnv *env, jobject this,
jint family) {
int fd=-1, fd1=-1;
SOCKETADDRESS him;
int len = 0;
int port;
jobject iaObj;
int ipv6_supported = ipv6_available();
fd = getFD(env, this);
if (ipv6_supported) {
fd1 = getFD1(env, this);
}
if (fd < 0 && fd1 < 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
return NULL;
}
/* find out local IP address */
len = sizeof (struct sockaddr_in);
/* family==-1 when socket is not connected */
if ((family == IPv6) || (family == -1 && fd == -1)) {
fd = fd1; /* must be IPv6 only */
len = sizeof (struct SOCKADDR_IN6);
}
if (fd == -1) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
return NULL;
}
if (getsockname(fd, (struct sockaddr *)&him, &len) == -1) {
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"Error getting socket name");
return NULL;
}
iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port);
return iaObj;
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: setTimeToLive
* Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_setTimeToLive(JNIEnv *env, jobject this,
jint ttl) {
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID);
int fd = -1, fd1 = -1;
int ittl = (int)ttl;
if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
return;
} else {
if (!IS_NULL(fdObj)) {
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
}
if (!IS_NULL(fd1Obj)) {
fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID);
}
}
/* setsockopt to be correct ttl */
if (fd >= 0) {
if (NET_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl,
sizeof (ittl)) < 0) {
NET_ThrowCurrent(env, "set IP_MULTICAST_TTL failed");
}
}
if (fd1 >= 0) {
if (NET_SetSockOpt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&ittl,
sizeof(ittl)) <0) {
NET_ThrowCurrent(env, "set IPV6_MULTICAST_HOPS failed");
}
}
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: setTTL
* Signature: (B)V
*/
JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_setTTL(JNIEnv *env, jobject this,
jbyte ttl) {
Java_java_net_TwoStacksPlainDatagramSocketImpl_setTimeToLive(env, this,
(jint)ttl & 0xFF);
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: getTimeToLive
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_getTimeToLive(JNIEnv *env, jobject this) {
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID);
int fd = -1, fd1 = -1;
int ttl = 0;
int len = sizeof(ttl);
if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
return -1;
} else {
if (!IS_NULL(fdObj)) {
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
}
if (!IS_NULL(fd1Obj)) {
fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID);
}
}
/* getsockopt of ttl */
if (fd >= 0) {
if (NET_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, &len) < 0) {
NET_ThrowCurrent(env, "get IP_MULTICAST_TTL failed");
return -1;
}
return (jint)ttl;
}
if (fd1 >= 0) {
if (NET_GetSockOpt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char*)&ttl, &len) < 0) {
NET_ThrowCurrent(env, "get IP_MULTICAST_TTL failed");
return -1;
}
return (jint)ttl;
}
return -1;
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: getTTL
* Signature: ()B
*/
JNIEXPORT jbyte JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_getTTL(JNIEnv *env, jobject this) {
int result = Java_java_net_TwoStacksPlainDatagramSocketImpl_getTimeToLive(env, this);
return (jbyte)result;
}
/* join/leave the named group on the named interface, or if no interface specified
* then the interface set with setInterfac(), or the default interface otherwise */
static void mcast_join_leave(JNIEnv *env, jobject this,
jobject iaObj, jobject niObj,
jboolean join)
{
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID);
jint fd = -1, fd1 = -1;
SOCKETADDRESS name;
struct ip_mreq mname;
struct ipv6_mreq mname6;
struct in_addr in;
DWORD ifindex = 0;
int len, family;
int ipv6_supported = ipv6_available();
int cmd ;
memset((char *)&in, 0, sizeof(in));
memset((char *)&name, 0, sizeof(name));
if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
return;
}
if (!IS_NULL(fdObj)) {
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
}
if (ipv6_supported && !IS_NULL(fd1Obj)) {
fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID);
}
if (IS_NULL(iaObj)) {
JNU_ThrowNullPointerException(env, "address");
return;
}
if (NET_InetAddressToSockaddr(env, iaObj, 0, (struct sockaddr *)&name, &len, JNI_FALSE) != 0) {
return;
}
/* Set the multicast group address in the ip_mreq field
* eventually this check should be done by the security manager
*/
family = name.him.sa_family;
if (family == AF_INET) {
int address = name.him4.sin_addr.s_addr;
if (!IN_MULTICAST(ntohl(address))) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "not in multicast");
return;
}
mname.imr_multiaddr.s_addr = address;
if (fd < 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Can't join an IPv4 group on an IPv6 only socket");
return;
}
if (IS_NULL(niObj)) {
len = sizeof (in);
if (NET_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF,
(char *)&in, &len) < 0) {
NET_ThrowCurrent(env, "get IP_MULTICAST_IF failed");
return;
}
mname.imr_interface.s_addr = in.s_addr;
} else {
if (getInet4AddrFromIf (env, niObj, &mname.imr_interface) != 0) {
NET_ThrowCurrent(env, "no Inet4Address associated with interface");
return;
}
}
cmd = join ? IP_ADD_MEMBERSHIP: IP_DROP_MEMBERSHIP;
/* Join the multicast group */
if (NET_SetSockOpt(fd, IPPROTO_IP, cmd, (char *) &mname, sizeof (mname)) < 0) {
if (WSAGetLastError() == WSAENOBUFS) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"IP_ADD_MEMBERSHIP failed (out of hardware filters?)");
} else {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException","error setting options");
}
}
} else /* AF_INET6 */ {
if (ipv6_supported) {
struct in6_addr *address;
address = &name.him6.sin6_addr;
if (!IN6_IS_ADDR_MULTICAST(address)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "not in6 multicast");
return;
}
mname6.ipv6mr_multiaddr = *address;
} else {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "IPv6 not supported");
return;
}
if (fd1 < 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Can't join an IPv6 group on a IPv4 socket");
return;
}
if (IS_NULL(niObj)) {
len = sizeof (ifindex);
if (NET_GetSockOpt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, &len) < 0) {
NET_ThrowCurrent(env, "get IPV6_MULTICAST_IF failed");
return;
}
} else {
ifindex = getIndexFromIf (env, niObj);
if (ifindex == -1) {
NET_ThrowCurrent(env, "get ifindex failed");
return;
}
}
mname6.ipv6mr_interface = ifindex;
cmd = join ? IPV6_ADD_MEMBERSHIP: IPV6_DROP_MEMBERSHIP;
/* Join the multicast group */
if (NET_SetSockOpt(fd1, IPPROTO_IPV6, cmd, (char *) &mname6, sizeof (mname6)) < 0) {
if (WSAGetLastError() == WSAENOBUFS) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"IP_ADD_MEMBERSHIP failed (out of hardware filters?)");
} else {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException","error setting options");
}
}
}
return;
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: join
* Signature: (Ljava/net/InetAddress;)V
*/
JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_join(JNIEnv *env, jobject this,
jobject iaObj, jobject niObj)
{
mcast_join_leave (env, this, iaObj, niObj, JNI_TRUE);
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: leave
* Signature: (Ljava/net/InetAddress;)V
*/
JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_leave(JNIEnv *env, jobject this,
jobject iaObj, jobject niObj)
{
mcast_join_leave (env, this, iaObj, niObj, JNI_FALSE);
}
/*
* Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: dataAvailable
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_java_net_TwoStacksPlainDatagramSocketImpl_dataAvailable
(JNIEnv *env, jobject this) {
SOCKET fd;
SOCKET fd1;
int rv = -1, rv1 = -1;
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
jobject fd1Obj;
if (!IS_NULL(fdObj)) {
int retval = 0;
fd = (SOCKET)(*env)->GetIntField(env, fdObj, IO_fd_fdID);
rv = ioctlsocket(fd, FIONREAD, &retval);
if (retval > 0) {
return retval;
}
}
fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID);
if (!IS_NULL(fd1Obj)) {
int retval = 0;
fd1 = (SOCKET)(*env)->GetIntField(env, fd1Obj, IO_fd_fdID);
rv1 = ioctlsocket(fd1, FIONREAD, &retval);
if (retval > 0) {
return retval;
}
}
if (rv < 0 && rv1 < 0) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
return -1;
}
return 0;
}