/*
* 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.header;
import java.util.Iterator;
import java.util.TreeMap;
import de.javawi.jstun.attribute.MessageAttribute;
import de.javawi.jstun.attribute.MessageAttributeParsingException;
import de.javawi.jstun.util.Utility;
import de.javawi.jstun.util.UtilityException;
public class MessageHeader implements MessageHeaderInterface {
/*
* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | STUN
* Message Type | Message Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* Transaction ID
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
public static MessageHeader parseHeader(byte[] data)
throws MessageHeaderParsingException {
try {
final MessageHeader mh = new MessageHeader();
final byte[] typeArray = new byte[2];
System.arraycopy(data, 0, typeArray, 0, 2);
final int type = Utility.twoBytesToInteger(typeArray);
switch (type) {
case BINDINGREQUEST:
mh.setType(MessageHeaderType.BindingRequest);
break;
case BINDINGRESPONSE:
mh.setType(MessageHeaderType.BindingResponse);
break;
case BINDINGERRORRESPONSE:
mh.setType(MessageHeaderType.BindingErrorResponse);
break;
case SHAREDSECRETREQUEST:
mh.setType(MessageHeaderType.SharedSecretRequest);
break;
case SHAREDSECRETRESPONSE:
mh.setType(MessageHeaderType.SharedSecretResponse);
break;
case SHAREDSECRETERRORRESPONSE:
mh.setType(MessageHeaderType.SharedSecretErrorResponse);
break;
default:
throw new MessageHeaderParsingException("Message type " + type
+ "is not supported");
}
return mh;
} catch (final UtilityException ue) {
throw new MessageHeaderParsingException("Parsing error");
}
}
public static int typeToInteger(MessageHeaderType type) {
if (type == MessageHeaderType.BindingRequest) {
return BINDINGREQUEST;
}
if (type == MessageHeaderType.BindingResponse) {
return BINDINGRESPONSE;
}
if (type == MessageHeaderType.BindingErrorResponse) {
return BINDINGERRORRESPONSE;
}
if (type == MessageHeaderType.SharedSecretRequest) {
return SHAREDSECRETREQUEST;
}
if (type == MessageHeaderType.SharedSecretResponse) {
return SHAREDSECRETRESPONSE;
}
if (type == MessageHeaderType.SharedSecretErrorResponse) {
return SHAREDSECRETERRORRESPONSE;
}
return -1;
}
MessageHeaderType type;
byte[] id = new byte[16];
TreeMap<MessageAttribute.MessageAttributeType, MessageAttribute> ma = new TreeMap<MessageAttribute.MessageAttributeType, MessageAttribute>();
public MessageHeader() {
super();
}
public MessageHeader(MessageHeaderType type) {
super();
setType(type);
}
public void addMessageAttribute(MessageAttribute attri) {
ma.put(attri.getType(), attri);
}
public boolean equalTransactionID(MessageHeader header) {
final byte[] idHeader = header.getTransactionID();
if (idHeader.length != 16) {
return false;
}
if ((idHeader[0] == id[0]) && (idHeader[1] == id[1])
&& (idHeader[2] == id[2]) && (idHeader[3] == id[3])
&& (idHeader[4] == id[4]) && (idHeader[5] == id[5])
&& (idHeader[6] == id[6]) && (idHeader[7] == id[7])
&& (idHeader[8] == id[8]) && (idHeader[9] == id[9])
&& (idHeader[10] == id[10]) && (idHeader[11] == id[11])
&& (idHeader[12] == id[12]) && (idHeader[13] == id[13])
&& (idHeader[14] == id[14]) && (idHeader[15] == id[15])) {
return true;
} else {
return false;
}
}
public void generateTransactionID() throws UtilityException {
System.arraycopy(
Utility.integerToTwoBytes((int) (Math.random() * 65536)), 0,
id, 0, 2);
System.arraycopy(
Utility.integerToTwoBytes((int) (Math.random() * 65536)), 0,
id, 2, 2);
System.arraycopy(
Utility.integerToTwoBytes((int) (Math.random() * 65536)), 0,
id, 4, 2);
System.arraycopy(
Utility.integerToTwoBytes((int) (Math.random() * 65536)), 0,
id, 6, 2);
System.arraycopy(
Utility.integerToTwoBytes((int) (Math.random() * 65536)), 0,
id, 8, 2);
System.arraycopy(
Utility.integerToTwoBytes((int) (Math.random() * 65536)), 0,
id, 10, 2);
System.arraycopy(
Utility.integerToTwoBytes((int) (Math.random() * 65536)), 0,
id, 12, 2);
System.arraycopy(
Utility.integerToTwoBytes((int) (Math.random() * 65536)), 0,
id, 14, 2);
}
public byte[] getBytes() throws UtilityException {
int length = 20;
Iterator<MessageAttribute.MessageAttributeType> it = ma.keySet()
.iterator();
while (it.hasNext()) {
final MessageAttribute attri = ma.get(it.next());
length += attri.getLength();
}
// add attribute size + attributes.getSize();
final byte[] result = new byte[length];
System.arraycopy(Utility.integerToTwoBytes(typeToInteger(type)), 0,
result, 0, 2);
System.arraycopy(Utility.integerToTwoBytes(length - 20), 0, result, 2,
2);
System.arraycopy(id, 0, result, 4, 16);
// arraycopy of attributes
int offset = 20;
it = ma.keySet().iterator();
while (it.hasNext()) {
final MessageAttribute attri = ma.get(it.next());
System.arraycopy(attri.getBytes(), 0, result, offset,
attri.getLength());
offset += attri.getLength();
}
return result;
}
public int getLength() throws UtilityException {
return getBytes().length;
}
public MessageAttribute getMessageAttribute(
MessageAttribute.MessageAttributeType type) {
return ma.get(type);
}
public byte[] getTransactionID() {
final byte[] idCopy = new byte[id.length];
System.arraycopy(id, 0, idCopy, 0, id.length);
return idCopy;
}
public MessageHeaderType getType() {
return type;
}
public void parseAttributes(byte[] data)
throws MessageAttributeParsingException {
try {
final byte[] lengthArray = new byte[2];
System.arraycopy(data, 2, lengthArray, 0, 2);
int length = Utility.twoBytesToInteger(lengthArray);
System.arraycopy(data, 4, id, 0, 16);
byte[] cuttedData;
int offset = 20;
while (length > 0) {
cuttedData = new byte[length];
System.arraycopy(data, offset, cuttedData, 0, length);
final MessageAttribute ma = MessageAttribute
.parseCommonHeader(cuttedData);
addMessageAttribute(ma);
length -= ma.getLength();
offset += ma.getLength();
}
} catch (final UtilityException ue) {
throw new MessageAttributeParsingException("Parsing error");
}
}
public void setTransactionID(byte[] id) {
System.arraycopy(id, 0, this.id, 0, 16);
}
public void setType(MessageHeaderType type) {
this.type = type;
}
}