diff --git a/core/src/mindustry/net/Addresses.java b/core/src/mindustry/net/Addresses.java new file mode 100644 index 0000000000..e12a36c05c --- /dev/null +++ b/core/src/mindustry/net/Addresses.java @@ -0,0 +1,231 @@ +package mindustry.net; + +/** Utility class for parsing IPv4/IPv6. Taken from IPAddressUtil in the JDK. */ +public class Addresses{ + + /** @return the IPv4 or IPv6 address of the string, or null if the address is not valid. */ + public static byte[] getAddress(String src){ + byte[] ipv4 = getIpv4(src); + if(ipv4 != null) return ipv4; + return getIpv6(src); + } + + /** @return the IPv4 address of the string, or null if the address is not valid. Exotic formats (such as decimal) are allowed. */ + public static byte[] getIpv4(String src){ + byte[] res = new byte[4]; + long tmpValue = 0L; + int currByte = 0; + boolean newOctet = true; + int len = src.length(); + if(len != 0 && len <= 15){ + for(int i = 0; i < len; ++i){ + char c = src.charAt(i); + if(c == '.'){ + if(newOctet || tmpValue < 0L || tmpValue > 255L || currByte == 3){ + return null; + } + + res[currByte++] = (byte)((int)(tmpValue & 255L)); + tmpValue = 0L; + newOctet = true; + }else{ + int digit = digit(c, 10); + if(digit < 0){ + return null; + } + + tmpValue *= 10L; + tmpValue += (long)digit; + newOctet = false; + } + } + + if(!newOctet && tmpValue >= 0L && tmpValue < 1L << (4 - currByte) * 8){ + switch(currByte){ + case 0: + res[0] = (byte)((int)(tmpValue >> 24 & 255L)); + case 1: + res[1] = (byte)((int)(tmpValue >> 16 & 255L)); + case 2: + res[2] = (byte)((int)(tmpValue >> 8 & 255L)); + case 3: + res[3] = (byte)((int)(tmpValue >> 0 & 255L)); + default: + return res; + } + }else{ + return null; + } + }else{ + return null; + } + } + + /** @return the IPv6 address of the string, or null if the address is not valid. */ + public static byte[] getIpv6(String src){ + if(src.length() < 2){ + return null; + }else{ + char[] srcb = src.toCharArray(); + byte[] dst = new byte[16]; + int srcb_length = srcb.length; + int pc = src.indexOf(37); + if(pc == srcb_length - 1){ + return null; + }else{ + if(pc != -1){ + srcb_length = pc; + } + + int colonp = -1; + int i = 0; + int j = 0; + if(srcb[i] == ':'){ + ++i; + if(srcb[i] != ':'){ + return null; + } + } + + int curtok = i; + boolean saw_xdigit = false; + int val = 0; + + while(true){ + int n; + while(i < srcb_length){ + char ch = srcb[i++]; + n = digit(ch, 16); + if(n != -1){ + val <<= 4; + val |= n; + if(val > 65535){ + return null; + } + + saw_xdigit = true; + }else{ + if(ch != ':'){ + if(ch == '.' && j + 4 <= 16){ + String ia4 = src.substring(curtok, srcb_length); + int dot_count = 0; + + for(int index = 0; (index = ia4.indexOf(46, index)) != -1; ++index){ + ++dot_count; + } + + if(dot_count != 3){ + return null; + } + + byte[] v4addr = getIpv4(ia4); + if(v4addr == null){ + return null; + } + + for(int k = 0; k < 4; ++k){ + dst[j++] = v4addr[k]; + } + + saw_xdigit = false; + break; + } + + return null; + } + + curtok = i; + if(!saw_xdigit){ + if(colonp != -1){ + return null; + } + + colonp = j; + }else{ + if(i == srcb_length){ + return null; + } + + if(j + 2 > 16){ + return null; + } + + dst[j++] = (byte)(val >> 8 & 255); + dst[j++] = (byte)(val & 255); + saw_xdigit = false; + val = 0; + } + } + } + + if(saw_xdigit){ + if(j + 2 > 16){ + return null; + } + + dst[j++] = (byte)(val >> 8 & 255); + dst[j++] = (byte)(val & 255); + } + + if(colonp != -1){ + n = j - colonp; + if(j == 16){ + return null; + } + + for(i = 1; i <= n; ++i){ + dst[16 - i] = dst[colonp + n - i]; + dst[colonp + n - i] = 0; + } + + j = 16; + } + + if(j != 16){ + return null; + } + + byte[] newdst = convertFromIPv4MappedAddress(dst); + if(newdst != null){ + return newdst; + } + + return dst; + } + } + } + } + + private static boolean isIPv4MappedAddress(byte[] addr){ + if(addr.length < 16){ + return false; + }else{ + return addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0 && addr[4] == 0 && addr[5] == 0 && addr[6] == 0 && addr[7] == 0 && addr[8] == 0 && addr[9] == 0 && addr[10] == -1 && addr[11] == -1; + } + } + + private static byte[] convertFromIPv4MappedAddress(byte[] addr){ + if(isIPv4MappedAddress(addr)){ + byte[] newAddr = new byte[4]; + System.arraycopy(addr, 12, newAddr, 0, 4); + return newAddr; + }else{ + return null; + } + } + + private static int digit(char ch, int radix){ + return parseAsciiDigit(ch, radix); + } + + private static int parseAsciiDigit(char c, int radix){ + if(radix == 16){ + char c1 = Character.toLowerCase(c); + return c1 >= 'a' && c1 <= 'f' ? c1 - 97 + 10 : parseAsciiDigit(c1, 10); + }else{ + int val = c - 48; + return val >= 0 && val < radix ? val : -1; + } + } + +} diff --git a/core/src/mindustry/net/ArcNetProvider.java b/core/src/mindustry/net/ArcNetProvider.java index 2989e4e03c..5780a32fba 100644 --- a/core/src/mindustry/net/ArcNetProvider.java +++ b/core/src/mindustry/net/ArcNetProvider.java @@ -162,7 +162,7 @@ public class ArcNetProvider implements NetProvider{ } @Override - public @Nullable ServerConnectFilter getConnectFilter(){ + public @Nullable Server.ServerConnectFilter getConnectFilter(){ return server.getConnectFilter(); } diff --git a/core/src/mindustry/net/Dns.java b/core/src/mindustry/net/Dns.java index e7d7935343..3c37fc775e 100644 --- a/core/src/mindustry/net/Dns.java +++ b/core/src/mindustry/net/Dns.java @@ -10,14 +10,12 @@ import java.net.*; import java.nio.*; import java.nio.channels.*; import java.nio.charset.*; -import java.util.regex.*; +import java.util.concurrent.*; 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<>(); - //TODO remove these - //private static Seq defNameservers = Seq.with(new InetSocketAddress("1.1.1.1", 53), new InetSocketAddress("8.8.8.8", 53)); + private static IntMap>> cache = new IntMap<>(); //TODO remove this cache? + private static ConcurrentHashMap domainToIp = new ConcurrentHashMap<>(); static void resolve(int type, String domain, Func reader, Cons> result, Cons error){ ObjectMap> map; @@ -42,16 +40,31 @@ public class Dns{ }, error); } - //TODO breaks for ipv6 - //TODO no SRV recrod support + //TODO no SRV or AAAA record support static void resolveAddress(String domain, Cons result, Cons error){ - if(ipPattern.matcher(domain).matches()){ + + //since parsing the address may be slow, check the cache first. + var cachedIp = domainToIp.get(domain); + if(cachedIp != null){ try{ - result.get(InetAddress.getByName(domain)); + result.get(cachedIp); }catch(Exception e){ error.get(e); - return; } + return; + } + + //attempt to resolve ipv4 or ipv6 address + byte[] rawAddress = Addresses.getAddress(domain); + if(rawAddress != null){ + try{ + var address = InetAddress.getByAddress(domain, rawAddress); + domainToIp.put(domain, address); + result.get(address); + }catch(Exception e){ + error.get(e); + } + return; } resolve(aRecord, domain, bytes -> {