GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — New Feature: DNS over SOCKS by tw-datascientist · Pull Request #347 · dnsjava/dnsjava

Conversation

@tw-datascientist

@tw-datascientist tw-datascientist commented Oct 19, 2024

Copy link
Copy Markdown

I added SOCKS5 support to the ioClients.
It does work for TCP and UDP (udp associate).
It also supports user/pwd authentication.

The implementation is based on

  • RFC 1928: SOCKS Protocol Version 5 (https://datatracker.ietf.org/doc/html/rfc1928)
  • RFC 1929: Username/Password Authentication for SOCKS V5 (https://datatracker.ietf.org/doc/html/rfc1929)

I tested it on a Dante SOCKS5 proxy with following config.

# logging
errorlog: /var/log/sockd.errlog
logoutput: /var/log/sockd.log

# server address specification
internal: 0.0.0.0 port = 1080
external: eth0

# auth
#user.privileged: root
user.notprivileged: nobody
socksmethod: none   # "none" or "username" user/pwd auth

# Allow everyone to connect to this server.
client pass {
    from: 0.0.0.0/0 to: 0.0.0.0/0
    log: connect disconnect error 
}

# Allow all operations for connected clients on this server.
socks pass {
    from: 0.0.0.0/0 to: 0.0.0.0/0
    command: bind connect udpassociate
    log: connect disconnect iooperation error  
    socksmethod: none  # "none" or "username" user/pwd auth

    udp.portrange: 10000-10001
}

# Allow all inbound packets.
socks pass {
    from: 0.0.0.0/0 to: 0.0.0.0/0
    command: bindreply udpreply
    log: error connect disconnect iooperation 
}

You should be able to install the Dante SOCKS5 proxy via package managers like apt, apk or brew.
I can also provide you a Docker setup.

I tried it like

InetSocketAddress localAddress = new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0);
InetSocketAddress remoteAddress = new InetSocketAddress(InetAddress.getByName("1.1.1.1"), 53);
InetSocketAddress proxyAddress = new InetSocketAddress(InetAddress.getByName("<socks5-host>"), 1080);
Socks5Proxy proxy = new Socks5Proxy(proxyAddress, remoteAddress, localAddress);
res = new SimpleResolver(proxy);
res.setTCP(true); // optional

or

InetSocketAddress localAddress = new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0);
InetSocketAddress remoteAddress = new InetSocketAddress(InetAddress.getByName("1.1.1.1"), 53);
InetSocketAddress proxyAddress = new InetSocketAddress(InetAddress.getByName("<socks5-host>"), 1080);
String socks5User = "<user>";
String socks5Password = "<password>";
Socks5Proxy proxy = new Socks5Proxy(proxyAddress, remoteAddress, localAddress, socks5User, socks5Password);
res = new SimpleResolver(proxy);
res.setTCP(true);  // optional

I implemented it with method overloading on sendAndReceiveTcp and sendAndReceiveUDP. In this way it does not affect the tests.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@ibauersachs

ibauersachs commented Nov 10, 2024

Copy link
Copy Markdown
Member

I haven't forgotten about this, but as mentioned, it'll take some more time until I can review it properly.
Thanks for your work so far!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@tw-datascientist

tw-datascientist commented Nov 12, 2024

Copy link
Copy Markdown
Author

No worries, take your time. Thanks for the update.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ibauersachs

ibauersachs commented Nov 26, 2024

Copy link
Copy Markdown
Member

I'll review in more detail later, but as a first change, please undo the new *sendAndReceive methods in the interfaces and their corresponding usage in SimpleResolver (as well as the new proxy constructor there) and DefaultIoClient. Instead, please create a dedicated IoClientFactory, e.g. Socks5ProxyIoClientFactory and Socks5ProxyIoClient, this can then internally handle the proxy.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ibauersachs
ibauersachs self-requested a review November 26, 2024 14:01
ibauersachs
ibauersachs requested changes Nov 26, 2024
View reviewed changes
Comment thread src/main/java/org/xbill/DNS/NioTcpClient.java Outdated

c.connect(remote);
if (proxy != null) {
c.configureBlocking(true);

@ibauersachs ibauersachs Nov 26, 2024

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@tw-datascientist tw-datascientist Dec 5, 2024

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/main/java/org/xbill/DNS/NioUdpClient.java Outdated
if (local != null) {
tcpChannel.bind(local);
}
tcpChannel.connect(proxy.getProxyAddress());

@ibauersachs ibauersachs Nov 26, 2024

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Here too, this needs to be fully async. Also, this creates a dedicated TCP connection to the proxy for each request. Shouldn't there be just one (or maybe one per remote) and reused?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@tw-datascientist tw-datascientist Dec 5, 2024

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

You need only one open TCP connection to establish the UDP relay.
It might be optimal to have just one or a pool off connections.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/main/java/org/xbill/DNS/NioTcpClient.java Outdated
if (!state.channel.isConnected()) {
state.channel.register(selector, SelectionKey.OP_CONNECT, state);
} else {
if (state.channel.keyFor(selector) == null) {

@ibauersachs ibauersachs Nov 26, 2024

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

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?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@tw-datascientist tw-datascientist Dec 5, 2024

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/main/java/org/xbill/DNS/Socks5Proxy.java Outdated

private void writeToChannel(SocketChannel c, ByteBuffer buffer) {
try {
c.write(buffer);

@ibauersachs ibauersachs Nov 26, 2024

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/main/java/org/xbill/DNS/Socks5Proxy.java Outdated

private void readFromChannel(SocketChannel c, ByteBuffer buffer) {
try {
c.read(buffer);

@ibauersachs ibauersachs Nov 26, 2024

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

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

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/main/java/org/xbill/DNS/Socks5Proxy.java Outdated
return new InetSocketAddress(this.getProxyAddress().getAddress(), port);
}

public byte[] addUdpHeader(byte[] in, InetSocketAddress to) {

@ibauersachs ibauersachs Nov 26, 2024

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

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

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@tw-datascientist tw-datascientist Dec 5, 2024

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

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

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/main/java/org/xbill/DNS/Socks5Proxy.java Outdated
return buffer.array();
}

public byte[] removeUdpHeader(byte[] in) {

@ibauersachs ibauersachs Nov 26, 2024

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Handle IPv6

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@tw-datascientist tw-datascientist Dec 5, 2024

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

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

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/main/java/org/xbill/DNS/Socks5Proxy.java Outdated
buffer.put(SOCKS5_VERSION);
buffer.put(SOCKS5_CMD_CONNECT);
buffer.put(SOCKS5_RESERVED);
buffer.put(SOCKS5_ATYP_IPV4);

@ibauersachs ibauersachs Nov 26, 2024

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

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?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@tw-datascientist tw-datascientist Dec 5, 2024

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

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());

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/main/java/org/xbill/DNS/Socks5Proxy.java Outdated
ByteBuffer buffer = ByteBuffer.allocate(3);
buffer.put(SOCKS5_VERSION);
buffer.put((byte) 1);
buffer.put((this.socks5User != null && this.socks5Password != null) ? SOCKS5_AUTH_USER_PASS : SOCKS5_AUTH_NONE);

@ibauersachs ibauersachs Nov 26, 2024

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Java has built-in GSSAPI support. Would be nice to include the support here too, or at least prepare the code flow to be extendable.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@tw-datascientist tw-datascientist Dec 5, 2024

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I tried to use org.ietf.jgss, but I'm getting

Field gssCredential: Undefined reference: org.ietf.jgss.GSSCredential
Undefined reference: org.ietf.jgss.GSSCredential

when running mvn clean package

Adding requires java.security.jgss; to the module-info.java didn't help.
Do you have an idea, why this is happening?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/main/java/org/xbill/DNS/Socks5Proxy.java Outdated
import java.util.Objects;

@Getter
public class Socks5Proxy {

@ibauersachs ibauersachs Nov 26, 2024

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

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?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@tw-datascientist tw-datascientist Dec 5, 2024

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@tw-datascientist

tw-datascientist commented Dec 5, 2024

Copy link
Copy Markdown
Author

I undid the changes in SimpleResolver, DefaultIoClient and *sendAndReceive.

I also added a Socks5ProxyIoClientFactory. Now, you can set it up like following:

InetSocketAddress proxyAddress = new InetSocketAddress(InetAddress.getByName("<socks5-host>"), 1080);
String socks5User = "<user>";
String socks5Password = "<password>";
Socks5ProxyConfig config = new Socks5ProxyConfig(proxyAddress, socks5User, socks5Password);
res.setIoClientFactory(new Socks5ProxyIoClientFactory(config));

I couldn't figure out how to add the SOCKS5 transactions with the NioClient. I implemented it like an event loop in the Socks5ProxyIoClientFactory and added the read and write operations in the attachment of the channel keys. I tried to implement the timeout handling with a ScheduledExecutorService, but I think it is not ready, yet.

Now it's fully async. IPv6 worked with 2001:4860:4860::8888 for me. It also reuses the connection without repeating the SOCKS5 handshake again.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ibauersachs ibauersachs linked an issue Dec 15, 2024 that may be closed by this pull request
DNS over SOCKS #341
Open
@tw-datascientist

tw-datascientist commented Jan 4, 2025

Copy link
Copy Markdown
Author

Hi @ibauersachs. I suggested a huge amount of code additions and I also have changed a lot since the requested changes. I think, I should give some more descriptions.

Initially, I did not understand the NioClient back then. So, I attempted to implement it in a way that suited the handling of the SOCKS5 handshake. My concept was to start with an event loop that uses the Selector as thread safe queue and store the read/write logic as Runnables in the attachment of the SelectionKey. A new Runnable is set in the attachment at the end of the previous one to chain the Runnables in the right order. Or the CompletableFuture is completed. It should also work together with a worker pool to execute the Runnables in a multithreaded way.

Then later I realized I could not sync up the close operations for the implementation of timeouts into the event loop through the Selector. I didn't want to use selector.select(timeout) and opted for a solution with the java.util.concurrent.ScheduledExecutorService. I think, there is still a problem with synchronizing the close operation with the connect, read and write operations in a thread safe way. Now I think, I should write a method that handles connect, read and write and wrap it around a synchronized(tcpSelectionKey) block in Socks5ProxyTcpIoClient. Similar for Socks5ProxyUdpIoClient.

To reuse the connections and avoiding the repetition of the SOCKS handshake, I defined private static final Map<String, Map<String, Socks5Proxy>> socks5ConnectionPool = new ConcurrentHashMap<>(); Initially, I used a simple Map<String, Socks5Proxy>, but I realized that I needed to track the state of the TCP connection to prevent the SOCKS handshake and read/write operations from being disrupted by concurrent usage. Therefore, I decided to use a nested Map structure to track the connections per remote address in the outer Map and manage multiple connections for the same remote host in a the inner Map. This structure might require a cleanup strategy to close connections that aren't used for a longer time. I used the SelectionKey registration and deregistration from the Selector to track whether a connection is usable or if a new connection to the host needs to be established. This way I prevent that concurrent use messes up the UDP/TCP transaction of a connection.

What do you think?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ibauersachs

ibauersachs commented Jan 4, 2025

Copy link
Copy Markdown
Member

Hi, sorry for not getting back to you yet. I haven't had time to thoroughly review this PR again, and as you probably figured out, the NIO stuff is nasty.

At first glance, there's a lot of duplication between the existing IO client classes and what you wrote now. I wonder if there couldn't be more shared code, especially the selector thread and timeout handling. The IO clients are currently package-private, so there can be changes to the API if needed.
The general idea with the map of connections etc. is probably correct.

It would also be good if there were some unit/integration tests. You could use Testcontainers to run a real proxy to test against.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@tw-datascientist

tw-datascientist commented Jan 5, 2025

Copy link
Copy Markdown
Author

Hi. Thanks for your quick reply.

Yes. I now believe that the logic for the SOCKS proxy should be built on top of the NioClients. I can't use the current NioTcpClient because of this part ChannelState channel = channelMap.computeIfAbsent(new ChannelKey(local, remote), ...). For SOCKS, the remote would always be the proxy.
It would suggest to have the selector thread, timeout handling and connection pooling in the IoClientFactory and passing a connection handler to the IoClients. I will try something like this.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ibauersachs

ibauersachs commented Jan 5, 2025

Copy link
Copy Markdown
Member

The current TcpIoClient implementation currently makes a hard assumption that it's talking directly with DNS servers, also with the prefix-length expected in each reply. You can also try introducing an intermediate class if it helps:

         NioClient 
             |
     AbstractTcpClient
     |               |
DnsTcpClient    SocksClient

Going back to the IoClientFactory will provide the challenge of keeping the public interface stable.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@tw-datascientist

tw-datascientist commented Jan 5, 2025

Copy link
Copy Markdown
Author

That might help. I try it out.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@tw-datascientist

tw-datascientist commented Jan 26, 2025

Copy link
Copy Markdown
Author

Hallo @ibauersachs. Thanks for your input. I appreciate it.

I integrated the transactions with the IoClients. Selector and timeout handling is now shared. Also I added 3 basic tests with Testcontainers (ComposeContainer).

The builds for this pull request are currently failing. Probably, Docker is not set up in the build containers.

Error:  Errors: 
Error:  org.xbill.DNS.io.SimpleSocksTest.null
Error:    Run 1: SimpleSocksTest.setUp:27 � IllegalState Could not find a valid Docker environment. Please see logs and check configuration
Error:    Run 2: SimpleSocksTest.setUp:27 � ContainerFetch Can't get Docker image: RemoteDockerImage(imageName=docker:24.0.2, imagePullPolicy=DefaultPullPolicy(), 

It works fine on my server.

[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 38.84 s -- in org.xbill.DNS.io.SimpleSocksTest

It seems that on Mac, there is an issue with routing UDP traffic into the Docker container. I do not believe this is related to the proper functioning of the SOCKS interactions.

[ERROR] Errors: 
[ERROR] org.xbill.DNS.io.SimpleSocksTest.testAuth
[ERROR]   Run 1: SimpleSocksTest.testAuth » PortUnreachable

It works fine when I try it from a test container in the same Docker network.

The introduction of intermediate classes worked well.

                                NioClient 
                            /              \                                
                NioTcpHandler               NioUdpHandler
           /        |          \             /       \          
NioTcpClient NioSocksTcpClient  NioSocksUdpClient    NioUdpClient
                          \        /        
                        NioSocksHandler

For the NioSocksHandler, I now handle I/O using futures and the provided Transaction classes. I use a future for the entire SOCKS handshake and one for each transaction.

For the NioSocksTcpClient, I moved the logic from NioTcpClient to the NioTcpHandler. I introduced boolean attributes to perform operations for the SOCKS interactions when necessary. I added a future as an attribute for the SOCKS handshake on the ChannelState to manage the reuse of the TCP channels without redoing the SOCKS handshake.

For the NioSocksUdpClient, I encountered an issue where the SOCKS proxy only accepts UDP interactions from the same port after the first interaction. To address this, I wrote NioSocksUdpAssociateChannelPool, that keeps track of the established SOCKS UDP associations and handles multiple SOCKS connections per local+remote address.

I plan to add more tests and further improve and simplify the NioSocksUdpAssociateChannelPool to avoid needing a new SOCKS connection for each remote address. And I'm looking for a way to reduce the log output of Testcontainers.

It would be nice to get a short review and your opinion on the pull request.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ibauersachs

ibauersachs commented Jan 26, 2025

Copy link
Copy Markdown
Member

Thanks! I'll need a while to dig through all of this.

As for the integration tests, you could add a tag to only run them on the stage that also runs Sonar (i.e. Ubuntu and currently Java 21). Docker might need to be installed beforehand, you can do that as an actions step with apt-get install.

Also, can you please rebase the branch on top of master?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Thomas-Internetx and others added 16 commits January 26, 2025 16:58
@Thomas-Internetx
added SOCKS5 support for DNS over tcp and udp (first working version)
5ff82ed
@Thomas-Internetx
refactored code for udp associate into IoClient
5a41225
@Thomas-Internetx
added user/pwd authentication and refactored some of the code
bee0c76
@Thomas-Internetx
redo code changes in SimpleResolver and the IoClients
2a1192c
@Thomas-Internetx
implemented SOCKS5 requests in an async way and added requested changes
80dfcb6
@Thomas-Internetx
clean up
156c797
@Thomas-Internetx
integration of SOCKS5 with the current selector thread and timeout im…
3dae148
…plementation
@tw-datascientist @Thomas-Internetx
channel pool for UDP associate
8efe515
@Thomas-Internetx
integration the UDP channel pool
f9c1feb
@Thomas-Internetx
clean up of idle udp associate connections
9d0df8b
@Thomas-Internetx
further cleanup of the NioSocksHandler
44f4415
@Thomas-Internetx
better error handling
4cfaf31
@Thomas-Internetx
cleanup
07a6977
@Thomas-Internetx
adjustments for the socks5 handshake future
c365e36
@Thomas-Internetx
Make compatible with Java 8
260c4d5
@Thomas-Internetx
adjusting changes to upstream
02c3168
Thomas-Internetx added 7 commits January 26, 2025 17:01
@Thomas-Internetx
implemented simple test for DNS over Socks
d0f3460
@Thomas-Internetx
adjusting changes to upstream
1f88de9
@Thomas-Internetx
adjusting changes to upstream
02ef998
@Thomas-Internetx
further clean up
3970b97
@Thomas-Internetx
further clean up
00bdea2
@Thomas-Internetx
further clean up
688fe6d
@Thomas-Internetx
further clean up
e94d15c
@tw-datascientist
tw-datascientist force-pushed the adjust_to_upstream branch from bb16b23 to e94d15c Compare January 26, 2025 16:05
@tw-datascientist

tw-datascientist commented Jan 26, 2025

Copy link
Copy Markdown
Author

I rebased the branch on top of master.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Thomas-Internetx added 19 commits January 26, 2025 17:33
@Thomas-Internetx
improved comment for DST.ADDR and DST.PORT in the SOCKS handshake
b621b32
@Thomas-Internetx
improved comment for static registrationQueue and channelMap
a12d004
@Thomas-Internetx
improved comment for response to future mapping
a8be13b
@Thomas-Internetx
added step for docker setup
fe2a79e
@Thomas-Internetx
set DOCKER_HOST
73c0db0
@Thomas-Internetx
reduced logs for Testcontainers
99455be
https://java.testcontainers.org/supported_docker_environment/logging_config/
@Thomas-Internetx
fixed format violations
fb3f015
@Thomas-Internetx
fixed format violations
938e726
@Thomas-Internetx
try to make Testcontainers work with windows https://java.testcontain…
b255bc3
…ers.org/supported_docker_environment/windows/
@Thomas-Internetx
try to make Testcontainers work with windows https://java.testcontain…
10141bc
…ers.org/supported_docker_environment/windows/
@Thomas-Internetx
try to make Testcontainers work with windows https://java.testcontain…
7caf075
…ers.org/supported_docker_environment/windows/
@Thomas-Internetx
try to make Testcontainers work with windows https://java.testcontain…
fd301ba
…ers.org/supported_docker_environment/windows/
@Thomas-Internetx
try to make Testcontainers work with windows https://java.testcontain…
3b62593
…ers.org/supported_docker_environment/windows/
@Thomas-Internetx
try to make Testcontainers work with windows https://java.testcontain…
b97610d
…ers.org/supported_docker_environment/windows/
@Thomas-Internetx
try to make Testcontainers work with windows https://java.testcontain…
4008b84
…ers.org/supported_docker_environment/windows/
@Thomas-Internetx
try to make Testcontainers work with windows https://java.testcontain…
d416209
…ers.org/supported_docker_environment/windows/
@Thomas-Internetx
removed differentiation of remote address for SocksUdpAssociateChanne…
7b37e19
…l + better error handling
@Thomas-Internetx
fixed style
d8dde40
@Thomas-Internetx
fixed style
be7245c
@ibauersachs
ibauersachs force-pushed the master branch 2 times, most recently from a601d27 to 378aa24 Compare May 10, 2026 11:16

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@ibauersachs ibauersachs ibauersachs requested changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

DNS over SOCKS

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@tw-datascientist @ibauersachs @Thomas-Internetx
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.