Package net.tomp2p.message

Source Code of net.tomp2p.message.Decoder

package net.tomp2p.message;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramChannel;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.TreeMap;

import net.tomp2p.connection.SignatureFactory;
import net.tomp2p.connection.TimeoutFactory;
import net.tomp2p.message.Message.Content;
import net.tomp2p.p2p.PeerBuilder;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.Number640;
import net.tomp2p.peers.PeerAddress;
import net.tomp2p.peers.PeerSocketAddress;
import net.tomp2p.peers.PeerStatistic;
import net.tomp2p.rpc.SimpleBloomFilter;
import net.tomp2p.storage.AlternativeCompositeByteBuf;
import net.tomp2p.storage.Data;
import net.tomp2p.storage.DataBuffer;
import net.tomp2p.utils.Utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Decoder {

  public static final AttributeKey<InetSocketAddress> INET_ADDRESS_KEY = AttributeKey.valueOf("inet-addr");
  public static final AttributeKey<PeerAddress> PEER_ADDRESS_KEY = AttributeKey.valueOf("peer-addr");

  private static final Logger LOG = LoggerFactory.getLogger(Decoder.class);

  private final Queue<Content> contentTypes = new LinkedList<Message.Content>();

  // private Message2 result = null;

  // current state - needs to be deleted if we want to reuse
  private Message message = null;

  private int neighborSize = -1;
  private NeighborSet neighborSet = null;
 
  private int peerSocketAddressSize = -1;
  private List<PeerSocketAddress> peerSocketAddresses = null;

  private int keyCollectionSize = -1;
  private KeyCollection keyCollection = null;

  private int mapSize = -1;
  private DataMap dataMap = null;
  private Data data = null;
  private Number640 key = null;

  private int keyMap640KeysSize = -1;
  private KeyMap640Keys keyMap640Keys = null;

  private int keyMapByteSize = -1;
  private KeyMapByte keyMapByte = null;

  private int bufferSize = -1;
  private DataBuffer buffer = null;

  private int trackerDataSize = -1;
  private TrackerData trackerData = null;
  private Data currentTrackerData = null;

  private Content lastContent = null;

  private final SignatureFactory signatureFactory;

  public Decoder(SignatureFactory signatureFactory) {
    this.signatureFactory = signatureFactory;
  }

  public boolean decode(ChannelHandlerContext ctx, final ByteBuf buf, InetSocketAddress recipient,
      final InetSocketAddress sender) {

    LOG.debug("Decoding of TomP2P starts now. Readable: {}.", buf.readableBytes());

    try {
      final int readerBefore = buf.readerIndex();
      // set the sender of this message for handling timeout
      final Attribute<InetSocketAddress> attributeInet = ctx.attr(INET_ADDRESS_KEY);
      attributeInet.set(sender);

      if (message == null) {
        boolean doneHeader = decodeHeader(buf, recipient, sender);
        if (doneHeader) {
          // store the sender as an attribute
          final Attribute<PeerAddress> attributePeerAddress = ctx.attr(PEER_ADDRESS_KEY);
          attributePeerAddress.set(message.sender());
          message.udp(ctx.channel() instanceof DatagramChannel);
          if (message.isFireAndForget() && message.isUdp()) {
            TimeoutFactory.removeTimeout(ctx);
          }
        } else {
          return false;
        }
      }
     
      final boolean donePayload = decodePayload(buf);
      decodeSignature(buf, readerBefore, donePayload);
     
      if(donePayload) {
        boolean isRelay = message.sender().isRelayed();
        if(isRelay && !message.peerSocketAddresses().isEmpty()) {
          PeerAddress tmpSender = message.sender().changePeerSocketAddresses(message.peerSocketAddresses());
          message.sender(tmpSender);
            }
      }
     
      // see https://github.com/netty/netty/issues/1976
      buf.discardSomeReadBytes();
      return donePayload;

    } catch (Exception e) {
      ctx.fireExceptionCaught(e);
      e.printStackTrace();
      return true;
    }
  }
 
  public void decodeSignature(final ByteBuf buf, final int readerBefore, final boolean donePayload) throws InvalidKeyException, SignatureException, IOException {
    final int readerAfter = buf.readerIndex();
    final int len = readerAfter - readerBefore;
    if(len > 0) {
      verifySignature(buf, readerBefore, len, donePayload);
    }
  }

  private void verifySignature(final ByteBuf buf, final int readerBefore, final int len, final boolean donePayload)
          throws SignatureException, IOException, InvalidKeyException {

    if (!message.isSign()) {
      return;
    }
    // if we read the complete data, we also read the signature
    // for the verification, we should not use this for the signature
    final int length = donePayload ? len - (Number160.BYTE_ARRAY_SIZE + Number160.BYTE_ARRAY_SIZE) : len;
    ByteBuffer[] byteBuffers = buf.nioBuffers(readerBefore, length);
   
    Signature signature = signatureFactory.update(message.publicKey(0), byteBuffers);

    if (donePayload) {
      byte[] signatureReceived = message.receivedSignature().encode();
      if (signature.verify(signatureReceived)) {
        // set public key only if signature is correct
        message.setVerified();
        LOG.debug("Signature check OK.");
      } else {
        LOG.warn("Signature check NOT OK. Message: {}.", message);
      }
    }
  }

  public boolean decodeHeader(final ByteBuf buf, InetSocketAddress recipient, final InetSocketAddress sender) {
    if (message == null) {
      if (buf.readableBytes() < MessageHeaderCodec.HEADER_SIZE) {
        // we don't have the header yet, we need the full header first
        // wait for more data
        return false;
      }
      message = MessageHeaderCodec.decodeHeader(buf, recipient, sender);
      // we have set the content types already
      message.presetContentTypes(true);

      for (Content content : message.contentTypes()) {
        if (content == Content.EMPTY) {
          break;
        }
        if (content == Content.PUBLIC_KEY_SIGNATURE) {
          message.setHintSign();
        }
        contentTypes.offer(content);
      }
      LOG.debug("Parsed message {}.", message);
      return true;
    }
    return false;
  }

  public boolean decodePayload(final ByteBuf buf) throws NoSuchAlgorithmException, InvalidKeySpecException,
      InvalidKeyException {
    LOG.debug("About to pass message {} to {}. Buffer to read: {}.", message, message.senderSocket(), buf.readableBytes());
    if (!message.hasContent()) {
      return true;
    }

    // payload comes here
    int size;
    PublicKey receivedPublicKey;
    while (contentTypes.size() > 0) {
      Content content = contentTypes.peek();
      LOG.debug("Go for content: {}.", content);
      switch (content) {
      case INTEGER:
        if (buf.readableBytes() < Utils.INTEGER_BYTE_SIZE) {
          return false;
        }
        message.intValue(buf.readInt());
        lastContent = contentTypes.poll();
        break;
      case LONG:
        if (buf.readableBytes() < Utils.LONG_BYTE_SIZE) {
          return false;
        }
        message.longValue(buf.readLong());
        lastContent = contentTypes.poll();
        break;
      case KEY:
        if (buf.readableBytes() < Number160.BYTE_ARRAY_SIZE) {
          return false;
        }
        byte[] me = new byte[Number160.BYTE_ARRAY_SIZE];
        buf.readBytes(me);
        message.key(new Number160(me));
        lastContent = contentTypes.poll();
        break;
      case BLOOM_FILTER:
        if (buf.readableBytes() < Utils.SHORT_BYTE_SIZE) {
          return false;
        }
        size = buf.getUnsignedShort(buf.readerIndex());
        if (buf.readableBytes() < size) {
          return false;
        }
        message.bloomFilter(new SimpleBloomFilter<Number160>(buf));
        lastContent = contentTypes.poll();
        break;
      case SET_NEIGHBORS:
        if (neighborSize == -1 && buf.readableBytes() < Utils.BYTE_BYTE_SIZE) {
          return false;
        }
        if (neighborSize == -1) {
          neighborSize = buf.readUnsignedByte();
        }
        if (neighborSet == null) {
          neighborSet = new NeighborSet(-1, new ArrayList<PeerAddress>(neighborSize));
        }
        for (int i = neighborSet.size(); i < neighborSize; i++) {
          if (buf.readableBytes() < Utils.SHORT_BYTE_SIZE) {
            return false;
          }
          int header = buf.getUnsignedShort(buf.readerIndex());
          size = PeerAddress.size(header);
          if (buf.readableBytes() < size) {
            return false;
          }
          PeerAddress pa = new PeerAddress(buf);
          neighborSet.add(pa);
        }
        message.neighborsSet(neighborSet);
        lastContent = contentTypes.poll();
        neighborSize = -1;
        neighborSet = null;
        break;
      case SET_PEER_SOCKET:
        if (peerSocketAddressSize == -1 && buf.readableBytes() < Utils.BYTE_BYTE_SIZE) {
          return false;
        }
        if (peerSocketAddressSize == -1) {
          peerSocketAddressSize = buf.readUnsignedByte();
        }
        if (peerSocketAddresses == null) {
          peerSocketAddresses = new ArrayList<PeerSocketAddress>(peerSocketAddressSize);
        }
        for (int i = peerSocketAddresses.size(); i < peerSocketAddressSize; i++) {
          if (buf.readableBytes() < Utils.BYTE_BYTE_SIZE) {
            return false;
          }
          int header = buf.getUnsignedByte(buf.readerIndex());
          boolean isIPv4 = header == 0;
          size = PeerSocketAddress.size(isIPv4);
          if (buf.readableBytes() < size + Utils.BYTE_BYTE_SIZE) {
            return false;
          }
          //skip the ipv4/ipv6 header
          buf.skipBytes(1);
          peerSocketAddresses.add(PeerSocketAddress.create(buf, isIPv4));
        }
        message.peerSocketAddresses(peerSocketAddresses);
        lastContent = contentTypes.poll();
        peerSocketAddressSize = -1;
        peerSocketAddresses = null;
        break;
      case SET_KEY640:
        if (keyCollectionSize == -1 && buf.readableBytes() < Utils.INTEGER_BYTE_SIZE) {
          return false;
        }
        if (keyCollectionSize == -1) {
          keyCollectionSize = buf.readInt();
        }
        if (keyCollection == null) {
          keyCollection = new KeyCollection(new ArrayList<Number640>(keyCollectionSize));
        }
        for (int i = keyCollection.size(); i < keyCollectionSize; i++) {
          if (buf.readableBytes() < Number160.BYTE_ARRAY_SIZE + Number160.BYTE_ARRAY_SIZE
              + Number160.BYTE_ARRAY_SIZE + Number160.BYTE_ARRAY_SIZE) {
            return false;
          }
          byte[] me2 = new byte[Number160.BYTE_ARRAY_SIZE];
          buf.readBytes(me2);
          Number160 locationKey = new Number160(me2);
          buf.readBytes(me2);
          Number160 domainKey = new Number160(me2);
          buf.readBytes(me2);
          Number160 contentKey = new Number160(me2);
          buf.readBytes(me2);
          Number160 versionKey = new Number160(me2);
          keyCollection.add(new Number640(locationKey, domainKey, contentKey, versionKey));
        }
        message.keyCollection(keyCollection);
        lastContent = contentTypes.poll();
        keyCollectionSize = -1;
        keyCollection = null;
        break;
      case MAP_KEY640_DATA:
        if (mapSize == -1 && buf.readableBytes() < Utils.INTEGER_BYTE_SIZE) {
          return false;
        }
        if (mapSize == -1) {
          mapSize = buf.readInt();
        }
        if (dataMap == null) {
          dataMap = new DataMap(new HashMap<Number640, Data>(2 * mapSize));
        }
        if (data != null) {
          if (!data.decodeBuffer(buf)) {
            return false;
          }
          if (!data.decodeDone(buf, message.publicKey(0), signatureFactory)) {
            return false;
          }
          data = null;
          key = null;
        }
        for (int i = dataMap.size(); i < mapSize; i++) {
          if (key == null) {
            if (buf.readableBytes() < Number160.BYTE_ARRAY_SIZE + Number160.BYTE_ARRAY_SIZE
                + Number160.BYTE_ARRAY_SIZE + Number160.BYTE_ARRAY_SIZE) {
              return false;
            }
            byte[] me3 = new byte[Number160.BYTE_ARRAY_SIZE];
            buf.readBytes(me3);
            Number160 locationKey = new Number160(me3);
            buf.readBytes(me3);
            Number160 domainKey = new Number160(me3);
            buf.readBytes(me3);
            Number160 contentKey = new Number160(me3);
            buf.readBytes(me3);
            Number160 versionKey = new Number160(me3);
            key = new Number640(locationKey, domainKey, contentKey, versionKey);
          }
          data = Data.decodeHeader(buf, signatureFactory);
          if (data == null) {
            return false;
          }
          dataMap.dataMap().put(key, data);

          if (!data.decodeBuffer(buf)) {
            return false;
          }
          if (!data.decodeDone(buf, message.publicKey(0), signatureFactory)) {
            return false;
          }
          // if we have signed the message, set the public key anyway, but only if we indicated so
          if (message.isSign() && message.publicKey(0) != null && data.hasPublicKey()
              && (data.publicKey() == null || data.publicKey() == PeerBuilder.EMPTY_PUBLIC_KEY)) {
            data.publicKey(message.publicKey(0));
          }
          data = null;
          key = null;
        }

        message.setDataMap(dataMap);
        lastContent = contentTypes.poll();
        mapSize = -1;
        dataMap = null;
        break;
      case MAP_KEY640_KEYS:
        if (keyMap640KeysSize == -1 && buf.readableBytes() < Utils.INTEGER_BYTE_SIZE) {
          return false;
        }
        if (keyMap640KeysSize == -1) {
          keyMap640KeysSize = buf.readInt();
        }
        if (keyMap640Keys == null) {
          keyMap640Keys = new KeyMap640Keys(new TreeMap<Number640, Collection<Number160>>());
        }

        final int meta = Number160.BYTE_ARRAY_SIZE + Number160.BYTE_ARRAY_SIZE
            + Number160.BYTE_ARRAY_SIZE + Number160.BYTE_ARRAY_SIZE;

        for (int i = keyMap640Keys.size(); i < keyMap640KeysSize; i++) {
          if (buf.readableBytes() < meta + Utils.BYTE_BYTE_SIZE) {
            return false;
          }
          size = buf.getUnsignedByte(buf.readerIndex() + meta);
         
          if (buf.readableBytes() < meta + Utils.BYTE_BYTE_SIZE + (size * Number160.BYTE_ARRAY_SIZE )) {
            return false;
          }
          byte[] me3 = new byte[Number160.BYTE_ARRAY_SIZE];
          buf.readBytes(me3);
          Number160 locationKey = new Number160(me3);
          buf.readBytes(me3);
          Number160 domainKey = new Number160(me3);
          buf.readBytes(me3);
          Number160 contentKey = new Number160(me3);
          buf.readBytes(me3);
          Number160 versionKey = new Number160(me3);

          int numBasedOn = buf.readByte();
          Set<Number160> value = new HashSet<Number160>(numBasedOn);
          for (int j = 0; j < numBasedOn; j++) {
            buf.readBytes(me3);
            Number160 basedOnKey = new Number160(me3);
            value.add(basedOnKey);
          }

          keyMap640Keys.put(new Number640(locationKey, domainKey, contentKey, versionKey), value);
        }

        message.keyMap640Keys(keyMap640Keys);
        lastContent = contentTypes.poll();
        keyMap640KeysSize = -1;
        keyMap640Keys = null;
        break;
      case MAP_KEY640_BYTE:
        if (keyMapByteSize == -1 && buf.readableBytes() < Utils.INTEGER_BYTE_SIZE) {
          return false;
        }
        if (keyMapByteSize == -1) {
          keyMapByteSize = buf.readInt();
        }
        if (keyMapByte == null) {
          keyMapByte = new KeyMapByte(new HashMap<Number640, Byte>(2 * keyMapByteSize));
        }

        for (int i = keyMapByte.size(); i < keyMapByteSize; i++) {
          if (buf.readableBytes() < Number160.BYTE_ARRAY_SIZE + Number160.BYTE_ARRAY_SIZE
              + Number160.BYTE_ARRAY_SIZE + Number160.BYTE_ARRAY_SIZE + 1) {
            return false;
          }
          byte[] me3 = new byte[Number160.BYTE_ARRAY_SIZE];
          buf.readBytes(me3);
          Number160 locationKey = new Number160(me3);
          buf.readBytes(me3);
          Number160 domainKey = new Number160(me3);
          buf.readBytes(me3);
          Number160 contentKey = new Number160(me3);
          buf.readBytes(me3);
          Number160 versionKey = new Number160(me3);
          byte value = buf.readByte();
          keyMapByte.put(new Number640(locationKey, domainKey, contentKey, versionKey), value);
        }

        message.keyMapByte(keyMapByte);
        lastContent = contentTypes.poll();
        keyMapByteSize = -1;
        keyMapByte = null;
        break;
      case BYTE_BUFFER:
        if (bufferSize == -1 && buf.readableBytes() < Utils.INTEGER_BYTE_SIZE) {
          return false;
        }
        if (bufferSize == -1) {
          bufferSize = buf.readInt();
        }
        if (buffer == null) {
          buffer = new DataBuffer();
        }
       
        final int already = buffer.alreadyTransferred();
        final int remaining = bufferSize - already;
        // already finished
        if (remaining != 0) {
          int read = buffer.transferFrom(buf, remaining);
          if(read != remaining) {
            LOG.debug("Still looking for data. Indicating that its not finished yet. Read = {}, Size = {}.", buffer.alreadyTransferred(), bufferSize);
            return false;
          }
        }
       
        ByteBuf buf2 = AlternativeCompositeByteBuf.compBuffer(buffer.toByteBufs());
        message.buffer(new Buffer(buf2, bufferSize));
        lastContent = contentTypes.poll();
        bufferSize = -1;
        buffer = null;
        break;
      case SET_TRACKER_DATA:
        if (trackerDataSize == -1 && buf.readableBytes() < Utils.BYTE_BYTE_SIZE) {
          return false;
        }
        if (trackerDataSize == -1) {
          trackerDataSize = buf.readUnsignedByte();
        }
        if (trackerData == null) {
          trackerData = new TrackerData(new HashMap<PeerStatistic, Data>(2 * trackerDataSize));
        }
        if (currentTrackerData != null) {
          if (!currentTrackerData.decodeBuffer(buf)) {
            return false;
          }
          if (!currentTrackerData.decodeDone(buf, message.publicKey(0), signatureFactory)) {
            return false;
          }
          currentTrackerData = null;
        }
        for (int i = trackerData.size(); i < trackerDataSize; i++) {

          if (buf.readableBytes() < Utils.SHORT_BYTE_SIZE) {
            return false;
          }

          int header = buf.getUnsignedShort(buf.readerIndex());

          size = PeerAddress.size(header);

          if (buf.readableBytes() < size) {
            return false;
          }
          PeerAddress pa = new PeerAddress(buf);
          PeerStatistic ps = new PeerStatistic(pa);

          currentTrackerData = Data.decodeHeader(buf, signatureFactory);
          if (currentTrackerData == null) {
            return false;
          }
          trackerData.peerAddresses().put(ps, currentTrackerData);
          if (message.isSign()) {
            currentTrackerData.publicKey(message.publicKey(0));
          }

          if (!currentTrackerData.decodeBuffer(buf)) {
            return false;
          }
          if (!currentTrackerData.decodeDone(buf, message.publicKey(0), signatureFactory)) {
            return false;
          }
          currentTrackerData = null;
        }

        message.trackerData(trackerData);
        lastContent = contentTypes.poll();
        trackerDataSize = -1;
        trackerData = null;
        break;
      case PUBLIC_KEY: // fall-through
      case PUBLIC_KEY_SIGNATURE:
        receivedPublicKey = signatureFactory.decodePublicKey(buf);
        if(content == Content.PUBLIC_KEY_SIGNATURE) {
          if (receivedPublicKey == PeerBuilder.EMPTY_PUBLIC_KEY) {
            throw new InvalidKeyException("The public key cannot be empty");
          }
        }
        if (receivedPublicKey == null) {
          return false;
        }
       
        message.publicKey(receivedPublicKey);
        lastContent = contentTypes.poll();
        break;
      default:
        break;
      }
    }
    if (message.isSign()) {
      SignatureCodec signatureEncode = signatureFactory.signatureCodec();
      size = signatureEncode.signatureSize();
      if (buf.readableBytes() < size) {
        return false;
      }
     
      signatureEncode.read(buf);
      message.receivedSignature(signatureEncode);
    }
    return true;
  }

  public Message prepareFinish() {
    Message ret = message;
    message.setDone();
    contentTypes.clear();
    message = null;
    neighborSize = -1;
    neighborSet = null;
    keyCollectionSize = -1;
    keyCollection = null;
    mapSize = -1;
    dataMap = null;
    data = null;
    keyMap640KeysSize = -1;
    keyMap640Keys = null;
    bufferSize = -1;
    buffer = null;
    return ret;
  }

  public Message message() {
    return message;
  }

  public Content lastContent() {
    return lastContent;
  }
}
TOP

Related Classes of net.tomp2p.message.Decoder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.