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 */
 | 
						|
}
 |