diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index 5b4b0ba029227e9528726389c0d0dfcaffe859b0..452860a15756ff9a9a6a0199e3b55d7b4d7e8666 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -84,9 +84,20 @@ public class TunnelController implements Logging { public static final String TYPE_SOCKS_IRC = "socksirctunnel"; public static final String TYPE_STD_CLIENT = "client"; public static final String TYPE_STD_SERVER = "server"; + /** Client in the UI and I2P side but a server on the localhost side */ public static final String TYPE_STREAMR_CLIENT = "streamrclient"; + /** Server in the UI and I2P side but a client on the localhost side */ public static final String TYPE_STREAMR_SERVER = "streamrserver"; + /** + * This is guaranteed to be available. + * @since 0.9.17 + */ + public static final SigType PREFERRED_SIGTYPE = SigType.ECDSA_SHA256_P256.isAvailable() ? + SigType.ECDSA_SHA256_P256 : + SigType.DSA_SHA1; + + /** * Create a new controller for a tunnel out of the specific config options. * The config may contain a large number of options - only ones that begin in @@ -125,15 +136,12 @@ public class TunnelController implements Logging { */ private boolean createPrivateKey() { I2PClient client = I2PClientFactory.createClient(); - String filename = getPrivKeyFile(); - if ( (filename == null) || (filename.trim().length() <= 0) ) { + File keyFile = getPrivateKeyFile(); + if (keyFile == null) { log("No filename specified for the private key"); return false; } - File keyFile = new File(getPrivKeyFile()); - if (!keyFile.isAbsolute()) - keyFile = new File(I2PAppContext.getGlobalContext().getConfigDir(), getPrivKeyFile()); if (keyFile.exists()) { //log("Not overwriting existing private keys in " + keyFile.getAbsolutePath()); return true; @@ -145,11 +153,11 @@ public class TunnelController implements Logging { FileOutputStream fos = null; try { fos = new SecureFileOutputStream(keyFile); - SigType stype = I2PClient.DEFAULT_SIGTYPE; + SigType stype = PREFERRED_SIGTYPE; String st = _config.getProperty(OPT_SIG_TYPE); if (st != null) { SigType type = SigType.parseSigType(st); - if (type != null) + if (type != null && type.isAvailable()) stype = type; else log("Unsupported sig type " + st + ", reverting to " + stype); @@ -586,11 +594,12 @@ public class TunnelController implements Logging { _config.setProperty(OPT_LOW_TAGS, "14"); } // same default logic as in EditBean.getSigType() - if ((type.equals(TYPE_IRC_CLIENT) || type.equals(TYPE_STD_CLIENT) || type.equals(TYPE_SOCKS_IRC)) - && !Boolean.valueOf(getSharedClient())) { - if (!_config.containsKey(OPT_SIG_TYPE) && - SigType.ECDSA_SHA256_P256.isAvailable()) - _config.setProperty(OPT_SIG_TYPE, "ECDSA_SHA256_P256"); + if (!isClient(type) || + ((type.equals(TYPE_IRC_CLIENT) || type.equals(TYPE_STD_CLIENT) || + type.equals(TYPE_SOCKS_IRC) || type.equals(TYPE_STREAMR_CLIENT)) + && !Boolean.valueOf(getSharedClient()))) { + if (!_config.containsKey(OPT_SIG_TYPE)) + _config.setProperty(OPT_SIG_TYPE, PREFERRED_SIGTYPE.name()); } } @@ -640,6 +649,34 @@ public class TunnelController implements Logging { public String getI2CPHost() { return _config.getProperty(PROP_I2CP_HOST); } public String getI2CPPort() { return _config.getProperty(PROP_I2CP_PORT); } + /** + * Is it a client or server in the UI and I2P side? + * Note that a streamr client is a UI and I2P client but a server on the localhost side. + * Note that a streamr server is a UI and I2P server but a client on the localhost side. + * + * @since 0.9.17 + */ + public boolean isClient() { + return isClient(getType()); + } + + /** + * Is it a client or server in the UI and I2P side? + * Note that a streamr client is a UI and I2P client but a server on the localhost side. + * Note that a streamr server is a UI and I2P server but a client on the localhost side. + * + * @since 0.9.17 moved from IndexBean + */ + public static boolean isClient(String type) { + return TYPE_STD_CLIENT.equals(type) || + TYPE_HTTP_CLIENT.equals(type) || + TYPE_SOCKS.equals(type) || + TYPE_SOCKS_IRC.equals(type) || + TYPE_CONNECT.equals(type) || + TYPE_STREAMR_CLIENT.equals(type) || + TYPE_IRC_CLIENT.equals(type); + } + /** * These are the ones with a prefix of "option." * @@ -664,7 +701,12 @@ public class TunnelController implements Logging { public String getTargetHost() { return _config.getProperty(PROP_TARGET_HOST); } public String getTargetPort() { return _config.getProperty(PROP_TARGET_PORT); } public String getSpoofedHost() { return _config.getProperty(PROP_SPOOFED_HOST); } + + /** + * Probably not absolute. May be null. getPrivateKeyFile() recommended. + */ public String getPrivKeyFile() { return _config.getProperty(PROP_FILE); } + public String getListenPort() { return _config.getProperty(PROP_LISTEN_PORT); } public String getTargetDestination() { return _config.getProperty(PROP_DEST); } public String getProxyList() { return _config.getProperty(PROP_PROXIES); } @@ -674,30 +716,59 @@ public class TunnelController implements Logging { public boolean getStartOnLoad() { return Boolean.parseBoolean(_config.getProperty(PROP_START, "true")); } public boolean getPersistentClientKey() { return Boolean.parseBoolean(_config.getProperty(OPT_PERSISTENT)); } + /** + * Does not necessarily exist. + * @return absolute path or null if unset + * @since 0.9.17 + */ + public File getPrivateKeyFile() { + String f = getPrivKeyFile(); + if (f == null) + return null; + f = f.trim(); + if (f.length() == 0) + return null; + File rv = new File(f); + if (!rv.isAbsolute()) + rv = new File(I2PAppContext.getGlobalContext().getConfigDir(), f); + return rv; + } + + /** + * Returns null if not running. + * @return Base64 or null + */ public String getMyDestination() { - if (_tunnel != null) { - List<I2PSession> sessions = _tunnel.getSessions(); - for (int i = 0; i < sessions.size(); i++) { - I2PSession session = sessions.get(i); - Destination dest = session.getMyDestination(); - if (dest != null) - return dest.toBase64(); - } - } + Destination dest = getDestination(); + if (dest != null) + return dest.toBase64(); return null; } /** + * Returns null if not running. * @return "{52 chars}.b32.i2p" or null */ public String getMyDestHashBase32() { + Destination dest = getDestination(); + if (dest != null) + return dest.toBase32(); + return null; + } + + /** + * Returns null if not running. + * @return Destination or null + * @since 0.9.17 + */ + public Destination getDestination() { if (_tunnel != null) { List<I2PSession> sessions = _tunnel.getSessions(); for (int i = 0; i < sessions.size(); i++) { I2PSession session = sessions.get(i); Destination dest = session.getMyDestination(); if (dest != null) - return dest.toBase32(); + return dest; } } return null; diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java index 5217befbea7ee487b915252f9702fc5ec6a99f27..bc0748e8375d42f7a29a4a807bf173dd47d2da77 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java @@ -41,6 +41,11 @@ import net.i2p.util.Addresses; public class EditBean extends IndexBean { public EditBean() { super(); } + /** + * Is it a client or server in the UI and I2P side? + * Note that a streamr client is a UI and I2P client but a server on the localhost side. + * Note that a streamr server is a UI and I2P server but a client on the localhost side. + */ public static boolean staticIsClient(int tunnel) { TunnelControllerGroup group = TunnelControllerGroup.getInstance(); if (group == null) @@ -190,6 +195,12 @@ public class EditBean extends IndexBean { String ttype; boolean isShared; if (tunnel >= 0) { + Destination d = getDestination(tunnel); + if (d != null) { + type = d.getSigType(); + if (type != null) + return type.getCode(); + } String stype = getProperty(tunnel, I2PClient.PROP_SIGTYPE, null); type = stype != null ? SigType.parseSigType(stype) : null; ttype = getTunnelType(tunnel); @@ -201,12 +212,13 @@ public class EditBean extends IndexBean { } if (type == null) { // same default logic as in TunnelController.setConfig() - if ((TunnelController.TYPE_IRC_CLIENT.equals(ttype) || - TunnelController.TYPE_SOCKS_IRC.equals(ttype) || - TunnelController.TYPE_STD_CLIENT.equals(ttype)) && - !isShared && - SigType.ECDSA_SHA256_P256.isAvailable()) - type = SigType.ECDSA_SHA256_P256; + if ((!TunnelController.isClient(ttype) || + ((TunnelController.TYPE_IRC_CLIENT.equals(ttype) || + TunnelController.TYPE_SOCKS_IRC.equals(ttype) || + TunnelController.TYPE_STREAMR_CLIENT.equals(ttype) || + TunnelController.TYPE_STD_CLIENT.equals(ttype)) && + !isShared))) + type = TunnelController.PREFERRED_SIGTYPE; else type = SigType.DSA_SHA1; } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java index 30aee1eb48fac67efe0fcf1049fb5952b6c9a641..53f3681b60470decc912454da57661817643bf1b 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -467,20 +467,24 @@ public class IndexBean { return _group.getControllers().size(); } + /** + * Is it a client or server in the UI and I2P side? + * Note that a streamr client is a UI and I2P client but a server on the localhost side. + * Note that a streamr server is a UI and I2P server but a client on the localhost side. + */ public boolean isClient(int tunnelNum) { TunnelController cur = getController(tunnelNum); if (cur == null) return false; - return isClient(cur.getType()); + return cur.isClient(); } + /** + * Is it a client or server in the UI and I2P side? + * Note that a streamr client is a UI and I2P client but a server on the localhost side. + * Note that a streamr server is a UI and I2P server but a client on the localhost side. + */ public static boolean isClient(String type) { - return ( (TunnelController.TYPE_STD_CLIENT.equals(type)) || - (TunnelController.TYPE_HTTP_CLIENT.equals(type)) || - (TunnelController.TYPE_SOCKS.equals(type)) || - (TunnelController.TYPE_SOCKS_IRC.equals(type)) || - (TunnelController.TYPE_CONNECT.equals(type)) || - (TunnelController.TYPE_STREAMR_CLIENT.equals(type)) || - (TunnelController.TYPE_IRC_CLIENT.equals(type))); + return TunnelController.isClient(type); } public String getTunnelName(int tunnel) { @@ -657,36 +661,50 @@ public class IndexBean { return ""; } - public String getDestinationBase64(int tunnel) { + /** + * Works even if tunnel is not running. + * @return Destination or null + * @since 0.9.17 + */ + protected Destination getDestination(int tunnel) { TunnelController tun = getController(tunnel); if (tun != null) { - String rv = tun.getMyDestination(); + Destination rv = tun.getDestination(); if (rv != null) return rv; // if not running, do this the hard way - String keyFile = tun.getPrivKeyFile(); - if (keyFile != null && keyFile.trim().length() > 0) { + File keyFile = tun.getPrivateKeyFile(); + if (keyFile != null) { PrivateKeyFile pkf = new PrivateKeyFile(keyFile); try { - Destination d = pkf.getDestination(); - if (d != null) - return d.toBase64(); + rv = pkf.getDestination(); + if (rv != null) + return rv; } catch (Exception e) {} } } + return null; + } + + /** + * Works even if tunnel is not running. + * @return Base64 or "" + */ + public String getDestinationBase64(int tunnel) { + Destination d = getDestination(tunnel); + if (d != null) + return d.toBase64(); return ""; } /** + * Works even if tunnel is not running. * @return "{52 chars}.b32.i2p" or "" */ public String getDestHashBase32(int tunnel) { - TunnelController tun = getController(tunnel); - if (tun != null) { - String rv = tun.getMyDestHashBase32(); - if (rv != null) - return rv; - } + Destination d = getDestination(tunnel); + if (d != null) + return d.toBase32(); return ""; } diff --git a/core/java/src/net/i2p/data/KeysAndCert.java b/core/java/src/net/i2p/data/KeysAndCert.java index 309799a01fa7099bbe85a589d5e333bd297a192c..431cb8fe4856ee91d76713847f77bfe68b71fb0c 100644 --- a/core/java/src/net/i2p/data/KeysAndCert.java +++ b/core/java/src/net/i2p/data/KeysAndCert.java @@ -16,6 +16,7 @@ import java.io.OutputStream; import java.util.Arrays; import net.i2p.crypto.SHA256Generator; +import net.i2p.crypto.SigType; /** * KeysAndCert has a public key, a signing key, and a certificate. @@ -51,6 +52,22 @@ public class KeysAndCert extends DataStructureImpl { _certificate = cert; } + /** + * @return null if not set or unknown + * @since 0.9.17 + */ + public SigType getSigType() { + if (_certificate == null) + return null; + if (_certificate.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) { + try { + KeyCertificate kcert = _certificate.toKeyCertificate(); + return kcert.getSigType(); + } catch (DataFormatException dfe) {} + } + return SigType.DSA_SHA1; + } + public PublicKey getPublicKey() { return _publicKey; } diff --git a/installer/resources/i2ptunnel.config b/installer/resources/i2ptunnel.config index 45d26d5431485a1056e8f9c25b5925c77ac971cc..7406d7118b145ed69b8990bde98ec83024abe4f2 100644 --- a/installer/resources/i2ptunnel.config +++ b/installer/resources/i2ptunnel.config @@ -91,6 +91,7 @@ tunnel.3.i2cpHost=127.0.0.1 tunnel.3.i2cpPort=7654 tunnel.3.option.inbound.nickname=eepsite tunnel.3.option.outbound.nickname=eepsite +tunnel.3.option.i2cp.destination.sigType=ECDSA_SHA256_P256 tunnel.3.option.inbound.length=3 tunnel.3.option.inbound.lengthVariance=0 tunnel.3.option.outbound.length=3