/** * JRadius - A RADIUS Server Java Adapter * Copyright (C) 2004-2005 PicoPoint, B.V. * Copyright (c) 2006 David Bird <david@coova.com> * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package net.jradius.packet; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import net.jradius.log.RadiusLog; import net.jradius.packet.attribute.RadiusAttribute; import net.jradius.packet.attribute.VSAttribute; import net.jradius.packet.attribute.value.AttributeValue; /** * Default RadiusPacket/RadiusAttribute format class. This class formats * and parses UDP RADIUS Packets. Derived classes implement other formats. * * @author David Bird */ public class RadiusFormat extends Format { private static final int HEADER_LENGTH = 2; public static final int VSA_HEADER_LENGTH = 8; private static final RadiusFormat staticFormat = new RadiusFormat(); /** * @return Returns a static instnace of this class */ public static RadiusFormat getInstance() { return staticFormat; } /** * Parses attributes and places them in a RadiusPacket * @param packet The RadiusPacket to parse attributes into * @param bAttributes The attribute bytes to parse */ public static void setAttributeBytes(RadiusPacket packet, ByteBuffer buffer, int length) { staticFormat.unpackAttributes(packet.getAttributes(), buffer, length, packet.isRecyclable()); } /** * Packs a RadiusPacket into a byte array * @param packet The RadiusPacket to pack * @return Returns the packed RadiusPacket public byte[] packPacket(RadiusPacket packet, String sharedSecret) throws IOException { return packPacket(packet, sharedSecret, false); } */ public void packPacket(RadiusPacket packet, String sharedSecret, ByteBuffer buffer, boolean onWire) throws IOException { if (packet == null) { throw new IllegalArgumentException("Packet is null."); } int initialPosition = buffer.position(); buffer.position(initialPosition + RadiusPacket.RADIUS_HEADER_LENGTH); packAttributeList(packet.getAttributes(), buffer, onWire); int totalLength = buffer.position() - initialPosition; int attributesLength = totalLength - RadiusPacket.RADIUS_HEADER_LENGTH; try { buffer.position(initialPosition); packHeader(buffer, packet, buffer.array(), initialPosition + RadiusPacket.RADIUS_HEADER_LENGTH, attributesLength, sharedSecret); buffer.position(totalLength + initialPosition); } catch(Exception e) { RadiusLog.warn(e.getMessage(), e); } } /* public byte[] packPacket(RadiusPacket packet, String sharedSecret, boolean onWire) throws IOException { if (packet == null) { throw new IllegalArgumentException("Packet is null."); } ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] attributeBytes = packAttributeList(packet.getAttributes(), onWire); try { packHeader(out, packet, attributeBytes, sharedSecret); if (attributeBytes != null) out.write(attributeBytes); out.close(); } catch(Exception e) { RadiusLog.warn(e.getMessage(), e); } return out.toByteArray(); } */ /** * Packs the RadiusPacket into a DataOutputStream * @param out The DataOutputStream to write to * @param p The RadiusPacket to pack * @param attributeBytes The RadiusPacket attributes * @throws IOException public void packHeader(OutputStream out, RadiusPacket p, byte[] attributeBytes, String sharedSecret) throws IOException { int length = attributeBytes.length + RadiusPacket.RADIUS_HEADER_LENGTH; writeUnsignedByte(out, p.getCode()); writeUnsignedByte(out, p.getIdentifier()); writeUnsignedShort(out, length); out.write(p.getAuthenticator(attributeBytes, sharedSecret)); } */ public void packHeader(ByteBuffer buffer, RadiusPacket p, byte[] attributeBytes, int offset, int attributesLength, String sharedSecret) throws IOException { int length = attributesLength + RadiusPacket.RADIUS_HEADER_LENGTH; putUnsignedByte(buffer, p.getCode()); putUnsignedByte(buffer, p.getIdentifier()); putUnsignedShort(buffer, length); buffer.put(p.getAuthenticator(attributeBytes, offset, attributesLength, sharedSecret)); } /** * Packs a RadiusAttribute into a DataOutputStream * @param out The DataOutputStream to write attributes to * @param a The RadiusAttribute to pack * @throws IOException public void packAttribute(OutputStream out, RadiusAttribute a) throws IOException { AttributeValue attributeValue = a.getValue(); packHeader(out, a); attributeValue.getBytes(out); } */ public void packAttribute(ByteBuffer buffer, RadiusAttribute a) { AttributeValue attributeValue = a.getValue(); if (a instanceof VSAttribute) { VSAttribute vsa = (VSAttribute) a; if (vsa.hasContinuationByte()) { int headerLength = headerLength(vsa); int valueLength = attributeValue.getLength(); int maxLength = 255 - headerLength; int len; if (valueLength > maxLength) { for (int off = 0; off < valueLength; off += maxLength) { len = valueLength - off; if (len > maxLength) { len = maxLength; vsa.setContinuation(); } else { vsa.unsetContinuation(); } packHeader(buffer, a, len); attributeValue.getBytes(buffer, off, len); } return; } } } packHeader(buffer, a); attributeValue.getBytes(buffer); } /** * Packs a RadiusAttribute header into a DataOutputStream * @param out The DataOutputStream to write to * @param a The RadiusAttribute to pack * @throws IOException public void packHeader(OutputStream out, RadiusAttribute a) throws IOException { if (a instanceof VSAttribute) { packHeader(out, (VSAttribute)a); return; } AttributeValue attributeValue = a.getValue(); writeUnsignedByte(out, (int) a.getType()); writeUnsignedByte(out, attributeValue.getLength() + HEADER_LENGTH); } */ public void packHeader(ByteBuffer buffer, RadiusAttribute a) { packHeader(buffer, a, a.getValue().getLength()); } public void packHeader(ByteBuffer buffer, RadiusAttribute a, int valueLength) { if (a instanceof VSAttribute) { packHeader(buffer, (VSAttribute) a, valueLength); return; } putUnsignedByte(buffer, (int) a.getType()); putUnsignedByte(buffer, valueLength + HEADER_LENGTH); } /** * Packs a VSAttribute header into a DataOutputStream * @param out The DataOutputStream to write to * @param a The VSAttribute to pack * @throws IOException public void packHeader(OutputStream out, VSAttribute a) throws IOException { AttributeValue attributeValue = a.getValue(); int len = attributeValue.getLength(); int vsaHeader = VSA_HEADER_LENGTH; if (a.hasContinuationByte()) vsaHeader ++; writeUnsignedByte(out, (int)a.getType()); writeUnsignedByte(out, len + vsaHeader); writeUnsignedInt(out, a.getVendorId()); writeUnsignedByte(out, (int)a.getVsaAttributeType()); len += 2; if (a.hasContinuationByte()) len ++; switch(a.getLengthLength()) { case 1: writeUnsignedByte(out, len); break; case 2: writeUnsignedShort(out, len); break; case 4: writeUnsignedInt(out, len); break; } if (a.hasContinuationByte()) { writeUnsignedByte(out, a.getContinuation()); } } */ public int headerLength(VSAttribute a) { int vsaHeader = 6; vsaHeader += a.getTypeLength(); vsaHeader += a.getLengthLength(); if (a.hasContinuationByte()) { vsaHeader ++; } return vsaHeader; } public int headerLength(RadiusAttribute a) { if (a instanceof VSAttribute) { return headerLength((VSAttribute) a); } return HEADER_LENGTH; } public void packHeader(ByteBuffer buffer, VSAttribute a) { packHeader(buffer, a, a.getValue().getLength()); } public void packHeader(ByteBuffer buffer, VSAttribute a, int len) { int vsaHeader = headerLength(a); /* * Enforce the maximum packet length here. */ if (len > (255 - vsaHeader)) { throw new RuntimeException("RADIUS attribute value too long ("+len+")"); } putUnsignedByte(buffer, (int)a.getType()); putUnsignedByte(buffer, len + vsaHeader); putUnsignedInt(buffer, a.getVendorId()); putUnsignedByte(buffer, (int)a.getVsaAttributeType()); len += 2; if (a.hasContinuationByte()) { len ++; } switch(a.getLengthLength()) { case 1: putUnsignedByte(buffer, len); break; case 2: putUnsignedShort(buffer, len); break; case 4: putUnsignedInt(buffer, len); break; } if (a.hasContinuationByte()) { putUnsignedByte(buffer, a.getContinuation()); } } /** * Unpacks the header of a RadiusAttribute from a DataInputStream * @param in The DataInputStream to read from * @param ctx The Attribute Parser Context * @return Returns the additional offset length for this header * @throws IOException */ public void unpackAttributeHeader(InputStream in, AttributeParseContext ctx) throws IOException { ctx.attributeOp = 0; ctx.vendorNumber = -1; ctx.padding = 0; ctx.attributeType = readUnsignedByte(in); ctx.attributeLength = readUnsignedByte(in); ctx.headerLength = 2; } public void unpackAttributeHeader(ByteBuffer buffer, AttributeParseContext ctx) throws IOException { ctx.attributeOp = 0; ctx.vendorNumber = -1; ctx.padding = 0; ctx.attributeType = getUnsignedByte(buffer); ctx.attributeLength = getUnsignedByte(buffer); ctx.headerLength = 2; } }