[freenet-cvs] r13535 - in trunk/freenet/src/freenet: l10n node/updater
toad at freenetproject.org
toad at freenetproject.org
Tue Jun 12 17:32:13 UTC 2007
Author: toad
Date: 2007-06-12 17:32:13 +0000 (Tue, 12 Jun 2007)
New Revision: 13535
Modified:
trunk/freenet/src/freenet/l10n/freenet.l10n.en.properties
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:
If a peer says the auto-update is blown, tell the user, and prevent auto-updating while we try to fetch it (except we don't fetch it yet).
Modified: trunk/freenet/src/freenet/l10n/freenet.l10n.en.properties
===================================================================
--- trunk/freenet/src/freenet/l10n/freenet.l10n.en.properties 2007-06-12 17:09:04 UTC (rev 13534)
+++ trunk/freenet/src/freenet/l10n/freenet.l10n.en.properties 2007-06-12 17:32:13 UTC (rev 13535)
@@ -607,6 +607,13 @@
PeerManagerUserAlert.tooOldNeverConnectedPeers=One or more of your node's peers have never connected in the two weeks since they were added. Consider removing them since they are marginally affecting performance (wasting packets talking to nodes that aren't there).
PeerManagerUserAlert.tooOldNeverConnectedPeersTitle=Never connected peer(s) too old
PeerManagerUserAlert.twoConns=This node has only two connections. Performance and security will not be very good, and your node is not doing any routing for other nodes. Your node is embedded like a 'chain' in the network and does not contribute (much) to the network's health. Try to get at least 3 (ideally more like 5-10) connected peers at any given time.
+PeersSayKeyBlownAlert.intro=One or more of your peers says that the auto-update key is blown! This means that an attacker may know the private key for the auto-update system and can therefore cause your node to run code of his choice! The auto-update system has been disabled. It is also possible that your peers are deliberately lying about it.
+PeersSayKeyBlownAlert.fetching=Your node is attempting to download the revocation certificate to find out more details.
+PeersSayKeyBlownAlert.failedFetch=Your node has been unable to download the revocation certificate. Possible causes include an attack on your node to try to get you to update despite the key being blown, or your nodes lying about the key being blown. Please contact the developers or other Freenet users to sort out this mess.
+PeersSayKeyBlownAlert.connectedSayBlownLabel=These connected nodes say that the key has been blown (we are trying to download the revocation cert from them):
+PeersSayKeyBlownAlert.disconnectedSayBlownLabel=These nodes told us that the key has been blown, but then disconnected, so we could not fetch the revocation certificate:
+PeersSayKeyBlownAlert.failedTransferSayBlownLabel=These nodes told us that the key has been blown, but then failed to transfer the revocation certificate:
+PeersSayKeyBlownAlert.titleWithCount=Auto-update key blown according to ${count} peer(s)!
PluginManager.cannotSetOnceLoaded=Cannot set the plugins list once loaded
PluginManager.loadedOnStartup=Plugins to load on startup
PluginManager.loadedOnStartupLong=Classpath, name and location for plugins to load when node starts up
Modified: trunk/freenet/src/freenet/node/updater/NodeUpdateManager.java
===================================================================
--- trunk/freenet/src/freenet/node/updater/NodeUpdateManager.java 2007-06-12 17:09:04 UTC (rev 13534)
+++ trunk/freenet/src/freenet/node/updater/NodeUpdateManager.java 2007-06-12 17:32:13 UTC (rev 13535)
@@ -64,6 +64,7 @@
final RevocationChecker revocationChecker;
private String revocationMessage;
private boolean hasBeenBlown;
+ private boolean peersSayBlown;
/** Is there a new main jar ready to deploy? */
private boolean hasNewMainJar;
@@ -329,6 +330,7 @@
synchronized(this) {
if(!(hasNewMainJar || hasNewExtJar)) return false; // no jar
if(hasBeenBlown) return false; // Duh
+ if(peersSayBlown) return false;
// Don't immediately deploy if still fetching
startedMillisAgo = now - Math.max(startedFetchingNextMainJar, startedFetchingNextExtJar);
if(startedMillisAgo < WAIT_FOR_SECOND_FETCH_TO_COMPLETE)
@@ -350,11 +352,18 @@
try {
synchronized(this) {
if(hasBeenBlown) {
- String msg = "Trying to update but key has been blown! Message was "+revocationMessage;
+ String msg = "Trying to update but key has been blown! Not updating, message was "+revocationMessage;
Logger.error(this, msg);
System.err.println(msg);
return;
}
+ if(peersSayBlown) {
+ String msg = "Trying to update but at least one peer says the key has been blown! Not updating.";
+ Logger.error(this, msg);
+ System.err.println(msg);
+ return;
+
+ }
if(!isEnabled()) return;
if(!(isAutoUpdateAllowed || armed)) return;
if(!isReadyToDeployUpdate(false)) return;
@@ -785,4 +794,18 @@
}
+ /** Called when a peer indicates in its UOMAnnounce that it has fetched the revocation key
+ * (or failed to do so in a way suggesting that somebody knows the key).
+ * @param source The node which is claiming this.
+ */
+ void peerClaimsKeyBlown(PeerNode source) {
+ // Note that UpdateOverMandatoryManager manages the list of peers who think this.
+ // All we have to do is cancel the update.
+
+ synchronized(this) {
+ peersSayBlown = false;
+ armed = false;
+ }
+ }
+
}
Modified: trunk/freenet/src/freenet/node/updater/RevocationChecker.java
===================================================================
--- trunk/freenet/src/freenet/node/updater/RevocationChecker.java 2007-06-12 17:09:04 UTC (rev 13534)
+++ trunk/freenet/src/freenet/node/updater/RevocationChecker.java 2007-06-12 17:32:13 UTC (rev 13535)
@@ -155,6 +155,10 @@
}
private void moveBlob() {
+ if(tmpBlobFile == null) {
+ Logger.error(this, "No temporary binary blob file moving it: may not be able to propagate revocation, bug???");
+ return;
+ }
if(!tmpBlobFile.renameTo(blobFile)) {
blobFile.delete();
if(!tmpBlobFile.renameTo(blobFile)) {
@@ -162,23 +166,24 @@
System.err.println("Not able to rename binary blob for revocation fetcher: "+tmpBlobFile+" -> "+blobFile+" - may not be able to tell other peers about this revocation");
}
}
- if(tmpBlobFile != null)
- tmpBlobFile.renameTo(blobFile);
}
public void onFailure(FetchException e, ClientGetter state) {
Logger.minor(this, "Revocation fetch failed: "+e);
- if(tmpBlobFile != null) tmpBlobFile.delete();
logMINOR = Logger.shouldLog(Logger.MINOR, this);
int errorCode = e.getMode();
boolean completed = false;
long now = System.currentTimeMillis();
- if(errorCode == FetchException.CANCELLED) return; // cancelled by us above, or killed; either way irrelevant and doesn't need to be restarted
+ if(errorCode == FetchException.CANCELLED) {
+ if(tmpBlobFile != null) tmpBlobFile.delete();
+ return; // cancelled by us above, or killed; either way irrelevant and doesn't need to be restarted
+ }
if(e.isFatal()) {
manager.blow("Permanent error fetching revocation (error inserting the revocation key?): "+e.toString());
moveBlob(); // other peers need to know
return;
}
+ if(tmpBlobFile != null) tmpBlobFile.delete();
if(e.newURI != null) {
manager.blow("Revocation URI redirecting to "+e.newURI+" - maybe you set the revocation URI to the update URI?");
}
Modified: trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java
===================================================================
--- trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java 2007-06-12 17:09:04 UTC (rev 13534)
+++ trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java 2007-06-12 17:32:13 UTC (rev 13535)
@@ -3,9 +3,20 @@
* http://www.gnu.org/ for further details of the GPL. */
package freenet.node.updater;
+import java.net.MalformedURLException;
+import java.util.HashSet;
+import java.util.Vector;
+
+import freenet.io.comm.AsyncMessageCallback;
import freenet.io.comm.DMT;
import freenet.io.comm.Message;
+import freenet.io.comm.NotConnectedException;
+import freenet.keys.FreenetURI;
+import freenet.l10n.L10n;
import freenet.node.PeerNode;
+import freenet.node.useralerts.UserAlert;
+import freenet.support.HTMLNode;
+import freenet.support.Logger;
/**
* Co-ordinates update over mandatory. Update over mandatory = updating from your peers, even
@@ -18,8 +29,19 @@
final NodeUpdateManager updateManager;
+ /** Set of WeakReferences to PeerNode's which say (or said before they disconnected)
+ * the key has been revoked */
+ private final HashSet nodesSayKeyRevoked;
+ /** Set of WeakReferences to PeerNode's which say the key has been revoked but failed
+ * to transfer the revocation key. */
+ private final HashSet nodesSayKeyRevokedFailedTransfer;
+
+ private UserAlert alert;
+
public UpdateOverMandatoryManager(NodeUpdateManager manager) {
this.updateManager = manager;
+ nodesSayKeyRevoked = new HashSet();
+ nodesSayKeyRevokedFailedTransfer = new HashSet();
}
/**
@@ -29,7 +51,7 @@
* @param source The PeerNode which sent the message.
* @return True unless we don't want the message (in this case, always true).
*/
- public boolean handleAnnounce(Message m, PeerNode source) {
+ public boolean handleAnnounce(Message m, final PeerNode source) {
String jarKey = m.getString(DMT.MAIN_JAR_KEY);
String extraJarKey = m.getString(DMT.EXTRA_JAR_KEY);
String revocationKey = m.getString(DMT.REVOCATION_KEY);
@@ -43,14 +65,263 @@
long extraJarFileLength = m.getLong(DMT.EXTRA_JAR_FILE_LENGTH);
int pingTime = m.getInt(DMT.PING_TIME);
int delayTime = m.getInt(DMT.BWLIMIT_DELAY_TIME);
- System.err.println("Update Over Mandatory offer from node "+source.getPeer()+" : "+source.getName()+":");
- System.err.println("Main jar key: "+jarKey+" version="+mainJarVersion+" length="+mainJarFileLength);
- System.err.println("Extra jar key: "+extraJarKey+" version="+extraJarVersion+" length="+extraJarFileLength);
- System.err.println("Revocation key: "+revocationKey+" found="+haveRevocationKey+" length="+revocationKeyFileLength+" last had 3 DNFs "+revocationKeyLastTried+" ms ago, "+revocationKeyDNFs+" DNFs so far");
- System.err.println("Load stats: "+pingTime+"ms ping, "+delayTime+"ms bwlimit delay time");
+
+ // Log it
+
+ boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
+ if(logMINOR) {
+ Logger.minor(this, "Update Over Mandatory offer from node "+source.getPeer()+" : "+source.getName()+":");
+ Logger.minor(this, "Main jar key: "+jarKey+" version="+mainJarVersion+" length="+mainJarFileLength);
+ Logger.minor(this, "Extra jar key: "+extraJarKey+" version="+extraJarVersion+" length="+extraJarFileLength);
+ Logger.minor(this, "Revocation key: "+revocationKey+" found="+haveRevocationKey+" length="+revocationKeyFileLength+" last had 3 DNFs "+revocationKeyLastTried+" ms ago, "+revocationKeyDNFs+" DNFs so far");
+ Logger.minor(this, "Load stats: "+pingTime+"ms ping, "+delayTime+"ms bwlimit delay time");
+ }
+
+ // Now the core logic
+
+ // First off, if a node says it has the revocation key, and its key is the same as ours,
+ // we should 1) suspend any auto-updates and tell the user, 2) try to download it, and
+ // 3) if the download fails, move the notification; if the download succeeds, process it
+
+ if(haveRevocationKey) {
+ // First, is the key the same as ours?
+ try {
+ FreenetURI revocationURI = new FreenetURI(revocationKey);
+ if(/*revocationURI.equals(updateManager.revocationURI)*/true) {
+
+ // Uh oh...
+
+ // Have to do this first to avoid race condition
+ synchronized(this) {
+ nodesSayKeyRevoked.add(source);
+ }
+
+ // Disable the update
+ updateManager.peerClaimsKeyBlown(source);
+
+ // Tell the user
+ alertUser();
+
+ System.err.println("Your peer "+source.getPeer()+" : "+source.getName()+" says that the auto-update key is blown!");
+ System.err.println("Attempting to fetch it...");
+
+ // Try to transfer it.
+
+ Message msg = DMT.createUOMRequestRevocation();
+ source.sendAsync(msg, new AsyncMessageCallback() {
+ public void acknowledged() {
+ // Ok
+ }
+ public void disconnected() {
+ // :(
+ System.err.println("Failed to send request for revocation key to "+source.getPeer()+" : "+source.getName()+" because it disconnected!");
+ synchronized(UpdateOverMandatoryManager.this) {
+ nodesSayKeyRevokedFailedTransfer.add(source);
+ }
+ }
+ public void fatalError() {
+ // Not good!
+ System.err.println("Failed to send request for revocation key to "+source.getPeer()+" : "+source.getName()+" because of a fatal error.");
+ }
+ public void sent() {
+ // Cool
+ }
+ }, 0, null);
+
+ // The reply message will start the transfer. It includes the revocation URI
+ // so we can tell if anything wierd is happening.
+
+ } else {
+ // Should probably also be a useralert?
+ Logger.normal(this, "Node "+source+" sent us a UOM claiming that the auto-update key was blown, but it used a different key to us: \nour key="+updateManager.revocationURI+"\nhis key="+revocationURI);
+ System.err.println("Node "+source.getPeer()+" : "+source.getName()+" sent us a UOM claiming that the revocation key was blown, but it used a different key to us: \nour key="+updateManager.revocationURI+"\nhis key="+revocationURI);
+ }
+ } catch (MalformedURLException e) {
+ // Should maybe be a useralert?
+ Logger.error(this, "Node "+source+" sent us a UOMAnnounce claiming that the auto-update key was blown, but it had an invalid revocation URI: "+revocationKey+" : "+e, e);
+ System.err.println("Node "+source.getPeer()+" : "+source.getName()+" sent us a UOMAnnounce claiming that the revocation key was blown, but it had an invalid revocation URI: "+revocationKey+" : "+e);
+ } catch (NotConnectedException e) {
+ System.err.println("Node "+source+" says that the auto-update key was blown, but has now gone offline! Something BAD is happening!");
+ Logger.error(this, "Node "+source+" says that the auto-update key was blown, but has now gone offline! Something BAD is happening!");
+ synchronized(UpdateOverMandatoryManager.this) {
+ nodesSayKeyRevokedFailedTransfer.add(source);
+ }
+ }
+
+ }
+
return true;
}
+ private void alertUser() {
+ synchronized(this) {
+ if(alert != null) return;
+ alert = new PeersSayKeyBlownAlert();
+ }
+ updateManager.node.clientCore.alerts.register(alert);
+ }
+
+ class PeersSayKeyBlownAlert implements UserAlert {
+
+ public String dismissButtonText() {
+ // Cannot dismiss
+ return null;
+ }
+
+ public HTMLNode getHTMLText() {
+ HTMLNode div = new HTMLNode("div");
+
+ div.addChild("p").addChild("#", l10n("intro"));
+
+ PeerNode[][] nodes = getNodesSayBlown();
+ PeerNode[] nodesSayBlownConnected = nodes[0];
+ PeerNode[] nodesSayBlownDisconnected = nodes[1];
+ PeerNode[] nodesSayBlownFailedTransfer = nodes[2];
+
+ if(nodesSayBlownConnected.length > 0) {
+ div.addChild("p").addChild("#", l10n("fetching"));
+ } else {
+ div.addChild("p").addChild("#", l10n("failedFetch"));
+ }
+
+ if(nodesSayBlownConnected.length > 0) {
+ div.addChild("p").addChild("#", l10n("connectedSayBlownLabel"));
+ HTMLNode list = div.addChild("ul");
+ for(int i=0;i<nodesSayBlownConnected.length;i++) {
+ list.addChild("li", nodesSayBlownConnected[i].getName()+" ("+nodesSayBlownConnected[i].getPeer()+")");
+ }
+ }
+
+ if(nodesSayBlownDisconnected.length > 0) {
+ div.addChild("p").addChild("#", l10n("disconnectedSayBlownLabel"));
+ HTMLNode list = div.addChild("ul");
+ for(int i=0;i<nodesSayBlownDisconnected.length;i++) {
+ list.addChild("li", nodesSayBlownDisconnected[i].getName()+" ("+nodesSayBlownDisconnected[i].getPeer()+")");
+ }
+ }
+
+ if(nodesSayBlownFailedTransfer.length > 0) {
+ div.addChild("p").addChild("#", l10n("failedTransferSayBlownLabel"));
+ HTMLNode list = div.addChild("ul");
+ for(int i=0;i<nodesSayBlownFailedTransfer.length;i++) {
+ list.addChild("li", nodesSayBlownFailedTransfer[i].getName()+" ("+nodesSayBlownFailedTransfer[i].getPeer()+")");
+ }
+ }
+
+ return div;
+ }
+
+ private String l10n(String key) {
+ return L10n.getString("PeersSayKeyBlownAlert."+key);
+ }
+
+ private String l10n(String key, String pattern, String value) {
+ return L10n.getString("PeersSayKeyBlownAlert."+key, pattern, value);
+ }
+
+ public short getPriorityClass() {
+ return UserAlert.CRITICAL_ERROR;
+ }
+
+ public String getText() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(l10n("intro")).append("\n\n");
+ PeerNode[][] nodes = getNodesSayBlown();
+ PeerNode[] nodesSayBlownConnected = nodes[0];
+ PeerNode[] nodesSayBlownDisconnected = nodes[1];
+ PeerNode[] nodesSayBlownFailedTransfer = nodes[2];
+
+ if(nodesSayBlownConnected.length > 0) {
+ sb.append(l10n("fetching")).append("\n\n");
+ } else {
+ sb.append(l10n("failedFetch")).append("\n\n");
+ }
+
+ if(nodesSayBlownConnected.length > 0) {
+ sb.append(l10n("connectedSayBlownLabel")).append("\n\n");
+ for(int i=0;i<nodesSayBlownConnected.length;i++) {
+ sb.append(nodesSayBlownConnected[i].getName()+" ("+nodesSayBlownConnected[i].getPeer()+")").append("\n");
+ }
+ sb.append("\n");
+ }
+
+ if(nodesSayBlownDisconnected.length > 0) {
+ sb.append(l10n("disconnectedSayBlownLabel"));
+
+ for(int i=0;i<nodesSayBlownDisconnected.length;i++) {
+ sb.append(nodesSayBlownDisconnected[i].getName()+" ("+nodesSayBlownDisconnected[i].getPeer()+")").append("\n");
+ }
+ sb.append("\n");
+ }
+
+ if(nodesSayBlownFailedTransfer.length > 0) {
+ sb.append(l10n("failedTransferSayBlownLabel"));
+
+ for(int i=0;i<nodesSayBlownFailedTransfer.length;i++) {
+ sb.append(nodesSayBlownFailedTransfer[i].getName()+" ("+nodesSayBlownFailedTransfer[i].getPeer()+")").append('\n');
+ }
+ sb.append("\n");
+ }
+
+ return sb.toString();
+ }
+
+ public String getTitle() {
+ return l10n("titleWithCount", "count", Integer.toString(nodesSayKeyRevoked.size()));
+ }
+
+ public boolean isValid() {
+ return true;
+ }
+
+ public void isValid(boolean validity) {
+ // Do nothing
+ }
+
+ public void onDismiss() {
+ // Do nothing
+ }
+
+ public boolean shouldUnregisterOnDismiss() {
+ // Can't dismiss
+ return false;
+ }
+
+ public boolean userCanDismiss() {
+ // Can't dismiss
+ return false;
+ }
+
+ }
+
+ public PeerNode[][] getNodesSayBlown() {
+ Vector nodesConnectedSayRevoked = new Vector();
+ Vector nodesDisconnectedSayRevoked = new Vector();
+ Vector nodesFailedSayRevoked = new Vector();
+ synchronized(this) {
+ PeerNode[] nodesSayRevoked = (PeerNode[]) nodesSayKeyRevoked.toArray(new PeerNode[nodesSayKeyRevoked.size()]);
+ for(int i=0;i<nodesSayRevoked.length;i++) {
+ PeerNode pn = nodesSayRevoked[i];
+ if(nodesSayKeyRevokedFailedTransfer.contains(pn))
+ nodesFailedSayRevoked.add(pn);
+ else
+ nodesConnectedSayRevoked.add(pn);
+ }
+ }
+ for(int i=0;i<nodesConnectedSayRevoked.size();i++) {
+ PeerNode pn = (PeerNode) nodesConnectedSayRevoked.get(i);
+ if(!pn.isConnected()) {
+ nodesDisconnectedSayRevoked.add(pn);
+ nodesConnectedSayRevoked.remove(i);
+ i--;
+ continue;
+ }
+ }
+ return new PeerNode[][] {
+ (PeerNode[]) nodesConnectedSayRevoked.toArray(new PeerNode[nodesConnectedSayRevoked.size()]),
+ (PeerNode[]) nodesDisconnectedSayRevoked.toArray(new PeerNode[nodesDisconnectedSayRevoked.size()]),
+ (PeerNode[]) nodesFailedSayRevoked.toArray(new PeerNode[nodesFailedSayRevoked.size()]),
+ };
+ }
-
}
More information about the cvs
mailing list