/*
* TeleStax, Open Source Cloud Communications
* Copyright 2011-2014, TeleStax Inc. and individual contributors
* by the @authors tag.
*
* This program is free software: you can redistribute it and/or modify
* under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* JBoss, Home of Professional Open Source
* Copyright 2007-2011, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This 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 software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jdiameter.client.impl.parser;
import static org.jdiameter.api.Avp.ACCT_APPLICATION_ID;
import static org.jdiameter.api.Avp.AUTH_APPLICATION_ID;
import static org.jdiameter.api.Avp.SESSION_ID;
import static org.jdiameter.api.Avp.VENDOR_SPECIFIC_APPLICATION_ID;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
import org.jdiameter.api.Avp;
import org.jdiameter.api.AvpDataException;
import org.jdiameter.api.AvpSet;
import org.jdiameter.api.Request;
import org.jdiameter.client.api.IMessage;
import org.jdiameter.client.api.IRequest;
import org.jdiameter.client.api.parser.IMessageParser;
import org.jdiameter.client.api.parser.ParseException;
import org.jdiameter.client.impl.helpers.UIDGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author erick.svenson@yahoo.com
* @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
* @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
*/
public class MessageParser extends ElementParser implements IMessageParser {
private static final Logger logger = LoggerFactory.getLogger(MessageParser.class);
protected UIDGenerator endToEndGen = new UIDGenerator(
(int) (TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) & 0xFFF) << 20
);
public MessageParser() {
}
@Override
public IMessage createMessage(byte[] message) throws AvpDataException {
// Read header
try {
long tmp;
DataInputStream in = new DataInputStream(new ByteArrayInputStream(message));
tmp = in.readInt();
short version = (short) (tmp >> 24);
if (version != 1) {
throw new Exception("Illegal value of version " + version);
}
if (message.length != (tmp & 0x00FFFFFF)) {
//throw new ParseException("Wrong length of data: " + (tmp & 0x00FFFFFF));
throw new Exception("Wrong length of data: " + (tmp & 0x00FFFFFF));
}
tmp = in.readInt();
short flags = (short) ((tmp >> 24) & 0xFF);
int commandCode = (int) (tmp & 0xFFFFFF);
long applicationId = ((long) in.readInt() << 32) >>> 32;
long hopByHopId = ((long) in.readInt() << 32) >>> 32;
long endToEndId = ((long) in.readInt() << 32) >>> 32;
// Read body
// byte[] body = new byte[message.length - 20];
// System.arraycopy(message, 20, body, 0, body.length);
// AvpSetImpl avpSet = decodeAvpSet(body);
AvpSetImpl avpSet = decodeAvpSet(message, 20);
return new MessageImpl(commandCode, applicationId, flags, hopByHopId, endToEndId, avpSet);
}
catch (Exception exc) {
throw new AvpDataException(exc);
}
}
@Override
public IMessage createMessage(ByteBuffer data) throws AvpDataException {
byte[] message = data.array();
return createMessage(message);
}
@Override
public <T> T createMessage(Class<?> iface, ByteBuffer data) throws AvpDataException {
if (iface == IMessage.class) {
return (T) createMessage(data);
}
return null;
}
@Override
public <T> T createEmptyMessage(Class<?> iface, IMessage parentMessage) {
if (iface == Request.class) {
return (T) createEmptyMessage(parentMessage, parentMessage.getCommandCode());
}
else {
return null;
}
}
@Override
public IMessage createEmptyMessage(IMessage prnMessage) {
return createEmptyMessage(prnMessage, prnMessage.getCommandCode());
}
@Override
public IMessage createEmptyMessage(IMessage prnMessage, int commandCode) {
//
MessageImpl newMessage = new MessageImpl(
commandCode,
prnMessage.getHeaderApplicationId(),
(short) prnMessage.getFlags(),
prnMessage.getHopByHopIdentifier(),
endToEndGen.nextLong(),
null
);
copyBasicAvps(newMessage, prnMessage, false);
return newMessage;
}
void copyBasicAvps(IMessage newMessage, IMessage prnMessage, boolean invertPoints) {
//left it here, but
Avp avp;
// Copy session id's information
{
avp = prnMessage.getAvps().getAvp(SESSION_ID);
if (avp != null) {
newMessage.getAvps().addAvp(new AvpImpl(avp));
}
avp = prnMessage.getAvps().getAvp(Avp.ACC_SESSION_ID);
if (avp != null) {
newMessage.getAvps().addAvp(new AvpImpl(avp));
}
avp = prnMessage.getAvps().getAvp(Avp.ACC_SUB_SESSION_ID);
if (avp != null) {
newMessage.getAvps().addAvp(new AvpImpl(avp));
}
avp = prnMessage.getAvps().getAvp(Avp.ACC_MULTI_SESSION_ID);
if (avp != null) {
newMessage.getAvps().addAvp(new AvpImpl(avp));
}
}
// Copy Applicatio id's information
{
avp = prnMessage.getAvps().getAvp(VENDOR_SPECIFIC_APPLICATION_ID);
if (avp != null) {
newMessage.getAvps().addAvp(new AvpImpl(avp));
}
avp = prnMessage.getAvps().getAvp(ACCT_APPLICATION_ID);
if (avp != null) {
newMessage.getAvps().addAvp(new AvpImpl(avp));
}
avp = prnMessage.getAvps().getAvp(AUTH_APPLICATION_ID);
if (avp != null) {
newMessage.getAvps().addAvp(new AvpImpl(avp));
}
}
// Copy proxy information
{
AvpSet avps = prnMessage.getAvps().getAvps(Avp.PROXY_INFO);
for (Avp piAvp : avps) {
newMessage.getAvps().addAvp(new AvpImpl(piAvp));
}
}
// Copy route information
{
if (newMessage.isRequest()) {
if (invertPoints) {
// set Dest host
avp = prnMessage.getAvps().getAvp(Avp.ORIGIN_HOST);
if (avp != null) {
newMessage.getAvps().addAvp(new AvpImpl(Avp.DESTINATION_HOST, avp));
}
// set Dest realm
avp = prnMessage.getAvps().getAvp(Avp.ORIGIN_REALM);
if (avp != null) {
newMessage.getAvps().addAvp(new AvpImpl(Avp.DESTINATION_REALM, avp));
}
}
else {
// set Dest host
avp = prnMessage.getAvps().getAvp(Avp.DESTINATION_HOST);
if (avp != null) {
newMessage.getAvps().addAvp(avp);
}
// set Dest realm
avp = prnMessage.getAvps().getAvp(Avp.DESTINATION_REALM);
if (avp != null) {
newMessage.getAvps().addAvp(avp);
}
}
}
// // set Orig host and realm
// try {
// newMessage.getAvps().addAvp(Avp.ORIGIN_HOST, metaData.getLocalPeer().getUri().getFQDN(), true, false, true);
// newMessage.getAvps().addAvp(Avp.ORIGIN_REALM, metaData.getLocalPeer().getRealmName(), true, false, true);
// }
// catch (Exception e) {
// logger.debug("Error copying Origin-Host/Realm AVPs", e);
// }
}
}
@Override
public ByteBuffer encodeMessage(IMessage message) throws ParseException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
byte[] rawData = encodeAvpSet(message.getAvps());
DataOutputStream data = new DataOutputStream(out);
// Wasting processor time, are we ?
// int tmp = (1 << 24) & 0xFF000000;
int tmp = (1 << 24);
tmp += 20 + rawData.length;
data.writeInt(tmp);
// Again, unneeded operation ?
// tmp = (message.getFlags() << 24) & 0xFF000000;
tmp = (message.getFlags() << 24);
tmp += message.getCommandCode();
data.writeInt(tmp);
data.write(toBytes(message.getHeaderApplicationId()));
data.write(toBytes(message.getHopByHopIdentifier()));
data.write(toBytes(message.getEndToEndIdentifier()));
data.write(rawData);
}
catch (Exception e) {
//logger.debug("Error during encode message", e);
throw new ParseException("Failed to encode message.", e);
}
try {
return prepareBuffer(out.toByteArray(), out.size());
}
catch (AvpDataException ade) {
throw new ParseException(ade);
}
}
private byte[] toBytes(long value) {
byte[] data = new byte[4];
data[0] = (byte) ((value >> 24) & 0xFF);
data[1] = (byte) ((value >> 16) & 0xFF);
data[2] = (byte) ((value >> 8) & 0xFF);
data[3] = (byte) ((value) & 0xFF);
return data;
}
@Override
public IMessage createEmptyMessage(int commandCode, long headerAppId) {
return new MessageImpl(commandCode, headerAppId);
}
@Override
public <T> T createEmptyMessage(Class<?> iface, int commandCode, long headerAppId) {
if (iface == IRequest.class) {
return (T) new MessageImpl(commandCode, headerAppId);
}
return null;
}
public int getNextEndToEndId() {
return endToEndGen.nextInt();
}
}