Source code: RadiusMessageBuffer.java

index |  440 lines ]

package nl.west.aaa.radius;

import java.io.*;
import java.util.*;

class RadiusMessageBuffer
{

    private static int DEFAULTBUFFERSIZE=1024;
    
    private static int SPECIALVENDOR=
        (('w'&0xff)<<24)|(('e'&0xff)<<16)|(('s'&0xff)<<8)|('t'&0xff);
    
    private byte[] buffer;
    private int idx;
    
    /**
     * Make a new empty RadiusMessagebuffer.
     * No fields have been set and the buffer must be filled
     * before sending.
     */
    public RadiusMessageBuffer()
    {
        buffer=new byte[DEFAULTBUFFERSIZE];
        idx=20;
        buffer[2]=(byte)((idx>>8)&0xff);
        buffer[3]=(byte)(idx&0xff);
    }
    
    /**
     * Beging building a new buffer.
     * authenticator.length=16
     */
    public RadiusMessageBuffer(int code,int id,byte[] authenticator)
    {
        buffer=new byte[DEFAULTBUFFERSIZE];
        buffer[0]=(byte)code;
        buffer[1]=(byte)id;
        System.arraycopy(authenticator,0,buffer,4,16);
        idx=20;
        buffer[2]=(byte)((idx>>8)&0xff);
        buffer[3]=(byte)(idx&0xff);
    }
    
    /**
     * Make a new MessageBuffer based on the data given.
     */
    public RadiusMessageBuffer(byte[] data)
    {
        buffer=data;
        idx=20;
    }
    
    public synchronized void setMessageCode(int code)
    {
        buffer[0]=(byte)code;
    }
    
    public synchronized void setMessageId(int id)
    {
        buffer[1]=(byte)id;
    }
    
    public synchronized void setMessageAuthenticator(byte[] authenticator)
    {
        System.arraycopy(authenticator,0,buffer,4,16);
    }
        
    public synchronized void addTextAVP(int type,String text)
    {
        try 
        {
            addDataAVP(type,text.getBytes("UTF8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public synchronized void addStringAVP(int type,String string)
    {
        try 
        {
            addDataAVP(type,string.getBytes("ASCII"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public synchronized void addAddressAVP(int type,String address)
    {
        StringTokenizer st=new StringTokenizer(address,".");
        byte[] a=new byte[4];
        a[0]=(byte)Integer.parseInt(st.nextToken());
        a[1]=(byte)Integer.parseInt(st.nextToken());
        a[2]=(byte)Integer.parseInt(st.nextToken());
        a[3]=(byte)Integer.parseInt(st.nextToken());
        addDataAVP(type,a);
    }

    public synchronized void addIntegerAVP(int type,int i)
    {
        byte[] n=new byte[4];
        n[0]=(byte)((idx>>24)&0xff);
        n[1]=(byte)((idx>>16)&0xff);
        n[2]=(byte)((idx>>8)&0xff);
        n[3]=(byte)(idx&0xff);
        addDataAVP(type,n);
    }

    public synchronized void addDataAVP(int type,byte[] data)
    {
        if(buffer.length<idx+data.length+3)
        {
            byte[] tmp=new byte[(int)(2+buffer.length*1.8)];
            System.arraycopy(buffer,0,tmp,0,buffer.length);
            buffer=tmp;
        }
        buffer[idx++]=(byte)type;
        buffer[idx++]=(byte)(data.length+2);
        System.arraycopy(data,0,buffer,idx,data.length);
        idx+=data.length;
        // idx=length save msb first (save length)
        buffer[2]=(byte)((idx>>8)&0xff);
        buffer[3]=(byte)(idx&0xff);
    }
    
    public synchronized void addVendorAVP(int vendor,byte[] data)
    {
        byte[] tmp=new byte[data.length+4];
        tmp[0]=(byte)((vendor>>24)&0xff);
        tmp[1]=(byte)((vendor>>16)&0xff);
        tmp[2]=(byte)((vendor>>8)&0xff);
        tmp[3]=(byte)(vendor&0xff);
        System.arraycopy(data,0,tmp,4,data.length);
        addDataAVP(RadiusFormat.AVP_VENDOR_SPECIFIC,tmp);
    }

/*
    // NOT USED    
    public synchronized void addVendorAVP(int vendor,int vendorType,
                int vendorLength,byte[] data)
    {
        byte[] tmp=new byte[data.length+2];
        tmp[0]=(byte)vendorType;
        tmp[1]=(byte)vendorLength;
        System.arraycopy(data,0,tmp,2,data.length);
        addVendorAVP(vendor,tmp);
    }
*/
    
    /**
     * Dirty hack to send arbitrary attributes in RADIUS useing
     * the vendor-specific attribute.
     */
    public synchronized void addSpecialAVP(String name,String value)
    {
        /*
        
       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
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |     Type      |  Length       |            Vendor-Id
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           Vendor-Id (cont)           | AVP name len  |  AVP name...
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |    AVP value...          
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
        
        */
        try 
        {
            byte[] nm=name.getBytes("UTF8");
            byte[] vl=value.getBytes("UTF8");
            byte[] tmp=new byte[nm.length+vl.length+1];
            tmp[0]=(byte)nm.length;
            System.arraycopy(nm,0,tmp,1,nm.length);
            System.arraycopy(vl,0,tmp,1+nm.length,vl.length);
            addVendorAVP(SPECIALVENDOR,tmp);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    /**
     * Return the whole message as a byte array.
     */
    public byte[] getBytes()
    {
        int len=getMessageLength();
        byte[] res=new byte[len];
        System.arraycopy(buffer,0,res,0,len);
        return res;
    }
    
    /**
     * Get the RADIUS code to identify the type
     * of message.
     */
    public int getMessageCode()
    {
        return buffer[0]&0xff;
    }
    
    /**
     * Get the message-id.
     */
    public int getMessageId()
    {
        return buffer[1]&0xff;
    }
    
    /**
     * Get the length of the message.
     */
    public int getMessageLength()
    {
        return ((buffer[2]&0xff)<<8)|(buffer[3]&0xff);
    }
    
    /**
     * Get the message authenticator.
     */
    public byte[] getMessageAuthenticator()
    {
        byte[] tmp=new byte[16];
        System.arraycopy(buffer,4,tmp,0,16);
        return tmp;
    }
    
    /**
     * Reset the read-write position to the first
     * AVP.
     */
    public synchronized void setFirstAVP()
    {
        idx=20;
    }

    /**
     * Check if more AVPs are available from this point.
     */        
    public boolean hasMoreAVPs()
    {
        return idx<getMessageLength();
    }
    
    /**
     * Move the read-write pointer to the next AVP.
     */
    public synchronized void nextAVP()
    {
        int len=buffer[idx+1]&0xff;
        idx+=len;
    }

    /**
     * Get the AVP Code.
     */
    public synchronized int getAVPCode()
    {
        return buffer[idx]&0xff;
    }
    
    /**
     * Get the AVP data, assuming it is text.
     */
    public synchronized String getAVPText()
    {
        try 
        {
            return new String(getAVPData(),"UTF8");
        } 
        catch (UnsupportedEncodingException e) 
        {
            e.printStackTrace();
            return null;
        }
    }        

    /**
     * Get the AVP data, assuming it is a string.
     */
    public synchronized String getAVPString()
    {
        try
        {
            return new String(getAVPData(),"ASCII");
        } 
        catch (UnsupportedEncodingException e) 
        {
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * Get the AVP data, assuming it is a address.
     * Returns the string representation of the address.
     * If the length of the data indicates that this cannot
     * be an address, an Exception is thrown.
     * <br>
     * This method returns a string insteadof an InetAddress because
     * InetAddress suggests that the host exists and this method
     * could return 0.0.0.0 or 255.255.255.255 (which are valid
     * values, but may not be valid real addresses).
     */
    public synchronized String getAVPAddress()
    {
        byte[] tmp=getAVPData();
        if(tmp.length!=4)
            throw new RuntimeException("Attribute is not an address");
        return (tmp[0]&0xff)+"."+
               (tmp[1]&0xff)+"."+
               (tmp[2]&0xff)+"."+
               (tmp[3]&0xff);
    }
    
    /**
     * Get the AVP data, assuming it is an integer.
     * The result is an unsigned integer.
     * If the length indicates the data cannot be an integer
     * an exception is thrown.
     */
    public synchronized int getAVPInteger()
    {
        byte[] tmp=getAVPData();
        if(tmp.length!=4)
            throw new RuntimeException("Attribute is not an integer");
        int res=tmp[0]&0xff;
        res=(res<<8)|tmp[1]&0xff;
        res=(res<<8)|tmp[2]&0xff;
        res=(res<<8)|tmp[3]&0xff;
        return res;
    }
    
    /**
     * Get the AVP data, assuming it is an integer.
     * The results is a signed integer. (probably never used)
     * If the length indicates the data cannot be an integer
     * an exception is thrown.
     */
    public synchronized int getAVPSignedInteger()
    {
        byte[] tmp=getAVPData();
        if(tmp.length!=4)
            throw new RuntimeException("Attribute is not an integer");
        int res=tmp[0];
        res=(res<<8)|tmp[1]&0xff;
        res=(res<<8)|tmp[2]&0xff;
        res=(res<<8)|tmp[3]&0xff;
        return res;
    }
    
    /**
     * Get the raw AVP data.
     */
    public synchronized byte[] getAVPData()
    {
        int len=(buffer[idx+1]&0xff)-2;
        byte[] tmp=new byte[len];
        System.arraycopy(buffer,idx+2,tmp,0,len);
        return tmp;
    }
    
    public synchronized int getAVPVendorID()
    {
        if(getAVPCode()!=RadiusFormat.AVP_VENDOR_SPECIFIC)
            throw new RuntimeException("Attribute is not vendor specific");
        byte[] tmp=getAVPData();
        if(tmp.length<4)
            throw new RuntimeException("Attribute is not valid vendor specific");
        int res=tmp[0];
        res=(res<<8)|tmp[1]&0xff;
        res=(res<<8)|tmp[2]&0xff;
        res=(res<<8)|tmp[3]&0xff;
        return res;
    }
    
    public synchronized byte[] getAVPVendorData()
    {
        if(getAVPCode()!=RadiusFormat.AVP_VENDOR_SPECIFIC)
            throw new RuntimeException("Attribute is not vendor specific");
        byte[] tmp=getAVPData();
        if(tmp.length<=4)
            throw new RuntimeException("Attribute is not valid vendor specific");
        byte[] res=new byte[tmp.length-4];
        System.arraycopy(tmp,4,res,0,res.length);
        return res;
    }
    
    public boolean isSpecialAVP()
    {
        if(getAVPCode()!=RadiusFormat.AVP_VENDOR_SPECIFIC)
            return false;
        if(getAVPData().length<7)
            return false;
        if(getAVPVendorID()!=SPECIALVENDOR)
            return false;
        return true;
    }
    
    public synchronized String getAVPSpecialName()
    {
        if(getAVPVendorID()!=SPECIALVENDOR)
            throw new RuntimeException("Attribute is not special");
        byte[] tmp=getAVPVendorData();
        if(tmp.length<=4)
            throw new RuntimeException("Attribute is not special");
        int nlen=tmp[0]&0xff;
        try 
        {
            return new String(tmp,1,nlen,"UTF8");
        } 
        catch (UnsupportedEncodingException e) 
        {
            e.printStackTrace();
            return null;
        }
    }
    
    public synchronized String getAVPSpecialValue()
    {
        if(getAVPVendorID()!=SPECIALVENDOR)
            throw new RuntimeException("Attribute is not special");
        byte[] tmp=getAVPVendorData();
        if(tmp.length<=4)
            throw new RuntimeException("Attribute is not special");
        int nlen=tmp[0]&0xff;
        try 
        {
            return new String(tmp,1+nlen,tmp.length-1-nlen,"UTF8");
        } 
        catch (UnsupportedEncodingException e) 
        {
            e.printStackTrace();
            return null;
        }
    }

}


Arthur <arthur@ch.twi.tudelft.nl> http://ch.twi.tudelft.nl/~arthur/
2002-05-27