/** * JRadius - A RADIUS Server Java Adapter * Copyright (C) 2004-2005 PicoPoint, B.V. * * 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.net.DatagramPacket; import java.nio.ByteBuffer; import java.util.LinkedHashMap; import net.jradius.client.RadiusClient; import net.jradius.exception.RadiusException; import net.jradius.freeradius.FreeRadiusFormat; import net.jradius.log.RadiusLog; import net.jradius.packet.attribute.AttributeList; import org.apache.commons.pool.KeyedObjectPool; import org.apache.commons.pool.KeyedPoolableObjectFactory; import org.apache.commons.pool.impl.GenericKeyedObjectPool; /** * RADIUS Packet Factor. Parses RADIUS packets and constructs * the appropriate Java class instance. * * @author David Bird */ public class PacketFactory { private static LinkedHashMap<Integer, Class<?>> codeMap = new LinkedHashMap<Integer, Class<?>>(); static { codeMap.put(new Integer(0), NullPacket.class); // 0 codeMap.put(new Integer(AccessRequest.CODE), AccessRequest.class); // 1 codeMap.put(new Integer(AccessAccept.CODE), AccessAccept.class); // 2 codeMap.put(new Integer(AccessReject.CODE), AccessReject.class); // 3 codeMap.put(new Integer(AccountingRequest.CODE), AccountingRequest.class); // 4 codeMap.put(new Integer(AccountingResponse.CODE), AccountingResponse.class); // 5 codeMap.put(new Integer(AccountingStatus.CODE), AccountingStatus.class); // 6 codeMap.put(new Integer(PasswordRequest.CODE), PasswordRequest.class); // 7 codeMap.put(new Integer(PasswordAck.CODE), PasswordAck.class); // 8 codeMap.put(new Integer(PasswordReject.CODE), PasswordReject.class); // 9 codeMap.put(new Integer(AccessChallenge.CODE), AccessChallenge.class); // 11 codeMap.put(new Integer(DisconnectRequest.CODE), DisconnectRequest.class); // 40 codeMap.put(new Integer(DisconnectACK.CODE), DisconnectACK.class); // 41 codeMap.put(new Integer(DisconnectNAK.CODE), DisconnectNAK.class); // 42 codeMap.put(new Integer(CoARequest.CODE), CoARequest.class); // 43 codeMap.put(new Integer(CoAACK.CODE), CoAACK.class); // 44 codeMap.put(new Integer(CoANAK.CODE), CoANAK.class); // 45 codeMap.put(new Integer(DHCPDiscover.CODE), DHCPDiscover.class); // 1025 codeMap.put(new Integer(DHCPOffer.CODE), DHCPOffer.class); // 1026 codeMap.put(new Integer(DHCPRequest.CODE), DHCPRequest.class); // 1027 codeMap.put(new Integer(DHCPDecline.CODE), DHCPDecline.class); // 1028 codeMap.put(new Integer(DHCPAck.CODE), DHCPAck.class); // 1029 codeMap.put(new Integer(DHCPNack.CODE), DHCPNack.class); // 1030 codeMap.put(new Integer(DHCPRelease.CODE), DHCPRelease.class); // 1031 codeMap.put(new Integer(DHCPInform.CODE), DHCPInform.class); // 1032 codeMap.put(new Integer(DHCPForceRenew.CODE), DHCPForceRenew.class); // 1033 } public static class PacketFactoryPool extends GenericKeyedObjectPool { public PacketFactoryPool() { super(new KeyedPoolableObjectFactory() { public boolean validateObject(Object arg0, Object arg1) { return true; } public void passivateObject(Object arg0, Object arg1) throws Exception { RadiusPacket p = (RadiusPacket) arg1; p.recycled = true; } public Object makeObject(Object arg0) throws Exception { RadiusPacket p = createPacket((Integer) arg0); p.recyclable = true; p.recycled = false; return p; } public void destroyObject(Object arg0, Object arg1) throws Exception { } public void activateObject(Object arg0, Object arg1) throws Exception { RadiusPacket p = (RadiusPacket) arg1; p.setAuthenticator(null); p.recycled = false; } }); setMaxActive(-1); setMaxIdle(-1); } } private static KeyedObjectPool pktObjectPool = new PacketFactoryPool(); private static RadiusPacket createPacket(Integer code) throws Exception { Class<?> c = (Class<?>) codeMap.get(code); if (c == null) { throw new RadiusException("bad radius code " + code); } RadiusPacket p = (RadiusPacket) c.newInstance(); // System.err.println("Created packet " + p.toString()); return p; } public static RadiusPacket newPacket(Integer code, boolean pool) { try { if (pool && pktObjectPool != null) { RadiusPacket p = (RadiusPacket) pktObjectPool.borrowObject(code); // System.err.println("Borrowed packet " + p.toString()); return p; } return createPacket(code); } catch (Exception e) { throw new RuntimeException(e); } } public static RadiusPacket newPacket(Integer code) { return newPacket(code, true); } public static RadiusPacket newPacket(byte b) { return newPacket(new Integer(b)); } public static RadiusPacket newPacket(byte b, int identifier) { RadiusPacket p = newPacket(new Integer(b)); p.setIdentifier(identifier); return p; } public static RadiusPacket newPacket(byte b, int identifier, AttributeList list) { RadiusPacket p = newPacket(new Integer(b)); p.setIdentifier(identifier); p.getAttributes().add(list); return p; } public static RadiusPacket newPacket(byte b, AttributeList list) { RadiusPacket p = newPacket(new Integer(b)); p.getAttributes().add(list); return p; } public static RadiusRequest newPacket(byte b, RadiusClient client, AttributeList list) { RadiusRequest p = (RadiusRequest) newPacket(new Integer(b)); p.setRadiusClient(client); p.getAttributes().add(list); return p; } public static RadiusPacket copyPacket(RadiusPacket req, boolean pool) { RadiusPacket p = newPacket(req.code, pool); p.setIdentifier(req.getIdentifier()); p.setAuthenticator(req.getAuthenticator()); p.getAttributes().copy(req.getAttributes(), pool); return p; } /** * Parse a UDP RADIUS message * @param dp The Datagram to be parsed * @return Returns the RadiusPacket * @throws RadiusException */ public static RadiusPacket parse(DatagramPacket dp, boolean pool) throws RadiusException { ByteBuffer buffer = ByteBuffer.wrap(dp.getData(), dp.getOffset(), dp.getLength()); RadiusPacket rp = null; try { rp = parseUDP(buffer, pool); } catch (IOException e) { RadiusLog.error(e.getMessage(), e); } return rp; } public static RadiusPacket parseUDP(ByteBuffer buffer, boolean pool) throws RadiusException, IOException { int code = RadiusFormat.getUnsignedByte(buffer); int identifier = RadiusFormat.getUnsignedByte(buffer); int length = RadiusFormat.getUnsignedShort(buffer); return parseUDP(code, identifier, length, buffer, pool); } public static RadiusPacket parseUDP(int code, int identifier, int length, ByteBuffer buffer, boolean pool) throws RadiusException, IOException { RadiusPacket rp = null; Integer key = new Integer(code); if (pktObjectPool != null && pool) { try { rp = (RadiusPacket) pktObjectPool.borrowObject(key); } catch (Exception e) { e.printStackTrace(); } } if (rp == null) { Class<?> c = (Class<?>) codeMap.get(key); if (c == null) { throw new RadiusException("bad radius code - " + key); } try { rp = (RadiusPacket)c.newInstance(); } catch (Exception e) { RadiusLog.error(e.getMessage(), e); return null; } } byte[] bAuthenticator = new byte[16]; buffer.get(bAuthenticator); rp.setIdentifier(identifier); rp.setAuthenticator(bAuthenticator); length -= RadiusPacket.RADIUS_HEADER_LENGTH; if (length > 0) { RadiusFormat.setAttributeBytes(rp, buffer, length); } return rp; } /* public static RadiusPacket parsePacket(InputStream in) throws RadiusException, IOException { RadiusPacket rp = null; int code = (int) RadiusFormat.readUnsignedInt(in); int identifier = (int) RadiusFormat.readUnsignedInt(in); Integer key = new Integer(code); if (pktObjectPool != null) { try { rp = (RadiusPacket) pktObjectPool.borrowObject(key); } catch (Exception e) { e.printStackTrace(); } } if (rp == null) { Class<?> c = (Class<?>) codeMap.get(key); if (c == null) { throw new RadiusException("bad radius packet type: " + code); } try { rp = (RadiusPacket) c.newInstance(); } catch (Exception e) { RadiusLog.error(e.getMessage(), e); } } long length = Format.readUnsignedInt(in); byte[] bAttributes = new byte[(int) length]; in.read(bAttributes); try { rp.setIdentifier(identifier); FreeRadiusFormat.setAttributeBytes(rp, bAttributes); } catch (Exception e) { RadiusLog.error(e.getMessage(), e); } return rp; } */ public static RadiusPacket parsePacket(ByteBuffer buffer) throws RadiusException { RadiusPacket rp = null; int code = (int) Format.getUnsignedInt(buffer); int identifier = (int) Format.getUnsignedInt(buffer); long length = Format.getUnsignedInt(buffer); Integer key = new Integer(code); if (pktObjectPool != null) { try { rp = (RadiusPacket) pktObjectPool.borrowObject(key); } catch (Exception e) { e.printStackTrace(); } } if (rp == null) { Class<?> c = (Class<?>) codeMap.get(key); if (c == null) { throw new RadiusException("bad radius packet type: " + code); } try { rp = (RadiusPacket) c.newInstance(); } catch (Exception e) { RadiusLog.error(e.getMessage(), e); } } try { rp.setIdentifier(identifier); FreeRadiusFormat.setAttributeBytes(rp, buffer, (int) length); //buffer.position(buffer.position() + (int) length); } catch (Exception e) { RadiusLog.error(e.getMessage(), e); } return rp; } /** * Parse multiple RadiusPackets from a data stream * @param in The input data stream * @param packetCount Number of packets to expect * @return Returns an array of RadiusPackets * @throws RadiusException public static RadiusPacket[] parse(InputStream in, int packetCount) throws RadiusException { RadiusPacket rp[] = new RadiusPacket[packetCount]; try { for (int i=0; i < packetCount; i++) { rp[i] = parsePacket(in); } } catch (IOException e) { RadiusLog.error(e.getMessage(), e); } return rp; } */ public static RadiusPacket[] parse(ByteBuffer buffer, int packetCount) { RadiusPacket rp[] = new RadiusPacket[packetCount]; try { for (int i=0; i < packetCount; i++) { rp[i] = parsePacket(buffer); } } catch (RadiusException e) { RadiusLog.error(e.getMessage(), e); } return rp; } public static void poolStatus() { if (pktObjectPool == null) return; System.err.println("PacketPool: "+getPoolStatus()); } public static String getPoolStatus() { if (pktObjectPool == null) return ""; return "active="+pktObjectPool.getNumActive()+", idle="+pktObjectPool.getNumIdle(); } public static void recycle(RadiusPacket p) { synchronized (p) { AttributeList list = p.getAttributes(); list.clear(); if (pktObjectPool != null && p.recyclable) { try { pktObjectPool.returnObject(new Integer(p.getCode()), p); // System.err.print("Recycled packet "+p.toString()); } catch (Exception e) { e.printStackTrace(); } } // poolStatus(); // AttributeFactory.poolStatus(); } } public static void recycle(RadiusPacket[] rp) { if (rp != null) { for (int i = 0; i < rp.length; i++) { if (rp[i] != null) { recycle(rp[i]); } } } } }