forked from I2P_Developers/i2p.i2p
Compare commits
51 Commits
i2p_0_5_0_
...
i2p_0_6
Author | SHA1 | Date | |
---|---|---|---|
5c1f968afa | |||
aaaf437d62 | |||
a8a866b5f6 | |||
aeb8f02269 | |||
45767360ab | |||
3563aa2e4d | |||
843d5b625a | |||
0f8ede85ca | |||
9267d7cae2 | |||
dade5a981b | |||
f873cba27e | |||
108dec53a5 | |||
e9592ed400 | |||
4c230522a2 | |||
16bd19c6dc | |||
b4b6d49d34 | |||
9d5f16a889 | |||
51c492b842 | |||
d3380228ac | |||
ad47bf5da3 | |||
76e8631e31 | |||
f688b9112d | |||
18d3f5d25d | |||
440cf2c983 | |||
adeb09576a | |||
fd52bcf8cd | |||
c2696bba00 | |||
fef9d57483 | |||
c250692ef0 | |||
2a6024e196 | |||
835662b3c9 | |||
6b5b880ab6 | |||
3de23d4206 | |||
ea82f2a8cc | |||
b5ad7642bc | |||
0fbe84e9f0 | |||
8063889d23 | |||
6e1ac8e173 | |||
1b0bb5ea19 | |||
4ce51261f1 | |||
6e34d9b73e | |||
6e01637400 | |||
9a96798f9f | |||
c9db6f87d1 | |||
567ce84e1e | |||
cde7ac7e52 | |||
b2f0d17e94 | |||
dae6be14b7 | |||
20cec857d2 | |||
739f694cfe | |||
84779002fb |
@ -87,6 +87,7 @@ public class AddressBook {
|
||||
this.location = subscription.getLocation();
|
||||
|
||||
try {
|
||||
// EepGet get = new EepGet(I2PAppContext.getGlobalContext(), true, )
|
||||
URL url = new URL(subscription.getLocation());
|
||||
HttpURLConnection connection = (HttpURLConnection) url
|
||||
.openConnection();
|
||||
|
@ -1146,6 +1146,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
}
|
||||
|
||||
private String getPrefix() { return '[' + _tunnelId + "]: "; }
|
||||
|
||||
public I2PAppContext getContext() { return _context; }
|
||||
|
||||
/**
|
||||
* Call this whenever we lose touch with the router involuntarily (aka the router
|
||||
|
@ -110,7 +110,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
}
|
||||
}
|
||||
if (sockMgr == null) {
|
||||
_log.log(Log.CRIT, "Unable to create socket manager");
|
||||
_log.log(Log.CRIT, "Unable to create socket manager (our own? " + ownDest + ")");
|
||||
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
|
@ -35,85 +35,66 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privData, l, notifyThis, tunnel);
|
||||
_spoofHost = spoofHost;
|
||||
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
|
||||
}
|
||||
|
||||
public I2PTunnelHTTPServer(InetAddress host, int port, File privkey, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privkey, privkeyname, l, notifyThis, tunnel);
|
||||
_spoofHost = spoofHost;
|
||||
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
|
||||
}
|
||||
|
||||
public I2PTunnelHTTPServer(InetAddress host, int port, InputStream privData, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privData, privkeyname, l, notifyThis, tunnel);
|
||||
_spoofHost = spoofHost;
|
||||
_spoofHost = spoofHost;
|
||||
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
I2PServerSocket i2pss = sockMgr.getServerSocket();
|
||||
while (true) {
|
||||
I2PSocket i2ps = i2pss.accept();
|
||||
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
|
||||
I2PThread t = new I2PThread(new Handler(i2ps));
|
||||
t.start();
|
||||
}
|
||||
} catch (I2PException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
} catch (IOException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async handler to keep .accept() from blocking too long.
|
||||
* todo: replace with a thread pool so we dont get overrun by threads if/when
|
||||
* receiving a lot of connection requests concurrently.
|
||||
* Called by the thread pool of I2PSocket handlers
|
||||
*
|
||||
*/
|
||||
private class Handler implements Runnable {
|
||||
private I2PSocket _handleSocket;
|
||||
public Handler(I2PSocket socket) {
|
||||
_handleSocket = socket;
|
||||
}
|
||||
public void run() {
|
||||
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
|
||||
long afterSocket = -1;
|
||||
|
||||
//local is fast, so synchronously. Does not need that many
|
||||
//threads.
|
||||
protected void blockingHandle(I2PSocket socket) {
|
||||
long afterAccept = getTunnel().getContext().clock().now();
|
||||
long afterSocket = -1;
|
||||
//local is fast, so synchronously. Does not need that many
|
||||
//threads.
|
||||
try {
|
||||
socket.setReadTimeout(readTimeout);
|
||||
String modifiedHeader = getModifiedHeader(socket);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Modified header: [" + modifiedHeader + "]");
|
||||
|
||||
Socket s = new Socket(remoteHost, remotePort);
|
||||
afterSocket = getTunnel().getContext().clock().now();
|
||||
new I2PTunnelRunner(s, socket, slock, null, modifiedHeader.getBytes(), null);
|
||||
} catch (SocketException ex) {
|
||||
try {
|
||||
_handleSocket.setReadTimeout(readTimeout);
|
||||
String modifiedHeader = getModifiedHeader();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Modified header: [" + modifiedHeader + "]");
|
||||
|
||||
Socket s = new Socket(remoteHost, remotePort);
|
||||
afterSocket = I2PAppContext.getGlobalContext().clock().now();
|
||||
new I2PTunnelRunner(s, _handleSocket, slock, null, modifiedHeader.getBytes(), null);
|
||||
} catch (SocketException ex) {
|
||||
try {
|
||||
_handleSocket.close();
|
||||
} catch (IOException ioe) {
|
||||
socket.close();
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error while closing the received i2p con", ex);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
}
|
||||
|
||||
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
|
||||
long timeToHandle = afterHandle - afterAccept;
|
||||
if (timeToHandle > 1000)
|
||||
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: "
|
||||
+ (afterSocket-afterAccept) + "]");
|
||||
}
|
||||
private String getModifiedHeader() throws IOException {
|
||||
InputStream in = _handleSocket.getInputStream();
|
||||
|
||||
StringBuffer command = new StringBuffer(128);
|
||||
Properties headers = readHeaders(in, command);
|
||||
headers.setProperty("Host", _spoofHost);
|
||||
headers.setProperty("Connection", "close");
|
||||
return formatHeaders(headers, command);
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error while receiving the new HTTP request", ex);
|
||||
}
|
||||
|
||||
long afterHandle = getTunnel().getContext().clock().now();
|
||||
long timeToHandle = afterHandle - afterAccept;
|
||||
getTunnel().getContext().statManager().addRateData("i2ptunnel.httpserver.blockingHandleTime", timeToHandle, 0);
|
||||
if ( (timeToHandle > 1000) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
|
||||
}
|
||||
|
||||
private String getModifiedHeader(I2PSocket handleSocket) throws IOException {
|
||||
InputStream in = handleSocket.getInputStream();
|
||||
|
||||
StringBuffer command = new StringBuffer(128);
|
||||
Properties headers = readHeaders(in, command);
|
||||
headers.setProperty("Host", _spoofHost);
|
||||
headers.setProperty("Connection", "close");
|
||||
return formatHeaders(headers, command);
|
||||
}
|
||||
|
||||
private String formatHeaders(Properties headers, StringBuffer command) {
|
||||
|
@ -160,57 +160,71 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
I2PServerSocket i2pss = sockMgr.getServerSocket();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
I2PThread handler = new I2PThread(new Handler(i2pss), "Handle Server " + i);
|
||||
handler.start();
|
||||
}
|
||||
/*
|
||||
while (true) {
|
||||
I2PSocket i2ps = i2pss.accept();
|
||||
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
|
||||
I2PThread t = new I2PThread(new Handler(i2ps));
|
||||
t.start();
|
||||
}
|
||||
} catch (I2PException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
} catch (IOException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Async handler to keep .accept() from blocking too long.
|
||||
* todo: replace with a thread pool so we dont get overrun by threads if/when
|
||||
* receiving a lot of connection requests concurrently.
|
||||
* minor thread pool to pull off the accept() concurrently. there are still lots
|
||||
* (and lots) of wasted threads within the I2PTunnelRunner, but its a start
|
||||
*
|
||||
*/
|
||||
private class Handler implements Runnable {
|
||||
private I2PSocket _handleSocket;
|
||||
public Handler(I2PSocket socket) {
|
||||
_handleSocket = socket;
|
||||
private I2PServerSocket _serverSocket;
|
||||
public Handler(I2PServerSocket serverSocket) {
|
||||
_serverSocket = serverSocket;
|
||||
}
|
||||
public void run() {
|
||||
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
|
||||
long afterSocket = -1;
|
||||
//local is fast, so synchronously. Does not need that many
|
||||
//threads.
|
||||
try {
|
||||
_handleSocket.setReadTimeout(readTimeout);
|
||||
Socket s = new Socket(remoteHost, remotePort);
|
||||
afterSocket = I2PAppContext.getGlobalContext().clock().now();
|
||||
new I2PTunnelRunner(s, _handleSocket, slock, null, null);
|
||||
} catch (SocketException ex) {
|
||||
while (open) {
|
||||
try {
|
||||
_handleSocket.close();
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error while closing the received i2p con", ex);
|
||||
blockingHandle(_serverSocket.accept());
|
||||
} catch (I2PException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
return;
|
||||
} catch (IOException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
return;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
}
|
||||
|
||||
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
|
||||
long timeToHandle = afterHandle - afterAccept;
|
||||
if (timeToHandle > 1000)
|
||||
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
|
||||
}
|
||||
}
|
||||
|
||||
protected void blockingHandle(I2PSocket socket) {
|
||||
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
|
||||
long afterSocket = -1;
|
||||
//local is fast, so synchronously. Does not need that many
|
||||
//threads.
|
||||
try {
|
||||
socket.setReadTimeout(readTimeout);
|
||||
Socket s = new Socket(remoteHost, remotePort);
|
||||
afterSocket = I2PAppContext.getGlobalContext().clock().now();
|
||||
new I2PTunnelRunner(s, socket, slock, null, null);
|
||||
} catch (SocketException ex) {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error while closing the received i2p con", ex);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
}
|
||||
|
||||
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
|
||||
long timeToHandle = afterHandle - afterAccept;
|
||||
if (timeToHandle > 1000)
|
||||
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,445 +0,0 @@
|
||||
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
|
||||
* (c) 2003 - 2004 mihi
|
||||
*/
|
||||
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Quick and dirty socket listener to control an I2PTunnel.
|
||||
* Basically run this class as TunnelManager [listenHost] [listenPort] and
|
||||
* then send it commands on that port. Commands are one shot deals -
|
||||
* Send a command + newline, get a response plus newline, then get disconnected.
|
||||
* <p />
|
||||
* <b>Implemented commands:</b>
|
||||
* <pre>
|
||||
* -------------------------------------------------
|
||||
* lookup <name>\n
|
||||
* --
|
||||
* <base64 of the destination>\n
|
||||
* or
|
||||
* <error message, usually 'Unknown host'>\n
|
||||
*
|
||||
* Lookup the public key of a named destination (i.e. listed in hosts.txt)
|
||||
* -------------------------------------------------
|
||||
* genkey\n
|
||||
* --
|
||||
* <base64 of the destination>\t<base64 of private data>\n
|
||||
*
|
||||
* Generates a new public and private key pair
|
||||
* -------------------------------------------------
|
||||
* convertprivate <base64 of privkey>
|
||||
* --
|
||||
* <base64 of destination>\n
|
||||
* or
|
||||
* <error message>\n
|
||||
*
|
||||
* Returns the destination (pubkey) of a given private key.
|
||||
* -------------------------------------------------
|
||||
* listen_on <ip>\n
|
||||
* --
|
||||
* ok\n
|
||||
* or
|
||||
* error\n
|
||||
*
|
||||
* Sets the ip address clients will listen on. By default this is the
|
||||
* localhost (127.0.0.1)
|
||||
* -------------------------------------------------
|
||||
* openclient <listenPort> <peer>[ <sharedClient>]\n
|
||||
* --
|
||||
* ok [<jobId>]\n
|
||||
* or
|
||||
* ok <listenPort> [<jobId>]\n
|
||||
* or
|
||||
* error\n
|
||||
*
|
||||
* Open a tunnel on the given <listenport> to the destination specified
|
||||
* by <peer>. If <listenPort> is 0 a free port is picked and returned in
|
||||
* the reply message. Otherwise the short reply message is used.
|
||||
* Peer can be the base64 of the destination, a file with the public key
|
||||
* specified as 'file:<filename>' or the name of a destination listed in
|
||||
* hosts.txt. The <jobId> returned together with "ok" and <listenport> can
|
||||
* later be used as argument for the "close" command.
|
||||
* <sharedClient> indicates if this httpclient shares tunnels with other
|
||||
* clients or not (just use 'true' and 'false'
|
||||
* -------------------------------------------------
|
||||
* openhttpclient <listenPort> [<sharedClient>] [<proxy>]\n
|
||||
* --
|
||||
* ok [<jobId>]\n
|
||||
* or
|
||||
* ok <listenPort> [<jobId>]\n
|
||||
* or
|
||||
* error\n
|
||||
*
|
||||
* Open an HTTP proxy through the I2P on the given
|
||||
* <listenport>. <proxy> (optional) specifies a
|
||||
* destination to be used as an outbound proxy, to access normal WWW
|
||||
* sites out of the .i2p domain. If <listenPort> is 0 a free
|
||||
* port is picked and returned in the reply message. Otherwise the
|
||||
* short reply message is used. <proxy> can be the base64 of the
|
||||
* destination, a file with the public key specified as
|
||||
* 'file:<filename>' or the name of a destination listed in
|
||||
* hosts.txt. The <jobId> returned together with "ok" and
|
||||
* <listenport> can later be used as argument for the "close"
|
||||
* command.
|
||||
* <sharedClient> indicates if this httpclient shares tunnels with other
|
||||
* clients or not (just use 'true' and 'false'
|
||||
* -------------------------------------------------
|
||||
* opensockstunnel <listenPort>\n
|
||||
* --
|
||||
* ok [<jobId>]\n
|
||||
* or
|
||||
* ok <listenPort> [<jobId>]\n
|
||||
* or
|
||||
* error\n
|
||||
*
|
||||
* Open an SOCKS tunnel through the I2P on the given
|
||||
* <listenport>. If <listenPort> is 0 a free port is
|
||||
* picked and returned in the reply message. Otherwise the short
|
||||
* reply message is used. The <jobId> returned together with
|
||||
* "ok" and <listenport> can later be used as argument for the
|
||||
* "close" command.
|
||||
* -------------------------------------------------
|
||||
* openserver <serverHost> <serverPort> <serverKeys>\n
|
||||
* --
|
||||
* ok [<jobId>]\n
|
||||
* or
|
||||
* error\n
|
||||
*
|
||||
* Starts receiving traffic for the destination specified by <serverKeys>
|
||||
* and forwards it to the <serverPort> of <serverHost>.
|
||||
* <serverKeys> is the base 64 encoded private key set of the local
|
||||
* destination. The <joId> returned together with "ok" can later be used
|
||||
* as argument for the "close" command.
|
||||
* -------------------------------------------------
|
||||
* close [forced] <jobId>\n
|
||||
* or
|
||||
* close [forced] all\n
|
||||
* --
|
||||
* ok\n
|
||||
* or
|
||||
* error\n
|
||||
*
|
||||
* Closes the job specified by <jobId> or all jobs. Use the list command
|
||||
* for a list of running jobs.
|
||||
* Normally a connection job is not closed when it still has an active
|
||||
* connection. Use the optional 'forced' keyword to close connections
|
||||
* regardless of their use.
|
||||
* -------------------------------------------------
|
||||
* list\n
|
||||
* --
|
||||
* Example output:
|
||||
*
|
||||
* [0] i2p.dnsalias.net/69.55.226.145:5555 <- C:\i2pKeys\squidPriv
|
||||
* [1] 8767 -> HTTPClient
|
||||
* [2] 7575 -> file:C:\i2pKeys\squidPub
|
||||
* [3] 5252 -> sCcSANIO~f4AQtCNI1BvDp3ZBS~9Ag5O0k0Msm7XBWWz5eOnZWL3MQ-2rxlesucb9XnpASGhWzyYNBpWAfaIB3pux1J1xujQLOwscMIhm7T8BP76Ly5jx6BLZCYrrPj0BI0uV90XJyT~4UyQgUlC1jzFQdZ9HDgBPJDf1UI4-YjIwEHuJgdZynYlQ1oUFhgno~HhcDByXO~PDaO~1JDMDbBEfIh~v6MgmHp-Xchod1OfKFrxFrzHgcJbn7E8edTFjZA6JCi~DtFxFelQz1lSBd-QB1qJnA0g-pVL5qngNUojXJCXs4qWcQ7ICLpvIc-Fpfj-0F1gkVlGDSGkb1yLH3~8p4czYgR3W5D7OpwXzezz6clpV8kmbd~x2SotdWsXBPRhqpewO38coU4dJG3OEUbuYmdN~nJMfWbmlcM1lXzz2vBsys4sZzW6dV3hZnbvbfxNTqbdqOh-KXi1iAzXv7CVTun0ubw~CfeGpcAqutC5loRUq7Mq62ngOukyv8Z9AAAA
|
||||
*
|
||||
* Lists descriptions of all running jobs. The exact format of the
|
||||
* description depends on the type of job.
|
||||
* -------------------------------------------------
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @deprecated this isn't run by default, and no one seems to use it, and has
|
||||
* lots of things to maintain. so, at some point this may dissapear
|
||||
* unless someone pipes up ;)
|
||||
*/
|
||||
public class TunnelManager implements Runnable {
|
||||
private final static Log _log = new Log(TunnelManager.class);
|
||||
private I2PTunnel _tunnel;
|
||||
private ServerSocket _socket;
|
||||
private boolean _keepAccepting;
|
||||
|
||||
public TunnelManager(int listenPort) {
|
||||
this(null, listenPort);
|
||||
}
|
||||
|
||||
public TunnelManager(String listenHost, int listenPort) {
|
||||
_tunnel = new I2PTunnel();
|
||||
_keepAccepting = true;
|
||||
try {
|
||||
if (listenHost != null) {
|
||||
_socket = new ServerSocket(listenPort, 0, InetAddress.getByName(listenHost));
|
||||
_log.info("Listening for tunnel management clients on " + listenHost + ":" + listenPort);
|
||||
} else {
|
||||
_socket = new ServerSocket(listenPort);
|
||||
_log.info("Listening for tunnel management clients on localhost:" + listenPort);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
_log.error("Error starting up tunnel management listener on " + listenPort, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
int port = 7676;
|
||||
String host = null;
|
||||
if (args.length == 1) {
|
||||
try {
|
||||
port = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_log.error("Usage: TunnelManager [host] [port]");
|
||||
return;
|
||||
}
|
||||
} else if (args.length == 2) {
|
||||
host = args[0];
|
||||
try {
|
||||
port = Integer.parseInt(args[1]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_log.error("Usage: TunnelManager [host] [port]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TunnelManager mgr = new TunnelManager(host, port);
|
||||
Thread t = new I2PThread(mgr, "Listener");
|
||||
t.start();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if (_socket == null) {
|
||||
_log.error("Unable to start listening, since the socket was not bound. Already running?");
|
||||
return;
|
||||
}
|
||||
_log.debug("Running");
|
||||
try {
|
||||
while (_keepAccepting) {
|
||||
Socket socket = _socket.accept();
|
||||
_log.debug("Client accepted");
|
||||
if (socket != null) {
|
||||
Thread t = new I2PThread(new TunnelManagerClientRunner(this, socket));
|
||||
t.setName("TunnelManager Client");
|
||||
t.setPriority(I2PThread.MIN_PRIORITY);
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error accepting connections", ioe);
|
||||
} catch (Exception e) {
|
||||
_log.error("Other error?!", e);
|
||||
} finally {
|
||||
if (_socket != null) try {
|
||||
_socket.close();
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
|
||||
public void error(String msg, OutputStream out) throws IOException {
|
||||
out.write(msg.getBytes());
|
||||
out.write('\n');
|
||||
}
|
||||
|
||||
public void processQuit(OutputStream out) throws IOException {
|
||||
out.write("Nice try".getBytes());
|
||||
out.write('\n');
|
||||
}
|
||||
|
||||
public void processList(OutputStream out) throws IOException {
|
||||
BufferLogger buf = new BufferLogger();
|
||||
long startCommand = Clock.getInstance().now();
|
||||
_tunnel.runCommand("list", buf);
|
||||
Object obj = _tunnel.waitEventValue("listDone");
|
||||
long endCommand = Clock.getInstance().now();
|
||||
String str = buf.getBuffer();
|
||||
_log.debug("ListDone complete after " + (endCommand - startCommand) + "ms: [" + str + "]");
|
||||
out.write(str.getBytes());
|
||||
out.write('\n');
|
||||
buf.ignoreFurtherActions();
|
||||
}
|
||||
|
||||
public void processListenOn(String ip, OutputStream out) throws IOException {
|
||||
BufferLogger buf = new BufferLogger();
|
||||
_tunnel.runCommand("listen_on " + ip, buf);
|
||||
String status = (String) _tunnel.waitEventValue("listen_onResult");
|
||||
out.write((status + "\n").getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* "lookup <name>" returns with the result in base64, else "Unknown host" [or something like that],
|
||||
* then a newline.
|
||||
*
|
||||
*/
|
||||
public void processLookup(String name, OutputStream out) throws IOException {
|
||||
BufferLogger buf = new BufferLogger();
|
||||
_tunnel.runCommand("lookup " + name, buf);
|
||||
String rv = (String) _tunnel.waitEventValue("lookupResult");
|
||||
out.write(rv.getBytes());
|
||||
out.write('\n');
|
||||
buf.ignoreFurtherActions();
|
||||
}
|
||||
|
||||
public void processTestDestination(String destKey, OutputStream out) throws IOException {
|
||||
try {
|
||||
Destination d = new Destination();
|
||||
d.fromBase64(destKey);
|
||||
out.write("valid\n".getBytes());
|
||||
} catch (DataFormatException dfe) {
|
||||
out.write("invalid\n".getBytes());
|
||||
}
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public void processConvertPrivate(String priv, OutputStream out) throws IOException {
|
||||
try {
|
||||
Destination dest = new Destination();
|
||||
dest.fromBase64(priv);
|
||||
String str = dest.toBase64();
|
||||
out.write(str.getBytes());
|
||||
out.write('\n');
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.error("Error converting private data", dfe);
|
||||
out.write("Error converting private key\n".getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
public void processClose(String which, boolean forced, OutputStream out) throws IOException {
|
||||
BufferLogger buf = new BufferLogger();
|
||||
_tunnel.runCommand((forced ? "close forced " : "close ") + which, buf);
|
||||
String str = (String) _tunnel.waitEventValue("closeResult");
|
||||
out.write((str + "\n").getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* "genkey" returns with the base64 of the destination, followed by a tab, then the base64 of that
|
||||
* destination's private keys, then a newline.
|
||||
*
|
||||
*/
|
||||
public void processGenKey(OutputStream out) throws IOException {
|
||||
BufferLogger buf = new BufferLogger();
|
||||
_tunnel.runCommand("gentextkeys", buf);
|
||||
String priv = (String) _tunnel.waitEventValue("privateKey");
|
||||
String pub = (String) _tunnel.waitEventValue("publicDestination");
|
||||
out.write((pub + "\t" + priv).getBytes());
|
||||
out.write('\n');
|
||||
buf.ignoreFurtherActions();
|
||||
}
|
||||
|
||||
public void processOpenClient(int listenPort, String peer, String sharedClient, OutputStream out) throws IOException {
|
||||
BufferLogger buf = new BufferLogger();
|
||||
_tunnel.runCommand("client " + listenPort + " " + peer + " " + sharedClient, buf);
|
||||
Integer taskId = (Integer) _tunnel.waitEventValue("clientTaskId");
|
||||
if (taskId.intValue() < 0) {
|
||||
out.write("error\n".getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
return;
|
||||
}
|
||||
String rv = (String) _tunnel.waitEventValue("openClientResult");
|
||||
if (rv.equals("error")) {
|
||||
out.write((rv + "\n").getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
return;
|
||||
}
|
||||
|
||||
if (listenPort != 0) {
|
||||
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
return;
|
||||
}
|
||||
Integer port = (Integer) _tunnel.waitEventValue("clientLocalPort");
|
||||
out.write((rv + " " + port.intValue() + " [" + taskId.intValue() + "]\n").getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
}
|
||||
|
||||
public void processOpenHTTPClient(int listenPort, String sharedClient, String proxy, OutputStream out) throws IOException {
|
||||
BufferLogger buf = new BufferLogger();
|
||||
_tunnel.runCommand("httpclient " + listenPort + " " + sharedClient + " " + proxy, buf);
|
||||
Integer taskId = (Integer) _tunnel.waitEventValue("httpclientTaskId");
|
||||
if (taskId.intValue() < 0) {
|
||||
out.write("error\n".getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
return;
|
||||
}
|
||||
String rv = (String) _tunnel.waitEventValue("openHTTPClientResult");
|
||||
if (rv.equals("error")) {
|
||||
out.write((rv + "\n").getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
return;
|
||||
}
|
||||
|
||||
if (listenPort != 0) {
|
||||
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
return;
|
||||
}
|
||||
Integer port = (Integer) _tunnel.waitEventValue("clientLocalPort");
|
||||
out.write((rv + " " + port.intValue() + " [" + taskId.intValue() + "]\n").getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
}
|
||||
|
||||
public void processOpenSOCKSTunnel(int listenPort, OutputStream out) throws IOException {
|
||||
BufferLogger buf = new BufferLogger();
|
||||
_tunnel.runCommand("sockstunnel " + listenPort, buf);
|
||||
Integer taskId = (Integer) _tunnel.waitEventValue("sockstunnelTaskId");
|
||||
if (taskId.intValue() < 0) {
|
||||
out.write("error\n".getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
return;
|
||||
}
|
||||
String rv = (String) _tunnel.waitEventValue("openSOCKSTunnelResult");
|
||||
if (rv.equals("error")) {
|
||||
out.write((rv + "\n").getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
return;
|
||||
}
|
||||
|
||||
if (listenPort != 0) {
|
||||
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
return;
|
||||
}
|
||||
Integer port = (Integer) _tunnel.waitEventValue("clientLocalPort");
|
||||
out.write((rv + " " + port.intValue() + " [" + taskId.intValue() + "]\n").getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
}
|
||||
|
||||
public void processOpenServer(String serverHost, int serverPort, String privateKeys, OutputStream out)
|
||||
throws IOException {
|
||||
BufferLogger buf = new BufferLogger();
|
||||
_tunnel.runCommand("textserver " + serverHost + " " + serverPort + " " + privateKeys, buf);
|
||||
Integer taskId = (Integer) _tunnel.waitEventValue("serverTaskId");
|
||||
if (taskId.intValue() < 0) {
|
||||
out.write("error\n".getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
return;
|
||||
}
|
||||
|
||||
String rv = (String) _tunnel.waitEventValue("openServerResult");
|
||||
|
||||
if (rv.equals("error")) {
|
||||
out.write((rv + "\n").getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
return;
|
||||
}
|
||||
|
||||
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
|
||||
buf.ignoreFurtherActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Frisbee.
|
||||
*
|
||||
*/
|
||||
public void unknownCommand(String command, OutputStream out) throws IOException {
|
||||
out.write("Unknown command: ".getBytes());
|
||||
out.write(command.getBytes());
|
||||
out.write("\n".getBytes());
|
||||
}
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
|
||||
* (c) 2003 - 2004 mihi
|
||||
*/
|
||||
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Runner thread that reads commands from the socket and fires off commands to
|
||||
* the TunnelManager
|
||||
*
|
||||
*/
|
||||
class TunnelManagerClientRunner implements Runnable {
|
||||
private final static Log _log = new Log(TunnelManagerClientRunner.class);
|
||||
private TunnelManager _mgr;
|
||||
private Socket _clientSocket;
|
||||
|
||||
public TunnelManagerClientRunner(TunnelManager mgr, Socket socket) {
|
||||
_clientSocket = socket;
|
||||
_mgr = mgr;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
_log.debug("Client running");
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(_clientSocket.getInputStream()));
|
||||
OutputStream out = _clientSocket.getOutputStream();
|
||||
|
||||
String cmd = reader.readLine();
|
||||
if (cmd != null) processCommand(cmd, out);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error processing client commands", ioe);
|
||||
} finally {
|
||||
if (_clientSocket != null) try {
|
||||
_clientSocket.close();
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
}
|
||||
_log.debug("Client closed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the command string and fire off the appropriate tunnelManager method,
|
||||
* sending the results to the output stream
|
||||
*/
|
||||
private void processCommand(String command, OutputStream out) throws IOException {
|
||||
_log.debug("Processing [" + command + "]");
|
||||
StringTokenizer tok = new StringTokenizer(command);
|
||||
if (!tok.hasMoreTokens()) {
|
||||
_mgr.unknownCommand(command, out);
|
||||
} else {
|
||||
String cmd = tok.nextToken();
|
||||
if ("quit".equalsIgnoreCase(cmd)) {
|
||||
_mgr.processQuit(out);
|
||||
} else if ("lookup".equalsIgnoreCase(cmd)) {
|
||||
if (tok.hasMoreTokens())
|
||||
_mgr.processLookup(tok.nextToken(), out);
|
||||
else
|
||||
_mgr.error("Usage: lookup <hostname>", out);
|
||||
} else if ("testdestination".equalsIgnoreCase(cmd)) {
|
||||
if (tok.hasMoreTokens())
|
||||
_mgr.processTestDestination(tok.nextToken(), out);
|
||||
else
|
||||
_mgr.error("Usage: testdestination <publicDestination>", out);
|
||||
} else if ("convertprivate".equalsIgnoreCase(cmd)) {
|
||||
if (tok.hasMoreTokens())
|
||||
_mgr.processConvertPrivate(tok.nextToken(), out);
|
||||
else
|
||||
_mgr.error("Usage: convertprivate <privateData>", out);
|
||||
} else if ("close".equalsIgnoreCase(cmd)) {
|
||||
if (tok.hasMoreTokens()) {
|
||||
String closeArg;
|
||||
if ((closeArg = tok.nextToken()).equals("forced")) {
|
||||
if (tok.hasMoreTokens()) {
|
||||
_mgr.processClose(tok.nextToken(), true, out);
|
||||
} else {
|
||||
_mgr.error("Usage: close [forced] <jobnumber>|all", out);
|
||||
}
|
||||
} else {
|
||||
_mgr.processClose(closeArg, false, out);
|
||||
}
|
||||
} else {
|
||||
_mgr.error("Usage: close [forced] <jobnumber>|all", out);
|
||||
}
|
||||
} else if ("genkey".equalsIgnoreCase(cmd)) {
|
||||
_mgr.processGenKey(out);
|
||||
} else if ("list".equalsIgnoreCase(cmd)) {
|
||||
_mgr.processList(out);
|
||||
} else if ("listen_on".equalsIgnoreCase(cmd)) {
|
||||
if (tok.hasMoreTokens()) {
|
||||
_mgr.processListenOn(tok.nextToken(), out);
|
||||
} else {
|
||||
_mgr.error("Usage: listen_on <ip>", out);
|
||||
}
|
||||
} else if ("openclient".equalsIgnoreCase(cmd)) {
|
||||
int listenPort = 0;
|
||||
String peer = null;
|
||||
String sharedClient = null;
|
||||
int numTokens = tok.countTokens();
|
||||
if (numTokens < 2 || numTokens > 3) {
|
||||
_mgr.error("Usage: openclient <listenPort> <peer> <sharedClient>", out);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
listenPort = Integer.parseInt(tok.nextToken());
|
||||
peer = tok.nextToken();
|
||||
if (tok.hasMoreTokens())
|
||||
sharedClient = tok.nextToken();
|
||||
else
|
||||
sharedClient = "true";
|
||||
_mgr.processOpenClient(listenPort, peer, sharedClient, out);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_mgr.error("Bad listen port", out);
|
||||
return;
|
||||
}
|
||||
} else if ("openhttpclient".equalsIgnoreCase(cmd)) {
|
||||
int listenPort = 0;
|
||||
String proxy = "squid.i2p";
|
||||
String sharedClient = "true";
|
||||
int numTokens = tok.countTokens();
|
||||
if (numTokens < 1 || numTokens > 3) {
|
||||
_mgr.error("Usage: openhttpclient <listenPort> [<sharedClient>] [<proxy>]", out);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
listenPort = Integer.parseInt(tok.nextToken());
|
||||
if (tok.hasMoreTokens()) {
|
||||
String val = tok.nextToken();
|
||||
if (tok.hasMoreTokens()) {
|
||||
sharedClient = val;
|
||||
proxy = tok.nextToken();
|
||||
} else {
|
||||
if ( ("true".equals(val)) || ("false".equals(val)) ) {
|
||||
sharedClient = val;
|
||||
} else {
|
||||
proxy = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
_mgr.processOpenHTTPClient(listenPort, sharedClient, proxy, out);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_mgr.error("Bad listen port", out);
|
||||
return;
|
||||
}
|
||||
} else if ("opensockstunnel".equalsIgnoreCase(cmd)) {
|
||||
int listenPort = 0;
|
||||
if (!tok.hasMoreTokens()) {
|
||||
_mgr.error("Usage: opensockstunnel <listenPort>", out);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String portStr = tok.nextToken();
|
||||
listenPort = Integer.parseInt(portStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_mgr.error("Bad listen port", out);
|
||||
return;
|
||||
}
|
||||
if (tok.hasMoreTokens()) {
|
||||
_mgr.error("Usage: opensockstunnel <listenport>", out);
|
||||
return;
|
||||
}
|
||||
_mgr.processOpenSOCKSTunnel(listenPort, out);
|
||||
} else if ("openserver".equalsIgnoreCase(cmd)) {
|
||||
int listenPort = 0;
|
||||
String serverHost = null;
|
||||
String serverKeys = null;
|
||||
if (!tok.hasMoreTokens()) {
|
||||
_mgr.error("Usage: openserver <serverHost> <serverPort> <serverKeys>", out);
|
||||
return;
|
||||
}
|
||||
serverHost = tok.nextToken();
|
||||
|
||||
if (!tok.hasMoreTokens()) {
|
||||
_mgr.error("Usage: openserver <serverHost> <serverPort> <serverKeys>", out);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String portStr = tok.nextToken();
|
||||
listenPort = Integer.parseInt(portStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_mgr.error("Bad listen port", out);
|
||||
return;
|
||||
}
|
||||
if (!tok.hasMoreTokens()) {
|
||||
_mgr.error("Usage: openserver <serverHost> <serverPort> <serverKeys>", out);
|
||||
return;
|
||||
}
|
||||
serverKeys = tok.nextToken();
|
||||
_mgr.processOpenServer(serverHost, listenPort, serverKeys, out);
|
||||
} else {
|
||||
_mgr.unknownCommand(command, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -93,7 +93,7 @@ if (curTunnel >= 0) {
|
||||
</select>
|
||||
|
||||
<b>others:</b>
|
||||
<input type="text" name="reachablyByOther" size="20" />
|
||||
<input type="text" name="reachableByOther" size="20" />
|
||||
<% } else if ("0.0.0.0".equals(clientInterface)) { %>
|
||||
<option value="127.0.0.1">Locally (127.0.0.1)</option>
|
||||
<option value="0.0.0.0" selected="true">Everyone (0.0.0.0)</option>
|
||||
@ -102,7 +102,7 @@ if (curTunnel >= 0) {
|
||||
</select>
|
||||
|
||||
<b>others:</b>
|
||||
<input type="text" name="reachablyByOther" size="20" />
|
||||
<input type="text" name="reachableByOther" size="20" />
|
||||
<% } else { %>
|
||||
<option value="127.0.0.1">Locally (127.0.0.1)</option>
|
||||
<option value="0.0.0.0">Everyone (0.0.0.0)</option>
|
||||
|
@ -15,6 +15,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -74,61 +75,67 @@ public class StreamSinkClient {
|
||||
} finally {
|
||||
if (fis == null) try { fis.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
|
||||
System.out.println("Send " + _sendSize + "KB to " + peer.calculateHash().toBase64());
|
||||
|
||||
try {
|
||||
I2PSocket sock = mgr.connect(peer);
|
||||
byte buf[] = new byte[32*1024];
|
||||
Random rand = new Random();
|
||||
OutputStream out = sock.getOutputStream();
|
||||
long beforeSending = System.currentTimeMillis();
|
||||
for (int i = 0; i < _sendSize; i+= 32) {
|
||||
rand.nextBytes(buf);
|
||||
out.write(buf);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Send " + _sendSize + "KB to " + peer.calculateHash().toBase64());
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
I2PSocket sock = mgr.connect(peer);
|
||||
byte buf[] = new byte[Math.min(32*1024, _sendSize*1024)];
|
||||
Random rand = new Random();
|
||||
OutputStream out = sock.getOutputStream();
|
||||
long beforeSending = System.currentTimeMillis();
|
||||
for (int i = 0; (_sendSize < 0) || (i < _sendSize); i+= buf.length/1024) {
|
||||
rand.nextBytes(buf);
|
||||
out.write(buf);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Wrote " + ((1+i*buf.length)/1024) + "/" + _sendSize + "KB");
|
||||
if (_writeDelay > 0) {
|
||||
try { Thread.sleep(_writeDelay); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
sock.close();
|
||||
long afterSending = System.currentTimeMillis();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Wrote " + (i+32) + "/" + _sendSize + "KB");
|
||||
if (_writeDelay > 0) {
|
||||
try { Thread.sleep(_writeDelay); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
sock.close();
|
||||
long afterSending = System.currentTimeMillis();
|
||||
System.out.println("Sent " + _sendSize + "KB in " + (afterSending-beforeSending) + "ms");
|
||||
} catch (InterruptedIOException iie) {
|
||||
_log.error("Timeout connecting to the peer", iie);
|
||||
return;
|
||||
} catch (NoRouteToHostException nrthe) {
|
||||
_log.error("Unable to connect to the peer", nrthe);
|
||||
return;
|
||||
} catch (ConnectException ce) {
|
||||
_log.error("Connection already dropped", ce);
|
||||
return;
|
||||
} catch (I2PException ie) {
|
||||
_log.error("Error connecting to the peer", ie);
|
||||
return;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("IO error sending", ioe);
|
||||
return;
|
||||
_log.debug("Sent " + _sendSize + "KB in " + (afterSending-beforeSending) + "ms");
|
||||
} catch (InterruptedIOException iie) {
|
||||
_log.error("Timeout connecting to the peer", iie);
|
||||
//return;
|
||||
} catch (NoRouteToHostException nrthe) {
|
||||
_log.error("Unable to connect to the peer", nrthe);
|
||||
//return;
|
||||
} catch (ConnectException ce) {
|
||||
_log.error("Connection already dropped", ce);
|
||||
//return;
|
||||
} catch (I2PException ie) {
|
||||
_log.error("Error connecting to the peer", ie);
|
||||
return;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("IO error sending", ioe);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire up the client. <code>Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile</code> <br />
|
||||
* Fire up the client. <code>Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile [concurrentSends]</code> <br />
|
||||
* <ul>
|
||||
* <li><b>sendSizeKB</b>: how many KB to send</li>
|
||||
* <li><b>sendSizeKB</b>: how many KB to send, or -1 for unlimited</li>
|
||||
* <li><b>writeDelayMs</b>: how long to wait between each .write (0 for no delay)</li>
|
||||
* <li><b>serverDestFile</b>: file containing the StreamSinkServer's binary Destination</li>
|
||||
* <li><b>concurrentSends</b>: how many concurrent threads should send to the server at once</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
StreamSinkClient client = null;
|
||||
int sendSizeKB = -1;
|
||||
int writeDelayMs = -1;
|
||||
int concurrent = 1;
|
||||
|
||||
switch (args.length) {
|
||||
case 3:
|
||||
case 3: // fall through
|
||||
case 4:
|
||||
try {
|
||||
sendSizeKB = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
@ -141,9 +148,13 @@ public class StreamSinkClient {
|
||||
System.err.println("Write delay ms invalid [" + args[1] + "]");
|
||||
return;
|
||||
}
|
||||
if (args.length == 4) {
|
||||
try { concurrent = Integer.parseInt(args[3]); } catch (NumberFormatException nfe) {}
|
||||
}
|
||||
client = new StreamSinkClient(sendSizeKB, writeDelayMs, args[2]);
|
||||
break;
|
||||
case 5:
|
||||
case 5: // fall through
|
||||
case 6:
|
||||
try {
|
||||
int port = Integer.parseInt(args[1]);
|
||||
sendSizeKB = Integer.parseInt(args[2]);
|
||||
@ -152,11 +163,26 @@ public class StreamSinkClient {
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.err.println("arg error");
|
||||
}
|
||||
if (args.length == 6) {
|
||||
try { concurrent = Integer.parseInt(args[5]); } catch (NumberFormatException nfe) {}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
System.out.println("Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile");
|
||||
System.out.println("Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile [concurrentSends]");
|
||||
}
|
||||
if (client != null) {
|
||||
for (int i = 0; i < concurrent; i++)
|
||||
new I2PThread(new Runner(client), "Client " + i).start();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Runner implements Runnable {
|
||||
private StreamSinkClient _client;
|
||||
public Runner(StreamSinkClient client) {
|
||||
_client = client;
|
||||
}
|
||||
public void run() {
|
||||
_client.runClient();
|
||||
}
|
||||
if (client != null)
|
||||
client.runClient();
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@ -26,6 +28,7 @@ public class StreamSinkServer {
|
||||
private String _destFile;
|
||||
private String _i2cpHost;
|
||||
private int _i2cpPort;
|
||||
private int _handlers;
|
||||
|
||||
/**
|
||||
* Create but do not start the streaming server.
|
||||
@ -34,13 +37,14 @@ public class StreamSinkServer {
|
||||
* @param ourDestFile filename to write our binary destination to
|
||||
*/
|
||||
public StreamSinkServer(String sinkDir, String ourDestFile) {
|
||||
this(sinkDir, ourDestFile, null, -1);
|
||||
this(sinkDir, ourDestFile, null, -1, 3);
|
||||
}
|
||||
public StreamSinkServer(String sinkDir, String ourDestFile, String i2cpHost, int i2cpPort) {
|
||||
public StreamSinkServer(String sinkDir, String ourDestFile, String i2cpHost, int i2cpPort, int handlers) {
|
||||
_sinkDir = sinkDir;
|
||||
_destFile = ourDestFile;
|
||||
_i2cpHost = i2cpHost;
|
||||
_i2cpPort = i2cpPort;
|
||||
_handlers = handlers;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(StreamSinkServer.class);
|
||||
}
|
||||
|
||||
@ -56,7 +60,8 @@ public class StreamSinkServer {
|
||||
else
|
||||
mgr = I2PSocketManagerFactory.createManager();
|
||||
Destination dest = mgr.getSession().getMyDestination();
|
||||
System.out.println("Listening for connections on: " + dest.calculateHash().toBase64());
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Listening for connections on: " + dest.calculateHash().toBase64());
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(_destFile);
|
||||
@ -72,24 +77,16 @@ public class StreamSinkServer {
|
||||
}
|
||||
|
||||
I2PServerSocket sock = mgr.getServerSocket();
|
||||
while (true) {
|
||||
try {
|
||||
I2PSocket curSock = sock.accept();
|
||||
handle(curSock);
|
||||
} catch (I2PException ie) {
|
||||
_log.error("Error accepting connection", ie);
|
||||
return;
|
||||
} catch (ConnectException ce) {
|
||||
_log.error("Connection already dropped", ce);
|
||||
return;
|
||||
}
|
||||
}
|
||||
startup(sock);
|
||||
}
|
||||
|
||||
private void handle(I2PSocket socket) {
|
||||
I2PThread t = new I2PThread(new ClientRunner(socket));
|
||||
t.setName("Handle " + socket.getPeerDestination().calculateHash().toBase64().substring(0,4));
|
||||
t.start();
|
||||
public void startup(I2PServerSocket sock) {
|
||||
for (int i = 0; i < _handlers; i++) {
|
||||
I2PThread t = new I2PThread(new ClientRunner(sock));
|
||||
t.setName("Handler " + i);
|
||||
t.setDaemon(false);
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,27 +94,44 @@ public class StreamSinkServer {
|
||||
*
|
||||
*/
|
||||
private class ClientRunner implements Runnable {
|
||||
private I2PSocket _sock;
|
||||
private FileOutputStream _fos;
|
||||
public ClientRunner(I2PSocket socket) {
|
||||
_sock = socket;
|
||||
private I2PServerSocket _socket;
|
||||
public ClientRunner(I2PServerSocket socket) {
|
||||
_socket = socket;
|
||||
}
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
I2PSocket socket = _socket.accept();
|
||||
if (socket != null)
|
||||
handle(socket);
|
||||
} catch (I2PException ie) {
|
||||
_log.error("Error accepting connection", ie);
|
||||
return;
|
||||
} catch (ConnectException ce) {
|
||||
_log.error("Connection already dropped", ce);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handle(I2PSocket sock) {
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
File sink = new File(_sinkDir);
|
||||
if (!sink.exists())
|
||||
sink.mkdirs();
|
||||
File cur = File.createTempFile("clientSink", ".dat", sink);
|
||||
_fos = new FileOutputStream(cur);
|
||||
System.out.println("Writing to " + cur.getAbsolutePath());
|
||||
fos = new FileOutputStream(cur);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Writing to " + cur.getAbsolutePath());
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error creating sink", ioe);
|
||||
_fos = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
public void run() {
|
||||
if (_fos == null) return;
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
InputStream in = _sock.getInputStream();
|
||||
InputStream in = sock.getInputStream();
|
||||
byte buf[] = new byte[4096];
|
||||
long written = 0;
|
||||
int read = 0;
|
||||
@ -125,47 +139,55 @@ public class StreamSinkServer {
|
||||
//_fos.write(buf, 0, read);
|
||||
written += read;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read and wrote " + read);
|
||||
_log.debug("read and wrote " + read + " (" + written + ")");
|
||||
}
|
||||
_fos.write(("written: [" + written + "]\n").getBytes());
|
||||
fos.write(("written: [" + written + "]\n").getBytes());
|
||||
long lifetime = System.currentTimeMillis() - start;
|
||||
_log.error("Got EOF from client socket [written=" + written + " lifetime=" + lifetime + "]");
|
||||
_log.info("Got EOF from client socket [written=" + written + " lifetime=" + lifetime + "]");
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing the sink", ioe);
|
||||
} finally {
|
||||
if (_fos != null) try { _fos.close(); } catch (IOException ioe) {}
|
||||
if (_sock != null) try { _sock.close(); } catch (IOException ioe) {}
|
||||
_log.error("Client socket closed");
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
if (sock != null) try { sock.close(); } catch (IOException ioe) {}
|
||||
_log.debug("Client socket closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire up the streaming server. <code>Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile</code><br />
|
||||
* Fire up the streaming server. <code>Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [numHandlers]</code><br />
|
||||
* <ul>
|
||||
* <li><b>sinkDir</b>: Directory to store received files in</li>
|
||||
* <li><b>ourDestFile</b>: filename to write our binary destination to</li>
|
||||
* <li><b>numHandlers</b>: how many concurrent connections to handle</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
StreamSinkServer server = null;
|
||||
switch (args.length) {
|
||||
case 0:
|
||||
server = new StreamSinkServer("dataDir", "server.key", "localhost", 7654);
|
||||
server = new StreamSinkServer("dataDir", "server.key", "localhost", 7654, 3);
|
||||
break;
|
||||
case 2:
|
||||
server = new StreamSinkServer(args[0], args[1]);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
int handlers = 3;
|
||||
if (args.length == 5) {
|
||||
try {
|
||||
handlers = Integer.parseInt(args[4]);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
try {
|
||||
int port = Integer.parseInt(args[1]);
|
||||
server = new StreamSinkServer(args[2], args[3], args[0], port);
|
||||
server = new StreamSinkServer(args[2], args[3], args[0], port, handlers);
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile");
|
||||
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [handlers]");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile");
|
||||
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [handlers]");
|
||||
}
|
||||
if (server != null)
|
||||
server.runServer();
|
||||
|
@ -17,7 +17,7 @@
|
||||
<property location="doc/q/api" name="javadoc.dir"/>
|
||||
<property name="project.name" value="${ant.project.name}"/>
|
||||
<property location="${project.name}.jar" name="jar"/>
|
||||
<property location="${project.name}.war" name="war"/>
|
||||
<property location="q.war" name="war"/>
|
||||
</target>
|
||||
|
||||
<target name="builddep">
|
||||
@ -59,9 +59,9 @@
|
||||
<!-- To make a standalone app, insert into <jar>: -->
|
||||
<!-- <manifest><attribute name="Main-Class" value="com.foo.Main"/></manifest> -->
|
||||
<war compress="true" jarfile="${war}" webxml="web.xml">
|
||||
<!-- <fileset dir="${classes.dir}" includes="**/QConsole.class"/> -->
|
||||
<classes file="build/net/i2p/aum/q/QConsole.class"/>
|
||||
<classes file="build/HTML/**"/>
|
||||
<!-- <fileset file="build/net/i2p/aum/q/QConsole.class"/> -->
|
||||
<classes dir="build" includes="**/QConsole.class"/>
|
||||
<classes dir="build" includes="**/HTML/**"/>
|
||||
<!-- <fileset includes="**/HTML/*.class"/> -->
|
||||
<lib file="xmlrpc.jar"/>
|
||||
</war>
|
||||
|
@ -157,6 +157,8 @@ public abstract class QNode extends Thread
|
||||
*/
|
||||
|
||||
public String nodeType = "(base)";
|
||||
|
||||
public boolean isRunning;
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// CONSTRUCTORS
|
||||
@ -580,6 +582,13 @@ public abstract class QNode extends Thread
|
||||
System.out.println("scheduleStartupJobs: c<p="+updateCatalogFromPeers+", isClient="+isClient);
|
||||
}
|
||||
|
||||
public void scheduleShutdown()
|
||||
{
|
||||
Hashtable job = new Hashtable();
|
||||
job.put("cmd", "shutdown");
|
||||
runAfter(1000, job, "shutdown");
|
||||
}
|
||||
|
||||
public void schedulePeerUploadJob(QDataItem item)
|
||||
{
|
||||
String uri = (String)item.get("uri");
|
||||
@ -790,6 +799,8 @@ public abstract class QNode extends Thread
|
||||
{
|
||||
log.info("Starting background tasks");
|
||||
|
||||
isRunning = true;
|
||||
|
||||
// mark our start time
|
||||
nodeStartTime = new Date();
|
||||
|
||||
@ -833,7 +844,7 @@ public abstract class QNode extends Thread
|
||||
|
||||
// fetch items from the job queue, and launch
|
||||
// threads to execute them
|
||||
while (true)
|
||||
while (isRunning)
|
||||
{
|
||||
// get a thread slot from the thread pool
|
||||
try {
|
||||
|
@ -341,13 +341,15 @@ public class QServerMethods {
|
||||
//System.out.println("shutdown: our privkey="+node.privKeyStr);
|
||||
//System.out.println("shutdown: nodePrivKey="+nodePrivKey);
|
||||
if (nodePrivKey.equals(node.privKeyStr)) {
|
||||
|
||||
res.put("status", "ok");
|
||||
//node.scheduleShutdown();
|
||||
// get a runtime
|
||||
System.out.println("Node at "+node.dataDir+" shutting down");
|
||||
//System.out.println("Node at "+node.dataDir+" shutting down");
|
||||
Runtime r = Runtime.getRuntime();
|
||||
|
||||
// and terminate the vm
|
||||
r.exit(0);
|
||||
//r.halt(0);
|
||||
//r.exit(0);
|
||||
r.halt(0);
|
||||
}
|
||||
else {
|
||||
res.put("status", "error");
|
||||
|
@ -62,6 +62,9 @@ class QWorkerThread extends Thread {
|
||||
else if (cmd.equals("test")) {
|
||||
doTest();
|
||||
}
|
||||
else if (cmd.equals("shutdown")) {
|
||||
doShutdown();
|
||||
}
|
||||
else {
|
||||
node.log.error("workerthread.run: unrecognised command '"+cmd+"'");
|
||||
System.out.println("workerthread.run: unrecognised command '"+cmd+"'");
|
||||
@ -90,6 +93,21 @@ class QWorkerThread extends Thread {
|
||||
System.out.println("TESTJOB: msg='"+msg+"'");
|
||||
}
|
||||
|
||||
public void doShutdown() throws Exception {
|
||||
|
||||
try {
|
||||
new File(node.jobsDir + node.sep + jobTime).delete();
|
||||
new File(node.jobsDir + node.sep + jobTime + ".desc").delete();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
SimpleFile f = new SimpleFile("/tmp/eeee", "rws");
|
||||
f.write("xxx");
|
||||
node.isRunning = false;
|
||||
Runtime.getRuntime().halt(0);
|
||||
}
|
||||
|
||||
public void doLocalPutItem() throws Exception {
|
||||
Hashtable metadata = (Hashtable)job.get("metadata");
|
||||
String path = (String)job.get("localDataFilePath");
|
||||
|
@ -28,7 +28,8 @@ public class ConfigNetHandler extends FormHandler {
|
||||
private boolean _reseedRequested;
|
||||
private boolean _saveRequested;
|
||||
private boolean _timeSyncEnabled;
|
||||
private String _port;
|
||||
private String _tcpPort;
|
||||
private String _udpPort;
|
||||
private String _inboundRate;
|
||||
private String _inboundBurst;
|
||||
private String _outboundRate;
|
||||
@ -56,8 +57,11 @@ public class ConfigNetHandler extends FormHandler {
|
||||
public void setHostname(String hostname) {
|
||||
_hostname = (hostname != null ? hostname.trim() : null);
|
||||
}
|
||||
public void setPort(String port) {
|
||||
_port = (port != null ? port.trim() : null);
|
||||
public void setTcpPort(String port) {
|
||||
_tcpPort = (port != null ? port.trim() : null);
|
||||
}
|
||||
public void setUdpPort(String port) {
|
||||
_udpPort = (port != null ? port.trim() : null);
|
||||
}
|
||||
public void setInboundrate(String rate) {
|
||||
_inboundRate = (rate != null ? rate.trim() : null);
|
||||
@ -207,14 +211,25 @@ public class ConfigNetHandler extends FormHandler {
|
||||
restartRequired = true;
|
||||
}
|
||||
}
|
||||
if ( (_port != null) && (_port.length() > 0) ) {
|
||||
if ( (_tcpPort != null) && (_tcpPort.length() > 0) ) {
|
||||
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT);
|
||||
if ( (oldPort == null) && (_port.equals("8887")) ) {
|
||||
if ( (oldPort == null) && (_tcpPort.equals("8887")) ) {
|
||||
// still on default.. noop
|
||||
} else if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_port)) ) {
|
||||
} else if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_tcpPort)) ) {
|
||||
// its not the default OR it has changed
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _port);
|
||||
addFormNotice("Updating TCP port from " + oldPort + " to " + _port);
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _tcpPort);
|
||||
addFormNotice("Updating TCP port from " + oldPort + " to " + _tcpPort);
|
||||
restartRequired = true;
|
||||
}
|
||||
}
|
||||
if ( (_udpPort != null) && (_udpPort.length() > 0) ) {
|
||||
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_UDP_PORT);
|
||||
if ( (oldPort == null) && (_udpPort.equals("8887")) ) {
|
||||
// still on default.. noop
|
||||
} else if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_udpPort)) ) {
|
||||
// its not the default OR it has changed
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _udpPort);
|
||||
addFormNotice("Updating UDP port from " + oldPort + " to " + _udpPort);
|
||||
restartRequired = true;
|
||||
}
|
||||
}
|
||||
|
@ -24,11 +24,13 @@ public class ConfigNetHelper {
|
||||
/** copied from various private TCP components */
|
||||
public final static String PROP_I2NP_TCP_HOSTNAME = "i2np.tcp.hostname";
|
||||
public final static String PROP_I2NP_TCP_PORT = "i2np.tcp.port";
|
||||
public final static String PROP_I2NP_UDP_PORT = "i2np.udp.port";
|
||||
public final static String PROP_I2NP_INTERNAL_UDP_PORT = "i2np.udp.internalPort";
|
||||
|
||||
public String getHostname() {
|
||||
return _context.getProperty(PROP_I2NP_TCP_HOSTNAME);
|
||||
}
|
||||
public String getPort() {
|
||||
public String getTcpPort() {
|
||||
int port = 8887;
|
||||
String val = _context.getProperty(PROP_I2NP_TCP_PORT);
|
||||
if (val != null) {
|
||||
@ -41,6 +43,21 @@ public class ConfigNetHelper {
|
||||
return "" + port;
|
||||
}
|
||||
|
||||
public String getUdpPort() {
|
||||
int port = 8887;
|
||||
String val = _context.getProperty(PROP_I2NP_UDP_PORT);
|
||||
if (val == null)
|
||||
val = _context.getProperty(PROP_I2NP_INTERNAL_UDP_PORT);
|
||||
if (val != null) {
|
||||
try {
|
||||
port = Integer.parseInt(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// ignore, use default from above
|
||||
}
|
||||
}
|
||||
return "" + port;
|
||||
}
|
||||
|
||||
public String getEnableTimeSyncChecked() {
|
||||
String disabled = _context.getProperty(Timestamper.PROP_DISABLED, "false");
|
||||
if ( (disabled != null) && ("true".equalsIgnoreCase(disabled)) )
|
||||
|
@ -0,0 +1,96 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.stat.StatManager;
|
||||
|
||||
/**
|
||||
* Handler to deal with form submissions from the stats config form and act
|
||||
* upon the values.
|
||||
*
|
||||
*/
|
||||
public class ConfigStatsHandler extends FormHandler {
|
||||
private String _filename;
|
||||
private List _stats;
|
||||
private boolean _explicitFilter;
|
||||
private String _explicitFilterValue;
|
||||
|
||||
public ConfigStatsHandler() {
|
||||
super();
|
||||
_stats = new ArrayList();
|
||||
_explicitFilter = false;
|
||||
}
|
||||
|
||||
protected void processForm() {
|
||||
saveChanges();
|
||||
}
|
||||
|
||||
public void setFilename(String filename) {
|
||||
_filename = (filename != null ? filename.trim() : null);
|
||||
}
|
||||
|
||||
public void setStatList(String stats[]) {
|
||||
if (stats != null) {
|
||||
for (int i = 0; i < stats.length; i++) {
|
||||
String cur = stats[i].trim();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Stat: [" + cur + "]");
|
||||
if ( (cur.length() > 0) && (!_stats.contains(cur)) )
|
||||
_stats.add(cur);
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Updated stats: " + _stats);
|
||||
}
|
||||
|
||||
public void setExplicitFilter(String foo) { _explicitFilter = true; }
|
||||
public void setExplicitFilterValue(String filter) { _explicitFilterValue = filter; }
|
||||
|
||||
/**
|
||||
* The user made changes to the config and wants to save them, so
|
||||
* lets go ahead and do so.
|
||||
*
|
||||
*/
|
||||
private void saveChanges() {
|
||||
if (_filename == null)
|
||||
_filename = StatManager.DEFAULT_STAT_FILE;
|
||||
_context.router().setConfigSetting(StatManager.PROP_STAT_FILE, _filename);
|
||||
|
||||
if (_explicitFilter) {
|
||||
_stats.clear();
|
||||
|
||||
if (_explicitFilterValue.indexOf(',') != -1) {
|
||||
StringTokenizer tok = new StringTokenizer(_explicitFilterValue, ",");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String cur = tok.nextToken().trim();
|
||||
if ( (cur.length() > 0) && (!_stats.contains(cur)) )
|
||||
_stats.add(cur);
|
||||
}
|
||||
} else {
|
||||
String stat = _explicitFilterValue.trim();
|
||||
if ( (stat.length() > 0) && (!_stats.contains(stat)) )
|
||||
_stats.add(stat);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuffer stats = new StringBuffer();
|
||||
for (int i = 0; i < _stats.size(); i++) {
|
||||
stats.append((String)_stats.get(i));
|
||||
if (i + 1 < _stats.size())
|
||||
stats.append(',');
|
||||
}
|
||||
|
||||
_context.router().setConfigSetting(StatManager.PROP_STAT_FILTER, stats.toString());
|
||||
boolean ok = _context.router().saveConfig();
|
||||
if (ok)
|
||||
addFormNotice("Stat filter and location updated successfully to: " + stats.toString());
|
||||
else
|
||||
addFormError("Failed to update the stat filter and location");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import net.i2p.stat.RateStat;
|
||||
import net.i2p.stat.FrequencyStat;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class ConfigStatsHelper {
|
||||
private RouterContext _context;
|
||||
private Log _log;
|
||||
private String _filter;
|
||||
private Set _filters;
|
||||
/** list of names of stats which are remaining, ordered by nested groups */
|
||||
private List _stats;
|
||||
private String _currentStatName;
|
||||
private String _currentStatDescription;
|
||||
private String _currentGroup;
|
||||
/** true if the current stat is the first in the group */
|
||||
private boolean _currentIsFirstInGroup;
|
||||
/** true if the stat is being logged */
|
||||
private boolean _currentIsLogged;
|
||||
|
||||
/**
|
||||
* Configure this bean to query a particular router context
|
||||
*
|
||||
* @param contextId begging few characters of the routerHash, or null to pick
|
||||
* the first one we come across.
|
||||
*/
|
||||
public void setContextId(String contextId) {
|
||||
try {
|
||||
_context = ContextHelper.getContext(contextId);
|
||||
_log = _context.logManager().getLog(ConfigStatsHelper.class);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
_stats = new ArrayList();
|
||||
Map groups = _context.statManager().getStatsByGroup();
|
||||
for (Iterator iter = groups.values().iterator(); iter.hasNext(); ) {
|
||||
Set stats = (Set)iter.next();
|
||||
for (Iterator statIter = stats.iterator(); statIter.hasNext(); )
|
||||
_stats.add(statIter.next());
|
||||
}
|
||||
_filter = _context.statManager().getStatFilter();
|
||||
if (_filter == null)
|
||||
_filter = "";
|
||||
|
||||
_filters = new HashSet();
|
||||
StringTokenizer tok = new StringTokenizer(_filter, ",");
|
||||
while (tok.hasMoreTokens())
|
||||
_filters.add(tok.nextToken().trim());
|
||||
}
|
||||
|
||||
public ConfigStatsHelper() {}
|
||||
|
||||
public String getFilename() { return _context.statManager().getStatFile(); }
|
||||
|
||||
/**
|
||||
* move the cursor to the next known stat, returning true if a valid
|
||||
* stat is available.
|
||||
*
|
||||
* @return true if a valid stat is available, otherwise false
|
||||
*/
|
||||
public boolean hasMoreStats() {
|
||||
if (_stats.size() <= 0)
|
||||
return false;
|
||||
_currentStatName = (String)_stats.remove(0);
|
||||
RateStat rs = _context.statManager().getRate(_currentStatName);
|
||||
if (rs != null) {
|
||||
_currentStatDescription = rs.getDescription();
|
||||
if (_currentGroup == null)
|
||||
_currentIsFirstInGroup = true;
|
||||
else if (!rs.getGroupName().equals(_currentGroup))
|
||||
_currentIsFirstInGroup = true;
|
||||
else
|
||||
_currentIsFirstInGroup = false;
|
||||
_currentGroup = rs.getGroupName();
|
||||
} else {
|
||||
FrequencyStat fs = _context.statManager().getFrequency(_currentStatName);
|
||||
if (fs != null) {
|
||||
_currentStatDescription = fs.getDescription();
|
||||
if (_currentGroup == null)
|
||||
_currentIsFirstInGroup = true;
|
||||
else if (!fs.getGroupName().equals(_currentGroup))
|
||||
_currentIsFirstInGroup = true;
|
||||
else
|
||||
_currentIsFirstInGroup = false;
|
||||
_currentGroup = fs.getGroupName();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Stat does not exist?! [" + _currentStatName + "]");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_filters.contains("*") || _filters.contains(_currentStatName))
|
||||
_currentIsLogged = true;
|
||||
else
|
||||
_currentIsLogged = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Is the current stat the first in the group? */
|
||||
public boolean groupRequired() {
|
||||
if (_currentIsFirstInGroup) {
|
||||
_currentIsFirstInGroup = false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/** What group is the current stat in */
|
||||
public String getCurrentGroupName() { return _currentGroup; }
|
||||
public String getCurrentStatName() { return _currentStatName; }
|
||||
public String getCurrentStatDescription() { return _currentStatDescription; }
|
||||
public boolean getCurrentIsLogged() { return _currentIsLogged; }
|
||||
public String getExplicitFilter() { return _filter; }
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.web.ConfigServiceHandler.UpdateWrapperManagerTask;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -31,6 +35,15 @@ public class ConfigUpdateHandler extends FormHandler {
|
||||
public static final String DEFAULT_PROXY_PORT = "4444";
|
||||
|
||||
protected void processForm() {
|
||||
if ("Check for update now".equals(_action)) {
|
||||
NewsFetcher fetcher = NewsFetcher.getInstance(I2PAppContext.getGlobalContext());
|
||||
fetcher.fetchNews();
|
||||
if (fetcher.updateAvailable())
|
||||
addFormNotice("Update available, click link on left");
|
||||
else
|
||||
addFormNotice("No update available");
|
||||
}
|
||||
|
||||
if ( (_newsURL != null) && (_newsURL.length() > 0) ) {
|
||||
String oldURL = _context.router().getConfigSetting(PROP_NEWS_URL);
|
||||
if ( (oldURL == null) || (!_newsURL.equals(oldURL)) ) {
|
||||
@ -38,6 +51,7 @@ public class ConfigUpdateHandler extends FormHandler {
|
||||
addFormNotice("Updating news URL to " + _newsURL);
|
||||
}
|
||||
}
|
||||
|
||||
if ( (_updateURL != null) && (_updateURL.length() > 0) ) {
|
||||
String oldURL = _context.router().getConfigSetting(PROP_UPDATE_URL);
|
||||
if ( (oldURL == null) || (!_updateURL.equals(oldURL)) ) {
|
||||
@ -56,7 +70,7 @@ public class ConfigUpdateHandler extends FormHandler {
|
||||
|
||||
if ( (_proxyPort != null) && (_proxyPort.length() > 0) ) {
|
||||
String oldPort = _context.router().getConfigSetting(PROP_PROXY_PORT);
|
||||
if ( (oldPort == null) || (!_proxyHost.equals(oldPort)) ) {
|
||||
if ( (oldPort == null) || (!_proxyPort.equals(oldPort)) ) {
|
||||
_context.router().setConfigSetting(PROP_PROXY_PORT, _proxyPort);
|
||||
addFormNotice("Updating proxy port to " + _proxyPort);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Simple form handler base class - does not depend on servlets or jsp,
|
||||
@ -16,6 +17,7 @@ import net.i2p.router.RouterContext;
|
||||
*/
|
||||
public class FormHandler {
|
||||
protected RouterContext _context;
|
||||
protected Log _log;
|
||||
private String _nonce;
|
||||
protected String _action;
|
||||
private List _errors;
|
||||
@ -41,6 +43,7 @@ public class FormHandler {
|
||||
public void setContextId(String contextId) {
|
||||
try {
|
||||
_context = ContextHelper.getContext(contextId);
|
||||
_log = _context.logManager().getLog(getClass());
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private void fetchNews() {
|
||||
public void fetchNews() {
|
||||
String newsURL = _context.getProperty(ConfigUpdateHandler.PROP_NEWS_URL, ConfigUpdateHandler.DEFAULT_NEWS_URL);
|
||||
boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
|
||||
String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST);
|
||||
|
@ -25,8 +25,11 @@ public class NoticeHelper {
|
||||
|
||||
public String getSystemNotice() {
|
||||
if (_context.router().gracefulShutdownInProgress()) {
|
||||
return "Graceful shutdown in "
|
||||
+ DataHelper.formatDuration(_context.router().getShutdownTimeRemaining());
|
||||
long remaining = _context.router().getShutdownTimeRemaining();
|
||||
if (remaining > 0)
|
||||
return "Graceful shutdown in " + DataHelper.formatDuration(remaining);
|
||||
else
|
||||
return "Graceful shutdown imminent, please be patient as state is written to disk";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
|
||||
public class PeerHelper {
|
||||
private RouterContext _context;
|
||||
private Writer _out;
|
||||
/**
|
||||
* Configure this bean to query a particular router context
|
||||
*
|
||||
* @param contextId begging few characters of the routerHash, or null to pick
|
||||
* the first one we come across.
|
||||
*/
|
||||
public void setContextId(String contextId) {
|
||||
try {
|
||||
_context = ContextHelper.getContext(contextId);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public PeerHelper() {}
|
||||
|
||||
public void setOut(Writer out) { _out = out; }
|
||||
|
||||
public String getPeerSummary() {
|
||||
try {
|
||||
_context.commSystem().renderStatusHTML(_out);
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
@ -28,13 +28,13 @@
|
||||
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigNetHandler.nonce")%>" />
|
||||
<input type="hidden" name="action" value="blah" />
|
||||
|
||||
TCP port:
|
||||
<input name="port" type="text" size="4" value="<jsp:getProperty name="nethelper" property="port" />" /> <br />
|
||||
UDP port: <i><jsp:getProperty name="nethelper" property="udpPort" /></i><br />
|
||||
<!-- <input name="udpPort" type="text" size="5" value="<jsp:getProperty name="nethelper" property="udpPort" />" /><br /> -->
|
||||
<b>You must poke a hole in your firewall or NAT (if applicable) to receive new inbound UDP packets on
|
||||
this port from arbitrary peers (this requirement will be removed in i2p 0.6.1, but is necessary now)</b><br />
|
||||
TCP port: <input name="tcpPort" type="text" size="5" value="<jsp:getProperty name="nethelper" property="tcpPort" />" /> <br />
|
||||
<b>You must poke a hole in your firewall or NAT (if applicable) so that you can receive inbound TCP
|
||||
connections on it.</b> Nothing will work if you don't. Sorry. We know how to make it so
|
||||
this restriction won't be necessary, but its later on in the
|
||||
<a href="http://www.i2p.net/roadmap">roadmap</a> and we only have so many coder-hours (but if you want
|
||||
to help, please <a href="http://www.i2p.net/getinvolved">get involved!</a>)
|
||||
connections on it (this requirement will be removed in i2p 0.6.1, but is necessary now)</b>
|
||||
<hr />
|
||||
|
||||
<b>Bandwidth limiter</b><br />
|
||||
@ -57,7 +57,7 @@
|
||||
packets on port 123 to one of the pool.ntp.org machines (or some other SNTP server).</i>
|
||||
<hr />
|
||||
<input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br />
|
||||
<i>Changing the TCP port will force a 'soft restart' - dropping your connections and clients as
|
||||
<i>Changing the TCP or UDP port will force a 'soft restart' - dropping your connections and clients as
|
||||
if the router was stopped and restarted. <b>Please be patient</b> - it may take
|
||||
a few seconds to complete.</i>
|
||||
</form>
|
||||
@ -73,6 +73,13 @@
|
||||
"i2p.reseedURL=someURL" (e.g. java -Di2p.reseedURL=http://dev.i2p.net/i2pdb/ ...). You can
|
||||
also do it manually by getting routerInfo-*.dat files from someone (a friend, someone on IRC,
|
||||
whatever) and saving them to your netDb/ directory.</p>
|
||||
<p>
|
||||
With the SSU transport, the internal UDP port may be different from the external
|
||||
UDP port (in case of a firewall/NAT) - the UDP port field above specifies the
|
||||
external one and assumes they are the same, but if you want to set the internal
|
||||
port to something else, you can add "i2np.udp.internalPort=1234" to the
|
||||
<a href="configadvanced.jsp">advanced</a> config and restart the router.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
@ -8,5 +8,7 @@
|
||||
%>Tunnels | <% } else { %><a href="configtunnels.jsp">Tunnels</a> | <% }
|
||||
if (request.getRequestURI().indexOf("configlogging.jsp") != -1) {
|
||||
%>Logging | <% } else { %><a href="configlogging.jsp">Logging</a> | <% }
|
||||
if (request.getRequestURI().indexOf("configstats.jsp") != -1) {
|
||||
%>Stats | <% } else { %><a href="configstats.jsp">Stats</a> | <% }
|
||||
if (request.getRequestURI().indexOf("configadvanced.jsp") != -1) {
|
||||
%>Advanced<% } else { %><a href="configadvanced.jsp">Advanced</a><% } %></h4>
|
||||
|
104
apps/routerconsole/jsp/configstats.jsp
Normal file
104
apps/routerconsole/jsp/configstats.jsp
Normal file
@ -0,0 +1,104 @@
|
||||
<%@page contentType="text/html"%>
|
||||
<%@page pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<title>I2P Router Console - config stats</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
<script type="text/javascript">
|
||||
function init()
|
||||
{
|
||||
checkAll = false;
|
||||
}
|
||||
function toggleAll(category)
|
||||
{
|
||||
var inputs = document.getElementsByTagName("input");
|
||||
for(index = 0; index < inputs.length; index++)
|
||||
{
|
||||
if(inputs[index].id == category)
|
||||
{
|
||||
if(inputs[index].checked == 0)
|
||||
{
|
||||
inputs[index].checked = 1;
|
||||
}
|
||||
else if(inputs[index].checked == 1)
|
||||
{
|
||||
inputs[index].checked = 0;
|
||||
}
|
||||
}
|
||||
if(category == '*')
|
||||
{
|
||||
if (checkAll == false)
|
||||
{
|
||||
inputs[index].checked = 1;
|
||||
}
|
||||
else if (checkAll == true)
|
||||
{
|
||||
inputs[index].checked = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(category == '*')
|
||||
{
|
||||
if (checkAll == false)
|
||||
{
|
||||
checkAll = true;
|
||||
}
|
||||
else if (checkAll == true)
|
||||
{
|
||||
checkAll = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head><body onLoad="init();">
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<%@include file="confignav.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigStatsHandler" id="formhandler" scope="request" />
|
||||
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
<jsp:setProperty name="formhandler" property="*" />
|
||||
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
|
||||
<i><jsp:getProperty name="formhandler" property="notices" /></i>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigStatsHelper" id="statshelper" scope="request" />
|
||||
<jsp:setProperty name="statshelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
|
||||
<form id="statsForm" name="statsForm" action="configstats.jsp" method="POST">
|
||||
<% String prev = System.getProperty("net.i2p.router.web.ConfigStatsHandler.nonce");
|
||||
if (prev != null) System.setProperty("net.i2p.router.web.ConfigStatsHandler.noncePrev", prev);
|
||||
System.setProperty("net.i2p.router.web.ConfigStatsHandler.nonce", new java.util.Random().nextLong()+""); %>
|
||||
<input type="hidden" name="action" value="foo" />
|
||||
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigStatsHandler.nonce")%>" />
|
||||
Stat file: <input type="text" name="filename" value="<%=statshelper.getFilename()%>" /><br />
|
||||
Filter: (<a href="javascript: void(null);" onclick="toggleAll('*')">toggle all</a>)<br />
|
||||
<table>
|
||||
<% while (statshelper.hasMoreStats()) {
|
||||
while (statshelper.groupRequired()) { %>
|
||||
<tr><td valign="top" align="left" colspan="2">
|
||||
<b><%=statshelper.getCurrentGroupName()%></b>
|
||||
(<a href="javascript: void(null);" onclick="toggleAll('<%=statshelper.getCurrentGroupName()%>')">toggle all</a>)
|
||||
</td></tr><%
|
||||
} // end iterating over required groups for the current stat %>
|
||||
<tr><td valign="top" align="left">
|
||||
<input id="<%=statshelper.getCurrentGroupName()%>" type="checkbox" name="statList" value="<%=statshelper.getCurrentStatName()%>" <%
|
||||
if (statshelper.getCurrentIsLogged()) { %>checked="true" <% } %>/></td>
|
||||
<td valign="top" align="left"><b><%=statshelper.getCurrentStatName()%>:</b><br />
|
||||
<%=statshelper.getCurrentStatDescription()%></td></tr><%
|
||||
} // end iterating over all stats %>
|
||||
<tr><td colspan="2"><hr /></td></tr>
|
||||
<tr><td><input type="checkbox" name="explicitFilter" /></td>
|
||||
<td>Advanced filter:
|
||||
<input type="text" name="explicitFilterValue" value="<%=statshelper.getExplicitFilter()%>" size="40" /></td></tr>
|
||||
<tr><td colspan="2"><hr /></td></tr>
|
||||
<tr><td><input type="submit" name="shouldsave" value="Save changes" /> </td>
|
||||
<td><input type="reset" value="Cancel" /></td></tr>
|
||||
</form>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -27,7 +27,7 @@
|
||||
if (prev != null) System.setProperty("net.i2p.router.web.ConfigUpdateHandler.noncePrev", prev);
|
||||
System.setProperty("net.i2p.router.web.ConfigUpdateHandler.nonce", new java.util.Random().nextLong()+""); %>
|
||||
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigUpdateHandler.nonce")%>" />
|
||||
<input type="hidden" name="action" value="update" />
|
||||
<input type="submit" name="action" value="Check for update now" /><br /><br />
|
||||
News URL:
|
||||
<input type="text" size="60" name="newsURL" value="<jsp:getProperty name="updatehelper" property="newsURL" />"><br />
|
||||
Refresh frequency:
|
||||
@ -36,10 +36,10 @@
|
||||
<input type="text" size="60" name="updateURL" value="<jsp:getProperty name="updatehelper" property="updateURL" />"><br />
|
||||
Update policy:
|
||||
<jsp:getProperty name="updatehelper" property="updatePolicySelectBox" /><br />
|
||||
Update anonymously?
|
||||
Update through the eepProxy?
|
||||
<jsp:getProperty name="updatehelper" property="updateThroughProxy" /><br />
|
||||
Proxy host: <input type="text" size="10" name="proxyHost" value="<jsp:getProperty name="updatehelper" property="proxyHost" />" /><br />
|
||||
Proxy port: <input type="text" size="4" name="proxyPort" value="<jsp:getProperty name="updatehelper" property="proxyPort" />" /><br />
|
||||
eepProxy host: <input type="text" size="10" name="proxyHost" value="<jsp:getProperty name="updatehelper" property="proxyHost" />" /><br />
|
||||
eepProxy port: <input type="text" size="4" name="proxyPort" value="<jsp:getProperty name="updatehelper" property="proxyPort" />" /><br />
|
||||
<!-- prompt for the eepproxy -->
|
||||
Trusted keys:
|
||||
<textarea name="trustedKeys" disabled="true" cols="60" rows="2"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea>
|
||||
|
21
apps/routerconsole/jsp/peers.jsp
Normal file
21
apps/routerconsole/jsp/peers.jsp
Normal file
@ -0,0 +1,21 @@
|
||||
<%@page contentType="text/html"%>
|
||||
<%@page pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<title>I2P Router Console - peer connections</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head><body>
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<jsp:useBean class="net.i2p.router.web.PeerHelper" id="peerHelper" scope="request" />
|
||||
<jsp:setProperty name="peerHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
<jsp:setProperty name="peerHelper" property="out" value="<%=out%>" />
|
||||
<jsp:getProperty name="peerHelper" property="peerSummary" />
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -33,7 +33,7 @@
|
||||
}
|
||||
%><hr />
|
||||
|
||||
<u><b>Peers</b></u><br />
|
||||
<u><b><a href="peers.jsp">Peers</a></b></u><br />
|
||||
<b>Active:</b> <jsp:getProperty name="helper" property="activePeers" />/<jsp:getProperty name="helper" property="activeProfiles" /><br />
|
||||
<b>Fast:</b> <jsp:getProperty name="helper" property="fastPeers" /><br />
|
||||
<b>High capacity:</b> <jsp:getProperty name="helper" property="highCapacityPeers" /><br />
|
||||
|
64
apps/sam/c/Makefile
Normal file
64
apps/sam/c/Makefile
Normal file
@ -0,0 +1,64 @@
|
||||
FLAGS+=-g
|
||||
CFLAGS+=$(FLAGS)
|
||||
LDFLAGS+=$(FLAGS)
|
||||
|
||||
OBJS:=obj/sam.lo obj/strl.lo obj/parse.lo obj/tinystring.lo
|
||||
DEPS:=$(patsubst obj/%.lo, .deps/%.d, $(OBJS))
|
||||
DESTDIR:=$(if $(DESTDIR),$(DESTDIR)/lib,/usr/lib)
|
||||
|
||||
MAKEFLAGS=-s -r
|
||||
|
||||
PERL=$(shell which perl 2>/dev/null)
|
||||
ifneq ($(PERL),)
|
||||
STATUS=$(PERL) ./status
|
||||
else
|
||||
STATUS=echo
|
||||
endif
|
||||
|
||||
LIBTOOL_LOG=libtool.log
|
||||
|
||||
all:: cleanlog .deps/finish
|
||||
|
||||
cleanlog:
|
||||
echo >$(LIBTOOL_LOG)
|
||||
|
||||
lib/libsam.so: obj/libsam.la
|
||||
libtool --mode=install install $^ `pwd`/$@ >>$(LIBTOOL_LOG)
|
||||
|
||||
obj/libsam-static.la: $(OBJS)
|
||||
$(STATUS) library '(static)'
|
||||
libtool --mode=link gcc -static $(LDFLAGS) -o $@ $^ >>$(LIBTOOL_LOG)
|
||||
|
||||
obj/libsam.la: $(OBJS)
|
||||
$(STATUS) library '(shared)'
|
||||
libtool --mode=link gcc -rpath $(DESTDIR) $(LDFLAGS) -o $@ $^ >>$(LIBTOOL_LOG)
|
||||
|
||||
obj/%.lo: src/%.c
|
||||
$(STATUS) compile $*
|
||||
libtool --mode=compile gcc $(CFLAGS) -Iinc/ -c -o $@ $< >>$(LIBTOOL_LOG)
|
||||
|
||||
$(OBJS):|obj
|
||||
obj:
|
||||
$(STATUS) MKDIR $@
|
||||
mkdir -p $@
|
||||
|
||||
.deps/%.d: src/%.c
|
||||
$(STATUS) deps $*
|
||||
gcc -Iinc/ -MM -MT obj/$*.o $< -o $@
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
DEPS+=.deps/finish
|
||||
.deps/finish: lib/libsam.so
|
||||
libtool --finish $(DESTDIR) >>$(LIBTOOL_LOG) && touch $@
|
||||
$(DEPS):|.deps
|
||||
.deps:
|
||||
$(STATUS) MKDIR $@
|
||||
mkdir -p $@
|
||||
|
||||
clean:
|
||||
$(STATUS) clean
|
||||
libtool --mode=clean rm -f obj/*.l* lib/*.l* lib/*.so* lib/*.a >>$(LIBTOOL_LOG)
|
||||
rm -Rf .deps libtool.log
|
||||
|
||||
.PHONY: all cleanlog clean
|
@ -1,25 +0,0 @@
|
||||
#
|
||||
# This Makefile contains instructions common to all platforms
|
||||
#
|
||||
|
||||
#
|
||||
# Build rules
|
||||
#
|
||||
|
||||
all: clean depend libsam
|
||||
|
||||
depend:
|
||||
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
libsam: $(OBJS)
|
||||
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-$(RM) -f $(LIBDIR)/libsam.a $(OBJDIR)/*.o .depend
|
@ -1,48 +0,0 @@
|
||||
#
|
||||
# This Makefile is compatible with GNU Make and should work on Cygwin
|
||||
#
|
||||
|
||||
#
|
||||
# Your operating system
|
||||
#
|
||||
|
||||
OS = CYGWIN
|
||||
|
||||
#
|
||||
# Directories
|
||||
#
|
||||
|
||||
INCDIR = inc
|
||||
LIBDIR = lib
|
||||
OBJDIR = obj
|
||||
SRCDIR = src
|
||||
|
||||
#
|
||||
# Programs
|
||||
#
|
||||
|
||||
AR = ar
|
||||
CC = gcc
|
||||
RM = rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -DOS=$(OS)
|
||||
CFLAGS += -I$(INCDIR)
|
||||
|
||||
#
|
||||
# Object files
|
||||
#
|
||||
|
||||
OBJS = $(OBJDIR)/sam.o \
|
||||
$(OBJDIR)/snprintf.o \
|
||||
$(OBJDIR)/strl.o
|
||||
|
||||
#
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
include Makefile.common
|
@ -1,46 +0,0 @@
|
||||
#
|
||||
# This Makefile is compatible with GNU Make and should work on FreeBSD
|
||||
#
|
||||
|
||||
#
|
||||
# Your operating system
|
||||
#
|
||||
|
||||
OS = FREEBSD
|
||||
|
||||
#
|
||||
# Directories
|
||||
#
|
||||
|
||||
INCDIR = inc
|
||||
LIBDIR = lib
|
||||
OBJDIR = obj
|
||||
SRCDIR = src
|
||||
|
||||
#
|
||||
# Programs
|
||||
#
|
||||
|
||||
AR = ar
|
||||
CC = gcc
|
||||
RM = rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -DOS=$(OS)
|
||||
CFLAGS += -I$(INCDIR)
|
||||
|
||||
#
|
||||
# Object files
|
||||
#
|
||||
|
||||
OBJS = $(OBJDIR)/sam.o
|
||||
|
||||
#
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
include Makefile.common
|
@ -1,47 +0,0 @@
|
||||
#
|
||||
# This Makefile is compatible with GNU Make and should work on Linux
|
||||
#
|
||||
|
||||
#
|
||||
# Your operating system
|
||||
#
|
||||
|
||||
OS = LINUX
|
||||
|
||||
#
|
||||
# Directories
|
||||
#
|
||||
|
||||
INCDIR = inc
|
||||
LIBDIR = lib
|
||||
OBJDIR = obj
|
||||
SRCDIR = src
|
||||
|
||||
#
|
||||
# Programs
|
||||
#
|
||||
|
||||
AR = ar
|
||||
CC = gcc
|
||||
RM = rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -DOS=$(OS)
|
||||
CFLAGS += -I$(INCDIR)
|
||||
|
||||
#
|
||||
# Object files
|
||||
#
|
||||
|
||||
OBJS = $(OBJDIR)/sam.o \
|
||||
$(OBJDIR)/strl.o
|
||||
|
||||
#
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
include Makefile.common
|
@ -1,47 +0,0 @@
|
||||
#
|
||||
# This Makefile is compatible with GNU Make and should work on Windows (MingW)
|
||||
#
|
||||
|
||||
#
|
||||
# Your operating system
|
||||
#
|
||||
|
||||
OS = MINGW
|
||||
|
||||
#
|
||||
# Directories
|
||||
#
|
||||
|
||||
INCDIR = inc
|
||||
LIBDIR = lib
|
||||
OBJDIR = obj
|
||||
SRCDIR = src
|
||||
|
||||
#
|
||||
# Programs
|
||||
#
|
||||
|
||||
AR = C:\MinGW\bin\ar
|
||||
CC = C:\MinGW\bin\gcc
|
||||
RM = C:\MinGW\bin\rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -DOS=$(OS)
|
||||
CFLAGS += -I$(INCDIR)
|
||||
|
||||
#
|
||||
# Object files
|
||||
#
|
||||
|
||||
OBJS = $(OBJDIR)/sam.o \
|
||||
$(OBJDIR)/strl.o
|
||||
|
||||
#
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
include Makefile.common
|
@ -1,39 +1,54 @@
|
||||
#
|
||||
# This Makefile is compatible with GNU Make and should work on POSIX systems
|
||||
#
|
||||
FLAGS+=-g
|
||||
|
||||
#
|
||||
# Programs
|
||||
#
|
||||
CFLAGS = $(FLAGS) -pipe -std=c99 -Wall
|
||||
CFLAGS += -I../../inc
|
||||
LDFLAGS = $(FLAGS) -L../../lib -lsam
|
||||
|
||||
CC = gcc
|
||||
INSTALL = install
|
||||
RM = rm
|
||||
OBJS:=i2p-ping.lo
|
||||
DEPS:=$(patsubst obj/%.lo, .deps/%.d, $(OBJS))
|
||||
DESTDIR:=$(if $(DESTDIR),$(DESTDIR)/lib,/usr/lib)
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
MAKEFLAGS=-s -r
|
||||
PERL=$(shell which perl 2>/dev/null)
|
||||
ifneq ($(PERL),)
|
||||
STATUS=$(PERL) ../../status
|
||||
else
|
||||
STATUS=echo
|
||||
endif
|
||||
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -I../../inc -L../../lib
|
||||
LIBS = -lsam
|
||||
LIBTOOL_LOG=libtool.log
|
||||
|
||||
#
|
||||
# Build rules
|
||||
#
|
||||
all:: cleanlog i2p-ping
|
||||
|
||||
all: clean i2p-ping
|
||||
cleanlog:
|
||||
>$(LIBTOOL_LOG)
|
||||
|
||||
i2p-ping: i2p-ping.c
|
||||
$(CC) $(CFLAGS) -o i2p-ping.o -c i2p-ping.c
|
||||
$(CC) $(CFLAGS) -o i2p-ping i2p-ping.o $(LIBS)
|
||||
i2p-ping: $(OBJS)
|
||||
$(STATUS) link
|
||||
libtool --mode=link gcc $(LDFLAGS) -o $@ $^ >>$(LIBTOOL_LOG)
|
||||
|
||||
install: i2p-ping
|
||||
$(INSTALL) i2p-ping $(HOME)/bin
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
%.lo: %.c
|
||||
$(STATUS) compile $*
|
||||
libtool --mode=compile gcc $(CFLAGS) -Iinc/ -c -o $@ $< >>$(LIBTOOL_LOG)
|
||||
.deps/%.d: src/%.c
|
||||
$(STATUS) deps $*
|
||||
gcc -Iinc/ -MM -MT obj/$*.o $^ -o $@
|
||||
|
||||
clean:
|
||||
-$(RM) -f i2p-ping *.o
|
||||
$(STATUS) clean
|
||||
rm -Rf .deps obj libtool.log
|
||||
libtool --mode=clean rm -f i2p-ping i2p-ping.lo >>$(LIBTOOL_LOG)
|
||||
|
||||
$(OBJS):|obj
|
||||
obj:
|
||||
$(STATUS) MKDIR $@
|
||||
mkdir -p $@
|
||||
|
||||
-include $(DEPS)
|
||||
$(DEPS):|.deps
|
||||
.deps:
|
||||
$(STATUS) MKDIR $@
|
||||
mkdir -p $@
|
||||
|
||||
|
||||
.PHONY: all cleanlog clean
|
24
apps/sam/c/inc/parse.h
Normal file
24
apps/sam/c/inc/parse.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef _PARSE_HEADER_FEEP
|
||||
#define _PARSE_HEADER_FEEP
|
||||
|
||||
#include "tinystring.h"
|
||||
|
||||
typedef struct arg_s {
|
||||
string_t name;
|
||||
string_t value;
|
||||
// int pos;
|
||||
} arg_t;
|
||||
|
||||
typedef struct {
|
||||
arg_t* arg;
|
||||
int num;
|
||||
} args_t;
|
||||
|
||||
args_t arg_parse(const char*);
|
||||
void arg_done(args_t);
|
||||
arg_t* arg_get(args_t,int);
|
||||
arg_t* arg_find(args_t,string_t);
|
||||
|
||||
#define AG(a,b) arg_get(a,b)
|
||||
|
||||
#endif /* _PARSE_HEADER_FEEP */
|
@ -121,9 +121,9 @@ bool sam_read_buffer(sam_sess_t *session);
|
||||
const char *sam_strerror(samerr_t code);
|
||||
/* SAM controls - callbacks */
|
||||
void (*sam_diedback)(sam_sess_t *session);
|
||||
void (*sam_logback)(char *str);
|
||||
void (*sam_namingback)(sam_sess_t *session, char *name,
|
||||
sam_pubkey_t pubkey, samerr_t result);
|
||||
void (*sam_logback)(const char *str);
|
||||
void (*sam_namingback)(sam_sess_t *session, const char *name,
|
||||
sam_pubkey_t pubkey, samerr_t result, const char* message);
|
||||
|
||||
/* Stream commands */
|
||||
void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
|
||||
@ -131,14 +131,15 @@ sam_sid_t sam_stream_connect(sam_sess_t *session, const sam_pubkey_t dest);
|
||||
samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
|
||||
const void *data, size_t size);
|
||||
/* Stream commands - callbacks */
|
||||
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t reason);
|
||||
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t reason, const char* message);
|
||||
|
||||
void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest);
|
||||
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest);
|
||||
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
void *data, size_t size);
|
||||
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result);
|
||||
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result, const char* message);
|
||||
|
||||
/* Stream send queue (experimental) */
|
||||
void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
|
||||
|
48
apps/sam/c/inc/tinystring.h
Normal file
48
apps/sam/c/inc/tinystring.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef TINYSTRING_HEADER
|
||||
#define TINYSTRING_HEADER
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef bool
|
||||
#define bool short int
|
||||
#endif
|
||||
|
||||
struct string_s;
|
||||
#define string_t struct string_s*
|
||||
//Mysteeeerious *waggles mysteriously*
|
||||
|
||||
/*{
|
||||
char* data;
|
||||
long int size;
|
||||
} *string_t;
|
||||
*/
|
||||
|
||||
string_t string_create(const char*);
|
||||
string_t string_ncreate(const char* cstr,long int length);
|
||||
|
||||
string_t string_wrap(const char*);
|
||||
//Does not malloc, do NOT pass to string_free
|
||||
|
||||
string_t string_fmt(const char* fmt, ...);
|
||||
string_t string_cat(string_t,string_t);
|
||||
|
||||
/* Source Dest */
|
||||
void string_copy(string_t,string_t);
|
||||
void string_copy_raw(string_t,void*,size_t);
|
||||
|
||||
const char* string_data(string_t);
|
||||
long int string_size(string_t);
|
||||
|
||||
void string_free(string_t);
|
||||
|
||||
bool string_equal(string_t,string_t);
|
||||
bool string_equali(string_t,string_t);
|
||||
int string_cmp(string_t,string_t);
|
||||
int string_cmpi(string_t,string_t);
|
||||
|
||||
#define _sw(a) string_wrap(a)
|
||||
#define _scr(a,b,c) string_copy_raw(a,b,c)
|
||||
|
||||
#define string_is(a,b) (! strncmp(string_data(a),(b),string_size(a)))
|
||||
|
||||
#endif /* TINYSTRING_HEADER */
|
78
apps/sam/c/src/parse.c
Normal file
78
apps/sam/c/src/parse.c
Normal file
@ -0,0 +1,78 @@
|
||||
#include "parse.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <malloc.h>
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
|
||||
args_t arg_parse(const char* line_raw) {
|
||||
args_t self;
|
||||
int numargs = 0;
|
||||
const char *end, *last;
|
||||
/* First pass to count how many args... */
|
||||
end = line_raw;
|
||||
while(*end && isspace(*end)) ++end;
|
||||
//Skip initial space...
|
||||
for(;;) {
|
||||
while(*end && !isspace(*end)) ++end;
|
||||
//Go to end of argument
|
||||
++numargs;
|
||||
while(*end && isspace(*end)) ++end;
|
||||
//Go to end of space after argument
|
||||
if(!*end) break;
|
||||
}
|
||||
self.num = numargs; // One more # args than spaces.
|
||||
self.arg = malloc(sizeof(arg_t)*numargs);
|
||||
|
||||
/* Second pass to assign args. (Lemee alone, is more efficient than a linked list!) */
|
||||
last = line_raw;
|
||||
numargs = 0; //Now numargs is which current arg.
|
||||
end = line_raw;
|
||||
while(*end && isspace(*end)) ++end;
|
||||
//Skip initial space...
|
||||
for(;;) {
|
||||
arg_t* nextarg = self.arg + numargs;;
|
||||
const char* isbinary;
|
||||
while(*end && !isspace(*end)) ++end;
|
||||
//Go to end of argument
|
||||
isbinary = strchr(last,'='); //Is there a value?
|
||||
|
||||
//Make sure not to pass end in our search for =
|
||||
if(isbinary && (isbinary < end)) {
|
||||
nextarg->name = string_ncreate(last,isbinary-last);
|
||||
nextarg->value = string_ncreate(isbinary+1,end-isbinary-1);
|
||||
} else {
|
||||
nextarg->name = string_ncreate(last,end-last);
|
||||
nextarg->value = string_create(NULL);
|
||||
}
|
||||
++numargs;
|
||||
while(*end && isspace(*end)) ++end;
|
||||
//Go to end of space after argument
|
||||
if(!*end) break;
|
||||
last = end;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
void arg_done(args_t self) {
|
||||
free(self.arg);
|
||||
self.arg = NULL;
|
||||
self.num = 0;
|
||||
}
|
||||
|
||||
arg_t* arg_get(args_t self, int index) {
|
||||
if(index >= self.num) return NULL;
|
||||
return self.arg + index;
|
||||
}
|
||||
|
||||
arg_t* arg_find(args_t self,string_t testname) {
|
||||
int index;
|
||||
for(index=0;index<self.num;++index) {
|
||||
if(string_equali(self.arg[index].name,testname)) {
|
||||
return self.arg + index;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
@ -30,6 +30,10 @@
|
||||
|
||||
#include "sam.h"
|
||||
#include "platform.h"
|
||||
#include "parse.h"
|
||||
#include "tinystring.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
static bool sam_hello(sam_sess_t *session);
|
||||
static void sam_log(const char *format, ...);
|
||||
@ -57,7 +61,7 @@ static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n);
|
||||
*/
|
||||
|
||||
/* a peer closed the connection */
|
||||
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
|
||||
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason, const char* message)
|
||||
= NULL;
|
||||
|
||||
/* a peer connected to us */
|
||||
@ -76,15 +80,14 @@ void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
void (*sam_diedback)(sam_sess_t *session) = NULL;
|
||||
|
||||
/* logging callback */
|
||||
void (*sam_logback)(char *str) = NULL;
|
||||
void (*sam_logback)(const char *str) = NULL;
|
||||
|
||||
/* naming lookup reply - `pubkey' will be NULL if `result' isn't SAM_OK */
|
||||
void (*sam_namingback)(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result) = NULL;
|
||||
void (*sam_namingback)(sam_sess_t *session, const char *name, sam_pubkey_t pubkey, samerr_t result, const char* message) = NULL;
|
||||
|
||||
/* our connection to a peer has completed */
|
||||
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result) = NULL;
|
||||
samerr_t result, const char* message) = NULL;
|
||||
|
||||
/* a peer sent some raw data (`data' MUST be freed) */
|
||||
void (*sam_rawback)(sam_sess_t *session, void *data, size_t size) = NULL;
|
||||
@ -290,13 +293,13 @@ static void sam_log(const char *format, ...)
|
||||
*/
|
||||
void sam_naming_lookup(sam_sess_t *session, const char *name)
|
||||
{
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_CMD_LEN];
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_CMD_LEN];
|
||||
|
||||
snprintf(cmd, sizeof cmd, "NAMING LOOKUP NAME=%s\n", name);
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
snprintf(cmd, sizeof cmd, "NAMING LOOKUP NAME=%s\n", name);
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -304,242 +307,193 @@ void sam_naming_lookup(sam_sess_t *session, const char *name)
|
||||
*
|
||||
* s - string of data that we read (read past tense)
|
||||
*/
|
||||
bool sam_parse_args(sam_sess_t *session, args_t args);
|
||||
static void sam_parse(sam_sess_t *session, char *s)
|
||||
{
|
||||
assert(session != NULL);
|
||||
#define SAM_DGRAM_RECEIVED_REPLY "DATAGRAM RECEIVED"
|
||||
#define SAM_NAMING_REPLY "NAMING REPLY"
|
||||
#define SAM_NAMING_REPLY_OK "NAMING REPLY RESULT=OK"
|
||||
#define SAM_NAMING_REPLY_IK "NAMING REPLY RESULT=INVALID_KEY"
|
||||
#define SAM_NAMING_REPLY_KNF "NAMING REPLY RESULT=KEY_NOT_FOUND"
|
||||
#define SAM_RAW_RECEIVED_REPLY "RAW RECEIVED"
|
||||
#define SAM_STREAM_CLOSED_REPLY "STREAM CLOSED"
|
||||
#define SAM_STREAM_CONNECTED_REPLY "STREAM CONNECTED"
|
||||
#define SAM_STREAM_RECEIVED_REPLY "STREAM RECEIVED"
|
||||
#define SAM_STREAM_STATUS_REPLY "STREAM STATUS"
|
||||
#define SAM_STREAM_STATUS_REPLY_OK "STREAM STATUS RESULT=OK"
|
||||
#define SAM_STREAM_STATUS_REPLY_CRP "STREAM STATUS RESULT=CANT_REACH_PEER"
|
||||
#define SAM_STREAM_STATUS_REPLY_I2E "STREAM STATUS RESULT=I2P_ERROR"
|
||||
#define SAM_STREAM_STATUS_REPLY_IK "STREAM STATUS RESULT=INVALID_KEY"
|
||||
#define SAM_STREAM_STATUS_REPLY_TO "STREAM STATUS RESULT=TIMEOUT"
|
||||
//Wrapper for ease of memory management
|
||||
args_t args;
|
||||
assert(session != NULL);
|
||||
args = arg_parse(s);
|
||||
if(!sam_parse_args(session, args)) {
|
||||
SAMLOG("Unknown SAM command received: %s", s);
|
||||
}
|
||||
arg_done(args);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: add raw parsing
|
||||
*/
|
||||
long int strtol_checked(const char* str) {
|
||||
static char* end = NULL;
|
||||
long int ret = strtol(str,&end,10);
|
||||
assert(str != end || "No number found at all!");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (strncmp(s, SAM_DGRAM_RECEIVED_REPLY,
|
||||
strlen(SAM_DGRAM_RECEIVED_REPLY)) == 0) {
|
||||
char *p;
|
||||
sam_pubkey_t dest;
|
||||
size_t size;
|
||||
void *data;
|
||||
|
||||
p = strchr(s, '='); /* DESTINATION= */
|
||||
assert(p != NULL);
|
||||
p++;
|
||||
strlcpy(dest, p, sizeof dest);
|
||||
p = strchr(p, '='); /* SIZE= */
|
||||
assert(p != NULL);
|
||||
p++;
|
||||
size = strtol(p, NULL, 10);
|
||||
assert(size != 0);
|
||||
data = malloc(size + 1); /* +1 for NUL termination, so when we are
|
||||
receiving a string it will just work and it
|
||||
won't be necessary to send NUL. When binary
|
||||
data is sent, the extra NUL character will
|
||||
just be ignored by the client program,
|
||||
because it is not added to the size */
|
||||
if (data == NULL) {
|
||||
SAMLOGS("Out of memory");
|
||||
abort();
|
||||
}
|
||||
if (sam_read2(session, data, size) != -1) {
|
||||
p = data + size;
|
||||
*p = '\0'; /* see above NUL note */
|
||||
sam_dgramback(session, dest, data, size); /* `data' must be freed */
|
||||
} else
|
||||
free(data);
|
||||
|
||||
return;
|
||||
|
||||
} else if (strncmp(s, SAM_NAMING_REPLY, strlen(SAM_NAMING_REPLY)) == 0) {
|
||||
char *p;
|
||||
char *q;
|
||||
char name[SAM_NAME_LEN];
|
||||
|
||||
p = strchr(s, '='); /* can't use strrchar because of option
|
||||
MESSAGE= */
|
||||
assert(p != NULL); /* RESULT= */
|
||||
p++;
|
||||
p = strchr(p, '='); /* NAME= */
|
||||
assert(p != NULL);
|
||||
p++;
|
||||
|
||||
if (strncmp(s, SAM_NAMING_REPLY_OK, strlen(SAM_NAMING_REPLY_OK)) == 0) {
|
||||
sam_pubkey_t pubkey;
|
||||
|
||||
q = strchr(p, ' '); /* ' 'VAL.. */
|
||||
assert(q != NULL);
|
||||
*q = '\0';
|
||||
q++;
|
||||
q = strchr(q, '='); /* VALUE= */
|
||||
assert(q != NULL);
|
||||
q++;
|
||||
strlcpy(name, p, sizeof name);
|
||||
strlcpy(pubkey, q, sizeof pubkey);
|
||||
sam_namingback(session, name, pubkey, SAM_OK);
|
||||
|
||||
} else if (strncmp(s, SAM_NAMING_REPLY_IK,
|
||||
strlen(SAM_NAMING_REPLY_IK)) == 0) {
|
||||
q = strchr(p, ' '); /* ' 'MES.. (optional) */
|
||||
if (q != NULL)
|
||||
*q = '\0';
|
||||
strlcpy(name, p, sizeof name);
|
||||
sam_namingback(session, name, NULL, SAM_INVALID_KEY);
|
||||
|
||||
} else if (strncmp(s, SAM_NAMING_REPLY_KNF,
|
||||
strlen(SAM_NAMING_REPLY_KNF)) == 0) {
|
||||
q = strchr(p, ' '); /* ' 'MES.. (optional) */
|
||||
if (q != NULL)
|
||||
*q = '\0';
|
||||
strlcpy(name, p, sizeof name);
|
||||
sam_namingback(session, name, NULL, SAM_KEY_NOT_FOUND);
|
||||
|
||||
} else {
|
||||
q = strchr(p, ' '); /* ' 'MES.. (optional) */
|
||||
if (q != NULL)
|
||||
*q = '\0';
|
||||
strlcpy(name, p, sizeof name);
|
||||
sam_namingback(session, name, NULL, SAM_UNKNOWN);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
} else if (strncmp(s, SAM_STREAM_CLOSED_REPLY,
|
||||
strlen(SAM_STREAM_CLOSED_REPLY)) == 0) {
|
||||
char *p;
|
||||
sam_sid_t stream_id;
|
||||
|
||||
p = strchr(s, '='); /* can't use strrchar because of option MESSAGE= */
|
||||
assert(p != NULL); /* ID= */
|
||||
p++;
|
||||
stream_id = strtol(p, NULL, 10);
|
||||
assert(stream_id != 0);
|
||||
p = strchr(p, '='); /* RESULT= */
|
||||
assert(p != NULL);
|
||||
p++;
|
||||
if (strncmp(p, "OK", strlen("OK")) == 0)
|
||||
sam_closeback(session, stream_id, SAM_OK);
|
||||
else if (strncmp(p, "CANT_REACH_PEER", strlen("CANT_REACH_PEER")) == 0)
|
||||
sam_closeback(session, stream_id, SAM_CANT_REACH_PEER);
|
||||
else if (strncmp(p, "I2P_ERROR", strlen("I2P_ERROR")) == 0)
|
||||
sam_closeback(session, stream_id, SAM_I2P_ERROR);
|
||||
else if (strncmp(p, "PEER_NOT_FOUND", strlen("PEER_NOT_FOUND")) == 0)
|
||||
sam_closeback(session, stream_id, SAM_PEER_NOT_FOUND);
|
||||
else if (strncmp(p, "TIMEOUT", strlen("TIMEOUT")) == 0)
|
||||
sam_closeback(session, stream_id, SAM_TIMEOUT);
|
||||
else
|
||||
sam_closeback(session, stream_id, SAM_UNKNOWN);
|
||||
|
||||
return;
|
||||
|
||||
} else if (strncmp(s, SAM_STREAM_CONNECTED_REPLY,
|
||||
strlen(SAM_STREAM_CONNECTED_REPLY)) == 0) {
|
||||
char *p;
|
||||
sam_sid_t stream_id;
|
||||
sam_pubkey_t dest;
|
||||
|
||||
p = strrchr(s, '='); /* ID= */
|
||||
assert(p != NULL);
|
||||
*p = '\0';
|
||||
p++;
|
||||
stream_id = strtol(p, NULL, 10);
|
||||
assert(stream_id != 0);
|
||||
p = strstr(s, "N="); /* DESTINATION= */
|
||||
p += 2;
|
||||
strlcpy(dest, p, sizeof dest);
|
||||
sam_connectback(session, stream_id, dest);
|
||||
bool sam_parse_args(sam_sess_t *session, args_t args)
|
||||
{
|
||||
arg_t* arg; // The current argument being examined...
|
||||
const char* message = NULL; // Almost EVERYTHING can have a message...
|
||||
|
||||
return;
|
||||
if(args.num <= 0) return 0;
|
||||
|
||||
} else if (strncmp(s, SAM_STREAM_RECEIVED_REPLY,
|
||||
strlen(SAM_STREAM_RECEIVED_REPLY)) == 0) {
|
||||
char *p;
|
||||
sam_sid_t stream_id;
|
||||
#define ARG_IS(a,b) string_equal(AG(args,a)->name,string_wrap(b))
|
||||
#define ARG_FIND(a) arg_find(args,_sw(a))
|
||||
|
||||
// Almost EVERYTHING can have a message...
|
||||
arg = ARG_FIND("MESSAGE");
|
||||
if(arg) {
|
||||
message = string_data(arg->value);
|
||||
}
|
||||
|
||||
if(ARG_IS(0,"DATAGRAM") &&
|
||||
ARG_IS(1,"RECEIVED")) {
|
||||
sam_pubkey_t dest;
|
||||
size_t size;
|
||||
void *data;
|
||||
|
||||
arg = ARG_FIND("DESTINATION");
|
||||
assert(arg != NULL);
|
||||
_scr(arg->value, dest, sizeof dest);
|
||||
|
||||
arg = ARG_FIND("SIZE");
|
||||
assert(arg != NULL);
|
||||
size = strtol_checked(string_data(arg->value));
|
||||
|
||||
data = malloc(size + 1);
|
||||
/* +1 for NUL termination, so when we are
|
||||
receiving a string it will just work and it
|
||||
won't be necessary to send NUL. When binary
|
||||
data is sent, the extra NUL character will
|
||||
just be ignored by the client program,
|
||||
because it is not added to the size */
|
||||
if (data == NULL) {
|
||||
SAMLOGS("Out of memory");
|
||||
abort();
|
||||
}
|
||||
if (sam_read2(session, data, size) != -1) {
|
||||
char* p = data + size;
|
||||
*p = '\0'; /* see above NUL note */
|
||||
sam_dgramback(session, dest, data, size); /* `data' must be freed */
|
||||
} else
|
||||
free(data);
|
||||
|
||||
} else if (ARG_IS(0,"NAMING") &&
|
||||
ARG_IS(1, "REPLY")) {
|
||||
if(NULL == (arg = ARG_FIND("RESULT"))) {
|
||||
SAMLOGS("Naming reply with no result");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (string_is(arg->value,"OK")) {
|
||||
sam_pubkey_t pubkey;
|
||||
arg = ARG_FIND("VALUE");
|
||||
assert(arg != NULL);
|
||||
_scr(arg->value, pubkey, sizeof pubkey);
|
||||
arg = ARG_FIND("NAME");
|
||||
assert(arg != NULL);
|
||||
|
||||
sam_namingback(session, string_data(arg->value), pubkey, SAM_OK, message);
|
||||
} else if(string_is(arg->value,"INVALID_KEY")) {
|
||||
arg_t* namearg = ARG_FIND("NAME");
|
||||
assert(namearg != NULL);
|
||||
sam_namingback(session, string_data(namearg->value), NULL,
|
||||
SAM_INVALID_KEY, message);
|
||||
} else if(string_is(arg->value,"KEY_NOT_FOUND")) {
|
||||
arg_t* namearg = ARG_FIND("NAME");
|
||||
assert(namearg != NULL);
|
||||
sam_namingback(session, string_data(namearg->value), NULL,
|
||||
SAM_KEY_NOT_FOUND, message);
|
||||
} else {
|
||||
arg_t* namearg = ARG_FIND("NAME");
|
||||
assert(namearg != NULL);
|
||||
sam_namingback(session, string_data(namearg->value), NULL,
|
||||
SAM_UNKNOWN, message);
|
||||
}
|
||||
|
||||
} else if (ARG_IS(0,"STREAM")) {
|
||||
sam_sid_t stream_id;
|
||||
arg = ARG_FIND("ID");
|
||||
assert(arg != 0);
|
||||
stream_id = strtol_checked(string_data(arg->value));
|
||||
|
||||
if(ARG_IS(1,"CLOSED")) {
|
||||
arg = ARG_FIND("RESULT");
|
||||
assert(arg != NULL);
|
||||
if (string_is(arg->value,"OK")) {
|
||||
sam_closeback(session, stream_id, SAM_OK, message);
|
||||
} else if (string_is(arg->value,"CANT_REACH_PEER")) {
|
||||
sam_closeback(session, stream_id, SAM_CANT_REACH_PEER, message);
|
||||
} else if (string_is(arg->value,"I2P_ERROR")) {
|
||||
sam_closeback(session, stream_id, SAM_I2P_ERROR, message);
|
||||
} else if (string_is(arg->value,"PEER_NOT_FOUND")) {
|
||||
sam_closeback(session, stream_id, SAM_PEER_NOT_FOUND, message);
|
||||
} else if (string_is(arg->value,"TIMEOUT")) {
|
||||
sam_closeback(session, stream_id, SAM_TIMEOUT, message);
|
||||
} else {
|
||||
sam_closeback(session, stream_id, SAM_UNKNOWN, message);
|
||||
}
|
||||
|
||||
} else if(ARG_IS(1,"CONNECTED")) {
|
||||
sam_pubkey_t dest;
|
||||
|
||||
arg = ARG_FIND("DESTINATION");
|
||||
assert(arg != NULL);
|
||||
_scr(arg->value, dest, sizeof dest);
|
||||
|
||||
sam_connectback(session, stream_id, dest);
|
||||
|
||||
} else if(ARG_IS(1,"RECEIVED")) {
|
||||
size_t size;
|
||||
void *data;
|
||||
|
||||
p = strrchr(s, '='); /* SIZE= */
|
||||
assert(p != NULL);
|
||||
p++;
|
||||
size = strtol(p, NULL, 10);
|
||||
assert(size != 0);
|
||||
p -= 6;
|
||||
*p = '\0';
|
||||
p = strrchr(s, '='); /* ID= */
|
||||
assert(p != NULL);
|
||||
p++;
|
||||
stream_id = strtol(p, NULL, 10);
|
||||
assert(stream_id != 0);
|
||||
data = malloc(size + 1); /* +1 for NUL termination, so when we are
|
||||
receiving a string it will just work and it
|
||||
won't be necessary to send NUL. When binary
|
||||
data is sent, the extra NUL character will
|
||||
just be ignored by the client program,
|
||||
because it is not added to the size */
|
||||
arg = ARG_FIND("SIZE");
|
||||
assert(arg != NULL);
|
||||
size = strtol_checked(string_data(arg->value));
|
||||
|
||||
data = malloc(size + 1);
|
||||
/* +1 for NUL termination, so when we are
|
||||
receiving a string it will just work and it
|
||||
won't be necessary to send NUL. When binary
|
||||
data is sent, the extra NUL character will
|
||||
just be ignored by the client program,
|
||||
because it is not added to the size */
|
||||
if (data == NULL) {
|
||||
SAMLOGS("Out of memory");
|
||||
abort();
|
||||
}
|
||||
if (sam_read2(session, data, size) != -1) {
|
||||
p = data + size;
|
||||
char* p = data + size;
|
||||
*p = '\0'; /* see above NUL note */
|
||||
sam_databack(session, stream_id, data, size);
|
||||
/* ^^^ `data' must be freed ^^^*/
|
||||
} else
|
||||
free(data);
|
||||
|
||||
return;
|
||||
|
||||
} else if (strncmp(s, SAM_STREAM_STATUS_REPLY,
|
||||
strlen(SAM_STREAM_STATUS_REPLY)) == 0) {
|
||||
char *p;
|
||||
sam_sid_t stream_id;
|
||||
|
||||
p = strchr(s, '='); /* can't use strrchar because of option MESSAGE= */
|
||||
assert(p != NULL); /* RESULT= */
|
||||
p++;
|
||||
p = strchr(p, '='); /* ID= */
|
||||
assert(p != NULL);
|
||||
p++;
|
||||
stream_id = strtol(p, NULL, 10);
|
||||
assert(stream_id != 0);
|
||||
if (strncmp(s, SAM_STREAM_STATUS_REPLY_OK,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_OK)) == 0)
|
||||
sam_statusback(session, stream_id, SAM_OK);
|
||||
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_CRP,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_CRP)) == 0)
|
||||
sam_statusback(session, stream_id, SAM_CANT_REACH_PEER);
|
||||
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_I2E,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_I2E)) == 0)
|
||||
sam_statusback(session, stream_id, SAM_I2P_ERROR);
|
||||
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_IK,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_IK)) == 0)
|
||||
sam_statusback(session, stream_id, SAM_INVALID_KEY);
|
||||
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_TO,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_TO)) == 0)
|
||||
sam_statusback(session, stream_id, SAM_TIMEOUT);
|
||||
else
|
||||
sam_statusback(session, stream_id, SAM_UNKNOWN);
|
||||
|
||||
return;
|
||||
|
||||
} else
|
||||
SAMLOG("Unknown SAM command received: %s", s);
|
||||
|
||||
return;
|
||||
} else if(ARG_IS(1,"STATUS")) {
|
||||
arg = ARG_FIND("RESULT");
|
||||
assert(arg != NULL);
|
||||
if (string_is(arg->value,"OK")) {
|
||||
sam_statusback(session, stream_id, SAM_OK, message);
|
||||
} else if (string_is(arg->value,"CANT_REACH_PEER")) {
|
||||
sam_statusback(session, stream_id,
|
||||
SAM_CANT_REACH_PEER, message);
|
||||
} else if (string_is(arg->value,"I2P_ERROR")) {
|
||||
sam_statusback(session, stream_id, SAM_I2P_ERROR, message);
|
||||
} else if (string_is(arg->value,"INVALID_KEY")) {
|
||||
sam_statusback(session, stream_id, SAM_INVALID_KEY, message);
|
||||
} else if (string_is(arg->value,"TIMEOUT")) {
|
||||
sam_statusback(session, stream_id, SAM_TIMEOUT, message);
|
||||
} else {
|
||||
sam_statusback(session, stream_id, SAM_UNKNOWN, message);
|
||||
}
|
||||
}
|
||||
} else
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef ARG_IS
|
||||
#undef ARG_FIND
|
||||
|
||||
|
||||
/*
|
||||
* Sends data to a destination in a raw packet
|
||||
*
|
||||
|
128
apps/sam/c/src/tinystring.c
Normal file
128
apps/sam/c/src/tinystring.c
Normal file
@ -0,0 +1,128 @@
|
||||
#include "tinystring.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a) > (b) ? (b) : (a))
|
||||
#endif
|
||||
|
||||
extern char *strndup(const char *s, size_t n);
|
||||
|
||||
|
||||
struct string_s {
|
||||
const char* data;
|
||||
long int size;
|
||||
bool _no_del; //SIGH...
|
||||
};
|
||||
|
||||
string_t string_ncreate(const char* cstr,long int length) {
|
||||
string_t self = malloc(sizeof(struct string_s));
|
||||
self->size = length;
|
||||
if(cstr) self->data = strndup(cstr,length);
|
||||
else self->data = NULL;
|
||||
self->_no_del = 0;
|
||||
return self;
|
||||
}
|
||||
|
||||
string_t string_create(const char* cstr) {
|
||||
if(!cstr)
|
||||
return string_ncreate(NULL, 0);
|
||||
return string_ncreate(cstr, strlen(cstr));
|
||||
}
|
||||
|
||||
string_t string_nwrap(const char* cstr, long int length) {
|
||||
static struct string_s self;
|
||||
self.size = length;
|
||||
self.data = cstr;
|
||||
self._no_del = 1;
|
||||
return &self;
|
||||
}
|
||||
|
||||
string_t string_wrap(const char* cstr) {
|
||||
if(!cstr)
|
||||
return string_nwrap(NULL, 0);
|
||||
return string_nwrap(cstr, strlen(cstr));
|
||||
}
|
||||
|
||||
string_t string_fmt(const char* fmt, ...) {
|
||||
va_list args;
|
||||
FILE* tmp = tmpfile();
|
||||
string_t self = malloc(sizeof(struct string_s));
|
||||
char* data;
|
||||
va_start(args, fmt);
|
||||
vfprintf(tmp, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
self->size = ftell(tmp);
|
||||
|
||||
rewind(tmp);
|
||||
data = malloc(self->size);
|
||||
fread(data, self->size, sizeof(char), tmp);
|
||||
|
||||
fclose(tmp);
|
||||
self->data = data;
|
||||
return self;
|
||||
}
|
||||
|
||||
string_t string_cat(string_t head,string_t tail) {
|
||||
//There are two ways to skin a cat...
|
||||
string_t self = malloc(sizeof(struct string_s));
|
||||
char* data;
|
||||
self->size = head->size+tail->size;
|
||||
data = malloc(self->size);
|
||||
memcpy(data, head->data, head->size);
|
||||
memcpy(data+head->size,tail->data,tail->size);
|
||||
self->data = data;
|
||||
return self;
|
||||
}
|
||||
|
||||
/* Source Dest */
|
||||
void string_copy(string_t src,string_t dest) {
|
||||
dest->data = realloc((char*)dest->data,src->size);
|
||||
memcpy((char*)dest->data,src->data,dest->size);
|
||||
}
|
||||
|
||||
void string_copy_raw(string_t src, void* dest,size_t size) {
|
||||
size = min(src->size,size);
|
||||
memcpy(dest,src->data,size);
|
||||
}
|
||||
|
||||
const char* string_data(string_t self) {
|
||||
return self->data;
|
||||
}
|
||||
|
||||
long int string_size(string_t self) {
|
||||
return self->size;
|
||||
}
|
||||
|
||||
void string_free(string_t self) {
|
||||
if(!self->_no_del)
|
||||
free((char*)self->data);
|
||||
|
||||
free(self);
|
||||
}
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
bool string_equal(string_t this,string_t that) {
|
||||
return !memcmp(this->data,that->data,min(this->size,that->size));
|
||||
}
|
||||
|
||||
bool string_equali(string_t this,string_t that) {
|
||||
return !strncasecmp(this->data,that->data,min(this->size,that->size));
|
||||
}
|
||||
|
||||
int string_cmp(string_t this,string_t that) {
|
||||
return memcmp(this->data,that->data,min(this->size,that->size));
|
||||
}
|
||||
|
||||
int string_cmpi(string_t this,string_t that) {
|
||||
return strncasecmp(this->data,that->data,min(this->size,that->size));
|
||||
}
|
4
apps/sam/c/status
Normal file
4
apps/sam/c/status
Normal file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
printf "%-8s ",uc(shift @ARGV);
|
||||
print join(' ', @ARGV),"\n";
|
@ -65,14 +65,15 @@ public class Connection {
|
||||
private Object _connectLock;
|
||||
/** how many messages have been resent and not yet ACKed? */
|
||||
private int _activeResends;
|
||||
private ConEvent _connectionEvent;
|
||||
|
||||
private long _lifetimeBytesSent;
|
||||
private long _lifetimeBytesReceived;
|
||||
private long _lifetimeDupMessageSent;
|
||||
private long _lifetimeDupMessageReceived;
|
||||
|
||||
public static final long MAX_RESEND_DELAY = 20*1000;
|
||||
public static final long MIN_RESEND_DELAY = 10*1000;
|
||||
public static final long MAX_RESEND_DELAY = 10*1000;
|
||||
public static final long MIN_RESEND_DELAY = 3*1000;
|
||||
|
||||
/** wait up to 5 minutes after disconnection so we can ack/close packets */
|
||||
public static int DISCONNECT_TIMEOUT = 5*60*1000;
|
||||
@ -116,9 +117,12 @@ public class Connection {
|
||||
_connectLock = new Object();
|
||||
_activeResends = 0;
|
||||
_resetSentOn = -1;
|
||||
_connectionEvent = new ConEvent();
|
||||
_context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.chokeSizeBegin", "How many messages were outstanding when we started to choke?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.chokeSizeEnd", "How many messages were outstanding when we stopped being choked?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("New connection created with options: " + _options);
|
||||
}
|
||||
|
||||
public long getNextOutboundPacketNum() {
|
||||
@ -152,7 +156,8 @@ public class Connection {
|
||||
if (!_connected)
|
||||
return false;
|
||||
started = true;
|
||||
if ( (_outboundPackets.size() >= _options.getWindowSize()) || (_activeResends > 0) ) {
|
||||
if ( (_outboundPackets.size() >= _options.getWindowSize()) || (_activeResends > 0) ||
|
||||
(_lastSendId - _highestAckedThrough > _options.getWindowSize()) ) {
|
||||
if (writeExpire > 0) {
|
||||
if (timeLeft <= 0) {
|
||||
_log.error("Outbound window is full of " + _outboundPackets.size()
|
||||
@ -802,9 +807,29 @@ public class Connection {
|
||||
buf.append(" close received");
|
||||
buf.append(" acked packets ").append(getAckedPackets());
|
||||
|
||||
buf.append(" maxWin ").append(getOptions().getMaxWindowSize());
|
||||
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public SimpleTimer.TimedEvent getConnectionEvent() { return _connectionEvent; }
|
||||
|
||||
/**
|
||||
* fired to reschedule event notification
|
||||
*/
|
||||
class ConEvent implements SimpleTimer.TimedEvent {
|
||||
private Exception _addedBy;
|
||||
public ConEvent() {
|
||||
//_addedBy = new Exception("added by");
|
||||
}
|
||||
public void timeReached() {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("firing event on " + _connection, _addedBy);
|
||||
eventOccurred();
|
||||
}
|
||||
public String toString() { return "event on " + Connection.this.toString(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Coordinate the resends of a given packet
|
||||
@ -864,14 +889,15 @@ public class Connection {
|
||||
newWindowSize /= 2;
|
||||
if (newWindowSize <= 0)
|
||||
newWindowSize = 1;
|
||||
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Congestion resending packet " + _packet.getSequenceNum() + ": new windowSize " + newWindowSize
|
||||
+ ") for " + Connection.this.toString());
|
||||
|
||||
|
||||
// setRTT has its own ceiling
|
||||
getOptions().setRTT(getOptions().getRTT() + 10*1000);
|
||||
getOptions().setWindowSize(newWindowSize);
|
||||
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Congestion resending packet " + _packet.getSequenceNum() + ": new windowSize " + newWindowSize
|
||||
+ "/" + getOptions().getWindowSize() + ") for " + Connection.this.toString());
|
||||
|
||||
windowAdjusted();
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ public class ConnectionManager {
|
||||
_context.statManager().createRateStat("stream.con.lifetimeRTT", "What is the final RTT when a stream closes?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.lifetimeCongestionSeenAt", "When was the last congestion seen at when a stream closes?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.lifetimeSendWindowSize", "What is the final send window size when a stream closes?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.receiveActive", "How many streams are active when a new one is received (period being not yet dropped)", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
}
|
||||
|
||||
Connection getConnectionByInboundId(byte[] id) {
|
||||
@ -109,7 +110,14 @@ public class ConnectionManager {
|
||||
byte receiveId[] = new byte[4];
|
||||
_context.random().nextBytes(receiveId);
|
||||
boolean reject = false;
|
||||
int active = 0;
|
||||
int total = 0;
|
||||
synchronized (_connectionLock) {
|
||||
total = _connectionByInboundId.size();
|
||||
for (Iterator iter = _connectionByInboundId.values().iterator(); iter.hasNext(); ) {
|
||||
if ( ((Connection)iter.next()).getIsConnected() )
|
||||
active++;
|
||||
}
|
||||
if (locked_tooManyStreams()) {
|
||||
reject = true;
|
||||
} else {
|
||||
@ -121,12 +129,16 @@ public class ConnectionManager {
|
||||
} else {
|
||||
_connectionByInboundId.put(ba, oldCon);
|
||||
// receiveId already taken, try another
|
||||
// (need to realloc receiveId, as ba.getData() points to the old value)
|
||||
receiveId = new byte[4];
|
||||
_context.random().nextBytes(receiveId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_context.statManager().addRateData("stream.receiveActive", active, total);
|
||||
|
||||
if (reject) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Refusing connection since we have exceeded our max of "
|
||||
@ -227,6 +239,8 @@ public class ConnectionManager {
|
||||
}
|
||||
|
||||
private boolean locked_tooManyStreams() {
|
||||
if (_maxConcurrentStreams <= 0) return false;
|
||||
if (_connectionByInboundId.size() < _maxConcurrentStreams) return false;
|
||||
int active = 0;
|
||||
for (Iterator iter = _connectionByInboundId.values().iterator(); iter.hasNext(); ) {
|
||||
Connection con = (Connection)iter.next();
|
||||
@ -238,8 +252,6 @@ public class ConnectionManager {
|
||||
_log.info("More than 100 connections! " + active
|
||||
+ " total: " + _connectionByInboundId.size());
|
||||
|
||||
if (_maxConcurrentStreams <= 0) return false;
|
||||
if (_connectionByInboundId.size() < _maxConcurrentStreams) return false;
|
||||
return (active >= _maxConcurrentStreams);
|
||||
}
|
||||
|
||||
|
@ -98,8 +98,8 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 5*60*1000));
|
||||
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_DISCONNECT));
|
||||
setInboundBufferSize(getMaxMessageSize() * (Connection.MAX_WINDOW_SIZE + 2));
|
||||
setCongestionAvoidanceGrowthRateFactor(getInt(opts, PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR, 2));
|
||||
setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, 2));
|
||||
setCongestionAvoidanceGrowthRateFactor(getInt(opts, PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR, 1));
|
||||
setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, 1));
|
||||
|
||||
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
|
||||
setMaxWindowSize(getInt(opts, PROP_MAX_WINDOW_SIZE, Connection.MAX_WINDOW_SIZE));
|
||||
|
@ -33,7 +33,7 @@ public class ConnectionPacketHandler {
|
||||
boolean ok = verifyPacket(packet, con);
|
||||
if (!ok) {
|
||||
if ( (!packet.isFlagSet(Packet.FLAG_RESET)) && (_log.shouldLog(Log.ERROR)) )
|
||||
_log.error("Packet does NOT verify: " + packet);
|
||||
_log.error("Packet does NOT verify: " + packet + " on " + con);
|
||||
packet.releasePayload();
|
||||
return;
|
||||
}
|
||||
@ -167,11 +167,14 @@ public class ConnectionPacketHandler {
|
||||
// non-ack message payloads are queued in the MessageInputStream
|
||||
packet.releasePayload();
|
||||
}
|
||||
|
||||
//if (choke)
|
||||
// con.fastRetransmit();
|
||||
}
|
||||
|
||||
private boolean ack(Connection con, long ackThrough, long nacks[], Packet packet, boolean isNew, boolean choke) {
|
||||
if ( (nacks != null) && (nacks.length > 0) )
|
||||
con.getOptions().setRTT(con.getOptions().getRTT() + nacks.length*1000);
|
||||
//if ( (nacks != null) && (nacks.length > 0) )
|
||||
// con.getOptions().setRTT(con.getOptions().getRTT() + nacks.length*1000);
|
||||
|
||||
int numResends = 0;
|
||||
List acked = con.ackPackets(ackThrough, nacks);
|
||||
@ -224,15 +227,17 @@ public class ConnectionPacketHandler {
|
||||
oldSize >>>= 1;
|
||||
if (oldSize <= 0)
|
||||
oldSize = 1;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Congestion occurred - new windowSize " + oldSize + " congestionSeenAt: "
|
||||
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
|
||||
+ ") for " + con);
|
||||
|
||||
// setRTT has its own ceiling
|
||||
con.getOptions().setRTT(con.getOptions().getRTT() + 10*1000);
|
||||
con.getOptions().setWindowSize(oldSize);
|
||||
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Congestion occurred - new windowSize " + oldSize + " / " + con.getOptions().getWindowSize() + " congestionSeenAt: "
|
||||
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
|
||||
+ ") for " + con);
|
||||
|
||||
|
||||
congested = true;
|
||||
}
|
||||
|
||||
@ -253,7 +258,7 @@ public class ConnectionPacketHandler {
|
||||
newWindowSize += 1;
|
||||
} else {
|
||||
// slow start, but modified to take into account the fact
|
||||
// that windows in the streaming lib are messages, not bytes,
|
||||
// that windows in the streaming lib are messages, not bytes,
|
||||
// so we only grow 1 every N times (where N = the slow start factor)
|
||||
int shouldIncrement = _context.random().nextInt(con.getOptions().getSlowStartGrowthRateFactor());
|
||||
if (shouldIncrement <= 0)
|
||||
@ -263,13 +268,14 @@ public class ConnectionPacketHandler {
|
||||
|
||||
if (newWindowSize <= 0)
|
||||
newWindowSize = 1;
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("New window size " + newWindowSize + "/" + oldWindow + " congestionSeenAt: "
|
||||
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
|
||||
+ ") for " + con);
|
||||
|
||||
con.getOptions().setWindowSize(newWindowSize);
|
||||
con.setCongestionWindowEnd(newWindowSize + lowest);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("New window size " + newWindowSize + "/" + oldWindow + "/" + con.getOptions().getWindowSize() + " congestionSeenAt: "
|
||||
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
|
||||
+ ") for " + con);
|
||||
}
|
||||
|
||||
con.windowAdjusted();
|
||||
@ -299,16 +305,16 @@ public class ConnectionPacketHandler {
|
||||
if (packet.getSequenceNum() <= 2) {
|
||||
return true;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Packet without RST or SYN where we dont know stream ID: "
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Packet without RST or SYN where we dont know stream ID: "
|
||||
+ packet);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!DataHelper.eq(con.getSendStreamId(), packet.getReceiveStreamId())) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Packet received with the wrong reply stream id: "
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Packet received with the wrong reply stream id: "
|
||||
+ con + " / " + packet);
|
||||
return false;
|
||||
} else {
|
||||
@ -325,8 +331,8 @@ public class ConnectionPacketHandler {
|
||||
if (DataHelper.eq(con.getReceiveStreamId(), packet.getSendStreamId())) {
|
||||
boolean ok = packet.verifySignature(_context, packet.getOptionalFrom(), null);
|
||||
if (!ok) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Received unsigned / forged RST on " + con);
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Received unsigned / forged RST on " + con);
|
||||
return;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
|
@ -91,8 +91,8 @@ public class MessageHandler implements I2PSessionListener {
|
||||
*
|
||||
*/
|
||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("error occurred: " + message + "- " + error.getMessage());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("error occurred: " + message + "- " + error.getMessage());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("cause", error);
|
||||
//_manager.disconnectAllHard();
|
||||
|
@ -38,6 +38,10 @@ public class MessageOutputStream extends OutputStream {
|
||||
* size
|
||||
*/
|
||||
private volatile int _nextBufferSize;
|
||||
// rate calc helpers
|
||||
private long _sendPeriodBeginTime;
|
||||
private long _sendPeriodBytes;
|
||||
private int _sendBps;
|
||||
|
||||
public MessageOutputStream(I2PAppContext ctx, DataReceiver receiver) {
|
||||
this(ctx, receiver, Packet.MAX_PAYLOAD_SIZE);
|
||||
@ -55,6 +59,10 @@ public class MessageOutputStream extends OutputStream {
|
||||
_writeTimeout = -1;
|
||||
_passiveFlushDelay = 500;
|
||||
_nextBufferSize = -1;
|
||||
_sendPeriodBeginTime = ctx.clock().now();
|
||||
_sendPeriodBytes = 0;
|
||||
_sendBps = 0;
|
||||
_context.statManager().createRateStat("stream.sendBps", "How fast we pump data through the stream", "Stream", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
|
||||
_flusher = new Flusher();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("MessageOutputStream created");
|
||||
@ -137,6 +145,21 @@ public class MessageOutputStream extends OutputStream {
|
||||
if ( (elapsed > 10*1000) && (_log.shouldLog(Log.DEBUG)) )
|
||||
_log.debug("wtf, took " + elapsed + "ms to write to the stream?", new Exception("foo"));
|
||||
throwAnyError();
|
||||
updateBps(len);
|
||||
}
|
||||
|
||||
private void updateBps(int len) {
|
||||
long now = _context.clock().now();
|
||||
int periods = (int)Math.floor((now - _sendPeriodBeginTime) / 1000d);
|
||||
if (periods > 0) {
|
||||
// first term decays on slow transmission
|
||||
_sendBps = (int)(((float)0.9f*((float)_sendBps/(float)periods)) + ((float)0.1f*((float)_sendPeriodBytes/(float)periods)));
|
||||
_sendPeriodBytes = len;
|
||||
_sendPeriodBeginTime = now;
|
||||
_context.statManager().addRateData("stream.sendBps", _sendBps, 0);
|
||||
} else {
|
||||
_sendPeriodBytes += len;
|
||||
}
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
|
@ -119,6 +119,19 @@ public class PacketHandler {
|
||||
}
|
||||
|
||||
private void receiveKnownCon(Connection con, Packet packet) {
|
||||
if (packet.isFlagSet(Packet.FLAG_ECHO)) {
|
||||
if (packet.getSendStreamId() != null) {
|
||||
receivePing(packet);
|
||||
} else if (packet.getReceiveStreamId() != null) {
|
||||
receivePong(packet);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Echo packet received with no stream IDs: " + packet);
|
||||
}
|
||||
packet.releasePayload();
|
||||
return;
|
||||
}
|
||||
|
||||
// the packet is pointed at a stream ID we're receiving on
|
||||
if (isValidMatch(con.getSendStreamId(), packet.getReceiveStreamId())) {
|
||||
// the packet's receive stream ID also matches what we expect
|
||||
@ -163,8 +176,19 @@ public class PacketHandler {
|
||||
} else {
|
||||
if (!con.getResetSent()) {
|
||||
// someone is sending us a packet on the wrong stream
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Received a packet on the wrong stream: " + packet + " connection: " + con);
|
||||
if (_log.shouldLog(Log.ERROR)) {
|
||||
Set cons = _manager.listConnections();
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("Received a packet on the wrong stream: ");
|
||||
buf.append(packet);
|
||||
buf.append(" connection: ");
|
||||
buf.append(con);
|
||||
for (Iterator iter = cons.iterator(); iter.hasNext();) {
|
||||
Connection cur = (Connection)iter.next();
|
||||
buf.append(" ").append(cur);
|
||||
}
|
||||
_log.error(buf.toString(), new Exception("Wrong stream"));
|
||||
}
|
||||
}
|
||||
packet.releasePayload();
|
||||
}
|
||||
|
@ -17,21 +17,6 @@ abstract class SchedulerImpl implements TaskScheduler {
|
||||
}
|
||||
|
||||
protected void reschedule(long msToWait, Connection con) {
|
||||
SimpleTimer.getInstance().addEvent(new ConEvent(con), msToWait);
|
||||
}
|
||||
|
||||
private class ConEvent implements SimpleTimer.TimedEvent {
|
||||
private Connection _connection;
|
||||
private Exception _addedBy;
|
||||
public ConEvent(Connection con) {
|
||||
_connection = con;
|
||||
//_addedBy = new Exception("added by");
|
||||
}
|
||||
public void timeReached() {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("firing event on " + _connection, _addedBy);
|
||||
_connection.eventOccurred();
|
||||
}
|
||||
public String toString() { return "event on " + _connection; }
|
||||
SimpleTimer.getInstance().addEvent(con.getConnectionEvent(), msToWait);
|
||||
}
|
||||
}
|
||||
|
@ -1,239 +1,239 @@
|
||||
/*
|
||||
* Created on Nov 9, 2004
|
||||
*
|
||||
* This file is part of susimail project, see http://susi.i2p/
|
||||
*
|
||||
* Copyright (C) 2004-2005 <susi23@mail.i2p>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.1 $
|
||||
*/
|
||||
package i2p.susi.webmail;
|
||||
|
||||
import i2p.susi.util.Config;
|
||||
import i2p.susi.util.ReadBuffer;
|
||||
import i2p.susi.webmail.encoding.Encoding;
|
||||
import i2p.susi.webmail.encoding.EncodingFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* data structure to hold a single message, mostly used with folder view and sorting
|
||||
*
|
||||
* @author susi
|
||||
*/
|
||||
public class Mail {
|
||||
|
||||
public static final String DATEFORMAT = "date.format";
|
||||
|
||||
public static final String unknown = "unknown";
|
||||
|
||||
public int id, size;
|
||||
public String sender, reply, subject, dateString,
|
||||
formattedSender, formattedSubject, formattedDate,
|
||||
shortSender, shortSubject, quotedDate, uidl;
|
||||
public Date date;
|
||||
public ReadBuffer header, body;
|
||||
public MailPart part;
|
||||
Object[] to, cc;
|
||||
|
||||
public String error;
|
||||
|
||||
public boolean markForDeletion;
|
||||
|
||||
public boolean deleted;
|
||||
|
||||
public Mail() {
|
||||
id = 0;
|
||||
size = 0;
|
||||
formattedSender = unknown;
|
||||
formattedSubject = unknown;
|
||||
formattedDate = unknown;
|
||||
shortSender = unknown;
|
||||
shortSubject = unknown;
|
||||
quotedDate = unknown;
|
||||
error = "";
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
public static boolean validateAddress( String address )
|
||||
{
|
||||
if( address == null || address.length() == 0 )
|
||||
return false;
|
||||
|
||||
address = address.trim();
|
||||
|
||||
if( address.indexOf( "\n" ) != -1 ||
|
||||
address.indexOf( "\r" ) != -1 )
|
||||
return false;
|
||||
|
||||
String[] tokens = address.split( "[ \t]+" );
|
||||
|
||||
int addresses = 0;
|
||||
|
||||
for( int i = 0; i < tokens.length; i++ ) {
|
||||
if( tokens[i].matches( "^[^@< \t]+@[^> \t]+$" ) ||
|
||||
tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
|
||||
addresses++;
|
||||
}
|
||||
return addresses == 1;
|
||||
}
|
||||
/**
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
public static String getAddress(String address )
|
||||
{
|
||||
String[] tokens = address.split( "[ \t]+" );
|
||||
|
||||
for( int i = 0; i < tokens.length; i++ ) {
|
||||
if( tokens[i].matches( "^[^@< \t]+@[^> \t]+$" ) )
|
||||
return "<" + tokens[i] + ">";
|
||||
if( tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
|
||||
return tokens[i];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static boolean getRecipientsFromList( ArrayList recipients, String text, boolean ok )
|
||||
{
|
||||
if( text != null && text.length() > 0 ) {
|
||||
String[] ccs = text.split( "," );
|
||||
for( int i = 0; i < ccs.length; i++ ) {
|
||||
String recipient = ccs[i].trim();
|
||||
if( validateAddress( recipient ) ) {
|
||||
String str = getAddress( recipient );
|
||||
if( str != null && str.length() > 0 ) {
|
||||
recipients.add( str );
|
||||
}
|
||||
else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
public static void appendRecipients( StringBuffer buf, ArrayList recipients, String prefix )
|
||||
{
|
||||
for( Iterator it = recipients.iterator(); it.hasNext(); ) {
|
||||
buf.append( prefix );
|
||||
prefix ="\t";
|
||||
buf.append( (String)it.next() );
|
||||
buf.append( "\r\n" );
|
||||
}
|
||||
}
|
||||
public void parseHeaders()
|
||||
{
|
||||
DateFormat dateFormatter = new SimpleDateFormat( Config.getProperty( DATEFORMAT, "mm/dd/yyyy HH:mm:ss" ) );
|
||||
DateFormat mailDateFormatter = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH );
|
||||
|
||||
error = "";
|
||||
if( header != null ) {
|
||||
|
||||
boolean ok = true;
|
||||
|
||||
Encoding html = EncodingFactory.getEncoding( "HTML" );
|
||||
|
||||
if( html == null ) {
|
||||
error += "HTML encoder not found.<br>";
|
||||
ok = false;
|
||||
}
|
||||
|
||||
Encoding hl = EncodingFactory.getEncoding( "HEADERLINE" );
|
||||
|
||||
if( hl == null ) {
|
||||
error += "Header line encoder not found.<br>";
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if( ok ) {
|
||||
|
||||
try {
|
||||
ReadBuffer decoded = hl.decode( header );
|
||||
BufferedReader reader = new BufferedReader( new InputStreamReader( new ByteArrayInputStream( decoded.content, decoded.offset, decoded.length ), "ISO-8859-1" ) );
|
||||
String line;
|
||||
while( ( line = reader.readLine() ) != null ) {
|
||||
if( line.length() == 0 )
|
||||
break;
|
||||
|
||||
if( line.startsWith( "From:" ) ) {
|
||||
sender = line.substring( 5 ).trim();
|
||||
formattedSender = getAddress( sender );
|
||||
shortSender = formattedSender.trim();
|
||||
if( shortSender.length() > 40 ) {
|
||||
shortSender = shortSender.substring( 0, 37 ).trim() + "...";
|
||||
}
|
||||
shortSender = html.encode( shortSender );
|
||||
}
|
||||
else if( line.startsWith( "Date:" ) ) {
|
||||
dateString = line.substring( 5 ).trim();
|
||||
try {
|
||||
date = mailDateFormatter.parse( dateString );
|
||||
formattedDate = dateFormatter.format( date );
|
||||
quotedDate = html.encode( dateString );
|
||||
}
|
||||
catch (ParseException e) {
|
||||
date = null;
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else if( line.startsWith( "Subject:" ) ) {
|
||||
subject = line.substring( 8 ).trim();
|
||||
formattedSubject = subject;
|
||||
shortSubject = formattedSubject;
|
||||
if( formattedSubject.length() > 60 )
|
||||
shortSubject = formattedSubject.substring( 0, 57 ).trim() + "...";
|
||||
shortSubject = html.encode( shortSubject );
|
||||
}
|
||||
else if( line.toLowerCase().startsWith( "Reply-To:" ) ) {
|
||||
reply = Mail.getAddress( line.substring( 9 ).trim() );
|
||||
}
|
||||
else if( line.startsWith( "To:" ) ) {
|
||||
ArrayList list = new ArrayList();
|
||||
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
|
||||
to = list.toArray();
|
||||
}
|
||||
else if( line.startsWith( "Cc:" ) ) {
|
||||
ArrayList list = new ArrayList();
|
||||
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
|
||||
cc = list.toArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception e ) {
|
||||
error += "Error parsing mail header: " + e.getClass().getName() + "<br>";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Created on Nov 9, 2004
|
||||
*
|
||||
* This file is part of susimail project, see http://susi.i2p/
|
||||
*
|
||||
* Copyright (C) 2004-2005 <susi23@mail.i2p>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.2 $
|
||||
*/
|
||||
package i2p.susi.webmail;
|
||||
|
||||
import i2p.susi.util.Config;
|
||||
import i2p.susi.util.ReadBuffer;
|
||||
import i2p.susi.webmail.encoding.Encoding;
|
||||
import i2p.susi.webmail.encoding.EncodingFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* data structure to hold a single message, mostly used with folder view and sorting
|
||||
*
|
||||
* @author susi
|
||||
*/
|
||||
public class Mail {
|
||||
|
||||
public static final String DATEFORMAT = "date.format";
|
||||
|
||||
public static final String unknown = "unknown";
|
||||
|
||||
public int id, size;
|
||||
public String sender, reply, subject, dateString,
|
||||
formattedSender, formattedSubject, formattedDate,
|
||||
shortSender, shortSubject, quotedDate, uidl;
|
||||
public Date date;
|
||||
public ReadBuffer header, body;
|
||||
public MailPart part;
|
||||
Object[] to, cc;
|
||||
|
||||
public String error;
|
||||
|
||||
public boolean markForDeletion;
|
||||
|
||||
public boolean deleted;
|
||||
|
||||
public Mail() {
|
||||
id = 0;
|
||||
size = 0;
|
||||
formattedSender = unknown;
|
||||
formattedSubject = unknown;
|
||||
formattedDate = unknown;
|
||||
shortSender = unknown;
|
||||
shortSubject = unknown;
|
||||
quotedDate = unknown;
|
||||
error = "";
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
public static boolean validateAddress( String address )
|
||||
{
|
||||
if( address == null || address.length() == 0 )
|
||||
return false;
|
||||
|
||||
address = address.trim();
|
||||
|
||||
if( address.indexOf( "\n" ) != -1 ||
|
||||
address.indexOf( "\r" ) != -1 )
|
||||
return false;
|
||||
|
||||
String[] tokens = address.split( "[ \t]+" );
|
||||
|
||||
int addresses = 0;
|
||||
|
||||
for( int i = 0; i < tokens.length; i++ ) {
|
||||
if( tokens[i].matches( "^[^@< \t]+@[^> \t]+$" ) ||
|
||||
tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
|
||||
addresses++;
|
||||
}
|
||||
return addresses == 1;
|
||||
}
|
||||
/**
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
public static String getAddress(String address )
|
||||
{
|
||||
String[] tokens = address.split( "[ \t]+" );
|
||||
|
||||
for( int i = 0; i < tokens.length; i++ ) {
|
||||
if( tokens[i].matches( "^[^@< \t]+@[^> \t]+$" ) )
|
||||
return "<" + tokens[i] + ">";
|
||||
if( tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
|
||||
return tokens[i];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static boolean getRecipientsFromList( ArrayList recipients, String text, boolean ok )
|
||||
{
|
||||
if( text != null && text.length() > 0 ) {
|
||||
String[] ccs = text.split( "," );
|
||||
for( int i = 0; i < ccs.length; i++ ) {
|
||||
String recipient = ccs[i].trim();
|
||||
if( validateAddress( recipient ) ) {
|
||||
String str = getAddress( recipient );
|
||||
if( str != null && str.length() > 0 ) {
|
||||
recipients.add( str );
|
||||
}
|
||||
else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
public static void appendRecipients( StringBuffer buf, ArrayList recipients, String prefix )
|
||||
{
|
||||
for( Iterator it = recipients.iterator(); it.hasNext(); ) {
|
||||
buf.append( prefix );
|
||||
prefix ="\t";
|
||||
buf.append( (String)it.next() );
|
||||
buf.append( "\r\n" );
|
||||
}
|
||||
}
|
||||
public void parseHeaders()
|
||||
{
|
||||
DateFormat dateFormatter = new SimpleDateFormat( Config.getProperty( DATEFORMAT, "mm/dd/yyyy HH:mm:ss" ) );
|
||||
DateFormat mailDateFormatter = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH );
|
||||
|
||||
error = "";
|
||||
if( header != null ) {
|
||||
|
||||
boolean ok = true;
|
||||
|
||||
Encoding html = EncodingFactory.getEncoding( "HTML" );
|
||||
|
||||
if( html == null ) {
|
||||
error += "HTML encoder not found.<br>";
|
||||
ok = false;
|
||||
}
|
||||
|
||||
Encoding hl = EncodingFactory.getEncoding( "HEADERLINE" );
|
||||
|
||||
if( hl == null ) {
|
||||
error += "Header line encoder not found.<br>";
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if( ok ) {
|
||||
|
||||
try {
|
||||
ReadBuffer decoded = hl.decode( header );
|
||||
BufferedReader reader = new BufferedReader( new InputStreamReader( new ByteArrayInputStream( decoded.content, decoded.offset, decoded.length ), "ISO-8859-1" ) );
|
||||
String line;
|
||||
while( ( line = reader.readLine() ) != null ) {
|
||||
if( line.length() == 0 )
|
||||
break;
|
||||
|
||||
if( line.startsWith( "From:" ) ) {
|
||||
sender = line.substring( 5 ).trim();
|
||||
formattedSender = getAddress( sender );
|
||||
shortSender = formattedSender.trim();
|
||||
if( shortSender.length() > 40 ) {
|
||||
shortSender = shortSender.substring( 0, 37 ).trim() + "...";
|
||||
}
|
||||
shortSender = html.encode( shortSender );
|
||||
}
|
||||
else if( line.startsWith( "Date:" ) ) {
|
||||
dateString = line.substring( 5 ).trim();
|
||||
try {
|
||||
date = mailDateFormatter.parse( dateString );
|
||||
formattedDate = dateFormatter.format( date );
|
||||
quotedDate = html.encode( dateString );
|
||||
}
|
||||
catch (ParseException e) {
|
||||
date = null;
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else if( line.startsWith( "Subject:" ) ) {
|
||||
subject = line.substring( 8 ).trim();
|
||||
formattedSubject = subject;
|
||||
shortSubject = formattedSubject;
|
||||
if( formattedSubject.length() > 60 )
|
||||
shortSubject = formattedSubject.substring( 0, 57 ).trim() + "...";
|
||||
shortSubject = html.encode( shortSubject );
|
||||
}
|
||||
else if( line.toLowerCase().startsWith( "Reply-To:" ) ) {
|
||||
reply = Mail.getAddress( line.substring( 9 ).trim() );
|
||||
}
|
||||
else if( line.startsWith( "To:" ) ) {
|
||||
ArrayList list = new ArrayList();
|
||||
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
|
||||
to = list.toArray();
|
||||
}
|
||||
else if( line.startsWith( "Cc:" ) ) {
|
||||
ArrayList list = new ArrayList();
|
||||
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
|
||||
cc = list.toArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception e ) {
|
||||
error += "Error parsing mail header: " + e.getClass().getName() + "<br>";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,102 +1,102 @@
|
||||
/*
|
||||
* Created on Nov 23, 2004
|
||||
*
|
||||
* This file is part of susimail project, see http://susi.i2p/
|
||||
*
|
||||
* Copyright (C) 2004-2005 <susi23@mail.i2p>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.1 $
|
||||
*/
|
||||
package i2p.susi.webmail;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import i2p.susi.webmail.pop3.POP3MailBox;
|
||||
|
||||
/**
|
||||
* @author user
|
||||
*/
|
||||
public class MailCache {
|
||||
|
||||
public static final boolean FETCH_HEADER = true;
|
||||
public static final boolean FETCH_ALL = false;
|
||||
|
||||
private POP3MailBox mailbox;
|
||||
private String error;
|
||||
private Hashtable mails;
|
||||
private Object synchronizer;
|
||||
|
||||
MailCache( POP3MailBox mailbox ) {
|
||||
this.mailbox = mailbox;
|
||||
mails = new Hashtable();
|
||||
synchronizer = new Object();
|
||||
}
|
||||
/**
|
||||
* Fetch any needed data from pop3 server.
|
||||
*
|
||||
* @param id message id to get
|
||||
* @param headerOnly fetch only header lines?
|
||||
* @return
|
||||
*/
|
||||
public Mail getMail( String uidl, boolean headerOnly ) {
|
||||
|
||||
Mail mail = null, newMail = null;
|
||||
|
||||
if( mailbox != null ) {
|
||||
/*
|
||||
* synchronize update to hashtable
|
||||
*/
|
||||
synchronized( synchronizer ) {
|
||||
|
||||
mail = (Mail)mails.get( uidl );
|
||||
|
||||
if( mail == null ) {
|
||||
newMail = new Mail();
|
||||
mails.put( uidl, newMail );
|
||||
}
|
||||
}
|
||||
if( mail == null ) {
|
||||
mail = newMail;
|
||||
mail.uidl = uidl;
|
||||
mail.size = mailbox.getSize( uidl );
|
||||
}
|
||||
if( mail.size < 1024 )
|
||||
headerOnly = false;
|
||||
|
||||
boolean parseHeaders = mail.header == null;
|
||||
|
||||
if( headerOnly ) {
|
||||
if( mail.header == null )
|
||||
mail.header = mailbox.getHeader( uidl );
|
||||
}
|
||||
else {
|
||||
if( mail.body == null ) {
|
||||
mail.body = mailbox.getBody( uidl );
|
||||
if( mail.body != null ) {
|
||||
mail.header = mail.body;
|
||||
MailPart.parse( mail );
|
||||
}
|
||||
}
|
||||
}
|
||||
if( parseHeaders && mail.header != null )
|
||||
mail.parseHeaders();
|
||||
}
|
||||
if( mail != null && mail.deleted )
|
||||
mail = null;
|
||||
return mail;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Created on Nov 23, 2004
|
||||
*
|
||||
* This file is part of susimail project, see http://susi.i2p/
|
||||
*
|
||||
* Copyright (C) 2004-2005 <susi23@mail.i2p>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.2 $
|
||||
*/
|
||||
package i2p.susi.webmail;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import i2p.susi.webmail.pop3.POP3MailBox;
|
||||
|
||||
/**
|
||||
* @author user
|
||||
*/
|
||||
public class MailCache {
|
||||
|
||||
public static final boolean FETCH_HEADER = true;
|
||||
public static final boolean FETCH_ALL = false;
|
||||
|
||||
private POP3MailBox mailbox;
|
||||
private String error;
|
||||
private Hashtable mails;
|
||||
private Object synchronizer;
|
||||
|
||||
MailCache( POP3MailBox mailbox ) {
|
||||
this.mailbox = mailbox;
|
||||
mails = new Hashtable();
|
||||
synchronizer = new Object();
|
||||
}
|
||||
/**
|
||||
* Fetch any needed data from pop3 server.
|
||||
*
|
||||
* @param id message id to get
|
||||
* @param headerOnly fetch only header lines?
|
||||
* @return
|
||||
*/
|
||||
public Mail getMail( String uidl, boolean headerOnly ) {
|
||||
|
||||
Mail mail = null, newMail = null;
|
||||
|
||||
if( mailbox != null ) {
|
||||
/*
|
||||
* synchronize update to hashtable
|
||||
*/
|
||||
synchronized( synchronizer ) {
|
||||
|
||||
mail = (Mail)mails.get( uidl );
|
||||
|
||||
if( mail == null ) {
|
||||
newMail = new Mail();
|
||||
mails.put( uidl, newMail );
|
||||
}
|
||||
}
|
||||
if( mail == null ) {
|
||||
mail = newMail;
|
||||
mail.uidl = uidl;
|
||||
mail.size = mailbox.getSize( uidl );
|
||||
}
|
||||
if( mail.size < 1024 )
|
||||
headerOnly = false;
|
||||
|
||||
boolean parseHeaders = mail.header == null;
|
||||
|
||||
if( headerOnly ) {
|
||||
if( mail.header == null )
|
||||
mail.header = mailbox.getHeader( uidl );
|
||||
}
|
||||
else {
|
||||
if( mail.body == null ) {
|
||||
mail.body = mailbox.getBody( uidl );
|
||||
if( mail.body != null ) {
|
||||
mail.header = mail.body;
|
||||
MailPart.parse( mail );
|
||||
}
|
||||
}
|
||||
}
|
||||
if( parseHeaders && mail.header != null )
|
||||
mail.parseHeaders();
|
||||
}
|
||||
if( mail != null && mail.deleted )
|
||||
mail = null;
|
||||
return mail;
|
||||
}
|
||||
}
|
||||
|
@ -92,11 +92,7 @@ public class UrlLauncher {
|
||||
|
||||
} else {
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("konqueror " + url, 5))
|
||||
return true;
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("galeon " + url, 5))
|
||||
return true;
|
||||
// fall through
|
||||
}
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("opera -newpage " + url, 5))
|
||||
@ -111,11 +107,18 @@ public class UrlLauncher {
|
||||
if (_shellCommand.executeSilentAndWaitTimed("netscape " + url, 5))
|
||||
return true;
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("konqueror " + url, 5))
|
||||
return true;
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("galeon " + url, 5))
|
||||
return true;
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("links " + url, 5))
|
||||
return true;
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("lynx " + url, 5))
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
35
build.xml
35
build.xml
@ -76,9 +76,9 @@
|
||||
windowtitle="I2P">
|
||||
<sourcepath>
|
||||
<pathelement location="core/java/src" />
|
||||
<pathelement location="core/java/test" />
|
||||
<!--<pathelement location="core/java/test" />-->
|
||||
<pathelement location="router/java/src" />
|
||||
<pathelement location="router/java/test" />
|
||||
<!--<pathelement location="router/java/test" />-->
|
||||
<pathelement location="apps/ministreaming/java/src" />
|
||||
<pathelement location="apps/streaming/java/src" />
|
||||
<pathelement location="apps/i2ptunnel/java/src" />
|
||||
@ -89,6 +89,7 @@
|
||||
<pathelement location="apps/jetty/jettylib/org.mortbay.jetty.jar" />
|
||||
<pathelement location="apps/systray/java/lib/systray4j.jar" />
|
||||
<pathelement location="installer/lib/wrapper/win32/wrapper.jar" />
|
||||
<pathelement location="core/lib/junit.jar" />
|
||||
</classpath>
|
||||
</javadoc>
|
||||
</target>
|
||||
@ -245,7 +246,15 @@
|
||||
<tarfileset dir="pkg-temp" includes="**/*" prefix="i2p" />
|
||||
</tar>
|
||||
</target>
|
||||
<target name="updater" depends="distclean, build">
|
||||
<target name="updater" depends="prepupdate">
|
||||
<zip destfile="i2pupdate.zip" basedir="pkg-temp" />
|
||||
</target>
|
||||
<target name="updateTest" depends="prepupdate">
|
||||
<ant dir="core/java/" target="jarTest" />
|
||||
<copy file="core/java/build/i2ptest.jar" todir="pkg-temp/lib" />
|
||||
<zip destfile="i2pupdate.zip" basedir="pkg-temp" />
|
||||
</target>
|
||||
<target name="prepupdate" depends="distclean, build">
|
||||
<delete dir="pkg-temp" />
|
||||
<copy file="build/i2p.jar" todir="pkg-temp/lib/" />
|
||||
<copy file="build/i2ptunnel.jar" todir="pkg-temp/lib/" />
|
||||
@ -285,7 +294,6 @@
|
||||
<mkdir dir="pkg-temp/eepsite" />
|
||||
<mkdir dir="pkg-temp/eepsite/webapps" />
|
||||
<mkdir dir="pkg-temp/eepsite/cgi-bin" />
|
||||
<zip destfile="i2pupdate.zip" basedir="pkg-temp" />
|
||||
</target>
|
||||
<taskdef name="izpack" classpath="${basedir}/installer/lib/izpack/standalone-compiler.jar" classname="com.izforge.izpack.ant.IzPackTask" />
|
||||
<target name="installer" depends="preppkg">
|
||||
@ -300,4 +308,23 @@
|
||||
</jar>
|
||||
<izpack input="${basedir}/installer/install.xml" output="${basedir}/install.jar" installerType="standard" basedir="${basedir}" />
|
||||
</target>
|
||||
<target name="test">
|
||||
<ant dir="core/java/" target="test" />
|
||||
</target>
|
||||
<target name="junit.report">
|
||||
<ant dir="core/java/" target="junit.report" />
|
||||
</target>
|
||||
<target name="clover.report">
|
||||
<ant dir="core/java/" target="clover.report" />
|
||||
</target>
|
||||
<target name="test.report" depends="junit.report, clover.report"/>
|
||||
<target name="useclover">
|
||||
<taskdef resource="clovertasks"/>
|
||||
<mkdir dir="reports/core/clover" />
|
||||
<mkdir dir="reports/core/clover" />
|
||||
<clover-setup initString="reports/core/clover/coverage.db"/>
|
||||
</target>
|
||||
<target name="fulltest">
|
||||
<ant dir="core/java/" target="fulltest" />
|
||||
</target>
|
||||
</project>
|
||||
|
@ -8,16 +8,90 @@
|
||||
<target name="compile">
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/obj" />
|
||||
<javac srcdir="./src:./test" debug="true" source="1.3" target="1.3" deprecation="on" destdir="./build/obj" />
|
||||
<javac srcdir="./src" debug="true" source="1.3" target="1.3" deprecation="on" destdir="./build/obj" />
|
||||
</target>
|
||||
<target name="compileTest">
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/obj" />
|
||||
<javac srcdir="./src:./test" debug="true" source="1.3" target="1.3" deprecation="on" destdir="./build/obj" classpath="./lib/junit.jar" />
|
||||
</target>
|
||||
<target name="jar" depends="compile">
|
||||
<jar destfile="./build/i2p.jar" basedir="./build/obj" includes="**/*.class" />
|
||||
</target>
|
||||
<target name="jarTest" depends="compileTest">
|
||||
<jar destfile="./build/i2ptest.jar" basedir="./build/obj" includes="**/*.class" />
|
||||
</target>
|
||||
<target name="javadoc">
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/javadoc" />
|
||||
<javadoc sourcepath="./src:./test" destdir="./build/javadoc" packagenames="*" use="true" splitindex="true" windowtitle="I2P SDK" />
|
||||
</target>
|
||||
<target name="test" depends="clean, compileTest">
|
||||
<junit printsummary="on" fork="yes">
|
||||
<classpath>
|
||||
<pathelement path="${classpath}" />
|
||||
<pathelement location="./build/obj" />
|
||||
<pathelement location="./lib/junit.jar" />
|
||||
<pathelement location="../../installer/lib/jbigi/jbigi.jar" />
|
||||
<pathelement path="${ant.home}/lib/clover.jar"/>
|
||||
</classpath>
|
||||
<batchtest>
|
||||
<fileset dir="./test/">
|
||||
<include name="**/*Test.java" />
|
||||
<exclude name="**/ElGamalAESEngineTest.java" />
|
||||
<exclude name="**/StructureTest.java" />
|
||||
</fileset>
|
||||
</batchtest>
|
||||
<formatter type="xml"/>
|
||||
</junit>
|
||||
<mkdir dir="../../reports/" />
|
||||
<mkdir dir="../../reports/core/" />
|
||||
<mkdir dir="../../reports/core/junit/" />
|
||||
<delete>
|
||||
<fileset dir="../../reports/core/junit">
|
||||
<include name="TEST-*.xml"/>
|
||||
</fileset>
|
||||
</delete>
|
||||
<copy todir="../../reports/core/junit">
|
||||
<fileset dir=".">
|
||||
<include name="TEST-*.xml"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
<delete>
|
||||
<fileset dir=".">
|
||||
<include name="TEST-*.xml"/>
|
||||
</fileset>
|
||||
</delete>
|
||||
</target>
|
||||
<target name="junit.report">
|
||||
<junitreport todir="../../reports/core/junit">
|
||||
<fileset dir="../../reports/core/junit">
|
||||
<include name="TEST-*.xml"/>
|
||||
</fileset>
|
||||
<report format="frames" todir="../../reports/core/html/junit"/>
|
||||
</junitreport>
|
||||
</target>
|
||||
<target name="clover.report">
|
||||
<taskdef resource="clovertasks"/>
|
||||
<mkdir dir="../../reports/" />
|
||||
<mkdir dir="../../reports/core" />
|
||||
<mkdir dir="../../reports/core/clover" />
|
||||
<clover-setup initString="../../reports/core/clover/coverage.db"/>
|
||||
<clover-report>
|
||||
<current outfile="../../reports/core/html/clover">
|
||||
<format type="html"/>
|
||||
</current>
|
||||
</clover-report>
|
||||
</target>
|
||||
<target name="test.report" depends="junit.report, clover.report"/>
|
||||
<target name="useclover">
|
||||
<taskdef resource="clovertasks"/>
|
||||
<mkdir dir="../../reports/" />
|
||||
<mkdir dir="../../reports/core/" />
|
||||
<mkdir dir="../../reports/core/clover" />
|
||||
<clover-setup initString="../../reports/core/clover/coverage.db"/>
|
||||
</target>
|
||||
<target name="fulltest" depends="useclover, test, test.report" />
|
||||
<target name="clean">
|
||||
<delete dir="./build" />
|
||||
</target>
|
||||
|
@ -14,8 +14,8 @@ package net.i2p;
|
||||
*
|
||||
*/
|
||||
public class CoreVersion {
|
||||
public final static String ID = "$Revision: 1.34 $ $Date: 2005/04/06 10:43:26 $";
|
||||
public final static String VERSION = "0.5.0.7";
|
||||
public final static String ID = "$Revision: 1.35 $ $Date: 2005/04/20 15:14:20 $";
|
||||
public final static String VERSION = "0.6";
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Core version: " + VERSION);
|
||||
|
@ -10,6 +10,8 @@ import net.i2p.crypto.CryptixAESEngine;
|
||||
import net.i2p.crypto.DSAEngine;
|
||||
import net.i2p.crypto.DummyDSAEngine;
|
||||
import net.i2p.crypto.DummyElGamalEngine;
|
||||
import net.i2p.crypto.DummyHMACSHA256Generator;
|
||||
import net.i2p.crypto.DummyPooledRandomSource;
|
||||
import net.i2p.crypto.ElGamalAESEngine;
|
||||
import net.i2p.crypto.ElGamalEngine;
|
||||
import net.i2p.crypto.HMACSHA256Generator;
|
||||
@ -22,6 +24,7 @@ import net.i2p.stat.StatManager;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.LogManager;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.util.PooledRandomSource;
|
||||
|
||||
/**
|
||||
* <p>Provide a base scope for accessing singletons that I2P exposes. Rather than
|
||||
@ -327,8 +330,12 @@ public class I2PAppContext {
|
||||
}
|
||||
private void initializeHMAC() {
|
||||
synchronized (this) {
|
||||
if (_hmac == null)
|
||||
_hmac= new HMACSHA256Generator(this);
|
||||
if (_hmac == null) {
|
||||
if ("true".equals(getProperty("i2p.fakeHMAC", "false")))
|
||||
_hmac = new DummyHMACSHA256Generator(this);
|
||||
else
|
||||
_hmac= new HMACSHA256Generator(this);
|
||||
}
|
||||
_hmacInitialized = true;
|
||||
}
|
||||
}
|
||||
@ -431,8 +438,12 @@ public class I2PAppContext {
|
||||
}
|
||||
private void initializeRandom() {
|
||||
synchronized (this) {
|
||||
if (_random == null)
|
||||
_random = new RandomSource(this);
|
||||
if (_random == null) {
|
||||
if ("true".equals(getProperty("i2p.weakPRNG", "false")))
|
||||
_random = new DummyPooledRandomSource(this);
|
||||
else
|
||||
_random = new PooledRandomSource(this);
|
||||
}
|
||||
_randomInitialized = true;
|
||||
}
|
||||
}
|
||||
|
@ -39,9 +39,14 @@ import net.i2p.util.Log;
|
||||
class I2CPMessageProducer {
|
||||
private final static Log _log = new Log(I2CPMessageProducer.class);
|
||||
private I2PAppContext _context;
|
||||
private int _sendBps;
|
||||
private long _sendPeriodBytes;
|
||||
private long _sendPeriodBeginTime;
|
||||
|
||||
public I2CPMessageProducer(I2PAppContext context) {
|
||||
_context = context;
|
||||
_sendBps = 0;
|
||||
context.statManager().createRateStat("client.sendBpsRaw", "How fast we pump out I2CP data messages", "ClientMessages", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,8 +99,30 @@ class I2CPMessageProducer {
|
||||
Payload data = createPayload(dest, payload, tag, key, tags, newKey);
|
||||
msg.setPayload(data);
|
||||
session.sendMessage(msg);
|
||||
updateBps(payload.length);
|
||||
}
|
||||
|
||||
private void updateBps(int len) {
|
||||
long now = _context.clock().now();
|
||||
float period = ((float)now-_sendPeriodBeginTime)/1000f;
|
||||
if (period >= 1f) {
|
||||
// first term decays on slow transmission
|
||||
_sendBps = (int)(((float)0.9f * (float)_sendBps) + ((float)0.1f*((float)_sendPeriodBytes)/period));
|
||||
_sendPeriodBytes = len;
|
||||
_sendPeriodBeginTime = now;
|
||||
_context.statManager().addRateData("client.sendBpsRaw", _sendBps, 0);
|
||||
} else {
|
||||
_sendPeriodBytes += len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we include the I2CP end to end crypto (which is in addition to any
|
||||
* garlic crypto added by the router)
|
||||
*
|
||||
*/
|
||||
static final boolean END_TO_END_CRYPTO = false;
|
||||
|
||||
/**
|
||||
* Create a new signed payload and send it off to the destination
|
||||
*
|
||||
@ -106,6 +133,10 @@ class I2CPMessageProducer {
|
||||
if (payload == null) throw new I2PSessionException("No payload specified");
|
||||
|
||||
Payload data = new Payload();
|
||||
if (!END_TO_END_CRYPTO) {
|
||||
data.setEncryptedData(payload);
|
||||
return data;
|
||||
}
|
||||
// no padding at this level
|
||||
// the garlic may pad, and the tunnels may pad, and the transports may pad
|
||||
int size = payload.length;
|
||||
@ -150,4 +181,4 @@ class I2CPMessageProducer {
|
||||
msg.setSessionId(session.getSessionId());
|
||||
session.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,20 +29,27 @@ import net.i2p.util.Log;
|
||||
class I2PClientMessageHandlerMap {
|
||||
private final static Log _log = new Log(I2PClientMessageHandlerMap.class);
|
||||
/** map of message type id --> I2CPMessageHandler */
|
||||
private Map _handlers;
|
||||
private I2CPMessageHandler _handlers[];
|
||||
|
||||
public I2PClientMessageHandlerMap(I2PAppContext context) {
|
||||
_handlers = new HashMap();
|
||||
_handlers.put(new Integer(DisconnectMessage.MESSAGE_TYPE), new DisconnectMessageHandler(context));
|
||||
_handlers.put(new Integer(SessionStatusMessage.MESSAGE_TYPE), new SessionStatusMessageHandler(context));
|
||||
_handlers.put(new Integer(RequestLeaseSetMessage.MESSAGE_TYPE), new RequestLeaseSetMessageHandler(context));
|
||||
_handlers.put(new Integer(MessagePayloadMessage.MESSAGE_TYPE), new MessagePayloadMessageHandler(context));
|
||||
_handlers.put(new Integer(MessageStatusMessage.MESSAGE_TYPE), new MessageStatusMessageHandler(context));
|
||||
_handlers.put(new Integer(SetDateMessage.MESSAGE_TYPE), new SetDateMessageHandler(context));
|
||||
int highest = DisconnectMessage.MESSAGE_TYPE;
|
||||
highest = Math.max(highest, SessionStatusMessage.MESSAGE_TYPE);
|
||||
highest = Math.max(highest, RequestLeaseSetMessage.MESSAGE_TYPE);
|
||||
highest = Math.max(highest, MessagePayloadMessage.MESSAGE_TYPE);
|
||||
highest = Math.max(highest, MessageStatusMessage.MESSAGE_TYPE);
|
||||
highest = Math.max(highest, SetDateMessage.MESSAGE_TYPE);
|
||||
|
||||
_handlers = new I2CPMessageHandler[highest+1];
|
||||
_handlers[DisconnectMessage.MESSAGE_TYPE] = new DisconnectMessageHandler(context);
|
||||
_handlers[SessionStatusMessage.MESSAGE_TYPE] = new SessionStatusMessageHandler(context);
|
||||
_handlers[RequestLeaseSetMessage.MESSAGE_TYPE] = new RequestLeaseSetMessageHandler(context);
|
||||
_handlers[MessagePayloadMessage.MESSAGE_TYPE] = new MessagePayloadMessageHandler(context);
|
||||
_handlers[MessageStatusMessage.MESSAGE_TYPE] = new MessageStatusMessageHandler(context);
|
||||
_handlers[SetDateMessage.MESSAGE_TYPE] = new SetDateMessageHandler(context);
|
||||
}
|
||||
|
||||
public I2CPMessageHandler getHandler(int messageTypeId) {
|
||||
I2CPMessageHandler handler = (I2CPMessageHandler) _handlers.get(new Integer(messageTypeId));
|
||||
return handler;
|
||||
if ( (messageTypeId < 0) || (messageTypeId >= _handlers.length) ) return null;
|
||||
return _handlers[messageTypeId];
|
||||
}
|
||||
}
|
@ -78,7 +78,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
|
||||
/** class that generates new messages */
|
||||
protected I2CPMessageProducer _producer;
|
||||
/** map of integer --> MessagePayloadMessage */
|
||||
/** map of Long --> MessagePayloadMessage */
|
||||
private Map _availableMessages;
|
||||
|
||||
protected I2PClientMessageHandlerMap _handlerMap;
|
||||
@ -295,7 +295,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
public byte[] receiveMessage(int msgId) throws I2PSessionException {
|
||||
MessagePayloadMessage msg = null;
|
||||
synchronized (_availableMessages) {
|
||||
msg = (MessagePayloadMessage) _availableMessages.remove(new Integer(msgId));
|
||||
msg = (MessagePayloadMessage) _availableMessages.remove(new Long(msgId));
|
||||
}
|
||||
if (msg == null) return null;
|
||||
return msg.getPayload().getUnencryptedData();
|
||||
@ -346,9 +346,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
*/
|
||||
public void addNewMessage(MessagePayloadMessage msg) {
|
||||
synchronized (_availableMessages) {
|
||||
_availableMessages.put(new Integer(msg.getMessageId().getMessageId()), msg);
|
||||
_availableMessages.put(new Long(msg.getMessageId()), msg);
|
||||
}
|
||||
int id = msg.getMessageId().getMessageId();
|
||||
long id = msg.getMessageId();
|
||||
byte data[] = msg.getPayload().getUnencryptedData();
|
||||
if ((data == null) || (data.length <= 0)) {
|
||||
if (_log.shouldLog(Log.CRIT))
|
||||
@ -363,12 +363,12 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
SimpleTimer.getInstance().addEvent(new VerifyUsage(id), 30*1000);
|
||||
}
|
||||
private class VerifyUsage implements SimpleTimer.TimedEvent {
|
||||
private int _msgId;
|
||||
public VerifyUsage(int id) { _msgId = id; }
|
||||
private long _msgId;
|
||||
public VerifyUsage(long id) { _msgId = id; }
|
||||
public void timeReached() {
|
||||
MessagePayloadMessage removed = null;
|
||||
synchronized (_availableMessages) {
|
||||
removed = (MessagePayloadMessage)_availableMessages.remove(new Integer(_msgId));
|
||||
removed = (MessagePayloadMessage)_availableMessages.remove(new Long(_msgId));
|
||||
}
|
||||
if (removed != null)
|
||||
_log.log(Log.CRIT, "Message NOT removed! id=" + _msgId + ": " + removed);
|
||||
@ -393,9 +393,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
}
|
||||
}
|
||||
|
||||
public void available(int msgId, int size) {
|
||||
public void available(long msgId, int size) {
|
||||
synchronized (AvailabilityNotifier.this) {
|
||||
_pendingIds.add(new Integer(msgId));
|
||||
_pendingIds.add(new Long(msgId));
|
||||
_pendingSizes.add(new Integer(size));
|
||||
AvailabilityNotifier.this.notifyAll();
|
||||
}
|
||||
@ -403,7 +403,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
public void run() {
|
||||
_alive = true;
|
||||
while (_alive) {
|
||||
Integer msgId = null;
|
||||
Long msgId = null;
|
||||
Integer size = null;
|
||||
synchronized (AvailabilityNotifier.this) {
|
||||
if (_pendingIds.size() <= 0) {
|
||||
@ -413,7 +413,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
}
|
||||
}
|
||||
if (_pendingIds.size() > 0) {
|
||||
msgId = (Integer)_pendingIds.remove(0);
|
||||
msgId = (Long)_pendingIds.remove(0);
|
||||
size = (Integer)_pendingSizes.remove(0);
|
||||
}
|
||||
}
|
||||
@ -532,8 +532,8 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
* Pass off the error to the listener
|
||||
*/
|
||||
void propogateError(String msg, Throwable error) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error(getPrefix() + "Error occurred: " + msg + " - " + error.getMessage());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix() + "Error occurred: " + msg + " - " + error.getMessage());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix() + " cause", error);
|
||||
|
||||
|
@ -119,49 +119,57 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
|
||||
private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent)
|
||||
throws I2PSessionException {
|
||||
long begin = _context.clock().now();
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("begin sendBestEffort");
|
||||
SessionKey key = _context.sessionKeyManager().getCurrentKey(dest.getPublicKey());
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("key fetched");
|
||||
if (key == null) key = _context.sessionKeyManager().createSession(dest.getPublicKey());
|
||||
SessionTag tag = _context.sessionKeyManager().consumeNextAvailableTag(dest.getPublicKey(), key);
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("tag consumed");
|
||||
SessionKey key = null;
|
||||
SessionKey newKey = null;
|
||||
SessionTag tag = null;
|
||||
Set sentTags = null;
|
||||
int oldTags = _context.sessionKeyManager().getAvailableTags(dest.getPublicKey(), key);
|
||||
long availTimeLeft = _context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key);
|
||||
int oldTags = 0;
|
||||
long begin = _context.clock().now();
|
||||
if (I2CPMessageProducer.END_TO_END_CRYPTO) {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("begin sendBestEffort");
|
||||
key = _context.sessionKeyManager().getCurrentKey(dest.getPublicKey());
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("key fetched");
|
||||
if (key == null) key = _context.sessionKeyManager().createSession(dest.getPublicKey());
|
||||
tag = _context.sessionKeyManager().consumeNextAvailableTag(dest.getPublicKey(), key);
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("tag consumed");
|
||||
sentTags = null;
|
||||
oldTags = _context.sessionKeyManager().getAvailableTags(dest.getPublicKey(), key);
|
||||
long availTimeLeft = _context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key);
|
||||
|
||||
if ( (tagsSent == null) || (tagsSent.size() <= 0) ) {
|
||||
if (oldTags < 10) {
|
||||
sentTags = createNewTags(NUM_TAGS);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("** sendBestEffort only had " + oldTags + " with " + availTimeLeft + ", adding " + NUM_TAGS);
|
||||
} else if (availTimeLeft < 2 * 60 * 1000) {
|
||||
// if we have > 10 tags, but they expire in under 2 minutes, we want more
|
||||
sentTags = createNewTags(NUM_TAGS);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Tags expiring in " + availTimeLeft + ", adding " + NUM_TAGS + " new ones");
|
||||
//_log.error("** sendBestEffort available time left " + availTimeLeft);
|
||||
if ( (tagsSent == null) || (tagsSent.size() <= 0) ) {
|
||||
if (oldTags < NUM_TAGS) {
|
||||
sentTags = createNewTags(NUM_TAGS);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("** sendBestEffort only had " + oldTags + " with " + availTimeLeft + ", adding " + NUM_TAGS + ": " + sentTags);
|
||||
} else if (availTimeLeft < 2 * 60 * 1000) {
|
||||
// if we have > 50 tags, but they expire in under 2 minutes, we want more
|
||||
sentTags = createNewTags(NUM_TAGS);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Tags expiring in " + availTimeLeft + ", adding " + NUM_TAGS + " new ones: " + sentTags);
|
||||
//_log.error("** sendBestEffort available time left " + availTimeLeft);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("sendBestEffort old tags: " + oldTags + " available time left: " + availTimeLeft);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("sendBestEffort old tags: " + oldTags + " available time left: " + availTimeLeft);
|
||||
_log.debug("sendBestEffort is sending " + tagsSent.size() + " with " + availTimeLeft
|
||||
+ "ms left, " + oldTags + " tags known and "
|
||||
+ (tag == null ? "no tag" : " a valid tag"));
|
||||
}
|
||||
|
||||
if (false) // rekey
|
||||
newKey = _context.keyGenerator().generateSessionKey();
|
||||
|
||||
if ( (tagsSent != null) && (tagsSent.size() > 0) ) {
|
||||
if (sentTags == null)
|
||||
sentTags = new HashSet();
|
||||
sentTags.addAll(tagsSent);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("sendBestEffort is sending " + tagsSent.size() + " with " + availTimeLeft
|
||||
+ "ms left, " + oldTags + " tags known and "
|
||||
+ (tag == null ? "no tag" : " a valid tag"));
|
||||
// not using end to end crypto, so don't ever bundle any tags
|
||||
}
|
||||
|
||||
SessionKey newKey = null;
|
||||
if (false) // rekey
|
||||
newKey = _context.keyGenerator().generateSessionKey();
|
||||
|
||||
if ( (tagsSent != null) && (tagsSent.size() > 0) ) {
|
||||
if (sentTags == null)
|
||||
sentTags = new HashSet();
|
||||
sentTags.addAll(tagsSent);
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("before creating nonce");
|
||||
|
||||
long nonce = _context.random().nextInt(Integer.MAX_VALUE);
|
||||
@ -174,10 +182,14 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Setting key = " + key);
|
||||
|
||||
if (keyUsed != null) {
|
||||
if (newKey != null)
|
||||
keyUsed.setData(newKey.getData());
|
||||
else
|
||||
keyUsed.setData(key.getData());
|
||||
if (I2CPMessageProducer.END_TO_END_CRYPTO) {
|
||||
if (newKey != null)
|
||||
keyUsed.setData(newKey.getData());
|
||||
else
|
||||
keyUsed.setData(key.getData());
|
||||
} else {
|
||||
keyUsed.setData(SessionKey.INVALID_KEY.getData());
|
||||
}
|
||||
}
|
||||
if (tagsSent != null) {
|
||||
if (sentTags != null) {
|
||||
|
@ -35,7 +35,7 @@ class MessagePayloadMessageHandler extends HandlerImpl {
|
||||
_log.debug("Handle message " + message);
|
||||
try {
|
||||
MessagePayloadMessage msg = (MessagePayloadMessage) message;
|
||||
MessageId id = msg.getMessageId();
|
||||
long id = msg.getMessageId();
|
||||
Payload payload = decryptPayload(msg, session);
|
||||
session.addNewMessage(msg);
|
||||
|
||||
@ -55,6 +55,11 @@ class MessagePayloadMessageHandler extends HandlerImpl {
|
||||
*/
|
||||
private Payload decryptPayload(MessagePayloadMessage msg, I2PSessionImpl session) throws DataFormatException {
|
||||
Payload payload = msg.getPayload();
|
||||
if (!I2CPMessageProducer.END_TO_END_CRYPTO) {
|
||||
payload.setUnencryptedData(payload.getEncryptedData());
|
||||
return payload;
|
||||
}
|
||||
|
||||
byte[] data = _context.elGamalAESEngine().decrypt(payload.getEncryptedData(), session.getDecryptionKey());
|
||||
if (data == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
|
@ -46,7 +46,7 @@ class MessageStatusMessageHandler extends HandlerImpl {
|
||||
}
|
||||
return;
|
||||
case MessageStatusMessage.STATUS_SEND_ACCEPTED:
|
||||
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
|
||||
session.receiveStatus((int)msg.getMessageId(), msg.getNonce(), msg.getStatus());
|
||||
// noop
|
||||
return;
|
||||
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
|
||||
@ -54,14 +54,14 @@ class MessageStatusMessageHandler extends HandlerImpl {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Message delivery succeeded for message " + msg.getMessageId());
|
||||
//if (!skipStatus)
|
||||
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
|
||||
session.receiveStatus((int)msg.getMessageId(), msg.getNonce(), msg.getStatus());
|
||||
return;
|
||||
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
|
||||
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Message delivery FAILED for message " + msg.getMessageId());
|
||||
//if (!skipStatus)
|
||||
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
|
||||
session.receiveStatus((int)msg.getMessageId(), msg.getNonce(), msg.getStatus());
|
||||
return;
|
||||
default:
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
|
@ -159,7 +159,6 @@ public class AESEngine {
|
||||
System.arraycopy(payload, inIndex, rv, outIndex, rv.length - outIndex);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String args[]) {
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
SessionKey key = ctx.keyGenerator().generateSessionKey();
|
||||
|
@ -123,15 +123,17 @@ public class CryptixAESEngine extends AESEngine {
|
||||
}
|
||||
|
||||
public final void encryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte out[], int outIndex) {
|
||||
CryptixAESKeyCache.KeyCacheEntry keyData = _cache.acquireKey();
|
||||
try {
|
||||
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16, keyData);
|
||||
CryptixRijndael_Algorithm.blockEncrypt(payload, out, inIndex, outIndex, key, 16);
|
||||
} catch (InvalidKeyException ike) {
|
||||
_log.error("Invalid key", ike);
|
||||
} finally {
|
||||
_cache.releaseKey(keyData);
|
||||
if (sessionKey.getPreparedKey() == null) {
|
||||
try {
|
||||
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16);
|
||||
sessionKey.setPreparedKey(key);
|
||||
} catch (InvalidKeyException ike) {
|
||||
_log.log(Log.CRIT, "Invalid key", ike);
|
||||
throw new IllegalArgumentException("wtf, invalid key? " + ike.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
CryptixRijndael_Algorithm.blockEncrypt(payload, out, inIndex, outIndex, sessionKey.getPreparedKey(), 16);
|
||||
}
|
||||
|
||||
/** decrypt the data with the session key provided
|
||||
@ -146,15 +148,17 @@ public class CryptixAESEngine extends AESEngine {
|
||||
throw new IllegalArgumentException("bad block args [payload.len=" + payload.length
|
||||
+ " inIndex=" + inIndex + " rv.len=" + rv.length
|
||||
+ " outIndex="+outIndex);
|
||||
CryptixAESKeyCache.KeyCacheEntry keyData = _cache.acquireKey();
|
||||
try {
|
||||
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16, keyData);
|
||||
CryptixRijndael_Algorithm.blockDecrypt(payload, rv, inIndex, outIndex, key, 16);
|
||||
} catch (InvalidKeyException ike) {
|
||||
_log.error("Invalid key", ike);
|
||||
} finally {
|
||||
_cache.releaseKey(keyData);
|
||||
if (sessionKey.getPreparedKey() == null) {
|
||||
try {
|
||||
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16);
|
||||
sessionKey.setPreparedKey(key);
|
||||
} catch (InvalidKeyException ike) {
|
||||
_log.log(Log.CRIT, "Invalid key", ike);
|
||||
throw new IllegalArgumentException("wtf, invalid key? " + ike.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
CryptixRijndael_Algorithm.blockDecrypt(payload, rv, inIndex, outIndex, sessionKey.getPreparedKey(), 16);
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
|
@ -18,6 +18,7 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.SessionKey;
|
||||
@ -157,8 +158,14 @@ public class DHSessionKeyBuilder {
|
||||
// read: Y
|
||||
BigInteger Y = readBigI(in);
|
||||
if (Y == null) return null;
|
||||
builder.setPeerPublicValue(Y);
|
||||
return builder;
|
||||
try {
|
||||
builder.setPeerPublicValue(Y);
|
||||
return builder;
|
||||
} catch (InvalidPublicParameterException ippe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Key exchange failed (hostile peer?)", ippe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static BigInteger readBigI(InputStream in) throws IOException {
|
||||
@ -175,7 +182,7 @@ public class DHSessionKeyBuilder {
|
||||
System.arraycopy(Y, 0, Y2, 1, 256);
|
||||
Y = Y2;
|
||||
}
|
||||
return new NativeBigInteger(Y);
|
||||
return new NativeBigInteger(1, Y);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -253,7 +260,10 @@ public class DHSessionKeyBuilder {
|
||||
*
|
||||
*/
|
||||
public byte[] getMyPublicValueBytes() {
|
||||
BigInteger bi = getMyPublicValue();
|
||||
return toByteArray(getMyPublicValue());
|
||||
}
|
||||
|
||||
private static final byte[] toByteArray(BigInteger bi) {
|
||||
byte data[] = bi.toByteArray();
|
||||
byte rv[] = new byte[256];
|
||||
if (data.length == 257) // high byte has the sign bit
|
||||
@ -269,10 +279,11 @@ public class DHSessionKeyBuilder {
|
||||
* Specify the value given by the peer for use in the session key negotiation
|
||||
*
|
||||
*/
|
||||
public void setPeerPublicValue(BigInteger peerVal) {
|
||||
public void setPeerPublicValue(BigInteger peerVal) throws InvalidPublicParameterException {
|
||||
validatePublic(peerVal);
|
||||
_peerValue = peerVal;
|
||||
}
|
||||
public void setPeerPublicValue(byte val[]) {
|
||||
public void setPeerPublicValue(byte val[]) throws InvalidPublicParameterException {
|
||||
if (val.length != 256)
|
||||
throw new IllegalArgumentException("Peer public value must be exactly 256 bytes");
|
||||
|
||||
@ -284,12 +295,16 @@ public class DHSessionKeyBuilder {
|
||||
System.arraycopy(val, 0, val2, 1, 256);
|
||||
val = val2;
|
||||
}
|
||||
_peerValue = new NativeBigInteger(val);
|
||||
setPeerPublicValue(new NativeBigInteger(1, val));
|
||||
//_peerValue = new NativeBigInteger(val);
|
||||
}
|
||||
|
||||
public BigInteger getPeerPublicValue() {
|
||||
return _peerValue;
|
||||
}
|
||||
public byte[] getPeerPublicValueBytes() {
|
||||
return toByteArray(getPeerPublicValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the session key, calculating it if necessary (and if possible).
|
||||
@ -355,8 +370,58 @@ public class DHSessionKeyBuilder {
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* rfc2631:
|
||||
* The following algorithm MAY be used to validate a received public key y.
|
||||
*
|
||||
* 1. Verify that y lies within the interval [2,p-1]. If it does not,
|
||||
* the key is invalid.
|
||||
* 2. Compute y^q mod p. If the result == 1, the key is valid.
|
||||
* Otherwise the key is invalid.
|
||||
*/
|
||||
private static final void validatePublic(BigInteger publicValue) throws InvalidPublicParameterException {
|
||||
int cmp = publicValue.compareTo(NativeBigInteger.ONE);
|
||||
if (cmp <= 0)
|
||||
throw new InvalidPublicParameterException("Public value is below two: " + publicValue.toString());
|
||||
|
||||
cmp = publicValue.compareTo(CryptoConstants.elgp);
|
||||
if (cmp >= 0)
|
||||
throw new InvalidPublicParameterException("Public value is above p-1: " + publicValue.toString());
|
||||
|
||||
// todo:
|
||||
// whatever validation needs to be done to mirror the rfc's part 2 (we don't have a q, so can't do
|
||||
// if (NativeBigInteger.ONE.compareTo(publicValue.modPow(q, CryptoConstants.elgp)) != 0)
|
||||
// throw new InvalidPublicParameterException("Invalid public value with y^q mod p != 1");
|
||||
//
|
||||
}
|
||||
|
||||
/*
|
||||
private static void testValidation() {
|
||||
NativeBigInteger bi = new NativeBigInteger("-3416069082912684797963255430346582466254460710249795973742848334283491150671563023437888953432878859472362439146158925287289114133666004165938814597775594104058593692562989626922979416277152479694258099203456493995467386903611666213773085025718340335205240293383622352894862685806192183268523899615405287022135356656720938278415659792084974076416864813957028335830794117802560169423133816961503981757298122040391506600117301607823659479051969827845787626261515313227076880722069706394405554113103165334903531980102626092646197079218895216346725765704256096661045699444128316078549709132753443706200863682650825635513");
|
||||
try {
|
||||
validatePublic(bi);
|
||||
System.err.println("valid?!");
|
||||
} catch (InvalidPublicParameterException ippe) {
|
||||
System.err.println("Ok, invalid. cool");
|
||||
}
|
||||
|
||||
byte val[] = bi.toByteArray();
|
||||
System.out.println("Len: " + val.length + " first is ok? " + ( (val[0] & 0x80) == 1)
|
||||
+ "\n" + DataHelper.toString(val, 64));
|
||||
NativeBigInteger bi2 = new NativeBigInteger(1, val);
|
||||
try {
|
||||
validatePublic(bi2);
|
||||
System.out.println("valid");
|
||||
} catch (InvalidPublicParameterException ippe) {
|
||||
System.out.println("invalid");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public static void main(String args[]) {
|
||||
//if (true) { testValidation(); return; }
|
||||
|
||||
RandomSource.getInstance().nextBoolean(); // warm it up
|
||||
try {
|
||||
Thread.sleep(20 * 1000);
|
||||
@ -365,36 +430,40 @@ public class DHSessionKeyBuilder {
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
_log.debug("\n\n\n\nBegin test\n");
|
||||
long negTime = 0;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
long startNeg = Clock.getInstance().now();
|
||||
DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder();
|
||||
DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder();
|
||||
BigInteger pub1 = builder1.getMyPublicValue();
|
||||
builder2.setPeerPublicValue(pub1);
|
||||
BigInteger pub2 = builder2.getMyPublicValue();
|
||||
builder1.setPeerPublicValue(pub2);
|
||||
SessionKey key1 = builder1.getSessionKey();
|
||||
SessionKey key2 = builder2.getSessionKey();
|
||||
long endNeg = Clock.getInstance().now();
|
||||
negTime += endNeg - startNeg;
|
||||
try {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
long startNeg = Clock.getInstance().now();
|
||||
DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder();
|
||||
DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder();
|
||||
BigInteger pub1 = builder1.getMyPublicValue();
|
||||
builder2.setPeerPublicValue(pub1);
|
||||
BigInteger pub2 = builder2.getMyPublicValue();
|
||||
builder1.setPeerPublicValue(pub2);
|
||||
SessionKey key1 = builder1.getSessionKey();
|
||||
SessionKey key2 = builder2.getSessionKey();
|
||||
long endNeg = Clock.getInstance().now();
|
||||
negTime += endNeg - startNeg;
|
||||
|
||||
if (!key1.equals(key2))
|
||||
_log.error("**ERROR: Keys do not match");
|
||||
else
|
||||
_log.debug("**Success: Keys match");
|
||||
if (!key1.equals(key2))
|
||||
_log.error("**ERROR: Keys do not match");
|
||||
else
|
||||
_log.debug("**Success: Keys match");
|
||||
|
||||
byte iv[] = new byte[16];
|
||||
RandomSource.getInstance().nextBytes(iv);
|
||||
String origVal = "1234567890123456"; // 16 bytes max using AESEngine
|
||||
byte enc[] = new byte[16];
|
||||
byte dec[] = new byte[16];
|
||||
ctx.aes().encrypt(origVal.getBytes(), 0, enc, 0, key1, iv, 16);
|
||||
ctx.aes().decrypt(enc, 0, dec, 0, key2, iv, 16);
|
||||
String tranVal = new String(dec);
|
||||
if (origVal.equals(tranVal))
|
||||
_log.debug("**Success: D(E(val)) == val");
|
||||
else
|
||||
_log.error("**ERROR: D(E(val)) != val [val=(" + tranVal + "), origVal=(" + origVal + ")");
|
||||
byte iv[] = new byte[16];
|
||||
RandomSource.getInstance().nextBytes(iv);
|
||||
String origVal = "1234567890123456"; // 16 bytes max using AESEngine
|
||||
byte enc[] = new byte[16];
|
||||
byte dec[] = new byte[16];
|
||||
ctx.aes().encrypt(origVal.getBytes(), 0, enc, 0, key1, iv, 16);
|
||||
ctx.aes().decrypt(enc, 0, dec, 0, key2, iv, 16);
|
||||
String tranVal = new String(dec);
|
||||
if (origVal.equals(tranVal))
|
||||
_log.debug("**Success: D(E(val)) == val");
|
||||
else
|
||||
_log.error("**ERROR: D(E(val)) != val [val=(" + tranVal + "), origVal=(" + origVal + ")");
|
||||
}
|
||||
} catch (InvalidPublicParameterException ippe) {
|
||||
_log.error("Invalid dh", ippe);
|
||||
}
|
||||
_log.debug("Negotiation time for 5 runs: " + negTime + " @ " + negTime / 5l + "ms each");
|
||||
try {
|
||||
@ -451,4 +520,13 @@ public class DHSessionKeyBuilder {
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvalidPublicParameterException extends I2PException {
|
||||
public InvalidPublicParameterException() {
|
||||
super();
|
||||
}
|
||||
public InvalidPublicParameterException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
}
|
52
core/java/src/net/i2p/crypto/DummyHMACSHA256Generator.java
Normal file
52
core/java/src/net/i2p/crypto/DummyHMACSHA256Generator.java
Normal file
@ -0,0 +1,52 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
|
||||
/**
|
||||
* Calculate the HMAC-SHA256 of a key+message. All the good stuff occurs
|
||||
* in {@link org.bouncycastle.crypto.macs.HMac} and
|
||||
* {@link org.bouncycastle.crypto.digests.SHA256Digest}.
|
||||
*
|
||||
*/
|
||||
public class DummyHMACSHA256Generator extends HMACSHA256Generator {
|
||||
private I2PAppContext _context;
|
||||
public DummyHMACSHA256Generator(I2PAppContext context) {
|
||||
super(context);
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public static HMACSHA256Generator getInstance() {
|
||||
return I2PAppContext.getGlobalContext().hmac();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the HMAC of the data with the given key
|
||||
*/
|
||||
public Hash calculate(SessionKey key, byte data[]) {
|
||||
if ((key == null) || (key.getData() == null) || (data == null))
|
||||
throw new NullPointerException("Null arguments for HMAC");
|
||||
return calculate(key, data, 0, data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the HMAC of the data with the given key
|
||||
*/
|
||||
public Hash calculate(SessionKey key, byte data[], int offset, int length) {
|
||||
if ((key == null) || (key.getData() == null) || (data == null))
|
||||
throw new NullPointerException("Null arguments for HMAC");
|
||||
|
||||
byte rv[] = new byte[Hash.HASH_LENGTH];
|
||||
System.arraycopy(key.getData(), 0, rv, 0, Hash.HASH_LENGTH);
|
||||
if (Hash.HASH_LENGTH >= length)
|
||||
DataHelper.xor(data, offset, rv, 0, rv, 0, length);
|
||||
else
|
||||
DataHelper.xor(data, offset, rv, 0, rv, 0, Hash.HASH_LENGTH);
|
||||
return new Hash(rv);
|
||||
}
|
||||
}
|
98
core/java/src/net/i2p/crypto/DummyPooledRandomSource.java
Normal file
98
core/java/src/net/i2p/crypto/DummyPooledRandomSource.java
Normal file
@ -0,0 +1,98 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import java.util.Random;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.PooledRandomSource;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class DummyPooledRandomSource extends PooledRandomSource {
|
||||
public DummyPooledRandomSource(I2PAppContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
protected void initializePool(I2PAppContext context) {
|
||||
_pool = new RandomSource[POOL_SIZE];
|
||||
for (int i = 0; i < POOL_SIZE; i++) {
|
||||
_pool[i] = new DummyRandomSource(context);
|
||||
_pool[i].nextBoolean();
|
||||
}
|
||||
_nextPool = 0;
|
||||
}
|
||||
|
||||
private class DummyRandomSource extends RandomSource {
|
||||
private Random _prng;
|
||||
public DummyRandomSource(I2PAppContext context) {
|
||||
super(context);
|
||||
// when we replace to have hooks for fortuna (etc), replace with
|
||||
// a factory (or just a factory method)
|
||||
_prng = new Random();
|
||||
}
|
||||
|
||||
/**
|
||||
* According to the java docs (http://java.sun.com/j2se/1.4.1/docs/api/java/util/Random.html#nextInt(int))
|
||||
* nextInt(n) should return a number between 0 and n (including 0 and excluding n). However, their pseudocode,
|
||||
* as well as sun's, kaffe's, and classpath's implementation INCLUDES NEGATIVE VALUES.
|
||||
* WTF. Ok, so we're going to have it return between 0 and n (including 0, excluding n), since
|
||||
* thats what it has been used for.
|
||||
*
|
||||
*/
|
||||
public int nextInt(int n) {
|
||||
if (n == 0) return 0;
|
||||
int val = _prng.nextInt(n);
|
||||
if (val < 0) val = 0 - val;
|
||||
if (val >= n) val = val % n;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like the modified nextInt, nextLong(n) returns a random number from 0 through n,
|
||||
* including 0, excluding n.
|
||||
*/
|
||||
public long nextLong(long n) {
|
||||
long v = _prng.nextLong();
|
||||
if (v < 0) v = 0 - v;
|
||||
if (v >= n) v = v % n;
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public boolean nextBoolean() { return _prng.nextBoolean(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public void nextBytes(byte buf[]) { _prng.nextBytes(buf); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public double nextDouble() { return _prng.nextDouble(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public float nextFloat() { return _prng.nextFloat(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public double nextGaussian() { return _prng.nextGaussian(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public int nextInt() { return _prng.nextInt(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public long nextLong() { return _prng.nextLong(); }
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
@ -52,7 +53,7 @@ public class ElGamalAESEngine {
|
||||
_context.statManager().createFrequencyStat("crypto.elGamalAES.decryptExistingSession",
|
||||
"how frequently we decrypt with an existing ElGamal/AES+SessionTag session?",
|
||||
"Encryption", new long[] { 60 * 1000l, 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
|
||||
_context.statManager().createFrequencyStat("crypto.elGamalAES.decryptFail",
|
||||
_context.statManager().createFrequencyStat("crypto.elGamalAES.decryptFailed",
|
||||
"how frequently we fail to decrypt with ElGamal/AES+SessionTag?", "Encryption",
|
||||
new long[] { 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
|
||||
}
|
||||
@ -81,21 +82,38 @@ public class ElGamalAESEngine {
|
||||
SessionKey usedKey = new SessionKey();
|
||||
Set foundTags = new HashSet();
|
||||
byte decrypted[] = null;
|
||||
boolean wasExisting = false;
|
||||
if (key != null) {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is known for tag " + st);
|
||||
//if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is known for tag " + st);
|
||||
usedKey.setData(key.getData());
|
||||
long id = _context.random().nextLong();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(id + ": Decrypting existing session encrypted with tag: " + st.toString() + ": key: " + key.toBase64() + ": " + data.length + " bytes: " + Base64.encode(data, 0, 64));
|
||||
|
||||
decrypted = decryptExistingSession(data, key, targetPrivateKey, foundTags, usedKey, foundKey);
|
||||
if (decrypted != null)
|
||||
if (decrypted != null) {
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptExistingSession");
|
||||
else
|
||||
if ( (foundTags.size() > 0) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn(id + ": ElG/AES decrypt success with " + st + ": found tags: " + foundTags);
|
||||
wasExisting = true;
|
||||
} else {
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptFailed");
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
_log.warn(id + ": ElG decrypt fail: known tag [" + st + "], failed decrypt");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is NOT known for tag " + st);
|
||||
decrypted = decryptNewSession(data, targetPrivateKey, foundTags, usedKey, foundKey);
|
||||
if (decrypted != null)
|
||||
if (decrypted != null) {
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptNewSession");
|
||||
else
|
||||
if ( (foundTags.size() > 0) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("ElG decrypt success: found tags: " + foundTags);
|
||||
} else {
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptFailed");
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("ElG decrypt fail: unknown tag: " + st);
|
||||
}
|
||||
}
|
||||
|
||||
if ((key == null) && (decrypted == null)) {
|
||||
@ -104,10 +122,12 @@ public class ElGamalAESEngine {
|
||||
|
||||
if (foundTags.size() > 0) {
|
||||
if (foundKey.getData() != null) {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Found key: " + foundKey);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Found key: " + foundKey.toBase64() + " tags: " + foundTags + " wasExisting? " + wasExisting);
|
||||
_context.sessionKeyManager().tagsReceived(foundKey, foundTags);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Used key: " + usedKey);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Used key: " + usedKey.toBase64() + " tags: " + foundTags + " wasExisting? " + wasExisting);
|
||||
_context.sessionKeyManager().tagsReceived(usedKey, foundTags);
|
||||
}
|
||||
}
|
||||
@ -131,10 +151,10 @@ public class ElGamalAESEngine {
|
||||
byte[] decryptNewSession(byte data[], PrivateKey targetPrivateKey, Set foundTags, SessionKey usedKey,
|
||||
SessionKey foundKey) throws DataFormatException {
|
||||
if (data == null) {
|
||||
if (_log.shouldLog(Log.WARN)) _log.warn("Data is null, unable to decrypt new session");
|
||||
//if (_log.shouldLog(Log.WARN)) _log.warn("Data is null, unable to decrypt new session");
|
||||
return null;
|
||||
} else if (data.length < 514) {
|
||||
if (_log.shouldLog(Log.WARN)) _log.warn("Data length is too small (" + data.length + ")");
|
||||
//if (_log.shouldLog(Log.WARN)) _log.warn("Data length is too small (" + data.length + ")");
|
||||
return null;
|
||||
}
|
||||
byte elgEncr[] = new byte[514];
|
||||
@ -145,8 +165,8 @@ public class ElGamalAESEngine {
|
||||
}
|
||||
byte elgDecr[] = _context.elGamalEngine().decrypt(elgEncr, targetPrivateKey);
|
||||
if (elgDecr == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("decrypt returned null", new Exception("decrypt failed"));
|
||||
//if (_log.shouldLog(Log.WARN))
|
||||
// _log.warn("decrypt returned null", new Exception("decrypt failed"));
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -172,9 +192,9 @@ public class ElGamalAESEngine {
|
||||
|
||||
byte aesDecr[] = decryptAESBlock(data, 514, data.length-514, usedKey, iv, null, foundTags, foundKey);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Decrypt with a NEW session successfull: # tags read = " + foundTags.size(),
|
||||
new Exception("Decrypted by"));
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Decrypt with a NEW session successfull: # tags read = " + foundTags.size(),
|
||||
// new Exception("Decrypted by"));
|
||||
return aesDecr;
|
||||
}
|
||||
|
||||
@ -211,14 +231,23 @@ public class ElGamalAESEngine {
|
||||
byte decrypted[] = decryptAESBlock(data, 32, data.length-32, key, iv, preIV, foundTags, foundKey);
|
||||
if (decrypted == null) {
|
||||
// it begins with a valid session tag, but thats just a coincidence.
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Decrypt with a non session tag, but tags read: " + foundTags.size());
|
||||
return decryptNewSession(data, targetPrivateKey, foundTags, usedKey, foundKey);
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Decrypt with a non session tag, but tags read: " + foundTags.size());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Decrypting looks negative... existing key fails with existing tag, lets try as a new one");
|
||||
byte rv[] = decryptNewSession(data, targetPrivateKey, foundTags, usedKey, foundKey);
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
if (rv == null)
|
||||
_log.warn("Decrypting failed with a known existing tag as either an existing message or a new session");
|
||||
else
|
||||
_log.warn("Decrypting suceeded as a new session, even though it used an existing tag!");
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
// existing session decrypted successfully!
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Decrypt with an EXISTING session tag successfull, # tags read: " + foundTags.size(),
|
||||
new Exception("Decrypted by"));
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Decrypt with an EXISTING session tag successfull, # tags read: " + foundTags.size(),
|
||||
// new Exception("Decrypted by"));
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
@ -261,7 +290,7 @@ public class ElGamalAESEngine {
|
||||
long numTags = DataHelper.fromLong(decrypted, cur, 2);
|
||||
cur += 2;
|
||||
//_log.debug("# tags: " + numTags);
|
||||
if ((numTags < 0) || (numTags > 65535)) throw new Exception("Invalid number of session tags");
|
||||
if ((numTags < 0) || (numTags > 200)) throw new Exception("Invalid number of session tags");
|
||||
if (numTags * SessionTag.BYTE_LENGTH > decrypted.length - 2) {
|
||||
throw new Exception("# tags: " + numTags + " is too many for " + (decrypted.length - 2));
|
||||
}
|
||||
@ -333,7 +362,10 @@ public class ElGamalAESEngine {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Current tag is NOT null, encrypting as existing session", new Exception("encrypt existing"));
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.encryptExistingSession");
|
||||
return encryptExistingSession(data, target, key, tagsForDelivery, currentTag, newKey, paddedSize);
|
||||
byte rv[] = encryptExistingSession(data, target, key, tagsForDelivery, currentTag, newKey, paddedSize);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Existing session encrypted with tag: " + currentTag.toString() + ": " + rv.length + " bytes and key: " + key.toBase64() + ": " + Base64.encode(rv, 0, 64));
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -396,7 +428,7 @@ public class ElGamalAESEngine {
|
||||
if (elgEncr.length < 514) {
|
||||
byte elg[] = new byte[514];
|
||||
int diff = elg.length - elgEncr.length;
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Difference in size: " + diff);
|
||||
//if (_log.shouldLog(Log.DEBUG)) _log.debug("Difference in size: " + diff);
|
||||
System.arraycopy(elgEncr, 0, elg, diff, elgEncr.length);
|
||||
elgEncr = elg;
|
||||
}
|
||||
@ -415,8 +447,8 @@ public class ElGamalAESEngine {
|
||||
System.arraycopy(aesEncr, 0, rv, elgEncr.length, aesEncr.length);
|
||||
//_log.debug("Return length: " + rv.length);
|
||||
long finish = _context.clock().now();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("after the elgEngine.encrypt took a total of " + (finish - after) + "ms");
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("after the elgEngine.encrypt took a total of " + (finish - after) + "ms");
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -493,7 +525,7 @@ public class ElGamalAESEngine {
|
||||
System.arraycopy(tag.getData(), 0, aesData, cur, SessionTag.BYTE_LENGTH);
|
||||
cur += SessionTag.BYTE_LENGTH;
|
||||
}
|
||||
//_log.debug("# tags created, registered, and written: " + tags.size());
|
||||
//_log.debug("# tags created, registered, and written: " + tagsForDelivery.size());
|
||||
DataHelper.toLong(aesData, cur, 4, data.length);
|
||||
cur += 4;
|
||||
//_log.debug("data length: " + data.length);
|
||||
@ -519,8 +551,8 @@ public class ElGamalAESEngine {
|
||||
System.arraycopy(padding, 0, aesData, cur, padding.length);
|
||||
cur += padding.length;
|
||||
|
||||
//Hash h = _context.sha().calculateHash(aesUnencr);
|
||||
//_log.debug("Hash of entire aes block before encryption: (len=" + aesUnencr.length + ")\n" + DataHelper.toString(h.getData(), 32));
|
||||
//Hash h = _context.sha().calculateHash(data);
|
||||
//_log.debug("Hash of entire aes block before encryption: (len=" + data.length + ")\n" + DataHelper.toString(h.getData(), 32));
|
||||
_context.aes().encrypt(aesData, prefixBytes, aesData, prefixBytes, key, iv, aesData.length - prefixBytes);
|
||||
//_log.debug("Encrypted length: " + aesEncr.length);
|
||||
//return aesEncr;
|
||||
|
@ -9,20 +9,36 @@ import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
|
||||
import org.bouncycastle.crypto.digests.SHA256Digest;
|
||||
import org.bouncycastle.crypto.digests.MD5Digest;
|
||||
import org.bouncycastle.crypto.macs.HMac;
|
||||
|
||||
/**
|
||||
* Calculate the HMAC-SHA256 of a key+message. All the good stuff occurs
|
||||
* in {@link org.bouncycastle.crypto.macs.HMac} and
|
||||
* {@link org.bouncycastle.crypto.digests.SHA256Digest}.
|
||||
* {@link org.bouncycastle.crypto.digests.SHA256Digest}. Alternately, if
|
||||
* the context property "i2p.HMACMD5" is set to true, then this whole HMAC
|
||||
* generator will be transformed into HMACMD5, maintaining the same size and
|
||||
* using {@link org.bouncycastle.crypto.digests.MD5Digest}.
|
||||
*
|
||||
*/
|
||||
public class HMACSHA256Generator {
|
||||
private I2PAppContext _context;
|
||||
/** set of available HMAC instances for calculate */
|
||||
private List _available;
|
||||
/** set of available byte[] buffers for verify */
|
||||
private List _availableTmp;
|
||||
private boolean _useMD5;
|
||||
|
||||
public static final boolean DEFAULT_USE_MD5 = true;
|
||||
|
||||
public HMACSHA256Generator(I2PAppContext context) {
|
||||
_context = context;
|
||||
_available = new ArrayList(32);
|
||||
_availableTmp = new ArrayList(32);
|
||||
if ("true".equals(context.getProperty("i2p.HMACMD5", Boolean.toString(DEFAULT_USE_MD5).toLowerCase())))
|
||||
_useMD5 = true;
|
||||
else
|
||||
_useMD5 = false;
|
||||
}
|
||||
|
||||
public static HMACSHA256Generator getInstance() {
|
||||
@ -35,12 +51,15 @@ public class HMACSHA256Generator {
|
||||
public Hash calculate(SessionKey key, byte data[]) {
|
||||
if ((key == null) || (key.getData() == null) || (data == null))
|
||||
throw new NullPointerException("Null arguments for HMAC");
|
||||
return calculate(key, data, 0, data.length);
|
||||
byte rv[] = new byte[Hash.HASH_LENGTH];
|
||||
calculate(key, data, 0, data.length, rv, 0);
|
||||
return new Hash(rv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the HMAC of the data with the given key
|
||||
*/
|
||||
/*
|
||||
public Hash calculate(SessionKey key, byte data[], int offset, int length) {
|
||||
if ((key == null) || (key.getData() == null) || (data == null))
|
||||
throw new NullPointerException("Null arguments for HMAC");
|
||||
@ -53,12 +72,60 @@ public class HMACSHA256Generator {
|
||||
release(mac);
|
||||
return new Hash(rv);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Calculate the HMAC of the data with the given key
|
||||
*/
|
||||
public void calculate(SessionKey key, byte data[], int offset, int length, byte target[], int targetOffset) {
|
||||
if ((key == null) || (key.getData() == null) || (data == null))
|
||||
throw new NullPointerException("Null arguments for HMAC");
|
||||
|
||||
HMac mac = acquire();
|
||||
mac.init(key.getData());
|
||||
mac.update(data, offset, length);
|
||||
//byte rv[] = new byte[Hash.HASH_LENGTH];
|
||||
mac.doFinal(target, targetOffset);
|
||||
release(mac);
|
||||
//return new Hash(rv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the MAC inline, reducing some unnecessary memory churn.
|
||||
*
|
||||
* @param key session key to verify the MAC with
|
||||
* @param curData MAC to verify
|
||||
* @param curOffset index into curData to MAC
|
||||
* @param curLength how much data in curData do we want to run the HMAC over
|
||||
* @param origMAC what do we expect the MAC of curData to equal
|
||||
* @param origMACOffset index into origMAC
|
||||
* @param origMACLength how much of the MAC do we want to verify
|
||||
*/
|
||||
public boolean verify(SessionKey key, byte curData[], int curOffset, int curLength, byte origMAC[], int origMACOffset, int origMACLength) {
|
||||
if ((key == null) || (key.getData() == null) || (curData == null))
|
||||
throw new NullPointerException("Null arguments for HMAC");
|
||||
|
||||
HMac mac = acquire();
|
||||
mac.init(key.getData());
|
||||
mac.update(curData, curOffset, curLength);
|
||||
byte rv[] = acquireTmp();
|
||||
//byte rv[] = new byte[Hash.HASH_LENGTH];
|
||||
mac.doFinal(rv, 0);
|
||||
release(mac);
|
||||
|
||||
boolean eq = DataHelper.eq(rv, 0, origMAC, origMACOffset, origMACLength);
|
||||
releaseTmp(rv);
|
||||
return eq;
|
||||
}
|
||||
|
||||
private HMac acquire() {
|
||||
synchronized (_available) {
|
||||
if (_available.size() > 0)
|
||||
return (HMac)_available.remove(0);
|
||||
}
|
||||
if (_useMD5)
|
||||
return new HMac(new MD5Digest());
|
||||
else
|
||||
return new HMac(new SHA256Digest());
|
||||
}
|
||||
private void release(HMac mac) {
|
||||
@ -67,4 +134,24 @@ public class HMACSHA256Generator {
|
||||
_available.add(mac);
|
||||
}
|
||||
}
|
||||
|
||||
// temp buffers for verify(..)
|
||||
private byte[] acquireTmp() {
|
||||
byte rv[] = null;
|
||||
synchronized (_availableTmp) {
|
||||
if (_availableTmp.size() > 0)
|
||||
rv = (byte[])_availableTmp.remove(0);
|
||||
}
|
||||
if (rv != null)
|
||||
Arrays.fill(rv, (byte)0x0);
|
||||
else
|
||||
rv = new byte[Hash.HASH_LENGTH];
|
||||
return rv;
|
||||
}
|
||||
private void releaseTmp(byte tmp[]) {
|
||||
synchronized (_availableTmp) {
|
||||
if (_availableTmp.size() < 64)
|
||||
_availableTmp.add((Object)tmp);
|
||||
}
|
||||
}
|
||||
}
|
@ -58,6 +58,8 @@ public class PersistentSessionKeyManager extends TransientSessionKeyManager {
|
||||
*
|
||||
*/
|
||||
public void saveState(OutputStream out) throws IOException, DataFormatException {
|
||||
if (true) return;
|
||||
|
||||
Set tagSets = getInboundTagSets();
|
||||
Set sessions = getOutboundSessions();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
@ -12,7 +14,10 @@ import org.bouncycastle.crypto.digests.SHA256Digest;
|
||||
*
|
||||
*/
|
||||
public final class SHA256Generator {
|
||||
public SHA256Generator(I2PAppContext context) {}
|
||||
private List _digests;
|
||||
public SHA256Generator(I2PAppContext context) {
|
||||
_digests = new ArrayList(32);
|
||||
}
|
||||
|
||||
public static final SHA256Generator getInstance() {
|
||||
return I2PAppContext.getGlobalContext().sha();
|
||||
@ -27,11 +32,35 @@ public final class SHA256Generator {
|
||||
}
|
||||
public final Hash calculateHash(byte[] source, int start, int len) {
|
||||
byte rv[] = new byte[Hash.HASH_LENGTH];
|
||||
SHA256Digest digest = new SHA256Digest();
|
||||
digest.update(source, start, len);
|
||||
digest.doFinal(rv, 0);
|
||||
calculateHash(source, start, len, rv, 0);
|
||||
return new Hash(rv);
|
||||
}
|
||||
public final void calculateHash(byte[] source, int start, int len, byte out[], int outOffset) {
|
||||
SHA256Digest digest = acquire();
|
||||
digest.update(source, start, len);
|
||||
digest.doFinal(out, outOffset);
|
||||
release(digest);
|
||||
}
|
||||
|
||||
private SHA256Digest acquire() {
|
||||
SHA256Digest rv = null;
|
||||
synchronized (_digests) {
|
||||
if (_digests.size() > 0)
|
||||
rv = (SHA256Digest)_digests.remove(0);
|
||||
}
|
||||
if (rv != null)
|
||||
rv.reset();
|
||||
else
|
||||
rv = new SHA256Digest();
|
||||
return rv;
|
||||
}
|
||||
private void release(SHA256Digest digest) {
|
||||
synchronized (_digests) {
|
||||
if (_digests.size() < 32) {
|
||||
_digests.add(digest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
|
@ -174,7 +174,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
if (sess.getCurrentKey().equals(key)) {
|
||||
SessionTag nxt = sess.consumeNext();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Tag consumed: " + nxt);
|
||||
_log.debug("Tag consumed: " + nxt + " with key: " + key.toBase64());
|
||||
return nxt;
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -220,6 +220,11 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
*
|
||||
*/
|
||||
public void tagsDelivered(PublicKey target, SessionKey key, Set sessionTags) {
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
//_log.debug("Tags delivered to set " + set + " on session " + sess);
|
||||
if (sessionTags.size() > 0)
|
||||
_log.debug("Tags delivered: " + sessionTags.size() + " for key: " + key.toBase64() + ": " + sessionTags);
|
||||
}
|
||||
OutboundSession sess = getSession(target);
|
||||
if (sess == null) {
|
||||
createSession(target, key);
|
||||
@ -228,11 +233,6 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
sess.setCurrentKey(key);
|
||||
TagSet set = new TagSet(sessionTags, key, _context.clock().now());
|
||||
sess.addTags(set);
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("Tags delivered to set " + set + " on session " + sess);
|
||||
if (sessionTags.size() > 0)
|
||||
_log.debug("Tags delivered: " + sessionTags.size() + " total = " + sess.availableTags());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -252,13 +252,44 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
public void tagsReceived(SessionKey key, Set sessionTags) {
|
||||
int overage = 0;
|
||||
TagSet tagSet = new TagSet(sessionTags, key, _context.clock().now());
|
||||
TagSet old = null;
|
||||
SessionTag dupTag = null;
|
||||
for (Iterator iter = sessionTags.iterator(); iter.hasNext();) {
|
||||
SessionTag tag = (SessionTag) iter.next();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Receiving tag " + tag + " for key " + key);
|
||||
_log.debug("Receiving tag " + tag + " for key " + key.toBase64() + " / " + key.toString() + ": tagSet: " + tagSet);
|
||||
synchronized (_inboundTagSets) {
|
||||
_inboundTagSets.put(tag, tagSet);
|
||||
old = (TagSet)_inboundTagSets.put(tag, tagSet);
|
||||
overage = _inboundTagSets.size() - MAX_INBOUND_SESSION_TAGS;
|
||||
if (old != null) {
|
||||
if (!old.getAssociatedKey().equals(tagSet.getAssociatedKey())) {
|
||||
_inboundTagSets.remove(tag);
|
||||
dupTag = tag;
|
||||
break;
|
||||
} else {
|
||||
old = null; // ignore the dup
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (old != null) {
|
||||
// drop both old and tagSet tags
|
||||
synchronized (_inboundTagSets) {
|
||||
for (Iterator iter = old.getTags().iterator(); iter.hasNext(); ) {
|
||||
SessionTag tag = (SessionTag)iter.next();
|
||||
_inboundTagSets.remove(tag);
|
||||
}
|
||||
for (Iterator iter = sessionTags.iterator(); iter.hasNext(); ) {
|
||||
SessionTag tag = (SessionTag)iter.next();
|
||||
_inboundTagSets.remove(tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
_log.warn("Multiple tags matching! tagSet: " + tagSet + " and old tagSet: " + old + " tag: " + dupTag + "/" + dupTag.toBase64());
|
||||
_log.warn("Earlier tag set creation: " + old + ": key=" + old.getAssociatedKey().toBase64(), old.getCreatedBy());
|
||||
_log.warn("Current tag set creation: " + tagSet + ": key=" + tagSet.getAssociatedKey().toBase64(), tagSet.getCreatedBy());
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,6 +298,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
|
||||
if ( (sessionTags.size() <= 0) && (_log.shouldLog(Log.DEBUG)) )
|
||||
_log.debug("Received 0 tags for key " + key);
|
||||
if (false) aggressiveExpire();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -329,6 +361,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
*
|
||||
*/
|
||||
public SessionKey consumeTag(SessionTag tag) {
|
||||
if (false) aggressiveExpire();
|
||||
synchronized (_inboundTagSets) {
|
||||
TagSet tagSet = (TagSet) _inboundTagSets.remove(tag);
|
||||
if (tagSet == null) {
|
||||
@ -340,7 +373,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
|
||||
SessionKey key = tagSet.getAssociatedKey();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Consuming tag " + tag + " for sessionKey " + key);
|
||||
_log.debug("Consuming tag " + tag.toString() + " for sessionKey " + key.toBase64() + " / " + key.toString() + " on tagSet: " + tagSet);
|
||||
return key;
|
||||
}
|
||||
}
|
||||
@ -378,19 +411,36 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
int removed = 0;
|
||||
int remaining = 0;
|
||||
long now = _context.clock().now();
|
||||
StringBuffer buf = null;
|
||||
StringBuffer bufSummary = null;
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
buf = new StringBuffer(128);
|
||||
buf.append("Expiring inbound: ");
|
||||
bufSummary = new StringBuffer(1024);
|
||||
}
|
||||
synchronized (_inboundTagSets) {
|
||||
for (Iterator iter = _inboundTagSets.keySet().iterator(); iter.hasNext();) {
|
||||
SessionTag tag = (SessionTag) iter.next();
|
||||
TagSet ts = (TagSet) _inboundTagSets.get(tag);
|
||||
if (ts.getDate() < now - SESSION_LIFETIME_MAX_MS) {
|
||||
long age = now - ts.getDate();
|
||||
if (age > SESSION_LIFETIME_MAX_MS) {
|
||||
//if (ts.getDate() < now - SESSION_LIFETIME_MAX_MS) {
|
||||
iter.remove();
|
||||
removed++;
|
||||
if (buf != null)
|
||||
buf.append(tag.toString()).append(" @ age ").append(DataHelper.formatDuration(age));
|
||||
} else if (false && (bufSummary != null) ) {
|
||||
bufSummary.append("\nTagSet: " + ts.toString() + ", key: " + ts.getAssociatedKey().toBase64()+"/" + ts.getAssociatedKey().toString()
|
||||
+ ": tag: " + tag.toString());
|
||||
}
|
||||
}
|
||||
remaining = _inboundTagSets.size();
|
||||
}
|
||||
_context.statManager().addRateData("crypto.sessionTagsRemaining", remaining, 0);
|
||||
|
||||
if ( (buf != null) && (removed > 0) )
|
||||
_log.debug(buf.toString());
|
||||
if (bufSummary != null)
|
||||
_log.debug("Cleaning up with remaining: " + bufSummary.toString());
|
||||
|
||||
//_log.warn("Expiring tags: [" + tagsToDrop + "]");
|
||||
|
||||
@ -606,6 +656,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
private Set _sessionTags;
|
||||
private SessionKey _key;
|
||||
private long _date;
|
||||
private Exception _createdBy;
|
||||
|
||||
public TagSet(Set tags, SessionKey key, long date) {
|
||||
if (key == null) throw new IllegalArgumentException("Missing key");
|
||||
@ -613,6 +664,12 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
_sessionTags = tags;
|
||||
_key = key;
|
||||
_date = date;
|
||||
if (true) {
|
||||
long now = I2PAppContext.getGlobalContext().clock().now();
|
||||
_createdBy = new Exception("Created by: key=" + _key.toBase64() + " on "
|
||||
+ new Date(now) + "/" + now
|
||||
+ " via " + Thread.currentThread().getName());
|
||||
}
|
||||
}
|
||||
|
||||
/** when the tag set was created */
|
||||
@ -652,6 +709,8 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
_sessionTags.remove(first);
|
||||
return first;
|
||||
}
|
||||
|
||||
public Exception getCreatedBy() { return _createdBy; }
|
||||
|
||||
public int hashCode() {
|
||||
long rv = 0;
|
||||
@ -669,4 +728,4 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
&& ts.getDate() == getDate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,12 +55,11 @@ Hash: SHA1
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1.2.4 (GNU/Linux)
|
||||
|
||||
iD8DBQFCQXoJGnFL2th344YRAtsIAKCUy/sOIsxahUnT2hKLXFL9lXsAmACfUHa5
|
||||
CPah6TDXYJCWmR0n3oPtrvo=
|
||||
=mD0t
|
||||
iD8DBQFCZ38IWYfZ3rPnHH0RAgOHAJ4wNgmfO2AkL8IXiGnPtWrTlXcVogCfQ79z
|
||||
jP69nPbh4KLGhF+SD0+0bW4=
|
||||
=npPe
|
||||
-----END PGP SIGNATURE-----
|
||||
*/
|
||||
|
||||
private static final String VALID_VERSION_CHARS = "0123456789.";
|
||||
private static final int VERSION_BYTES = 16;
|
||||
private static final int HEADER_BYTES = Signature.SIGNATURE_BYTES + VERSION_BYTES;
|
||||
@ -288,15 +287,20 @@ CPah6TDXYJCWmR0n3oPtrvo=
|
||||
|
||||
try {
|
||||
fileInputStream = new FileInputStream(signedFile);
|
||||
byte[] data = new byte[VERSION_BYTES];
|
||||
int bytesRead = DataHelper.read(fileInputStream, data, Signature.SIGNATURE_BYTES, VERSION_BYTES);
|
||||
|
||||
if (bytesRead != VERSION_BYTES)
|
||||
long skipped = fileInputStream.skip(Signature.SIGNATURE_BYTES);
|
||||
if (skipped != Signature.SIGNATURE_BYTES)
|
||||
return "";
|
||||
byte[] data = new byte[VERSION_BYTES];
|
||||
int bytesRead = DataHelper.read(fileInputStream, data);
|
||||
|
||||
if (bytesRead != VERSION_BYTES) {
|
||||
return "";
|
||||
}
|
||||
|
||||
for (int i = 0; i < VERSION_BYTES; i++)
|
||||
if (data[i] == 0x00)
|
||||
if (data[i] == 0x00) {
|
||||
return new String(data, 0, i, "UTF-8");
|
||||
}
|
||||
|
||||
return new String(data, "UTF-8");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
|
@ -84,6 +84,17 @@ public class Base64 {
|
||||
(byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1',
|
||||
(byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
|
||||
(byte) '8', (byte) '9', (byte) '+', (byte) '/'};
|
||||
private final static byte[] ALPHABET_ALT = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
|
||||
(byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
|
||||
(byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R',
|
||||
(byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X',
|
||||
(byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd',
|
||||
(byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
|
||||
(byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
|
||||
(byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v',
|
||||
(byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1',
|
||||
(byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
|
||||
(byte) '8', (byte) '9', (byte) '-', (byte) '~'};
|
||||
|
||||
/**
|
||||
* Translates a Base64 value to either its 6-bit reconstruction value
|
||||
@ -131,6 +142,7 @@ public class Base64 {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
test();
|
||||
if (args.length == 0) {
|
||||
help();
|
||||
return;
|
||||
@ -312,6 +324,49 @@ public class Base64 {
|
||||
} // end switch
|
||||
} // end encode3to4
|
||||
|
||||
private static void encode3to4(byte[] source, int srcOffset, int numSigBytes, StringBuffer buf, byte alpha[]) {
|
||||
// 1 2 3
|
||||
// 01234567890123456789012345678901 Bit position
|
||||
// --------000000001111111122222222 Array position from threeBytes
|
||||
// --------| || || || | Six bit groups to index ALPHABET
|
||||
// >>18 >>12 >> 6 >> 0 Right shift necessary
|
||||
// 0x3f 0x3f 0x3f Additional AND
|
||||
|
||||
// Create buffer with zero-padding if there are only one or two
|
||||
// significant bytes passed in the array.
|
||||
// We have to shift left 24 in order to flush out the 1's that appear
|
||||
// when Java treats a value as negative that is cast from a byte to an int.
|
||||
int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
|
||||
| (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
|
||||
| (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
|
||||
|
||||
switch (numSigBytes) {
|
||||
case 3:
|
||||
buf.append((char)alpha[(inBuff >>> 18)]);
|
||||
buf.append((char)alpha[(inBuff >>> 12) & 0x3f]);
|
||||
buf.append((char)alpha[(inBuff >>> 6) & 0x3f]);
|
||||
buf.append((char)alpha[(inBuff) & 0x3f]);
|
||||
return;
|
||||
|
||||
case 2:
|
||||
buf.append((char)alpha[(inBuff >>> 18)]);
|
||||
buf.append((char)alpha[(inBuff >>> 12) & 0x3f]);
|
||||
buf.append((char)alpha[(inBuff >>> 6) & 0x3f]);
|
||||
buf.append((char)EQUALS_SIGN);
|
||||
return;
|
||||
|
||||
case 1:
|
||||
buf.append((char)alpha[(inBuff >>> 18)]);
|
||||
buf.append((char)alpha[(inBuff >>> 12) & 0x3f]);
|
||||
buf.append((char)EQUALS_SIGN);
|
||||
buf.append((char)EQUALS_SIGN);
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
} // end switch
|
||||
} // end encode3to4
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base64 notation.
|
||||
* Equivalen to calling
|
||||
@ -331,14 +386,12 @@ public class Base64 {
|
||||
private static String safeEncode(byte[] source, int off, int len, boolean useStandardAlphabet) {
|
||||
if (len + off > source.length)
|
||||
throw new ArrayIndexOutOfBoundsException("Trying to encode too much! source.len=" + source.length + " off=" + off + " len=" + len);
|
||||
String encoded = encodeBytes(source, off, len, false);
|
||||
if (useStandardAlphabet) {
|
||||
// noop
|
||||
} else {
|
||||
encoded = encoded.replace('/', '~');
|
||||
encoded = encoded.replace('+', '-');
|
||||
}
|
||||
return encoded;
|
||||
StringBuffer buf = new StringBuffer(len * 4 / 3);
|
||||
if (useStandardAlphabet)
|
||||
encodeBytes(source, off, len, false, buf, ALPHABET);
|
||||
else
|
||||
encodeBytes(source, off, len, false, buf, ALPHABET_ALT);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -381,6 +434,12 @@ public class Base64 {
|
||||
return encodeBytes(source, off, len, true);
|
||||
} // end encodeBytes
|
||||
|
||||
private static String encodeBytes(byte[] source, int off, int len, boolean breakLines) {
|
||||
StringBuffer buf = new StringBuffer( (len*4)/3 );
|
||||
encodeBytes(source, off, len, breakLines, buf, ALPHABET);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base64 notation.
|
||||
*
|
||||
@ -390,32 +449,36 @@ public class Base64 {
|
||||
* @param breakLines Break lines at 80 characters or less.
|
||||
* @since 1.4
|
||||
*/
|
||||
private static String encodeBytes(byte[] source, int off, int len, boolean breakLines) {
|
||||
private static void encodeBytes(byte[] source, int off, int len, boolean breakLines, StringBuffer out, byte alpha[]) {
|
||||
int len43 = len * 4 / 3;
|
||||
byte[] outBuff = new byte[(len43) // Main 4:3
|
||||
+ ((len % 3) > 0 ? 4 : 0) // Account for padding
|
||||
+ (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
|
||||
//byte[] outBuff = new byte[(len43) // Main 4:3
|
||||
// + ((len % 3) > 0 ? 4 : 0) // Account for padding
|
||||
// + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
|
||||
int d = 0;
|
||||
int e = 0;
|
||||
int len2 = len - 2;
|
||||
int lineLength = 0;
|
||||
for (; d < len2; d += 3, e += 4) {
|
||||
encode3to4(source, d + off, 3, outBuff, e);
|
||||
//encode3to4(source, d + off, 3, outBuff, e);
|
||||
encode3to4(source, d + off, 3, out, alpha);
|
||||
|
||||
lineLength += 4;
|
||||
if (breakLines && lineLength == MAX_LINE_LENGTH) {
|
||||
outBuff[e + 4] = NEW_LINE;
|
||||
//outBuff[e + 4] = NEW_LINE;
|
||||
out.append('\n');
|
||||
e++;
|
||||
lineLength = 0;
|
||||
} // end if: end of line
|
||||
} // en dfor: each piece of array
|
||||
|
||||
if (d < len) {
|
||||
encode3to4(source, d + off, len - d, outBuff, e);
|
||||
//encode3to4(source, d + off, len - d, outBuff, e);
|
||||
encode3to4(source, d + off, len - d, out, alpha);
|
||||
e += 4;
|
||||
} // end if: some padding needed
|
||||
|
||||
return new String(outBuff, 0, e);
|
||||
//out.append(new String(outBuff, 0, e));
|
||||
//return new String(outBuff, 0, e);
|
||||
} // end encodeBytes
|
||||
|
||||
/**
|
||||
|
@ -10,6 +10,7 @@ package net.i2p.data;
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import net.i2p.data.Base64;
|
||||
|
||||
/**
|
||||
* Wrap up an array of bytes so that they can be compared and placed in hashes,
|
||||
@ -26,8 +27,9 @@ public class ByteArray implements Serializable, Comparable {
|
||||
}
|
||||
|
||||
public ByteArray(byte[] data) {
|
||||
_offset = 0;
|
||||
_data = data;
|
||||
_valid = 0;
|
||||
_valid = (data != null ? data.length : 0);
|
||||
}
|
||||
public ByteArray(byte[] data, int offset, int length) {
|
||||
_data = data;
|
||||
@ -56,19 +58,20 @@ public class ByteArray implements Serializable, Comparable {
|
||||
public final boolean equals(Object o) {
|
||||
if (o == null) return false;
|
||||
if (o instanceof ByteArray) {
|
||||
return compare(getData(), ((ByteArray) o).getData());
|
||||
ByteArray ba = (ByteArray)o;
|
||||
return compare(getData(), _offset, _valid, ba.getData(), ba.getOffset(), ba.getValid());
|
||||
}
|
||||
|
||||
try {
|
||||
byte val[] = (byte[]) o;
|
||||
return compare(getData(), val);
|
||||
return compare(getData(), _offset, _valid, val, 0, val.length);
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final boolean compare(byte[] lhs, byte[] rhs) {
|
||||
return DataHelper.eq(lhs, rhs);
|
||||
private static final boolean compare(byte[] lhs, int loff, int llen, byte[] rhs, int roff, int rlen) {
|
||||
return (llen == rlen) && DataHelper.eq(lhs, loff, rhs, roff, llen);
|
||||
}
|
||||
|
||||
public final int compareTo(Object obj) {
|
||||
@ -81,6 +84,10 @@ public class ByteArray implements Serializable, Comparable {
|
||||
}
|
||||
|
||||
public final String toString() {
|
||||
return DataHelper.toString(getData(), 32);
|
||||
return super.toString() + "/" + DataHelper.toString(getData(), 32) + "." + _valid;
|
||||
}
|
||||
|
||||
public final String toBase64() {
|
||||
return Base64.encode(_data, _offset, _valid);
|
||||
}
|
||||
}
|
@ -721,7 +721,7 @@ public class DataHelper {
|
||||
public static int hashCode(byte b[]) {
|
||||
int rv = 0;
|
||||
if (b != null) {
|
||||
for (int i = 0; i < b.length && i < 8; i++)
|
||||
for (int i = 0; i < b.length && i < 32; i++)
|
||||
rv += (b[i] << i);
|
||||
}
|
||||
return rv;
|
||||
|
@ -24,8 +24,10 @@ import net.i2p.util.Log;
|
||||
public class SessionKey extends DataStructureImpl {
|
||||
private final static Log _log = new Log(SessionKey.class);
|
||||
private byte[] _data;
|
||||
private Object _preparedKey;
|
||||
|
||||
public final static int KEYSIZE_BYTES = 32;
|
||||
public static final SessionKey INVALID_KEY = new SessionKey(new byte[KEYSIZE_BYTES]);
|
||||
|
||||
public SessionKey() {
|
||||
this(null);
|
||||
@ -38,9 +40,23 @@ public class SessionKey extends DataStructureImpl {
|
||||
return _data;
|
||||
}
|
||||
|
||||
/**
|
||||
* caveat: this method isn't synchronized with the preparedKey, so don't
|
||||
* try to *change* the key data after already doing some
|
||||
* encryption/decryption (or if you do change it, be sure this object isn't
|
||||
* mid decrypt)
|
||||
*/
|
||||
public void setData(byte[] data) {
|
||||
_data = data;
|
||||
_preparedKey = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve an internal representation of the session key, as known
|
||||
* by the AES engine used. this can be reused safely
|
||||
*/
|
||||
public Object getPreparedKey() { return _preparedKey; }
|
||||
public void setPreparedKey(Object obj) { _preparedKey = obj; }
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
_data = new byte[KEYSIZE_BYTES];
|
||||
@ -63,7 +79,8 @@ public class SessionKey extends DataStructureImpl {
|
||||
return DataHelper.hashCode(_data);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
public String toString() {
|
||||
if (true) return super.toString();
|
||||
StringBuffer buf = new StringBuffer(64);
|
||||
buf.append("[SessionKey: ");
|
||||
if (_data == null) {
|
||||
|
@ -37,10 +37,12 @@ public class SessionTag extends ByteArray {
|
||||
}
|
||||
|
||||
public void setData(byte val[]) throws IllegalArgumentException {
|
||||
if (val == null) super.setData(null);
|
||||
if (val == null)
|
||||
throw new NullPointerException("SessionTags cannot be null");
|
||||
if (val.length != BYTE_LENGTH)
|
||||
throw new IllegalArgumentException("SessionTags must be " + BYTE_LENGTH + " bytes");
|
||||
super.setData(val);
|
||||
setValid(BYTE_LENGTH);
|
||||
}
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
|
@ -61,6 +61,7 @@ public abstract class I2CPMessageImpl extends DataStructureImpl implements I2CPM
|
||||
+ " class: " + getClass().getName() + ")");
|
||||
if (length < 0) throw new IOException("Negative payload size");
|
||||
|
||||
/*
|
||||
byte buf[] = new byte[length];
|
||||
int read = DataHelper.read(in, buf);
|
||||
if (read != length)
|
||||
@ -69,6 +70,8 @@ public abstract class I2CPMessageImpl extends DataStructureImpl implements I2CPM
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(buf);
|
||||
|
||||
doReadMessage(bis, length);
|
||||
*/
|
||||
doReadMessage(in, length);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,22 +26,25 @@ import net.i2p.util.Log;
|
||||
*/
|
||||
public class MessageId extends DataStructureImpl {
|
||||
private final static Log _log = new Log(MessageId.class);
|
||||
private int _messageId;
|
||||
private long _messageId;
|
||||
|
||||
public MessageId() {
|
||||
setMessageId(-1);
|
||||
}
|
||||
public MessageId(long id) {
|
||||
setMessageId(id);
|
||||
}
|
||||
|
||||
public int getMessageId() {
|
||||
public long getMessageId() {
|
||||
return _messageId;
|
||||
}
|
||||
|
||||
public void setMessageId(int id) {
|
||||
public void setMessageId(long id) {
|
||||
_messageId = id;
|
||||
}
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
_messageId = (int) DataHelper.readLong(in, 4);
|
||||
_messageId = DataHelper.readLong(in, 4);
|
||||
}
|
||||
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
@ -55,7 +58,7 @@ public class MessageId extends DataStructureImpl {
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return getMessageId();
|
||||
return (int)getMessageId();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
@ -27,29 +27,29 @@ import net.i2p.util.Log;
|
||||
public class MessagePayloadMessage extends I2CPMessageImpl {
|
||||
private final static Log _log = new Log(MessagePayloadMessage.class);
|
||||
public final static int MESSAGE_TYPE = 31;
|
||||
private SessionId _sessionId;
|
||||
private MessageId _messageId;
|
||||
private long _sessionId;
|
||||
private long _messageId;
|
||||
private Payload _payload;
|
||||
|
||||
public MessagePayloadMessage() {
|
||||
setSessionId(null);
|
||||
setMessageId(null);
|
||||
setSessionId(-1);
|
||||
setMessageId(-1);
|
||||
setPayload(null);
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
public long getSessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(SessionId id) {
|
||||
public void setSessionId(long id) {
|
||||
_sessionId = id;
|
||||
}
|
||||
|
||||
public MessageId getMessageId() {
|
||||
public long getMessageId() {
|
||||
return _messageId;
|
||||
}
|
||||
|
||||
public void setMessageId(MessageId id) {
|
||||
public void setMessageId(long id) {
|
||||
_messageId = id;
|
||||
}
|
||||
|
||||
@ -63,10 +63,8 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
|
||||
|
||||
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
|
||||
try {
|
||||
_sessionId = new SessionId();
|
||||
_sessionId.readBytes(in);
|
||||
_messageId = new MessageId();
|
||||
_messageId.readBytes(in);
|
||||
_sessionId = DataHelper.readLong(in, 2);
|
||||
_messageId = DataHelper.readLong(in, 4);
|
||||
_payload = new Payload();
|
||||
_payload.readBytes(in);
|
||||
} catch (DataFormatException dfe) {
|
||||
@ -84,9 +82,9 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
|
||||
*
|
||||
*/
|
||||
public void writeMessage(OutputStream out) throws I2CPMessageException, IOException {
|
||||
if (_sessionId == null)
|
||||
if (_sessionId <= 0)
|
||||
throw new I2CPMessageException("Unable to write out the message, as the session ID has not been defined");
|
||||
if (_messageId == null)
|
||||
if (_messageId < 0)
|
||||
throw new I2CPMessageException("Unable to write out the message, as the message ID has not been defined");
|
||||
if (_payload == null)
|
||||
throw new I2CPMessageException("Unable to write out the message, as the payload has not been defined");
|
||||
@ -95,8 +93,8 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
|
||||
try {
|
||||
DataHelper.writeLong(out, 4, size);
|
||||
DataHelper.writeLong(out, 1, getType());
|
||||
DataHelper.writeLong(out, 2, _sessionId.getSessionId());
|
||||
DataHelper.writeLong(out, 4, _messageId.getMessageId());
|
||||
DataHelper.writeLong(out, 2, _sessionId);
|
||||
DataHelper.writeLong(out, 4, _messageId);
|
||||
DataHelper.writeLong(out, 4, _payload.getSize());
|
||||
out.write(_payload.getEncryptedData());
|
||||
} catch (DataFormatException dfe) {
|
||||
|
@ -12,6 +12,7 @@ package net.i2p.data.i2cp;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
@ -26,8 +27,8 @@ import net.i2p.util.Log;
|
||||
public class MessageStatusMessage extends I2CPMessageImpl {
|
||||
private final static Log _log = new Log(SessionStatusMessage.class);
|
||||
public final static int MESSAGE_TYPE = 22;
|
||||
private SessionId _sessionId;
|
||||
private MessageId _messageId;
|
||||
private long _sessionId;
|
||||
private long _messageId;
|
||||
private long _nonce;
|
||||
private long _size;
|
||||
private int _status;
|
||||
@ -40,18 +41,18 @@ public class MessageStatusMessage extends I2CPMessageImpl {
|
||||
public final static int STATUS_SEND_GUARANTEED_FAILURE = 5;
|
||||
|
||||
public MessageStatusMessage() {
|
||||
setSessionId(null);
|
||||
setSessionId(-1);
|
||||
setStatus(-1);
|
||||
setMessageId(null);
|
||||
setMessageId(-1);
|
||||
setSize(-1);
|
||||
setNonce(-1);
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
public long getSessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(SessionId id) {
|
||||
public void setSessionId(long id) {
|
||||
_sessionId = id;
|
||||
}
|
||||
|
||||
@ -63,11 +64,11 @@ public class MessageStatusMessage extends I2CPMessageImpl {
|
||||
_status = status;
|
||||
}
|
||||
|
||||
public MessageId getMessageId() {
|
||||
public long getMessageId() {
|
||||
return _messageId;
|
||||
}
|
||||
|
||||
public void setMessageId(MessageId id) {
|
||||
public void setMessageId(long id) {
|
||||
_messageId = id;
|
||||
}
|
||||
|
||||
@ -108,10 +109,8 @@ public class MessageStatusMessage extends I2CPMessageImpl {
|
||||
|
||||
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
|
||||
try {
|
||||
_sessionId = new SessionId();
|
||||
_sessionId.readBytes(in);
|
||||
_messageId = new MessageId();
|
||||
_messageId.readBytes(in);
|
||||
_sessionId = DataHelper.readLong(in, 2);
|
||||
_messageId = DataHelper.readLong(in, 4);
|
||||
_status = (int) DataHelper.readLong(in, 1);
|
||||
_size = DataHelper.readLong(in, 4);
|
||||
_nonce = DataHelper.readLong(in, 4);
|
||||
@ -120,20 +119,32 @@ public class MessageStatusMessage extends I2CPMessageImpl {
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||
if ((_sessionId == null) || (_messageId == null) || (_status < 0) || (_nonce <= 0))
|
||||
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(64);
|
||||
|
||||
/**
|
||||
* Override to reduce mem churn
|
||||
*/
|
||||
public void writeMessage(OutputStream out) throws I2CPMessageException, IOException {
|
||||
int len = 2 + // sessionId
|
||||
4 + // messageId
|
||||
1 + // status
|
||||
4 + // size
|
||||
4; // nonce
|
||||
|
||||
try {
|
||||
_sessionId.writeBytes(os);
|
||||
_messageId.writeBytes(os);
|
||||
DataHelper.writeLong(os, 1, _status);
|
||||
DataHelper.writeLong(os, 4, _size);
|
||||
DataHelper.writeLong(os, 4, _nonce);
|
||||
DataHelper.writeLong(out, 4, len);
|
||||
DataHelper.writeLong(out, 1, getType());
|
||||
DataHelper.writeLong(out, 2, _sessionId);
|
||||
DataHelper.writeLong(out, 4, _messageId);
|
||||
DataHelper.writeLong(out, 1, _status);
|
||||
DataHelper.writeLong(out, 4, _size);
|
||||
DataHelper.writeLong(out, 4, _nonce);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2CPMessageException("Error writing out the message data", dfe);
|
||||
throw new I2CPMessageException("Unable to write the message length or type", dfe);
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||
throw new UnsupportedOperationException("This shouldn't be called... use writeMessage(out)");
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
|
@ -12,6 +12,7 @@ package net.i2p.data.i2cp;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
@ -26,50 +27,62 @@ import net.i2p.util.Log;
|
||||
public class ReceiveMessageBeginMessage extends I2CPMessageImpl {
|
||||
private final static Log _log = new Log(ReceiveMessageBeginMessage.class);
|
||||
public final static int MESSAGE_TYPE = 6;
|
||||
private SessionId _sessionId;
|
||||
private MessageId _messageId;
|
||||
private long _sessionId;
|
||||
private long _messageId;
|
||||
|
||||
public ReceiveMessageBeginMessage() {
|
||||
setSessionId(null);
|
||||
setMessageId(null);
|
||||
setSessionId(-1);
|
||||
setMessageId(-1);
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
public long getSessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(SessionId id) {
|
||||
public void setSessionId(long id) {
|
||||
_sessionId = id;
|
||||
}
|
||||
|
||||
public MessageId getMessageId() {
|
||||
public long getMessageId() {
|
||||
return _messageId;
|
||||
}
|
||||
|
||||
public void setMessageId(MessageId id) {
|
||||
public void setMessageId(long id) {
|
||||
_messageId = id;
|
||||
}
|
||||
|
||||
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
|
||||
try {
|
||||
_sessionId = new SessionId();
|
||||
_sessionId.readBytes(in);
|
||||
_messageId = new MessageId();
|
||||
_messageId.readBytes(in);
|
||||
_sessionId = DataHelper.readLong(in, 2);
|
||||
_messageId = DataHelper.readLong(in, 4);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2CPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||
if ((_sessionId == null) || (_messageId == null))
|
||||
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
|
||||
byte rv[] = new byte[2+4];
|
||||
DataHelper.toLong(rv, 0, 2, _sessionId.getSessionId());
|
||||
DataHelper.toLong(rv, 2, 4, _messageId.getMessageId());
|
||||
return rv;
|
||||
throw new UnsupportedOperationException("This shouldn't be called... use writeMessage(out)");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Override to reduce mem churn
|
||||
*/
|
||||
public void writeMessage(OutputStream out) throws I2CPMessageException, IOException {
|
||||
int len = 2 + // sessionId
|
||||
4; // messageId
|
||||
|
||||
try {
|
||||
DataHelper.writeLong(out, 4, len);
|
||||
DataHelper.writeLong(out, 1, getType());
|
||||
DataHelper.writeLong(out, 2, _sessionId);
|
||||
DataHelper.writeLong(out, 4, _messageId);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2CPMessageException("Unable to write the message length or type", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int getType() {
|
||||
return MESSAGE_TYPE;
|
||||
}
|
||||
|
@ -26,47 +26,45 @@ import net.i2p.util.Log;
|
||||
public class ReceiveMessageEndMessage extends I2CPMessageImpl {
|
||||
private final static Log _log = new Log(ReceiveMessageEndMessage.class);
|
||||
public final static int MESSAGE_TYPE = 7;
|
||||
private SessionId _sessionId;
|
||||
private MessageId _messageId;
|
||||
private long _sessionId;
|
||||
private long _messageId;
|
||||
|
||||
public ReceiveMessageEndMessage() {
|
||||
setSessionId(null);
|
||||
setMessageId(null);
|
||||
setSessionId(-1);
|
||||
setMessageId(-1);
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
public long getSessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(SessionId id) {
|
||||
public void setSessionId(long id) {
|
||||
_sessionId = id;
|
||||
}
|
||||
|
||||
public MessageId getMessageId() {
|
||||
public long getMessageId() {
|
||||
return _messageId;
|
||||
}
|
||||
|
||||
public void setMessageId(MessageId id) {
|
||||
public void setMessageId(long id) {
|
||||
_messageId = id;
|
||||
}
|
||||
|
||||
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
|
||||
try {
|
||||
_sessionId = new SessionId();
|
||||
_sessionId.readBytes(in);
|
||||
_messageId = new MessageId();
|
||||
_messageId.readBytes(in);
|
||||
_sessionId = DataHelper.readLong(in, 2);
|
||||
_messageId = DataHelper.readLong(in, 4);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2CPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||
if ((_sessionId == null) || (_messageId == null))
|
||||
if ((_sessionId < 0) || (_messageId < 0))
|
||||
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
|
||||
byte rv[] = new byte[2+4];
|
||||
DataHelper.toLong(rv, 0, 2, _sessionId.getSessionId());
|
||||
DataHelper.toLong(rv, 2, 4, _messageId.getMessageId());
|
||||
DataHelper.toLong(rv, 0, 2, _sessionId);
|
||||
DataHelper.toLong(rv, 2, 4, _messageId);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,7 @@ public class RequestLeaseSetMessage extends I2CPMessageImpl {
|
||||
RequestLeaseSetMessage msg = (RequestLeaseSetMessage) object;
|
||||
if (getEndpoints() != msg.getEndpoints()) return false;
|
||||
for (int i = 0; i < getEndpoints(); i++) {
|
||||
if (!DataHelper.eq(getRouter(i), msg.getRouter(i)) || DataHelper.eq(getTunnelId(i), msg.getTunnelId(i)))
|
||||
if (!DataHelper.eq(getRouter(i), msg.getRouter(i)) || !DataHelper.eq(getTunnelId(i), msg.getTunnelId(i)))
|
||||
return false;
|
||||
}
|
||||
return DataHelper.eq(getSessionId(), msg.getSessionId()) && DataHelper.eq(getEndDate(), msg.getEndDate());
|
||||
|
@ -30,11 +30,15 @@ public class BufferedStatLog implements StatLog {
|
||||
private BufferedWriter _out;
|
||||
private String _outFile;
|
||||
|
||||
private static final int BUFFER_SIZE = 1024;
|
||||
private static final boolean DISABLE_LOGGING = false;
|
||||
|
||||
public BufferedStatLog(I2PAppContext ctx) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(BufferedStatLog.class);
|
||||
_events = new StatEvent[1000];
|
||||
for (int i = 0; i < 1000; i++)
|
||||
_events = new StatEvent[BUFFER_SIZE];
|
||||
if (DISABLE_LOGGING) return;
|
||||
for (int i = 0; i < BUFFER_SIZE; i++)
|
||||
_events[i] = new StatEvent();
|
||||
_eventNext = 0;
|
||||
_lastWrite = _events.length-1;
|
||||
@ -46,6 +50,7 @@ public class BufferedStatLog implements StatLog {
|
||||
}
|
||||
|
||||
public void addData(String scope, String stat, long value, long duration) {
|
||||
if (DISABLE_LOGGING) return;
|
||||
synchronized (_events) {
|
||||
_events[_eventNext].init(scope, stat, value, duration);
|
||||
_eventNext = (_eventNext + 1) % _events.length;
|
||||
@ -71,9 +76,9 @@ public class BufferedStatLog implements StatLog {
|
||||
return _statFilters.contains(stat) || _statFilters.contains("*");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void updateFilters() {
|
||||
String val = _context.getProperty("stat.logFilters");
|
||||
String val = _context.getProperty(StatManager.PROP_STAT_FILTER);
|
||||
if (val != null) {
|
||||
if ( (_lastFilters != null) && (_lastFilters.equals(val)) ) {
|
||||
// noop
|
||||
@ -90,9 +95,9 @@ public class BufferedStatLog implements StatLog {
|
||||
synchronized (_statFilters) { _statFilters.clear(); }
|
||||
}
|
||||
|
||||
String filename = _context.getProperty("stat.logFile");
|
||||
String filename = _context.getProperty(StatManager.PROP_STAT_FILE);
|
||||
if (filename == null)
|
||||
filename = "stats.log";
|
||||
filename = StatManager.DEFAULT_STAT_FILE;
|
||||
if ( (_outFile != null) && (_outFile.equals(filename)) ) {
|
||||
// noop
|
||||
} else {
|
||||
|
@ -29,6 +29,10 @@ public class StatManager {
|
||||
private Map _rateStats;
|
||||
private StatLog _statLog;
|
||||
|
||||
public static final String PROP_STAT_FILTER = "stat.logFilters";
|
||||
public static final String PROP_STAT_FILE = "stat.logFile";
|
||||
public static final String DEFAULT_STAT_FILE = "stats.log";
|
||||
|
||||
/**
|
||||
* The stat manager should only be constructed and accessed through the
|
||||
* application context. This constructor should only be used by the
|
||||
@ -139,7 +143,7 @@ public class StatManager {
|
||||
return _frequencyStats.containsKey(statName);
|
||||
}
|
||||
|
||||
/** Group name (String) to a Set of stat names */
|
||||
/** Group name (String) to a Set of stat names, ordered alphabetically */
|
||||
public Map getStatsByGroup() {
|
||||
Map groups = new TreeMap();
|
||||
for (Iterator iter = _frequencyStats.values().iterator(); iter.hasNext();) {
|
||||
@ -156,4 +160,7 @@ public class StatManager {
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
public String getStatFilter() { return _context.getProperty(PROP_STAT_FILTER); }
|
||||
public String getStatFile() { return _context.getProperty(PROP_STAT_FILE, DEFAULT_STAT_FILE); }
|
||||
}
|
||||
|
228
core/java/src/net/i2p/util/BufferedRandomSource.java
Normal file
228
core/java/src/net/i2p/util/BufferedRandomSource.java
Normal file
@ -0,0 +1,228 @@
|
||||
package net.i2p.util;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by jrandom in 2005 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.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.EntropyHarvester;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* Allocate data out of a large buffer of data, rather than the PRNG's
|
||||
* (likely) small buffer to reduce the frequency of prng recalcs (though
|
||||
* the recalcs are now more time consuming).
|
||||
*
|
||||
*/
|
||||
public class BufferedRandomSource extends RandomSource {
|
||||
private byte _buffer[];
|
||||
private int _nextByte;
|
||||
private int _nextBit;
|
||||
private static volatile long _reseeds;
|
||||
|
||||
private static final int DEFAULT_BUFFER_SIZE = 256*1024;
|
||||
|
||||
public BufferedRandomSource(I2PAppContext context) {
|
||||
this(context, DEFAULT_BUFFER_SIZE);
|
||||
}
|
||||
public BufferedRandomSource(I2PAppContext context, int bufferSize) {
|
||||
super(context);
|
||||
context.statManager().createRateStat("prng.reseedCount", "How many times the prng has been reseeded", "Encryption", new long[] { 60*1000, 10*60*1000, 60*60*1000 } );
|
||||
_buffer = new byte[bufferSize];
|
||||
refillBuffer();
|
||||
// stagger reseeding
|
||||
_nextByte = ((int)_reseeds-1) * 16 * 1024;
|
||||
}
|
||||
|
||||
private final void refillBuffer() {
|
||||
long before = System.currentTimeMillis();
|
||||
doRefillBuffer();
|
||||
long duration = System.currentTimeMillis() - before;
|
||||
if ( (_reseeds % 1) == 0)
|
||||
_context.statManager().addRateData("prng.reseedCount", _reseeds, duration);
|
||||
}
|
||||
|
||||
private synchronized final void doRefillBuffer() {
|
||||
super.nextBytes(_buffer);
|
||||
_nextByte = 0;
|
||||
_nextBit = 0;
|
||||
_reseeds++;
|
||||
}
|
||||
|
||||
private static final byte GOBBLE_MASK[] = { 0x0, // 0 bits
|
||||
0x1, // 1 bit
|
||||
0x3, // 2 bits
|
||||
0x7, // 3 bits
|
||||
0xF, // 4 bits
|
||||
0x1F, // 5 bits
|
||||
0x3F, // 6 bits
|
||||
0x7F, // 7 bits
|
||||
(byte)0xFF // 8 bits
|
||||
};
|
||||
|
||||
private synchronized final long nextBits(int numBits) {
|
||||
if (false) {
|
||||
long rv = 0;
|
||||
for (int curBit = 0; curBit < numBits; curBit++) {
|
||||
if (_nextBit >= 8) {
|
||||
_nextBit = 0;
|
||||
_nextByte++;
|
||||
}
|
||||
if (_nextByte >= _buffer.length)
|
||||
refillBuffer();
|
||||
rv += (_buffer[_nextByte] << curBit);
|
||||
_nextBit++;
|
||||
/*
|
||||
int avail = 8 - _nextBit;
|
||||
// this is not correct! (or is it?)
|
||||
rv += (_buffer[_nextByte] << 8 - avail);
|
||||
_nextBit += avail;
|
||||
numBits -= avail;
|
||||
if (_nextBit >= 8) {
|
||||
_nextBit = 0;
|
||||
_nextByte++;
|
||||
}
|
||||
*/
|
||||
}
|
||||
return rv;
|
||||
} else {
|
||||
long rv = 0;
|
||||
int curBit = 0;
|
||||
while (curBit < numBits) {
|
||||
if (_nextBit >= 8) {
|
||||
_nextBit = 0;
|
||||
_nextByte++;
|
||||
}
|
||||
if (_nextByte >= _buffer.length)
|
||||
refillBuffer();
|
||||
int gobbleBits = 8 - _nextBit;
|
||||
int want = numBits - curBit;
|
||||
if (gobbleBits > want)
|
||||
gobbleBits = want;
|
||||
curBit += gobbleBits;
|
||||
int shift = 8 - _nextBit - gobbleBits;
|
||||
int c = (_buffer[_nextByte] & (GOBBLE_MASK[gobbleBits] << shift));
|
||||
rv += ((c >>> shift) << (curBit-gobbleBits));
|
||||
_nextBit += gobbleBits;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized final void nextBytes(byte buf[]) {
|
||||
int outOffset = 0;
|
||||
while (outOffset < buf.length) {
|
||||
int availableBytes = _buffer.length - _nextByte - (_nextBit != 0 ? 1 : 0);
|
||||
if (availableBytes <= 0)
|
||||
refillBuffer();
|
||||
int start = _buffer.length - availableBytes;
|
||||
int writeSize = Math.min(buf.length - outOffset, availableBytes);
|
||||
System.arraycopy(_buffer, start, buf, outOffset, writeSize);
|
||||
outOffset += writeSize;
|
||||
_nextByte += writeSize;
|
||||
_nextBit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public final int nextInt(int n) {
|
||||
if (n <= 0) return 0;
|
||||
int val = ((int)nextBits(countBits(n))) % n;
|
||||
if (val < 0)
|
||||
return 0 - val;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
|
||||
public final int nextInt() { return nextInt(Integer.MAX_VALUE); }
|
||||
|
||||
/**
|
||||
* Like the modified nextInt, nextLong(n) returns a random number from 0 through n,
|
||||
* including 0, excluding n.
|
||||
*/
|
||||
public final long nextLong(long n) {
|
||||
if (n <= 0) return 0;
|
||||
long val = nextBits(countBits(n)) % n;
|
||||
if (val < 0)
|
||||
return 0 - val;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
|
||||
public final long nextLong() { return nextLong(Long.MAX_VALUE); }
|
||||
|
||||
static final int countBits(long val) {
|
||||
int rv = 0;
|
||||
while (val > Integer.MAX_VALUE) {
|
||||
rv += 31;
|
||||
val >>>= 31;
|
||||
}
|
||||
|
||||
while (val > 0) {
|
||||
rv++;
|
||||
val >>= 1;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public final boolean nextBoolean() {
|
||||
return nextBits(1) != 0;
|
||||
}
|
||||
|
||||
private static final double DOUBLE_DENOMENATOR = (double)(1L << 53);
|
||||
/** defined per javadoc ( ((nextBits(26)<<27) + nextBits(27)) / (1 << 53)) */
|
||||
public final double nextDouble() {
|
||||
long top = (((long)nextBits(26) << 27) + nextBits(27));
|
||||
return top / DOUBLE_DENOMENATOR;
|
||||
}
|
||||
private static final float FLOAT_DENOMENATOR = (float)(1 << 24);
|
||||
/** defined per javadoc (nextBits(24) / ((float)(1 << 24)) ) */
|
||||
public float nextFloat() {
|
||||
long top = nextBits(24);
|
||||
return top / FLOAT_DENOMENATOR;
|
||||
}
|
||||
public double nextGaussian() {
|
||||
// bah, unbuffered
|
||||
return super.nextGaussian();
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
for (int i = 0; i < 16; i++)
|
||||
test();
|
||||
}
|
||||
private static void test() {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
byte data[] = new byte[16*1024];
|
||||
for (int i = 0; i < data.length; i += 4) {
|
||||
long l = ctx.random().nextLong();
|
||||
if (l < 0) l = 0 - l;
|
||||
DataHelper.toLong(data, i, 4, l);
|
||||
}
|
||||
byte compressed[] = DataHelper.compress(data);
|
||||
System.out.println("Data: " + data.length + "/" + compressed.length + ": " + toString(data));
|
||||
}
|
||||
private static final String toString(byte data[]) {
|
||||
StringBuffer buf = new StringBuffer(data.length * 9);
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if ((data[i] & (1 << j)) != 0)
|
||||
buf.append('1');
|
||||
else
|
||||
buf.append('0');
|
||||
}
|
||||
buf.append(' ');
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -85,11 +85,15 @@ public final class ByteCache {
|
||||
*
|
||||
*/
|
||||
public final void release(ByteArray entry) {
|
||||
release(entry, true);
|
||||
}
|
||||
public final void release(ByteArray entry, boolean shouldZero) {
|
||||
if (_cache) {
|
||||
if ( (entry == null) || (entry.getData() == null) )
|
||||
return;
|
||||
|
||||
Arrays.fill(entry.getData(), (byte)0x0);
|
||||
if (shouldZero)
|
||||
Arrays.fill(entry.getData(), (byte)0x0);
|
||||
synchronized (_available) {
|
||||
if (_available.size() < _maxCached)
|
||||
_available.add(entry);
|
||||
|
@ -59,6 +59,7 @@ public class Clock implements Timestamper.UpdateListener {
|
||||
*
|
||||
*/
|
||||
public void setOffset(long offsetMs, boolean force) {
|
||||
if (false) return;
|
||||
long delta = offsetMs - _offset;
|
||||
if (!force) {
|
||||
if ((offsetMs > MAX_OFFSET) || (offsetMs < 0 - MAX_OFFSET)) {
|
||||
|
@ -28,6 +28,8 @@ public class DecayingBloomFilter {
|
||||
private long _currentDuplicates;
|
||||
private boolean _keepDecaying;
|
||||
private DecayEvent _decayEvent;
|
||||
|
||||
private static final boolean ALWAYS_MISS = false;
|
||||
|
||||
/**
|
||||
* Create a bloom filter that will decay its entries over time.
|
||||
@ -41,8 +43,8 @@ public class DecayingBloomFilter {
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(DecayingBloomFilter.class);
|
||||
_entryBytes = entryBytes;
|
||||
_current = new BloomSHA1(23, 11);
|
||||
_previous = new BloomSHA1(23, 11);
|
||||
_current = new BloomSHA1(23, 11); //new BloomSHA1(23, 11);
|
||||
_previous = new BloomSHA1(23, 11); //new BloomSHA1(23, 11);
|
||||
_durationMs = durationMs;
|
||||
int numExtenders = (32+ (entryBytes-1))/entryBytes - 1;
|
||||
if (numExtenders < 0)
|
||||
@ -78,6 +80,7 @@ public class DecayingBloomFilter {
|
||||
*
|
||||
*/
|
||||
public boolean add(byte entry[]) {
|
||||
if (ALWAYS_MISS) return false;
|
||||
if (entry == null)
|
||||
throw new IllegalArgumentException("Null entry");
|
||||
if (entry.length != _entryBytes)
|
||||
@ -95,6 +98,7 @@ public class DecayingBloomFilter {
|
||||
*
|
||||
*/
|
||||
public boolean add(long entry) {
|
||||
if (ALWAYS_MISS) return false;
|
||||
synchronized (this) {
|
||||
if (_entryBytes <= 7)
|
||||
entry &= _longToEntryMask;
|
||||
@ -108,7 +112,30 @@ public class DecayingBloomFilter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if the entry is already known. this does NOT add the
|
||||
* entry however.
|
||||
*
|
||||
*/
|
||||
public boolean isKnown(long entry) {
|
||||
if (ALWAYS_MISS) return false;
|
||||
synchronized (this) {
|
||||
if (_entryBytes <= 7)
|
||||
entry &= _longToEntryMask;
|
||||
if (entry < 0) {
|
||||
DataHelper.toLong(_longToEntry, 0, _entryBytes, 0-entry);
|
||||
_longToEntry[0] |= (1 << 7);
|
||||
} else {
|
||||
DataHelper.toLong(_longToEntry, 0, _entryBytes, entry);
|
||||
}
|
||||
return locked_add(_longToEntry, false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean locked_add(byte entry[]) {
|
||||
return locked_add(entry, true);
|
||||
}
|
||||
private boolean locked_add(byte entry[], boolean addIfNew) {
|
||||
if (_extended != null) {
|
||||
// extend the entry to 32 bytes
|
||||
System.arraycopy(entry, 0, _extended, 0, entry.length);
|
||||
@ -121,8 +148,10 @@ public class DecayingBloomFilter {
|
||||
_currentDuplicates++;
|
||||
return true;
|
||||
} else {
|
||||
_current.insert(_extended);
|
||||
_previous.insert(_extended);
|
||||
if (addIfNew) {
|
||||
_current.insert(_extended);
|
||||
_previous.insert(_extended);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
@ -132,8 +161,10 @@ public class DecayingBloomFilter {
|
||||
_currentDuplicates++;
|
||||
return true;
|
||||
} else {
|
||||
_current.locked_insert(entry);
|
||||
_previous.locked_insert(entry);
|
||||
if (addIfNew) {
|
||||
_current.locked_insert(entry);
|
||||
_previous.locked_insert(entry);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -140,8 +140,8 @@ public class LogManager {
|
||||
public Log getLog(String name) { return getLog(null, name); }
|
||||
public Log getLog(Class cls, String name) {
|
||||
Log rv = null;
|
||||
String scope = Log.getScope(name, cls);
|
||||
synchronized (_logs) {
|
||||
String scope = Log.getScope(name, cls);
|
||||
rv = (Log)_logs.get(scope);
|
||||
if (rv == null) {
|
||||
rv = new Log(this, cls, name);
|
||||
@ -154,10 +154,7 @@ public class LogManager {
|
||||
public List getLogs() {
|
||||
List rv = null;
|
||||
synchronized (_logs) {
|
||||
rv = new ArrayList(_logs.size());
|
||||
for (Iterator iter = _logs.values().iterator(); iter.hasNext(); ) {
|
||||
rv.add(iter.next());
|
||||
}
|
||||
rv = new ArrayList(_logs.values());
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
188
core/java/src/net/i2p/util/PooledRandomSource.java
Normal file
188
core/java/src/net/i2p/util/PooledRandomSource.java
Normal file
@ -0,0 +1,188 @@
|
||||
package net.i2p.util;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by jrandom in 2005 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.
|
||||
*
|
||||
*/
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.EntropyHarvester;
|
||||
|
||||
/**
|
||||
* Maintain a set of PRNGs to feed the apps
|
||||
*/
|
||||
public class PooledRandomSource extends RandomSource {
|
||||
private Log _log;
|
||||
protected RandomSource _pool[];
|
||||
protected volatile int _nextPool;
|
||||
|
||||
public static final int POOL_SIZE = 16;
|
||||
/**
|
||||
* How much random data will we precalculate and feed from (as opposed to on demand
|
||||
* reseeding, etc). If this is not set, a default will be used (4MB), or if it is
|
||||
* set to 0, no buffer will be used, otherwise the amount specified will be allocated
|
||||
* across the pooled PRNGs.
|
||||
*
|
||||
*/
|
||||
public static final String PROP_BUFFER_SIZE = "i2p.prng.totalBufferSizeKB";
|
||||
|
||||
public PooledRandomSource(I2PAppContext context) {
|
||||
super(context);
|
||||
_log = context.logManager().getLog(PooledRandomSource.class);
|
||||
initializePool(context);
|
||||
}
|
||||
|
||||
protected void initializePool(I2PAppContext context) {
|
||||
_pool = new RandomSource[POOL_SIZE];
|
||||
|
||||
String totalSizeProp = context.getProperty(PROP_BUFFER_SIZE);
|
||||
int totalSize = -1;
|
||||
if (totalSizeProp != null) {
|
||||
try {
|
||||
totalSize = Integer.parseInt(totalSizeProp);
|
||||
} catch (NumberFormatException nfe) {
|
||||
totalSize = -1;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < POOL_SIZE; i++) {
|
||||
if (totalSize < 0)
|
||||
_pool[i] = new BufferedRandomSource(context);
|
||||
else if (totalSize > 0)
|
||||
_pool[i] = new BufferedRandomSource(context, (totalSize*1024) / POOL_SIZE);
|
||||
else
|
||||
_pool[i] = new RandomSource(context);
|
||||
_pool[i].nextBoolean();
|
||||
}
|
||||
_nextPool = 0;
|
||||
}
|
||||
|
||||
private final RandomSource pickPRNG() {
|
||||
// how much more explicit can we get?
|
||||
int cur = _nextPool;
|
||||
cur = cur % POOL_SIZE;
|
||||
RandomSource rv = _pool[cur];
|
||||
cur++;
|
||||
cur = cur % POOL_SIZE;
|
||||
_nextPool = cur;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* According to the java docs (http://java.sun.com/j2se/1.4.1/docs/api/java/util/Random.html#nextInt(int))
|
||||
* nextInt(n) should return a number between 0 and n (including 0 and excluding n). However, their pseudocode,
|
||||
* as well as sun's, kaffe's, and classpath's implementation INCLUDES NEGATIVE VALUES.
|
||||
* WTF. Ok, so we're going to have it return between 0 and n (including 0, excluding n), since
|
||||
* thats what it has been used for.
|
||||
*
|
||||
*/
|
||||
public int nextInt(int n) {
|
||||
RandomSource prng = pickPRNG();
|
||||
synchronized (prng) {
|
||||
return prng.nextInt(n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like the modified nextInt, nextLong(n) returns a random number from 0 through n,
|
||||
* including 0, excluding n.
|
||||
*/
|
||||
public long nextLong(long n) {
|
||||
RandomSource prng = pickPRNG();
|
||||
synchronized (prng) {
|
||||
return prng.nextLong(n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public boolean nextBoolean() {
|
||||
RandomSource prng = pickPRNG();
|
||||
synchronized (prng) {
|
||||
return prng.nextBoolean();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public void nextBytes(byte buf[]) {
|
||||
RandomSource prng = pickPRNG();
|
||||
synchronized (prng) {
|
||||
prng.nextBytes(buf);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public double nextDouble() {
|
||||
RandomSource prng = pickPRNG();
|
||||
synchronized (prng) {
|
||||
return prng.nextDouble();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public float nextFloat() {
|
||||
RandomSource prng = pickPRNG();
|
||||
synchronized (prng) {
|
||||
return prng.nextFloat();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public double nextGaussian() {
|
||||
RandomSource prng = pickPRNG();
|
||||
synchronized (prng) {
|
||||
return prng.nextGaussian();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public int nextInt() {
|
||||
RandomSource prng = pickPRNG();
|
||||
synchronized (prng) {
|
||||
return prng.nextInt();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public long nextLong() {
|
||||
RandomSource prng = pickPRNG();
|
||||
synchronized (prng) {
|
||||
return prng.nextLong();
|
||||
}
|
||||
}
|
||||
|
||||
public EntropyHarvester harvester() {
|
||||
RandomSource prng = pickPRNG();
|
||||
return prng.harvester();
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
PooledRandomSource prng = new PooledRandomSource(I2PAppContext.getGlobalContext());
|
||||
int size = 8*1024*1024;
|
||||
try {
|
||||
java.io.FileOutputStream out = new java.io.FileOutputStream("random.file");
|
||||
for (int i = 0; i < size; i++) {
|
||||
out.write(prng.nextInt());
|
||||
}
|
||||
out.close();
|
||||
} catch (Exception e) { e.printStackTrace(); }
|
||||
System.out.println("Written to random.file");
|
||||
}
|
||||
}
|
@ -22,9 +22,11 @@ import net.i2p.crypto.EntropyHarvester;
|
||||
public class RandomSource extends SecureRandom {
|
||||
private Log _log;
|
||||
private EntropyHarvester _entropyHarvester;
|
||||
protected I2PAppContext _context;
|
||||
|
||||
public RandomSource(I2PAppContext context) {
|
||||
super();
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(RandomSource.class);
|
||||
// when we replace to have hooks for fortuna (etc), replace with
|
||||
// a factory (or just a factory method)
|
||||
@ -42,7 +44,7 @@ public class RandomSource extends SecureRandom {
|
||||
* thats what it has been used for.
|
||||
*
|
||||
*/
|
||||
public synchronized int nextInt(int n) {
|
||||
public int nextInt(int n) {
|
||||
if (n == 0) return 0;
|
||||
int val = super.nextInt(n);
|
||||
if (val < 0) val = 0 - val;
|
||||
@ -54,7 +56,7 @@ public class RandomSource extends SecureRandom {
|
||||
* Like the modified nextInt, nextLong(n) returns a random number from 0 through n,
|
||||
* including 0, excluding n.
|
||||
*/
|
||||
public synchronized long nextLong(long n) {
|
||||
public long nextLong(long n) {
|
||||
long v = super.nextLong();
|
||||
if (v < 0) v = 0 - v;
|
||||
if (v >= n) v = v % n;
|
||||
@ -65,37 +67,37 @@ public class RandomSource extends SecureRandom {
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public synchronized boolean nextBoolean() { return super.nextBoolean(); }
|
||||
public boolean nextBoolean() { return super.nextBoolean(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public synchronized void nextBytes(byte buf[]) { super.nextBytes(buf); }
|
||||
public void nextBytes(byte buf[]) { super.nextBytes(buf); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public synchronized double nextDouble() { return super.nextDouble(); }
|
||||
public double nextDouble() { return super.nextDouble(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public synchronized float nextFloat() { return super.nextFloat(); }
|
||||
public float nextFloat() { return super.nextFloat(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public synchronized double nextGaussian() { return super.nextGaussian(); }
|
||||
public double nextGaussian() { return super.nextGaussian(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public synchronized int nextInt() { return super.nextInt(); }
|
||||
public int nextInt() { return super.nextInt(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public synchronized long nextLong() { return super.nextLong(); }
|
||||
public long nextLong() { return super.nextLong(); }
|
||||
|
||||
public EntropyHarvester harvester() { return _entropyHarvester; }
|
||||
|
||||
|
@ -45,7 +45,10 @@ public class SimpleTimer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue up the given event to be fired no sooner than timeoutMs from now
|
||||
* Queue up the given event to be fired no sooner than timeoutMs from now.
|
||||
* However, if this event is already scheduled, the event will be scheduled
|
||||
* for the earlier of the two timeouts, which may be before this stated
|
||||
* timeout. If this is not the desired behavior, call removeEvent first.
|
||||
*
|
||||
*/
|
||||
public void addEvent(TimedEvent event, long timeoutMs) {
|
||||
@ -55,8 +58,15 @@ public class SimpleTimer {
|
||||
Long time = new Long(eventTime);
|
||||
synchronized (_events) {
|
||||
// remove the old scheduled position, then reinsert it
|
||||
if (_eventTimes.containsKey(event))
|
||||
_events.remove(_eventTimes.get(event));
|
||||
Long oldTime = (Long)_eventTimes.get(event);
|
||||
if (oldTime != null) {
|
||||
if (oldTime.longValue() < eventTime) {
|
||||
_events.notifyAll();
|
||||
return; // already scheduled for sooner than requested
|
||||
} else {
|
||||
_events.remove(oldTime);
|
||||
}
|
||||
}
|
||||
while (_events.containsKey(time))
|
||||
time = new Long(time.longValue() + 1);
|
||||
_events.put(time, event);
|
||||
|
302
core/java/src/org/bouncycastle/crypto/digests/MD5Digest.java
Normal file
302
core/java/src/org/bouncycastle/crypto/digests/MD5Digest.java
Normal file
@ -0,0 +1,302 @@
|
||||
package org.bouncycastle.crypto.digests;
|
||||
|
||||
|
||||
/**
|
||||
* implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347.
|
||||
*/
|
||||
public class MD5Digest
|
||||
extends GeneralDigest
|
||||
{
|
||||
private static final int DIGEST_LENGTH = 16;
|
||||
|
||||
private int H1, H2, H3, H4; // IV's
|
||||
|
||||
private int[] X = new int[16];
|
||||
private int xOff;
|
||||
|
||||
/**
|
||||
* Standard constructor
|
||||
*/
|
||||
public MD5Digest()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor. This will copy the state of the provided
|
||||
* message digest.
|
||||
*/
|
||||
public MD5Digest(MD5Digest t)
|
||||
{
|
||||
super(t);
|
||||
|
||||
H1 = t.H1;
|
||||
H2 = t.H2;
|
||||
H3 = t.H3;
|
||||
H4 = t.H4;
|
||||
|
||||
System.arraycopy(t.X, 0, X, 0, t.X.length);
|
||||
xOff = t.xOff;
|
||||
}
|
||||
|
||||
public String getAlgorithmName()
|
||||
{
|
||||
return "MD5";
|
||||
}
|
||||
|
||||
public int getDigestSize()
|
||||
{
|
||||
return DIGEST_LENGTH;
|
||||
}
|
||||
|
||||
protected void processWord(
|
||||
byte[] in,
|
||||
int inOff)
|
||||
{
|
||||
X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8)
|
||||
| ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24);
|
||||
|
||||
if (xOff == 16)
|
||||
{
|
||||
processBlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected void processLength(
|
||||
long bitLength)
|
||||
{
|
||||
if (xOff > 14)
|
||||
{
|
||||
processBlock();
|
||||
}
|
||||
|
||||
X[14] = (int)(bitLength & 0xffffffff);
|
||||
X[15] = (int)(bitLength >>> 32);
|
||||
}
|
||||
|
||||
private void unpackWord(
|
||||
int word,
|
||||
byte[] out,
|
||||
int outOff)
|
||||
{
|
||||
out[outOff] = (byte)word;
|
||||
out[outOff + 1] = (byte)(word >>> 8);
|
||||
out[outOff + 2] = (byte)(word >>> 16);
|
||||
out[outOff + 3] = (byte)(word >>> 24);
|
||||
}
|
||||
|
||||
public int doFinal(
|
||||
byte[] out,
|
||||
int outOff)
|
||||
{
|
||||
finish();
|
||||
|
||||
unpackWord(H1, out, outOff);
|
||||
unpackWord(H2, out, outOff + 4);
|
||||
unpackWord(H3, out, outOff + 8);
|
||||
unpackWord(H4, out, outOff + 12);
|
||||
|
||||
reset();
|
||||
|
||||
return DIGEST_LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* reset the chaining variables to the IV values.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
super.reset();
|
||||
|
||||
H1 = 0x67452301;
|
||||
H2 = 0xefcdab89;
|
||||
H3 = 0x98badcfe;
|
||||
H4 = 0x10325476;
|
||||
|
||||
xOff = 0;
|
||||
|
||||
for (int i = 0; i != X.length; i++)
|
||||
{
|
||||
X[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// round 1 left rotates
|
||||
//
|
||||
private static final int S11 = 7;
|
||||
private static final int S12 = 12;
|
||||
private static final int S13 = 17;
|
||||
private static final int S14 = 22;
|
||||
|
||||
//
|
||||
// round 2 left rotates
|
||||
//
|
||||
private static final int S21 = 5;
|
||||
private static final int S22 = 9;
|
||||
private static final int S23 = 14;
|
||||
private static final int S24 = 20;
|
||||
|
||||
//
|
||||
// round 3 left rotates
|
||||
//
|
||||
private static final int S31 = 4;
|
||||
private static final int S32 = 11;
|
||||
private static final int S33 = 16;
|
||||
private static final int S34 = 23;
|
||||
|
||||
//
|
||||
// round 4 left rotates
|
||||
//
|
||||
private static final int S41 = 6;
|
||||
private static final int S42 = 10;
|
||||
private static final int S43 = 15;
|
||||
private static final int S44 = 21;
|
||||
|
||||
/*
|
||||
* rotate int x left n bits.
|
||||
*/
|
||||
private int rotateLeft(
|
||||
int x,
|
||||
int n)
|
||||
{
|
||||
return (x << n) | (x >>> (32 - n));
|
||||
}
|
||||
|
||||
/*
|
||||
* F, G, H and I are the basic MD5 functions.
|
||||
*/
|
||||
private int F(
|
||||
int u,
|
||||
int v,
|
||||
int w)
|
||||
{
|
||||
return (u & v) | (~u & w);
|
||||
}
|
||||
|
||||
private int G(
|
||||
int u,
|
||||
int v,
|
||||
int w)
|
||||
{
|
||||
return (u & w) | (v & ~w);
|
||||
}
|
||||
|
||||
private int H(
|
||||
int u,
|
||||
int v,
|
||||
int w)
|
||||
{
|
||||
return u ^ v ^ w;
|
||||
}
|
||||
|
||||
private int K(
|
||||
int u,
|
||||
int v,
|
||||
int w)
|
||||
{
|
||||
return v ^ (u | ~w);
|
||||
}
|
||||
|
||||
protected void processBlock()
|
||||
{
|
||||
int a = H1;
|
||||
int b = H2;
|
||||
int c = H3;
|
||||
int d = H4;
|
||||
|
||||
//
|
||||
// Round 1 - F cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft((a + F(b, c, d) + X[ 0] + 0xd76aa478), S11) + b;
|
||||
d = rotateLeft((d + F(a, b, c) + X[ 1] + 0xe8c7b756), S12) + a;
|
||||
c = rotateLeft((c + F(d, a, b) + X[ 2] + 0x242070db), S13) + d;
|
||||
b = rotateLeft((b + F(c, d, a) + X[ 3] + 0xc1bdceee), S14) + c;
|
||||
a = rotateLeft((a + F(b, c, d) + X[ 4] + 0xf57c0faf), S11) + b;
|
||||
d = rotateLeft((d + F(a, b, c) + X[ 5] + 0x4787c62a), S12) + a;
|
||||
c = rotateLeft((c + F(d, a, b) + X[ 6] + 0xa8304613), S13) + d;
|
||||
b = rotateLeft((b + F(c, d, a) + X[ 7] + 0xfd469501), S14) + c;
|
||||
a = rotateLeft((a + F(b, c, d) + X[ 8] + 0x698098d8), S11) + b;
|
||||
d = rotateLeft((d + F(a, b, c) + X[ 9] + 0x8b44f7af), S12) + a;
|
||||
c = rotateLeft((c + F(d, a, b) + X[10] + 0xffff5bb1), S13) + d;
|
||||
b = rotateLeft((b + F(c, d, a) + X[11] + 0x895cd7be), S14) + c;
|
||||
a = rotateLeft((a + F(b, c, d) + X[12] + 0x6b901122), S11) + b;
|
||||
d = rotateLeft((d + F(a, b, c) + X[13] + 0xfd987193), S12) + a;
|
||||
c = rotateLeft((c + F(d, a, b) + X[14] + 0xa679438e), S13) + d;
|
||||
b = rotateLeft((b + F(c, d, a) + X[15] + 0x49b40821), S14) + c;
|
||||
|
||||
//
|
||||
// Round 2 - G cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft((a + G(b, c, d) + X[ 1] + 0xf61e2562), S21) + b;
|
||||
d = rotateLeft((d + G(a, b, c) + X[ 6] + 0xc040b340), S22) + a;
|
||||
c = rotateLeft((c + G(d, a, b) + X[11] + 0x265e5a51), S23) + d;
|
||||
b = rotateLeft((b + G(c, d, a) + X[ 0] + 0xe9b6c7aa), S24) + c;
|
||||
a = rotateLeft((a + G(b, c, d) + X[ 5] + 0xd62f105d), S21) + b;
|
||||
d = rotateLeft((d + G(a, b, c) + X[10] + 0x02441453), S22) + a;
|
||||
c = rotateLeft((c + G(d, a, b) + X[15] + 0xd8a1e681), S23) + d;
|
||||
b = rotateLeft((b + G(c, d, a) + X[ 4] + 0xe7d3fbc8), S24) + c;
|
||||
a = rotateLeft((a + G(b, c, d) + X[ 9] + 0x21e1cde6), S21) + b;
|
||||
d = rotateLeft((d + G(a, b, c) + X[14] + 0xc33707d6), S22) + a;
|
||||
c = rotateLeft((c + G(d, a, b) + X[ 3] + 0xf4d50d87), S23) + d;
|
||||
b = rotateLeft((b + G(c, d, a) + X[ 8] + 0x455a14ed), S24) + c;
|
||||
a = rotateLeft((a + G(b, c, d) + X[13] + 0xa9e3e905), S21) + b;
|
||||
d = rotateLeft((d + G(a, b, c) + X[ 2] + 0xfcefa3f8), S22) + a;
|
||||
c = rotateLeft((c + G(d, a, b) + X[ 7] + 0x676f02d9), S23) + d;
|
||||
b = rotateLeft((b + G(c, d, a) + X[12] + 0x8d2a4c8a), S24) + c;
|
||||
|
||||
//
|
||||
// Round 3 - H cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft((a + H(b, c, d) + X[ 5] + 0xfffa3942), S31) + b;
|
||||
d = rotateLeft((d + H(a, b, c) + X[ 8] + 0x8771f681), S32) + a;
|
||||
c = rotateLeft((c + H(d, a, b) + X[11] + 0x6d9d6122), S33) + d;
|
||||
b = rotateLeft((b + H(c, d, a) + X[14] + 0xfde5380c), S34) + c;
|
||||
a = rotateLeft((a + H(b, c, d) + X[ 1] + 0xa4beea44), S31) + b;
|
||||
d = rotateLeft((d + H(a, b, c) + X[ 4] + 0x4bdecfa9), S32) + a;
|
||||
c = rotateLeft((c + H(d, a, b) + X[ 7] + 0xf6bb4b60), S33) + d;
|
||||
b = rotateLeft((b + H(c, d, a) + X[10] + 0xbebfbc70), S34) + c;
|
||||
a = rotateLeft((a + H(b, c, d) + X[13] + 0x289b7ec6), S31) + b;
|
||||
d = rotateLeft((d + H(a, b, c) + X[ 0] + 0xeaa127fa), S32) + a;
|
||||
c = rotateLeft((c + H(d, a, b) + X[ 3] + 0xd4ef3085), S33) + d;
|
||||
b = rotateLeft((b + H(c, d, a) + X[ 6] + 0x04881d05), S34) + c;
|
||||
a = rotateLeft((a + H(b, c, d) + X[ 9] + 0xd9d4d039), S31) + b;
|
||||
d = rotateLeft((d + H(a, b, c) + X[12] + 0xe6db99e5), S32) + a;
|
||||
c = rotateLeft((c + H(d, a, b) + X[15] + 0x1fa27cf8), S33) + d;
|
||||
b = rotateLeft((b + H(c, d, a) + X[ 2] + 0xc4ac5665), S34) + c;
|
||||
|
||||
//
|
||||
// Round 4 - K cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft((a + K(b, c, d) + X[ 0] + 0xf4292244), S41) + b;
|
||||
d = rotateLeft((d + K(a, b, c) + X[ 7] + 0x432aff97), S42) + a;
|
||||
c = rotateLeft((c + K(d, a, b) + X[14] + 0xab9423a7), S43) + d;
|
||||
b = rotateLeft((b + K(c, d, a) + X[ 5] + 0xfc93a039), S44) + c;
|
||||
a = rotateLeft((a + K(b, c, d) + X[12] + 0x655b59c3), S41) + b;
|
||||
d = rotateLeft((d + K(a, b, c) + X[ 3] + 0x8f0ccc92), S42) + a;
|
||||
c = rotateLeft((c + K(d, a, b) + X[10] + 0xffeff47d), S43) + d;
|
||||
b = rotateLeft((b + K(c, d, a) + X[ 1] + 0x85845dd1), S44) + c;
|
||||
a = rotateLeft((a + K(b, c, d) + X[ 8] + 0x6fa87e4f), S41) + b;
|
||||
d = rotateLeft((d + K(a, b, c) + X[15] + 0xfe2ce6e0), S42) + a;
|
||||
c = rotateLeft((c + K(d, a, b) + X[ 6] + 0xa3014314), S43) + d;
|
||||
b = rotateLeft((b + K(c, d, a) + X[13] + 0x4e0811a1), S44) + c;
|
||||
a = rotateLeft((a + K(b, c, d) + X[ 4] + 0xf7537e82), S41) + b;
|
||||
d = rotateLeft((d + K(a, b, c) + X[11] + 0xbd3af235), S42) + a;
|
||||
c = rotateLeft((c + K(d, a, b) + X[ 2] + 0x2ad7d2bb), S43) + d;
|
||||
b = rotateLeft((b + K(c, d, a) + X[ 9] + 0xeb86d391), S44) + c;
|
||||
|
||||
H1 += a;
|
||||
H2 += b;
|
||||
H3 += c;
|
||||
H4 += d;
|
||||
|
||||
//
|
||||
// reset the offset and clean out the word buffer.
|
||||
//
|
||||
xOff = 0;
|
||||
for (int i = 0; i != X.length; i++)
|
||||
{
|
||||
X[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -31,13 +31,17 @@ import org.bouncycastle.crypto.Digest;
|
||||
import org.bouncycastle.crypto.Mac;
|
||||
//import org.bouncycastle.crypto.params.KeyParameter;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* HMAC implementation based on RFC2104
|
||||
*
|
||||
* H(K XOR opad, H(K XOR ipad, text))
|
||||
*
|
||||
* modified by jrandom to use the session key byte array directly
|
||||
* modified by jrandom to use the session key byte array directly and to cache
|
||||
* a frequently used buffer (called on doFinal). changes released into the public
|
||||
* domain in 2005.
|
||||
*
|
||||
*/
|
||||
public class HMac
|
||||
implements Mac
|
||||
@ -137,11 +141,13 @@ implements Mac
|
||||
byte[] out,
|
||||
int outOff)
|
||||
{
|
||||
byte[] tmp = new byte[digestSize];
|
||||
byte[] tmp = acquireTmp();
|
||||
//byte[] tmp = new byte[digestSize];
|
||||
digest.doFinal(tmp, 0);
|
||||
|
||||
digest.update(outputPad, 0, outputPad.length);
|
||||
digest.update(tmp, 0, tmp.length);
|
||||
releaseTmp(tmp);
|
||||
|
||||
int len = digest.doFinal(out, outOff);
|
||||
|
||||
@ -149,6 +155,26 @@ implements Mac
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
private static ArrayList _tmpBuf = new ArrayList();
|
||||
private static byte[] acquireTmp() {
|
||||
byte rv[] = null;
|
||||
synchronized (_tmpBuf) {
|
||||
if (_tmpBuf.size() > 0)
|
||||
rv = (byte[])_tmpBuf.remove(0);
|
||||
}
|
||||
if (rv != null)
|
||||
Arrays.fill(rv, (byte)0x0);
|
||||
else
|
||||
rv = new byte[32]; // hard coded against SHA256 (should be digestSize)
|
||||
return rv;
|
||||
}
|
||||
private static void releaseTmp(byte buf[]) {
|
||||
synchronized (_tmpBuf) {
|
||||
if (_tmpBuf.size() < 100)
|
||||
_tmpBuf.add((Object)buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the mac generator.
|
||||
|
@ -47,6 +47,16 @@ public class BloomSHA1 {
|
||||
protected final int filterBits;
|
||||
protected final int filterWords;
|
||||
|
||||
public static void main(String args[]) {
|
||||
BloomSHA1 b = new BloomSHA1(24, 11);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
byte v[] = new byte[32];
|
||||
v[0] = (byte)i;
|
||||
b.insert(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a filter with 2^m bits and k 'hash functions', where
|
||||
* each hash function is portion of the 160-bit SHA1 hash.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user