mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			966 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			966 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
| /* 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 */
 | |
| }
 |