Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
}
Loading