Package freenet.node

Source Code of freenet.node.SSKInsertHandler

/* This code is part of Freenet. It is distributed under the GNU General
* Public License, version 2 (or at your option any later version). See
* http://www.gnu.org/ for further details of the GPL. */
package freenet.node;

import freenet.crypt.CryptFormatException;
import freenet.crypt.DSAPublicKey;
import freenet.io.comm.ByteCounter;
import freenet.io.comm.DMT;
import freenet.io.comm.DisconnectedException;
import freenet.io.comm.Message;
import freenet.io.comm.MessageFilter;
import freenet.io.comm.NotConnectedException;
import freenet.io.comm.PeerRestartedException;
import freenet.io.xfer.WaitedTooLongException;
import freenet.keys.NodeSSK;
import freenet.keys.SSKBlock;
import freenet.keys.SSKVerifyException;
import freenet.store.KeyCollisionException;
import freenet.support.Logger;
import freenet.support.ShortBuffer;
import freenet.support.Logger.LogLevel;
import freenet.support.io.NativeThread;

/**
* Handles an incoming SSK insert.
* SSKs need their own insert/request classes, see comments in SSKInsertSender.
*/
public class SSKInsertHandler implements PrioRunnable, ByteCounter {

  private static boolean logMINOR;
 
    static final int DATA_INSERT_TIMEOUT = 30000;
   
    final Node node;
    final long uid;
    final PeerNode source;
    final NodeSSK key;
    final long startTime;
    private SSKBlock block;
    private DSAPublicKey pubKey;
    private short htl;
    private SSKInsertSender sender;
    private byte[] data;
    private byte[] headers;
    private boolean canCommit;
    final InsertTag tag;
    private final boolean canWriteDatastore;
  private final boolean forkOnCacheable;
  private final boolean preferInsert;
  private final boolean ignoreLowBackoff;
  private final boolean realTimeFlag;

  private boolean collided = false;
   
    SSKInsertHandler(NodeSSK key, byte[] data, byte[] headers, short htl, PeerNode source, long id, Node node, long startTime, InsertTag tag, boolean canWriteDatastore, boolean forkOnCacheable, boolean preferInsert, boolean ignoreLowBackoff, boolean realTimeFlag) {
        this.node = node;
        this.uid = id;
        this.source = source;
        this.startTime = startTime;
        this.key = key;
        this.htl = htl;
        this.data = data;
        this.headers = headers;
        this.tag = tag;
        this.canWriteDatastore = canWriteDatastore;
        byte[] pubKeyHash = key.getPubKeyHash();
        pubKey = node.getPubKey.getKey(pubKeyHash, false, false, null);
        canCommit = false;
        logMINOR = Logger.shouldLog(LogLevel.MINOR, this);
        this.forkOnCacheable = forkOnCacheable;
        this.preferInsert = preferInsert;
        this.ignoreLowBackoff = ignoreLowBackoff;
        this.realTimeFlag = realTimeFlag;
    }
   
    @Override
  public String toString() {
        return super.toString()+" for "+uid;
    }
   
    @Override
    public void run() {
        freenet.support.Logger.OSThread.logPID(this);
        try {
            realRun();
        } catch (Throwable t) {
            Logger.error(this, "Caught "+t, t);
        } finally {
            if(logMINOR) Logger.minor(this, "Exiting InsertHandler.run() for "+uid);
            tag.unlockHandler();
        }
    }

    private void realRun() {
        // Send Accepted
        Message accepted = DMT.createFNPSSKAccepted(uid, pubKey == null);
       
        try {
      source.sendAsync(accepted, null, this);
    } catch (NotConnectedException e1) {
      if(logMINOR) Logger.minor(this, "Lost connection to source");
      return;
    }
   
    if(tag.shouldSlowDown()) {
      try {
        source.sendAsync(DMT.createFNPRejectedOverload(uid, false, false, realTimeFlag), null, this);
      } catch (NotConnectedException e) {
        // Ignore.
      }
    }
   
    while(headers == null || data == null || pubKey == null) {
      MessageFilter mfDataInsertRejected = MessageFilter.create().setType(DMT.FNPDataInsertRejected).setField(DMT.UID, uid).setSource(source).setTimeout(DATA_INSERT_TIMEOUT);
      MessageFilter mf = mfDataInsertRejected;
      if(headers == null) {
        MessageFilter m = MessageFilter.create().setType(DMT.FNPSSKInsertRequestHeaders).setField(DMT.UID, uid).setSource(source).setTimeout(DATA_INSERT_TIMEOUT);
        mf = m.or(mf);
      }
      if(data == null) {
        MessageFilter m = MessageFilter.create().setType(DMT.FNPSSKInsertRequestData).setField(DMT.UID, uid).setSource(source).setTimeout(DATA_INSERT_TIMEOUT);
        mf = m.or(mf);
      }
      if(pubKey == null) {
        MessageFilter m = MessageFilter.create().setType(DMT.FNPSSKPubKey).setField(DMT.UID, uid).setSource(source).setTimeout(DATA_INSERT_TIMEOUT);
        mf = m.or(mf);
      }
      Message msg;
      try {
        msg = node.usm.waitFor(mf, this);
      } catch (DisconnectedException e) {
        if(logMINOR) Logger.minor(this, "Lost connection to source on "+uid);
        return;
      }
      if(msg == null) {
        Logger.normal(this, "Failed to receive all parts (data="+(data==null?"null":"ok")+" headers="+(headers==null?"null":"ok")+" pk="+pubKey+") for "+uid);
        Message failed = DMT.createFNPDataInsertRejected(uid, DMT.DATA_INSERT_REJECTED_RECEIVE_FAILED);
        try {
          source.sendSync(failed, this, realTimeFlag);
        } catch (NotConnectedException e) {
          // Ignore
        } catch (SyncSendWaitedTooLongException e) {
          // Ignore
        }
        return;
      } else if(msg.getSpec() == DMT.FNPSSKInsertRequestHeaders) {
        headers = ((ShortBuffer)msg.getObject(DMT.BLOCK_HEADERS)).getData();
      } else if(msg.getSpec() == DMT.FNPSSKInsertRequestData) {
        data = ((ShortBuffer)msg.getObject(DMT.DATA)).getData();
      } else if(msg.getSpec() == DMT.FNPSSKPubKey) {
        byte[] pubkeyAsBytes = ((ShortBuffer)msg.getObject(DMT.PUBKEY_AS_BYTES)).getData();
        try {
          pubKey = DSAPublicKey.create(pubkeyAsBytes);
          if(logMINOR) Logger.minor(this, "Got pubkey on "+uid+" : "+pubKey);
          Message confirm = DMT.createFNPSSKPubKeyAccepted(uid);
          try {
            source.sendAsync(confirm, null, this);
          } catch (NotConnectedException e) {
            if(logMINOR) Logger.minor(this, "Lost connection to source on "+uid);
            return;
          }
        } catch (CryptFormatException e) {
          Logger.error(this, "Invalid pubkey from "+source+" on "+uid);
          msg = DMT.createFNPDataInsertRejected(uid, DMT.DATA_INSERT_REJECTED_SSK_ERROR);
          try {
            source.sendSync(msg, this, realTimeFlag);
          } catch (NotConnectedException ee) {
            // Ignore
          } catch (SyncSendWaitedTooLongException ee) {
            // Ignore
          }
          return;
        }
      } else if(msg.getSpec() == DMT.FNPDataInsertRejected) {
            try {
          source.sendAsync(DMT.createFNPDataInsertRejected(uid, msg.getShort(DMT.DATA_INSERT_REJECTED_REASON)), null, this);
        } catch (NotConnectedException e) {
          // Ignore.
        }
        return;
      } else {
        Logger.error(this, "Unexpected message? "+msg+" on "+this);
      }
    }
   
    try {
      key.setPubKey(pubKey);
      block = new SSKBlock(data, headers, key, false);
    } catch (SSKVerifyException e1) {
      Logger.error(this, "Invalid SSK from "+source, e1);
      Message msg = DMT.createFNPDataInsertRejected(uid, DMT.DATA_INSERT_REJECTED_SSK_ERROR);
      try {
        source.sendSync(msg, this, realTimeFlag);
      } catch (NotConnectedException e) {
        // Ignore
      } catch (SyncSendWaitedTooLongException e) {
        // Ignore
      }
      return;
    }
   
    SSKBlock storedBlock = node.fetch(key, false, false, false, canWriteDatastore, false, null);
   
    if((storedBlock != null) && !storedBlock.equals(block)) {
      try {
        RequestHandler.sendSSK(storedBlock.getRawHeaders(), storedBlock.getRawData(), false, pubKey, source, uid, this, realTimeFlag);
      } catch (NotConnectedException e1) {
        if(logMINOR) Logger.minor(this, "Lost connection to source on "+uid);
        return;
      } catch (WaitedTooLongException e1) {
        Logger.error(this, "Took too long to send ssk datareply to "+uid+" (because of throttling)");
        return;
      } catch (PeerRestartedException e) {
        if(logMINOR) Logger.minor(this, "Source restarted on "+uid);
        return;
      } catch (SyncSendWaitedTooLongException e) {
        Logger.error(this, "Took too long to send ssk datareply to "+uid);
        return;
      }
      block = storedBlock;
    }
   
    if(logMINOR) Logger.minor(this, "Got block for "+key+" for "+uid);
   
        if(htl > 0)
            sender = node.makeInsertSender(block, htl, uid, tag, source, false, false, canWriteDatastore, forkOnCacheable, preferInsert, ignoreLowBackoff, realTimeFlag);
       
        boolean receivedRejectedOverload = false;
       
        while(true) {
            synchronized(sender) {
                try {
                  if(sender.getStatus() == SSKInsertSender.NOT_FINISHED)
                    sender.wait(5000);
                } catch (InterruptedException e) {
                  // Ignore
                }
            }

            if((!receivedRejectedOverload) && sender.receivedRejectedOverload()) {
              receivedRejectedOverload = true;
              // Forward it
              // Does not need to be sent synchronously since is non-terminal.
              Message m = DMT.createFNPRejectedOverload(uid, false, true, realTimeFlag);
              try {
          source.sendAsync(m, null, this);
        } catch (NotConnectedException e) {
          if(logMINOR) Logger.minor(this, "Lost connection to source");
          return;
        }
            }
           
            if(sender.hasRecentlyCollided()) {
              // Forward collision
              data = sender.getData();
              headers = sender.getHeaders();
              collided = true;
            try {
          block = new SSKBlock(data, headers, key, true);
        } catch (SSKVerifyException e1) {
          // Is verified elsewhere...
          throw new Error("Impossible: " + e1, e1);
        }
        try {
          RequestHandler.sendSSK(headers, data, false, pubKey, source, uid, this, realTimeFlag);
        } catch (NotConnectedException e1) {
          if(logMINOR) Logger.minor(this, "Lost connection to source on "+uid);
          return;
        } catch (WaitedTooLongException e1) {
          Logger.error(this, "Took too long to send ssk datareply to "+uid+" because of bwlimiting");
          return;
        } catch (PeerRestartedException e) {
          Logger.error(this, "Peer restarted on "+uid);
          return;
        } catch (SyncSendWaitedTooLongException e) {
          Logger.error(this, "Took too long to send ssk datareply to "+uid);
          return;
        }
            }
           
            int status = sender.getStatus();
           
            if(status == SSKInsertSender.NOT_FINISHED) {
               continue;
            }
           
            // Local RejectedOverload's (fatal).
            // Internal error counts as overload. It'd only create a timeout otherwise, which is the same thing anyway.
            // We *really* need a good way to deal with nodes that constantly R_O!
            if((status == SSKInsertSender.TIMED_OUT) ||
                (status == SSKInsertSender.GENERATED_REJECTED_OVERLOAD) ||
                (status == SSKInsertSender.INTERNAL_ERROR)) {
              // Unlock early for originator, late for target; see UIDTag comments.
              tag.unlockHandler();
                Message msg = DMT.createFNPRejectedOverload(uid, true, true, realTimeFlag);
                try {
          source.sendSync(msg, this, realTimeFlag);
        } catch (NotConnectedException e) {
          if(logMINOR) Logger.minor(this, "Lost connection to source");
          return;
        } catch (SyncSendWaitedTooLongException e) {
          Logger.error(this, "Took too long to send "+msg+" to "+source);
          return;
        }
                // Might as well store it anyway.
                if((status == SSKInsertSender.TIMED_OUT) ||
                    (status == SSKInsertSender.GENERATED_REJECTED_OVERLOAD))
                  canCommit = true;
                finish(status);
                return;
            }
           
            if((status == SSKInsertSender.ROUTE_NOT_FOUND) || (status == SSKInsertSender.ROUTE_REALLY_NOT_FOUND)) {
              // Unlock early for originator, late for target; see UIDTag comments.
              tag.unlockHandler();
                Message msg = DMT.createFNPRouteNotFound(uid, sender.getHTL());
                try {
          source.sendSync(msg, this, realTimeFlag);
        } catch (NotConnectedException e) {
          if(logMINOR) Logger.minor(this, "Lost connection to source");
          return;
        } catch (SyncSendWaitedTooLongException e) {
          Logger.error(this, "Took too long to send "+msg+" to source");
        }
                canCommit = true;
                finish(status);
                return;
            }
           
            if(status == SSKInsertSender.SUCCESS) {
              // Unlock early for originator, late for target; see UIDTag comments.
              tag.unlockHandler();
              Message msg = DMT.createFNPInsertReply(uid);
              try {
          source.sendSync(msg, this, realTimeFlag);
        } catch (NotConnectedException e) {
          if(logMINOR) Logger.minor(this, "Lost connection to source");
          return;
        } catch (SyncSendWaitedTooLongException e) {
          Logger.error(this, "Took too long to send "+msg+" to "+source);
        }
                canCommit = true;
                finish(status);
                return;
            }
           
            // Otherwise...?
            Logger.error(this, "Unknown status code: "+sender.getStatusString());
          // Unlock early for originator, late for target; see UIDTag comments.
          tag.unlockHandler();
            Message msg = DMT.createFNPRejectedOverload(uid, true, true, realTimeFlag);
            try {
        source.sendSync(msg, this, realTimeFlag);
      } catch (NotConnectedException e) {
        // Ignore
      } catch (SyncSendWaitedTooLongException e) {
        Logger.error(this, "Took too long to send "+msg+" to "+source);
      }
            finish(status);
            return;
        }
    }

    /**
     * If canCommit, and we have received all the data, and it
     * verifies, then commit it.
     */
    private void finish(int code) {
      if(logMINOR) Logger.minor(this, "Finishing");
     
      if(canCommit) {
        commit();
      }
     
        if(code != SSKInsertSender.TIMED_OUT && code != SSKInsertSender.GENERATED_REJECTED_OVERLOAD &&
            code != SSKInsertSender.INTERNAL_ERROR && code != SSKInsertSender.ROUTE_REALLY_NOT_FOUND) {
          int totalSent = getTotalSentBytes();
          int totalReceived = getTotalReceivedBytes();
          if(sender != null) {
            totalSent += sender.getTotalSentBytes();
            totalReceived += sender.getTotalReceivedBytes();
          }
          if(logMINOR) Logger.minor(this, "Remote SSK insert cost "+totalSent+ '/' +totalReceived+" bytes ("+code+ ')');
          node.nodeStats.remoteSskInsertBytesSentAverage.report(totalSent);
          node.nodeStats.remoteSskInsertBytesReceivedAverage.report(totalReceived);
          if(code == SSKInsertSender.SUCCESS) {
            // Can report both sides
            node.nodeStats.successfulSskInsertBytesSentAverage.report(totalSent);
            node.nodeStats.successfulSskInsertBytesReceivedAverage.report(totalReceived);
          }
        }

    }

    private void commit() {
    try {
      node.store(block, node.shouldStoreDeep(key, source, sender == null ? new PeerNode[0] : sender.getRoutedTo()), collided, false, canWriteDatastore, false);
    } catch (KeyCollisionException e) {
      Logger.normal(this, "Collision on "+this);
    }
  }

  private final Object totalBytesSync = new Object();
    private int totalBytesSent;
    private int totalBytesReceived;
   
  @Override
  public void sentBytes(int x) {
    synchronized(totalBytesSync) {
      totalBytesSent += x;
    }
    node.nodeStats.insertSentBytes(true, x);
  }

  @Override
  public void receivedBytes(int x) {
    synchronized(totalBytesSync) {
      totalBytesReceived += x;
    }
    node.nodeStats.insertReceivedBytes(true, x);
  }
 
  public int getTotalSentBytes() {
    return totalBytesSent;
  }
 
  public int getTotalReceivedBytes() {
    return totalBytesReceived;
  }

  @Override
  public void sentPayload(int x) {
    node.sentPayload(x);
    node.nodeStats.insertSentBytes(true, -x);
  }

  @Override
  public int getPriority() {
    return NativeThread.HIGH_PRIORITY;
  }
   
}
TOP

Related Classes of freenet.node.SSKInsertHandler

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.