/** * 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.io.OutputStream; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import net.jradius.log.RadiusLog; import net.jradius.packet.attribute.AttributeDictionary; import net.jradius.packet.attribute.AttributeFactory; import net.jradius.packet.attribute.AttributeList; import net.jradius.packet.attribute.RadiusAttribute; import net.jradius.packet.attribute.VSAttribute; /** * @author David Bird */ public abstract class Format { //abstract public void packAttribute(OutputStream out, RadiusAttribute a) throws IOException; abstract public void packAttribute(ByteBuffer buffer, RadiusAttribute a); public void packAttributes(ByteBuffer buffer, List<VSAttribute> list) { for (VSAttribute a : list) packAttribute(buffer, a); } //abstract public int unpackAttributeHeader(InputStream in, AttributeParseContext ctx) throws IOException; abstract public void unpackAttributeHeader(ByteBuffer buffer, AttributeParseContext ctx) throws IOException; /** * Packs an AttributeList into a byte array * @param attrs The AttributeList to pack * @return Returns the packed AttributeList public byte[] packAttributeList(AttributeList attrs) { return packAttributeList(attrs, false); } */ /* public byte[] packAttributeList(AttributeList attrs, boolean onWire) { ByteArrayOutputStream out = new ByteArrayOutputStream(); Iterator<RadiusAttribute> iterator = attrs.getAttributeList().iterator(); while (iterator.hasNext()) { RadiusAttribute attr = iterator.next(); if (onWire && attr.getType() > 1024) { continue; } try { packAttribute(out, attr); } catch (Exception e) { RadiusLog.warn(e.getMessage(), e); } } try { out.close(); } catch(Exception e) { RadiusLog.warn(e.getMessage(), e); } return out.toByteArray(); } */ public void packAttributeList(AttributeList attrs, ByteBuffer buffer, boolean onWire) { Iterator<RadiusAttribute> iterator = attrs.getAttributeList().iterator(); while (iterator.hasNext()) { RadiusAttribute attr = iterator.next(); if (attr instanceof VSAttribute) { VSAttribute vsa = (VSAttribute) attr; if (vsa.isGrouped()) { List<VSAttribute> group = new LinkedList<VSAttribute>(); group.add(vsa); attr = iterator.hasNext() ? iterator.next() : null; while (attr != null && attr.getFormattedType() == vsa.getFormattedType()) { group.add((VSAttribute)attr); attr = iterator.hasNext() ? iterator.next() : null; } packAttributes(buffer, group); if (attr == null) break; } } if (onWire && attr.getType() > 1024) { continue; } if (attr.isOverflow()) { continue; } int currentPosition = buffer.position(); try { packAttribute(buffer, attr); } catch (Throwable e) { RadiusAttribute a = attrs.get(AttributeDictionary.CHARGEABLE_USER_IDENTITY); RadiusLog.error("Truncating RADIUS packet " + (a == null ? "unknown" : a.toString())+" :: "+attr.toString(), null); buffer.position(currentPosition); attr.setOverflow(true); } } } protected class AttributeParseContext { public long attributeType = 0; public long attributeLength = 0; public long attributeOp = RadiusAttribute.Operator.EQ; public long attributeValueLength = 0; public byte[] attributeValue = null; public int headerLength = 0; public int vendorNumber = -1; public int padding = 0; public long lengthRemaining = 0; } /** * Unpacks RadiusAttributes from a byte array into an AttributeList * @param attrs The AttributeList to put unpacked attributes * @param bytes The bytes to be unpacked * @param bLength The length of the bytes to be unpacked public void unpackAttributes(AttributeList attrs, byte[] bytes, int bOffset, int bLength) { InputStream attributeInput = new ByteArrayInputStream(bytes, bOffset, bLength); try { for (int pos = 0; pos < bLength; ) { AttributeParseContext ctx = new AttributeParseContext(); pos += unpackAttributeHeader(attributeInput, ctx); RadiusAttribute attribute = null; ctx.attributeValue = new byte[(int)(ctx.attributeLength - ctx.headerLength)]; attributeInput.read(ctx.attributeValue, 0, (int)(ctx.attributeLength - ctx.headerLength)); attribute = AttributeFactory.newAttribute(ctx.vendorNumber, ctx.attributeType, ctx.attributeValue, (int) ctx.attributeOp); if (attribute == null) { RadiusLog.warn("Unknown attribute with type = " + ctx.attributeType); } else { attrs._add(attribute, false); } if (ctx.padding > 0) { pos += ctx.padding; while (ctx.padding-- > 0) { readUnsignedByte(attributeInput); } } pos += ctx.attributeLength; } attributeInput.close(); } catch (IOException e) { RadiusLog.warn(e.getMessage(), e); } } */ public void unpackAttributes(AttributeList attrs, ByteBuffer buffer, int length, boolean pool) { AttributeParseContext ctx = new AttributeParseContext(); int pos = 0; while (pos < length) { try { unpackAttributeHeader(buffer, ctx); } catch (Exception e) { RadiusLog.error(e.getMessage(), e); return; } boolean hasMore; boolean seenOne = false; long len = ctx.attributeLength - ctx.headerLength; do { hasMore = false; RadiusAttribute attribute = AttributeFactory.newAttribute( ctx.vendorNumber, ctx.attributeType, len, (int) ctx.attributeOp, buffer, pool); if (attribute == null) { RadiusLog.warn("Unknown attribute with type = " + ctx.attributeType); } else { attrs._add(attribute, false); } long vlen = attribute.getValue().getLength(); if (vlen < len) { if (attribute instanceof VSAttribute) { ctx.vendorNumber = (int) ((VSAttribute)attribute).getVendorId(); ctx.attributeType = -1; if (!seenOne) vlen += 4; vlen += 2; len -= vlen; seenOne = true; hasMore = len > 0; } else { break; } } if (seenOne) { ((VSAttribute)attribute).setGrouped(true); } } while(hasMore); if (ctx.padding > 0) { pos += ctx.padding; while (ctx.padding-- > 0) { getUnsignedByte(buffer); } } pos += ctx.attributeLength; } } public static long readUnsignedInt(InputStream in) throws IOException { byte[] b = new byte[4]; in.read(b); long value = b[3] & 0xFF; value |= (b[2] & 0xFF) << 8; value |= (b[1] & 0xFF) << 16; value |= (b[0] & 0xFF) << 24; return value; } public static int readUnsignedShort(InputStream in) throws IOException { byte[] b = new byte[2]; in.read(b); int value = b[1] & 0xFF; value |= (b[0] & 0xFF) << 8; return value; } public static int readUnsignedByte(InputStream in) throws IOException { return in.read() & 0xFF; } public static void writeUnsignedByte(OutputStream out, int b) throws IOException { out.write(b); } public static void writeUnsignedShort(OutputStream out, int s) throws IOException { out.write((s >> 8) & 0xFF); out.write(s & 0xFF); } public static void writeUnsignedInt(OutputStream out, long i) throws IOException { writeUnsignedShort(out, (int)(i >> 16) & 0xFFFF); writeUnsignedShort(out, (int)i & 0xFFFF); } public static short getUnsignedByte (ByteBuffer bb) { return ((short)(bb.get() & 0xff)); } public static void putUnsignedByte (ByteBuffer bb, int value) { bb.put ((byte)(value & 0xff)); } public static short getUnsignedByte (ByteBuffer bb, int position) { return ((short)(bb.get (position) & (short)0xff)); } public static void putUnsignedByte (ByteBuffer bb, int position, int value) { bb.put (position, (byte)(value & 0xff)); } public static int getUnsignedShort (ByteBuffer bb) { return (bb.getShort() & 0xffff); } public static void putUnsignedShort (ByteBuffer bb, int value) { bb.putShort ((short)(value & 0xffff)); } public static int getUnsignedShort (ByteBuffer bb, int position) { return (bb.getShort (position) & 0xffff); } public static void putUnsignedShort (ByteBuffer bb, int position, int value) { bb.putShort (position, (short)(value & 0xffff)); } public static long getUnsignedInt (ByteBuffer bb) { return ((long)bb.getInt() & 0xffffffffL); } public static void putUnsignedInt (ByteBuffer bb, long value) { bb.putInt ((int)(value & 0xffffffffL)); } public static long getUnsignedInt (ByteBuffer bb, int position) { return ((long)bb.getInt (position) & 0xffffffffL); } public static void putUnsignedInt (ByteBuffer bb, int position, long value) { bb.putInt (position, (int)(value & 0xffffffffL)); } }