mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-08-03 07:36:31 -04:00
add Arc capes DNS injector
This commit is contained in:
parent
97dace30c5
commit
0a3385acd2
BIN
app_pojavlauncher/src/main/assets/arc_dns_injector.jar
Normal file
BIN
app_pojavlauncher/src/main/assets/arc_dns_injector.jar
Normal file
Binary file not shown.
@ -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");
|
||||
|
@ -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()) {
|
||||
|
@ -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('='));
|
||||
|
@ -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>
|
||||
|
@ -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
1
arc_dns_injector/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
15
arc_dns_injector/build.gradle
Normal file
15
arc_dns_injector/build.gradle
Normal 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/"))
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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() {
|
||||
}
|
||||
}
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,3 +2,4 @@ rootProject.name='PojavLauncher'
|
||||
include ':jre_lwjgl3glfw'
|
||||
include ':app_pojavlauncher'
|
||||
|
||||
include ':arc_dns_injector'
|
||||
|
Loading…
x
Reference in New Issue
Block a user