format (shendaras)

This commit is contained in:
shendaras
2004-04-10 11:50:11 +00:00
committed by zzz
parent 17a1b11f66
commit 51c49d7c1b
17 changed files with 1462 additions and 1464 deletions

View File

@ -6,154 +6,152 @@ public class ByteCollector {
int size; int size;
public ByteCollector() { public ByteCollector() {
contents=new byte[80]; contents = new byte[80];
size=0; size = 0;
} }
public ByteCollector(byte[] b) { public ByteCollector(byte[] b) {
this(); this();
append(b); append(b);
} }
public ByteCollector(byte b) { public ByteCollector(byte b) {
this(); this();
append(b); append(b);
} }
public ByteCollector append (byte b) { public ByteCollector append(byte b) {
ensureCapacity(size+1); ensureCapacity(size + 1);
contents[size++]=b; contents[size++] = b;
return this; return this;
} }
public ByteCollector append (byte[] b) { public ByteCollector append(byte[] b) {
ensureCapacity(size+b.length); ensureCapacity(size + b.length);
System.arraycopy(b,0,contents,size,b.length); System.arraycopy(b, 0, contents, size, b.length);
size+=b.length; size += b.length;
return this; return this;
} }
public ByteCollector append(byte[] b, int len) { public ByteCollector append(byte[] b, int len) {
return append(b,0,len); return append(b, 0, len);
} }
public ByteCollector append(byte[] b, int off, int len) { public ByteCollector append(byte[] b, int off, int len) {
ensureCapacity(size+len); ensureCapacity(size + len);
System.arraycopy(b,off,contents,size,len); System.arraycopy(b, off, contents, size, len);
size+=len; size += len;
return this; return this;
} }
public ByteCollector append(ByteCollector bc) { public ByteCollector append(ByteCollector bc) {
// optimieren? // optimieren?
return append(bc.toByteArray()); return append(bc.toByteArray());
} }
public byte[] toByteArray() { public byte[] toByteArray() {
byte[] result=new byte[size]; byte[] result = new byte[size];
System.arraycopy(contents,0,result,0,size); System.arraycopy(contents, 0, result, 0, size);
return result; return result;
} }
public byte[] startToByteArray(int maxlen) { public byte[] startToByteArray(int maxlen) {
if (size < maxlen) { if (size < maxlen) {
byte[] res = toByteArray(); byte[] res = toByteArray();
clear(); clear();
return res; return res;
} else { } else {
byte[] result = new byte[maxlen]; byte[] result = new byte[maxlen];
System.arraycopy(contents,0,result,0,maxlen); System.arraycopy(contents, 0, result, 0, maxlen);
System.arraycopy(contents,maxlen,contents,0,size-maxlen); System.arraycopy(contents, maxlen, contents, 0, size - maxlen);
size-=maxlen; size -= maxlen;
return result; return result;
} }
} }
public int getCurrentSize() { public int getCurrentSize() {
return size; return size;
} }
public boolean ensureCapacity(int cap) { public boolean ensureCapacity(int cap) {
if (contents.length<cap) { if (contents.length < cap) {
int l=contents.length; int l = contents.length;
while (l<cap) { while (l < cap) {
l=(l*3)/2+1; l = (l * 3) / 2 + 1;
} }
byte[] newcont=new byte[l]; byte[] newcont = new byte[l];
System.arraycopy(contents,0,newcont,0,size); System.arraycopy(contents, 0, newcont, 0, size);
contents=newcont; contents = newcont;
return true; return true;
} }
return false; return false;
} }
public boolean isEmpty() { public boolean isEmpty() {
return size==0; return size == 0;
} }
public int indexOf(ByteCollector bc) { public int indexOf(ByteCollector bc) {
// optimieren? // optimieren?
return indexOf(bc.toByteArray()); return indexOf(bc.toByteArray());
} }
public int indexOf(byte b) { public int indexOf(byte b) {
// optimieren? // optimieren?
return indexOf(new byte[] {b}); return indexOf(new byte[] { b});
} }
public int indexOf(byte[] ba) { public int indexOf(byte[] ba) {
loop: loop: for (int i = 0; i < size - ba.length + 1; i++) {
for (int i=0;i<size-ba.length+1;i++) { for (int j = 0; j < ba.length; j++) {
for (int j=0;j<ba.length;j++) { if (contents[i + j] != ba[j]) continue loop;
if (contents[i+j]!=ba[j]) continue loop; }
} return i;
return i; }
} return -1;
return -1;
} }
public void clear() { public void clear() {
size=0; size = 0;
} }
public void clearAndShorten() { public void clearAndShorten() {
size=0; size = 0;
contents=new byte[80]; contents = new byte[80];
} }
public String toString() { public String toString() {
return new String(toByteArray()); return new String(toByteArray());
} }
public int hashCode() { public int hashCode() {
int h =0; int h = 0;
for (int i=0;i<size;i++) { for (int i = 0; i < size; i++) {
h+=contents[i]*contents[i]; h += contents[i] * contents[i];
} }
return h; return h;
} }
public boolean equals(Object o) { public boolean equals(Object o) {
if (o instanceof ByteCollector) { if (o instanceof ByteCollector) {
ByteCollector by=(ByteCollector)o; ByteCollector by = (ByteCollector) o;
if (size!=by.size) return false; if (size != by.size) return false;
for (int i=0;i<size;i++) { for (int i = 0; i < size; i++) {
if (contents[i]!=by.contents[i]) return false; if (contents[i] != by.contents[i]) return false;
} }
return true; return true;
} else { } else {
return false; return false;
} }
} }
public byte removeFirst() { public byte removeFirst() {
byte bb=contents[0]; byte bb = contents[0];
if (size==0) if (size == 0) throw new IllegalArgumentException("ByteCollector is empty");
throw new IllegalArgumentException("ByteCollector is empty"); if (size > 1)
if(size>1) System.arraycopy(contents, 1, contents, 0, --size);
System.arraycopy(contents,1,contents,0,--size); else
else size = 0;
size=0; return bb;
return bb;
} }
} }

View File

@ -11,7 +11,7 @@ public interface I2PServerSocket {
* Closes the socket. * Closes the socket.
*/ */
public void close() throws I2PException; public void close() throws I2PException;
/** /**
* Waits for the next socket connecting. If a remote user tried to make a * Waits for the next socket connecting. If a remote user tried to make a
* connection and the local application wasn't .accept()ing new connections, * connection and the local application wasn't .accept()ing new connections,
@ -26,4 +26,4 @@ public interface I2PServerSocket {
* Access the manager which is coordinating the server socket * Access the manager which is coordinating the server socket
*/ */
public I2PSocketManager getManager(); public I2PSocketManager getManager();
} }

View File

@ -10,41 +10,44 @@ import net.i2p.util.Log;
class I2PServerSocketImpl implements I2PServerSocket { class I2PServerSocketImpl implements I2PServerSocket {
private final static Log _log = new Log(I2PServerSocketImpl.class); private final static Log _log = new Log(I2PServerSocketImpl.class);
private I2PSocketManager mgr; private I2PSocketManager mgr;
private I2PSocket cached=null; // buffer one socket here private I2PSocket cached = null; // buffer one socket here
public I2PServerSocketImpl(I2PSocketManager mgr) { public I2PServerSocketImpl(I2PSocketManager mgr) {
this.mgr = mgr; this.mgr = mgr;
}
public synchronized I2PSocket accept() throws I2PException {
while(cached == null) {
myWait();
}
I2PSocket ret=cached;
cached=null;
notifyAll();
_log.debug("TIMING: handed out accept result "+ret.hashCode());
return ret;
}
public synchronized boolean getNewSocket(I2PSocket s){
while(cached != null) {
myWait();
}
cached=s;
notifyAll();
return true;
}
public void close() throws I2PException {
//noop
}
private void myWait() {
try{
wait();
} catch (InterruptedException ex) {}
} }
public I2PSocketManager getManager() { return mgr; } public synchronized I2PSocket accept() throws I2PException {
} while (cached == null) {
myWait();
}
I2PSocket ret = cached;
cached = null;
notifyAll();
_log.debug("TIMING: handed out accept result " + ret.hashCode());
return ret;
}
public synchronized boolean getNewSocket(I2PSocket s) {
while (cached != null) {
myWait();
}
cached = s;
notifyAll();
return true;
}
public void close() throws I2PException {
//noop
}
private void myWait() {
try {
wait();
} catch (InterruptedException ex) {
}
}
public I2PSocketManager getManager() {
return mgr;
}
}

View File

@ -36,4 +36,4 @@ public interface I2PSocket {
* Closes the socket if not closed yet * Closes the socket if not closed yet
*/ */
public void close() throws IOException; public void close() throws IOException;
} }

View File

@ -18,9 +18,9 @@ import net.i2p.util.Log;
class I2PSocketImpl implements I2PSocket { class I2PSocketImpl implements I2PSocket {
private final static Log _log = new Log(I2PSocketImpl.class); private final static Log _log = new Log(I2PSocketImpl.class);
public static final int MAX_PACKET_SIZE = 1024*32; public static final int MAX_PACKET_SIZE = 1024 * 32;
public static final int PACKET_DELAY=100; public static final int PACKET_DELAY = 100;
private I2PSocketManager manager; private I2PSocketManager manager;
private Destination local; private Destination local;
private Destination remote; private Destination remote;
@ -31,307 +31,306 @@ class I2PSocketImpl implements I2PSocket {
private I2POutputStream out; private I2POutputStream out;
private boolean outgoing; private boolean outgoing;
private Object flagLock = new Object(); private Object flagLock = new Object();
private boolean closed = false, sendClose=true, closed2=false; private boolean closed = false, sendClose = true, closed2 = false;
public I2PSocketImpl(Destination peer, I2PSocketManager mgr, public I2PSocketImpl(Destination peer, I2PSocketManager mgr, boolean outgoing, String localID) {
boolean outgoing, String localID) { this.outgoing = outgoing;
this.outgoing=outgoing; manager = mgr;
manager = mgr; remote = peer;
remote = peer; local = mgr.getSession().getMyDestination();
local = mgr.getSession().getMyDestination(); in = new I2PInputStream();
in = new I2PInputStream(); I2PInputStream pin = new I2PInputStream();
I2PInputStream pin = new I2PInputStream(); out = new I2POutputStream(pin);
out = new I2POutputStream(pin); new I2PSocketRunner(pin);
new I2PSocketRunner(pin); this.localID = localID;
this.localID = localID;
} }
public String getLocalID() { public String getLocalID() {
return localID; return localID;
} }
public void setRemoteID(String id) { public void setRemoteID(String id) {
synchronized(remoteIDWaiter) { synchronized (remoteIDWaiter) {
remoteID=id; remoteID = id;
remoteIDWaiter.notifyAll(); remoteIDWaiter.notifyAll();
} }
} }
public String getRemoteID(boolean wait) throws InterruptedIOException { public String getRemoteID(boolean wait) throws InterruptedIOException {
return getRemoteID(wait, -1); return getRemoteID(wait, -1);
} }
public String getRemoteID(boolean wait, long maxWait) throws InterruptedIOException { public String getRemoteID(boolean wait, long maxWait) throws InterruptedIOException {
long dieAfter = System.currentTimeMillis() + maxWait; long dieAfter = System.currentTimeMillis() + maxWait;
synchronized(remoteIDWaiter) { synchronized (remoteIDWaiter) {
while (wait && remoteID==null) { while (wait && remoteID == null) {
try { try {
if (maxWait > 0) if (maxWait > 0)
remoteIDWaiter.wait(maxWait); remoteIDWaiter.wait(maxWait);
else else
remoteIDWaiter.wait(); remoteIDWaiter.wait();
} catch (InterruptedException ex) {} } catch (InterruptedException ex) {
}
if ( (maxWait > 0) && (System.currentTimeMillis() > dieAfter) )
throw new InterruptedIOException("Timed out waiting for remote ID"); if ((maxWait > 0) && (System.currentTimeMillis() > dieAfter))
} throw new InterruptedIOException("Timed out waiting for remote ID");
if (wait) { }
_log.debug("TIMING: RemoteID set to " + I2PSocketManager.getReadableForm(remoteID) +" for "+this.hashCode()); if (wait) {
} _log.debug("TIMING: RemoteID set to " + I2PSocketManager.getReadableForm(remoteID) + " for "
return remoteID; + this.hashCode());
} }
return remoteID;
}
} }
public String getRemoteID() throws InterruptedIOException { public String getRemoteID() throws InterruptedIOException {
return getRemoteID(false); return getRemoteID(false);
} }
public void queueData(byte[] data) { public void queueData(byte[] data) {
in.queueData(data); in.queueData(data);
} }
/** /**
* Return the Destination of this side of the socket. * Return the Destination of this side of the socket.
*/ */
public Destination getThisDestination() { return local; } public Destination getThisDestination() {
return local;
}
/** /**
* Return the destination of the peer. * Return the destination of the peer.
*/ */
public Destination getPeerDestination() { return remote; } public Destination getPeerDestination() {
return remote;
}
/** /**
* Return an InputStream to read from the socket. * Return an InputStream to read from the socket.
*/ */
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
if ( (in == null) ) if ((in == null)) throw new IOException("Not connected");
throw new IOException("Not connected"); return in;
return in;
} }
/** /**
* Return an OutputStream to write into the socket. * Return an OutputStream to write into the socket.
*/ */
public OutputStream getOutputStream() throws IOException { public OutputStream getOutputStream() throws IOException {
if ( (out == null) ) if ((out == null)) throw new IOException("Not connected");
throw new IOException("Not connected"); return out;
return out;
} }
/** /**
* Closes the socket if not closed yet * Closes the socket if not closed yet
*/ */
public void close() throws IOException { public void close() throws IOException {
synchronized(flagLock) { synchronized (flagLock) {
_log.debug("Closing connection"); _log.debug("Closing connection");
closed=true; closed = true;
} }
out.close(); out.close();
in.notifyClosed(); in.notifyClosed();
} }
public void internalClose() { public void internalClose() {
synchronized(flagLock) { synchronized (flagLock) {
closed=true; closed = true;
closed2=true; closed2 = true;
sendClose=false; sendClose = false;
} }
out.close(); out.close();
in.notifyClosed(); in.notifyClosed();
} }
private byte getMask(int add) { private byte getMask(int add) {
return (byte)((outgoing?(byte)0xA0:(byte)0x50)+(byte)add); return (byte) ((outgoing ? (byte) 0xA0 : (byte) 0x50) + (byte) add);
} }
//-------------------------------------------------- //--------------------------------------------------
public class I2PInputStream extends InputStream { public class I2PInputStream extends InputStream {
private ByteCollector bc = new ByteCollector(); private ByteCollector bc = new ByteCollector();
public int read() throws IOException { public int read() throws IOException {
byte[] b = new byte[1]; byte[] b = new byte[1];
int res = read(b); int res = read(b);
if (res == 1) return b[0] & 0xff; if (res == 1) return b[0] & 0xff;
if (res == -1) return -1; if (res == -1) return -1;
throw new RuntimeException("Incorrect read() result"); throw new RuntimeException("Incorrect read() result");
} }
public synchronized int read(byte[] b, int off, int len) throws IOException { public synchronized int read(byte[] b, int off, int len) throws IOException {
_log.debug("Read called: "+this.hashCode()); _log.debug("Read called: " + this.hashCode());
if (len==0) return 0; if (len == 0) return 0;
byte[] read = bc.startToByteArray(len); byte[] read = bc.startToByteArray(len);
while (read.length==0) { while (read.length == 0) {
synchronized(flagLock) { synchronized (flagLock) {
if (closed){ if (closed) {
_log.debug("Closed is set, so closing stream: "+this.hashCode()); _log.debug("Closed is set, so closing stream: " + this.hashCode());
return -1; return -1;
} }
} }
try { try {
wait(); wait();
} catch (InterruptedException ex) {} } catch (InterruptedException ex) {
read = bc.startToByteArray(len); }
} read = bc.startToByteArray(len);
if (read.length>len) throw new RuntimeException("BUG"); }
System.arraycopy(read,0,b,off,read.length); if (read.length > len) throw new RuntimeException("BUG");
System.arraycopy(read, 0, b, off, read.length);
if (_log.shouldLog(Log.DEBUG)) { if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Read from I2PInputStream " + this.hashCode() _log.debug("Read from I2PInputStream " + this.hashCode() + " returned " + read.length + " bytes");
+ " returned "+read.length+" bytes"); }
} //if (_log.shouldLog(Log.DEBUG)) {
//if (_log.shouldLog(Log.DEBUG)) { // _log.debug("Read from I2PInputStream " + this.hashCode()
// _log.debug("Read from I2PInputStream " + this.hashCode() // + " returned "+read.length+" bytes:\n"
// + " returned "+read.length+" bytes:\n" // + HexDump.dump(read));
// + HexDump.dump(read)); //}
//} return read.length;
return read.length; }
}
public int available() { public int available() {
return bc.getCurrentSize(); return bc.getCurrentSize();
} }
public void queueData(byte[] data) { public void queueData(byte[] data) {
queueData(data,0,data.length); queueData(data, 0, data.length);
} }
public synchronized void queueData(byte[] data, int off, int len) { public synchronized void queueData(byte[] data, int off, int len) {
_log.debug("Insert "+len+" bytes into queue: "+this.hashCode()); _log.debug("Insert " + len + " bytes into queue: " + this.hashCode());
bc.append(data, off, len); bc.append(data, off, len);
notifyAll(); notifyAll();
} }
public synchronized void notifyClosed() {
notifyAll();
}
public synchronized void notifyClosed() {
notifyAll();
}
} }
public class I2POutputStream extends OutputStream { public class I2POutputStream extends OutputStream {
public I2PInputStream sendTo; public I2PInputStream sendTo;
public I2POutputStream(I2PInputStream sendTo) {
this.sendTo=sendTo;
}
public void write(int b) throws IOException {
write(new byte[] {(byte)b});
}
public void write (byte[] b, int off, int len) throws IOException { public I2POutputStream(I2PInputStream sendTo) {
sendTo.queueData(b,off,len); this.sendTo = sendTo;
} }
public void close() { public void write(int b) throws IOException {
sendTo.notifyClosed(); write(new byte[] { (byte) b});
} }
public void write(byte[] b, int off, int len) throws IOException {
sendTo.queueData(b, off, len);
}
public void close() {
sendTo.notifyClosed();
}
} }
public class I2PSocketRunner extends I2PThread { public class I2PSocketRunner extends I2PThread {
public InputStream in; public InputStream in;
public I2PSocketRunner(InputStream in) { public I2PSocketRunner(InputStream in) {
_log.debug("Runner's input stream is: "+in.hashCode()); _log.debug("Runner's input stream is: " + in.hashCode());
this.in=in; this.in = in;
setName("SocketRunner from " + I2PSocketImpl.this.remote.calculateHash().toBase64().substring(0, 4)); setName("SocketRunner from " + I2PSocketImpl.this.remote.calculateHash().toBase64().substring(0, 4));
start(); start();
} }
public void run() { public void run() {
byte[] buffer = new byte[MAX_PACKET_SIZE]; byte[] buffer = new byte[MAX_PACKET_SIZE];
ByteCollector bc = new ByteCollector(); ByteCollector bc = new ByteCollector();
boolean sent = true; boolean sent = true;
try { try {
int len, bcsize; int len, bcsize;
// try { // try {
while (true) { while (true) {
len = in.read(buffer); len = in.read(buffer);
bcsize = bc.getCurrentSize(); bcsize = bc.getCurrentSize();
if (len != -1) { if (len != -1) {
bc.append(buffer,len); bc.append(buffer, len);
} else if (bcsize == 0) { } else if (bcsize == 0) {
break; break;
} }
if ((bcsize < MAX_PACKET_SIZE) if ((bcsize < MAX_PACKET_SIZE) && (in.available() == 0)) {
&& (in.available()==0)) { _log.debug("Runner Point d: " + this.hashCode());
_log.debug("Runner Point d: "+this.hashCode());
try {
try { Thread.sleep(PACKET_DELAY);
Thread.sleep(PACKET_DELAY); } catch (InterruptedException e) {
} catch (InterruptedException e) { e.printStackTrace();
e.printStackTrace(); }
} }
} if ((bcsize >= MAX_PACKET_SIZE) || (in.available() == 0)) {
if ((bcsize >= MAX_PACKET_SIZE) byte[] data = bc.startToByteArray(MAX_PACKET_SIZE);
|| (in.available()==0) ) { if (data.length > 0) {
byte[] data = bc.startToByteArray(MAX_PACKET_SIZE); _log.debug("Message size is: " + data.length);
if (data.length > 0) { sent = sendBlock(data);
_log.debug("Message size is: "+data.length); if (!sent) {
sent = sendBlock(data); _log.error("Error sending message to peer. Killing socket runner");
if (!sent) { break;
_log.error("Error sending message to peer. Killing socket runner"); }
break; }
} }
} }
} if ((bc.getCurrentSize() > 0) && sent) {
} _log.error("A SCARY MONSTER HAS EATEN SOME DATA! " + "(input stream: " + in.hashCode() + "; "
if ((bc.getCurrentSize() > 0) && sent) { + "queue size: " + bc.getCurrentSize() + ")");
_log.error("A SCARY MONSTER HAS EATEN SOME DATA! " }
+ "(input stream: " + in.hashCode() + "; " synchronized (flagLock) {
+ "queue size: " + bc.getCurrentSize() + ")"); closed2 = true;
} }
synchronized(flagLock) { // } catch (IOException ex) {
closed2=true; // if (_log.shouldLog(Log.INFO))
} // _log.info("Error reading and writing", ex);
// } catch (IOException ex) { // }
// if (_log.shouldLog(Log.INFO)) boolean sc;
// _log.info("Error reading and writing", ex); synchronized (flagLock) {
// } sc = sendClose;
boolean sc; } // FIXME: Race here?
synchronized(flagLock) { if (sc) {
sc=sendClose; _log.info("Sending close packet: " + outgoing);
} // FIXME: Race here? byte[] packet = I2PSocketManager.makePacket((byte) (getMask(0x02)), remoteID, new byte[0]);
if (sc) { synchronized (manager.getSession()) {
_log.info("Sending close packet: "+outgoing); sent = manager.getSession().sendMessage(remote, packet);
byte[] packet = I2PSocketManager.makePacket }
((byte)(getMask(0x02)),remoteID, new byte[0]); if (!sent) {
synchronized(manager.getSession()) { _log.error("Error sending close packet to peer");
sent = manager.getSession().sendMessage(remote, packet); }
} }
if (!sent) { manager.removeSocket(I2PSocketImpl.this);
_log.error("Error sending close packet to peer"); } catch (IOException ex) {
} // WHOEVER removes this event on inconsistent
} // state before fixing the inconsistent state (a
manager.removeSocket(I2PSocketImpl.this); // reference on the socket in the socket manager
} catch (IOException ex) { // etc.) will get hanged by me personally -- mihi
// WHOEVER removes this event on inconsistent _log.error("Error running - **INCONSISTENT STATE!!!**", ex);
// state before fixing the inconsistent state (a } catch (I2PException ex) {
// reference on the socket in the socket manager _log.error("Error running - **INCONSISTENT STATE!!!**", ex);
// etc.) will get hanged by me personally -- mihi }
_log.error("Error running - **INCONSISTENT STATE!!!**", ex); }
} catch (I2PException ex) {
_log.error("Error running - **INCONSISTENT STATE!!!**" , ex); private boolean sendBlock(byte data[]) throws I2PSessionException {
} _log.debug("TIMING: Block to send for " + I2PSocketImpl.this.hashCode());
} if (remoteID == null) {
_log.error("NULL REMOTEID");
private boolean sendBlock(byte data[]) throws I2PSessionException { return false;
_log.debug("TIMING: Block to send for "+I2PSocketImpl.this.hashCode()); }
if (remoteID==null) { byte[] packet = I2PSocketManager.makePacket(getMask(0x00), remoteID, data);
_log.error("NULL REMOTEID"); boolean sent;
return false; synchronized (flagLock) {
} if (closed2) return false;
byte[] packet = I2PSocketManager.makePacket(getMask(0x00), remoteID, }
data); synchronized (manager.getSession()) {
boolean sent; sent = manager.getSession().sendMessage(remote, packet);
synchronized(flagLock) { }
if (closed2) return false; return sent;
} }
synchronized(manager.getSession()) {
sent = manager.getSession().sendMessage(remote, packet);
}
return sent;
}
} }
} }

View File

@ -37,201 +37,199 @@ public class I2PSocketManager implements I2PSessionListener {
private HashMap _inSockets; private HashMap _inSockets;
private I2PSocketOptions _defaultOptions; private I2PSocketOptions _defaultOptions;
public static final int PUBKEY_LENGTH=387; public static final int PUBKEY_LENGTH = 387;
public I2PSocketManager() { public I2PSocketManager() {
_session=null; _session = null;
_serverSocket = new I2PServerSocketImpl(this); _serverSocket = new I2PServerSocketImpl(this);
_inSockets = new HashMap(16); _inSockets = new HashMap(16);
_outSockets = new HashMap(16); _outSockets = new HashMap(16);
} }
public I2PSession getSession() { public I2PSession getSession() {
return _session; return _session;
}
public void setSession(I2PSession session) {
_session = session;
if (session != null) session.setSessionListener(this);
}
public void disconnected(I2PSession session) {
_log.error("Disconnected from the session");
}
public void errorOccurred(I2PSession session, String message, Throwable error) {
_log.error("Error occurred: [" + message + "]", error);
}
public void messageAvailable(I2PSession session, int msgId, long size) {
try {
I2PSocketImpl s;
byte msg[] = session.receiveMessage(msgId);
if (msg.length == 1 && msg[0] == -1) {
_log.debug("Ping received");
return;
}
if (msg.length < 4) {
_log.error("==== packet too short ====");
return;
}
int type = msg[0] & 0xff;
String id = new String(new byte[] { msg[1], msg[2], msg[3]}, "ISO-8859-1");
byte[] payload = new byte[msg.length - 4];
System.arraycopy(msg, 4, payload, 0, payload.length);
_log.debug("Message read: type = [" + Integer.toHexString(type) + "] id = [" + getReadableForm(id)
+ "] payload length: " + payload.length + "]");
synchronized (lock) {
switch (type) {
case 0x51:
// ACK outgoing
s = (I2PSocketImpl) _outSockets.get(id);
if (s == null) {
_log.warn("No socket responsible for ACK packet");
return;
}
if (payload.length == 3 && s.getRemoteID(false) == null) {
String newID = new String(payload, "ISO-8859-1");
s.setRemoteID(newID);
return;
} else {
if (payload.length != 3)
_log.warn("Ack packet had " + payload.length + " bytes");
else
_log.warn("Remote ID already exists? " + s.getRemoteID());
return;
}
case 0x52:
// disconnect outgoing
_log.debug("*Disconnect outgoing!");
try {
s = (I2PSocketImpl) _outSockets.get(id);
if (payload.length == 0 && s != null) {
s.internalClose();
_outSockets.remove(id);
return;
} else {
if (payload.length > 0) _log.warn("Disconnect packet had " + payload.length + " bytes");
return;
}
} catch (Exception t) {
_log.error("Ignoring error on disconnect", t);
}
case 0x50:
// packet send outgoing
_log.debug("*Packet send outgoing [" + payload.length + "]");
s = (I2PSocketImpl) _outSockets.get(id);
if (s != null) {
s.queueData(payload);
return;
} else {
_log.error("Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
case 0xA1:
// SYN incoming
_log.debug("*Syn!");
if (payload.length == PUBKEY_LENGTH) {
String newLocalID = makeID(_inSockets);
Destination d = new Destination();
d.readBytes(new ByteArrayInputStream(payload));
s = new I2PSocketImpl(d, this, false, newLocalID);
s.setRemoteID(id);
if (_serverSocket.getNewSocket(s)) {
_inSockets.put(newLocalID, s);
byte[] packet = makePacket((byte) 0x51, id, newLocalID.getBytes("ISO-8859-1"));
boolean replySentOk = false;
synchronized (_session) {
replySentOk = _session.sendMessage(d, packet);
}
if (!replySentOk) {
_log.error("Error sending reply to " + d.calculateHash().toBase64()
+ " in response to a new con message", new Exception("Failed creation"));
s.internalClose();
}
} else {
byte[] packet = (" " + id).getBytes("ISO-8859-1");
packet[0] = 0x52;
boolean nackSent = session.sendMessage(d, packet);
if (!nackSent) {
_log.error("Error sending NACK for session creation");
}
s.internalClose();
}
return;
} else {
_log.error("Syn packet that has a payload not equal to the pubkey length (" + payload.length
+ " != " + PUBKEY_LENGTH + ")");
return;
}
case 0xA2:
// disconnect incoming
_log.debug("*Disconnect incoming!");
try {
s = (I2PSocketImpl) _inSockets.get(id);
if (payload.length == 0 && s != null) {
s.internalClose();
_inSockets.remove(id);
return;
} else {
if (payload.length > 0) _log.warn("Disconnect packet had " + payload.length + " bytes");
return;
}
} catch (Exception t) {
_log.error("Ignoring error on disconnect", t);
return;
}
case 0xA0:
// packet send incoming
_log.debug("*Packet send incoming [" + payload.length + "]");
s = (I2PSocketImpl) _inSockets.get(id);
if (s != null) {
s.queueData(payload);
return;
} else {
_log.error("Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
case 0xFF:
// ignore
return;
}
_log.error("\n\n=============== Unknown packet! " + "============" + "\nType: " + (int) type
+ "\nID: " + getReadableForm(id) + "\nBase64'ed Data: " + Base64.encode(payload)
+ "\n\n\n");
if (id != null) {
_inSockets.remove(id);
_outSockets.remove(id);
}
}
} catch (I2PException ise) {
_log.error("Error processing", ise);
} catch (IOException ioe) {
_log.error("Error processing", ioe);
} catch (IllegalStateException ise) {
_log.debug("Error processing", ise);
}
}
public void reportAbuse(I2PSession session, int severity) {
_log.error("Abuse reported [" + severity + "]");
}
public void setDefaultOptions(I2PSocketOptions options) {
_defaultOptions = options;
}
public I2PSocketOptions getDefaultOptions() {
return _defaultOptions;
}
public I2PServerSocket getServerSocket() {
return _serverSocket;
} }
public void setSession(I2PSession session) {
_session = session;
if (session != null)
session.setSessionListener(this);
}
public void disconnected(I2PSession session) {
_log.error("Disconnected from the session");
}
public void errorOccurred(I2PSession session, String message, Throwable error) {
_log.error("Error occurred: [" + message + "]", error);
}
public void messageAvailable(I2PSession session, int msgId, long size) {
try {
I2PSocketImpl s;
byte msg[] = session.receiveMessage(msgId);
if (msg.length == 1 && msg[0] == -1) {
_log.debug("Ping received");
return;
}
if (msg.length <4) {
_log.error("==== packet too short ====");
return;
}
int type = msg[0] & 0xff;
String id = new String(new byte[] {msg[1], msg[2], msg[3]},
"ISO-8859-1");
byte[] payload = new byte[msg.length-4];
System.arraycopy(msg, 4, payload, 0, payload.length);
_log.debug("Message read: type = [" + Integer.toHexString(type) +
"] id = [" + getReadableForm(id)+
"] payload length: " + payload.length + "]");
synchronized(lock) {
switch(type) {
case 0x51: // ACK outgoing
s = (I2PSocketImpl) _outSockets.get(id);
if (s == null) {
_log.warn("No socket responsible for ACK packet");
return;
}
if (payload.length==3 && s.getRemoteID(false)==null) {
String newID = new String(payload,
"ISO-8859-1");
s.setRemoteID(newID);
return;
} else {
if (payload.length != 3)
_log.warn("Ack packet had " + payload.length + " bytes");
else
_log.warn("Remote ID already exists? " + s.getRemoteID());
return;
}
case 0x52: // disconnect outgoing
_log.debug("*Disconnect outgoing!");
try {
s = (I2PSocketImpl) _outSockets.get(id);
if (payload.length==0 && s != null) {
s.internalClose();
_outSockets.remove(id);
return;
} else {
if (payload.length > 0)
_log.warn("Disconnect packet had " + payload.length + " bytes");
return;
}
} catch (Exception t) {
_log.error("Ignoring error on disconnect", t);
}
case 0x50: // packet send outgoing
_log.debug("*Packet send outgoing [" + payload.length + "]");
s = (I2PSocketImpl) _outSockets.get(id);
if (s != null) {
s.queueData(payload);
return;
} else {
_log.error("Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
case 0xA1: // SYN incoming
_log.debug("*Syn!");
if (payload.length==PUBKEY_LENGTH) {
String newLocalID = makeID(_inSockets);
Destination d = new Destination();
d.readBytes(new ByteArrayInputStream(payload));
s = new I2PSocketImpl(d, this, false,
newLocalID);
s.setRemoteID(id);
if (_serverSocket.getNewSocket(s)) {
_inSockets.put(newLocalID, s);
byte[] packet = makePacket
((byte)0x51, id,
newLocalID.getBytes("ISO-8859-1"));
boolean replySentOk = false;
synchronized(_session) {
replySentOk = _session.sendMessage(d, packet);
}
if (!replySentOk) {
_log.error("Error sending reply to " +
d.calculateHash().toBase64() +
" in response to a new con message",
new Exception("Failed creation"));
s.internalClose();
}
} else {
byte[] packet =
(" "+id).getBytes("ISO-8859-1");
packet[0]=0x52;
boolean nackSent = session.sendMessage(d, packet);
if (!nackSent) {
_log.error("Error sending NACK for session creation");
}
s.internalClose();
}
return;
} else {
_log.error("Syn packet that has a payload not equal to the pubkey length (" + payload.length + " != " + PUBKEY_LENGTH + ")");
return;
}
case 0xA2: // disconnect incoming
_log.debug("*Disconnect incoming!");
try {
s = (I2PSocketImpl) _inSockets.get(id);
if (payload.length==0 && s != null) {
s.internalClose();
_inSockets.remove(id);
return;
} else {
if (payload.length > 0)
_log.warn("Disconnect packet had " + payload.length + " bytes");
return;
}
} catch (Exception t) {
_log.error("Ignoring error on disconnect", t);
return;
}
case 0xA0: // packet send incoming
_log.debug("*Packet send incoming [" + payload.length + "]");
s = (I2PSocketImpl) _inSockets.get(id);
if (s != null) {
s.queueData(payload);
return;
} else {
_log.error("Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
case 0xFF: // ignore
return;
}
_log.error("\n\n=============== Unknown packet! "+
"============"+
"\nType: "+(int)type+
"\nID: " + getReadableForm(id)+
"\nBase64'ed Data: "+Base64.encode(payload)+
"\n\n\n");
if (id != null) {
_inSockets.remove(id);
_outSockets.remove(id);
}
}
} catch (I2PException ise) {
_log.error("Error processing", ise);
} catch (IOException ioe) {
_log.error("Error processing", ioe);
} catch (IllegalStateException ise) {
_log.debug("Error processing", ise);
}
}
public void reportAbuse(I2PSession session, int severity) {
_log.error("Abuse reported [" + severity + "]");
}
public void setDefaultOptions(I2PSocketOptions options) { _defaultOptions = options; }
public I2PSocketOptions getDefaultOptions() { return _defaultOptions ; }
public I2PServerSocket getServerSocket() { return _serverSocket; }
/** /**
* Create a new connected socket (block until the socket is created) * Create a new connected socket (block until the socket is created)
* *
@ -239,104 +237,101 @@ public class I2PSocketManager implements I2PSessionListener {
*/ */
public I2PSocket connect(Destination peer, I2PSocketOptions options) throws I2PException { public I2PSocket connect(Destination peer, I2PSocketOptions options) throws I2PException {
String localID, lcID; String localID, lcID;
I2PSocketImpl s; I2PSocketImpl s;
synchronized(lock) { synchronized (lock) {
localID=makeID(_outSockets); localID = makeID(_outSockets);
lcID=getReadableForm(localID); lcID = getReadableForm(localID);
s = new I2PSocketImpl(peer, this, true, localID); s = new I2PSocketImpl(peer, this, true, localID);
_outSockets.put(s.getLocalID(),s); _outSockets.put(s.getLocalID(), s);
} }
try { try {
ByteArrayOutputStream pubkey = new ByteArrayOutputStream(); ByteArrayOutputStream pubkey = new ByteArrayOutputStream();
_session.getMyDestination().writeBytes(pubkey); _session.getMyDestination().writeBytes(pubkey);
String remoteID; String remoteID;
byte[] packet = makePacket((byte)0xA1, localID, byte[] packet = makePacket((byte) 0xA1, localID, pubkey.toByteArray());
pubkey.toByteArray()); boolean sent = false;
boolean sent = false; synchronized (_session) {
synchronized(_session) { sent = _session.sendMessage(peer, packet);
sent = _session.sendMessage(peer, packet); }
} if (!sent) {
if (!sent) { _log.info("Unable to send & receive ack for SYN packet");
_log.info("Unable to send & receive ack for SYN packet"); synchronized (lock) {
synchronized(lock) { _outSockets.remove(s.getLocalID());
_outSockets.remove(s.getLocalID()); }
} throw new I2PException("Unable to reach peer");
throw new I2PException("Unable to reach peer"); }
} remoteID = s.getRemoteID(true, options.getConnectTimeout());
remoteID = s.getRemoteID(true, options.getConnectTimeout()); if ("".equals(remoteID)) { throw new I2PException("Unable to reach peer"); }
if ("".equals(remoteID)) { _log.debug("TIMING: s given out for remoteID " + getReadableForm(remoteID));
throw new I2PException("Unable to reach peer"); return s;
} } catch (InterruptedIOException ioe) {
_log.debug("TIMING: s given out for remoteID "+getReadableForm(remoteID)); _log.error("Timeout waiting for ack from syn for id " + getReadableForm(lcID), ioe);
return s; synchronized (lock) {
} catch (InterruptedIOException ioe) { _outSockets.remove(s.getLocalID());
_log.error("Timeout waiting for ack from syn for id " + getReadableForm(lcID), ioe); }
synchronized(lock) { throw new I2PException("Timeout waiting for ack");
_outSockets.remove(s.getLocalID()); } catch (IOException ex) {
} _log.error("Error sending syn on id " + getReadableForm(lcID), ex);
throw new I2PException("Timeout waiting for ack"); synchronized (lock) {
} catch (IOException ex) { _outSockets.remove(s.getLocalID());
_log.error("Error sending syn on id " + getReadableForm(lcID), ex); }
synchronized(lock) { throw new I2PException("IOException occurred");
_outSockets.remove(s.getLocalID()); } catch (I2PException ex) {
} _log.info("Error sending syn on id " + getReadableForm(lcID), ex);
throw new I2PException("IOException occurred"); synchronized (lock) {
} catch (I2PException ex) { _outSockets.remove(s.getLocalID());
_log.info("Error sending syn on id " + getReadableForm(lcID), ex); }
synchronized(lock) { throw ex;
_outSockets.remove(s.getLocalID()); }
}
throw ex;
}
} }
public I2PSocket connect(Destination peer) throws I2PException { public I2PSocket connect(Destination peer) throws I2PException {
return connect(peer, null); return connect(peer, null);
} }
/** /**
* Retrieve a set of currently connected I2PSockets, either initiated locally or remotely. * Retrieve a set of currently connected I2PSockets, either initiated locally or remotely.
* *
*/ */
public Set listSockets() { public Set listSockets() {
Set sockets = new HashSet(8); Set sockets = new HashSet(8);
synchronized (lock) { synchronized (lock) {
sockets.addAll(_inSockets.values()); sockets.addAll(_inSockets.values());
sockets.addAll(_outSockets.values()); sockets.addAll(_outSockets.values());
} }
return sockets; return sockets;
} }
/** /**
* Ping the specified peer, returning true if they replied to the ping within * Ping the specified peer, returning true if they replied to the ping within
* the timeout specified, false otherwise. This call blocks. * the timeout specified, false otherwise. This call blocks.
* *
*/ */
public boolean ping(Destination peer, long timeoutMs) { public boolean ping(Destination peer, long timeoutMs) {
try { try {
return _session.sendMessage(peer, new byte[] {(byte)0xFF}); return _session.sendMessage(peer, new byte[] { (byte) 0xFF});
} catch (I2PException ex) { } catch (I2PException ex) {
_log.error("I2PException:",ex); _log.error("I2PException:", ex);
return false; return false;
} }
} }
public void removeSocket(I2PSocketImpl sock) { public void removeSocket(I2PSocketImpl sock) {
synchronized(lock) { synchronized (lock) {
_inSockets.remove(sock.getLocalID()); _inSockets.remove(sock.getLocalID());
_outSockets.remove(sock.getLocalID()); _outSockets.remove(sock.getLocalID());
} }
} }
public static String getReadableForm(String id) { public static String getReadableForm(String id) {
try { try {
if (id.length() != 3) return "Bogus"; if (id.length() != 3) return "Bogus";
return Base64.encode(id.getBytes("ISO-8859-1")); return Base64.encode(id.getBytes("ISO-8859-1"));
} catch (UnsupportedEncodingException ex) { } catch (UnsupportedEncodingException ex) {
ex.printStackTrace(); ex.printStackTrace();
return null; return null;
} }
} }
/** /**
@ -345,21 +340,21 @@ public class I2PSocketManager implements I2PSessionListener {
* @param uniqueIn map of already known local IDs so we don't collide. WARNING - NOT THREADSAFE! * @param uniqueIn map of already known local IDs so we don't collide. WARNING - NOT THREADSAFE!
*/ */
public static String makeID(HashMap uniqueIn) { public static String makeID(HashMap uniqueIn) {
String newID; String newID;
try { try {
do { do {
int id = (int)(Math.random()*16777215+1); int id = (int) (Math.random() * 16777215 + 1);
byte[] nid = new byte[3]; byte[] nid = new byte[3];
nid[0]=(byte)(id / 65536); nid[0] = (byte) (id / 65536);
nid[1] = (byte)((id/256) % 256); nid[1] = (byte) ((id / 256) % 256);
nid[2]= (byte)(id %256); nid[2] = (byte) (id % 256);
newID = new String(nid, "ISO-8859-1"); newID = new String(nid, "ISO-8859-1");
} while (uniqueIn.get(newID) != null); } while (uniqueIn.get(newID) != null);
return newID; return newID;
} catch (UnsupportedEncodingException ex) { } catch (UnsupportedEncodingException ex) {
ex.printStackTrace(); ex.printStackTrace();
return null; return null;
} }
} }
/** /**
@ -367,20 +362,17 @@ public class I2PSocketManager implements I2PSessionListener {
* the given payload * the given payload
*/ */
public static byte[] makePacket(byte type, String id, byte[] payload) { public static byte[] makePacket(byte type, String id, byte[] payload) {
try { try {
byte[] packet = new byte[payload.length+4]; byte[] packet = new byte[payload.length + 4];
packet[0]=type; packet[0] = type;
byte[] temp = id.getBytes("ISO-8859-1"); byte[] temp = id.getBytes("ISO-8859-1");
if (temp.length != 3) if (temp.length != 3) throw new RuntimeException("Incorrect ID length: " + temp.length);
throw new RuntimeException("Incorrect ID length: "+ System.arraycopy(temp, 0, packet, 1, 3);
temp.length); System.arraycopy(payload, 0, packet, 4, payload.length);
System.arraycopy(temp,0,packet,1,3); return packet;
System.arraycopy(payload,0,packet,4,payload.length); } catch (UnsupportedEncodingException ex) {
return packet; if (_log.shouldLog(Log.ERROR)) _log.error("Error building the packet", ex);
} catch (UnsupportedEncodingException ex) { return new byte[0];
if (_log.shouldLog(Log.ERROR)) }
_log.error("Error building the packet", ex);
return new byte[0];
}
} }
} }

View File

@ -22,7 +22,7 @@ import net.i2p.util.Log;
*/ */
public class I2PSocketManagerFactory { public class I2PSocketManagerFactory {
private final static Log _log = new Log(I2PSocketManagerFactory.class); private final static Log _log = new Log(I2PSocketManagerFactory.class);
/** /**
* Create a socket manager using a brand new destination connected to the * Create a socket manager using a brand new destination connected to the
* I2CP router on the local machine on the default port (7654). * I2CP router on the local machine on the default port (7654).
@ -30,9 +30,9 @@ public class I2PSocketManagerFactory {
* @return the newly created socket manager, or null if there were errors * @return the newly created socket manager, or null if there were errors
*/ */
public static I2PSocketManager createManager() { public static I2PSocketManager createManager() {
return createManager("localhost", 7654, new Properties()); return createManager("localhost", 7654, new Properties());
} }
/** /**
* Create a socket manager using a brand new destination connected to the * Create a socket manager using a brand new destination connected to the
* I2CP router on the given machine reachable through the given port. * I2CP router on the given machine reachable through the given port.
@ -40,21 +40,21 @@ public class I2PSocketManagerFactory {
* @return the newly created socket manager, or null if there were errors * @return the newly created socket manager, or null if there were errors
*/ */
public static I2PSocketManager createManager(String i2cpHost, int i2cpPort, Properties opts) { public static I2PSocketManager createManager(String i2cpHost, int i2cpPort, Properties opts) {
I2PClient client = I2PClientFactory.createClient(); I2PClient client = I2PClientFactory.createClient();
ByteArrayOutputStream keyStream = new ByteArrayOutputStream(512); ByteArrayOutputStream keyStream = new ByteArrayOutputStream(512);
try { try {
Destination dest = client.createDestination(keyStream); Destination dest = client.createDestination(keyStream);
ByteArrayInputStream in = new ByteArrayInputStream(keyStream.toByteArray()); ByteArrayInputStream in = new ByteArrayInputStream(keyStream.toByteArray());
return createManager(in, i2cpHost, i2cpPort, opts); return createManager(in, i2cpHost, i2cpPort, opts);
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error creating the destination for socket manager", ioe); _log.error("Error creating the destination for socket manager", ioe);
return null; return null;
} catch (I2PException ie) { } catch (I2PException ie) {
_log.error("Error creating the destination for socket manager", ie); _log.error("Error creating the destination for socket manager", ie);
return null; return null;
} }
} }
/** /**
* Create a socket manager using the destination loaded from the given private key * Create a socket manager using the destination loaded from the given private key
* stream and connected to the I2CP router on the specified machine on the given * stream and connected to the I2CP router on the specified machine on the given
@ -62,24 +62,25 @@ public class I2PSocketManagerFactory {
* *
* @return the newly created socket manager, or null if there were errors * @return the newly created socket manager, or null if there were errors
*/ */
public static I2PSocketManager createManager(InputStream myPrivateKeyStream, String i2cpHost, int i2cpPort, Properties opts) { public static I2PSocketManager createManager(InputStream myPrivateKeyStream, String i2cpHost, int i2cpPort,
I2PClient client = I2PClientFactory.createClient(); Properties opts) {
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED); I2PClient client = I2PClientFactory.createClient();
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost); opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
opts.setProperty(I2PClient.PROP_TCP_PORT, ""+i2cpPort); opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
try { opts.setProperty(I2PClient.PROP_TCP_PORT, "" + i2cpPort);
I2PSession session = client.createSession(myPrivateKeyStream, opts); try {
session.connect(); I2PSession session = client.createSession(myPrivateKeyStream, opts);
return createManager(session); session.connect();
} catch (I2PSessionException ise) { return createManager(session);
_log.error("Error creating session for socket manager", ise); } catch (I2PSessionException ise) {
return null; _log.error("Error creating session for socket manager", ise);
} return null;
}
} }
private static I2PSocketManager createManager(I2PSession session) { private static I2PSocketManager createManager(I2PSession session) {
I2PSocketManager mgr = new I2PSocketManager(); I2PSocketManager mgr = new I2PSocketManager();
mgr.setSession(session); mgr.setSession(session);
return mgr; return mgr;
} }
} }

View File

@ -7,15 +7,21 @@ package net.i2p.client.streaming;
*/ */
public class I2PSocketOptions { public class I2PSocketOptions {
private long _connectTimeout; private long _connectTimeout;
public I2PSocketOptions() { public I2PSocketOptions() {
_connectTimeout = -1; _connectTimeout = -1;
} }
/** /**
* How long we will wait for the ACK from a SYN, in milliseconds. * How long we will wait for the ACK from a SYN, in milliseconds.
* *
* @return milliseconds to wait, or -1 if we will wait indefinitely * @return milliseconds to wait, or -1 if we will wait indefinitely
*/ */
public long getConnectTimeout() { return _connectTimeout; } public long getConnectTimeout() {
public void setConnectTimeout(long ms) { _connectTimeout = ms; } return _connectTimeout;
} }
public void setConnectTimeout(long ms) {
_connectTimeout = ms;
}
}

View File

@ -1,4 +1,5 @@
package net.i2p.phttprelay; package net.i2p.phttprelay;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@ -44,70 +45,70 @@ import javax.servlet.http.HttpServletResponse;
*/ */
public class CheckSendStatusServlet extends PHTTPRelayServlet { public class CheckSendStatusServlet extends PHTTPRelayServlet {
/* URL parameters on the check */ /* URL parameters on the check */
/** H(routerIdent).toBase64() of the target to receive the message */ /** H(routerIdent).toBase64() of the target to receive the message */
public final static String PARAM_SEND_TARGET = "target"; public final static String PARAM_SEND_TARGET = "target";
/** msgId parameter */ /** msgId parameter */
public final static String PARAM_MSG_ID = "msgId"; public final static String PARAM_MSG_ID = "msgId";
public final static String PROP_STATUS = "status"; public final static String PROP_STATUS = "status";
public final static String STATUS_PENDING = "pending"; public final static String STATUS_PENDING = "pending";
public final static String STATUS_UNKNOWN = "unknown"; public final static String STATUS_UNKNOWN = "unknown";
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String target = req.getParameter(PARAM_SEND_TARGET); String target = req.getParameter(PARAM_SEND_TARGET);
String msgIdStr = req.getParameter(PARAM_MSG_ID); String msgIdStr = req.getParameter(PARAM_MSG_ID);
log("Checking status of [" + target + "] message [" + msgIdStr + "]"); log("Checking status of [" + target + "] message [" + msgIdStr + "]");
if (!isKnownMessage(target, msgIdStr)) { if (!isKnownMessage(target, msgIdStr)) {
log("Not known - its not pending"); log("Not known - its not pending");
notPending(req, resp); notPending(req, resp);
return; return;
} else { } else {
log("Known - its still pending"); log("Known - its still pending");
pending(req, resp); pending(req, resp);
return; return;
} }
} }
private boolean isKnownMessage(String target, String msgId) throws IOException { private boolean isKnownMessage(String target, String msgId) throws IOException {
if ( (target == null) || (target.trim().length() <= 0) ) return false; if ((target == null) || (target.trim().length() <= 0)) return false;
if ( (msgId == null) || (msgId.trim().length() <= 0) ) return false; if ((msgId == null) || (msgId.trim().length() <= 0)) return false;
File identDir = getIdentDir(target); File identDir = getIdentDir(target);
if (identDir.exists()) { if (identDir.exists()) {
File identFile = new File(identDir, "identity.dat"); File identFile = new File(identDir, "identity.dat");
if (identFile.exists()) { if (identFile.exists()) {
// known and valid (maybe we need to check the file format... naw, fuck it // known and valid (maybe we need to check the file format... naw, fuck it
File msgFile = new File(identDir, "msg" + msgId + ".dat"); File msgFile = new File(identDir, "msg" + msgId + ".dat");
if (msgFile.exists()) if (msgFile.exists())
return true; return true;
else else
return false; return false;
} else { } else {
return false; return false;
} }
} else { } else {
return false; return false;
} }
} }
private void pending(HttpServletRequest req, HttpServletResponse resp) throws IOException { private void pending(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setStatus(HttpServletResponse.SC_OK); resp.setStatus(HttpServletResponse.SC_OK);
ServletOutputStream out = resp.getOutputStream(); ServletOutputStream out = resp.getOutputStream();
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append(PROP_STATUS).append('=').append(STATUS_PENDING).append('\n'); buf.append(PROP_STATUS).append('=').append(STATUS_PENDING).append('\n');
out.write(buf.toString().getBytes()); out.write(buf.toString().getBytes());
out.flush(); out.flush();
out.close(); out.close();
} }
private void notPending(HttpServletRequest req, HttpServletResponse resp) throws IOException { private void notPending(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setStatus(HttpServletResponse.SC_OK); resp.setStatus(HttpServletResponse.SC_OK);
ServletOutputStream out = resp.getOutputStream(); ServletOutputStream out = resp.getOutputStream();
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append(PROP_STATUS).append('=').append(STATUS_UNKNOWN).append('\n'); buf.append(PROP_STATUS).append('=').append(STATUS_UNKNOWN).append('\n');
out.write(buf.toString().getBytes()); out.write(buf.toString().getBytes());
out.flush(); out.flush();
out.close(); out.close();
} }
} }

View File

@ -1,4 +1,5 @@
package net.i2p.phttprelay; package net.i2p.phttprelay;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@ -18,23 +19,26 @@ import java.util.Set;
*/ */
class LockManager { class LockManager {
private volatile static Set _locks = new HashSet(); // target private volatile static Set _locks = new HashSet(); // target
public static void lockIdent(String target) { public static void lockIdent(String target) {
while (true) { while (true) {
synchronized (_locks) { synchronized (_locks) {
if (!_locks.contains(target)) { if (!_locks.contains(target)) {
_locks.add(target); _locks.add(target);
return; return;
} }
try { _locks.wait(1000); } catch (InterruptedException ie) {} try {
} _locks.wait(1000);
} } catch (InterruptedException ie) {
}
}
}
} }
public static void unlockIdent(String target) { public static void unlockIdent(String target) {
synchronized (_locks) { synchronized (_locks) {
_locks.remove(target); _locks.remove(target);
_locks.notifyAll(); _locks.notifyAll();
} }
} }
} }

View File

@ -1,4 +1,5 @@
package net.i2p.phttprelay; package net.i2p.phttprelay;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@ -25,49 +26,50 @@ abstract class PHTTPRelayServlet extends HttpServlet {
/* config params */ /* config params */
/*public final static String PARAM_BASEDIR = "baseDir";*/ /*public final static String PARAM_BASEDIR = "baseDir";*/
public final static String ENV_BASEDIR = "phttpRelay.baseDir"; public final static String ENV_BASEDIR = "phttpRelay.baseDir";
/** match the clock fudge factor on the router, rather than importing the entire router cvs module */ /** match the clock fudge factor on the router, rather than importing the entire router cvs module */
public final static long CLOCK_FUDGE_FACTOR = 1*60*1000; public final static long CLOCK_FUDGE_FACTOR = 1 * 60 * 1000;
protected String buildURL(HttpServletRequest req, String path) { protected String buildURL(HttpServletRequest req, String path) {
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append(req.getScheme()).append("://"); buf.append(req.getScheme()).append("://");
buf.append(req.getServerName()).append(":").append(req.getServerPort()); buf.append(req.getServerName()).append(":").append(req.getServerPort());
buf.append(req.getContextPath()); buf.append(req.getContextPath());
buf.append(path); buf.append(path);
log("URL built: " + buf.toString()); log("URL built: " + buf.toString());
return buf.toString(); return buf.toString();
} }
protected File getIdentDir(String target) throws IOException { protected File getIdentDir(String target) throws IOException {
if ( (_baseDir == null) || (target == null) ) throw new IOException("dir not specified to deal with"); if ((_baseDir == null) || (target == null)) throw new IOException("dir not specified to deal with");
File baseDir = new File(_baseDir); File baseDir = new File(_baseDir);
if (!baseDir.exists()) { if (!baseDir.exists()) {
boolean created = baseDir.mkdirs(); boolean created = baseDir.mkdirs();
log("Creating PHTTP Relay Base Directory: " + baseDir.getAbsolutePath() + " - ok? " + created); log("Creating PHTTP Relay Base Directory: " + baseDir.getAbsolutePath() + " - ok? " + created);
} }
File identDir = new File(baseDir, target); File identDir = new File(baseDir, target);
log("Ident dir: " + identDir.getAbsolutePath()); log("Ident dir: " + identDir.getAbsolutePath());
return identDir; return identDir;
} }
public void init(ServletConfig config) throws ServletException { public void init(ServletConfig config) throws ServletException {
super.init(config); super.init(config);
String dir = System.getProperty(ENV_BASEDIR); String dir = System.getProperty(ENV_BASEDIR);
if (dir == null) { if (dir == null) {
_log.warn("Base directory for the polling http relay system not in the environment [" + ENV_BASEDIR +"]"); _log.warn("Base directory for the polling http relay system not in the environment [" + ENV_BASEDIR + "]");
_log.warn("Setting the base directory to ./relayDir for " + getServletName()); _log.warn("Setting the base directory to ./relayDir for " + getServletName());
_baseDir = ".relayDir"; _baseDir = ".relayDir";
} else { } else {
_baseDir = dir; _baseDir = dir;
log("Loaded up " + getServletName() + " with base directory " + _baseDir); log("Loaded up " + getServletName() + " with base directory " + _baseDir);
} }
} }
public void log(String msg) { public void log(String msg) {
_log.debug(msg); _log.debug(msg);
} }
public void log(String msg, Throwable t) { public void log(String msg, Throwable t) {
_log.debug(msg, t); _log.debug(msg, t);
} }
} }

View File

@ -1,4 +1,5 @@
package net.i2p.phttprelay; package net.i2p.phttprelay;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@ -55,209 +56,208 @@ import net.i2p.util.Clock;
* baseDir is the directory under which registrants and their pending messages are stored * baseDir is the directory under which registrants and their pending messages are stored
* *
*/ */
public class PollServlet extends PHTTPRelayServlet { public class PollServlet extends PHTTPRelayServlet {
/* URL parameters on the check */ /* URL parameters on the check */
/** H(routerIdent).toBase64() of the target to receive the message */ /** H(routerIdent).toBase64() of the target to receive the message */
public final static String PARAM_SEND_TARGET = "target"; public final static String PARAM_SEND_TARGET = "target";
/** HTTP error code if the target is not known*/ /** HTTP error code if the target is not known*/
public final static int CODE_UNKNOWN = HttpServletResponse.SC_NOT_FOUND; public final static int CODE_UNKNOWN = HttpServletResponse.SC_NOT_FOUND;
/** HTTP error code if the signature failed */ /** HTTP error code if the signature failed */
public final static int CODE_UNAUTHORIZED = HttpServletResponse.SC_UNAUTHORIZED; public final static int CODE_UNAUTHORIZED = HttpServletResponse.SC_UNAUTHORIZED;
/** HTTP error code if everything is ok */ /** HTTP error code if everything is ok */
public final static int CODE_OK = HttpServletResponse.SC_OK; public final static int CODE_OK = HttpServletResponse.SC_OK;
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
byte data[] = getData(req); byte data[] = getData(req);
if (data == null) return; if (data == null) return;
ByteArrayInputStream bais = new ByteArrayInputStream(data); ByteArrayInputStream bais = new ByteArrayInputStream(data);
String target = getTarget(bais); String target = getTarget(bais);
if (target == null) { if (target == null) {
log("Target not specified"); log("Target not specified");
resp.sendError(CODE_UNKNOWN); resp.sendError(CODE_UNKNOWN);
return; return;
} }
if (!isKnown(target)) { if (!isKnown(target)) {
resp.sendError(CODE_UNKNOWN); resp.sendError(CODE_UNKNOWN);
return; return;
} }
if (!isAuthorized(target, bais)) { if (!isAuthorized(target, bais)) {
resp.sendError(CODE_UNAUTHORIZED); resp.sendError(CODE_UNAUTHORIZED);
return; return;
} else { } else {
log("Authorized access for target " + target); log("Authorized access for target " + target);
} }
sendMessages(resp, target); sendMessages(resp, target);
} }
private byte[] getData(HttpServletRequest req) throws ServletException, IOException { private byte[] getData(HttpServletRequest req) throws ServletException, IOException {
ServletInputStream in = req.getInputStream(); ServletInputStream in = req.getInputStream();
int len = req.getContentLength(); int len = req.getContentLength();
byte data[] = new byte[len]; byte data[] = new byte[len];
int cur = 0; int cur = 0;
int read = DataHelper.read(in, data); int read = DataHelper.read(in, data);
if (read != len) { if (read != len) {
log("Size read is incorrect [" + read + " instead of expected " + len + "]"); log("Size read is incorrect [" + read + " instead of expected " + len + "]");
return null; return null;
} else { } else {
log("Read data length: " + data.length + " in base64: " + Base64.encode(data)); log("Read data length: " + data.length + " in base64: " + Base64.encode(data));
return data; return data;
} }
} }
private String getTarget(InputStream in) throws IOException { private String getTarget(InputStream in) throws IOException {
StringBuffer buf = new StringBuffer(64); StringBuffer buf = new StringBuffer(64);
int numBytes = 0; int numBytes = 0;
int c = 0; int c = 0;
while ( (c = in.read()) != -1) { while ((c = in.read()) != -1) {
if (c == (int)'&') break; if (c == (int) '&') break;
buf.append((char)c); buf.append((char) c);
numBytes++; numBytes++;
if (numBytes > 128) { if (numBytes > 128) {
log("Target didn't find the & after 128 bytes [" + buf.toString() + "]"); log("Target didn't find the & after 128 bytes [" + buf.toString() + "]");
return null; return null;
} }
} }
if (buf.toString().indexOf("target=") != 0) { if (buf.toString().indexOf("target=") != 0) {
log("Did not start with target= [" + buf.toString() + "]"); log("Did not start with target= [" + buf.toString() + "]");
return null; return null;
} }
return buf.substring("target=".length()); return buf.substring("target=".length());
} }
private void sendMessages(HttpServletResponse resp, String target) throws IOException { private void sendMessages(HttpServletResponse resp, String target) throws IOException {
log("Before lock " + target); log("Before lock " + target);
LockManager.lockIdent(target); LockManager.lockIdent(target);
log("Locked " + target); log("Locked " + target);
try { try {
File identDir = getIdentDir(target); File identDir = getIdentDir(target);
expire(identDir); expire(identDir);
File messageFiles[] = identDir.listFiles(); File messageFiles[] = identDir.listFiles();
resp.setStatus(CODE_OK); resp.setStatus(CODE_OK);
log("Sending back " + (messageFiles.length -1) + " messages"); log("Sending back " + (messageFiles.length - 1) + " messages");
ServletOutputStream out = resp.getOutputStream(); ServletOutputStream out = resp.getOutputStream();
DataHelper.writeDate(out, new Date(Clock.getInstance().now())); DataHelper.writeDate(out, new Date(Clock.getInstance().now()));
DataHelper.writeLong(out, 2, messageFiles.length -1); DataHelper.writeLong(out, 2, messageFiles.length - 1);
for (int i = 0; i < messageFiles.length; i++) { for (int i = 0; i < messageFiles.length; i++) {
if ("identity.dat".equals(messageFiles[i].getName())) { if ("identity.dat".equals(messageFiles[i].getName())) {
// skip // skip
} else { } else {
log("Message file " + messageFiles[i].getName() + " is " + messageFiles[i].length() + " bytes"); log("Message file " + messageFiles[i].getName() + " is " + messageFiles[i].length() + " bytes");
DataHelper.writeLong(out, 4, messageFiles[i].length()); DataHelper.writeLong(out, 4, messageFiles[i].length());
writeFile(out, messageFiles[i]); writeFile(out, messageFiles[i]);
boolean deleted = messageFiles[i].delete(); boolean deleted = messageFiles[i].delete();
if (!deleted) { if (!deleted) {
log("!!!Error removing message file " + messageFiles[i].getAbsolutePath() + " - please delete!"); log("!!!Error removing message file " + messageFiles[i].getAbsolutePath() + " - please delete!");
} }
} }
} }
out.flush(); out.flush();
out.close(); out.close();
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
log("Error sending message", dfe); log("Error sending message", dfe);
} finally { } finally {
LockManager.unlockIdent(target); LockManager.unlockIdent(target);
log("Unlocked " + target); log("Unlocked " + target);
} }
} }
private final static long EXPIRE_DELAY = 60*1000; // expire messages every minute private final static long EXPIRE_DELAY = 60 * 1000; // expire messages every minute
private void expire(File identDir) throws IOException { private void expire(File identDir) throws IOException {
File files[] = identDir.listFiles(); File files[] = identDir.listFiles();
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
for (int i = 0 ; i < files.length; i++) { for (int i = 0; i < files.length; i++) {
if ("identity.dat".equals(files[i].getName())) { if ("identity.dat".equals(files[i].getName())) {
continue; continue;
} }
if (files[i].lastModified() + EXPIRE_DELAY < now) { if (files[i].lastModified() + EXPIRE_DELAY < now) {
log("Expiring " + files[i].getAbsolutePath()); log("Expiring " + files[i].getAbsolutePath());
files[i].delete(); files[i].delete();
} }
} }
} }
private void writeFile(ServletOutputStream out, File file) throws IOException { private void writeFile(ServletOutputStream out, File file) throws IOException {
FileInputStream fis = new FileInputStream(file); FileInputStream fis = new FileInputStream(file);
try { try {
byte buf[] = new byte[4096]; byte buf[] = new byte[4096];
while (true) { while (true) {
int read = DataHelper.read(fis, buf); int read = DataHelper.read(fis, buf);
if (read > 0) if (read > 0)
out.write(buf, 0, read); out.write(buf, 0, read);
else else
break; break;
} }
} finally { } finally {
fis.close(); fis.close();
} }
} }
private boolean isKnown(String target) throws IOException { private boolean isKnown(String target) throws IOException {
File identDir = getIdentDir(target); File identDir = getIdentDir(target);
if (identDir.exists()) { if (identDir.exists()) {
File identFile = new File(identDir, "identity.dat"); File identFile = new File(identDir, "identity.dat");
if (identFile.exists()) { if (identFile.exists()) {
// known and valid (maybe we need to check the file format... naw, fuck it // known and valid (maybe we need to check the file format... naw, fuck it
return true; return true;
} else { } else {
return false; return false;
} }
} else { } else {
return false; return false;
} }
} }
private boolean isAuthorized(String target, InputStream in) throws IOException { private boolean isAuthorized(String target, InputStream in) throws IOException {
RouterIdentity ident = null; RouterIdentity ident = null;
try { try {
ident = getRouterIdentity(target); ident = getRouterIdentity(target);
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
log("Identity was not valid", dfe); log("Identity was not valid", dfe);
} }
if (ident == null) { if (ident == null) {
log("Identity not registered"); log("Identity not registered");
return false; return false;
} }
try { try {
long val = DataHelper.readLong(in, 4); long val = DataHelper.readLong(in, 4);
Signature sig = new Signature(); Signature sig = new Signature();
sig.readBytes(in); sig.readBytes(in);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataHelper.writeLong(baos, 4, val); DataHelper.writeLong(baos, 4, val);
if (DSAEngine.getInstance().verifySignature(sig, baos.toByteArray(), ident.getSigningPublicKey())) { if (DSAEngine.getInstance().verifySignature(sig, baos.toByteArray(), ident.getSigningPublicKey())) {
return true; return true;
} else { } else {
log("Signature does NOT match"); log("Signature does NOT match");
return false; return false;
} }
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
log("Format error reading the nonce and signature", dfe); log("Format error reading the nonce and signature", dfe);
return false; return false;
} }
} }
private RouterIdentity getRouterIdentity(String target) throws IOException, DataFormatException { private RouterIdentity getRouterIdentity(String target) throws IOException, DataFormatException {
File identDir = getIdentDir(target); File identDir = getIdentDir(target);
if (identDir.exists()) { if (identDir.exists()) {
File identFile = new File(identDir, "identity.dat"); File identFile = new File(identDir, "identity.dat");
if (identFile.exists()) { if (identFile.exists()) {
// known and valid (maybe we need to check the file format... naw, fuck it // known and valid (maybe we need to check the file format... naw, fuck it
RouterIdentity ident = new RouterIdentity(); RouterIdentity ident = new RouterIdentity();
ident.readBytes(new FileInputStream(identFile)); ident.readBytes(new FileInputStream(identFile));
return ident; return ident;
} else { } else {
return null; return null;
} }
} else { } else {
return null; return null;
} }
} }
} }

View File

@ -1,4 +1,5 @@
package net.i2p.phttprelay; package net.i2p.phttprelay;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@ -66,89 +67,92 @@ public class RegisterServlet extends PHTTPRelayServlet {
/* config params */ /* config params */
public final static String PARAM_POLL_PATH = "pollPath"; public final static String PARAM_POLL_PATH = "pollPath";
public final static String PARAM_SEND_PATH = "sendPath"; public final static String PARAM_SEND_PATH = "sendPath";
/* key=val keys sent back on registration */ /* key=val keys sent back on registration */
public final static String PROP_STATUS = "status"; public final static String PROP_STATUS = "status";
public final static String PROP_POLL_URL = "pollURL"; public final static String PROP_POLL_URL = "pollURL";
public final static String PROP_SEND_URL = "sendURL"; public final static String PROP_SEND_URL = "sendURL";
public final static String PROP_TIME_OFFSET = "timeOffset"; // ms (local-remote) public final static String PROP_TIME_OFFSET = "timeOffset"; // ms (local-remote)
/* values for the PROP_STATUS */ /* values for the PROP_STATUS */
public final static String STATUS_FAILED = "failed"; public final static String STATUS_FAILED = "failed";
public final static String STATUS_REGISTERED = "registered"; public final static String STATUS_REGISTERED = "registered";
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletInputStream in = req.getInputStream();
RouterIdentity ident = new RouterIdentity();
try {
Date remoteTime = DataHelper.readDate(in);
long skew = getSkew(remoteTime);
ident.readBytes(in);
boolean ok = registerIdent(ident);
sendURLs(req, resp, skew, ok);
} catch (DataFormatException dfe) {
log("Invalid format for router identity posted", dfe);
} finally {
in.close();
}
}
private long getSkew(Date remoteDate) {
if (remoteDate == null) {
log("*ERROR: remote date was null");
return Long.MAX_VALUE;
} else {
long diff = Clock.getInstance().now() - remoteDate.getTime();
return diff;
}
}
private boolean registerIdent(RouterIdentity ident) throws DataFormatException, IOException {
File identDir = getIdentDir(ident.getHash().toBase64());
boolean created = identDir.mkdirs();
File identFile = new File(identDir, "identity.dat");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(identFile);
ident.writeBytes(fos);
} finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
}
log("Identity registered into " + identFile.getAbsolutePath());
return true;
}
private void sendURLs(HttpServletRequest req, HttpServletResponse resp, long skew, boolean ok) throws IOException {
ServletOutputStream out = resp.getOutputStream();
log("*Debug: clock skew of " + skew + "ms (local-remote)"); public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletInputStream in = req.getInputStream();
StringBuffer buf = new StringBuffer(); RouterIdentity ident = new RouterIdentity();
if (ok) { try {
buf.append(PROP_POLL_URL).append("=").append(buildURL(req, _pollPath)).append("\n"); Date remoteTime = DataHelper.readDate(in);
buf.append(PROP_SEND_URL).append("=").append(buildURL(req, _sendPath)).append("\n"); long skew = getSkew(remoteTime);
buf.append(PROP_TIME_OFFSET).append("=").append(skew).append("\n"); ident.readBytes(in);
buf.append(PROP_STATUS).append("=").append(STATUS_REGISTERED).append("\n"); boolean ok = registerIdent(ident);
} else { sendURLs(req, resp, skew, ok);
buf.append(PROP_TIME_OFFSET).append("=").append(skew).append("\n"); } catch (DataFormatException dfe) {
buf.append(PROP_STATUS).append("=").append(STATUS_FAILED).append("\n"); log("Invalid format for router identity posted", dfe);
} } finally {
out.write(buf.toString().getBytes()); in.close();
out.close(); }
} }
private long getSkew(Date remoteDate) {
if (remoteDate == null) {
log("*ERROR: remote date was null");
return Long.MAX_VALUE;
} else {
long diff = Clock.getInstance().now() - remoteDate.getTime();
return diff;
}
}
private boolean registerIdent(RouterIdentity ident) throws DataFormatException, IOException {
File identDir = getIdentDir(ident.getHash().toBase64());
boolean created = identDir.mkdirs();
File identFile = new File(identDir, "identity.dat");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(identFile);
ident.writeBytes(fos);
} finally {
if (fos != null) try {
fos.close();
} catch (IOException ioe) {
}
}
log("Identity registered into " + identFile.getAbsolutePath());
return true;
}
private void sendURLs(HttpServletRequest req, HttpServletResponse resp, long skew, boolean ok) throws IOException {
ServletOutputStream out = resp.getOutputStream();
log("*Debug: clock skew of " + skew + "ms (local-remote)");
StringBuffer buf = new StringBuffer();
if (ok) {
buf.append(PROP_POLL_URL).append("=").append(buildURL(req, _pollPath)).append("\n");
buf.append(PROP_SEND_URL).append("=").append(buildURL(req, _sendPath)).append("\n");
buf.append(PROP_TIME_OFFSET).append("=").append(skew).append("\n");
buf.append(PROP_STATUS).append("=").append(STATUS_REGISTERED).append("\n");
} else {
buf.append(PROP_TIME_OFFSET).append("=").append(skew).append("\n");
buf.append(PROP_STATUS).append("=").append(STATUS_FAILED).append("\n");
}
out.write(buf.toString().getBytes());
out.close();
}
public void init(ServletConfig config) throws ServletException { public void init(ServletConfig config) throws ServletException {
super.init(config); super.init(config);
String pollPath = config.getInitParameter(PARAM_POLL_PATH); String pollPath = config.getInitParameter(PARAM_POLL_PATH);
if (pollPath == null) if (pollPath == null)
throw new ServletException("Polling path for the registration servlet required [" + PARAM_POLL_PATH + "]"); throw new ServletException("Polling path for the registration servlet required [" + PARAM_POLL_PATH + "]");
else else
_pollPath = pollPath; _pollPath = pollPath;
String sendPath = config.getInitParameter(PARAM_SEND_PATH); String sendPath = config.getInitParameter(PARAM_SEND_PATH);
if (sendPath == null) if (sendPath == null)
throw new ServletException("Sending path for the registration servlet required [" + PARAM_SEND_PATH + "]"); throw new ServletException("Sending path for the registration servlet required [" + PARAM_SEND_PATH + "]");
else else
_sendPath = sendPath; _sendPath = sendPath;
} }
} }

View File

@ -1,4 +1,5 @@
package net.i2p.phttprelay; package net.i2p.phttprelay;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@ -63,256 +64,261 @@ import javax.servlet.http.HttpServletResponse;
public class SendServlet extends PHTTPRelayServlet { public class SendServlet extends PHTTPRelayServlet {
private String _checkPath; private String _checkPath;
private int _maxMessagesPerIdent; private int _maxMessagesPerIdent;
/* config params */ /* config params */
public final static String PARAM_CHECK_PATH = "checkPath"; public final static String PARAM_CHECK_PATH = "checkPath";
public final static String PARAM_MAX_MESSAGES_PER_IDENT = "maxMessagesPerIdent"; public final static String PARAM_MAX_MESSAGES_PER_IDENT = "maxMessagesPerIdent";
/* URL parameters on the send */ /* URL parameters on the send */
/** H(routerIdent).toBase64() of the target to receive the message */ /** H(routerIdent).toBase64() of the target to receive the message */
public final static String PARAM_SEND_TARGET = "target"; public final static String PARAM_SEND_TARGET = "target";
/** # ms to wait for the message to be delivered before failing it */ /** # ms to wait for the message to be delivered before failing it */
public final static String PARAM_SEND_TIMEOUTMS = "timeoutMs"; public final static String PARAM_SEND_TIMEOUTMS = "timeoutMs";
/** # bytes to be sent in the message */ /** # bytes to be sent in the message */
public final static String PARAM_SEND_DATA_LENGTH = "dataLength"; public final static String PARAM_SEND_DATA_LENGTH = "dataLength";
/** sending router's time in ms */ /** sending router's time in ms */
public final static String PARAM_SEND_TIME = "localTime"; public final static String PARAM_SEND_TIME = "localTime";
/** msgId parameter to access the check path servlet with (along side PARAM_SEND_TARGET) */ /** msgId parameter to access the check path servlet with (along side PARAM_SEND_TARGET) */
public final static String PARAM_MSG_ID = "msgId"; public final static String PARAM_MSG_ID = "msgId";
/* key=val keys sent back on registration */ /* key=val keys sent back on registration */
public final static String PROP_CHECK_URL = "statusCheckURL"; public final static String PROP_CHECK_URL = "statusCheckURL";
public final static String PROP_STATUS = "status"; public final static String PROP_STATUS = "status";
public final static String STATUS_OK = "accepted"; public final static String STATUS_OK = "accepted";
public final static String STATUS_UNKNOWN = "unknown"; public final static String STATUS_UNKNOWN = "unknown";
private final static String STATUS_CLOCKSKEW = "clockSkew_"; /** prefix for (local-remote) */ private final static String STATUS_CLOCKSKEW = "clockSkew_";
/** prefix for (local-remote) */
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletInputStream in = req.getInputStream(); ServletInputStream in = req.getInputStream();
try { try {
int contentLen = req.getContentLength(); int contentLen = req.getContentLength();
String firstLine = getFirstLine(in, contentLen); String firstLine = getFirstLine(in, contentLen);
if (firstLine == null) { if (firstLine == null) { return; }
return; Map params = getParameters(firstLine);
} String target = (String) params.get(PARAM_SEND_TARGET);
Map params = getParameters(firstLine); String timeoutStr = (String) params.get(PARAM_SEND_TIMEOUTMS);
String target = (String)params.get(PARAM_SEND_TARGET); String lenStr = (String) params.get(PARAM_SEND_DATA_LENGTH);
String timeoutStr = (String)params.get(PARAM_SEND_TIMEOUTMS); String remoteTimeStr = (String) params.get(PARAM_SEND_TIME);
String lenStr = (String)params.get(PARAM_SEND_DATA_LENGTH); long skew = 0;
String remoteTimeStr = (String)params.get(PARAM_SEND_TIME); try {
long skew = 0; long remTime = Long.parseLong(remoteTimeStr);
try { skew = System.currentTimeMillis() - remTime;
long remTime = Long.parseLong(remoteTimeStr); } catch (Throwable t) {
skew = System.currentTimeMillis() - remTime; skew = Long.MAX_VALUE;
} catch (Throwable t) { log("*ERROR could not parse the remote time from [" + remoteTimeStr + "]");
skew = Long.MAX_VALUE; }
log("*ERROR could not parse the remote time from [" + remoteTimeStr + "]");
}
log("Target [" + target + "] timeout [" + timeoutStr + "] length [" + lenStr + "] skew [" + skew + "]"); log("Target [" + target + "] timeout [" + timeoutStr + "] length [" + lenStr + "] skew [" + skew + "]");
if ( (skew > CLOCK_FUDGE_FACTOR) || (skew < 0 - CLOCK_FUDGE_FACTOR) ) { if ((skew > CLOCK_FUDGE_FACTOR) || (skew < 0 - CLOCK_FUDGE_FACTOR)) {
log("Attempt to send by a skewed router: skew = " + skew + "ms (local-remote)"); log("Attempt to send by a skewed router: skew = " + skew + "ms (local-remote)");
failSkewed(req, resp, skew); failSkewed(req, resp, skew);
} }
if (!isValidTarget(target)) {
log("Attempt to send to an invalid target [" + target + "]");
fail(req, resp, "Unknown or invalid target");
return;
}
long len = -1; if (!isValidTarget(target)) {
try { log("Attempt to send to an invalid target [" + target + "]");
len = Long.parseLong(lenStr); fail(req, resp, "Unknown or invalid target");
} catch (Throwable t) { return;
log("Unable to parse length parameter [" + PARAM_SEND_DATA_LENGTH + "] (" + lenStr + ")"); }
fail(req, resp, "Invalid length parameter");
return;
}
int msgId = saveFile(in, resp, target, len); long len = -1;
if (msgId >= 0) { try {
sendSuccess(req, resp, target, msgId); len = Long.parseLong(lenStr);
} else { } catch (Throwable t) {
fail(req, resp, "Unable to queue up the message for delivery"); log("Unable to parse length parameter [" + PARAM_SEND_DATA_LENGTH + "] (" + lenStr + ")");
} fail(req, resp, "Invalid length parameter");
} finally { return;
try { in.close(); } catch (IOException ioe) {} }
}
int msgId = saveFile(in, resp, target, len);
if (msgId >= 0) {
sendSuccess(req, resp, target, msgId);
} else {
fail(req, resp, "Unable to queue up the message for delivery");
}
} finally {
try {
in.close();
} catch (IOException ioe) {
}
}
} }
private String getFirstLine(ServletInputStream in, int len) throws ServletException, IOException { private String getFirstLine(ServletInputStream in, int len) throws ServletException, IOException {
StringBuffer buf = new StringBuffer(128); StringBuffer buf = new StringBuffer(128);
int numBytes = 0; int numBytes = 0;
int c = 0; int c = 0;
while ( (c = in.read()) != -1) { while ((c = in.read()) != -1) {
if (c == (int)'\n') break; if (c == (int) '\n') break;
buf.append((char)c); buf.append((char) c);
numBytes++; numBytes++;
if (numBytes > 512) { if (numBytes > 512) {
log("First line is > 512 bytes [" + buf.toString() + "]"); log("First line is > 512 bytes [" + buf.toString() + "]");
return null; return null;
} }
} }
log("First line: " + buf.toString()); log("First line: " + buf.toString());
return buf.toString(); return buf.toString();
} }
private static Map getParameters(String line) { private static Map getParameters(String line) {
//StringTokenizer tok = new StringTokenizer(line, "&=", true); //StringTokenizer tok = new StringTokenizer(line, "&=", true);
Map params = new HashMap(); Map params = new HashMap();
while (line != null) { while (line != null) {
String key = null; String key = null;
String val = null; String val = null;
int firstAmp = line.indexOf('&'); int firstAmp = line.indexOf('&');
int firstEq = line.indexOf('='); int firstEq = line.indexOf('=');
if (firstAmp > 0) { if (firstAmp > 0) {
key = line.substring(0, firstEq); key = line.substring(0, firstEq);
val = line.substring(firstEq+1, firstAmp); val = line.substring(firstEq + 1, firstAmp);
line = line.substring(firstAmp+1); line = line.substring(firstAmp + 1);
params.put(key, val); params.put(key, val);
} else { } else {
line = null; line = null;
} }
} }
return params; return params;
} }
private boolean isValidTarget(String target) throws IOException { private boolean isValidTarget(String target) throws IOException {
File identDir = getIdentDir(target); File identDir = getIdentDir(target);
if (identDir.exists()) { if (identDir.exists()) {
File identFile = new File(identDir, "identity.dat"); File identFile = new File(identDir, "identity.dat");
if (identFile.exists()) { if (identFile.exists()) {
// known and valid (maybe we need to check the file format... naw, fuck it // known and valid (maybe we need to check the file format... naw, fuck it
String files[] = identDir.list(); String files[] = identDir.list();
// we skip 1 because of identity.dat // we skip 1 because of identity.dat
if (files.length -1 > _maxMessagesPerIdent) { if (files.length - 1 > _maxMessagesPerIdent) {
log("Too many messages pending for " + target + ": " + (files.length-1)); log("Too many messages pending for " + target + ": " + (files.length - 1));
return false; return false;
} else { } else {
return true; return true;
} }
} else { } else {
log("Ident directory exists, but identity does not... corrupt for " + target); log("Ident directory exists, but identity does not... corrupt for " + target);
return false; return false;
} }
} else { } else {
log("Unknown ident " + target); log("Unknown ident " + target);
return false; return false;
} }
} }
private int saveFile(InputStream in, HttpServletResponse resp, String target, long len) throws IOException { private int saveFile(InputStream in, HttpServletResponse resp, String target, long len) throws IOException {
File identDir = getIdentDir(target); File identDir = getIdentDir(target);
if (!identDir.exists()) return -1; if (!identDir.exists()) return -1;
try { try {
LockManager.lockIdent(target); LockManager.lockIdent(target);
int i = 0; int i = 0;
while (true) { while (true) {
File curFile = new File(identDir, "msg" + i + ".dat"); File curFile = new File(identDir, "msg" + i + ".dat");
if (!curFile.exists()) { if (!curFile.exists()) {
boolean ok = writeFile(curFile, in, len); boolean ok = writeFile(curFile, in, len);
if (ok) if (ok)
return i; return i;
else else
return -1; return -1;
} }
i++; i++;
continue; continue;
} }
} finally { } finally {
LockManager.unlockIdent(target); LockManager.unlockIdent(target);
} }
} }
private boolean writeFile(File file, InputStream in, long len) throws IOException { private boolean writeFile(File file, InputStream in, long len) throws IOException {
long remaining = len; long remaining = len;
FileOutputStream fos = null; FileOutputStream fos = null;
try { try {
fos = new FileOutputStream(file); fos = new FileOutputStream(file);
byte buf[] = new byte[4096]; byte buf[] = new byte[4096];
while (remaining > 0) { while (remaining > 0) {
int read = in.read(buf); int read = in.read(buf);
if (read == -1) if (read == -1) break;
break; remaining -= read;
remaining -= read; if (read > 0) fos.write(buf, 0, read);
if (read > 0) }
fos.write(buf, 0, read); } finally {
} if (fos != null) {
} finally { try {
if (fos != null) { fos.close();
try { fos.close(); } catch (IOException ioe) {} } catch (IOException ioe) {
} }
if (remaining != 0) { }
log("Invalid remaining bytes [" + remaining + " out of " + len + "] - perhaps message was cancelled partway through delivery? deleting " + file.getAbsolutePath()); if (remaining != 0) {
boolean deleted = file.delete(); log("Invalid remaining bytes [" + remaining + " out of " + len
if (!deleted) + "] - perhaps message was cancelled partway through delivery? deleting " + file.getAbsolutePath());
log("!!!Error deleting temporary file " + file.getAbsolutePath()); boolean deleted = file.delete();
return false; if (!deleted) log("!!!Error deleting temporary file " + file.getAbsolutePath());
} return false;
} }
return true; }
return true;
} }
private void sendSuccess(HttpServletRequest req, HttpServletResponse resp, String target, int msgId) throws IOException { private void sendSuccess(HttpServletRequest req, HttpServletResponse resp, String target, int msgId)
ServletOutputStream out = resp.getOutputStream(); throws IOException {
StringBuffer buf = new StringBuffer(); ServletOutputStream out = resp.getOutputStream();
buf.append(PROP_STATUS).append('=').append(STATUS_OK).append('\n'); StringBuffer buf = new StringBuffer();
buf.append(PROP_CHECK_URL).append('=').append(buildURL(req, _checkPath)); buf.append(PROP_STATUS).append('=').append(STATUS_OK).append('\n');
buf.append('?'); buf.append(PROP_CHECK_URL).append('=').append(buildURL(req, _checkPath));
buf.append(PARAM_SEND_TARGET).append('=').append(target).append("&"); buf.append('?');
buf.append(PARAM_MSG_ID).append('=').append(msgId).append("\n"); buf.append(PARAM_SEND_TARGET).append('=').append(target).append("&");
out.write(buf.toString().getBytes()); buf.append(PARAM_MSG_ID).append('=').append(msgId).append("\n");
out.flush(); out.write(buf.toString().getBytes());
out.flush();
} }
private void fail(HttpServletRequest req, HttpServletResponse resp, String err) throws IOException { private void fail(HttpServletRequest req, HttpServletResponse resp, String err) throws IOException {
ServletOutputStream out = resp.getOutputStream(); ServletOutputStream out = resp.getOutputStream();
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append(PROP_STATUS).append('=').append(STATUS_UNKNOWN).append('\n'); buf.append(PROP_STATUS).append('=').append(STATUS_UNKNOWN).append('\n');
out.write(buf.toString().getBytes()); out.write(buf.toString().getBytes());
out.flush(); out.flush();
} }
private void failSkewed(HttpServletRequest req, HttpServletResponse resp, long skew) throws IOException { private void failSkewed(HttpServletRequest req, HttpServletResponse resp, long skew) throws IOException {
ServletOutputStream out = resp.getOutputStream(); ServletOutputStream out = resp.getOutputStream();
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append(PROP_STATUS).append('=').append(STATUS_CLOCKSKEW).append(skew).append('\n'); buf.append(PROP_STATUS).append('=').append(STATUS_CLOCKSKEW).append(skew).append('\n');
out.write(buf.toString().getBytes()); out.write(buf.toString().getBytes());
out.flush(); out.flush();
} }
public void init(ServletConfig config) throws ServletException { public void init(ServletConfig config) throws ServletException {
super.init(config); super.init(config);
String checkPath = config.getInitParameter(PARAM_CHECK_PATH); String checkPath = config.getInitParameter(PARAM_CHECK_PATH);
if (checkPath == null) if (checkPath == null)
throw new ServletException("Check status path for the sending servlet required [" + PARAM_CHECK_PATH + "]"); throw new ServletException("Check status path for the sending servlet required [" + PARAM_CHECK_PATH + "]");
else else
_checkPath = checkPath; _checkPath = checkPath;
String maxMessagesPerIdentStr = config.getInitParameter(PARAM_MAX_MESSAGES_PER_IDENT); String maxMessagesPerIdentStr = config.getInitParameter(PARAM_MAX_MESSAGES_PER_IDENT);
if (maxMessagesPerIdentStr == null) if (maxMessagesPerIdentStr == null)
throw new ServletException("Max messages per ident for the sending servlet required [" + PARAM_MAX_MESSAGES_PER_IDENT + "]"); throw new ServletException("Max messages per ident for the sending servlet required ["
try { + PARAM_MAX_MESSAGES_PER_IDENT + "]");
_maxMessagesPerIdent = Integer.parseInt(maxMessagesPerIdentStr); try {
} catch (Throwable t) { _maxMessagesPerIdent = Integer.parseInt(maxMessagesPerIdentStr);
throw new ServletException("Valid max messages per ident for the sending servlet required [" + PARAM_MAX_MESSAGES_PER_IDENT + "]"); } catch (Throwable t) {
} throw new ServletException("Valid max messages per ident for the sending servlet required ["
+ PARAM_MAX_MESSAGES_PER_IDENT + "]");
}
} }
public static void main(String args[]) { public static void main(String args[]) {
String line = "target=pp0ARjQiB~IKC-0FsMUsPEMrwR3gxVBZGRYfEr1IzHI=&timeoutMs=52068&dataLength=2691&"; String line = "target=pp0ARjQiB~IKC-0FsMUsPEMrwR3gxVBZGRYfEr1IzHI=&timeoutMs=52068&dataLength=2691&";
Map props = getParameters(line); Map props = getParameters(line);
for (java.util.Iterator iter = props.keySet().iterator(); iter.hasNext(); ) { for (java.util.Iterator iter = props.keySet().iterator(); iter.hasNext();) {
String key = (String)iter.next(); String key = (String) iter.next();
String val = (String)props.get(key); String val = (String) props.get(key);
System.out.println("[" + key + "] = [" + val + "]"); System.out.println("[" + key + "] = [" + val + "]");
} }
} }
} }

View File

@ -8,87 +8,81 @@ public class BasicEchoTestAnalyzer implements EchoTestAnalyzer {
* printed. Default is every 20 events. * printed. Default is every 20 events.
*/ */
private static int REPORT_DELAY = 20; private static int REPORT_DELAY = 20;
private static int SUMMARY_SIZE = 100; private static int SUMMARY_SIZE = 100;
public BasicEchoTestAnalyzer() { public BasicEchoTestAnalyzer() {
this(20, 100); this(20, 100);
} }
public BasicEchoTestAnalyzer(int reportDelay, int summarySize) { public BasicEchoTestAnalyzer(int reportDelay, int summarySize) {
REPORT_DELAY = reportDelay; REPORT_DELAY = reportDelay;
SUMMARY_SIZE = summarySize; SUMMARY_SIZE = summarySize;
} }
private int events = 0, private int events = 0, packetLosses = 0, packetLossesDisconnect = 0, disconnects = 0, disconnectsRefused = 0,
packetLosses = 0, delayCount = 0, lastDelayPtr = 0;
packetLossesDisconnect=0, private long minDelay = Long.MAX_VALUE, maxDelay = 0, delaySum = 0;
disconnects = 0,
disconnectsRefused = 0,
delayCount=0,
lastDelayPtr = 0;
private long minDelay=Long.MAX_VALUE, maxDelay = 0, delaySum=0;
private long[] lastDelays = new long[SUMMARY_SIZE]; private long[] lastDelays = new long[SUMMARY_SIZE];
public synchronized void packetLossOccurred(boolean beforeDisconnect) { public synchronized void packetLossOccurred(boolean beforeDisconnect) {
System.out.println("1: Packet lost"+ System.out.println("1: Packet lost" + (beforeDisconnect ? " before disconnect" : "") + ".");
(beforeDisconnect?" before disconnect":"")+ packetLosses++;
"."); if (beforeDisconnect) packetLossesDisconnect++;
packetLosses++; countEvent();
if (beforeDisconnect) packetLossesDisconnect++;
countEvent();
} }
public synchronized void successOccurred(long delay) { public synchronized void successOccurred(long delay) {
System.out.println("0: Delay = "+delay); System.out.println("0: Delay = " + delay);
if (delay > maxDelay) maxDelay=delay; if (delay > maxDelay) maxDelay = delay;
if (delay < minDelay) minDelay=delay; if (delay < minDelay) minDelay = delay;
delaySum+=delay; delaySum += delay;
delayCount++; delayCount++;
lastDelays[lastDelayPtr++]=delay; lastDelays[lastDelayPtr++] = delay;
lastDelayPtr%=SUMMARY_SIZE; lastDelayPtr %= SUMMARY_SIZE;
countEvent(); countEvent();
} }
public synchronized void disconnected(boolean refused) { public synchronized void disconnected(boolean refused) {
System.out.println("2: Disconnected"+ System.out.println("2: Disconnected" + (refused ? " (connection refused)" : "") + ".");
(refused?" (connection refused)":"")+ disconnects++;
"."); if (refused) disconnectsRefused++;
disconnects++; countEvent();
if (refused) disconnectsRefused++;
countEvent();
} }
private void countEvent() { private void countEvent() {
events++; events++;
if (events % REPORT_DELAY == 0) { if (events % REPORT_DELAY == 0) {
int packets = packetLosses+delayCount; int packets = packetLosses + delayCount;
long delaySummary=0; long delaySummary = 0;
for (int i=0;i<SUMMARY_SIZE;i++) { for (int i = 0; i < SUMMARY_SIZE; i++) {
delaySummary+=lastDelays[i]; delaySummary += lastDelays[i];
} }
System.out.println System.out.println("++++++++++++++++ ECHO STATISTICS +++++++++++++++++++++++++"
("++++++++++++++++ ECHO STATISTICS +++++++++++++++++++++++++"+ + "\n++ Number of total echo messages: "
"\n++ Number of total echo messages: "+packets+ + packets
"\n++ No response for "+packetLosses+ + "\n++ No response for "
"\n++ (of which "+ packetLossesDisconnect+ + packetLosses
" due to a disconnect)"+ + "\n++ (of which "
"\n++ Disconnects: "+disconnects+ + packetLossesDisconnect
"\n++ (of which "+disconnectsRefused+ + " due to a disconnect)"
" due to 'connection refused')"+ + "\n++ Disconnects: "
(disconnects>0 || true + disconnects
?"\n++ Average lost packets per disconnect: "+ + "\n++ (of which "
(packetLossesDisconnect/(float)disconnects) + disconnectsRefused
:"")+ + " due to 'connection refused')"
"\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++"+ + (disconnects > 0 || true ? "\n++ Average lost packets per disconnect: "
"\n++ Minimal delay: "+minDelay+ + (packetLossesDisconnect / (float) disconnects) : "")
"\n++ Average delay: "+(delaySum/(float)delayCount)+ + "\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
"\n++ Maximal delay: "+maxDelay+ + "\n++ Minimal delay: "
(delayCount >=SUMMARY_SIZE + minDelay
?"\n++ Average delay over last " + SUMMARY_SIZE + ": "+(delaySummary/(float)SUMMARY_SIZE) + "\n++ Average delay: "
:"")+ + (delaySum / (float) delayCount)
"\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); + "\n++ Maximal delay: "
} + maxDelay
+ (delayCount >= SUMMARY_SIZE ? "\n++ Average delay over last " + SUMMARY_SIZE + ": "
+ (delaySummary / (float) SUMMARY_SIZE) : "")
+ "\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
}
} }
} }

View File

@ -15,5 +15,3 @@ public interface EchoTestAnalyzer {
} }

View File

@ -26,152 +26,142 @@ public class EchoTester extends Thread {
/** /**
* How long to wait between packets. Default is 6 seconds. * How long to wait between packets. Default is 6 seconds.
*/ */
private static long PACKET_DELAY= 6000; private static long PACKET_DELAY = 6000;
/** /**
* How many packets may be on the way before the connection is * How many packets may be on the way before the connection is
* seen as "broken" and disconnected. * seen as "broken" and disconnected.
*/ */
private static final long MAX_PACKETS_QUEUED=50; // unused private static final long MAX_PACKETS_QUEUED = 50; // unused
private EchoTestAnalyzer eta; private EchoTestAnalyzer eta;
private String host; private String host;
private int port; private int port;
// the following vars are synchronized via the lock. // the following vars are synchronized via the lock.
private Object lock = new Object(); private Object lock = new Object();
private long nextPacket=0; private long nextPacket = 0;
private long nextUnreceived=0; private long nextUnreceived = 0;
private boolean readerRunning=false; private boolean readerRunning = false;
public static void main(String[] args) { public static void main(String[] args) {
if (args.length == 3) if (args.length == 3) PACKET_DELAY = Long.parseLong(args[2]);
PACKET_DELAY = Long.parseLong(args[2]); new EchoTester(args[0], Integer.parseInt(args[1]), new BasicEchoTestAnalyzer());
new EchoTester(args[0], Integer.parseInt(args[1]),
new BasicEchoTestAnalyzer());
} }
public EchoTester(String host, int port, EchoTestAnalyzer eta) { public EchoTester(String host, int port, EchoTestAnalyzer eta) {
this.eta=eta; this.eta = eta;
this.host=host; this.host = host;
this.port=port; this.port = port;
start(); start();
} }
public void run() { public void run() {
try { try {
while (true) { while (true) {
Socket s; Socket s;
try { try {
s = new Socket(host, port); s = new Socket(host, port);
} catch (ConnectException ex) { } catch (ConnectException ex) {
eta.disconnected(true); eta.disconnected(true);
Thread.sleep(PACKET_DELAY); Thread.sleep(PACKET_DELAY);
continue; continue;
} }
System.out.println("41: Connected to "+host+":"+port); System.out.println("41: Connected to " + host + ":" + port);
synchronized(lock) { synchronized (lock) {
nextUnreceived=nextPacket; nextUnreceived = nextPacket;
} }
Thread t = new ResponseReaderThread(s); Thread t = new ResponseReaderThread(s);
Writer w = new BufferedWriter(new OutputStreamWriter Writer w = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
(s.getOutputStream())); while (true) {
while (true) { long no;
long no; synchronized (lock) {
synchronized(lock) { no = nextPacket++;
no = nextPacket++; }
} try {
try { w.write(no + " " + System.currentTimeMillis() + "\n");
w.write(no+" "+System.currentTimeMillis()+"\n"); w.flush();
w.flush(); } catch (SocketException ex) {
} catch (SocketException ex) { break;
break; }
} Thread.sleep(PACKET_DELAY);
Thread.sleep(PACKET_DELAY); }
} s.close();
s.close(); t.join();
t.join(); synchronized (lock) {
synchronized(lock) { if (readerRunning) {
if (readerRunning) { System.out.println("*** WHY IS THIS THREAD STILL" + " RUNNING?");
System.out.println("*** WHY IS THIS THREAD STILL"+ }
" RUNNING?"); while (nextUnreceived < nextPacket) {
} nextUnreceived++;
while (nextUnreceived < nextPacket) { eta.packetLossOccurred(true);
nextUnreceived++; }
eta.packetLossOccurred(true); if (nextUnreceived > nextPacket) {
} System.out.println("*** WTF? " + nextUnreceived + " > " + nextPacket);
if (nextUnreceived > nextPacket) { }
System.out.println("*** WTF? "+nextUnreceived+" > "+ }
nextPacket); eta.disconnected(false);
} }
} } catch (InterruptedException ex) {
eta.disconnected(false); ex.printStackTrace();
} System.exit(1); // treat these errors as fatal
} catch (InterruptedException ex) { } catch (IOException ex) {
ex.printStackTrace(); ex.printStackTrace();
System.exit(1); // treat these errors as fatal System.exit(1); // treat these errors as fatal
} catch (IOException ex) { }
ex.printStackTrace();
System.exit(1); // treat these errors as fatal
}
} }
private class ResponseReaderThread extends Thread { private class ResponseReaderThread extends Thread {
private Socket s; private Socket s;
public ResponseReaderThread(Socket s) { public ResponseReaderThread(Socket s) {
this.s=s; this.s = s;
synchronized(lock) { synchronized (lock) {
readerRunning=true; readerRunning = true;
} }
start(); start();
} }
public void run() { public void run() {
try { try {
BufferedReader br = new BufferedReader(new InputStreamReader BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
(s.getInputStream())); String line;
String line; int index;
int index; while ((line = br.readLine()) != null) {
while ((line=br.readLine()) != null) { if ((index = line.indexOf(" ")) == -1) continue;
if ((index=line.indexOf(" ")) == -1) long now, packetNumber, packetTime;
continue; now = System.currentTimeMillis();
long now, packetNumber, packetTime; try {
now = System.currentTimeMillis(); packetNumber = Long.parseLong(line.substring(0, index));
try { packetTime = Long.parseLong(line.substring(index + 1));
packetNumber = Long.parseLong } catch (NumberFormatException ex) {
(line.substring(0,index)); System.out.println(ex.toString());
packetTime = Long.parseLong continue;
(line.substring(index+1)); }
} catch (NumberFormatException ex) { synchronized (lock) {
System.out.println(ex.toString()); while (packetNumber > nextUnreceived) {
continue; nextUnreceived++;
} eta.packetLossOccurred(false);
synchronized (lock) { }
while (packetNumber > nextUnreceived) { if (nextUnreceived > packetNumber) {
nextUnreceived++; System.out.println("*** DOUBLE PACKET!");
eta.packetLossOccurred(false); } else {
} nextUnreceived++;
if (nextUnreceived > packetNumber) { }
System.out.println("*** DOUBLE PACKET!"); }
} else { eta.successOccurred(now - packetTime);
nextUnreceived++; }
} } catch (SocketException ex) {
} // ignore
eta.successOccurred(now-packetTime); } catch (IOException ex) {
} ex.printStackTrace();
} catch (SocketException ex) { System.exit(0);
// ignore }
} catch (IOException ex) { synchronized (lock) {
ex.printStackTrace(); readerRunning = false;
System.exit(0); }
} }
synchronized(lock) {
readerRunning=false;
}
}
} }
} }