summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2010-05-04 13:36:53 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2010-05-04 13:36:53 -0700
commit252299f5206edf81e208f368deef054c464e9d2e (patch)
treee8b13354977c730f2fc3ab252ff1098312fe5cf0
parentf4c62bce9efbba1ded656acacb0695694a16d309 (diff)
parent4401c75ac7d85da33472d0398d8166df6ce4beb5 (diff)
downloadlibcore-252299f5206edf81e208f368deef054c464e9d2e.zip
libcore-252299f5206edf81e208f368deef054c464e9d2e.tar.gz
libcore-252299f5206edf81e208f368deef054c464e9d2e.tar.bz2
Merge "Fix IPv6 multicast TTLs." into dalvik-dev
-rw-r--r--luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java4
-rw-r--r--luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp338
2 files changed, 130 insertions, 212 deletions
diff --git a/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java b/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
index 766538c..d4f8e6f 100644
--- a/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
+++ b/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
@@ -158,12 +158,12 @@ public class PlainDatagramSocketImpl extends DatagramSocketImpl {
@Override
public int getTimeToLive() throws IOException {
- return getTTL() & 0xff;
+ return ((Integer) getOption(IP_MULTICAST_TTL)).intValue();
}
@Override
public byte getTTL() throws IOException {
- return ((Byte) getOption(IP_MULTICAST_TTL)).byteValue();
+ return (byte) getTimeToLive();
}
@Override
diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
index bd82613..ffb271a 100644
--- a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
+++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
@@ -123,7 +123,7 @@
#define JAVASOCKOPT_SO_REUSEADDR 4
#define JAVASOCKOPT_SO_KEEPALIVE 8
#define JAVASOCKOPT_IP_MULTICAST_IF 16
-#define JAVASOCKOPT_MCAST_TTL 17
+#define JAVASOCKOPT_MULTICAST_TTL 17
#define JAVASOCKOPT_IP_MULTICAST_LOOP 18
#define JAVASOCKOPT_MCAST_ADD_MEMBERSHIP 19
#define JAVASOCKOPT_MCAST_DROP_MEMBERSHIP 20
@@ -165,7 +165,6 @@ static struct CachedFields {
jmethodID boolean_class_init;
jfieldID boolean_class_value;
jclass byte_class;
- jmethodID byte_class_init;
jfieldID byte_class_value;
jclass socketimpl_class;
jfieldID socketimpl_address;
@@ -588,23 +587,6 @@ static jobject newJavaLangBoolean(JNIEnv * env, jint anInt) {
}
/**
- * Answer a new java.lang.Byte object.
- *
- * @param env pointer to the JNI library
- * @param anInt the Byte constructor argument
- *
- * @return the new Byte
- */
-static jobject newJavaLangByte(JNIEnv * env, jbyte val) {
- jclass tempClass;
- jmethodID tempMethod;
-
- tempClass = gCachedFields.byte_class;
- tempMethod = gCachedFields.byte_class_init;
- return env->NewObject(tempClass, tempMethod, val);
-}
-
-/**
* Answer a new java.lang.Integer object.
*
* @param env pointer to the JNI library
@@ -1371,7 +1353,6 @@ static bool initCachedFields(JNIEnv* env) {
{&c->i4addr_class_init, c->i4addr_class, "<init>", "([B)V", false},
{&c->integer_class_init, c->integer_class, "<init>", "(I)V", false},
{&c->boolean_class_init, c->boolean_class, "<init>", "(Z)V", false},
- {&c->byte_class_init, c->byte_class, "<init>", "(B)V", false},
{&c->iaddr_getbyaddress, c->iaddr_class, "getByAddress",
"([B)Ljava/net/InetAddress;", true}
};
@@ -1448,8 +1429,19 @@ static void osNetworkSystem_createStreamSocket(JNIEnv* env, jobject, jobject fil
createSocketFileDescriptor(env, fileDescriptor, SOCK_STREAM);
}
-static void osNetworkSystem_createDatagramSocket(JNIEnv* env, jobject, jobject fd, jboolean) {
- createSocketFileDescriptor(env, fd, SOCK_DGRAM);
+static void osNetworkSystem_createDatagramSocket(JNIEnv* env, jobject, jobject fileDescriptor, jboolean) {
+ int fd = createSocketFileDescriptor(env, fileDescriptor, SOCK_DGRAM);
+#ifdef __linux__
+ // The RFC (http://tools.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults to 1.
+ // The Linux kernel (at least up to 2.6.32) accidentally defaults to 64 (which would be correct
+ // for the *unicast* hop limit). See http://www.spinics.net/lists/netdev/msg129022.html.
+ // When that's fixed, we can remove this code. Until then, we manually set the hop limit on
+ // IPv6 datagram sockets. (IPv4 is already correct.)
+ if (fd != -1 && getSocketAddressFamily(fd) == AF_INET6) {
+ int ttl = 1;
+ setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(int));
+ }
+#endif
}
static jint osNetworkSystem_readDirect(JNIEnv* env, jobject,
@@ -2312,137 +2304,79 @@ static jint osNetworkSystem_getSocketLocalPort(JNIEnv* env, jobject,
return getSocketAddressPort(&addr);
}
-static jobject osNetworkSystem_getSocketOption(JNIEnv* env, jobject,
- jobject fileDescriptor, jint anOption) {
- int intValue = 0;
- socklen_t intSize = sizeof(int);
- int result;
- struct sockaddr_storage sockVal;
- socklen_t sockSize = sizeof(sockVal);
-
- int handle;
- if (!jniGetFd(env, fileDescriptor, handle)) {
- return 0;
+template <typename T>
+static bool getSocketOption(JNIEnv* env, int fd, int level, int option, T* value) {
+ socklen_t size = sizeof(*value);
+ int rc = getsockopt(fd, level, option, value, &size);
+ if (rc == -1) {
+ LOGE("getSocketOption(fd=%i, level=%i, option=%i) failed: %s (errno=%i)",
+ fd, level, option, strerror(errno), errno);
+ jniThrowSocketException(env, errno);
+ return false;
}
+ return true;
+}
- switch ((int) anOption & 0xffff) {
- case JAVASOCKOPT_SO_LINGER: {
- struct linger lingr;
- socklen_t size = sizeof(struct linger);
- result = getsockopt(handle, SOL_SOCKET, SO_LINGER, &lingr, &size);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- if (!lingr.l_onoff) {
- intValue = -1;
- } else {
- intValue = lingr.l_linger;
- }
- return newJavaLangInteger(env, intValue);
- }
-
- case JAVASOCKOPT_TCP_NODELAY: {
- result = getsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangBoolean(env, intValue);
- }
-
- case JAVASOCKOPT_SO_SNDBUF: {
- result = getsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangInteger(env, intValue);
- }
-
- case JAVASOCKOPT_SO_RCVBUF: {
- result = getsockopt(handle, SOL_SOCKET, SO_RCVBUF, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangInteger(env, intValue);
- }
+static jobject getSocketOption_Boolean(JNIEnv* env, int fd, int level, int option) {
+ int value;
+ return getSocketOption(env, fd, level, option, &value) ? newJavaLangBoolean(env, value) : NULL;
+}
- case JAVASOCKOPT_SO_BROADCAST: {
- result = getsockopt(handle, SOL_SOCKET, SO_BROADCAST, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangBoolean(env, intValue);
- }
+static jobject getSocketOption_Integer(JNIEnv* env, int fd, int level, int option) {
+ int value;
+ return getSocketOption(env, fd, level, option, &value) ? newJavaLangInteger(env, value) : NULL;
+}
- case JAVASOCKOPT_SO_REUSEADDR: {
- result = getsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangBoolean(env, intValue);
- }
+static jobject osNetworkSystem_getSocketOption(JNIEnv* env, jobject, jobject fileDescriptor, jint option) {
+ int fd;
+ if (!jniGetFd(env, fileDescriptor, fd)) {
+ return NULL;
+ }
- case JAVASOCKOPT_SO_KEEPALIVE: {
- result = getsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangBoolean(env, intValue);
- }
+ int family = getSocketAddressFamily(fd);
+ if (family != AF_INET && family != AF_INET6) {
+ jniThrowSocketException(env, EAFNOSUPPORT);
+ return NULL;
+ }
- case JAVASOCKOPT_SO_OOBINLINE: {
- result = getsockopt(handle, SOL_SOCKET, SO_OOBINLINE, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangBoolean(env, intValue);
+ switch (option) {
+ case JAVASOCKOPT_TCP_NODELAY:
+ return getSocketOption_Boolean(env, fd, IPPROTO_TCP, TCP_NODELAY);
+ case JAVASOCKOPT_SO_SNDBUF:
+ return getSocketOption_Integer(env, fd, SOL_SOCKET, SO_SNDBUF);
+ case JAVASOCKOPT_SO_RCVBUF:
+ return getSocketOption_Integer(env, fd, SOL_SOCKET, SO_RCVBUF);
+ case JAVASOCKOPT_SO_BROADCAST:
+ return getSocketOption_Boolean(env, fd, SOL_SOCKET, SO_BROADCAST);
+ case JAVASOCKOPT_SO_REUSEADDR:
+ return getSocketOption_Boolean(env, fd, SOL_SOCKET, SO_REUSEADDR);
+ case JAVASOCKOPT_SO_KEEPALIVE:
+ return getSocketOption_Boolean(env, fd, SOL_SOCKET, SO_KEEPALIVE);
+ case JAVASOCKOPT_SO_OOBINLINE:
+ return getSocketOption_Boolean(env, fd, SOL_SOCKET, SO_OOBINLINE);
+ case JAVASOCKOPT_IP_TOS:
+ if (family == AF_INET) {
+ return getSocketOption_Boolean(env, fd, IPPROTO_IP, IP_TOS);
+ } else {
+ return getSocketOption_Boolean(env, fd, IPPROTO_IPV6, IPV6_TCLASS);
}
-
- case JAVASOCKOPT_IP_TOS: {
- result = getSocketOption(handle, IP_TOS,
- IPV6_TCLASS, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangInteger(env, intValue);
+ case JAVASOCKOPT_SO_LINGER:
+ {
+ linger lingr;
+ bool ok = getSocketOption(env, fd, SOL_SOCKET, SO_LINGER, &lingr);
+ return ok ? newJavaLangInteger(env, !lingr.l_onoff ? -1 : lingr.l_linger) : NULL;
}
-
- case JAVASOCKOPT_SO_RCVTIMEOUT: {
- struct timeval timeout;
- socklen_t size = sizeof(timeout);
- result = getsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout, &size);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangInteger(env, toMs(timeout));
+ case JAVASOCKOPT_SO_RCVTIMEOUT:
+ {
+ timeval timeout;
+ bool ok = getSocketOption(env, fd, SOL_SOCKET, SO_RCVTIMEO, &timeout);
+ return ok ? newJavaLangInteger(env, toMs(timeout)) : NULL;
}
-
#ifdef ENABLE_MULTICAST
- case JAVASOCKOPT_MCAST_TTL: {
- // Java uses a byte to store the TTL, but the kernel uses an int.
- result = getSocketOption(handle, IP_MULTICAST_TTL,
- IPV6_MULTICAST_HOPS, &intValue,
- &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangByte(env, (jbyte)(intValue & 0xFF));
- }
-
- case JAVASOCKOPT_IP_MULTICAST_IF: {
- result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockVal, &sockSize);
- if (result == -1) {
- jniThrowSocketException(env, errno);
+ case JAVASOCKOPT_IP_MULTICAST_IF:
+ {
+ struct sockaddr_storage sockVal;
+ if (!getSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_IF, &sockVal)) {
return NULL;
}
if (sockVal.ss_family != AF_INET) {
@@ -2453,62 +2387,44 @@ static jobject osNetworkSystem_getSocketOption(JNIEnv* env, jobject,
}
return socketAddressToInetAddress(env, &sockVal);
}
-
- case JAVASOCKOPT_IP_MULTICAST_IF2: {
+ case JAVASOCKOPT_IP_MULTICAST_IF2:
+ if (family == AF_INET) {
struct ip_mreqn multicastRequest;
- int interfaceIndex = 0;
- socklen_t optionLength;
- int addressFamily = getSocketAddressFamily(handle);
- switch (addressFamily) {
- case AF_INET:
- optionLength = sizeof(multicastRequest);
- result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF,
- &multicastRequest, &optionLength);
- if (result == 0)
- interfaceIndex = multicastRequest.imr_ifindex;
- break;
- case AF_INET6:
- optionLength = sizeof(interfaceIndex);
- result = getsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_IF,
- &interfaceIndex, &optionLength);
- break;
- default:
- jniThrowSocketException(env, EAFNOSUPPORT);
- return NULL;
- }
-
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangInteger(env, interfaceIndex);
+ bool ok = getSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_IF, &multicastRequest);
+ return ok ? newJavaLangInteger(env, multicastRequest.imr_ifindex) : NULL;
+ } else {
+ return getSocketOption_Integer(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_IF);
}
-
- case JAVASOCKOPT_IP_MULTICAST_LOOP: {
- result = getSocketOption(handle,
- IP_MULTICAST_LOOP,
- IPV6_MULTICAST_LOOP, &intValue,
- &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangBoolean(env, intValue);
+ case JAVASOCKOPT_IP_MULTICAST_LOOP:
+ if (family == AF_INET) {
+ // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte.
+ u_char loopback;
+ bool ok = getSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loopback);
+ return ok ? newJavaLangBoolean(env, loopback) : NULL;
+ } else {
+ return getSocketOption_Boolean(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP);
}
-#else
- case JAVASOCKOPT_MCAST_TTL:
- case JAVASOCKOPT_IP_MULTICAST_IF:
- case JAVASOCKOPT_IP_MULTICAST_IF2:
- case JAVASOCKOPT_IP_MULTICAST_LOOP: {
- jniThrowException(env, "java/lang/UnsupportedOperationException", NULL);
- return NULL;
+ case JAVASOCKOPT_MULTICAST_TTL:
+ if (family == AF_INET) {
+ // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
+ // IPv4 multicast TTL uses a byte.
+ u_char ttl;
+ bool ok = getSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl);
+ return ok ? newJavaLangInteger(env, ttl) : NULL;
+ } else {
+ return getSocketOption_Integer(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
}
+#else
+ case JAVASOCKOPT_MULTICAST_TTL:
+ case JAVASOCKOPT_IP_MULTICAST_IF:
+ case JAVASOCKOPT_IP_MULTICAST_IF2:
+ case JAVASOCKOPT_IP_MULTICAST_LOOP:
+ jniThrowException(env, "java/lang/UnsupportedOperationException", NULL);
+ return NULL;
#endif // def ENABLE_MULTICAST
-
- default: {
- jniThrowSocketException(env, ENOPROTOOPT);
- return NULL;
- }
+ default:
+ jniThrowSocketException(env, ENOPROTOOPT);
+ return NULL;
}
}
@@ -2517,7 +2433,7 @@ static void setSocketOption(JNIEnv* env, int fd, int level, int option, T* value
int rc = setsockopt(fd, level, option, value, sizeof(*value));
if (rc == -1) {
LOGE("setSocketOption(fd=%i, level=%i, option=%i) failed: %s (errno=%i)",
- fd, level, option, errno, strerror(errno), errno);
+ fd, level, option, strerror(errno), errno);
jniThrowSocketException(env, errno);
}
}
@@ -2535,10 +2451,10 @@ static void osNetworkSystem_setSocketOption(JNIEnv* env, jobject, jobject fileDe
intVal = (int) env->GetBooleanField(optVal, gCachedFields.boolean_class_value);
} else if (env->IsInstanceOf(optVal, gCachedFields.byte_class)) {
intVal = (int) env->GetByteField(optVal, gCachedFields.byte_class_value);
- } else if (env->IsInstanceOf(optVal, gCachedFields.genericipmreq_class)) {
+ } else if (env->IsInstanceOf(optVal, gCachedFields.genericipmreq_class) || env->IsInstanceOf(optVal, gCachedFields.iaddr_class)) {
// we'll use optVal directly
} else {
- jniThrowSocketException(env, ENOPROTOOPT);
+ jniThrowSocketException(env, EINVAL);
return;
}
@@ -2551,7 +2467,7 @@ static void osNetworkSystem_setSocketOption(JNIEnv* env, jobject, jobject fileDe
switch (option) {
case JAVASOCKOPT_SO_LINGER:
{
- struct linger lingr;
+ linger lingr;
lingr.l_onoff = intVal > 0 ? 1 : 0;
lingr.l_linger = intVal;
setSocketOption(env, fd, SOL_SOCKET, SO_LINGER, &lingr);
@@ -2596,16 +2512,6 @@ static void osNetworkSystem_setSocketOption(JNIEnv* env, jobject, jobject fileDe
setSocketOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &intVal);
return;
#ifdef ENABLE_MULTICAST
- case JAVASOCKOPT_MCAST_TTL:
- if (family == AF_INET) {
- // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
- // IPv4 multicast TTL uses a byte.
- char ttlVal = intVal;
- setSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttlVal);
- } else {
- setSocketOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &intVal);
- }
- return;
case JAVASOCKOPT_MCAST_ADD_MEMBERSHIP:
mcastAddDropMembership(env, fd, optVal, IP_ADD_MEMBERSHIP);
return;
@@ -2643,15 +2549,27 @@ static void osNetworkSystem_setSocketOption(JNIEnv* env, jobject, jobject fileDe
setSocketOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &intVal);
}
return;
+ case JAVASOCKOPT_MULTICAST_TTL:
+ if (family == AF_INET) {
+ // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
+ // IPv4 multicast TTL uses a byte.
+ u_char ttl = intVal;
+ setSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl);
+ } else {
+ setSocketOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &intVal);
+ }
+ return;
case JAVASOCKOPT_IP_MULTICAST_LOOP:
if (family == AF_INET) {
- setSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_LOOP, &intVal);
+ // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte.
+ u_char loopback = intVal;
+ setSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loopback);
} else {
setSocketOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &intVal);
}
return;
#else
- case JAVASOCKOPT_MCAST_TTL:
+ case JAVASOCKOPT_MULTICAST_TTL:
case JAVASOCKOPT_MCAST_ADD_MEMBERSHIP:
case JAVASOCKOPT_MCAST_DROP_MEMBERSHIP:
case JAVASOCKOPT_IP_MULTICAST_IF: