package org.xmpp.jnodes.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.util.Random;
public class PublicIPResolver {
final static byte BINDING_REQUEST_ID = 0x0001;
final static int MAPPED_ADDRESS = 0x0001;
final static byte CHANGE_REQUEST_NO_CHANGE[] = {0, 3, 0, 4, 0, 0, 0, 0};
final static Random r = new Random(System.nanoTime());
private static byte[] getHeader(final int contentLenght) {
final byte header[] = new byte[20];
header[0] = 0;
header[1] = BINDING_REQUEST_ID;
header[2] = 0;
header[3] = (byte) contentLenght;
header[4] = (byte) (r.nextInt(9));
header[5] = (byte) (r.nextInt(8));
header[6] = (byte) (r.nextInt(7));
header[7] = (byte) (r.nextInt(6));
return header;
}
public static ByteBuffer createSTUNChangeRequest() {
final byte header[] = getHeader(CHANGE_REQUEST_NO_CHANGE.length);
final byte data[] = new byte[header.length + CHANGE_REQUEST_NO_CHANGE.length];
System.arraycopy(header, 0, data, 0, header.length);
System.arraycopy(CHANGE_REQUEST_NO_CHANGE, 0, data, header.length, CHANGE_REQUEST_NO_CHANGE.length);
return ByteBuffer.wrap(data);
}
public static Header parseResponse(byte[] data) {
byte[] lengthArray = new byte[2];
System.arraycopy(data, 2, lengthArray, 0, 2);
int length = unsignedShortToInt(lengthArray);
byte[] cuttedData;
int offset = 20;
while (length > 0) {
cuttedData = new byte[length];
System.arraycopy(data, offset, cuttedData, 0, length);
Header h = parseHeader(cuttedData);
if (h.getType() == MAPPED_ADDRESS) {
return h;
}
length -= h.getLength();
offset += h.getLength();
}
return null;
}
private static Header parseHeader(byte[] data) {
byte[] typeArray = new byte[2];
System.arraycopy(data, 0, typeArray, 0, 2);
int type = unsignedShortToInt(typeArray);
byte[] lengthArray = new byte[2];
System.arraycopy(data, 2, lengthArray, 0, 2);
int lengthValue = unsignedShortToInt(lengthArray);
byte[] valueArray = new byte[lengthValue];
System.arraycopy(data, 4, valueArray, 0, lengthValue);
if (data.length >= 8) {
int family = unsignedByteToInt(valueArray[1]);
if (family == 1) {
byte[] portArray = new byte[2];
System.arraycopy(valueArray, 2, portArray, 0, 2);
int port = unsignedShortToInt(portArray);
int firstOctet = unsignedByteToInt(valueArray[4]);
int secondOctet = unsignedByteToInt(valueArray[5]);
int thirdOctet = unsignedByteToInt(valueArray[6]);
int fourthOctet = unsignedByteToInt(valueArray[7]);
final StringBuilder ip = new StringBuilder().append(firstOctet).append(".").append(secondOctet).append(".").append(thirdOctet).append(".").append(fourthOctet);
return new Header(new InetSocketAddress(ip.toString(), port), type, lengthValue + 4);
}
}
return new Header(null, -1, lengthValue + 4);
}
public static int unsignedShortToInt(final byte[] b) {
int a = b[0] & 0xFF;
int aa = b[1] & 0xFF;
return ((a << 8) + aa);
}
public static int unsignedByteToInt(byte b) {
return (int) b & 0xFF;
}
public static class Header {
final InetSocketAddress address;
final int type;
final int length;
public Header(final InetSocketAddress address, int type, int length) {
this.address = address;
this.type = type;
this.length = length;
}
public int getType() {
return type;
}
public InetSocketAddress getAddress() {
return address;
}
public int getLength() {
return length;
}
}
public static InetSocketAddress getPublicAddress(final String stunServer, final int port) {
int lport = 10002;
for (int t = 0; t < 3; t++) {
try {
final SelDatagramChannel channel = SelDatagramChannel.open(null, new InetSocketAddress(System.getProperty("os.name")!=null&&System.getProperty("os.name").toLowerCase().indexOf("win") > -1 ? LocalIPResolver.getLocalIP() : "0.0.0.0", lport));
return getPublicAddress(channel, stunServer, port);
} catch (IOException e) {
lport += r.nextInt(10) + 1;
}
}
return null;
}
public static InetSocketAddress getPublicAddress(final SelDatagramChannel channel, final String stunServer, final int port) {
final Header[] h = new Header[1];
try {
channel.setDatagramListener(new DatagramListener() {
public void datagramReceived(SelDatagramChannel channel, ByteBuffer buffer, SocketAddress address) {
final byte b[] = new byte[buffer.position()];
buffer.rewind();
buffer.get(b, 0, b.length);
h[0] = parseResponse(b);
}
});
channel.send(createSTUNChangeRequest(), new InetSocketAddress(stunServer, port));
Thread.sleep(100);
for (int i = 0; i < 5; i++) {
Thread.sleep(100);
if (h[0] != null) {
return h[0].getAddress();
}
if (i % 2 == 0) {
channel.send(createSTUNChangeRequest(), new InetSocketAddress(stunServer, port));
}
}
return null;
} catch (IOException e) {
return null;
} catch (InterruptedException e) {
return null;
}
}
}