diff --git a/transport-native-epoll/src/main/c/netty_unix_socket.c b/transport-native-epoll/src/main/c/netty_unix_socket.c index 6ef9c19e6c..77522d0b3a 100644 --- a/transport-native-epoll/src/main/c/netty_unix_socket.c +++ b/transport-native-epoll/src/main/c/netty_unix_socket.c @@ -13,6 +13,12 @@ * License for the specific language governing permissions and limitations * under the License. */ + /* + * Since glibc 2.8, the _GNU_SOURCE feature test macro must be defined + * (before including any header files) in order to obtain the + * definition of this structure. See + */ +#define _GNU_SOURCE #include #include #include @@ -29,8 +35,10 @@ #include "netty_unix_util.h" static jclass datagramSocketAddressClass = NULL; +static jclass peerCredentialsClass = NULL; static jmethodID datagramSocketAddrMethodId = NULL; static jmethodID inetSocketAddrMethodId = NULL; +static jmethodID peerCredentialsMethodId = NULL; static jclass inetSocketAddressClass = NULL; static jclass netUtilClass = NULL; static jmethodID netUtilClassIpv4PreferredMethodId = NULL; @@ -647,6 +655,14 @@ static jint netty_unix_socket_isTcpQuickAck(JNIEnv* env, jclass clazz, jint fd) } return optval; } + +static jobject netty_channel_unix_socket_getPeerCredentials(JNIEnv *env, jclass clazz, jint fd) { + struct ucred credentials; + if(netty_unix_socket_getOption(env,fd, SOL_SOCKET, SO_PEERCRED, &credentials, sizeof (credentials))) { + return NULL; + } + return (*env)->NewObject(env, peerCredentialsClass, peerCredentialsMethodId, credentials.pid, credentials.uid, credentials.gid); +} // JNI Registered Methods End // JNI Method Registration Table Begin @@ -690,7 +706,7 @@ static const JNINativeMethod fixed_method_table[] = { static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]); static jint dynamicMethodsTableSize() { - return fixed_method_table_size + 2; + return fixed_method_table_size + 3; } static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) { @@ -708,6 +724,12 @@ static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) { dynamicMethod->signature = netty_unix_util_prepend("(IJII)L", dynamicTypeName); dynamicMethod->fnPtr = (void *) netty_unix_socket_recvFromAddress; free(dynamicTypeName); + ++dynamicMethod; + dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials;"); + dynamicMethod->name = "getPeerCredentials"; + dynamicMethod->signature = netty_unix_util_prepend("(I)L", dynamicTypeName); + dynamicMethod->fnPtr = (void *) netty_channel_unix_socket_getPeerCredentials; + free(dynamicTypeName); return dynamicMethods; } @@ -788,6 +810,26 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { netty_unix_errors_throwRuntimeException(env, "failed to get method ID: NetUild.isIpV4StackPreferred()"); return JNI_ERR; } + + nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials"); + jclass localPeerCredsClass = (*env)->FindClass(env, nettyClassName); + free(nettyClassName); + nettyClassName = NULL; + if (localPeerCredsClass == NULL) { + // pending exception... + return JNI_ERR; + } + peerCredentialsClass = (jclass) (*env)->NewGlobalRef(env, localPeerCredsClass); + if (peerCredentialsClass == NULL) { + // out-of-memory! + netty_unix_errors_throwOutOfMemoryError(env); + return JNI_ERR; + } + peerCredentialsMethodId = (*env)->GetMethodID(env, peerCredentialsClass, "", "(III)V"); + if (peerCredentialsMethodId == NULL) { + netty_unix_errors_throwRuntimeException(env, "failed to get method ID: PeerCredentials.(int, int, int)"); + return JNI_ERR; + } void* mem = malloc(1); if (mem == NULL) { @@ -826,4 +868,8 @@ void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) { (*env)->DeleteGlobalRef(env, netUtilClass); netUtilClass = NULL; } + if (peerCredentialsClass != NULL) { + (*env)->DeleteGlobalRef(env, peerCredentialsClass); + peerCredentialsClass = NULL; + } } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannel.java index 8ff56eb925..bc283034e0 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannel.java @@ -22,11 +22,13 @@ import io.netty.channel.ChannelPipeline; import io.netty.channel.unix.DomainSocketAddress; import io.netty.channel.unix.DomainSocketChannel; import io.netty.channel.unix.FileDescriptor; +import io.netty.channel.unix.PeerCredentials; import io.netty.channel.unix.Socket; import java.net.SocketAddress; import static io.netty.channel.unix.Socket.newSocketDomain; +import java.io.IOException; public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel implements DomainSocketChannel { private final EpollDomainSocketChannelConfig config = new EpollDomainSocketChannelConfig(this); @@ -132,6 +134,14 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i return super.filterOutboundMessage(msg); } + /** + * Returns the unix credentials (uid, gid, pid) of the peer + * SO_PEERCRED + */ + public PeerCredentials peerCredentials() throws IOException { + return fd().getPeerCredentials(); + } + private final class EpollDomainUnsafe extends EpollStreamUnsafe { @Override void epollInReady() { diff --git a/transport-native-epoll/src/main/java/io/netty/channel/unix/PeerCredentials.java b/transport-native-epoll/src/main/java/io/netty/channel/unix/PeerCredentials.java new file mode 100644 index 0000000000..93c6259cfd --- /dev/null +++ b/transport-native-epoll/src/main/java/io/netty/channel/unix/PeerCredentials.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016 The Netty Project + * + * The Netty Project licenses this file to you 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 io.netty.channel.unix; + +/** + * User credentials discovered for the peer unix domain socket. + * + * The PID, UID and GID of the user connected on the other side of the unix domain socket + * For details see: + * SO_PEERCRED + */ +public final class PeerCredentials { + private final int pid; + private final int uid; + private final int gid; + + // These values are set by JNI via Socket.peerCredentials() + PeerCredentials(int p, int u, int g) { + pid = p; + uid = u; + gid = g; + } + + public int pid() { + return pid; + } + + public int uid() { + return uid; + } + + public int gid() { + return gid; + } + + @Override + public String toString() { + return "UserCredentials[" + + "pid=" + pid + + "; uid=" + uid + + "; gid=" + gid + + "]"; + } +} diff --git a/transport-native-epoll/src/main/java/io/netty/channel/unix/Socket.java b/transport-native-epoll/src/main/java/io/netty/channel/unix/Socket.java index 0da6a2de00..4151413c3c 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/unix/Socket.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/unix/Socket.java @@ -319,6 +319,10 @@ public final class Socket extends FileDescriptor { return getSoError(fd); } + public PeerCredentials getPeerCredentials() throws IOException { + return getPeerCredentials(fd); + } + public void setKeepAlive(boolean keepAlive) throws IOException { setKeepAlive(fd, keepAlive ? 1 : 0); } @@ -419,6 +423,7 @@ public final class Socket extends FileDescriptor { private static native int getSoError(int fd) throws IOException; private static native int getTcpDeferAccept(int fd) throws IOException; private static native int isTcpQuickAck(int fd) throws IOException; + private static native PeerCredentials getPeerCredentials(int fd) throws IOException; private static native void setKeepAlive(int fd, int keepAlive) throws IOException; private static native void setReceiveBufferSize(int fd, int receiveBufferSize) throws IOException; diff --git a/transport-native-epoll/src/test/java/io/netty/channel/unix/SocketTest.java b/transport-native-epoll/src/test/java/io/netty/channel/unix/SocketTest.java index 8671bdedfa..c8f98f0f93 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/unix/SocketTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/unix/SocketTest.java @@ -16,6 +16,7 @@ package io.netty.channel.unix; import io.netty.channel.epoll.Epoll; +import java.io.File; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -96,4 +97,30 @@ public class SocketTest { socket.close(); socket.close(); } + + @Test + public void testPeerCreds() throws IOException { + Socket s1 = Socket.newSocketDomain(); + Socket s2 = Socket.newSocketDomain(); + File domainSocketFile = null; + + try { + domainSocketFile = File.createTempFile("netty-test", "sckt"); + DomainSocketAddress dsa = new DomainSocketAddress(domainSocketFile); + s1.bind(dsa); + s1.listen(1); + + assertTrue(s2.connect(dsa)); + byte [] addr = new byte[64]; + s1.accept(addr); + PeerCredentials pc = s1.getPeerCredentials(); + assertNotEquals(pc.uid(), -1); + } finally { + s1.close(); + s2.close(); + if (domainSocketFile != null) { + domainSocketFile.delete(); + } + } + } }