summaryrefslogtreecommitdiff
path: root/libjava/classpath/native/jni/java-net/gnu_java_net_VMPlainSocketImpl.c
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/native/jni/java-net/gnu_java_net_VMPlainSocketImpl.c')
-rw-r--r--libjava/classpath/native/jni/java-net/gnu_java_net_VMPlainSocketImpl.c965
1 files changed, 965 insertions, 0 deletions
diff --git a/libjava/classpath/native/jni/java-net/gnu_java_net_VMPlainSocketImpl.c b/libjava/classpath/native/jni/java-net/gnu_java_net_VMPlainSocketImpl.c
new file mode 100644
index 000000000..c9620a4e7
--- /dev/null
+++ b/libjava/classpath/native/jni/java-net/gnu_java_net_VMPlainSocketImpl.c
@@ -0,0 +1,965 @@
+/* VMPlainSocketImpl.c - Native methods for PlainSocketImpl class
+ Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <config-int.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <jni.h>
+#include <jcl.h>
+
+#include "cpnative.h"
+#include "cpnet.h"
+#include "cpio.h"
+#include "javanet.h"
+
+#include "gnu_java_net_VMPlainSocketImpl.h"
+
+#define THROW_NO_NETWORK(env) JCL_ThrowException (env, "java/lang/InternalError", "this platform not configured for network support")
+#define THROW_NO_IPV6(env) JCL_ThrowException (env, "java/lang/InternalError", "IPv6 support not available")
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: bind
+ * Signature: (I[BI)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_bind (JNIEnv *env,
+ jclass clazz __attribute__((unused)),
+ jint fd, jbyteArray addr, jint port)
+{
+ struct sockaddr_in sockaddr;
+ jbyte *elems = NULL;
+ int ret;
+
+ if (addr != NULL)
+ elems = (*env)->GetByteArrayElements (env, addr, NULL);
+
+ memset(&sockaddr, 0, sizeof (struct sockaddr_in));
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons (port);
+ /* addr is already in network byte order. */
+ if (elems != NULL)
+ sockaddr.sin_addr.s_addr = *((uint32_t *) elems);
+ else
+ sockaddr.sin_addr.s_addr = INADDR_ANY;
+
+ /* bind(2) from BSD says bind will never return EINTR */
+ /* bind is not a blocking system call */
+ ret = bind (fd, (struct sockaddr *) &sockaddr, sizeof (struct sockaddr_in));
+
+ if (elems != NULL)
+ (*env)->ReleaseByteArrayElements (env, addr, elems, JNI_ABORT);
+
+ if (-1 == ret)
+ JCL_ThrowException (env, BIND_EXCEPTION, strerror (errno));
+
+ cpio_closeOnExec(ret);
+}
+
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: bind6
+ * Signature: (I[BI)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_bind6 (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jbyteArray addr, jint port)
+{
+#ifdef HAVE_INET6
+ struct sockaddr_in6 sockaddr;
+ jbyte *elems;
+ int ret;
+
+ elems = (*env)->GetByteArrayElements (env, addr, NULL);
+
+ memset (&sockaddr, 0, sizeof (struct sockaddr_in6));
+ sockaddr.sin6_family = AF_INET6;
+ sockaddr.sin6_port = htons (port);
+ memcpy (&sockaddr.sin6_addr.s6_addr, elems, 16);
+
+ /* bind(2) from BSD says bind will never return EINTR */
+ /* bind is not a blocking system call */
+ ret = bind (fd, (struct sockaddr *) &sockaddr,
+ sizeof (struct sockaddr_in6));
+
+ (*env)->ReleaseByteArrayElements (env, addr, elems, JNI_ABORT);
+
+ if (-1 == ret)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+#else
+ THROW_NO_IPV6(env);
+#endif
+}
+
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: listen
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_listen (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jint backlog)
+{
+ int ret;
+
+ /* listen(2) says that this call will never return EINTR */
+ /* listen is not a blocking system call */
+ if ((ret = listen (fd, backlog)) == -1)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+}
+
+
+/* These constants are also defined in java/net/SocketOptions.java.
+ * Except for CPNET_IP_TTL which is defined in
+ * vm/reference/gnu/java/net/VMPlainSocketImpl.java .
+ */
+enum java_sockopt {
+ CPNET_SO_KEEPALIVE = 0x8,
+ CPNET_SO_LINGER = 0x80,
+ CPNET_SO_TIMEOUT = 0x1006,
+ CPNET_SO_BINDADDR = 0x0F,
+ CPNET_SO_SNDBUF = 0x1001,
+ CPNET_SO_RCVBUF = 0x1002,
+ CPNET_SO_REUSEADDR = 0x04,
+ CPNET_SO_BROADCAST = 0x20,
+ CPNET_SO_OOBINLINE = 0x1003,
+ CPNET_TCP_NODELAY = 0x01,
+ CPNET_IP_MULTICAST_IF = 0x10,
+ CPNET_IP_MULTICAST_IF2 = 0x1F,
+ CPNET_IP_MULTICAST_LOOP = 0x12,
+ CPNET_IP_TOS = 0x03,
+ CPNET_IP_TTL = 0x1E61
+};
+
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: setOption
+ * Signature: (III)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_setOption (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jint option, jint value)
+{
+ enum java_sockopt joption = (enum java_sockopt) option;
+ int optname = -1;
+ int level = SOL_SOCKET;
+ const int _value = value;
+ struct linger _linger;
+ struct timeval _timeo;
+ void *optval = (void *) &_value;
+ socklen_t optlen = sizeof (int);
+
+ switch (joption)
+ {
+ case CPNET_IP_MULTICAST_LOOP:
+ level = IPPROTO_IP;
+ optname = IP_MULTICAST_LOOP;
+ break;
+
+ case CPNET_SO_KEEPALIVE:
+ optname = SO_KEEPALIVE;
+ break;
+
+ case CPNET_SO_LINGER:
+ optname = SO_LINGER;
+ if (_value == -1)
+ _linger.l_onoff = 0;
+ else
+ _linger.l_onoff = 1;
+ _linger.l_linger = _value;
+ optval = &_linger;
+ optlen = sizeof (struct linger);
+ break;
+
+ case CPNET_SO_TIMEOUT:
+ optname = SO_RCVTIMEO;
+ _timeo.tv_sec = value / 1000;
+ _timeo.tv_usec = (value % 1000) * 1000;
+ optval = &_timeo;
+ optlen = sizeof (struct timeval);
+ break;
+
+ case CPNET_SO_SNDBUF:
+ optname = SO_SNDBUF;
+ break;
+
+ case CPNET_SO_RCVBUF:
+ optname = SO_RCVBUF;
+ break;
+
+ case CPNET_SO_REUSEADDR:
+ optname = SO_REUSEADDR;
+ break;
+
+ case CPNET_SO_BROADCAST:
+ optname = SO_BROADCAST;
+ break;
+
+ case CPNET_SO_OOBINLINE:
+ optname = SO_OOBINLINE;
+ break;
+
+ case CPNET_TCP_NODELAY:
+ level = IPPROTO_TCP;
+ optname = TCP_NODELAY;
+ break;
+
+ case CPNET_IP_TOS:
+ level = IPPROTO_IP;
+ optname = IP_TOS;
+ break;
+
+ case CPNET_IP_TTL:
+ level = IPPROTO_IP;
+ optname = IP_TTL;
+ break;
+
+ case CPNET_SO_BINDADDR:
+ case CPNET_IP_MULTICAST_IF:
+ case CPNET_IP_MULTICAST_IF2:
+ JCL_ThrowException (env, IO_EXCEPTION, "argument not a boolean or integer option");
+ return;
+ }
+
+ if (setsockopt (fd, level, optname, (const void *) optval, optlen) == -1)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+}
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: getOption
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_getOption (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jint option)
+{
+ enum java_sockopt joption = (enum java_sockopt) option;
+ int optname = -1;
+ int level = SOL_SOCKET;
+ int value;
+ struct linger linger;
+ struct timeval timeo;
+ void *optval = &value;
+ socklen_t optlen = sizeof (int);
+
+ switch (joption)
+ {
+ case CPNET_IP_MULTICAST_LOOP:
+ level = IPPROTO_IP;
+ optname = IP_MULTICAST_LOOP;
+ break;
+
+ case CPNET_SO_KEEPALIVE:
+ optname = SO_KEEPALIVE;
+ break;
+
+ case CPNET_SO_LINGER:
+ optname = SO_LINGER;
+ optval = &linger;
+ optlen = sizeof (struct linger);
+ break;
+
+ case CPNET_SO_TIMEOUT:
+ optname = SO_RCVTIMEO;
+ optval = &timeo;
+ optlen = sizeof (struct timeval);
+ break;
+
+ case CPNET_SO_SNDBUF:
+ optname = SO_SNDBUF;
+ break;
+
+ case CPNET_SO_RCVBUF:
+ optname = SO_RCVBUF;
+ break;
+
+ case CPNET_SO_REUSEADDR:
+ optname = SO_REUSEADDR;
+ break;
+
+ case CPNET_SO_BROADCAST:
+ optname = SO_BROADCAST;
+ break;
+
+ case CPNET_SO_OOBINLINE:
+ optname = SO_OOBINLINE;
+ break;
+
+ case CPNET_TCP_NODELAY:
+ level = IPPROTO_TCP;
+ optname = TCP_NODELAY;
+ break;
+
+ case CPNET_IP_TOS:
+ level = IPPROTO_IP;
+ optname = IP_TOS;
+ break;
+
+ case CPNET_IP_TTL:
+ level = IPPROTO_IP;
+ optname = IP_TTL;
+ break;
+
+ case CPNET_SO_BINDADDR:
+ case CPNET_IP_MULTICAST_IF:
+ case CPNET_IP_MULTICAST_IF2:
+ JCL_ThrowException (env, IO_EXCEPTION, "argument not a boolean or integer option");
+ return -1;
+ }
+
+ if (getsockopt (fd, level, optname, optval, &optlen) == -1)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+
+ /* Returns the linger value if it is enabled or -1 in case
+ * it is disabled. This is how the Java API expects it.
+ */
+ if (joption == CPNET_SO_LINGER)
+ return (linger.l_onoff) ? linger.l_linger : -1;
+ if (joption == CPNET_SO_TIMEOUT)
+ return (timeo.tv_sec * 1000) + (timeo.tv_usec / 1000);
+
+ return value;
+}
+
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_setMulticastInterface (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd,
+ jint optionId __attribute__((unused)),
+ jobject addr)
+{
+ int result;
+ cpnet_address *cpaddr = _javanet_get_ip_netaddr (env, addr);
+
+ if ((*env)->ExceptionOccurred (env))
+ return;
+
+ result = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
+ (struct sockaddr *) cpaddr->data, cpaddr->len);
+
+ cpnet_freeAddress (env, cpaddr);
+
+ if (result == -1)
+ JCL_ThrowException (env, SOCKET_EXCEPTION, cpnative_getErrorString (errno));
+}
+
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_setMulticastInterface6 (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd,
+ jint optionId __attribute__((unused)),
+ jstring ifname)
+{
+#ifdef HAVE_SETSOCKOPT
+#ifdef HAVE_INET6
+ int result;
+ const char *str_ifname = JCL_jstring_to_cstring (env, ifname);
+ unsigned int if_index;
+
+ if ((*env)->ExceptionOccurred (env))
+ {
+ JCL_free_cstring(env, ifname, str_ifname);
+ return;
+ }
+
+ if_index = if_nametoindex(str_ifname);
+ if (!if_index)
+ {
+ JCL_free_cstring(env, ifname, str_ifname);
+ JCL_ThrowException (env, SOCKET_EXCEPTION, "interface does not exist");
+ return;
+ }
+
+ result = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ (unsigned int *) &if_index, sizeof(if_index));
+
+ JCL_free_cstring(env, ifname, str_ifname);
+
+ if (result == -1)
+ JCL_ThrowException (env, SOCKET_EXCEPTION, cpnative_getErrorString (errno));
+#else
+ (void) fd;
+ JCL_ThrowException (env, "java/lang/InternalError",
+ "IPv6 support not available");
+#endif /* HAVE_INET6 */
+#else
+ (void) fd;
+ THROW_NO_IPV6(env);
+#endif /* HAVE_SETSOCKOPT */
+}
+
+JNIEXPORT jobject JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_getMulticastInterface (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd,
+ jint optionId __attribute__((unused)))
+{
+ jobject obj;
+ cpnet_address *cpaddr;
+ int result = cpnet_getMulticastIF (env, fd, &cpaddr);
+
+ if (result != CPNATIVE_OK)
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION,
+ cpnative_getErrorString (result));
+ return (0);
+ }
+
+ obj = _javanet_create_inetaddress (env, cpaddr);
+ cpnet_freeAddress (env, cpaddr);
+
+ return obj;
+}
+
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: shutdownInput
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_shutdownInput (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd)
+{
+ if (shutdown (fd, SHUT_RD) == -1)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+}
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: shutdownOutput
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_shutdownOutput (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd)
+{
+ if (shutdown (fd, SHUT_WR) == -1)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+}
+
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: sendUrgentData
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_sendUrgentData (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jint data)
+{
+ const char x = (char) data;
+
+ if (send (fd, &x, 1, MSG_OOB) == -1)
+ JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
+}
+
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: join
+ * Signature: (I[B)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_join (JNIEnv *env,
+ jclass clazz __attribute__((unused)),
+ jint fd, jbyteArray addr)
+{
+#ifdef HAVE_SETSOCKOPT
+ struct ip_mreq maddr;
+ jbyte *addr_elems;
+
+ addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
+ if (addr_elems == NULL)
+ return;
+
+ maddr.imr_multiaddr.s_addr = * ((uint32_t *) addr_elems);
+ maddr.imr_interface.s_addr = INADDR_ANY;
+
+ (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
+
+ if (-1 == setsockopt (fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &maddr, sizeof (struct ip_mreq)))
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+#else
+ (void) fd;
+ (void) addr;
+ JCL_ThrowException (env, "java/lang/InternalError",
+ "socket options not supported");
+#endif /* HAVE_SETSOCKOPT */
+}
+
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: join6
+ * Signature: (I[B)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_join6 (JNIEnv *env,
+ jclass clazz __attribute__((unused)),
+ jint fd, jbyteArray addr)
+{
+#ifdef HAVE_SETSOCKOPT
+#ifdef HAVE_INET6
+ struct ipv6_mreq maddr;
+ jbyte *addr_elems;
+
+ addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
+ if (addr_elems == NULL)
+ return;
+
+ memcpy (&(maddr.ipv6mr_multiaddr.s6_addr), addr_elems, 16);
+ maddr.ipv6mr_interface = 0;
+
+ (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
+
+ if (-1 == setsockopt (fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &maddr, sizeof (struct ipv6_mreq)))
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+#else
+ (void) fd;
+ (void) addr;
+ THROW_NO_IPV6(env);
+#endif /* HAVE_INET6 */
+#else
+ (void) fd;
+ (void) addr;
+ JCL_ThrowException (env, "java/lang/InternalError",
+ "socket options not supported");
+#endif /* HAVE_SETSOCKOPT */
+}
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: leave
+ * Signature: (I[B)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_leave (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jbyteArray addr)
+{
+#ifdef HAVE_SETSOCKOPT
+ struct ip_mreq maddr;
+ jbyte *addr_elems;
+
+ addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
+ if (addr_elems == NULL)
+ return;
+
+ maddr.imr_multiaddr.s_addr = * ((uint32_t *) addr_elems);
+ maddr.imr_interface.s_addr = INADDR_ANY;
+
+ (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
+
+ if (-1 == setsockopt (fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ &maddr, sizeof (struct ip_mreq)))
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+#else
+ (void) fd;
+ (void) addr;
+ JCL_ThrowException (env, "java/lang/InternalError",
+ "socket options not supported");
+#endif /* HAVE_SETSOCKOPT */
+}
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: leave6
+ * Signature: (I[B)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_leave6 (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jbyteArray addr)
+{
+#ifdef HAVE_SETSOCKOPT
+#ifdef HAVE_INET6
+ struct ipv6_mreq maddr;
+ jbyte *addr_elems;
+
+ addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
+ if (addr_elems == NULL)
+ return;
+
+ memcpy (&(maddr.ipv6mr_multiaddr.s6_addr), addr_elems, 16);
+ maddr.ipv6mr_interface = 0;
+
+ (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
+
+ if (-1 == setsockopt (fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+ &maddr, sizeof (struct ipv6_mreq)))
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+#else
+ (void) fd;
+ (void) addr;
+ THROW_NO_IPV6(env);
+#endif /* HAVE_INET6 */
+#else
+ (void) fd;
+ (void) addr;
+ JCL_ThrowException (env, "java/lang/InternalError",
+ "socket options not supported");
+#endif /* HAVE_SETSOCKOPT */
+}
+
+static uint32_t getif_address (JNIEnv *env, const char *ifname);
+static int getif_index (JNIEnv *env, const char *ifname);
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: joinGroup
+ * Signature: (I[BILjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_joinGroup (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jbyteArray addr,
+ jstring ifname)
+{
+#ifdef HAVE_SETSOCKOPT
+ struct ip_mreq maddr;
+ jbyte *addr_elems;
+ const char *str_ifname;
+
+ if (ifname != NULL)
+ {
+ str_ifname = JCL_jstring_to_cstring(env, ifname);
+ maddr.imr_interface.s_addr = getif_address (env, str_ifname);
+ JCL_free_cstring(env, ifname, str_ifname);
+
+ if ((*env)->ExceptionCheck (env))
+ return;
+ }
+ else
+ maddr.imr_interface.s_addr = INADDR_ANY;
+
+ addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
+ if (addr_elems == NULL)
+ return;
+
+ maddr.imr_multiaddr.s_addr = * ((uint32_t *) addr_elems);
+
+ (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
+
+ if (-1 == setsockopt (fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &maddr, sizeof (struct ip_mreq)))
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+
+#else
+ (void) fd;
+ (void) addr;
+ (void) ifname;
+ JCL_ThrowException (env, "java/lang/InternalError",
+ "socket options not supported");
+#endif /* HAVE_SETSOCKOPT */
+}
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: joinGroup6
+ * Signature: (I[BILjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_joinGroup6 (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jbyteArray addr,
+ jstring ifname)
+{
+#ifdef HAVE_SETSOCKOPT
+#ifdef HAVE_INET6
+ struct ipv6_mreq maddr;
+ jbyte *addr_elems;
+ const char *str_ifname;
+
+ if (ifname == NULL)
+ {
+ str_ifname = JCL_jstring_to_cstring(env, ifname);
+ maddr.ipv6mr_interface = getif_index (env, str_ifname);
+ JCL_free_cstring(env, ifname, str_ifname);
+
+ if ((*env)->ExceptionCheck (env))
+ return;
+ }
+ else
+ maddr.ipv6mr_interface = 0;
+
+ addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
+ if (addr_elems == NULL)
+ return;
+
+ memcpy (&(maddr.ipv6mr_multiaddr.s6_addr), addr_elems, 16);
+
+ (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
+
+ if (-1 == setsockopt (fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &maddr, sizeof (struct ipv6_mreq)))
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+#else
+ (void) fd;
+ (void) addr;
+ THROW_NO_IPV6(env);
+#endif /* HAVE_INET6 */
+#else
+ (void) fd;
+ (void) addr;
+ JCL_ThrowException (env, "java/lang/InternalError",
+ "socket options not supported");
+#endif /* HAVE_SETSOCKOPT */
+}
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: leaveGroup
+ * Signature: (I[BILjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_leaveGroup (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jbyteArray addr,
+ jstring ifname)
+{
+#ifdef HAVE_SETSOCKOPT
+ struct ip_mreq maddr;
+ jbyte *addr_elems;
+ const char *str_ifname;
+
+ if (ifname != NULL)
+ {
+ str_ifname = JCL_jstring_to_cstring(env, ifname);
+ maddr.imr_interface.s_addr = getif_address (env, str_ifname);
+ JCL_free_cstring(env, ifname, str_ifname);
+
+ if ((*env)->ExceptionCheck (env))
+ return;
+ }
+ else
+ maddr.imr_interface.s_addr = INADDR_ANY;
+
+ addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
+ if (addr_elems == NULL)
+ return;
+
+ maddr.imr_multiaddr.s_addr = * ((uint32_t *) addr_elems);
+
+ (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
+
+ if (-1 == setsockopt (fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ &maddr, sizeof (struct ip_mreq)))
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+#else
+ (void) fd;
+ (void) addr;
+ (void) ifname;
+ JCL_ThrowException (env, "java/lang/InternalError",
+ "socket options not supported");
+#endif /* HAVE_SETSOCKOPT */
+}
+
+/*
+ * Class: gnu_java_net_VMPlainSocketImpl
+ * Method: leaveGroup6
+ * Signature: (I[BILjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_net_VMPlainSocketImpl_leaveGroup6 (JNIEnv *env,
+ jclass c __attribute__((unused)),
+ jint fd, jbyteArray addr,
+ jstring ifname)
+{
+#ifdef HAVE_SETSOCKOPT
+#ifdef HAVE_INET6
+ struct ipv6_mreq maddr;
+ jbyte *addr_elems;
+ const char *str_ifname;
+
+ if (ifname == NULL)
+ {
+ str_ifname = JCL_jstring_to_cstring(env, ifname);
+ maddr.ipv6mr_interface = getif_index (env, str_ifname);
+ JCL_free_cstring(env, ifname, str_ifname);
+
+ if ((*env)->ExceptionCheck (env))
+ return;
+ }
+ else
+ maddr.ipv6mr_interface = 0;
+
+ addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
+ if (addr_elems == NULL)
+ return;
+
+ memcpy (&(maddr.ipv6mr_multiaddr.s6_addr), addr_elems, 16);
+
+ (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
+
+ if (-1 == setsockopt (fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+ &maddr, sizeof (struct ipv6_mreq)))
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+#else
+ (void) fd;
+ (void) addr;
+ THROW_NO_IPV6(env);
+#endif /* HAVE_INET6 */
+#else
+ (void) fd;
+ (void) addr;
+ JCL_ThrowException (env, "java/lang/InternalError",
+ "socket options not supported");
+#endif /* HAVE_SETSOCKOPT */
+}
+
+static uint32_t
+getif_address (JNIEnv *env, const char *ifname)
+{
+#if defined (HAVE_IFADDRS_H) && defined (HAVE_GETIFADDRS)
+ struct ifaddrs *ifaddrs, *i;
+ uint32_t addr = 0;
+ int foundaddr = 0;
+
+ if (getifaddrs (&ifaddrs) == -1)
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return 0;
+ }
+
+ for (i = ifaddrs; i != NULL; i = i->ifa_next)
+ {
+ if (strcmp (ifname, i->ifa_name) == 0)
+ {
+ /* Matched the name; see if there is an IPv4 address. */
+ if (i->ifa_addr->sa_family == AF_INET)
+ {
+ foundaddr = 1;
+ addr = ((struct sockaddr_in *) i->ifa_addr)->sin_addr.s_addr;
+ break;
+ }
+ }
+ }
+
+ if (!foundaddr)
+ JCL_ThrowException (env, SOCKET_EXCEPTION, "interface has no IPv4 address");
+
+ freeifaddrs (ifaddrs);
+
+ return addr;
+#else
+ (void) ifname;
+ JCL_ThrowException (env, "java/lang/InternalError",
+ "getifaddrs not available");
+ return 0;
+#endif /* HAVE_IFADDRS_H && HAVE_GETIFADDRS */
+}
+
+static int
+getif_index (JNIEnv *env, const char *ifname)
+{
+#if defined (HAVE_IFADDRS_H) && defined (HAVE_GETIFADDRS)
+ struct ifaddrs *ifaddrs, *i;
+ char *lastname = NULL;
+ int index = 1;
+ int foundname = 0;
+
+ if (getifaddrs (&ifaddrs) == -1)
+ {
+ JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
+ return -1;
+ }
+
+ lastname = ifaddrs->ifa_name;
+ for (i = ifaddrs; i != NULL; i = i->ifa_next)
+ {
+ if (strcmp (lastname, ifaddrs->ifa_name) != 0)
+ {
+ lastname = ifaddrs->ifa_name;
+ index++;
+ }
+ if (strcmp (ifname, ifaddrs->ifa_name) == 0)
+ {
+ foundname = 1;
+ break;
+ }
+ }
+
+ if (!foundname)
+ JCL_ThrowException (env, SOCKET_EXCEPTION,
+ "no interface with that name");
+
+ freeifaddrs (ifaddrs);
+
+ return index;
+#else
+ (void) ifname;
+ JCL_ThrowException (env, "java/lang/InternalError",
+ "getifaddrs not available");
+ return -1;
+#endif /* HAVE_GETIFADDRS */
+}