Skip to content

Commit

Permalink
Use the JDK's built-in support for Unix Domain Sockets on Java 16+
Browse files Browse the repository at this point in the history
  • Loading branch information
mcculls committed Jan 14, 2025
1 parent b0770e9 commit 27676b4
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 2 deletions.
29 changes: 29 additions & 0 deletions utils/socket-utils/build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,37 @@
apply from: "$rootDir/gradle/java.gradle"
apply plugin: "idea"

sourceSets {
main_java17 {
java.srcDirs "${project.projectDir}/src/main/java17"
}
}

compileMain_java17Java.configure {
setJavaVersion(it, 17)
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

dependencies {
compileOnly sourceSets.main_java17.output

implementation libs.slf4j
implementation project(':internal-api')

implementation group: 'com.github.jnr', name: 'jnr-unixsocket', version: libs.versions.jnr.unixsocket.get()
}

jar {
from sourceSets.main_java17.output
}

forbiddenApisMain_java17 {
failOnMissingClasses = false
}

idea {
module {
jdkName = '17'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static java.util.concurrent.TimeUnit.MINUTES;

import datadog.trace.api.Config;
import datadog.trace.api.Platform;
import datadog.trace.relocate.api.RatelimitedLogger;
import java.io.File;
import java.io.IOException;
Expand All @@ -24,6 +25,8 @@
public final class UnixDomainSocketFactory extends SocketFactory {
private static final Logger log = LoggerFactory.getLogger(UnixDomainSocketFactory.class);

private static final boolean JDK_SUPPORTS_UDS = Platform.isJavaVersionAtLeast(16);

private final RatelimitedLogger rlLog = new RatelimitedLogger(log, 5, MINUTES);

private final File path;
Expand All @@ -35,8 +38,14 @@ public UnixDomainSocketFactory(final File path) {
@Override
public Socket createSocket() throws IOException {
try {
final UnixSocketChannel channel = UnixSocketChannel.open();
return new TunnelingUnixSocket(path, channel);
if (JDK_SUPPORTS_UDS) {
try {
return new TunnelingJdkSocket(path.toPath());
} catch (Throwable ignore) {
// fall back to jnr-unixsocket library
}
}
return new TunnelingUnixSocket(path, UnixSocketChannel.open());
} catch (Throwable e) {
if (Config.get().isAgentConfiguredUsingDefault()) {
// fall back to port if we previously auto-discovered this socket file
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package datadog.common.socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnixDomainSocketAddress;
import java.nio.channels.Channels;
import java.nio.channels.SocketChannel;
import java.nio.file.Path;

/**
* Subtype UNIX socket for a higher-fidelity impersonation of TCP sockets. This is named "tunneling"
* because it assumes the ultimate destination has a hostname and port.
*
* <p>Bsed on {@link TunnelingUnixSocket}; adapted to use the built-in UDS support added in Java 16.
*/
final class TunnelingJdkSocket extends Socket {
private final SocketAddress unixSocketAddress;
private InetSocketAddress inetSocketAddress;

private SocketChannel unixSocketChannel;

private int timeout;
private boolean shutIn;
private boolean shutOut;
private boolean closed;

TunnelingJdkSocket(final Path path) {
this.unixSocketAddress = UnixDomainSocketAddress.of(path);
}

TunnelingJdkSocket(final Path path, final InetSocketAddress address) {
this(path);
inetSocketAddress = address;
}

@Override
public boolean isConnected() {
return null != unixSocketChannel;
}

@Override
public boolean isInputShutdown() {
return shutIn;
}

@Override
public boolean isOutputShutdown() {
return shutOut;
}

@Override
public boolean isClosed() {
return closed;
}

@Override
public synchronized void setSoTimeout(int timeout) throws SocketException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
if (timeout < 0) {
throw new IllegalArgumentException("Socket timeout can't be negative");
}
this.timeout = timeout;
}

@Override
public synchronized int getSoTimeout() throws SocketException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
return timeout;
}

@Override
public void connect(final SocketAddress endpoint) throws IOException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
if (isConnected()) {
throw new SocketException("Socket is already connected");
}
inetSocketAddress = (InetSocketAddress) endpoint;
unixSocketChannel = SocketChannel.open(unixSocketAddress);
}

@Override
public void connect(final SocketAddress endpoint, final int timeout) throws IOException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
if (isConnected()) {
throw new SocketException("Socket is already connected");
}
inetSocketAddress = (InetSocketAddress) endpoint;
unixSocketChannel = SocketChannel.open(unixSocketAddress);
}

@Override
public SocketChannel getChannel() {
return unixSocketChannel;
}

@Override
public InputStream getInputStream() throws IOException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
if (!isConnected()) {
throw new SocketException("Socket is not connected");
}
if (isInputShutdown()) {
throw new SocketException("Socket input is shutdown");
}
return Channels.newInputStream(unixSocketChannel);
}

@Override
public OutputStream getOutputStream() throws IOException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
if (!isConnected()) {
throw new SocketException("Socket is not connected");
}
if (isInputShutdown()) {
throw new SocketException("Socket output is shutdown");
}
return Channels.newOutputStream(unixSocketChannel);
}

@Override
public void shutdownInput() throws IOException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
if (!isConnected()) {
throw new SocketException("Socket is not connected");
}
if (isInputShutdown()) {
throw new SocketException("Socket input is already shutdown");
}
unixSocketChannel.shutdownInput();
shutIn = true;
}

@Override
public void shutdownOutput() throws IOException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
if (!isConnected()) {
throw new SocketException("Socket is not connected");
}
if (isOutputShutdown()) {
throw new SocketException("Socket output is already shutdown");
}
unixSocketChannel.shutdownOutput();
shutOut = true;
}

@Override
public InetAddress getInetAddress() {
return inetSocketAddress.getAddress();
}

@Override
public void close() throws IOException {
if (isClosed()) {
return;
}
if (null != unixSocketChannel) {
unixSocketChannel.close();
}
closed = true;
}
}

0 comments on commit 27676b4

Please sign in to comment.