Merge "Add support for Unix domain sockets."
diff --git a/luni/src/main/java/java/net/InetSocketAddress.java b/luni/src/main/java/java/net/InetSocketAddress.java
index 49dcfc4..4f4a348 100644
--- a/luni/src/main/java/java/net/InetSocketAddress.java
+++ b/luni/src/main/java/java/net/InetSocketAddress.java
@@ -131,18 +131,14 @@
}
/**
- * Gets the port number of this socket.
- *
- * @return the socket endpoint port number.
+ * Returns this socket address' port.
*/
public final int getPort() {
return port;
}
/**
- * Gets the address of this socket.
- *
- * @return the socket endpoint address.
+ * Returns this socket address' address.
*/
public final InetAddress getAddress() {
return addr;
diff --git a/luni/src/main/java/java/net/InetUnixAddress.java b/luni/src/main/java/java/net/InetUnixAddress.java
new file mode 100644
index 0000000..95bb097
--- /dev/null
+++ b/luni/src/main/java/java/net/InetUnixAddress.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.net;
+
+import java.nio.charset.Charsets;
+
+import static libcore.io.OsConstants.*;
+
+/**
+ * An AF_UNIX address. See {@link InetAddress}.
+ * @hide
+ */
+public final class InetUnixAddress extends InetAddress {
+ /**
+ * Constructs an AF_UNIX InetAddress for the given path.
+ */
+ public InetUnixAddress(String path) {
+ this(path.getBytes(Charsets.UTF_8));
+ }
+
+ /**
+ * Constructs an AF_UNIX InetAddress for the given path.
+ */
+ public InetUnixAddress(byte[] path) {
+ super(AF_UNIX, path, null);
+ }
+
+ /**
+ * Returns a string form of this InetAddress.
+ */
+ @Override public String toString() {
+ return "InetUnixAddress[" + new String(ipaddress, Charsets.UTF_8) + "]";
+ }
+}
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index fab3cc9..1c582ca 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -64,6 +64,7 @@
public int getgid() { return os.getgid(); }
public String getenv(String name) { return os.getenv(name); }
public String getnameinfo(InetAddress address, int flags) throws GaiException { return os.getnameinfo(address, flags); }
+ public SocketAddress getpeername(FileDescriptor fd) throws ErrnoException { return os.getpeername(fd); }
public int getpid() { return os.getpid(); }
public int getppid() { return os.getppid(); }
public StructPasswd getpwnam(String name) throws ErrnoException { return os.getpwnam(name); }
@@ -74,6 +75,7 @@
public int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptInt(fd, level, option); }
public StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptLinger(fd, level, option); }
public StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptTimeval(fd, level, option); }
+ public StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptUcred(fd, level, option); }
public int getuid() { return os.getuid(); }
public String if_indextoname(int index) { return os.if_indextoname(index); }
public InetAddress inet_pton(int family, String address) { return os.inet_pton(family, address); }
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index 4aa839e..28bf9ea 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -56,6 +56,7 @@
public String getenv(String name);
/* TODO: break into getnameinfoHost and getnameinfoService? */
public String getnameinfo(InetAddress address, int flags) throws GaiException;
+ public SocketAddress getpeername(FileDescriptor fd) throws ErrnoException;
public int getpid();
public int getppid();
public StructPasswd getpwnam(String name) throws ErrnoException;
@@ -66,6 +67,7 @@
public int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException;
public StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException;
public StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException;
+ public StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException;
public int getuid();
public String if_indextoname(int index);
public InetAddress inet_pton(int family, String address);
diff --git a/luni/src/main/java/libcore/io/OsConstants.java b/luni/src/main/java/libcore/io/OsConstants.java
index 9f381f5..78df386 100644
--- a/luni/src/main/java/libcore/io/OsConstants.java
+++ b/luni/src/main/java/libcore/io/OsConstants.java
@@ -302,6 +302,8 @@
public static final int SO_KEEPALIVE = placeholder();
public static final int SO_LINGER = placeholder();
public static final int SO_OOBINLINE = placeholder();
+ public static final int SO_PASSCRED = placeholder();
+ public static final int SO_PEERCRED = placeholder();
public static final int SO_RCVBUF = placeholder();
public static final int SO_RCVLOWAT = placeholder();
public static final int SO_RCVTIMEO = placeholder();
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index 27d3510..147750e 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -58,6 +58,7 @@
public native int getgid();
public native String getenv(String name);
public native String getnameinfo(InetAddress address, int flags) throws GaiException;
+ public native SocketAddress getpeername(FileDescriptor fd) throws ErrnoException;
public native int getpid();
public native int getppid();
public native StructPasswd getpwnam(String name) throws ErrnoException;
@@ -68,6 +69,7 @@
public native int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException;
public native StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException;
public native StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException;
+ public native StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException;
public native int getuid();
public native String if_indextoname(int index);
public native InetAddress inet_pton(int family, String address);
diff --git a/luni/src/main/java/libcore/io/StructUcred.java b/luni/src/main/java/libcore/io/StructUcred.java
new file mode 100644
index 0000000..359995d
--- /dev/null
+++ b/luni/src/main/java/libcore/io/StructUcred.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.io;
+
+/**
+ * Corresponds to C's {@code struct ucred}.
+ */
+public final class StructUcred {
+ /** The peer's process id. */
+ public final int pid;
+
+ /** The peer process' uid. */
+ public final int uid;
+
+ /** The peer process' gid. */
+ public final int gid;
+
+ private StructUcred(int pid, int uid, int gid) {
+ this.pid = pid;
+ this.uid = uid;
+ this.gid = gid;
+ }
+
+ @Override public String toString() {
+ return "StructUcred[pid=" + pid + ",uid=" + uid + ",gid=" + gid + "]";
+ }
+}
diff --git a/luni/src/main/native/JniConstants.cpp b/luni/src/main/native/JniConstants.cpp
index 6b3c7bd..8693cf5 100644
--- a/luni/src/main/native/JniConstants.cpp
+++ b/luni/src/main/native/JniConstants.cpp
@@ -37,6 +37,7 @@
jclass JniConstants::inet6AddressClass;
jclass JniConstants::inetAddressClass;
jclass JniConstants::inetSocketAddressClass;
+jclass JniConstants::inetUnixAddressClass;
jclass JniConstants::inflaterClass;
jclass JniConstants::inputStreamClass;
jclass JniConstants::integerClass;
@@ -63,6 +64,7 @@
jclass JniConstants::structStatClass;
jclass JniConstants::structStatFsClass;
jclass JniConstants::structTimevalClass;
+jclass JniConstants::structUcredClass;
jclass JniConstants::structUtsnameClass;
static jclass findClass(JNIEnv* env, const char* name) {
@@ -94,6 +96,7 @@
inet6AddressClass = findClass(env, "java/net/Inet6Address");
inetAddressClass = findClass(env, "java/net/InetAddress");
inetSocketAddressClass = findClass(env, "java/net/InetSocketAddress");
+ inetUnixAddressClass = findClass(env, "java/net/InetUnixAddress");
inflaterClass = findClass(env, "java/util/zip/Inflater");
inputStreamClass = findClass(env, "java/io/InputStream");
integerClass = findClass(env, "java/lang/Integer");
@@ -120,5 +123,6 @@
structStatClass = findClass(env, "libcore/io/StructStat");
structStatFsClass = findClass(env, "libcore/io/StructStatFs");
structTimevalClass = findClass(env, "libcore/io/StructTimeval");
+ structUcredClass = findClass(env, "libcore/io/StructUcred");
structUtsnameClass = findClass(env, "libcore/io/StructUtsname");
}
diff --git a/luni/src/main/native/JniConstants.h b/luni/src/main/native/JniConstants.h
index 414cef8..8fd7bc8 100644
--- a/luni/src/main/native/JniConstants.h
+++ b/luni/src/main/native/JniConstants.h
@@ -58,6 +58,7 @@
static jclass inet6AddressClass;
static jclass inetAddressClass;
static jclass inetSocketAddressClass;
+ static jclass inetUnixAddressClass;
static jclass inflaterClass;
static jclass inputStreamClass;
static jclass integerClass;
@@ -84,6 +85,7 @@
static jclass structStatClass;
static jclass structStatFsClass;
static jclass structTimevalClass;
+ static jclass structUcredClass;
static jclass structUtsnameClass;
};
diff --git a/luni/src/main/native/NetworkUtilities.cpp b/luni/src/main/native/NetworkUtilities.cpp
index 9f4d770..de07201 100644
--- a/luni/src/main/native/NetworkUtilities.cpp
+++ b/luni/src/main/native/NetworkUtilities.cpp
@@ -26,45 +26,50 @@
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
+#include <sys/un.h>
-jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage* ss, jint* port) {
+jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage& ss, jint* port) {
// Convert IPv4-mapped IPv6 addresses to IPv4 addresses.
// The RI states "Java will never return an IPv4-mapped address".
- sockaddr_storage tmp;
- memset(&tmp, 0, sizeof(tmp));
- const sockaddr_in6* sin6 = reinterpret_cast<const sockaddr_in6*>(ss);
- if (ss->ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ const sockaddr_in6& sin6 = reinterpret_cast<const sockaddr_in6&>(ss);
+ if (ss.ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) {
// Copy the IPv6 address into the temporary sockaddr_storage.
- memcpy(&tmp, ss, sizeof(sockaddr_in6));
+ sockaddr_storage tmp;
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp, &ss, sizeof(sockaddr_in6));
// Unmap it into an IPv4 address.
- sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&tmp);
- sin->sin_family = AF_INET;
- sin->sin_port = sin6->sin6_port;
- memcpy(&sin->sin_addr.s_addr, &sin6->sin6_addr.s6_addr[12], 4);
- // Fall through into the regular conversion using the unmapped address.
- ss = &tmp;
+ sockaddr_in& sin = reinterpret_cast<sockaddr_in&>(tmp);
+ sin.sin_family = AF_INET;
+ sin.sin_port = sin6.sin6_port;
+ memcpy(&sin.sin_addr.s_addr, &sin6.sin6_addr.s6_addr[12], 4);
+ // Do the regular conversion using the unmapped address.
+ return sockaddrToInetAddress(env, tmp, port);
}
const void* rawAddress;
size_t addressLength;
- int sin_port;
+ int sin_port = 0;
int scope_id = 0;
- if (ss->ss_family == AF_INET) {
- const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(ss);
- rawAddress = &sin->sin_addr.s_addr;
+ if (ss.ss_family == AF_INET) {
+ const sockaddr_in& sin = reinterpret_cast<const sockaddr_in&>(ss);
+ rawAddress = &sin.sin_addr.s_addr;
addressLength = 4;
- sin_port = ntohs(sin->sin_port);
- } else if (ss->ss_family == AF_INET6) {
- const sockaddr_in6* sin6 = reinterpret_cast<const sockaddr_in6*>(ss);
- rawAddress = &sin6->sin6_addr.s6_addr;
+ sin_port = ntohs(sin.sin_port);
+ } else if (ss.ss_family == AF_INET6) {
+ const sockaddr_in6& sin6 = reinterpret_cast<const sockaddr_in6&>(ss);
+ rawAddress = &sin6.sin6_addr.s6_addr;
addressLength = 16;
- sin_port = ntohs(sin6->sin6_port);
- scope_id = sin6->sin6_scope_id;
+ sin_port = ntohs(sin6.sin6_port);
+ scope_id = sin6.sin6_scope_id;
+ } else if (ss.ss_family == AF_UNIX) {
+ const sockaddr_un& sun = reinterpret_cast<const sockaddr_un&>(ss);
+ rawAddress = &sun.sun_path;
+ addressLength = strlen(sun.sun_path);
} else {
// We can't throw SocketException. We aren't meant to see bad addresses, so seeing one
// really does imply an internal error.
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "sockaddrToInetAddress bad ss_family: %i", ss->ss_family);
+ "sockaddrToInetAddress unsupported ss_family: %i", ss.ss_family);
return NULL;
}
if (port != NULL) {
@@ -78,6 +83,14 @@
env->SetByteArrayRegion(byteArray.get(), 0, addressLength,
reinterpret_cast<const jbyte*>(rawAddress));
+ if (ss.ss_family == AF_UNIX) {
+ // Note that we get here for AF_UNIX sockets on accept(2). The unix(7) man page claims
+ // that the peer's sun_path will contain the path, but in practice it doesn't, and the
+ // peer length is returned as 2 (meaning only the sun_family field was set).
+ static jmethodID ctor = env->GetMethodID(JniConstants::inetUnixAddressClass, "<init>", "([B)V");
+ return env->NewObject(JniConstants::inetUnixAddressClass, ctor, byteArray.get());
+ }
+
static jmethodID getByAddressMethod = env->GetStaticMethodID(JniConstants::inetAddressClass,
"getByAddress", "(Ljava/lang/String;[BI)Ljava/net/InetAddress;");
if (getByAddressMethod == NULL) {
@@ -87,8 +100,9 @@
NULL, byteArray.get(), scope_id);
}
-static bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss, bool map) {
- memset(ss, 0, sizeof(*ss));
+static bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage& ss, socklen_t& sa_len, bool map) {
+ memset(&ss, 0, sizeof(ss));
+ sa_len = 0;
if (inetAddress == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -97,15 +111,15 @@
// Get the address family.
static jfieldID familyFid = env->GetFieldID(JniConstants::inetAddressClass, "family", "I");
- ss->ss_family = env->GetIntField(inetAddress, familyFid);
- if (ss->ss_family == AF_UNSPEC) {
+ ss.ss_family = env->GetIntField(inetAddress, familyFid);
+ if (ss.ss_family == AF_UNSPEC) {
return true; // Job done!
}
// Check this is an address family we support.
- if (ss->ss_family != AF_INET && ss->ss_family != AF_INET6) {
+ if (ss.ss_family != AF_INET && ss.ss_family != AF_INET6 && ss.ss_family != AF_UNIX) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "inetAddressToSockaddr bad family: %i", ss->ss_family);
+ "inetAddressToSockaddr bad family: %i", ss.ss_family);
return false;
}
@@ -117,16 +131,42 @@
return false;
}
+ // Handle the AF_UNIX special case.
+ if (ss.ss_family == AF_UNIX) {
+ sockaddr_un& sun = reinterpret_cast<sockaddr_un&>(ss);
+
+ size_t path_length = env->GetArrayLength(addressBytes.get());
+ if (path_length >= sizeof(sun.sun_path)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "inetAddressToSockaddr path too long for AF_UNIX: %i", path_length);
+ return false;
+ }
+
+ // Copy the bytes...
+ jbyte* dst = reinterpret_cast<jbyte*>(&sun.sun_path);
+ memset(dst, 0, sizeof(sun.sun_path));
+ env->GetByteArrayRegion(addressBytes.get(), 0, path_length, dst);
+ sa_len = sizeof(sun.sun_path);
+ return true;
+ }
+
+ // TODO: bionic's getnameinfo(3) seems to want its length parameter to be exactly
+ // sizeof(sockaddr_in) for an IPv4 address and sizeof (sockaddr_in6) for an
+ // IPv6 address. Fix getnameinfo so it accepts sizeof(sockaddr_storage), and
+ // then unconditionally set sa_len to sizeof(sockaddr_storage) instead of having
+ // to deal with this case by case.
+
// We use AF_INET6 sockets, so we want an IPv6 address (which may be a IPv4-mapped address).
- sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(ss);
- sin6->sin6_port = htons(port);
- if (ss->ss_family == AF_INET6) {
+ sockaddr_in6& sin6 = reinterpret_cast<sockaddr_in6&>(ss);
+ sin6.sin6_port = htons(port);
+ if (ss.ss_family == AF_INET6) {
// IPv6 address. Copy the bytes...
- jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr);
+ jbyte* dst = reinterpret_cast<jbyte*>(&sin6.sin6_addr.s6_addr);
env->GetByteArrayRegion(addressBytes.get(), 0, 16, dst);
// ...and set the scope id...
static jfieldID scopeFid = env->GetFieldID(JniConstants::inet6AddressClass, "scope_id", "I");
- sin6->sin6_scope_id = env->GetIntField(inetAddress, scopeFid);
+ sin6.sin6_scope_id = env->GetIntField(inetAddress, scopeFid);
+ sa_len = sizeof(sockaddr_in6);
return true;
}
@@ -134,31 +174,33 @@
if (map) {
// We should represent this Inet4Address as an IPv4-mapped IPv6 sockaddr_in6.
// Change the family...
- sin6->sin6_family = AF_INET6;
+ sin6.sin6_family = AF_INET6;
// Copy the bytes...
- jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr[12]);
+ jbyte* dst = reinterpret_cast<jbyte*>(&sin6.sin6_addr.s6_addr[12]);
env->GetByteArrayRegion(addressBytes.get(), 0, 4, dst);
// INADDR_ANY and in6addr_any are both all-zeros...
- if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+ if (!IN6_IS_ADDR_UNSPECIFIED(&sin6.sin6_addr)) {
// ...but all other IPv4-mapped addresses are ::ffff:a.b.c.d, so insert the ffff...
- memset(&(sin6->sin6_addr.s6_addr[10]), 0xff, 2);
+ memset(&(sin6.sin6_addr.s6_addr[10]), 0xff, 2);
}
+ sa_len = sizeof(sockaddr_in6);
} else {
// We should represent this Inet4Address as an IPv4 sockaddr_in.
- sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(ss);
- sin->sin_port = htons(port);
- jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr);
+ sockaddr_in& sin = reinterpret_cast<sockaddr_in&>(ss);
+ sin.sin_port = htons(port);
+ jbyte* dst = reinterpret_cast<jbyte*>(&sin.sin_addr.s_addr);
env->GetByteArrayRegion(addressBytes.get(), 0, 4, dst);
+ sa_len = sizeof(sockaddr_in);
}
return true;
}
-bool inetAddressToSockaddrVerbatim(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss) {
- return inetAddressToSockaddr(env, inetAddress, port, ss, false);
+bool inetAddressToSockaddrVerbatim(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage& ss, socklen_t& sa_len) {
+ return inetAddressToSockaddr(env, inetAddress, port, ss, sa_len, false);
}
-bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss) {
- return inetAddressToSockaddr(env, inetAddress, port, ss, true);
+bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage& ss, socklen_t& sa_len) {
+ return inetAddressToSockaddr(env, inetAddress, port, ss, sa_len, true);
}
bool setBlocking(int fd, bool blocking) {
diff --git a/luni/src/main/native/NetworkUtilities.h b/luni/src/main/native/NetworkUtilities.h
index 1ae6c53..28e9fa5 100644
--- a/luni/src/main/native/NetworkUtilities.h
+++ b/luni/src/main/native/NetworkUtilities.h
@@ -17,20 +17,27 @@
#include "jni.h"
#include <sys/socket.h>
-// Convert from sockaddr_storage to InetAddress and an optional int port.
-jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage* ss, int* port);
+// Convert from sockaddr_storage to Inet4Address (AF_INET), Inet6Address (AF_INET6), or
+// InetUnixAddress (AF_UNIX). If 'port' is non-NULL and the address family includes a notion
+// of port number, *port will be set to the port number.
+jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage& ss, int* port);
-// Convert from InetAddress to sockaddr_storage. An Inet4Address will be converted to
-// an IPv4-mapped AF_INET6 sockaddr_in6. This is what you want if you're about to perform an
-// operation on a socket, since all our sockets are AF_INET6.
-bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss);
+// Convert from InetAddress to sockaddr_storage. An InetUnixAddress will be converted to
+// an AF_UNIX sockaddr_un. An Inet6Address will be converted to an AF_INET6 sockaddr_in6.
+// An Inet4Address will be converted to an IPv4-mapped AF_INET6 sockaddr_in6. This is what
+// you want if you're about to perform an operation on a socket, since all our sockets
+// are AF_INET6.
+bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port,
+ sockaddr_storage& ss, socklen_t& sa_len);
-// Convert from InetAddress to sockaddr_storage. An Inet6Address will be converted to
-// a sockaddr_in6 while an Inet4Address will be converted to a sockaddr_in. This is
-// probably only useful for getnameinfo(2), where we'll be presenting the result to
-// the user and the user may actually care whether the original address was pure IPv4
-// or an IPv4-mapped IPv6 address, and for the MCAST_JOIN_GROUP socket option.
-bool inetAddressToSockaddrVerbatim(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss);
+// Convert from InetAddress to sockaddr_storage. An InetUnixAddress will be converted to
+// an AF_UNIX sockaddr_un. An Inet6Address will be converted to an AF_INET6 sockaddr_in6.
+// An Inet4Address will be converted to a sockaddr_in. This is probably only useful for
+// getnameinfo(2), where we'll be presenting the result to the user and the user may actually
+// care whether the original address was pure IPv4 or an IPv4-mapped IPv6 address, and
+// for the MCAST_JOIN_GROUP socket option.
+bool inetAddressToSockaddrVerbatim(JNIEnv* env, jobject inetAddress, int port,
+ sockaddr_storage& ss, socklen_t& sa_len);
diff --git a/luni/src/main/native/libcore_io_OsConstants.cpp b/luni/src/main/native/libcore_io_OsConstants.cpp
index 7247a38..e23fc8b 100644
--- a/luni/src/main/native/libcore_io_OsConstants.cpp
+++ b/luni/src/main/native/libcore_io_OsConstants.cpp
@@ -357,6 +357,8 @@
initConstant(env, c, "SO_KEEPALIVE", SO_KEEPALIVE);
initConstant(env, c, "SO_LINGER", SO_LINGER);
initConstant(env, c, "SO_OOBINLINE", SO_OOBINLINE);
+ initConstant(env, c, "SO_PASSCRED", SO_PASSCRED);
+ initConstant(env, c, "SO_PEERCRED", SO_PEERCRED);
initConstant(env, c, "SO_RCVBUF", SO_RCVBUF);
initConstant(env, c, "SO_RCVLOWAT", SO_RCVLOWAT);
initConstant(env, c, "SO_RCVTIMEO", SO_RCVTIMEO);
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index 336c982..e73b990 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -211,8 +211,7 @@
std::vector<ScopedT*> mScopedBuffers;
};
-static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage* ss) {
- // TODO: support AF_UNIX and AF_UNSPEC (and other families?)
+static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage& ss) {
jint port;
jobject inetAddress = sockaddrToInetAddress(env, ss, &port);
if (inetAddress == NULL) {
@@ -279,6 +278,11 @@
static_cast<jlong>(tv.tv_sec), static_cast<jlong>(tv.tv_usec));
}
+static jobject makeStructUcred(JNIEnv* env, const struct ucred& u) {
+ static jmethodID ctor = env->GetMethodID(JniConstants::structUcredClass, "<init>", "(III)V");
+ return env->NewObject(JniConstants::structUcredClass, ctor, u.pid, u.uid, u.gid);
+}
+
static jobject makeStructUtsname(JNIEnv* env, const struct utsname& buf) {
TO_JAVA_STRING(sysname, buf.sysname);
TO_JAVA_STRING(nodename, buf.nodename);
@@ -302,7 +306,7 @@
return true;
}
-static bool fillInetSocketAddress(JNIEnv* env, jint rc, jobject javaInetSocketAddress, const sockaddr_storage* ss) {
+static bool fillInetSocketAddress(JNIEnv* env, jint rc, jobject javaInetSocketAddress, const sockaddr_storage& ss) {
if (rc == -1 || javaInetSocketAddress == NULL) {
return true;
}
@@ -334,6 +338,21 @@
return makeStructStat(env, sb);
}
+static jobject doGetSockName(JNIEnv* env, jobject javaFd, bool is_sockname) {
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ sockaddr_storage ss;
+ sockaddr* sa = reinterpret_cast<sockaddr*>(&ss);
+ socklen_t byteCount = sizeof(ss);
+ memset(&ss, 0, byteCount);
+ int rc = is_sockname ? TEMP_FAILURE_RETRY(getsockname(fd, sa, &byteCount))
+ : TEMP_FAILURE_RETRY(getpeername(fd, sa, &byteCount));
+ if (rc == -1) {
+ throwErrnoException(env, is_sockname ? "getsockname" : "getpeername");
+ return NULL;
+ }
+ return makeSocketAddress(env, ss);
+}
+
class Passwd {
public:
Passwd(JNIEnv* env) : mEnv(env), mResult(NULL) {
@@ -382,7 +401,7 @@
sockaddr* peer = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
socklen_t* peerLength = (javaInetSocketAddress != NULL) ? &sl : 0;
jint clientFd = NET_FAILURE_RETRY(env, int, accept, javaFd, peer, peerLength);
- if (clientFd == -1 || !fillInetSocketAddress(env, clientFd, javaInetSocketAddress, &ss)) {
+ if (clientFd == -1 || !fillInetSocketAddress(env, clientFd, javaInetSocketAddress, ss)) {
close(clientFd);
return NULL;
}
@@ -403,12 +422,13 @@
static void Posix_bind(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) {
sockaddr_storage ss;
- if (!inetAddressToSockaddr(env, javaAddress, port, &ss)) {
+ socklen_t sa_len;
+ if (!inetAddressToSockaddr(env, javaAddress, port, ss, sa_len)) {
return;
}
const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
// We don't need the return value because we'll already have thrown.
- (void) NET_FAILURE_RETRY(env, int, bind, javaFd, sa, sizeof(sockaddr_storage));
+ (void) NET_FAILURE_RETRY(env, int, bind, javaFd, sa, sa_len);
}
static void Posix_chmod(JNIEnv* env, jobject, jstring javaPath, jint mode) {
@@ -441,12 +461,13 @@
static void Posix_connect(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) {
sockaddr_storage ss;
- if (!inetAddressToSockaddr(env, javaAddress, port, &ss)) {
+ socklen_t sa_len;
+ if (!inetAddressToSockaddr(env, javaAddress, port, ss, sa_len)) {
return;
}
const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
// We don't need the return value because we'll already have thrown.
- (void) NET_FAILURE_RETRY(env, int, connect, javaFd, sa, sizeof(sockaddr_storage));
+ (void) NET_FAILURE_RETRY(env, int, connect, javaFd, sa, sa_len);
}
static jobject Posix_dup(JNIEnv* env, jobject, jobject javaOldFd) {
@@ -638,7 +659,7 @@
}
// Convert each IP address into a Java byte array.
- sockaddr_storage* address = reinterpret_cast<sockaddr_storage*>(ai->ai_addr);
+ sockaddr_storage& address = *reinterpret_cast<sockaddr_storage*>(ai->ai_addr);
ScopedLocalRef<jobject> inetAddress(env, sockaddrToInetAddress(env, address, NULL));
if (inetAddress.get() == NULL) {
return NULL;
@@ -671,17 +692,13 @@
static jstring Posix_getnameinfo(JNIEnv* env, jobject, jobject javaAddress, jint flags) {
sockaddr_storage ss;
- if (!inetAddressToSockaddrVerbatim(env, javaAddress, 0, &ss)) {
+ socklen_t sa_len;
+ if (!inetAddressToSockaddrVerbatim(env, javaAddress, 0, ss, sa_len)) {
return NULL;
}
- // TODO: bionic's getnameinfo(3) seems to want its length parameter to be exactly
- // sizeof(sockaddr_in) for an IPv4 address and sizeof (sockaddr_in6) for an
- // IPv6 address. Fix getnameinfo so it accepts sizeof(sockaddr_storage), and
- // then remove this hack.
- socklen_t size = (ss.ss_family == AF_INET) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
char buf[NI_MAXHOST]; // NI_MAXHOST is longer than INET6_ADDRSTRLEN.
errno = 0;
- int rc = getnameinfo(reinterpret_cast<sockaddr*>(&ss), size, buf, sizeof(buf), NULL, 0, flags);
+ int rc = getnameinfo(reinterpret_cast<sockaddr*>(&ss), sa_len, buf, sizeof(buf), NULL, 0, flags);
if (rc != 0) {
throwGaiException(env, "getnameinfo", rc);
return NULL;
@@ -689,6 +706,10 @@
return env->NewStringUTF(buf);
}
+static jobject Posix_getpeername(JNIEnv* env, jobject, jobject javaFd) {
+ return doGetSockName(env, javaFd, false);
+}
+
static jint Posix_getpid(JNIEnv*, jobject) {
return getpid();
}
@@ -710,17 +731,7 @@
}
static jobject Posix_getsockname(JNIEnv* env, jobject, jobject javaFd) {
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- sockaddr_storage ss;
- sockaddr* sa = reinterpret_cast<sockaddr*>(&ss);
- socklen_t byteCount = sizeof(ss);
- memset(&ss, 0, byteCount);
- int rc = TEMP_FAILURE_RETRY(getsockname(fd, sa, &byteCount));
- if (rc == -1) {
- throwErrnoException(env, "getsockname");
- return NULL;
- }
- return makeSocketAddress(env, &ss);
+ return doGetSockName(env, javaFd, true);
}
static jint Posix_getsockoptByte(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) {
@@ -743,7 +754,7 @@
throwErrnoException(env, "getsockopt");
return NULL;
}
- return sockaddrToInetAddress(env, &ss, NULL);
+ return sockaddrToInetAddress(env, ss, NULL);
}
static jint Posix_getsockoptInt(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) {
@@ -780,6 +791,19 @@
return makeStructTimeval(env, tv);
}
+static jobject Posix_getsockoptUcred(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) {
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ struct ucred u;
+ socklen_t size = sizeof(u);
+ memset(&u, 0, size);
+ int rc = TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &u, &size));
+ if (rc == -1) {
+ throwErrnoException(env, "getsockopt");
+ return NULL;
+ }
+ return makeStructUcred(env, u);
+}
+
static jint Posix_getuid(JNIEnv*, jobject) {
return getuid();
}
@@ -805,7 +829,7 @@
return NULL;
}
ss.ss_family = family;
- return sockaddrToInetAddress(env, &ss, NULL);
+ return sockaddrToInetAddress(env, ss, NULL);
}
static jobject Posix_ioctlInetAddress(JNIEnv* env, jobject, jobject javaFd, jint cmd, jstring javaInterfaceName) {
@@ -818,7 +842,7 @@
if (rc == -1) {
return NULL;
}
- return sockaddrToInetAddress(env, reinterpret_cast<sockaddr_storage*>(&req.ifr_addr), NULL);
+ return sockaddrToInetAddress(env, reinterpret_cast<sockaddr_storage&>(req.ifr_addr), NULL);
}
static jint Posix_ioctlInt(JNIEnv* env, jobject, jobject javaFd, jint cmd, jobject javaArg) {
@@ -1039,7 +1063,7 @@
sockaddr* from = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
socklen_t* fromLength = (javaInetSocketAddress != NULL) ? &sl : 0;
jint recvCount = NET_FAILURE_RETRY(env, ssize_t, recvfrom, javaFd, bytes.get() + byteOffset, byteCount, flags, from, fromLength);
- fillInetSocketAddress(env, recvCount, javaInetSocketAddress, &ss);
+ fillInetSocketAddress(env, recvCount, javaInetSocketAddress, ss);
return recvCount;
}
@@ -1087,12 +1111,12 @@
return -1;
}
sockaddr_storage ss;
- if (javaInetAddress != NULL && !inetAddressToSockaddr(env, javaInetAddress, port, &ss)) {
+ socklen_t sa_len = 0;
+ if (javaInetAddress != NULL && !inetAddressToSockaddr(env, javaInetAddress, port, ss, sa_len)) {
return -1;
}
const sockaddr* to = (javaInetAddress != NULL) ? reinterpret_cast<const sockaddr*>(&ss) : NULL;
- socklen_t toLength = (javaInetAddress != NULL) ? sizeof(ss) : 0;
- return NET_FAILURE_RETRY(env, ssize_t, sendto, javaFd, bytes.get() + byteOffset, byteCount, flags, to, toLength);
+ return NET_FAILURE_RETRY(env, ssize_t, sendto, javaFd, bytes.get() + byteOffset, byteCount, flags, to, sa_len);
}
static void Posix_setegid(JNIEnv* env, jobject, jint egid) {
@@ -1165,7 +1189,8 @@
// Get the IPv4 or IPv6 multicast address to join or leave.
static jfieldID grGroupFid = env->GetFieldID(JniConstants::structGroupReqClass, "gr_group", "Ljava/net/InetAddress;");
ScopedLocalRef<jobject> javaGroup(env, env->GetObjectField(javaGroupReq, grGroupFid));
- if (!inetAddressToSockaddrVerbatim(env, javaGroup.get(), 0, &req.gr_group)) {
+ socklen_t sa_len;
+ if (!inetAddressToSockaddrVerbatim(env, javaGroup.get(), 0, req.gr_group, sa_len)) {
return;
}
@@ -1365,6 +1390,7 @@
NATIVE_METHOD(Posix, getgid, "()I"),
NATIVE_METHOD(Posix, getenv, "(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(Posix, getnameinfo, "(Ljava/net/InetAddress;I)Ljava/lang/String;"),
+ NATIVE_METHOD(Posix, getpeername, "(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;"),
NATIVE_METHOD(Posix, getpid, "()I"),
NATIVE_METHOD(Posix, getppid, "()I"),
NATIVE_METHOD(Posix, getpwnam, "(Ljava/lang/String;)Llibcore/io/StructPasswd;"),
@@ -1375,6 +1401,7 @@
NATIVE_METHOD(Posix, getsockoptInt, "(Ljava/io/FileDescriptor;II)I"),
NATIVE_METHOD(Posix, getsockoptLinger, "(Ljava/io/FileDescriptor;II)Llibcore/io/StructLinger;"),
NATIVE_METHOD(Posix, getsockoptTimeval, "(Ljava/io/FileDescriptor;II)Llibcore/io/StructTimeval;"),
+ NATIVE_METHOD(Posix, getsockoptUcred, "(Ljava/io/FileDescriptor;II)Llibcore/io/StructUcred;"),
NATIVE_METHOD(Posix, getuid, "()I"),
NATIVE_METHOD(Posix, if_indextoname, "(I)Ljava/lang/String;"),
NATIVE_METHOD(Posix, inet_pton, "(ILjava/lang/String;)Ljava/net/InetAddress;"),
diff --git a/luni/src/test/java/libcore/io/OsTest.java b/luni/src/test/java/libcore/io/OsTest.java
index e6446ae..a15bbdd 100644
--- a/luni/src/test/java/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/io/OsTest.java
@@ -17,21 +17,116 @@
package libcore.io;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.InetUnixAddress;
import java.net.ServerSocket;
+import java.net.SocketAddress;
import junit.framework.TestCase;
import static libcore.io.OsConstants.*;
public class OsTest extends TestCase {
- public void testIsSocket() throws Exception {
- File f = new File("/dev/null");
- FileInputStream fis = new FileInputStream(f);
- assertFalse(S_ISSOCK(Libcore.os.fstat(fis.getFD()).st_mode));
- fis.close();
+ public void testIsSocket() throws Exception {
+ File f = new File("/dev/null");
+ FileInputStream fis = new FileInputStream(f);
+ assertFalse(S_ISSOCK(Libcore.os.fstat(fis.getFD()).st_mode));
+ fis.close();
- ServerSocket s = new ServerSocket();
- assertTrue(S_ISSOCK(Libcore.os.fstat(s.getImpl$().getFD$()).st_mode));
- s.close();
+ ServerSocket s = new ServerSocket();
+ assertTrue(S_ISSOCK(Libcore.os.fstat(s.getImpl$().getFD$()).st_mode));
+ s.close();
+ }
+
+ public void testUnixDomainSockets_in_file_system() throws Exception {
+ String path = System.getProperty("java.io.tmpdir") + "/test_unix_socket";
+ new File(path).delete();
+ checkUnixDomainSocket(new InetUnixAddress(path), false);
+ }
+
+ public void testUnixDomainSocket_abstract_name() throws Exception {
+ // Linux treats a sun_path starting with a NUL byte as an abstract name. See unix(7).
+ byte[] path = "/abstract_name_unix_socket".getBytes("UTF-8");
+ path[0] = 0;
+ checkUnixDomainSocket(new InetUnixAddress(path), true);
+ }
+
+ private void checkUnixDomainSocket(final InetUnixAddress address, final boolean isAbstract) throws Exception {
+ final FileDescriptor serverFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
+ Libcore.os.bind(serverFd, address, 0);
+ Libcore.os.listen(serverFd, 5);
+
+ checkSockName(serverFd, isAbstract, address);
+
+ Thread server = new Thread(new Runnable() {
+ public void run() {
+ try {
+ InetSocketAddress peerAddress = new InetSocketAddress();
+ FileDescriptor clientFd = Libcore.os.accept(serverFd, peerAddress);
+ checkSockName(clientFd, isAbstract, address);
+ checkNoName(peerAddress);
+
+ checkNoPeerName(clientFd);
+
+ StructUcred credentials = Libcore.os.getsockoptUcred(clientFd, SOL_SOCKET, SO_PEERCRED);
+ assertEquals(Libcore.os.getpid(), credentials.pid);
+ assertEquals(Libcore.os.getuid(), credentials.uid);
+ assertEquals(Libcore.os.getgid(), credentials.gid);
+
+ byte[] request = new byte[256];
+ int requestLength = Libcore.os.read(clientFd, request, 0, request.length);
+
+ String s = new String(request, "UTF-8");
+ byte[] response = s.toUpperCase().getBytes("UTF-8");
+ Libcore.os.write(clientFd, response, 0, response.length);
+
+ Libcore.os.close(clientFd);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ });
+ server.start();
+
+ FileDescriptor clientFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
+
+ Libcore.os.connect(clientFd, address, 0);
+ checkNoSockName(clientFd);
+
+ String string = "hello, world!";
+
+ byte[] request = string.getBytes("UTF-8");
+ assertEquals(request.length, Libcore.os.write(clientFd, request, 0, request.length));
+
+ byte[] response = new byte[request.length];
+ assertEquals(response.length, Libcore.os.read(clientFd, response, 0, response.length));
+
+ assertEquals(string.toUpperCase(), new String(response, "UTF-8"));
+
+ Libcore.os.close(clientFd);
+ }
+
+ private void checkSockName(FileDescriptor fd, boolean isAbstract, InetAddress address) throws Exception {
+ InetSocketAddress isa = (InetSocketAddress) Libcore.os.getsockname(fd);
+ if (isAbstract) {
+ checkNoName(isa);
+ } else {
+ assertEquals(address, isa.getAddress());
}
+ }
+
+ private void checkNoName(SocketAddress sa) {
+ InetSocketAddress isa = (InetSocketAddress) sa;
+ assertEquals(0, isa.getAddress().getAddress().length);
+ }
+
+ private void checkNoPeerName(FileDescriptor fd) throws Exception {
+ checkNoName(Libcore.os.getpeername(fd));
+ }
+
+ private void checkNoSockName(FileDescriptor fd) throws Exception {
+ checkNoName(Libcore.os.getsockname(fd));
+ }
}