jdk/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c
changeset 2 90ce3da70b43
child 910 1f53246fb014
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,2524 @@
+/*
+ * Copyright 1997-2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include <windows.h>
+#include <winsock2.h>
+#include <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 "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
+ * oddies 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 necesary 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, "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);
+
+    /*
+     * 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) {
+    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;
+    int address;
+
+    family = (*env)->GetIntField(env, addressObj, ia_familyID);
+    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 = (*env)->GetIntField(env, addressObj, ia_addressID);
+    }
+
+    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) != -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_ThrowCurrent (env, "Cannot bind");
+            return;
+        }
+    } else {
+        if (bind(fd, (struct sockaddr *)&lcladdr, lcladdrlen) == -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 = (*env)->GetIntField(env, address, ia_addressID);
+
+    family = (*env)->GetIntField(env, address, ia_familyID);
+    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 un 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(&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, x2; /* 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;
+    int x; /* DELETE ME */
+
+
+    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 = (*env)->GetIntField(env, iaObj, ia_familyID);
+    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);
+
+    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 = (*env)->GetIntField(env, iaObj, ia_addressID);
+            } 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
+         * 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, "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 = (*env)->GetIntField(env, addressObj, ia_addressID);
+        /* 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 -= (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;
+    }
+    (*env)->SetIntField(env, addressObj, ia_addressID,
+                        ntohl(remote_addr.sin_addr.s_addr));
+    (*env)->SetIntField(env, addressObj, ia_familyID, 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;
+    jbyteArray data;
+
+    int checkBoth = 0, datalen;
+    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, "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 -= (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;
+    jbyteArray data;
+
+    int datalen;
+    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, "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 -= (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;
+        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;
+    static jfieldID ia_familyID=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);
+        c = (*env)->FindClass(env,"java/net/InetAddress");
+        CHECK_NULL_RETURN (c, -1);
+        ia_familyID = (*env)->GetFieldID(env, c, "family", "I");
+        CHECK_NULL_RETURN (ia_familyID, -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 = (*env)->GetIntField(env, addr, ia_familyID);
+        if (fam == family) {
+            *iaddr = addr;
+            return 0;
+        }
+    }
+    return -1;
+}
+
+static int getInet4AddrFromIf (JNIEnv *env, jobject nif, struct in_addr *iaddr)
+{
+    jobject addr;
+    static jfieldID ia_addressID;
+
+    int ret = getInetAddrFromIf (env, IPv4, nif, &addr);
+    if (ret == -1) {
+        return -1;
+    }
+
+    if (ia_addressID == 0) {
+        jclass c = (*env)->FindClass(env,"java/net/InetAddress");
+        CHECK_NULL_RETURN (c, -1);
+        ia_addressID = (*env)->GetFieldID(env, c, "address", "I");
+        CHECK_NULL_RETURN (ia_addressID, -1);
+    }
+    iaddr->s_addr = htonl((*env)->GetIntField(env, addr, ia_addressID));
+    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);
+}
+
+/*
+ * 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 {
+            static jfieldID ia_addressID;
+            struct in_addr in;
+
+            if (ia_addressID == NULL) {
+                        jclass c = (*env)->FindClass(env,"java/net/InetAddress");
+                CHECK_NULL(c);
+                ia_addressID = (*env)->GetFieldID(env, c, "address", "I");
+                CHECK_NULL(ia_addressID);
+            }
+
+            in.s_addr = htonl((*env)->GetIntField(env, value, ia_addressID));
+
+            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 (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:    socketSetOption
+ * Signature: (ILjava/lang/Object;)V
+ */
+JNIEXPORT void JNICALL
+Java_java_net_TwoStacksPlainDatagramSocketImpl_socketSetOption(JNIEnv *env,jobject this,
+                                                      jint opt,jobject value) {
+
+    int fd=-1, fd1=-1;
+    int levelv4, levelv6, optnamev4, optnamev6, optlen;
+    union {
+        int i;
+        char c;
+    } optval;
+    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;
+        }
+    }
+}
+
+/*
+ * 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) {
+        static jclass inet4_class;
+        static jmethodID inet4_ctrID;
+        static jfieldID inet4_addrID;
+
+        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_addrID = (*env)->GetFieldID(env, c, "address", "I");
+            CHECK_NULL_RETURN(inet4_addrID, 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);
+
+        (*env)->SetIntField(env, addr, inet4_addrID, 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;
+        }
+
+        /*
+         * The address doesn't appear to be bound at any known
+         * NetworkInterface. Therefore we construct a NetworkInterface
+         * with this address.
+         */
+        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);
+        return ni;
+    }
+
+
+    /*
+     * 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_getByIndex(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;
+        }
+
+        /*
+         * 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;
+    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);
+    }
+
+    if (opt == java_net_SocketOptions_SO_BINDADDR) {
+        /* find out local IP address */
+        SOCKETADDRESS him;
+        int len = 0;
+        int port;
+        jobject iaObj;
+
+        len = sizeof (struct sockaddr_in);
+
+        if (fd == -1) {
+            fd = fd1; /* must be IPv6 only */
+            len = sizeof (struct SOCKADDR_IN6);
+        }
+
+        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;
+    }
+
+    /*
+     * 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 errmsg[255];
+        sprintf(errmsg, "error getting socket option: %s\n", strerror(errno));
+        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;
+
+    }
+}
+
+/*
+ * 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;
+
+    int len, family;
+    int ipv6_supported = ipv6_available();
+    int cmd ;
+
+    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);
+}