/*
* Copyright 2011 Future Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.krakenapps.radius.protocol;
import java.net.UnknownHostException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public abstract class RadiusPacket {
private int code; // 1byte
private int identifier; // 1byte
private int length; // 2byte
private byte[] authenticator; // 16byte
private List<RadiusAttribute> attrs = new ArrayList<RadiusAttribute>();
protected boolean finalized = false;
public static byte[] newRequestAuthenticator() {
UUID uuid = UUID.randomUUID();
ByteBuffer bb = ByteBuffer.allocate(16);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
}
public static RadiusPacket parse(String sharedSecret, byte[] buf) {
return parse(sharedSecret, buf, 0, buf.length);
}
public static RadiusPacket parse(String sharedSecret, byte[] buf, int offset, int length) {
ByteBuffer bb = ByteBuffer.wrap(buf);
bb.position(offset);
int code = bb.get();
RadiusPacket p = null;
switch (code) {
case 1:
p = new AccessRequest();
break;
case 2:
p = new AccessAccept();
break;
case 3:
p = new AccessReject();
break;
case 4:
p = new AccountingRequest();
break;
case 5:
p = new AccountingResponse();
break;
case 11:
p = new AccessChallenge();
break;
}
p.setIdentifier(bb.get());
p.setLength(bb.getShort() & 0xffff);
if (p.getLength() > length)
throw new BufferUnderflowException();
byte[] authenticator = new byte[16];
bb.get(authenticator);
p.setAuthenticator(authenticator);
int attrLength = p.getLength() - 20;
for (int i = 0; i < attrLength;) {
bb.mark();
bb.get();
int len = bb.get();
bb.reset();
try {
RadiusAttribute attr = RadiusAttribute.parse(authenticator, sharedSecret, bb.array(), bb.position(),
bb.remaining());
if (attr != null)
p.getAttributes().add(attr);
} catch (UnknownHostException e) {
}
i += len;
bb.position(bb.position() + len);
}
return p;
}
public int getCode() {
return code;
}
public void setCode(int code) {
guard();
this.code = code;
}
public int getIdentifier() {
return identifier;
}
public void setIdentifier(int identifier) {
guard();
this.identifier = identifier;
}
public int getLength() {
return length;
}
public void setLength(int length) {
guard();
this.length = length;
}
public byte[] getAuthenticator() {
return authenticator;
}
public void setAuthenticator(byte[] authenticator) {
guard();
this.authenticator = authenticator;
}
public List<RadiusAttribute> getAttributes() {
return attrs;
}
public void setAttributes(List<RadiusAttribute> attrs) {
guard();
this.attrs = attrs;
}
public RadiusAttribute findAttribute(int type) {
for (RadiusAttribute attr : attrs)
if (attr.getType() == type)
return attr;
return null;
}
public byte[] getBytes() {
if (!finalized)
throw new IllegalStateException("invoke finalize() before byte serialization");
ByteBuffer bb = ByteBuffer.allocate(length);
bb.put((byte) code);
bb.put((byte) identifier);
bb.putShort((short) length);
bb.put(getAuthenticator());
for (RadiusAttribute attr : attrs)
bb.put(attr.getBytes());
return bb.array();
}
private void guard() {
if (finalized)
throw new IllegalStateException("already finalized packet");
}
public boolean isFinalized() {
return finalized;
}
public void finalize() {
if (finalized)
return;
int len = 20;
List<RadiusAttribute> attrs = getAttributes();
for (RadiusAttribute attr : attrs)
len += attr.getBytes().length;
this.length = len;
finalized = true;
}
}