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