NEW BOB, a replacement for SAM, added It does have a different API. See it's java-doc.

This commit is contained in:
sponge
2008-10-07 18:30:07 +00:00
parent f1c4a85991
commit bb32672c11
29 changed files with 3463 additions and 1 deletions

View File

@ -0,0 +1,157 @@
/**
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) sponge
* Planet Earth
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*
* See...
*
* http://sam.zoy.org/wtfpl/
* and
* http://en.wikipedia.org/wiki/WTFPL
*
* ...for any additional details and liscense questions.
*/
package net.i2p.BOB;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
import net.i2p.client.I2PClient;
import net.i2p.client.streaming.RetransmissionTimer;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer;
/**
*
* BOB, main command socket listener, launches the command parser engine.
*
* @author sponge
*/
public class BOB {
private final static Log _log = new Log(BOB.class);
public final static String PROP_CONFIG_LOCATION = "BOB.config";
public final static String PROP_BOB_PORT = "BOB.port";
public final static String PROP_BOB_HOST = "BOB.host";
private static int maxConnections = 0;
private static nickname database;
/**
* Log a warning
*
* @param arg
*/
public static void warn(String arg) {
System.out.println(arg);
_log.warn(arg);
}
/**
* Log an error
*
* @param arg
*/
public static void error(String arg) {
System.out.println(arg);
_log.error(arg);
}
/**
* Listen for incoming connections and handle them
*
* @param args
*/
public static void main(String[] args) {
database = new nickname();
int i = 0;
boolean save = false;
// Set up all defaults to be passed forward to other threads.
// Re-reading the config file in each thread is pretty damn stupid.
// I2PClient client = I2PClientFactory.createClient();
Properties props = new Properties();
String configLocation = System.getProperty(PROP_CONFIG_LOCATION, "bob.config");
// This is here just to ensure there is no interference with our threadgroups.
SimpleTimer Y = RetransmissionTimer.getInstance();
i = Y.hashCode();
try {
props.load(new FileInputStream(configLocation));
} catch(FileNotFoundException fnfe) {
warn("Unable to load up the BOB config file " + configLocation + ", Using defaults.");
warn(fnfe.toString());
save = true;
} catch(IOException ioe) {
warn("IOException on BOB config file " + configLocation + ", using defaults.");
warn(ioe.toString());
}
// Global router and client API configurations that are missing are set to defaults here.
if(!props.containsKey(I2PClient.PROP_TCP_HOST)) {
props.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
}
if(!props.containsKey(I2PClient.PROP_TCP_PORT)) {
props.setProperty(I2PClient.PROP_TCP_PORT, "7654");
}
if(!props.containsKey(I2PClient.PROP_RELIABILITY)) {
props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
}
if(!props.containsKey(PROP_BOB_PORT)) {
props.setProperty(PROP_BOB_PORT, "2827"); // 0xB0B
}
if(!props.containsKey("inbound.length")) {
props.setProperty("inbound.length", "1");
}
if(!props.containsKey("outbound.length")) {
props.setProperty("outbound.length", "1");
}
if(!props.containsKey("inbound.lengthVariance")) {
props.setProperty("inbound.lengthVariance", "0");
}
if(!props.containsKey("outbound.lengthVariance")) {
props.setProperty("outbound.lengthVariance", "0");
}
if(!props.containsKey(PROP_BOB_HOST)) {
props.setProperty(PROP_BOB_HOST, "localhost");
}
if(save) {
try {
warn("Writing new defaults file " + configLocation);
props.store(new FileOutputStream(configLocation), configLocation);
} catch(IOException ioe) {
warn("IOException on BOB config file " + configLocation + ", " + ioe);
}
}
try {
ServerSocket listener = new ServerSocket(Integer.parseInt(props.getProperty(PROP_BOB_PORT)), 10, InetAddress.getByName(props.getProperty(PROP_BOB_HOST)));
Socket server;
while((i++ < maxConnections) || (maxConnections == 0)) {
//doCMDS connection;
server = listener.accept();
doCMDS conn_c = new doCMDS(server, props, database, _log);
Thread t = new Thread(conn_c);
t.start();
}
} catch(IOException ioe) {
warn("IOException on socket listen: " + ioe);
ioe.printStackTrace();
}
}
}

View File

@ -0,0 +1,21 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) sponge
Planet Earth
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
See...
http://sam.zoy.org/wtfpl/
and
http://en.wikipedia.org/wiki/WTFPL
...for any additional details and license questions.

View File

@ -0,0 +1,128 @@
/**
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) sponge
* Planet Earth
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*
* See...
*
* http://sam.zoy.org/wtfpl/
* and
* http://en.wikipedia.org/wiki/WTFPL
*
* ...for any additional details and liscense questions.
*/
package net.i2p.BOB;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import net.i2p.I2PException;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.util.Log;
/**
* Listen on I2P and connect to TCP
*
* @author sponge
*/
public class I2Plistener implements Runnable {
private nickname info;
private Log _log;
private int tgwatch;
public I2PSocketManager socketManager;
public I2PServerSocket serverSocket;
/**
* Constructor
* @param S
* @param info
* @param _log
*/
I2Plistener(I2PSocketManager S, nickname info, Log _log) {
this.info = info;
this._log = _log;
this.socketManager = S;
serverSocket = socketManager.getServerSocket();
tgwatch = 1;
}
/**
* Simply listen on I2P port, and thread connections
*
* @throws RuntimeException
*/
public void run() throws RuntimeException {
boolean g = false;
I2PSocket sessSocket = null;
// needed to hack in this method :-/
serverSocket.setSoTimeout(1000);
if(info.exists("INPORT")) {
tgwatch = 2;
}
while(info.get("RUNNING").equals(true)) {
try {
try {
sessSocket = serverSocket.accept();
g = true;
} catch(ConnectException ce) {
g = false;
} catch (SocketTimeoutException ste) {
g = false;
}
if(g) {
g = false;
// toss the connection to a new thread.
I2PtoTCP conn_c = new I2PtoTCP(sessSocket, info);
Thread t = new Thread(conn_c, "BOBI2PtoTCP");
t.start();
}
} catch(I2PException e) {
System.out.println("Exception "+e);
}
}
try {
serverSocket.close();
} catch(I2PException e) {
// nop
}
while(Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish
// System.out.println("STOP Thread count " + Thread.activeCount());
try {
Thread.sleep(1000); //sleep for 1000 ms (One second)
} catch(Exception e) {
// nop
}
}
// System.out.println("STOP Thread count " + Thread.activeCount());
// need to kill off the socket manager too.
I2PSession session = socketManager.getSession();
if(session != null) {
try {
session.destroySession();
} catch(I2PSessionException ex) {
// nop
}
// System.out.println("destroySession Thread count " + Thread.activeCount());
}
}
}

View File

@ -0,0 +1,106 @@
/**
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) sponge
* Planet Earth
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*
* See...
*
* http://sam.zoy.org/wtfpl/
* and
* http://en.wikipedia.org/wiki/WTFPL
*
* ...for any additional details and liscense questions.
*/
package net.i2p.BOB;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import net.i2p.client.streaming.I2PSocket;
/**
* Process I2P->TCP
*
* @author sponge
*/
public class I2PtoTCP implements Runnable {
private I2PSocket I2P;
private nickname info;
private Socket sock;
/**
* Constructor
*
* @param I2Psock
* @param db
*/
I2PtoTCP(I2PSocket I2Psock, nickname db) {
this.I2P = I2Psock;
this.info = db;
}
/**
* I2P stream to TCP stream thread starter
*
*/
public void run() {
try {
sock = new Socket(info.get("OUTHOST").toString(), Integer.parseInt(info.get("OUTPORT").toString()));
// make readers/writers
InputStream in = sock.getInputStream();
OutputStream out = sock.getOutputStream();
InputStream Iin = I2P.getInputStream();
OutputStream Iout = I2P.getOutputStream();
I2P.setReadTimeout(0); // temp bugfix, this *SHOULD* be the default
if(info.get("QUIET").equals(false)) {
// tell who is connecting
out.write(I2P.getPeerDestination().toBase64().getBytes());
out.write(10); // nl
out.flush(); // not really needed, but...
}
// setup to cross the streams
TCPio conn_c = new TCPio(in, Iout, info); // app -> I2P
TCPio conn_a = new TCPio(Iin, out, info); // I2P -> app
Thread t = new Thread(conn_c, "TCPioA");
Thread q = new Thread(conn_a, "TCPioB");
// Fire!
t.start();
q.start();
while(t.isAlive() && q.isAlive()) { // AND is used here to kill off the other thread
try {
Thread.sleep(10); //sleep for 10 ms
} catch(InterruptedException e) {
// nop
}
}
} catch(UnknownHostException ex) {
// OOPS!
} catch(IOException ex) {
// OOPS!
}
try {
I2P.close();
} catch(IOException ex) {
}
try {
sock.close();
} catch(IOException ex) {
}
}
}

View File

@ -0,0 +1,118 @@
/**
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) sponge
* Planet Earth
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*
* See...
*
* http://sam.zoy.org/wtfpl/
* and
* http://en.wikipedia.org/wiki/WTFPL
*
* ...for any additional details and liscense questions.
*/
package net.i2p.BOB;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Properties;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.util.Log;
/**
*
* Multiplex listeners for TCP and I2P
*
* @author sponge
*/
public class MUXlisten implements Runnable {
private nickname info;
private Log _log;
private I2PSocketManager socketManager;
private ByteArrayInputStream prikey;
private ThreadGroup tg;
private String N;
/**
* Constructor
*
* @param info
* @param _log
* @throws net.i2p.I2PException
* @throws java.io.IOException
*/
MUXlisten(nickname info, Log _log) throws I2PException, IOException {
this.info = info;
this._log = _log;
this.info.add("STARTING", true);
N = this.info.get("NICKNAME").toString();
prikey = new ByteArrayInputStream((byte[])info.get("KEYS"));
socketManager = I2PSocketManagerFactory.createManager(prikey, (Properties)info.get("PROPERTIES"));
}
/**
* MUX sockets, fire off a thread to connect, get destination info, and do I/O
*
*/
public void run() {
tg = new ThreadGroup(N);
info.add("RUNNING", true);
info.add("STARTING", false);
// toss the connections to a new threads.
// will wrap with TCP and UDP when UDP works
if(info.exists("OUTPORT")) {
// I2P -> TCP
I2Plistener conn = new I2Plistener(socketManager, info, _log);
Thread t = new Thread(tg, conn, "BOBI2Plistener " + N);
t.start();
}
if(info.exists("INPORT")) {
// TCP -> I2P
TCPlistener conn = new TCPlistener(socketManager, info, _log);
Thread q = new Thread(tg, conn,"BOBTCPlistener" + N);
q.start();
}
while(info.get("STOPPING").equals(false)) {
try {
Thread.sleep(1000); //sleep for 1000 ms (One second)
} catch(InterruptedException e) {
// nop
}
}
info.add("RUNNING", false);
// wait for child threads and thread groups to die
while (tg.activeCount() + tg.activeGroupCount() != 0) {
try {
Thread.sleep(1000); //sleep for 1000 ms (One second)
} catch(InterruptedException ex) {
// nop
}
}
socketManager.destroySocketManager();
tg.destroy();
// Zap reference to the ThreadGroup so the JVM can GC it.
tg = null;
info.add("STOPPING", false);
info.add("STARTING", false);
}
}

View File

@ -0,0 +1,48 @@
/**
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) sponge
* Planet Earth
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*
* See...
*
* http://sam.zoy.org/wtfpl/
* and
* http://en.wikipedia.org/wiki/WTFPL
*
* ...for any additional details and liscense questions.
*/
package net.i2p.BOB;
import net.i2p.client.streaming.RetransmissionTimer;
import net.i2p.util.SimpleTimer;
/**
* Start from command line
*
* @author sponge
*
*/
public class Main {
/**
* @param args the command line arguments, these are not used yet
*/
public static void main(String[] args) {
// THINK THINK THINK THINK THINK THINK
SimpleTimer Y = RetransmissionTimer.getInstance();
BOB.main(args);
Y.removeSimpleTimer();
}
}

View File

@ -0,0 +1,93 @@
/**
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) sponge
* Planet Earth
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*
* See...
*
* http://sam.zoy.org/wtfpl/
* and
* http://en.wikipedia.org/wiki/WTFPL
*
* ...for any additional details and liscense questions.
*/
package net.i2p.BOB;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Shove data from one stream to the other.
*
* @author sponge
*/
public class TCPio implements Runnable {
private InputStream Ain;
private OutputStream Aout;
private nickname info;
/**
* Constructor
*
* @param Ain
* @param Aout
* @param db
*/
TCPio(InputStream Ain, OutputStream Aout, nickname db) {
this.Ain = Ain;
this.Aout = Aout;
this.info = db;
}
/**
* kill off the streams, to hopefully cause an IOException in the thread in order to kill it.
*/
/**
* Copy from source to destination...
* and yes, we are totally OK to block here on writes,
* The OS has buffers, and I intend to use them.
*
*/
public void run() {
int b;
byte a[] = new byte[1];
try {
while(info.get("RUNNING").equals(true)) {
b = Ain.read(a, 0, 1);
// System.out.println(info.get("NICKNAME").toString() + " " + b);
if(b > 0) {
Aout.write(a,0,1);
// Aout.flush(); too slow!
} else if(b == 0) {
try {
// Thread.yield();
Thread.sleep(10);
} catch(InterruptedException ex) {
}
} else {
/* according to the specs:
*
* The total number of bytes read into the buffer,
* or -1 is there is no more data because the end of
* the stream has been reached.
*
*/
return;
}
}
} catch(IOException e) {
}
}
}

View File

@ -0,0 +1,123 @@
/**
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) sponge
* Planet Earth
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*
* See...
*
* http://sam.zoy.org/wtfpl/
* and
* http://en.wikipedia.org/wiki/WTFPL
*
* ...for any additional details and liscense questions.
*/
package net.i2p.BOB;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.util.Log;
/**
* Listen on TCP port and connect to I2P
*
* @author sponge
*/
public class TCPlistener implements Runnable {
private nickname info;
private Log _log;
private int tgwatch;
public I2PSocketManager socketManager;
public I2PServerSocket serverSocket;
private int backlog = 50; // should this be more? less?
/**
* Constructor
* @param S
* @param info
* @param _log
*/
TCPlistener(I2PSocketManager S, nickname info, Log _log) {
this.info = info;
this._log = _log;
this.socketManager = S;
tgwatch = 1;
}
/**
* Simply listen on TCP port, and thread connections
* @throws java.lang.RuntimeException
*/
public void run() throws RuntimeException {
boolean g = false;
if(info.exists("OUTPORT")) {
tgwatch = 2;
}
try {
// System.out.println("Starting thread count " + Thread.activeCount());
ServerSocket listener = new ServerSocket(Integer.parseInt(info.get("INPORT").toString()), backlog, InetAddress.getByName(info.get("INHOST").toString()));
Socket server = new Socket();
listener.setSoTimeout(1000);
while(info.get("RUNNING").equals(true)) {
// System.out.println("Thread count " + Thread.activeCount());
try {
server = listener.accept();
g = true;
} catch(SocketTimeoutException ste) {
g = false;
}
if(g) {
// toss the connection to a new thread.
TCPtoI2P conn_c = new TCPtoI2P(socketManager, server, info);
Thread t = new Thread(conn_c, "BOBTCPtoI2P");
t.start();
g = false;
}
}
listener.close();
} catch(IOException ioe) {
// throw new RuntimeException(ioe);
}
//System.out.println("STOP!");
while(Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish
// System.out.println("STOP Thread count " + Thread.activeCount());
try {
Thread.sleep(1000); //sleep for 1000 ms (One second)
} catch(Exception e) {
// nop
}
}
// System.out.println("STOP Thread count " + Thread.activeCount());
// need to kill off the socket manager too.
I2PSession session = socketManager.getSession();
if(session != null) {
try {
session.destroySession();
} catch(I2PSessionException ex) {
// nop
}
// System.out.println("destroySession Thread count " + Thread.activeCount());
}
}
}

View File

@ -0,0 +1,186 @@
/**
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) sponge
* Planet Earth
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*
* See...
*
* http://sam.zoy.org/wtfpl/
* and
* http://en.wikipedia.org/wiki/WTFPL
*
* ...for any additional details and liscense questions.
*/
package net.i2p.BOB;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.net.Socket;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.I2PTunnel;
/**
*
* Process TCP->I2P
*
* @author sponge
*/
public class TCPtoI2P implements Runnable {
private I2PSocket I2P;
private nickname info;
private Socket sock;
private I2PSocketManager socketManager;
/**
* This is a more forgiving readline,
* it works on unbuffered streams
*
* @param in
* @return line of text as a String
* @throws java.io.IOException
*/
public static String Lread(InputStream in) throws IOException {
String S;
int b;
char c;
S = new String();
while(true) {
b = in.read();
if(b == 13) {
//skip CR
continue;
}
if(b < 20 || b > 126) {
// exit on anything not legal
break;
}
c = (char)(b & 0x7f); // We only really give a fuck about ASCII
S = new String(S + c);
}
return S;
}
/**
* Constructor
* @param i2p
* @param socket
* @param db
*/
TCPtoI2P(I2PSocketManager i2p, Socket socket, nickname db) {
this.sock = socket;
this.info = db;
this.socketManager = i2p;
}
/**
* Print an error message to out
*
* @param e
* @param out
* @throws java.io.IOException
*/
private void Emsg(String e, OutputStream out) throws IOException {
System.out.println("ERROR TCPtoI2P: " + e);
out.write("ERROR".concat(e).getBytes());
out.write(13); // cr
out.flush();
sock.close();
}
/**
* TCP stream to I2P stream thread starter
*/
public void run() {
String line, input;
try {
InputStream in = sock.getInputStream();
OutputStream out = sock.getOutputStream();
try {
line = Lread(in);
input = line.toLowerCase();
Destination dest = null;
if(input.endsWith(".i2p")) {
dest = I2PTunnel.destFromName(input);
line = dest.toBase64();
}
dest = new Destination();
dest.fromBase64(line);
try {
// get a client socket
I2P = socketManager.connect(dest);
I2P.setReadTimeout(0); // temp bugfix, this *SHOULD* be the default
// make readers/writers
InputStream Iin = I2P.getInputStream();
OutputStream Iout = I2P.getOutputStream();
// setup to cross the streams
TCPio conn_c = new TCPio(in, Iout, info); // app -> I2P
TCPio conn_a = new TCPio(Iin, out, info); // I2P -> app
Thread t = new Thread(conn_c, "TCPioA");
Thread q = new Thread(conn_a, "TCPioB");
// Fire!
t.start();
q.start();
while(t.isAlive() && q.isAlive()) { // AND is used here to kill off the other thread
try {
Thread.sleep(10); //sleep for 10 ms
} catch(InterruptedException e) {
// nop
}
}
} catch(I2PException e) {
Emsg("ERROR " + e.toString(), out);
} catch(ConnectException e) {
Emsg("ERROR " + e.toString(), out);
} catch(NoRouteToHostException e) {
Emsg("ERROR " + e.toString(), out);
} catch(InterruptedIOException e) {
Emsg("ERROR " + e.toString(), out);
}
} catch(DataFormatException e) {
Emsg("ERROR " + e.toString(), out);
} catch(NullPointerException e) {
Emsg("ERROR " + e.toString(), out);
}
} catch(IOException ioe) {
}
try {
I2P.close();
} catch(IOException ex) {
} catch(NullPointerException e) {
}
try {
sock.close();
} catch(IOException ex) {
} catch(NullPointerException e) {
}
}
}

View File

@ -0,0 +1,144 @@
/**
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) sponge
* Planet Earth
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*
* See...
*
* http://sam.zoy.org/wtfpl/
* and
* http://en.wikipedia.org/wiki/WTFPL
*
* ...for any additional details and liscense questions.
*/
package net.i2p.BOB;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionListener;
import net.i2p.data.Destination;
import net.i2p.util.Log;
/**
* UDP IO on I2P
*
* FIX ME: Untested, and incomplete!
* I have no personal need to UDP yet,
* however alot of p2p apps pretty much demand it.
* The skeletal frame is here, just needs to be finished.
*
* @author sponge
*/
public class UDPIOthread implements I2PSessionListener, Runnable {
private nickname info;
private Log _log;
private Socket socket;
private DataInputStream in;
private DataOutputStream out;
private I2PSession _session;
private Destination _peerDestination;
private boolean up;
/**
* Constructor
* @param info
* @param _log
* @param socket
* @param _session
*/ UDPIOthread(nickname info, Log _log, Socket socket, I2PSession _session) {
this.info = info;
this._log = _log;
this.socket = socket;
this._session = _session;
}
/**
*
*/
public void run() {
byte data[] = new byte[1024];
up = true;
try {
in = new DataInputStream(socket.getInputStream());
out = new DataOutputStream(socket.getOutputStream());
while(up) {
int c = in.read(data);
// Note: could do a loopback test here with a wrapper.
boolean ok = _session.sendMessage(_peerDestination, data, 0, c);
if(!ok) {
up = false; // Is this the right thing to do??
}
}
} catch(IOException ioe) {
_log.error("Error running", ioe);
} catch(I2PSessionException ise) {
_log.error("Error communicating", ise);
// } catch(DataFormatException dfe) {
// _log.error("Peer destination file is not valid", dfe);
} finally {
if(_session != null) {
try {
_session.destroySession();
} catch(I2PSessionException ise) {
// ignored
}
}
}
}
/**
*
* @param session
* @param msgId
* @param size
*/
public void messageAvailable(I2PSession session, int msgId, long size) {
// _log.debug("Message available: id = " + msgId + " size = " + size);
try {
byte msg[] = session.receiveMessage(msgId);
out.write(msg);
out.flush();
} catch(I2PSessionException ise) {
up = false;
} catch(IOException ioe) {
up = false;
}
}
// Great, can these be used to kill ourselves.
/** required by {@link I2PSessionListener I2PSessionListener} to notify of disconnect */
public void disconnected(I2PSession session) {
_log.debug("Disconnected");
// up = false;
}
/** required by {@link I2PSessionListener I2PSessionListener} to notify of error */
public void errorOccurred(I2PSession session, String message, Throwable error) {
_log.debug("Error occurred: " + message, error);
// up = false;
}
/** required by {@link I2PSessionListener I2PSessionListener} to notify of abuse */
public void reportAbuse(I2PSession session, int severity) {
_log.debug("Abuse reported of severity " + severity);
// up = false;
}
}

View File

@ -0,0 +1,568 @@
/**
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) sponge
* Planet Earth
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*
* See...
*
* http://sam.zoy.org/wtfpl/
* and
* http://en.wikipedia.org/wiki/WTFPL
*
* ...for any additional details and liscense questions.
*/
package net.i2p.BOB;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PException;
import net.i2p.client.I2PClientFactory;
import net.i2p.data.Destination;
import net.i2p.util.Log;
/**
* Simplistic command parser for BOB
*
* @author sponge
*
*/
public class doCMDS implements Runnable {
// FIX ME
// I need a better way to do versioning, but this will do for now.
public static final String BMAJ = "00", BMIN = "00", BREV = "01", BEXT = "-6";
public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT;
private Socket server;
private Properties props;
private nickname database;
private String line;
private Destination d;
private ByteArrayOutputStream prikey;
private boolean dk, ns, ip, op;
private nickname nickinfo;
private Log _log;
/* database strings */
private static final String P_INHOST = "INHOST";
private static final String P_INPORT = "INPORT";
private static final String P_KEYS = "KEYS";
private static final String P_NICKNAME = "NICKNAME";
private static final String P_OUTHOST = "OUTHOST";
private static final String P_OUTPORT = "OUTPORT";
private static final String P_PROPERTIES = "PROPERTIES";
private static final String P_QUIET = "QUIET";
private static final String P_RUNNING = "RUNNING";
private static final String P_STARTING = "STARTING";
private static final String P_STOPPING = "STOPPING";
/* command strings */
private static final String C_help = "help";
private static final String C_clear = "clear";
private static final String C_getkeys = "getkeys";
private static final String C_getnick = "getnick";
private static final String C_inhost = "inhost";
private static final String C_inport = "inport";
private static final String C_list = "list";
private static final String C_newkeys = "newkeys";
private static final String C_outhost = "outhost";
private static final String C_outport = "outport";
private static final String C_quiet = "quiet";
private static final String C_quit = "quit";
private static final String C_setkeys = "setkeys";
private static final String C_setnick = "setnick";
private static final String C_show = "show";
private static final String C_start = "start";
private static final String C_status = "status";
private static final String C_stop = "stop";
/* all the coomands available, plus description */
private static final String C_ALL[][] = {
{C_help, C_help + " <command> * Get help on a command."},
{C_clear, C_clear + " * Clear the current nickname out of the list."},
{C_getkeys, C_getkeys + " * Return the keypair for the current nickname."},
{C_getnick, C_getnick + " tunnelname * Set the nickname from the database."},
{C_inhost, C_inhost + " hostname | IP * Set the inbound hostname or IP."},
{C_inport, C_inport + " port_number * Set the inbound port number nickname listens on."},
{C_list, C_list + " * List all tunnels."},
{C_newkeys, C_newkeys + " * Generate a new keypair for the current nickname."},
{C_outhost, C_outhost + " hostname | IP * Set the outbound hostname or IP."},
{C_outport, C_outport + " port_number * Set the outbound port that nickname contacts."},
{C_quiet, C_quiet + " *"},
{C_quit, C_quit + " * Quits this session with BOB."},
{C_setkeys, C_setkeys + " BASE64_keypair * Sets the keypair for the current nickname."},
{C_setnick, C_setnick + " nickname * Create a new nickname."},
{C_show, C_show + " * Display the status of the current nickname."},
{C_start, C_start + " * Start the current nickname tunnel."},
{C_status, C_status + " nickname * Display status of a nicknamed tunnel."},
{C_stop, C_stop + " * Stops the current nicknamed tunnel."},
{"", "COMMANDS: " + // this is ugly, but...
C_help + " " +
C_clear + " " +
C_getkeys + " " +
C_getnick + " " +
C_inhost + " " +
C_inport + " " +
C_list + " " +
C_newkeys + " " +
C_outhost + " " +
C_outport + " " +
C_quiet + " " +
C_quit + " " +
C_setkeys + " " +
C_setnick + " " +
C_show + " " +
C_start + " " +
C_status + " " +
C_stop
},
{" ", " "} // end of list
};
/**
*
* @param server
* @param props
* @param database
* @param _log
*/
doCMDS(Socket server, Properties props, nickname database, Log _log) {
this.server = server;
this.props = props;
this.database = database;
this._log = _log;
}
/**
* Try to print info from the database
*
* @param out
* @param info
* @param key
*/
public void trypnt(PrintStream out, nickname info, Object key) {
out.print(" " + key + ": ");
if(info.exists(key)) {
out.print(info.get(key));
} else {
out.print("not_set");
}
}
/**
* Print true or false if an object exists
*
* @param out
* @param info
* @param key
*/
public void tfpnt(PrintStream out, nickname info, Object key) {
out.print(" " + key + ": ");
out.print(info.exists(key));
}
/**
* Print an error message
*
* @param out
*/
public void nns(PrintStream out) {
out.println("ERROR no nickname has been set");
}
/**
* Dump various information from the database
*
* @param out
* @param info
*/
public void nickprint(PrintStream out, nickname info) {
trypnt(out, info, P_NICKNAME);
trypnt(out, info, P_STARTING);
trypnt(out, info, P_RUNNING);
trypnt(out, info, P_STOPPING);
tfpnt(out, info, P_KEYS);
trypnt(out, info, P_QUIET);
trypnt(out, info, P_INPORT);
trypnt(out, info, P_INHOST);
trypnt(out, info, P_OUTPORT);
trypnt(out, info, P_OUTHOST);
out.println();
}
/**
* Print information on a specific record, indicated by nickname
* @param out
* @param database
* @param Arg
*/
public void ttlpnt(PrintStream out, nickname database, Object Arg) {
if(database.exists(Arg)) {
out.print("DATA");
nickprint(out, (nickname)database.get(Arg));
}
}
/**
* Is this nickname's tunnel active?
*
* @param Arg
* @return true if the tunnel is active
*/
public boolean tunnelactive(nickname Arg) {
return (Arg.get(P_STARTING).equals(true) ||
Arg.get(P_STOPPING).equals(true) ||
Arg.get(P_RUNNING).equals(true));
}
/**
* Does the base64 information look OK
*
* @param data
* @return
*/
private boolean is64ok(String data) {
String dest = new String(data);
if(dest.replaceAll("[a-zA-Z0-9~-]", "").length() == 0) {
return true;
}
return false;
}
/**
* The actual parser.
* It probabbly needs a rewrite into functions, but I kind-of like inline code.
*
*/
public void run() {
dk = ns = ip = op = false;
try {
// Get input from the client
BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
PrintStream out = new PrintStream(server.getOutputStream());
prikey = new ByteArrayOutputStream();
out.println("BOB " + BOBversion);
out.println("OK");
while((line = in.readLine()) != null) {
System.gc(); // yes, this does make a huge difference...
StringTokenizer token = new StringTokenizer(line, " "); // use a space as a delimiter
String Command = "";
String Arg = "";
nickname info;
if(token.countTokens() != 0) {
Command = token.nextToken();
Command = Command.toLowerCase();
if(token.countTokens() != 0) {
Arg = token.nextToken();
} else {
Arg = "";
}
// The rest of the tokens are considered junk,
// and discarded without any warnings.
if(Command.equals(C_help)) {
for(int i = 0; !C_ALL[i][0].equals(" "); i++) {
if(C_ALL[i][0].equalsIgnoreCase(Arg)) {
out.println("OK " + C_ALL[i][1]);
}
}
} else if(Command.equals(C_list)) {
// Produce a formatted list of all nicknames
for(int i = 0; i < database.getcount(); i++) {
try {
info = (nickname)database.getnext(i);
} catch(RuntimeException b) {
break; // something bad happened.
}
out.print("DATA");
nickprint(out, info);
}
out.println("OK Listing done");
} else if(Command.equals(C_quit)) {
// End the command session
break;
} else if(Command.equals(C_newkeys)) {
if(ns) {
if(tunnelactive(nickinfo)) {
out.println("ERROR tunnel is active");
} else {
try {
// Make a new PublicKey and PrivateKey
prikey = new ByteArrayOutputStream();
d = I2PClientFactory.createClient().createDestination(prikey);
dk = true;
nickinfo.add(P_KEYS, prikey.toByteArray());
// System.out.println(prikey.toByteArray().length);
out.println("OK " + d.toBase64());
} catch(IOException ioe) {
BOB.error("Error generating keys" + ioe);
out.println("ERROR generating keys");
} catch(I2PException ipe) {
BOB.error("Error generating keys" + ipe);
out.println("ERROR generating keys");
}
}
} else {
nns(out);
}
} else if(Command.equals(C_getkeys)) {
// Return public key
if(dk) {
prikey = new ByteArrayOutputStream();
prikey.write(((byte[])nickinfo.get(P_KEYS)));
out.println("OK " + net.i2p.data.Base64.encode(prikey.toByteArray()));
} else {
out.println("ERROR no public key has been set");
}
} else if(Command.equals(C_quiet)) {
if(ns) {
if(tunnelactive(nickinfo)) {
out.println("ERROR tunnel is active");
} else {
nickinfo.add(P_QUIET, (Boolean.parseBoolean(Arg) == true));
out.println("OK Quiet set");
}
} else {
nns(out);
}
} else if(Command.equals(C_setkeys)) {
// Set the nickname to a privatekey in BASE64 format
if(ns) {
if(tunnelactive(nickinfo)) {
out.println("ERROR tunnel is active");
} else {
prikey = new ByteArrayOutputStream();
prikey.write(net.i2p.data.Base64.decode(Arg));
if((Arg.length() == 884) && is64ok(Arg)) {
nickinfo.add(P_KEYS, prikey.toByteArray());
out.println("OK Keys set");
dk = true;
} else {
out.println("ERROR not in BASE64 format");
}
}
} else {
nns(out);
}
} else if(Command.equals(C_setnick)) {
ns = dk = ip = op = false;
try {
nickinfo = (nickname)database.get(Arg);
if(!tunnelactive(nickinfo)) {
nickinfo = null;
ns = true;
}
} catch(RuntimeException b) {
nickinfo = null;
ns = true;
}
// Clears and Sets the initial nickname structure to work with
if(ns) {
nickinfo = new nickname();
database.add(Arg, nickinfo);
nickinfo.add(P_NICKNAME, Arg);
nickinfo.add(P_STARTING, false);
nickinfo.add(P_RUNNING, false);
nickinfo.add(P_STOPPING, false);
nickinfo.add(P_QUIET, false);
nickinfo.add(P_INHOST, "localhost");
nickinfo.add(P_OUTHOST, "localhost");
Properties Q = props;
Q.setProperty("inbound.nickname", (String)nickinfo.get(P_NICKNAME));
Q.setProperty("outbound.nickname", (String)nickinfo.get(P_NICKNAME));
nickinfo.add(P_PROPERTIES, Q);
out.println("OK Nickname set to " + Arg);
} else {
out.println("ERROR tunnel is active");
}
} else if(Command.equals(C_getnick)) {
// Get the nickname to work with...
try {
nickinfo = (nickname)database.get(Arg);
ns = true;
} catch(RuntimeException b) {
nns(out);
}
if(ns) {
dk = nickinfo.exists(P_KEYS);
ip = nickinfo.exists(P_INPORT);
op = nickinfo.exists(P_OUTPORT);
// Finally say OK.
out.println("OK Nickname set to " + Arg);
}
} else if(Command.equals(C_inport)) {
// Set the nickname inbound TO the router port
// app --> BOB
if(ns) {
if(tunnelactive(nickinfo)) {
out.println("ERROR tunnel is active");
} else {
int prt;
nickinfo.kill(P_INPORT);
try {
prt = Integer.parseInt(Arg);
if(prt > 1 && prt < 65536) {
nickinfo.add(P_INPORT, prt);
}
} catch(NumberFormatException nfe) {
out.println("ERROR not a number");
}
ip = nickinfo.exists(P_INPORT);
if(ip) {
out.println("OK inbound port set");
} else {
out.println("ERROR port out of range");
}
}
} else {
nns(out);
}
} else if(Command.equals(C_outport)) {
// Set the nickname outbound FROM the router port
// BOB --> app
if(ns) {
if(tunnelactive(nickinfo)) {
out.println("ERROR tunnel is active");
} else {
int prt;
nickinfo.kill(P_OUTPORT);
try {
prt = Integer.parseInt(Arg);
if(prt > 1 && prt < 65536) {
nickinfo.add(P_OUTPORT, prt);
}
} catch(NumberFormatException nfe) {
out.println("ERROR not a number");
}
ip = nickinfo.exists(P_OUTPORT);
if(ip) {
out.println("OK outbound port set");
} else {
out.println("ERROR port out of range");
}
}
} else {
nns(out);
}
} else if(Command.equals(C_inhost)) {
if(ns) {
if(tunnelactive(nickinfo)) {
out.println("ERROR tunnel is active");
} else {
nickinfo.add(P_INHOST, Arg);
out.println("OK inhost set");
}
} else {
nns(out);
}
} else if(Command.equals(C_outhost)) {
if(ns) {
if(tunnelactive(nickinfo)) {
out.println("ERROR tunnel is active");
} else {
nickinfo.add(P_OUTHOST, Arg);
out.println("OK outhost set");
}
} else {
nns(out);
}
} else if(Command.equals(C_show)) {
// Get the current nickname properties
if(ns) {
out.print("OK");
nickprint(out, nickinfo);
} else {
nns(out);
}
} else if(Command.equals(C_start)) {
// Start the tunnel, if we have all the information
if(ns && dk && (ip || op)) {
if(tunnelactive(nickinfo)) {
out.println("ERROR tunnel is active");
} else {
MUXlisten tunnel;
try {
tunnel = new MUXlisten(nickinfo, _log);
Thread t = new Thread(tunnel);
t.start();
nickinfo.add(P_STARTING, true);
out.println("OK tunnel starting");
} catch(I2PException e) {
out.println("ERROR starting tunnel: " + e);
} catch(IOException e) {
out.println("ERROR starting tunnel: " + e);
}
}
} else {
out.println("ERROR tunnel settings incomplete");
}
} else if(Command.equals(C_stop)) {
// Stop the tunnel, if it is running
if(ns) {
if(nickinfo.get(P_RUNNING).equals(true) && nickinfo.get(P_STOPPING).equals(false)) {
nickinfo.add(P_STOPPING, true);
out.println("OK tunnel stopping");
} else {
out.println("ERROR tunnel is inactive");
}
} else {
nns(out);
}
} else if(Command.equals(C_clear)) {
// Clear use of the nickname if stopped
if(ns) {
if(tunnelactive(nickinfo)) {
out.println("ERROR tunnel is active");
} else {
database.kill(nickinfo.get(P_NICKNAME));
dk = ns = ip = op = false;
out.println("OK cleared");
}
} else {
nns(out);
}
} else if(Command.equals(C_status)) {
if(database.exists(Arg)) {
// Show status of a nickname
out.print("OK ");
ttlpnt(out, database, Arg);
} else {
nns(out);
}
} else {
out.println("ERROR UNKNOWN COMMAND! Try help");
}
}
}
// Say goodbye.
out.println("OK Bye!");
server.close();
} catch(IOException ioe) {
BOB.warn("IOException on socket listen: " + ioe);
ioe.printStackTrace();
}
}
}

View File

@ -0,0 +1,165 @@
/**
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) sponge
* Planet Earth
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*
* See...
*
* http://sam.zoy.org/wtfpl/
* and
* http://en.wikipedia.org/wiki/WTFPL
*
* ...for any additional details and liscense questions.
*/
package net.i2p.BOB;
/**
* Internal database to relate nicknames to options to values
*
* @author sponge
*/
public class nickname {
private Object[][] data;
private int index = 0;
/**
* make initial NULL object
*
*/
public nickname() {
data = new Object[1][2];
}
/**
* Find objects in the array, returns it's index or throws exception
* @param key
* @return an objects index
*/
public synchronized int idx(Object key) {
for(int i = 0; i < index; i++) {
if(key.equals(data[i][0])) {
return i;
}
}
throw new ArrayIndexOutOfBoundsException("Can't locate key for index");
}
/**
* Delete an object from array if it exists
*
* @param key
*/
public synchronized void kill(Object key) {
int i, j, k, l;
Object[][] olddata;
int didsomething = 0;
try {
k = idx(key);
} catch(ArrayIndexOutOfBoundsException b) {
return;
}
olddata = new Object[index + 2][2];
// copy to olddata, skipping 'k'
for(i = 0 , l = 0; l < index; i++, l++) {
if(i == k) {
l++;
didsomething++;
}
for(j = 0; j < 2; j++) {
olddata[i][j] = data[l][j];
}
}
index -= didsomething;
data = olddata;
}
/**
* Add object to the array, deletes the old one if it exists
*
* @param key
* @param val
*/
public synchronized void add(Object key, Object val) {
Object[][] olddata;
int i, j;
i = 0;
kill(key);
olddata = new Object[index + 2][2];
// copy to olddata
for(i = 0; i < index; i++) {
for(j = 0; j < 2; j++) {
olddata[i][j] = data[i][j];
}
}
data = olddata;
data[index++] = new Object[] {key, val};
}
/**
* Get the object, and return it, throws RuntimeException
*
* @param key
* @return Object
* @throws java.lang.RuntimeException
*/
public synchronized Object get(Object key) throws RuntimeException {
for(int i = 0; i < index; i++) {
if(key.equals(data[i][0])) {
return data[i][1];
}
}
throw new RuntimeException("Key not found");
}
/**
* returns true if an object exists, else returns false
*
* @param key
* @return true if an object exists, else returns false
*/
public synchronized boolean exists(Object key) {
for(int i = 0; i < index; i++) {
if(key.equals(data[i][0])) {
return true;
}
}
return false;
}
/**
*
* @param i index
* @return an indexed Object
* @throws java.lang.RuntimeException
*/
public synchronized Object getnext(int i) throws RuntimeException {
if(i < index && i > -1) {
return data[i][1];
}
throw new RuntimeException("No more data");
}
/**
* @return the count of how many objects
*/
public synchronized int getcount() {
return index;
}
}