diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelConfig.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..93212e3f19432d0b2d140c15aec0582dd9d19b81 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelConfig.java @@ -0,0 +1,715 @@ +package net.i2p.i2ptunnel; + +import java.security.GeneralSecurityException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.concurrent.ConcurrentHashMap; + +import net.i2p.I2PAppContext; +import net.i2p.client.I2PClient; +import net.i2p.crypto.KeyGenerator; +import net.i2p.crypto.SigType; +import net.i2p.data.Base64; +import net.i2p.data.Destination; +import net.i2p.data.SimpleDataStructure; +import net.i2p.util.ConcurrentHashSet; +import net.i2p.util.PasswordManager; + +/** + * Helper class to generate a valid TunnelController configuration from provided + * settings. + * + * @since 0.9.19 logic moved from IndexBean + */ +public class TunnelConfig { + public static final String SHARED_CLIENT_NICKNAME = "shared clients"; + + private static final String OPT = TunnelController.PFX_OPTION; + + protected final I2PAppContext _context; + + private String _type; + private String _name; + private String _description; + private String _i2cpHost; + private String _i2cpPort; + private int _tunnelDepth = -1; + private int _tunnelQuantity = -1; + private int _tunnelVariance = -1; + private int _tunnelBackupQuantity = -1; + private boolean _connectDelay; + private String _customOptions; + private String _proxyList; + private int _port = -1; + private String _reachableBy; + private String _targetDestination; + private String _targetHost; + private int _targetPort = -1; + private String _spoofedHost; + private String _privKeyFile; + private String _profile; + private boolean _startOnLoad; + private boolean _sharedClient; + private final Set<String> _booleanOptions; + private final Map<String, String> _otherOptions; + private String _newProxyUser; + private String _newProxyPW; + private Destination _dest; + + public TunnelConfig() { + _context = I2PAppContext.getGlobalContext(); + _booleanOptions = new ConcurrentHashSet<String>(4); + _otherOptions = new ConcurrentHashMap<String, String>(4); + } + + /** + * What type of tunnel (httpclient, ircclient, client, or server). This is + * required when adding a new tunnel. + * + */ + public void setType(String type) { + _type = (type != null ? type.trim() : null); + } + public String getType() { + return _type; + } + + /** Short name of the tunnel */ + public void setName(String name) { + _name = (name != null ? name.trim() : null); + } + /** one line description */ + public void setDescription(String description) { + // '#' will blow up DataHelper.storeProps() + _description = (description != null ? description.replace('#', ' ').trim() : null); + } + /** I2CP host the router is on, ignored when in router context */ + public void setClientHost(String host) { + _i2cpHost = (host != null ? host.trim() : null); + } + /** I2CP port the router is on, ignored when in router context */ + public void setClientPort(String port) { + _i2cpPort = (port != null ? port.trim() : null); + } + /** how many hops to use for inbound tunnels */ + public void setTunnelDepth(int tunnelDepth) { + _tunnelDepth = tunnelDepth; + } + /** how many parallel inbound tunnels to use */ + public void setTunnelQuantity(int tunnelQuantity) { + _tunnelQuantity = tunnelQuantity; + } + /** how much randomisation to apply to the depth of tunnels */ + public void setTunnelVariance(int tunnelVariance) { + _tunnelVariance = tunnelVariance; + } + /** how many tunnels to hold in reserve to guard against failures */ + public void setTunnelBackupQuantity(int tunnelBackupQuantity) { + _tunnelBackupQuantity = tunnelBackupQuantity; + } + /** what I2P session overrides should be used */ + public void setCustomOptions(String customOptions) { + _customOptions = (customOptions != null ? customOptions.trim() : null); + } + /** what HTTP outproxies should be used (httpclient specific) */ + public void setProxyList(String proxyList) { + _proxyList = (proxyList != null ? proxyList.trim() : null); + } + /** what port should this client/httpclient/ircclient listen on */ + public void setPort(int port) { + _port = port; + } + /** + * what interface should this client/httpclient/ircclient listen on + */ + public void setReachableBy(String reachableBy) { + _reachableBy = (reachableBy != null ? reachableBy.trim() : null); + } + /** What peer does this client tunnel point at */ + public void setTargetDestination(String dest) { + _targetDestination = (dest != null ? dest.trim() : null); + } + /** What host does this server tunnel point at */ + public void setTargetHost(String host) { + _targetHost = (host != null ? host.trim() : null); + } + /** What port does this server tunnel point at */ + public void setTargetPort(int port) { + _targetPort = port; + } + /** What host does this http server tunnel spoof */ + public void setSpoofedHost(String host) { + _spoofedHost = (host != null ? host.trim() : null); + } + /** What filename is this server tunnel's private keys stored in */ + public void setPrivKeyFile(String file) { + _privKeyFile = (file != null ? file.trim() : null); + } + public String getPrivKeyFile() { + return _privKeyFile; + } + /** + * If called with any value, we want this tunnel to start whenever it is + * loaded (aka right now and whenever the router is started up) + */ + public void setStartOnLoad(boolean val) { + _startOnLoad = val; + } + public void setShared(boolean val) { + _sharedClient = val; + } + public void setConnectDelay(boolean val) { + _connectDelay = val; + } + public void setProfile(String profile) { + _profile = profile; + } + + public void setReduce() { + _booleanOptions.add("i2cp.reduceOnIdle"); + } + public void setClose() { + _booleanOptions.add("i2cp.closeOnIdle"); + } + public void setEncrypt() { + _booleanOptions.add("i2cp.encryptLeaseSet"); + } + public void setDCC() { + _booleanOptions.add(I2PTunnelIRCClient.PROP_DCC); + } + public void setUseSSL() { + _booleanOptions.add(I2PTunnelServer.PROP_USE_SSL); + } + public void setRejectInproxy() { + _booleanOptions.add(I2PTunnelHTTPServer.OPT_REJECT_INPROXY); + } + public void setUniqueLocal() { + _booleanOptions.add(I2PTunnelServer.PROP_UNIQUE_LOCAL); + } + + protected static final String PROP_ENABLE_ACCESS_LIST = "i2cp.enableAccessList"; + protected static final String PROP_ENABLE_BLACKLIST = "i2cp.enableBlackList"; + + public void setAccessMode(String val) { + if ("1".equals(val)) + _booleanOptions.add(PROP_ENABLE_ACCESS_LIST); + else if ("2".equals(val)) + _booleanOptions.add(PROP_ENABLE_BLACKLIST); + } + + public void setDelayOpen() { + _booleanOptions.add("i2cp.delayOpen"); + } + public void setNewDest(String val) { + if ("1".equals(val)) + _booleanOptions.add("i2cp.newDestOnResume"); + else if ("2".equals(val)) + _booleanOptions.add("persistentClientKey"); + } + + public void setReduceTime(int val) { + _otherOptions.put("i2cp.reduceIdleTime", Integer.toString(val * 60*1000)); + } + public void setReduceCount(int val) { + _otherOptions.put("i2cp.reduceQuantity", Integer.toString(val)); + } + public void setEncryptKey(String val) { + if (val != null) + _otherOptions.put("i2cp.leaseSetKey", val.trim()); + } + + public void setAccessList(String val) { + if (val != null) + _otherOptions.put("i2cp.accessList", val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ",")); + } + + public void setJumpList(String val) { + if (val != null) + _otherOptions.put(I2PTunnelHTTPClient.PROP_JUMP_SERVERS, val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ",")); + } + + public void setCloseTime(int val) { + _otherOptions.put("i2cp.closeIdleTime", Integer.toString(val * 60*1000)); + } + + public void setAllowUserAgent() { + _booleanOptions.add(I2PTunnelHTTPClient.PROP_USER_AGENT); + } + public void setAllowReferer() { + _booleanOptions.add(I2PTunnelHTTPClient.PROP_REFERER); + } + public void setAllowAccept() { + _booleanOptions.add(I2PTunnelHTTPClient.PROP_ACCEPT); + } + public void setAllowInternalSSL() { + _booleanOptions.add(I2PTunnelHTTPClient.PROP_INTERNAL_SSL); + } + + public void setMultihome() { + _booleanOptions.add("shouldBundleReplyInfo"); + } + + /** all proxy auth */ + public void setProxyAuth(String s) { + if (s != null) + _otherOptions.put(I2PTunnelHTTPClientBase.PROP_AUTH, I2PTunnelHTTPClientBase.DIGEST_AUTH); + } + + public void setProxyUsername(String s) { + if (s != null) + _newProxyUser = s.trim(); + } + + public void setProxyPassword(String s) { + if (s != null) + _newProxyPW = s.trim(); + } + + public void setOutproxyAuth(String s) { + _otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH, I2PTunnelHTTPClientBase.DIGEST_AUTH); + } + + public void setOutproxyUsername(String s) { + if (s != null) + _otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_USER, s.trim()); + } + + public void setOutproxyPassword(String s) { + if (s != null) + _otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_PW, s.trim()); + } + + public void setSslProxies(String s) { + if (s != null) + _otherOptions.put(I2PTunnelHTTPClient.PROP_SSL_OUTPROXIES, s.trim().replace(" ", ",")); + } + + public void setUseOutproxyPlugin() { + _booleanOptions.add(I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN); + } + + /** + * all of these are @since 0.8.3 (moved from IndexBean) + */ + public static final String PROP_MAX_CONNS_MIN = "i2p.streaming.maxConnsPerMinute"; + public static final String PROP_MAX_CONNS_HOUR = "i2p.streaming.maxConnsPerHour"; + public static final String PROP_MAX_CONNS_DAY = "i2p.streaming.maxConnsPerDay"; + public static final String PROP_MAX_TOTAL_CONNS_MIN = "i2p.streaming.maxTotalConnsPerMinute"; + public static final String PROP_MAX_TOTAL_CONNS_HOUR = "i2p.streaming.maxTotalConnsPerHour"; + public static final String PROP_MAX_TOTAL_CONNS_DAY = "i2p.streaming.maxTotalConnsPerDay"; + public static final String PROP_MAX_STREAMS = "i2p.streaming.maxConcurrentStreams"; + + public void setLimitMinute(int val) { + _otherOptions.put(PROP_MAX_CONNS_MIN, Integer.toString(val)); + } + + public void setLimitHour(int val) { + _otherOptions.put(PROP_MAX_CONNS_HOUR, Integer.toString(val)); + } + + public void setLimitDay(int val) { + _otherOptions.put(PROP_MAX_CONNS_DAY, Integer.toString(val)); + } + + public void setTotalMinute(int val) { + _otherOptions.put(PROP_MAX_TOTAL_CONNS_MIN, Integer.toString(val)); + } + + public void setTotalHour(int val) { + _otherOptions.put(PROP_MAX_TOTAL_CONNS_HOUR, Integer.toString(val)); + } + + public void setTotalDay(int val) { + _otherOptions.put(PROP_MAX_TOTAL_CONNS_DAY, Integer.toString(val)); + } + + public void setMaxStreams(int val) { + _otherOptions.put(PROP_MAX_STREAMS, Integer.toString(val)); + } + + /** + * POST limits + */ + public void setPostMax(int val) { + _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_MAX, Integer.toString(val)); + } + + public void setPostTotalMax(int val) { + _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_TOTAL_MAX, Integer.toString(val)); + } + + public void setPostCheckTime(int val) { + _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_WINDOW, Integer.toString(val * 60)); + } + + public void setPostBanTime(int val) { + _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_BAN_TIME, Integer.toString(val * 60)); + } + + public void setPostTotalBanTime(int val) { + _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME, Integer.toString(val * 60)); + } + + public void setSigType(String val) { + if (val != null) + _otherOptions.put(I2PClient.PROP_SIGTYPE, val); + } + + /** + * Random keys + */ + public void setInboundRandomKey(String s) { + if (s != null) + _otherOptions.put("inbound.randomKey", s.trim()); + } + + public void setOutboundRandomKey(String s) { + if (s != null) + _otherOptions.put("outbound.randomKey", s.trim()); + } + + public void setLeaseSetSigningPrivateKey(String s) { + if (s != null) + _otherOptions.put("i2cp.leaseSetSigningPrivateKey", s.trim()); + } + + public void setLeaseSetPrivateKey(String s) { + if (s != null) + _otherOptions.put("i2cp.leaseSetPrivateKey", s.trim()); + } + + /** + * This is easier than requiring TunnelConfig to talk to + * TunnelControllerGroup and TunnelController. + * + * @param dest the current Destination for this tunnel. + */ + public void setDestination(Destination dest) { + _dest = dest; + } + + /** + * Based on all provided data, create a set of configuration parameters + * suitable for use in a TunnelController. This will replace (not add to) + * any existing parameters, so this should return a comprehensive mapping. + * + */ + public Properties getConfig() { + Properties config = new Properties(); + updateConfigGeneric(config); + + if ((TunnelController.isClient(_type) && !TunnelController.TYPE_STREAMR_CLIENT.equals(_type)) || + TunnelController.TYPE_STREAMR_SERVER.equals(_type)) { + // streamrserver uses interface + if (_reachableBy != null) + config.setProperty(TunnelController.PROP_INTFC, _reachableBy); + else + config.setProperty(TunnelController.PROP_INTFC, ""); + } else { + // streamrclient uses targetHost + if (_targetHost != null) + config.setProperty(TunnelController.PROP_TARGET_HOST, _targetHost); + } + + if (TunnelController.isClient(_type)) { + // generic client stuff + if (_port >= 0) + config.setProperty(TunnelController.PROP_LISTEN_PORT, Integer.toString(_port)); + config.setProperty(TunnelController.PROP_SHARED, _sharedClient + ""); + for (String p : _booleanClientOpts) + config.setProperty(OPT + p, "" + _booleanOptions.contains(p)); + for (String p : _otherClientOpts) { + if (_otherOptions.containsKey(p)) + config.setProperty(OPT + p, _otherOptions.get(p)); + } + } else { + // generic server stuff + if (_targetPort >= 0) + config.setProperty(TunnelController.PROP_TARGET_PORT, Integer.toString(_targetPort)); + for (String p : _booleanServerOpts) + config.setProperty(OPT + p, "" + _booleanOptions.contains(p)); + for (String p : _otherServerOpts) { + if (_otherOptions.containsKey(p)) + config.setProperty(OPT + p, _otherOptions.get(p)); + } + } + + // generic proxy stuff + if (TunnelController.TYPE_HTTP_CLIENT.equals(_type) || TunnelController.TYPE_CONNECT.equals(_type) || + TunnelController.TYPE_SOCKS.equals(_type) ||TunnelController.TYPE_SOCKS_IRC.equals(_type)) { + for (String p : _booleanProxyOpts) + config.setProperty(OPT + p, "" + _booleanOptions.contains(p)); + if (_proxyList != null) + config.setProperty(TunnelController.PROP_PROXIES, _proxyList); + } + + // Proxy auth including migration to MD5 + if (TunnelController.TYPE_HTTP_CLIENT.equals(_type) || TunnelController.TYPE_CONNECT.equals(_type)) { + // Migrate even if auth is disabled + // go get the old from custom options that updateConfigGeneric() put in there + String puser = OPT + I2PTunnelHTTPClientBase.PROP_USER; + String user = config.getProperty(puser); + String ppw = OPT + I2PTunnelHTTPClientBase.PROP_PW; + String pw = config.getProperty(ppw); + if (user != null && pw != null && user.length() > 0 && pw.length() > 0) { + String pmd5 = OPT + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_PREFIX + + user + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_SUFFIX; + if (config.getProperty(pmd5) == null) { + // not in there, migrate + String realm = _type.equals(TunnelController.TYPE_HTTP_CLIENT) ? I2PTunnelHTTPClient.AUTH_REALM + : I2PTunnelConnectClient.AUTH_REALM; + String hex = PasswordManager.md5Hex(realm, user, pw); + if (hex != null) { + config.setProperty(pmd5, hex); + config.remove(puser); + config.remove(ppw); + } + } + } + // New user/password + String auth = _otherOptions.get(I2PTunnelHTTPClientBase.PROP_AUTH); + if (auth != null && !auth.equals("false")) { + if (_newProxyUser != null && _newProxyPW != null && + _newProxyUser.length() > 0 && _newProxyPW.length() > 0) { + String pmd5 = OPT + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_PREFIX + + _newProxyUser + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_SUFFIX; + String realm = _type.equals(TunnelController.TYPE_HTTP_CLIENT) ? I2PTunnelHTTPClient.AUTH_REALM + : I2PTunnelConnectClient.AUTH_REALM; + String hex = PasswordManager.md5Hex(realm, _newProxyUser, _newProxyPW); + if (hex != null) + config.setProperty(pmd5, hex); + } + } + } + + if (TunnelController.TYPE_IRC_CLIENT.equals(_type) || + TunnelController.TYPE_STD_CLIENT.equals(_type) || + TunnelController.TYPE_STREAMR_CLIENT.equals(_type)) { + if (_targetDestination != null) + config.setProperty(TunnelController.PROP_DEST, _targetDestination); + } else if (TunnelController.TYPE_HTTP_SERVER.equals(_type) || + TunnelController.TYPE_HTTP_BIDIR_SERVER.equals(_type)) { + if (_spoofedHost != null) + config.setProperty(TunnelController.PROP_SPOOFED_HOST, _spoofedHost); + for (String p : _httpServerOpts) + if (_otherOptions.containsKey(p)) + config.setProperty(OPT + p, _otherOptions.get(p)); + } + if (TunnelController.TYPE_HTTP_BIDIR_SERVER.equals(_type)) { + if (_port >= 0) + config.setProperty(TunnelController.PROP_LISTEN_PORT, Integer.toString(_port)); + if (_reachableBy != null) + config.setProperty(TunnelController.PROP_INTFC, _reachableBy); + else if (_targetHost != null) + config.setProperty(TunnelController.PROP_INTFC, _targetHost); + else + config.setProperty(TunnelController.PROP_INTFC, ""); + } + + if (TunnelController.TYPE_IRC_CLIENT.equals(_type)) { + boolean dcc = _booleanOptions.contains(I2PTunnelIRCClient.PROP_DCC); + config.setProperty(OPT + I2PTunnelIRCClient.PROP_DCC, + "" + dcc); + // add some sane server options since they aren't in the GUI (yet) + if (dcc) { + config.setProperty(OPT + PROP_MAX_CONNS_MIN, "3"); + config.setProperty(OPT + PROP_MAX_CONNS_HOUR, "10"); + config.setProperty(OPT + PROP_MAX_TOTAL_CONNS_MIN, "5"); + config.setProperty(OPT + PROP_MAX_TOTAL_CONNS_HOUR, "25"); + } + } + + if (!TunnelController.isClient(_type) || _booleanOptions.contains("persistentClientKey")) { + // As of 0.9.17, add a persistent random key if not present + String p = OPT + "inbound.randomKey"; + if (!config.containsKey(p)) { + byte[] rk = new byte[32]; + _context.random().nextBytes(rk); + config.setProperty(p, Base64.encode(rk)); + p = OPT + "outbound.randomKey"; + _context.random().nextBytes(rk); + config.setProperty(p, Base64.encode(rk)); + } + // As of 0.9.18, add persistent leaseset keys if not present + // but only if we know the sigtype + p = OPT + "i2cp.leaseSetSigningPrivateKey"; + if (_dest != null && !config.containsKey(p)) { + try { + SigType type = _dest.getSigType(); + SimpleDataStructure keys[] = KeyGenerator.getInstance().generateSigningKeys(type); + config.setProperty(p, type.name() + ':' + keys[1].toBase64()); + p = OPT + "i2cp.leaseSetPrivateKey"; + keys = KeyGenerator.getInstance().generatePKIKeys(); + config.setProperty(p, "ELGAMAL_2048:" + keys[1].toBase64()); + } catch (GeneralSecurityException gse) { + // so much for that + } + } + } + + return config; + } + + private static final String _noShowOpts[] = { + "inbound.length", "outbound.length", "inbound.lengthVariance", "outbound.lengthVariance", + "inbound.backupQuantity", "outbound.backupQuantity", "inbound.quantity", "outbound.quantity", + "inbound.nickname", "outbound.nickname", "i2p.streaming.connectDelay", "i2p.streaming.maxWindowSize", + I2PTunnelIRCClient.PROP_DCC + }; + private static final String _booleanClientOpts[] = { + "i2cp.reduceOnIdle", "i2cp.closeOnIdle", "i2cp.newDestOnResume", "persistentClientKey", "i2cp.delayOpen", + I2PTunnelClientBase.PROP_USE_SSL, + }; + private static final String _booleanProxyOpts[] = { + I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH, + I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN, + I2PTunnelHTTPClient.PROP_USER_AGENT, + I2PTunnelHTTPClient.PROP_REFERER, + I2PTunnelHTTPClient.PROP_ACCEPT, + I2PTunnelHTTPClient.PROP_INTERNAL_SSL + }; + private static final String _booleanServerOpts[] = { + "i2cp.reduceOnIdle", "i2cp.encryptLeaseSet", PROP_ENABLE_ACCESS_LIST, PROP_ENABLE_BLACKLIST, + I2PTunnelServer.PROP_USE_SSL, + I2PTunnelHTTPServer.OPT_REJECT_INPROXY, + I2PTunnelServer.PROP_UNIQUE_LOCAL, + "shouldBundleReplyInfo" + }; + private static final String _otherClientOpts[] = { + "i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.closeIdleTime", + "outproxyUsername", "outproxyPassword", + I2PTunnelHTTPClient.PROP_JUMP_SERVERS, + I2PTunnelHTTPClientBase.PROP_AUTH, + I2PClient.PROP_SIGTYPE, + I2PTunnelHTTPClient.PROP_SSL_OUTPROXIES, + // following are mostly server but could also be persistent client + "inbound.randomKey", "outbound.randomKey", "i2cp.leaseSetSigningPrivateKey", "i2cp.leaseSetPrivateKey" + }; + private static final String _otherServerOpts[] = { + "i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.leaseSetKey", "i2cp.accessList", + PROP_MAX_CONNS_MIN, PROP_MAX_CONNS_HOUR, PROP_MAX_CONNS_DAY, + PROP_MAX_TOTAL_CONNS_MIN, PROP_MAX_TOTAL_CONNS_HOUR, PROP_MAX_TOTAL_CONNS_DAY, + PROP_MAX_STREAMS, I2PClient.PROP_SIGTYPE, + "inbound.randomKey", "outbound.randomKey", "i2cp.leaseSetSigningPrivateKey", "i2cp.leaseSetPrivateKey" + }; + private static final String _httpServerOpts[] = { + I2PTunnelHTTPServer.OPT_POST_WINDOW, + I2PTunnelHTTPServer.OPT_POST_BAN_TIME, + I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME, + I2PTunnelHTTPServer.OPT_POST_MAX, + I2PTunnelHTTPServer.OPT_POST_TOTAL_MAX + }; + + /** + * do NOT add these to noShoOpts, we must leave them in for HTTPClient and ConnectCLient + * so they will get migrated to MD5 + * TODO migrate socks to MD5 + */ + private static final String _otherProxyOpts[] = { + "proxyUsername", "proxyPassword" + }; + + public static final Set<String> _noShowSet = new HashSet<String>(128); + public static final Set<String> _nonProxyNoShowSet = new HashSet<String>(4); + static { + _noShowSet.addAll(Arrays.asList(_noShowOpts)); + _noShowSet.addAll(Arrays.asList(_booleanClientOpts)); + _noShowSet.addAll(Arrays.asList(_booleanProxyOpts)); + _noShowSet.addAll(Arrays.asList(_booleanServerOpts)); + _noShowSet.addAll(Arrays.asList(_otherClientOpts)); + _noShowSet.addAll(Arrays.asList(_otherServerOpts)); + _noShowSet.addAll(Arrays.asList(_httpServerOpts)); + _nonProxyNoShowSet.addAll(Arrays.asList(_otherProxyOpts)); + } + + private void updateConfigGeneric(Properties config) { + config.setProperty(TunnelController.PROP_TYPE, _type); + if (_name != null) + config.setProperty(TunnelController.PROP_NAME, _name); + if (_description != null) + config.setProperty(TunnelController.PROP_DESCR, _description); + if (!_context.isRouterContext()) { + if (_i2cpHost != null) + config.setProperty(TunnelController.PROP_I2CP_HOST, _i2cpHost); + if ( (_i2cpPort != null) && (_i2cpPort.trim().length() > 0) ) { + config.setProperty(TunnelController.PROP_I2CP_PORT, _i2cpPort); + } else { + config.setProperty(TunnelController.PROP_I2CP_PORT, "7654"); + } + } + if (_privKeyFile != null) + config.setProperty(TunnelController.PROP_FILE, _privKeyFile); + + if (_customOptions != null) { + StringTokenizer tok = new StringTokenizer(_customOptions); + while (tok.hasMoreTokens()) { + String pair = tok.nextToken(); + int eq = pair.indexOf('='); + if ( (eq <= 0) || (eq >= pair.length()) ) + continue; + String key = pair.substring(0, eq); + if (_noShowSet.contains(key)) + continue; + // leave in for HTTP and Connect so it can get migrated to MD5 + // hide for SOCKS until migrated to MD5 + if ((!TunnelController.TYPE_HTTP_CLIENT.equals(_type)) && + (!TunnelController.TYPE_CONNECT.equals(_type)) && + _nonProxyNoShowSet.contains(key)) + continue; + String val = pair.substring(eq+1); + config.setProperty(OPT + key, val); + } + } + + config.setProperty(TunnelController.PROP_START, _startOnLoad + ""); + + updateTunnelQuantities(config); + if (_connectDelay) + config.setProperty("option.i2p.streaming.connectDelay", "1000"); + else + config.setProperty("option.i2p.streaming.connectDelay", "0"); + if (TunnelController.isClient(_type) && _sharedClient) { + config.setProperty("option.inbound.nickname", SHARED_CLIENT_NICKNAME); + config.setProperty("option.outbound.nickname", SHARED_CLIENT_NICKNAME); + } else if (_name != null) { + config.setProperty("option.inbound.nickname", _name); + config.setProperty("option.outbound.nickname", _name); + } + if ("interactive".equals(_profile)) + // This was 1 which doesn't make much sense + // The real way to make it interactive is to make the streaming lib + // MessageInputStream flush faster but there's no option for that yet, + // Setting it to 16 instead of the default but not sure what good that is either. + config.setProperty("option.i2p.streaming.maxWindowSize", "16"); + else + config.remove("option.i2p.streaming.maxWindowSize"); + } + + /** + * Update tunnel quantities for the provided config from this TunnelConfig. + * + * @param config the config to update. + */ + public void updateTunnelQuantities(Properties config) { + if (_tunnelQuantity >= 0) { + config.setProperty("option.inbound.quantity", Integer.toString(_tunnelQuantity)); + config.setProperty("option.outbound.quantity", Integer.toString(_tunnelQuantity)); + } + if (_tunnelDepth >= 0) { + config.setProperty("option.inbound.length", Integer.toString(_tunnelDepth)); + config.setProperty("option.outbound.length", Integer.toString(_tunnelDepth)); + } + if (_tunnelVariance >= 0) { + config.setProperty("option.inbound.lengthVariance", Integer.toString(_tunnelVariance)); + config.setProperty("option.outbound.lengthVariance", Integer.toString(_tunnelVariance)); + } + if (_tunnelBackupQuantity >= 0) { + config.setProperty("option.inbound.backupQuantity", Integer.toString(_tunnelBackupQuantity)); + config.setProperty("option.outbound.backupQuantity", Integer.toString(_tunnelBackupQuantity)); + } + } +} 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 cd4c406c2579da164e749e489e0cbd539d0e45ad..d75b418f4a8c1470fb1501028abff98c7e6a1038 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java @@ -27,6 +27,7 @@ import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase; import net.i2p.i2ptunnel.I2PTunnelHTTPServer; import net.i2p.i2ptunnel.I2PTunnelIRCClient; import net.i2p.i2ptunnel.I2PTunnelServer; +import net.i2p.i2ptunnel.TunnelConfig; import net.i2p.i2ptunnel.TunnelController; import net.i2p.i2ptunnel.TunnelControllerGroup; import net.i2p.util.Addresses; @@ -359,31 +360,31 @@ public class EditBean extends IndexBean { /** all of these are @since 0.8.3 */ public int getLimitMinute(int tunnel) { - return getProperty(tunnel, PROP_MAX_CONNS_MIN, 0); + return getProperty(tunnel, TunnelConfig.PROP_MAX_CONNS_MIN, 0); } public int getLimitHour(int tunnel) { - return getProperty(tunnel, PROP_MAX_CONNS_HOUR, 0); + return getProperty(tunnel, TunnelConfig.PROP_MAX_CONNS_HOUR, 0); } public int getLimitDay(int tunnel) { - return getProperty(tunnel, PROP_MAX_CONNS_DAY, 0); + return getProperty(tunnel, TunnelConfig.PROP_MAX_CONNS_DAY, 0); } public int getTotalMinute(int tunnel) { - return getProperty(tunnel, PROP_MAX_TOTAL_CONNS_MIN, 0); + return getProperty(tunnel, TunnelConfig.PROP_MAX_TOTAL_CONNS_MIN, 0); } public int getTotalHour(int tunnel) { - return getProperty(tunnel, PROP_MAX_TOTAL_CONNS_HOUR, 0); + return getProperty(tunnel, TunnelConfig.PROP_MAX_TOTAL_CONNS_HOUR, 0); } public int getTotalDay(int tunnel) { - return getProperty(tunnel, PROP_MAX_TOTAL_CONNS_DAY, 0); + return getProperty(tunnel, TunnelConfig.PROP_MAX_TOTAL_CONNS_DAY, 0); } public int getMaxStreams(int tunnel) { - return getProperty(tunnel, PROP_MAX_STREAMS, 0); + return getProperty(tunnel, TunnelConfig.PROP_MAX_STREAMS, 0); } /** @@ -499,12 +500,12 @@ public class EditBean extends IndexBean { Map<String, String> sorted = new TreeMap<String, String>(); for (Map.Entry<Object, Object> e : opts.entrySet()) { String key = (String)e.getKey(); - if (_noShowSet.contains(key)) + if (TunnelConfig._noShowSet.contains(key)) continue; // leave in for HTTP and Connect so it can get migrated to MD5 // hide for SOCKS until migrated to MD5 if ((!isMD5Proxy) && - _nonProxyNoShowSet.contains(key)) + TunnelConfig._nonProxyNoShowSet.contains(key)) continue; sorted.put(key, (String)e.getValue()); } 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 3b6e7cdb9537f2ed90facdd375141fa025eed771..f16d64b5963b01f2dcfdcc21e8275beffa1b7822 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -10,46 +10,30 @@ package net.i2p.i2ptunnel.web; import java.io.File; import java.io.IOException; -import java.security.GeneralSecurityException; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Properties; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.concurrent.ConcurrentHashMap; import net.i2p.I2PAppContext; import net.i2p.app.ClientAppManager; import net.i2p.app.Outproxy; -import net.i2p.client.I2PClient; -import net.i2p.crypto.KeyGenerator; -import net.i2p.crypto.SigType; -import net.i2p.data.Base64; import net.i2p.data.Certificate; import net.i2p.data.DataHelper; import net.i2p.data.Destination; import net.i2p.data.PrivateKeyFile; import net.i2p.data.SessionKey; -import net.i2p.data.SimpleDataStructure; import net.i2p.i2ptunnel.I2PTunnelClientBase; -import net.i2p.i2ptunnel.I2PTunnelConnectClient; import net.i2p.i2ptunnel.I2PTunnelHTTPClient; -import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase; import net.i2p.i2ptunnel.I2PTunnelHTTPServer; -import net.i2p.i2ptunnel.I2PTunnelIRCClient; import net.i2p.i2ptunnel.I2PTunnelServer; import net.i2p.i2ptunnel.SSLClientUtil; +import net.i2p.i2ptunnel.TunnelConfig; import net.i2p.i2ptunnel.TunnelController; import net.i2p.i2ptunnel.TunnelControllerGroup; import net.i2p.util.Addresses; -import net.i2p.util.ConcurrentHashSet; import net.i2p.util.FileUtil; import net.i2p.util.Log; -import net.i2p.util.PasswordManager; import net.i2p.util.SecureFile; /** @@ -71,36 +55,11 @@ public class IndexBean { private String _curNonce; //private long _nextNonce; - private String _type; - private String _name; - private String _description; - private String _i2cpHost; - private String _i2cpPort; - private String _tunnelDepth; - private String _tunnelQuantity; - private String _tunnelVariance; - private String _tunnelBackupQuantity; - private boolean _connectDelay; - private String _customOptions; - private String _proxyList; - private String _port; - private String _reachableBy; - private String _targetDestination; - private String _targetHost; - private String _targetPort; - private String _spoofedHost; - private String _privKeyFile; - private String _profile; - private boolean _startOnLoad; - private boolean _sharedClient; + private TunnelConfig _config; private boolean _removeConfirmed; - private final Set<String> _booleanOptions; - private final Map<String, String> _otherOptions; private int _hashCashValue; private int _certType; private String _certSigner; - private String _newProxyUser; - private String _newProxyPW; public static final int RUNNING = 1; public static final int STARTING = 2; @@ -114,7 +73,6 @@ public class IndexBean { /** store nonces in a static FIFO instead of in System Properties @since 0.8.1 */ private static final List<String> _nonces = new ArrayList<String>(MAX_NONCES + 1); - static final String CLIENT_NICKNAME = "shared clients"; public static final String PROP_THEME_NAME = "routerconsole.theme"; public static final String DEFAULT_THEME = "light"; public static final String PROP_CSS_DISABLED = "routerconsole.css.disabled"; @@ -140,8 +98,7 @@ public class IndexBean { _tunnel = -1; _curNonce = "-1"; addNonce(); - _booleanOptions = new ConcurrentHashSet<String>(4); - _otherOptions = new ConcurrentHashMap<String, String>(4); + _config = new TunnelConfig(); } /** @@ -332,24 +289,9 @@ public class IndexBean { // if it belongs to a shared destination, and is of supported type if (Boolean.parseBoolean(c.getSharedClient()) && isClient(c.getType())) { Properties cOpt = c.getConfig(""); - if (_tunnelQuantity != null) { - cOpt.setProperty("option.inbound.quantity", _tunnelQuantity); - cOpt.setProperty("option.outbound.quantity", _tunnelQuantity); - } - if (_tunnelDepth != null) { - cOpt.setProperty("option.inbound.length", _tunnelDepth); - cOpt.setProperty("option.outbound.length", _tunnelDepth); - } - if (_tunnelVariance != null) { - cOpt.setProperty("option.inbound.lengthVariance", _tunnelVariance); - cOpt.setProperty("option.outbound.lengthVariance", _tunnelVariance); - } - if (_tunnelBackupQuantity != null) { - cOpt.setProperty("option.inbound.backupQuantity", _tunnelBackupQuantity); - cOpt.setProperty("option.outbound.backupQuantity", _tunnelBackupQuantity); - } - cOpt.setProperty("option.inbound.nickname", CLIENT_NICKNAME); - cOpt.setProperty("option.outbound.nickname", CLIENT_NICKNAME); + _config.updateTunnelQuantities(config); + cOpt.setProperty("option.inbound.nickname", TunnelConfig.SHARED_CLIENT_NICKNAME); + cOpt.setProperty("option.outbound.nickname", TunnelConfig.SHARED_CLIENT_NICKNAME); c.setConfig(cOpt, ""); } @@ -396,7 +338,7 @@ public class IndexBean { // Use configured file name if available, not the one from the form. String pk = cur.getPrivKeyFile(); if (pk == null) - pk = _privKeyFile; + pk = _config.getPrivKeyFile(); if (pk != null && pk.startsWith("i2ptunnel") && pk.endsWith("-privKeys.dat") && ((!isClient(cur.getType())) || cur.getPersistentClientKey())) { File pkf = new File(_context.getConfigDir(), pk); @@ -743,80 +685,103 @@ public class IndexBean { * */ public void setType(String type) { - _type = (type != null ? type.trim() : null); + _config.setType(type); } - String getType() { return _type; } + String getType() { return _config.getType(); } /** Short name of the tunnel */ public void setName(String name) { - _name = (name != null ? name.trim() : null); + _config.setName(name); } /** one line description */ public void setNofilter_description(String description) { - // '#' will blow up DataHelper.storeProps() - _description = (description != null ? description.replace('#', ' ').trim() : null); + _config.setDescription(description); } /** I2CP host the router is on, ignored when in router context */ public void setClientHost(String host) { - _i2cpHost = (host != null ? host.trim() : null); + _config.setClientHost(host); } /** I2CP port the router is on, ignored when in router context */ public void setClientport(String port) { - _i2cpPort = (port != null ? port.trim() : null); + _config.setClientPort(port); } /** how many hops to use for inbound tunnels */ - public void setTunnelDepth(String tunnelDepth) { - _tunnelDepth = (tunnelDepth != null ? tunnelDepth.trim() : null); + public void setTunnelDepth(String tunnelDepth) { + if (tunnelDepth != null) { + try { + _config.setTunnelDepth(Integer.parseInt(tunnelDepth.trim())); + } catch (NumberFormatException nfe) {} + } } /** how many parallel inbound tunnels to use */ - public void setTunnelQuantity(String tunnelQuantity) { - _tunnelQuantity = (tunnelQuantity != null ? tunnelQuantity.trim() : null); + public void setTunnelQuantity(String tunnelQuantity) { + if (tunnelQuantity != null) { + try { + _config.setTunnelQuantity(Integer.parseInt(tunnelQuantity.trim())); + } catch (NumberFormatException nfe) {} + } } /** how much randomisation to apply to the depth of tunnels */ - public void setTunnelVariance(String tunnelVariance) { - _tunnelVariance = (tunnelVariance != null ? tunnelVariance.trim() : null); + public void setTunnelVariance(String tunnelVariance) { + if (tunnelVariance != null) { + try { + _config.setTunnelVariance(Integer.parseInt(tunnelVariance.trim())); + } catch (NumberFormatException nfe) {} + } } /** how many tunnels to hold in reserve to guard against failures */ - public void setTunnelBackupQuantity(String tunnelBackupQuantity) { - _tunnelBackupQuantity = (tunnelBackupQuantity != null ? tunnelBackupQuantity.trim() : null); + public void setTunnelBackupQuantity(String tunnelBackupQuantity) { + if (tunnelBackupQuantity != null) { + try { + _config.setTunnelBackupQuantity(Integer.parseInt(tunnelBackupQuantity.trim())); + } catch (NumberFormatException nfe) {} + } } /** what I2P session overrides should be used */ public void setNofilter_customOptions(String customOptions) { - _customOptions = (customOptions != null ? customOptions.trim() : null); + _config.setCustomOptions(customOptions); } /** what HTTP outproxies should be used (httpclient specific) */ public void setProxyList(String proxyList) { - _proxyList = (proxyList != null ? proxyList.trim() : null); + _config.setProxyList(proxyList); } /** what port should this client/httpclient/ircclient listen on */ public void setPort(String port) { - _port = (port != null ? port.trim() : null); + if (port != null) { + try { + _config.setPort(Integer.parseInt(port.trim())); + } catch (NumberFormatException nfe) {} + } } /** * what interface should this client/httpclient/ircclient listen on */ public void setReachableBy(String reachableBy) { - _reachableBy = (reachableBy != null ? reachableBy.trim() : null); + _config.setReachableBy(reachableBy); } /** What peer does this client tunnel point at */ public void setTargetDestination(String dest) { - _targetDestination = (dest != null ? dest.trim() : null); + _config.setTargetDestination(dest); } /** What host does this server tunnel point at */ public void setTargetHost(String host) { - _targetHost = (host != null ? host.trim() : null); + _config.setTargetHost(host); } /** What port does this server tunnel point at */ - public void setTargetPort(String port) { - _targetPort = (port != null ? port.trim() : null); + public void setTargetPort(String port) { + if (port != null) { + try { + _config.setTargetPort(Integer.parseInt(port.trim())); + } catch (NumberFormatException nfe) {} + } } /** What host does this http server tunnel spoof */ public void setSpoofedHost(String host) { - _spoofedHost = (host != null ? host.trim() : null); + _config.setSpoofedHost(host); } /** What filename is this server tunnel's private keys stored in */ public void setPrivKeyFile(String file) { - _privKeyFile = (file != null ? file.trim() : null); + _config.setPrivKeyFile(file); } /** * If called with any value (and the form submitted with action=Remove), @@ -830,39 +795,39 @@ public class IndexBean { * loaded (aka right now and whenever the router is started up) */ public void setStartOnLoad(String moo) { - _startOnLoad = true; + _config.setStartOnLoad(true); } public void setShared(String moo) { - _sharedClient=true; + _config.setShared(true); } public void setShared(boolean val) { - _sharedClient=val; + _config.setShared(val); } public void setConnectDelay(String moo) { - _connectDelay = true; + _config.setConnectDelay(true); } public void setProfile(String profile) { - _profile = profile; + _config.setProfile(profile); } public void setReduce(String moo) { - _booleanOptions.add("i2cp.reduceOnIdle"); + _config.setReduce(); } public void setClose(String moo) { - _booleanOptions.add("i2cp.closeOnIdle"); + _config.setClose(); } public void setEncrypt(String moo) { - _booleanOptions.add("i2cp.encryptLeaseSet"); + _config.setEncrypt(); } /** @since 0.8.9 */ public void setDCC(String moo) { - _booleanOptions.add(I2PTunnelIRCClient.PROP_DCC); + _config.setDCC(); } /** @since 0.9.9 */ public void setUseSSL(String moo) { - _booleanOptions.add(I2PTunnelServer.PROP_USE_SSL); + _config.setUseSSL(); } /** @since 0.9.9 */ @@ -877,7 +842,7 @@ public class IndexBean { /** @since 0.9.12 */ public void setRejectInproxy(String moo) { - _booleanOptions.add(I2PTunnelHTTPServer.OPT_REJECT_INPROXY); + _config.setRejectInproxy(); } /** @since 0.9.12 */ @@ -892,171 +857,171 @@ public class IndexBean { /** @since 0.9.13 */ public void setUniqueLocal(String moo) { - _booleanOptions.add(I2PTunnelServer.PROP_UNIQUE_LOCAL); + _config.setUniqueLocal(); } protected static final String PROP_ENABLE_ACCESS_LIST = "i2cp.enableAccessList"; protected static final String PROP_ENABLE_BLACKLIST = "i2cp.enableBlackList"; public void setAccessMode(String val) { - if ("1".equals(val)) - _booleanOptions.add(PROP_ENABLE_ACCESS_LIST); - else if ("2".equals(val)) - _booleanOptions.add(PROP_ENABLE_BLACKLIST); + _config.setAccessMode(val); } public void setDelayOpen(String moo) { - _booleanOptions.add("i2cp.delayOpen"); + _config.setDelayOpen(); } public void setNewDest(String val) { - if ("1".equals(val)) - _booleanOptions.add("i2cp.newDestOnResume"); - else if ("2".equals(val)) - _booleanOptions.add("persistentClientKey"); + _config.setNewDest(val); } public void setReduceTime(String val) { if (val != null) { try { - _otherOptions.put("i2cp.reduceIdleTime", Integer.toString(Integer.parseInt(val.trim()) * 60*1000)); + _config.setReduceTime(Integer.parseInt(val.trim())); } catch (NumberFormatException nfe) {} } } public void setReduceCount(String val) { - if (val != null) - _otherOptions.put("i2cp.reduceQuantity", val.trim()); + if (val != null) { + try { + _config.setReduceCount(Integer.parseInt(val.trim())); + } catch (NumberFormatException nfe) {} + } } public void setEncryptKey(String val) { - if (val != null) - _otherOptions.put("i2cp.leaseSetKey", val.trim()); + _config.setEncryptKey(val); } public void setAccessList(String val) { - if (val != null) - _otherOptions.put("i2cp.accessList", val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ",")); + _config.setAccessList(val); } public void setJumpList(String val) { - if (val != null) - _otherOptions.put(I2PTunnelHTTPClient.PROP_JUMP_SERVERS, val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ",")); + _config.setJumpList(val); } public void setCloseTime(String val) { if (val != null) { try { - _otherOptions.put("i2cp.closeIdleTime", Integer.toString(Integer.parseInt(val.trim()) * 60*1000)); + _config.setCloseTime(Integer.parseInt(val.trim())); } catch (NumberFormatException nfe) {} } } /** @since 0.9.14 */ public void setAllowUserAgent(String moo) { - _booleanOptions.add(I2PTunnelHTTPClient.PROP_USER_AGENT); + _config.setAllowUserAgent(); } /** @since 0.9.14 */ public void setAllowReferer(String moo) { - _booleanOptions.add(I2PTunnelHTTPClient.PROP_REFERER); + _config.setAllowReferer(); } /** @since 0.9.14 */ public void setAllowAccept(String moo) { - _booleanOptions.add(I2PTunnelHTTPClient.PROP_ACCEPT); + _config.setAllowAccept(); } /** @since 0.9.14 */ public void setAllowInternalSSL(String moo) { - _booleanOptions.add(I2PTunnelHTTPClient.PROP_INTERNAL_SSL); + _config.setAllowInternalSSL(); } /** @since 0.9.18 */ public void setMultihome(String moo) { - _booleanOptions.add("shouldBundleReplyInfo"); + _config.setMultihome(); } /** all proxy auth @since 0.8.2 */ public void setProxyAuth(String s) { - if (s != null) - _otherOptions.put(I2PTunnelHTTPClientBase.PROP_AUTH, I2PTunnelHTTPClientBase.DIGEST_AUTH); + _config.setProxyAuth(s); } public void setProxyUsername(String s) { - if (s != null) - _newProxyUser = s.trim(); + _config.setProxyUsername(s); } public void setNofilter_proxyPassword(String s) { - if (s != null) - _newProxyPW = s.trim(); + _config.setProxyPassword(s); } public void setOutproxyAuth(String s) { - _otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH, I2PTunnelHTTPClientBase.DIGEST_AUTH); + _config.setOutproxyAuth(s); } public void setOutproxyUsername(String s) { - if (s != null) - _otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_USER, s.trim()); + _config.setOutproxyUsername(s); } public void setNofilter_outproxyPassword(String s) { - if (s != null) - _otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_PW, s.trim()); + _config.setOutproxyPassword(s); } /** @since 0.9.11 */ public void setSslProxies(String s) { - if (s != null) - _otherOptions.put(I2PTunnelHTTPClient.PROP_SSL_OUTPROXIES, s.trim().replace(" ", ",")); + _config.setSslProxies(s); } /** @since 0.9.11 */ public void setUseOutproxyPlugin(String moo) { - _booleanOptions.add(I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN); + _config.setUseOutproxyPlugin(); } - - /** all of these are @since 0.8.3 */ - protected static final String PROP_MAX_CONNS_MIN = "i2p.streaming.maxConnsPerMinute"; - protected static final String PROP_MAX_CONNS_HOUR = "i2p.streaming.maxConnsPerHour"; - protected static final String PROP_MAX_CONNS_DAY = "i2p.streaming.maxConnsPerDay"; - protected static final String PROP_MAX_TOTAL_CONNS_MIN = "i2p.streaming.maxTotalConnsPerMinute"; - protected static final String PROP_MAX_TOTAL_CONNS_HOUR = "i2p.streaming.maxTotalConnsPerHour"; - protected static final String PROP_MAX_TOTAL_CONNS_DAY = "i2p.streaming.maxTotalConnsPerDay"; - protected static final String PROP_MAX_STREAMS = "i2p.streaming.maxConcurrentStreams"; public void setLimitMinute(String s) { - if (s != null) - _otherOptions.put(PROP_MAX_CONNS_MIN, s.trim()); + if (s != null) { + try { + _config.setLimitMinute(Integer.parseInt(s.trim())); + } catch (NumberFormatException nfe) {} + } } public void setLimitHour(String s) { - if (s != null) - _otherOptions.put(PROP_MAX_CONNS_HOUR, s.trim()); + if (s != null) { + try { + _config.setLimitHour(Integer.parseInt(s.trim())); + } catch (NumberFormatException nfe) {} + } } public void setLimitDay(String s) { - if (s != null) - _otherOptions.put(PROP_MAX_CONNS_DAY, s.trim()); + if (s != null) { + try { + _config.setLimitDay(Integer.parseInt(s.trim())); + } catch (NumberFormatException nfe) {} + } } public void setTotalMinute(String s) { - if (s != null) - _otherOptions.put(PROP_MAX_TOTAL_CONNS_MIN, s.trim()); + if (s != null) { + try { + _config.setTotalMinute(Integer.parseInt(s.trim())); + } catch (NumberFormatException nfe) {} + } } public void setTotalHour(String s) { - if (s != null) - _otherOptions.put(PROP_MAX_TOTAL_CONNS_HOUR, s.trim()); + if (s != null) { + try { + _config.setTotalHour(Integer.parseInt(s.trim())); + } catch (NumberFormatException nfe) {} + } } public void setTotalDay(String s) { - if (s != null) - _otherOptions.put(PROP_MAX_TOTAL_CONNS_DAY, s.trim()); + if (s != null) { + try { + _config.setTotalDay(Integer.parseInt(s.trim())); + } catch (NumberFormatException nfe) {} + } } public void setMaxStreams(String s) { - if (s != null) - _otherOptions.put(PROP_MAX_STREAMS, s.trim()); + if (s != null) { + try { + _config.setMaxStreams(Integer.parseInt(s.trim())); + } catch (NumberFormatException nfe) {} + } } /** @@ -1064,28 +1029,43 @@ public class IndexBean { * @since 0.9.9 */ public void setPostMax(String s) { - if (s != null) - _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_MAX, s.trim()); + if (s != null) { + try { + _config.setPostMax(Integer.parseInt(s.trim())); + } catch (NumberFormatException nfe) {} + } } public void setPostTotalMax(String s) { - if (s != null) - _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_TOTAL_MAX, s.trim()); + if (s != null) { + try { + _config.setPostTotalMax(Integer.parseInt(s.trim())); + } catch (NumberFormatException nfe) {} + } } public void setPostCheckTime(String s) { - if (s != null) - _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_WINDOW, Integer.toString(Integer.parseInt(s.trim()) * 60)); + if (s != null) { + try { + _config.setPostCheckTime(Integer.parseInt(s.trim())); + } catch (NumberFormatException nfe) {} + } } public void setPostBanTime(String s) { - if (s != null) - _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_BAN_TIME, Integer.toString(Integer.parseInt(s.trim()) * 60)); + if (s != null) { + try { + _config.setPostBanTime(Integer.parseInt(s.trim())); + } catch (NumberFormatException nfe) {} + } } public void setPostTotalBanTime(String s) { - if (s != null) - _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME, Integer.toString(Integer.parseInt(s.trim()) * 60)); + if (s != null) { + try { + _config.setPostTotalBanTime(Integer.parseInt(s.trim())); + } catch (NumberFormatException nfe) {} + } } /** params needed for hashcash and dest modification */ @@ -1112,7 +1092,7 @@ public class IndexBean { /** @since 0.9.12 */ public void setSigType(String val) { if (val != null) { - _otherOptions.put(I2PClient.PROP_SIGTYPE, val); + _config.setSigType(val); if (val.equals("0")) _certType = 0; else @@ -1127,28 +1107,25 @@ public class IndexBean { * @since 0.9.18 */ public void setKey1(String s) { - if (s != null) - _otherOptions.put("inbound.randomKey", s.trim()); + _config.setInboundRandomKey(s); } public void setKey2(String s) { - if (s != null) - _otherOptions.put("outbound.randomKey", s.trim()); + _config.setOutboundRandomKey(s); } public void setKey3(String s) { - if (s != null) - _otherOptions.put("i2cp.leaseSetSigningPrivateKey", s.trim()); + _config.setLeaseSetSigningPrivateKey(s); } public void setKey4(String s) { - if (s != null) - _otherOptions.put("i2cp.leaseSetPrivateKey", s.trim()); + _config.setLeaseSetPrivateKey(s); } /** Modify or create a destination */ private String modifyDestination() { - if (_privKeyFile == null || _privKeyFile.trim().length() <= 0) + String privKeyFile = _config.getPrivKeyFile(); + if (privKeyFile == null) return "Private Key File not specified"; TunnelController tun = getController(_tunnel); @@ -1162,9 +1139,9 @@ public class IndexBean { return "Tunnel must be stopped before modifying destination"; } - File keyFile = new File(_privKeyFile); + File keyFile = new File(privKeyFile); if (!keyFile.isAbsolute()) - keyFile = new File(_context.getConfigDir(), _privKeyFile); + keyFile = new File(_context.getConfigDir(), privKeyFile); PrivateKeyFile pkf = new PrivateKeyFile(keyFile); try { pkf.createIfAbsent(); @@ -1194,7 +1171,7 @@ public class IndexBean { } if (signerPKF == null || signerPKF.length() <= 0) return "Signing destination " + _certSigner + " not found"; - if (_privKeyFile.equals(signerPKF)) + if (privKeyFile.equals(signerPKF)) return "Self-signed destinations not allowed"; Certificate c = pkf.setSignedCert(new PrivateKeyFile(signerPKF)); if (c == null) @@ -1243,310 +1220,10 @@ public class IndexBean { * */ private Properties getConfig() { - Properties config = new Properties(); - updateConfigGeneric(config); - - if ((isClient(_type) && !TunnelController.TYPE_STREAMR_CLIENT.equals(_type)) || - TunnelController.TYPE_STREAMR_SERVER.equals(_type)) { - // streamrserver uses interface - if (_reachableBy != null) - config.setProperty(TunnelController.PROP_INTFC, _reachableBy); - else - config.setProperty(TunnelController.PROP_INTFC, ""); - } else { - // streamrclient uses targetHost - if (_targetHost != null) - config.setProperty(TunnelController.PROP_TARGET_HOST, _targetHost); - } - - if (isClient(_type)) { - // generic client stuff - if (_port != null) - config.setProperty(TunnelController.PROP_LISTEN_PORT, _port); - config.setProperty(TunnelController.PROP_SHARED, _sharedClient + ""); - for (String p : _booleanClientOpts) - config.setProperty(OPT + p, "" + _booleanOptions.contains(p)); - for (String p : _otherClientOpts) { - if (_otherOptions.containsKey(p)) - config.setProperty(OPT + p, _otherOptions.get(p)); - } - } else { - // generic server stuff - if (_targetPort != null) - config.setProperty(TunnelController.PROP_TARGET_PORT, _targetPort); - for (String p : _booleanServerOpts) - config.setProperty(OPT + p, "" + _booleanOptions.contains(p)); - for (String p : _otherServerOpts) { - if (_otherOptions.containsKey(p)) - config.setProperty(OPT + p, _otherOptions.get(p)); - } - } - - // generic proxy stuff - if (TunnelController.TYPE_HTTP_CLIENT.equals(_type) || TunnelController.TYPE_CONNECT.equals(_type) || - TunnelController.TYPE_SOCKS.equals(_type) ||TunnelController.TYPE_SOCKS_IRC.equals(_type)) { - for (String p : _booleanProxyOpts) - config.setProperty(OPT + p, "" + _booleanOptions.contains(p)); - if (_proxyList != null) - config.setProperty(TunnelController.PROP_PROXIES, _proxyList); - } - - // Proxy auth including migration to MD5 - if (TunnelController.TYPE_HTTP_CLIENT.equals(_type) || TunnelController.TYPE_CONNECT.equals(_type)) { - // Migrate even if auth is disabled - // go get the old from custom options that updateConfigGeneric() put in there - String puser = OPT + I2PTunnelHTTPClientBase.PROP_USER; - String user = config.getProperty(puser); - String ppw = OPT + I2PTunnelHTTPClientBase.PROP_PW; - String pw = config.getProperty(ppw); - if (user != null && pw != null && user.length() > 0 && pw.length() > 0) { - String pmd5 = OPT + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_PREFIX + - user + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_SUFFIX; - if (config.getProperty(pmd5) == null) { - // not in there, migrate - String realm = _type.equals(TunnelController.TYPE_HTTP_CLIENT) ? I2PTunnelHTTPClient.AUTH_REALM - : I2PTunnelConnectClient.AUTH_REALM; - String hex = PasswordManager.md5Hex(realm, user, pw); - if (hex != null) { - config.setProperty(pmd5, hex); - config.remove(puser); - config.remove(ppw); - } - } - } - // New user/password - String auth = _otherOptions.get(I2PTunnelHTTPClientBase.PROP_AUTH); - if (auth != null && !auth.equals("false")) { - if (_newProxyUser != null && _newProxyPW != null && - _newProxyUser.length() > 0 && _newProxyPW.length() > 0) { - String pmd5 = OPT + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_PREFIX + - _newProxyUser + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_SUFFIX; - String realm = _type.equals(TunnelController.TYPE_HTTP_CLIENT) ? I2PTunnelHTTPClient.AUTH_REALM - : I2PTunnelConnectClient.AUTH_REALM; - String hex = PasswordManager.md5Hex(realm, _newProxyUser, _newProxyPW); - if (hex != null) - config.setProperty(pmd5, hex); - } - } - } - - if (TunnelController.TYPE_IRC_CLIENT.equals(_type) || - TunnelController.TYPE_STD_CLIENT.equals(_type) || - TunnelController.TYPE_STREAMR_CLIENT.equals(_type)) { - if (_targetDestination != null) - config.setProperty(TunnelController.PROP_DEST, _targetDestination); - } else if (TunnelController.TYPE_HTTP_SERVER.equals(_type) || - TunnelController.TYPE_HTTP_BIDIR_SERVER.equals(_type)) { - if (_spoofedHost != null) - config.setProperty(TunnelController.PROP_SPOOFED_HOST, _spoofedHost); - for (String p : _httpServerOpts) - if (_otherOptions.containsKey(p)) - config.setProperty(OPT + p, _otherOptions.get(p)); - } - if (TunnelController.TYPE_HTTP_BIDIR_SERVER.equals(_type)) { - if (_port != null) - config.setProperty(TunnelController.PROP_LISTEN_PORT, _port); - if (_reachableBy != null) - config.setProperty(TunnelController.PROP_INTFC, _reachableBy); - else if (_targetHost != null) - config.setProperty(TunnelController.PROP_INTFC, _targetHost); - else - config.setProperty(TunnelController.PROP_INTFC, ""); - } - - if (TunnelController.TYPE_IRC_CLIENT.equals(_type)) { - boolean dcc = _booleanOptions.contains(I2PTunnelIRCClient.PROP_DCC); - config.setProperty(OPT + I2PTunnelIRCClient.PROP_DCC, - "" + dcc); - // add some sane server options since they aren't in the GUI (yet) - if (dcc) { - config.setProperty(OPT + PROP_MAX_CONNS_MIN, "3"); - config.setProperty(OPT + PROP_MAX_CONNS_HOUR, "10"); - config.setProperty(OPT + PROP_MAX_TOTAL_CONNS_MIN, "5"); - config.setProperty(OPT + PROP_MAX_TOTAL_CONNS_HOUR, "25"); - } - } - - if (!isClient(_type) || _booleanOptions.contains("persistentClientKey")) { - // As of 0.9.17, add a persistent random key if not present - String p = OPT + "inbound.randomKey"; - if (!config.containsKey(p)) { - byte[] rk = new byte[32]; - _context.random().nextBytes(rk); - config.setProperty(p, Base64.encode(rk)); - p = OPT + "outbound.randomKey"; - _context.random().nextBytes(rk); - config.setProperty(p, Base64.encode(rk)); - } - // As of 0.9.18, add persistent leaseset keys if not present - // but only if we know the sigtype - p = OPT + "i2cp.leaseSetSigningPrivateKey"; - Destination dest = getDestination(_tunnel); - if (dest != null && !config.containsKey(p)) { - try { - SigType type = dest.getSigType(); - SimpleDataStructure keys[] = KeyGenerator.getInstance().generateSigningKeys(type); - config.setProperty(p, type.name() + ':' + keys[1].toBase64()); - p = OPT + "i2cp.leaseSetPrivateKey"; - keys = KeyGenerator.getInstance().generatePKIKeys(); - config.setProperty(p, "ELGAMAL_2048:" + keys[1].toBase64()); - } catch (GeneralSecurityException gse) { - // so much for that - } - } - } - - return config; - } - - private static final String _noShowOpts[] = { - "inbound.length", "outbound.length", "inbound.lengthVariance", "outbound.lengthVariance", - "inbound.backupQuantity", "outbound.backupQuantity", "inbound.quantity", "outbound.quantity", - "inbound.nickname", "outbound.nickname", "i2p.streaming.connectDelay", "i2p.streaming.maxWindowSize", - I2PTunnelIRCClient.PROP_DCC - }; - private static final String _booleanClientOpts[] = { - "i2cp.reduceOnIdle", "i2cp.closeOnIdle", "i2cp.newDestOnResume", "persistentClientKey", "i2cp.delayOpen", - I2PTunnelClientBase.PROP_USE_SSL, - }; - private static final String _booleanProxyOpts[] = { - I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH, - I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN, - I2PTunnelHTTPClient.PROP_USER_AGENT, - I2PTunnelHTTPClient.PROP_REFERER, - I2PTunnelHTTPClient.PROP_ACCEPT, - I2PTunnelHTTPClient.PROP_INTERNAL_SSL - }; - private static final String _booleanServerOpts[] = { - "i2cp.reduceOnIdle", "i2cp.encryptLeaseSet", PROP_ENABLE_ACCESS_LIST, PROP_ENABLE_BLACKLIST, - I2PTunnelServer.PROP_USE_SSL, - I2PTunnelHTTPServer.OPT_REJECT_INPROXY, - I2PTunnelServer.PROP_UNIQUE_LOCAL, - "shouldBundleReplyInfo" - }; - private static final String _otherClientOpts[] = { - "i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.closeIdleTime", - "outproxyUsername", "outproxyPassword", - I2PTunnelHTTPClient.PROP_JUMP_SERVERS, - I2PTunnelHTTPClientBase.PROP_AUTH, - I2PClient.PROP_SIGTYPE, - I2PTunnelHTTPClient.PROP_SSL_OUTPROXIES, - // following are mostly server but could also be persistent client - "inbound.randomKey", "outbound.randomKey", "i2cp.leaseSetSigningPrivateKey", "i2cp.leaseSetPrivateKey" - }; - private static final String _otherServerOpts[] = { - "i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.leaseSetKey", "i2cp.accessList", - PROP_MAX_CONNS_MIN, PROP_MAX_CONNS_HOUR, PROP_MAX_CONNS_DAY, - PROP_MAX_TOTAL_CONNS_MIN, PROP_MAX_TOTAL_CONNS_HOUR, PROP_MAX_TOTAL_CONNS_DAY, - PROP_MAX_STREAMS, I2PClient.PROP_SIGTYPE, - "inbound.randomKey", "outbound.randomKey", "i2cp.leaseSetSigningPrivateKey", "i2cp.leaseSetPrivateKey" - }; - private static final String _httpServerOpts[] = { - I2PTunnelHTTPServer.OPT_POST_WINDOW, - I2PTunnelHTTPServer.OPT_POST_BAN_TIME, - I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME, - I2PTunnelHTTPServer.OPT_POST_MAX, - I2PTunnelHTTPServer.OPT_POST_TOTAL_MAX - }; - - /** - * do NOT add these to noShoOpts, we must leave them in for HTTPClient and ConnectCLient - * so they will get migrated to MD5 - * TODO migrate socks to MD5 - */ - private static final String _otherProxyOpts[] = { - "proxyUsername", "proxyPassword" - }; - - protected static final Set<String> _noShowSet = new HashSet<String>(128); - protected static final Set<String> _nonProxyNoShowSet = new HashSet<String>(4); - static { - _noShowSet.addAll(Arrays.asList(_noShowOpts)); - _noShowSet.addAll(Arrays.asList(_booleanClientOpts)); - _noShowSet.addAll(Arrays.asList(_booleanProxyOpts)); - _noShowSet.addAll(Arrays.asList(_booleanServerOpts)); - _noShowSet.addAll(Arrays.asList(_otherClientOpts)); - _noShowSet.addAll(Arrays.asList(_otherServerOpts)); - _noShowSet.addAll(Arrays.asList(_httpServerOpts)); - _nonProxyNoShowSet.addAll(Arrays.asList(_otherProxyOpts)); - } - - private void updateConfigGeneric(Properties config) { - config.setProperty(TunnelController.PROP_TYPE, _type); - if (_name != null) - config.setProperty(TunnelController.PROP_NAME, _name); - if (_description != null) - config.setProperty(TunnelController.PROP_DESCR, _description); - if (!_context.isRouterContext()) { - if (_i2cpHost != null) - config.setProperty(TunnelController.PROP_I2CP_HOST, _i2cpHost); - if ( (_i2cpPort != null) && (_i2cpPort.trim().length() > 0) ) { - config.setProperty(TunnelController.PROP_I2CP_PORT, _i2cpPort); - } else { - config.setProperty(TunnelController.PROP_I2CP_PORT, "7654"); - } - } - if (_privKeyFile != null) - config.setProperty(TunnelController.PROP_FILE, _privKeyFile); - - if (_customOptions != null) { - StringTokenizer tok = new StringTokenizer(_customOptions); - while (tok.hasMoreTokens()) { - String pair = tok.nextToken(); - int eq = pair.indexOf('='); - if ( (eq <= 0) || (eq >= pair.length()) ) - continue; - String key = pair.substring(0, eq); - if (_noShowSet.contains(key)) - continue; - // leave in for HTTP and Connect so it can get migrated to MD5 - // hide for SOCKS until migrated to MD5 - if ((!TunnelController.TYPE_HTTP_CLIENT.equals(_type)) && - (!TunnelController.TYPE_CONNECT.equals(_type)) && - _nonProxyNoShowSet.contains(key)) - continue; - String val = pair.substring(eq+1); - config.setProperty(OPT + key, val); - } - } - - config.setProperty(TunnelController.PROP_START, _startOnLoad + ""); - - if (_tunnelQuantity != null) { - config.setProperty("option.inbound.quantity", _tunnelQuantity); - config.setProperty("option.outbound.quantity", _tunnelQuantity); - } - if (_tunnelDepth != null) { - config.setProperty("option.inbound.length", _tunnelDepth); - config.setProperty("option.outbound.length", _tunnelDepth); - } - if (_tunnelVariance != null) { - config.setProperty("option.inbound.lengthVariance", _tunnelVariance); - config.setProperty("option.outbound.lengthVariance", _tunnelVariance); - } - if (_tunnelBackupQuantity != null) { - config.setProperty("option.inbound.backupQuantity", _tunnelBackupQuantity); - config.setProperty("option.outbound.backupQuantity", _tunnelBackupQuantity); - } - if (_connectDelay) - config.setProperty("option.i2p.streaming.connectDelay", "1000"); - else - config.setProperty("option.i2p.streaming.connectDelay", "0"); - if (isClient(_type) && _sharedClient) { - config.setProperty("option.inbound.nickname", CLIENT_NICKNAME); - config.setProperty("option.outbound.nickname", CLIENT_NICKNAME); - } else if (_name != null) { - config.setProperty("option.inbound.nickname", _name); - config.setProperty("option.outbound.nickname", _name); - } - if ("interactive".equals(_profile)) - // This was 1 which doesn't make much sense - // The real way to make it interactive is to make the streaming lib - // MessageInputStream flush faster but there's no option for that yet, - // Setting it to 16 instead of the default but not sure what good that is either. - config.setProperty("option.i2p.streaming.maxWindowSize", "16"); - else - config.remove("option.i2p.streaming.maxWindowSize"); + // This is easier than requiring TunnelConfig to talk to + // TunnelControllerGroup and TunnelController + _config.setDestination(getDestination(_tunnel)); + return _config.getConfig(); } ///