All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.littleshoot.stun.stack.encoder.StunAttributeEncoder Maven / Gradle / Ivy

The newest version!
package org.littleshoot.stun.stack.encoder;

import java.math.BigInteger;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;

import org.littleshoot.mina.common.ByteBuffer;
import org.littleshoot.stun.stack.message.attributes.ErrorCodeAttribute;
import org.littleshoot.stun.stack.message.attributes.MappedAddressAttribute;
import org.littleshoot.stun.stack.message.attributes.StunAddressAttribute;
import org.littleshoot.stun.stack.message.attributes.StunAttribute;
import org.littleshoot.stun.stack.message.attributes.StunAttributeVisitor;
import org.littleshoot.stun.stack.message.attributes.ice.IceControlledAttribute;
import org.littleshoot.stun.stack.message.attributes.ice.IceControllingAttribute;
import org.littleshoot.stun.stack.message.attributes.ice.IcePriorityAttribute;
import org.littleshoot.stun.stack.message.attributes.ice.IceUseCandidateAttribute;
import org.littleshoot.stun.stack.message.attributes.turn.ConnectionStatus;
import org.littleshoot.stun.stack.message.attributes.turn.ConnectionStatusAttribute;
import org.littleshoot.stun.stack.message.attributes.turn.DataAttribute;
import org.littleshoot.stun.stack.message.attributes.turn.RelayAddressAttribute;
import org.littleshoot.stun.stack.message.attributes.turn.RemoteAddressAttribute;
import org.littleshoot.util.mina.MinaUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Writes STUN attributes.
 */
public class StunAttributeEncoder implements StunAttributeVisitor
    {

    private final Logger LOG = LoggerFactory.getLogger(getClass());
    
    private static final CharsetEncoder UTF_8_ENCODER =
        Charset.forName("UTF-8").newEncoder();
    
    private final ByteBuffer m_buf;

    /**
     * Creates a new class for writing STUN attributes.
     * 
     * @param buf The attribute buffer.
     */
    public StunAttributeEncoder(final ByteBuffer buf)
        {
        m_buf = buf;
        }
    
    public void visitData(final DataAttribute data)
        {
        writeHeader(data);
        final byte[] dataBytes = data.getData();
        m_buf.put(dataBytes);
        }
    
    public void visitConnectionStatus(final ConnectionStatusAttribute attribute)
        {
        LOG.debug("Writing connection status attribute: {}", attribute);
        writeHeader(attribute);
        final ConnectionStatus status = attribute.getConnectionStatus();
        MinaUtils.putUnsignedInt(m_buf, status.toLong());
        }

    public void visitIceControlled(final IceControlledAttribute attribute)
        {
        writeHeader(attribute);
        final byte[] tieBreaker = attribute.getTieBreaker();
        LOG.debug("Encoding controlled: {}", tieBreaker);
        m_buf.put(tieBreaker);
        }

    public void visitIceControlling(final IceControllingAttribute attribute)
        {
        writeHeader(attribute);
        final byte[] tieBreaker = attribute.getTieBreaker();
        LOG.debug("Encoding controlling: {}", tieBreaker);
        m_buf.put(tieBreaker);
        }

    public void visitIcePriority(final IcePriorityAttribute attribute)
        {
        writeHeader(attribute);
        final long priority = attribute.getPriority();
        MinaUtils.putUnsignedInt(m_buf, priority);
        }

    public void visitIceUseCandidate(final IceUseCandidateAttribute attribute)
        {
        writeHeader(attribute);
        // Note that USE-CANDIDATE doesn't have any body.
        }
    
    public void visiteErrorCode(final ErrorCodeAttribute attribute)
        {
        writeHeader(attribute);
        
        // The first 21 bits are zero-filled for 32 bit alignment.  We skip
        // the first 16 here so we can just write a full byte for the class
        // on the next call.
        m_buf.skip(2);
        MinaUtils.putUnsignedByte(this.m_buf, attribute.getErrorClass());
        MinaUtils.putUnsignedByte(this.m_buf, attribute.getErrorNumber());
        try
            {
            m_buf.putString(attribute.getReasonPhrase(), UTF_8_ENCODER);
            }
        catch (final CharacterCodingException e)
            {
            LOG.error("Could not encode reason phrase", e);
            throw new IllegalArgumentException(
                "Could not encode reason phrase", e);
            }
        }

    public void visitRelayAddress(final RelayAddressAttribute address)
        {
        visitAddressAttribute(address);
        }
    
    public void visitMappedAddress(final MappedAddressAttribute address)
        {
        visitAddressAttribute(address);
        }
    
    public void visitRemoteAddress(final RemoteAddressAttribute address)
        {
        visitAddressAttribute(address);
        }
    
    private void visitAddressAttribute(final StunAddressAttribute address)
        {
        writeHeader(address);

        // Now put the attribute body.
        
        // The first byte is ignored in address attributes.
        MinaUtils.putUnsignedByte(m_buf, 0x00);
        
        final int family = address.getAddressFamily();
        
        if (LOG.isDebugEnabled())
            {
            LOG.debug("Writing family: "+family);
            }
        final InetSocketAddress socketAddress = address.getInetSocketAddress();
        final int port = socketAddress.getPort();
        final InetAddress ia = socketAddress.getAddress();
        final byte[] addressBytes = ia.getAddress();
        MinaUtils.putUnsignedByte(m_buf, family);
        MinaUtils.putUnsignedShort(m_buf, port);
        m_buf.put(addressBytes);
        }

    private void writeHeader(final StunAttribute sa)
        {
        MinaUtils.putUnsignedShort(m_buf, sa.getAttributeType().toInt());
        MinaUtils.putUnsignedShort(m_buf, sa.getBodyLength());
        }
    }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy