Skip to content

Commit 896fe0b

Browse files
mccullssarahchen6
authored andcommitted
Use the JDK's built-in support for Unix Domain Sockets on Java 16+
1 parent dbbe55c commit 896fe0b

File tree

3 files changed

+223
-2
lines changed

3 files changed

+223
-2
lines changed

utils/socket-utils/build.gradle

+29
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,37 @@
11
apply from: "$rootDir/gradle/java.gradle"
2+
apply plugin: "idea"
3+
4+
sourceSets {
5+
main_java17 {
6+
java.srcDirs "${project.projectDir}/src/main/java17"
7+
}
8+
}
9+
10+
compileMain_java17Java.configure {
11+
setJavaVersion(it, 17)
12+
sourceCompatibility = JavaVersion.VERSION_1_8
13+
targetCompatibility = JavaVersion.VERSION_1_8
14+
}
215

316
dependencies {
17+
compileOnly sourceSets.main_java17.output
18+
419
implementation libs.slf4j
520
implementation project(':internal-api')
621

722
implementation group: 'com.github.jnr', name: 'jnr-unixsocket', version: libs.versions.jnr.unixsocket.get()
823
}
24+
25+
jar {
26+
from sourceSets.main_java17.output
27+
}
28+
29+
forbiddenApisMain_java17 {
30+
failOnMissingClasses = false
31+
}
32+
33+
idea {
34+
module {
35+
jdkName = '17'
36+
}
37+
}

utils/socket-utils/src/main/java/datadog/common/socket/UnixDomainSocketFactory.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static java.util.concurrent.TimeUnit.MINUTES;
44

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

28+
private static final boolean JDK_SUPPORTS_UDS = Platform.isJavaVersionAtLeast(16);
29+
2730
private final RatelimitedLogger rlLog = new RatelimitedLogger(log, 5, MINUTES);
2831

2932
private final File path;
@@ -35,8 +38,14 @@ public UnixDomainSocketFactory(final File path) {
3538
@Override
3639
public Socket createSocket() throws IOException {
3740
try {
38-
final UnixSocketChannel channel = UnixSocketChannel.open();
39-
return new TunnelingUnixSocket(path, channel);
41+
if (JDK_SUPPORTS_UDS) {
42+
try {
43+
return new TunnelingJdkSocket(path.toPath());
44+
} catch (Throwable ignore) {
45+
// fall back to jnr-unixsocket library
46+
}
47+
}
48+
return new TunnelingUnixSocket(path, UnixSocketChannel.open());
4049
} catch (Throwable e) {
4150
if (Config.get().isAgentConfiguredUsingDefault()) {
4251
// fall back to port if we previously auto-discovered this socket file
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package datadog.common.socket;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.io.OutputStream;
6+
import java.net.InetAddress;
7+
import java.net.InetSocketAddress;
8+
import java.net.Socket;
9+
import java.net.SocketAddress;
10+
import java.net.SocketException;
11+
import java.net.UnixDomainSocketAddress;
12+
import java.nio.channels.Channels;
13+
import java.nio.channels.SocketChannel;
14+
import java.nio.file.Path;
15+
16+
/**
17+
* Subtype UNIX socket for a higher-fidelity impersonation of TCP sockets. This is named "tunneling"
18+
* because it assumes the ultimate destination has a hostname and port.
19+
*
20+
* <p>Bsed on {@link TunnelingUnixSocket}; adapted to use the built-in UDS support added in Java 16.
21+
*/
22+
final class TunnelingJdkSocket extends Socket {
23+
private final SocketAddress unixSocketAddress;
24+
private InetSocketAddress inetSocketAddress;
25+
26+
private SocketChannel unixSocketChannel;
27+
28+
private int timeout;
29+
private boolean shutIn;
30+
private boolean shutOut;
31+
private boolean closed;
32+
33+
TunnelingJdkSocket(final Path path) {
34+
this.unixSocketAddress = UnixDomainSocketAddress.of(path);
35+
}
36+
37+
TunnelingJdkSocket(final Path path, final InetSocketAddress address) {
38+
this(path);
39+
inetSocketAddress = address;
40+
}
41+
42+
@Override
43+
public boolean isConnected() {
44+
return null != unixSocketChannel;
45+
}
46+
47+
@Override
48+
public boolean isInputShutdown() {
49+
return shutIn;
50+
}
51+
52+
@Override
53+
public boolean isOutputShutdown() {
54+
return shutOut;
55+
}
56+
57+
@Override
58+
public boolean isClosed() {
59+
return closed;
60+
}
61+
62+
@Override
63+
public synchronized void setSoTimeout(int timeout) throws SocketException {
64+
if (isClosed()) {
65+
throw new SocketException("Socket is closed");
66+
}
67+
if (timeout < 0) {
68+
throw new IllegalArgumentException("Socket timeout can't be negative");
69+
}
70+
this.timeout = timeout;
71+
}
72+
73+
@Override
74+
public synchronized int getSoTimeout() throws SocketException {
75+
if (isClosed()) {
76+
throw new SocketException("Socket is closed");
77+
}
78+
return timeout;
79+
}
80+
81+
@Override
82+
public void connect(final SocketAddress endpoint) throws IOException {
83+
if (isClosed()) {
84+
throw new SocketException("Socket is closed");
85+
}
86+
if (isConnected()) {
87+
throw new SocketException("Socket is already connected");
88+
}
89+
inetSocketAddress = (InetSocketAddress) endpoint;
90+
unixSocketChannel = SocketChannel.open(unixSocketAddress);
91+
}
92+
93+
@Override
94+
public void connect(final SocketAddress endpoint, final int timeout) throws IOException {
95+
if (isClosed()) {
96+
throw new SocketException("Socket is closed");
97+
}
98+
if (isConnected()) {
99+
throw new SocketException("Socket is already connected");
100+
}
101+
inetSocketAddress = (InetSocketAddress) endpoint;
102+
unixSocketChannel = SocketChannel.open(unixSocketAddress);
103+
}
104+
105+
@Override
106+
public SocketChannel getChannel() {
107+
return unixSocketChannel;
108+
}
109+
110+
@Override
111+
public InputStream getInputStream() throws IOException {
112+
if (isClosed()) {
113+
throw new SocketException("Socket is closed");
114+
}
115+
if (!isConnected()) {
116+
throw new SocketException("Socket is not connected");
117+
}
118+
if (isInputShutdown()) {
119+
throw new SocketException("Socket input is shutdown");
120+
}
121+
return Channels.newInputStream(unixSocketChannel);
122+
}
123+
124+
@Override
125+
public OutputStream getOutputStream() throws IOException {
126+
if (isClosed()) {
127+
throw new SocketException("Socket is closed");
128+
}
129+
if (!isConnected()) {
130+
throw new SocketException("Socket is not connected");
131+
}
132+
if (isInputShutdown()) {
133+
throw new SocketException("Socket output is shutdown");
134+
}
135+
return Channels.newOutputStream(unixSocketChannel);
136+
}
137+
138+
@Override
139+
public void shutdownInput() throws IOException {
140+
if (isClosed()) {
141+
throw new SocketException("Socket is closed");
142+
}
143+
if (!isConnected()) {
144+
throw new SocketException("Socket is not connected");
145+
}
146+
if (isInputShutdown()) {
147+
throw new SocketException("Socket input is already shutdown");
148+
}
149+
unixSocketChannel.shutdownInput();
150+
shutIn = true;
151+
}
152+
153+
@Override
154+
public void shutdownOutput() throws IOException {
155+
if (isClosed()) {
156+
throw new SocketException("Socket is closed");
157+
}
158+
if (!isConnected()) {
159+
throw new SocketException("Socket is not connected");
160+
}
161+
if (isOutputShutdown()) {
162+
throw new SocketException("Socket output is already shutdown");
163+
}
164+
unixSocketChannel.shutdownOutput();
165+
shutOut = true;
166+
}
167+
168+
@Override
169+
public InetAddress getInetAddress() {
170+
return inetSocketAddress.getAddress();
171+
}
172+
173+
@Override
174+
public void close() throws IOException {
175+
if (isClosed()) {
176+
return;
177+
}
178+
if (null != unixSocketChannel) {
179+
unixSocketChannel.close();
180+
}
181+
closed = true;
182+
}
183+
}

0 commit comments

Comments
 (0)