diff --git a/readme.md b/readme.md index 9bcb004..30179f8 100644 --- a/readme.md +++ b/readme.md @@ -1,8 +1,15 @@ # lestrange -A playground for dark magic with Java class loaders. This is a work in progress. Currently uses reflection to invoke methods. +A playground for dark magic with Java class loaders. This is a work in +progress. There are two implmentations to call methods on dynamically +loaded classes currently: +- Using reflection to call a method by name on a class file passed in + an multi-part HTTP POST request +- Using the interface `Callable` and invoking the `call` method on the + object created from the class file passed -This library is an attempt to understand how dynamic class loaders work and how in languages like Clojure, code is evaluated. +This library is an attempt to understand how dynamic class loaders +work and how in languages like Clojure, code is evaluated. ![Bellatrix](/utils/bella.png) @@ -14,12 +21,25 @@ Start the server with: mvn clean install exec:java ``` -Send a class file to be executed: +Send a class file to be executed via reflection: ```bash curl -XPOST "localhost:8080/loadclass/?classname=SystemInfo&methodname=info" -F "file=@SystemInfo.class" curl -XPOST "localhost:8080/loadclass/?classname=RandomNumber&methodname=sayHello" -F "file=@RandomNumber.class" ``` +To run the `call` method on a `Callable` class: +```bash +curl -XPOST "localhost:8080/callable/?classname=CallableAPI" -F "file=@CallableAPI.class" + _ _ + | | | | + | | ___ ___| |_ _ __ __ _ _ __ __ _ ___ + | | / _ \ __| __| '__/ _` | '_ \ / _` |/ _ \ + | |____ __\__ \ |_| | | (_| | | | | (_| | __/ + |______\___|___/\__|_| \__,_|_| |_|\__, |\___| + __/ | + |___/ +``` + # Generating class files Ensure that clases are public and the method being accessed is public. ```bash @@ -27,6 +47,7 @@ cd utils javac RandomNumber.java javac HelloClass.java javac SystemInfo.java +javac CallableAPI.java ``` # Notes @@ -55,6 +76,7 @@ while true; curl -XPOST "localhost:8080/loadclass/?classname=SystemInfo&methodname=info" -F "file=@SystemInfo.class"; curl -XPOST "localhost:8080/loadclass/?classname=HelloClass&methodname=sayHello" -F "file=@HelloClass.class"; curl -XPOST "localhost:8080/loadclass/?classname=RandomNumber&methodname=sayHello" -F "file=@RandomNumber.class"; + curl -XPOST "localhost:8080/callable/?classname=CallableAPI" -F "file=@CallableAPI.class"; done ``` diff --git a/src/main/java/lestrange/CallableLoader.java b/src/main/java/lestrange/CallableLoader.java new file mode 100644 index 0000000..738552e --- /dev/null +++ b/src/main/java/lestrange/CallableLoader.java @@ -0,0 +1,47 @@ +package lestrange; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.concurrent.Callable; + +import javax.servlet.ServletException; +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpStatus; + +public class CallableLoader extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.setStatus(HttpStatus.OK_200); + resp.getWriter().println("Use the POST method."); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + String cl = req.getParameter("classname"); + + InputStream x = req.getPart("file").getInputStream(); + SimpleClassLoader hc = new SimpleClassLoader(x); + + @SuppressWarnings("unchecked") + Class> callable = (Class>) hc.loadClass(cl); + + Object result = callable.newInstance().call(); + + resp.setStatus(HttpStatus.OK_200); + resp.getWriter().println(result == null ? "Result is a null" : result.toString()); + + } catch (Exception e) { + e.printStackTrace(); + resp.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500); + resp.getWriter().println("That's too bad... " + e.getMessage()); + } + } + +} diff --git a/src/main/java/lestrange/Main.java b/src/main/java/lestrange/Main.java index 5d4f102..15becc1 100644 --- a/src/main/java/lestrange/Main.java +++ b/src/main/java/lestrange/Main.java @@ -1,6 +1,8 @@ package lestrange; import javax.servlet.MultipartConfigElement; +import javax.servlet.annotation.MultipartConfig; + import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.thread.QueuedThreadPool; @@ -8,17 +10,20 @@ public class Main { public static void main(String[] args) throws Exception { Server server = new Server(8080); - + QueuedThreadPool tp = (QueuedThreadPool) server.getThreadPool(); tp.setLowThreadsThreshold(0); tp.setReservedThreads(-1); tp.setMinThreads(1); tp.setMaxThreads(5); - ServletContextHandler handler = new ServletContextHandler(server, "/loadclass"); + ServletContextHandler handler = new ServletContextHandler(server, "/"); + + MultipartConfigElement mpc = new MultipartConfigElement(""); + + handler.addServlet(ReflectiveLoader.class, "/loadclass/").getRegistration().setMultipartConfig(mpc); - handler.addServlet(LoaderServlet.class, "/").getRegistration() - .setMultipartConfig(new MultipartConfigElement("")); + handler.addServlet(CallableLoader.class, "/callable/").getRegistration().setMultipartConfig(mpc); server.start(); server.join(); diff --git a/src/main/java/lestrange/LoaderServlet.java b/src/main/java/lestrange/ReflectiveLoader.java similarity index 96% rename from src/main/java/lestrange/LoaderServlet.java rename to src/main/java/lestrange/ReflectiveLoader.java index ee2401e..78676a0 100644 --- a/src/main/java/lestrange/LoaderServlet.java +++ b/src/main/java/lestrange/ReflectiveLoader.java @@ -11,7 +11,7 @@ import org.eclipse.jetty.http.HttpStatus; -public class LoaderServlet extends HttpServlet { +public class ReflectiveLoader extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { diff --git a/utils/CallableAPI.java b/utils/CallableAPI.java new file mode 100644 index 0000000..dfc5c42 --- /dev/null +++ b/utils/CallableAPI.java @@ -0,0 +1,30 @@ +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.concurrent.Callable; + +public class CallableAPI implements Callable { + + public String call() throws Exception { + URL url = new URL("http://artii.herokuapp.com/make?text=Lestrange"); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + + con.setRequestMethod("GET"); + con.setConnectTimeout(5000); + con.setReadTimeout(5000); + con.setDoOutput(true); + + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer content = new StringBuffer(); + while ((inputLine = in.readLine()) != null) { + content.append(inputLine+"\n"); + } + + in.close(); + con.disconnect(); + return content.toString(); + } +}