2005-04-18 17:03:21 +00:00
|
|
|
|
|
|
|
// I2P equivalent of 'netcat'
|
|
|
|
|
|
|
|
package net.i2p.aum;
|
|
|
|
|
2008-07-16 13:42:54 +00:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.InterruptedIOException;
|
|
|
|
import java.io.OutputStream;
|
|
|
|
import java.io.OutputStreamWriter;
|
|
|
|
import java.net.ConnectException;
|
|
|
|
import java.net.NoRouteToHostException;
|
|
|
|
import java.util.Properties;
|
|
|
|
|
|
|
|
import net.i2p.I2PAppContext;
|
|
|
|
import net.i2p.I2PException;
|
|
|
|
import net.i2p.client.naming.HostsTxtNamingService;
|
|
|
|
import net.i2p.client.streaming.I2PServerSocket;
|
|
|
|
import net.i2p.client.streaming.I2PSocket;
|
|
|
|
import net.i2p.client.streaming.I2PSocketManager;
|
|
|
|
import net.i2p.client.streaming.I2PSocketManagerFactory;
|
|
|
|
import net.i2p.data.DataFormatException;
|
|
|
|
import net.i2p.data.DataHelper;
|
|
|
|
import net.i2p.data.Destination;
|
|
|
|
import net.i2p.util.Log;
|
2005-04-18 17:03:21 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A I2P equivalent of the much-beloved 'netcat' utility.
|
|
|
|
* This command-line utility can either connect to a remote
|
|
|
|
* destination, or listen on a private destination for incoming
|
|
|
|
* connections. Once a connection is established, input on stdin
|
|
|
|
* is sent to the remote peer, and anything received from the
|
|
|
|
* remote peer is printed to stdout
|
|
|
|
*/
|
|
|
|
|
|
|
|
public class I2PCat extends Thread
|
|
|
|
{
|
|
|
|
public I2PSocketManager socketManager;
|
|
|
|
public I2PServerSocket serverSocket;
|
|
|
|
public I2PSocket sessSocket;
|
|
|
|
|
|
|
|
public PrivDestination key;
|
|
|
|
public Destination dest;
|
|
|
|
|
|
|
|
public InputStream socketIn;
|
|
|
|
public OutputStream socketOutStream;
|
|
|
|
public OutputStreamWriter socketOut;
|
|
|
|
|
|
|
|
public SockInput rxThread;
|
|
|
|
|
|
|
|
protected static Log _log;
|
|
|
|
|
|
|
|
public static String defaultHost = "127.0.0.1";
|
|
|
|
public static int defaultPort = 7654;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* a thread for reading from socket and displaying on stdout
|
|
|
|
*/
|
|
|
|
private class SockInput extends Thread {
|
|
|
|
|
|
|
|
InputStream _in;
|
|
|
|
|
|
|
|
protected Log _log;
|
|
|
|
public SockInput(InputStream i) {
|
|
|
|
|
|
|
|
_in = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void run()
|
|
|
|
{
|
|
|
|
// the thread portion, receives incoming bytes on
|
|
|
|
// the socket input stream and spits them to stdout
|
|
|
|
|
|
|
|
byte [] ch = new byte[1];
|
|
|
|
|
|
|
|
print("Receiver thread listening...");
|
|
|
|
|
|
|
|
try {
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
//String line = DataHelper.readLine(socketIn);
|
|
|
|
if (_in.read(ch) != 1) {
|
|
|
|
print("failed to receive from socket");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
//System.out.println(line);
|
|
|
|
System.out.write(ch, 0, 1);
|
|
|
|
System.out.flush();
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
print("Receiver thread crashed, terminating!!");
|
|
|
|
System.exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void print(String msg)
|
|
|
|
{
|
|
|
|
System.out.println("-=- I2PCat: "+msg);
|
|
|
|
|
|
|
|
if (_log != null) {
|
|
|
|
_log.debug(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public I2PCat()
|
|
|
|
{
|
|
|
|
_log = new Log("I2PCat");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Runs I2PCat in server mode, listening on the given destination
|
|
|
|
* for one incoming connection. Once connection is established,
|
|
|
|
* copyies data between the remote peer and
|
|
|
|
* the local terminal console.
|
|
|
|
*/
|
|
|
|
public void runServer(String keyStr) throws IOException, DataFormatException
|
|
|
|
{
|
|
|
|
Properties props = new Properties();
|
|
|
|
props.setProperty("inbound.length", "0");
|
|
|
|
props.setProperty("outbound.length", "0");
|
|
|
|
props.setProperty("inbound.lengthVariance", "0");
|
|
|
|
props.setProperty("outbound.lengthVariance", "0");
|
|
|
|
|
|
|
|
// generate new key if needed
|
|
|
|
if (keyStr.equals("new")) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
key = PrivDestination.newKey();
|
|
|
|
} catch (I2PException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
print("Creating new server dest...");
|
|
|
|
|
|
|
|
socketManager = I2PSocketManagerFactory.createManager(key.getInputStream(), props);
|
|
|
|
|
|
|
|
print("Getting server socket...");
|
|
|
|
|
|
|
|
serverSocket = socketManager.getServerSocket();
|
|
|
|
|
|
|
|
print("Server socket created, ready to run...");
|
|
|
|
|
|
|
|
dest = socketManager.getSession().getMyDestination();
|
|
|
|
|
|
|
|
print("private key follows:");
|
|
|
|
System.out.println(key.toBase64());
|
|
|
|
|
|
|
|
print("dest follows:");
|
|
|
|
System.out.println(dest.toBase64());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
key = PrivDestination.fromBase64String(keyStr);
|
|
|
|
|
|
|
|
String dest64Abbrev = key.toBase64().substring(0, 16);
|
|
|
|
|
|
|
|
print("Creating server socket manager on dest "+dest64Abbrev+"...");
|
|
|
|
|
|
|
|
socketManager = I2PSocketManagerFactory.createManager(key.getInputStream(), props);
|
|
|
|
|
|
|
|
serverSocket = socketManager.getServerSocket();
|
|
|
|
|
|
|
|
print("Server socket created, ready to run...");
|
|
|
|
}
|
|
|
|
|
|
|
|
print("Awaiting client connection...");
|
|
|
|
|
|
|
|
I2PSocket sessSocket;
|
|
|
|
|
|
|
|
try {
|
|
|
|
sessSocket = serverSocket.accept();
|
|
|
|
} catch (I2PException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
} catch (ConnectException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
print("Got connection from client");
|
|
|
|
|
|
|
|
chat(sessSocket);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public void runClient(String destStr)
|
|
|
|
throws DataFormatException, IOException
|
|
|
|
{
|
|
|
|
runClient(destStr, defaultHost, defaultPort);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* runs I2PCat in client mode, connecting to a remote
|
|
|
|
* destination then copying data between the remote peer and
|
|
|
|
* the local terminal console
|
|
|
|
*/
|
|
|
|
public void runClient(String destStr, String host, int port)
|
|
|
|
throws DataFormatException, IOException
|
|
|
|
{
|
|
|
|
// accept 'file:' prefix
|
|
|
|
if (destStr.startsWith("file:", 0))
|
|
|
|
{
|
|
|
|
String path = destStr.substring(5);
|
|
|
|
destStr = new SimpleFile(path, "r").read();
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (destStr.length() < 255) {
|
|
|
|
// attempt hosts file lookup
|
|
|
|
I2PAppContext ctx = new I2PAppContext();
|
|
|
|
HostsTxtNamingService h = new HostsTxtNamingService(ctx);
|
|
|
|
Destination dest1 = h.lookup(destStr);
|
|
|
|
if (dest1 == null) {
|
|
|
|
usage("Cannot resolve hostname: '"+destStr+"'");
|
|
|
|
}
|
|
|
|
|
|
|
|
// successful lookup
|
|
|
|
runClient(dest1, host, port);
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
// otherwise, bigger strings are assumed to be base64 dests
|
|
|
|
|
|
|
|
Destination dest = new Destination();
|
|
|
|
dest.fromBase64(destStr);
|
|
|
|
runClient(dest, host, port);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void runClient(Destination dest) {
|
|
|
|
runClient(dest, "127.0.0.1", 7654);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An alternative constructor which accepts an I2P Destination object
|
|
|
|
*/
|
|
|
|
public void runClient(Destination dest, String host, int port)
|
|
|
|
{
|
|
|
|
this.dest = dest;
|
|
|
|
|
|
|
|
String destAbbrev = dest.toBase64().substring(0, 16)+"...";
|
|
|
|
|
|
|
|
print("Connecting via i2cp "+host+":"+port+" to destination "+destAbbrev+"...");
|
|
|
|
System.out.flush();
|
|
|
|
|
|
|
|
try {
|
|
|
|
// get a socket manager
|
|
|
|
socketManager = I2PSocketManagerFactory.createManager(host, port);
|
|
|
|
|
|
|
|
// get a client socket
|
|
|
|
print("socketManager="+socketManager);
|
|
|
|
|
|
|
|
sessSocket = socketManager.connect(dest);
|
|
|
|
|
|
|
|
} catch (I2PException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
} catch (ConnectException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
} catch (NoRouteToHostException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
} catch (InterruptedIOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
print("Successfully connected!");
|
|
|
|
print("(Press Control-C to quit)");
|
|
|
|
|
|
|
|
// Perform console interaction
|
|
|
|
chat(sessSocket);
|
|
|
|
|
|
|
|
try {
|
|
|
|
sessSocket.close();
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Launch the background thread to copy incoming data to stdout, then
|
|
|
|
* loop in foreground copying lines from stdin and sending them to remote peer
|
|
|
|
*/
|
|
|
|
public void chat(I2PSocket sessSocket) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
socketIn = sessSocket.getInputStream();
|
|
|
|
socketOutStream = sessSocket.getOutputStream();
|
|
|
|
socketOut = new OutputStreamWriter(socketOutStream);
|
|
|
|
|
|
|
|
// launch receiver thread
|
|
|
|
start();
|
|
|
|
//launchRx();
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
String line = DataHelper.readLine(System.in);
|
|
|
|
print("sent: '"+line+"'");
|
|
|
|
|
|
|
|
socketOut.write(line+"\n");
|
|
|
|
socketOut.flush();
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* executes in a thread, receiving incoming bytes on
|
|
|
|
* the socket input stream and spitting them to stdout
|
|
|
|
*/
|
|
|
|
public void run()
|
|
|
|
{
|
|
|
|
|
|
|
|
byte [] ch = new byte[1];
|
|
|
|
|
|
|
|
print("Receiver thread listening...");
|
|
|
|
|
|
|
|
try {
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
//String line = DataHelper.readLine(socketIn);
|
|
|
|
if (socketIn.read(ch) != 1) {
|
|
|
|
print("failed to receive from socket");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
//System.out.println(line);
|
|
|
|
System.out.write(ch, 0, 1);
|
|
|
|
System.out.flush();
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
print("Receiver thread crashed, terminating!!");
|
|
|
|
System.exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void launchRx() {
|
|
|
|
|
|
|
|
rxThread = new SockInput(socketIn);
|
|
|
|
rxThread.start();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print(String msg)
|
|
|
|
{
|
|
|
|
System.out.println("-=- I2PCat: "+msg);
|
|
|
|
|
|
|
|
if (_log != null) {
|
|
|
|
_log.debug(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void usage(String msg)
|
|
|
|
{
|
|
|
|
usage(msg, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void usage(String msg, int ret)
|
|
|
|
{
|
|
|
|
System.out.println(msg);
|
|
|
|
usage(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void usage(int ret)
|
|
|
|
{
|
|
|
|
System.out.print(
|
|
|
|
"This utility is an I2P equivalent of the standard *nix 'netcat' utility\n"+
|
|
|
|
"usage:\n"+
|
|
|
|
" net.i2p.aum.I2PCat [-h]\n"+
|
|
|
|
" - display this help\n"+
|
|
|
|
" net.i2p.aum.I2PCat dest [host [port]]\n"+
|
|
|
|
" - run in client mode, 'dest' should be one of:\n"+
|
|
|
|
" hostname.i2p - an I2P hostname listed in hosts.txt\n"+
|
|
|
|
" (only works with a hosts.txt in current directory)\n"+
|
|
|
|
" base64dest - a full base64 destination string\n"+
|
|
|
|
" file:b64filename - filename of a file containing base64 dest\n"+
|
|
|
|
" net.i2p.aum.I2PCat -l privkey\n"+
|
|
|
|
" - run in server mode, 'key' should be one of:\n"+
|
|
|
|
" base64privkey - a full base64 private key string\n"+
|
|
|
|
" file:b64filename - filename of a file containing base64 privkey\n"+
|
|
|
|
"\n"
|
|
|
|
);
|
|
|
|
System.exit(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void main(String [] args) throws IOException, DataFormatException
|
|
|
|
{
|
|
|
|
int argc = args.length;
|
|
|
|
|
|
|
|
// barf if no args
|
|
|
|
if (argc == 0) {
|
|
|
|
usage("Missing argument");
|
|
|
|
}
|
|
|
|
|
|
|
|
// show help on request
|
|
|
|
if (args[0].equals("-h") || args[0].equals("--help")) {
|
|
|
|
usage(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// server or client?
|
|
|
|
if (args[0].equals("-l")) {
|
|
|
|
if (argc != 2) {
|
|
|
|
usage("Bad argument count");
|
|
|
|
}
|
|
|
|
|
|
|
|
new I2PCat().runServer(args[1]);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// client mode - barf if not 1-3 args
|
|
|
|
if (argc < 1 || argc > 3) {
|
|
|
|
usage("Bad argument count");
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
int port = defaultPort;
|
|
|
|
String host = defaultHost;
|
|
|
|
if (args.length > 1) {
|
|
|
|
host = args[1];
|
|
|
|
if (args.length > 2) {
|
|
|
|
port = new Integer(args[2]).intValue();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
new I2PCat().runClient(args[0], host, port);
|
|
|
|
|
|
|
|
} catch (DataFormatException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|