All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jdiameter.client.impl.parser.ElementParser Maven / Gradle / Ivy

There is a newer version: 1.7.1-123
Show newest version
/*
 * TeleStax, Open Source Cloud Communications
 * Copyright 2011-2016, 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 
 * 
 * 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 org.jdiameter.api.Avp;
import org.jdiameter.api.AvpDataException;
import org.jdiameter.api.AvpSet;
import org.jdiameter.client.api.parser.ParseException;
import org.jdiameter.client.api.parser.IElementParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Date;

/**
 * 
 * [email protected]
 * @author  Alexandre Mendonca 
 * @author  Bartosz Baranowski 
 */
public class ElementParser implements IElementParser {

  private static final Logger logger = LoggerFactory.getLogger(ElementParser.class);
    /**
     * This is seconds shift (70 years in seconds) applied to date, 
     * since NTP date starts since 1900, not 1970.
     */
    private static final long SECOND_SHIFT = 2208988800L;
    
    private static final int INT_INET4 = 1;
    private static final int INT_INET6 = 2;
    
    private static final int INT32_SIZE = 4;
    private static final int INT64_SIZE = 8;

    public int bytesToInt(byte[] rawData) throws AvpDataException {
      // http://stackoverflow.com/a/9581858
      return 
          (rawData[0] & 0xFF) << 24 | 
          (rawData[1] & 0xFF) << 16 | 
          (rawData[2] & 0xFF) << 8 | 
          (rawData[3] & 0xFF) << 0;
    }

    public long bytesToLong(byte[] rawData) throws AvpDataException {
      // http://stackoverflow.com/a/1026804
      return (rawData[0] & 0xFFL) << 56
          | (rawData[1] & 0xFFL) << 48
          | (rawData[2] & 0xFFL) << 40
          | (rawData[3] & 0xFFL) << 32
          | (rawData[4] & 0xFFL) << 24
          | (rawData[5] & 0xFFL) << 16
          | (rawData[6] & 0xFFL) << 8
          | (rawData[7] & 0xFFL) << 0;
    }

    public long bytesToUnsignedInt32(byte[] rawData) throws AvpDataException {
      byte[] u32ext = new byte[INT64_SIZE];
      System.arraycopy(rawData, 0, u32ext, 4, 4);
      return bytesToLong(u32ext);
    }

    public float bytesToFloat(byte[] rawData) throws AvpDataException {
      // http://stackoverflow.com/a/14308765
      return Float.intBitsToFloat(bytesToInt(rawData));
    }

    public double bytesToDouble(byte[] rawData) throws AvpDataException {
      return Double.longBitsToDouble(bytesToLong(rawData));
    }

    public String bytesToOctetString(byte[] rawData) throws AvpDataException {
      try {
        return new String(rawData, "iso-8859-1");
      }
      catch (UnsupportedEncodingException e) {
        throw new AvpDataException("Invalid data type", e);
      }
    }

    public String bytesToUtf8String(byte[] rawData) throws AvpDataException {
      try {
        return new String(rawData, "utf8");
      }
      catch (UnsupportedEncodingException e) {
        throw new AvpDataException("Invalid data type", e);
      }
    }

    public Date bytesToDate(byte[] rawData) throws AvpDataException {
      try {
          byte[] tmp = new byte[8];
          System.arraycopy(rawData, 0 , tmp, 4, 4);
          return new Date(((bytesToLong(tmp) - SECOND_SHIFT) * 1000L));
      }
      catch (Exception e) {
          throw new AvpDataException(e);
      }
    }

    public InetAddress bytesToAddress(byte[] rawData) throws AvpDataException {
      InetAddress inetAddress;
      try {
        boolean isIPv6 = rawData[INT_INET4] != INT_INET4; 
        byte[] address = new byte[isIPv6 ? 16 : 4];
        System.arraycopy(rawData, 2, address, 0, address.length);
        inetAddress = isIPv6 ? Inet6Address.getByAddress(address) : Inet4Address.getByAddress(address);
      }
      catch (Exception e) {
        throw new AvpDataException(e);
      }
      return inetAddress;
    }

    public byte [] int32ToBytes(int value){
      byte[] bytes = new byte[INT32_SIZE];
      bytes[0] = (byte)(value >> 24 & 0xFF);
      bytes[1] = (byte)(value >> 16 & 0xFF);
      bytes[2] = (byte)(value >>  8 & 0xFF);
      bytes[3] = (byte)(value >>  0 & 0xFF);
      return bytes;
    }

    public byte [] intU32ToBytes(long value){
      byte[] bytes = int64ToBytes(value);
      return new byte[]{bytes[4],bytes[5],bytes[6],bytes[7]};
    }

    public byte [] int64ToBytes(long value) {
      byte[] bytes = new byte[INT64_SIZE];
      bytes[0] = (byte)(value >> 56 & 0xFF);
      bytes[1] = (byte)(value >> 48 & 0xFF);
      bytes[2] = (byte)(value >> 40 & 0xFF);
      bytes[3] = (byte)(value >> 32 & 0xFF);
      bytes[4] = (byte)(value >> 24 & 0xFF);
      bytes[5] = (byte)(value >> 16 & 0xFF);
      bytes[6] = (byte)(value >>  8 & 0xFF);
      bytes[7] = (byte)(value >>  0 & 0xFF);
      return bytes;
    }

    public byte [] float32ToBytes(float value) {
      // http://stackoverflow.com/a/14308774
      return int32ToBytes(Float.floatToIntBits(value));
    }

    public byte [] float64ToBytes(double value) {
      return int64ToBytes(Double.doubleToLongBits(value));
    }

    public byte[] octetStringToBytes(String value) throws ParseException{
      try {
        return value.getBytes("iso-8859-1");
      }
      catch (UnsupportedEncodingException e) {
        throw new ParseException(e);
      }
    }

    public byte[] utf8StringToBytes(String value) throws ParseException {
      try {
        return value.getBytes("utf8");
      }
      catch (Exception e) {
        throw new ParseException(e);
      }
    }

    public byte[] addressToBytes(InetAddress address) {
      byte byteAddrOrig[] = address.getAddress();

      byte[] data = new byte[byteAddrOrig.length + 2];

      int addrType = address instanceof Inet4Address ? INT_INET4 : INT_INET6;
      data[0] = (byte) ((addrType >> 8) & 0xFF);
      data[INT_INET4] = (byte) ((addrType >> 0) & 0xFF);

      System.arraycopy(byteAddrOrig, 0, data, 2, byteAddrOrig.length);
      return data;
    }

    public byte[] dateToBytes(Date date) {
      byte[] data = new byte[4];
      System.arraycopy(int64ToBytes((date.getTime()/1000L) + SECOND_SHIFT), 4, data, 0, 4);
      return data;
    }

    public  T bytesToObject(java.lang.Class iface, byte[] rawdata) throws AvpDataException {
      return null;
    }

    public byte [] objectToBytes(Object data) throws ParseException {
      return null;
    }

    public AvpSetImpl decodeAvpSet(byte[] buffer) throws IOException, AvpDataException {
      return this.decodeAvpSet(buffer, 0);
    }

    /**
     * 
     * @param buffer
     * @param shift - shift in buffer, for instance for whole message it will have non zero value
     * @return
     * @throws IOException
     * @throws AvpDataException
     */
    public AvpSetImpl decodeAvpSet(byte[] buffer, int shift) throws IOException, AvpDataException {
      AvpSetImpl avps = new AvpSetImpl();
      int tmp, counter = shift;
      DataInputStream in = new DataInputStream(new ByteArrayInputStream(buffer, shift, buffer.length /* - shift ? */));

      while (counter < buffer.length) {
        int code = in.readInt();
        tmp = in.readInt();
        int flags = (tmp >> 24) & 0xFF;
        int length  = tmp & 0xFFFFFF;
        if(length < 0 || counter + length > buffer.length) {
          throw new AvpDataException("Not enough data in buffer!");
        }
        long vendor = 0;
        if ((flags & 0x80) != 0) {
          vendor = in.readInt();
        }
        // Determine body L = length - 4(code) -1(flags) -3(length) [-4(vendor)]
        byte[] rawData = new byte[length - (8 + (vendor == 0 ? 0 : 4))];
        in.read(rawData);
        // skip remaining.
        // TODO: Do we need to padd everything? Or on send stack should properly fill byte[] ... ?
        if (length % 4 != 0) {
          for (int i; length % 4 != 0; length += i) {
            i = (int) in.skip((4 - length % 4));
          }
        }
        AvpImpl avp = new AvpImpl(code, (short) flags, (int) vendor, rawData);
        avps.addAvp(avp);
        counter += length;
      }
      return avps;  
    }

    public byte[] encodeAvpSet(AvpSet avps) {
      //ByteArrayOutputStream out = new ByteArrayOutputStream();
      DynamicByteArray dba = new DynamicByteArray(0);
      try {
        //DataOutputStream data = new DataOutputStream(out);
        for (Avp a : avps) {
          if (a instanceof AvpImpl) {
            AvpImpl aImpl = (AvpImpl) a;
            if (aImpl.rawData.length == 0 && aImpl.groupedData != null) {
              aImpl.rawData = encodeAvpSet(a.getGrouped());
            }
            //data.write(newEncodeAvp(aImpl));
            dba.add(encodeAvp(aImpl));
          }
        }
      }
      catch (Exception e) {
        e.printStackTrace();
        logger.debug("Error during encode avps", e);
      }
      return dba.getResult();
    }

    protected class DynamicByteArray {

      private byte[] array;
      private int size;

      public DynamicByteArray(int cap) {
        array = new byte[cap > 0 ? cap : 256];
        size = 0;
      }

      public int get(int pos) {
        if (pos >= size) {
          throw new ArrayIndexOutOfBoundsException();
        }
        return array[pos];
      }

      public void add(byte[] bytes) {
        if(size + bytes.length > array.length) {
          byte[] newarray = new byte[array.length+bytes.length*2];
          System.arraycopy(array, 0, newarray, 0, size);
          array = newarray;
        }
        System.arraycopy(bytes, 0, array, size, bytes.length);
        size += bytes.length;
      }
      
      public byte[] getResult() {
        return Arrays.copyOfRange(array, 0, size);
      }
    }

    public byte[] encodeAvp(AvpImpl avp) {
      try {
        int payloadSize = avp.getRaw().length;
        boolean hasVendorId = avp.getVendorId() != 0;
        int origLength = payloadSize + 8 + (hasVendorId ? 4 : 0);
        int tmp = payloadSize % 4;
        int paddingSize = tmp > 0 ? (4 - tmp) : 0;

        byte[] bCode = this.int32ToBytes(avp.getCode());
        int flags = (byte) ((hasVendorId ? 0x80 : 0) |
            (avp.isMandatory() ? 0x40 : 0) | (avp.isEncrypted() ? 0x20 : 0));
        byte[] bFlags = this.int32ToBytes(((flags << 24) & 0xFF000000) + origLength);
        byte[] bVendor = hasVendorId ? int32ToBytes((int)avp.getVendorId()) : new byte[0];
        return concat(origLength + paddingSize, bCode, bFlags, bVendor, avp.getRaw());
      }
      catch (Exception e) {
        logger.debug("Error during encode avp", e);
        return new byte[0];
      }
    }

    private byte[] concat(int length, byte[]... arrays) {
      if (length == 0) {
        for (byte[] array : arrays) {
          length += array.length;
        }
      }
      byte[] result = new byte[length];
      int pos = 0;
      for (byte[] array : arrays) {
        System.arraycopy(array, 0, result, pos, array.length);
        pos += array.length;
      }
      return result;
    }
    
    protected ByteBuffer prepareBuffer(byte [] bytes, int len) throws AvpDataException  {
      if (bytes.length != len) {
        throw new AvpDataException("Incorrect data length");
      }
      return ByteBuffer.wrap(bytes);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy