/* * * 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; import org.restcomm.media.stun.StunException; import org.restcomm.media.stun.messages.attributes.address.AlternateServerAttribute; import org.restcomm.media.stun.messages.attributes.address.ChangedAddressAttribute; import org.restcomm.media.stun.messages.attributes.address.ReflectedFromAttribute; import org.restcomm.media.stun.messages.attributes.address.ResponseAddressAttribute; import org.restcomm.media.stun.messages.attributes.address.SourceAddressAttribute; import org.restcomm.media.stun.messages.attributes.address.XorMappedAddressAttribute; import org.restcomm.media.stun.messages.attributes.address.XorPeerAddressAttribute; import org.restcomm.media.stun.messages.attributes.address.XorRelayedAddressAttribute; import org.restcomm.media.stun.messages.attributes.control.ControlledAttribute; import org.restcomm.media.stun.messages.attributes.control.ControllingAttribute; import org.restcomm.media.stun.messages.attributes.data.DataAttribute; import org.restcomm.media.stun.messages.attributes.general.ChangeRequestAttribute; import org.restcomm.media.stun.messages.attributes.general.ChannelNumberAttribute; import org.restcomm.media.stun.messages.attributes.general.DontFragmentAttribute; import org.restcomm.media.stun.messages.attributes.general.ErrorCodeAttribute; import org.restcomm.media.stun.messages.attributes.general.EvenPortAttribute; import org.restcomm.media.stun.messages.attributes.general.FingerprintAttribute; import org.restcomm.media.stun.messages.attributes.general.LifetimeAttribute; import org.restcomm.media.stun.messages.attributes.general.MappedAddressAttribute; import org.restcomm.media.stun.messages.attributes.general.MessageIntegrityAttribute; import org.restcomm.media.stun.messages.attributes.general.NonceAttribute; import org.restcomm.media.stun.messages.attributes.general.OptionalAttribute; import org.restcomm.media.stun.messages.attributes.general.PriorityAttribute; import org.restcomm.media.stun.messages.attributes.general.RealmAttribute; import org.restcomm.media.stun.messages.attributes.general.RequestedTransportAttribute; import org.restcomm.media.stun.messages.attributes.general.ReservationTokenAttribute; import org.restcomm.media.stun.messages.attributes.general.SoftwareAttribute; import org.restcomm.media.stun.messages.attributes.general.UnknownAttributesAttribute; import org.restcomm.media.stun.messages.attributes.general.UseCandidateAttribute; import org.restcomm.media.stun.messages.attributes.general.UsernameAttribute; import org.restcomm.media.stun.messages.attributes.general.XorOnlyAttribute; /** * Provides utilities for decoding a binary stream into an Stun Attribute * * @see StunAttribute */ public class StunAttributeDecoder { /** * Decodes the specified binary array and returns the corresponding * attribute object. * * @param bytes * the binary array that should be decoded. * @param offset * the index where the message starts. * @param length * the number of bytes that the message is long. * * @return An object representing the attribute encoded in bytes or null if * the attribute was not recognized. * * @throws StunException * if bytes is not a valid STUN attribute. */ public static StunAttribute decode(byte[] bytes, char offset, char length) throws StunException { if (bytes == null || bytes.length < StunAttribute.HEADER_LENGTH) { throw new StunException(StunException.ILLEGAL_ARGUMENT, "Could not decode the specified binary array."); } // Discover attribute type char attributeType = (char) ((bytes[offset] << 8) | bytes[offset + 1]); int len1 = bytes[offset + 2] & 0xff; int len2 = bytes[offset + 3] & 0xff; char attributeLength = (char) ((len1 << 8) | len2); if (attributeLength > bytes.length - offset) throw new StunException(StunException.ILLEGAL_ARGUMENT, "Could not decode the specified binary array."); StunAttribute decodedAttribute = null; switch (attributeType) { /* STUN attributes */ case StunAttribute.CHANGE_REQUEST: decodedAttribute = new ChangeRequestAttribute(); break; case StunAttribute.CHANGED_ADDRESS: decodedAttribute = new ChangedAddressAttribute(); break; case StunAttribute.MAPPED_ADDRESS: decodedAttribute = new MappedAddressAttribute(); break; case StunAttribute.ERROR_CODE: decodedAttribute = new ErrorCodeAttribute(); break; case StunAttribute.MESSAGE_INTEGRITY: decodedAttribute = new MessageIntegrityAttribute(); break; // case StunAttribute.PASSWORD: // handle as an unknown attribute case StunAttribute.REFLECTED_FROM: decodedAttribute = new ReflectedFromAttribute(); break; case StunAttribute.RESPONSE_ADDRESS: decodedAttribute = new ResponseAddressAttribute(); break; case StunAttribute.SOURCE_ADDRESS: decodedAttribute = new SourceAddressAttribute(); break; case StunAttribute.UNKNOWN_ATTRIBUTES: decodedAttribute = new UnknownAttributesAttribute(); break; case StunAttribute.XOR_MAPPED_ADDRESS: decodedAttribute = new XorMappedAddressAttribute(); break; case StunAttribute.XOR_ONLY: decodedAttribute = new XorOnlyAttribute(); break; case StunAttribute.SOFTWARE: decodedAttribute = new SoftwareAttribute(); break; case StunAttribute.USERNAME: decodedAttribute = new UsernameAttribute(); break; case StunAttribute.REALM: decodedAttribute = new RealmAttribute(); break; case StunAttribute.NONCE: decodedAttribute = new NonceAttribute(); break; case StunAttribute.FINGERPRINT: decodedAttribute = new FingerprintAttribute(); break; case StunAttribute.ALTERNATE_SERVER: decodedAttribute = new AlternateServerAttribute(); break; case StunAttribute.CHANNEL_NUMBER: decodedAttribute = new ChannelNumberAttribute(); break; case StunAttribute.LIFETIME: decodedAttribute = new LifetimeAttribute(); break; case StunAttribute.XOR_PEER_ADDRESS: decodedAttribute = new XorPeerAddressAttribute(); break; case StunAttribute.DATA: decodedAttribute = new DataAttribute(); break; case StunAttribute.XOR_RELAYED_ADDRESS: decodedAttribute = new XorRelayedAddressAttribute(); break; case StunAttribute.EVEN_PORT: decodedAttribute = new EvenPortAttribute(); break; case StunAttribute.REQUESTED_TRANSPORT: decodedAttribute = new RequestedTransportAttribute(); break; case StunAttribute.DONT_FRAGMENT: decodedAttribute = new DontFragmentAttribute(); break; case StunAttribute.RESERVATION_TOKEN: decodedAttribute = new ReservationTokenAttribute(); break; case StunAttribute.PRIORITY: decodedAttribute = new PriorityAttribute(); break; case StunAttribute.ICE_CONTROLLING: decodedAttribute = new ControllingAttribute(); break; case StunAttribute.ICE_CONTROLLED: decodedAttribute = new ControlledAttribute(); break; case StunAttribute.USE_CANDIDATE: decodedAttribute = new UseCandidateAttribute(); break; // According to rfc3489 we should silently ignore unknown attributes. default: decodedAttribute = new OptionalAttribute( StunAttribute.UNKNOWN_OPTIONAL_ATTRIBUTE); break; } decodedAttribute.setAttributeType(attributeType); decodedAttribute.setLocationInMessage(offset); decodedAttribute.decodeAttributeBody(bytes, (char) (StunAttribute.HEADER_LENGTH + offset), attributeLength); return decodedAttribute; } }