/**
* WTFPL
* Version 2, December 2004
*
* Copyright (C) sponge
* Planet Earth
*
* See...
*
* http://sam.zoy.org/wtfpl/
* and
* http://en.wikipedia.org/wiki/WTFPL
*
* ...for any additional details and license 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 java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.I2PAppContext;
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.util.I2PAppThread;
/**
*
* Process TCP->I2P
*
* @author sponge
*/
public class TCPtoI2P implements Runnable {
private I2PSocket I2P;
private final Socket sock;
private final I2PSocketManager socketManager;
private final AtomicBoolean lives;
/**
* Constructor
* @param i2p
* @param socket
* @param info unused
* @param database unused
*/
TCPtoI2P(I2PSocketManager i2p, Socket socket, NamedDB info, NamedDB database, AtomicBoolean lives) {
this.sock = socket;
this.socketManager = i2p;
this.lives = lives;
}
/**
* This is a more forgiving readline,
* it works on unbuffered streams
*
* @param in
* @return line of text as a String
* @throws IOException
*/
private static String lnRead(InputStream in) throws IOException {
StringBuilder builder = new StringBuilder();
int b;
char c;
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 care about ASCII
builder.append(c);
}
return builder.toString();
}
/**
* Print an error message to out
*
* @param e
* @param out
* @throws java.io.IOException
*/
private void Emsg(String e, OutputStream out) throws IOException {
// Debugging System.out.println("ERROR TCPtoI2P: " + e);
out.write("ERROR ".concat(e).getBytes("UTF-8"));
out.write(13);
out.write(10);
out.flush();
}
/**
* TCP stream to I2P stream thread starter
*
*/
public void run() {
String line, input;
InputStream Iin = null;
OutputStream Iout = null;
InputStream in = null;
OutputStream out = null;
Thread t = null;
Thread q = null;
try {
try {
in = sock.getInputStream();
out = sock.getOutputStream();
line = lnRead(in);
input = line.toLowerCase(Locale.US);
Destination dest = null;
if (input.endsWith(".i2p")) {
//dest = I2PTunnel.destFromName(input);
dest = I2PAppContext.getGlobalContext().namingService().lookup(input);
if (dest != null) {
line = dest.toBase64();
} else {
Emsg("Can't find destination: " + input, out);
return;
}
}
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
Iin = I2P.getInputStream();
Iout = I2P.getOutputStream();
// setup to cross the streams
TCPio conn_c = new TCPio(in, Iout, lives); // app -> I2P
TCPio conn_a = new TCPio(Iin, out, lives); // I2P -> app
t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " TCPioA");
q = new I2PAppThread(conn_a, Thread.currentThread().getName() + " TCPioB");
// Fire!
t.start();
q.start();
while (t.isAlive() && q.isAlive() && lives.get()) { // AND is used here to kill off the other thread
Thread.sleep(10); //sleep for 10 ms
}
} catch (I2PException e) {
Emsg(e.toString(), out);
} catch (ConnectException e) {
Emsg(e.toString(), out);
} catch (NoRouteToHostException e) {
Emsg(e.toString(), out);
}
} catch (InterruptedIOException e) {
// We're breaking away.
} catch (InterruptedException e) {
// ditto
} catch (IOException e) {
try {
Emsg(e.toString(), out);
} catch (IOException ex) {
// ditto
}
} catch (DataFormatException e) {
try {
Emsg(e.toString(), out);
} catch (IOException ex) {
// ditto
}
}
} finally {
try {
t.interrupt();
} catch (Exception e) {
}
try {
q.interrupt();
} catch (Exception e) {
}
try {
in.close();
} catch (Exception e) {
}
try {
out.close();
} catch (Exception e) {
}
try {
Iin.close();
} catch (Exception e) {
}
try {
Iout.close();
} catch (Exception e) {
}
try {
// System.out.println("TCPtoI2P: Close I2P");
I2P.close();
} catch (Exception e) {
}
try {
// System.out.println("TCPtoI2P: Close sock");
sock.close();
} catch (Exception e) {
}
}
// System.out.println("TCPtoI2P: Done.");
}
}