/*
* This file is part of JSTUN.
*
* Copyright (c) 2005 Thomas King <king@t-king.de> - All rights
* reserved.
*
* This software is licensed under either the GNU Public License (GPL),
* or the Apache 2.0 license. Copies of both license agreements are
* included in this distribution.
*/
package de.javawi.jstun.test;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.Timer;
import java.util.TimerTask;
import de.javawi.jstun.attribute.ChangeRequest;
import de.javawi.jstun.attribute.ErrorCode;
import de.javawi.jstun.attribute.MappedAddress;
import de.javawi.jstun.attribute.MessageAttribute;
import de.javawi.jstun.attribute.MessageAttributeException;
import de.javawi.jstun.attribute.MessageAttributeParsingException;
import de.javawi.jstun.attribute.ResponseAddress;
import de.javawi.jstun.header.MessageHeader;
import de.javawi.jstun.header.MessageHeaderParsingException;
import de.javawi.jstun.util.UtilityException;
public class BindingLifetimeTest {
class BindingLifetimeTask extends TimerTask {
public BindingLifetimeTask() {
super();
}
public void lifetimeQuery() throws UtilityException,
MessageAttributeException, MessageHeaderParsingException,
MessageAttributeParsingException, IOException {
try {
final DatagramSocket socket = new DatagramSocket();
socket.connect(InetAddress.getByName(stunServer), port);
socket.setSoTimeout(timeout);
final MessageHeader sendMH = new MessageHeader(
MessageHeader.MessageHeaderType.BindingRequest);
sendMH.generateTransactionID();
final ChangeRequest changeRequest = new ChangeRequest();
final ResponseAddress responseAddress = new ResponseAddress();
responseAddress.setAddress(ma.getAddress());
responseAddress.setPort(ma.getPort());
sendMH.addMessageAttribute(changeRequest);
sendMH.addMessageAttribute(responseAddress);
final byte[] data = sendMH.getBytes();
final DatagramPacket send = new DatagramPacket(data,
data.length, InetAddress.getByName(stunServer), port);
socket.send(send);
MessageHeader receiveMH = new MessageHeader();
while (!(receiveMH.equalTransactionID(sendMH))) {
final DatagramPacket receive = new DatagramPacket(
new byte[200], 200);
initialSocket.receive(receive);
receiveMH = MessageHeader.parseHeader(receive.getData());
receiveMH.parseAttributes(receive.getData());
}
final ErrorCode ec = (ErrorCode) receiveMH
.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
if (ec != null) {
return;
}
if (upperBinarySearchLifetime == (lowerBinarySearchLifetime + 1)) {
completed = true;
return;
}
lifetime = binarySearchLifetime;
lowerBinarySearchLifetime = binarySearchLifetime;
binarySearchLifetime = (upperBinarySearchLifetime + lowerBinarySearchLifetime) / 2;
if (binarySearchLifetime > 0) {
final BindingLifetimeTask task = new BindingLifetimeTask();
timer.schedule(task, binarySearchLifetime);
} else {
completed = true;
}
} catch (final SocketTimeoutException ste) {
if (upperBinarySearchLifetime == (lowerBinarySearchLifetime + 1)) {
completed = true;
return;
}
upperBinarySearchLifetime = binarySearchLifetime;
binarySearchLifetime = (upperBinarySearchLifetime + lowerBinarySearchLifetime) / 2;
if (binarySearchLifetime > 0) {
if (bindingCommunicationInitialSocket()) {
return;
}
final BindingLifetimeTask task = new BindingLifetimeTask();
timer.schedule(task, binarySearchLifetime);
} else {
completed = true;
}
}
}
@Override
public void run() {
try {
lifetimeQuery();
} catch (final Exception e) {
e.printStackTrace();
}
}
}
String stunServer;
int port;
int timeout = 300; // ms
MappedAddress ma;
Timer timer;
DatagramSocket initialSocket;
// start value for binary search - should be carefully choosen
int upperBinarySearchLifetime = 345000; // ms
int lowerBinarySearchLifetime = 0;
int binarySearchLifetime = (upperBinarySearchLifetime + lowerBinarySearchLifetime) / 2;
// lifetime value
int lifetime = -1; // -1 means undefined.
boolean completed = false;
public BindingLifetimeTest(String stunServer, int port) {
super();
this.stunServer = stunServer;
this.port = port;
timer = new Timer(true);
}
private boolean bindingCommunicationInitialSocket()
throws UtilityException, IOException,
MessageHeaderParsingException, MessageAttributeParsingException {
final MessageHeader sendMH = new MessageHeader(
MessageHeader.MessageHeaderType.BindingRequest);
sendMH.generateTransactionID();
final ChangeRequest changeRequest = new ChangeRequest();
sendMH.addMessageAttribute(changeRequest);
final byte[] data = sendMH.getBytes();
final DatagramPacket send = new DatagramPacket(data, data.length,
InetAddress.getByName(stunServer), port);
initialSocket.send(send);
MessageHeader receiveMH = new MessageHeader();
while (!(receiveMH.equalTransactionID(sendMH))) {
final DatagramPacket receive = new DatagramPacket(new byte[200],
200);
initialSocket.receive(receive);
receiveMH = MessageHeader.parseHeader(receive.getData());
receiveMH.parseAttributes(receive.getData());
}
ma = (MappedAddress) receiveMH
.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
final ErrorCode ec = (ErrorCode) receiveMH
.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
if (ec != null) {
return true;
}
if (ma == null) {
return true;
}
return false;
}
public int getLifetime() {
return lifetime;
}
public boolean isCompleted() {
return completed;
}
public void setUpperBinarySearchLifetime(int upperBinarySearchLifetime) {
this.upperBinarySearchLifetime = upperBinarySearchLifetime;
binarySearchLifetime = (upperBinarySearchLifetime + lowerBinarySearchLifetime) / 2;
}
public void test() throws UtilityException, SocketException,
UnknownHostException, IOException,
MessageAttributeParsingException, MessageAttributeException,
MessageHeaderParsingException {
initialSocket = new DatagramSocket();
initialSocket.connect(InetAddress.getByName(stunServer), port);
initialSocket.setSoTimeout(timeout);
if (bindingCommunicationInitialSocket()) {
return;
}
final BindingLifetimeTask task = new BindingLifetimeTask();
timer.schedule(task, binarySearchLifetime);
}
}