From 0a3385acd229b8c1fc9e4c7951cf251aec7dadaa Mon Sep 17 00:00:00 2001 From: artDev Date: Fri, 4 Feb 2022 19:51:31 +0300 Subject: [PATCH] add Arc capes DNS injector --- .../src/main/assets/arc_dns_injector.jar | Bin 0 -> 13243 bytes .../kdt/pojavlaunch/PojavLoginActivity.java | 1 + .../prefs/LauncherPreferences.java | 2 + .../net/kdt/pojavlaunch/utils/JREUtils.java | 8 +- .../src/main/res/values/strings.xml | 2 +- .../src/main/res/xml/pref_misc.xml | 5 + arc_dns_injector/.gitignore | 1 + arc_dns_injector/build.gradle | 15 + .../artdeell/arcdns/ArcDNSInjectorAgent.java | 20 + .../git/artdeell/arcdns/CacheUtilCommons.java | 324 ++++++++++++++++ .../git/artdeell/arcdns/CacheUtil_J8.java | 153 ++++++++ .../git/artdeell/arcdns/CacheUtil_J9.java | 145 +++++++ .../artdeell/arcdns/other/JavaVersion.java | 366 ++++++++++++++++++ settings.gradle | 1 + 14 files changed, 1040 insertions(+), 3 deletions(-) create mode 100644 app_pojavlauncher/src/main/assets/arc_dns_injector.jar create mode 100644 arc_dns_injector/.gitignore create mode 100644 arc_dns_injector/build.gradle create mode 100644 arc_dns_injector/src/main/java/git/artdeell/arcdns/ArcDNSInjectorAgent.java create mode 100644 arc_dns_injector/src/main/java/git/artdeell/arcdns/CacheUtilCommons.java create mode 100644 arc_dns_injector/src/main/java/git/artdeell/arcdns/CacheUtil_J8.java create mode 100644 arc_dns_injector/src/main/java/git/artdeell/arcdns/CacheUtil_J9.java create mode 100644 arc_dns_injector/src/main/java/git/artdeell/arcdns/other/JavaVersion.java diff --git a/app_pojavlauncher/src/main/assets/arc_dns_injector.jar b/app_pojavlauncher/src/main/assets/arc_dns_injector.jar new file mode 100644 index 0000000000000000000000000000000000000000..3c9ddd6bf377c3efdb4f9439231f654c2b9a5332 GIT binary patch literal 13243 zcma*O1yEeiwmnR64esvl?(XgmK?V!1gC)4Tdmy;GTX1&^4uiXEkT0ow-<99H_x-

eX20*|+xK zY2@w{$H(W~CS@6zn$%>b7-r-d=wxLTCq_Q7C8%FV+z~`37`_Au-qfij%3ntk%&FhV z0b7Bsf0@GXQU5Vl$lrZSYuEoZ&42Yl`n!*bv#U7(U~Btt&ThZg^sml;*7OJPAI>Ju zX6E)T{}%J-J&FD=mH+|2<+qfVlmcAwub0545&jhG1g88m3LhO z;gRiCo#^coXPd`N^6@jXWZsbwl{88ZKk}|wQ+tX`o+qq%jG5F&Ft!Z!Eo&h+Z5t)` z`6jq`2MBQ!#R(vfo}D=#kV5f8-af@fmElLE|#$@bkkfEKVNr6!I z8H%ZEL?%x%Qm;zD*8Rjq6w!;HGg4(Em~wxkATrs3=N~-lzuHYi;^Bk4fFq?8+HrI& z*8yKjxmRB%?4l8{rm)ils>u_|;HcD0=Axu{;eyRZGJs`s3iIQIXO$LYxZy)qO`p9e zuw*ANYP2<);Cb*sCh8<#jVLUb4vp6Bsq_NQRMnpekfHJ`dn6>P*d?UP*b^|)I8LpR zdQ^&Ac`(Dd`n9Vqf&fu!S0dvIPkq}#=?c&2;ahtC>6pfd2iMFAbnG0XONgpsm1JV_ zd^;l;*n+UQIQppSD3CqeX zOHBc81io>Nn-%j>gH9UM3(&U0QNgwvpMm1CdBJGNdD<;~f?g&gE?aARUBvpF_>4k# z>BQB!#BdgRe6)Gi8pDEGp~wc@#0{wSBhnme;2WZLJ06y4e28?ddLPs2Vhv2JHITp? z4wt$C^1Et1InEt5pAJ&3#-c(~|H%2oag0Qtg;<}^Ekc$PALFD&33F_}4qUt=FFJ@G z`6J!~m}MVdUYIN*+2{=w(vKH;^^2K>jv-#f&3nSLv26($uym z(p_57R!s;ti(+5oV|oa=4z|7)ODv-T0QHd04HeEx(wG+uBDKC_WaKUqDbuU$8}7@R`8ggx!@ z##~x5H^FcZ&z3U9!LKs&)Lz-*BV83NWC){r>S-j-)&BS#-KoMp;HU@`XoR7OSiXXz zM9!RmN?n5FN+P3>prbZ}5~p=g07}RZRgsn~>_-?7%Lz^`G(?g)_q!v+Q!iJiOp~8c z>4*evKq5!W;5L0BZp!*7a| z1#ZG=hUNtMcKKQ?CyDE;3KWj(Yr4A&6TCWdJ*-G>{#aFaPnfVTWeV{@-@k;7-q`z| zwCOb59CNLf9VJ697&lc6mu9xiWQh$T!vl%of~UztFf}RE`$9Jdc!F@GOuBsH<1Ih9 zp$N?pMUYslEmfb+b>4S69*s4Bit%8^IOt5(0&zHA%3(5 zVK3J^XUO4!mq4gEs%#3-!X?IO;Puw&7?JJ%&6FnOGd*8tNblCuT8Mu2MgU)5D;)Wl zApW_Cs3&R^l1GmjkAZ%HLh|;Z8?0>Sb6cjKz?ZV~KHzAnLg17c@I1+2jTrYT6+h=r zH0R<}Q^&sKm>yH;$Y^(z?|P5XnfVKO z)FYiyv1(8)9zCiwnxRhMSSfwDo6NSJ>yFpxy38?zPUKmKg0ZPl?DgU=e3xIORXt#f z`suHH$_jOCDK+H+0()o-18h`^i$SLxl0^t(mA3qsxKEiCO207Zb*Q;UUtl&#z6EN1 zjpAAiNNnm3m6vtV)l`M>^{Z;ZfYinKDC&kX^mKB-Qx?n;LvHL7&|&5ngSNw=IJUig z7Kph698-h2ieE_52T1YImwFsOi+XgH_j?W(1MkAUS@lxFdhQ!+dK_1|#97yb2DsMG zZ+`mPgwI{hj}S31INf^%HU6}%_447pPiK(qGo#VJYBi@2UyowddVl5+&#$Tt5!V$b z3eBrb{MM^hu0+WG3pyq$-UeG<5RjRhx-x6CaO4XSE0=8@sBuu(W@S~?%A|8*_zd}l-cm3ffiZtq5+ zl3V$9UtWOo+8_mvvQ;9}t5f1D>AD(})CWR_$tg zjXf&zeTWHVCn@Ky;i9^1P*XBT8qCQI4BGR1F_W)!6K+%83DY zdg1OkRcO&;wuxc9kVIQpT7#ASvF*7#_9>@WM#v8}D$QsWO{f*;)CG4}YO9l7K4Al| zEUO2oShx8MN7)QZh742d6_tXvc|))svGpeRbtd<CUydLP>l z`-S`WfbtYqO**k2{m>osTp+<(+>6LxSN=dS;)wM}8G+E)0cPH>NbgwESK&a2q5jNA zjgH_Qkf9INhUM5;h;RTIYo@kH7?PgIi5?ueC8_o<`nJOCwbdCJxDw;9x*TmW7Zg7y^kx(>EcU=NkOg>h5Uk+~6f1ZN z`T!^vvXV+ng0`}A4Lk!5q-j$PNU;p_#d-uf`U9gbHO4BcQJPU#Y~saZonajn1+;s| z5ODH8*1!w&V~0<{d%?1!nF~NjOc5jupwzA5Y@udI9w;Ui>f<*KC*y;@a<%NxIJYfC zOtCJm5Ds|9_*9||JhtHEe(sAzVnON4Nz(p8Kps?ijdOawGrR+BxO@iwL0#*539&)gFB4)rc=UtVS_iKfGI;t9dAnHh&WL)eTdFUz{Cx$ z=#M`A(^cHSA$?R(rW^ODhuNA@RO-q=ICa_Otmxb1*d_C}wp#ZxlNg;7 zeT*f-FQJAwIxs3KVHqT3$3`e<%+c7U5+2(m!mVdNn`WkxLqEnw(NxaFSNe!kecS zoXFcK4PvF0FxPld87QawQUI!pATED|1LzEtb1gCEANYkVs8+L zpPkB9uPN|TgU+&ygqIT)6|cRRQrds%Zb;HBsEhbUXJrWMmwAiS#2PPTH$IgP5b8pE znmJ}W#cVV!ruLGgm>NVX9wvsTQylsf1O-GkTVz!y^xa_M4E1f|Kj1e?yJ3G$+`#6$ z(8xIHy;&yzLR*@Z>r0ju&zD2{tYFgq&J1l_U2nZFFb|-7jbCS;r2wrih}D$=%rsQYc7Jw81HzN? zR&NrSP_SsVW~}v!>J!%oaIqQl0iVWQ`CM0J@ucVmB+0A+d=z7%E8HvH(eU*A=(Mqn z7|NDskjatox@s~@OmS?X7c|xUf~=(Nqf?wO;MLYZ$XFW|N3lL;)=CqCq7eF)vTZOB zJXWe+^)}v@LeLVP#bsNBF4EFfNXV}%zkvN<22vi<>g+1(Zt+0XD}1ExflR!O-c>yN zEe||^D#9PiTA8O@r}+Sd zQ##)%cVRxP^IM7=(*dH6N~g--C07W}pfxQz&^EPgJyiHIIUO7x(L6v-h~k`Tm@`9DoY(p7_!SE=1oc$-S{3rb26Al*uDd zlhI9C_LN8+_GL!@Dc^#;d-QRq#JGW{>X&5kwl`FhDgIRR*o~J}N$*Ej=T{UDBS|QY zu4XP~$>p)?+=%M2X$#a{sn$|qoTx#(fO?f??eXtyZ5<%KGH@P&$m#0{Tn0X$;EoTc zyY(MH(mH}EX0&>b3o%m1-7v^59`ypN5rz`rt;fCgrfB8%wA2+*9AV zgaQZVW}(1sgEclNdN}96YA5|aj|P5Dh#M_%k5&mOcoQXq4L_0=%KK$pfYR1%Jdog# zd(CTD9Cg}GD_s52h2>c!KFcMN;Z0I zn u_k-GSL&Tew>pYRcrP55ItqCum`1wObSVS8kCKNz23T(pY+{l8EGRVxe;pCF3D>%|dQp-S^d_daLX)3uR z^mBnWfA}M5m4}>p#u&)6O?facDt=A6Z0P1Y^A#g-HO-v;WdOH51{0p?dw66rJap?< z2Uq9^LkUce3=D&}%T4X;96izWPit80;dtp%j17$AEdwQ#*k@7<@w8fl^A3ByCK@1{ z8%M(XxHe1T1DonKI3F<0;Ek8AR8*Iro&%RP-TQmJP31awOr~yb|hh^@!rR zhKa=1Y{cyMbtlxyv5}5KVp!7^pf>h~X-Tz&qJAOnLE{IG-8g$SnnRUY)H2mvK`%zm zs=~_+(U`iin@5oW1#eMN3&I>r!_{0H z=;-x^>TGbs;Ht0l881N2%P^*Hz4f-xcIO~GGA1*VZKSc@$gu|&^iMn&chQIfpU_jp zM;S^dKOq&RsU7_E_*M}6jck4H_R>>pw0_rzi8z>(p&d?*roc9sa!1v`O1)KV%rg(( zTET14Lx#DP(wz_=W7x~8JHaDg-*k$d*=XxoEIyab2|%$fel0wXL0cy9%*VG+$3X#c zOD6cDChC?l_Y-k5nS+P=Q($O;p2-|&lkQDY&Z(j(SF}!dZ94)Z8Gi&fMEK|Og*m_8$ha7OJ7ZabnMX#3= z;R4C`{rme*An}sj+7CAbZo8=OdcEIkOK?*t7bXMA*3jr!Zp1BBFQ9Pyd_HqeSE;O|vLPPz-Rz9p z(KvYK*JBAM1U~FSLYF#>6?4)p(6yD%uuqpUFfxeMbAxZXe)`*6t zd=)9N>a&E~MKwptD*fH?0e?71RZKPjNNqyX%@7h8dj;2LrKK#>LdbXS#lYD&AS%7g zK`T5b`cvUMO*lKk=Z}k5rNLNz^JDs(M^Jgpn0bb+)4?qZq1i~KA%f77x z8Z;u{Bf63%JPZvUzeLj`eMmC`LiQrNj<9KkTA9+1FVF|9S@{=pL9bBxtIF1`K7Q;7 zw?w&OJ+Z3x>Cse&?OuK()Jc^JOD>6D(u1?sF+t`y$zhi1gGX7((R5GMGr~>L@OeXr zrYW`2?2id#ij-NPVmQ`7goXn_vF_kVPdDT%E~fbGLJ#->VU^Y_29_Ad|gA`24M5F^P% z4pjihHS=+?B`*>V4Y4|@IepfVp_HGlIC8FbbOp2rwhw%PJ+atV2|yol#BPQHW7Jp& zcz*}$$0p?J=SxD;D??!;bhPSiZ#*MxF$dT@dc3OV%Od2>hi%5~jaAcikC&33cKD|L z1g+89IaX?GVqSjRbfx$HAiE+peVC}9NxbH~_mof*+b)9@GubQ^BlfETnJi{X4?pTa zMo^kFj|Bi8>s5`Kr<=kDs9z=1Xz2ISQRxs)?5wI5)=)@*QyJ^L4PhRjD-9kY@gXq} z561>EEaPp7LKLXF}CTIum*9QCd6se*`?@rk4T zf%F!;n11wdp5P>L_!sA4N06+%XyH)^m5O~Lr|M8xHg(G^9Fp+#Wlae2Z2$B?8ZS9R z0mq9>Uo7W~M4vL}i$dR2sb#v$lhZ|~vCb%Ot=+6E^m(XZrQO-XzBYqNff}k=Mx0jN z*QdIG6@khRtctD20z&j9%u&OI>4hcpnB{E+YhKLC2DM(RSxS=`g&ca?(9EK^-~4(p zOAYnN&ic{U5mC?1n21)jZHh2Vwny;rV$u?K`zz87xBLqmN5MAwf*j}@HICz*Q<`=t zf8lpEU;DN*y^J>-Z84G0Yo%ypMsOPxEb5v!Zf5A{c1TN9blRq0j$KofBe{%^a>o-^ z#1=zW&a=gans+Hxhy)}O#8N8sF;PYrE2{LjGusbj3t=Hz%2B-RUz)ESaoJk-a-hSx zo1$x#>6a0Z&+mlZPs?w1>5!H0$5PF?M@_aJcJi4AMn1e1gZpRBeyUuCV)3quzgG{i#g7P8|mau`j$5&CZ})!U2{ zl&))?l8xCIlFo`b3%0oD9iid_*h6>p3h^==N?d8!vr=T58iT1@e6$Y43_FkD)r*`R z_ue`bx~o8MLLbXK)AzU%5?k%Oh*^G~=0t~g|51-E8OINpq58*LtHKca_dDJ8#a@rRnN2Jex;k$-dNggy=7DI2}0s zD1zP?Fsz8(Jcf0-0)xECc4p9CdqX9-H<04E6%jF|jJOpsOrIt?Ahl12=zVSCg_<2> z=SQE`(r#K+z4S=67hCH^t9bj9ksS?l^ykq}-ZIflmtxmM&Ng$ec!^a8KW@fXYx+QA zA3*p!lEn3_#K%C*d%8>TEkISAbcC-cDczU#3_#y;O;n~tLf{`oSUpUCm?@h*B1wg6 zx>}YRAt~gWmiW;$yNJ`)C zGP7m?b0DE&vvu_~gfptw8d?;TJ3HUQpeQZdYsQl^@D8uhQ1uVr^ zgnzXdne%|ae~ZZxfe=kx4-v`VmH)H@P88>}EYW3)vi0P>5J!Tz2kSOhY?YHL(!_4R z)hyk_WZ5*r=M+JdUDUr*GO(1^w6i;at{@VaJ9Id-OklidrkFyhIPa6iDHGHXtLN)1 z&2(Fpv=!<1g;U_03BM|FQm*%yy?55?J|)XoGF20(?|2@xtctkiY271_YtqBaWDmHV zN9#-{6WsVw=k-oD{GBUmU8H<`X3Q6gX2PGbpN{}d=LSG;EXCI!sjn#J zKOvF1SI~F1CQ)9F1IOgf1h^;}-zc$_9%-5(DD*(#{S;$w_g^ zehJR{C0^odIPNbtPUCo0)Th?q#!Yhu3^{v1LtEW2^TEMD;fuk>D@9RBjXDt-CfYYX z%15HxAA#l*O&+ojXKY&zH!$Ihb$_jrMJvw&z#_dKVj>}f3)k&$^S{0 z4ySK*$g`kEpg1aN@rL|Qy7Z?+<~Kw7N1YDdf1yhbu2ul&|5aRbj|7}4LjVIS{asu` z|9?ixnYf#10i0c|9qj*SZ-(#*V*#_%!M%=uSs`HrA;|cV@p+RSaf>Sb5j(CF&uaNsF*(3#=RTI0@| z<<45+&RXQo+TfO8+h}!{p11$le}*O6m*N_-)8pRGii1!EyUe-mNI?`vo|=}M^{hEu z$$iu5dEngkoKoULv z{OPCB&*iS(7$Xdk<+wn250mm-+~98$wTBhhad+|iA| z(@9EX)#WUjyJNoZ{}=!}G+|Z0^JfM9sJKP|v;KWT4%Gi9U4So?(^KCet8@@@;9|)}tSN zY7=v3B4d(o5?)HvW%vlp7Ivz<&bYvHd-A@l-p8uGMF=T=(yHDNd-uTXu3fK}Rq>gv zJHnn_vTYcJBk)Ve|thA{6D@l$vskbamDCQS%j9rJ@Gjw#Tnf(po++gaG0rT=s1o{y!s0uTvetI-{*9EPTUi#b~=4B@3*v6 zgUp<4(DU$>jByFp2qJ>c=8#u|>9v+8oH*u^-UyBH;rmV5p-sEhu`^gNWz(m!?J5xa zwWwsUDnHUW2AYEB{j|1Rl=YJxMn`P^xjx?Ewe8cDzuANZ38F`Zjq=OJnAA>PC*@xo zJfGJ&h)AoSAn4+m4xc)`;SF6|?Lpked8KdS!)jLT=TKSsZdK41GpLP_A@oB^xLa(b zFnV8xhY0pM6WC7+Fv1Pv%B6E}S=Pdo33m$$kg@0$so)}sZG|8tj4-AC_*lm$4gnfm z>s^^@7ig)m_p2Od8X7Xj_|_qAfxIIw2YKpRGuBg1cW3cBlp#*cH)Drr8 z7pzlaZsYMN(qxp|$Bn}+FBoo~mYE*KkM{1DR4~mf3FcDjuxjLWA?%{PTvs!Xm~DjR z8z1PAhlMYM&;$zz$(M|D0=kaPr8NAW^sO`5zp^=7R%;)38OiZV@l7o$rN47FMr0)G z;4e8v_yFujzB$xNANuO!;3uFQN-{~D1kwy83pw1=$& zvRHCYf-IbOf6B?js9n;op16I8docMj_)yXqPRB~*@0af{MDjL|df7SzJ_WJ=f%(He zguXx252Y=hEw3#g$s*qis_7XCpBLC8k0}5;4@2%cjP*vn1|d#lyT{cQ zO&}IObSI5^{REXh`~ozwdFTmvWEy`|+h@d5-q=kIO!n+dhOb{`c1gmm?O+M~!Yh%k ztz*rS`n;zC_+2uUhW)t50+_6wHPL$7a|CGV;IdFZ@4*7FbkbR*p8!>CF#1^I3f%;y z3eCx!><^^pk%mc+08u7jCM_4SMobTvs>S;SUG?iA{A_aa~J#VZh#WT|@>zO)SXI12> zPCHz0?jK$@b7pTWAbKgYAGKJ_{8}M-R>7B}ZZLS3zz=kY1<$h%7F&u4)~81g;nD>* z9`(+bNrZ6e>#P`%Hs17;R2uDCMGm0YmEx3DqV+W_n6@-YMorsek~i`X(AX8@BeT2*_R#miPWz2;UB(*yvE0fuchF3a=-11fpGe^nJ2rnWt zp;>xzEwQ4KOp`Tww%>KM7j6oRJt2PX@62EL0pTl1a-9^EcmYcH|h ztHg_6@qT1kP@_Kg@ha(8$rkK{HJ?P|K$WZ@N2<4}QSD?OQCZ@_{j8_Ad*V_m&WOve zx>1Pb_%!K^5^;-z1jqwK%$rg*0};*dIW_~m6nF9&eyP1e1K-?o-}=h|OhzckJX3vJ z*IMus(Q8-!N4sAz<6NdbVU2GFzGb&riV7{o9u&^_P^S`nVTH?dz0A^wjGv-o8%Q3x zQ><7N51oI;oiATJnAtWL2Fkch-I3&q#?Hj|U$3w+^aXjpz#b`H`=oY=6cK)Ze&vwk z#X5A(Q_zl?8Fey=FrZ*hGK+F%a37;&Q|BF|lMa+NqF~$`R*#89f}Bu`@!7L#Si44} zO)eJkWNH+sleM6q;@Q)+!M$eVmGK;;pW4$ZdkG+@5Ex6cY~54zqM|`AQPvtI*rV0R z@dJ!vI)`0j&??s_GwJWD>|N2u+5v{OcojTfM&~R5qsh)K*BJE5({;OaykeeIbW_P% zDTpHmd$g)K?((BG1f>Fnqv*AD-lgKN-EytP`hhW^00h^i>4LyZybLuFPb5YAI3sa% z8tUr25wI&x4qZn~uq}KTKNkTKH)2MJtwbwu5+qkxmYy&_*Eli~hz{6cNKSBu8gdc@ zNVvEy84<3MN)utYw=!8DWzGPluoseBwuV=XvRs$xjs;g%S9-6aOss$g{L8yKj3GNH z!h8~=G{RfFF4ow&yWU(RQ=WB(h@+3Jk?vuVuCZ1Sy4I80Qyxbpy+eO;D<8lyApW}q z;rG7(x1oTYiT-*1Ly+*VVgE^x@W=U&75LNszW*caU*rjYMfl_WKiY>sdV;^%pMfiW zcmIe1{;!BX>Vm)7pNQ$--G7r0{A=ic(i;3T^!2Zye^(y-E5d(L9{dgA&w^P${AKXJ zBm8F_!e4R!S_t${+~?nG{I^B@755)yLH~;RpE73u#H7Uj&zS!;fA+7?e_q31kM92o z{qUP?{y(Ps_k;YuV*bJSpI^cs#`-t=^Sx?N{AbL+@z=lN{FT4{4^n}D%>Rk=zu2sb XJk;-8BN!OM?~CGh=Fy+!&!_(n&s|-S literal 0 HcmV?d00001 diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavLoginActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavLoginActivity.java index 7f71feffe..4366ae75c 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavLoginActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavLoginActivity.java @@ -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"); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java index f005a4389..a2375ae02 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java @@ -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()) { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java index f62ce4dfa..d5f9e0c2c 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java @@ -343,7 +343,8 @@ public class JREUtils { List userArguments = parseJavaArguments(LauncherPreferences.PREF_CUSTOM_JAVA_ARGS); String resolvFile; resolvFile = new File(Tools.DIR_DATA,"resolv.conf").getAbsolutePath(); - String[] overridableArguments = new String[]{ + + ArrayList 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 additionalArguments = new ArrayList<>(); for(String arg : overridableArguments) { String strippedArg = arg.substring(0,arg.indexOf('=')); diff --git a/app_pojavlauncher/src/main/res/values/strings.xml b/app_pojavlauncher/src/main/res/values/strings.xml index 1bd9ab9f7..c56acc483 100644 --- a/app_pojavlauncher/src/main/res/values/strings.xml +++ b/app_pojavlauncher/src/main/res/values/strings.xml @@ -292,5 +292,5 @@ Force openGL 1 Help with compatibility on some old versions Arc Capes - Enables capes from Arc. For more information please visit https://arccapes.com + Enables capes from Arc. For more information please visit https://arccapes.com. Requires OptiFine. diff --git a/app_pojavlauncher/src/main/res/xml/pref_misc.xml b/app_pojavlauncher/src/main/res/xml/pref_misc.xml index dff30194a..bb241cfbf 100644 --- a/app_pojavlauncher/src/main/res/xml/pref_misc.xml +++ b/app_pojavlauncher/src/main/res/xml/pref_misc.xml @@ -26,6 +26,11 @@ android:key="checkLibraries" android:summary="@string/mcl_setting_check_libraries_subtitle" android:title="@string/mcl_setting_check_libraries" /> + diff --git a/arc_dns_injector/.gitignore b/arc_dns_injector/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/arc_dns_injector/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/arc_dns_injector/build.gradle b/arc_dns_injector/build.gradle new file mode 100644 index 000000000..85e789bf6 --- /dev/null +++ b/arc_dns_injector/build.gradle @@ -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/")) +} \ No newline at end of file diff --git a/arc_dns_injector/src/main/java/git/artdeell/arcdns/ArcDNSInjectorAgent.java b/arc_dns_injector/src/main/java/git/artdeell/arcdns/ArcDNSInjectorAgent.java new file mode 100644 index 000000000..70fbb05fa --- /dev/null +++ b/arc_dns_injector/src/main/java/git/artdeell/arcdns/ArcDNSInjectorAgent.java @@ -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(); + } + } +} \ No newline at end of file diff --git a/arc_dns_injector/src/main/java/git/artdeell/arcdns/CacheUtilCommons.java b/arc_dns_injector/src/main/java/git/artdeell/arcdns/CacheUtilCommons.java new file mode 100644 index 000000000..5de79fb24 --- /dev/null +++ b/arc_dns_injector/src/main/java/git/artdeell/arcdns/CacheUtilCommons.java @@ -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 boolean 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; + } + } + +} diff --git a/arc_dns_injector/src/main/java/git/artdeell/arcdns/CacheUtil_J8.java b/arc_dns_injector/src/main/java/git/artdeell/arcdns/CacheUtil_J8.java new file mode 100644 index 000000000..7c53da9be --- /dev/null +++ b/arc_dns_injector/src/main/java/git/artdeell/arcdns/CacheUtil_J8.java @@ -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 getCache() + throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException { + return getCacheOfInetAddress$Cache0(getAddressCacheOfInetAddress()); + } + + private static Map getNegativeCache() + throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException { + return getCacheOfInetAddress$Cache0(getNegativeCacheOfInetAddress()); + } + + + private static volatile Field cacheMapFieldOfInetAddress$Cache = null; + + @SuppressWarnings("unchecked") + private static Map 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) 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() { + } +} diff --git a/arc_dns_injector/src/main/java/git/artdeell/arcdns/CacheUtil_J9.java b/arc_dns_injector/src/main/java/git/artdeell/arcdns/CacheUtil_J9.java new file mode 100644 index 000000000..b54060bae --- /dev/null +++ b/arc_dns_injector/src/main/java/git/artdeell/arcdns/CacheUtil_J9.java @@ -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 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 getCacheOfInetAddress() + throws NoSuchFieldException, IllegalAccessException { + return (ConcurrentMap) getCacheAndExpirySetOfInetAddress0()[0]; + } + + @SuppressWarnings("unchecked") + private static ConcurrentSkipListSet getExpirySetOfInetAddress() + throws NoSuchFieldException, IllegalAccessException { + return (ConcurrentSkipListSet) 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() { + } + +} diff --git a/arc_dns_injector/src/main/java/git/artdeell/arcdns/other/JavaVersion.java b/arc_dns_injector/src/main/java/git/artdeell/arcdns/other/JavaVersion.java new file mode 100644 index 000000000..2f0afb47d --- /dev/null +++ b/arc_dns_injector/src/main/java/git/artdeell/arcdns/other/JavaVersion.java @@ -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 +// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +/** + *

An enum representing all the versions of the Java specification. + * This is intended to mirror available values from the + * java.specification.version System property.

+ * + * @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; + } + + //----------------------------------------------------------------------- + + /** + *

Whether this version of Java is at least the version of Java passed in.

+ * + *

For example:
+ * {@code myVersion.atLeast(JavaVersion.JAVA_1_4)}

+ * + * @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; + } + + //----------------------------------------------------------------------- + + /** + *

Whether this version of Java is at most the version of Java passed in.

+ * + *

For example:
+ * {@code myVersion.atMost(JavaVersion.JAVA_1_4)}

+ * + * @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 null 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 null 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; + } + + //----------------------------------------------------------------------- + + /** + *

The string value is overridden to return the standard name.

+ * + *

For example, {@code "1.5"}.

+ * + * @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; + } + + /** + *

Convert a {@code String} to a {@code float}, returning a + * default value if the conversion fails.

+ * + *

If the string {@code str} is {@code null}, the default + * value is returned.

+ * + *
+     *   NumberUtils.toFloat(null, 1.1f)   = 1.0f
+     *   NumberUtils.toFloat("", 1.1f)     = 1.1f
+     *   NumberUtils.toFloat("1.5", 0.0f)  = 1.5f
+     * 
+ * + * @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; + } + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 1e810efdd..cb6045552 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,3 +2,4 @@ rootProject.name='PojavLauncher' include ':jre_lwjgl3glfw' include ':app_pojavlauncher' +include ':arc_dns_injector'