[freenet-cvs] r14803 - in branches/freenet-jfk/src/freenet/node: . fcp simulator updater useralerts
kryptos at freenetproject.org
kryptos at freenetproject.org
Mon Aug 20 14:10:53 UTC 2007
Author: kryptos
Date: 2007-08-20 14:10:51 +0000 (Mon, 20 Aug 2007)
New Revision: 14803
Added:
branches/freenet-jfk/src/freenet/node/DarknetPeerNode.java
branches/freenet-jfk/src/freenet/node/DarknetPeerNodeStatus.java
branches/freenet-jfk/src/freenet/node/FailureTable.java
branches/freenet-jfk/src/freenet/node/FailureTableEntry.java
branches/freenet-jfk/src/freenet/node/NodeCrypto.java
branches/freenet-jfk/src/freenet/node/NodeCryptoConfig.java
branches/freenet-jfk/src/freenet/node/NodeIPPortDetector.java
branches/freenet-jfk/src/freenet/node/NodeInitException.java
branches/freenet-jfk/src/freenet/node/OpennetDisabledException.java
branches/freenet-jfk/src/freenet/node/OpennetManager.java
branches/freenet-jfk/src/freenet/node/OpennetPeerNode.java
branches/freenet-jfk/src/freenet/node/OpennetPeerNodeStatus.java
branches/freenet-jfk/src/freenet/node/TimeSkewDetectorCallback.java
branches/freenet-jfk/src/freenet/node/fcp/ListPeerMessage.java
branches/freenet-jfk/src/freenet/node/updater/UpdateOverMandatoryManager.java
branches/freenet-jfk/src/freenet/node/useralerts/OpennetUserAlert.java
branches/freenet-jfk/src/freenet/node/useralerts/TimeSkewDetectedUserAlert.java
Modified:
branches/freenet-jfk/src/freenet/node/CHKInsertSender.java
branches/freenet-jfk/src/freenet/node/ConfigurablePersister.java
branches/freenet-jfk/src/freenet/node/DNSRequester.java
branches/freenet-jfk/src/freenet/node/GlobalProbe.java
branches/freenet-jfk/src/freenet/node/IPDetectorPluginManager.java
branches/freenet-jfk/src/freenet/node/InsertHandler.java
branches/freenet-jfk/src/freenet/node/KeyTracker.java
branches/freenet-jfk/src/freenet/node/Location.java
branches/freenet-jfk/src/freenet/node/LocationManager.java
branches/freenet-jfk/src/freenet/node/LoggingConfigHandler.java
branches/freenet-jfk/src/freenet/node/LowLevelGetException.java
branches/freenet-jfk/src/freenet/node/Node.java
branches/freenet-jfk/src/freenet/node/NodeARKInserter.java
branches/freenet-jfk/src/freenet/node/NodeClientCore.java
branches/freenet-jfk/src/freenet/node/NodeDispatcher.java
branches/freenet-jfk/src/freenet/node/NodeIPDetector.java
branches/freenet-jfk/src/freenet/node/NodePinger.java
branches/freenet-jfk/src/freenet/node/NodeStarter.java
branches/freenet-jfk/src/freenet/node/NodeStats.java
branches/freenet-jfk/src/freenet/node/OutgoingPacketMangler.java
branches/freenet-jfk/src/freenet/node/PacketSender.java
branches/freenet-jfk/src/freenet/node/PeerManager.java
branches/freenet-jfk/src/freenet/node/PeerNode.java
branches/freenet-jfk/src/freenet/node/PeerNodeStatus.java
branches/freenet-jfk/src/freenet/node/Persister.java
branches/freenet-jfk/src/freenet/node/ProbeCallback.java
branches/freenet-jfk/src/freenet/node/RequestHandler.java
branches/freenet-jfk/src/freenet/node/RequestSender.java
branches/freenet-jfk/src/freenet/node/RequestStarter.java
branches/freenet-jfk/src/freenet/node/SSKInsertHandler.java
branches/freenet-jfk/src/freenet/node/SSKInsertSender.java
branches/freenet-jfk/src/freenet/node/SemiOrderedShutdownHook.java
branches/freenet-jfk/src/freenet/node/SendableGet.java
branches/freenet-jfk/src/freenet/node/SendableRequest.java
branches/freenet-jfk/src/freenet/node/SimpleSendableInsert.java
branches/freenet-jfk/src/freenet/node/TestnetHandler.java
branches/freenet-jfk/src/freenet/node/TestnetStatusUploader.java
branches/freenet-jfk/src/freenet/node/TextModeClientInterface.java
branches/freenet-jfk/src/freenet/node/TextModeClientInterfaceServer.java
branches/freenet-jfk/src/freenet/node/Version.java
branches/freenet-jfk/src/freenet/node/fcp/AddPeer.java
branches/freenet-jfk/src/freenet/node/fcp/ClientGet.java
branches/freenet-jfk/src/freenet/node/fcp/ClientGetMessage.java
branches/freenet-jfk/src/freenet/node/fcp/ClientPut.java
branches/freenet-jfk/src/freenet/node/fcp/DiskDirPutFile.java
branches/freenet-jfk/src/freenet/node/fcp/FCPClient.java
branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionHandler.java
branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionInputHandler.java
branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionOutputHandler.java
branches/freenet-jfk/src/freenet/node/fcp/FCPMessage.java
branches/freenet-jfk/src/freenet/node/fcp/FCPServer.java
branches/freenet-jfk/src/freenet/node/fcp/GetFailedMessage.java
branches/freenet-jfk/src/freenet/node/fcp/GetNode.java
branches/freenet-jfk/src/freenet/node/fcp/ListPeerNotesMessage.java
branches/freenet-jfk/src/freenet/node/fcp/ModifyPeer.java
branches/freenet-jfk/src/freenet/node/fcp/ModifyPeerNote.java
branches/freenet-jfk/src/freenet/node/fcp/NodeData.java
branches/freenet-jfk/src/freenet/node/fcp/ProtocolErrorMessage.java
branches/freenet-jfk/src/freenet/node/fcp/RemovePeer.java
branches/freenet-jfk/src/freenet/node/fcp/TestDDACompleteMessage.java
branches/freenet-jfk/src/freenet/node/simulator/RealNodePingTest.java
branches/freenet-jfk/src/freenet/node/simulator/RealNodeRequestInsertTest.java
branches/freenet-jfk/src/freenet/node/simulator/RealNodeRoutingTest.java
branches/freenet-jfk/src/freenet/node/updater/NodeUpdateManager.java
branches/freenet-jfk/src/freenet/node/updater/NodeUpdater.java
branches/freenet-jfk/src/freenet/node/updater/RevocationChecker.java
branches/freenet-jfk/src/freenet/node/useralerts/IPUndetectedUserAlert.java
branches/freenet-jfk/src/freenet/node/useralerts/MeaningfulNodeNameUserAlert.java
branches/freenet-jfk/src/freenet/node/useralerts/N2NTMUserAlert.java
branches/freenet-jfk/src/freenet/node/useralerts/PeerManagerUserAlert.java
branches/freenet-jfk/src/freenet/node/useralerts/UpdatedVersionAvailableUserAlert.java
branches/freenet-jfk/src/freenet/node/useralerts/UserAlertManager.java
Log:
Node Merged with trunk(r14796) and r13448:Link level encryption using JFK
Modified: branches/freenet-jfk/src/freenet/node/CHKInsertSender.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/CHKInsertSender.java 2007-08-20 13:57:42 UTC (rev 14802)
+++ branches/freenet-jfk/src/freenet/node/CHKInsertSender.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -18,6 +18,7 @@
import freenet.keys.CHKBlock;
import freenet.keys.CHKVerifyException;
import freenet.keys.NodeCHK;
+import freenet.support.Executor;
import freenet.support.Logger;
import freenet.support.OOMHandler;
@@ -27,15 +28,17 @@
final AwaitingCompletion completion;
final BlockTransmitter bt;
+ final Executor executor;
- public Sender(AwaitingCompletion ac) {
+ public Sender(AwaitingCompletion ac, Executor executor) {
this.bt = ac.bt;
this.completion = ac;
+ this.executor = executor;
}
public void run() {
try {
- bt.send();
+ bt.send(executor);
if(bt.failedDueToOverload()) {
completion.completedTransfer(false);
} else {
@@ -75,10 +78,8 @@
}
void start() {
- Sender s = new Sender(this);
- Thread senderThread = new Thread(s, "Sender for "+uid+" to "+pn.getPeer());
- senderThread.setDaemon(true);
- senderThread.start();
+ Sender s = new Sender(this, node.executor);
+ node.executor.execute(s, "Sender for "+uid+" to "+pn.getPeer());
}
void completed(boolean timeout, boolean success) {
@@ -137,9 +138,7 @@
}
void start() {
- Thread t = new Thread(this, "CHKInsertSender for UID "+uid+" on "+node.portNumber+" at "+System.currentTimeMillis());
- t.setDaemon(true);
- t.start();
+ node.executor.execute(this, "CHKInsertSender for UID "+uid+" on "+node.getDarknetPortNumber()+" at "+System.currentTimeMillis());
}
static boolean logMINOR;
@@ -247,9 +246,9 @@
PeerNode next;
// Can backtrack, so only route to nodes closer than we are to target.
double nextValue;
- next = node.peers.closerPeer(source, nodesRoutedTo, nodesNotIgnored, target, true, node.isAdvancedModeEnabled(), -1);
+ next = node.peers.closerPeer(source, nodesRoutedTo, nodesNotIgnored, target, true, node.isAdvancedModeEnabled(), -1, null);
if(next != null)
- nextValue = next.getLocation().getValue();
+ nextValue = next.getLocation();
else
nextValue = -1.0;
@@ -263,7 +262,7 @@
Message req;
synchronized (this) {
- if(PeerManager.distance(target, nextValue) > PeerManager.distance(target, closestLocation)) {
+ if(Location.distance(target, nextValue) > Location.distance(target, closestLocation)) {
if(logMINOR) Logger.minor(this, "Backtracking: target="+target+" next="+nextValue+" closest="+closestLocation);
htl = node.decrementHTL(source, htl);
}
@@ -572,6 +571,10 @@
notifyAll();
}
}
+
+ if(status == SUCCESS && next != null)
+ next.onSuccess(true, false);
+
if(logMINOR) Logger.minor(this, "Returning from finish()");
}
@@ -622,16 +625,13 @@
private void makeCompletionWaiter() {
if(logMINOR)
Logger.minor(this, "Creating completion waiter for "+uid);
- Thread t;
synchronized (this) {
if(cw == null)
cw = new CompletionWaiter();
else
return;
}
- t = new Thread(cw, "Completion waiter for "+uid);
- t.setDaemon(true);
- t.start();
+ node.executor.execute(cw, "Completion waiter for "+uid);
}
private class CompletionWaiter implements Runnable {
Modified: branches/freenet-jfk/src/freenet/node/ConfigurablePersister.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/ConfigurablePersister.java 2007-08-20 13:57:42 UTC (rev 14802)
+++ branches/freenet-jfk/src/freenet/node/ConfigurablePersister.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -6,7 +6,6 @@
import freenet.config.InvalidConfigValueException;
import freenet.config.SubConfig;
import freenet.l10n.L10n;
-import freenet.node.Node.NodeInitException;
import freenet.support.api.StringCallback;
public class ConfigurablePersister extends Persister {
@@ -30,7 +29,7 @@
try {
setThrottles(throttleFile);
} catch (InvalidConfigValueException e2) {
- throw new NodeInitException(Node.EXIT_THROTTLE_FILE_ERROR, e2.getMessage());
+ throw new NodeInitException(NodeInitException.EXIT_THROTTLE_FILE_ERROR, e2.getMessage());
}
}
@@ -44,7 +43,10 @@
break;
} else {
try {
- f.createNewFile();
+ if(!f.createNewFile()) {
+ if(f.exists()) continue;
+ throw new InvalidConfigValueException(l10n("doesNotExistCannotCreate"));
+ }
} catch (IOException e) {
throw new InvalidConfigValueException(l10n("doesNotExistCannotCreate"));
}
Modified: branches/freenet-jfk/src/freenet/node/DNSRequester.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/DNSRequester.java 2007-08-20 13:57:42 UTC (rev 14802)
+++ branches/freenet-jfk/src/freenet/node/DNSRequester.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -19,7 +19,7 @@
DNSRequester(Node node) {
this.node = node;
- myThread = new Thread(this, "DNSRequester thread for "+node.portNumber);
+ myThread = new Thread(this, "DNSRequester thread for "+node.getDarknetPortNumber());
myThread.setDaemon(true);
}
Copied: branches/freenet-jfk/src/freenet/node/DarknetPeerNode.java (from rev 14796, trunk/freenet/src/freenet/node/DarknetPeerNode.java)
===================================================================
--- branches/freenet-jfk/src/freenet/node/DarknetPeerNode.java (rev 0)
+++ branches/freenet-jfk/src/freenet/node/DarknetPeerNode.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -0,0 +1,1529 @@
+package freenet.node;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+
+import freenet.client.DefaultMIMETypes;
+import freenet.io.comm.DMT;
+import freenet.io.comm.DisconnectedException;
+import freenet.io.comm.FreenetInetAddress;
+import freenet.io.comm.Message;
+import freenet.io.comm.NotConnectedException;
+import freenet.io.comm.Peer;
+import freenet.io.comm.PeerParseException;
+import freenet.io.comm.ReferenceSignatureVerificationException;
+import freenet.io.comm.RetrievalException;
+import freenet.io.xfer.BulkReceiver;
+import freenet.io.xfer.BulkTransmitter;
+import freenet.io.xfer.PartiallyReceivedBulk;
+import freenet.l10n.L10n;
+import freenet.node.useralerts.N2NTMUserAlert;
+import freenet.node.useralerts.UserAlert;
+import freenet.support.Base64;
+import freenet.support.Fields;
+import freenet.support.HTMLNode;
+import freenet.support.IllegalBase64Exception;
+import freenet.support.Logger;
+import freenet.support.SimpleFieldSet;
+import freenet.support.SizeUtil;
+import freenet.support.io.FileUtil;
+import freenet.support.io.RandomAccessFileWrapper;
+import freenet.support.io.RandomAccessThing;
+
+public class DarknetPeerNode extends PeerNode {
+
+ /** Name of this node */
+ String myName;
+
+ /** True if this peer is not to be connected with */
+ private boolean isDisabled;
+
+ /** True if we don't send handshake requests to this peer, but will connect if we receive one */
+ private boolean isListenOnly;
+
+ /** True if we send handshake requests to this peer in infrequent bursts */
+ private boolean isBurstOnly;
+
+ /** True if we are currently sending this peer a burst of handshake requests */
+ private boolean isBursting;
+
+ /** True if we want to ignore the source port of the node's sent packets.
+ * This is normally set when dealing with an Evil Corporate Firewall which rewrites the port on outgoing
+ * packets but does not redirect incoming packets destined to the rewritten port.
+ * What it does is this: If we have an address with the same IP but a different port, to the detectedPeer,
+ * we use that instead. */
+ private boolean ignoreSourcePort;
+
+ /** True if we want to allow LAN/localhost addresses. */
+ private boolean allowLocalAddresses;
+
+ /** Extra peer data file numbers */
+ private LinkedHashSet extraPeerDataFileNumbers;
+
+ /** Private comment on the peer for /friends/ page */
+ private String privateDarknetComment;
+
+ /** Private comment on the peer for /friends/ page's extra peer data file number */
+ private int privateDarknetCommentFileNumber;
+
+ /** Queued-to-send N2NTM extra peer data file numbers */
+ private LinkedHashSet queuedToSendN2NTMExtraPeerDataFileNumbers;
+
+ /** Number of handshake attempts (while in ListenOnly mode) since the beginning of this burst */
+ private int listeningHandshakeBurstCount;
+
+ /** Total number of handshake attempts (while in ListenOnly mode) to be in this burst */
+ private int listeningHandshakeBurstSize;
+
+ private static boolean logMINOR;
+
+ /**
+ * Create a darknet PeerNode from a SimpleFieldSet
+ * @param fs The SimpleFieldSet to parse
+ * @param node2 The running Node we are part of.
+ */
+ public DarknetPeerNode(SimpleFieldSet fs, Node node2, NodeCrypto crypto, PeerManager peers, boolean fromLocal, OutgoingPacketMangler mangler) throws FSParseException, PeerParseException, ReferenceSignatureVerificationException {
+ super(fs, node2, crypto, peers, fromLocal, mangler, false);
+
+ logMINOR = Logger.shouldLog(Logger.MINOR, this);
+
+ long now = System.currentTimeMillis();
+
+ String name = fs.get("myName");
+ if(name == null) throw new FSParseException("No name");
+ myName = name;
+
+ if(fromLocal) {
+ SimpleFieldSet metadata = fs.subset("metadata");
+
+ isDisabled = Fields.stringToBool(metadata.get("isDisabled"), false);
+ isListenOnly = Fields.stringToBool(metadata.get("isListenOnly"), false);
+ isBurstOnly = Fields.stringToBool(metadata.get("isBurstOnly"), false);
+ ignoreSourcePort = Fields.stringToBool(metadata.get("ignoreSourcePort"), false);
+ allowLocalAddresses = Fields.stringToBool(metadata.get("allowLocalAddresses"), false);
+ }
+
+ listeningHandshakeBurstCount = 0;
+ listeningHandshakeBurstSize = Node.MIN_BURSTING_HANDSHAKE_BURST_SIZE
+ + node.random.nextInt(Node.RANDOMIZED_BURSTING_HANDSHAKE_BURST_SIZE);
+ if(isBurstOnly) {
+ Logger.minor(this, "First BurstOnly mode handshake in "+(sendHandshakeTime - now)+"ms for "+getName()+" (count: "+listeningHandshakeBurstCount+", size: "+listeningHandshakeBurstSize+ ')');
+ }
+
+ // Setup the private darknet comment note
+ privateDarknetComment = "";
+ privateDarknetCommentFileNumber = -1;
+
+ // Setup the extraPeerDataFileNumbers
+ extraPeerDataFileNumbers = new LinkedHashSet();
+
+ // Setup the queuedToSendN2NTMExtraPeerDataFileNumbers
+ queuedToSendN2NTMExtraPeerDataFileNumbers = new LinkedHashSet();
+
+ }
+
+ /**
+ *
+ * Normally this is the address that packets have been received from from this node.
+ * However, if ignoreSourcePort is set, we will search for a similar address with a different port
+ * number in the node reference.
+ */
+ public synchronized Peer getPeer(){
+ Peer detectedPeer = super.getPeer();
+ if(ignoreSourcePort) {
+ FreenetInetAddress addr = detectedPeer == null ? null : detectedPeer.getFreenetAddress();
+ int port = detectedPeer == null ? -1 : detectedPeer.getPort();
+ if(nominalPeer == null) return detectedPeer;
+ for(int i=0;i<nominalPeer.size();i++) {
+ Peer p = (Peer) nominalPeer.get(i);
+ if(p.getPort() != port && p.getFreenetAddress().equals(addr)) {
+ return p;
+ }
+ }
+ }
+ return detectedPeer;
+ }
+
+ /**
+ * @return True, if we are disconnected and it has been a
+ * sufficient time period since we last sent a handshake
+ * attempt.
+ */
+ public boolean shouldSendHandshake() {
+ synchronized(this) {
+ if(isDisabled) return false;
+ if(isListenOnly) return false;
+ if(!super.shouldSendHandshake()) return false;
+ if(isBurstOnly())
+ isBursting = true;
+ else
+ return true;
+ }
+ // Might have changed from burst only to bursting
+ setPeerNodeStatus(System.currentTimeMillis());
+ return true;
+ }
+
+ protected synchronized boolean innerProcessNewNoderef(SimpleFieldSet fs, boolean forARK) throws FSParseException {
+ boolean changedAnything = super.innerProcessNewNoderef(fs, forARK);
+ String name = fs.get("myName");
+ if(name != null && !name.equals(myName)) {
+ changedAnything = true;
+ myName = name;
+ }
+ return changedAnything;
+ }
+
+ public synchronized SimpleFieldSet exportFieldSet() {
+ SimpleFieldSet fs = super.exportFieldSet();
+ fs.putSingle("myName", getName());
+ return fs;
+ }
+
+ public synchronized SimpleFieldSet exportMetadataFieldSet() {
+ SimpleFieldSet fs = super.exportMetadataFieldSet();
+ if(isDisabled)
+ fs.putSingle("isDisabled", "true");
+ if(isListenOnly)
+ fs.putSingle("isListenOnly", "true");
+ if(isBurstOnly)
+ fs.putSingle("isBurstOnly", "true");
+ if(ignoreSourcePort)
+ fs.putSingle("ignoreSourcePort", "true");
+ if(allowLocalAddresses)
+ fs.putSingle("allowLocalAddresses", "true");
+ return fs;
+ }
+
+ public synchronized String getName() {
+ return myName;
+ }
+
+ protected synchronized int getPeerNodeStatus(long now, long backedOffUntil) {
+ if(isDisabled) {
+ return PeerManager.PEER_NODE_STATUS_DISABLED;
+ }
+ int status = super.getPeerNodeStatus(now, backedOffUntil);
+ if(status == PeerManager.PEER_NODE_STATUS_CONNECTED ||
+ status == PeerManager.PEER_NODE_STATUS_CLOCK_PROBLEM ||
+ status == PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF ||
+ status == PeerManager.PEER_NODE_STATUS_CONN_ERROR ||
+ status == PeerManager.PEER_NODE_STATUS_TOO_NEW ||
+ status == PeerManager.PEER_NODE_STATUS_TOO_OLD)
+ return status;
+ if(isListenOnly)
+ return PeerManager.PEER_NODE_STATUS_LISTEN_ONLY;
+ if(isBursting)
+ return PeerManager.PEER_NODE_STATUS_BURSTING;
+ if(isBurstOnly)
+ return PeerManager.PEER_NODE_STATUS_LISTENING;
+ return status;
+ }
+
+ public void enablePeer() {
+ synchronized(this) {
+ isDisabled = false;
+ }
+ setPeerNodeStatus(System.currentTimeMillis());
+ node.peers.writePeers();
+ }
+
+ public void disablePeer() {
+ synchronized(this) {
+ isDisabled = true;
+ }
+ if(isConnected()) {
+ forceDisconnect();
+ }
+ stopARKFetcher();
+ setPeerNodeStatus(System.currentTimeMillis());
+ node.peers.writePeers();
+ }
+
+ public synchronized boolean isDisabled() {
+ return isDisabled;
+ }
+
+ public void setListenOnly(boolean setting) {
+ synchronized(this) {
+ isListenOnly = setting;
+ }
+ if(setting && isBurstOnly()) {
+ setBurstOnly(false);
+ }
+ if(setting) {
+ stopARKFetcher();
+ }
+ setPeerNodeStatus(System.currentTimeMillis());
+ node.peers.writePeers();
+ }
+
+ public synchronized boolean isListenOnly() {
+ return isListenOnly;
+ }
+
+ public void setBurstOnly(boolean setting) {
+ synchronized(this) {
+ isBurstOnly = setting;
+ }
+ if(setting && isListenOnly()) {
+ setListenOnly(false);
+ }
+ long now = System.currentTimeMillis();
+ if(!setting) {
+ synchronized(this) {
+ sendHandshakeTime = now; // don't keep any long handshake delays we might have had under BurstOnly
+ }
+ }
+ setPeerNodeStatus(now);
+ node.peers.writePeers();
+ }
+
+ public void setIgnoreSourcePort(boolean setting) {
+ synchronized(this) {
+ ignoreSourcePort = setting;
+ }
+ }
+
+
+ public boolean isIgnoreSourcePort() {
+ return ignoreSourcePort;
+ }
+
+ public boolean isIgnoreSource() {
+ return ignoreSourcePort;
+ }
+
+ public synchronized boolean isBurstOnly() {
+ return isBurstOnly;
+ }
+
+ public boolean allowLocalAddresses() {
+ synchronized(this) {
+ if(allowLocalAddresses) return true;
+ }
+ return super.allowLocalAddresses();
+ }
+
+ public void setAllowLocalAddresses(boolean setting) {
+ synchronized(this) {
+ allowLocalAddresses = setting;
+ }
+ node.peers.writePeers();
+ }
+
+ public boolean readExtraPeerData() {
+ String extraPeerDataDirPath = node.getExtraPeerDataDir();
+ File extraPeerDataPeerDir = new File(extraPeerDataDirPath+File.separator+getIdentityString());
+ if(!extraPeerDataPeerDir.exists()) {
+ return false;
+ }
+ if(!extraPeerDataPeerDir.isDirectory()) {
+ Logger.error(this, "Extra peer data directory for peer not a directory: "+extraPeerDataPeerDir.getPath());
+ return false;
+ }
+ File[] extraPeerDataFiles = extraPeerDataPeerDir.listFiles();
+ if(extraPeerDataFiles == null) {
+ return false;
+ }
+ boolean gotError = false;
+ boolean readResult = false;
+ for (int i = 0; i < extraPeerDataFiles.length; i++) {
+ Integer fileNumber;
+ try {
+ fileNumber = new Integer(extraPeerDataFiles[i].getName());
+ } catch (NumberFormatException e) {
+ gotError = true;
+ continue;
+ }
+ synchronized(extraPeerDataFileNumbers) {
+ extraPeerDataFileNumbers.add(fileNumber);
+ }
+ readResult = readExtraPeerDataFile(extraPeerDataFiles[i], fileNumber.intValue());
+ if(!readResult) {
+ gotError = true;
+ }
+ }
+ return !gotError;
+ }
+
+ public boolean rereadExtraPeerDataFile(int fileNumber) {
+ if(logMINOR)
+ Logger.minor(this, "Rereading peer data file "+fileNumber+" for "+shortToString());
+ String extraPeerDataDirPath = node.getExtraPeerDataDir();
+ File extraPeerDataPeerDir = new File(extraPeerDataDirPath+File.separator+getIdentityString());
+ if(!extraPeerDataPeerDir.exists()) {
+ Logger.error(this, "Extra peer data directory for peer does not exist: "+extraPeerDataPeerDir.getPath());
+ return false;
+ }
+ if(!extraPeerDataPeerDir.isDirectory()) {
+ Logger.error(this, "Extra peer data directory for peer not a directory: "+extraPeerDataPeerDir.getPath());
+ return false;
+ }
+ File extraPeerDataFile = new File(extraPeerDataDirPath+File.separator+getIdentityString()+File.separator+fileNumber);
+ if(!extraPeerDataFile.exists()) {
+ Logger.error(this, "Extra peer data file for peer does not exist: "+extraPeerDataFile.getPath());
+ return false;
+ }
+ return readExtraPeerDataFile(extraPeerDataFile, fileNumber);
+ }
+
+ public boolean readExtraPeerDataFile(File extraPeerDataFile, int fileNumber) {
+ if(logMINOR) Logger.minor(this, "Reading "+extraPeerDataFile+" : "+fileNumber+" for "+shortToString());
+ boolean gotError = false;
+ if(!extraPeerDataFile.exists()) {
+ if(logMINOR)
+ Logger.minor(this, "Does not exist");
+ return false;
+ }
+ Logger.normal(this, "extraPeerDataFile: "+extraPeerDataFile.getPath());
+ FileInputStream fis;
+ try {
+ fis = new FileInputStream(extraPeerDataFile);
+ } catch (FileNotFoundException e1) {
+ Logger.normal(this, "Extra peer data file not found: "+extraPeerDataFile.getPath());
+ return false;
+ }
+ InputStreamReader isr;
+ try {
+ isr = new InputStreamReader(fis, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new Error("Impossible: JVM doesn't support UTF-8: "+e, e);
+ }
+ BufferedReader br = new BufferedReader(isr);
+ SimpleFieldSet fs = null;
+ try {
+ // Read in the single SimpleFieldSet
+ fs = new SimpleFieldSet(br, false, true);
+ } catch (EOFException e3) {
+ // End of file, fine
+ } catch (IOException e4) {
+ Logger.error(this, "Could not read extra peer data file: "+e4, e4);
+ } finally {
+ try {
+ br.close();
+ } catch (IOException e5) {
+ Logger.error(this, "Ignoring "+e5+" caught reading "+extraPeerDataFile.getPath(), e5);
+ }
+ }
+ if(fs == null) {
+ Logger.normal(this, "Deleting corrupt (too short?) file: "+extraPeerDataFile);
+ deleteExtraPeerDataFile(fileNumber);
+ return true;
+ }
+ boolean parseResult = false;
+ try {
+ parseResult = parseExtraPeerData(fs, extraPeerDataFile, fileNumber);
+ if(!parseResult) {
+ gotError = true;
+ }
+ } catch (FSParseException e2) {
+ Logger.error(this, "Could not parse extra peer data: "+e2+ '\n' +fs.toString(),e2);
+ gotError = true;
+ }
+ return !gotError;
+ }
+
+ private boolean parseExtraPeerData(SimpleFieldSet fs, File extraPeerDataFile, int fileNumber) throws FSParseException {
+ String extraPeerDataTypeString = fs.get("extraPeerDataType");
+ int extraPeerDataType = -1;
+ try {
+ extraPeerDataType = Integer.parseInt(extraPeerDataTypeString);
+ } catch (NumberFormatException e) {
+ Logger.error(this, "NumberFormatException parsing extraPeerDataType ("+extraPeerDataTypeString+") in file "+extraPeerDataFile.getPath());
+ return false;
+ }
+ if(extraPeerDataType == Node.EXTRA_PEER_DATA_TYPE_N2NTM) {
+ node.handleNodeToNodeTextMessageSimpleFieldSet(fs, this, fileNumber);
+ return true;
+ } else if(extraPeerDataType == Node.EXTRA_PEER_DATA_TYPE_PEER_NOTE) {
+ String peerNoteTypeString = fs.get("peerNoteType");
+ int peerNoteType = -1;
+ try {
+ peerNoteType = Integer.parseInt(peerNoteTypeString);
+ } catch (NumberFormatException e) {
+ Logger.error(this, "NumberFormatException parsing peerNoteType ("+peerNoteTypeString+") in file "+extraPeerDataFile.getPath());
+ return false;
+ }
+ if(peerNoteType == Node.PEER_NOTE_TYPE_PRIVATE_DARKNET_COMMENT) {
+ synchronized(privateDarknetComment) {
+ try {
+ privateDarknetComment = new String(Base64.decode(fs.get("privateDarknetComment")));
+ } catch (IllegalBase64Exception e) {
+ Logger.error(this, "Bad Base64 encoding when decoding a private darknet comment SimpleFieldSet", e);
+ return false;
+ }
+ privateDarknetCommentFileNumber = fileNumber;
+ }
+ return true;
+ }
+ Logger.error(this, "Read unknown peer note type '"+peerNoteType+"' from file "+extraPeerDataFile.getPath());
+ return false;
+ } else if(extraPeerDataType == Node.EXTRA_PEER_DATA_TYPE_QUEUED_TO_SEND_N2NTM) {
+ boolean sendSuccess = false;
+ int type = fs.getInt("n2nType", 1); // FIXME remove default
+ fs.putOverwrite("n2nType", Integer.toString(type));
+ if(isConnected()) {
+ Message n2ntm;
+ if(fs.get("extraPeerDataType") != null) {
+ fs.removeValue("extraPeerDataType");
+ }
+ if(fs.get("senderFileNumber") != null) {
+ fs.removeValue("senderFileNumber");
+ }
+ fs.putOverwrite("senderFileNumber", String.valueOf(fileNumber));
+ if(fs.get("sentTime") != null) {
+ fs.removeValue("sentTime");
+ }
+ fs.putOverwrite("sentTime", Long.toString(System.currentTimeMillis()));
+
+ try {
+ n2ntm = DMT.createNodeToNodeMessage(type, fs.toString().getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ Logger.error(this, "UnsupportedEncodingException processing extraPeerDataType ("+extraPeerDataTypeString+") in file "+extraPeerDataFile.getPath(), e);
+ return false;
+ }
+
+ try {
+ synchronized(queuedToSendN2NTMExtraPeerDataFileNumbers) {
+ node.usm.send(this, n2ntm, null);
+ Logger.normal(this, "Sent queued ("+fileNumber+") N2NTM to '"+getName()+"': "+n2ntm);
+ sendSuccess = true;
+ queuedToSendN2NTMExtraPeerDataFileNumbers.remove(new Integer(fileNumber));
+ }
+ deleteExtraPeerDataFile(fileNumber);
+ } catch (NotConnectedException e) {
+ sendSuccess = false; // redundant, but clear
+ }
+ }
+ if(!sendSuccess) {
+ synchronized(queuedToSendN2NTMExtraPeerDataFileNumbers) {
+ fs.putOverwrite("extraPeerDataType", Integer.toString(extraPeerDataType));
+ fs.removeValue("sentTime");
+ queuedToSendN2NTMExtraPeerDataFileNumbers.add(new Integer(fileNumber));
+ }
+ }
+ return true;
+ }
+ Logger.error(this, "Read unknown extra peer data type '"+extraPeerDataType+"' from file "+extraPeerDataFile.getPath());
+ return false;
+ }
+
+ public int writeNewExtraPeerDataFile(SimpleFieldSet fs, int extraPeerDataType) {
+ String extraPeerDataDirPath = node.getExtraPeerDataDir();
+ if(extraPeerDataType > 0)
+ fs.putOverwrite("extraPeerDataType", Integer.toString(extraPeerDataType));
+ File extraPeerDataPeerDir = new File(extraPeerDataDirPath+File.separator+getIdentityString());
+ if(!extraPeerDataPeerDir.exists()) {
+ if(!extraPeerDataPeerDir.mkdir()) {
+ Logger.error(this, "Extra peer data directory for peer could not be created: "+extraPeerDataPeerDir.getPath());
+ return -1;
+ }
+ }
+ if(!extraPeerDataPeerDir.isDirectory()) {
+ Logger.error(this, "Extra peer data directory for peer not a directory: "+extraPeerDataPeerDir.getPath());
+ return -1;
+ }
+ Integer[] localFileNumbers = null;
+ int nextFileNumber = 0;
+ synchronized(extraPeerDataFileNumbers) {
+ // Find the first free slot
+ localFileNumbers = (Integer[]) extraPeerDataFileNumbers.toArray(new Integer[extraPeerDataFileNumbers.size()]);
+ Arrays.sort(localFileNumbers);
+ for (int i = 0; i < localFileNumbers.length; i++) {
+ if(localFileNumbers[i].intValue() > nextFileNumber) {
+ break;
+ }
+ nextFileNumber = localFileNumbers[i].intValue() + 1;
+ }
+ extraPeerDataFileNumbers.add(new Integer(nextFileNumber));
+ }
+ FileOutputStream fos;
+ File extraPeerDataFile = new File(extraPeerDataPeerDir.getPath()+File.separator+nextFileNumber);
+ if(extraPeerDataFile.exists()) {
+ Logger.error(this, "Extra peer data file already exists: "+extraPeerDataFile.getPath());
+ return -1;
+ }
+ String f = extraPeerDataFile.getPath();
+ try {
+ fos = new FileOutputStream(f);
+ } catch (FileNotFoundException e2) {
+ Logger.error(this, "Cannot write extra peer data file to disk: Cannot create "
+ + f + " - " + e2, e2);
+ return -1;
+ }
+ OutputStreamWriter w;
+ try {
+ w = new OutputStreamWriter(fos, "UTF-8");
+ } catch (UnsupportedEncodingException e2) {
+ throw new Error("UTF-8 unsupported!: "+e2, e2);
+ }
+ BufferedWriter bw = new BufferedWriter(w);
+ try {
+ fs.writeTo(bw);
+ bw.close();
+ } catch (IOException e) {
+ try {
+ fos.close();
+ } catch (IOException e1) {
+ Logger.error(this, "Cannot close extra peer data file: "+e, e);
+ }
+ Logger.error(this, "Cannot write file: " + e, e);
+ return -1;
+ }
+ return nextFileNumber;
+ }
+
+ public void deleteExtraPeerDataFile(int fileNumber) {
+ String extraPeerDataDirPath = node.getExtraPeerDataDir();
+ File extraPeerDataPeerDir = new File(extraPeerDataDirPath, getIdentityString());
+ if(!extraPeerDataPeerDir.exists()) {
+ Logger.error(this, "Extra peer data directory for peer does not exist: "+extraPeerDataPeerDir.getPath());
+ return;
+ }
+ if(!extraPeerDataPeerDir.isDirectory()) {
+ Logger.error(this, "Extra peer data directory for peer not a directory: "+extraPeerDataPeerDir.getPath());
+ return;
+ }
+ File extraPeerDataFile = new File(extraPeerDataPeerDir, Integer.toString(fileNumber));
+ if(!extraPeerDataFile.exists()) {
+ Logger.error(this, "Extra peer data file for peer does not exist: "+extraPeerDataFile.getPath());
+ return;
+ }
+ synchronized(extraPeerDataFileNumbers) {
+ extraPeerDataFileNumbers.remove(new Integer(fileNumber));
+ }
+ if(!extraPeerDataFile.delete()) {
+ if(extraPeerDataFile.exists()) {
+ Logger.error(this, "Cannot delete file "+extraPeerDataFile+" after sending message to "+getPeer()+" - it may be resent on resting the node");
+ } else {
+ Logger.normal(this, "File does not exist when deleting: "+extraPeerDataFile+" after sending message to "+getPeer());
+ }
+ }
+ }
+
+ public void removeExtraPeerDataDir() {
+ String extraPeerDataDirPath = node.getExtraPeerDataDir();
+ File extraPeerDataPeerDir = new File(extraPeerDataDirPath+File.separator+getIdentityString());
+ if(!extraPeerDataPeerDir.exists()) {
+ Logger.error(this, "Extra peer data directory for peer does not exist: "+extraPeerDataPeerDir.getPath());
+ return;
+ }
+ if(!extraPeerDataPeerDir.isDirectory()) {
+ Logger.error(this, "Extra peer data directory for peer not a directory: "+extraPeerDataPeerDir.getPath());
+ return;
+ }
+ Integer[] localFileNumbers = null;
+ synchronized(extraPeerDataFileNumbers) {
+ localFileNumbers = (Integer[]) extraPeerDataFileNumbers.toArray(new Integer[extraPeerDataFileNumbers.size()]);
+ }
+ for (int i = 0; i < localFileNumbers.length; i++) {
+ deleteExtraPeerDataFile(localFileNumbers[i].intValue());
+ }
+ extraPeerDataPeerDir.delete();
+ }
+
+ public boolean rewriteExtraPeerDataFile(SimpleFieldSet fs, int extraPeerDataType, int fileNumber) {
+ String extraPeerDataDirPath = node.getExtraPeerDataDir();
+ if(extraPeerDataType > 0)
+ fs.putOverwrite("extraPeerDataType", Integer.toString(extraPeerDataType));
+ File extraPeerDataPeerDir = new File(extraPeerDataDirPath+File.separator+getIdentityString());
+ if(!extraPeerDataPeerDir.exists()) {
+ Logger.error(this, "Extra peer data directory for peer does not exist: "+extraPeerDataPeerDir.getPath());
+ return false;
+ }
+ if(!extraPeerDataPeerDir.isDirectory()) {
+ Logger.error(this, "Extra peer data directory for peer not a directory: "+extraPeerDataPeerDir.getPath());
+ return false;
+ }
+ File extraPeerDataFile = new File(extraPeerDataDirPath+File.separator+getIdentityString()+File.separator+fileNumber);
+ if(!extraPeerDataFile.exists()) {
+ Logger.error(this, "Extra peer data file for peer does not exist: "+extraPeerDataFile.getPath());
+ return false;
+ }
+ String f = extraPeerDataFile.getPath();
+ FileOutputStream fos;
+ try {
+ fos = new FileOutputStream(f);
+ } catch (FileNotFoundException e2) {
+ Logger.error(this, "Cannot write extra peer data file to disk: Cannot open "
+ + f + " - " + e2, e2);
+ return false;
+ }
+ OutputStreamWriter w;
+ try {
+ w = new OutputStreamWriter(fos, "UTF-8");
+ } catch (UnsupportedEncodingException e2) {
+ throw new Error("JVM doesn't support UTF-8 charset!: "+e2, e2);
+ }
+ BufferedWriter bw = new BufferedWriter(w);
+ try {
+ fs.writeTo(bw);
+ bw.close();
+ } catch (IOException e) {
+ try {
+ fos.close();
+ } catch (IOException e1) {
+ Logger.error(this, "Cannot close extra peer data file: "+e, e);
+ }
+ Logger.error(this, "Cannot write file: " + e, e);
+ return false;
+ }
+ return true;
+ }
+
+ public synchronized String getPrivateDarknetCommentNote() {
+ return privateDarknetComment;
+ }
+
+ public synchronized void setPrivateDarknetCommentNote(String comment) {
+ int localFileNumber;
+ synchronized(privateDarknetComment) {
+ privateDarknetComment = comment;
+ localFileNumber = privateDarknetCommentFileNumber;
+ }
+ SimpleFieldSet fs = new SimpleFieldSet(true);
+ fs.put("peerNoteType", Node.PEER_NOTE_TYPE_PRIVATE_DARKNET_COMMENT);
+ fs.putSingle("privateDarknetComment", Base64.encode(comment.getBytes()));
+ if(localFileNumber == -1) {
+ localFileNumber = writeNewExtraPeerDataFile(fs, Node.EXTRA_PEER_DATA_TYPE_PEER_NOTE);
+ synchronized(privateDarknetComment) {
+ privateDarknetCommentFileNumber = localFileNumber;
+ }
+ } else {
+ rewriteExtraPeerDataFile(fs, Node.EXTRA_PEER_DATA_TYPE_PEER_NOTE, localFileNumber);
+ }
+ }
+
+ public void queueN2NTM(SimpleFieldSet fs) {
+ int fileNumber = writeNewExtraPeerDataFile( fs, Node.EXTRA_PEER_DATA_TYPE_QUEUED_TO_SEND_N2NTM);
+ synchronized(queuedToSendN2NTMExtraPeerDataFileNumbers) {
+ queuedToSendN2NTMExtraPeerDataFileNumbers.add(new Integer(fileNumber));
+ }
+ }
+
+ public void sendQueuedN2NTMs() {
+ if(logMINOR)
+ Logger.minor(this, "Sending queued N2NTMs for "+shortToString());
+ Integer[] localFileNumbers = null;
+ synchronized(queuedToSendN2NTMExtraPeerDataFileNumbers) {
+ localFileNumbers = (Integer[]) queuedToSendN2NTMExtraPeerDataFileNumbers.toArray(new Integer[queuedToSendN2NTMExtraPeerDataFileNumbers.size()]);
+ }
+ Arrays.sort(localFileNumbers);
+ for (int i = 0; i < localFileNumbers.length; i++) {
+ rereadExtraPeerDataFile(localFileNumbers[i].intValue());
+ }
+ }
+
+ void startARKFetcher() {
+ synchronized(this) {
+ if(isListenOnly) {
+ Logger.minor(this, "Not starting ark fetcher for "+this+" as it's in listen-only mode.");
+ return;
+ }
+ }
+ super.startARKFetcher();
+ }
+
+ public String getTMCIPeerInfo() {
+ return getName()+'\t'+super.getTMCIPeerInfo();
+ }
+
+ /**
+ * A method to be called once at the beginning of every time isConnected() is true
+ */
+ protected void onConnect() {
+ sendQueuedN2NTMs();
+ }
+
+ // File transfer offers
+ // FIXME this should probably be somewhere else, along with the N2NTM stuff... but where?
+ // FIXME this should be persistent across node restarts
+
+ /** Files I have offered to this peer */
+ private final HashMap myFileOffersByUID = new HashMap();
+ /** Files this peer has offered to me */
+ private final HashMap hisFileOffersByUID = new HashMap();
+
+ private void storeOffers() {
+ // FIXME do something
+ }
+
+ class FileOffer {
+ final long uid;
+ final String filename;
+ final String mimeType;
+ final String comment;
+ private RandomAccessThing data;
+ final long size;
+ /** Who is offering it? True = I am offering it, False = I am being offered it */
+ final boolean amIOffering;
+ private PartiallyReceivedBulk prb;
+ private BulkTransmitter transmitter;
+ private BulkReceiver receiver;
+ /** True if the offer has either been accepted or rejected */
+ private boolean acceptedOrRejected;
+
+ FileOffer(long uid, RandomAccessThing data, String filename, String mimeType, String comment) throws IOException {
+ this.uid = uid;
+ this.data = data;
+ this.filename = filename;
+ this.mimeType = mimeType;
+ this.comment = comment;
+ size = data.size();
+ amIOffering = true;
+ }
+
+ public FileOffer(SimpleFieldSet fs, boolean amIOffering) throws FSParseException {
+ uid = fs.getLong("uid");
+ size = fs.getLong("size");
+ mimeType = fs.get("metadata.contentType");
+ filename = FileUtil.sanitize(fs.get("filename"), mimeType);
+ String s = fs.get("comment");
+ if(s != null) {
+ try {
+ s = new String(Base64.decode(s), "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new Error(e);
+ } catch (IllegalBase64Exception e) {
+ // Maybe it wasn't encoded? FIXME remove
+ }
+ }
+ comment = s;
+ this.amIOffering = amIOffering;
+ }
+
+ public void toFieldSet(SimpleFieldSet fs) {
+ fs.put("uid", uid);
+ fs.putSingle("filename", filename);
+ fs.putSingle("metadata.contentType", mimeType);
+ try {
+ fs.putSingle("comment", Base64.encode(comment.getBytes("UTF-8")));
+ } catch (UnsupportedEncodingException e) {
+ throw new Error(e);
+ }
+ fs.put("size", size);
+ }
+
+ public void accept() {
+ acceptedOrRejected = true;
+ File dest = new File(node.clientCore.downloadDir, "direct-"+FileUtil.sanitize(getName())+"-"+filename);
+ try {
+ data = new RandomAccessFileWrapper(dest, "rw");
+ } catch (FileNotFoundException e) {
+ // Impossible
+ throw new Error("Impossible: FileNotFoundException opening with RAF with rw! "+e, e);
+ }
+ prb = new PartiallyReceivedBulk(node.usm, size, Node.PACKET_SIZE, data, false);
+ receiver = new BulkReceiver(prb, DarknetPeerNode.this, uid);
+ // FIXME make this persistent
+ node.executor.execute(new Runnable() {
+ public void run() {
+ if(logMINOR)
+ Logger.minor(this, "Received file");
+ try {
+ if(!receiver.receive()) {
+ String err = "Failed to receive "+this;
+ Logger.error(this, err);
+ System.err.println(err);
+ onReceiveFailure();
+ } else {
+ onReceiveSuccess();
+ }
+ } catch (Throwable t) {
+ Logger.error(this, "Caught "+t+" receiving file", t);
+ onReceiveFailure();
+ } finally {
+ remove();
+ }
+ if(logMINOR)
+ Logger.minor(this, "Received file");
+ }
+ }, "Receiver for bulk transfer "+uid+":"+filename);
+ sendFileOfferAccepted(uid);
+ }
+
+ protected void remove() {
+ Long l = new Long(uid);
+ synchronized(DarknetPeerNode.this) {
+ myFileOffersByUID.remove(l);
+ hisFileOffersByUID.remove(l);
+ }
+ data.close();
+ }
+
+ public void send() throws DisconnectedException {
+ prb = new PartiallyReceivedBulk(node.usm, size, Node.PACKET_SIZE, data, true);
+ transmitter = new BulkTransmitter(prb, DarknetPeerNode.this, uid, node.outputThrottle);
+ if(logMINOR)
+ Logger.minor(this, "Sending "+uid);
+ node.executor.execute(new Runnable() {
+ public void run() {
+ if(logMINOR)
+ Logger.minor(this, "Sending file");
+ try {
+ if(!transmitter.send()) {
+ String err = "Failed to send "+uid+" for "+FileOffer.this;
+ Logger.error(this, err);
+ System.err.println(err);
+ }
+ } catch (Throwable t) {
+ Logger.error(this, "Caught "+t+" sending file", t);
+ remove();
+ }
+ if(logMINOR)
+ Logger.minor(this, "Sent file");
+ }
+
+ }, "Sender for bulk transfer "+uid+":"+filename);
+ }
+
+ public void reject() {
+ acceptedOrRejected = true;
+ sendFileOfferRejected(uid);
+ }
+
+ public void onRejected() {
+ transmitter.cancel();
+ // FIXME prb's can't be shared, right? Well they aren't here...
+ prb.abort(RetrievalException.CANCELLED_BY_RECEIVER, "Cancelled by receiver");
+ }
+
+ protected void onReceiveFailure() {
+ UserAlert alert = new UserAlert() {
+ public String dismissButtonText() {
+ return L10n.getString("UserAlert.hide");
+ }
+ public HTMLNode getHTMLText() {
+ HTMLNode div = new HTMLNode("div");
+
+ div.addChild("p", l10n("failedReceiveHeader", new String[] { "filename", "node" },
+ new String[] { filename, getName() }));
+
+ // Descriptive table
+
+ HTMLNode table = div.addChild("table", "border", "0");
+ HTMLNode row = table.addChild("tr");
+ row.addChild("td").addChild("#", l10n("fileLabel"));
+ row.addChild("td").addChild("#", filename);
+ row = table.addChild("tr");
+ row.addChild("td").addChild("#", l10n("sizeLabel"));
+ row.addChild("td").addChild("#", SizeUtil.formatSize(size));
+ row = table.addChild("tr");
+ row.addChild("td").addChild("#", l10n("mimeLabel"));
+ row.addChild("td").addChild("#", mimeType);
+ row = table.addChild("tr");
+ row.addChild("td").addChild("#", l10n("senderLabel"));
+ row.addChild("td").addChild("#", getName());
+ row = table.addChild("tr");
+ if(comment != null && comment.length() > 0) {
+ row.addChild("td").addChild("#", l10n("commentLabel"));
+ addComment(row.addChild("td"));
+ }
+
+ return div;
+ }
+
+ public short getPriorityClass() {
+ return UserAlert.MINOR;
+ }
+
+ public String getText() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(l10n("failedReceiveHeader", new String[] { "filename", "node" },
+ new String[] { filename, getName() }));
+ sb.append('\n');
+ sb.append(l10n("fileLabel"));
+ sb.append(' ');
+ sb.append(filename);
+ sb.append('\n');
+ sb.append(l10n("sizeLabel"));
+ sb.append(' ');
+ sb.append(SizeUtil.formatSize(size));
+ sb.append('\n');
+ sb.append(l10n("mimeLabel"));
+ sb.append(' ');
+ sb.append(mimeType);
+ sb.append('\n');
+ sb.append(l10n("senderLabel"));
+ sb.append(' ');
+ sb.append(getName());
+ sb.append('\n');
+ if(comment != null && comment.length() > 0) {
+ sb.append(l10n("commentLabel"));
+ sb.append(' ');
+ sb.append(comment);
+ }
+ return sb.toString();
+ }
+
+ public String getTitle() {
+ return l10n("failedReceiveTitle");
+ }
+
+ public boolean isValid() {
+ return true;
+ }
+
+ public void isValid(boolean validity) {
+ // Ignore
+ }
+
+ public void onDismiss() {
+ // Ignore
+ }
+
+ public boolean shouldUnregisterOnDismiss() {
+ return true;
+ }
+
+ public boolean userCanDismiss() {
+ return true;
+ }
+
+ };
+ node.clientCore.alerts.register(alert);
+ }
+
+ private void onReceiveSuccess() {
+ UserAlert alert = new UserAlert() {
+ public String dismissButtonText() {
+ return L10n.getString("UserAlert.hide");
+ }
+ public HTMLNode getHTMLText() {
+ HTMLNode div = new HTMLNode("div");
+
+ // FIXME localise!!!
+
+ div.addChild("p", l10n("succeededReceiveHeader", new String[] { "filename", "node" },
+ new String[] { filename, getName() }));
+
+ // Descriptive table
+
+ HTMLNode table = div.addChild("table", "border", "0");
+ HTMLNode row = table.addChild("tr");
+ row.addChild("td").addChild("#", l10n("fileLabel"));
+ row.addChild("td").addChild("#", filename);
+ row = table.addChild("tr");
+ row.addChild("td").addChild("#", l10n("sizeLabel"));
+ row.addChild("td").addChild("#", SizeUtil.formatSize(size));
+ row = table.addChild("tr");
+ row.addChild("td").addChild("#", l10n("mimeLabel"));
+ row.addChild("td").addChild("#", mimeType);
+ row = table.addChild("tr");
+ row.addChild("td").addChild("#", l10n("senderLabel"));
+ row.addChild("td").addChild("#", getName());
+ row = table.addChild("tr");
+ if(comment != null && comment.length() > 0) {
+ row.addChild("td").addChild("#", l10n("commentLabel"));
+ addComment(row.addChild("td"));
+ }
+
+ return div;
+ }
+
+ public short getPriorityClass() {
+ return UserAlert.MINOR;
+ }
+
+ public String getText() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(l10n("succeededReceiveHeader", new String[] { "filename", "node" },
+ new String[] { filename, getName() }));
+ sb.append('\n');
+ sb.append(l10n("fileLabel"));
+ sb.append(' ');
+ sb.append(filename);
+ sb.append('\n');
+ sb.append(l10n("sizeLabel"));
+ sb.append(' ');
+ sb.append(SizeUtil.formatSize(size));
+ sb.append('\n');
+ sb.append(l10n("mimeLabel"));
+ sb.append(' ');
+ sb.append(mimeType);
+ sb.append('\n');
+ sb.append(l10n("senderLabel"));
+ sb.append(' ');
+ sb.append(userToString());
+ sb.append('\n');
+ if(comment != null && comment.length() > 0) {
+ sb.append(l10n("commentLabel"));
+ sb.append(' ');
+ sb.append(comment);
+ }
+ return sb.toString();
+ }
+
+ public String getTitle() {
+ return l10n("succeededReceiveTitle");
+ }
+
+ public boolean isValid() {
+ return true;
+ }
+
+ public void isValid(boolean validity) {
+ // Ignore
+ }
+
+ public void onDismiss() {
+ // Ignore
+ }
+
+ public boolean shouldUnregisterOnDismiss() {
+ return true;
+ }
+
+ public boolean userCanDismiss() {
+ return true;
+ }
+
+ };
+ node.clientCore.alerts.register(alert);
+ }
+
+
+ /** Ask the user whether (s)he wants to download a file from a direct peer */
+ public UserAlert askUserUserAlert() {
+ return new UserAlert() {
+ public String dismissButtonText() {
+ return null; // Cannot hide, but can reject
+ }
+ public HTMLNode getHTMLText() {
+ HTMLNode div = new HTMLNode("div");
+
+ div.addChild("p", l10n("offeredFileHeader", "name", getName()));
+
+ // Descriptive table
+
+ HTMLNode table = div.addChild("table", "border", "0");
+ HTMLNode row = table.addChild("tr");
+ row.addChild("td").addChild("#", l10n("fileLabel"));
+ row.addChild("td").addChild("#", filename);
+ row = table.addChild("tr");
+ row.addChild("td").addChild("#", l10n("sizeLabel"));
+ row.addChild("td").addChild("#", SizeUtil.formatSize(size));
+ row = table.addChild("tr");
+ row.addChild("td").addChild("#", l10n("mimeLabel"));
+ row.addChild("td").addChild("#", mimeType);
+ row = table.addChild("tr");
+ row.addChild("td").addChild("#", l10n("senderLabel"));
+ row.addChild("td").addChild("#", getName());
+ row = table.addChild("tr");
+ if(comment != null && comment.length() > 0) {
+ row.addChild("td").addChild("#", l10n("commentLabel"));
+ addComment(row.addChild("td"));
+ }
+
+ // Accept/reject form
+
+ // Hopefully we will have a container when this function is called!
+ HTMLNode form = node.clientCore.getToadletContainer().addFormChild(div, "/friends/", "f2fFileOfferAcceptForm");
+
+ // FIXME node_ is inefficient
+ form.addChild("input", new String[] { "type", "name" },
+ new String[] { "hidden", "node_"+DarknetPeerNode.this.hashCode() });
+
+ form.addChild("input", new String[] { "type", "name", "value" },
+ new String[] { "hidden", "id", Long.toString(uid) });
+
+ form.addChild("input", new String[] { "type", "name", "value" },
+ new String[] { "submit", "acceptTransfer", l10n("acceptTransferButton") });
+
+ form.addChild("input", new String[] { "type", "name", "value" },
+ new String[] { "submit", "rejectTransfer", l10n("rejectTransferButton") });
+
+ return div;
+ }
+ public short getPriorityClass() {
+ return UserAlert.MINOR;
+ }
+ public String getText() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(l10n("offeredFileHeader", "name", getName()));
+ sb.append('\n');
+ sb.append(l10n("fileLabel"));
+ sb.append(' ');
+ sb.append(filename);
+ sb.append('\n');
+ sb.append(l10n("sizeLabel"));
+ sb.append(' ');
+ sb.append(SizeUtil.formatSize(size));
+ sb.append('\n');
+ sb.append(l10n("mimeLabel"));
+ sb.append(' ');
+ sb.append(mimeType);
+ sb.append('\n');
+ sb.append(l10n("senderLabel"));
+ sb.append(' ');
+ sb.append(userToString());
+ sb.append('\n');
+ if(comment != null && comment.length() > 0) {
+ sb.append(l10n("commentLabel"));
+ sb.append(' ');
+ sb.append(comment);
+ }
+ return sb.toString();
+ }
+ public String getTitle() {
+ return l10n("askUserTitle");
+ }
+
+ public boolean isValid() {
+ if(acceptedOrRejected) {
+ node.clientCore.alerts.unregister(this);
+ return false;
+ }
+ return true;
+ }
+ public void isValid(boolean validity) {
+ // Ignore
+ }
+ public void onDismiss() {
+ // Ignore
+ }
+ public boolean shouldUnregisterOnDismiss() {
+ return false;
+ }
+
+ public boolean userCanDismiss() {
+ return false; // should accept or reject
+ }
+ };
+
+ }
+ protected void addComment(HTMLNode node) {
+ String[] lines = comment.split("\n");
+ for (int i = 0, c = lines.length; i < c; i++) {
+ node.addChild("#", lines[i]);
+ if(i != lines.length - 1)
+ node.addChild("br");
+ }
+ }
+
+ private String l10n(String key) {
+ return L10n.getString("FileOffer."+key);
+ }
+ private String l10n(String key, String pattern, String value) {
+ return L10n.getString("FileOffer."+key, pattern, value);
+ }
+ private String l10n(String key, String[] pattern, String[] value) {
+ return L10n.getString("FileOffer."+key, pattern, value);
+ }
+ }
+
+ public int sendTextMessage(String message) {
+ long now = System.currentTimeMillis();
+ SimpleFieldSet fs = new SimpleFieldSet(true);
+ fs.put("n2nType", Node.N2N_MESSAGE_TYPE_FPROXY);
+ fs.put("type", Node.N2N_TEXT_MESSAGE_TYPE_USERALERT);
+ try {
+ fs.putSingle("source_nodename", Base64.encode(node.getMyName().getBytes("UTF-8")));
+ fs.putSingle("target_nodename", Base64.encode(getName().getBytes("UTF-8")));
+ fs.putSingle("text", Base64.encode(message.getBytes("UTF-8")));
+ fs.put("composedTime", now);
+ fs.put("sentTime", now);
+ Message n2ntm;
+ n2ntm = DMT.createNodeToNodeMessage(
+ Node.N2N_MESSAGE_TYPE_FPROXY, fs
+ .toString().getBytes("UTF-8"));
+ try {
+ sendAsync(n2ntm, null, 0, null);
+ } catch (NotConnectedException e) {
+ fs.removeValue("sentTime");
+ queueN2NTM(fs);
+ setPeerNodeStatus(System.currentTimeMillis());
+ return getPeerNodeStatus();
+ }
+ this.setPeerNodeStatus(System.currentTimeMillis());
+ return getPeerNodeStatus();
+ } catch (UnsupportedEncodingException e) {
+ throw new Error("Impossible: "+e, e);
+ }
+ }
+
+ public int sendFileOfferAccepted(long uid) {
+ storeOffers();
+ long now = System.currentTimeMillis();
+ SimpleFieldSet fs = new SimpleFieldSet(true);
+ fs.put("n2nType", Node.N2N_MESSAGE_TYPE_FPROXY);
+ fs.put("type", Node.N2N_TEXT_MESSAGE_TYPE_FILE_OFFER_ACCEPTED);
+ try {
+ fs.putSingle("source_nodename", Base64.encode(node.getMyName().getBytes("UTF-8")));
+ fs.putSingle("target_nodename", Base64.encode(getName().getBytes("UTF-8")));
+ fs.put("composedTime", now);
+ fs.put("sentTime", now);
+ fs.put("uid", uid);
+ if(logMINOR)
+ Logger.minor(this, "Sending node to node message (file offer accepted):\n"+fs);
+ Message n2ntm;
+ n2ntm = DMT.createNodeToNodeMessage(
+ Node.N2N_MESSAGE_TYPE_FPROXY, fs
+ .toString().getBytes("UTF-8"));
+ try {
+ sendAsync(n2ntm, null, 0, null);
+ } catch (NotConnectedException e) {
+ fs.removeValue("sentTime");
+ queueN2NTM(fs);
+ setPeerNodeStatus(System.currentTimeMillis());
+ return getPeerNodeStatus();
+ }
+ this.setPeerNodeStatus(System.currentTimeMillis());
+ return getPeerNodeStatus();
+ } catch (UnsupportedEncodingException e) {
+ throw new Error("Impossible: "+e, e);
+ }
+ }
+
+ public int sendFileOfferRejected(long uid) {
+ storeOffers();
+ long now = System.currentTimeMillis();
+ SimpleFieldSet fs = new SimpleFieldSet(true);
+ fs.put("n2nType", Node.N2N_MESSAGE_TYPE_FPROXY);
+ fs.put("type", Node.N2N_TEXT_MESSAGE_TYPE_FILE_OFFER_REJECTED);
+ try {
+ fs.putSingle("source_nodename", Base64.encode(node.getMyName().getBytes("UTF-8")));
+ fs.putSingle("target_nodename", Base64.encode(getName().getBytes("UTF-8")));
+ fs.put("composedTime", now);
+ fs.put("sentTime", now);
+ fs.put("uid", uid);
+ if(logMINOR)
+ Logger.minor(this, "Sending node to node message (file offer rejected):\n"+fs);
+ Message n2ntm;
+ n2ntm = DMT.createNodeToNodeMessage(
+ Node.N2N_MESSAGE_TYPE_FPROXY, fs
+ .toString().getBytes("UTF-8"));
+ try {
+ sendAsync(n2ntm, null, 0, null);
+ } catch (NotConnectedException e) {
+ fs.removeValue("sentTime");
+ queueN2NTM(fs);
+ setPeerNodeStatus(System.currentTimeMillis());
+ return getPeerNodeStatus();
+ }
+ this.setPeerNodeStatus(System.currentTimeMillis());
+ return getPeerNodeStatus();
+ } catch (UnsupportedEncodingException e) {
+ throw new Error("Impossible: "+e, e);
+ }
+ }
+
+ public int sendFileOffer(File filename, String message) throws IOException {
+ String fnam = filename.getName();
+ String mime = DefaultMIMETypes.guessMIMEType(fnam, false);
+ long uid = node.random.nextLong();
+ RandomAccessThing data = new RandomAccessFileWrapper(filename, "r");
+ FileOffer fo = new FileOffer(uid, data, fnam, mime, message);
+ synchronized(this) {
+ myFileOffersByUID.put(new Long(uid), fo);
+ }
+ storeOffers();
+ long now = System.currentTimeMillis();
+ SimpleFieldSet fs = new SimpleFieldSet(true);
+ fs.put("n2nType", Node.N2N_MESSAGE_TYPE_FPROXY);
+ fs.put("type", Node.N2N_TEXT_MESSAGE_TYPE_FILE_OFFER);
+ try {
+ fs.putSingle("source_nodename", Base64.encode(node.getMyName().getBytes("UTF-8")));
+ fs.putSingle("target_nodename", Base64.encode(getName().getBytes("UTF-8")));
+ fs.put("composedTime", now);
+ fs.put("sentTime", now);
+ fo.toFieldSet(fs);
+ if(logMINOR)
+ Logger.minor(this, "Sending node to node message (file offer):\n"+fs);
+ Message n2ntm;
+ int status = getPeerNodeStatus();
+ n2ntm = DMT.createNodeToNodeMessage(
+ Node.N2N_MESSAGE_TYPE_FPROXY, fs
+ .toString().getBytes("UTF-8"));
+ try {
+ sendAsync(n2ntm, null, 0, null);
+ } catch (NotConnectedException e) {
+ fs.removeValue("sentTime");
+ queueN2NTM(fs);
+ setPeerNodeStatus(System.currentTimeMillis());
+ return getPeerNodeStatus();
+ }
+ return status;
+ } catch (UnsupportedEncodingException e) {
+ throw new Error("Impossible: "+e, e);
+ }
+ }
+
+ public void handleFproxyN2NTM(SimpleFieldSet fs, int fileNumber) {
+ String source_nodename = null;
+ String target_nodename = null;
+ String text = null;
+ long composedTime;
+ long sentTime;
+ long receivedTime;
+ try {
+ source_nodename = new String(Base64.decode(fs.get("source_nodename")));
+ target_nodename = new String(Base64.decode(fs.get("target_nodename")));
+ text = new String(Base64.decode(fs.get("text")));
+ composedTime = fs.getLong("composedTime", -1);
+ sentTime = fs.getLong("sentTime", -1);
+ receivedTime = fs.getLong("receivedTime", -1);
+ } catch (IllegalBase64Exception e) {
+ Logger.error(this, "Bad Base64 encoding when decoding a N2NTM SimpleFieldSet", e);
+ return;
+ }
+ N2NTMUserAlert userAlert = new N2NTMUserAlert(this, source_nodename, target_nodename, text, fileNumber, composedTime, sentTime, receivedTime);
+ node.clientCore.alerts.register(userAlert);
+ }
+
+ public void handleFproxyFileOffer(SimpleFieldSet fs, int fileNumber) {
+ final FileOffer offer;
+ try {
+ offer = new FileOffer(fs, false);
+ } catch (FSParseException e) {
+ Logger.error(this, "Could not parse offer: "+e+" on "+this+" :\n"+fs, e);
+ return;
+ }
+ Long u = new Long(offer.uid);
+ synchronized(this) {
+ if(hisFileOffersByUID.containsKey(u)) return; // Ignore re-advertisement
+ hisFileOffersByUID.put(u, offer);
+ }
+
+ // Don't persist for now - FIXME
+ this.deleteExtraPeerDataFile(fileNumber);
+
+ UserAlert alert = offer.askUserUserAlert();
+
+ node.clientCore.alerts.register(alert);
+ }
+
+ public void acceptTransfer(long id) {
+ if(logMINOR)
+ Logger.minor(this, "Accepting transfer "+id+" on "+this);
+ FileOffer fo;
+ synchronized(this) {
+ fo = (FileOffer) hisFileOffersByUID.get(new Long(id));
+ }
+ fo.accept();
+ }
+
+ public void rejectTransfer(long id) {
+ FileOffer fo;
+ synchronized(this) {
+ fo = (FileOffer) hisFileOffersByUID.remove(new Long(id));
+ }
+ fo.reject();
+ }
+
+ public void handleFproxyFileOfferAccepted(SimpleFieldSet fs, int fileNumber) {
+ // Don't persist for now - FIXME
+ this.deleteExtraPeerDataFile(fileNumber);
+
+ long uid;
+ try {
+ uid = fs.getLong("uid");
+ } catch (FSParseException e) {
+ Logger.error(this, "Could not parse offer accepted: "+e+" on "+this+" :\n"+fs, e);
+ return;
+ }
+ if(logMINOR)
+ Logger.minor(this, "Offer accepted for "+uid);
+ Long u = new Long(uid);
+ FileOffer fo;
+ synchronized(this) {
+ fo = (FileOffer) (myFileOffersByUID.get(u));
+ }
+ if(fo == null) {
+ Logger.error(this, "No such offer: "+uid);
+ try {
+ sendAsync(DMT.createFNPBulkSendAborted(uid), null, fileNumber, null);
+ } catch (NotConnectedException e) {
+ // Fine by me!
+ }
+ return;
+ }
+ try {
+ fo.send();
+ } catch (DisconnectedException e) {
+ Logger.error(this, "Cannot send because node disconnected: "+e+" for "+uid+":"+fo.filename, e);
+ }
+ }
+
+ public void handleFproxyFileOfferRejected(SimpleFieldSet fs, int fileNumber) {
+ // Don't persist for now - FIXME
+ this.deleteExtraPeerDataFile(fileNumber);
+
+ long uid;
+ try {
+ uid = fs.getLong("uid");
+ } catch (FSParseException e) {
+ Logger.error(this, "Could not parse offer rejected: "+e+" on "+this+" :\n"+fs, e);
+ return;
+ }
+
+ FileOffer fo;
+ synchronized(this) {
+ fo = (FileOffer) myFileOffersByUID.remove(new Long(uid));
+ }
+ fo.onRejected();
+ }
+
+ public String userToString() {
+ return ""+getPeer()+" : "+getName();
+ }
+
+ protected synchronized boolean innerCalcNextHandshake(boolean successfulHandshakeSend, boolean dontFetchARK, long now) {
+ if(isBurstOnly) {
+ boolean fetchARKFlag = false;
+ listeningHandshakeBurstCount++;
+ if(verifiedIncompatibleOlderVersion || verifiedIncompatibleNewerVersion) {
+ // Let them know we're here, but have no hope of connecting
+ // Send one packet only.
+ listeningHandshakeBurstCount = 0;
+ } else if(listeningHandshakeBurstCount >= listeningHandshakeBurstSize) {
+ listeningHandshakeBurstCount = 0;
+ fetchARKFlag = true;
+ }
+ if(listeningHandshakeBurstCount == 0) { // 0 only if we just reset it above
+ sendHandshakeTime = now + Node.MIN_TIME_BETWEEN_BURSTING_HANDSHAKE_BURSTS
+ + node.random.nextInt(Node.RANDOMIZED_TIME_BETWEEN_BURSTING_HANDSHAKE_BURSTS);
+ listeningHandshakeBurstSize = Node.MIN_BURSTING_HANDSHAKE_BURST_SIZE
+ + node.random.nextInt(Node.RANDOMIZED_BURSTING_HANDSHAKE_BURST_SIZE);
+ isBursting = false;
+ } else {
+ sendHandshakeTime = now + Node.MIN_TIME_BETWEEN_HANDSHAKE_SENDS
+ + node.random.nextInt(Node.RANDOMIZED_TIME_BETWEEN_HANDSHAKE_SENDS);
+ }
+ if(logMINOR) Logger.minor(this, "Next BurstOnly mode handshake in "+(sendHandshakeTime - now)+"ms for "+getName()+" (count: "+listeningHandshakeBurstCount+", size: "+listeningHandshakeBurstSize+ ')', new Exception("double-called debug"));
+ return fetchARKFlag;
+ } else {
+ return super.innerCalcNextHandshake(successfulHandshakeSend, dontFetchARK, now);
+ }
+ }
+
+ public PeerNodeStatus getStatus() {
+ return new DarknetPeerNodeStatus(this);
+ }
+
+ public boolean isOpennet() {
+ return false;
+ }
+
+ public void onSuccess(boolean insert, boolean ssk) {
+ // Ignore it
+ }
+
+ public void onRemove() {
+ // Do nothing
+ // FIXME is there anything we should do?
+ }
+}
Copied: branches/freenet-jfk/src/freenet/node/DarknetPeerNodeStatus.java (from rev 14796, trunk/freenet/src/freenet/node/DarknetPeerNodeStatus.java)
===================================================================
--- branches/freenet-jfk/src/freenet/node/DarknetPeerNodeStatus.java (rev 0)
+++ branches/freenet-jfk/src/freenet/node/DarknetPeerNodeStatus.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -0,0 +1,62 @@
+package freenet.node;
+
+public class DarknetPeerNodeStatus extends PeerNodeStatus {
+
+ private final String name;
+
+ private final boolean burstOnly;
+
+ private final boolean listening;
+
+ private final boolean disabled;
+
+ private final String privateDarknetCommentNote;
+
+ public DarknetPeerNodeStatus(DarknetPeerNode peerNode) {
+ super(peerNode);
+ this.name = peerNode.getName();
+ this.burstOnly = peerNode.isBurstOnly();
+ this.listening = peerNode.isListenOnly();
+ this.disabled = peerNode.isDisabled();
+ this.privateDarknetCommentNote = peerNode.getPrivateDarknetCommentNote();
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return the burstOnly
+ */
+ public boolean isBurstOnly() {
+ return burstOnly;
+ }
+
+ /**
+ * @return the disabled
+ */
+ public boolean isDisabled() {
+ return disabled;
+ }
+
+ /**
+ * @return the listening
+ */
+ public boolean isListening() {
+ return listening;
+ }
+
+ /**
+ * @return the privateDarknetCommentNote
+ */
+ public String getPrivateDarknetCommentNote() {
+ return privateDarknetCommentNote;
+ }
+
+ public String toString() {
+ return name + ' ' + super.toString();
+ }
+}
Copied: branches/freenet-jfk/src/freenet/node/FailureTable.java (from rev 14796, trunk/freenet/src/freenet/node/FailureTable.java)
===================================================================
--- branches/freenet-jfk/src/freenet/node/FailureTable.java (rev 0)
+++ branches/freenet-jfk/src/freenet/node/FailureTable.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -0,0 +1,267 @@
+/* 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 java.lang.ref.WeakReference;
+
+import freenet.keys.Key;
+import freenet.keys.KeyBlock;
+import freenet.keys.NodeCHK;
+import freenet.support.LRUHashtable;
+
+/**
+ * Tracks recently DNFed keys, where they were routed to, what the location was at the time, who requested them.
+ * Implements Ultra-Lightweight Persistent Requests: Refuse requests for a key for 10 minutes after it's DNFed
+ * (UNLESS we find a better route for the request), and when it is found, offer it to those who've asked for it
+ * in the last hour.
+ * @author toad
+ */
+public class FailureTable {
+
+ /** FailureTableEntry's by key. Note that we push an entry only when sentTime changes. */
+ private final LRUHashtable entriesByKey;
+ /** BlockOfferList by key */
+ private final LRUHashtable blockOfferListByKey;
+ /** Peers */
+ private final PeerManager peers;
+ private final Node node;
+
+ /** Maximum number of keys to track */
+ static final int MAX_ENTRIES = 20*1000;
+ /** Maximum number of offers to track */
+ static final int MAX_OFFERS = 10*1000;
+ /** Terminate a request if there was a DNF on the same key less than 10 minutes ago */
+ static final int REJECT_TIME = 10*60*1000;
+ /** After 1 hour we forget about an entry completely */
+ static final int MAX_LIFETIME = 60*60*1000;
+ /** Offers expire after 10 minutes */
+ static final int OFFER_EXPIRY_TIME = 10*60*1000;
+
+ FailureTable(PeerManager peers, Node node) {
+ entriesByKey = new LRUHashtable();
+ blockOfferListByKey = new LRUHashtable();
+ this.peers = peers;
+ this.node = node;
+ }
+
+ /**
+ * Called when a key DNFs, or is killed by a RecentlyFailed message. Either way this can create a
+ * FailureTableEntry.
+ * @param key The key that was fetched.
+ * @param htl The HTL it was fetched at.
+ * @param requestors The nodes requesting it (if any).
+ * @param requested The single node it was forwarded to, which DNFed.
+ * @param now The time at which the request was sent.
+ * @param timeout The number of millis from when the request was sent to when the failure block times out.
+ * I.e. between 0 and REJECT_TIME.
+ */
+ public void onFailure(Key key, short htl, PeerNode[] requestors, PeerNode requested, int timeout, long now) {
+ FailureTableEntry entry;
+ synchronized(this) {
+ entry = (FailureTableEntry) entriesByKey.get(key);
+ if(entry == null) {
+ entry = new FailureTableEntry(key, htl, requestors, requested);
+ entriesByKey.push(key, entry);
+ return;
+ } else {
+ entriesByKey.push(key, entry);
+ }
+ trimEntries(now);
+ }
+ entry.onFailure(htl, requestors, requested, timeout, now);
+ }
+
+ private void trimEntries(long now) {
+ while(entriesByKey.size() > MAX_ENTRIES) {
+ entriesByKey.popKey();
+ }
+ while(true) {
+ FailureTableEntry e = (FailureTableEntry) entriesByKey.peekValue();
+ if(now - e.creationTime > MAX_LIFETIME) entriesByKey.popKey();
+ else break;
+ }
+ }
+
+ /**
+ * Called when a request is made. Determine whether we should fail the request, and add the requestors to the list
+ * of interested nodes.
+ * @param key The key to fetch.
+ * @param htl The HTL it will be fetched at.
+ * @param requestor The node requesting it.
+ * @return True if the request should be failed with an FNPRecentlyFailed.
+ */
+ public synchronized boolean shouldFail(Key key, short htl, PeerNode requestor) {
+ long now = System.currentTimeMillis();
+ FailureTableEntry entry = (FailureTableEntry) entriesByKey.get(key);
+ if(entry == null) {
+ // Don't know anything about the key
+ return false;
+ }
+ entry.addRequestors(new PeerNode[] { requestor }, now);
+ if(htl > entry.htl) {
+ // If the HTL is higher this time, let it through
+ entriesByKey.push(key, entry);
+ return false;
+ }
+ if(now > entry.timeoutTime) {
+ // If it's more than 10 minutes since we sent a request, let it through
+ return false;
+ }
+ /*
+ * If the best node available now is closer than the best location we have routed to so far, out of those
+ * nodes which are still connected, then accept the request.
+ *
+ * Note that this means we can route to the same node twice - but only if its location improves.
+ */
+ double bestLiveLocDiff = entry.bestLiveLocDiff();
+
+ PeerNode p = peers.closerPeer(requestor, null, null, key.toNormalizedDouble(), true, false, 0, null, bestLiveLocDiff);
+
+ if(p != null) return false; // there is a better route now / we want to retry an old one
+
+ return true; // kill the request
+ }
+
+ private final class BlockOfferList {
+ private BlockOffer[] offers;
+ final FailureTableEntry entry;
+
+ BlockOfferList(FailureTableEntry entry, BlockOffer offer) {
+ this.entry = entry;
+ this.offers = new BlockOffer[] { offer };
+ }
+
+ public long expires() {
+ long last = 0;
+ for(int i=0;i<offers.length;i++) {
+ if(offers[i].offeredTime > last) last = offers[i].offeredTime;
+ }
+ return last;
+ }
+
+ public boolean isEmpty(long now) {
+ for(int i=0;i<offers.length;i++) {
+ if(offers[i].offeredTime > now) return false;
+ }
+ return true;
+ }
+ }
+
+ private final class BlockOffer {
+ final long offeredTime;
+ /** Either offered by or offered to this node */
+ final WeakReference nodeRef;
+
+ BlockOffer(PeerNode pn, long now) {
+ this.nodeRef = pn.myRef;
+ this.offeredTime = now;
+ }
+ }
+
+ /**
+ * Called when a data block is found (after it has been stored; there is a good chance of its being available in the
+ * near future). If there are nodes waiting for it, we will offer it to them.
+ */
+ public void onFound(KeyBlock block) {
+ Key key = block.getKey();
+ FailureTableEntry entry;
+ synchronized(this) {
+ entry = (FailureTableEntry) entriesByKey.get(key);
+ if(entry == null) return; // Nobody cares
+ entriesByKey.removeKey(key);
+ }
+ entry.offer();
+ }
+
+ /**
+ * Called when we get an offer for a key. If this is an SSK, we will only accept it if we have previously asked for it.
+ * If it is a CHK, we will accept it if we want it.
+ * @param key The key we are being offered.
+ * @param peer The node offering it.
+ */
+ public void onOffer(Key key, PeerNode peer) {
+ if(wantOffer(key, peer)) {
+ // Okay, we want the offer. Now what?
+ // Two ClientRequestScheduler's? Then you'd have to remove the key from two different RGA's :(
+ // Anyway, we don't want a key to be requested just because another key in the same group has an offer.
+ // So what we want is a list of keys at each level which have been offered.
+ // These would be considered before the other keys at that level, but only if the offer is still valid.
+ }
+ }
+
+ boolean wantOffer(Key key, PeerNode peer) {
+ FailureTableEntry entry;
+ long now = System.currentTimeMillis();
+ synchronized(this) {
+ entry = (FailureTableEntry) entriesByKey.get(key);
+ if(entry == null) return false; // we haven't asked for it
+
+ /*
+ * Accept (subject to later checks) if we asked for it.
+ * Should we accept it if we were asked for it? This is "bidirectional propagation".
+ * It's good because it makes the whole structure much more reliable; it's bad because
+ * it's not entirely under our control - we didn't choose to route it to the node, the node
+ * routed it to us. Now it's found it before we did...
+ *
+ * Attacks:
+ * - Frost spamming etc: Is it easier to offer data to our peers rather than inserting it? Will
+ * it result in it being propagated further? The peer node would then do the request, rather than
+ * this node doing an insert. Is that beneficial?
+ *
+ * Not relevant with CHKs anyway.
+ *
+ * On the plus side, propagation to nodes that have asked is worthwhile because reduced polling
+ * cost enables more secure messaging systems e.g. outbox polling...
+ * - Social engineering: If a key is unpopular, you can put a different copy of it on different
+ * nodes. You can then use this to trace the requestor - identify that he is or isn't on the target.
+ * You can't do this with a regular insert because it will often go several nodes even at htl 0.
+ * With subscriptions, you might be able to bypass this - but only if you know no other nodes in the
+ * neighbourhood are subscribed. Easier with SSKs; with CHKs you have only binary information of
+ * whether the person got the key (with social engineering). Hard to exploit on darknet; if you're
+ * that close to the suspect there are easier ways to get at them e.g. correlation attacks.
+ *
+ * Conclusion: We should accept the request if:
+ * - We asked for it from that node. (Note that a node might both have asked us and been asked).
+ * - That node asked for it, and it's a CHK.
+ */
+
+ if(!(entry.askedFromPeer(peer, now) ||
+ ((key instanceof NodeCHK) && entry.askedByPeer(peer, now)))) {
+ if(entry.isEmpty(now)) entriesByKey.removeKey(key);
+ return false;
+ }
+ if(entry.isEmpty(now)) entriesByKey.removeKey(key);
+
+ // Valid offer.
+
+ // Add to offers list
+
+ BlockOfferList bl = (BlockOfferList) blockOfferListByKey.get(key);
+ BlockOffer offer = new BlockOffer(peer, now);
+ if(bl == null) {
+ bl = new BlockOfferList(entry, offer);
+ }
+ blockOfferListByKey.push(key, offer);
+ trimOffersList(now);
+ }
+
+ // Now, does anyone want it?
+ // Firstly, do we want it?
+ if(!node.clientCore.clientWantKey(key)) return true;
+ if(entry.othersWant(peer)) return true;
+ return false;
+ }
+
+ private synchronized void trimOffersList(long now) {
+ while(true) {
+ if(blockOfferListByKey.isEmpty()) return;
+ BlockOfferList bl = (BlockOfferList) blockOfferListByKey.peekValue();
+ if(bl.isEmpty(now) || bl.expires() < now || blockOfferListByKey.size() > MAX_OFFERS) {
+ blockOfferListByKey.popKey();
+ } else {
+ return;
+ }
+ }
+ }
+}
Copied: branches/freenet-jfk/src/freenet/node/FailureTableEntry.java (from rev 14796, trunk/freenet/src/freenet/node/FailureTableEntry.java)
===================================================================
--- branches/freenet-jfk/src/freenet/node/FailureTableEntry.java (rev 0)
+++ branches/freenet-jfk/src/freenet/node/FailureTableEntry.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -0,0 +1,394 @@
+/**
+ *
+ */
+package freenet.node;
+
+import java.lang.ref.WeakReference;
+
+import freenet.keys.Key;
+
+class FailureTableEntry {
+
+ /** The key */
+ Key key; // FIXME should this be stored compressed somehow e.g. just the routing key?
+ /** The HTL at which it was requested last time. Any request of higher HTL will be let through. */
+ short htl;
+ /** Time of creation of this entry */
+ long creationTime;
+ /** Time we last received a request for the key */
+ long receivedTime;
+ /** Time we last received a DNF after sending a request for a key */
+ long sentTime;
+ /** Time at which we can send a request again */
+ long timeoutTime;
+ /** WeakReference's to PeerNode's who have requested the key */
+ WeakReference[] requestorNodes;
+ /** Times at which they requested it */
+ long[] requestorTimes;
+ /** Boot ID when they requested it. We don't send it to restarted nodes, as a
+ * (weak, but useful if combined with other measures) protection against seizure. */
+ long[] requestorBootIDs;
+ /** WeakReference's to PeerNode's we have requested it from */
+ WeakReference[] requestedNodes;
+ /** Their locations when we requested it */
+ double[] requestedLocs;
+ long[] requestedBootIDs;
+ long[] requestedTimes;
+
+ /** We remember that a node has asked us for a key for up to an hour; after that, we won't offer the key, and
+ * if we receive an offer from that node, we will reject it */
+ static final int MAX_TIME_BETWEEN_REQUEST_AND_OFFER = 60 * 60 * 1000;
+
+ FailureTableEntry(Key key2, short htl2, PeerNode[] requestors, PeerNode requested) {
+ long now = System.currentTimeMillis();
+ this.key = key2;
+ this.htl = htl2;
+ creationTime = now;
+ receivedTime = now;
+ sentTime = now;
+ requestorNodes = new WeakReference[requestors.length];
+ requestorTimes = new long[requestors.length];
+ requestorBootIDs = new long[requestors.length];
+ for(int i=0;i<requestorNodes.length;i++) {
+ requestorNodes[i] = requestors[i].myRef;
+ requestorTimes[i] = now;
+ requestorBootIDs[i] = requestors[i].getBootID();
+ }
+ requestedNodes = new WeakReference[] { requested.myRef };
+ requestedLocs = new double[] { requested.getLocation() };
+ requestedBootIDs = new long[] { requested.getBootID() };
+ }
+
+ /**
+ * Called when there is a failure which could cause a block to be added: Either a DataNotFound, or a
+ * RecentlyFailed.
+ * @param htl2
+ * @param requestors
+ * @param requested
+ */
+ public void onFailure(short htl2, PeerNode[] requestors, PeerNode requested, int timeout, long now) {
+ synchronized(this) {
+ long newTimeoutTime = now + timeout;
+ if(now > timeoutTime /* has expired */ || newTimeoutTime > timeoutTime) {
+ htl = htl2;
+ timeoutTime = newTimeoutTime;
+ }
+ addRequestors(requestors, now);
+ addRequestedFrom(new PeerNode[] { requested }, now);
+ }
+ }
+
+ // These are rather low level, in an attempt to absolutely minimize memory usage...
+ // The two methods have almost identical code/logic.
+ // Dunno if there's a more elegant way of dealing with this which doesn't significantly increase
+ // per entry byte cost.
+ // Note also this will generate some churn...
+
+ synchronized void addRequestors(PeerNode[] requestors, long now) {
+ receivedTime = now;
+ int notIncluded = 0;
+ int nulls = 0;
+ int ptr = 0;
+ for(int i=0;i<requestors.length;i++) {
+ PeerNode req = requestors[i];
+ boolean requestorIncluded = false;
+ for(int j=0;j<requestorNodes.length;j++) {
+ PeerNode got = requestorNodes[i] == null ? null : (PeerNode) requestorNodes[i].get();
+ // No longer subscribed if they have rebooted, or expired
+ if(got.getBootID() != requestorBootIDs[i] ||
+ now - requestorTimes[i] > MAX_TIME_BETWEEN_REQUEST_AND_OFFER) {
+ requestorNodes[i] = null;
+ got = null;
+ }
+ if(got == null)
+ nulls++;
+ if(got == req) {
+ // Update existing entry
+ requestorIncluded = true;
+ requestorTimes[j] = now;
+ requestorBootIDs[j] = req.getBootID();
+ break;
+ }
+ }
+ if(!requestorIncluded) {
+ notIncluded++;
+ requestors[ptr++] = requestors[i];
+ } // if it's new, keep it in requestors
+ }
+ for(int i=ptr;i<requestors.length;i++) requestors[i] = null;
+ if(notIncluded == 0 && nulls == 0) return;
+ // Because weak, these can become null; doesn't matter, but we want to minimise memory usage
+ if(notIncluded == nulls) {
+ // Nice special case
+ int x = 0;
+ for(int i=0;i<requestorNodes.length;i++) {
+ if(requestorNodes[i] == null || requestorNodes[i].get() == null) {
+ PeerNode pn = requestors[x++];
+ requestorNodes[i] = pn.myRef;
+ requestorTimes[i] = now;
+ requestorBootIDs[i] = pn.getBootID();
+ if(x == ptr) break;
+ }
+ }
+ return;
+ }
+ WeakReference[] newRequestorNodes = new WeakReference[requestorNodes.length+notIncluded-nulls];
+ long[] newRequestorTimes = new long[requestorNodes.length+notIncluded-nulls];
+ long[] newRequestorBootIDs = new long[requestorNodes.length+notIncluded-nulls];
+ int toIndex = 0;
+
+ for(int i=0;i<requestorNodes.length;i++) {
+ WeakReference ref = requestorNodes[i];
+ if(ref == null || ref.get() == null) continue;
+ newRequestorNodes[toIndex] = requestorNodes[i];
+ newRequestorTimes[toIndex] = requestorTimes[i];
+ newRequestorBootIDs[toIndex] = requestorBootIDs[i];
+ toIndex++;
+ }
+
+ for(int fromIndex=0;fromIndex<ptr;fromIndex++) {
+ PeerNode pn = requestors[fromIndex];
+ if(pn != null) {
+ newRequestorNodes[toIndex] = pn.myRef;
+ newRequestorTimes[toIndex] = now;
+ newRequestorBootIDs[toIndex] = pn.getBootID();
+ toIndex++;
+ }
+ }
+
+ for(int i=toIndex;i<newRequestorNodes.length;i++) newRequestorNodes[i] = null;
+ if(toIndex > newRequestorNodes.length + 2) {
+ WeakReference[] newNewRequestorNodes = new WeakReference[toIndex];
+ long[] newNewRequestorTimes = new long[toIndex];
+ long[] newNewRequestorBootIDs = new long[toIndex];
+ System.arraycopy(newRequestorNodes, 0, newNewRequestorNodes, 0, toIndex);
+ System.arraycopy(newRequestorTimes, 0, newNewRequestorTimes, 0, toIndex);
+ System.arraycopy(newRequestorBootIDs, 0, newNewRequestorBootIDs, 0, toIndex);
+ newRequestorNodes = newNewRequestorNodes;
+ newRequestorTimes = newNewRequestorTimes;
+ newRequestorBootIDs = newNewRequestorBootIDs;
+ }
+ requestorNodes = newRequestorNodes;
+ requestorTimes = newRequestorTimes;
+ requestorBootIDs = newRequestorBootIDs;
+ }
+
+ private synchronized void addRequestedFrom(PeerNode[] requestedFrom, long now) {
+ sentTime = now;
+ int notIncluded = 0;
+ int nulls = 0;
+ int ptr = 0;
+ for(int i=0;i<requestedFrom.length;i++) {
+ PeerNode req = requestedFrom[i];
+ boolean requestorIncluded = false;
+ for(int j=0;j<requestedNodes.length;j++) {
+ PeerNode got = requestedNodes[i] == null ? null : (PeerNode) requestedNodes[i].get();
+ if(got == null)
+ nulls++;
+ if(got == req) {
+ // Update existing entry
+ requestorIncluded = true;
+ requestedLocs[j] = req.getLocation();
+ requestedBootIDs[j] = req.getBootID();
+ requestedTimes[j] = now;
+ break;
+ }
+ }
+ if(!requestorIncluded) {
+ notIncluded++;
+ requestedFrom[ptr++] = requestedFrom[i];
+ } // if it's new, keep it in requestedFrom, otherwise delete it
+ }
+ for(int i=ptr;i<requestedFrom.length;i++) requestedFrom[i] = null;
+ if(notIncluded == 0 && nulls == 0) return;
+ // Because weak, these can become null; doesn't matter, but we want to minimise memory usage
+ if(notIncluded == nulls) {
+ // Nice special case
+ int x = 0;
+ for(int i=0;i<requestedNodes.length;i++) {
+ if(requestedNodes[i] == null || requestedNodes[i].get() == null) {
+ PeerNode pn = requestedFrom[x++];
+ requestedNodes[i] = pn.myRef;
+ requestedLocs[i] = pn.getLocation();
+ requestedTimes[i] = now;
+ if(x == ptr) break;
+ }
+ }
+ return;
+ }
+ WeakReference[] newRequestedNodes = new WeakReference[requestedNodes.length+notIncluded-nulls];
+ double[] newRequestedLocs = new double[requestedNodes.length+notIncluded-nulls];
+ long[] newRequestedBootIDs = new long[requestedNodes.length+notIncluded-nulls];
+ long[] newRequestedTimes = new long[requestedNodes.length+notIncluded-nulls];
+
+ int toIndex = 0;
+ for(int i=0;i<requestorNodes.length;i++) {
+ WeakReference ref = requestorNodes[i];
+ if(ref == null || ref.get() == null) continue;
+ newRequestedNodes[toIndex] = requestedNodes[i];
+ newRequestedTimes[toIndex] = requestedTimes[i];
+ newRequestedBootIDs[toIndex] = requestedBootIDs[i];
+ newRequestedLocs[toIndex] = requestedLocs[i];
+ toIndex++;
+ }
+
+ for(int fromIndex=0;fromIndex<ptr;fromIndex++) {
+ PeerNode pn = requestedFrom[fromIndex];
+ if(pn != null) {
+ newRequestedNodes[toIndex] = pn.myRef;
+ newRequestedTimes[toIndex] = now;
+ newRequestedBootIDs[toIndex] = pn.getBootID();
+ newRequestedLocs[toIndex] = pn.getLocation();
+ toIndex++;
+ }
+ }
+
+ for(int i=toIndex;i<newRequestedNodes.length;i++) newRequestedNodes[i] = null;
+ if(toIndex > newRequestedNodes.length + 2) {
+ WeakReference[] newNewRequestedNodes = new WeakReference[toIndex];
+ double[] newNewRequestedLocs = new double[toIndex];
+ long[] newNewRequestedBootIDs = new long[toIndex];
+ long[] newNewRequestedTimes = new long[toIndex];
+ System.arraycopy(newRequestedNodes, 0, newNewRequestedNodes, 0, toIndex);
+ System.arraycopy(newRequestedLocs, 0, newNewRequestedLocs, 0, toIndex);
+ System.arraycopy(newRequestedBootIDs, 0, newNewRequestedBootIDs, 0, toIndex);
+ System.arraycopy(newRequestedTimes, 0, newNewRequestedTimes, 0, toIndex);
+ newRequestedNodes = newNewRequestedNodes;
+ newRequestedLocs = newNewRequestedLocs;
+ newRequestedBootIDs = newNewRequestedBootIDs;
+ newRequestedTimes = newNewRequestedTimes;
+ }
+ requestedNodes = newRequestedNodes;
+ requestedLocs = newRequestedLocs;
+ requestedBootIDs = newRequestedBootIDs;
+ requestedTimes = newRequestedTimes;
+ }
+
+ public synchronized double bestLiveLocDiff() {
+ double bestDiff = 2.0;
+ for(int i=0;i<requestedNodes.length;i++) {
+ if(requestedNodes[i] == null) continue;
+ PeerNode pn = (PeerNode) requestedNodes[i].get();
+ if(pn == null) continue;
+ if(!(pn.isRoutable() && pn.isRoutingBackedOff())) continue;
+ double diff = Location.distance(key.toNormalizedDouble(), requestedLocs[i]);
+ if(diff < bestDiff) bestDiff = diff;
+ }
+ return bestDiff;
+ }
+
+ /** Offer this key to all the nodes that have requested it, and all the nodes it has been requested from.
+ * Called after a) the data has been stored, and b) this entry has been removed from the FT */
+ public void offer() {
+ for(int i=0;i<requestorNodes.length;i++) {
+ WeakReference ref = requestorNodes[i];
+ if(ref == null) continue;
+ PeerNode pn = (PeerNode) ref.get();
+ if(pn == null) continue;
+ if(pn.getBootID() != requestorBootIDs[i]) continue;
+ pn.offer(key);
+ }
+ for(int i=0;i<requestedNodes.length;i++) {
+ WeakReference ref = requestedNodes[i];
+ if(ref == null) continue;
+ PeerNode pn = (PeerNode) ref.get();
+ if(pn == null) continue;
+ if(pn.getBootID() != requestedBootIDs[i]) continue;
+ pn.offer(key);
+ }
+ }
+
+ /**
+ * Has any node asked for this key?
+ */
+ public synchronized boolean othersWant(PeerNode peer) {
+ boolean anyValid = false;
+ for(int i=0;i<requestorNodes.length;i++) {
+ WeakReference ref = requestorNodes[i];
+ if(ref == null) continue;
+ PeerNode pn = (PeerNode) ref.get();
+ if(pn == null) {
+ requestorNodes[i] = null;
+ continue;
+ }
+ long bootID = pn.getBootID();
+ if(bootID != requestorBootIDs[i]) {
+ requestorNodes[i] = null;
+ continue;
+ }
+ anyValid = true;
+ if(pn == peer) return true;
+ }
+ if(!anyValid) {
+ requestorNodes = new WeakReference[0];
+ requestorTimes = requestorBootIDs = new long[0];
+ }
+ return false;
+ }
+
+ /**
+ * Has this peer asked us for the key?
+ */
+ public synchronized boolean askedByPeer(PeerNode peer, long now) {
+ boolean anyValid = false;
+ for(int i=0;i<requestorNodes.length;i++) {
+ WeakReference ref = requestorNodes[i];
+ if(ref == null) continue;
+ PeerNode pn = (PeerNode) ref.get();
+ if(pn == null) {
+ requestorNodes[i] = null;
+ continue;
+ }
+ long bootID = pn.getBootID();
+ if(bootID != requestorBootIDs[i]) {
+ requestorNodes[i] = null;
+ continue;
+ }
+ if(now - requestorTimes[i] > MAX_TIME_BETWEEN_REQUEST_AND_OFFER) return true;
+ anyValid = true;
+ if(pn == peer) return true;
+ }
+ if(!anyValid) {
+ requestorNodes = new WeakReference[0];
+ requestorTimes = requestorBootIDs = new long[0];
+ }
+ return false;
+ }
+
+ /**
+ * Have we asked this peer for the key?
+ */
+ public synchronized boolean askedFromPeer(PeerNode peer, long now) {
+ boolean anyValid = false;
+ for(int i=0;i<requestedNodes.length;i++) {
+ WeakReference ref = requestedNodes[i];
+ if(ref == null) continue;
+ PeerNode pn = (PeerNode) ref.get();
+ if(pn == null) {
+ requestedNodes[i] = null;
+ continue;
+ }
+ long bootID = pn.getBootID();
+ if(bootID != requestedBootIDs[i]) {
+ requestedNodes[i] = null;
+ continue;
+ }
+ if(now - requestedTimes[i] > MAX_TIME_BETWEEN_REQUEST_AND_OFFER) return true;
+ anyValid = true;
+ if(pn == peer) return true;
+ }
+ if(!anyValid) {
+ requestedNodes = new WeakReference[0];
+ requestedTimes = requestedBootIDs = new long[0];
+ }
+ return false;
+ }
+
+ public synchronized boolean isEmpty(long now) {
+ if(timeoutTime > now) return false;
+ if(requestedNodes.length > 0) return false;
+ if(requestorNodes.length > 0) return false;
+ return true;
+ }
+
+}
\ No newline at end of file
Modified: branches/freenet-jfk/src/freenet/node/GlobalProbe.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/GlobalProbe.java 2007-08-20 13:57:42 UTC (rev 14802)
+++ branches/freenet-jfk/src/freenet/node/GlobalProbe.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -19,8 +19,8 @@
GlobalProbe(Node n) {
this.node = n;
cb = new ProbeCallback() {
- public void onCompleted(String reason, double target, double best, double nearest, long id, short counter) {
- String msg = "Completed probe request: "+target+" -> "+best+"\r\nNearest actually hit "+nearest+", "+counter+" hops in "+(System.currentTimeMillis() - lastTime)+", id "+id+"\r\n";
+ public void onCompleted(String reason, double target, double best, double nearest, long id, short counter, short linearCount) {
+ String msg = "Completed probe request: "+target+" -> "+best+"\r\nNearest actually hit "+nearest+", "+counter+" nodes ("+linearCount+" hops) in "+(System.currentTimeMillis() - lastTime)+", id "+id+"\r\n";
Logger.error(this, msg);
synchronized(GlobalProbe.this) {
doneSomething = true;
@@ -30,9 +30,9 @@
}
}
- public void onTrace(long uid, double target, double nearest, double best, short htl, short counter, double location, long nodeUID, double[] peerLocs, long[] peerUIDs) {
- String msg = "Probe trace: UID="+uid+" target="+target+" nearest="+nearest+" best="+best+" htl="+htl+" counter="+counter+" location="+location+" node UID="+nodeUID+" peer locs="+StringArray.toString(peerLocs)+" peer UIDs="+StringArray.toString(peerUIDs);
- Logger.error(this, msg);
+ public void onTrace(long uid, double target, double nearest, double best, short htl, short counter, double location, long nodeUID, double[] peerLocs, long[] peerUIDs, double[] locsNotVisited, short forkCount, short linearCount, String reason, long prevUID) {
+ String msg = "Probe trace: UID="+uid+" target="+target+" nearest="+nearest+" best="+best+" htl="+htl+" counter="+counter+" location="+location+" node UID="+nodeUID+" prev UID="+prevUID+" peers="+NodeDispatcher.peersUIDsToString(peerUIDs, peerLocs)+" locs not visited: "+StringArray.toString(locsNotVisited)+" fork count: "+forkCount+" linear count: "+linearCount+" from "+reason;
+ Logger.normal(this, msg);
}
};
Modified: branches/freenet-jfk/src/freenet/node/IPDetectorPluginManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/IPDetectorPluginManager.java 2007-08-20 13:57:42 UTC (rev 14802)
+++ branches/freenet-jfk/src/freenet/node/IPDetectorPluginManager.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -4,6 +4,7 @@
import java.util.HashSet;
import java.util.Vector;
+import freenet.io.comm.FreenetInetAddress;
import freenet.io.comm.Peer;
import freenet.l10n.L10n;
import freenet.node.useralerts.ProxyUserAlert;
@@ -45,7 +46,7 @@
div.addChild("#", text);
if(suggestPortForward) {
L10n.addL10nSubstitution(div, "IPDetectorPluginManager.suggestForwardPortWithLink", new String[] { "link", "/link", "port" },
- new String[] { "<a href=\"/?_CHECKED_HTTP_=http://wiki.freenetproject.org/FirewallAndRouterIssues\">", "</a>", Integer.toString(node.portNumber) });
+ new String[] { "<a href=\"/?_CHECKED_HTTP_=http://wiki.freenetproject.org/FirewallAndRouterIssues\">", "</a>", Integer.toString(node.getDarknetPortNumber()) });
}
return div;
}
@@ -58,7 +59,7 @@
if(!suggestPortForward) return text;
StringBuffer sb = new StringBuffer();
sb.append(text);
- sb.append(l10n("suggestForwardPort", "port", Integer.toString(node.portNumber)));
+ sb.append(l10n("suggestForwardPort", "port", Integer.toString(node.getDarknetPortNumber())));
return sb.toString();
}
@@ -90,7 +91,6 @@
static boolean logMINOR;
private final NodeIPDetector detector;
- private final Ticker ticker;
private final Node node;
FredPluginIPDetector[] plugins;
private final MyUserAlert noConnectionAlert;
@@ -105,7 +105,6 @@
logMINOR = Logger.shouldLog(Logger.MINOR, getClass());
plugins = new FredPluginIPDetector[0];
this.node = node;
- this.ticker = node.ps;
this.detector = detector;
noConnectionAlert = new MyUserAlert( l10n("noConnectivityTitle"), l10n("noConnectivity"),
true, UserAlert.ERROR);
@@ -151,7 +150,7 @@
} catch (Throwable t) {
Logger.error(this, "Caught "+t, t);
}
- ticker.queueTimedJob(new Runnable() {
+ node.getTicker().queueTimedJob(new Runnable() {
public void run() {
tryMaybeRun();
}
@@ -239,7 +238,7 @@
if(logMINOR) Logger.minor(this, "Maybe running IP detection plugins", new Exception("debug"));
PeerNode[] peers = node.getPeerNodes();
PeerNode[] conns = node.getConnectedPeers();
- Peer[] nodeAddrs = detector.getPrimaryIPAddress();
+ FreenetInetAddress[] nodeAddrs = detector.getPrimaryIPAddress();
long now = System.currentTimeMillis();
synchronized(this) {
if(plugins.length == 0) {
@@ -407,7 +406,7 @@
* @param nodeAddrs Our peers' addresses.
* @return True if we should run a detection.
*/
- private boolean shouldDetectDespiteRealIP(long now, PeerNode[] peers, Peer[] nodeAddrs) {
+ private boolean shouldDetectDespiteRealIP(long now, PeerNode[] peers, FreenetInetAddress[] nodeAddrs) {
// We might still be firewalled?
// First, check only once per day or startup
if(now - lastDetectAttemptEndedTime < 12*60*60*1000) {
@@ -432,7 +431,7 @@
// Is it internal?
boolean internal = false;
for(int j=0;j<nodeAddrs.length;j++) {
- if(addr.equals(nodeAddrs[j].getAddress())) {
+ if(addr.equals(nodeAddrs[j])) {
// Internal
internal = true;
break;
@@ -469,9 +468,7 @@
if(logMINOR) Logger.minor(this, "Detecting...");
synchronized(this) {
runner = new DetectorRunner();
- Thread t = new Thread(runner);
- t.setDaemon(true);
- t.start();
+ node.executor.execute(runner, "Plugin detector runner");
}
}
Modified: branches/freenet-jfk/src/freenet/node/InsertHandler.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/InsertHandler.java 2007-08-20 13:57:42 UTC (rev 14802)
+++ branches/freenet-jfk/src/freenet/node/InsertHandler.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -57,8 +57,8 @@
htl = req.getShort(DMT.HTL);
closestLoc = req.getDouble(DMT.NEAREST_LOCATION);
double targetLoc = key.toNormalizedDouble();
- double myLoc = node.lm.getLocation().getValue();
- if(PeerManager.distance(targetLoc, myLoc) < PeerManager.distance(targetLoc, closestLoc)) {
+ double myLoc = node.lm.getLocation();
+ if(Location.distance(targetLoc, myLoc) < Location.distance(targetLoc, closestLoc)) {
closestLoc = myLoc;
htl = node.maxHTL();
}
@@ -79,7 +79,7 @@
Logger.error(this, "Caught in run() "+t, t);
} finally {
if(logMINOR) Logger.minor(this, "Exiting InsertHandler.run() for "+uid);
- node.unlockUID(uid, false, true);
+ node.unlockUID(uid, false, true, false);
}
}
@@ -147,9 +147,7 @@
// Receive the data, off thread
Runnable dataReceiver = new DataReceiver();
- Thread t = new Thread(dataReceiver, "InsertHandler$DataReceiver for UID "+uid);
- t.setDaemon(true);
- t.start();
+ node.executor.execute(dataReceiver, "InsertHandler$DataReceiver for UID "+uid);
if(htl == 0) {
canCommit = true;
Modified: branches/freenet-jfk/src/freenet/node/KeyTracker.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/KeyTracker.java 2007-08-20 13:57:42 UTC (rev 14802)
+++ branches/freenet-jfk/src/freenet/node/KeyTracker.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -407,8 +407,8 @@
super(packetNumber);
this.createdTime = System.currentTimeMillis();
if(sendSoon) {
- activeTime -= 500;
- urgentTime -= 500;
+ activeTime -= activeDelay;
+ urgentTime -= activeDelay;
}
}
Modified: branches/freenet-jfk/src/freenet/node/Location.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/Location.java 2007-08-20 13:57:42 UTC (rev 14802)
+++ branches/freenet-jfk/src/freenet/node/Location.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -3,7 +3,7 @@
* http://www.gnu.org/ for further details of the GPL. */
package freenet.node;
-import freenet.crypt.RandomSource;
+import freenet.support.Logger;
/**
* @author amphibian
@@ -12,55 +12,43 @@
* Simply a number from 0.0 to 1.0.
*/
public class Location {
- private double loc;
- private int hashCode;
-
- private Location(double location) {
- setValue(location);
- }
- public Location(String init) throws FSParseException {
- try {
- setValue(Double.parseDouble(init));
- } catch (NumberFormatException e) {
- throw new FSParseException(e);
- }
- }
+ public static double getLocation(String init) throws FSParseException {
+ try {
+ if(init == null) throw new FSParseException("Null location");
+ double d = Double.parseDouble(init);
+ if(d < 0.0 || d > 1.0) throw new FSParseException("Invalid location "+d);
+ return d;
+ } catch (NumberFormatException e) {
+ throw new FSParseException(e);
+ }
+ }
+
+ static double distance(PeerNode p, double loc) {
+ double d = distance(p.getLocation(), loc);
+ return d;
+ //return d * p.getBias();
+ }
- public double getValue() {
- return loc;
- }
+ /**
+ * Distance between two locations.
+ * Both parameters must be in [0.0, 1.0].
+ */
+ public static double distance(double a, double b) {
+ return distance(a, b, false);
+ }
- /**
- * @return A random Location to initialize the node to.
- */
- public static Location randomInitialLocation(RandomSource r) {
- return new Location(r.nextDouble());
- }
+ public static double distance(double a, double b, boolean allowCrazy) {
+ if(((a < 0.0 || a > 1.0)||(b < 0.0 || b > 1.0)) && !allowCrazy) {
+ Logger.error(PeerManager.class, "Invalid Location ! a = "+a +" b = "+ b + "Please report this bug!", new Exception("error"));
+ throw new NullPointerException();
+ }
+ // Circular keyspace
+ if (a > b) return Math.min (a - b, 1.0 - a + b);
+ else return Math.min (b - a, 1.0 - b + a);
+ }
- public void setValue(double newLoc) {
- if((loc < 0.0) || (loc > 1.0))
- throw new IllegalArgumentException();
- this.loc = newLoc;
- long l = Double.doubleToLongBits(newLoc);
- hashCode = ((int)(l >>> 32)) ^ ((int)l);
- }
-
- public boolean equals(Object o) {
- if(o instanceof Location) {
- return Math.abs(((Location)o).loc - loc) <= Double.MIN_VALUE;
- }
- return false;
- }
-
- public int hashCode() {
- return hashCode;
- }
-
- /**
- * Randomize the location.
- */
- public synchronized void randomize(RandomSource r) {
- setValue(r.nextDouble());
- }
+ public static boolean equals(double newLoc, double currentLocation) {
+ return Math.abs(newLoc - currentLocation) < Double.MIN_VALUE * 2;
+ }
}
Modified: branches/freenet-jfk/src/freenet/node/LocationManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/LocationManager.java 2007-08-20 13:57:42 UTC (rev 14802)
+++ branches/freenet-jfk/src/freenet/node/LocationManager.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -57,34 +57,38 @@
long timeLastSuccessfullySwapped;
public LocationManager(RandomSource r) {
- loc = Location.randomInitialLocation(r);
+ loc = r.nextDouble();
sender = new SwapRequestSender();
this.r = r;
recentlyForwardedIDs = new Hashtable();
logMINOR = Logger.shouldLog(Logger.MINOR, this);
}
- Location loc;
- double locChangeSession = 0.0;
+ private double loc;
+ private double locChangeSession = 0.0;
int numberOfRemotePeerLocationsSeenInSwaps = 0;
/**
* @return The current Location of this node.
*/
- public Location getLocation() {
+ public synchronized double getLocation() {
return loc;
}
/**
* @param l
*/
- public void setLocation(Location l) {
+ public synchronized void setLocation(double l) {
+ if(l < 0.0 || l > 1.0) {
+ Logger.error(this, "Setting invalid location: "+l, new Exception("error"));
+ return;
+ }
this.loc = l;
}
- public void updateLocationChangeSession(double newLoc) {
- double oldLoc = this.loc.getValue();
+ public synchronized void updateLocationChangeSession(double newLoc) {
+ double oldLoc = loc;
// Patterned after PeerManager.distance( double, double ), but also need to know the direction of the change
if (newLoc > oldLoc) {
double directDifference = newLoc - oldLoc;
@@ -116,9 +120,7 @@
public void startSender(Node n, SwapRequestInterval interval) {
this.node = n;
this.interval = interval;
- Thread t = new Thread(sender, "SwapRequest sender");
- t.setDaemon(true);
- t.start();
+ n.executor.execute(sender, "SwapRequest sender");
}
/**
@@ -154,12 +156,12 @@
if(System.currentTimeMillis() - timeLastSuccessfullySwapped > 30*1000) {
try {
boolean myFlag = false;
- double myLoc = loc.getValue();
+ double myLoc = getLocation();
PeerNode[] peers = node.peers.connectedPeers;
for(int i=0;i<peers.length;i++) {
PeerNode pn = peers[i];
if(pn.isRoutable()) {
- double ploc = pn.getLocation().getValue();
+ double ploc = pn.getLocation();
if(Math.abs(ploc - myLoc) <= Double.MIN_VALUE) {
myFlag = true;
// Log an ERROR
@@ -171,7 +173,7 @@
}
}
if(myFlag) {
- loc.randomize(node.random);
+ setLocation(node.random.nextDouble());
announceLocChange();
node.writeNodeFile();
}
@@ -196,10 +198,8 @@
* the wilderness.
*/
private void startSwapRequest() {
- Thread t = new Thread(new OutgoingSwapRequestHandler(),
- "Outgoing swap request handler for port "+node.portNumber);
- t.setDaemon(true);
- t.start();
+ node.executor.execute(new OutgoingSwapRequestHandler(),
+ "Outgoing swap request handler for port "+node.getDarknetPortNumber());
}
/**
@@ -246,7 +246,7 @@
// Create my side
long random = r.nextLong();
- double myLoc = loc.getValue();
+ double myLoc = getLocation();
LocationUIDPair[] friendLocsAndUIDs = node.peers.getPeerLocationsAndUIDs();
double[] friendLocs = extractLocs(friendLocsAndUIDs);
long[] myValueLong = new long[1+1+friendLocs.length];
@@ -326,7 +326,7 @@
// Send our SwapComplete
Message confirm = DMT.createFNPSwapComplete(uid, myValue);
- confirm.addSubMessage(DMT.createFNPSwapLocations(Fields.longsToBytes(extractUIDs(friendLocsAndUIDs))));
+ confirm.addSubMessage(DMT.createFNPSwapLocations(extractUIDs(friendLocsAndUIDs)));
node.usm.send(pn, confirm, null);
@@ -338,7 +338,7 @@
timeLastSuccessfullySwapped = System.currentTimeMillis();
// Swap
updateLocationChangeSession(hisLoc);
- loc.setValue(hisLoc);
+ setLocation(hisLoc);
if(logMINOR) Logger.minor(this, "Swapped: "+myLoc+" <-> "+hisLoc+" - "+uid);
swaps++;
announceLocChange();
@@ -376,7 +376,7 @@
// We can't lock friends_locations, so lets just
// pretend that they're locked
long random = r.nextLong();
- double myLoc = loc.getValue();
+ double myLoc = getLocation();
LocationUIDPair[] friendLocsAndUIDs = node.peers.getPeerLocationsAndUIDs();
double[] friendLocs = extractLocs(friendLocsAndUIDs);
long[] myValueLong = new long[1+1+friendLocs.length];
@@ -438,7 +438,7 @@
byte[] hisHash = ((ShortBuffer)reply.getObject(DMT.HASH)).getData();
Message confirm = DMT.createFNPSwapCommit(uid, myValue);
- confirm.addSubMessage(DMT.createFNPSwapLocations(Fields.longsToBytes(extractUIDs(friendLocsAndUIDs))));
+ confirm.addSubMessage(DMT.createFNPSwapLocations(extractUIDs(friendLocsAndUIDs)));
filter1.clearOr();
MessageFilter filter3 = MessageFilter.create().setField(DMT.UID, uid).setType(DMT.FNPSwapComplete).setTimeout(TIMEOUT).setSource(pn);
@@ -518,7 +518,7 @@
timeLastSuccessfullySwapped = System.currentTimeMillis();
// Swap
updateLocationChangeSession(hisLoc);
- loc.setValue(hisLoc);
+ setLocation(hisLoc);
if(logMINOR) Logger.minor(this, "Swapped: "+myLoc+" <-> "+hisLoc+" - "+uid);
swaps++;
announceLocChange();
@@ -542,8 +542,8 @@
* Tell all connected peers that our location has changed
*/
private void announceLocChange() {
- Message msg = DMT.createFNPLocChangeNotification(loc.getValue());
- node.peers.localBroadcast(msg);
+ Message msg = DMT.createFNPLocChangeNotification(getLocation());
+ node.peers.localBroadcast(msg, false);
}
private boolean locked;
@@ -569,7 +569,7 @@
if(logMINOR) Logger.minor(this, "Already locked");
return false;
}
- if(logMINOR) Logger.minor(this, "Locking on port "+node.portNumber);
+ if(logMINOR) Logger.minor(this, "Locking on port "+node.getDarknetPortNumber());
locked = true;
lockedTime = System.currentTimeMillis();
return true;
@@ -581,7 +581,7 @@
locked = false;
long lockTime = System.currentTimeMillis() - lockedTime;
if(logMINOR) {
- Logger.minor(this, "Unlocking on port "+node.portNumber);
+ Logger.minor(this, "Unlocking on port "+node.getDarknetPortNumber());
Logger.minor(this, "lockTime: "+lockTime);
}
}
@@ -640,22 +640,22 @@
double A = 1.0;
for(int i=0;i<friendLocs.length;i++) {
if(Math.abs(friendLocs[i] - myLoc) <= Double.MIN_VALUE) continue;
- A *= PeerManager.distance(friendLocs[i], myLoc);
+ A *= Location.distance(friendLocs[i], myLoc);
}
for(int i=0;i<hisFriendLocs.length;i++) {
if(Math.abs(hisFriendLocs[i] - hisLoc) <= Double.MIN_VALUE) continue;
- A *= PeerManager.distance(hisFriendLocs[i], hisLoc);
+ A *= Location.distance(hisFriendLocs[i], hisLoc);
}
// B = the same, with our two values swapped
double B = 1.0;
for(int i=0;i<friendLocs.length;i++) {
if(Math.abs(friendLocs[i] - hisLoc) <= Double.MIN_VALUE) continue;
- B *= PeerManager.distance(friendLocs[i], hisLoc);
+ B *= Location.distance(friendLocs[i], hisLoc);
}
for(int i=0;i<hisFriendLocs.length;i++) {
if(Math.abs(hisFriendLocs[i] - myLoc) <= Double.MIN_VALUE) continue;
- B *= PeerManager.distance(hisFriendLocs[i], myLoc);
+ B *= Location.distance(hisFriendLocs[i], myLoc);
}
//Logger.normal(this, "A="+A+" B="+B);
@@ -762,9 +762,7 @@
IncomingSwapRequestHandler isrh =
new IncomingSwapRequestHandler(m, pn, item);
if(logMINOR) Logger.minor(this, "Handling... "+uid);
- Thread t = new Thread(isrh, "Incoming swap request handler for port "+node.portNumber);
- t.setDaemon(true);
- t.start();
+ node.executor.execute(isrh, "Incoming swap request handler for port "+node.getDarknetPortNumber());
return true;
} catch (Error e) {
unlock();
@@ -961,7 +959,8 @@
/** Spy on locations in somebody else's swap request. Greatly increases the
* speed at which we can gather location data to estimate the network's size.
- * @param swappingWithMe
+ * @param swappingWithMe True if this node is participating in the swap, false if it is
+ * merely spying on somebody else's swap.
*/
private void spyOnLocations(Message m, boolean ignoreIfOld, boolean swappingWithMe, double myLoc) {
@@ -1060,7 +1059,7 @@
recentlyForwardedIDs.remove(new Long(item.outgoingID));
}
- private final long MAX_AGE = 7*24*60*60*1000;
+ private static final long MAX_AGE = 7*24*60*60*1000;
private final TimeSortedHashtable knownLocs = new TimeSortedHashtable();
@@ -1125,7 +1124,7 @@
public static double[] extractLocs(PeerNode[] peers, boolean indicateBackoff) {
double[] locs = new double[peers.length];
for(int i=0;i<peers.length;i++) {
- locs[i] = peers[i].getLocation().getValue();
+ locs[i] = peers[i].getLocation();
if(indicateBackoff) {
if(peers[i].isRoutingBackedOff())
locs[i] += 1;
@@ -1142,4 +1141,8 @@
uids[i] = peers[i].swapIdentifier;
return uids;
}
+
+ public synchronized double getLocChangeSession() {
+ return locChangeSession;
+ }
}
Modified: branches/freenet-jfk/src/freenet/node/LoggingConfigHandler.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/LoggingConfigHandler.java 2007-08-20 13:57:42 UTC (rev 14802)
+++ branches/freenet-jfk/src/freenet/node/LoggingConfigHandler.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -10,6 +10,7 @@
import freenet.config.InvalidConfigValueException;
import freenet.config.OptionFormatException;
import freenet.config.SubConfig;
+import freenet.support.Executor;
import freenet.support.FileLoggerHook;
import freenet.support.Logger;
import freenet.support.LoggerHook;
@@ -55,9 +56,11 @@
private String logRotateInterval;
private long maxCachedLogBytes;
private int maxCachedLogLines;
+ private final Executor executor;
- public LoggingConfigHandler(SubConfig loggingConfig) throws InvalidConfigValueException {
+ public LoggingConfigHandler(SubConfig loggingConfig, Executor executor) throws InvalidConfigValueException {
this.config = loggingConfig;
+ this.executor = executor;
loggingConfig.register("enabled", true, 1, true, false, "LogConfigHandler.enabled", "LogConfigHandler.enabledLong",
new BooleanCallback() {
@@ -295,9 +298,7 @@
}
void start() {
- Thread t = new Thread(this, "Old log directory "+logDir+" deleter");
- t.setDaemon(true);
- t.start();
+ executor.execute(this, "Old log directory "+logDir+" deleter");
}
public void run() {
Modified: branches/freenet-jfk/src/freenet/node/LowLevelGetException.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/LowLevelGetException.java 2007-08-20 13:57:42 UTC (rev 14802)
+++ branches/freenet-jfk/src/freenet/node/LowLevelGetException.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -29,6 +29,8 @@
public static final int VERIFY_FAILED = 8;
/** Request cancelled by user */
public static final int CANCELLED = 9;
+ /** Ran into a failure table */
+ public static final int RECENTLY_FAILED = 10;
static final String getMessage(int reason) {
switch(reason) {
@@ -50,6 +52,8 @@
return "Node sent us invalid data";
case CANCELLED:
return "Request cancelled";
+ case RECENTLY_FAILED:
+ return "Request killed by failure table due to recently DNFing on a downstream node";
default:
return "Unknown error code: "+reason;
}
Modified: branches/freenet-jfk/src/freenet/node/Node.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/Node.java 2007-08-20 13:57:42 UTC (rev 14802)
+++ branches/freenet-jfk/src/freenet/node/Node.java 2007-08-20 14:10:51 UTC (rev 14803)
@@ -8,7 +8,6 @@
import java.io.BufferedReader;
import java.io.BufferedWriter;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -16,10 +15,7 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.math.BigInteger;
import java.net.InetAddress;
-import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.util.Arrays;
@@ -28,10 +24,9 @@
import java.util.Iterator;
import java.util.Locale;
import java.util.MissingResourceException;
-import java.util.zip.DeflaterOutputStream;
+import java.util.Random;
-import net.i2p.util.NativeBigInteger;
-
+import org.spaceroots.mantissa.random.MersenneTwister;
import org.tanukisoftware.wrapper.WrapperManager;
import com.sleepycat.je.DatabaseException;
@@ -48,23 +43,19 @@
import freenet.config.LongOption;
import freenet.config.PersistentConfig;
import freenet.config.SubConfig;
-import freenet.crypt.DSA;
-import freenet.crypt.DSAGroup;
-import freenet.crypt.DSAPrivateKey;
import freenet.crypt.DSAPublicKey;
-import freenet.crypt.DSASignature;
-import freenet.crypt.Global;
import freenet.crypt.RandomSource;
import freenet.crypt.SHA256;
+import freenet.crypt.Yarrow;
import freenet.io.comm.DMT;
import freenet.io.comm.DisconnectedException;
import freenet.io.comm.FreenetInetAddress;
import freenet.io.comm.Message;
+import freenet.io.comm.MessageCore;
import freenet.io.comm.MessageFilter;
import freenet.io.comm.Peer;
import freenet.io.comm.PeerParseException;
import freenet.io.comm.ReferenceSignatureVerificationException;
-import freenet.io.comm.UdpSocketManager;
import freenet.io.xfer.PartiallyReceivedBlock;
import freenet.keys.CHKBlock;
import freenet.keys.CHKVerifyException;
@@ -74,8 +65,6 @@
import freenet.keys.ClientKeyBlock;
import freenet.keys.ClientSSK;
import freenet.keys.ClientSSKBlock;
-import freenet.keys.FreenetURI;
-import freenet.keys.InsertableClientSSK;
import freenet.keys.Key;
import freenet.keys.KeyBlock;
import freenet.keys.KeyVerifyException;
@@ -88,24 +77,25 @@
import freenet.node.useralerts.BuildOldAgeUserAlert;
import freenet.node.useralerts.ExtOldAgeUserAlert;
import freenet.node.useralerts.MeaningfulNodeNameUserAlert;
-import freenet.node.useralerts.N2NTMUserAlert;
+import freenet.node.useralerts.OpennetUserAlert;
+import freenet.node.useralerts.TimeSkewDetectedUserAlert;
import freenet.node.useralerts.UserAlert;
import freenet.pluginmanager.PluginManager;
import freenet.store.BerkeleyDBFreenetStore;
import freenet.store.FreenetStore;
import freenet.store.KeyCollisionException;
-import freenet.support.Base64;
import freenet.support.DoubleTokenBucket;
+import freenet.support.Executor;
import freenet.support.Fields;
import freenet.support.FileLoggerHook;
import freenet.support.HTMLEncoder;
import freenet.support.HTMLNode;
import freenet.support.HexUtil;
-import freenet.support.IllegalBase64Exception;
import freenet.support.ImmutableByteArrayWrapper;
import freenet.support.LRUHashtable;
import freenet.support.LRUQueue;
import freenet.support.Logger;
+import freenet.support.OOMHandler;
import freenet.support.ShortBuffer;
import freenet.support.SimpleFieldSet;
import freenet.support.api.BooleanCallback;
@@ -117,34 +107,13 @@
/**
* @author amphibian
*/
-public class Node {
+public class Node implements TimeSkewDetectorCallback {
private static boolean logMINOR;
- static class NodeBindtoCallback implements StringCallback {
-
- final Node node;
-
- NodeBindtoCallback(Node n) {
- this.node = n;
- }
-
- public String get() {
- if(node.getBindTo()!=null)
- return node.getBindTo();
- else
- return "0.0.0.0";
- }
-
- public void set(String val) throws InvalidConfigValueException {
- if(val.equals(get())) return;
- // FIXME why not? Can't we use freenet.io.NetworkInterface like everywhere else, just adapt it for UDP?
- throw new InvalidConfigValueException("Cannot be updated on the fly");
- }
- }
-
private static MeaningfulNodeNameUserAlert nodeNameUserAlert;
private static BuildOldAgeUserAlert buildOldAgeUserAlert;
+ private static TimeSkewDetectedUserAlert timeSkewDetectedUserAlert;
public class NodeNameCallback implements StringCallback{
Node node;
@@ -162,12 +131,12 @@
}
public void set(String val) throws InvalidConfigValueException {
+ if(get().equals(val)) return;
+ else if(val.length() > 128)
+ throw new InvalidConfigValueException("The given node name is too long ("+val+')');
+ else if("".equals(val))
+ val = "~none~";
myName = val;
- if(myName.startsWith("Node id|")|| myName.equals("MyFirstFreenetNode")){
- clientCore.alerts.register(nodeNameUserAlert);
- }else{
- clientCore.alerts.unregister(nodeNameUserAlert);
- }
}
}
@@ -256,9 +225,6 @@
static final long TESTNET_MIN_MAX_ZIPPED_LOGFILES = 512*1024*1024;
static final String TESTNET_MIN_MAX_ZIPPED_LOGFILES_STRING = "512M";
- // FIXME: abstract out address stuff? Possibly to something like NodeReference?
- final int portNumber;
-
/** Datastore directory */
private final File storeDir;
@@ -311,22 +277,6 @@
private final HashSet transferringRequestHandlers;
/** CHKInsertSender's currently running, by KeyHTLPair */
private final HashMap insertSenders;
- /** My crypto group */
- private DSAGroup myCryptoGroup;
- /** My private key */
- private DSAPrivateKey myPrivKey;
- /** My public key */
- private DSAPublicKey myPubKey;
-
- /** My ARK SSK private key */
- InsertableClientSSK myARK;
- /** My ARK sequence number */
- long myARKNumber;
- // FIXME remove old ARK support
- /** My old ARK SSK private key */
- InsertableClientSSK myOldARK;
- /** My old ARK sequence number */
- long myOldARKNumber;
/** FetchContext for ARKs */
public final FetchContext arkFetcherContext;
@@ -345,20 +295,9 @@
private final HashSet runningCHKPutUIDs;
private final HashSet runningSSKPutUIDs;
- byte[] myIdentity; // FIXME: simple identity block; should be unique
- /** Hash of identity. Used as setup key. */
- byte[] identityHash;
- /** Hash of hash of identity i.e. hash of setup key. */
- byte[] identityHashHash;
/** Semi-unique ID for swap requests. Used to identify us so that the
* topology can be reconstructed. */
public long swapIdentifier;
- /** The signature of the above fieldset */
- private DSASignature myReferenceSignature = null;
- /** A synchronization object used while signing the reference fiedlset */
- private volatile Object referenceSync = new Object();
- /** An ordered version of the FieldSet, without the signature */
- private String mySignedReference = null;
private String myName;
final LocationManager lm;
/** My peers */
@@ -369,10 +308,26 @@
final File extraPeerDataDir;
/** Strong RNG */
public final RandomSource random;
- final UdpSocketManager usm;
- final FNPPacketMangler packetMangler;
- final DNSRequester dnsr;
+ /** Weak but fast RNG */
+ public fin