This commit is contained in:
地平线
2015-09-02 19:35:46 +08:00
parent df0c5c9727
commit 4419629c71
12 changed files with 622 additions and 0 deletions

View File

@@ -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);
}
}

View File

@@ -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<ByteBuffer> bufferPool,
ObjectPool<Response> 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();
}
}

View File

@@ -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);
}
}

View File

@@ -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<IcepRequest, IcepResponse> {
private final HashMap<Integer, IcepServlet> 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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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<IcepRequest> {
protected IcepResponse(Context context, IcepRequest request) {
super(context, request);
}
public static ObjectPool<Response> createPool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator<Response> creator) {
return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x) -> ((IcepResponse) x).prepare(), (x) -> ((IcepResponse) x).recycle());
}
}

View File

@@ -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<ByteBuffer> 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<Response> 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;
}
}

View File

@@ -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<IcepRequest, IcepResponse> {
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();
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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<StunAttribute> 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<StunAttribute> getAttributes() {
return attributes;
}
}