0N/A/*
3909N/A * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A#include <errno.h>
0N/A#include <netinet/in.h>
0N/A#include <stdlib.h>
0N/A#include <string.h>
0N/A#include <sys/types.h>
0N/A#include <sys/socket.h>
0N/A
0N/A#ifdef __solaris__
0N/A#include <fcntl.h>
0N/A#endif
0N/A#ifdef __linux__
3652N/A#include <unistd.h>
3652N/A#include <sys/sysctl.h>
0N/A#include <sys/utsname.h>
0N/A#include <netinet/ip.h>
0N/A
0N/A#define IPV6_MULTICAST_IF 17
0N/A#ifndef SO_BSDCOMPAT
0N/A#define SO_BSDCOMPAT 14
0N/A#endif
0N/A#endif
0N/A
0N/A#ifndef IPTOS_TOS_MASK
0N/A#define IPTOS_TOS_MASK 0x1e
0N/A#endif
0N/A#ifndef IPTOS_PREC_MASK
0N/A#define IPTOS_PREC_MASK 0xe0
0N/A#endif
0N/A
0N/A#include "jvm.h"
0N/A#include "jni_util.h"
0N/A#include "net_util.h"
0N/A
0N/A#include "java_net_SocketOptions.h"
0N/A#include "java_net_PlainDatagramSocketImpl.h"
0N/A#include "java_net_NetworkInterface.h"
0N/A/************************************************************************
0N/A * PlainDatagramSocketImpl
0N/A */
0N/A
0N/Astatic jfieldID IO_fd_fdID;
0N/A
0N/Astatic jfieldID pdsi_fdID;
0N/Astatic jfieldID pdsi_timeoutID;
0N/Astatic jfieldID pdsi_trafficClassID;
0N/Astatic jfieldID pdsi_localPortID;
0N/Astatic jfieldID pdsi_connected;
0N/Astatic jfieldID pdsi_connectedAddress;
0N/Astatic jfieldID pdsi_connectedPort;
0N/A
0N/A#ifdef __linux__
0N/Astatic jboolean isOldKernel;
0N/A#endif
0N/A
0N/A#if defined(__linux__) && defined(AF_INET6)
0N/Astatic jfieldID pdsi_multicastInterfaceID;
0N/Astatic jfieldID pdsi_loopbackID;
0N/Astatic jfieldID pdsi_ttlID;
0N/A#endif
0N/A
4638N/Aextern void setDefaultScopeID(JNIEnv *env, struct sockaddr *him);
4805N/Aextern int getDefaultScopeID(JNIEnv *env);
4638N/A
0N/A/*
0N/A * Returns a java.lang.Integer based on 'i'
0N/A */
0N/Astatic jobject createInteger(JNIEnv *env, int i) {
0N/A static jclass i_class;
0N/A static jmethodID i_ctrID;
0N/A
0N/A if (i_class == NULL) {
0N/A jclass c = (*env)->FindClass(env, "java/lang/Integer");
0N/A CHECK_NULL_RETURN(c, NULL);
0N/A i_ctrID = (*env)->GetMethodID(env, c, "<init>", "(I)V");
0N/A CHECK_NULL_RETURN(i_ctrID, NULL);
0N/A i_class = (*env)->NewGlobalRef(env, c);
0N/A CHECK_NULL_RETURN(i_class, NULL);
0N/A }
0N/A
0N/A return ( (*env)->NewObject(env, i_class, i_ctrID, i) );
0N/A}
0N/A
0N/A/*
0N/A * Returns a java.lang.Boolean based on 'b'
0N/A */
0N/Astatic jobject createBoolean(JNIEnv *env, int b) {
0N/A static jclass b_class;
0N/A static jmethodID b_ctrID;
0N/A
0N/A if (b_class == NULL) {
0N/A jclass c = (*env)->FindClass(env, "java/lang/Boolean");
0N/A CHECK_NULL_RETURN(c, NULL);
0N/A b_ctrID = (*env)->GetMethodID(env, c, "<init>", "(Z)V");
0N/A CHECK_NULL_RETURN(b_ctrID, NULL);
0N/A b_class = (*env)->NewGlobalRef(env, c);
0N/A CHECK_NULL_RETURN(b_class, NULL);
0N/A }
0N/A
0N/A return( (*env)->NewObject(env, b_class, b_ctrID, (jboolean)(b!=0)) );
0N/A}
0N/A
0N/A
0N/A/*
0N/A * Returns the fd for a PlainDatagramSocketImpl or -1
0N/A * if closed.
0N/A */
0N/Astatic int getFD(JNIEnv *env, jobject this) {
0N/A jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
0N/A if (fdObj == NULL) {
0N/A return -1;
0N/A }
0N/A return (*env)->GetIntField(env, fdObj, IO_fd_fdID);
0N/A}
0N/A
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: init
0N/A * Signature: ()V
0N/A */
0N/AJNIEXPORT void JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_init(JNIEnv *env, jclass cls) {
0N/A
0N/A#ifdef __linux__
0N/A struct utsname sysinfo;
0N/A#endif
0N/A pdsi_fdID = (*env)->GetFieldID(env, cls, "fd",
0N/A "Ljava/io/FileDescriptor;");
0N/A CHECK_NULL(pdsi_fdID);
0N/A pdsi_timeoutID = (*env)->GetFieldID(env, cls, "timeout", "I");
0N/A CHECK_NULL(pdsi_timeoutID);
0N/A pdsi_trafficClassID = (*env)->GetFieldID(env, cls, "trafficClass", "I");
0N/A CHECK_NULL(pdsi_trafficClassID);
0N/A pdsi_localPortID = (*env)->GetFieldID(env, cls, "localPort", "I");
0N/A CHECK_NULL(pdsi_localPortID);
0N/A pdsi_connected = (*env)->GetFieldID(env, cls, "connected", "Z");
0N/A CHECK_NULL(pdsi_connected);
0N/A pdsi_connectedAddress = (*env)->GetFieldID(env, cls, "connectedAddress",
0N/A "Ljava/net/InetAddress;");
0N/A CHECK_NULL(pdsi_connectedAddress);
0N/A pdsi_connectedPort = (*env)->GetFieldID(env, cls, "connectedPort", "I");
0N/A CHECK_NULL(pdsi_connectedPort);
0N/A
0N/A IO_fd_fdID = NET_GetFileDescriptorID(env);
0N/A CHECK_NULL(IO_fd_fdID);
0N/A
0N/A Java_java_net_InetAddress_init(env, 0);
0N/A Java_java_net_Inet4Address_init(env, 0);
0N/A Java_java_net_Inet6Address_init(env, 0);
0N/A Java_java_net_NetworkInterface_init(env, 0);
0N/A
0N/A#ifdef __linux__
0N/A /*
0N/A * We need to determine if this is a 2.2 kernel.
0N/A */
0N/A if (uname(&sysinfo) == 0) {
0N/A sysinfo.release[3] = '\0';
0N/A isOldKernel = (strcmp(sysinfo.release, "2.2") == 0);
0N/A } else {
0N/A /*
0N/A * uname failed - move to plan B and examine /proc/version
0N/A * If this fails assume that /proc has changed and that
0N/A * this must be new /proc format and hence new kernel.
0N/A */
0N/A FILE *fP;
0N/A isOldKernel = JNI_FALSE;
0N/A if ((fP = fopen("/proc/version", "r")) != NULL) {
0N/A char ver[25];
0N/A if (fgets(ver, sizeof(ver), fP) != NULL) {
0N/A isOldKernel = (strstr(ver, "2.2.") != NULL);
0N/A }
0N/A fclose(fP);
0N/A }
0N/A }
0N/A
0N/A#ifdef AF_INET6
0N/A pdsi_multicastInterfaceID = (*env)->GetFieldID(env, cls, "multicastInterface", "I");
0N/A CHECK_NULL(pdsi_multicastInterfaceID);
0N/A pdsi_loopbackID = (*env)->GetFieldID(env, cls, "loopbackMode", "Z");
0N/A CHECK_NULL(pdsi_loopbackID);
0N/A pdsi_ttlID = (*env)->GetFieldID(env, cls, "ttl", "I");
0N/A CHECK_NULL(pdsi_ttlID);
0N/A#endif
0N/A
0N/A#endif
0N/A
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: bind
0N/A * Signature: (ILjava/net/InetAddress;)V
0N/A */
0N/AJNIEXPORT void JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this,
0N/A jint localport, jobject iaObj) {
0N/A /* fdObj is the FileDescriptor field on this */
0N/A jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
0N/A /* fd is an int field on fdObj */
0N/A int fd;
0N/A int len = 0;
0N/A SOCKADDR him;
0N/A
0N/A if (IS_NULL(fdObj)) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Socket closed");
0N/A return;
0N/A } else {
0N/A fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
0N/A }
0N/A
0N/A if (IS_NULL(iaObj)) {
0N/A JNU_ThrowNullPointerException(env, "iaObj is null.");
0N/A return;
0N/A }
0N/A
0N/A /* bind */
0N/A if (NET_InetAddressToSockaddr(env, iaObj, localport, (struct sockaddr *)&him, &len, JNI_TRUE) != 0) {
0N/A return;
0N/A }
4638N/A setDefaultScopeID(env, (struct sockaddr *)&him);
0N/A
0N/A if (NET_Bind(fd, (struct sockaddr *)&him, len) < 0) {
0N/A if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
0N/A errno == EPERM || errno == EACCES) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "BindException",
0N/A "Bind failed");
0N/A } else {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Bind failed");
0N/A }
0N/A return;
0N/A }
0N/A
0N/A /* intialize the local port */
0N/A if (localport == 0) {
0N/A /* Now that we're a connected socket, let's extract the port number
0N/A * that the system chose for us and store it in the Socket object.
0N/A */
0N/A if (JVM_GetSockName(fd, (struct sockaddr *)&him, &len) == -1) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Error getting socket name");
0N/A return;
0N/A }
0N/A
0N/A localport = NET_GetPortFromSockaddr((struct sockaddr *)&him);
0N/A
0N/A (*env)->SetIntField(env, this, pdsi_localPortID, localport);
0N/A } else {
0N/A (*env)->SetIntField(env, this, pdsi_localPortID, localport);
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: connect0
0N/A * Signature: (Ljava/net/InetAddress;I)V
0N/A */
0N/AJNIEXPORT void JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_connect0(JNIEnv *env, jobject this,
0N/A jobject address, jint port) {
0N/A /* The object's field */
0N/A jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
0N/A /* The fdObj'fd */
0N/A jint fd;
0N/A /* The packetAddress address, family and port */
0N/A SOCKADDR rmtaddr;
0N/A int len = 0;
0N/A
0N/A if (IS_NULL(fdObj)) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Socket closed");
0N/A return;
0N/A }
0N/A fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
0N/A
0N/A if (IS_NULL(address)) {
0N/A JNU_ThrowNullPointerException(env, "address");
0N/A return;
0N/A }
0N/A
0N/A if (NET_InetAddressToSockaddr(env, address, port, (struct sockaddr *)&rmtaddr, &len, JNI_TRUE) != 0) {
0N/A return;
0N/A }
0N/A
0N/A#ifdef __linux__
0N/A if (isOldKernel) {
0N/A int t = 0;
0N/A setsockopt(fd, SOL_SOCKET, SO_BSDCOMPAT, (char*) &t, sizeof(int));
0N/A } else
0N/A#endif
4638N/A setDefaultScopeID(env, (struct sockaddr *)&rmtaddr);
0N/A {
0N/A if (JVM_Connect(fd, (struct sockaddr *)&rmtaddr, len) == -1) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
0N/A "Connect failed");
0N/A return;
0N/A }
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: disconnect0
0N/A * Signature: ()V
0N/A */
0N/AJNIEXPORT void JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_disconnect0(JNIEnv *env, jobject this, jint family) {
0N/A /* The object's field */
0N/A jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
0N/A /* The fdObj'fd */
0N/A jint fd;
0N/A
4632N/A#if defined(__linux__) || defined(_ALLBSD_SOURCE)
0N/A SOCKADDR addr;
0N/A int len;
0N/A#endif
0N/A
0N/A if (IS_NULL(fdObj)) {
0N/A return;
0N/A }
0N/A fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
0N/A
4632N/A#if defined(__linux__) || defined(_ALLBSD_SOURCE)
0N/A#ifdef __linux__
0N/A if (isOldKernel) {
0N/A int t = 1;
0N/A setsockopt(fd, SOL_SOCKET, SO_BSDCOMPAT, (char*) &t, sizeof(int));
0N/A } else {
4632N/A#endif /* __linux__ */
0N/A memset(&addr, 0, sizeof(addr));
0N/A#ifdef AF_INET6
0N/A if (ipv6_available()) {
0N/A struct sockaddr_in6 *him6 = (struct sockaddr_in6 *)&addr;
0N/A him6->sin6_family = AF_UNSPEC;
0N/A len = sizeof(struct sockaddr_in6);
0N/A } else
0N/A#endif
0N/A {
0N/A struct sockaddr_in *him4 = (struct sockaddr_in*)&addr;
0N/A him4->sin_family = AF_UNSPEC;
0N/A len = sizeof(struct sockaddr_in);
0N/A }
0N/A JVM_Connect(fd, (struct sockaddr *)&addr, len);
0N/A
4632N/A#ifdef __linux__
0N/A // After disconnecting a UDP socket, Linux kernel will set
0N/A // local port to zero if the port number comes from implicit
0N/A // bind. Successive send/recv on the same socket will fail.
0N/A // So bind again with former port number here.
0N/A int localPort = 0;
0N/A if (JVM_GetSockName(fd, (struct sockaddr *)&addr, &len) == -1) {
0N/A return;
0N/A }
453N/A localPort = NET_GetPortFromSockaddr((struct sockaddr *)&addr);
0N/A if (localPort == 0) {
0N/A localPort = (*env)->GetIntField(env, this, pdsi_localPortID);
0N/A#ifdef AF_INET6
0N/A if (((struct sockaddr*)&addr)->sa_family == AF_INET6) {
0N/A ((struct sockaddr_in6*)&addr)->sin6_port = htons(localPort);
0N/A } else
0N/A#endif /* AF_INET6 */
0N/A {
0N/A ((struct sockaddr_in*)&addr)->sin_port = htons(localPort);
0N/A }
0N/A NET_Bind(fd, (struct sockaddr *)&addr, len);
0N/A }
0N/A }
4632N/A#endif
0N/A#else
0N/A JVM_Connect(fd, 0, 0);
0N/A#endif
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: send
0N/A * Signature: (Ljava/net/DatagramPacket;)V
0N/A */
0N/AJNIEXPORT void JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_send(JNIEnv *env, jobject this,
0N/A jobject packet) {
0N/A
0N/A char BUF[MAX_BUFFER_LEN];
0N/A char *fullPacket = NULL;
0N/A int ret, mallocedPacket = JNI_FALSE;
0N/A /* The object's field */
0N/A jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
0N/A jint trafficClass = (*env)->GetIntField(env, this, pdsi_trafficClassID);
0N/A
0N/A jbyteArray packetBuffer;
0N/A jobject packetAddress;
0N/A jint packetBufferOffset, packetBufferLen, packetPort;
0N/A jboolean connected;
0N/A
0N/A /* The fdObj'fd */
0N/A jint fd;
0N/A
0N/A SOCKADDR rmtaddr, *rmtaddrP=&rmtaddr;
0N/A int len;
0N/A
0N/A if (IS_NULL(fdObj)) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Socket closed");
0N/A return;
0N/A }
0N/A fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
0N/A
0N/A if (IS_NULL(packet)) {
0N/A JNU_ThrowNullPointerException(env, "packet");
0N/A return;
0N/A }
0N/A
0N/A connected = (*env)->GetBooleanField(env, this, pdsi_connected);
0N/A
0N/A packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
0N/A packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
0N/A if (IS_NULL(packetBuffer) || IS_NULL(packetAddress)) {
0N/A JNU_ThrowNullPointerException(env, "null buffer || null address");
0N/A return;
0N/A }
0N/A
0N/A packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
0N/A packetBufferLen = (*env)->GetIntField(env, packet, dp_lengthID);
0N/A
0N/A#ifdef __linux__
0N/A if (connected && !isOldKernel) {
0N/A#else
0N/A if (connected) {
0N/A#endif
0N/A /* arg to NET_Sendto () null in this case */
0N/A len = 0;
0N/A rmtaddrP = 0;
0N/A } else {
0N/A packetPort = (*env)->GetIntField(env, packet, dp_portID);
0N/A if (NET_InetAddressToSockaddr(env, packetAddress, packetPort, (struct sockaddr *)&rmtaddr, &len, JNI_TRUE) != 0) {
0N/A return;
0N/A }
0N/A }
4638N/A setDefaultScopeID(env, (struct sockaddr *)&rmtaddr);
0N/A
0N/A if (packetBufferLen > MAX_BUFFER_LEN) {
0N/A /* When JNI-ifying the JDK's IO routines, we turned
0N/A * read's and write's of byte arrays of size greater
0N/A * than 2048 bytes into several operations of size 2048.
0N/A * This saves a malloc()/memcpy()/free() for big
0N/A * buffers. This is OK for file IO and TCP, but that
0N/A * strategy violates the semantics of a datagram protocol.
0N/A * (one big send) != (several smaller sends). So here
0N/A * we *must* alloc the buffer. Note it needn't be bigger
0N/A * than 65,536 (0xFFFF) the max size of an IP packet.
0N/A * Anything bigger should be truncated anyway.
0N/A *
0N/A * We may want to use a smarter allocation scheme at some
0N/A * point.
0N/A */
0N/A if (packetBufferLen > MAX_PACKET_LEN) {
0N/A packetBufferLen = MAX_PACKET_LEN;
0N/A }
0N/A fullPacket = (char *)malloc(packetBufferLen);
0N/A
0N/A if (!fullPacket) {
5476N/A JNU_ThrowOutOfMemoryError(env, "Send buffer native heap allocation failed");
0N/A return;
0N/A } else {
0N/A mallocedPacket = JNI_TRUE;
0N/A }
0N/A } else {
0N/A fullPacket = &(BUF[0]);
0N/A }
0N/A
0N/A (*env)->GetByteArrayRegion(env, packetBuffer, packetBufferOffset, packetBufferLen,
0N/A (jbyte *)fullPacket);
0N/A#ifdef AF_INET6
0N/A if (trafficClass != 0 && ipv6_available()) {
0N/A NET_SetTrafficClass((struct sockaddr *)&rmtaddr, trafficClass);
0N/A }
0N/A#endif /* AF_INET6 */
0N/A
0N/A
0N/A /*
0N/A * Send the datagram.
0N/A *
0N/A * If we are connected it's possible that sendto will return
0N/A * ECONNREFUSED indicating that an ICMP port unreachable has
0N/A * received.
0N/A */
0N/A ret = NET_SendTo(fd, fullPacket, packetBufferLen, 0,
0N/A (struct sockaddr *)rmtaddrP, len);
0N/A
0N/A if (ret < 0) {
0N/A switch (ret) {
0N/A case JVM_IO_ERR :
0N/A if (errno == ECONNREFUSED) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
0N/A "ICMP Port Unreachable");
0N/A } else {
0N/A NET_ThrowByNameWithLastError(env, "java/io/IOException", "sendto failed");
0N/A }
0N/A break;
0N/A
0N/A case JVM_IO_INTR:
0N/A JNU_ThrowByName(env, "java/io/InterruptedIOException",
0N/A "operation interrupted");
0N/A break;
0N/A }
0N/A }
0N/A
0N/A if (mallocedPacket) {
0N/A free(fullPacket);
0N/A }
0N/A return;
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: peek
0N/A * Signature: (Ljava/net/InetAddress;)I
0N/A */
0N/AJNIEXPORT jint JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_peek(JNIEnv *env, jobject this,
0N/A jobject addressObj) {
0N/A
0N/A jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
0N/A jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
0N/A jint fd;
0N/A ssize_t n;
0N/A SOCKADDR remote_addr;
0N/A int len;
0N/A char buf[1];
0N/A jint family;
0N/A jobject iaObj;
0N/A int port;
0N/A if (IS_NULL(fdObj)) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
0N/A return -1;
0N/A } else {
0N/A fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
0N/A }
0N/A if (IS_NULL(addressObj)) {
0N/A JNU_ThrowNullPointerException(env, "Null address in peek()");
0N/A }
0N/A if (timeout) {
0N/A int ret = NET_Timeout(fd, timeout);
0N/A if (ret == 0) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
0N/A "Peek timed out");
0N/A return ret;
0N/A } else if (ret == JVM_IO_ERR) {
0N/A if (errno == EBADF) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
0N/A } else {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Peek failed");
0N/A }
0N/A return ret;
0N/A } else if (ret == JVM_IO_INTR) {
0N/A JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
0N/A "operation interrupted");
0N/A return ret; /* WARNING: SHOULD WE REALLY RETURN -2??? */
0N/A }
0N/A }
0N/A
0N/A len = SOCKADDR_LEN;
0N/A n = NET_RecvFrom(fd, buf, 1, MSG_PEEK,
0N/A (struct sockaddr *)&remote_addr, &len);
0N/A
0N/A if (n == JVM_IO_ERR) {
0N/A
0N/A#ifdef __solaris__
0N/A if (errno == ECONNREFUSED) {
0N/A int orig_errno = errno;
0N/A (void) recv(fd, buf, 1, 0);
0N/A errno = orig_errno;
0N/A }
0N/A#endif
0N/A if (errno == ECONNREFUSED) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
0N/A "ICMP Port Unreachable");
0N/A } else {
0N/A if (errno == EBADF) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
0N/A } else {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Peek failed");
0N/A }
0N/A }
0N/A return 0;
0N/A } else if (n == JVM_IO_INTR) {
0N/A JNU_ThrowByName(env, "java/io/InterruptedIOException", 0);
0N/A return 0;
0N/A }
0N/A
0N/A iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&remote_addr, &port);
2112N/A#ifdef AF_INET6
5888N/A family = getInetAddress_family(env, iaObj) == IPv4? AF_INET : AF_INET6;
2112N/A#else
2112N/A family = AF_INET;
2112N/A#endif
0N/A if (family == AF_INET) { /* this api can't handle IPV6 addresses */
5888N/A int address = getInetAddress_addr(env, iaObj);
5888N/A setInetAddress_addr(env, addressObj, address);
0N/A }
0N/A return port;
0N/A}
0N/A
0N/AJNIEXPORT jint JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_peekData(JNIEnv *env, jobject this,
0N/A jobject packet) {
0N/A
0N/A char BUF[MAX_BUFFER_LEN];
0N/A char *fullPacket = NULL;
0N/A int mallocedPacket = JNI_FALSE;
0N/A jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
0N/A jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
0N/A
0N/A jbyteArray packetBuffer;
0N/A jint packetBufferOffset, packetBufferLen;
0N/A
0N/A int fd;
0N/A
0N/A int n;
0N/A SOCKADDR remote_addr;
0N/A int len;
0N/A int port;
0N/A
0N/A if (IS_NULL(fdObj)) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Socket closed");
0N/A return -1;
0N/A }
0N/A
0N/A fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
0N/A
0N/A if (IS_NULL(packet)) {
0N/A JNU_ThrowNullPointerException(env, "packet");
0N/A return -1;
0N/A }
0N/A
0N/A packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
0N/A if (IS_NULL(packetBuffer)) {
0N/A JNU_ThrowNullPointerException(env, "packet buffer");
0N/A return -1;
0N/A }
0N/A packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
0N/A packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID);
0N/A if (timeout) {
0N/A int ret = NET_Timeout(fd, timeout);
0N/A if (ret == 0) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
0N/A "Receive timed out");
0N/A return -1;
0N/A } else if (ret == JVM_IO_ERR) {
0N/A#ifdef __linux__
0N/A if (errno == EBADF) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
0N/A } else {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Receive failed");
0N/A }
0N/A#else
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
0N/A#endif
0N/A return -1;
0N/A } else if (ret == JVM_IO_INTR) {
0N/A JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
0N/A "operation interrupted");
0N/A return -1;
0N/A }
0N/A }
0N/A
0N/A if (packetBufferLen > MAX_BUFFER_LEN) {
0N/A
0N/A /* When JNI-ifying the JDK's IO routines, we turned
0N/A * read's and write's of byte arrays of size greater
0N/A * than 2048 bytes into several operations of size 2048.
0N/A * This saves a malloc()/memcpy()/free() for big
0N/A * buffers. This is OK for file IO and TCP, but that
0N/A * strategy violates the semantics of a datagram protocol.
0N/A * (one big send) != (several smaller sends). So here
0N/A * we *must* alloc the buffer. Note it needn't be bigger
0N/A * than 65,536 (0xFFFF) the max size of an IP packet.
0N/A * anything bigger is truncated anyway.
0N/A *
0N/A * We may want to use a smarter allocation scheme at some
0N/A * point.
0N/A */
0N/A if (packetBufferLen > MAX_PACKET_LEN) {
0N/A packetBufferLen = MAX_PACKET_LEN;
0N/A }
0N/A fullPacket = (char *)malloc(packetBufferLen);
0N/A
0N/A if (!fullPacket) {
5476N/A JNU_ThrowOutOfMemoryError(env, "Peek buffer native heap allocation failed");
0N/A return -1;
0N/A } else {
0N/A mallocedPacket = JNI_TRUE;
0N/A }
0N/A } else {
0N/A fullPacket = &(BUF[0]);
0N/A }
0N/A
0N/A len = SOCKADDR_LEN;
0N/A n = NET_RecvFrom(fd, fullPacket, packetBufferLen, MSG_PEEK,
0N/A (struct sockaddr *)&remote_addr, &len);
0N/A /* truncate the data if the packet's length is too small */
0N/A if (n > packetBufferLen) {
0N/A n = packetBufferLen;
0N/A }
0N/A if (n == JVM_IO_ERR) {
0N/A
0N/A#ifdef __solaris__
0N/A if (errno == ECONNREFUSED) {
0N/A int orig_errno = errno;
0N/A (void) recv(fd, fullPacket, 1, 0);
0N/A errno = orig_errno;
0N/A }
0N/A#endif
0N/A (*env)->SetIntField(env, packet, dp_offsetID, 0);
0N/A (*env)->SetIntField(env, packet, dp_lengthID, 0);
0N/A if (errno == ECONNREFUSED) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
0N/A "ICMP Port Unreachable");
0N/A } else {
0N/A if (errno == EBADF) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
0N/A } else {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Receive failed");
0N/A }
0N/A }
0N/A } else if (n == JVM_IO_INTR) {
0N/A (*env)->SetIntField(env, packet, dp_offsetID, 0);
0N/A (*env)->SetIntField(env, packet, dp_lengthID, 0);
0N/A JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
0N/A "operation interrupted");
0N/A } else {
0N/A /*
0N/A * success - fill in received address...
0N/A *
0N/A * REMIND: Fill in an int on the packet, and create inetadd
0N/A * object in Java, as a performance improvement. Also
0N/A * construct the inetadd object lazily.
0N/A */
0N/A
0N/A jobject packetAddress;
0N/A
0N/A /*
0N/A * Check if there is an InetAddress already associated with this
0N/A * packet. If so we check if it is the same source address. We
0N/A * can't update any existing InetAddress because it is immutable
0N/A */
0N/A packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
0N/A if (packetAddress != NULL) {
0N/A if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&remote_addr, packetAddress)) {
0N/A /* force a new InetAddress to be created */
0N/A packetAddress = NULL;
0N/A }
0N/A }
0N/A if (packetAddress == NULL) {
0N/A packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *)&remote_addr, &port);
0N/A /* stuff the new Inetaddress in the packet */
0N/A (*env)->SetObjectField(env, packet, dp_addressID, packetAddress);
0N/A } else {
0N/A /* only get the new port number */
0N/A port = NET_GetPortFromSockaddr((struct sockaddr *)&remote_addr);
0N/A }
0N/A /* and fill in the data, remote address/port and such */
0N/A (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n,
0N/A (jbyte *)fullPacket);
0N/A (*env)->SetIntField(env, packet, dp_portID, port);
0N/A (*env)->SetIntField(env, packet, dp_lengthID, n);
0N/A }
0N/A
0N/A if (mallocedPacket) {
0N/A free(fullPacket);
0N/A }
0N/A return port;
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: receive
0N/A * Signature: (Ljava/net/DatagramPacket;)V
0N/A */
0N/AJNIEXPORT void JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_receive0(JNIEnv *env, jobject this,
0N/A jobject packet) {
0N/A
0N/A char BUF[MAX_BUFFER_LEN];
0N/A char *fullPacket = NULL;
0N/A int mallocedPacket = JNI_FALSE;
0N/A jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
0N/A jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
0N/A
0N/A jbyteArray packetBuffer;
0N/A jint packetBufferOffset, packetBufferLen;
0N/A
0N/A int fd;
0N/A
0N/A int n;
0N/A SOCKADDR remote_addr;
0N/A int len;
0N/A jboolean retry;
0N/A#ifdef __linux__
0N/A jboolean connected = JNI_FALSE;
2112N/A jobject connectedAddress = NULL;
2112N/A jint connectedPort = 0;
2112N/A jlong prevTime = 0;
0N/A#endif
0N/A
0N/A if (IS_NULL(fdObj)) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Socket closed");
0N/A return;
0N/A }
0N/A
0N/A fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
0N/A
0N/A if (IS_NULL(packet)) {
0N/A JNU_ThrowNullPointerException(env, "packet");
0N/A return;
0N/A }
0N/A
0N/A packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
0N/A if (IS_NULL(packetBuffer)) {
0N/A JNU_ThrowNullPointerException(env, "packet buffer");
0N/A return;
0N/A }
0N/A packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
0N/A packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID);
0N/A
0N/A if (packetBufferLen > MAX_BUFFER_LEN) {
0N/A
0N/A /* When JNI-ifying the JDK's IO routines, we turned
0N/A * read's and write's of byte arrays of size greater
0N/A * than 2048 bytes into several operations of size 2048.
0N/A * This saves a malloc()/memcpy()/free() for big
0N/A * buffers. This is OK for file IO and TCP, but that
0N/A * strategy violates the semantics of a datagram protocol.
0N/A * (one big send) != (several smaller sends). So here
0N/A * we *must* alloc the buffer. Note it needn't be bigger
0N/A * than 65,536 (0xFFFF) the max size of an IP packet.
0N/A * anything bigger is truncated anyway.
0N/A *
0N/A * We may want to use a smarter allocation scheme at some
0N/A * point.
0N/A */
0N/A if (packetBufferLen > MAX_PACKET_LEN) {
0N/A packetBufferLen = MAX_PACKET_LEN;
0N/A }
0N/A fullPacket = (char *)malloc(packetBufferLen);
0N/A
0N/A if (!fullPacket) {
5476N/A JNU_ThrowOutOfMemoryError(env, "Receive buffer native heap allocation failed");
0N/A return;
0N/A } else {
0N/A mallocedPacket = JNI_TRUE;
0N/A }
0N/A } else {
0N/A fullPacket = &(BUF[0]);
0N/A }
0N/A
0N/A#ifdef __linux__
0N/A /*
0N/A * On Linux with the 2.2 kernel we simulate connected datagrams by
0N/A * discarding packets
0N/A */
0N/A if (isOldKernel) {
0N/A connected = (*env)->GetBooleanField(env, this, pdsi_connected);
0N/A if (connected) {
0N/A connectedAddress = (*env)->GetObjectField(env, this, pdsi_connectedAddress);
0N/A connectedPort = (*env)->GetIntField(env, this, pdsi_connectedPort);
0N/A
0N/A if (timeout) {
0N/A prevTime = JVM_CurrentTimeMillis(env, 0);
0N/A }
0N/A }
0N/A }
0N/A#endif
0N/A
0N/A do {
0N/A retry = JNI_FALSE;
0N/A
0N/A if (timeout) {
0N/A int ret = NET_Timeout(fd, timeout);
0N/A if (ret <= 0) {
0N/A if (ret == 0) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
0N/A "Receive timed out");
0N/A } else if (ret == JVM_IO_ERR) {
0N/A#ifdef __linux__
0N/A if (errno == EBADF) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
0N/A } else {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Receive failed");
0N/A }
0N/A#else
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
0N/A#endif
0N/A } else if (ret == JVM_IO_INTR) {
0N/A JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
0N/A "operation interrupted");
0N/A }
0N/A
0N/A if (mallocedPacket) {
0N/A free(fullPacket);
0N/A }
0N/A
0N/A return;
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Security Note: For Linux 2.2 with connected datagrams ensure that
0N/A * you receive into the stack/heap allocated buffer - do not attempt
0N/A * to receive directly into DatagramPacket's byte array.
0N/A * (ie: if the virtual machine support pinning don't use
0N/A * GetByteArrayElements or a JNI critical section and receive
0N/A * directly into the byte array)
0N/A */
0N/A len = SOCKADDR_LEN;
0N/A n = NET_RecvFrom(fd, fullPacket, packetBufferLen, 0,
0N/A (struct sockaddr *)&remote_addr, &len);
0N/A /* truncate the data if the packet's length is too small */
0N/A if (n > packetBufferLen) {
0N/A n = packetBufferLen;
0N/A }
0N/A if (n == JVM_IO_ERR) {
0N/A (*env)->SetIntField(env, packet, dp_offsetID, 0);
0N/A (*env)->SetIntField(env, packet, dp_lengthID, 0);
0N/A if (errno == ECONNREFUSED) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
0N/A "ICMP Port Unreachable");
0N/A } else {
0N/A if (errno == EBADF) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
0N/A } else {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Receive failed");
0N/A }
0N/A }
0N/A } else if (n == JVM_IO_INTR) {
0N/A (*env)->SetIntField(env, packet, dp_offsetID, 0);
0N/A (*env)->SetIntField(env, packet, dp_lengthID, 0);
0N/A JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
0N/A "operation interrupted");
0N/A } else {
0N/A int port;
0N/A jobject packetAddress;
0N/A
0N/A /*
0N/A * If we are connected then we know that the datagram that we have
0N/A * received is from the address that we are connected too. However
0N/A * on Linux with 2.2 kernel we have to simulate this behaviour by
0N/A * discarding any datagrams that aren't from the connected address.
0N/A */
0N/A#ifdef __linux__
0N/A if (isOldKernel && connected) {
0N/A
0N/A if (NET_GetPortFromSockaddr((struct sockaddr *)&remote_addr) != connectedPort ||
0N/A !NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&remote_addr, connectedAddress)) {
0N/A
0N/A /*
0N/A * Discard the datagram as it's not from the connected
0N/A * address
0N/A */
0N/A retry = JNI_TRUE;
0N/A
0N/A /*
0N/A * Adjust timeout if necessary to ensure that we adhere to
0N/A * timeout semantics.
0N/A */
0N/A if (timeout) {
0N/A jlong newTime = JVM_CurrentTimeMillis(env, 0);
0N/A timeout -= (newTime - prevTime);
0N/A if (timeout <= 0) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
0N/A "Receive timed out");
0N/A if (mallocedPacket) {
0N/A free(fullPacket);
0N/A }
0N/A return;
0N/A }
0N/A prevTime = newTime;
0N/A }
0N/A
0N/A continue;
0N/A }
0N/A }
0N/A#endif
0N/A
0N/A /*
0N/A * success - fill in received address...
0N/A *
0N/A * REMIND: Fill in an int on the packet, and create inetadd
0N/A * object in Java, as a performance improvement. Also
0N/A * construct the inetadd object lazily.
0N/A */
0N/A
0N/A /*
0N/A * Check if there is an InetAddress already associated with this
0N/A * packet. If so we check if it is the same source address. We
0N/A * can't update any existing InetAddress because it is immutable
0N/A */
0N/A packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
0N/A if (packetAddress != NULL) {
0N/A if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&remote_addr, packetAddress)) {
0N/A /* force a new InetAddress to be created */
0N/A packetAddress = NULL;
0N/A }
0N/A }
0N/A if (packetAddress == NULL) {
0N/A packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *)&remote_addr, &port);
0N/A /* stuff the new Inetaddress in the packet */
0N/A (*env)->SetObjectField(env, packet, dp_addressID, packetAddress);
0N/A } else {
0N/A /* only get the new port number */
0N/A port = NET_GetPortFromSockaddr((struct sockaddr *)&remote_addr);
0N/A }
0N/A /* and fill in the data, remote address/port and such */
0N/A (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n,
0N/A (jbyte *)fullPacket);
0N/A (*env)->SetIntField(env, packet, dp_portID, port);
0N/A (*env)->SetIntField(env, packet, dp_lengthID, n);
0N/A }
0N/A
0N/A } while (retry);
0N/A
0N/A if (mallocedPacket) {
0N/A free(fullPacket);
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: datagramSocketCreate
0N/A * Signature: ()V
0N/A */
0N/AJNIEXPORT void JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_datagramSocketCreate(JNIEnv *env,
0N/A jobject this) {
0N/A jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
4639N/A int arg, fd, t = 1;
2653N/A#ifdef AF_INET6
2653N/A int domain = ipv6_available() ? AF_INET6 : AF_INET;
2653N/A#else
2653N/A int domain = AF_INET;
2653N/A#endif
0N/A
0N/A if (IS_NULL(fdObj)) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Socket closed");
0N/A return;
0N/A }
2653N/A
2653N/A if ((fd = JVM_Socket(domain, SOCK_DGRAM, 0)) == JVM_IO_ERR) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Error creating socket");
0N/A return;
0N/A }
0N/A
2653N/A#ifdef AF_INET6
2653N/A /* Disable IPV6_V6ONLY to ensure dual-socket support */
2653N/A if (domain == AF_INET6) {
4639N/A arg = 0;
2653N/A if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
2653N/A sizeof(int)) < 0) {
2653N/A NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6");
2653N/A close(fd);
2653N/A return;
2653N/A }
2653N/A }
2653N/A#endif /* AF_INET6 */
2653N/A
4639N/A#ifdef __APPLE__
4639N/A arg = 65507;
4639N/A if (JVM_SetSockOpt(fd, SOL_SOCKET, SO_SNDBUF,
4639N/A (char *)&arg, sizeof(arg)) < 0) {
4639N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
4639N/A strerror(errno));
4639N/A return;
4639N/A }
4639N/A if (JVM_SetSockOpt(fd, SOL_SOCKET, SO_RCVBUF,
4639N/A (char *)&arg, sizeof(arg)) < 0) {
4639N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
4639N/A strerror(errno));
4639N/A return;
4639N/A }
4639N/A#endif /* __APPLE__ */
4639N/A
0N/A setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*) &t, sizeof(int));
0N/A
0N/A#ifdef __linux__
0N/A if (isOldKernel) {
0N/A setsockopt(fd, SOL_SOCKET, SO_BSDCOMPAT, (char*) &t, sizeof(int));
0N/A }
0N/A
0N/A#ifdef AF_INET6
0N/A /*
0N/A * On Linux for IPv6 sockets we must set the hop limit
0N/A * to 1 to be compatible with default ttl of 1 for IPv4 sockets.
0N/A */
2653N/A if (domain == AF_INET6) {
0N/A int ttl = 1;
0N/A setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&ttl,
0N/A sizeof(ttl));
0N/A
0N/A if (isOldKernel) {
0N/A (*env)->SetIntField(env, this, pdsi_ttlID, ttl);
0N/A }
0N/A }
0N/A#endif
0N/A
0N/A#endif /* __linux__ */
0N/A
0N/A (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: datagramSocketClose
0N/A * Signature: ()V
0N/A */
0N/AJNIEXPORT void JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_datagramSocketClose(JNIEnv *env,
0N/A jobject this) {
0N/A /*
0N/A * REMIND: PUT A LOCK AROUND THIS CODE
0N/A */
0N/A jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
0N/A int fd;
0N/A
0N/A if (IS_NULL(fdObj)) {
0N/A return;
0N/A }
0N/A fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
0N/A if (fd == -1) {
0N/A return;
0N/A }
0N/A (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1);
0N/A NET_SocketClose(fd);
0N/A}
0N/A
0N/A
0N/A/*
0N/A * Set outgoing multicast interface designated by a NetworkInterface.
0N/A * Throw exception if failed.
0N/A */
0N/Astatic void mcast_set_if_by_if_v4(JNIEnv *env, jobject this, int fd, jobject value) {
0N/A static jfieldID ni_addrsID;
0N/A struct in_addr in;
0N/A jobjectArray addrArray;
0N/A jsize len;
0N/A jobject addr;
0N/A int i;
0N/A
5888N/A if (ni_addrsID == NULL ) {
0N/A jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
0N/A CHECK_NULL(c);
0N/A ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
0N/A "[Ljava/net/InetAddress;");
0N/A CHECK_NULL(ni_addrsID);
0N/A }
0N/A
0N/A addrArray = (*env)->GetObjectField(env, value, ni_addrsID);
0N/A len = (*env)->GetArrayLength(env, addrArray);
0N/A
0N/A /*
0N/A * Check that there is at least one address bound to this
0N/A * interface.
0N/A */
0N/A if (len < 1) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "bad argument for IP_MULTICAST_IF2: No IP addresses bound to interface");
0N/A return;
0N/A }
0N/A
0N/A /*
0N/A * We need an ipv4 address here
0N/A */
0N/A for (i = 0; i < len; i++) {
0N/A addr = (*env)->GetObjectArrayElement(env, addrArray, i);
5888N/A if (getInetAddress_family(env, addr) == IPv4) {
5888N/A in.s_addr = htonl(getInetAddress_addr(env, addr));
0N/A break;
0N/A }
0N/A }
0N/A
0N/A if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF,
0N/A (const char*)&in, sizeof(in)) < 0) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Error setting socket option");
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Set outgoing multicast interface designated by a NetworkInterface.
0N/A * Throw exception if failed.
0N/A */
2112N/A#ifdef AF_INET6
0N/Astatic void mcast_set_if_by_if_v6(JNIEnv *env, jobject this, int fd, jobject value) {
0N/A static jfieldID ni_indexID;
0N/A int index;
0N/A
0N/A if (ni_indexID == NULL) {
0N/A jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
0N/A CHECK_NULL(c);
0N/A ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
0N/A CHECK_NULL(ni_indexID);
0N/A }
0N/A index = (*env)->GetIntField(env, value, ni_indexID);
0N/A
0N/A if (JVM_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
0N/A (const char*)&index, sizeof(index)) < 0) {
0N/A if (errno == EINVAL && index > 0) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "IPV6_MULTICAST_IF failed (interface has IPv4 "
0N/A "address only?)");
0N/A } else {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Error setting socket option");
0N/A }
0N/A return;
0N/A }
0N/A
0N/A#ifdef __linux__
0N/A /*
0N/A * Linux 2.2 kernel doesn't support IPV6_MULTICAST_IF socket
0N/A * option so record index for later retrival.
0N/A */
0N/A if (isOldKernel) {
0N/A (*env)->SetIntField(env, this, pdsi_multicastInterfaceID,
0N/A (jint)index);
0N/A }
0N/A#endif
0N/A}
2112N/A#endif /* AF_INET6 */
0N/A
0N/A/*
0N/A * Set outgoing multicast interface designated by an InetAddress.
0N/A * Throw exception if failed.
0N/A */
0N/Astatic void mcast_set_if_by_addr_v4(JNIEnv *env, jobject this, int fd, jobject value) {
0N/A struct in_addr in;
0N/A
5888N/A in.s_addr = htonl( getInetAddress_addr(env, value) );
0N/A
0N/A if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF,
0N/A (const char*)&in, sizeof(in)) < 0) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Error setting socket option");
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Set outgoing multicast interface designated by an InetAddress.
0N/A * Throw exception if failed.
0N/A */
2112N/A#ifdef AF_INET6
0N/Astatic void mcast_set_if_by_addr_v6(JNIEnv *env, jobject this, int fd, jobject value) {
0N/A static jclass ni_class;
0N/A if (ni_class == NULL) {
0N/A jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
0N/A CHECK_NULL(c);
0N/A ni_class = (*env)->NewGlobalRef(env, c);
0N/A CHECK_NULL(ni_class);
0N/A }
0N/A
0N/A value = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, value);
0N/A if (value == NULL) {
0N/A if (!(*env)->ExceptionOccurred(env)) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "bad argument for IP_MULTICAST_IF"
0N/A ": address not bound to any interface");
0N/A }
0N/A return;
0N/A }
0N/A
0N/A mcast_set_if_by_if_v6(env, this, fd, value);
0N/A}
2112N/A#endif
0N/A
0N/A/*
0N/A * Sets the multicast interface.
0N/A *
0N/A * SocketOptions.IP_MULTICAST_IF :-
0N/A * value is a InetAddress
0N/A * IPv4: set outgoing multicast interface using
0N/A * IPPROTO_IP/IP_MULTICAST_IF
0N/A * IPv6: Get the index of the interface to which the
0N/A * InetAddress is bound
0N/A * Set outgoing multicast interface using
0N/A * IPPROTO_IPV6/IPV6_MULTICAST_IF
0N/A * On Linux 2.2 record interface index as can't
0N/A * query the multicast interface.
0N/A *
0N/A * SockOptions.IF_MULTICAST_IF2 :-
0N/A * value is a NetworkInterface
0N/A * IPv4: Obtain IP address bound to network interface
0N/A * (NetworkInterface.addres[0])
0N/A * set outgoing multicast interface using
0N/A * IPPROTO_IP/IP_MULTICAST_IF
0N/A * IPv6: Obtain NetworkInterface.index
0N/A * Set outgoing multicast interface using
0N/A * IPPROTO_IPV6/IPV6_MULTICAST_IF
0N/A * On Linux 2.2 record interface index as can't
0N/A * query the multicast interface.
0N/A *
0N/A */
0N/Astatic void setMulticastInterface(JNIEnv *env, jobject this, int fd,
0N/A jint opt, jobject value)
0N/A{
0N/A if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
0N/A /*
0N/A * value is an InetAddress.
0N/A */
2112N/A#ifdef AF_INET6
5474N/A#ifdef __linux__
5474N/A mcast_set_if_by_addr_v4(env, this, fd, value);
5474N/A if (ipv6_available()) {
5474N/A mcast_set_if_by_addr_v6(env, this, fd, value);
5474N/A }
5474N/A#else /* __linux__ not defined */
0N/A if (ipv6_available()) {
0N/A mcast_set_if_by_addr_v6(env, this, fd, value);
0N/A } else {
0N/A mcast_set_if_by_addr_v4(env, this, fd, value);
0N/A }
5474N/A#endif /* __linux__ */
2112N/A#else
2112N/A mcast_set_if_by_addr_v4(env, this, fd, value);
2112N/A#endif /* AF_INET6 */
0N/A }
0N/A
0N/A if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
0N/A /*
0N/A * value is a NetworkInterface.
0N/A */
2112N/A#ifdef AF_INET6
5474N/A#ifdef __linux__
5474N/A mcast_set_if_by_if_v4(env, this, fd, value);
5474N/A if (ipv6_available()) {
5474N/A mcast_set_if_by_if_v6(env, this, fd, value);
5474N/A }
5474N/A#else /* __linux__ not defined */
0N/A if (ipv6_available()) {
0N/A mcast_set_if_by_if_v6(env, this, fd, value);
0N/A } else {
0N/A mcast_set_if_by_if_v4(env, this, fd, value);
0N/A }
5474N/A#endif /* __linux__ */
2112N/A#else
2112N/A mcast_set_if_by_if_v4(env, this, fd, value);
2112N/A#endif /* AF_INET6 */
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Enable/disable local loopback of multicast datagrams.
0N/A */
0N/Astatic void mcast_set_loop_v4(JNIEnv *env, jobject this, int fd, jobject value) {
0N/A jclass cls;
0N/A jfieldID fid;
0N/A jboolean on;
0N/A char loopback;
0N/A
0N/A cls = (*env)->FindClass(env, "java/lang/Boolean");
0N/A CHECK_NULL(cls);
0N/A fid = (*env)->GetFieldID(env, cls, "value", "Z");
0N/A CHECK_NULL(fid);
0N/A
0N/A on = (*env)->GetBooleanField(env, value, fid);
0N/A loopback = (!on ? 1 : 0);
0N/A
0N/A if (NET_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (const void *)&loopback, sizeof(char)) < 0) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
0N/A return;
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Enable/disable local loopback of multicast datagrams.
0N/A */
2112N/A#ifdef AF_INET6
0N/Astatic void mcast_set_loop_v6(JNIEnv *env, jobject this, int fd, jobject value) {
0N/A jclass cls;
0N/A jfieldID fid;
0N/A jboolean on;
0N/A int loopback;
0N/A
0N/A cls = (*env)->FindClass(env, "java/lang/Boolean");
0N/A CHECK_NULL(cls);
0N/A fid = (*env)->GetFieldID(env, cls, "value", "Z");
0N/A CHECK_NULL(fid);
0N/A
0N/A on = (*env)->GetBooleanField(env, value, fid);
0N/A loopback = (!on ? 1 : 0);
0N/A
0N/A if (NET_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const void *)&loopback, sizeof(int)) < 0) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
0N/A return;
0N/A }
0N/A
0N/A#ifdef __linux__
0N/A /*
0N/A * Can't query IPV6_MULTICAST_LOOP on Linux 2.2 kernel so
0N/A * store it in impl so that we can simulate getsockopt.
0N/A */
0N/A if (isOldKernel) {
0N/A (*env)->SetBooleanField(env, this, pdsi_loopbackID, on);
0N/A }
0N/A#endif
0N/A}
2112N/A#endif /* AF_INET6 */
0N/A
0N/A/*
0N/A * Sets the multicast loopback mode.
0N/A */
0N/Astatic void setMulticastLoopbackMode(JNIEnv *env, jobject this, int fd,
0N/A jint opt, jobject value) {
2112N/A#ifdef AF_INET6
5474N/A#ifdef __linux__
5474N/A mcast_set_loop_v4(env, this, fd, value);
5474N/A if (ipv6_available()) {
5474N/A mcast_set_loop_v6(env, this, fd, value);
5474N/A }
5474N/A#else /* __linux__ not defined */
0N/A if (ipv6_available()) {
0N/A mcast_set_loop_v6(env, this, fd, value);
0N/A } else {
0N/A mcast_set_loop_v4(env, this, fd, value);
0N/A }
5474N/A#endif /* __linux__ */
2112N/A#else
2112N/A mcast_set_loop_v4(env, this, fd, value);
2112N/A#endif /* AF_INET6 */
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: socketSetOption
0N/A * Signature: (ILjava/lang/Object;)V
0N/A */
0N/AJNIEXPORT void JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_socketSetOption(JNIEnv *env,
0N/A jobject this,
0N/A jint opt,
0N/A jobject value) {
0N/A int fd;
0N/A int level, optname, optlen;
0N/A union {
0N/A int i;
0N/A char c;
0N/A } optval;
0N/A
0N/A /*
0N/A * Check that socket hasn't been closed
0N/A */
0N/A fd = getFD(env, this);
0N/A if (fd < 0) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Socket closed");
0N/A return;
0N/A }
0N/A
0N/A /*
0N/A * Check argument has been provided
0N/A */
0N/A if (IS_NULL(value)) {
0N/A JNU_ThrowNullPointerException(env, "value argument");
0N/A return;
0N/A }
0N/A
0N/A /*
0N/A * Setting the multicast interface handled seperately
0N/A */
0N/A if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
0N/A opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
0N/A
0N/A setMulticastInterface(env, this, fd, opt, value);
0N/A return;
0N/A }
0N/A
0N/A /*
0N/A * Setting the multicast loopback mode handled separately
0N/A */
0N/A if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP) {
0N/A setMulticastLoopbackMode(env, this, fd, opt, value);
0N/A return;
0N/A }
0N/A
0N/A /*
0N/A * Map the Java level socket option to the platform specific
0N/A * level and option name.
0N/A */
0N/A if (NET_MapSocketOption(opt, &level, &optname)) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
0N/A return;
0N/A }
0N/A
0N/A switch (opt) {
0N/A case java_net_SocketOptions_SO_SNDBUF :
0N/A case java_net_SocketOptions_SO_RCVBUF :
0N/A case java_net_SocketOptions_IP_TOS :
0N/A {
0N/A jclass cls;
0N/A jfieldID fid;
0N/A
0N/A cls = (*env)->FindClass(env, "java/lang/Integer");
0N/A CHECK_NULL(cls);
0N/A fid = (*env)->GetFieldID(env, cls, "value", "I");
0N/A CHECK_NULL(fid);
0N/A
0N/A optval.i = (*env)->GetIntField(env, value, fid);
0N/A optlen = sizeof(optval.i);
0N/A break;
0N/A }
0N/A
0N/A case java_net_SocketOptions_SO_REUSEADDR:
0N/A case java_net_SocketOptions_SO_BROADCAST:
0N/A {
0N/A jclass cls;
0N/A jfieldID fid;
0N/A jboolean on;
0N/A
0N/A cls = (*env)->FindClass(env, "java/lang/Boolean");
0N/A CHECK_NULL(cls);
0N/A fid = (*env)->GetFieldID(env, cls, "value", "Z");
0N/A CHECK_NULL(fid);
0N/A
0N/A on = (*env)->GetBooleanField(env, value, fid);
0N/A
0N/A /* SO_REUSEADDR or SO_BROADCAST */
0N/A optval.i = (on ? 1 : 0);
0N/A optlen = sizeof(optval.i);
0N/A
0N/A break;
0N/A }
0N/A
0N/A default :
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Socket option not supported by PlainDatagramSocketImp");
0N/A break;
0N/A
0N/A }
0N/A
0N/A if (NET_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
0N/A return;
0N/A }
0N/A}
0N/A
0N/A
0N/A/*
0N/A * Return the multicast interface:
0N/A *
0N/A * SocketOptions.IP_MULTICAST_IF
0N/A * IPv4: Query IPPROTO_IP/IP_MULTICAST_IF
0N/A * Create InetAddress
0N/A * IP_MULTICAST_IF returns struct ip_mreqn on 2.2
0N/A * kernel but struct in_addr on 2.4 kernel
0N/A * IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF or
0N/A * obtain from impl is Linux 2.2 kernel
0N/A * If index == 0 return InetAddress representing
0N/A * anyLocalAddress.
0N/A * If index > 0 query NetworkInterface by index
0N/A * and returns addrs[0]
0N/A *
0N/A * SocketOptions.IP_MULTICAST_IF2
0N/A * IPv4: Query IPPROTO_IP/IP_MULTICAST_IF
0N/A * Query NetworkInterface by IP address and
0N/A * return the NetworkInterface that the address
0N/A * is bound too.
0N/A * IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
0N/A * (except Linux .2 kernel)
0N/A * Query NetworkInterface by index and
0N/A * return NetworkInterface.
0N/A */
0N/Ajobject getMulticastInterface(JNIEnv *env, jobject this, int fd, jint opt) {
0N/A jboolean isIPV4 = JNI_TRUE;
0N/A
0N/A#ifdef AF_INET6
0N/A if (ipv6_available()) {
0N/A isIPV4 = JNI_FALSE;
0N/A }
0N/A#endif
0N/A
0N/A /*
0N/A * IPv4 implementation
0N/A */
0N/A if (isIPV4) {
0N/A static jclass inet4_class;
0N/A static jmethodID inet4_ctrID;
0N/A
0N/A static jclass ni_class;
0N/A static jmethodID ni_ctrID;
0N/A static jfieldID ni_indexID;
0N/A static jfieldID ni_addrsID;
0N/A
0N/A jobjectArray addrArray;
0N/A jobject addr;
0N/A jobject ni;
0N/A
0N/A struct in_addr in;
0N/A struct in_addr *inP = &in;
0N/A int len = sizeof(struct in_addr);
0N/A
0N/A#ifdef __linux__
0N/A struct ip_mreqn mreqn;
0N/A if (isOldKernel) {
0N/A inP = (struct in_addr *)&mreqn;
0N/A len = sizeof(struct ip_mreqn);
0N/A }
0N/A#endif
0N/A
0N/A if (JVM_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF,
0N/A (char *)inP, &len) < 0) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Error getting socket option");
0N/A return NULL;
0N/A }
0N/A
0N/A /*
0N/A * Construct and populate an Inet4Address
0N/A */
0N/A if (inet4_class == NULL) {
0N/A jclass c = (*env)->FindClass(env, "java/net/Inet4Address");
0N/A CHECK_NULL_RETURN(c, NULL);
0N/A inet4_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
0N/A CHECK_NULL_RETURN(inet4_ctrID, NULL);
0N/A inet4_class = (*env)->NewGlobalRef(env, c);
0N/A CHECK_NULL_RETURN(inet4_class, NULL);
0N/A }
0N/A addr = (*env)->NewObject(env, inet4_class, inet4_ctrID, 0);
0N/A CHECK_NULL_RETURN(addr, NULL);
0N/A
0N/A#ifdef __linux__
5888N/A setInetAddress_addr(env, addr, (isOldKernel ?
5888N/A ntohl(mreqn.imr_address.s_addr) : ntohl(in.s_addr)));
0N/A#else
5888N/A setInetAddress_addr(env, addr, ntohl(in.s_addr));
0N/A#endif
0N/A
0N/A /*
0N/A * For IP_MULTICAST_IF return InetAddress
0N/A */
0N/A if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
0N/A return addr;
0N/A }
0N/A
0N/A /*
0N/A * For IP_MULTICAST_IF2 we get the NetworkInterface for
0N/A * this address and return it
0N/A */
0N/A if (ni_class == NULL) {
0N/A jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
0N/A CHECK_NULL_RETURN(c, NULL);
0N/A ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
0N/A CHECK_NULL_RETURN(ni_ctrID, NULL);
0N/A ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
0N/A CHECK_NULL_RETURN(ni_indexID, NULL);
0N/A ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
0N/A "[Ljava/net/InetAddress;");
0N/A CHECK_NULL_RETURN(ni_addrsID, NULL);
0N/A ni_class = (*env)->NewGlobalRef(env, c);
0N/A CHECK_NULL_RETURN(ni_class, NULL);
0N/A }
0N/A ni = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, addr);
0N/A if (ni) {
0N/A return ni;
0N/A }
0N/A
0N/A /*
0N/A * The address doesn't appear to be bound at any known
0N/A * NetworkInterface. Therefore we construct a NetworkInterface
0N/A * with this address.
0N/A */
0N/A ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
0N/A CHECK_NULL_RETURN(ni, NULL);
0N/A
0N/A (*env)->SetIntField(env, ni, ni_indexID, -1);
0N/A addrArray = (*env)->NewObjectArray(env, 1, inet4_class, NULL);
0N/A CHECK_NULL_RETURN(addrArray, NULL);
0N/A (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
0N/A (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
0N/A return ni;
0N/A }
0N/A
0N/A
0N/A#ifdef AF_INET6
0N/A /*
0N/A * IPv6 implementation
0N/A */
0N/A if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) ||
0N/A (opt == java_net_SocketOptions_IP_MULTICAST_IF2)) {
0N/A
0N/A static jclass ni_class;
0N/A static jmethodID ni_ctrID;
0N/A static jfieldID ni_indexID;
0N/A static jfieldID ni_addrsID;
0N/A static jclass ia_class;
0N/A static jmethodID ia_anyLocalAddressID;
0N/A
0N/A int index;
0N/A int len = sizeof(index);
0N/A
0N/A jobjectArray addrArray;
0N/A jobject addr;
0N/A jobject ni;
0N/A
0N/A#ifdef __linux__
0N/A /*
0N/A * Linux 2.2 kernel doesn't support IPV6_MULTICAST_IF socke option
0N/A * so use cached index.
0N/A */
0N/A if (isOldKernel) {
0N/A index = (*env)->GetIntField(env, this, pdsi_multicastInterfaceID);
0N/A } else
0N/A#endif
0N/A {
0N/A if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
0N/A (char*)&index, &len) < 0) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Error getting socket option");
0N/A return NULL;
0N/A }
0N/A }
0N/A
0N/A if (ni_class == NULL) {
0N/A jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
0N/A CHECK_NULL_RETURN(c, NULL);
0N/A ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
0N/A CHECK_NULL_RETURN(ni_ctrID, NULL);
0N/A ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
0N/A CHECK_NULL_RETURN(ni_indexID, NULL);
0N/A ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
0N/A "[Ljava/net/InetAddress;");
0N/A CHECK_NULL_RETURN(ni_addrsID, NULL);
0N/A
0N/A ia_class = (*env)->FindClass(env, "java/net/InetAddress");
0N/A CHECK_NULL_RETURN(ia_class, NULL);
0N/A ia_class = (*env)->NewGlobalRef(env, ia_class);
0N/A CHECK_NULL_RETURN(ia_class, NULL);
0N/A ia_anyLocalAddressID = (*env)->GetStaticMethodID(env,
0N/A ia_class,
0N/A "anyLocalAddress",
0N/A "()Ljava/net/InetAddress;");
0N/A CHECK_NULL_RETURN(ia_anyLocalAddressID, NULL);
0N/A ni_class = (*env)->NewGlobalRef(env, c);
0N/A CHECK_NULL_RETURN(ni_class, NULL);
0N/A }
0N/A
0N/A /*
0N/A * If multicast to a specific interface then return the
0N/A * interface (for IF2) or the any address on that interface
0N/A * (for IF).
0N/A */
0N/A if (index > 0) {
509N/A ni = Java_java_net_NetworkInterface_getByIndex0(env, ni_class,
0N/A index);
0N/A if (ni == NULL) {
0N/A char errmsg[255];
0N/A sprintf(errmsg,
0N/A "IPV6_MULTICAST_IF returned index to unrecognized interface: %d",
0N/A index);
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", errmsg);
0N/A return NULL;
0N/A }
0N/A
0N/A /*
0N/A * For IP_MULTICAST_IF2 return the NetworkInterface
0N/A */
0N/A if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
0N/A return ni;
0N/A }
0N/A
0N/A /*
0N/A * For IP_MULTICAST_IF return addrs[0]
0N/A */
0N/A addrArray = (*env)->GetObjectField(env, ni, ni_addrsID);
0N/A if ((*env)->GetArrayLength(env, addrArray) < 1) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "IPV6_MULTICAST_IF returned interface without IP bindings");
0N/A return NULL;
0N/A }
0N/A
0N/A addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
0N/A return addr;
0N/A }
0N/A
0N/A /*
0N/A * Multicast to any address - return anyLocalAddress
0N/A * or a NetworkInterface with addrs[0] set to anyLocalAddress
0N/A */
0N/A
0N/A addr = (*env)->CallStaticObjectMethod(env, ia_class, ia_anyLocalAddressID,
0N/A NULL);
0N/A if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
0N/A return addr;
0N/A }
0N/A
0N/A ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
0N/A CHECK_NULL_RETURN(ni, NULL);
0N/A (*env)->SetIntField(env, ni, ni_indexID, -1);
0N/A addrArray = (*env)->NewObjectArray(env, 1, ia_class, NULL);
0N/A CHECK_NULL_RETURN(addrArray, NULL);
0N/A (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
0N/A (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
0N/A return ni;
0N/A }
0N/A#endif
0N/A return NULL;
0N/A}
0N/A
0N/A
0N/A
0N/A/*
0N/A * Returns relevant info as a jint.
0N/A *
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: socketGetOption
0N/A * Signature: (I)Ljava/lang/Object;
0N/A */
0N/AJNIEXPORT jobject JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_socketGetOption(JNIEnv *env, jobject this,
0N/A jint opt) {
0N/A int fd;
0N/A int level, optname, optlen;
0N/A union {
0N/A int i;
0N/A char c;
0N/A } optval;
0N/A
0N/A fd = getFD(env, this);
0N/A if (fd < 0) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "socket closed");
0N/A return NULL;
0N/A }
0N/A
0N/A /*
0N/A * Handle IP_MULTICAST_IF seperately
0N/A */
0N/A if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
0N/A opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
0N/A return getMulticastInterface(env, this, fd, opt);
0N/A
0N/A }
0N/A
0N/A /*
0N/A * SO_BINDADDR implemented using getsockname
0N/A */
0N/A if (opt == java_net_SocketOptions_SO_BINDADDR) {
0N/A /* find out local IP address */
0N/A SOCKADDR him;
2112N/A socklen_t len = 0;
0N/A int port;
0N/A jobject iaObj;
0N/A
0N/A len = SOCKADDR_LEN;
0N/A
0N/A if (getsockname(fd, (struct sockaddr *)&him, &len) == -1) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Error getting socket name");
0N/A return NULL;
0N/A }
0N/A iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port);
0N/A
0N/A return iaObj;
0N/A }
0N/A
0N/A /*
0N/A * Map the Java level socket option to the platform specific
0N/A * level and option name.
0N/A */
0N/A if (NET_MapSocketOption(opt, &level, &optname)) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
0N/A return NULL;
0N/A }
0N/A
0N/A /*
0N/A * IP_MULTICAST_LOOP socket option isn't available on Linux 2.2
0N/A * kernel with IPv6 so return value stored in impl.
0N/A */
0N/A#if defined(AF_INET6) && defined(__linux__)
0N/A if (isOldKernel && opt == java_net_SocketOptions_IP_MULTICAST_LOOP &&
0N/A level == IPPROTO_IPV6) {
0N/A int mode = (int)(*env)->GetBooleanField(env, this, pdsi_loopbackID);
0N/A return createBoolean(env, mode);
0N/A }
0N/A#endif
0N/A
0N/A if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP &&
0N/A level == IPPROTO_IP) {
0N/A optlen = sizeof(optval.c);
0N/A } else {
0N/A optlen = sizeof(optval.i);
0N/A }
0N/A
0N/A if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Error getting socket option");
0N/A return NULL;
0N/A }
0N/A
0N/A switch (opt) {
0N/A case java_net_SocketOptions_IP_MULTICAST_LOOP:
0N/A /* getLoopbackMode() returns true if IP_MULTICAST_LOOP disabled */
0N/A if (level == IPPROTO_IP) {
0N/A return createBoolean(env, (int)!optval.c);
0N/A } else {
0N/A return createBoolean(env, !optval.i);
0N/A }
0N/A
0N/A case java_net_SocketOptions_SO_BROADCAST:
0N/A case java_net_SocketOptions_SO_REUSEADDR:
0N/A return createBoolean(env, optval.i);
0N/A
0N/A case java_net_SocketOptions_SO_SNDBUF:
0N/A case java_net_SocketOptions_SO_RCVBUF:
0N/A case java_net_SocketOptions_IP_TOS:
0N/A return createInteger(env, optval.i);
0N/A
0N/A }
0N/A
0N/A /* should never rearch here */
0N/A return NULL;
0N/A}
0N/A
0N/A/*
0N/A * Multicast-related calls
0N/A */
0N/A
0N/AJNIEXPORT void JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_setTTL(JNIEnv *env, jobject this,
0N/A jbyte ttl) {
0N/A jint ittl = ttl;
0N/A if (ittl < 0) {
0N/A ittl += 0x100;
0N/A }
0N/A Java_java_net_PlainDatagramSocketImpl_setTimeToLive(env, this, ittl);
0N/A}
0N/A
0N/A/*
0N/A * Set TTL for a socket. Throw exception if failed.
0N/A */
0N/Astatic void setTTL(JNIEnv *env, int fd, jint ttl) {
0N/A char ittl = (char)ttl;
0N/A if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl,
0N/A sizeof(ittl)) < 0) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Error setting socket option");
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Set hops limit for a socket. Throw exception if failed.
0N/A */
2112N/A#ifdef AF_INET6
0N/Astatic void setHopLimit(JNIEnv *env, int fd, jint ttl) {
0N/A int ittl = (int)ttl;
0N/A if (JVM_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
0N/A (char*)&ittl, sizeof(ittl)) < 0) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Error setting socket option");
0N/A }
0N/A}
2112N/A#endif
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: setTTL
0N/A * Signature: (B)V
0N/A */
0N/AJNIEXPORT void JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_setTimeToLive(JNIEnv *env, jobject this,
0N/A jint ttl) {
0N/A
0N/A jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
0N/A int fd;
0N/A /* it is important to cast this to a char, otherwise setsockopt gets confused */
0N/A
0N/A if (IS_NULL(fdObj)) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Socket closed");
0N/A return;
0N/A } else {
0N/A fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
0N/A }
0N/A /* setsockopt to be correct ttl */
2112N/A#ifdef AF_INET6
0N/A#ifdef __linux__
0N/A setTTL(env, fd, ttl);
0N/A if (ipv6_available()) {
0N/A setHopLimit(env, fd, ttl);
0N/A if (isOldKernel) {
0N/A (*env)->SetIntField(env, this, pdsi_ttlID, ttl);
0N/A }
0N/A }
5474N/A#else /* __linux__ not defined */
5474N/A if (ipv6_available()) {
5474N/A setHopLimit(env, fd, ttl);
5474N/A } else {
5474N/A setTTL(env, fd, ttl);
5474N/A }
5474N/A#endif /* __linux__ */
2112N/A#else
2112N/A setTTL(env, fd, ttl);
2112N/A#endif /* AF_INET6 */
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: getTTL
0N/A * Signature: ()B
0N/A */
0N/AJNIEXPORT jbyte JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_getTTL(JNIEnv *env, jobject this) {
0N/A return (jbyte)Java_java_net_PlainDatagramSocketImpl_getTimeToLive(env, this);
0N/A}
0N/A
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: getTTL
0N/A * Signature: ()B
0N/A */
0N/AJNIEXPORT jint JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_getTimeToLive(JNIEnv *env, jobject this) {
0N/A
0N/A jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
0N/A jint fd = -1;
0N/A
0N/A if (IS_NULL(fdObj)) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Socket closed");
0N/A return -1;
0N/A } else {
0N/A fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
0N/A }
0N/A /* getsockopt of ttl */
0N/A#ifdef AF_INET6
0N/A if (ipv6_available()) {
0N/A int ttl = 0;
0N/A int len = sizeof(ttl);
0N/A
0N/A#ifdef __linux__
0N/A /*
0N/A * Linux 2.2 kernel doesn't support IPV6_MULTICAST_HOPS socket option
0N/A */
0N/A if (isOldKernel) {
0N/A return (*env)->GetIntField(env, this, pdsi_ttlID);
0N/A }
0N/A#endif
0N/A
0N/A if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
0N/A (char*)&ttl, &len) < 0) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Error getting socket option");
0N/A return -1;
0N/A }
0N/A return (jint)ttl;
0N/A } else
0N/A#endif /* AF_INET6 */
0N/A {
0N/A u_char ttl = 0;
0N/A int len = sizeof(ttl);
0N/A if (JVM_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
0N/A (char*)&ttl, &len) < 0) {
0N/A NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
0N/A "Error getting socket option");
0N/A return -1;
0N/A }
0N/A return (jint)ttl;
0N/A }
0N/A}
0N/A
0N/A
0N/A/*
0N/A * mcast_join_leave: Join or leave a multicast group.
0N/A *
0N/A * For IPv4 sockets use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
0N/A * to join/leave multicast group.
0N/A *
0N/A * For IPv6 sockets use IPV6_ADD_MEMBERSHIP/IPV6_DROP_MEMBERSHIP socket option
0N/A * to join/leave multicast group. If multicast group is an IPv4 address then
0N/A * an IPv4-mapped address is used.
0N/A *
0N/A * On Linux with IPv6 if we wish to join/leave an IPv4 multicast group then
0N/A * we must use the IPv4 socket options. This is because the IPv6 socket options
0N/A * don't support IPv4-mapped addresses. This is true as per 2.2.19 and 2.4.7
0N/A * kernel releases. In the future it's possible that IP_ADD_MEMBERSHIP
0N/A * will be updated to return ENOPROTOOPT if uses with an IPv6 socket (Solaris
0N/A * already does this). Thus to cater for this we first try with the IPv4
0N/A * socket options and if they fail we use the IPv6 socket options. This
0N/A * seems a reasonable failsafe solution.
0N/A */
0N/Astatic void mcast_join_leave(JNIEnv *env, jobject this,
0N/A jobject iaObj, jobject niObj,
0N/A jboolean join) {
0N/A
0N/A jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
0N/A jint fd;
0N/A jint ipv6_join_leave;
0N/A
0N/A if (IS_NULL(fdObj)) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Socket closed");
0N/A return;
0N/A } else {
0N/A fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
0N/A }
0N/A if (IS_NULL(iaObj)) {
0N/A JNU_ThrowNullPointerException(env, "iaObj");
0N/A return;
0N/A }
0N/A
0N/A /*
0N/A * Determine if this is an IPv4 or IPv6 join/leave.
0N/A */
0N/A#ifdef AF_INET6
0N/A ipv6_join_leave = ipv6_available();
0N/A
0N/A#ifdef __linux__
5888N/A if (getInetAddress_family(env, iaObj) == IPv4) {
0N/A ipv6_join_leave = JNI_FALSE;
0N/A }
0N/A#endif
0N/A
0N/A#else
0N/A /*
0N/A * IPv6 not compiled in
0N/A */
0N/A ipv6_join_leave = JNI_FALSE;
0N/A#endif
0N/A
0N/A /*
0N/A * For IPv4 join use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
0N/A *
0N/A * On Linux if IPv4 or IPv6 use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP
0N/A */
0N/A if (!ipv6_join_leave) {
0N/A#ifdef __linux__
0N/A struct ip_mreqn mname;
0N/A#else
0N/A struct ip_mreq mname;
0N/A#endif
0N/A int mname_len;
0N/A
0N/A /*
0N/A * joinGroup(InetAddress, NetworkInterface) implementation :-
0N/A *
0N/A * Linux/IPv6: use ip_mreqn structure populated with multicast
0N/A * address and interface index.
0N/A *
0N/A * IPv4: use ip_mreq structure populated with multicast
0N/A * address and first address obtained from
0N/A * NetworkInterface
0N/A */
0N/A if (niObj != NULL) {
0N/A#if defined(__linux__) && defined(AF_INET6)
0N/A if (ipv6_available()) {
0N/A static jfieldID ni_indexID;
0N/A
0N/A if (ni_indexID == NULL) {
0N/A jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
0N/A CHECK_NULL(c);
0N/A ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
0N/A CHECK_NULL(ni_indexID);
0N/A }
0N/A
5888N/A mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
0N/A mname.imr_address.s_addr = 0;
0N/A mname.imr_ifindex = (*env)->GetIntField(env, niObj, ni_indexID);
0N/A mname_len = sizeof(struct ip_mreqn);
0N/A } else
0N/A#endif
0N/A {
0N/A jobjectArray addrArray = (*env)->GetObjectField(env, niObj, ni_addrsID);
0N/A jobject addr;
0N/A
0N/A if ((*env)->GetArrayLength(env, addrArray) < 1) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "bad argument for IP_ADD_MEMBERSHIP: "
0N/A "No IP addresses bound to interface");
0N/A return;
0N/A }
0N/A addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
0N/A
5888N/A mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
0N/A#ifdef __linux__
5888N/A mname.imr_address.s_addr = htonl(getInetAddress_addr(env, addr));
0N/A#else
5888N/A mname.imr_interface.s_addr = htonl(getInetAddress_addr(env, addr));
0N/A#endif
0N/A mname_len = sizeof(struct ip_mreq);
0N/A }
0N/A }
0N/A
0N/A
0N/A /*
0N/A * joinGroup(InetAddress) implementation :-
0N/A *
0N/A * Linux/IPv6: use ip_mreqn structure populated with multicast
0N/A * address and interface index. index obtained
0N/A * from cached value or IPV6_MULTICAST_IF.
0N/A *
0N/A * IPv4: use ip_mreq structure populated with multicast
0N/A * address and local address obtained from
0N/A * IP_MULTICAST_IF. On Linux IP_MULTICAST_IF
0N/A * returns different structure depending on
0N/A * kernel.
0N/A */
0N/A
0N/A if (niObj == NULL) {
0N/A
0N/A#if defined(__linux__) && defined(AF_INET6)
0N/A if (ipv6_available()) {
0N/A
0N/A int index;
0N/A int len = sizeof(index);
0N/A
0N/A if (isOldKernel) {
0N/A index = (*env)->GetIntField(env, this, pdsi_multicastInterfaceID);
0N/A } else {
0N/A if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
0N/A (char*)&index, &len) < 0) {
0N/A NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
0N/A return;
0N/A }
0N/A }
0N/A
5888N/A mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
0N/A mname.imr_address.s_addr = 0 ;
0N/A mname.imr_ifindex = index;
0N/A mname_len = sizeof(struct ip_mreqn);
0N/A } else
0N/A#endif
0N/A {
0N/A struct in_addr in;
0N/A struct in_addr *inP = &in;
2920N/A socklen_t len = sizeof(struct in_addr);
0N/A
0N/A#ifdef __linux__
0N/A struct ip_mreqn mreqn;
0N/A if (isOldKernel) {
0N/A inP = (struct in_addr *)&mreqn;
0N/A len = sizeof(struct ip_mreqn);
0N/A }
0N/A#endif
0N/A if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)inP, &len) < 0) {
0N/A NET_ThrowCurrent(env, "getsockopt IP_MULTICAST_IF failed");
0N/A return;
0N/A }
0N/A
0N/A#ifdef __linux__
0N/A mname.imr_address.s_addr =
0N/A (isOldKernel ? mreqn.imr_address.s_addr : in.s_addr);
0N/A
0N/A#else
0N/A mname.imr_interface.s_addr = in.s_addr;
0N/A#endif
5888N/A mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
0N/A mname_len = sizeof(struct ip_mreq);
0N/A }
0N/A }
0N/A
0N/A
0N/A /*
0N/A * Join the multicast group.
0N/A */
0N/A if (JVM_SetSockOpt(fd, IPPROTO_IP, (join ? IP_ADD_MEMBERSHIP:IP_DROP_MEMBERSHIP),
0N/A (char *) &mname, mname_len) < 0) {
0N/A
0N/A /*
0N/A * If IP_ADD_MEMBERSHIP returns ENOPROTOOPT on Linux and we've got
0N/A * IPv6 enabled then it's possible that the kernel has been fixed
0N/A * so we switch to IPV6_ADD_MEMBERSHIP socket option.
0N/A * As of 2.4.7 kernel IPV6_ADD_MEMERSHIP can't handle IPv4-mapped
0N/A * addresses so we have to use IP_ADD_MEMERSHIP for IPv4 multicast
0N/A * groups. However if the socket is an IPv6 socket then then setsockopt
0N/A * should reurn ENOPROTOOPT. We assume this will be fixed in Linux
0N/A * at some stage.
0N/A */
0N/A#if defined(__linux__) && defined(AF_INET6)
0N/A if (errno == ENOPROTOOPT) {
0N/A if (ipv6_available()) {
0N/A ipv6_join_leave = JNI_TRUE;
0N/A errno = 0;
0N/A } else {
0N/A errno = ENOPROTOOPT; /* errno can be changed by ipv6_available */
0N/A }
0N/A }
0N/A#endif
0N/A if (errno) {
0N/A if (join) {
0N/A NET_ThrowCurrent(env, "setsockopt IP_ADD_MEMBERSHIP failed");
0N/A } else {
0N/A if (errno == ENOENT)
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Not a member of the multicast group");
0N/A else
0N/A NET_ThrowCurrent(env, "setsockopt IP_DROP_MEMBERSHIP failed");
0N/A }
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * If we haven't switched to IPv6 socket option then we're done.
0N/A */
0N/A if (!ipv6_join_leave) {
0N/A return;
0N/A }
0N/A }
0N/A
0N/A
0N/A /*
0N/A * IPv6 join. If it's an IPv4 multicast group then we use an IPv4-mapped
0N/A * address.
0N/A */
0N/A#ifdef AF_INET6
0N/A {
0N/A struct ipv6_mreq mname6;
0N/A jbyteArray ipaddress;
0N/A jbyte caddr[16];
0N/A jint family;
0N/A jint address;
5888N/A family = getInetAddress_family(env, iaObj) == IPv4? AF_INET : AF_INET6;
0N/A if (family == AF_INET) { /* will convert to IPv4-mapped address */
0N/A memset((char *) caddr, 0, 16);
5888N/A address = getInetAddress_addr(env, iaObj);
0N/A
0N/A caddr[10] = 0xff;
0N/A caddr[11] = 0xff;
0N/A
0N/A caddr[12] = ((address >> 24) & 0xff);
0N/A caddr[13] = ((address >> 16) & 0xff);
0N/A caddr[14] = ((address >> 8) & 0xff);
0N/A caddr[15] = (address & 0xff);
0N/A } else {
0N/A ipaddress = (*env)->GetObjectField(env, iaObj, ia6_ipaddressID);
0N/A (*env)->GetByteArrayRegion(env, ipaddress, 0, 16, caddr);
0N/A }
0N/A
0N/A memcpy((void *)&(mname6.ipv6mr_multiaddr), caddr, sizeof(struct in6_addr));
0N/A if (IS_NULL(niObj)) {
0N/A int index;
0N/A int len = sizeof(index);
0N/A
0N/A#ifdef __linux__
0N/A /*
0N/A * 2.2 kernel doens't support IPV6_MULTICAST_IF socket option
0N/A */
0N/A if (isOldKernel) {
0N/A index = (*env)->GetIntField(env, this, pdsi_multicastInterfaceID);
0N/A } else
0N/A#endif
0N/A {
0N/A if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
0N/A (char*)&index, &len) < 0) {
0N/A NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
0N/A return;
0N/A }
0N/A }
0N/A
0N/A#ifdef __linux__
0N/A /*
0N/A * On 2.4.8+ if we join a group with the interface set to 0
0N/A * then the kernel records the interface it decides. This causes
0N/A * subsequent leave groups to fail as there is no match. Thus we
0N/A * pick the interface if there is a matching route.
0N/A */
0N/A if (index == 0 && !isOldKernel) {
0N/A int rt_index = getDefaultIPv6Interface(&(mname6.ipv6mr_multiaddr));
0N/A if (rt_index > 0) {
0N/A index = rt_index;
0N/A }
0N/A }
0N/A#endif
4805N/A#ifdef MACOSX
4805N/A if (family == AF_INET6 && index == 0) {
4805N/A index = getDefaultScopeID(env);
4805N/A }
4805N/A#endif
0N/A mname6.ipv6mr_interface = index;
0N/A } else {
0N/A jint idx = (*env)->GetIntField(env, niObj, ni_indexID);
0N/A mname6.ipv6mr_interface = idx;
0N/A }
0N/A
4632N/A#if defined(_ALLBSD_SOURCE)
4632N/A#define ADD_MEMBERSHIP IPV6_JOIN_GROUP
4632N/A#define DRP_MEMBERSHIP IPV6_LEAVE_GROUP
4632N/A#define S_ADD_MEMBERSHIP "IPV6_JOIN_GROUP"
4632N/A#define S_DRP_MEMBERSHIP "IPV6_LEAVE_GROUP"
4632N/A#else
4632N/A#define ADD_MEMBERSHIP IPV6_ADD_MEMBERSHIP
4632N/A#define DRP_MEMBERSHIP IPV6_DROP_MEMBERSHIP
4632N/A#define S_ADD_MEMBERSHIP "IPV6_ADD_MEMBERSHIP"
4632N/A#define S_DRP_MEMBERSHIP "IPV6_DROP_MEMBERSHIP"
4632N/A#endif
4632N/A
0N/A /* Join the multicast group */
4632N/A if (JVM_SetSockOpt(fd, IPPROTO_IPV6, (join ? ADD_MEMBERSHIP : DRP_MEMBERSHIP),
0N/A (char *) &mname6, sizeof (mname6)) < 0) {
0N/A
0N/A if (join) {
4632N/A NET_ThrowCurrent(env, "setsockopt " S_ADD_MEMBERSHIP " failed");
0N/A } else {
0N/A if (errno == ENOENT) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Not a member of the multicast group");
0N/A } else {
4632N/A NET_ThrowCurrent(env, "setsockopt " S_DRP_MEMBERSHIP " failed");
0N/A }
0N/A }
0N/A }
0N/A }
0N/A#endif
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: join
0N/A * Signature: (Ljava/net/InetAddress;)V
0N/A */
0N/AJNIEXPORT void JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_join(JNIEnv *env, jobject this,
0N/A jobject iaObj, jobject niObj)
0N/A{
0N/A mcast_join_leave(env, this, iaObj, niObj, JNI_TRUE);
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_PlainDatagramSocketImpl
0N/A * Method: leave
0N/A * Signature: (Ljava/net/InetAddress;)V
0N/A */
0N/AJNIEXPORT void JNICALL
0N/AJava_java_net_PlainDatagramSocketImpl_leave(JNIEnv *env, jobject this,
0N/A jobject iaObj, jobject niObj)
0N/A{
0N/A mcast_join_leave(env, this, iaObj, niObj, JNI_FALSE);
0N/A}