diff options
author | Elliott Hughes <enh@google.com> | 2013-03-21 00:23:48 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2013-03-21 00:23:48 +0000 |
commit | 0f7e3bb45f03a1b54cc1d6c0b9df608d9607f24d (patch) | |
tree | 269e6105a0bb7437bb15ebfb03c10fc735f17bf8 /luni | |
parent | 2add675fc2b3fc25c8a6be5e7f6c6c51db44cd72 (diff) | |
parent | 482a3fc5635ac431b8a7476d7fe3397af4c2e8ec (diff) | |
download | libcore-0f7e3bb45f03a1b54cc1d6c0b9df608d9607f24d.zip libcore-0f7e3bb45f03a1b54cc1d6c0b9df608d9607f24d.tar.gz libcore-0f7e3bb45f03a1b54cc1d6c0b9df608d9607f24d.tar.bz2 |
Merge "Add support for Unix domain sockets."
Diffstat (limited to 'luni')
-rw-r--r-- | luni/src/main/java/java/net/InetSocketAddress.java | 8 | ||||
-rw-r--r-- | luni/src/main/java/java/net/InetUnixAddress.java | 48 | ||||
-rw-r--r-- | luni/src/main/java/libcore/io/ForwardingOs.java | 2 | ||||
-rw-r--r-- | luni/src/main/java/libcore/io/Os.java | 2 | ||||
-rw-r--r-- | luni/src/main/java/libcore/io/OsConstants.java | 2 | ||||
-rw-r--r-- | luni/src/main/java/libcore/io/Posix.java | 2 | ||||
-rw-r--r-- | luni/src/main/java/libcore/io/StructUcred.java | 41 | ||||
-rw-r--r-- | luni/src/main/native/JniConstants.cpp | 4 | ||||
-rw-r--r-- | luni/src/main/native/JniConstants.h | 2 | ||||
-rw-r--r-- | luni/src/main/native/NetworkUtilities.cpp | 132 | ||||
-rw-r--r-- | luni/src/main/native/NetworkUtilities.h | 31 | ||||
-rw-r--r-- | luni/src/main/native/libcore_io_OsConstants.cpp | 2 | ||||
-rw-r--r-- | luni/src/main/native/libcore_io_Posix.cpp | 97 | ||||
-rw-r--r-- | luni/src/test/java/libcore/io/OsTest.java | 113 |
14 files changed, 379 insertions, 107 deletions
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 @@ public class InetSocketAddress extends SocketAddress { } /** - * 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 class ForwardingOs implements Os { 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 class ForwardingOs implements Os { 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 interface Os { 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 interface Os { 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 final class OsConstants { 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 final class Posix implements Os { 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 final class Posix implements Os { 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::gaiExceptionClass; 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::structPollfdClass; 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 @@ void JniConstants::init(JNIEnv* env) { 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 @@ void JniConstants::init(JNIEnv* env) { 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 @@ struct JniConstants { 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 @@ struct JniConstants { 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 @@ jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage* ss, jint* por 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 @@ jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage* ss, jint* por 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 @@ static bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, so // 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 @@ static bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, so 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 @@ static bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, so 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 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) { 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 @@ private: 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 jobject makeStructTimeval(JNIEnv* env, const struct timeval& tv) { 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 @@ static bool fillIfreq(JNIEnv* env, jstring javaInterfaceName, struct ifreq& req) 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 @@ static jobject doStat(JNIEnv* env, jstring javaPath, bool isLstat) { 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 @@ static jobject Posix_accept(JNIEnv* env, jobject, jobject javaFd, jobject javaIn 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 jboolean Posix_access(JNIEnv* env, jobject, jstring javaPath, jint mode) 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_close(JNIEnv* env, jobject, jobject javaFd) { 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 @@ static jobjectArray Posix_getaddrinfo(JNIEnv* env, jobject, jstring javaNode, jo } // 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_getenv(JNIEnv* env, jobject, jstring javaName) { 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 @@ static jstring Posix_getnameinfo(JNIEnv* env, jobject, jobject javaAddress, jint 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_getpwuid(JNIEnv* env, jobject, jint uid) { } 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 @@ static jobject Posix_getsockoptInAddr(JNIEnv* env, jobject, jobject javaFd, jint 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 @@ static jobject Posix_getsockoptTimeval(JNIEnv* env, jobject, jobject javaFd, jin 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 @@ static jobject Posix_inet_pton(JNIEnv* env, jobject, jint family, jstring javaNa 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 @@ static jobject Posix_ioctlInetAddress(JNIEnv* env, jobject, jobject javaFd, jint 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 @@ static jint Posix_recvfromBytes(JNIEnv* env, jobject, jobject javaFd, jobject ja 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 @@ static jint Posix_sendtoBytes(JNIEnv* env, jobject, jobject javaFd, jobject java 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 @@ static void Posix_setsockoptGroupReq(JNIEnv* env, jobject, jobject javaFd, jint // 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 @@ static JNINativeMethod gMethods[] = { 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 @@ static JNINativeMethod gMethods[] = { 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(); - - ServerSocket s = new ServerSocket(); - assertTrue(S_ISSOCK(Libcore.os.fstat(s.getImpl$().getFD$()).st_mode)); - s.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(); + } + + 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)); + } } |