/* * * Code derived and adapted from the Jitsi client side STUN framework. * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.restcomm.media.stun.messages.attributes.address; import org.restcomm.media.network.deprecated.TransportAddress; import org.restcomm.media.network.deprecated.TransportAddress.TransportProtocol; import org.restcomm.media.stun.StunException; import org.restcomm.media.stun.messages.attributes.StunAttribute; /** * This class is used to represent Stun attributes that contain an address. Such * attributes are: *<p> * MAPPED-ADDRESS <br/> * RESPONSE-ADDRESS <br/> * SOURCE-ADDRESS <br/> * CHANGED-ADDRESS <br/> * REFLECTED-FROM <br/> * ALTERNATE-SERVER <br/> * XOR-PEER-ADDRESS <br/> * XOR-RELAYED-ADDRESS <br/> *</p> *<p> * The different attributes are distinguished by the attributeType of * org.ice4j.attribute.Attribute. *</p> *<p> * Address attributes indicate the mapped IP address and * port. They consist of an eight bit address family, and a sixteen bit * port, followed by a fixed length value representing the IP address. *<code> * 0 1 2 3 <br/> * 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 <br/> * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ <br/> * |x x x x x x x x| Family | Port | <br/> * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ <br/> * | Address | <br/> * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ <br/> * <br/> * </p> * <p> * The port is a network byte ordered representation of the mapped port. * The address family is always 0x01, corresponding to IPv4. The first * 8 bits of the MAPPED-ADDRESS are ignored, for the purposes of * aligning parameters on natural boundaries. The IPv4 address is 32 * bits. * </p> */ public abstract class AddressAttribute extends StunAttribute { protected static final byte ADDRESS_FAMILY_IPV4 = 0x01; protected static final byte ADDRESS_FAMILY_IPV6 = 0x02; private static final char DATA_LENGTH_FOR_IPV4 = (char) 8; private static final char DATA_LENGTH_FOR_IPV6 = (char) 20; protected TransportAddress transportAddress; protected AddressAttribute(char attributeType) { super(attributeType); if (!isTypeValid(attributeType)) { throw new IllegalArgumentException("Invalid STUN attribut type: " + (int) attributeType); } } /** * Verifies that type is a valid address attribute type. * * @param type * the type to test * @return true if the type is a valid address attribute type and false * otherwise */ private boolean isTypeValid(char type) { return (type == MAPPED_ADDRESS || type == RESPONSE_ADDRESS || type == SOURCE_ADDRESS || type == CHANGED_ADDRESS || type == REFLECTED_FROM || type == XOR_MAPPED_ADDRESS || type == ALTERNATE_SERVER || type == XOR_PEER_ADDRESS || type == XOR_RELAYED_ADDRESS || type == DESTINATION_ADDRESS); } @Override protected void setAttributeType(char type) { if (!isTypeValid(type)) { throw new IllegalArgumentException("Invalid STUN attribut type: " + (int) type); } super.setAttributeType(type); } public TransportAddress getAddress() { return this.transportAddress; } public void setAddress(TransportAddress address) { this.transportAddress = address; } public int getPort() { return this.transportAddress.getPort(); } /** * Returns the bytes of the address. * * @return the <tt>byte[]</tt> array containing the address. */ public byte[] getAddressBytes() { return this.transportAddress.getAddressBytes(); } /** * Returns the family that the this.address belongs to. * * @return the family that the this.address belongs to. */ public byte getFamily() { if (this.transportAddress.isIPv4()) { return ADDRESS_FAMILY_IPV4; } return ADDRESS_FAMILY_IPV6; } public char getDataLength() { if (getFamily() == ADDRESS_FAMILY_IPV4) { return DATA_LENGTH_FOR_IPV4; } return DATA_LENGTH_FOR_IPV6; } public byte[] encode() { char type = getAttributeType(); byte binValue[] = new byte[HEADER_LENGTH + getDataLength()]; // Type binValue[0] = (byte) (type >> 8); binValue[1] = (byte) (type & 0x00FF); // Length binValue[2] = (byte) (getDataLength() >> 8); binValue[3] = (byte) (getDataLength() & 0x00FF); // Not used binValue[4] = 0x00; // Family binValue[5] = getFamily(); // port binValue[6] = (byte) (getPort() >> 8); binValue[7] = (byte) (getPort() & 0x00FF); // address if (this.transportAddress.isIPv4()) { System.arraycopy(getAddressBytes(), 0, binValue, 8, 4); } else { System.arraycopy(getAddressBytes(), 0, binValue, 8, 16); } return binValue; } @Override protected void decodeAttributeBody(byte[] attributeValue, char offset, char length) throws StunException { // Skip through padding offset++; byte family = attributeValue[offset++]; char port = ((char) ((attributeValue[offset++] << 8) | (attributeValue[offset++] & 0xFF))); int addressLength = (family == ADDRESS_FAMILY_IPV4) ? 4 : 16; byte addressData[] = new byte[addressLength]; System.arraycopy(attributeValue, offset, addressData, 0, addressLength); TransportAddress address = new TransportAddress( new String(addressData), port, TransportProtocol.UDP); setAddress(address); } /** * Compares two STUN Attributes. Attributes are considered equal when their * type, length, and all data are the same. * * @param other * the object to compare this attribute with. * @return true if the attributes are equal and false otherwise. */ public boolean equals(Object other) { if (other == null || !(other instanceof AddressAttribute)) { return false; } if (other == this) { return true; } AddressAttribute att = (AddressAttribute) other; if (att.getAttributeType() != getAttributeType() || att.getDataLength() != getDataLength() || att.getFamily() != getFamily() || (att.getAddress() != null && !this.transportAddress .equals(att.getAddress()))) { return false; } if (att.getAddress() == null && getAddress() == null) { return true; } return true; } }