add Arc capes DNS injector

This commit is contained in:
artDev 2022-02-04 19:51:31 +03:00
parent 97dace30c5
commit 0a3385acd2
14 changed files with 1040 additions and 3 deletions

Binary file not shown.

View File

@ -340,6 +340,7 @@ public class PojavLoginActivity extends BaseActivity
// TODO: Remove after implement.
Tools.copyAssetFile(this, "launcher_profiles.json", Tools.DIR_GAME_NEW, false);
Tools.copyAssetFile(this,"resolv.conf",Tools.DIR_DATA, true);
Tools.copyAssetFile(this,"arc_dns_injector.jar",Tools.DIR_DATA, true);
AssetManager am = this.getAssets();
unpackComponent(am, "caciocavallo");

View File

@ -38,6 +38,7 @@ public class LauncherPreferences
public static boolean PREF_VBO_DISABLE_HACK = false;
public static boolean PREF_VIRTUAL_MOUSE_START = false;
public static boolean PREF_OPENGL_VERSION_HACK = false;
public static boolean PREF_ARC_CAPES = false;
public static void loadPreferences(Context ctx) {
@ -71,6 +72,7 @@ public class LauncherPreferences
PREF_VBO_DISABLE_HACK = DEFAULT_PREF.getBoolean("vbo_disable_hack", false);
PREF_VIRTUAL_MOUSE_START = DEFAULT_PREF.getBoolean("mouse_start", false);
PREF_OPENGL_VERSION_HACK = DEFAULT_PREF.getBoolean("gles_version_hack", false);
PREF_ARC_CAPES = DEFAULT_PREF.getBoolean("arc_capes",false);
/*
if (PREF_CUSTOM_JAVA_ARGS.isEmpty()) {

View File

@ -343,7 +343,8 @@ public class JREUtils {
List<String> userArguments = parseJavaArguments(LauncherPreferences.PREF_CUSTOM_JAVA_ARGS);
String resolvFile;
resolvFile = new File(Tools.DIR_DATA,"resolv.conf").getAbsolutePath();
String[] overridableArguments = new String[]{
ArrayList<String> overridableArguments = new ArrayList<>(Arrays.asList(
"-Djava.home=" + Tools.DIR_HOME_JRE,
"-Djava.io.tmpdir=" + ctx.getCacheDir().getAbsolutePath(),
"-Duser.home=" + new File(Tools.DIR_GAME_NEW).getParent(),
@ -366,7 +367,10 @@ public class JREUtils {
"-Dnet.minecraft.clientmodname=" + Tools.APP_NAME,
"-Dfml.earlyprogresswindow=false" //Forge 1.14+ workaround
};
));
if(LauncherPreferences.PREF_ARC_CAPES) {
overridableArguments.add("-javaagent:"+new File(Tools.DIR_DATA,"arc_dns_injector.jar").getAbsolutePath()+"=23.95.137.176");
}
List<String> additionalArguments = new ArrayList<>();
for(String arg : overridableArguments) {
String strippedArg = arg.substring(0,arg.indexOf('='));

View File

@ -292,5 +292,5 @@
<string name="gles_version_hack_title">Force openGL 1</string>
<string name="gles_version_hack_description">Help with compatibility on some old versions</string>
<string name="arc_capes_title">Arc Capes</string>
<string name="arc_capes_desc">Enables capes from Arc. For more information please visit https://arccapes.com</string>
<string name="arc_capes_desc">Enables capes from Arc. For more information please visit https://arccapes.com. Requires OptiFine.</string>
</resources>

View File

@ -26,6 +26,11 @@
android:key="checkLibraries"
android:summary="@string/mcl_setting_check_libraries_subtitle"
android:title="@string/mcl_setting_check_libraries" />
<SwitchPreference
android:defaultValue="false"
android:key="arc_capes"
android:summary="@string/arc_capes_desc"
android:title="@string/arc_capes_title" />
</PreferenceCategory>

1
arc_dns_injector/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,15 @@
plugins {
id 'java-library'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
}
jar {
manifest {
attributes("Manifest-Version": "1.0",
"PreMain-Class": "git.artdeell.arcdns.ArcDNSInjectorAgent")
}
destinationDirectory.set(file("../app_pojavlauncher/src/main/assets/"))
}

View File

@ -0,0 +1,20 @@
package git.artdeell.arcdns;
public class ArcDNSInjectorAgent {
public static void premain(String args) {
System.out.println("Arc Capes DNS Injector");
System.out.println("Parts of Alibaba's DCM library were used, please read https://github.com/alibaba/java-dns-cache-manipulator/blob/main/README.md for more info");
try {
String[] injectedIps = new String[]{args};
if (CacheUtilCommons.isJavaVersionAtMost8()) {
CacheUtil_J8.setInetAddressCache("s.optifine.net", injectedIps, CacheUtilCommons.NEVER_EXPIRATION);
} else {
CacheUtil_J9.setInetAddressCache("s.optifine.net", injectedIps, CacheUtilCommons.NEVER_EXPIRATION);
}
System.out.println("Added DNS cache entry: s.optifine.net/"+args);
}catch (Exception e) {
System.out.println("Failed to inject cache!");
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,324 @@
package git.artdeell.arcdns;
import java.net.InetAddress;
import java.net.UnknownHostException;
import git.artdeell.arcdns.other.JavaVersion;
public class CacheUtilCommons {
public static final long NEVER_EXPIRATION = Long.MAX_VALUE;
static InetAddress[] toInetAddressArray(String host, String[] ips) throws UnknownHostException {
InetAddress[] addresses = new InetAddress[ips.length];
for (int i = 0; i < addresses.length; i++) {
addresses[i] = InetAddress.getByAddress(host, ip2ByteArray(ips[i]));
}
return addresses;
}
private static final String INVALID_IP_V6_ADDRESS = ": invalid IPv6 address";
private static final String INVALID_IP_ADDRESS = ": invalid IP address";
static byte[] ip2ByteArray(String ip) {
boolean ipv6Expected = false;
if (ip.charAt(0) == '[') {
// This is supposed to be an IPv6 literal
if (ip.length() > 2 && ip.charAt(ip.length() - 1) == ']') {
ip = ip.substring(1, ip.length() - 1);
ipv6Expected = true;
} else {
// This was supposed to be a IPv6 address, but it's not!
throw new IllegalArgumentException(ip + INVALID_IP_V6_ADDRESS);
}
}
if (Character.digit(ip.charAt(0), 16) != -1 || (ip.charAt(0) == ':')) {
// see if it is IPv4 address
byte[] address = textToNumericFormatV4(ip);
if (address != null) return address;
// see if it is IPv6 address
// Check if a numeric or string zone id is present
address = textToNumericFormatV6(ip);
if (address != null) return address;
if (ipv6Expected) {
throw new IllegalArgumentException(ip + INVALID_IP_V6_ADDRESS);
} else {
throw new IllegalArgumentException(ip + INVALID_IP_ADDRESS);
}
} else {
throw new IllegalArgumentException(ip + INVALID_IP_ADDRESS);
}
}
private static final int INADDR4SZ = 4;
private static final int INADDR16SZ = 16;
private static final int INT16SZ = 2;
/*
* Converts IPv4 address in its textual presentation form
* into its numeric binary form.
*
* @param src a String representing an IPv4 address in standard format
* @return a byte array representing the IPv4 numeric address
*/
@SuppressWarnings("fallthrough")
static byte[] textToNumericFormatV4(String src)
{
byte[] res = new byte[INADDR4SZ];
long tmpValue = 0;
int currByte = 0;
boolean newOctet = true;
int len = src.length();
if (len == 0 || len > 15) {
return null;
}
/*
* When only one part is given, the value is stored directly in
* the network address without any byte rearrangement.
*
* When a two part address is supplied, the last part is
* interpreted as a 24-bit quantity and placed in the right
* most three bytes of the network address. This makes the
* two part address format convenient for specifying Class A
* network addresses as net.host.
*
* When a three part address is specified, the last part is
* interpreted as a 16-bit quantity and placed in the right
* most two bytes of the network address. This makes the
* three part address format convenient for specifying
* Class B net- work addresses as 128.net.host.
*
* When four parts are specified, each is interpreted as a
* byte of data and assigned, from left to right, to the
* four bytes of an IPv4 address.
*
* We determine and parse the leading parts, if any, as single
* byte values in one pass directly into the resulting byte[],
* then the remainder is treated as a 8-to-32-bit entity and
* translated into the remaining bytes in the array.
*/
for (int i = 0; i < len; i++) {
char c = src.charAt(i);
if (c == '.') {
if (newOctet || tmpValue < 0 || tmpValue > 0xff || currByte == 3) {
return null;
}
res[currByte++] = (byte) (tmpValue & 0xff);
tmpValue = 0;
newOctet = true;
} else {
int digit = Character.digit(c, 10);
if (digit < 0) {
return null;
}
tmpValue *= 10;
tmpValue += digit;
newOctet = false;
}
}
if (newOctet || tmpValue < 0 || tmpValue >= (1L << ((4 - currByte) * 8))) {
return null;
}
switch (currByte) {
case 0:
res[0] = (byte) ((tmpValue >> 24) & 0xff);
case 1:
res[1] = (byte) ((tmpValue >> 16) & 0xff);
case 2:
res[2] = (byte) ((tmpValue >> 8) & 0xff);
case 3:
res[3] = (byte) ((tmpValue >> 0) & 0xff);
}
return res;
}
/*
* Convert IPv6 presentation level address to network order binary form.
* credit:
* Converted from C code from Solaris 8 (inet_pton)
*
* Any component of the string following a per-cent % is ignored.
*
* @param src a String representing an IPv6 address in textual format
* @return a byte array representing the IPv6 numeric address
*/
static byte[] textToNumericFormatV6(String src)
{
// Shortest valid string is "::", hence at least 2 chars
if (src.length() < 2) {
return null;
}
int colonp;
char ch;
boolean saw_xdigit;
int val;
char[] srcb = src.toCharArray();
byte[] dst = new byte[INADDR16SZ];
int srcb_length = srcb.length;
int pc = src.indexOf ('%');
if (pc == srcb_length -1) {
return null;
}
if (pc != -1) {
srcb_length = pc;
}
colonp = -1;
int i = 0, j = 0;
/* Leading :: requires some special handling. */
if (srcb[i] == ':')
if (srcb[++i] != ':')
return null;
int curtok = i;
saw_xdigit = false;
val = 0;
while (i < srcb_length) {
ch = srcb[i++];
int chval = Character.digit(ch, 16);
if (chval != -1) {
val <<= 4;
val |= chval;
if (val > 0xffff)
return null;
saw_xdigit = true;
continue;
}
if (ch == ':') {
curtok = i;
if (!saw_xdigit) {
if (colonp != -1)
return null;
colonp = j;
continue;
} else if (i == srcb_length) {
return null;
}
if (j + INT16SZ > INADDR16SZ)
return null;
dst[j++] = (byte) ((val >> 8) & 0xff);
dst[j++] = (byte) (val & 0xff);
saw_xdigit = false;
val = 0;
continue;
}
if (ch == '.' && ((j + INADDR4SZ) <= INADDR16SZ)) {
String ia4 = src.substring(curtok, srcb_length);
/* check this IPv4 address has 3 dots, ie. A.B.C.D */
int dot_count = 0, index=0;
while ((index = ia4.indexOf ('.', index)) != -1) {
dot_count ++;
index ++;
}
if (dot_count != 3) {
return null;
}
byte[] v4addr = textToNumericFormatV4(ia4);
if (v4addr == null) {
return null;
}
for (int k = 0; k < INADDR4SZ; k++) {
dst[j++] = v4addr[k];
}
saw_xdigit = false;
break; /* '\0' was seen by inet_pton4(). */
}
return null;
}
if (saw_xdigit) {
if (j + INT16SZ > INADDR16SZ)
return null;
dst[j++] = (byte) ((val >> 8) & 0xff);
dst[j++] = (byte) (val & 0xff);
}
if (colonp != -1) {
int n = j - colonp;
if (j == INADDR16SZ)
return null;
for (i = 1; i <= n; i++) {
dst[INADDR16SZ - i] = dst[colonp + n - i];
dst[colonp + n - i] = 0;
}
j = INADDR16SZ;
}
if (j != INADDR16SZ)
return null;
byte[] newdst = convertFromIPv4MappedAddress(dst);
if (newdst != null) {
return newdst;
} else {
return dst;
}
}
/*
* Convert IPv4-Mapped address to IPv4 address. Both input and
* returned value are in network order binary form.
*
* @param src a String representing an IPv4-Mapped address in textual format
* @return a byte array representing the IPv4 numeric address
*/
private static byte[] convertFromIPv4MappedAddress(byte[] addr) {
if (isIPv4MappedAddress(addr)) {
byte[] newAddr = new byte[INADDR4SZ];
System.arraycopy(addr, 12, newAddr, 0, INADDR4SZ);
return newAddr;
}
return null;
}
/**
* Utility routine to check if the InetAddress is an
* IPv4 mapped IPv6 address.
*
* @return a <code>boolean</code> indicating if the InetAddress is
* an IPv4 mapped IPv6 address; or false if address is IPv4 address.
*/
private static boolean isIPv4MappedAddress(byte[] addr) {
if (addr.length < INADDR16SZ) {
return false;
}
if ((addr[0] == 0x00) && (addr[1] == 0x00) &&
(addr[2] == 0x00) && (addr[3] == 0x00) &&
(addr[4] == 0x00) && (addr[5] == 0x00) &&
(addr[6] == 0x00) && (addr[7] == 0x00) &&
(addr[8] == 0x00) && (addr[9] == 0x00) &&
(addr[10] == (byte)0xff) &&
(addr[11] == (byte)0xff)) {
return true;
}
return false;
}
public static boolean isJavaVersionAtMost8() {
return JAVA_SPECIFICATION_VERSION_AS_ENUM.atMost(JavaVersion.JAVA_1_8);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Below source code is copied from commons-lang-3.12.0:
//
// https://github.com/apache/commons-lang/blob/rel/commons-lang-3.12.0/src/main/java/org/apache/commons/lang3/SystemUtils.java
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private static final String JAVA_SPECIFICATION_VERSION = getSystemProperty("java.specification.version");
private static final JavaVersion JAVA_SPECIFICATION_VERSION_AS_ENUM = JavaVersion.get(JAVA_SPECIFICATION_VERSION);
@SuppressWarnings({"CommentedOutCode", "SameParameterValue"})
private static String getSystemProperty(final String property) {
try {
return System.getProperty(property);
} catch (final SecurityException ex) {
// we are not allowed to look at this property
// System.err.println("Caught a SecurityException reading the system property '" + property
// + "'; the SystemUtils property value will default to null.");
return null;
}
}
}

View File

@ -0,0 +1,153 @@
package git.artdeell.arcdns;
import static git.artdeell.arcdns.CacheUtilCommons.NEVER_EXPIRATION;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;
public final class CacheUtil_J8 {
public static void setInetAddressCache(String host, String[] ips, long expireMillis)
throws UnknownHostException, IllegalAccessException, InstantiationException,
InvocationTargetException, ClassNotFoundException, NoSuchFieldException {
host = host.toLowerCase();
long expiration = expireMillis == NEVER_EXPIRATION ? NEVER_EXPIRATION : System.currentTimeMillis() + expireMillis;
Object entry = newCacheEntry(host, ips, expiration);
synchronized (getAddressCacheOfInetAddress()) {
getCache().put(host, entry);
getNegativeCache().remove(host);
}
}
private static Object newCacheEntry(String host, String[] ips, long expiration)
throws UnknownHostException, ClassNotFoundException, IllegalAccessException,
InvocationTargetException, InstantiationException {
// InetAddress.CacheEntry has only one constructor
return getConstructorOfInetAddress$CacheEntry().newInstance(CacheUtilCommons.toInetAddressArray(host, ips), expiration);
}
private static volatile Constructor<?> constructorOfInetAddress$CacheEntry = null;
private static Constructor<?> getConstructorOfInetAddress$CacheEntry() throws ClassNotFoundException {
if (constructorOfInetAddress$CacheEntry != null) return constructorOfInetAddress$CacheEntry;
synchronized (CacheUtilCommons.class) {
// double check
if (constructorOfInetAddress$CacheEntry != null) return constructorOfInetAddress$CacheEntry;
final String className = "java.net.InetAddress$CacheEntry";
final Class<?> clazz = Class.forName(className);
// InetAddress.CacheEntry has only one constructor:
// - for jdk 6, constructor signature is CacheEntry(Object address, long expiration)
// - for jdk 7/8, constructor signature is CacheEntry(InetAddress[] addresses, long expiration)
//
// code in jdk 6:
// https://hg.openjdk.java.net/jdk6/jdk6/jdk/file/8deef18bb749/src/share/classes/java/net/InetAddress.java#l739
// code in jdk 7:
// https://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/4dd5e486620d/src/share/classes/java/net/InetAddress.java#l742
// code in jdk 8:
// https://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/45e4e636b757/src/share/classes/java/net/InetAddress.java#l748
final Constructor<?> constructor = clazz.getDeclaredConstructors()[0];
constructor.setAccessible(true);
constructorOfInetAddress$CacheEntry = constructor;
return constructor;
}
}
public static void removeInetAddressCache(String host)
throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
host = host.toLowerCase();
synchronized (getAddressCacheOfInetAddress()) {
getCache().remove(host);
getNegativeCache().remove(host);
}
}
private static Map<String, Object> getCache()
throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
return getCacheOfInetAddress$Cache0(getAddressCacheOfInetAddress());
}
private static Map<String, Object> getNegativeCache()
throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
return getCacheOfInetAddress$Cache0(getNegativeCacheOfInetAddress());
}
private static volatile Field cacheMapFieldOfInetAddress$Cache = null;
@SuppressWarnings("unchecked")
private static Map<String, Object> getCacheOfInetAddress$Cache0(Object inetAddressCache)
throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
if (cacheMapFieldOfInetAddress$Cache == null) {
synchronized (CacheUtil_J8.class) {
if (cacheMapFieldOfInetAddress$Cache == null) { // double check
final Class<?> clazz = Class.forName("java.net.InetAddress$Cache");
final Field f = clazz.getDeclaredField("cache");
f.setAccessible(true);
cacheMapFieldOfInetAddress$Cache = f;
}
}
}
return (Map<String, Object>) cacheMapFieldOfInetAddress$Cache.get(inetAddressCache);
}
private static Object getAddressCacheOfInetAddress()
throws NoSuchFieldException, IllegalAccessException {
return getAddressCacheAndNegativeCacheOfInetAddress0()[0];
}
private static Object getNegativeCacheOfInetAddress()
throws NoSuchFieldException, IllegalAccessException {
return getAddressCacheAndNegativeCacheOfInetAddress0()[1];
}
private static volatile Object[] ADDRESS_CACHE_AND_NEGATIVE_CACHE = null;
private static Object[] getAddressCacheAndNegativeCacheOfInetAddress0()
throws NoSuchFieldException, IllegalAccessException {
if (ADDRESS_CACHE_AND_NEGATIVE_CACHE != null) return ADDRESS_CACHE_AND_NEGATIVE_CACHE;
synchronized (CacheUtil_J8.class) {
// double check
if (ADDRESS_CACHE_AND_NEGATIVE_CACHE != null) return ADDRESS_CACHE_AND_NEGATIVE_CACHE;
final Field cacheField = InetAddress.class.getDeclaredField("addressCache");
cacheField.setAccessible(true);
final Field negativeCacheField = InetAddress.class.getDeclaredField("negativeCache");
negativeCacheField.setAccessible(true);
ADDRESS_CACHE_AND_NEGATIVE_CACHE = new Object[]{
cacheField.get(InetAddress.class),
negativeCacheField.get(InetAddress.class)
};
return ADDRESS_CACHE_AND_NEGATIVE_CACHE;
}
}
private static boolean isDnsCacheEntryExpired(String host) {
return null == host || "0.0.0.0".equals(host);
}
public static void clearInetAddressCache()
throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
synchronized (getAddressCacheOfInetAddress()) {
getCache().clear();
getNegativeCache().clear();
}
}
private CacheUtil_J8() {
}
}

View File

@ -0,0 +1,145 @@
package git.artdeell.arcdns;
import static git.artdeell.arcdns.CacheUtilCommons.NEVER_EXPIRATION;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListSet;
public class CacheUtil_J9 {
public static void setInetAddressCache(String host, String[] ips, long expireMillis)
throws UnknownHostException, IllegalAccessException, InstantiationException,
InvocationTargetException, ClassNotFoundException, NoSuchFieldException {
long expiration = expireMillis == NEVER_EXPIRATION ? NEVER_EXPIRATION : System.nanoTime() + expireMillis * 1_000_000;
Object cachedAddresses = newCachedAddresses(host, ips, expiration);
getCacheOfInetAddress().put(host, cachedAddresses);
getExpirySetOfInetAddress().add(cachedAddresses);
}
private static Object newCachedAddresses(String host, String[] ips, long expiration)
throws ClassNotFoundException, UnknownHostException, IllegalAccessException,
InvocationTargetException, InstantiationException {
// InetAddress.CachedAddresses has only one constructor
return getConstructorOfInetAddress$CachedAddresses().newInstance(host, CacheUtilCommons.toInetAddressArray(host, ips), expiration);
}
private static volatile Constructor<?> constructorOfInetAddress$CachedAddresses = null;
private static Constructor<?> getConstructorOfInetAddress$CachedAddresses() throws ClassNotFoundException {
if (constructorOfInetAddress$CachedAddresses != null) return constructorOfInetAddress$CachedAddresses;
synchronized (CacheUtilCommons.class) {
// double check
if (constructorOfInetAddress$CachedAddresses != null) return constructorOfInetAddress$CachedAddresses;
final Class<?> clazz = Class.forName(inetAddress$CachedAddresses_ClassName);
// InetAddress.CacheEntry has only one constructor:
//
// - for jdk 9-jdk12, constructor signature is CachedAddresses(String host, InetAddress[] inetAddresses, long expiryTime)
// code in jdk 9:
// https://hg.openjdk.java.net/jdk9/jdk9/jdk/file/65464a307408/src/java.base/share/classes/java/net/InetAddress.java#l783
// code in jdk 11:
// https://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/java/net/InetAddress.java#l787
final Constructor<?> constructor = clazz.getDeclaredConstructors()[0];
constructor.setAccessible(true);
constructorOfInetAddress$CachedAddresses = constructor;
return constructor;
}
}
public static void removeInetAddressCache(String host) throws NoSuchFieldException, IllegalAccessException {
getCacheOfInetAddress().remove(host);
removeHostFromExpirySetOfInetAddress(host);
}
/**
* @see #getExpirySetOfInetAddress()
*/
private static void removeHostFromExpirySetOfInetAddress(String host)
throws NoSuchFieldException, IllegalAccessException {
for (Iterator<Object> iterator = getExpirySetOfInetAddress().iterator(); iterator.hasNext(); ) {
Object cachedAddresses = iterator.next();
if (getHostOfInetAddress$CacheAddress(cachedAddresses).equals(host)) {
iterator.remove();
}
}
}
private static volatile Field hostFieldOfInetAddress$CacheAddress = null;
private static String getHostOfInetAddress$CacheAddress(Object cachedAddresses)
throws NoSuchFieldException, IllegalAccessException {
if (hostFieldOfInetAddress$CacheAddress == null) {
synchronized (CacheUtil_J9.class) {
if (hostFieldOfInetAddress$CacheAddress == null) { // double check
final Field f = cachedAddresses.getClass().getDeclaredField("host");
f.setAccessible(true);
hostFieldOfInetAddress$CacheAddress = f;
}
}
}
return (String) hostFieldOfInetAddress$CacheAddress.get(cachedAddresses);
}
//////////////////////////////////////////////////////////////////////////////
// getters of static cache related fields of InetAddress
//////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("unchecked")
private static ConcurrentMap<String, Object> getCacheOfInetAddress()
throws NoSuchFieldException, IllegalAccessException {
return (ConcurrentMap<String, Object>) getCacheAndExpirySetOfInetAddress0()[0];
}
@SuppressWarnings("unchecked")
private static ConcurrentSkipListSet<Object> getExpirySetOfInetAddress()
throws NoSuchFieldException, IllegalAccessException {
return (ConcurrentSkipListSet<Object>) getCacheAndExpirySetOfInetAddress0()[1];
}
private static volatile Object[] ADDRESS_CACHE_AND_EXPIRY_SET = null;
private static Object[] getCacheAndExpirySetOfInetAddress0()
throws NoSuchFieldException, IllegalAccessException {
if (ADDRESS_CACHE_AND_EXPIRY_SET != null) return ADDRESS_CACHE_AND_EXPIRY_SET;
synchronized (CacheUtil_J9.class) {
if (ADDRESS_CACHE_AND_EXPIRY_SET != null) return ADDRESS_CACHE_AND_EXPIRY_SET;
final Field cacheField = InetAddress.class.getDeclaredField("cache");
cacheField.setAccessible(true);
final Field expirySetField = InetAddress.class.getDeclaredField("expirySet");
expirySetField.setAccessible(true);
ADDRESS_CACHE_AND_EXPIRY_SET = new Object[]{
cacheField.get(InetAddress.class),
expirySetField.get(InetAddress.class)
};
return ADDRESS_CACHE_AND_EXPIRY_SET;
}
}
//////////////////////////////////////////////////////////////////////////////
private static final String inetAddress$CachedAddresses_ClassName = "java.net.InetAddress$CachedAddresses";
public static void clearInetAddressCache() throws NoSuchFieldException, IllegalAccessException {
getCacheOfInetAddress().clear();
getExpirySetOfInetAddress().clear();
}
private CacheUtil_J9() {
}
}

View File

@ -0,0 +1,366 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git.artdeell.arcdns.other;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// This source code file is copied and small adopted from commons-lang-3.12.0:
//
// https://github.com/apache/commons-lang/blob/rel/commons-lang-3.12.0/src/main/java/org/apache/commons/lang3/JavaVersion.java
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* <p>An enum representing all the versions of the Java specification.
* This is intended to mirror available values from the
* <em>java.specification.version</em> System property. </p>
*
* @since 3.0
*/
@SuppressWarnings({"unused", "SameParameterValue"})
public
enum JavaVersion {
/**
* The Java version reported by Android. This is not an official Java version number.
*/
JAVA_0_9(1.5f, "0.9"),
/**
* Java 1.1.
*/
JAVA_1_1(1.1f, "1.1"),
/**
* Java 1.2.
*/
JAVA_1_2(1.2f, "1.2"),
/**
* Java 1.3.
*/
JAVA_1_3(1.3f, "1.3"),
/**
* Java 1.4.
*/
JAVA_1_4(1.4f, "1.4"),
/**
* Java 1.5.
*/
JAVA_1_5(1.5f, "1.5"),
/**
* Java 1.6.
*/
JAVA_1_6(1.6f, "1.6"),
/**
* Java 1.7.
*/
JAVA_1_7(1.7f, "1.7"),
/**
* Java 1.8.
*/
JAVA_1_8(1.8f, "1.8"),
/**
* Java 1.9.
*
* @deprecated As of release 3.5, replaced by {@link #JAVA_9}
*/
@Deprecated
JAVA_1_9(9.0f, "9"),
/**
* Java 9.
*
* @since 3.5
*/
JAVA_9(9.0f, "9"),
/**
* Java 10.
*
* @since 3.7
*/
JAVA_10(10.0f, "10"),
/**
* Java 11.
*
* @since 3.8
*/
JAVA_11(11.0f, "11"),
/**
* Java 12.
*
* @since 3.9
*/
JAVA_12(12.0f, "12"),
/**
* Java 13.
*
* @since 3.9
*/
JAVA_13(13.0f, "13"),
/**
* Java 14.
*
* @since 3.11
*/
JAVA_14(14.0f, "14"),
/**
* Java 15.
*
* @since 3.11
*/
JAVA_15(15.0f, "15"),
/**
* Java 16.
*
* @since 3.11
*/
JAVA_16(16.0f, "16"),
/**
* Java 17.
*
* @since 3.12.0
*/
JAVA_17(17.0f, "17"),
/**
* The most recent java version. Mainly introduced to avoid to break when a new version of Java is used.
*/
JAVA_RECENT(maxVersion(), Float.toString(maxVersion()));
/**
* The float value.
*/
private final float value;
/**
* The standard name.
*/
private final String name;
/**
* Constructor.
*
* @param value the float value
* @param name the standard name, not null
*/
JavaVersion(final float value, final String name) {
this.value = value;
this.name = name;
}
//-----------------------------------------------------------------------
/**
* <p>Whether this version of Java is at least the version of Java passed in.</p>
*
* <p>For example:<br>
* {@code myVersion.atLeast(JavaVersion.JAVA_1_4)}<p>
*
* @param requiredVersion the version to check against, not null
* @return true if this version is equal to or greater than the specified version
*/
public boolean atLeast(final JavaVersion requiredVersion) {
return this.value >= requiredVersion.value;
}
//-----------------------------------------------------------------------
/**
* <p>Whether this version of Java is at most the version of Java passed in.</p>
*
* <p>For example:<br>
* {@code myVersion.atMost(JavaVersion.JAVA_1_4)}<p>
*
* @param requiredVersion the version to check against, not null
* @return true if this version is equal to or greater than the specified version
* @since 3.9
*/
public boolean atMost(final JavaVersion requiredVersion) {
return this.value <= requiredVersion.value;
}
/**
* Transforms the given string with a Java version number to the
* corresponding constant of this enumeration class. This method is used
* internally.
*
* @param nom the Java version as string
* @return the corresponding enumeration constant or <b>null</b> if the
* version is unknown
*/
// helper for static importing
static JavaVersion getJavaVersion(final String nom) {
return get(nom);
}
/**
* Transforms the given string with a Java version number to the
* corresponding constant of this enumeration class. This method is used
* internally.
*
* @param versionStr the Java version as string
* @return the corresponding enumeration constant or <b>null</b> if the
* version is unknown
*/
public static JavaVersion get(final String versionStr) {
if (versionStr == null) {
return null;
}
switch (versionStr) {
case "0.9":
return JAVA_0_9;
case "1.1":
return JAVA_1_1;
case "1.2":
return JAVA_1_2;
case "1.3":
return JAVA_1_3;
case "1.4":
return JAVA_1_4;
case "1.5":
return JAVA_1_5;
case "1.6":
return JAVA_1_6;
case "1.7":
return JAVA_1_7;
case "1.8":
return JAVA_1_8;
case "9":
return JAVA_9;
case "10":
return JAVA_10;
case "11":
return JAVA_11;
case "12":
return JAVA_12;
case "13":
return JAVA_13;
case "14":
return JAVA_14;
case "15":
return JAVA_15;
case "16":
return JAVA_16;
case "17":
return JAVA_17;
}
final float v = toFloatVersion(versionStr);
if ((v - 1.) < 1.) { // then we need to check decimals > .9
final int firstComma = Math.max(versionStr.indexOf('.'), versionStr.indexOf(','));
final int end = Math.max(versionStr.length(), versionStr.indexOf(',', firstComma));
if (Float.parseFloat(versionStr.substring(firstComma + 1, end)) > .9f) {
return JAVA_RECENT;
}
} else if (v > 10) {
return JAVA_RECENT;
}
return null;
}
//-----------------------------------------------------------------------
/**
* <p>The string value is overridden to return the standard name.</p>
*
* <p>For example, {@code "1.5"}.</p>
*
* @return the name, not null
*/
@Override
public String toString() {
return name;
}
/**
* Gets the Java Version from the system or 99.0 if the {@code java.specification.version} system property is not set.
*
* @return the value of {@code java.specification.version} system property or 99.0 if it is not set.
*/
private static float maxVersion() {
final float v = toFloatVersion(System.getProperty("java.specification.version", "99.0"));
if (v > 0) {
return v;
}
return 99f;
}
/**
* Parses a float value from a String.
*
* @param value the String to parse.
* @return the float value represented by the string or -1 if the given String can not be parsed.
*/
private static float toFloatVersion(final String value) {
final int defaultReturnValue = -1;
if (value.contains(".")) {
final String[] toParse = value.split("\\.");
if (toParse.length >= 2) {
return toFloat(toParse[0] + '.' + toParse[1], defaultReturnValue);
}
} else {
return toFloat(value, defaultReturnValue);
}
return defaultReturnValue;
}
/**
* <p>Convert a {@code String} to a {@code float}, returning a
* default value if the conversion fails.</p>
*
* <p>If the string {@code str} is {@code null}, the default
* value is returned.</p>
*
* <pre>
* NumberUtils.toFloat(null, 1.1f) = 1.0f
* NumberUtils.toFloat("", 1.1f) = 1.1f
* NumberUtils.toFloat("1.5", 0.0f) = 1.5f
* </pre>
*
* @param str the string to convert, may be {@code null}
* @param defaultValue the default value
* @return the float represented by the string, or defaultValue
* if conversion fails
* @since 2.1
*/
private static float toFloat(final String str, final float defaultValue) {
if (str == null) {
return defaultValue;
}
try {
return Float.parseFloat(str);
} catch (final NumberFormatException nfe) {
return defaultValue;
}
}
}

View File

@ -2,3 +2,4 @@ rootProject.name='PojavLauncher'
include ':jre_lwjgl3glfw'
include ':app_pojavlauncher'
include ':arc_dns_injector'