From b496e8457ca3001b31dc73a88688512d8907c996 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 26 Feb 2023 15:32:35 -0500 Subject: [PATCH] it works minus SRV --- core/src/mindustry/net/ArcNetProvider.java | 16 ++- core/src/mindustry/net/AsyncUdp.java | 19 +-- core/src/mindustry/net/Dns.java | 146 +++++++++++++++++++++ 3 files changed, 165 insertions(+), 16 deletions(-) create mode 100644 core/src/mindustry/net/Dns.java diff --git a/core/src/mindustry/net/ArcNetProvider.java b/core/src/mindustry/net/ArcNetProvider.java index 8bde23969b..14777c1510 100644 --- a/core/src/mindustry/net/ArcNetProvider.java +++ b/core/src/mindustry/net/ArcNetProvider.java @@ -212,14 +212,16 @@ public class ArcNetProvider implements NetProvider{ @Override public void pingHost(String address, int port, Cons valid, Cons invalid){ long time = Time.millis(); - var socket = new InetSocketAddress(address, port); - Log.info("Time to resolve @: @", address, Time.timeSinceMillis(time)); - AsyncUdp.send(socket, 2000, 512, ByteBuffer.wrap(new byte[]{-2, 1}), data -> { - Host host = NetworkIO.readServerData((int)Time.timeSinceMillis(time), socket.getAddress().getHostAddress(), data); - host.port = port; - Core.app.post(() -> valid.get(host)); - }, e -> Core.app.post(() -> invalid.get(e))); + Dns.resolveAddress(address, inetaddr -> { + var socket = new InetSocketAddress(inetaddr, port); + + AsyncUdp.send(socket, 2000, 512, ByteBuffer.wrap(new byte[]{-2, 1}), data -> { + Host host = NetworkIO.readServerData((int)Time.timeSinceMillis(time), socket.getAddress().getHostAddress(), data); + host.port = port; + Core.app.post(() -> valid.get(host)); + }, e -> Core.app.post(() -> invalid.get(e))); + }, err -> Core.app.post(() -> invalid.get(err))); /* try{ diff --git a/core/src/mindustry/net/AsyncUdp.java b/core/src/mindustry/net/AsyncUdp.java index 45d0d9725b..8e74aee22d 100644 --- a/core/src/mindustry/net/AsyncUdp.java +++ b/core/src/mindustry/net/AsyncUdp.java @@ -54,15 +54,14 @@ public class AsyncUdp{ request.received.get(buffer); request.close(); - }catch(IOException error){ + }catch(Exception error){ request.fail(error); - //TODO remove logging, this is not needed outside of debugging - Log.err(error); } } } - }catch(IOException e){ + }catch(Throwable e){ + //should not happen Log.err(e); } } @@ -98,7 +97,6 @@ public class AsyncUdp{ removals.offer(req); }catch(Exception e){ - Log.err(e); failed.get(e); } }); @@ -115,6 +113,8 @@ public class AsyncUdp{ final DatagramChannel channel; final SelectionKey key; + boolean closed = false; + public Request(InetSocketAddress address, long timeout, int bufferSize, ByteBuffer data, DatagramChannel channel, SelectionKey key, Cons received, Cons failed){ this.address = address; this.timeout = timeout; @@ -128,8 +128,8 @@ public class AsyncUdp{ } void close(){ - if(!key.isValid()) return; try{ + closed = true; key.cancel(); channel.close(); }catch(Exception close){ @@ -138,9 +138,10 @@ public class AsyncUdp{ } void fail(Exception error){ - if(!key.isValid()) return; - failed.get(error); - close(); + if(!closed){ + failed.get(error); + close(); + } } @Override diff --git a/core/src/mindustry/net/Dns.java b/core/src/mindustry/net/Dns.java new file mode 100644 index 0000000000..5bd1503e8a --- /dev/null +++ b/core/src/mindustry/net/Dns.java @@ -0,0 +1,146 @@ +package mindustry.net; + +import arc.func.*; +import arc.math.*; +import arc.struct.*; +import arc.util.*; + +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.charset.*; +import java.util.regex.*; + +public class Dns{ + private static final Pattern ipPattern = Pattern.compile("^((0|1\\d?\\d?|2[0-4]?\\d?|25[0-5]?|[3-9]\\d?)\\.){3}(0|1\\d?\\d?|2[0-4]?\\d?|25[0-5]?|[3-9]\\d?)$"); + private static final int aRecord = 1, srvRecord = 33; + private static IntMap>> cache = new IntMap<>(); + private static Seq nameservers = Seq.with(new InetSocketAddress("1.1.1.1", 53), new InetSocketAddress("8.8.8.8", 53)); + + static void resolve(int type, String domain, Func reader, Cons> result, Cons error){ + ObjectMap> map; + + synchronized(cache){ + map = cache.get(type, ObjectMap::new); + + //TODO timeout + if(map.containsKey(domain)){ + result.get((Seq)map.get(domain)); + return; + } + } + + send(Seq.with(new InetSocketAddress("1.1.1.1", 53)), 0, type, domain, reader, records -> { + synchronized(cache){ + //cache the records + map.put(domain, records); + } + + result.get(records); + }, error); + } + + static void resolveAddress(String domain, Cons result, Cons error){ + if(ipPattern.matcher(domain).matches()){ + try{ + result.get(InetAddress.getByName(domain)); + }catch(Exception e){ + error.get(e); + return; + } + } + + resolve(aRecord, domain, bytes -> { + byte[] address = new byte[4]; + bytes.get(address); + return address; + }, addresses -> { + try{ + if(addresses.size > 0){ + result.get(InetAddress.getByAddress(addresses.get(0))); + }else{ + //there are no records found + error.get(new UnresolvedAddressException()); + } + }catch(UnknownHostException unknown){ + error.get(unknown); + } + }, error); + } + + static void send(Seq addresses, int index, int type, String domain, Func reader, Cons> recordResult, Cons error){ + short id = (short)new Rand().nextInt(Short.MAX_VALUE); + + ByteBuffer buffer = ByteBuffer.allocate(512); + + buffer.putShort(id); // Id + buffer.putShort((short) 0x0100); // Flags (recursion enabled) + buffer.putShort((short) 1); // Questions + buffer.putShort((short) 0); // Answers + buffer.putShort((short) 0); // Authority + buffer.putShort((short) 0); // Additional + + // Domain + for(String part : domain.split("\\.")) { + buffer.put((byte) part.length()); + buffer.put(part.getBytes(StandardCharsets.UTF_8)); + } + buffer.put((byte) 0); + + buffer.putShort((short) type); // Type + buffer.putShort((short) 1); // Class (Internet) + + buffer.flip(); + + AsyncUdp.send(addresses.get(index), 2000, 512, buffer, result -> { + short responseId = result.getShort(); + if(responseId != id) { + throw new ArcRuntimeException("Invalid response ID"); + } + + result.getShort(); + result.getShort(); + int answers = result.getShort() & 0xFFFF; + result.getShort(); + result.getShort(); + + byte len; + while((len = result.get()) != 0) { + result.position(result.position() + len); + } + + result.getShort(); + result.getShort(); + + var records = new Seq(answers); + + for(int i = 0; i < answers; i++) { + result.getShort(); // OFFSET + int answerType = result.getShort() & 0xFFFF; // Type + result.getShort(); // Class + long ttl = result.getInt() & 0xFFFFFFFFL; // TTL + int length = result.getShort() & 0xFFFF; // Data length + + // Optionally CNAME results will be returned with the A results, skip those + if(answerType != type){ + result.position(result.position() + length); + continue; + } + + int position = result.position(); + + records.add(reader.get(result)); + + result.position(position + length); + } + + recordResult.get(records); + }, e -> { + if(index >= addresses.size - 1){ + error.get(e); + }else{ + send(addresses, index + 1, type, domain, reader, recordResult, error); + } + }); + } +}