diff --git a/apps/BOB/nbproject/private/private.xml b/apps/BOB/nbproject/private/private.xml index 237b674f1..c1f155a78 100644 --- a/apps/BOB/nbproject/private/private.xml +++ b/apps/BOB/nbproject/private/private.xml @@ -1,7 +1,4 @@ - - file:/root/NetBeansProjects/i2p.i2p/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java - diff --git a/apps/BOB/src/net/i2p/BOB/BOB.java b/apps/BOB/src/net/i2p/BOB/BOB.java index f08d27c2f..ee3f5937a 100644 --- a/apps/BOB/src/net/i2p/BOB/BOB.java +++ b/apps/BOB/src/net/i2p/BOB/BOB.java @@ -166,6 +166,7 @@ public class BOB { public static void stop() { spin.set(false); } + /** * Listen for incoming connections and handle them * @@ -188,9 +189,10 @@ public class BOB { i = Y2.hashCode(); try { { - File cfg = new File(configLocation); - if (!cfg.isAbsolute()) + File cfg = new File(configLocation); + if (!cfg.isAbsolute()) { cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), configLocation); + } try { FileInputStream fi = new FileInputStream(cfg); props.load(fi); @@ -233,9 +235,10 @@ public class BOB { props.setProperty(PROP_BOB_HOST, "localhost"); } if (save) { - File cfg = new File(configLocation); - if (!cfg.isAbsolute()) + File cfg = new File(configLocation); + if (!cfg.isAbsolute()) { cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), configLocation); + } try { warn("Writing new defaults file " + cfg.getAbsolutePath()); FileOutputStream fo = new FileOutputStream(cfg); diff --git a/apps/BOB/src/net/i2p/BOB/DoCMDS.java b/apps/BOB/src/net/i2p/BOB/DoCMDS.java index 9019ef85b..8e4afb143 100644 --- a/apps/BOB/src/net/i2p/BOB/DoCMDS.java +++ b/apps/BOB/src/net/i2p/BOB/DoCMDS.java @@ -36,6 +36,9 @@ import net.i2p.I2PException; import net.i2p.client.I2PClientFactory; import net.i2p.data.Destination; import net.i2p.util.Log; +// needed only for debugging. +// import java.util.logging.Level; +// import java.util.logging.Logger; /** * Simplistic command parser for BOB @@ -47,7 +50,7 @@ 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 = "07", BEXT = ""; + public static final String BMAJ = "00", BMIN = "00", BREV = "08", BEXT = ""; public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT; private Socket server; private Properties props; @@ -404,451 +407,545 @@ public class DoCMDS implements Runnable { */ 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()); - quit: - { - die: + try { + // Get input from the client + BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream())); + PrintStream out = new PrintStream(server.getOutputStream()); + quit: { - prikey = new ByteArrayOutputStream(); - out.println("BOB " + BOBversion); - out.println("OK"); - while ((line = in.readLine()) != null) { - StringTokenizer token = new StringTokenizer(line, " "); // use a space as a delimiter - String Command = ""; - String Arg = ""; - NamedDB info; + die: + { + prikey = new ByteArrayOutputStream(); + out.println("BOB " + BOBversion); + out.println("OK"); + while ((line = in.readLine()) != null) { + StringTokenizer token = new StringTokenizer(line, " "); // use a space as a delimiter + String Command = ""; + String Arg = ""; + NamedDB 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]); - } - + Command = token.nextToken(); + Command = + Command.toLowerCase(); + if (token.countTokens() != 0) { + Arg = token.nextToken(); + } else { + Arg = ""; } - } else if (Command.equals(C_visit)) { - visitAllThreads(); - out.println("OK "); - } else if (Command.equals(C_getdest)) { - if (ns) { - if (dk) { - try { - rlock(); - } catch (Exception ex) { - break die; + // 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]); } - try { - out.println("OK " + nickinfo.get(P_DEST)); - } catch (Exception e) { + } + } else if (Command.equals(C_visit)) { + visitAllThreads(); + out.println("OK "); + } else if (Command.equals(C_getdest)) { + if (ns) { + if (dk) { + try { + rlock(); + } catch (Exception ex) { + break die; + } + + try { + out.println("OK " + nickinfo.get(P_DEST)); + } catch (Exception e) { + try { + runlock(); + } catch (Exception ex) { + break die; + } + break die; + } + try { runlock(); } catch (Exception ex) { break die; } - break die; - } - try { - runlock(); - } catch (Exception ex) { - break die; + } else { + out.println("ERROR keys not set."); } } else { - out.println("ERROR keys not set."); + nns(out); } - } else { - nns(out); - } - - } else if (Command.equals(C_list)) { - // Produce a formatted list of all nicknames - database.getReadLock(); - for (int i = 0; i < - database.getcount(); i++) { - try { - info = (NamedDB) database.getnext(i); - out.print("DATA"); - } catch (Exception e) { - database.releaseReadLock(); - break die; - } - - try { - info.getReadLock(); - } catch (Exception ex) { - break die; - } - try { - nickprint(out, info); - } catch (Exception e) { + } else if (Command.equals(C_list)) { + // Produce a formatted list of all nicknames + database.getReadLock(); + for (int i = 0; i < + database.getcount(); i++) { try { - info.releaseReadLock(); + info = (NamedDB) database.getnext(i); + out.print("DATA"); + } catch (Exception e) { database.releaseReadLock(); + break die; + } + + try { + info.getReadLock(); + } catch (Exception ex) { + break die; + } + try { + nickprint(out, info); + } catch (Exception e) { + try { + info.releaseReadLock(); + database.releaseReadLock(); + } catch (Exception ex) { + break die; + } + break die; + } + + try { + info.releaseReadLock(); } catch (Exception ex) { break die; } - break die; } try { - info.releaseReadLock(); + database.releaseReadLock(); } catch (Exception ex) { break die; } - } - - try { - database.releaseReadLock(); - } catch (Exception ex) { - break die; - } - out.println("OK Listing done"); - } else if (Command.equals(C_quit)) { - // End the command session - break quit; - } else if (Command.equals(C_zap)) { - // Kill BOB!! (let's hope this works!) - LIVE.set(false); - // End the command session - break quit; - } else if (Command.equals(C_newkeys)) { - if (ns) { - try { - 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); + out.println("OK Listing done"); + } else if (Command.equals(C_quit)) { + // End the command session + break quit; + } else if (Command.equals(C_zap)) { + // Kill BOB!! (let's hope this works!) + LIVE.set(false); + // End the command session + break quit; + } else if (Command.equals(C_newkeys)) { + if (ns) { + try { + if (tunnelactive(nickinfo)) { + out.println("ERROR tunnel is active"); + } else { try { - wlock(); - } catch (Exception e) { - break die; - } + // Make a new PublicKey and PrivateKey + prikey = new ByteArrayOutputStream(); + d = I2PClientFactory.createClient().createDestination(prikey); + try { + wlock(); + } catch (Exception e) { + break die; + } - try { - nickinfo.add(P_KEYS, prikey.toByteArray()); - nickinfo.add(P_DEST, d.toBase64()); - } catch (Exception e) { + try { + nickinfo.add(P_KEYS, prikey.toByteArray()); + nickinfo.add(P_DEST, d.toBase64()); + } catch (Exception e) { + try { + wunlock(); + } catch (Exception ex) { + break die; + } + break die; + } + + dk = true; try { wunlock(); } catch (Exception ex) { break die; } - break die; - } - dk = true; - try { - wunlock(); - } catch (Exception ex) { - break die; - } - - try { - rlock(); - } catch (Exception ex) { - break die; - } - - try { - out.println("OK " + nickinfo.get(P_DEST)); - } catch (Exception e) { - runlock(); - break die; - } - - try { - runlock(); - } catch (Exception ex) { - break die; - } - } catch (I2PException ipe) { - BOB.error("Error generating keys" + ipe); - out.println("ERROR generating keys"); - } - - } - } catch (Exception e) { - break die; - } - - } else { - try { - nns(out); - } catch (Exception ex) { - break die; - } - } - - } else if (Command.equals(C_getkeys)) { - // Return public key - if (dk) { - prikey = new ByteArrayOutputStream(); - try { - rlock(); - } catch (Exception e) { - break die; - } - try { - prikey.write(((byte[]) nickinfo.get(P_KEYS))); - } catch (Exception ex) { - try { - runlock(); - } catch (Exception ee) { - break die; - } - break die; - } - try { - runlock(); - } catch (Exception e) { - break die; - } - - 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) { - try { - if (tunnelactive(nickinfo)) { - out.println("ERROR tunnel is active"); - } else { - try { - wlock(); - } catch (Exception ex) { - break die; - } - try { - nickinfo.add(P_QUIET, new Boolean(Boolean.parseBoolean(Arg) == true)); - } catch (Exception ex) { - try { - wunlock(); - } catch (Exception ee) { - break die; - } - break die; - } - - try { - wunlock(); - } catch (Exception ex) { - break die; - } - - out.println("OK Quiet set"); - } - - } catch (Exception ex) { - break die; - } - - } else { - try { - nns(out); - } catch (Exception ex) { - break die; - } - } - - } else if (Command.equals(C_verify)) { - if (is64ok(Arg)) { - out.println("OK"); - } else { - out.println("ERROR not in BASE64 format"); - } - } else if (Command.equals(C_setkeys)) { - // Set the NamedDB to a privatekey in BASE64 format - if (ns) { - try { - if (tunnelactive(nickinfo)) { - out.println("ERROR tunnel is active"); - } else { - try { - prikey = new ByteArrayOutputStream(); - prikey.write(net.i2p.data.Base64.decode(Arg)); - d.fromBase64(Arg); - } catch (Exception ex) { - Arg = ""; - } - - if ((Arg.length() == 884) && is64ok(Arg)) { - try { - wlock(); - } catch (Exception ex) { - break die; - } - try { - nickinfo.add(P_KEYS, prikey.toByteArray()); - nickinfo.add(P_DEST, d.toBase64()); - } catch (Exception ex) { try { - wunlock(); - } catch (Exception ee) { + rlock(); + } catch (Exception ex) { break die; } - break die; - } - dk = true; - try { - wunlock(); - } catch (Exception ex) { - break die; - } - try { - rlock(); - } catch (Exception ex) { - break die; - } + try { + out.println("OK " + nickinfo.get(P_DEST)); + } catch (Exception e) { + runlock(); + break die; + } - try { - out.println("OK " + nickinfo.get(P_DEST)); - } catch (Exception e) { try { runlock(); } catch (Exception ex) { break die; } + } catch (I2PException ipe) { + BOB.error("Error generating keys" + ipe); + out.println("ERROR generating keys"); + } + + } + } catch (Exception e) { + break die; + } + + } else { + try { + nns(out); + } catch (Exception ex) { + break die; + } + } + + } else if (Command.equals(C_getkeys)) { + // Return public key + if (dk) { + prikey = new ByteArrayOutputStream(); + try { + rlock(); + } catch (Exception e) { + break die; + } + try { + prikey.write(((byte[]) nickinfo.get(P_KEYS))); + } catch (Exception ex) { + try { + runlock(); + } catch (Exception ee) { + break die; + } + break die; + } + try { + runlock(); + } catch (Exception e) { + break die; + } + + 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) { + try { + if (tunnelactive(nickinfo)) { + out.println("ERROR tunnel is active"); + } else { + try { + wlock(); + } catch (Exception ex) { + break die; + } + try { + nickinfo.add(P_QUIET, new Boolean(Boolean.parseBoolean(Arg) == true)); + } catch (Exception ex) { + try { + wunlock(); + } catch (Exception ee) { + break die; + } break die; } try { - runlock(); + wunlock(); } catch (Exception ex) { break die; } - } else { - out.println("ERROR not in BASE64 format"); + + out.println("OK Quiet set"); } + } catch (Exception ex) { + break die; + } + + } else { + try { + nns(out); + } catch (Exception ex) { + break die; } - } catch (Exception ex) { - break die; } - } else { + } else if (Command.equals(C_verify)) { + if (is64ok(Arg)) { + out.println("OK"); + } else { + out.println("ERROR not in BASE64 format"); + } + } else if (Command.equals(C_setkeys)) { + // Set the NamedDB to a privatekey in BASE64 format + if (ns) { + try { + if (tunnelactive(nickinfo)) { + out.println("ERROR tunnel is active"); + } else { + try { + prikey = new ByteArrayOutputStream(); + prikey.write(net.i2p.data.Base64.decode(Arg)); + d.fromBase64(Arg); + } catch (Exception ex) { + Arg = ""; + } + + if ((Arg.length() == 884) && is64ok(Arg)) { + try { + wlock(); + } catch (Exception ex) { + break die; + } + try { + nickinfo.add(P_KEYS, prikey.toByteArray()); + nickinfo.add(P_DEST, d.toBase64()); + } catch (Exception ex) { + try { + wunlock(); + } catch (Exception ee) { + break die; + } + break die; + } + dk = true; + try { + wunlock(); + } catch (Exception ex) { + break die; + } + + try { + rlock(); + } catch (Exception ex) { + break die; + } + + try { + out.println("OK " + nickinfo.get(P_DEST)); + } catch (Exception e) { + try { + runlock(); + } catch (Exception ex) { + break die; + } + break die; + } + + try { + runlock(); + } catch (Exception ex) { + break die; + } + } else { + out.println("ERROR not in BASE64 format"); + } + + } + } catch (Exception ex) { + break die; + } + + } else { + try { + nns(out); + } catch (Exception ex) { + break die; + } + } + + } else if (Command.equals(C_setnick)) { + ns = dk = ip = op = false; try { - nns(out); + database.getReadLock(); } catch (Exception ex) { break die; } - } + try { + nickinfo = (NamedDB) database.get(Arg); + if (!tunnelactive(nickinfo)) { + nickinfo = null; + ns = true; + } - } else if (Command.equals(C_setnick)) { - ns = dk = ip = op = false; - try { - database.getReadLock(); - } catch (Exception ex) { - break die; - } - try { - nickinfo = (NamedDB) database.get(Arg); - if (!tunnelactive(nickinfo)) { + } catch (Exception b) { nickinfo = null; ns = true; } - } catch (Exception b) { - nickinfo = null; - ns = true; - } - - try { - database.releaseReadLock(); - } catch (Exception ex) { - break die; - } - // Clears and Sets the initial NamedDB structure to work with - if (ns) { - nickinfo = new NamedDB(); try { - wlock(); - } catch (Exception e) { + database.releaseReadLock(); + } catch (Exception ex) { break die; } - - try { - database.add(Arg, nickinfo); - nickinfo.add(P_NICKNAME, Arg); - nickinfo.add(P_STARTING, new Boolean(false)); - nickinfo.add(P_RUNNING, new Boolean(false)); - nickinfo.add(P_STOPPING, new Boolean(false)); - nickinfo.add(P_QUIET, new Boolean(false)); - nickinfo.add(P_INHOST, "localhost"); - nickinfo.add(P_OUTHOST, "localhost"); - Properties Q = new Properties(); - Lifted.copyProperties(this.props, Q); - Q.setProperty("inbound.nickname", Arg); - Q.setProperty("outbound.nickname", Arg); - nickinfo.add(P_PROPERTIES, Q); - } catch (Exception e) { + // Clears and Sets the initial NamedDB structure to work with + if (ns) { + nickinfo = new NamedDB(); try { - wunlock(); - break die; - } catch (Exception ee) { + wlock(); + } catch (Exception e) { break die; } + try { + database.add(Arg, nickinfo); + nickinfo.add(P_NICKNAME, Arg); + nickinfo.add(P_STARTING, new Boolean(false)); + nickinfo.add(P_RUNNING, new Boolean(false)); + nickinfo.add(P_STOPPING, new Boolean(false)); + nickinfo.add(P_QUIET, new Boolean(false)); + nickinfo.add(P_INHOST, "localhost"); + nickinfo.add(P_OUTHOST, "localhost"); + Properties Q = new Properties(); + Lifted.copyProperties(this.props, Q); + Q.setProperty("inbound.nickname", Arg); + Q.setProperty("outbound.nickname", Arg); + nickinfo.add(P_PROPERTIES, Q); + } catch (Exception e) { + try { + wunlock(); + break die; + } catch (Exception ee) { + break die; + } + + } + try { + wunlock(); + } catch (Exception e) { + break die; + } + + out.println("OK Nickname set to " + Arg); + } else { + out.println("ERROR tunnel is active"); } + + } else if (Command.equals(C_option)) { + if (ns) { + try { + if (tunnelactive(nickinfo)) { + out.println("ERROR tunnel is active"); + } else { + StringTokenizer otoken = new StringTokenizer(Arg, "="); // use an equal sign as a delimiter + if (otoken.countTokens() != 2) { + out.println("ERROR to many or no options."); + } else { + String pname = otoken.nextToken(); + String pval = otoken.nextToken(); + try { + rlock(); + } catch (Exception ex) { + break die; + } + + Properties Q = (Properties) nickinfo.get(P_PROPERTIES); + try { + runlock(); + } catch (Exception ex) { + break die; + } + + Q.setProperty(pname, pval); + try { + wlock(); + } catch (Exception ex) { + break die; + } + + try { + nickinfo.add(P_PROPERTIES, Q); + } catch (Exception ex) { + try { + wunlock(); + } catch (Exception ee) { + break die; + } + break die; + } + try { + wunlock(); + } catch (Exception ex) { + break die; + } + + out.println("OK " + pname + " set to " + pval); + } + + } + } catch (Exception ex) { + break die; + } + + } else { + nns(out); + } + + } else if (Command.equals(C_getnick)) { + // Get the NamedDB to work with... try { - wunlock(); - } catch (Exception e) { + database.getReadLock(); + } catch (Exception ex) { break die; } - - out.println("OK Nickname set to " + Arg); - } else { - out.println("ERROR tunnel is active"); - } - - } else if (Command.equals(C_option)) { - if (ns) { try { - if (tunnelactive(nickinfo)) { - out.println("ERROR tunnel is active"); - } else { - StringTokenizer otoken = new StringTokenizer(Arg, "="); // use an equal sign as a delimiter - if (otoken.countTokens() != 2) { - out.println("ERROR to many or no options."); + nickinfo = (NamedDB) database.get(Arg); + ns = true; + } catch (RuntimeException b) { + try { + nns(out); + } catch (Exception ex) { + try { + database.releaseReadLock(); + } catch (Exception ee) { + break die; + } + break die; + } + } + + database.releaseReadLock(); + if (ns) { + try { + rlock(); + } catch (Exception e) { + break die; + } + try { + dk = nickinfo.exists(P_KEYS); + ip = nickinfo.exists(P_INPORT); + op = nickinfo.exists(P_OUTPORT); + } catch (Exception ex) { + try { + runlock(); + } catch (Exception ee) { + break die; + } + break die; + } + try { + runlock(); + } catch (Exception e) { + break die; + } +// Finally say OK. + out.println("OK Nickname set to " + Arg); + } + + } else if (Command.equals(C_inport)) { + // Set the NamedDB inbound TO the router port + // app --> BOB + if (ns) { + try { + if (tunnelactive(nickinfo)) { + out.println("ERROR tunnel is active"); } else { - String pname = otoken.nextToken(); - String pval = otoken.nextToken(); - try { - rlock(); - } catch (Exception ex) { - break die; - } - - Properties Q = (Properties) nickinfo.get(P_PROPERTIES); - try { - runlock(); - } catch (Exception ex) { - break die; - } - - Q.setProperty(pname, pval); + int prt; try { wlock(); } catch (Exception ex) { @@ -856,7 +953,181 @@ public class DoCMDS implements Runnable { } try { - nickinfo.add(P_PROPERTIES, Q); + nickinfo.kill(P_INPORT); + } catch (Exception ex) { + try { + wunlock(); + } catch (Exception ee) { + break die; + } + + break die; + } + try { + prt = Integer.parseInt(Arg); + if (prt > 1 && prt < 65536) { + try { + nickinfo.add(P_INPORT, new Integer(prt)); + } catch (Exception ex) { + try { + wunlock(); + } catch (Exception ee) { + break die; + } + + break die; + } + } + + } catch (NumberFormatException nfe) { + out.println("ERROR not a number"); + } + + try { + wunlock(); + } catch (Exception ex) { + break die; + } + try { + rlock(); + } catch (Exception ex) { + break die; + } + + try { + ip = nickinfo.exists(P_INPORT); + } catch (Exception ex) { + try { + runlock(); + } catch (Exception ee) { + break die; + } + break die; + } + try { + runlock(); + } catch (Exception ex) { + break die; + } + + if (ip) { + out.println("OK inbound port set"); + } else { + out.println("ERROR port out of range"); + } + + } + } catch (Exception ex) { + break die; + } + + } else { + nns(out); + } + + } else if (Command.equals(C_outport)) { + // Set the NamedDB outbound FROM the router port + // BOB --> app + if (ns) { + try { + if (tunnelactive(nickinfo)) { + out.println("ERROR tunnel is active"); + } else { + int prt; + try { + wlock(); + } catch (Exception ex) { + break die; + } + + try { + nickinfo.kill(P_OUTPORT); + } catch (Exception ex) { + try { + wunlock(); + } catch (Exception ee) { + break die; + } + break die; + } + try { + prt = Integer.parseInt(Arg); + if (prt > 1 && prt < 65536) { + try { + nickinfo.add(P_OUTPORT, new Integer(prt)); + } catch (Exception ex) { + try { + wunlock(); + } catch (Exception ee) { + break die; + } + break die; + } + } + + } catch (NumberFormatException nfe) { + out.println("ERROR not a number"); + } + + try { + wunlock(); + } catch (Exception ex) { + break die; + } + try { + rlock(); + } catch (Exception ex) { + break die; + } + + try { + ip = nickinfo.exists(P_OUTPORT); + } catch (Exception ex) { + try { + runlock(); + } catch (Exception ee) { + break die; + } + break die; + } + try { + runlock(); + } catch (Exception ex) { + break die; + } + + if (ip) { + out.println("OK outbound port set"); + } else { + out.println("ERROR port out of range"); + } + + } + } catch (Exception ex) { + break die; + } + + } else { + try { + nns(out); + } catch (Exception ex) { + break die; + } + } + + } else if (Command.equals(C_inhost)) { + if (ns) { + try { + if (tunnelactive(nickinfo)) { + out.println("ERROR tunnel is active"); + } else { + try { + wlock(); + } catch (Exception ex) { + break die; + } + try { + nickinfo.add(P_INHOST, Arg); } catch (Exception ex) { try { wunlock(); @@ -871,555 +1142,92 @@ public class DoCMDS implements Runnable { break die; } - out.println("OK " + pname + " set to " + pval); + out.println("OK inhost set"); } - } - } catch (Exception ex) { - break die; - } - - } else { - nns(out); - } - - } else if (Command.equals(C_getnick)) { - // Get the NamedDB to work with... - try { - database.getReadLock(); - } catch (Exception ex) { - break die; - } - try { - nickinfo = (NamedDB) database.get(Arg); - ns = true; - } catch (RuntimeException b) { - try { - nns(out); - } catch (Exception ex) { - try { - database.releaseReadLock(); - } catch (Exception ee) { + } catch (Exception ex) { break die; } - break die; - } - } - database.releaseReadLock(); - if (ns) { - try { - rlock(); - } catch (Exception e) { - break die; - } - try { - dk = nickinfo.exists(P_KEYS); - ip = nickinfo.exists(P_INPORT); - op = nickinfo.exists(P_OUTPORT); - } catch (Exception ex) { + } else { try { - runlock(); - } catch (Exception ee) { + nns(out); + } catch (Exception ex) { break die; } - break die; } - try { - runlock(); - } catch (Exception e) { - break die; - } -// Finally say OK. - out.println("OK Nickname set to " + Arg); - } - } else if (Command.equals(C_inport)) { - // Set the NamedDB inbound TO the router port - // app --> BOB - if (ns) { - try { - if (tunnelactive(nickinfo)) { - out.println("ERROR tunnel is active"); - } else { - int prt; - try { - wlock(); - } catch (Exception ex) { - break die; - } - - try { - nickinfo.kill(P_INPORT); - } catch (Exception ex) { + } else if (Command.equals(C_outhost)) { + if (ns) { + try { + if (tunnelactive(nickinfo)) { + out.println("ERROR tunnel is active"); + } else { try { - wunlock(); - } catch (Exception ee) { + wlock(); + } catch (Exception ex) { break die; } - - break die; - } - try { - prt = Integer.parseInt(Arg); - if (prt > 1 && prt < 65536) { + try { + nickinfo.add(P_OUTHOST, Arg); + } catch (Exception ex) { try { - nickinfo.add(P_INPORT, new Integer(prt)); - } catch (Exception ex) { - try { - wunlock(); - } catch (Exception ee) { - break die; - } - + wunlock(); + } catch (Exception ee) { break die; } - } - - } catch (NumberFormatException nfe) { - out.println("ERROR not a number"); - } - - try { - wunlock(); - } catch (Exception ex) { - break die; - } - try { - rlock(); - } catch (Exception ex) { - break die; - } - - try { - ip = nickinfo.exists(P_INPORT); - } catch (Exception ex) { - try { - runlock(); - } catch (Exception ee) { break die; } - break die; - } - try { - runlock(); - } catch (Exception ex) { - break die; - } - - if (ip) { - out.println("OK inbound port set"); - } else { - out.println("ERROR port out of range"); - } - - } - } catch (Exception ex) { - break die; - } - - } else { - nns(out); - } - - } else if (Command.equals(C_outport)) { - // Set the NamedDB outbound FROM the router port - // BOB --> app - if (ns) { - try { - if (tunnelactive(nickinfo)) { - out.println("ERROR tunnel is active"); - } else { - int prt; - try { - wlock(); - } catch (Exception ex) { - break die; - } - - try { - nickinfo.kill(P_OUTPORT); - } catch (Exception ex) { try { wunlock(); - } catch (Exception ee) { + } catch (Exception ex) { break die; } - break die; - } - try { - prt = Integer.parseInt(Arg); - if (prt > 1 && prt < 65536) { - try { - nickinfo.add(P_OUTPORT, new Integer(prt)); - } catch (Exception ex) { - try { - wunlock(); - } catch (Exception ee) { - break die; - } - break die; - } - } - } catch (NumberFormatException nfe) { - out.println("ERROR not a number"); + out.println("OK outhost set"); } - try { - wunlock(); - } catch (Exception ex) { - break die; - } - try { - rlock(); - } catch (Exception ex) { - break die; - } - - try { - ip = nickinfo.exists(P_OUTPORT); - } catch (Exception ex) { - try { - runlock(); - } catch (Exception ee) { - break die; - } - break die; - } - try { - runlock(); - } catch (Exception ex) { - break die; - } - - if (ip) { - out.println("OK outbound port set"); - } else { - out.println("ERROR port out of range"); - } - - } - } catch (Exception ex) { - break die; - } - - } else { - try { - nns(out); - } catch (Exception ex) { - break die; - } - } - - } else if (Command.equals(C_inhost)) { - if (ns) { - try { - if (tunnelactive(nickinfo)) { - out.println("ERROR tunnel is active"); - } else { - try { - wlock(); - } catch (Exception ex) { - break die; - } - try { - nickinfo.add(P_INHOST, Arg); - } catch (Exception ex) { - try { - wunlock(); - } catch (Exception ee) { - break die; - } - break die; - } - try { - wunlock(); - } catch (Exception ex) { - break die; - } - - out.println("OK inhost set"); - } - - } catch (Exception ex) { - break die; - } - - } else { - try { - nns(out); - } catch (Exception ex) { - break die; - } - } - - } else if (Command.equals(C_outhost)) { - if (ns) { - try { - if (tunnelactive(nickinfo)) { - out.println("ERROR tunnel is active"); - } else { - try { - wlock(); - } catch (Exception ex) { - break die; - } - try { - nickinfo.add(P_OUTHOST, Arg); - } catch (Exception ex) { - try { - wunlock(); - } catch (Exception ee) { - break die; - } - break die; - } - try { - wunlock(); - } catch (Exception ex) { - break die; - } - - out.println("OK outhost set"); - } - - } catch (Exception ex) { - break die; - } - - } else { - try { - nns(out); - } catch (Exception ex) { - break die; - } - } - - } else if (Command.equals(C_show)) { - // Get the current NamedDB properties - if (ns) { - out.print("OK"); - try { - rlock(); - } catch (Exception e) { - break die; - } - - try { - nickprint(out, nickinfo); - } catch (Exception e) { - try { - runlock(); - } catch (Exception ee) { + } catch (Exception ex) { break die; } - out.println(); // this will cause an IOE if IOE - break die; - } - - try { - runlock(); - } catch (Exception e) { - break die; - } - - } else { - try { - nns(out); - } catch (Exception e) { - break die; - } - } - - } else if (Command.equals(C_show_props)) { - // Get the current options properties - if (ns) { - out.print("OK"); - try { - rlock(); - } catch (Exception e) { - break die; - } - - try { - propprint(out, nickinfo); - } catch (Exception e) { + } else { try { - runlock(); - } catch (Exception ee) { + nns(out); + } catch (Exception ex) { break die; } - - out.println(); // this will cause an IOE if IOE - break die; } - try { - runlock(); - } catch (Exception e) { - break die; - } - - } else { - try { - nns(out); - } catch (Exception e) { - break die; - } - } - - } else if (Command.equals(C_start)) { - // Start the tunnel, if we have all the information - if (ns && dk && (ip || op)) { - try { - if (tunnelactive(nickinfo)) { - out.println("ERROR tunnel is active"); - } else { - MUXlisten tunnel; - try { - while (!lock.compareAndSet(false, true)) { - // wait - } - tunnel = new MUXlisten(lock, database, nickinfo, _log); - Thread t = new Thread(tunnel); - t.start(); - // try { - // Thread.sleep(1000 * 10); // Slow down the startup. - // } catch(InterruptedException ie) { - // // ignore it - // } - out.println("OK tunnel starting"); - } catch (I2PException e) { - lock.set(false); - out.println("ERROR starting tunnel: " + e); - } catch (IOException e) { - lock.set(false); - out.println("ERROR starting tunnel: " + e); - } - } - } catch (Exception ex) { - break die; - } - - } else { - out.println("ERROR tunnel settings incomplete"); - } - - } else if (Command.equals(C_stop)) { - // Stop the tunnel, if it is running - if (ns) { - try { - rlock(); - } catch (Exception e) { - break die; - } - - try { - if (nickinfo.get(P_RUNNING).equals(Boolean.TRUE) && nickinfo.get(P_STOPPING).equals(Boolean.FALSE) && nickinfo.get(P_STARTING).equals(Boolean.FALSE)) { - try { - runlock(); - } catch (Exception e) { - break die; - } - - try { - wlock(); - } catch (Exception e) { - break die; - } - - nickinfo.add(P_STOPPING, new Boolean(true)); - try { - wunlock(); - - } catch (Exception e) { - break die; - } - - out.println("OK tunnel stopping"); - } else { - try { - runlock(); - } catch (Exception e) { - break die; - } - - out.println("ERROR tunnel is inactive"); - } - } catch (Exception e) { + } else if (Command.equals(C_show)) { + // Get the current NamedDB properties + if (ns) { + out.print("OK"); try { - runlock(); - } catch (Exception ee) { - break die; - } - break die; - } - - } else { - try { - nns(out); - } catch (Exception e) { - break die; - } - } - - } else if (Command.equals(C_clear)) { - // Clear use of the NamedDB if stopped - if (ns) { - try { - if (tunnelactive(nickinfo)) { - out.println("ERROR tunnel is active"); - } else { - try { - database.getWriteLock(); - } catch (Exception e) { - break die; - } - try { - database.kill(nickinfo.get(P_NICKNAME)); - } catch (Exception e) { - try { - database.releaseWriteLock(); - } catch (Exception ee) { - break die; - } - break die; - } - try { - database.releaseWriteLock(); - } catch (Exception e) { - break die; - } - dk = ns = ip = op = false; - out.println("OK cleared"); - } - - } catch (Exception ex) { - break die; - } - - } else { - try { - nns(out); - } catch (Exception e) { - break die; - } - } - - } else if (Command.equals(C_status)) { - try { - if (database.exists(Arg)) { - // Show status of a NamedDB - out.print("OK "); - try { - ttlpnt(out, Arg); + rlock(); } catch (Exception e) { + break die; + } + + try { + nickprint(out, nickinfo); + } catch (Exception e) { + try { + runlock(); + } catch (Exception ee) { + break die; + } + out.println(); // this will cause an IOE if IOE break die; } + try { + runlock(); + } catch (Exception e) { + break die; + } + } else { try { nns(out); @@ -1427,27 +1235,229 @@ public class DoCMDS implements Runnable { break die; } } - } catch (Exception e) { - break die; + + } else if (Command.equals(C_show_props)) { + // Get the current options properties + if (ns) { + out.print("OK"); + try { + rlock(); + } catch (Exception e) { + break die; + } + + try { + propprint(out, nickinfo); + } catch (Exception e) { + try { + runlock(); + } catch (Exception ee) { + break die; + } + + out.println(); // this will cause an IOE if IOE + break die; + } + + try { + runlock(); + } catch (Exception e) { + break die; + } + + } else { + try { + nns(out); + } catch (Exception e) { + break die; + } + } + + } else if (Command.equals(C_start)) { + // Start the tunnel, if we have all the information + if (ns && dk && (ip || op)) { + try { + if (tunnelactive(nickinfo)) { + out.println("ERROR tunnel is active"); + } else { + MUXlisten tunnel; + try { + while (!lock.compareAndSet(false, true)) { + // wait + } + tunnel = new MUXlisten(lock, database, nickinfo, _log); + Thread t = new Thread(tunnel); + t.start(); + // try { + // Thread.sleep(1000 * 10); // Slow down the startup. + // } catch(InterruptedException ie) { + // // ignore it + // } + out.println("OK tunnel starting"); + } catch (I2PException e) { + lock.set(false); + out.println("ERROR starting tunnel: " + e); + } catch (IOException e) { + lock.set(false); + out.println("ERROR starting tunnel: " + e); + } + } + } catch (Exception ex) { + break die; + } + + } else { + out.println("ERROR tunnel settings incomplete"); + } + + } else if (Command.equals(C_stop)) { + // Stop the tunnel, if it is running + if (ns) { + try { + rlock(); + } catch (Exception e) { + break die; + } + + try { + if (nickinfo.get(P_RUNNING).equals(Boolean.TRUE) && nickinfo.get(P_STOPPING).equals(Boolean.FALSE) && nickinfo.get(P_STARTING).equals(Boolean.FALSE)) { + try { + runlock(); + } catch (Exception e) { + break die; + } + + try { + wlock(); + } catch (Exception e) { + break die; + } + + nickinfo.add(P_STOPPING, new Boolean(true)); + try { + wunlock(); + + } catch (Exception e) { + break die; + } + + out.println("OK tunnel stopping"); + } else { + try { + runlock(); + } catch (Exception e) { + break die; + } + + out.println("ERROR tunnel is inactive"); + } + } catch (Exception e) { + try { + runlock(); + } catch (Exception ee) { + break die; + } + break die; + } + + } else { + try { + nns(out); + } catch (Exception e) { + break die; + } + } + + } else if (Command.equals(C_clear)) { + // Clear use of the NamedDB if stopped + if (ns) { + try { + if (tunnelactive(nickinfo)) { + out.println("ERROR tunnel is active"); + } else { + try { + database.getWriteLock(); + } catch (Exception e) { + break die; + } + try { + database.kill(nickinfo.get(P_NICKNAME)); + } catch (Exception e) { + try { + database.releaseWriteLock(); + } catch (Exception ee) { + break die; + } + break die; + } + try { + database.releaseWriteLock(); + } catch (Exception e) { + break die; + } + dk = ns = ip = op = false; + out.println("OK cleared"); + } + + } catch (Exception ex) { + break die; + } + + } else { + try { + nns(out); + } catch (Exception e) { + break die; + } + } + + } else if (Command.equals(C_status)) { + try { + if (database.exists(Arg)) { + // Show status of a NamedDB + out.print("OK "); + try { + ttlpnt(out, Arg); + } catch (Exception e) { + out.println(); // this will cause an IOE if IOE + break die; + } + + } else { + try { + nns(out); + } catch (Exception e) { + break die; + } + } + } catch (Exception e) { + break die; + } + + } else { + out.println("ERROR UNKNOWN COMMAND! Try help"); } - } else { - out.println("ERROR UNKNOWN COMMAND! Try help"); } - } - } - } // die - out.print("ERROR A really bad error just happened, "); - } // quit + } // die + out.print("ERROR A really bad error just happened, "); + } // quit // Say goodbye. - out.println("OK Bye!"); + out.println("OK Bye!"); - server.close(); - } catch (IOException ioe) { - BOB.warn("IOException on socket listen: " + ioe); - ioe.printStackTrace(); + } catch (IOException ioe) { + // not really needed, except to debug. + // BOB.warn("IOException on socket listen: " + ioe); + // ioe.printStackTrace(); + } + } finally { + try { + server.close(); + } catch (IOException ex) { + // nop + } } } // Debugging... None of this is normally used. @@ -1481,7 +1491,7 @@ public class DoCMDS implements Runnable { for (int i = 0; i < numThreads; i++) { // Get thread Thread thread = threads[i]; - System.out.println("BOB: " + indent + tn + ": " +thread.toString()); + System.out.println("BOB: " + indent + tn + ": " + thread.toString()); } // Get thread subgroups of `group' diff --git a/apps/BOB/src/net/i2p/BOB/I2Plistener.java b/apps/BOB/src/net/i2p/BOB/I2Plistener.java index b8d9145f2..e55e54b9d 100644 --- a/apps/BOB/src/net/i2p/BOB/I2Plistener.java +++ b/apps/BOB/src/net/i2p/BOB/I2Plistener.java @@ -25,6 +25,7 @@ package net.i2p.BOB; import java.net.ConnectException; import java.net.SocketTimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; import net.i2p.I2PException; import net.i2p.client.streaming.I2PServerSocket; import net.i2p.client.streaming.I2PSocket; @@ -43,6 +44,7 @@ public class I2Plistener implements Runnable { // private int tgwatch; public I2PSocketManager socketManager; public I2PServerSocket serverSocket; + private AtomicBoolean lives; /** * Constructor @@ -52,13 +54,14 @@ public class I2Plistener implements Runnable { * @param database * @param _log */ - I2Plistener(I2PServerSocket SS, I2PSocketManager S, NamedDB info, NamedDB database, Log _log) { + I2Plistener(I2PServerSocket SS, I2PSocketManager S, NamedDB info, NamedDB database, Log _log, AtomicBoolean lives) { this.database = database; this.info = info; this._log = _log; this.socketManager = S; - serverSocket = SS; + this.serverSocket = SS; // tgwatch = 1; + this.lives = lives; } private void rlock() throws Exception { @@ -82,27 +85,10 @@ public class I2Plistener implements Runnable { try { die: { + try { + serverSocket.setSoTimeout(50); - serverSocket.setSoTimeout(50); - boolean spin = true; - while (spin) { - - try { - rlock(); - } catch (Exception e) { - break die; - } - try { - spin = info.get("RUNNING").equals(Boolean.TRUE); - } catch (Exception e) { - try { - runlock(); - } catch (Exception e2) { - break die; - } - break die; - } - try { + while (lives.get()) { try { sessSocket = serverSocket.accept(); g = true; @@ -115,14 +101,15 @@ public class I2Plistener implements Runnable { g = false; conn++; // toss the connection to a new thread. - I2PtoTCP conn_c = new I2PtoTCP(sessSocket, info, database); + I2PtoTCP conn_c = new I2PtoTCP(sessSocket, info, database, lives); Thread t = new Thread(conn_c, Thread.currentThread().getName() + " I2PtoTCP " + conn); t.start(); } - } catch (Exception e) { - // System.out.println("Exception " + e); } + } catch (I2PException e) { + // bad shit + System.out.println("Exception " + e); } } } finally { diff --git a/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java b/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java index 5df1f3e0e..85dcf5b41 100644 --- a/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java +++ b/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java @@ -26,6 +26,7 @@ package net.i2p.BOB; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; +import java.util.concurrent.atomic.AtomicBoolean; import net.i2p.client.streaming.I2PSocket; /** @@ -38,6 +39,7 @@ public class I2PtoTCP implements Runnable { private I2PSocket I2P; private NamedDB info, database; private Socket sock; + private AtomicBoolean lives; /** * Constructor @@ -46,10 +48,11 @@ public class I2PtoTCP implements Runnable { * @param info * @param database */ - I2PtoTCP(I2PSocket I2Psock, NamedDB info, NamedDB database) { + I2PtoTCP(I2PSocket I2Psock, NamedDB info, NamedDB database, AtomicBoolean lives) { this.I2P = I2Psock; this.info = info; this.database = database; + this.lives = lives; } private void rlock() { @@ -113,35 +116,19 @@ public class I2PtoTCP implements Runnable { out.flush(); // not really needed, but... } // setup to cross the streams - TCPio conn_c = new TCPio(in, Iout /*, info, database */); // app -> I2P - TCPio conn_a = new TCPio(Iin, out /* , info, database */); // I2P -> app + TCPio conn_c = new TCPio(in, Iout, lives); // app -> I2P + TCPio conn_a = new TCPio(Iin, out, lives); // I2P -> app t = new Thread(conn_c, Thread.currentThread().getName() + " TCPioA"); q = new Thread(conn_a, Thread.currentThread().getName() + " TCPioB"); // Fire! t.start(); q.start(); - boolean spin = true; - while (t.isAlive() && q.isAlive() && spin) { // AND is used here to kill off the other thread + while (t.isAlive() && q.isAlive() && lives.get()) { // AND is used here to kill off the other thread try { Thread.sleep(10); //sleep for 10 ms } catch (InterruptedException e) { break die; } - try { - rlock(); - } catch (Exception e) { - break die; - } - try { - spin = info.get("RUNNING").equals(Boolean.TRUE); - } catch (Exception e) { - try { - runlock(); - } catch (Exception e2) { - break die; - } - break die; - } } // System.out.println("I2PtoTCP: Going away..."); } catch (Exception e) { @@ -150,14 +137,6 @@ public class I2PtoTCP implements Runnable { } } // die } finally { - try { - t.interrupt(); - } catch (Exception e) { - } - try { - q.interrupt(); - } catch (Exception e) { - } try { in.close(); } catch (Exception ex) { @@ -174,6 +153,14 @@ public class I2PtoTCP implements Runnable { Iout.close(); } catch (Exception ex) { } + try { + t.interrupt(); + } catch (Exception e) { + } + try { + q.interrupt(); + } catch (Exception e) { + } try { // System.out.println("I2PtoTCP: Close I2P"); I2P.close(); diff --git a/apps/BOB/src/net/i2p/BOB/MUXlisten.java b/apps/BOB/src/net/i2p/BOB/MUXlisten.java index d13b8217b..ca7d64497 100644 --- a/apps/BOB/src/net/i2p/BOB/MUXlisten.java +++ b/apps/BOB/src/net/i2p/BOB/MUXlisten.java @@ -43,7 +43,7 @@ import net.i2p.util.Log; */ public class MUXlisten implements Runnable { - private NamedDB database, info; + private NamedDB database, info; private Log _log; private I2PSocketManager socketManager; private ByteArrayInputStream prikey; @@ -54,6 +54,7 @@ public class MUXlisten implements Runnable { boolean go_out; boolean come_in; private AtomicBoolean lock; + private AtomicBoolean lives; /** * Constructor Will fail if INPORT is occupied. @@ -65,47 +66,86 @@ public class MUXlisten implements Runnable { * @throws java.io.IOException */ MUXlisten(AtomicBoolean lock, NamedDB database, NamedDB info, Log _log) throws I2PException, IOException, RuntimeException { - int port = 0; - InetAddress host = null; - this.lock = lock; - this.tg = null; - this.database = database; - this.info = info; - this._log = _log; + try { + int port = 0; + InetAddress host = null; + this.lock = lock; + this.tg = null; + this.database = database; + this.info = info; + this._log = _log; + lives = new AtomicBoolean(false); - this.database.getReadLock(); - this.info.getReadLock(); - N = this.info.get("NICKNAME").toString(); - prikey = new ByteArrayInputStream((byte[]) info.get("KEYS")); - // Make a new copy so that anything else won't muck with our database. - Properties R = (Properties) info.get("PROPERTIES"); - Properties Q = new Properties(); - Lifted.copyProperties(R, Q); - this.database.releaseReadLock(); - this.info.releaseReadLock(); + this.database.getWriteLock(); + this.info.getWriteLock(); + this.info.add("STARTING", new Boolean(true)); + this.info.releaseWriteLock(); + this.database.releaseWriteLock(); + this.database.getReadLock(); + this.info.getReadLock(); - this.database.getReadLock(); - this.info.getReadLock(); - this.go_out = info.exists("OUTPORT"); - this.come_in = info.exists("INPORT"); - if (this.come_in) { - port = Integer.parseInt(info.get("INPORT").toString()); - host = InetAddress.getByName(info.get("INHOST").toString()); + N = this.info.get("NICKNAME").toString(); + prikey = new ByteArrayInputStream((byte[]) info.get("KEYS")); + // Make a new copy so that anything else won't muck with our database. + Properties R = (Properties) info.get("PROPERTIES"); + Properties Q = new Properties(); + Lifted.copyProperties(R, Q); + this.database.releaseReadLock(); + this.info.releaseReadLock(); + + this.database.getReadLock(); + this.info.getReadLock(); + this.go_out = info.exists("OUTPORT"); + this.come_in = info.exists("INPORT"); + if (this.come_in) { + port = Integer.parseInt(info.get("INPORT").toString()); + host = InetAddress.getByName(info.get("INHOST").toString()); + } + this.database.releaseReadLock(); + this.info.releaseReadLock(); + + socketManager = I2PSocketManagerFactory.createManager(prikey, Q); + if (this.come_in) { + this.listener = new ServerSocket(port, backlog, host); + } + // I2PException, IOException, RuntimeException + // To bad we can't just catch and enumerate.... + // } catch (I2PException e) { + // Something went bad. + // this.database.getWriteLock(); + // this.info.getWriteLock(); + // this.info.add("STARTING", new Boolean(false)); + // this.info.releaseWriteLock(); + // this.database.releaseWriteLock(); + // throw new I2PException(e); + } catch (IOException e) { + // Something went bad. + this.database.getWriteLock(); + this.info.getWriteLock(); + this.info.add("STARTING", new Boolean(false)); + this.info.releaseWriteLock(); + this.database.releaseWriteLock(); + throw new IOException(e); + } catch (RuntimeException e) { + // Something went bad. + this.database.getWriteLock(); + this.info.getWriteLock(); + this.info.add("STARTING", new Boolean(false)); + this.info.releaseWriteLock(); + this.database.releaseWriteLock(); + throw new RuntimeException(e); + } catch (Exception e) { + // Something else went bad. + this.database.getWriteLock(); + this.info.getWriteLock(); + this.info.add("STARTING", new Boolean(false)); + this.info.releaseWriteLock(); + this.database.releaseWriteLock(); + // throw new Exception(e); + // Debugging, I guess. + e.printStackTrace(); + throw new RuntimeException(e); } - this.database.releaseReadLock(); - this.info.releaseReadLock(); - - socketManager = I2PSocketManagerFactory.createManager(prikey, Q); - if (this.come_in) { - this.listener = new ServerSocket(port, backlog, host); - } - - // Everything is OK as far as we can tell. - this.database.getWriteLock(); - this.info.getWriteLock(); - this.info.add("STARTING", new Boolean(true)); - this.info.releaseWriteLock(); - this.database.releaseWriteLock(); } private void rlock() throws Exception { @@ -142,18 +182,22 @@ public class MUXlisten implements Runnable { try { info.add("RUNNING", new Boolean(true)); } catch (Exception e) { + lock.set(false); wunlock(); return; } } catch (Exception e) { + lock.set(false); return; } try { wunlock(); } catch (Exception e) { + lock.set(false); return; } // socketManager.addDisconnectListener(new DisconnectListener()); + lives.set(true); lock.set(false); quit: { @@ -166,14 +210,14 @@ public class MUXlisten implements Runnable { if (go_out) { // I2P -> TCP SS = socketManager.getServerSocket(); - I2Plistener conn = new I2Plistener(SS, socketManager, info, database, _log); + I2Plistener conn = new I2Plistener(SS, socketManager, info, database, _log, lives); t = new Thread(tg, conn, "BOBI2Plistener " + N); t.start(); } if (come_in) { // TCP -> I2P - TCPlistener conn = new TCPlistener(listener, socketManager, info, database, _log); + TCPlistener conn = new TCPlistener(listener, socketManager, info, database, _log, lives); q = new Thread(tg, conn, "BOBTCPlistener " + N); q.start(); } @@ -195,7 +239,7 @@ public class MUXlisten implements Runnable { break quit; } boolean spin = true; - while (spin) { + while (spin && lives.get()) { try { Thread.sleep(1000); //sleep for 1 second } catch (InterruptedException e) { @@ -226,26 +270,30 @@ public class MUXlisten implements Runnable { } } // quit } finally { - // Start cleanup. - while (!lock.compareAndSet(false, true)) { - // wait + lives.set(false); + // Some grace time. + try { + Thread.sleep(100); + } catch (InterruptedException ex) { } - - // zero out everything. try { wlock(); try { info.add("STARTING", new Boolean(false)); - info.add("STOPPING", new Boolean(false)); + info.add("STOPPING", new Boolean(true)); info.add("RUNNING", new Boolean(false)); } catch (Exception e) { + lock.set(false); wunlock(); return; } wunlock(); } catch (Exception e) { } - + // Start cleanup. + while (!lock.compareAndSet(false, true)) { + // wait + } if (SS != null) { try { SS.close(); @@ -261,11 +309,28 @@ public class MUXlisten implements Runnable { // Some grace time. try { - Thread.sleep(250); + Thread.sleep(100); } catch (InterruptedException ex) { } + // zero out everything. + try { + wlock(); + try { + info.add("STARTING", new Boolean(false)); + info.add("STOPPING", new Boolean(false)); + info.add("RUNNING", new Boolean(false)); + } catch (Exception e) { + lock.set(false); + wunlock(); + return; + } + wunlock(); + } catch (Exception e) { + } + lock.set(false); // Should we force waiting for all threads?? + // Wait around till all threads are collected. if (tg != null) { String boner = tg.getName(); @@ -276,7 +341,7 @@ public class MUXlisten implements Runnable { // visit(tg, 0, boner); int foo = tg.activeCount() + tg.activeGroupCount(); // hopefully no longer needed! - // int bar = foo; + // int bar = lives; // System.out.println("BOB: MUXlisten: Waiting on threads for " + boner); // System.out.println("\nBOB: MUXlisten: ThreadGroup dump BEGIN " + boner); // visit(tg, 0, boner); @@ -284,12 +349,12 @@ public class MUXlisten implements Runnable { // Happily spin forever :-( while (foo != 0) { foo = tg.activeCount() + tg.activeGroupCount(); - // if (foo != bar && foo != 0) { + // if (lives != bar && lives != 0) { // System.out.println("\nBOB: MUXlisten: ThreadGroup dump BEGIN " + boner); // visit(tg, 0, boner); // System.out.println("BOB: MUXlisten: ThreadGroup dump END " + boner + "\n"); // } - // bar = foo; + // bar = lives; try { Thread.sleep(100); //sleep for 100 ms (One tenth second) } catch (InterruptedException ex) { @@ -356,5 +421,4 @@ public class MUXlisten implements Runnable { visit(groups[i], level + 1, groups[i].getName()); } } - } diff --git a/apps/BOB/src/net/i2p/BOB/Main.java b/apps/BOB/src/net/i2p/BOB/Main.java index aa56e83e0..53a7d9e71 100644 --- a/apps/BOB/src/net/i2p/BOB/Main.java +++ b/apps/BOB/src/net/i2p/BOB/Main.java @@ -26,6 +26,7 @@ package net.i2p.BOB; import net.i2p.client.streaming.RetransmissionTimer; import net.i2p.util.SimpleScheduler; import net.i2p.util.SimpleTimer2; + /** * Start from command line * diff --git a/apps/BOB/src/net/i2p/BOB/NamedDB.java b/apps/BOB/src/net/i2p/BOB/NamedDB.java index 8b0ba35df..b5189647b 100644 --- a/apps/BOB/src/net/i2p/BOB/NamedDB.java +++ b/apps/BOB/src/net/i2p/BOB/NamedDB.java @@ -43,10 +43,10 @@ public class NamedDB { } synchronized public void getReadLock() { - while((writersWaiting != 0)) { + while ((writersWaiting != 0)) { try { wait(); - } catch(InterruptedException ie) { + } catch (InterruptedException ie) { } } readers++; @@ -59,10 +59,10 @@ public class NamedDB { synchronized public void getWriteLock() { writersWaiting++; - while(readers != 0 && writersWaiting != 1 ) { + while (readers != 0 && writersWaiting != 1) { try { wait(); - } catch(InterruptedException ie) { + } catch (InterruptedException ie) { } } } @@ -79,8 +79,8 @@ public class NamedDB { * @throws ArrayIndexOutOfBoundsException when key does not exist */ public int idx(Object key) throws ArrayIndexOutOfBoundsException { - for(int i = 0; i < index; i++) { - if(key.equals(data[i][0])) { + for (int i = 0; i < index; i++) { + if (key.equals(data[i][0])) { return i; } } @@ -100,17 +100,17 @@ public class NamedDB { try { k = idx(key); - } catch(ArrayIndexOutOfBoundsException b) { + } 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) { + for (i = 0, l = 0; l < index; i++, l++) { + if (i == k) { l++; didsomething++; } - for(j = 0; j < 2; j++) { + for (j = 0; j < 2; j++) { olddata[i][j] = data[l][j]; } } @@ -132,13 +132,13 @@ public class NamedDB { olddata = new Object[index + 2][2]; // copy to olddata - for(i = 0; i < index; i++) { - for(j = 0; j < 2; j++) { + 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}; + data[index++] = new Object[]{key, val}; } /** @@ -149,8 +149,8 @@ public class NamedDB { * @throws java.lang.RuntimeException */ public Object get(Object key) throws RuntimeException { - for(int i = 0; i < index; i++) { - if(key.equals(data[i][0])) { + for (int i = 0; i < index; i++) { + if (key.equals(data[i][0])) { return data[i][1]; } } @@ -164,8 +164,8 @@ public class NamedDB { * @return true if an object exists, else returns false */ public boolean exists(Object key) { - for(int i = 0; i < index; i++) { - if(key.equals(data[i][0])) { + for (int i = 0; i < index; i++) { + if (key.equals(data[i][0])) { return true; } } @@ -180,7 +180,7 @@ public class NamedDB { * @throws java.lang.RuntimeException */ public Object getnext(int i) throws RuntimeException { - if(i < index && i > -1) { + if (i < index && i > -1) { return data[i][1]; } throw new RuntimeException("No more data"); diff --git a/apps/BOB/src/net/i2p/BOB/TCPio.java b/apps/BOB/src/net/i2p/BOB/TCPio.java index 99d408c43..d501759ef 100644 --- a/apps/BOB/src/net/i2p/BOB/TCPio.java +++ b/apps/BOB/src/net/i2p/BOB/TCPio.java @@ -26,6 +26,7 @@ package net.i2p.BOB; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.concurrent.atomic.AtomicBoolean; /** * Shove data from one stream to the other. @@ -36,7 +37,7 @@ public class TCPio implements Runnable { private InputStream Ain; private OutputStream Aout; - // private NamedDB info, database; + private AtomicBoolean lives; /** * Constructor @@ -46,11 +47,10 @@ public class TCPio implements Runnable { * * param database */ - TCPio(InputStream Ain, OutputStream Aout /*, NamedDB info , NamedDB database */) { + TCPio(InputStream Ain, OutputStream Aout, AtomicBoolean lives) { this.Ain = Ain; this.Aout = Aout; - // this.info = info; - // this.database = database; + this.lives = lives; } /** @@ -84,10 +84,9 @@ public class TCPio implements Runnable { int b; byte a[] = new byte[1]; - boolean spin = true; try { try { - while (spin) { + while (lives.get()) { b = Ain.read(a, 0, 1); if (b > 0) { Aout.write(a, 0, b); @@ -105,9 +104,6 @@ public class TCPio implements Runnable { * */ // System.out.println("TCPio: End Of Stream"); - // Ain.close(); - // Aout.close(); - //return; break; } } diff --git a/apps/BOB/src/net/i2p/BOB/TCPlistener.java b/apps/BOB/src/net/i2p/BOB/TCPlistener.java index 9155cb82b..b5addc27b 100644 --- a/apps/BOB/src/net/i2p/BOB/TCPlistener.java +++ b/apps/BOB/src/net/i2p/BOB/TCPlistener.java @@ -29,6 +29,7 @@ import java.net.Socket; import java.net.SocketTimeoutException; // import net.i2p.client.I2PSession; // import net.i2p.client.I2PSessionException; +import java.util.concurrent.atomic.AtomicBoolean; import net.i2p.client.streaming.I2PServerSocket; import net.i2p.client.streaming.I2PSocketManager; import net.i2p.util.Log; @@ -45,6 +46,7 @@ public class TCPlistener implements Runnable { public I2PSocketManager socketManager; public I2PServerSocket serverSocket; private ServerSocket listener; + private AtomicBoolean lives; /** * Constructor @@ -53,12 +55,13 @@ public class TCPlistener implements Runnable { * @param database * @param _log */ - TCPlistener(ServerSocket listener, I2PSocketManager S, NamedDB info, NamedDB database, Log _log) { + TCPlistener(ServerSocket listener, I2PSocketManager S, NamedDB info, NamedDB database, Log _log, AtomicBoolean lives) { this.database = database; this.info = info; this._log = _log; this.socketManager = S; this.listener = listener; + this.lives = lives; } private void rlock() throws Exception { @@ -77,7 +80,6 @@ public class TCPlistener implements Runnable { */ public void run() { boolean g = false; - boolean spin = true; int conn = 0; try { die: @@ -85,22 +87,7 @@ public class TCPlistener implements Runnable { try { Socket server = new Socket(); listener.setSoTimeout(50); // We don't block, we cycle and check. - while (spin) { - try { - rlock(); - } catch (Exception e) { - break die; - } - try { - spin = info.get("RUNNING").equals(Boolean.TRUE); - } catch (Exception e) { - try { - runlock(); - } catch (Exception e2) { - break die; - } - break die; - } + while (lives.get()) { try { server = listener.accept(); g = true; @@ -110,7 +97,7 @@ public class TCPlistener implements Runnable { if (g) { conn++; // toss the connection to a new thread. - TCPtoI2P conn_c = new TCPtoI2P(socketManager, server, info, database); + TCPtoI2P conn_c = new TCPtoI2P(socketManager, server, info, database, lives); Thread t = new Thread(conn_c, Thread.currentThread().getName() + " TCPtoI2P " + conn); t.start(); g = false; diff --git a/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java b/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java index 2073e0e69..a4a851c54 100644 --- a/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java +++ b/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java @@ -30,6 +30,7 @@ import java.io.OutputStream; import java.net.ConnectException; import java.net.NoRouteToHostException; import java.net.Socket; +import java.util.concurrent.atomic.AtomicBoolean; import net.i2p.I2PException; import net.i2p.client.streaming.I2PSocket; import net.i2p.client.streaming.I2PSocketManager; @@ -49,6 +50,7 @@ public class TCPtoI2P implements Runnable { private NamedDB info, database; private Socket sock; private I2PSocketManager socketManager; + private AtomicBoolean lives; /** * Constructor @@ -57,11 +59,12 @@ public class TCPtoI2P implements Runnable { * param info * param database */ - TCPtoI2P(I2PSocketManager i2p, Socket socket , NamedDB info, NamedDB database) { + TCPtoI2P(I2PSocketManager i2p, Socket socket, NamedDB info, NamedDB database, AtomicBoolean lives) { this.sock = socket; this.info = info; this.database = database; this.socketManager = i2p; + this.lives = lives; } /** @@ -157,19 +160,15 @@ public class TCPtoI2P implements Runnable { Iin = I2P.getInputStream(); Iout = I2P.getOutputStream(); // setup to cross the streams - TCPio conn_c = new TCPio(in, Iout /*, info, database */); // app -> I2P - TCPio conn_a = new TCPio(Iin, out /*, info, database */); // I2P -> app + TCPio conn_c = new TCPio(in, Iout, lives); // app -> I2P + TCPio conn_a = new TCPio(Iin, out, lives); // I2P -> app t = new Thread(conn_c, Thread.currentThread().getName() + " TCPioA"); q = new Thread(conn_a, Thread.currentThread().getName() + " TCPioB"); // Fire! t.start(); q.start(); - boolean spin = true; - while (t.isAlive() && q.isAlive()) { // AND is used here to kill off the other thread + while (t.isAlive() && q.isAlive() && lives.get()) { // AND is used here to kill off the other thread Thread.sleep(10); //sleep for 10 ms - rlock(); - spin = info.get("RUNNING").equals(Boolean.TRUE); - runlock(); } } catch (I2PException e) { Emsg(e.toString(), out); diff --git a/apps/BOB/src/net/i2p/BOB/UDPIOthread.java b/apps/BOB/src/net/i2p/BOB/UDPIOthread.java index 687fe8089..ff1a1f1ab 100644 --- a/apps/BOB/src/net/i2p/BOB/UDPIOthread.java +++ b/apps/BOB/src/net/i2p/BOB/UDPIOthread.java @@ -78,26 +78,26 @@ public class UDPIOthread implements I2PSessionListener, Runnable { try { in = new DataInputStream(socket.getInputStream()); out = new DataOutputStream(socket.getOutputStream()); - while(up) { + 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) { + if (!ok) { up = false; // Is this the right thing to do?? } } - } catch(IOException ioe) { + } catch (IOException ioe) { _log.error("Error running", ioe); - } catch(I2PSessionException ise) { + } catch (I2PSessionException ise) { _log.error("Error communicating", ise); // } catch(DataFormatException dfe) { // _log.error("Peer destination file is not valid", dfe); } finally { - if(_session != null) { + if (_session != null) { try { _session.destroySession(); - } catch(I2PSessionException ise) { + } catch (I2PSessionException ise) { // ignored } } @@ -116,9 +116,9 @@ public class UDPIOthread implements I2PSessionListener, Runnable { byte msg[] = session.receiveMessage(msgId); out.write(msg); out.flush(); - } catch(I2PSessionException ise) { + } catch (I2PSessionException ise) { up = false; - } catch(IOException ioe) { + } catch (IOException ioe) { up = false; } } diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java index 662fd9e57..41d49af03 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java @@ -20,15 +20,15 @@ class I2PServerSocketImpl implements I2PServerSocket { private final static Log _log = new Log(I2PServerSocketImpl.class); private I2PSocketManager mgr; /** list of sockets waiting for the client to accept them */ - private List pendingSockets = Collections.synchronizedList(new ArrayList(4)); + private final List pendingSockets = Collections.synchronizedList(new ArrayList(4)); /** have we been closed */ private volatile boolean closing = false; /** lock on this when accepting a pending socket, and wait on it for notification of acceptance */ - private Object socketAcceptedLock = new Object(); + private final Object socketAcceptedLock = new Object(); /** lock on this when adding a new socket to the pending list, and wait on it accordingly */ - private Object socketAddedLock = new Object(); + private final Object socketAddedLock = new Object(); /** * Set Sock Option accept timeout stub, does nothing in ministreaming diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java index b40b9091f..7015f0a9d 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java @@ -40,15 +40,15 @@ import net.i2p.util.Log; class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener { private I2PAppContext _context; private Log _log; - private I2PSession _session; + private /* final */ I2PSession _session; private I2PServerSocketImpl _serverSocket = null; - private Object lock = new Object(); // for locking socket lists + private final Object lock = new Object(); // for locking socket lists private HashMap _outSockets; private HashMap _inSockets; private I2PSocketOptions _defaultOptions; private long _acceptTimeout; private String _name; - private List _listeners; + private final List _listeners = new ArrayList(1);; private static int __managerId = 0; public static final short ACK = 0x51; @@ -79,7 +79,7 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener { _inSockets = new HashMap(16); _outSockets = new HashMap(16); _acceptTimeout = ACCEPT_TIMEOUT_DEFAULT; - _listeners = new ArrayList(1); + // _listeners = new ArrayList(1); setSession(session); setDefaultOptions(buildOptions(opts)); _context.statManager().createRateStat("streaming.lifetime", "How long before the socket is closed?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 }); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java index 157e48793..b904243b1 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java @@ -230,8 +230,8 @@ public class I2PSocketManagerFull implements I2PSocketManager { * */ public void destroySocketManager() { - _connectionManager.disconnectAllHard(); _connectionManager.setAllowIncomingConnections(false); + _connectionManager.disconnectAllHard(); // should we destroy the _session too? // yes, since the old lib did (and SAM wants it to, and i dont know why not) if ( (_session != null) && (!_session.isClosed()) ) { diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java index a5d8ed94d..5be6d2a6b 100644 --- a/core/java/src/net/i2p/client/I2PSessionImpl.java +++ b/core/java/src/net/i2p/client/I2PSessionImpl.java @@ -101,7 +101,12 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa private boolean _dateReceived; /** lock that we wait upon, that the SetDateMessageHandler notifies */ private final Object _dateReceivedLock = new Object(); - + + /** whether the session connection is in the process of being opened */ + protected boolean _opening; + + /** monitor for waiting until opened */ + private final Object _openingWait = new Object(); /** * thread that we tell when new messages are available who then tells us * to fetch them. The point of this is so that the fetch doesn't block the @@ -136,6 +141,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa _log = context.logManager().getLog(I2PSessionImpl.class); _handlerMap = new I2PClientMessageHandlerMap(context); _closed = true; + _opening = false; _closing = false; _producer = new I2CPMessageProducer(context); _availabilityNotifier = new AvailabilityNotifier(); @@ -212,6 +218,17 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa return _leaseSet; } + void setOpening(boolean ls) { + _opening = ls; + synchronized (_openingWait) { + _openingWait.notifyAll(); + } + } + + boolean getOpening() { + return _opening; + } + /** * Load up the destKeyFile for our Destination, PrivateKey, and SigningPrivateKey * @@ -235,6 +252,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa * not reachable */ public void connect() throws I2PSessionException { + setOpening(true); _closed = false; _availabilityNotifier.stopNotifying(); I2PThread notifier = new I2PThread(_availabilityNotifier); @@ -294,11 +312,14 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa + (connected - startConnect) + "ms - ready to participate in the network!"); startIdleMonitor(); + setOpening(false); } catch (UnknownHostException uhe) { _closed = true; + setOpening(false); throw new I2PSessionException(getPrefix() + "Invalid session configuration", uhe); } catch (IOException ioe) { _closed = true; + setOpening(false); throw new I2PSessionException(getPrefix() + "Problem connecting to " + _hostname + " on port " + _portNum, ioe); } } @@ -547,13 +568,28 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa } /** - * Tear down the session, and do NOT reconnect + * Tear down the session, and do NOT reconnect. + * + * Blocks if session has not been fully started. */ public void destroySession() { destroySession(true); } + /** + * Tear down the session, and do NOT reconnect. + * + * Blocks if session has not been fully started. + */ public void destroySession(boolean sendDisconnect) { + while (_opening) { + synchronized (_openingWait) { + try { + _openingWait.wait(1000); + } catch (InterruptedException ie) { // nop + } + } + } if (_closed) return; if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "Destroy the session", new Exception("DestroySession()")); diff --git a/history.txt b/history.txt index c2ec1c391..f7210dba6 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,15 @@ +2009-07-16 sponge + * ministreaming: + - small pedantic fix + * streaming: + - Fix a deadly race condition. + - Some small pedantic fixes. + * core: + - Fix a deadly race condition. + * BOB: + - Fixed some races that occured from fixing races in streaming and core. + - Some badly needed code refactoring to depend less on the database. + 2009-07-15 zzz * Console: - Make light the default theme diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 3e5e8985d..983dd2f06 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 12; + public final static long BUILD = 13; /** for example "-test" */ public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA;