forked from I2P_Developers/i2p.i2p
beginning of format, updated imports. (shendaras)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user