Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
5ff82ed
added SOCKS5 support for DNS over tcp and udp (first working version)
Thomas-Internetx Oct 2, 2024
5a41225
refactored code for udp associate into IoClient
Thomas-Internetx Oct 3, 2024
bee0c76
added user/pwd authentication and refactored some of the code
Thomas-Internetx Oct 18, 2024
2a1192c
redo code changes in SimpleResolver and the IoClients
Thomas-Internetx Dec 5, 2024
80dfcb6
implemented SOCKS5 requests in an async way and added requested changes
Thomas-Internetx Dec 5, 2024
156c797
clean up
Thomas-Internetx Dec 5, 2024
3dae148
integration of SOCKS5 with the current selector thread and timeout im…
Thomas-Internetx Jan 10, 2025
8efe515
channel pool for UDP associate
tw-datascientist Jan 12, 2025
f9c1feb
integration the UDP channel pool
Thomas-Internetx Jan 12, 2025
9d0df8b
clean up of idle udp associate connections
Thomas-Internetx Jan 17, 2025
44f4415
further cleanup of the NioSocksHandler
Thomas-Internetx Jan 18, 2025
4cfaf31
better error handling
Thomas-Internetx Jan 18, 2025
07a6977
cleanup
Thomas-Internetx Jan 18, 2025
c365e36
adjustments for the socks5 handshake future
Thomas-Internetx Jan 18, 2025
260c4d5
Make compatible with Java 8
Thomas-Internetx Jan 18, 2025
02c3168
adjusting changes to upstream
Thomas-Internetx Jan 25, 2025
588df3d
clean up
Thomas-Internetx Jan 25, 2025
46a1b24
added testcontainer
Thomas-Internetx Jan 25, 2025
66a1e1d
adjusted corefile path
Thomas-Internetx Jan 25, 2025
ba1ea1c
adjusted corefile path
Thomas-Internetx Jan 25, 2025
baf40f2
adjusted docker compose setup
Thomas-Internetx Jan 25, 2025
ba11e90
adjusted docker compose setup
Thomas-Internetx Jan 25, 2025
8b71b09
implemented simple test for DNS over Socks
Thomas-Internetx Jan 25, 2025
c0ffa87
implemented simple test for DNS over Socks
Thomas-Internetx Jan 25, 2025
f36df78
implemented simple test for DNS over Socks
Thomas-Internetx Jan 25, 2025
240d206
implemented simple test for DNS over Socks
Thomas-Internetx Jan 25, 2025
c807b02
implemented simple test for DNS over Socks
Thomas-Internetx Jan 25, 2025
d0f3460
implemented simple test for DNS over Socks
Thomas-Internetx Jan 25, 2025
1f88de9
adjusting changes to upstream
Thomas-Internetx Jan 25, 2025
02ef998
adjusting changes to upstream
Thomas-Internetx Jan 25, 2025
3970b97
further clean up
Thomas-Internetx Jan 25, 2025
00bdea2
further clean up
Thomas-Internetx Jan 25, 2025
688fe6d
further clean up
Thomas-Internetx Jan 25, 2025
e94d15c
further clean up
Thomas-Internetx Jan 25, 2025
b621b32
improved comment for DST.ADDR and DST.PORT in the SOCKS handshake
Thomas-Internetx Jan 26, 2025
a12d004
improved comment for static registrationQueue and channelMap
Thomas-Internetx Jan 26, 2025
a8be13b
improved comment for response to future mapping
Thomas-Internetx Jan 26, 2025
fe2a79e
added step for docker setup
Thomas-Internetx Jan 26, 2025
73c0db0
set DOCKER_HOST
Thomas-Internetx Jan 26, 2025
99455be
reduced logs for Testcontainers
Thomas-Internetx Jan 26, 2025
fb3f015
fixed format violations
Thomas-Internetx Jan 26, 2025
938e726
fixed format violations
Thomas-Internetx Jan 26, 2025
b255bc3
try to make Testcontainers work with windows https://java.testcontain…
Thomas-Internetx Jan 26, 2025
10141bc
try to make Testcontainers work with windows https://java.testcontain…
Thomas-Internetx Jan 26, 2025
7caf075
try to make Testcontainers work with windows https://java.testcontain…
Thomas-Internetx Jan 26, 2025
fd301ba
try to make Testcontainers work with windows https://java.testcontain…
Thomas-Internetx Jan 26, 2025
3b62593
try to make Testcontainers work with windows https://java.testcontain…
Thomas-Internetx Jan 26, 2025
b97610d
try to make Testcontainers work with windows https://java.testcontain…
Thomas-Internetx Jan 26, 2025
4008b84
try to make Testcontainers work with windows https://java.testcontain…
Thomas-Internetx Jan 26, 2025
d416209
try to make Testcontainers work with windows https://java.testcontain…
Thomas-Internetx Jan 26, 2025
7b37e19
removed differentiation of remote address for SocksUdpAssociateChanne…
Thomas-Internetx Feb 9, 2025
d8dde40
fixed style
Thomas-Internetx Feb 10, 2025
be7245c
fixed style
Thomas-Internetx Feb 10, 2025
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
Next Next commit
added SOCKS5 support for DNS over tcp and udp (first working version)
  • Loading branch information
Thomas-Internetx committed Jan 26, 2025
commit 5ff82ed97d4bfbb7a6ed95682ecb45cacb3ece28
11 changes: 11 additions & 0 deletions src/main/java/org/xbill/DNS/DefaultIoClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ public CompletableFuture<byte[]> sendAndReceiveTcp(
return tcpIoClient.sendAndReceiveTcp(local, remote, query, data, timeout);
}

@Override
public CompletableFuture<byte[]> sendAndReceiveTcp(
InetSocketAddress local,
InetSocketAddress remote,
Socks5Proxy proxy,
Message query,
byte[] data,
Duration timeout) {
return tcpIoClient.sendAndReceiveTcp(local, remote, proxy, query, data, timeout);
}

@Override
public CompletableFuture<byte[]> sendAndReceiveUdp(
InetSocketAddress local,
Expand Down
23 changes: 22 additions & 1 deletion src/main/java/org/xbill/DNS/NioTcpClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ private void processPendingRegistrations() {
if (!state.channel.isConnected()) {
state.channel.register(selector, SelectionKey.OP_CONNECT, state);
} else {
if (state.channel.keyFor(selector) == null) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What did you encounter that this is necessary? Also, would it be possible to merge the condition with the outer if?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I encountered:

Exception in thread "dnsjava NIO selector" java.lang.NullPointerException: Cannot invoke "java.nio.channels.SelectionKey.interestOps(int)" because the return value of "java.nio.channels.SocketChannel.keyFor(java.nio.channels.Selector)" is null
	at org.dnsjava/org.xbill.DNS.NioTcpClient.processPendingRegistrations(NioTcpClient.java:47)
	at org.dnsjava/org.xbill.DNS.NioClient.runTasks(NioClient.java:168)
	at org.dnsjava/org.xbill.DNS.NioClient.runSelector(NioClient.java:133)
	at java.base/java.lang.Thread.run(Thread.java:1583)
Exception in thread "main" java.io.IOException: Timed out while trying to resolve google.com./A, id=25831
	at org.dnsjava/org.xbill.DNS.Resolver.send(Resolver.java:172)
	at org.dnsjava/org.xbill.DNS.tools.dig.main(dig.java:227)
Caused by: java.util.concurrent.TimeoutException
	at java.base/java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1960)
	at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2095)
	at org.dnsjava/org.xbill.DNS.Resolver.send(Resolver.java:153)
	... 1 more

The merge with the outer condition didn't work out for me.

state.channel.register(selector, SelectionKey.OP_CONNECT, state);
}
state.channel.keyFor(selector).interestOps(SelectionKey.OP_WRITE);
}
} catch (IOException e) {
Expand Down Expand Up @@ -285,10 +288,21 @@ private static class ChannelKey {
final InetSocketAddress remote;
}

@Override
public CompletableFuture<byte[]> sendAndReceiveTcp(
InetSocketAddress local,
InetSocketAddress remote,
Message query,
byte[] data,
Duration timeout) {
return this.sendAndReceiveTcp(local, remote, null, query, data, timeout);
}

@Override
public CompletableFuture<byte[]> sendAndReceiveTcp(
InetSocketAddress local,
InetSocketAddress remote,
Socks5Proxy proxy,
Message query,
byte[] data,
Duration timeout) {
Expand All @@ -309,7 +323,14 @@ public CompletableFuture<byte[]> sendAndReceiveTcp(
c.bind(local);
}

c.connect(remote);
if (proxy != null) {
c.configureBlocking(true);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is too easy :-)
The whole process of connecting to the proxy must be async here too. If this method starts to block, the entire thread pool can get exhausted and all the timeouts won't work anymore.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I probably need to implement it as KeyProcessor. I would like to use the Transaction class from NioTcpClient and make it a separate public class.

c.connect(proxy.getProxyAddress());
proxy.socks5TcpHandshake(c, remote);
} else {
c.connect(remote);
}
c.configureBlocking(false);
return new ChannelState(c);
} catch (IOException e) {
if (c != null) {
Expand Down
62 changes: 58 additions & 4 deletions src/main/java/org/xbill/DNS/SimpleResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.channels.SocketChannel;
import java.time.Duration;
import java.util.List;
import java.util.Objects;
Expand All @@ -19,6 +20,7 @@
import lombok.extern.slf4j.Slf4j;
import org.xbill.DNS.io.DefaultIoClientFactory;
import org.xbill.DNS.io.IoClientFactory;
import org.xbill.DNS.io.TcpIoClient;

/**
* An implementation of Resolver that sends one query to one server. SimpleResolver handles TCP
Expand All @@ -40,6 +42,7 @@ public class SimpleResolver implements Resolver {

private InetSocketAddress address;
private InetSocketAddress localAddress;
private Socks5Proxy proxy;
private boolean useTCP;
private boolean ignoreTruncation;
private OPTRecord queryOPT = new OPTRecord(DEFAULT_EDNS_PAYLOADSIZE, 0, 0, 0);
Expand Down Expand Up @@ -99,6 +102,11 @@ public SimpleResolver(InetSocketAddress host) {
address = Objects.requireNonNull(host, "host must not be null");
}

/** Creates a SimpleResolver that will query the specified host via the specified SOCKS5 proxy */
public SimpleResolver(Socks5Proxy socks5Proxy) {
proxy = Objects.requireNonNull(socks5Proxy, "proxy must not be null");
}

/** Creates a SimpleResolver that will query the specified host */
public SimpleResolver(InetAddress host) {
Objects.requireNonNull(host, "host must not be null");
Expand Down Expand Up @@ -387,22 +395,68 @@ CompletableFuture<Message> sendAsync(Message query, boolean forceTcp, Executor e
}

CompletableFuture<byte[]> result;
SocketChannel c = null;
if (tcp) {
result =
ioClientFactory
.createOrGetTcpClient()
.sendAndReceiveTcp(localAddress, address, query, out, timeoutValue);
TcpIoClient tcpClient = ioClientFactory.createOrGetTcpClient();
if (proxy != null) {
localAddress = proxy.getLocalAddress();
address = proxy.getRemoteAddress();
result = tcpClient.sendAndReceiveTcp(localAddress, address, proxy, query, out, timeoutValue);
} else {
result = tcpClient.sendAndReceiveTcp(localAddress, address, query, out, timeoutValue);
}
} else {
if (proxy != null) {
try {
c = SocketChannel.open();
if (localAddress != null) {
c.bind(localAddress);
}
if (proxy != null) {
c.connect(proxy.getProxyAddress());
address = proxy.socks5UdpAssociateHandshake(c);
}
} catch (IOException e) {
if (c != null) {
try {
c.close();
} catch (IOException ee) {
// ignore
}
}
return new CompletableFuture<>().thenComposeAsync(in -> {
CompletableFuture<Message> f = new CompletableFuture<>();
f.completeExceptionally(new WireParseException("Error in Udp Associate SOCKS5 handshake", e));
return f;
}, executor);
}
out = proxy.addUdpHeader(out, proxy.getRemoteAddress());
localAddress = proxy.getLocalAddress();
}
result =
ioClientFactory
.createOrGetUdpClient()
.sendAndReceiveUdp(localAddress, address, query, out, udpSize, timeoutValue);
}

SocketChannel finalC = c;
return result.thenComposeAsync(
in -> {
CompletableFuture<Message> f = new CompletableFuture<>();

// finally close the tcp connection
// and remove SOCKS5 udp header from the response
if (proxy != null && !tcp) {
if (finalC != null) {
try {
finalC.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
in = proxy.removeUdpHeader(in);
}

// Check that the response is long enough.
if (in.length < Header.LENGTH) {
f.completeExceptionally(new WireParseException("invalid DNS header - too short"));
Expand Down
192 changes: 192 additions & 0 deletions src/main/java/org/xbill/DNS/Socks5Proxy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package org.xbill.DNS;

import lombok.Getter;

import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

@Getter
public class Socks5Proxy {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if there's a need for timeout handling?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can set timeouts on the Dante config. It should reply with "TTL expired" then. Don't know, how to do this properly. There might be the need to track and handle this. Or you add a retry mechanism.

private static final byte SOCKS5_VERSION = 0x05;

private static final byte SOCKS5_AUTH_NONE = 0x00;
private static final byte SOCKS5_AUTH_GSSAPI = 0x01;
private static final byte SOCKS5_AUTH_USER_PASS = 0x02;
private static final byte SOCKS5_AUTH_NO_ACCEPTABLE_METHODS = (byte) 0xFF;

private static final byte SOCKS5_CMD_CONNECT = 0x01;
private static final byte SOCKS5_CMD_BIND = 0x02;
private static final byte SOCKS5_CMD_UDP_ASSOCIATE = 0x03;

private static final byte SOCKS5_ATYP_IPV4 = 0x01;
private static final byte SOCKS5_ATYP_DOMAINNAME = 0x03;
private static final byte SOCKS5_ATYP_IPV6 = 0x04;

private static final byte SOCKS5_REP_SUCCEEDED = 0x00;
private static final byte SOCKS5_REP_GENERAL_FAILURE = 0x01;
private static final byte SOCKS5_REP_CONNECTION_NOT_ALLOWED = 0x02;
private static final byte SOCKS5_REP_NETWORK_UNREACHABLE = 0x03;
private static final byte SOCKS5_REP_HOST_UNREACHABLE = 0x04;
private static final byte SOCKS5_REP_CONNECTION_REFUSED = 0x05;
private static final byte SOCKS5_REP_TTL_EXPIRED = 0x06;
private static final byte SOCKS5_REP_COMMAND_NOT_SUPPORTED = 0x07;
private static final byte SOCKS5_REP_ADDRESS_TYPE_NOT_SUPPORTED = 0x08;

private static final byte SOCKS5_RESERVED = 0x00;

private final InetSocketAddress remoteAddress;
private final InetSocketAddress localAddress;
private final InetSocketAddress proxyAddress;


public Socks5Proxy(InetSocketAddress proxyAddress, InetSocketAddress remoteAddress, InetSocketAddress localAddress) {
this.remoteAddress = remoteAddress;
this.localAddress = localAddress;
this.proxyAddress = proxyAddress;
}

public void socks5MethodSelection(SocketChannel c) {
ByteBuffer buffer = ByteBuffer.allocate(3);
buffer.put(SOCKS5_VERSION);
buffer.put((byte) 1);
buffer.put(SOCKS5_AUTH_NONE);
buffer.flip();

try {
c.write(buffer);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return the return value, then handle it everywhere the write might have failed but not thrown.

} catch (Exception e) {
throw new IllegalArgumentException("Failed to write to TCP channel", e);
}

buffer.clear();

try {
c.read(buffer);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return the return value and handle it everywhere

} catch (Exception e) {
throw new IllegalArgumentException("Failed to read from TCP channel", e);
}

buffer.flip();
if (buffer.get() != SOCKS5_VERSION) {
throw new IllegalArgumentException("Invalid version");
}

if (buffer.get() == SOCKS5_AUTH_NO_ACCEPTABLE_METHODS) {
throw new IllegalArgumentException("No acceptable methods");
}
}

public void socks5HeaderExchange(SocketChannel c, InetSocketAddress remote) {
ByteBuffer buffer = ByteBuffer.allocate(10);
buffer.put(SOCKS5_VERSION);
buffer.put(SOCKS5_CMD_CONNECT);
buffer.put(SOCKS5_RESERVED);
buffer.put(SOCKS5_ATYP_IPV4);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle IPv6; and remote could also be a hostname. What if the proxy should resolve the hostname because the client isn't even able to?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I adjusted it to

if (remote.getAddress() instanceof Inet4Address) {
  addressType = SOCKS5_ATYP_IPV4;
  addressBytes = remote.getAddress().getAddress();
  buffer = ByteBuffer.allocate(10);
} else if (remote.getAddress() instanceof Inet6Address) {
  addressType = SOCKS5_ATYP_IPV6;
  addressBytes = remote.getAddress().getAddress();
  buffer = ByteBuffer.allocate(22);
} else {
  addressType = SOCKS5_ATYP_DOMAINNAME;
  addressBytes = remote.getHostName().getBytes(StandardCharsets.UTF_8);
  buffer = ByteBuffer.allocate(7 + addressBytes.length);
}

buffer.put(SOCKS5_VERSION);
buffer.put(SOCKS5_CMD_CONNECT);
buffer.put(SOCKS5_RESERVED);
buffer.put(addressType);
if (addressType == SOCKS5_ATYP_DOMAINNAME) {
  buffer.put((byte) addressBytes.length);
}
buffer.put(addressBytes);
buffer.putShort((short) remote.getPort());

buffer.put(remote.getAddress().getAddress());
buffer.putShort((short) remote.getPort());
buffer.flip();

try {
c.write(buffer);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to write to TCP channel", e);
}
buffer.clear();

try {
c.read(buffer);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to read from TCP channel", e);
}
buffer.flip();

if (buffer.get() != SOCKS5_VERSION) {
throw new IllegalArgumentException("Invalid version");
}

byte reply = buffer.get();
if (reply != SOCKS5_REP_SUCCEEDED) {
throw new IllegalArgumentException("Failed to connect to remote server: " + reply);
}
}

public InetSocketAddress socks5UdpAssociateExchange(SocketChannel c) {
ByteBuffer buffer = ByteBuffer.allocate(10);
buffer.put(SOCKS5_VERSION);
buffer.put(SOCKS5_CMD_UDP_ASSOCIATE);
buffer.put(SOCKS5_RESERVED);
buffer.put(SOCKS5_ATYP_IPV4);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This must be dynamic and properly handle IPv6, including all the places where only IPv4 is assumed later on too.

buffer.put(new byte[] {0, 0, 0, 0});
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use .putInt(0) instead to avoid the array creation.

buffer.putShort((short) 0x00);
buffer.flip();

try {
c.write(buffer);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to write to TCP channel", e);
}
buffer.clear();

try {
c.read(buffer);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to read from TCP channel", e);
}
buffer.flip();

if (buffer.get() != SOCKS5_VERSION) {
throw new IllegalArgumentException("Invalid version");
}

byte reply = buffer.get();
if (reply != SOCKS5_REP_SUCCEEDED) {
throw new IllegalArgumentException("Failed to connect to remote server: " + reply);
}

buffer.get(); // skip RSV byte

byte atyp = buffer.get();
if (atyp != SOCKS5_ATYP_IPV4) {
throw new IllegalArgumentException("Invalid address type");
}

byte[] addr = new byte[4];
buffer.get(addr);
int port = buffer.getShort() & 0xFFFF;
return new InetSocketAddress(this.getProxyAddress().getAddress(), port);
}

public byte[] addUdpHeader(byte[] in, InetSocketAddress to) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle IPv6 in this method

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this IPv6 worked for me

ByteBuffer buffer;
byte addressType;
byte[] addressBytes;
if (remote.getAddress() instanceof Inet4Address) {
  addressType = SOCKS5_ATYP_IPV4;
  addressBytes = remote.getAddress().getAddress();
  buffer = ByteBuffer.allocate(4 + addressBytes.length + 2 + data.length);
} else if (remote.getAddress() instanceof Inet6Address) {
  addressType = SOCKS5_ATYP_IPV6;
  addressBytes = remote.getAddress().getAddress();
  buffer = ByteBuffer.allocate(4 + addressBytes.length + 2 + data.length);
} else {
  addressType = SOCKS5_ATYP_DOMAINNAME;
  addressBytes = remote.getHostName().getBytes(StandardCharsets.UTF_8);
  buffer = ByteBuffer.allocate(4 + 1 + addressBytes.length + 2 + data.length);
}

buffer.put((byte) 0x00); // RSV
buffer.put((byte) 0x00); // RSV
buffer.put((byte) 0x00); // FRAG
buffer.put(addressType); // ATYP (IPv4)
if (addressType == SOCKS5_ATYP_DOMAINNAME) {
  buffer.put((byte) addressBytes.length);
}
buffer.put(addressBytes); // DST.ADDR
buffer.putShort((short) remote.getPort()); // DST.PORT
buffer.put(data); // DATA

ByteBuffer buffer = ByteBuffer.allocate(in.length + 10);
buffer.put(SOCKS5_VERSION);
buffer.put(SOCKS5_RESERVED);
buffer.put(SOCKS5_RESERVED);
buffer.put(SOCKS5_ATYP_IPV4);
buffer.put(to.getAddress().getAddress());
buffer.putShort((short) to.getPort());
buffer.put(in);

return buffer.array();
}

public byte[] removeUdpHeader(byte[] in) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle IPv6

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It keeps track of the headerLength in case of IPv4, IPv6 or domainname and removes it accordingly

byte[] out = new byte[in.length - 10];
System.arraycopy(in, 10, out, 0, in.length - 10);
return out;
}

public void socks5TcpHandshake(
SocketChannel c, InetSocketAddress remote) {
this.socks5MethodSelection(c);
this.socks5HeaderExchange(c, remote);
}

public InetSocketAddress socks5UdpAssociateHandshake(
SocketChannel c
) throws UnknownHostException {
this.socks5MethodSelection(c);
return this.socks5UdpAssociateExchange(c);
}
}
9 changes: 9 additions & 0 deletions src/main/java/org/xbill/DNS/io/TcpIoClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.concurrent.CompletableFuture;
import org.xbill.DNS.Message;
import org.xbill.DNS.Resolver;
import org.xbill.DNS.Socks5Proxy;

/**
* Serves as an interface from a {@link Resolver} to the underlying mechanism for sending bytes over
Expand All @@ -32,4 +33,12 @@ CompletableFuture<byte[]> sendAndReceiveTcp(
Message query,
byte[] data,
Duration timeout);

CompletableFuture<byte[]> sendAndReceiveTcp(
InetSocketAddress local,
InetSocketAddress remote,
Socks5Proxy proxy,
Message query,
byte[] data,
Duration timeout);
}