beginning of format, updated imports. (shendaras)

This commit is contained in:
shendaras
2004-04-10 11:39:00 +00:00
committed by zzz
parent dbe5dea525
commit 8a8e68146f
233 changed files with 10086 additions and 8697 deletions

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -26,10 +27,10 @@ import java.util.Properties;
import net.i2p.I2PException;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.LogManager;
import net.i2p.util.Clock;
/**
* ATalk - anonymous talk, demonstrating a trivial I2P usage scenario.
@@ -59,27 +60,39 @@ public class ATalk implements I2PSessionListener, Runnable {
/** string that messages must begin with to be treated as files */
private final static String FILE_COMMAND = ".file: ";
/** the, erm, manual */
private final static String MANUAL =
"ATalk: Anonymous Talk, a demo program for the Invisible Internet Project SDK" + NL +
"To generate a new destination:" + NL +
"\tATalk [fileToSavePrivateKeyIn] [fileToSavePublicKeyIn]" + NL +
"To talk to another destination:" + NL +
"\tATalk [myPrivateKeyFile] [peerPublicKey] [shouldLogToScreen]" + NL +
"shouldLogToScreen is 'true' or 'false', depending on whether you want log info on the screen" + NL +
"When talking to another destination, messages are sent after you hit return" + NL +
"To send a file, send a message saying:" + NL +
"\t" + FILE_COMMAND + "[filenameToSend]" + NL +
"The peer will then recieve the file and be notified of where it has been saved" + NL +
"To end the talk session, enter a period on a line by itself and hit return" + NL;
private final static String MANUAL = "ATalk: Anonymous Talk, a demo program for the Invisible Internet Project SDK"
+ NL
+ "To generate a new destination:"
+ NL
+ "\tATalk [fileToSavePrivateKeyIn] [fileToSavePublicKeyIn]"
+ NL
+ "To talk to another destination:"
+ NL
+ "\tATalk [myPrivateKeyFile] [peerPublicKey] [shouldLogToScreen]"
+ NL
+ "shouldLogToScreen is 'true' or 'false', depending on whether you want log info on the screen"
+ NL
+ "When talking to another destination, messages are sent after you hit return"
+ NL
+ "To send a file, send a message saying:"
+ NL
+ "\t"
+ FILE_COMMAND
+ "[filenameToSend]"
+ NL
+ "The peer will then recieve the file and be notified of where it has been saved"
+ NL
+ "To end the talk session, enter a period on a line by itself and hit return"
+ NL;
public final static String PROP_CONFIG_LOCATION = "configFile";
private static final SimpleDateFormat _fmt = new SimpleDateFormat("hh:mm:ss.SSS");
/** Construct the talk engine, but don't connect yet */
public ATalk(String myKeyFile, String theirDestFile) {
_myKeyFile = myKeyFile;
_theirDestinationFile = theirDestFile;
_myKeyFile = myKeyFile;
_theirDestinationFile = theirDestFile;
}
/** Actually start up the connection to the I2P network.
* Successful connect does not mean the peer is online or reachable.
*
@@ -88,102 +101,102 @@ public class ATalk implements I2PSessionListener, Runnable {
* @throws I2PSessionException if there is a problem contacting the I2P router
*/
public void connect() throws IOException, I2PSessionException, DataFormatException {
I2PClient client = I2PClientFactory.createClient();
File myFile = new File(_myKeyFile);
Properties props = new Properties();
String configLocation = System.getProperty(PROP_CONFIG_LOCATION, "atalk.config");
try {
props.load(new FileInputStream(configLocation));
} catch (FileNotFoundException fnfe) {
_log.warn("Unable to load up the ATalk config file " + configLocation);
}
// Provide any router or client API configuration here.
if (!props.containsKey(I2PClient.PROP_TCP_HOST))
props.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
if (!props.containsKey(I2PClient.PROP_TCP_PORT))
props.setProperty(I2PClient.PROP_TCP_PORT, "7654");
if (!props.containsKey(I2PClient.PROP_RELIABILITY))
props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
_session = client.createSession(new FileInputStream(myFile), props);
_session.setSessionListener(this);
_session.connect();
File peerDestFile = new File(_theirDestinationFile);
_peerDestination = new Destination();
_peerDestination.readBytes(new FileInputStream(peerDestFile));
return;
I2PClient client = I2PClientFactory.createClient();
File myFile = new File(_myKeyFile);
Properties props = new Properties();
String configLocation = System.getProperty(PROP_CONFIG_LOCATION, "atalk.config");
try {
props.load(new FileInputStream(configLocation));
} catch (FileNotFoundException fnfe) {
_log.warn("Unable to load up the ATalk config file " + configLocation);
}
// Provide any router or client API configuration here.
if (!props.containsKey(I2PClient.PROP_TCP_HOST)) props.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
if (!props.containsKey(I2PClient.PROP_TCP_PORT)) props.setProperty(I2PClient.PROP_TCP_PORT, "7654");
if (!props.containsKey(I2PClient.PROP_RELIABILITY))
props.setProperty(I2PClient.PROP_RELIABILITY,
I2PClient.PROP_RELIABILITY_BEST_EFFORT);
_session = client.createSession(new FileInputStream(myFile), props);
_session.setSessionListener(this);
_session.connect();
File peerDestFile = new File(_theirDestinationFile);
_peerDestination = new Destination();
_peerDestination.readBytes(new FileInputStream(peerDestFile));
return;
}
/** Actual bulk processing of the application, reading in user input,
* sending messages, and displaying results. When this function exits, the
* application is complete.
*
*/
public void run() {
try {
connect();
_in = new BufferedReader(new InputStreamReader(System.in));
_out = new BufferedWriter(new OutputStreamWriter(System.out));
_out.write("Starting up anonymous talk session"+NL);
while (true) {
String line = _in.readLine();
if ( (line == null) || (line.trim().length() <= 0) )
continue;
if (".".equals(line)) {
boolean ok = _session.sendMessage(_peerDestination, ("Peer disconnected at " + now()).getBytes());
// ignore ok, we're closing
break;
}
if (line.startsWith(FILE_COMMAND) && (line.trim().length() > FILE_COMMAND.length())) {
try {
String file = line.substring(FILE_COMMAND.length());
boolean sent = sendFile(file);
if (!sent) {
_out.write("Failed sending the file: " + file+NL);
}
} catch (IOException ioe) {
_out.write("Error sending the file: " + ioe.getMessage()+NL);
_log.error("Error sending the file", ioe);
}
} else {
boolean ok = _session.sendMessage(_peerDestination, ("[" + now() + "] " + line).getBytes());
if (!ok) {
_out.write("Failed sending message. Peer disconnected?" + NL);
}
}
}
} catch (IOException ioe) {
_log.error("Error running", ioe);
} catch (I2PSessionException ise) {
_log.error("Error communicating", ise);
} catch (DataFormatException dfe) {
_log.error("Peer destination file is not valid", dfe);
} finally {
try {
_log.debug("Exiting anonymous talk session");
if (_out != null)
_out.write("Exiting anonymous talk session");
} catch (IOException ioe) {
// ignored
}
if (_session != null) {
try {
_session.destroySession();
} catch (I2PSessionException ise) {
// ignored
}
}
try { Thread.sleep(5000); } catch (InterruptedException ie) {}
}
try {
connect();
_in = new BufferedReader(new InputStreamReader(System.in));
_out = new BufferedWriter(new OutputStreamWriter(System.out));
_out.write("Starting up anonymous talk session" + NL);
while (true) {
String line = _in.readLine();
if ((line == null) || (line.trim().length() <= 0)) continue;
if (".".equals(line)) {
boolean ok = _session.sendMessage(_peerDestination, ("Peer disconnected at " + now()).getBytes());
// ignore ok, we're closing
break;
}
if (line.startsWith(FILE_COMMAND) && (line.trim().length() > FILE_COMMAND.length())) {
try {
String file = line.substring(FILE_COMMAND.length());
boolean sent = sendFile(file);
if (!sent) {
_out.write("Failed sending the file: " + file + NL);
}
} catch (IOException ioe) {
_out.write("Error sending the file: " + ioe.getMessage() + NL);
_log.error("Error sending the file", ioe);
}
} else {
boolean ok = _session.sendMessage(_peerDestination, ("[" + now() + "] " + line).getBytes());
if (!ok) {
_out.write("Failed sending message. Peer disconnected?" + NL);
}
}
}
} catch (IOException ioe) {
_log.error("Error running", ioe);
} catch (I2PSessionException ise) {
_log.error("Error communicating", ise);
} catch (DataFormatException dfe) {
_log.error("Peer destination file is not valid", dfe);
} finally {
try {
_log.debug("Exiting anonymous talk session");
if (_out != null) _out.write("Exiting anonymous talk session");
} catch (IOException ioe) {
// ignored
}
if (_session != null) {
try {
_session.destroySession();
} catch (I2PSessionException ise) {
// ignored
}
}
try {
Thread.sleep(5000);
} catch (InterruptedException ie) {
}
}
}
private String now() {
Date now = new Date(Clock.getInstance().now());
return _fmt.format(now);
Date now = new Date(Clock.getInstance().now());
return _fmt.format(now);
}
/** Send the given file to the current peer. This works by sending a message
* saying ".file: filename\nbodyOfFile", where filename is the name of the file
* (which the recipient will be shown), and the bodyOfFile is the set of raw
@@ -193,35 +206,34 @@ public class ATalk implements I2PSessionListener, Runnable {
* @return false if the file could not be sent to the peer
*/
private boolean sendFile(String filename) throws IOException, I2PSessionException {
_log.debug("Sending file [" + filename + "]");
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
baos.write((FILE_COMMAND + filename+"\n").getBytes());
FileInputStream fin = new FileInputStream(filename);
byte buf[] = new byte[4096];
try {
while (true) {
int len = fin.read(buf);
if (len == -1)
break;
baos.write(buf, 0, len);
}
} catch (IOException ioe) {
_log.debug("Failed reading the file", ioe);
return false;
}
baos.close();
byte val[] = baos.toByteArray();
_log.debug("Sending " + filename + " with a full payload of " + val.length);
try {
boolean rv = _session.sendMessage(_peerDestination, val);
_log.debug("Sending " + filename + " complete: rv = " + rv);
return rv;
} catch (Throwable t) {
_log.error("Error sending file", t);
return false;
}
_log.debug("Sending file [" + filename + "]");
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
baos.write((FILE_COMMAND + filename + "\n").getBytes());
FileInputStream fin = new FileInputStream(filename);
byte buf[] = new byte[4096];
try {
while (true) {
int len = fin.read(buf);
if (len == -1) break;
baos.write(buf, 0, len);
}
} catch (IOException ioe) {
_log.debug("Failed reading the file", ioe);
return false;
}
baos.close();
byte val[] = baos.toByteArray();
_log.debug("Sending " + filename + " with a full payload of " + val.length);
try {
boolean rv = _session.sendMessage(_peerDestination, val);
_log.debug("Sending " + filename + " complete: rv = " + rv);
return rv;
} catch (Throwable t) {
_log.error("Error sending file", t);
return false;
}
}
/** I2PSessionListener.messageAvailable requires this method to be called whenever
* I2P wants to tell the session that a message is available. ATalk always grabs
* the message immediately and either processes it as a "send file" command (passing
@@ -230,27 +242,27 @@ public class ATalk implements I2PSessionListener, Runnable {
*
*/
public void messageAvailable(I2PSession session, int msgId, long size) {
_log.debug("Message available: id = " + msgId + " size = " + size);
try {
byte msg[] = session.receiveMessage(msgId);
// inefficient way to just read the first line of text, but its easy
BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(msg)));
String line = reader.readLine();
if (line.startsWith(FILE_COMMAND)) {
handleRecieveFile(line, msg);
} else {
// not a file command, so just plop 'er out on the screen
_out.write(now() + " --> " + new String(msg));
_out.write(NL);
_out.flush();
}
} catch (I2PSessionException ise) {
_log.error("Error fetching available message", ise);
} catch (IOException ioe) {
_log.error("Error writing out the message", ioe);
}
_log.debug("Message available: id = " + msgId + " size = " + size);
try {
byte msg[] = session.receiveMessage(msgId);
// inefficient way to just read the first line of text, but its easy
BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(msg)));
String line = reader.readLine();
if (line.startsWith(FILE_COMMAND)) {
handleRecieveFile(line, msg);
} else {
// not a file command, so just plop 'er out on the screen
_out.write(now() + " --> " + new String(msg));
_out.write(NL);
_out.flush();
}
} catch (I2PSessionException ise) {
_log.error("Error fetching available message", ise);
} catch (IOException ioe) {
_log.error("Error writing out the message", ioe);
}
}
/** React to a file being sent our way from the peer via {@link #sendFile sendFile}
* by saving the file to a temporary location and displaying where, what, and how large
* it is.
@@ -261,48 +273,56 @@ public class ATalk implements I2PSessionListener, Runnable {
* @param msg the entire message recieved, including the firstline
*/
private void handleRecieveFile(String firstline, byte msg[]) throws IOException {
_log.debug("handleRecieveFile called");
File f = File.createTempFile("recieve", ".dat", new File("."));
FileOutputStream fos = new FileOutputStream(f);
int lineLen = firstline.getBytes().length+"\n".getBytes().length;
int lenToCopy = msg.length - lineLen;
byte buf[] = new byte[lenToCopy];
System.arraycopy(msg, lineLen, buf, 0, lenToCopy);
fos.write(buf);
fos.close();
String name = firstline.substring(FILE_COMMAND.length());
_out.write("Recieved a file called [" + name + "] of size [" + lenToCopy + "] bytes, saved as [" + f.getAbsolutePath() + "]" + NL);
_out.flush();
_log.debug("handleRecieveFile called");
File f = File.createTempFile("recieve", ".dat", new File("."));
FileOutputStream fos = new FileOutputStream(f);
int lineLen = firstline.getBytes().length + "\n".getBytes().length;
int lenToCopy = msg.length - lineLen;
byte buf[] = new byte[lenToCopy];
System.arraycopy(msg, lineLen, buf, 0, lenToCopy);
fos.write(buf);
fos.close();
String name = firstline.substring(FILE_COMMAND.length());
_out.write("Recieved a file called [" + name + "] of size [" + lenToCopy + "] bytes, saved as ["
+ f.getAbsolutePath() + "]" + NL);
_out.flush();
}
/** driver */
public static void main(String args[]) {
if (args.length == 2) {
String myKeyFile = args[0];
String myDestinationFile = args[1];
boolean success = generateKeys(myKeyFile, myDestinationFile);
if (success)
_log.debug("Keys generated (private key file: " + myKeyFile + " destination file: " + myDestinationFile + ")");
else
_log.debug("Keys generation failed");
try { Thread.sleep(5000); } catch (InterruptedException ie) {}
} else if (args.length == 3) {
_log.debug("Starting chat");
String myKeyfile = args[0];
String peerDestFile = args[1];
String shouldLog = args[2];
if (Boolean.TRUE.toString().equalsIgnoreCase(shouldLog))
LogManager.getInstance().setDisplayOnScreen(true);
else
LogManager.getInstance().setDisplayOnScreen(false);
String logFile = args[2];
Thread talkThread = new I2PThread(new ATalk(myKeyfile, peerDestFile));
talkThread.start();
} else {
System.out.println(MANUAL);
try { Thread.sleep(5000); } catch (InterruptedException ie) {}
System.exit(-1);
}
if (args.length == 2) {
String myKeyFile = args[0];
String myDestinationFile = args[1];
boolean success = generateKeys(myKeyFile, myDestinationFile);
if (success)
_log.debug("Keys generated (private key file: " + myKeyFile + " destination file: " + myDestinationFile
+ ")");
else
_log.debug("Keys generation failed");
try {
Thread.sleep(5000);
} catch (InterruptedException ie) {
}
} else if (args.length == 3) {
_log.debug("Starting chat");
String myKeyfile = args[0];
String peerDestFile = args[1];
String shouldLog = args[2];
if (Boolean.TRUE.toString().equalsIgnoreCase(shouldLog))
LogManager.getInstance().setDisplayOnScreen(true);
else
LogManager.getInstance().setDisplayOnScreen(false);
String logFile = args[2];
Thread talkThread = new I2PThread(new ATalk(myKeyfile, peerDestFile));
talkThread.start();
} else {
System.out.println(MANUAL);
try {
Thread.sleep(5000);
} catch (InterruptedException ie) {
}
System.exit(-1);
}
}
/** Generate a new Destination, saving that destination and the associated
@@ -314,25 +334,33 @@ public class ATalk implements I2PSessionListener, Runnable {
* @param destinationFile file in which the Destination is serialized in
*/
private static boolean generateKeys(String privKeyFile, String destinationFile) {
try {
Destination d = I2PClientFactory.createClient().createDestination(new FileOutputStream(privKeyFile));
FileOutputStream fos = new FileOutputStream(destinationFile);
d.writeBytes(fos);
fos.flush();
fos.close();
return true;
} catch (IOException ioe) {
_log.error("Error generating keys", ioe);
} catch (I2PException ipe) {
_log.error("Error generating keys", ipe);
}
return false;
try {
Destination d = I2PClientFactory.createClient().createDestination(new FileOutputStream(privKeyFile));
FileOutputStream fos = new FileOutputStream(destinationFile);
d.writeBytes(fos);
fos.flush();
fos.close();
return true;
} catch (IOException ioe) {
_log.error("Error generating keys", ioe);
} catch (I2PException ipe) {
_log.error("Error generating keys", ipe);
}
return false;
}
/** required by {@link I2PSessionListener I2PSessionListener} to notify of disconnect */
public void disconnected(I2PSession session) { _log.debug("Disconnected"); }
public void disconnected(I2PSession session) {
_log.debug("Disconnected");
}
/** required by {@link I2PSessionListener I2PSessionListener} to notify of error */
public void errorOccurred(I2PSession session, String message, Throwable error) { _log.debug("Error occurred: " + message, error); }
public void errorOccurred(I2PSession session, String message, Throwable error) {
_log.debug("Error occurred: " + message, error);
}
/** required by {@link I2PSessionListener I2PSessionListener} to notify of abuse */
public void reportAbuse(I2PSession session, int severity) { _log.debug("Abuse reported of severity " + severity); }
public void reportAbuse(I2PSession session, int severity) {
_log.debug("Abuse reported of severity " + severity);
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -8,47 +9,42 @@ package net.i2p.client;
*
*/
import net.i2p.util.Log;
import net.i2p.util.Clock;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.I2CPMessageReader;
import net.i2p.data.i2cp.I2CPMessageException;
import net.i2p.data.i2cp.SessionStatusMessage;
import net.i2p.data.i2cp.SendMessageMessage;
import net.i2p.data.i2cp.CreateSessionMessage;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.i2cp.DisconnectMessage;
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
import net.i2p.data.i2cp.ReceiveMessageEndMessage;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.RequestLeaseSetMessage;
import net.i2p.data.i2cp.SessionId;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.SessionConfig;
import net.i2p.data.Destination;
import net.i2p.data.Payload;
import net.i2p.data.TunnelId;
import net.i2p.data.RouterIdentity;
import net.i2p.data.PublicKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.data.Certificate;
import net.i2p.crypto.KeyGenerator;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.Date;
import java.net.Socket;
import java.io.OutputStream;
import java.io.IOException;
import net.i2p.crypto.KeyGenerator;
import net.i2p.data.Certificate;
import net.i2p.data.Destination;
import net.i2p.data.Payload;
import net.i2p.data.PublicKey;
import net.i2p.data.RouterIdentity;
import net.i2p.data.SigningPublicKey;
import net.i2p.data.TunnelId;
import net.i2p.data.i2cp.CreateSessionMessage;
import net.i2p.data.i2cp.DisconnectMessage;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.I2CPMessageException;
import net.i2p.data.i2cp.I2CPMessageReader;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
import net.i2p.data.i2cp.ReceiveMessageEndMessage;
import net.i2p.data.i2cp.RequestLeaseSetMessage;
import net.i2p.data.i2cp.SendMessageMessage;
import net.i2p.data.i2cp.SessionConfig;
import net.i2p.data.i2cp.SessionId;
import net.i2p.data.i2cp.SessionStatusMessage;
import net.i2p.util.Clock;
import net.i2p.util.Log;
/**
* Run the server side of a connection as part of the TestServer. This class
@@ -83,31 +79,49 @@ class ConnectionRunner implements I2CPMessageReader.I2CPMessageEventListener {
/** next available message id */
private static int _messageId = 0;
private SessionConfig _config;
private Object _sessionIdLock = new Object();
private Object _messageIdLock = new Object();
// this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME
protected int getNextSessionId() { synchronized (_sessionIdLock) { int id = (++_id)%32767; _id = id; return id; } }
protected int getNextSessionId() {
synchronized (_sessionIdLock) {
int id = (++_id) % 32767;
_id = id;
return id;
}
}
// this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME
protected int getNextMessageId() { synchronized (_messageIdLock) { int id = (++_messageId)%32767; _messageId = id; return id; } }
protected SessionId getSessionId() { return _sessionId; }
protected int getNextMessageId() {
synchronized (_messageIdLock) {
int id = (++_messageId) % 32767;
_messageId = id;
return id;
}
}
protected SessionId getSessionId() {
return _sessionId;
}
protected ConnectionRunner getRunner(Destination dest) {
return (ConnectionRunner)_connections.get(dest);
return (ConnectionRunner) _connections.get(dest);
}
protected Set getRunnerDestinations() {
return new HashSet(_connections.keySet());
return new HashSet(_connections.keySet());
}
/**
* Create a new runner against the given socket
*
*/
public ConnectionRunner(Socket socket) {
_socket = socket;
_config = null;
_socket = socket;
_config = null;
}
/**
* Actually run the connection - listen for I2CP messages and respond. This
* is the main driver for this class, though it gets all its meat from the
@@ -115,16 +129,16 @@ class ConnectionRunner implements I2CPMessageReader.I2CPMessageEventListener {
*
*/
public void doYourThing() throws IOException {
I2CPMessageReader reader = new I2CPMessageReader(_socket.getInputStream(), this);
_out = _socket.getOutputStream();
reader.startReading();
I2CPMessageReader reader = new I2CPMessageReader(_socket.getInputStream(), this);
_out = _socket.getOutputStream();
reader.startReading();
}
/**
* Recieve notifiation that the peer disconnected
*/
public void disconnected(I2CPMessageReader reader) {
_log.info("Disconnected");
_log.info("Disconnected");
}
/**
@@ -132,166 +146,170 @@ class ConnectionRunner implements I2CPMessageReader.I2CPMessageEventListener {
*
*/
public void messageReceived(I2CPMessageReader reader, I2CPMessage message) {
_log.info("Message recieved: \n" + message);
switch (message.getType()) {
case CreateSessionMessage.MESSAGE_TYPE:
handleCreateSession(reader, (CreateSessionMessage)message);
break;
case SendMessageMessage.MESSAGE_TYPE:
handleSendMessage(reader, (SendMessageMessage)message);
break;
case ReceiveMessageBeginMessage.MESSAGE_TYPE:
handleReceiveBegin(reader, (ReceiveMessageBeginMessage)message);
break;
case ReceiveMessageEndMessage.MESSAGE_TYPE:
handleReceiveEnd(reader, (ReceiveMessageEndMessage)message);
break;
}
_log.info("Message recieved: \n" + message);
switch (message.getType()) {
case CreateSessionMessage.MESSAGE_TYPE:
handleCreateSession(reader, (CreateSessionMessage) message);
break;
case SendMessageMessage.MESSAGE_TYPE:
handleSendMessage(reader, (SendMessageMessage) message);
break;
case ReceiveMessageBeginMessage.MESSAGE_TYPE:
handleReceiveBegin(reader, (ReceiveMessageBeginMessage) message);
break;
case ReceiveMessageEndMessage.MESSAGE_TYPE:
handleReceiveEnd(reader, (ReceiveMessageEndMessage) message);
break;
}
}
/**
* Handle a CreateSessionMessage
*
*/
protected void handleCreateSession(I2CPMessageReader reader, CreateSessionMessage message) {
if (message.getSessionConfig().verifySignature()) {
_log.debug("Signature verified correctly on create session message");
} else {
_log.error("Signature verification *FAILED* on a create session message. Hijack attempt?");
DisconnectMessage msg = new DisconnectMessage();
msg.setReason("Invalid signature on CreateSessionMessage");
try {
doSend(msg);
} catch (I2CPMessageException ime) {
_log.error("Error writing out the disconnect message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the disconnect message", ioe);
}
return;
}
SessionStatusMessage msg = new SessionStatusMessage();
SessionId id = new SessionId();
id.setSessionId(getNextSessionId()); // should be mod 65535, but UnsignedInteger isn't fixed yet. FIXME.
_sessionId = id;
msg.setSessionId(id);
msg.setStatus(SessionStatusMessage.STATUS_CREATED);
try {
doSend(msg);
_connections.put(message.getSessionConfig().getDestination(), this);
_config = message.getSessionConfig();
sessionCreated();
} catch (I2CPMessageException ime) {
_log.error("Error writing out the session status message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the session status message", ioe);
}
// lets also request a new fake lease
RequestLeaseSetMessage rlsm = new RequestLeaseSetMessage();
rlsm.setEndDate(new Date(Clock.getInstance().now() + 60*60*1000));
rlsm.setSessionId(id);
RouterIdentity ri = new RouterIdentity();
Object rikeys[] = KeyGenerator.getInstance().generatePKIKeypair();
Object riSigningkeys[] = KeyGenerator.getInstance().generateSigningKeypair();
ri.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
ri.setPublicKey((PublicKey)rikeys[0]);
ri.setSigningPublicKey((SigningPublicKey)riSigningkeys[0]);
TunnelId tunnel = new TunnelId();
tunnel.setTunnelId(42);
rlsm.addEndpoint(ri, tunnel);
try {
doSend(rlsm);
} catch (I2CPMessageException ime) {
_log.error("Error writing out the request for a lease set", ime);
} catch (IOException ioe) {
_log.error("Error writing out the request for a lease set", ioe);
}
if (message.getSessionConfig().verifySignature()) {
_log.debug("Signature verified correctly on create session message");
} else {
_log.error("Signature verification *FAILED* on a create session message. Hijack attempt?");
DisconnectMessage msg = new DisconnectMessage();
msg.setReason("Invalid signature on CreateSessionMessage");
try {
doSend(msg);
} catch (I2CPMessageException ime) {
_log.error("Error writing out the disconnect message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the disconnect message", ioe);
}
return;
}
SessionStatusMessage msg = new SessionStatusMessage();
SessionId id = new SessionId();
id.setSessionId(getNextSessionId()); // should be mod 65535, but UnsignedInteger isn't fixed yet. FIXME.
_sessionId = id;
msg.setSessionId(id);
msg.setStatus(SessionStatusMessage.STATUS_CREATED);
try {
doSend(msg);
_connections.put(message.getSessionConfig().getDestination(), this);
_config = message.getSessionConfig();
sessionCreated();
} catch (I2CPMessageException ime) {
_log.error("Error writing out the session status message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the session status message", ioe);
}
// lets also request a new fake lease
RequestLeaseSetMessage rlsm = new RequestLeaseSetMessage();
rlsm.setEndDate(new Date(Clock.getInstance().now() + 60 * 60 * 1000));
rlsm.setSessionId(id);
RouterIdentity ri = new RouterIdentity();
Object rikeys[] = KeyGenerator.getInstance().generatePKIKeypair();
Object riSigningkeys[] = KeyGenerator.getInstance().generateSigningKeypair();
ri.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
ri.setPublicKey((PublicKey) rikeys[0]);
ri.setSigningPublicKey((SigningPublicKey) riSigningkeys[0]);
TunnelId tunnel = new TunnelId();
tunnel.setTunnelId(42);
rlsm.addEndpoint(ri, tunnel);
try {
doSend(rlsm);
} catch (I2CPMessageException ime) {
_log.error("Error writing out the request for a lease set", ime);
} catch (IOException ioe) {
_log.error("Error writing out the request for a lease set", ioe);
}
}
protected void sessionCreated() { }
protected SessionConfig getConfig() { return _config; }
protected void sessionCreated() {
}
protected SessionConfig getConfig() {
return _config;
}
/**
* Handle a SendMessageMessage
*
*/
protected void handleSendMessage(I2CPMessageReader reader, SendMessageMessage message) {
_log.debug("handleSendMessage called");
Payload payload = message.getPayload();
Destination dest = message.getDestination();
MessageId id = new MessageId();
id.setMessageId(getNextMessageId());
_log.debug("** Recieving message [" + id.getMessageId() + "] with payload: " + "[" + payload + "]");
_messages.put(id, payload);
MessageStatusMessage status = new MessageStatusMessage();
status.setMessageId(id);
status.setSessionId(message.getSessionId());
status.setSize(0L);
status.setNonce(message.getNonce());
status.setStatus(MessageStatusMessage.STATUS_SEND_ACCEPTED);
try {
doSend(status);
} catch (I2CPMessageException ime) {
_log.error("Error writing out the message status message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the message status message", ioe);
}
distributeMessageToPeer(status, dest, id);
_log.debug("handleSendMessage called");
Payload payload = message.getPayload();
Destination dest = message.getDestination();
MessageId id = new MessageId();
id.setMessageId(getNextMessageId());
_log.debug("** Recieving message [" + id.getMessageId() + "] with payload: " + "[" + payload + "]");
_messages.put(id, payload);
MessageStatusMessage status = new MessageStatusMessage();
status.setMessageId(id);
status.setSessionId(message.getSessionId());
status.setSize(0L);
status.setNonce(message.getNonce());
status.setStatus(MessageStatusMessage.STATUS_SEND_ACCEPTED);
try {
doSend(status);
} catch (I2CPMessageException ime) {
_log.error("Error writing out the message status message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the message status message", ioe);
}
distributeMessageToPeer(status, dest, id);
}
/**
* distribute the message to the destination, passing on the appropriate status
* messages to the sender of the SendMessageMessage
*
*/
private void distributeMessageToPeer(MessageStatusMessage status, Destination dest, MessageId id) {
ConnectionRunner runner = (ConnectionRunner)_connections.get(dest);
if (runner == null) {
distributeNonLocal(status, dest, id);
} else {
distributeLocal(runner, status, dest, id);
}
_log.debug("Done handling send message");
ConnectionRunner runner = (ConnectionRunner) _connections.get(dest);
if (runner == null) {
distributeNonLocal(status, dest, id);
} else {
distributeLocal(runner, status, dest, id);
}
_log.debug("Done handling send message");
}
protected void distributeLocal(ConnectionRunner runner, MessageStatusMessage status, Destination dest, MessageId id) {
if (runner.messageAvailable(id, 0L)) {
status.setStatus(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS);
status.setNonce(2);
try {
doSend(status);
} catch (I2CPMessageException ime) {
_log.error("Error writing out the success status message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the success status message", ioe);
}
_log.debug("Guaranteed success with the status message sent");
} else {
status.setStatus(MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE);
try {
doSend(status);
} catch (I2CPMessageException ime) {
_log.error("Error writing out the failure status message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the failure status message", ioe);
}
_log.debug("Guaranteed failure since messageAvailable failed");
}
if (runner.messageAvailable(id, 0L)) {
status.setStatus(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS);
status.setNonce(2);
try {
doSend(status);
} catch (I2CPMessageException ime) {
_log.error("Error writing out the success status message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the success status message", ioe);
}
_log.debug("Guaranteed success with the status message sent");
} else {
status.setStatus(MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE);
try {
doSend(status);
} catch (I2CPMessageException ime) {
_log.error("Error writing out the failure status message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the failure status message", ioe);
}
_log.debug("Guaranteed failure since messageAvailable failed");
}
}
protected void distributeNonLocal(MessageStatusMessage status, Destination dest, MessageId id) {
status.setStatus(MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE);
try {
doSend(status);
} catch (I2CPMessageException ime) {
_log.error("Error writing out the failure status message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the failure status message", ioe);
}
_log.debug("Guaranteed failure!");
status.setStatus(MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE);
try {
doSend(status);
} catch (I2CPMessageException ime) {
_log.error("Error writing out the failure status message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the failure status message", ioe);
}
_log.debug("Guaranteed failure!");
}
/**
* The client asked for a message, so we send it to them. This currently
* does not do any security checking (like making sure they're the one to
@@ -300,30 +318,31 @@ class ConnectionRunner implements I2CPMessageReader.I2CPMessageEventListener {
*
*/
public void handleReceiveBegin(I2CPMessageReader reader, ReceiveMessageBeginMessage message) {
_log.debug("Handling recieve begin: id = " + message.getMessageId());
MessagePayloadMessage msg = new MessagePayloadMessage();
msg.setMessageId(message.getMessageId());
msg.setSessionId(_sessionId);
Payload payload = (Payload)_messages.get(message.getMessageId());
if (payload == null) {
_log.error("Payload for message id [" + message.getMessageId() + "] is null! Unknown message id?", new Exception("Error, null payload"));
StringBuffer buf = new StringBuffer();
for (Iterator iter = _messages.keySet().iterator(); iter.hasNext(); ) {
buf.append("messageId: ").append(iter.next()).append(", ");
}
_log.error("Known message IDs: " + buf.toString());
return;
}
msg.setPayload(payload);
try {
doSend(msg);
} catch (IOException ioe) {
_log.error("Error delivering the payload", ioe);
} catch (I2CPMessageException ime) {
_log.error("Error delivering the payload", ime);
}
_log.debug("Handling recieve begin: id = " + message.getMessageId());
MessagePayloadMessage msg = new MessagePayloadMessage();
msg.setMessageId(message.getMessageId());
msg.setSessionId(_sessionId);
Payload payload = (Payload) _messages.get(message.getMessageId());
if (payload == null) {
_log.error("Payload for message id [" + message.getMessageId() + "] is null! Unknown message id?",
new Exception("Error, null payload"));
StringBuffer buf = new StringBuffer();
for (Iterator iter = _messages.keySet().iterator(); iter.hasNext();) {
buf.append("messageId: ").append(iter.next()).append(", ");
}
_log.error("Known message IDs: " + buf.toString());
return;
}
msg.setPayload(payload);
try {
doSend(msg);
} catch (IOException ioe) {
_log.error("Error delivering the payload", ioe);
} catch (I2CPMessageException ime) {
_log.error("Error delivering the payload", ime);
}
}
/**
* The client told us that the message has been recieved completely. This currently
* does not do any security checking prior to removing the message from the
@@ -331,45 +350,46 @@ class ConnectionRunner implements I2CPMessageReader.I2CPMessageEventListener {
*
*/
public void handleReceiveEnd(I2CPMessageReader reader, ReceiveMessageEndMessage message) {
_messages.remove(message.getMessageId());
_messages.remove(message.getMessageId());
}
/**
* Deliver notification to the client that the given message is available.
* This is called from the ConnectionRunner the message was sent from.
*
*/
public boolean messageAvailable(MessageId id, long size) {
MessageStatusMessage msg = new MessageStatusMessage();
msg.setMessageId(id);
msg.setSessionId(_sessionId);
msg.setSize(size);
msg.setNonce(1);
msg.setStatus(MessageStatusMessage.STATUS_AVAILABLE);
try {
doSend(msg);
return true;
} catch (I2CPMessageException ime) {
_log.error("Error writing out the message status message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the message status message", ioe);
}
return false;
MessageStatusMessage msg = new MessageStatusMessage();
msg.setMessageId(id);
msg.setSessionId(_sessionId);
msg.setSize(size);
msg.setNonce(1);
msg.setStatus(MessageStatusMessage.STATUS_AVAILABLE);
try {
doSend(msg);
return true;
} catch (I2CPMessageException ime) {
_log.error("Error writing out the message status message", ime);
} catch (IOException ioe) {
_log.error("Error writing out the message status message", ioe);
}
return false;
}
/**
* Handle notifiation that there was an error
*
*/
public void readError(I2CPMessageReader reader, Exception error) {
_log.info("Error occurred", error);
_log.info("Error occurred", error);
}
private Object _sendLock = new Object();
protected void doSend(I2CPMessage msg) throws I2CPMessageException, IOException {
synchronized (_sendLock) {
msg.writeMessage(_out);
_out.flush();
}
synchronized (_sendLock) {
msg.writeMessage(_out);
_out.flush();
}
}
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -8,7 +9,8 @@ package net.i2p.client;
*
*/
import net.i2p.data.i2cp.*;
import net.i2p.data.i2cp.DisconnectMessage;
import net.i2p.data.i2cp.I2CPMessage;
/**
* Handle I2CP disconnect messages from the router
@@ -19,6 +21,7 @@ class DisconnectMessageHandler extends HandlerImpl {
public DisconnectMessageHandler() {
super(DisconnectMessage.MESSAGE_TYPE);
}
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
_log.debug("Handle message " + message);
session.destroySession(false);

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -18,9 +19,13 @@ import net.i2p.util.Log;
abstract class HandlerImpl implements I2CPMessageHandler {
protected Log _log;
private int _type;
public HandlerImpl(int type) {
_type = type;
_log = new Log(getClass());
}
public int getType() { return _type; }
}
public int getType() {
return _type;
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -17,5 +18,6 @@ import net.i2p.data.i2cp.I2CPMessage;
*/
interface I2CPMessageHandler {
public int getType();
public void handleMessage(I2CPMessage message, I2PSessionImpl session);
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -40,7 +41,7 @@ import net.i2p.util.RandomSource;
class I2CPMessageProducer {
private final static Log _log = new Log(I2CPMessageProducer.class);
private final static RandomSource _rand = RandomSource.getInstance();
/**
* Send all the messages that a client needs to send to a router to establish
* a new session.
@@ -58,68 +59,69 @@ class I2CPMessageProducer {
msg.setSessionConfig(cfg);
session.sendMessage(msg);
}
/**
* Send messages to the router destroying the session and disconnecting
*
*/
public void disconnect(I2PSessionImpl session) throws I2PSessionException {
DestroySessionMessage dmsg = new DestroySessionMessage();
dmsg.setSessionId(session.getSessionId());
session.sendMessage(dmsg);
// use DisconnectMessage only if we fail and drop connection...
// todo: update the code to fire off DisconnectMessage on socket error
DestroySessionMessage dmsg = new DestroySessionMessage();
dmsg.setSessionId(session.getSessionId());
session.sendMessage(dmsg);
// use DisconnectMessage only if we fail and drop connection...
// todo: update the code to fire off DisconnectMessage on socket error
//DisconnectMessage msg = new DisconnectMessage();
//msg.setReason("Destroy called");
//session.sendMessage(msg);
}
/**
* Package up and send the payload to the router for delivery
*
*/
public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag, SessionKey key, Set tags, SessionKey newKey) throws I2PSessionException {
public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag,
SessionKey key, Set tags, SessionKey newKey) throws I2PSessionException {
SendMessageMessage msg = new SendMessageMessage();
msg.setDestination(dest);
msg.setSessionId(session.getSessionId());
msg.setNonce(nonce);
msg.setNonce(nonce);
Payload data = createPayload(dest, payload, tag, key, tags, newKey);
msg.setPayload(data);
session.sendMessage(msg);
}
/**
* Create a new signed payload and send it off to the destination
*
*/
private Payload createPayload(Destination dest, byte[] payload, SessionTag tag, SessionKey key, Set tags, SessionKey newKey) throws I2PSessionException {
if (dest == null)
throw new I2PSessionException("No destination specified");
if (payload == null)
throw new I2PSessionException("No payload specified");
private Payload createPayload(Destination dest, byte[] payload, SessionTag tag, SessionKey key, Set tags,
SessionKey newKey) throws I2PSessionException {
if (dest == null) throw new I2PSessionException("No destination specified");
if (payload == null) throw new I2PSessionException("No payload specified");
Payload data = new Payload();
// randomize padding
int size = payload.length + RandomSource.getInstance().nextInt(1024);
byte encr[] = ElGamalAESEngine.encrypt(payload, dest.getPublicKey(), key, tags, tag, newKey, size);
// yes, in an intelligent component, newTags would be queued for confirmation along with key, and
// generateNewTags would only generate tags if necessary
data.setEncryptedData(encr);
_log.debug("Encrypting the payload to public key " + dest.getPublicKey().toBase64() + "\nPayload: " + data.calculateHash());
return data;
Payload data = new Payload();
// randomize padding
int size = payload.length + RandomSource.getInstance().nextInt(1024);
byte encr[] = ElGamalAESEngine.encrypt(payload, dest.getPublicKey(), key, tags, tag, newKey, size);
// yes, in an intelligent component, newTags would be queued for confirmation along with key, and
// generateNewTags would only generate tags if necessary
data.setEncryptedData(encr);
_log.debug("Encrypting the payload to public key " + dest.getPublicKey().toBase64() + "\nPayload: "
+ data.calculateHash());
return data;
}
private static Set generateNewTags() {
Set tags = new HashSet();
for (int i = 0; i < 10; i++) {
byte tag[] = new byte[SessionTag.BYTE_LENGTH];
RandomSource.getInstance().nextBytes(tag);
tags.add(new SessionTag(tag));
}
return tags;
Set tags = new HashSet();
for (int i = 0; i < 10; i++) {
byte tag[] = new byte[SessionTag.BYTE_LENGTH];
RandomSource.getInstance().nextBytes(tag);
tags.add(new SessionTag(tag));
}
return tags;
}
/**
* Send an abuse message to the router
*/
@@ -136,18 +138,19 @@ class I2CPMessageProducer {
msg.setSeverity(sv);
session.sendMessage(msg);
}
/**
* Create a new signed leaseSet in response to a request to do so and send it
* to the router
*
*/
public void createLeaseSet(I2PSessionImpl session, LeaseSet leaseSet, SigningPrivateKey signingPriv, PrivateKey priv) throws I2PSessionException {
public void createLeaseSet(I2PSessionImpl session, LeaseSet leaseSet, SigningPrivateKey signingPriv, PrivateKey priv)
throws I2PSessionException {
CreateLeaseSetMessage msg = new CreateLeaseSetMessage();
msg.setLeaseSet(leaseSet);
msg.setPrivateKey(priv);
msg.setSigningPrivateKey(signingPriv);
msg.setSessionId(session.getSessionId());
msg.setLeaseSet(leaseSet);
msg.setPrivateKey(priv);
msg.setSigningPrivateKey(signingPriv);
msg.setSessionId(session.getSessionId());
session.sendMessage(msg);
}
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -8,16 +9,15 @@ package net.i2p.client;
*
*/
import net.i2p.I2PException;
import net.i2p.data.Destination;
import net.i2p.data.Certificate;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.util.Properties;
import net.i2p.I2PException;
import net.i2p.data.Certificate;
import net.i2p.data.Destination;
/**
* Define the standard means of interacting with the I2P system
*
@@ -34,10 +34,10 @@ public interface I2PClient {
public final static String PROP_RELIABILITY_BEST_EFFORT = "BestEffort";
/** Reliability value: guaranteed */
public final static String PROP_RELIABILITY_GUARANTEED = "Guaranteed";
/** protocol flag that must be sent when opening the i2cp connection to the router */
public final static int PROTOCOL_BYTE = 0x2A;
/** Create a new client session for the Destination stored at the destKeyStream
* using the specified options to both connect to the router, to instruct
* the router how to handle the new session, and to configure the end to end
@@ -46,16 +46,16 @@ public interface I2PClient {
* @param options set of options to configure the router with
* @return new session allowing a Destination to recieve all of its messages and send messages to any other Destination.
*/
public I2PSession createSession(InputStream destKeyStream, Properties options) throws I2PSessionException;
public I2PSession createSession(InputStream destKeyStream, Properties options) throws I2PSessionException;
/** Create a new destination with the default certificate creation properties and store
* it, along with the private encryption and signing keys at the specified location
* @param destKeyStream create a new destination and write out the object to the given stream,
* formatted as Destination, PrivateKey, and SigningPrivateKey
* @return new destination
*/
public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException;
public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException;
/** Create a new destination with the given certificate and store it, along with the private
* encryption and signing keys at the specified location
*

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -19,5 +20,5 @@ public class I2PClientFactory {
*/
public static I2PClient createClient() {
return new I2PClientImpl();
}
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -37,7 +38,7 @@ class I2PClientImpl implements I2PClient {
cert.setPayload(null);
return createDestination(destKeyStream, cert);
}
/**
* Create the destination with the given payload and write it out along with
* the PrivateKey and SigningPrivateKey to the destKeyStream
@@ -48,28 +49,28 @@ class I2PClientImpl implements I2PClient {
d.setCertificate(cert);
PublicKey publicKey = new PublicKey();
Object keypair[] = KeyGenerator.getInstance().generatePKIKeypair();
publicKey = (PublicKey)keypair[0];
PrivateKey privateKey = (PrivateKey)keypair[1];
Object signingKeys[] = KeyGenerator.getInstance().generateSigningKeypair();
SigningPublicKey signingPubKey = (SigningPublicKey)signingKeys[0];
SigningPrivateKey signingPrivKey = (SigningPrivateKey)signingKeys[1];
publicKey = (PublicKey) keypair[0];
PrivateKey privateKey = (PrivateKey) keypair[1];
Object signingKeys[] = KeyGenerator.getInstance().generateSigningKeypair();
SigningPublicKey signingPubKey = (SigningPublicKey) signingKeys[0];
SigningPrivateKey signingPrivKey = (SigningPrivateKey) signingKeys[1];
d.setPublicKey(publicKey);
d.setSigningPublicKey(signingPubKey);
d.writeBytes(destKeyStream);
privateKey.writeBytes(destKeyStream);
signingPrivKey.writeBytes(destKeyStream);
destKeyStream.flush();
return d;
}
/**
* Create a new session (though do not connect it yet)
*
*/
public I2PSession createSession(InputStream destKeyStream, Properties options) throws I2PSessionException {
//return new I2PSessionImpl(destKeyStream, options); // not thread safe
return new I2PSessionImpl2(destKeyStream, options); // thread safe
return new I2PSessionImpl2(destKeyStream, options); // thread safe
}
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -8,11 +9,16 @@ package net.i2p.client;
*
*/
import net.i2p.data.i2cp.*;
import net.i2p.util.Log;
import java.util.Map;
import java.util.HashMap;
import java.util.Map;
import net.i2p.data.i2cp.DisconnectMessage;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.i2cp.RequestLeaseSetMessage;
import net.i2p.data.i2cp.SessionStatusMessage;
import net.i2p.data.i2cp.SetDateMessage;
import net.i2p.util.Log;
/**
* Contains a map of message handlers that a session will want to use
@@ -23,7 +29,7 @@ class I2PClientMessageHandlerMap {
private final static Log _log = new Log(I2PClientMessageHandlerMap.class);
/** map of message type id --> I2CPMessageHandler */
private static Map _handlers;
static {
_handlers = new HashMap();
_handlers.put(new Integer(DisconnectMessage.MESSAGE_TYPE), new DisconnectMessageHandler());
@@ -33,9 +39,9 @@ class I2PClientMessageHandlerMap {
_handlers.put(new Integer(MessageStatusMessage.MESSAGE_TYPE), new MessageStatusMessageHandler());
_handlers.put(new Integer(SetDateMessage.MESSAGE_TYPE), new SetDateMessageHandler());
}
public static I2CPMessageHandler getHandler(int messageTypeId) {
I2CPMessageHandler handler = (I2CPMessageHandler)_handlers.get(new Integer(messageTypeId));
I2CPMessageHandler handler = (I2CPMessageHandler) _handlers.get(new Integer(messageTypeId));
return handler;
}
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -11,8 +12,8 @@ package net.i2p.client;
import java.util.Set;
import net.i2p.data.Destination;
import net.i2p.data.SessionKey;
import net.i2p.data.PrivateKey;
import net.i2p.data.SessionKey;
import net.i2p.data.SigningPrivateKey;
/**
@@ -30,6 +31,7 @@ public interface I2PSession {
* @return whether it was accepted by the router for delivery or not
*/
public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException;
/**
* Like sendMessage above, except the key used and the tags sent are exposed to the
* application. <p />
@@ -57,15 +59,16 @@ public interface I2PSession {
* the contents of the set is ignored during the call, but afterwards it contains a set of SessionTag
* objects that were sent along side the given keyUsed.
*/
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent)
throws I2PSessionException;
/** Receive a message that the router has notified the client about, returning
* the payload.
* @param msgId message to fetch
* @return unencrypted body of the message
*/
public byte[] receiveMessage(int msgId) throws I2PSessionException;
/** Instruct the router that the message received was abusive (including how
* abusive on a 1-100 scale) in the hopes the router can do something to
* minimize receiving abusive messages like that in the future.
@@ -73,39 +76,39 @@ public interface I2PSession {
* @param severity how abusive
*/
public void reportAbuse(int msgId, int severity) throws I2PSessionException;
/** Instruct the I2PSession where it should send event notifications
* @param lsnr listener to retrieve events
*/
public void setSessionListener(I2PSessionListener lsnr);
/**
* Tear down the session and release any resources.
*
*/
public void destroySession() throws I2PSessionException;
/**
* Actually connect the session and start recieving/sending messages
*
*/
public void connect() throws I2PSessionException;
/**
* Retrieve the Destination this session serves as the endpoint for.
* Returns null if no destination is available.
*
*/
public Destination getMyDestination();
/**
* Retrieve the decryption PrivateKey associated with the Destination
*
*/
public PrivateKey getDecryptionKey();
/**
* Retrieve the signing SigningPrivateKey associated with the Destination
*/
public SigningPrivateKey getPrivateKey();
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -8,8 +9,8 @@ package net.i2p.client;
*
*/
import net.i2p.util.Log;
import net.i2p.I2PException;
import net.i2p.util.Log;
/**
* Thrown when there is a problem doing something on the session
@@ -18,10 +19,11 @@ import net.i2p.I2PException;
*/
public class I2PSessionException extends I2PException {
private final static Log _log = new Log(I2PSessionException.class);
public I2PSessionException(String msg, Throwable t) {
super(msg, t);
}
public I2PSessionException(String msg) {
super(msg);
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -59,7 +60,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
private SessionId _sessionId;
/** currently granted lease set, or null */
private LeaseSet _leaseSet;
/** hostname of router */
private String _hostname;
/** port num to router */
@@ -70,37 +71,37 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
private I2CPMessageReader _reader;
/** where we pipe our messages */
private OutputStream _out;
/** who we send events to */
private I2PSessionListener _sessionListener;
/** class that generates new messages */
protected I2CPMessageProducer _producer;
/** map of integer --> MessagePayloadMessage */
Map _availableMessages;
/** MessageStatusMessage status from the most recent send that hasn't been consumed */
private List _receivedStatus;
private int _totalReconnectAttempts;
/** monitor for waiting until a lease set has been granted */
private Object _leaseSetWait = new Object();
/** whether the session connection has already been closed (or not yet opened) */
private boolean _closed;
/** have we received the current date from the router yet? */
private boolean _dateReceived;
/** lock that we wait upon, that the SetDateMessageHandler notifies */
private Object _dateReceivedLock = new Object();
void dateUpdated() {
_dateReceived = true;
synchronized (_dateReceivedLock) {
_dateReceivedLock.notifyAll();
}
_dateReceived = true;
synchronized (_dateReceivedLock) {
_dateReceivedLock.notifyAll();
}
}
/**
* Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey
* from the destKeyStream, and using the specified options to connect to the router
@@ -108,7 +109,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* @throws I2PSessionException if there is a problem loading the private keys or
*/
public I2PSessionImpl(InputStream destKeyStream, Properties options) throws I2PSessionException {
_closed = true;
_closed = true;
_producer = new I2CPMessageProducer();
_availableMessages = new HashMap();
try {
@@ -120,11 +121,11 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
}
loadConfig(options);
_sessionId = null;
_receivedStatus = new LinkedList();
_leaseSet = null;
_totalReconnectAttempts = 0;
_receivedStatus = new LinkedList();
_leaseSet = null;
_totalReconnectAttempts = 0;
}
/**
* Parse the config for anything we know about
*
@@ -133,51 +134,61 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_options = new Properties();
_options.putAll(filter(options));
_hostname = _options.getProperty(I2PClient.PROP_TCP_HOST, "localhost");
String portNum = _options.getProperty(I2PClient.PROP_TCP_PORT, TestServer.LISTEN_PORT+"");
String portNum = _options.getProperty(I2PClient.PROP_TCP_PORT, TestServer.LISTEN_PORT + "");
try {
_portNum = Integer.parseInt(portNum);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN)) _log.warn("Invalid port number specified, defaulting to "+TestServer.LISTEN_PORT, nfe);
if (_log.shouldLog(Log.WARN))
_log.warn("Invalid port number specified, defaulting to "
+ TestServer.LISTEN_PORT, nfe);
_portNum = TestServer.LISTEN_PORT;
}
}
private static Properties filter(Properties options) {
Properties rv = new Properties();
for (Iterator iter = options.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = options.getProperty(key);
if (key.startsWith("java")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping java.* property: " + key);
} else if (key.startsWith("user")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping user.* property: " + key);
} else if (key.startsWith("os")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping os.* property: " + key);
} else if (key.startsWith("sun")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping sun.* property: " + key);
} else if (key.startsWith("file")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping file.* property: " + key);
} else if (key.startsWith("line")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping line.* property: " + key);
} else if ( (key.length() > 255) || (val.length() > 255) ) {
if (_log.shouldLog(Log.WARN)) _log.warn("Not passing on property [" + key + "] in the session configuration as the value is too long (max = 255): " + val);
} else {
rv.setProperty(key, val);
}
}
return rv;
Properties rv = new Properties();
for (Iterator iter = options.keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
String val = options.getProperty(key);
if (key.startsWith("java")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping java.* property: " + key);
} else if (key.startsWith("user")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping user.* property: " + key);
} else if (key.startsWith("os")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping os.* property: " + key);
} else if (key.startsWith("sun")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping sun.* property: " + key);
} else if (key.startsWith("file")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping file.* property: " + key);
} else if (key.startsWith("line")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping line.* property: " + key);
} else if ((key.length() > 255) || (val.length() > 255)) {
if (_log.shouldLog(Log.WARN))
_log
.warn("Not passing on property ["
+ key
+ "] in the session configuration as the value is too long (max = 255): "
+ val);
} else {
rv.setProperty(key, val);
}
}
return rv;
}
void setLeaseSet(LeaseSet ls) {
_leaseSet = ls;
if (ls != null) {
synchronized (_leaseSetWait) {
_leaseSetWait.notifyAll();
}
}
void setLeaseSet(LeaseSet ls) {
_leaseSet = ls;
if (ls != null) {
synchronized (_leaseSetWait) {
_leaseSetWait.notifyAll();
}
}
}
LeaseSet getLeaseSet() { return _leaseSet; }
LeaseSet getLeaseSet() {
return _leaseSet;
}
/**
* Load up the destKeyFile for our Destination, PrivateKey, and SigningPrivateKey
*
@@ -192,7 +203,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_privateKey.readBytes(destKeyStream);
_signingPrivateKey.readBytes(destKeyStream);
}
/**
* Connect to the router and establish a session. This call blocks until
* a session is granted.
@@ -201,70 +212,79 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* not reachable
*/
public void connect() throws I2PSessionException {
_closed = false;
long startConnect = Clock.getInstance().now();
_closed = false;
long startConnect = Clock.getInstance().now();
try {
if (_log.shouldLog(Log.DEBUG)) _log.debug("connect begin to " + _hostname + ":" + _portNum);
_socket = new Socket(_hostname, _portNum);
_out = _socket.getOutputStream();
synchronized (_out) {
_out.write(I2PClient.PROTOCOL_BYTE);
}
InputStream in = _socket.getInputStream();
_reader = new I2CPMessageReader(in, this);
if (_log.shouldLog(Log.DEBUG)) _log.debug("before startReading");
_reader.startReading();
if (_log.shouldLog(Log.DEBUG)) _log.debug("Before getDate");
sendMessage(new GetDateMessage());
if (_log.shouldLog(Log.DEBUG)) _log.debug("After getDate / begin waiting for a response");
while (!_dateReceived) {
try {
synchronized (_dateReceivedLock) { _dateReceivedLock.wait(1000); }
} catch (InterruptedException ie) {}
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("After received a SetDate response");
if (_log.shouldLog(Log.DEBUG)) _log.debug("connect begin to " + _hostname + ":" + _portNum);
_socket = new Socket(_hostname, _portNum);
_out = _socket.getOutputStream();
synchronized (_out) {
_out.write(I2PClient.PROTOCOL_BYTE);
}
InputStream in = _socket.getInputStream();
_reader = new I2CPMessageReader(in, this);
if (_log.shouldLog(Log.DEBUG)) _log.debug("before startReading");
_reader.startReading();
if (_log.shouldLog(Log.DEBUG)) _log.debug("Before producer.connect()");
_producer.connect(this);
if (_log.shouldLog(Log.DEBUG)) _log.debug("After producer.connect()");
// wait until we have created a lease set
while (_leaseSet == null) {
synchronized (_leaseSetWait) {
try { _leaseSetWait.wait(1000); } catch (InterruptedException ie) {}
}
}
long connected = Clock.getInstance().now();
if (_log.shouldLog(Log.INFO)) _log.info("Lease set created with inbound tunnels after " + (connected-startConnect) + "ms - ready to participate in the network!");
if (_log.shouldLog(Log.DEBUG)) _log.debug("Before getDate");
sendMessage(new GetDateMessage());
if (_log.shouldLog(Log.DEBUG)) _log.debug("After getDate / begin waiting for a response");
while (!_dateReceived) {
try {
synchronized (_dateReceivedLock) {
_dateReceivedLock.wait(1000);
}
} catch (InterruptedException ie) {
}
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("After received a SetDate response");
if (_log.shouldLog(Log.DEBUG)) _log.debug("Before producer.connect()");
_producer.connect(this);
if (_log.shouldLog(Log.DEBUG)) _log.debug("After producer.connect()");
// wait until we have created a lease set
while (_leaseSet == null) {
synchronized (_leaseSetWait) {
try {
_leaseSetWait.wait(1000);
} catch (InterruptedException ie) {
}
}
}
long connected = Clock.getInstance().now();
if (_log.shouldLog(Log.INFO))
_log.info("Lease set created with inbound tunnels after "
+ (connected - startConnect)
+ "ms - ready to participate in the network!");
} catch (UnknownHostException uhe) {
_closed = true;
_closed = true;
throw new I2PSessionException("Invalid session configuration", uhe);
} catch (IOException ioe) {
_closed = true;
_closed = true;
throw new I2PSessionException("Problem connecting to " + _hostname + " on port " + _portNum, ioe);
}
}
/**
* Pull the unencrypted data from the message that we've already prefetched and
* notified the user that its available.
*
*/
public byte[] receiveMessage(int msgId) throws I2PSessionException {
MessagePayloadMessage msg = (MessagePayloadMessage)_availableMessages.remove(new Integer(msgId));
MessagePayloadMessage msg = (MessagePayloadMessage) _availableMessages.remove(new Integer(msgId));
if (msg == null) return null;
return msg.getPayload().getUnencryptedData();
}
/**
* Report abuse with regards to the given messageId
*/
public void reportAbuse(int msgId, int severity) throws I2PSessionException {
if (isClosed()) throw new I2PSessionException("Already closed");
if (isClosed()) throw new I2PSessionException("Already closed");
_producer.reportAbuse(this, msgId, severity);
}
/**
* Send the data to the destination.
* TODO: this currently always returns true, regardless of whether the message was
@@ -272,43 +292,49 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
*
*/
public abstract boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException;
public abstract boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
public abstract boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent)
throws I2PSessionException;
public abstract void receiveStatus(int msgId, long nonce, int status);
protected boolean isGuaranteed() {
return I2PClient.PROP_RELIABILITY_GUARANTEED.equals(_options.getProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED));
return I2PClient.PROP_RELIABILITY_GUARANTEED
.equals(_options.getProperty(I2PClient.PROP_RELIABILITY,
I2PClient.PROP_RELIABILITY_GUARANTEED));
}
protected static final Set createNewTags(int num) {
Set tags = new HashSet();
for (int i = 0; i < num; i++)
tags.add(new SessionTag(true));
return tags;
Set tags = new HashSet();
for (int i = 0; i < num; i++)
tags.add(new SessionTag(true));
return tags;
}
/**
* Recieve a payload message and let the app know its available
*/
public void addNewMessage(MessagePayloadMessage msg) {
_availableMessages.put(new Integer(msg.getMessageId().getMessageId()), msg);
final int id = msg.getMessageId().getMessageId();
byte data[] = msg.getPayload().getUnencryptedData();
if ( (data == null) || (data.length <= 0) ) {
if (_log.shouldLog(Log.ERROR)) _log.error("addNewMessage of a message with no unencrypted data", new Exception("Empty message"));
} else {
final long size = data.length;
Thread notifier = new I2PThread(new Runnable() {
public void run() {
if (_sessionListener != null)
_sessionListener.messageAvailable(I2PSessionImpl.this, id, size);
}
});
notifier.setName("Notifier [" + _sessionId + "/" + id + "]");
notifier.setDaemon(true);
notifier.start();
}
byte data[] = msg.getPayload().getUnencryptedData();
if ((data == null) || (data.length <= 0)) {
if (_log.shouldLog(Log.ERROR))
_log.error("addNewMessage of a message with no unencrypted data",
new Exception("Empty message"));
} else {
final long size = data.length;
Thread notifier = new I2PThread(new Runnable() {
public void run() {
if (_sessionListener != null) _sessionListener.messageAvailable(I2PSessionImpl.this, id, size);
}
});
notifier.setName("Notifier [" + _sessionId + "/" + id + "]");
notifier.setDaemon(true);
notifier.start();
}
}
/**
* Recieve notification of some I2CP message and handle it if possible
*
@@ -316,177 +342,208 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
public void messageReceived(I2CPMessageReader reader, I2CPMessage message) {
I2CPMessageHandler handler = I2PClientMessageHandlerMap.getHandler(message.getType());
if (handler == null) {
if (_log.shouldLog(Log.WARN)) _log.warn("Unknown message or unhandleable message received: type = " + message.getType());
if (_log.shouldLog(Log.WARN))
_log.warn("Unknown message or unhandleable message received: type = "
+ message.getType());
} else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Message received of type " + message.getType() + " to be handled by " + handler);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Message received of type " + message.getType()
+ " to be handled by " + handler);
handler.handleMessage(message, this);
}
}
/**
* Recieve notifiation of an error reading the I2CP stream
*
*/
public void readError(I2CPMessageReader reader, Exception error) {
propogateError("There was an error reading data", error);
disconnect();
disconnect();
}
/**
* Retrieve the destination of the session
*/
public Destination getMyDestination() { return _myDestination; }
public Destination getMyDestination() {
return _myDestination;
}
/**
* Retrieve the decryption PrivateKey
*/
public PrivateKey getDecryptionKey() { return _privateKey; }
public PrivateKey getDecryptionKey() {
return _privateKey;
}
/**
* Retrieve the signing SigningPrivateKey
*/
public SigningPrivateKey getPrivateKey() { return _signingPrivateKey; }
public SigningPrivateKey getPrivateKey() {
return _signingPrivateKey;
}
/**
* Retrieve the helper that generates I2CP messages
*/
I2CPMessageProducer getProducer() { return _producer; }
I2CPMessageProducer getProducer() {
return _producer;
}
/**
* Retrieve the configuration options
*/
Properties getOptions() { return _options; }
Properties getOptions() {
return _options;
}
/**
* Retrieve the session's ID
*/
SessionId getSessionId() { return _sessionId; }
SessionId getSessionId() {
return _sessionId;
}
/**
* Configure the session's ID
*/
void setSessionId(SessionId id) {
_sessionId = id;
void setSessionId(SessionId id) {
_sessionId = id;
}
/** configure the listener */
public void setSessionListener(I2PSessionListener lsnr) { _sessionListener = lsnr; }
public void setSessionListener(I2PSessionListener lsnr) {
_sessionListener = lsnr;
}
/** has the session been closed (or not yet connected)? */
public boolean isClosed() { return _closed; }
public boolean isClosed() {
return _closed;
}
/**
* Deliver an I2CP message to the router
*
* @throws I2PSessionException if the message is malformed or there is an error writing it out
*/
void sendMessage(I2CPMessage message) throws I2PSessionException {
if (isClosed()) throw new I2PSessionException("Already closed");
if (isClosed()) throw new I2PSessionException("Already closed");
try {
synchronized(_out) {
message.writeMessage(_out);
_out.flush();
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("Message written out and flushed");
synchronized (_out) {
message.writeMessage(_out);
_out.flush();
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("Message written out and flushed");
} catch (I2CPMessageException ime) {
throw new I2PSessionException("Error writing out the message", ime);
} catch (IOException ioe) {
throw new I2PSessionException("Error writing out the message", ioe);
}
}
/**
* Pass off the error to the listener
*/
void propogateError(String msg, Throwable error) {
if (_log.shouldLog(Log.ERROR)) _log.error("Error occurred: " + msg, error);
if (_sessionListener != null)
_sessionListener.errorOccurred(this, msg, error);
if (_log.shouldLog(Log.ERROR)) _log.error("Error occurred: " + msg, error);
if (_sessionListener != null) _sessionListener.errorOccurred(this, msg, error);
}
/**
* Tear down the session, and do NOT reconnect
*/
public void destroySession() { destroySession(true); }
public void destroySession(boolean sendDisconnect) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Destroy the session", new Exception("DestroySession()"));
_closed = true;
if (sendDisconnect) {
try {
_producer.disconnect(this);
} catch (I2PSessionException ipe) {
propogateError("Error destroying the session", ipe);
}
}
closeSocket();
if (_sessionListener != null)
_sessionListener.disconnected(this);
public void destroySession() {
destroySession(true);
}
public void destroySession(boolean sendDisconnect) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Destroy the session", new Exception("DestroySession()"));
_closed = true;
if (sendDisconnect) {
try {
_producer.disconnect(this);
} catch (I2PSessionException ipe) {
propogateError("Error destroying the session", ipe);
}
}
closeSocket();
if (_sessionListener != null) _sessionListener.disconnected(this);
}
/**
* Close the socket carefully
*
*/
private void closeSocket() {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Closing the socket", new Exception("closeSocket"));
_closed = true;
if (_reader != null)
_reader.stopReading();
_reader = null;
if (_log.shouldLog(Log.DEBUG)) _log.debug("Closing the socket", new Exception("closeSocket"));
_closed = true;
if (_reader != null) _reader.stopReading();
_reader = null;
if (_socket != null) {
try {
try {
_socket.close();
} catch (IOException ioe) {
propogateError("Caught an IO error closing the socket. ignored", ioe);
} finally {
_socket = null; // so when propogateError calls closeSocket, it doesnt loop
}
_socket = null; // so when propogateError calls closeSocket, it doesnt loop
}
}
}
/**
* Recieve notification that the I2CP connection was disconnected
*/
public void disconnected(I2CPMessageReader reader) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Disconnected", new Exception("Disconnected"));
disconnect();
if (_log.shouldLog(Log.DEBUG)) _log.debug("Disconnected", new Exception("Disconnected"));
disconnect();
}
protected void disconnect() {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Disconnect() called", new Exception("Disconnect"));
if (shouldReconnect()) {
if (reconnect()) {
if (_log.shouldLog(Log.INFO)) _log.info("I2CP reconnection successful");
return;
} else {
_log.error("I2CP reconnection failed");
}
}
_log.error("Disconned from the router, and not trying to reconnect further. I hope you're not hoping anything else will happen");
if (_sessionListener != null)
_sessionListener.disconnected(this);
closeSocket();
protected void disconnect() {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Disconnect() called", new Exception("Disconnect"));
if (shouldReconnect()) {
if (reconnect()) {
if (_log.shouldLog(Log.INFO)) _log.info("I2CP reconnection successful");
return;
} else {
_log.error("I2CP reconnection failed");
}
}
_log
.error("Disconned from the router, and not trying to reconnect further. I hope you're not hoping anything else will happen");
if (_sessionListener != null) _sessionListener.disconnected(this);
closeSocket();
}
private final static int MAX_RECONNECT_ATTEMPTS = 1;
private final static int MAX_TOTAL_RECONNECT_ATTEMPTS = 3;
protected boolean shouldReconnect() { return true; }
protected boolean shouldReconnect() {
return true;
}
protected boolean reconnect() {
closeSocket();
if (_totalReconnectAttempts < MAX_TOTAL_RECONNECT_ATTEMPTS) {
_totalReconnectAttempts++;
} else {
if (_log.shouldLog(Log.CRIT)) _log.log(Log.CRIT, "Max number of reconnects exceeded [" + _totalReconnectAttempts + "], we give up!");
return false;
}
if (_log.shouldLog(Log.INFO)) _log.info("Reconnecting...");
for (int i = 0; i < MAX_RECONNECT_ATTEMPTS; i++) {
try {
connect();
return true;
} catch (I2PSessionException ise) {
if (_log.shouldLog(Log.ERROR)) _log.error("Error reconnecting on attempt " + i, ise);
}
}
return false;
closeSocket();
if (_totalReconnectAttempts < MAX_TOTAL_RECONNECT_ATTEMPTS) {
_totalReconnectAttempts++;
} else {
if (_log.shouldLog(Log.CRIT))
_log.log(Log.CRIT, "Max number of reconnects exceeded ["
+ _totalReconnectAttempts + "], we give up!");
return false;
}
if (_log.shouldLog(Log.INFO)) _log.info("Reconnecting...");
for (int i = 0; i < MAX_RECONNECT_ATTEMPTS; i++) {
try {
connect();
return true;
} catch (I2PSessionException ise) {
if (_log.shouldLog(Log.ERROR)) _log.error("Error reconnecting on attempt " + i, ise);
}
}
return false;
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -16,10 +17,10 @@ import java.util.Set;
import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.SessionKeyManager;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.data.DataHelper;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.util.Clock;
@@ -33,14 +34,14 @@ import net.i2p.util.RandomSource;
*/
class I2PSessionImpl2 extends I2PSessionImpl {
private final static Log _log = new Log(I2PSessionImpl2.class);
/** set of MessageState objects, representing all of the messages in the process of being sent */
private Set _sendingStates;
/** max # seconds to wait for confirmation of the message send */
private final static long SEND_TIMEOUT = 60*1000; // 60 seconds to send
private final static long SEND_TIMEOUT = 60 * 1000; // 60 seconds to send
/** should we gzip each payload prior to sending it? */
private final static boolean SHOULD_COMPRESS = true;
/**
* Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey
* from the destKeyStream, and using the specified options to connect to the router
@@ -48,240 +49,270 @@ class I2PSessionImpl2 extends I2PSessionImpl {
* @throws I2PSessionException if there is a problem loading the private keys or
*/
public I2PSessionImpl2(InputStream destKeyStream, Properties options) throws I2PSessionException {
super(destKeyStream, options);
_sendingStates = new HashSet(32);
super(destKeyStream, options);
_sendingStates = new HashSet(32);
}
protected long getTimeout() { return SEND_TIMEOUT; }
protected long getTimeout() {
return SEND_TIMEOUT;
}
public void destroySession(boolean sendDisconnect) {
clearStates();
super.destroySession(sendDisconnect);
clearStates();
super.destroySession(sendDisconnect);
}
public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException {
return sendMessage(dest, payload, new SessionKey(), new HashSet(64));
return sendMessage(dest, payload, new SessionKey(), new HashSet(64));
}
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException {
if (isClosed()) throw new I2PSessionException("Already closed");
if (SHOULD_COMPRESS)
payload = DataHelper.compress(payload);
if (isGuaranteed()) {
return sendGuaranteed(dest, payload, keyUsed, tagsSent);
} else {
return sendBestEffort(dest, payload, keyUsed, tagsSent);
}
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent)
throws I2PSessionException {
if (isClosed()) throw new I2PSessionException("Already closed");
if (SHOULD_COMPRESS) payload = DataHelper.compress(payload);
if (isGuaranteed()) {
return sendGuaranteed(dest, payload, keyUsed, tagsSent);
} else {
return sendBestEffort(dest, payload, keyUsed, tagsSent);
}
}
/**
* pull the unencrypted AND DECOMPRESSED data
*/
public byte[] receiveMessage(int msgId) throws I2PSessionException {
byte compressed[] = super.receiveMessage(msgId);
if (SHOULD_COMPRESS)
return DataHelper.decompress(compressed);
else
return compressed;
byte compressed[] = super.receiveMessage(msgId);
if (SHOULD_COMPRESS)
return DataHelper.decompress(compressed);
else
return compressed;
}
private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent) throws I2PSessionException {
SessionKey key = SessionKeyManager.getInstance().getCurrentKey(dest.getPublicKey());
if (key == null)
key = SessionKeyManager.getInstance().createSession(dest.getPublicKey());
SessionTag tag = SessionKeyManager.getInstance().consumeNextAvailableTag(dest.getPublicKey(), key);
Set sentTags = null;
if (SessionKeyManager.getInstance().getAvailableTags(dest.getPublicKey(), key) < 10) {
sentTags = createNewTags(50);
} else if (SessionKeyManager.getInstance().getAvailableTimeLeft(dest.getPublicKey(), key) < 30*1000) {
// if we have > 10 tags, but they expire in under 30 seconds, we want more
sentTags = createNewTags(50);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Tags are almost expired, adding 50 new ones");
}
SessionKey newKey = null;
if (false) // rekey
newKey = KeyGenerator.getInstance().generateSessionKey();
long nonce = (long)RandomSource.getInstance().nextInt(Integer.MAX_VALUE);
MessageState state = new MessageState(nonce);
state.setKey(key);
state.setTags(sentTags);
state.setNewKey(newKey);
state.setTo(dest);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key = " + key);
private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent)
throws I2PSessionException {
SessionKey key = SessionKeyManager.getInstance().getCurrentKey(dest.getPublicKey());
if (key == null) key = SessionKeyManager.getInstance().createSession(dest.getPublicKey());
SessionTag tag = SessionKeyManager.getInstance().consumeNextAvailableTag(dest.getPublicKey(), key);
Set sentTags = null;
if (SessionKeyManager.getInstance().getAvailableTags(dest.getPublicKey(), key) < 10) {
sentTags = createNewTags(50);
} else if (SessionKeyManager.getInstance().getAvailableTimeLeft(dest.getPublicKey(), key) < 30 * 1000) {
// if we have > 10 tags, but they expire in under 30 seconds, we want more
sentTags = createNewTags(50);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Tags are almost expired, adding 50 new ones");
}
SessionKey newKey = null;
if (false) // rekey
newKey = KeyGenerator.getInstance().generateSessionKey();
if (keyUsed != null) {
if (newKey != null)
keyUsed.setData(newKey.getData());
else
keyUsed.setData(key.getData());
}
if (tagsSent != null) {
if (sentTags != null) {
tagsSent.addAll(sentTags);
}
}
long nonce = (long) RandomSource.getInstance().nextInt(Integer.MAX_VALUE);
MessageState state = new MessageState(nonce);
state.setKey(key);
state.setTags(sentTags);
state.setNewKey(newKey);
state.setTo(dest);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key = " + key);
synchronized (_sendingStates) {
_sendingStates.add(state);
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("Adding sending state " + state.getMessageId() + " / " + state.getNonce());
_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey);
state.waitFor(MessageStatusMessage.STATUS_SEND_ACCEPTED, Clock.getInstance().now() + getTimeout());
synchronized (_sendingStates) {
_sendingStates.remove(state);
}
boolean found = state.received(MessageStatusMessage.STATUS_SEND_ACCEPTED);
if (_log.shouldLog(Log.DEBUG)) _log.debug("After waitFor sending state " + state.getMessageId().getMessageId() + " / " + state.getNonce() + " found = " + found);
if (found) {
if (_log.shouldLog(Log.INFO)) _log.info("Message sent after " + state.getElapsed() + "ms with " + payload.length + " bytes");
} else {
if (_log.shouldLog(Log.INFO)) _log.info("Message send failed after " + state.getElapsed() + "ms with " + payload.length + " bytes");
if (_log.shouldLog(Log.ERROR)) _log.error("Never received *accepted* from the router! dropping and reconnecting");
disconnect();
return false;
}
return found;
if (keyUsed != null) {
if (newKey != null)
keyUsed.setData(newKey.getData());
else
keyUsed.setData(key.getData());
}
if (tagsSent != null) {
if (sentTags != null) {
tagsSent.addAll(sentTags);
}
}
synchronized (_sendingStates) {
_sendingStates.add(state);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Adding sending state " + state.getMessageId() + " / "
+ state.getNonce());
_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey);
state.waitFor(MessageStatusMessage.STATUS_SEND_ACCEPTED, Clock.getInstance().now() + getTimeout());
synchronized (_sendingStates) {
_sendingStates.remove(state);
}
boolean found = state.received(MessageStatusMessage.STATUS_SEND_ACCEPTED);
if (_log.shouldLog(Log.DEBUG))
_log.debug("After waitFor sending state " + state.getMessageId().getMessageId()
+ " / " + state.getNonce() + " found = " + found);
if (found) {
if (_log.shouldLog(Log.INFO))
_log.info("Message sent after " + state.getElapsed() + "ms with "
+ payload.length + " bytes");
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Message send failed after " + state.getElapsed() + "ms with "
+ payload.length + " bytes");
if (_log.shouldLog(Log.ERROR))
_log
.error("Never received *accepted* from the router! dropping and reconnecting");
disconnect();
return false;
}
return found;
}
private boolean sendGuaranteed(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent) throws I2PSessionException {
SessionKey key = SessionKeyManager.getInstance().getCurrentKey(dest.getPublicKey());
if (key == null)
key = SessionKeyManager.getInstance().createSession(dest.getPublicKey());
SessionTag tag = SessionKeyManager.getInstance().consumeNextAvailableTag(dest.getPublicKey(), key);
Set sentTags = null;
if (SessionKeyManager.getInstance().getAvailableTags(dest.getPublicKey(), key) < 10) {
sentTags = createNewTags(50);
} else if (SessionKeyManager.getInstance().getAvailableTimeLeft(dest.getPublicKey(), key) < 30*1000) {
// if we have > 10 tags, but they expire in under 30 seconds, we want more
sentTags = createNewTags(50);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Tags are almost expired, adding 50 new ones");
}
SessionKey newKey = null;
if (false) // rekey
newKey = KeyGenerator.getInstance().generateSessionKey();
long nonce = (long)RandomSource.getInstance().nextInt(Integer.MAX_VALUE);
MessageState state = new MessageState(nonce);
state.setKey(key);
state.setTags(sentTags);
state.setNewKey(newKey);
state.setTo(dest);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key = " + key);
private boolean sendGuaranteed(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent)
throws I2PSessionException {
SessionKey key = SessionKeyManager.getInstance().getCurrentKey(dest.getPublicKey());
if (key == null) key = SessionKeyManager.getInstance().createSession(dest.getPublicKey());
SessionTag tag = SessionKeyManager.getInstance().consumeNextAvailableTag(dest.getPublicKey(), key);
Set sentTags = null;
if (SessionKeyManager.getInstance().getAvailableTags(dest.getPublicKey(), key) < 10) {
sentTags = createNewTags(50);
} else if (SessionKeyManager.getInstance().getAvailableTimeLeft(dest.getPublicKey(), key) < 30 * 1000) {
// if we have > 10 tags, but they expire in under 30 seconds, we want more
sentTags = createNewTags(50);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Tags are almost expired, adding 50 new ones");
}
SessionKey newKey = null;
if (false) // rekey
newKey = KeyGenerator.getInstance().generateSessionKey();
if (keyUsed != null) {
if (newKey != null)
keyUsed.setData(newKey.getData());
else
keyUsed.setData(key.getData());
}
if (tagsSent != null) {
if (sentTags != null) {
tagsSent.addAll(sentTags);
}
}
long nonce = (long) RandomSource.getInstance().nextInt(Integer.MAX_VALUE);
MessageState state = new MessageState(nonce);
state.setKey(key);
state.setTags(sentTags);
state.setNewKey(newKey);
state.setTo(dest);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key = " + key);
synchronized (_sendingStates) {
_sendingStates.add(state);
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("Adding sending state " + state.getMessageId() + " / " + state.getNonce());
_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey);
state.waitFor(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS, Clock.getInstance().now() + SEND_TIMEOUT);
synchronized (_sendingStates) {
_sendingStates.remove(state);
}
boolean found = state.received(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS);
boolean accepted = state.received(MessageStatusMessage.STATUS_SEND_ACCEPTED);
if ( (!accepted) || (state.getMessageId() == null) ) {
if (_log.shouldLog(Log.ERROR)) _log.error("State with nonce " + state.getNonce() + " was not accepted? (no messageId!!)");
nackTags(state);
if (_log.shouldLog(Log.CRIT)) _log.log(Log.CRIT,"Disconnecting/reconnecting because we never were accepted!");
disconnect();
return false;
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("After waitFor sending state " + state.getMessageId().getMessageId() + " / " + state.getNonce() + " found = " + found);
if (found) {
if (_log.shouldLog(Log.INFO)) _log.info("Message sent after " + state.getElapsed() + "ms with " + payload.length + " bytes");
ackTags(state);
} else {
if (_log.shouldLog(Log.INFO)) _log.info("Message send failed after " + state.getElapsed() + "ms with " + payload.length + " bytes");
nackTags(state);
}
return found;
if (keyUsed != null) {
if (newKey != null)
keyUsed.setData(newKey.getData());
else
keyUsed.setData(key.getData());
}
if (tagsSent != null) {
if (sentTags != null) {
tagsSent.addAll(sentTags);
}
}
synchronized (_sendingStates) {
_sendingStates.add(state);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Adding sending state " + state.getMessageId() + " / "
+ state.getNonce());
_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey);
state.waitFor(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS, Clock.getInstance().now() + SEND_TIMEOUT);
synchronized (_sendingStates) {
_sendingStates.remove(state);
}
boolean found = state.received(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS);
boolean accepted = state.received(MessageStatusMessage.STATUS_SEND_ACCEPTED);
if ((!accepted) || (state.getMessageId() == null)) {
if (_log.shouldLog(Log.ERROR))
_log.error("State with nonce " + state.getNonce()
+ " was not accepted? (no messageId!!)");
nackTags(state);
if (_log.shouldLog(Log.CRIT))
_log.log(Log.CRIT,
"Disconnecting/reconnecting because we never were accepted!");
disconnect();
return false;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("After waitFor sending state " + state.getMessageId().getMessageId()
+ " / " + state.getNonce() + " found = " + found);
if (found) {
if (_log.shouldLog(Log.INFO))
_log.info("Message sent after " + state.getElapsed() + "ms with "
+ payload.length + " bytes");
ackTags(state);
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Message send failed after " + state.getElapsed() + "ms with "
+ payload.length + " bytes");
nackTags(state);
}
return found;
}
private void ackTags(MessageState state) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("ack tags for msgId " + state.getMessageId() + " / " + state.getNonce() + " key = " + state.getKey() + ", tags = " + state.getTags());
if ( (state.getTags() != null) && (state.getTags().size() > 0) ) {
if (state.getNewKey() == null)
SessionKeyManager.getInstance().tagsDelivered(state.getTo().getPublicKey(), state.getKey(), state.getTags());
else
SessionKeyManager.getInstance().tagsDelivered(state.getTo().getPublicKey(), state.getNewKey(), state.getTags());
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("ack tags for msgId " + state.getMessageId() + " / "
+ state.getNonce() + " key = " + state.getKey() + ", tags = "
+ state.getTags());
if ((state.getTags() != null) && (state.getTags().size() > 0)) {
if (state.getNewKey() == null)
SessionKeyManager.getInstance().tagsDelivered(state.getTo().getPublicKey(), state.getKey(),
state.getTags());
else
SessionKeyManager.getInstance().tagsDelivered(state.getTo().getPublicKey(), state.getNewKey(),
state.getTags());
}
}
private void nackTags(MessageState state) {
if (_log.shouldLog(Log.INFO)) _log.info("nack tags for msgId " + state.getMessageId() + " / " + state.getNonce() + " key = " + state.getKey());
SessionKeyManager.getInstance().failTags(state.getTo().getPublicKey());
if (_log.shouldLog(Log.INFO))
_log.info("nack tags for msgId " + state.getMessageId() + " / " + state.getNonce()
+ " key = " + state.getKey());
SessionKeyManager.getInstance().failTags(state.getTo().getPublicKey());
}
public void receiveStatus(int msgId, long nonce, int status) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received status " + status + " for msgId " + msgId + " / " + nonce);
MessageState state = null;
synchronized (_sendingStates) {
for (Iterator iter = _sendingStates.iterator(); iter.hasNext(); ) {
state = (MessageState)iter.next();
if (_log.shouldLog(Log.DEBUG)) _log.debug("State " + state.getMessageId() + " / " + state.getNonce());
if (state.getNonce() == nonce) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Found a matching state");
break;
} else if ( (state.getMessageId() != null) && (state.getMessageId().getMessageId() == msgId) ) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Found a matching state by msgId");
break;
} else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("State does not match");
state = null;
}
}
}
if (state != null) {
if (state.getMessageId() == null) {
MessageId id = new MessageId();
id.setMessageId(msgId);
state.setMessageId(id);
}
state.receive(status);
} else {
if (_log.shouldLog(Log.INFO)) _log.info("No matching state for messageId " + msgId + " / " + nonce + " w/ status = " + status);
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received status " + status + " for msgId " + msgId + " / " + nonce);
MessageState state = null;
synchronized (_sendingStates) {
for (Iterator iter = _sendingStates.iterator(); iter.hasNext();) {
state = (MessageState) iter.next();
if (_log.shouldLog(Log.DEBUG)) _log.debug("State " + state.getMessageId() + " / " + state.getNonce());
if (state.getNonce() == nonce) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Found a matching state");
break;
} else if ((state.getMessageId() != null) && (state.getMessageId().getMessageId() == msgId)) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Found a matching state by msgId");
break;
} else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("State does not match");
state = null;
}
}
}
if (state != null) {
if (state.getMessageId() == null) {
MessageId id = new MessageId();
id.setMessageId(msgId);
state.setMessageId(id);
}
state.receive(status);
} else {
if (_log.shouldLog(Log.INFO))
_log.info("No matching state for messageId " + msgId + " / " + nonce
+ " w/ status = " + status);
}
}
/**
* Called whenever we want to reconnect (used only in the superclass). We need
* to override this to clear out the message state
*
*/
protected boolean reconnect() {
// even if we succeed in reconnecting, we want to clear the old states,
// since this will be a new sessionId
clearStates();
return super.reconnect();
// even if we succeed in reconnecting, we want to clear the old states,
// since this will be a new sessionId
clearStates();
return super.reconnect();
}
private void clearStates() {
synchronized (_sendingStates) {
for (Iterator iter = _sendingStates.iterator(); iter.hasNext(); ) {
MessageState state = (MessageState)iter.next();
state.cancel();
}
if (_log.shouldLog(Log.INFO)) _log.info("Disconnecting " + _sendingStates.size() + " states");
_sendingStates.clear();
}
synchronized (_sendingStates) {
for (Iterator iter = _sendingStates.iterator(); iter.hasNext();) {
MessageState state = (MessageState) iter.next();
state.cancel();
}
if (_log.shouldLog(Log.INFO)) _log.info("Disconnecting " + _sendingStates.size() + " states");
_sendingStates.clear();
}
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -22,20 +23,20 @@ public interface I2PSessionListener {
* @param size size of the message
*/
void messageAvailable(I2PSession session, int msgId, long size);
/** Instruct the client that the session specified seems to be under attack
* and that the client may wish to move its destination to another router.
* @param session session to report abuse to
* @param severity how bad the abuse is
*/
void reportAbuse(I2PSession session, int severity);
/**
* Notify the client that the session has been terminated
*
*/
void disconnected(I2PSession session);
/**
* Notify the client that some error occurred
*

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -27,36 +28,39 @@ class MessagePayloadMessageHandler extends HandlerImpl {
public MessagePayloadMessageHandler() {
super(MessagePayloadMessage.MESSAGE_TYPE);
}
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
_log.debug("Handle message " + message);
try {
MessagePayloadMessage msg = (MessagePayloadMessage)message;
MessagePayloadMessage msg = (MessagePayloadMessage) message;
MessageId id = msg.getMessageId();
Payload payload = decryptPayload(msg, session);
session.addNewMessage(msg);
ReceiveMessageEndMessage m = new ReceiveMessageEndMessage();
m.setMessageId(id);
m.setSessionId(msg.getSessionId());
session.sendMessage(m);
ReceiveMessageEndMessage m = new ReceiveMessageEndMessage();
m.setMessageId(id);
m.setSessionId(msg.getSessionId());
session.sendMessage(m);
} catch (DataFormatException dfe) {
session.propogateError("Error handling a new payload message", dfe);
} catch (I2PSessionException ise) {
session.propogateError("Error handling a new payload message", ise);
}
}
}
/**
* Decrypt the payload
*/
private Payload decryptPayload(MessagePayloadMessage msg, I2PSessionImpl session) throws DataFormatException {
Payload payload = msg.getPayload();
byte[] data = ElGamalAESEngine.decrypt(payload.getEncryptedData(), session.getDecryptionKey());
if (data == null) {
_log.error("Error decrypting the payload to public key " + session.getMyDestination().getPublicKey().toBase64() + "\nPayload: " + payload.calculateHash());
throw new DataFormatException("Unable to decrypt the payload");
}
payload.setUnencryptedData(data);
if (data == null) {
_log
.error("Error decrypting the payload to public key "
+ session.getMyDestination().getPublicKey().toBase64() + "\nPayload: " + payload.calculateHash());
throw new DataFormatException("Unable to decrypt the payload");
}
payload.setUnencryptedData(data);
return payload;
}
}

View File

@@ -1,18 +1,17 @@
package net.i2p.client;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.i2p.data.Destination;
import net.i2p.data.SessionKey;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.SessionKey;
import net.i2p.data.Destination;
import net.i2p.util.Log;
import net.i2p.util.Clock;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import net.i2p.util.Log;
/**
* Contains the state of a payload message being sent to a peer
@@ -30,179 +29,240 @@ class MessageState {
private boolean _cancelled;
private long _created;
private Object _lock = new Object();
public MessageState(long nonce) {
_nonce = nonce;
_id = null;
_receivedStatus = new HashSet();
_cancelled = false;
_key = null;
_newKey = null;
_tags = null;
_to = null;
_created = Clock.getInstance().now();
_nonce = nonce;
_id = null;
_receivedStatus = new HashSet();
_cancelled = false;
_key = null;
_newKey = null;
_tags = null;
_to = null;
_created = Clock.getInstance().now();
}
public void receive(int status) {
synchronized (_receivedStatus) {
_receivedStatus.add(new Integer(status));
}
synchronized (_lock) {
_lock.notifyAll();
}
synchronized (_receivedStatus) {
_receivedStatus.add(new Integer(status));
}
synchronized (_lock) {
_lock.notifyAll();
}
}
public void setMessageId(MessageId id) { _id = id; }
public MessageId getMessageId() { return _id; }
public long getNonce() { return _nonce; }
public void setKey(SessionKey key) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key [" + _key + "] to [" + key + "]");
_key = key;
public void setMessageId(MessageId id) {
_id = id;
}
public SessionKey getKey() { return _key; }
public void setNewKey(SessionKey key) { _newKey = key; }
public SessionKey getNewKey() { return _newKey; }
public void setTags(Set tags) { _tags = tags; }
public Set getTags() { return _tags; }
public void setTo(Destination dest) { _to = dest; }
public Destination getTo() { return _to; }
public long getElapsed() { return Clock.getInstance().now() - _created; }
public MessageId getMessageId() {
return _id;
}
public long getNonce() {
return _nonce;
}
public void setKey(SessionKey key) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key [" + _key + "] to [" + key + "]");
_key = key;
}
public SessionKey getKey() {
return _key;
}
public void setNewKey(SessionKey key) {
_newKey = key;
}
public SessionKey getNewKey() {
return _newKey;
}
public void setTags(Set tags) {
_tags = tags;
}
public Set getTags() {
return _tags;
}
public void setTo(Destination dest) {
_to = dest;
}
public Destination getTo() {
return _to;
}
public long getElapsed() {
return Clock.getInstance().now() - _created;
}
public void waitFor(int status, long expiration) {
while (true) {
if (_cancelled) return;
long timeToWait = expiration - Clock.getInstance().now();
if (timeToWait <= 0) {
if (_log.shouldLog(Log.WARN)) _log.warn("Expired waiting for the status [" + status + "]");
return;
}
if (isSuccess(status) || isFailure(status)) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received a confirm (one way or the other)");
return;
}
if (timeToWait > 5000) {
timeToWait = 5000;
}
synchronized (_lock) {
try {
_lock.wait(timeToWait);
} catch (InterruptedException ie) {}
}
}
while (true) {
if (_cancelled) return;
long timeToWait = expiration - Clock.getInstance().now();
if (timeToWait <= 0) {
if (_log.shouldLog(Log.WARN)) _log.warn("Expired waiting for the status [" + status + "]");
return;
}
if (isSuccess(status) || isFailure(status)) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received a confirm (one way or the other)");
return;
}
if (timeToWait > 5000) {
timeToWait = 5000;
}
synchronized (_lock) {
try {
_lock.wait(timeToWait);
} catch (InterruptedException ie) {
}
}
}
}
private boolean isSuccess(int wantedStatus) {
List received = null;
synchronized (_receivedStatus) {
received = new ArrayList(_receivedStatus);
//_receivedStatus.clear();
}
boolean rv = false;
if (_log.shouldLog(Log.DEBUG)) _log.debug("isSuccess(" + wantedStatus + "): " + received);
for (Iterator iter = received.iterator(); iter.hasNext(); ) {
Integer val = (Integer)iter.next();
int recv = val.intValue();
switch (recv) {
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
if (_log.shouldLog(Log.WARN)) _log.warn("Received best effort failure after " + getElapsed() + " from " + this.toString());
rv = false;
break;
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
if (_log.shouldLog(Log.WARN)) _log.warn("Received guaranteed failure after " + getElapsed() + " from " + this.toString());
rv = false;
break;
case MessageStatusMessage.STATUS_SEND_ACCEPTED:
if (wantedStatus == MessageStatusMessage.STATUS_SEND_ACCEPTED) {
return true; // if we're only looking for accepted, take it directly (don't let any GUARANTEED_* override it)
} else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Got accepted, but we're waiting for more from " + this.toString());
continue;
// ignore accepted, as we want something better
}
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received best effort success after " + getElapsed() + " from " + this.toString());
if (wantedStatus == recv) {
rv = true;
} else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Not guaranteed success, but best effort after " + getElapsed() + " will do... from " + this.toString());
rv = true;
}
break;
case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS:
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received guaranteed success after " + getElapsed() + " from " + this.toString());
// even if we're waiting for best effort success, guaranteed is good enough
rv = true;
break;
case -1:
continue;
default:
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received something else [" + recv + "]...");
}
}
return rv;
List received = null;
synchronized (_receivedStatus) {
received = new ArrayList(_receivedStatus);
//_receivedStatus.clear();
}
boolean rv = false;
if (_log.shouldLog(Log.DEBUG)) _log.debug("isSuccess(" + wantedStatus + "): " + received);
for (Iterator iter = received.iterator(); iter.hasNext();) {
Integer val = (Integer) iter.next();
int recv = val.intValue();
switch (recv) {
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
if (_log.shouldLog(Log.WARN))
_log.warn("Received best effort failure after " + getElapsed() + " from "
+ this.toString());
rv = false;
break;
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
if (_log.shouldLog(Log.WARN))
_log.warn("Received guaranteed failure after " + getElapsed() + " from "
+ this.toString());
rv = false;
break;
case MessageStatusMessage.STATUS_SEND_ACCEPTED:
if (wantedStatus == MessageStatusMessage.STATUS_SEND_ACCEPTED) {
return true; // if we're only looking for accepted, take it directly (don't let any GUARANTEED_* override it)
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Got accepted, but we're waiting for more from "
+ this.toString());
continue;
// ignore accepted, as we want something better
}
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received best effort success after " + getElapsed()
+ " from " + this.toString());
if (wantedStatus == recv) {
rv = true;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Not guaranteed success, but best effort after "
+ getElapsed() + " will do... from " + this.toString());
rv = true;
}
break;
case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS:
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received guaranteed success after " + getElapsed() + " from "
+ this.toString());
// even if we're waiting for best effort success, guaranteed is good enough
rv = true;
break;
case -1:
continue;
default:
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received something else [" + recv + "]...");
}
}
return rv;
}
private boolean isFailure(int wantedStatus) {
List received = null;
synchronized (_receivedStatus) {
received = new ArrayList(_receivedStatus);
//_receivedStatus.clear();
}
boolean rv = false;
if (_log.shouldLog(Log.DEBUG)) _log.debug("isFailure(" + wantedStatus + "): " + received);
for (Iterator iter = received.iterator(); iter.hasNext(); ) {
Integer val = (Integer)iter.next();
int recv = val.intValue();
switch (recv) {
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
if (_log.shouldLog(Log.DEBUG)) _log.warn("Received best effort failure after " + getElapsed() + " from " + this.toString());
rv = true;
break;
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
if (_log.shouldLog(Log.DEBUG)) _log.warn("Received guaranteed failure after " + getElapsed() + " from " + this.toString());
rv = true;
break;
case MessageStatusMessage.STATUS_SEND_ACCEPTED:
if (wantedStatus == MessageStatusMessage.STATUS_SEND_ACCEPTED) {
rv = false;
} else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Got accepted, but we're waiting for more from " + this.toString());
continue;
// ignore accepted, as we want something better
}
break;
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received best effort success after " + getElapsed() + " from " + this.toString());
if (wantedStatus == recv) {
rv = false;
} else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Not guaranteed success, but best effort after " + getElapsed() + " will do... from " + this.toString());
rv = false;
}
break;
case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS:
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received guaranteed success after " + getElapsed() + " from " + this.toString());
// even if we're waiting for best effort success, guaranteed is good enough
rv = false;
break;
case -1:
continue;
default:
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received something else [" + recv + "]...");
}
}
return rv;
List received = null;
synchronized (_receivedStatus) {
received = new ArrayList(_receivedStatus);
//_receivedStatus.clear();
}
boolean rv = false;
if (_log.shouldLog(Log.DEBUG)) _log.debug("isFailure(" + wantedStatus + "): " + received);
for (Iterator iter = received.iterator(); iter.hasNext();) {
Integer val = (Integer) iter.next();
int recv = val.intValue();
switch (recv) {
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
if (_log.shouldLog(Log.DEBUG))
_log.warn("Received best effort failure after " + getElapsed() + " from "
+ this.toString());
rv = true;
break;
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
if (_log.shouldLog(Log.DEBUG))
_log.warn("Received guaranteed failure after " + getElapsed() + " from "
+ this.toString());
rv = true;
break;
case MessageStatusMessage.STATUS_SEND_ACCEPTED:
if (wantedStatus == MessageStatusMessage.STATUS_SEND_ACCEPTED) {
rv = false;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Got accepted, but we're waiting for more from "
+ this.toString());
continue;
// ignore accepted, as we want something better
}
break;
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received best effort success after " + getElapsed()
+ " from " + this.toString());
if (wantedStatus == recv) {
rv = false;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Not guaranteed success, but best effort after "
+ getElapsed() + " will do... from " + this.toString());
rv = false;
}
break;
case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS:
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received guaranteed success after " + getElapsed() + " from "
+ this.toString());
// even if we're waiting for best effort success, guaranteed is good enough
rv = false;
break;
case -1:
continue;
default:
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received something else [" + recv + "]...");
}
}
return rv;
}
/** true if the given status (or an equivilant) was received */
public boolean received(int status) {
return isSuccess(status);
return isSuccess(status);
}
public void cancel() {
_cancelled = true;
synchronized (_lock) {
_lock.notifyAll();
}
_cancelled = true;
synchronized (_lock) {
_lock.notifyAll();
}
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -8,7 +9,9 @@ package net.i2p.client;
*
*/
import net.i2p.data.i2cp.*;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
/**
* Handle I2CP MessageStatusMessages from the router. This currently only takes
@@ -21,42 +24,44 @@ class MessageStatusMessageHandler extends HandlerImpl {
public MessageStatusMessageHandler() {
super(MessageStatusMessage.MESSAGE_TYPE);
}
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
boolean skipStatus = true;
if (I2PClient.PROP_RELIABILITY_GUARANTEED.equals(session.getOptions().getProperty(
I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT)))
skipStatus = false;
boolean skipStatus = true;
if (I2PClient.PROP_RELIABILITY_GUARANTEED.equals(session.getOptions()
.getProperty(I2PClient.PROP_RELIABILITY,
I2PClient.PROP_RELIABILITY_BEST_EFFORT)))
skipStatus = false;
_log.debug("Handle message " + message);
MessageStatusMessage msg = (MessageStatusMessage)message;
switch (msg.getStatus()) {
case MessageStatusMessage.STATUS_AVAILABLE:
ReceiveMessageBeginMessage m = new ReceiveMessageBeginMessage();
m.setMessageId(msg.getMessageId());
m.setSessionId(msg.getSessionId());
try {
session.sendMessage(m);
} catch (I2PSessionException ise) {
_log.error("Error asking for the message", ise);
}
return;
case MessageStatusMessage.STATUS_SEND_ACCEPTED:
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
// noop
return;
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS:
_log.info("Message delivery succeeded for message " + msg.getMessageId());
//if (!skipStatus)
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
return;
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
_log.info("Message delivery FAILED for message " + msg.getMessageId());
//if (!skipStatus)
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
return;
default:
_log.error("Invalid message delivery status received: " + msg.getStatus());
}
MessageStatusMessage msg = (MessageStatusMessage) message;
switch (msg.getStatus()) {
case MessageStatusMessage.STATUS_AVAILABLE:
ReceiveMessageBeginMessage m = new ReceiveMessageBeginMessage();
m.setMessageId(msg.getMessageId());
m.setSessionId(msg.getSessionId());
try {
session.sendMessage(m);
} catch (I2PSessionException ise) {
_log.error("Error asking for the message", ise);
}
return;
case MessageStatusMessage.STATUS_SEND_ACCEPTED:
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
// noop
return;
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS:
_log.info("Message delivery succeeded for message " + msg.getMessageId());
//if (!skipStatus)
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
return;
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
_log.info("Message delivery FAILED for message " + msg.getMessageId());
//if (!skipStatus)
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
return;
default:
_log.error("Invalid message delivery status received: " + msg.getStatus());
}
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -33,91 +34,100 @@ import net.i2p.util.Log;
class RequestLeaseSetMessageHandler extends HandlerImpl {
private final static Log _log = new Log(RequestLeaseSetMessageHandler.class);
private Map _existingLeaseSets;
public RequestLeaseSetMessageHandler() {
super(RequestLeaseSetMessage.MESSAGE_TYPE);
_existingLeaseSets = new HashMap(32);
_existingLeaseSets = new HashMap(32);
}
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
_log.debug("Handle message " + message);
RequestLeaseSetMessage msg = (RequestLeaseSetMessage)message;
LeaseSet leaseSet = new LeaseSet();
for (int i = 0; i < msg.getEndpoints(); i++) {
Lease lease = new Lease();
lease.setRouterIdentity(msg.getRouter(i));
lease.setTunnelId(msg.getTunnelId(i));
lease.setEndDate(msg.getEndDate());
//lease.setStartDate(msg.getStartDate());
leaseSet.addLease(lease);
}
// also, if this session is connected to multiple routers, include other leases here
leaseSet.setDestination(session.getMyDestination());
// reuse the old keys for the client
LeaseInfo li = null;
synchronized (_existingLeaseSets) {
if (_existingLeaseSets.containsKey(session.getMyDestination()))
li = (LeaseInfo)_existingLeaseSets.get(session.getMyDestination());
}
if (li == null) {
li = new LeaseInfo(session.getMyDestination());
synchronized (_existingLeaseSets) {
_existingLeaseSets.put(session.getMyDestination(), li);
}
_log.debug("Creating new leaseInfo keys", new Exception("new leaseInfo keys"));
} else {
_log.debug("Caching the old leaseInfo keys", new Exception("cached! w00t"));
}
leaseSet.setEncryptionKey(li.getPublicKey());
leaseSet.setSigningKey(li.getSigningPublicKey());
RequestLeaseSetMessage msg = (RequestLeaseSetMessage) message;
LeaseSet leaseSet = new LeaseSet();
for (int i = 0; i < msg.getEndpoints(); i++) {
Lease lease = new Lease();
lease.setRouterIdentity(msg.getRouter(i));
lease.setTunnelId(msg.getTunnelId(i));
lease.setEndDate(msg.getEndDate());
//lease.setStartDate(msg.getStartDate());
leaseSet.addLease(lease);
}
// also, if this session is connected to multiple routers, include other leases here
leaseSet.setDestination(session.getMyDestination());
// reuse the old keys for the client
LeaseInfo li = null;
synchronized (_existingLeaseSets) {
if (_existingLeaseSets.containsKey(session.getMyDestination()))
li = (LeaseInfo) _existingLeaseSets.get(session.getMyDestination());
}
if (li == null) {
li = new LeaseInfo(session.getMyDestination());
synchronized (_existingLeaseSets) {
_existingLeaseSets.put(session.getMyDestination(), li);
}
_log.debug("Creating new leaseInfo keys", new Exception("new leaseInfo keys"));
} else {
_log.debug("Caching the old leaseInfo keys", new Exception("cached! w00t"));
}
leaseSet.setEncryptionKey(li.getPublicKey());
leaseSet.setSigningKey(li.getSigningPublicKey());
try {
leaseSet.sign(session.getPrivateKey());
session.getProducer().createLeaseSet(session, leaseSet, li.getSigningPrivateKey(), li.getPrivateKey());
session.setLeaseSet(leaseSet);
leaseSet.sign(session.getPrivateKey());
session.getProducer().createLeaseSet(session, leaseSet, li.getSigningPrivateKey(), li.getPrivateKey());
session.setLeaseSet(leaseSet);
} catch (DataFormatException dfe) {
session.propogateError("Error signing the leaseSet", dfe);
} catch (I2PSessionException ise) {
session.propogateError("Error sending the signed leaseSet", ise);
}
}
private static class LeaseInfo {
private PublicKey _pubKey;
private PrivateKey _privKey;
private SigningPublicKey _signingPubKey;
private SigningPrivateKey _signingPrivKey;
private Destination _dest;
public LeaseInfo(Destination dest) {
_dest = dest;
Object encKeys[] = KeyGenerator.getInstance().generatePKIKeypair();
Object signKeys[] = KeyGenerator.getInstance().generateSigningKeypair();
_pubKey = (PublicKey)encKeys[0];
_privKey = (PrivateKey)encKeys[1];
_signingPubKey = (SigningPublicKey)signKeys[0];
_signingPrivKey = (SigningPrivateKey)signKeys[1];
}
public PublicKey getPublicKey() { return _pubKey; }
public PrivateKey getPrivateKey() { return _privKey; }
public SigningPublicKey getSigningPublicKey() { return _signingPubKey; }
public SigningPrivateKey getSigningPrivateKey() { return _signingPrivKey; }
public int hashCode() {
return DataHelper.hashCode(_pubKey) +
7*DataHelper.hashCode(_privKey) +
7*7*DataHelper.hashCode(_signingPubKey) +
7*7*7*DataHelper.hashCode(_signingPrivKey);
}
public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof LeaseInfo) )
return false;
LeaseInfo li = (LeaseInfo)obj;
return DataHelper.eq(_pubKey, li.getPublicKey()) &&
DataHelper.eq(_privKey, li.getPrivateKey()) &&
DataHelper.eq(_signingPubKey, li.getSigningPublicKey()) &&
DataHelper.eq(_signingPrivKey, li.getSigningPrivateKey());
}
private PublicKey _pubKey;
private PrivateKey _privKey;
private SigningPublicKey _signingPubKey;
private SigningPrivateKey _signingPrivKey;
private Destination _dest;
public LeaseInfo(Destination dest) {
_dest = dest;
Object encKeys[] = KeyGenerator.getInstance().generatePKIKeypair();
Object signKeys[] = KeyGenerator.getInstance().generateSigningKeypair();
_pubKey = (PublicKey) encKeys[0];
_privKey = (PrivateKey) encKeys[1];
_signingPubKey = (SigningPublicKey) signKeys[0];
_signingPrivKey = (SigningPrivateKey) signKeys[1];
}
public PublicKey getPublicKey() {
return _pubKey;
}
public PrivateKey getPrivateKey() {
return _privKey;
}
public SigningPublicKey getSigningPublicKey() {
return _signingPubKey;
}
public SigningPrivateKey getSigningPrivateKey() {
return _signingPrivKey;
}
public int hashCode() {
return DataHelper.hashCode(_pubKey) + 7 * DataHelper.hashCode(_privKey) + 7 * 7
* DataHelper.hashCode(_signingPubKey) + 7 * 7 * 7 * DataHelper.hashCode(_signingPrivKey);
}
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof LeaseInfo)) return false;
LeaseInfo li = (LeaseInfo) obj;
return DataHelper.eq(_pubKey, li.getPublicKey()) && DataHelper.eq(_privKey, li.getPrivateKey())
&& DataHelper.eq(_signingPubKey, li.getSigningPublicKey())
&& DataHelper.eq(_signingPrivKey, li.getSigningPrivateKey());
}
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -8,7 +9,8 @@ package net.i2p.client;
*
*/
import net.i2p.data.i2cp.*;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.SessionStatusMessage;
/**
* Handle I2CP SessionStatusMessagese from the router, updating the session as
@@ -20,26 +22,27 @@ class SessionStatusMessageHandler extends HandlerImpl {
public SessionStatusMessageHandler() {
super(SessionStatusMessage.MESSAGE_TYPE);
}
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
_log.debug("Handle message " + message);
SessionStatusMessage msg = (SessionStatusMessage)message;
SessionStatusMessage msg = (SessionStatusMessage) message;
session.setSessionId(msg.getSessionId());
switch (msg.getStatus()) {
case SessionStatusMessage.STATUS_CREATED:
_log.info("Session created successfully");
break;
case SessionStatusMessage.STATUS_DESTROYED:
_log.info("Session destroyed");
session.destroySession();
break;
case SessionStatusMessage.STATUS_INVALID:
session.destroySession();
break;
case SessionStatusMessage.STATUS_UPDATED:
_log.info("Session status updated");
break;
default:
_log.warn("Unknown session status sent: " + msg.getStatus());
case SessionStatusMessage.STATUS_CREATED:
_log.info("Session created successfully");
break;
case SessionStatusMessage.STATUS_DESTROYED:
_log.info("Session destroyed");
session.destroySession();
break;
case SessionStatusMessage.STATUS_INVALID:
session.destroySession();
break;
case SessionStatusMessage.STATUS_UPDATED:
_log.info("Session status updated");
break;
default:
_log.warn("Unknown session status sent: " + msg.getStatus());
}
return;
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -8,7 +9,8 @@ package net.i2p.client;
*
*/
import net.i2p.data.i2cp.*;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.SetDateMessage;
import net.i2p.util.Clock;
/**
@@ -20,10 +22,11 @@ class SetDateMessageHandler extends HandlerImpl {
public SetDateMessageHandler() {
super(SetDateMessage.MESSAGE_TYPE);
}
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
_log.debug("Handle message " + message);
SetDateMessage msg = (SetDateMessage)message;
Clock.getInstance().setNow(msg.getDate().getTime());
session.dateUpdated();
SetDateMessage msg = (SetDateMessage) message;
Clock.getInstance().setNow(msg.getDate().getTime());
session.dateUpdated();
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -30,9 +31,9 @@ public class TestClient implements I2PSessionListener {
private static Destination _dest1;
private static Destination _dest2;
private boolean _stillRunning;
public void runTest(String keyfile, boolean isDest1) {
_stillRunning = true;
_stillRunning = true;
try {
I2PClient client = I2PClientFactory.createClient();
File file = new File(keyfile);
@@ -54,68 +55,91 @@ public class TestClient implements I2PSessionListener {
options.setProperty(I2PClient.PROP_TCP_PORT, System.getProperty(I2PClient.PROP_TCP_PORT));
I2PSession session = client.createSession(new FileInputStream(file), options);
session.setSessionListener(this);
_log.debug("Before connect...");
session.setSessionListener(this);
_log.debug("Before connect...");
session.connect();
_log.debug("Connected");
_log.debug("Connected");
// wait until the other one is connected
while ( (_dest1 == null) || (_dest2 == null) )
try { Thread.sleep(500); } catch (InterruptedException ie) {}
while ((_dest1 == null) || (_dest2 == null))
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
if (isDest1) {
Destination otherD = (isDest1 ? _dest2 : _dest1);
boolean accepted = session.sendMessage(otherD, ("Hello other side. I am" + (isDest1 ? "" : " NOT") + " dest1").getBytes());
} else {
while (_stillRunning) {
try {
_log.debug("waiting for a message...");
Thread.sleep(1000);
} catch (InterruptedException ie) {}
}
try { Thread.sleep(5000); } catch (InterruptedException ie) {}
System.exit(0);
}
if (isDest1) {
Destination otherD = (isDest1 ? _dest2 : _dest1);
boolean accepted = session
.sendMessage(
otherD,
("Hello other side. I am" + (isDest1 ? "" : " NOT") + " dest1")
.getBytes());
} else {
while (_stillRunning) {
try {
_log.debug("waiting for a message...");
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
}
try {
Thread.sleep(5000);
} catch (InterruptedException ie) {
}
System.exit(0);
}
//session.destroySession();
} catch (Exception e) {
_log.error("Error running the test for isDest1? " + isDest1, e);
}
}
public static void main(String args[]) {
doTest();
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {}
doTest();
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException ie) {
}
}
static void doTest() {
Thread test1 = new I2PThread(new Runnable() { public void run() { (new TestClient()).runTest("test1.keyfile", true); } } );
Thread test2 = new I2PThread(new Runnable() { public void run() { (new TestClient()).runTest("test2.keyfile", false); } } );
Thread test1 = new I2PThread(new Runnable() {
public void run() {
(new TestClient()).runTest("test1.keyfile", true);
}
});
Thread test2 = new I2PThread(new Runnable() {
public void run() {
(new TestClient()).runTest("test2.keyfile", false);
}
});
test1.start();
test2.start();
_log.debug("Test threads started");
}
public void disconnected(I2PSession session) {
_log.debug("Disconnected");
_stillRunning = false;
_log.debug("Disconnected");
_stillRunning = false;
}
public void errorOccurred(I2PSession session, String message, Throwable error) {
_log.debug("Error occurred: " + message, error);
_log.debug("Error occurred: " + message, error);
}
public void messageAvailable(I2PSession session, int msgId, long size) {
_log.debug("Message available for us! id = " + msgId + " of size " + size);
try {
byte msg[] = session.receiveMessage(msgId);
_log.debug("Content of message " + msgId+ ":\n"+new String(msg));
_stillRunning = false;
} catch (I2PSessionException ise) {
_log.error("Error fetching available message", ise);
}
_log.debug("Message available for us! id = " + msgId + " of size " + size);
try {
byte msg[] = session.receiveMessage(msgId);
_log.debug("Content of message " + msgId + ":\n" + new String(msg));
_stillRunning = false;
} catch (I2PSessionException ise) {
_log.error("Error fetching available message", ise);
}
}
public void reportAbuse(I2PSession session, int severity) {
_log.debug("Abuse reported of severity " + severity);
_log.debug("Abuse reported of severity " + severity);
}
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
@@ -27,9 +28,11 @@ public class TestServer implements Runnable {
private final static Log _log = new Log(TestServer.class);
private ServerSocket _socket;
public static int LISTEN_PORT = 7654;
protected void setPort(int port) { LISTEN_PORT = port; }
protected void setPort(int port) {
LISTEN_PORT = port;
}
/**
* Start up the socket listener, listens for connections, and
* fires those connections off via {@link #runConnection runConnection}.
@@ -38,47 +41,49 @@ public class TestServer implements Runnable {
*
*/
public void runServer() {
try {
_socket = new ServerSocket(LISTEN_PORT);
} catch (IOException ioe) {
_log.error("Error listening", ioe);
return;
}
while (true) {
try {
Socket socket = _socket.accept();
runConnection(socket);
} catch (IOException ioe) {
_log.error("Server error accepting", ioe);
}
}
try {
_socket = new ServerSocket(LISTEN_PORT);
} catch (IOException ioe) {
_log.error("Error listening", ioe);
return;
}
while (true) {
try {
Socket socket = _socket.accept();
runConnection(socket);
} catch (IOException ioe) {
_log.error("Server error accepting", ioe);
}
}
}
/**
* Handle the connection by passing it off to a ConnectionRunner
*
*/
protected void runConnection(Socket socket) throws IOException {
ConnectionRunner runner = new ConnectionRunner(socket);
runner.doYourThing();
ConnectionRunner runner = new ConnectionRunner(socket);
runner.doYourThing();
}
public void run() { runServer(); }
public void run() {
runServer();
}
/**
* Fire up the router
*/
*/
public static void main(String args[]) {
if (args.length == 1) {
} else if (args.length == 2) {
try {
LISTEN_PORT = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
_log.error("Invalid port number specified (" + args[1] + "), using " + LISTEN_PORT, nfe);
}
}
TestServer server = new TestServer();
Thread t = new I2PThread(server);
t.start();
if (args.length == 1) {
} else if (args.length == 2) {
try {
LISTEN_PORT = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
_log.error("Invalid port number specified (" + args[1] + "), using " + LISTEN_PORT, nfe);
}
}
TestServer server = new TestServer();
Thread t = new I2PThread(server);
t.start();
}
}

View File

@@ -1,23 +1,23 @@
/*
* free (adj.): unencumbered; not under the control of others
* Written by mihi in 2004 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*/
package net.i2p.client.naming;
import net.i2p.data.Destination;
/**
* A Dummy naming service that can only handle base64 destinations.
*/
class DummyNamingService extends NamingService {
public Destination lookup(String hostname) {
return lookupBase64(hostname);
}
public String reverseLookup(Destination dest) {
return null;
}
}
/*
* free (adj.): unencumbered; not under the control of others
* Written by mihi in 2004 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*/
package net.i2p.client.naming;
import net.i2p.data.Destination;
/**
* A Dummy naming service that can only handle base64 destinations.
*/
class DummyNamingService extends NamingService {
public Destination lookup(String hostname) {
return lookupBase64(hostname);
}
public String reverseLookup(Destination dest) {
return null;
}
}

View File

@@ -25,42 +25,44 @@ public class HostsTxtNamingService extends NamingService {
* given file for hostname=destKey values when resolving names
*/
public final static String PROP_HOSTS_FILE = "i2p.hostsfile";
/** default hosts.txt filename */
public final static String DEFAULT_HOSTS_FILE = "hosts.txt";
private final static Log _log = new Log(HostsTxtNamingService.class);
public Destination lookup(String hostname) {
// Try to look it up in hosts.txt
// Reload file each time to catch changes.
// (and it's easier :P
String hostsfile=System.getProperty(PROP_HOSTS_FILE,
DEFAULT_HOSTS_FILE );
Properties hosts=new Properties();
FileInputStream fis = null;
try {
File f = new File(hostsfile);
if(f.canRead()) {
fis = new FileInputStream(f);
hosts.load(fis);
} else {
_log.error("Hosts file " + hostsfile + " does not exist.");
}
} catch (Exception ioe) {
_log.error("Error loading hosts file " + hostsfile, ioe );
} finally {
if (fis != null) try {fis.close();} catch (IOException ioe) {}
}
String res = hosts.getProperty(hostname);
// If we can't find name in hosts, assume it's a key.
if ((res == null) || (res.trim().length() == 0)) {
res = hostname;
}
return lookupBase64(res);
// Try to look it up in hosts.txt
// Reload file each time to catch changes.
// (and it's easier :P
String hostsfile = System.getProperty(PROP_HOSTS_FILE, DEFAULT_HOSTS_FILE);
Properties hosts = new Properties();
FileInputStream fis = null;
try {
File f = new File(hostsfile);
if (f.canRead()) {
fis = new FileInputStream(f);
hosts.load(fis);
} else {
_log.error("Hosts file " + hostsfile + " does not exist.");
}
} catch (Exception ioe) {
_log.error("Error loading hosts file " + hostsfile, ioe);
} finally {
if (fis != null) try {
fis.close();
} catch (IOException ioe) {
}
}
String res = hosts.getProperty(hostname);
// If we can't find name in hosts, assume it's a key.
if ((res == null) || (res.trim().length() == 0)) {
res = hostname;
}
return lookupBase64(res);
}
public String reverseLookup(Destination dest) {
return null;
}
}
return null;
}
}

View File

@@ -19,9 +19,8 @@ public abstract class NamingService {
private final static Log _log = new Log(NamingService.class);
private static final String PROP_IMPL = "i2p.naming.impl";
private static final String DEFAULT_IMPL=
"net.i2p.client.naming.HostsTxtNamingService";
private static final String DEFAULT_IMPL = "net.i2p.client.naming.HostsTxtNamingService";
/**
* Look up a host name.
* @return the Destination for this host name, or
@@ -43,19 +42,18 @@ public abstract class NamingService {
* implementations.
*/
protected Destination lookupBase64(String hostname) {
try {
Destination result = new Destination();
result.fromBase64(hostname);
return result;
} catch (DataFormatException dfe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error translating [" + hostname + "]", dfe);
return null;
}
try {
Destination result = new Destination();
result.fromBase64(hostname);
return result;
} catch (DataFormatException dfe) {
if (_log.shouldLog(Log.WARN)) _log.warn("Error translating [" + hostname + "]", dfe);
return null;
}
}
private static NamingService instance = null;
/**
* Get a naming service instance. This method ensures that there
* will be only one naming service instance (singleton) as well as
@@ -63,16 +61,15 @@ public abstract class NamingService {
* property.
*/
public static synchronized NamingService getInstance() {
if (instance == null) {
String impl = System.getProperty(PROP_IMPL,
DEFAULT_IMPL);
try {
instance = (NamingService) Class.forName(impl).newInstance();
} catch (Exception ex) {
_log.error("Cannot loadNaming service implementation", ex);
instance = new DummyNamingService(); // fallback
}
}
return instance;
if (instance == null) {
String impl = System.getProperty(PROP_IMPL, DEFAULT_IMPL);
try {
instance = (NamingService) Class.forName(impl).newInstance();
} catch (Exception ex) {
_log.error("Cannot loadNaming service implementation", ex);
instance = new DummyNamingService(); // fallback
}
}
return instance;
}
}
}