From 4419629c71f56a318428db63fcb8ecb60e08586e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9C=B0=E5=B9=B3=E7=BA=BF?= <22250530@qq.com> Date: Wed, 2 Sep 2015 19:35:46 +0800 Subject: [PATCH] --- .../redkale/net/icep/BindingIcepServlet.java | 33 +++++ .../wentch/redkale/net/icep/IcepContext.java | 33 +++++ .../redkale/net/icep/IcepException.java | 29 ++++ .../redkale/net/icep/IcepPrepareServlet.java | 40 +++++ .../wentch/redkale/net/icep/IcepRequest.java | 74 ++++++++++ .../wentch/redkale/net/icep/IcepResponse.java | 25 ++++ .../wentch/redkale/net/icep/IcepServer.java | 67 +++++++++ .../wentch/redkale/net/icep/IcepServlet.java | 28 ++++ .../icep/attr/XorMappedAddressAttribute.java | 80 ++++++++++ .../redkale/net/icep/stun/StunAttribute.java | 23 +++ .../redkale/net/icep/stun/StunHeader.java | 139 ++++++++++++++++++ .../redkale/net/icep/stun/StunPacket.java | 51 +++++++ 12 files changed, 622 insertions(+) create mode 100644 src/com/wentch/redkale/net/icep/BindingIcepServlet.java create mode 100644 src/com/wentch/redkale/net/icep/IcepContext.java create mode 100644 src/com/wentch/redkale/net/icep/IcepException.java create mode 100644 src/com/wentch/redkale/net/icep/IcepPrepareServlet.java create mode 100644 src/com/wentch/redkale/net/icep/IcepRequest.java create mode 100644 src/com/wentch/redkale/net/icep/IcepResponse.java create mode 100644 src/com/wentch/redkale/net/icep/IcepServer.java create mode 100644 src/com/wentch/redkale/net/icep/IcepServlet.java create mode 100644 src/com/wentch/redkale/net/icep/attr/XorMappedAddressAttribute.java create mode 100644 src/com/wentch/redkale/net/icep/stun/StunAttribute.java create mode 100644 src/com/wentch/redkale/net/icep/stun/StunHeader.java create mode 100644 src/com/wentch/redkale/net/icep/stun/StunPacket.java diff --git a/src/com/wentch/redkale/net/icep/BindingIcepServlet.java b/src/com/wentch/redkale/net/icep/BindingIcepServlet.java new file mode 100644 index 000000000..84f21d3ab --- /dev/null +++ b/src/com/wentch/redkale/net/icep/BindingIcepServlet.java @@ -0,0 +1,33 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.net.icep; + +import com.wentch.redkale.net.icep.attr.*; +import com.wentch.redkale.net.icep.stun.*; +import com.wentch.redkale.util.*; +import java.io.*; +import java.nio.*; + +/** + * + * @author zhangjx + */ +@AutoLoad(false) +public class BindingIcepServlet extends IcepServlet { + + @Override + public void execute(IcepRequest request, IcepResponse response) throws IOException { + StunPacket packet = request.getStunPacket(); + ByteBuffer buffer = response.getContext().pollBuffer(); + packet.addAttribute(new XorMappedAddressAttribute(request.getRemoteAddress())); + packet.getHeader().setRequestid((short) (StunHeader.TYPE_SUCCESS | StunHeader.ACTION_BINDING)); + packet.encode(buffer); + buffer.flip(); + Utility.println("响应结果: ", buffer); + response.finish(buffer); + } + +} diff --git a/src/com/wentch/redkale/net/icep/IcepContext.java b/src/com/wentch/redkale/net/icep/IcepContext.java new file mode 100644 index 000000000..5b17588ff --- /dev/null +++ b/src/com/wentch/redkale/net/icep/IcepContext.java @@ -0,0 +1,33 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.net.icep; + +import com.wentch.redkale.convert.bson.*; +import com.wentch.redkale.net.*; +import com.wentch.redkale.util.*; +import com.wentch.redkale.watch.*; +import java.net.*; +import java.nio.*; +import java.nio.charset.*; +import java.util.concurrent.*; +import java.util.logging.*; + +/** + * + * @author zhangjx + */ +public final class IcepContext extends Context { + + protected final BsonFactory bsonFactory; + + public IcepContext(long serverStartTime, Logger logger, ExecutorService executor, ObjectPool bufferPool, + ObjectPool responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare, + WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond) { + super(serverStartTime, logger, executor, bufferPool, responsePool, maxbody, charset, + address, prepare, watch, readTimeoutSecond, writeTimeoutSecond); + this.bsonFactory = BsonFactory.root(); + } +} diff --git a/src/com/wentch/redkale/net/icep/IcepException.java b/src/com/wentch/redkale/net/icep/IcepException.java new file mode 100644 index 000000000..374498995 --- /dev/null +++ b/src/com/wentch/redkale/net/icep/IcepException.java @@ -0,0 +1,29 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.net.icep; + +/** + * + * @author zhangjx + */ +public class IcepException extends RuntimeException { + + public IcepException() { + super(); + } + + public IcepException(String s) { + super(s); + } + + public IcepException(String message, Throwable cause) { + super(message, cause); + } + + public IcepException(Throwable cause) { + super(cause); + } +} diff --git a/src/com/wentch/redkale/net/icep/IcepPrepareServlet.java b/src/com/wentch/redkale/net/icep/IcepPrepareServlet.java new file mode 100644 index 000000000..e87bc58f4 --- /dev/null +++ b/src/com/wentch/redkale/net/icep/IcepPrepareServlet.java @@ -0,0 +1,40 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.net.icep; + +import com.wentch.redkale.net.*; +import com.wentch.redkale.util.*; +import java.io.*; +import java.util.*; + +/** + * + * @author zhangjx + */ +public class IcepPrepareServlet extends PrepareServlet { + + private final HashMap servletmaps = new HashMap<>(); + + public IcepPrepareServlet() { + this.servletmaps.put(0x0001, new BindingIcepServlet()); + } + + @Override + public void init(Context context, AnyValue config) { + } + + // 28.[00,03,00,08, 21,12,a4,42,45,6f,4e,77,4e,47,71,55,32,37,77,39, 00,19,00,04,11,00,00,00] + @Override + public void execute(IcepRequest request, IcepResponse response) throws IOException { + IcepServlet servlet = servletmaps.get(request.getRequestid()); + if (servlet != null) { + servlet.execute(request, response); + } else { + response.finish(); + } + } + +} diff --git a/src/com/wentch/redkale/net/icep/IcepRequest.java b/src/com/wentch/redkale/net/icep/IcepRequest.java new file mode 100644 index 000000000..0741afb23 --- /dev/null +++ b/src/com/wentch/redkale/net/icep/IcepRequest.java @@ -0,0 +1,74 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.net.icep; + +import com.wentch.redkale.net.*; +import com.wentch.redkale.net.icep.stun.*; +import com.wentch.redkale.util.*; +import java.net.*; +import java.nio.*; + +/** + * + * @author zhangjx + */ +public class IcepRequest extends Request { + + private int requestid; + + private StunPacket stunPacket; + + protected IcepRequest(IcepContext context) { + super(context); + } + + // 20.[00,01,00,00,21,12,a4,42,63,6a,30,65,62,43,44,57,67,76,68,58] + @Override + protected int readHeader(ByteBuffer buffer) { + Utility.println(Utility.now() + "-------" + getRemoteAddress() + " ", buffer); + if (buffer.remaining() < 20) return -1; + this.requestid = buffer.getShort(); + short typeid = (short) ((this.requestid >> 16) & 0xffff); + short actionid = (short) (this.requestid & 0xffff); + byte[] bytes = new byte[16]; + buffer.get(bytes); + StunHeader header = new StunHeader(typeid, actionid, bytes); + this.stunPacket = new StunPacket(header); + return -1; + } + + public static void main(String[] args) throws Exception { + int a = 0xabcd0000; + System.out.println(Integer.toHexString((a >> 16) & 0xffff)); + } + + public InetSocketAddress getRemoteAddress() { + return (InetSocketAddress) channel.getRemoteAddress(); + } + + @Override + protected void readBody(ByteBuffer buffer) { + } + + @Override + protected void prepare() { + + } + + @Override + protected void recycle() { + + } + + public int getRequestid() { + return requestid; + } + + public StunPacket getStunPacket() { + return stunPacket; + } + +} diff --git a/src/com/wentch/redkale/net/icep/IcepResponse.java b/src/com/wentch/redkale/net/icep/IcepResponse.java new file mode 100644 index 000000000..ecf43be03 --- /dev/null +++ b/src/com/wentch/redkale/net/icep/IcepResponse.java @@ -0,0 +1,25 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.net.icep; + +import com.wentch.redkale.net.*; +import com.wentch.redkale.util.*; +import java.util.concurrent.atomic.*; + +/** + * + * @author zhangjx + */ +public class IcepResponse extends Response { + + protected IcepResponse(Context context, IcepRequest request) { + super(context, request); + } + + public static ObjectPool createPool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator creator) { + return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x) -> ((IcepResponse) x).prepare(), (x) -> ((IcepResponse) x).recycle()); + } +} diff --git a/src/com/wentch/redkale/net/icep/IcepServer.java b/src/com/wentch/redkale/net/icep/IcepServer.java new file mode 100644 index 000000000..09e34e9ba --- /dev/null +++ b/src/com/wentch/redkale/net/icep/IcepServer.java @@ -0,0 +1,67 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.net.icep; + +import com.wentch.redkale.net.*; +import com.wentch.redkale.util.*; +import com.wentch.redkale.util.AnyValue.DefaultAnyValue; +import com.wentch.redkale.watch.*; +import java.nio.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; + +/** + * + * @author zhangjx + */ +public final class IcepServer extends Server { + + public IcepServer() { + this(System.currentTimeMillis(), null); + } + + public static void main(String[] args) throws Exception { + DefaultAnyValue conf = new DefaultAnyValue(); + conf.addValue("host", "10.28.2.207"); + conf.addValue("port", "3478"); + final CountDownLatch cdl = new CountDownLatch(1); + final IcepServer server = new IcepServer(); + server.init(conf); + server.start(); + cdl.await(); + } + + public IcepServer(long serverStartTime, final WatchFactory watch) { + super(serverStartTime, "UDP", new IcepPrepareServlet(), watch); + } + + @Override + public void init(AnyValue config) throws Exception { + super.init(config); + } + + @Override + @SuppressWarnings("unchecked") + protected Context createContext() { + final int port = this.address.getPort(); + AtomicLong createBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("ICEP_" + port + ".Buffer.creatCounter"); + AtomicLong cycleBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("ICEP_" + port + ".Buffer.cycleCounter"); + int rcapacity = Math.max(this.capacity, 16 * 1024 + 8); //兼容 HTTP 2.0 + ObjectPool bufferPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, this.bufferPoolSize, + (Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> { + if (e == null || e.isReadOnly() || e.capacity() != rcapacity) return false; + e.clear(); + return true; + }); + AtomicLong createResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("ICEP_" + port + ".Response.creatCounter"); + AtomicLong cycleResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("ICEP_" + port + ".Response.cycleCounter"); + ObjectPool responsePool = IcepResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null); + IcepContext icepcontext = new IcepContext(this.serverStartTime, this.logger, executor, bufferPool, responsePool, + this.maxbody, this.charset, this.address, this.prepare, this.watch, this.readTimeoutSecond, this.writeTimeoutSecond); + responsePool.setCreator((Object... params) -> new IcepResponse(icepcontext, new IcepRequest(icepcontext))); + return icepcontext; + } +} diff --git a/src/com/wentch/redkale/net/icep/IcepServlet.java b/src/com/wentch/redkale/net/icep/IcepServlet.java new file mode 100644 index 000000000..657184320 --- /dev/null +++ b/src/com/wentch/redkale/net/icep/IcepServlet.java @@ -0,0 +1,28 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.net.icep; + +import com.wentch.redkale.net.*; +import com.wentch.redkale.util.*; + +/** + * + * @author zhangjx + */ +public abstract class IcepServlet implements Servlet { + + AnyValue conf; //当前HttpServlet的配置 + + @Override + public final boolean equals(Object obj) { + return obj != null && obj.getClass() == this.getClass(); + } + + @Override + public final int hashCode() { + return this.getClass().hashCode(); + } +} diff --git a/src/com/wentch/redkale/net/icep/attr/XorMappedAddressAttribute.java b/src/com/wentch/redkale/net/icep/attr/XorMappedAddressAttribute.java new file mode 100644 index 000000000..9375ef642 --- /dev/null +++ b/src/com/wentch/redkale/net/icep/attr/XorMappedAddressAttribute.java @@ -0,0 +1,80 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.net.icep.attr; + +import com.wentch.redkale.net.icep.*; +import com.wentch.redkale.net.icep.stun.*; +import com.wentch.redkale.util.*; +import java.net.*; +import java.nio.*; + +/** + * + * @author zhangjx + */ +public class XorMappedAddressAttribute extends StunAttribute { + + private InetSocketAddress address; + + public XorMappedAddressAttribute() { + } + + public XorMappedAddressAttribute(InetSocketAddress address) { + this.address = address; + } + + @Override + public StunAttribute decode(final ByteBuffer buffer, final byte[] transactionid) { + final short attrid = buffer.getShort(); + if (attrid != getAttributeid()) throw new IcepException(this.getClass().getSimpleName() + " has illegal attributeid " + attrid); + final int bodysize = buffer.getShort() & 0xffff; + final short family = buffer.getShort(); + if (bodysize == 24 && family != 0x0002) throw new IcepException("bodysize = " + bodysize + " but family = " + family); + if (bodysize == 12 && family != 0x0001) throw new IcepException("bodysize = " + bodysize + " but family = " + family); + final int port = (buffer.getShort() ^ ((transactionid[0] << 8 & 0x0000FF00) | (transactionid[1] & 0x000000FF))) & 0xffff; + byte[] bytes = new byte[family == 0x0002 ? 16 : 4]; + buffer.get(bytes); + for (int i = 0; i <= bytes.length; i++) { + bytes[i] ^= transactionid[i]; + } + try { + this.address = new InetSocketAddress(InetAddress.getByAddress(bytes), port); + } catch (UnknownHostException e) { + throw new IcepException("port = " + port + " and address = " + Utility.binToHexString(bytes), e); + } + return this; + } + + @Override + public ByteBuffer encode(final ByteBuffer buffer, final byte[] transactionid) { + final boolean ipv6 = this.address.getAddress() instanceof Inet6Address; + buffer.putShort((short) getAttributeid()); + buffer.putShort((short) (ipv6 ? 24 : 12)); + buffer.putShort((short) (ipv6 ? 0x0002 : 0x0001)); + buffer.putShort((short) (this.address.getPort() ^ ((transactionid[0] << 8 & 0x0000FF00) | (transactionid[1] & 0x000000FF)))); + final byte[] bytes = this.address.getAddress().getAddress(); + for (int i = 0; i <= bytes.length; i++) { + bytes[i] ^= transactionid[i]; + } + buffer.put(bytes); + return buffer; + } + + @Override + public short getAttributeid() { + return 0x0020; + } + + @Override + public String getName() { + return "XOR-MAPPED-ADDRESS"; + } + + public InetSocketAddress getAddress() { + return address; + } + +} diff --git a/src/com/wentch/redkale/net/icep/stun/StunAttribute.java b/src/com/wentch/redkale/net/icep/stun/StunAttribute.java new file mode 100644 index 000000000..c74775066 --- /dev/null +++ b/src/com/wentch/redkale/net/icep/stun/StunAttribute.java @@ -0,0 +1,23 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.net.icep.stun; + +import java.nio.*; + +/** + * + * @author zhangjx + */ +public abstract class StunAttribute { + + public abstract StunAttribute decode(final ByteBuffer buffer, final byte[] transactionid); + + public abstract ByteBuffer encode(final ByteBuffer buffer, final byte[] transactionid); + + public abstract short getAttributeid(); + + public abstract String getName(); +} diff --git a/src/com/wentch/redkale/net/icep/stun/StunHeader.java b/src/com/wentch/redkale/net/icep/stun/StunHeader.java new file mode 100644 index 000000000..88e609826 --- /dev/null +++ b/src/com/wentch/redkale/net/icep/stun/StunHeader.java @@ -0,0 +1,139 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.net.icep.stun; + +import com.wentch.redkale.util.*; +import java.nio.*; +import java.security.*; + +/** + * + * @author zhangjx + */ +public class StunHeader { + + public static final int MAGIC_COOKIE = 0x2112A442; + + //---------------------------- type ------------------------- + private static final int TYPE_BIT_MASK = 0x0110; + + public static final short TYPE_REQUEST = 0x0000; + + public static final short TYPE_INDICATION = 0x0010; + + public static final short TYPE_SUCCESS = 0x0100; + + public static final short TYPE_ERROR = 0x0110; + + //---------------------------- action ------------------------- + public static final int ACTION_BIT_MASK = 0xCEEF; + + public static final short ACTION_BINDING = 0x0001; + + public static final short ACTION_ALLOCATE = 0x0003; + + public static final short ACTION_REFRESH = 0x0004; + + public static final short ACTION_SEND = 0x0006; + + public static final short ACTION_DATA = 0x0007; + + public static final short ACTION_CREATE_PERMISSION = 0x0008; + + public static final short ACTION_CHANNELBIND = 0x0009; + + //----------------------------------------------------------- + private short typeid; //无符号 2bytes + + private short actionid; //无符号 2bytes + + private int bodysize; //无符号 2bytes + + private byte[] transactionid; //RFC5389 =MAGIC_COOKIE + byte[12] = byte[16]; + + public static byte[] generateTransactionid() { + byte[] transactions = new byte[16]; + // Get a secure PRNG + SecureRandom random = new SecureRandom(); + random.nextBytes(transactions); + transactions[0] = 0x21; + transactions[1] = 0x12; + transactions[2] = (byte) 0xA4; + transactions[3] = 0x42; + return transactions; + } + + public StunHeader(short typeid, short actionid, byte[] transactionid0) { + this.typeid = typeid; + this.actionid = actionid; + this.transactionid = transactionid0 == null ? generateTransactionid() : transactionid0; + } + + public StunHeader decode(final ByteBuffer buffer) { + short requestid = buffer.getShort(); + this.typeid = (short) (requestid << 2); + this.actionid = (short) (requestid & 0xff); + this.bodysize = buffer.getShort() & 0xffff; + this.transactionid = new byte[16]; + buffer.get(transactionid); + return this; + } + + public ByteBuffer encode(final ByteBuffer buffer) { + buffer.putShort((short) (this.typeid << 8 | this.actionid)); //requestid + buffer.putShort((short) 0); //bodysize + buffer.put(transactionid); + return buffer; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "[typeid = " + format(typeid) + ", actionid = " + format(actionid) + + ", bodysize = " + (int) (bodysize) + ", transactionid = " + Utility.binToHexString(transactionid) + "]"; + } + + private static String format(short value) { + String str = Integer.toHexString(value); + if (str.length() == 1) return "0x000" + str; + if (str.length() == 2) return "0x00" + str; + if (str.length() == 3) return "0x0" + str; + return "0x" + str; + } + + public void setRequestid(short requestid) { + this.typeid = (short) (requestid << 2); + this.actionid = (short) (requestid & 0xff); + } + + public void setBodysize(char bodysize) { + this.bodysize = bodysize; + } + + public void setTypeid(short typeid) { + this.typeid = typeid; + } + + public void setActionid(short actionid) { + this.actionid = actionid; + } + + public short getTypeid() { + return typeid; + } + + public short getActionid() { + return actionid; + } + + public int getBodysize() { + return bodysize; + } + + public byte[] getTransactionid() { + return transactionid; + } + +} diff --git a/src/com/wentch/redkale/net/icep/stun/StunPacket.java b/src/com/wentch/redkale/net/icep/stun/StunPacket.java new file mode 100644 index 000000000..96436d5a8 --- /dev/null +++ b/src/com/wentch/redkale/net/icep/stun/StunPacket.java @@ -0,0 +1,51 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.net.icep.stun; + +import java.nio.*; +import java.util.*; + +/** + * + * @author zhangjx + */ +public class StunPacket { + + private StunHeader header; + + private final List attributes = new ArrayList<>(); + + public StunPacket(StunHeader header) { + this.header = header; + } + + public ByteBuffer encode(final ByteBuffer buffer) { + int start = buffer.position(); + header.encode(buffer); + final byte[] transactionid = header.getTransactionid(); + for (StunAttribute attr : attributes) { + attr.encode(buffer, transactionid); + } + int end = buffer.position(); + buffer.position(start + 2); + buffer.putShort((short) (end - start - 20)); + buffer.position(end); + return buffer; + } + + public void addAttribute(StunAttribute attribute) { + this.attributes.add(attribute); + } + + public StunHeader getHeader() { + return header; + } + + public List getAttributes() { + return attributes; + } + +}