package net.tomp2p.holep.manual; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReferenceArray; import net.tomp2p.connection.Bindings; import net.tomp2p.p2p.Peer; import net.tomp2p.p2p.PeerBuilder; import net.tomp2p.peers.Number160; import net.tomp2p.peers.PeerAddress; import net.tomp2p.rpc.ObjectDataReply; public class LocalNATUtils { private static final String TAG = "##BASE64##:"; private static final RemotePeerCallback DEFAULT_CALLBACK = new RemotePeerCallback() { @Override public void onNull(int i) {} @Override public void onFinished(int i) {} }; /** * If you have a terrible lag in InetAddress.getLocalHost(), make sure the * hostname resolves in the other network domain. * * @throws ClassNotFoundException */ public static void main(String[] args) throws IOException, ClassNotFoundException { LocalNATUtils.handleMain(args); } public static int executeNatSetup(String action, String... cmd) throws IOException, InterruptedException { String startDir = System.getProperty("user.dir"); String[] cmds = new String[cmd.length + 3]; cmds[0] = "/usr/bin/sudo"; cmds[1] = startDir + "/src/test/resources/nat-net.sh"; cmds[2] = action; for (int i = 3; i < cmds.length; i++) { cmds[i] = cmd[i - 3]; } final ProcessBuilder builder = new ProcessBuilder(cmds); Process process = builder.start(); new StreamGobbler(process.getErrorStream(), "ERR_SETUP["+cmd[0]+"]").start(); new StreamGobbler(process.getInputStream(), "OUT_SETUP["+cmd[0]+"]").start(); process.waitFor(); return process.exitValue(); } public static RemotePeer executePeer(int nr, final CommandSync sync, final Command... cmd) throws IOException, InterruptedException, ClassNotFoundException { return executePeer(LocalNATUtils.class, nr, sync, cmd); } public static RemotePeer executePeer(int nr, final RemotePeerCallback remoteCallback, final CommandSync sync, final Command... cmd) throws IOException, InterruptedException, ClassNotFoundException { return executePeer(LocalNATUtils.class, nr, remoteCallback, sync, cmd); } public static RemotePeer executePeer(Class<?> klass, int nr, final CommandSync sync, final Command... cmd) throws IOException, InterruptedException, ClassNotFoundException { return executePeer(LocalNATUtils.class, nr, DEFAULT_CALLBACK, sync, cmd); } public static RemotePeer executePeer(Class<?> klass, final int nr, final RemotePeerCallback remoteCallback, final CommandSync sync, final Command... cmd) throws IOException, InterruptedException, ClassNotFoundException { final int cmdLen = cmdLen(cmd); sync.init(cmdLen); String javaHome = System.getProperty("java.home"); String javaBin = javaHome + File.separator + "bin" + File.separator + "java"; String classpath = System.getProperty("java.class.path"); String className = klass.getCanonicalName(); final String[] cmds = new String[cmdLen + 10]; cmds[0] = "sudo"; cmds[1] = "ip"; cmds[2] = "netns"; cmds[3] = "exec"; cmds[4] = "unr" + nr; cmds[5] = javaBin; cmds[6] = "-cp"; cmds[7] = classpath; cmds[8] = className; cmds[9] = ""+nr; int current = 0; for (int i = 10; i < cmds.length;) { final int repeat = repeat(cmd[current]); for(int j=0;j<repeat;j++) { cmds[i++] = toString(cmd[current]); } current++; } ProcessBuilder builder = new ProcessBuilder(cmds); final Process process = builder.start(); new StreamGobbler(process.getErrorStream(), "ERR["+nr+"]").start(); final CountDownLatch cl = new CountDownLatch(cmdLen); final AtomicReferenceArray<Object> results = new AtomicReferenceArray<Object>(cmdLen); new Thread(new Runnable() { @Override public void run() { try { InputStreamReader isr = new InputStreamReader(process.getInputStream()); BufferedReader br = new BufferedReader(isr); for (int i = 0; i < cmdLen; i++) { boolean done = false; while (!done) { String line = br.readLine(); if(line != null) { line = line.trim(); if (line.startsWith(TAG)) { line = line.substring(TAG.length()); Object o = fromString(line); results.set(i, o); done = true; sync.waitFor(i); process.getOutputStream().write(77); process.getOutputStream().flush(); } else { System.out.println("OUT["+nr+"]>" + line); } } else { System.out.println("OUT["+nr+"]>null"); remoteCallback.onNull(i); break; } } cl.countDown(); remoteCallback.onFinished(i); } process.getInputStream().close(); } catch (Throwable t) { t.printStackTrace(); } } }).start(); System.out.println("LOCAL> remote peer "+nr+" started"); return new RemotePeer(process, cl, cmd, results); } private static int cmdLen(Command[] cmd) { int total = 0; for(Command c:cmd) { total += repeat(c); } return total; } private static int repeat(Command command) { Repeat repeat; try { repeat = command.getClass().getMethod("execute").getAnnotation(Repeat.class); if(repeat == null) { return 1; } return repeat.repeat(); } catch (NoSuchMethodException e) { e.printStackTrace(); return 1; } catch (SecurityException e) { e.printStackTrace(); return 1; } } public static int killPeer(Process process) throws InterruptedException, IOException { process.getErrorStream().close(); process.getInputStream().close(); process.getOutputStream().close(); process.destroy(); return process.waitFor(); } /** * As set in: tomp2p/nat/src/test/resources/nat-net.sh */ public static Peer createRealNode(Number160 relayPeerId, String iface, int port) throws Exception { // relay Bindings b2 = new Bindings(); b2.addInterface(iface); return new PeerBuilder(relayPeerId).ports(port).bindings(b2).start(); } /** * As seen in: http://stackoverflow.com/questions/134492/how-to-serialize-an-object-into-a-string */ public static Object fromString(String s) throws IOException, ClassNotFoundException { byte[] data = Base64.getDecoder().decode(s); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream( data)); Object o = ois.readObject(); ois.close(); return o; } /** * As seen in: http://stackoverflow.com/questions/134492/how-to-serialize-an-object-into-a-string */ /** Write the object to a Base64 string. */ public static String toString(Serializable o) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); try { oos.writeObject(o); } catch (NotSerializableException e) { System.err.println("could not serialize object: " + o.getClass().getName()); throw e; } oos.close(); return Base64.getEncoder().encodeToString(baos.toByteArray()); } public static Command[] toObjects(String[] args) throws ClassNotFoundException, IOException { Command[] cmd = new Command[args.length-1]; for(int i=1;i<args.length;i++) { cmd[i-1] = (Command) fromString(args[i]); } return cmd; } public static void handleMain(String[] args) throws ClassNotFoundException, IOException { final Command[] cmds = LocalNATUtils.toObjects(args); for(Command cmd:cmds) { try { final CountDownLatch latch = new CountDownLatch(1); new Thread(new Runnable() { @Override public void run() { try { System.in.read(); } catch (IOException e) { e.printStackTrace(); } latch.countDown(); } }).start(); Serializable result = cmd.execute(); System.out.println(TAG + LocalNATUtils.toString(result)); //wait until we can continue latch.await(); } catch (Throwable e) { e.printStackTrace(); System.out.println(TAG + LocalNATUtils.toString(e.getMessage())); } } System.out.flush(); } public static PeerAddress peerAddress(String ip, int port, int peerId) throws UnknownHostException { return PeerAddress.create(Number160.createHash(peerId), ip, port, port, port); } public static Peer init(String ip, int port, int peerId) throws UnknownHostException, IOException { Bindings b = new Bindings(); b.addAddress(InetAddress.getByName(ip)); Peer peer = new PeerBuilder(Number160.createHash(peerId)).ports(port).bindings(b).behindFirewall().start(); System.out.println("Init "+peer.peerAddress()); return peer; } public static Peer init(String ip, int port, int peerId, int forwardedPort) throws UnknownHostException, IOException { Bindings b = new Bindings(); b.addAddress(InetAddress.getByName(ip)); Peer peer = new PeerBuilder(Number160.createHash(peerId)).portsExternal(forwardedPort).ports(port).bindings(b) .start(); System.out.println("Init "+peer.peerAddress()); return peer; } public static Serializable shutdown(Peer... peers) { if(peers != null) { for(Peer peer: peers) { if (peer != null) { peer.shutdown().awaitUninterruptibly(); } } } return "shutdown done"; } public static void shutdown(RemotePeer... unrs) { if(unrs != null) { for(RemotePeer unr : unrs) { if (unr != null) { try { LocalNATUtils.killPeer(unr.process()); } catch (Exception e) { e.printStackTrace(); } } } } } private static class StreamGobbler extends Thread { final private InputStream is; final private String type; private StreamGobbler(final InputStream is, final String type) { this.is = is; this.type = type; } @Override public void run() { try { final InputStreamReader isr = new InputStreamReader(is); final BufferedReader br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null) { System.out.println(type + "> " + line); } System.out.flush(); } catch (IOException ioe) { ioe.printStackTrace(); } } } public static Peer createNattedPeer(final String ip, final int port, final int nr, final String retVal) throws UnknownHostException, IOException { Peer peer = LocalNATUtils.init(ip, port, nr); peer.objectDataReply(new ObjectDataReply() { @Override public Object reply(PeerAddress sender, Object request) throws Exception { return retVal; } }); return peer; } public static Peer createNattedPeer(final String ip, final int port, final int nr, int forwardedPort, final String retVal) throws UnknownHostException, IOException { Peer peer = LocalNATUtils.init(ip, port, nr, forwardedPort); peer.objectDataReply(new ObjectDataReply() { @Override public Object reply(PeerAddress sender, Object request) throws Exception { return retVal; } }); return peer; } }