From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- .../jni/java-net/gnu_java_net_VMPlainSocketImpl.c | 965 +++++++++++++++++++++ 1 file changed, 965 insertions(+) create mode 100644 libjava/classpath/native/jni/java-net/gnu_java_net_VMPlainSocketImpl.c (limited to 'libjava/classpath/native/jni/java-net/gnu_java_net_VMPlainSocketImpl.c') 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include +#include +#include +#include +#ifdef HAVE_IFADDRS_H +#include +#endif +#include +#include +#ifdef HAVE_NET_IF_H +#include +#endif +#include +#include +#include +#include +#include + +#include +#include + +#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 */ +} -- cgit v1.2.3