Author: toad
Date: 2007-06-12 20:21:24 +0000 (Tue, 12 Jun 2007)
New Revision: 13540
Modified:
trunk/freenet/src/freenet/io/comm/DMT.java
trunk/freenet/src/freenet/node/NodeDispatcher.java
trunk/freenet/src/freenet/node/PeerNode.java
trunk/freenet/src/freenet/node/updater/NodeUpdateManager.java
trunk/freenet/src/freenet/node/updater/RevocationChecker.java
trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java
Log:
First stage of UpdateOverMandatory proper: Instant f2f propagation of the
revocation key.
Modified: trunk/freenet/src/freenet/io/comm/DMT.java
===================================================================
--- trunk/freenet/src/freenet/io/comm/DMT.java 2007-06-12 19:59:23 UTC (rev
13539)
+++ trunk/freenet/src/freenet/io/comm/DMT.java 2007-06-12 20:21:24 UTC (rev
13540)
@@ -1005,11 +1005,13 @@
}
public static final MessageType UOMRequestRevocation = new
MessageType("UOMRequestRevocation") {{
-
+ addField(UID, Long.class);
}};
- public static final Message createUOMRequestRevocation() {
- return new Message(UOMRequestRevocation);
+ public static final Message createUOMRequestRevocation(long uid) {
+ Message msg = new Message(UOMRequestRevocation);
+ msg.set(UID, uid);
+ return msg;
}
public static final MessageType UOMRequestMain = new
MessageType("UOMRequestMain") {{
Modified: trunk/freenet/src/freenet/node/NodeDispatcher.java
===================================================================
--- trunk/freenet/src/freenet/node/NodeDispatcher.java 2007-06-12 19:59:23 UTC
(rev 13539)
+++ trunk/freenet/src/freenet/node/NodeDispatcher.java 2007-06-12 20:21:24 UTC
(rev 13540)
@@ -131,6 +131,10 @@
return handleProbeTrace(m, source);
} else if(spec == DMT.UOMAnnounce) {
return node.nodeUpdater.uom.handleAnnounce(m, source);
+ } else if(spec == DMT.UOMRequestRevocation) {
+ return node.nodeUpdater.uom.handleRequestRevocation(m,
source);
+ } else if(spec == DMT.UOMSendingRevocation) {
+ return node.nodeUpdater.uom.handleSendingRevocation(m,
source);
}
return false;
}
Modified: trunk/freenet/src/freenet/node/PeerNode.java
===================================================================
--- trunk/freenet/src/freenet/node/PeerNode.java 2007-06-12 19:59:23 UTC
(rev 13539)
+++ trunk/freenet/src/freenet/node/PeerNode.java 2007-06-12 20:21:24 UTC
(rev 13540)
@@ -3893,4 +3893,8 @@
}
fo.onRejected();
}
+
+ public String userToString() {
+ return ""+getPeer()+" : "+getName();
+ }
}
Modified: trunk/freenet/src/freenet/node/updater/NodeUpdateManager.java
===================================================================
--- trunk/freenet/src/freenet/node/updater/NodeUpdateManager.java
2007-06-12 19:59:23 UTC (rev 13539)
+++ trunk/freenet/src/freenet/node/updater/NodeUpdateManager.java
2007-06-12 20:21:24 UTC (rev 13540)
@@ -38,6 +38,8 @@
public final static String UPDATE_URI = "freenet:USK at
BFa1voWr5PunINSZ5BGMqFwhkJTiDBBUrOZ0MYBXseg,BOrxeLzUMb6R9tEZzexymY0zyKAmBNvrU4A9Q0tAqu0,AQACAAE/update/"+Version.buildNumber();
public final static String REVOCATION_URI = "SSK at
tHlY8BK2KFB7JiO2bgeAw~e4sWU43YdJ6kmn73gjrIw,DnQzl0BYed15V8WQn~eRJxxIA-yADuI8XW7mnzEbut8,AQACAAE/revoked";
public final static String EXT_URI = "freenet:USK at
BFa1voWr5PunINSZ5BGMqFwhkJTiDBBUrOZ0MYBXseg,BOrxeLzUMb6R9tEZzexymY0zyKAmBNvrU4A9Q0tAqu0,AQACAAE/ext/"+NodeStarter.extBuildNumber;
+
+ public static final long MAX_REVOCATION_KEY_LENGTH = 16*1024*1024; //
16MB
FreenetURI updateURI;
FreenetURI extURI;
Modified: trunk/freenet/src/freenet/node/updater/RevocationChecker.java
===================================================================
--- trunk/freenet/src/freenet/node/updater/RevocationChecker.java
2007-06-12 19:59:23 UTC (rev 13539)
+++ trunk/freenet/src/freenet/node/updater/RevocationChecker.java
2007-06-12 20:21:24 UTC (rev 13540)
@@ -3,9 +3,9 @@
import java.io.File;
import java.io.IOException;
+import freenet.client.FetchContext;
import freenet.client.FetchException;
import freenet.client.FetchResult;
-import freenet.client.FetchContext;
import freenet.client.InsertException;
import freenet.client.async.BaseClientPutter;
import freenet.client.async.ClientCallback;
@@ -134,9 +134,13 @@
}
public void onSuccess(FetchResult result, ClientGetter state) {
+ onSuccess(result, state, tmpBlobFile);
+ }
+
+ void onSuccess(FetchResult result, ClientGetter state, File blob) {
// The key has been blown !
// FIXME: maybe we need a bigger warning message.
- moveBlob();
+ moveBlob(blob);
String msg = null;
try {
byte[] buf = result.asByteArray();
@@ -154,7 +158,7 @@
manager.blow(msg);
}
- private void moveBlob() {
+ private void moveBlob(File tmpBlobFile) {
if(tmpBlobFile == null) {
Logger.error(this, "No temporary binary blob file
moving it: may not be able to propagate revocation, bug???");
return;
@@ -169,6 +173,10 @@
}
public void onFailure(FetchException e, ClientGetter state) {
+ onFailure(e, state, tmpBlobFile);
+ }
+
+ void onFailure(FetchException e, ClientGetter state, File tmpBlobFile) {
Logger.minor(this, "Revocation fetch failed: "+e);
logMINOR = Logger.shouldLog(Logger.MINOR, this);
int errorCode = e.getMode();
@@ -180,7 +188,7 @@
}
if(e.isFatal()) {
manager.blow("Permanent error fetching revocation
(error inserting the revocation key?): "+e.toString());
- moveBlob(); // other peers need to know
+ moveBlob(tmpBlobFile); // other peers need to know
return;
}
if(tmpBlobFile != null) tmpBlobFile.delete();
@@ -239,4 +247,11 @@
return blobFile.length();
}
+ /** Get the binary blob, if we have fetched it. */
+ public File getBlobFile() {
+ if(!manager.isBlown()) return null;
+ if(blobFile.exists()) return blobFile;
+ return null;
+ }
+
}
Modified: trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java
===================================================================
--- trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java
2007-06-12 19:59:23 UTC (rev 13539)
+++ trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java
2007-06-12 20:21:24 UTC (rev 13540)
@@ -3,20 +3,46 @@
* http://www.gnu.org/ for further details of the GPL. */
package freenet.node.updater;
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
import java.net.MalformedURLException;
import java.util.HashSet;
import java.util.Vector;
+import freenet.client.FetchContext;
+import freenet.client.FetchException;
+import freenet.client.FetchResult;
+import freenet.client.InsertException;
+import freenet.client.async.BaseClientPutter;
+import freenet.client.async.BinaryBlob;
+import freenet.client.async.BinaryBlobFormatException;
+import freenet.client.async.ClientCallback;
+import freenet.client.async.ClientGetter;
+import freenet.client.async.ClientRequestScheduler;
+import freenet.client.async.SimpleBlockSet;
import freenet.io.comm.AsyncMessageCallback;
import freenet.io.comm.DMT;
+import freenet.io.comm.DisconnectedException;
import freenet.io.comm.Message;
import freenet.io.comm.NotConnectedException;
+import freenet.io.xfer.BulkReceiver;
+import freenet.io.xfer.BulkTransmitter;
+import freenet.io.xfer.PartiallyReceivedBulk;
import freenet.keys.FreenetURI;
import freenet.l10n.L10n;
+import freenet.node.Node;
import freenet.node.PeerNode;
import freenet.node.useralerts.UserAlert;
import freenet.support.HTMLNode;
import freenet.support.Logger;
+import freenet.support.SizeUtil;
+import freenet.support.api.Bucket;
+import freenet.support.io.FileBucket;
+import freenet.support.io.RandomAccessFileWrapper;
/**
* Co-ordinates update over mandatory. Update over mandatory = updating from
your peers, even
@@ -87,7 +113,7 @@
// First, is the key the same as ours?
try {
FreenetURI revocationURI = new
FreenetURI(revocationKey);
-
if(/*revocationURI.equals(updateManager.revocationURI)*/true) {
+
if(revocationURI.equals(updateManager.revocationURI)) {
// Uh oh...
@@ -107,7 +133,7 @@
// Try to transfer it.
- Message msg =
DMT.createUOMRequestRevocation();
+ Message msg =
DMT.createUOMRequestRevocation(updateManager.node.random.nextLong());
source.sendAsync(msg, new
AsyncMessageCallback() {
public void acknowledged() {
// Ok
@@ -323,5 +349,327 @@
(PeerNode[]) nodesFailedSayRevoked.toArray(new
PeerNode[nodesFailedSayRevoked.size()]),
};
}
+
+ /**
+ * A peer node requests us to send the binary blob of the revocation
key.
+ * @param m The message requesting the transfer.
+ * @param source The node requesting the transfer.
+ * @return True if we handled the message (i.e. always).
+ */
+ public boolean handleRequestRevocation(Message m, final PeerNode
source) {
+ // Do we have the data?
+
+ File data = updateManager.revocationChecker.getBlobFile();
+
+ if(data == null) {
+ Logger.normal(this, "Peer "+source+" asked us for the
blob file for the revocation key but we don't have it!");
+ // Probably a race condition on reconnect, hopefully
we'll be asked again
+ return true;
+ }
+
+ final long uid = m.getLong(DMT.UID);
+
+ RandomAccessFileWrapper raf;
+ try {
+ raf = new RandomAccessFileWrapper(data, "r");
+ } catch (FileNotFoundException e) {
+ Logger.error(this, "Peer "+source+" asked us for the
blob file for the revocation key, we have downloaded it but don't have the file
even though we did have it when we checked!: "+e, e);
+ return true;
+ }
+
+ final PartiallyReceivedBulk prb;
+ long length;
+ try {
+ length = raf.size();
+ prb = new
PartiallyReceivedBulk(updateManager.node.getUSM(), length,
+ Node.PACKET_SIZE, raf, true);
+ } catch (IOException e) {
+ Logger.error(this, "Peer "+source+" asked us for the
blob file for the revocation key, we have downloaded it but we can't determine
the file size: "+e, e);
+ return true;
+ }
+
+ final BulkTransmitter bt;
+ try {
+ bt = new BulkTransmitter(prb, source, uid,
updateManager.node.outputThrottle);
+ } catch (DisconnectedException e) {
+ Logger.error(this, "Peer "+source+" asked us for the
blob file for the revocation key, then disconnected: "+e, e);
+ return true;
+ }
+
+ final Runnable r = new Runnable() {
+ public void run() {
+ if(!bt.send()) {
+ Logger.error(this, "Failed to send
revocation key blob to "+source.getPeer()+" : "+source.getName());
+ } else {
+ Logger.normal(this, "Sent revocation
key blob to "+source.getPeer()+" : "+source.getName());
+ }
+ }
+
+ };
+
+ Message msg = DMT.createUOMSendingRevocation(uid, length,
updateManager.revocationURI.toString());
+
+ try {
+ source.sendAsync(msg, new AsyncMessageCallback() {
+ public void acknowledged() {
+ if(Logger.shouldLog(Logger.MINOR, this))
+ Logger.minor(this, "Sending
data...");
+ // Send the data
+ Thread t = new Thread(r, "Revocation
key send for "+uid+" to "+source.getPeer()+" : "+source.getName());
+ t.setDaemon(true);
+ t.start();
+ }
+ public void disconnected() {
+ // Argh
+ Logger.error(this, "Peer "+source+"
asked us for the blob file for the revocation key, then disconnected when we
tried to send the UOMSendingRevocation");
+ }
+
+ public void fatalError() {
+ // Argh
+ Logger.error(this, "Peer "+source+"
asked us for the blob file for the revocation key, then got a fatal error when
we tried to send the UOMSendingRevocation");
+ }
+
+ public void sent() {
+ if(Logger.shouldLog(Logger.MINOR, this))
+ Logger.minor(this, "Message
sent, data soon");
+ }
+
+ public String toString() {
+ return super.toString() +
"("+uid+":"+source.getPeer()+")";
+ }
+
+ }, 0, null);
+ } catch (NotConnectedException e) {
+ Logger.error(this, "Peer "+source+" asked us for the
blob file for the revocation key, then disconnected when we tried to send the
UOMSendingRevocation: "+e, e);
+ return true;
+ }
+
+ return true;
+ }
+
+ public boolean handleSendingRevocation(Message m, final PeerNode
source) {
+ final long uid = m.getLong(DMT.UID);
+ final long length = m.getLong(DMT.FILE_LENGTH);
+ String key = m.getString(DMT.REVOCATION_KEY);
+ FreenetURI revocationURI;
+ try {
+ revocationURI = new FreenetURI(key);
+ } catch (MalformedURLException e) {
+ Logger.error(this, "Failed receiving recovation because
URI not parsable: "+e+" for "+key, e);
+ System.err.println("Failed receiving recovation because
URI not parsable: "+e+" for "+key);
+ e.printStackTrace();
+ cancelSend(source, uid);
+ return true;
+ }
+
+ if(!revocationURI.equals(updateManager.revocationURI)) {
+ System.err.println("Node sending us a revocation
certificate from the wrong URI:\n"+
+ "Node: "+source.getPeer()+" :
"+source.getName()+"\n"+
+ "Our URI:
"+updateManager.revocationURI+"\n"+
+ "Their URI: "+revocationURI);
+ cancelSend(source, uid);
+ return true;
+ }
+
+ if(updateManager.isBlown()) {
+ if(Logger.shouldLog(Logger.MINOR, this))
+ Logger.minor(this, "Already blown, so not
receiving from "+source+ "("+uid+")");
+ cancelSend(source, uid);
+ return true;
+ }
+
+ if(length > NodeUpdateManager.MAX_REVOCATION_KEY_LENGTH) {
+ System.err.println("Node "+source.getPeer()+" :
"+source.getName()+" offered us a revocation certificate
"+SizeUtil.formatSize(length)+" long. This is unacceptably long so we have
refused the transfer.");
+ Logger.error(this, "Node "+source.getPeer()+" :
"+source.getName()+" offered us a revocation certificate
"+SizeUtil.formatSize(length)+" long. This is unacceptably long so we have
refused the transfer.");
+ synchronized(UpdateOverMandatoryManager.this) {
+ nodesSayKeyRevokedFailedTransfer.add(source);
+ }
+ cancelSend(source, uid);
+ return true;
+ }
+
+ // Okay, we can receive it
+
+ final File temp;
+
+ try {
+ temp = File.createTempFile("revocation-", ".fblob.tmp",
updateManager.node.getNodeDir());
+ temp.deleteOnExit();
+ } catch (IOException e) {
+ System.err.println("Cannot save revocation certificate
to disk and therefore cannot fetch it from our peer!: "+e);
+ e.printStackTrace();
+ updateManager.blow("Cannot fetch the revocation
certificate from our peer because we cannot write it to disk: "+e);
+ cancelSend(source, uid);
+ return true;
+ }
+
+ RandomAccessFileWrapper raf;
+ try {
+ raf = new RandomAccessFileWrapper(temp, "rw");
+ } catch (FileNotFoundException e) {
+ Logger.error(this, "Peer "+source+" asked us for the
blob file for the revocation key, we have downloaded it but don't have the file
even though we did have it when we checked!: "+e, e);
+ return true;
+ }
+
+ PartiallyReceivedBulk prb = new
PartiallyReceivedBulk(updateManager.node.getUSM(), length,
+ Node.PACKET_SIZE, raf, false);
+
+ final BulkReceiver br = new BulkReceiver(prb, source, uid);
+
+ Thread t = new Thread(new Runnable() {
+
+ public void run() {
+ if(br.receive()) {
+ // Success!
+ processRevocationBlob(temp, source);
+ } else {
+ Logger.error(this, "Failed to transfer
revocation certificate from "+source);
+ System.err.println("Failed to transfer
revocation certificate from "+source);
+
synchronized(UpdateOverMandatoryManager.this) {
+
nodesSayKeyRevokedFailedTransfer.add(source);
+ }
+ }
+ }
+
+ }, "Revocation key receive for "+uid+" from
"+source.getPeer()+" : "+source.getName());
+
+ t.setDaemon(true);
+ t.start();
+
+ return true;
+ }
+
+ /**
+ * Process a binary blob for a revocation certificate (the revocation
key).
+ * @param temp The file it was written to.
+ */
+ protected void processRevocationBlob(final File temp, final PeerNode
source) {
+
+ SimpleBlockSet blocks = new SimpleBlockSet();
+
+ DataInputStream dis = null;
+ try {
+ dis = new DataInputStream(new BufferedInputStream(new
FileInputStream(temp)));
+ BinaryBlob.readBinaryBlob(dis, blocks, true);
+ } catch (FileNotFoundException e) {
+ Logger.error(this, "Somebody deleted "+temp+" ? We lost
the revocation certificate from "+source.userToString()+"!");
+ System.err.println("Somebody deleted "+temp+" ? We lost
the revocation certificate from "+source.userToString()+"!");
+ return;
+ } catch (IOException e) {
+ Logger.error(this, "Could not read revocation cert from
temp file "+temp+" from node "+source.userToString()+" !");
+ System.err.println("Could not read revocation cert from
temp file "+temp+" from node "+source.userToString()+" !");
+ // FIXME will be kept until exit for debugging purposes
+ return;
+ } catch (BinaryBlobFormatException e) {
+ Logger.error(this, "Peer "+source.userToString()+" sent
us an invalid revocation certificate!: "+e, e);
+ System.err.println("Peer "+source.userToString()+" sent
us an invalid revocation certificate!: "+e);
+ e.printStackTrace();
+ synchronized(UpdateOverMandatoryManager.this) {
+ nodesSayKeyRevokedFailedTransfer.add(source);
+ }
+ // FIXME will be kept until exit for debugging purposes
+ return;
+ } finally {
+ if(dis != null)
+ try {
+ dis.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ // Fetch our revocation key from the datastore plus the binary
blob
+
+ FetchContext tempContext =
updateManager.node.clientCore.makeClient((short)0, true).getFetchContext();
+ tempContext.localRequestOnly = true;
+ tempContext.blocks = blocks;
+
+ File f;
+ FileBucket b = null;
+ try {
+ f = File.createTempFile("revocation-", ".fblob.tmp",
updateManager.node.getNodeDir());
+ b = new FileBucket(f, false, false, true, true, true);
+ } catch (IOException e) {
+ Logger.error(this, "Cannot share revocation key from
"+source.userToString()+" with our peers because cannot write the cleaned
version to disk: "+e, e);
+ System.err.println("Cannot share revocation key from
"+source.userToString()+" with our peers because cannot write the cleaned
version to disk: "+e);
+ e.printStackTrace();
+ b = null;
+ f = null;
+ }
+ final FileBucket cleanedBlob = b;
+ final File cleanedBlobFile = f;
+
+ ClientCallback myCallback = new ClientCallback() {
+
+ public void onFailure(FetchException e, ClientGetter
state) {
+ if(e.mode == FetchException.CANCELLED) {
+ // Eh?
+ Logger.error(this, "Cancelled fetch
from store/blob of revocation certificate from "+source.userToString());
+ System.err.println("Cancelled fetch
from store/blob of revocation certificate from "+source.userToString()+" to
"+temp+" - please report to developers");
+ // Probably best to keep files around
for now.
+ } else if(e.isFatal()) {
+ // Blown: somebody inserted a
revocation message, but it was corrupt as inserted
+ // However it had valid signatures etc.
+
+ // Blow the update, and propagate the
revocation certificate.
+
updateManager.revocationChecker.onFailure(e, state, cleanedBlobFile);
+ temp.delete();
+ } else {
+ Logger.error(this, "Failed to fetch
revocation certificate from blob from "+source.userToString());
+ System.err.println("Failed to fetch
revocation certificate from blob from "+source.userToString());
+
synchronized(UpdateOverMandatoryManager.this) {
+
nodesSayKeyRevokedFailedTransfer.add(source);
+ }
+ }
+ }
+
+ public void onFailure(InsertException e,
BaseClientPutter state) {
+ // Ignore, not possible
+ }
+
+ public void onFetchable(BaseClientPutter state) {
+ // Irrelevant
+ }
+
+ public void onGeneratedURI(FreenetURI uri,
BaseClientPutter state) {
+ // Ignore, not possible
+ }
+
+ public void onMajorProgress() {
+ // Ignore
+ }
+
+ public void onSuccess(FetchResult result, ClientGetter
state) {
+
updateManager.revocationChecker.onSuccess(result, state, cleanedBlobFile);
+ temp.delete();
+ }
+
+ public void onSuccess(BaseClientPutter state) {
+ // Ignore, not possible
+ }
+
+ };
+
+ ClientGetter cg = new ClientGetter(myCallback,
+
updateManager.node.clientCore.requestStarters.chkFetchScheduler,
+
updateManager.node.clientCore.requestStarters.sskFetchScheduler,
+ updateManager.revocationURI, tempContext,
(short)0, this, null, cleanedBlob);
+
+ try {
+ cg.start();
+ } catch (FetchException e1) {
+ myCallback.onFailure(e1, cg);
+ }
+
+ }
+
+ private void cancelSend(PeerNode source, long uid) {
+ Message msg = DMT.createFNPBulkReceiveAborted(uid);
+ try {
+ source.sendAsync(msg, null, 0, null);
+ } catch (NotConnectedException e1) {
+ // Ignore
+ }
+ }
}