diff --git a/build.xml b/build.xml index 28debc3..78c61f3 100644 --- a/build.xml +++ b/build.xml @@ -4,12 +4,12 @@ - - - - + + + + - + @@ -24,10 +24,7 @@ - - - - + @@ -46,14 +43,17 @@ + + + + - @@ -84,7 +84,7 @@ - + diff --git a/scripts/i2pcontrol.py b/scripts/i2pcontrol.py new file mode 100755 index 0000000..e279301 --- /dev/null +++ b/scripts/i2pcontrol.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python + +# +# If it fails "No module named yaml" +# then sudo apt install python-yaml +# + +import argparse +import json +import urllib2 +import httplib +import socket +import ssl +import sys +import yaml +from urllib2 import HTTPError, URLError +from string import whitespace + +# Info about requestable data can be found at https://geti2p.net/i2pcontrol.html & https://geti2p.net/ratestats.html + +address = "127.0.0.1" # Default I2PControl Address +port = 7650 # Default I2PControl Port +apiPassword = "itoopie" # Default I2PControl password + + +## Do not edit below +apiVersion = 1 # Default API Version +msgId = 1 +token = None + +def checkToken(): + global token + if (token == None): + token = getToken() + if (token == None): + print("Unable to login. Quitting..") + sys.exit() + +def getToken(): + loginStr = "{\"id\":" + str(msgId) + ", \"method\":\"Authenticate\",\"params\":{\"API\":" + str(apiVersion) + ", \"Password\":\"" + apiPassword + "\"}, \"jsonrpc\":\"2.0\"}" + + try: + jsonResp = sendMsg(loginStr) + return jsonResp.get("result").get("Token") + + except HTTPError, e: + print("HTTPError: %s" % e.reason) + except URLError, e: + print("URLError: %s" % e.reason) + +def getRate(rateName, ratePeriod): + checkToken() + msgStr = "{\"id\":" + str(msgId) + ", \"method\":\"GetRate\",\"params\":{\"Stat\":\"" + rateName + "\", \"Period\":" + str(ratePeriod) + ", \"Token\":\"" + token +"\" }, \"jsonrpc\":\"2.0\"}" + jsonResp = sendMsg(msgStr) + return jsonResp.get("result").get("Result") + +def getRouterInfo(infoName): + checkToken() + ## The parameter names in 'params' defines which answers are requested + msgStr = "{\"id\":" + str(msgId) + ", \"method\":\"RouterInfo\",\"params\":{\""+infoName+"\":\"\", \"Token\":\"" + token +"\" }, \"jsonrpc\":\"2.0\"}" + jsonResp = sendMsg(msgStr) + return jsonResp.get("result").get(infoName) + +def sendMsg(jsonStr): + global msgId + https_handler = UnauthenticatedHTTPSHandler() + url_opener = urllib2.build_opener(https_handler) + handle = url_opener.open("https://"+address+":"+ str(port) + "/jsonrpc", jsonStr) + response = handle.read() + handle.close() + msgId = msgId + 1; + + jsonResp = json.loads(response) + if (jsonResp.has_key("error")): + print ("Remote server: I2PControl Error: " + str(jsonResp.get("error").get("code")) + ", " + jsonResp.get("error").get("message")) + sys.exit() + return jsonResp + +### +# Overrides the version in httplib so that we can ignore server certificate authenticity +### +class UnauthenticatedHTTPSConnection(httplib.HTTPSConnection): + def connect(self): + # + sock = socket.create_connection((self.host, self.port), self.timeout) + if self._tunnel_host: + self.sock = sock + self._tunnel() + self.sock = ssl.wrap_socket(sock, + cert_reqs=ssl.CERT_NONE) + +### +# HTTPS handler which uses SSLv3 and ignores server cert authenticity +### +class UnauthenticatedHTTPSHandler(urllib2.HTTPSHandler): + def __init__(self, connection_class = UnauthenticatedHTTPSConnection): + self.specialized_conn_class = connection_class + urllib2.HTTPSHandler.__init__(self) + def https_open(self, req): + return self.do_open(self.specialized_conn_class, req) + +def zabbix_config(fileName, outfile): + yamlDict = dict() + for line in open(fileName): + li=line.strip() + if li.startswith("UserParameter"): + i2pCtrlOpt = li.strip("UserParameter=").split(",") + i2pCtrlOpt[1] = i2pCtrlOpt[1].split() + i2pCtrlOpt[1].pop(0) # Remove path of this script (i2pcontrol) + i2pCtrlParams = i2pCtrlOpt[1] + #print i2pCtrlOpt #Delete me! + result = "" + if (i2pCtrlParams[0] == "-i" or i2pCtrlParams[0] == "--router-info"): + result = getRouterInfo(i2pCtrlParams[1]) + elif (i2pCtrlParams[0] == "-s" or i2pCtrlParams[0] == "--rate-stat"): + result = getRate(i2pCtrlParams[1], i2pCtrlParams[2]) + else: + result = "Bad query syntax." + yamlDict[i2pCtrlParams[1]] = result + #print yaml.dump(yamlDict) + yaml.dump(yamlDict, open(outfile,'w')) + +def from_file(infile, parameter): + try: + yamlDict = yaml.load(open(infile,'r')) + print yamlDict[parameter] + except IOError, e: + print "File \""+ infile +"\" couldn't be read." +def main(): + parser = argparse.ArgumentParser(description='Fetch I2P info via the I2PControl API.') + parser.add_argument("-i", + "--router-info", + nargs=1, + metavar="router-info-id", + dest="router_info", + action="store", + help="Request info such as I2P version and uptime. Returned info can be of any type. Full list of options at https://geti2p.net/i2pcontrol.html. Usage: \"-i i2p.router.version\"") + + parser.add_argument("-s", + "--rate-stat", + nargs=2, + metavar=("rateStatName", "period"), + dest="rate_stat", + action="store", + help="Request info such as bandwidth, number active peers, clock skew, etc.. The period is measured in ms and must be longer than 60s. Full list at https://geti2p.net/ratestats.html. Usage: \"-s bw.receiveBps 3600000\"") + + parser.add_argument("-z", + "--zabbix", + nargs=2, + metavar=("\"path to zabbix_agent.conf\"", "\"path to output file\""), + dest="zabbix", + action="store", + help="Parse options to request, by reading a zabbix config file for \"UserParameter\"s relating to I2P. Usage: \"-z /etc/zabbix/zabbix_agent.conf\"") + + parser.add_argument("-f", + "--from-file", + nargs=1, + metavar=("\"path to input file\""), + dest="from_file", + action="store", + help="Parse options to request, by reading a zabbix config file for \"UserParameter\"s relating to I2P. Usage: \"-z /etc/zabbix/zabbix_agent.conf\"") + + if (len(sys.argv) == 1): + parser.parse_args(["-h"]) + options = parser.parse_args() + + if ((options.rate_stat != None) and (options.router_info != None)): + print("Error: Choose _one_ option. \n\n") + parser.parse_args(["-h"]) + + if ((options.zabbix != None) and ((options.rate_stat != None) or (options.router_info != None) or (options.from_file != None))): + print("Error: Don't combine option --zabbix with other options.\n") + parser.parse_args(["-h"]) + + # From-file can only be used when either router-info or rate-stat is enabled. + if ((options.from_file != None) and (options.rate_stat == None) and (options.router_info == None)): + print("Error: --from-file must be used with either --router-info or --rate-stat.\n") + parser.parse_args(["-h"]) + + if (options.from_file != None): + if (options.router_info != None): + from_file(options.from_file[0], options.router_info[0]) + if (options.rate_stat != None): + from_file(options.from_file[0], options.rate_stat[0]) + sys.exit() + + if (options.rate_stat != None): + try: + period = int(options.rate_stat[1]) + if (period < 60000): + raise ValueError + print getRate(options.rate_stat[0], period) + except ValueError, e: + print("Error: \""+options.rate_stat[1]+"\" is not an integer > 60000 \n\n") + parser.parse_args(["-h"]) + sys.exit() + + if (options.router_info != None): + print getRouterInfo(options.router_info[0]) + sys.exit() + + if (options.zabbix != None): + zabbix_config(options.zabbix[0], options.zabbix[1]) + sys.exit() + + + +if __name__ == "__main__": + main() diff --git a/scripts/makeplugin.sh b/scripts/makeplugin.sh index 97686fd..8611e2c 100755 --- a/scripts/makeplugin.sh +++ b/scripts/makeplugin.sh @@ -7,6 +7,18 @@ # zzz 2010-02 # zzz 2014-08 added support for su3 files # + +if [ -z "$I2P" -a -d "$PWD/../i2p/pkg-temp" ]; then + export I2P=$PWD/../i2p/pkg-temp +fi + +if [ ! -d "$I2P" ]; then + echo "Can't locate your I2P installation. Please add a environment variable named I2P with the path to the folder as value" + echo "On OSX this solved with running: export I2P=/Applications/i2p if default install directory is used." + exit 1 +fi + +CPATH=$I2P/lib/i2p.jar:/usr/share/java/gnu-getopt.jar PUBKEYDIR=$HOME/.i2p-plugin-keys PUBKEYFILE=$PUBKEYDIR/plugin-public-signing.key PRIVKEYFILE=$PUBKEYDIR/plugin-private-signing.key @@ -15,60 +27,59 @@ PUBKEYSTORE=$PUBKEYDIR/plugin-su3-public-signing.crt PRIVKEYSTORE=$PUBKEYDIR/plugin-su3-keystore.ks KEYTYPE=RSA_SHA512_4096 -export I2P=../i2p.i2p/pkg-temp - +# put your files in here PLUGINDIR=${1:-plugin} PC=plugin.config PCT=${PC}.tmp -if [ ! -d $PLUGINDIR ] +if [ ! -d "$PLUGINDIR" ] then echo "You must have a $PLUGINDIR directory" exit 1 fi -if [ ! -f $PLUGINDIR/$PC ] +if [ ! -f "$PLUGINDIR/$PC" ] then echo "You must have a $PLUGINDIR/$PC file" exit 1 fi -SIGNER=`grep '^signer=' $PLUGINDIR/$PC` +SIGNER=`grep '^signer=' "$PLUGINDIR/$PC"` if [ "$?" -ne "0" ] then - echo "You must have a plugin name in $PC" + echo "You must have a signer name in $PC" echo 'For example name=foo' exit 1 fi SIGNER=`echo $SIGNER | cut -f 2 -d '='` -if [ ! -f $PRIVKEYFILE ] +if [ ! -f "$PRIVKEYFILE" ] then echo "Creating new XPI2P DSA keys" - mkdir -p $PUBKEYDIR || exit 1 - java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate keygen $PUBKEYFILE $PRIVKEYFILE || exit 1 - java -cp $I2P/lib/i2p.jar net.i2p.data.Base64 encode $PUBKEYFILE $B64KEYFILE || exit 1 + mkdir -p "$PUBKEYDIR" || exit 1 + java -cp "$CPATH" net.i2p.crypto.TrustedUpdate keygen "$PUBKEYFILE" "$PRIVKEYFILE" || exit 1 + java -cp "$CPATH" net.i2p.data.Base64 encode "$PUBKEYFILE" "$B64KEYFILE" || exit 1 rm -rf logs/ - chmod 444 $PUBKEYFILE $B64KEYFILE - chmod 400 $PRIVKEYFILE + chmod 444 "$PUBKEYFILE" "$B64KEYFILE" + chmod 400 "$PRIVKEYFILE" echo "Created new XPI2P keys: $PUBKEYFILE $PRIVKEYFILE" fi -if [ ! -f $PRIVKEYSTORE ] +if [ ! -f "$PRIVKEYSTORE" ] then echo "Creating new SU3 $KEYTYPE keys for $SIGNER" - java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File keygen -t $KEYTYPE $PUBKEYSTORE $PRIVKEYSTORE $SIGNER || exit 1 + java -cp "$CPATH" net.i2p.crypto.SU3File keygen -t $KEYTYPE "$PUBKEYSTORE" "$PRIVKEYSTORE" $SIGNER || exit 1 echo '*** Save your password in a safe place!!! ***' rm -rf logs/ # copy to the router dir so verify will work CDIR=$I2P/certificates/plugin - mkdir -p $CDIR || exit 1 + mkdir -p "$CDIR" || exit 1 CFILE=$CDIR/`echo $SIGNER | sed s/@/_at_/`.crt - cp $PUBKEYSTORE $CFILE - chmod 444 $PUBKEYSTORE - chmod 400 $PRIVKEYSTORE - chmod 644 $CFILE + cp "$PUBKEYSTORE" "$CFILE" + chmod 444 "$PUBKEYSTORE" + chmod 400 "$PRIVKEYSTORE" + chmod 644 "$CFILE" echo "Created new SU3 keys: $PUBKEYSTORE $PRIVKEYSTORE" echo "Copied public key to $CFILE for testing" fi @@ -76,7 +87,7 @@ fi rm -f plugin.zip OPWD=$PWD -cd $PLUGINDIR +cd "$PLUGINDIR" grep -q '^name=' $PC if [ "$?" -ne "0" ] @@ -97,17 +108,17 @@ fi # update the date grep -v '^date=' $PC > $PCT DATE=`date '+%s000'` -echo "date=$DATE" >> $PCT +echo "date=$DATE" >> $PCT || exit 1 mv $PCT $PC || exit 1 # add our Base64 key grep -v '^key=' $PC > $PCT -B64KEY=`cat $B64KEYFILE` +B64KEY=`cat "$B64KEYFILE"` echo "key=$B64KEY" >> $PCT || exit 1 mv $PCT $PC || exit 1 # zip it -zip -r $OPWD/plugin.zip * || exit 1 +zip -r "$OPWD/plugin.zip" * || exit 1 # get the version and use it for the sud header VERSION=`grep '^version=' $PC | cut -f 2 -d '='` @@ -115,24 +126,24 @@ VERSION=`grep '^version=' $PC | cut -f 2 -d '='` NAME=`grep '^name=' $PC | cut -f 2 -d '='` XPI2P=${NAME}.xpi2p SU3=${NAME}.su3 -cd $OPWD +cd "$OPWD" # sign it echo 'Signing. ...' -java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate sign plugin.zip $XPI2P $PRIVKEYFILE $VERSION || exit 1 -java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File sign -c PLUGIN -t $KEYTYPE plugin.zip $SU3 $PRIVKEYSTORE $VERSION $SIGNER || exit 1 +java -cp "$CPATH" net.i2p.crypto.TrustedUpdate sign plugin.zip "$XPI2P" "$PRIVKEYFILE" "$VERSION" || exit 1 +java -cp "$CPATH" net.i2p.crypto.SU3File sign -c PLUGIN -t $KEYTYPE plugin.zip "$SU3" "$PRIVKEYSTORE" "$VERSION" "$SIGNER" || exit 1 rm -f plugin.zip # verify echo 'Verifying. ...' -java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate showversion $XPI2P || exit 1 -java -cp $I2P/lib/i2p.jar -Drouter.trustedUpdateKeys=$B64KEY net.i2p.crypto.TrustedUpdate verifysig $XPI2P || exit 1 -java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File showversion $SU3 || exit 1 -java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File verifysig -k $PUBKEYSTORE $SU3 || exit 1 +java -cp "$CPATH" net.i2p.crypto.TrustedUpdate showversion "$XPI2P" || exit 1 +java -cp "$CPATH" -Drouter.trustedUpdateKeys=$B64KEY net.i2p.crypto.TrustedUpdate verifysig "$XPI2P" || exit 1 +java -cp "$CPATH" net.i2p.crypto.SU3File showversion "$SU3" || exit 1 +java -cp "$CPATH" net.i2p.crypto.SU3File verifysig -k "$PUBKEYSTORE" "$SU3" || exit 1 rm -rf logs/ echo 'Plugin files created: ' -wc -c $XPI2P -wc -c $SU3 +wc -c "$XPI2P" +wc -c "$SU3" exit 0 diff --git a/scripts/plugin.config b/scripts/plugin.config index 4372afb..f3d369e 100644 --- a/scripts/plugin.config +++ b/scripts/plugin.config @@ -1,11 +1,11 @@ name=I2PControl -signer=dev@robertfoss.se +signer=zzz-plugin@mail.i2p consoleLinkName=I2PControl description=Remote Control Service author=hottuna -websiteURL=http://itoopie.i2p/ -updateURL=http://itoopie.i2p/files/I2PControl-update.xpi2p +websiteURL=http://zzz.i2p/forums/16 +updateURL=http://zzz.i2p/i2p/plugins/I2PControl-update.xpi2p +updateURL.su3=http://zzz.i2p/i2p/plugins/I2PControl-update.su3 license=Apache 2.0 -min-jetty-version=7 -max-jetty-version=8.9999 -min-i2p-version=0.9.20 +min-jetty-version=9 +min-i2p-version=0.9.30 diff --git a/src/build.xml b/src/build.xml index 93e4591..bfee176 100644 --- a/src/build.xml +++ b/src/build.xml @@ -3,24 +3,15 @@ - + - - - - - - - - - @@ -41,7 +32,7 @@ destdir="./build/obj" classpath="${cp}"> - + diff --git a/src/java/com/thetransactioncompany/jsonrpc2/JSONRPC2ParseException.java b/src/java/com/thetransactioncompany/jsonrpc2/JSONRPC2ParseException.java index 434ee20..ba07e44 100644 --- a/src/java/com/thetransactioncompany/jsonrpc2/JSONRPC2ParseException.java +++ b/src/java/com/thetransactioncompany/jsonrpc2/JSONRPC2ParseException.java @@ -18,13 +18,13 @@ public class JSONRPC2ParseException extends Exception { * Indicates a parse exception caused by a JSON message not conforming * to the JSON-RPC 2.0 protocol. */ - public static int PROTOCOL = 0; + public static final int PROTOCOL = 0; /** * Indicates a parse exception caused by invalid JSON. */ - public static int JSON = 1; + public static final int JSON = 1; /** diff --git a/src/java/net/i2p/i2pcontrol/I2PControlController.java b/src/java/net/i2p/i2pcontrol/I2PControlController.java index 0eb2cae..a3395e3 100644 --- a/src/java/net/i2p/i2pcontrol/I2PControlController.java +++ b/src/java/net/i2p/i2pcontrol/I2PControlController.java @@ -17,16 +17,29 @@ package net.i2p.i2pcontrol; */ import net.i2p.I2PAppContext; +import net.i2p.app.ClientAppManager; +import net.i2p.app.ClientAppState; +import static net.i2p.app.ClientAppState.*; +import net.i2p.router.RouterContext; +import net.i2p.router.app.RouterApp; +import net.i2p.util.I2PSSLSocketFactory; +import net.i2p.util.Log; + import net.i2p.i2pcontrol.security.KeyStoreProvider; import net.i2p.i2pcontrol.security.SecurityManager; import net.i2p.i2pcontrol.servlets.JSONRPC2Servlet; import net.i2p.i2pcontrol.servlets.configuration.ConfigurationManager; -import net.i2p.util.Log; import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ssl.SslSocketConnector; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.ssl.SslContextFactory; import java.io.File; import java.io.IOException; @@ -45,14 +58,109 @@ import java.security.KeyStore; * * @author hottuna */ -public class I2PControlController { - private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(I2PControlController.class); - private static String _pluginDir = ""; - private static ConfigurationManager _conf; - private static SecurityManager _secMan; - private static Server _server; +public class I2PControlController implements RouterApp { + // non-null + private final I2PAppContext _appContext; + // warning, null in app context + private final RouterContext _context; + private final ClientAppManager _mgr; + private final Log _log; + private final String _pluginDir; + private final ConfigurationManager _conf; + private final KeyStoreProvider _ksp; + private final SecurityManager _secMan; + private final Server _server; + private ClientAppState _state = UNINITIALIZED; + // only for main() + private static I2PControlController _instance; + + public I2PControlController(RouterContext ctx, ClientAppManager mgr, String args[]) { + _appContext = _context = ctx; + _mgr = mgr; + _log = _appContext.logManager().getLog(I2PControlController.class); + File pluginDir = new File(_context.getAppDir(), "plugins/I2PControl"); + _pluginDir = pluginDir.getAbsolutePath(); + _conf = new ConfigurationManager(_pluginDir); + _ksp = new KeyStoreProvider(_pluginDir); + _secMan = new SecurityManager(_ksp, _conf); + _server = buildServer(); + _state = INITIALIZED; + } + + /** + * From main() + */ + public I2PControlController(File pluginDir) { + _appContext = I2PAppContext.getGlobalContext(); + if (_appContext instanceof RouterContext) + _context = (RouterContext) _appContext; + else + _context = null; + _mgr = null; + _log = _appContext.logManager().getLog(I2PControlController.class); + _pluginDir = pluginDir.getAbsolutePath(); + _conf = new ConfigurationManager(_pluginDir); + _ksp = new KeyStoreProvider(_pluginDir); + _secMan = new SecurityManager(_ksp, _conf); + _server = buildServer(); + _state = INITIALIZED; + } + + /////// ClientApp methods + + public synchronized void startup() { + changeState(STARTING); + try { + start(null); + changeState(RUNNING); + } catch (Exception e) { + changeState(START_FAILED, "Failed to start", e); + _log.error("Unable to start jetty server", e); + } + } + + public synchronized void shutdown(String[] args) { + if (_state == STOPPED) + return; + changeState(STOPPING); + stop(); + changeState(STOPPED); + } + + public synchronized ClientAppState getState() { + return _state; + } + + public String getName() { + return "I2PControl"; + } + + public String getDisplayName() { + return "I2PControl"; + } + + /////// end ClientApp methods + + private void changeState(ClientAppState state) { + changeState(state, null, null); + } + + private synchronized void changeState(ClientAppState state, String msg, Exception e) { + _state = state; + if (_mgr != null) + _mgr.notify(this, state, msg, e); + if (_context == null) { + if (msg != null) + System.out.println(state + ": " + msg); + if (e != null) + e.printStackTrace(); + } + } + /** + * Deprecated, use constructor + */ public static void main(String args[]) { if (args.length != 3 || (!"-d".equals(args[0]))) throw new IllegalArgumentException("Usage: PluginController -d $PLUGINDIR [start|stop]"); @@ -61,37 +169,33 @@ public class I2PControlController { File pluginDir = new File(args[1]); if (!pluginDir.exists()) throw new IllegalArgumentException("Plugin directory " + pluginDir.getAbsolutePath() + " does not exist"); - _pluginDir = pluginDir.getAbsolutePath(); - ConfigurationManager.setConfDir(pluginDir.getAbsolutePath()); - _conf = ConfigurationManager.getInstance(); - _secMan = SecurityManager.getInstance(); - start(args); - //stop(); // Delete Me - - } else if ("stop".equals(args[2])) - stop(); - else + synchronized(I2PControlController.class) { + if (_instance != null) + throw new IllegalStateException(); + I2PControlController i2pcc = new I2PControlController(pluginDir); + try { + i2pcc.startup(); + _instance = i2pcc; + } catch (Exception e) { + e.printStackTrace(); + } + } + } else if ("stop".equals(args[2])) { + synchronized(I2PControlController.class) { + if (_instance != null) { + _instance.shutdown(null); + _instance = null; + } + } + } else { throw new IllegalArgumentException("Usage: PluginController -d $PLUGINDIR [start|stop]"); + } } - private static void start(String args[]) { - I2PAppContext.getGlobalContext().logManager().getLog(JSONRPC2Servlet.class).setMinimumPriority(Log.DEBUG); - - try { - Connector ssl = buildDefaultListenter(); - _server = buildServer(ssl); - } catch (IOException e) { - _log.error("Unable to add listener " + _conf.getConf("i2pcontrol.listen.address", "127.0.0.1") + ":" + _conf.getConf("i2pcontrol.listen.port", 7560) + " - " + e.getMessage()); - } catch (ClassNotFoundException e) { - _log.error("Unable to find class net.i2p.i2pcontrol.JSONRPCServlet: " + e.getMessage()); - } catch (InstantiationException e) { - _log.error("Unable to instantiate class net.i2p.i2pcontrol.JSONRPCServlet: " + e.getMessage()); - } catch (IllegalAccessException e) { - _log.error("Illegal access: " + e.getMessage()); - } catch (Exception e) { - _log.error("Unable to start jetty server: " + e.getMessage()); - } + private synchronized void start(String args[]) throws Exception { + _appContext.logManager().getLog(JSONRPC2Servlet.class).setMinimumPriority(Log.DEBUG); + _server.start(); } @@ -99,10 +203,9 @@ public class I2PControlController { /** * Builds a new server. Used for changing ports during operation and such. * @return Server - A new server built from current configuration. - * @throws UnknownHostException */ - public static Connector buildDefaultListenter() throws UnknownHostException { - SslSocketConnector ssl = buildSslListener(_conf.getConf("i2pcontrol.listen.address", "127.0.0.1"), + private Connector buildDefaultListener(Server server) { + Connector ssl = buildSslListener(server, _conf.getConf("i2pcontrol.listen.address", "127.0.0.1"), _conf.getConf("i2pcontrol.listen.port", 7650)); return ssl; } @@ -110,20 +213,19 @@ public class I2PControlController { /** * Builds a new server. Used for changing ports during operation and such. + * + * Does NOT start the server. Must call start() on the returned server. + * * @return Server - A new server built from current configuration. - * @throws UnknownHostException - * @throws Exception - * @throws InstantiationException - * @throws IllegalAccessException */ - public static Server buildServer(Connector ssl) throws UnknownHostException, Exception, InstantiationException, IllegalAccessException { + public Server buildServer() { Server server = new Server(); + Connector ssl = buildDefaultListener(server); server.addConnector(ssl); ServletHandler sh = new ServletHandler(); - sh.addServletWithMapping(net.i2p.i2pcontrol.servlets.JSONRPC2Servlet.class, "/"); + sh.addServletWithMapping(new ServletHolder(new JSONRPC2Servlet(_context, _secMan)), "/"); server.getServer().setHandler(sh); - server.start(); return server; } @@ -134,24 +236,38 @@ public class I2PControlController { * @param address - The address the listener will listen to. * @param port - The port the listener will listen to. * @return - Newly created listener - * @throws UnknownHostException */ - public static SslSocketConnector buildSslListener(String address, int port) throws UnknownHostException { + private Connector buildSslListener(Server server, String address, int port) { int listeners = 0; - if (_server != null) { - listeners = _server.getConnectors().length; + if (server != null) { + listeners = server.getConnectors().length; } - SslSocketConnector ssl = new SslSocketConnector(); - ssl.setProvider(_secMan.getSecurityProvider()); - //ssl.setCipherSuites(_secMan.getSupprtedSSLCipherSuites()); Removed in Jetty 5->6 port. + // the keystore path and password + SslContextFactory sslFactory = new SslContextFactory(_ksp.getKeyStoreLocation()); + sslFactory.setKeyStorePassword(KeyStoreProvider.DEFAULT_KEYSTORE_PASSWORD); + // the X.509 cert password (if not present, verifyKeyStore() returned false) + sslFactory.setKeyManagerPassword(KeyStoreProvider.DEFAULT_KEYSTORE_PASSWORD); + sslFactory.addExcludeProtocols(I2PSSLSocketFactory.EXCLUDE_PROTOCOLS.toArray( + new String[I2PSSLSocketFactory.EXCLUDE_PROTOCOLS.size()])); + sslFactory.addExcludeCipherSuites(I2PSSLSocketFactory.EXCLUDE_CIPHERS.toArray( + new String[I2PSSLSocketFactory.EXCLUDE_CIPHERS.size()])); + + HttpConfiguration httpConfig = new HttpConfiguration(); + httpConfig.setSecureScheme("https"); + httpConfig.setSecurePort(port); + httpConfig.addCustomizer(new SecureRequestCustomizer()); + // number of acceptors, (default) number of selectors + ServerConnector ssl = new ServerConnector(server, 1, 0, + new SslConnectionFactory(sslFactory, "http/1.1"), + new HttpConnectionFactory(httpConfig)); ssl.setHost(address); ssl.setPort(port); - ssl.setWantClientAuth(false); // Don't care about client authentication. - ssl.setPassword(KeyStoreProvider.DEFAULT_KEYSTORE_PASSWORD); - ssl.setKeyPassword(KeyStoreProvider.DEFAULT_KEYSTORE_PASSWORD); - ssl.setKeystoreType(KeyStore.getDefaultType()); - ssl.setKeystore(KeyStoreProvider.getKeyStoreLocation()); + ssl.setIdleTimeout(90*1000); // default 10 sec + // all with same name will use the same thread pool + //ssll.setName("ConsoleSocket"); + ssl.setName("I2PControl"); + ssl.setName("SSL Listener-" + ++listeners); return ssl; @@ -166,36 +282,45 @@ public class I2PControlController { * @param listener * @throws Exception */ - public static void replaceListener(Connector listener) throws Exception { +/**** + public synchronized void replaceListener(Connector listener) throws Exception { if (_server != null) { stopServer(); } _server = buildServer(listener); } +****/ /** * Get all listeners of the server. * @return */ - public static Connector[] getListeners() { +/**** + public synchronized Connector[] getListeners() { if (_server != null) { return _server.getConnectors(); } return new Connector[0]; } +****/ /** * Removes all listeners */ - public static void clearListeners() { +/**** + public synchronized void clearListeners() { if (_server != null) { for (Connector listen : getListeners()) { _server.removeConnector(listen); } } } +****/ - private static void stopServer() + /** + * Stop it + */ + private synchronized void stopServer() { try { if (_server != null) { @@ -204,21 +329,18 @@ public class I2PControlController { listener.stop(); } _server.destroy(); - _server = null; } } catch (Exception e) { - _log.error("Stopping server" + e); + _log.error("Stopping server", e); } } - private static void stop() { - ConfigurationManager.writeConfFile(); - if (_secMan != null) { - _secMan.stopTimedEvents(); - } - + private synchronized void stop() { + _conf.writeConfFile(); + _secMan.stopTimedEvents(); stopServer(); +/**** // Get and stop all running threads ThreadGroup threadgroup = Thread.currentThread().getThreadGroup(); Thread[] threads = new Thread[threadgroup.activeCount() + 3]; @@ -237,9 +359,10 @@ public class I2PControlController { threadgroup.interrupt(); //Thread.currentThread().getThreadGroup().destroy(); +****/ } - public static String getPluginDir() { + public String getPluginDir() { return _pluginDir; } } diff --git a/src/java/net/i2p/i2pcontrol/router/RouterManager.java b/src/java/net/i2p/i2pcontrol/router/RouterManager.java deleted file mode 100644 index 8219306..0000000 --- a/src/java/net/i2p/i2pcontrol/router/RouterManager.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.i2p.i2pcontrol.router; - - -import net.i2p.I2PAppContext; -import net.i2p.router.RouterContext; -import net.i2p.util.Log; - -/** - * Handle communications with the router instance. - * @author mathias - * - */ -public class RouterManager { - private final static Log _log = new Log(RouterManager.class); - private static I2PAppContext context = I2PAppContext.getCurrentContext(); - private static boolean startedTestRouter = false; - private final static String I2P_DIR = "/home/hottuna/Apps/i2p/"; - private final static String I2P_CONFIG_FILE = "/home/hottuna/.i2p/router.config"; - - - public static I2PAppContext getAppContext() { - return context; - } - - public static RouterContext getRouterContext() throws Exception { - // If not running as a plugin from within I2P. - if (context.isRouterContext()) { - return (RouterContext) context; - } else { - throw new Exception("No RouterContext available!"); - } - } -} diff --git a/src/java/net/i2p/i2pcontrol/security/AuthToken.java b/src/java/net/i2p/i2pcontrol/security/AuthToken.java index 5eb5680..e17468a 100644 --- a/src/java/net/i2p/i2pcontrol/security/AuthToken.java +++ b/src/java/net/i2p/i2pcontrol/security/AuthToken.java @@ -11,8 +11,8 @@ public class AuthToken { private String id; private Date expiry; - public AuthToken(String password) { - _secMan = SecurityManager.getInstance(); + public AuthToken(SecurityManager secMan, String password) { + _secMan = secMan; String hash = _secMan.getPasswdHash(password); this.id = _secMan.getHash(hash + Calendar.getInstance().getTimeInMillis()); Calendar expiry = Calendar.getInstance(); diff --git a/src/java/net/i2p/i2pcontrol/security/KeyStoreProvider.java b/src/java/net/i2p/i2pcontrol/security/KeyStoreProvider.java index eaa037e..c77aac7 100644 --- a/src/java/net/i2p/i2pcontrol/security/KeyStoreProvider.java +++ b/src/java/net/i2p/i2pcontrol/security/KeyStoreProvider.java @@ -1,9 +1,13 @@ package net.i2p.i2pcontrol.security; import net.i2p.crypto.KeyStoreUtil; -import net.i2p.i2pcontrol.I2PControlController; -import java.io.*; -import java.security.*; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -15,10 +19,14 @@ public class KeyStoreProvider { public final static String DEFAULT_CERTIFICATE_ALIAS = "I2PControl CA"; public static final String DEFAULT_KEYSTORE_NAME = "key.store"; public static final String DEFAULT_KEYSTORE_PASSWORD = "nut'nfancy"; - private static KeyStore _keystore = null; + private final String _pluginDir; + private KeyStore _keystore; + public KeyStoreProvider(String pluginDir) { + _pluginDir = pluginDir; + } - public static void initialize() { + public void initialize() { KeyStoreUtil.createKeys(new File(getKeyStoreLocation()), DEFAULT_KEYSTORE_PASSWORD, DEFAULT_CERTIFICATE_ALIAS, @@ -30,6 +38,10 @@ public class KeyStoreProvider { DEFAULT_KEYSTORE_PASSWORD); } + /** + * @param password unused + * @return null on failure + */ public static X509Certificate readCert(KeyStore ks, String certAlias, String password) { try { X509Certificate cert = (X509Certificate) ks.getCertificate(certAlias); @@ -51,6 +63,11 @@ public class KeyStoreProvider { return null; } + /** + * @param password for the keystore + * @return null on failure + */ +/**** public static X509Certificate readCert(File keyStoreFile, String certAlias, String password) { try { KeyStore ks = getDefaultKeyStore(); @@ -81,7 +98,13 @@ public class KeyStoreProvider { } return null; } +****/ + /** + * @param password for the key + * @return null on failure, or throws RuntimeException... + */ +/**** public static PrivateKey readPrivateKey(KeyStore ks, String alias, String password) { try { // load the key entry from the keystore @@ -102,7 +125,12 @@ public class KeyStoreProvider { } return null; } +****/ + /** + * @return null on failure + */ +/**** public static PrivateKey readPrivateKey(String alias, File keyStoreFile, String keyStorePassword, String keyPassword) { try { KeyStore ks = getDefaultKeyStore(); @@ -120,7 +148,12 @@ public class KeyStoreProvider { } return null; } +****/ + /** + * @return null on failure + */ +/**** public static KeyStore writeCACertToKeyStore(KeyStore keyStore, String keyPassword, String alias, PrivateKey caPrivKey, X509Certificate caCert) { try { X509Certificate[] chain = new X509Certificate[1]; @@ -143,8 +176,12 @@ public class KeyStoreProvider { } return null; } +****/ - public static synchronized KeyStore getDefaultKeyStore() { + /** + * @return null on failure + */ + public synchronized KeyStore getDefaultKeyStore() { if (_keystore == null) { File keyStoreFile = new File(getKeyStoreLocation()); @@ -157,7 +194,6 @@ public class KeyStoreProvider { } initialize(); - _keystore = KeyStore.getInstance(KeyStore.getDefaultType()); if (keyStoreFile.exists()) { InputStream is = new FileInputStream(keyStoreFile); _keystore.load(is, DEFAULT_KEYSTORE_PASSWORD.toCharArray()); @@ -174,8 +210,8 @@ public class KeyStoreProvider { } } - public static String getKeyStoreLocation() { - File keyStoreFile = new File(I2PControlController.getPluginDir() + File.separator + DEFAULT_KEYSTORE_NAME); + public String getKeyStoreLocation() { + File keyStoreFile = new File(_pluginDir, DEFAULT_KEYSTORE_NAME); return keyStoreFile.getAbsolutePath(); } } diff --git a/src/java/net/i2p/i2pcontrol/security/SecurityManager.java b/src/java/net/i2p/i2pcontrol/security/SecurityManager.java index d73a579..02aa489 100644 --- a/src/java/net/i2p/i2pcontrol/security/SecurityManager.java +++ b/src/java/net/i2p/i2pcontrol/security/SecurityManager.java @@ -35,24 +35,16 @@ import java.util.*; * Manage the password storing for I2PControl. */ public class SecurityManager { - private final static String SSL_PROVIDER = "SunJSSE"; private final static String DEFAULT_AUTH_BCRYPT_SALT = "$2a$11$5aOLx2x/8i4fNaitoCSSWu"; private final static String DEFAULT_AUTH_PASSWORD = "$2a$11$5aOLx2x/8i4fNaitoCSSWuut2wEl3Hupuca8DCT.NXzvH9fq1pBU."; - private HashMap authTokens; - private Timer timer; - private String[] SSL_CIPHER_SUITES; - private KeyStore _ks; - private Log _log; - private static SecurityManager _securityManager; + private final HashMap authTokens; + private final Timer timer; + private final KeyStore _ks; + private final Log _log; + private final ConfigurationManager _conf; - public static SecurityManager getInstance() { - if (_securityManager == null) { - _securityManager = new SecurityManager(); - } - return _securityManager; - } - - private SecurityManager() { + public SecurityManager(KeyStoreProvider ksp, ConfigurationManager conf) { + _conf = conf; _log = I2PAppContext.getGlobalContext().logManager().getLog(SecurityManager.class); authTokens = new HashMap(); @@ -60,23 +52,7 @@ public class SecurityManager { // Start running periodic task after 20 minutes, run periodically every 10th minute. timer.scheduleAtFixedRate(new Sweeper(), 1000 * 60 * 20, 1000 * 60 * 10); - // Get supported SSL cipher suites. - SocketFactory SSLF = SSLSocketFactory.getDefault(); - try { - SSL_CIPHER_SUITES = ((SSLSocket)SSLF.createSocket()).getSupportedCipherSuites(); - } catch (Exception e) { - _log.log(Log.CRIT, "Unable to create SSLSocket used for fetching supported ssl cipher suites.", e); - } - - _ks = KeyStoreProvider.getDefaultKeyStore(); - } - - public String[] getSupprtedSSLCipherSuites() { - return SSL_CIPHER_SUITES; - } - - public String getSecurityProvider() { - return SSL_PROVIDER; + _ks = ksp.getDefaultKeyStore(); } public void stopTimedEvents() { @@ -116,7 +92,7 @@ public class SecurityManager { * @return BCrypt hash of salt and input string */ public String getPasswdHash(String pwd) { - return BCrypt.hashpw(pwd, ConfigurationManager.getInstance().getConf("auth.salt", DEFAULT_AUTH_BCRYPT_SALT)); + return BCrypt.hashpw(pwd, _conf.getConf("auth.salt", DEFAULT_AUTH_BCRYPT_SALT)); } /** @@ -138,9 +114,9 @@ public class SecurityManager { * @return Returns AuthToken if password is valid. If password is invalid null will be returned. */ public AuthToken validatePasswd(String pwd) { - String storedPass = ConfigurationManager.getInstance().getConf("auth.password", DEFAULT_AUTH_PASSWORD); + String storedPass = _conf.getConf("auth.password", DEFAULT_AUTH_PASSWORD); if (getPasswdHash(pwd).equals(storedPass)) { - AuthToken token = new AuthToken(pwd); + AuthToken token = new AuthToken(this, pwd); synchronized (authTokens) { authTokens.put(token.getId(), token); } @@ -157,10 +133,10 @@ public class SecurityManager { */ public boolean setPasswd(String newPasswd) { String newHash = getPasswdHash(newPasswd); - String oldHash = ConfigurationManager.getInstance().getConf("auth.password", DEFAULT_AUTH_PASSWORD); + String oldHash = _conf.getConf("auth.password", DEFAULT_AUTH_PASSWORD); if (!newHash.equals(oldHash)) { - ConfigurationManager.getInstance().setConf("auth.password", newHash); + _conf.setConf("auth.password", newHash); synchronized (authTokens) { authTokens.clear(); } diff --git a/src/java/net/i2p/i2pcontrol/servlets/JSONRPC2Servlet.java b/src/java/net/i2p/i2pcontrol/servlets/JSONRPC2Servlet.java index 229d49f..b9cc570 100644 --- a/src/java/net/i2p/i2pcontrol/servlets/JSONRPC2Servlet.java +++ b/src/java/net/i2p/i2pcontrol/servlets/JSONRPC2Servlet.java @@ -18,17 +18,27 @@ package net.i2p.i2pcontrol.servlets; import com.thetransactioncompany.jsonrpc2.*; import com.thetransactioncompany.jsonrpc2.server.Dispatcher; + import net.i2p.I2PAppContext; +import net.i2p.router.RouterContext; +import net.i2p.util.Log; + import net.i2p.i2pcontrol.I2PControlVersion; import net.i2p.i2pcontrol.servlets.jsonrpc2handlers.*; -import net.i2p.util.Log; +import net.i2p.i2pcontrol.security.SecurityManager; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.*; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; /** @@ -38,25 +48,33 @@ public class JSONRPC2Servlet extends HttpServlet { private static final long serialVersionUID = -45075606818515212L; private static final int BUFFER_LENGTH = 2048; - private static Dispatcher disp; - private static char[] readBuffer; - private static Log _log; + private Dispatcher disp; + private Log _log; + private final SecurityManager _secMan; + private final JSONRPC2Helper _helper; + private final RouterContext _context; + public JSONRPC2Servlet(RouterContext ctx, SecurityManager secMan) { + _context = ctx; + _secMan = secMan; + _helper = new JSONRPC2Helper(_secMan); + if (ctx != null) + _log = ctx.logManager().getLog(JSONRPC2Servlet.class); + else + _log = I2PAppContext.getGlobalContext().logManager().getLog(JSONRPC2Servlet.class); + } @Override public void init() { - _log = I2PAppContext.getGlobalContext().logManager().getLog(JSONRPC2Servlet.class); - readBuffer = new char[BUFFER_LENGTH]; - disp = new Dispatcher(); - disp.register(new EchoHandler()); - disp.register(new GetRateHandler()); - disp.register(new AuthenticateHandler()); - disp.register(new NetworkSettingHandler()); - disp.register(new RouterInfoHandler()); - disp.register(new RouterManagerHandler()); - disp.register(new I2PControlHandler()); - disp.register(new AdvancedSettingsHandler()); + disp.register(new EchoHandler(_helper)); + disp.register(new GetRateHandler(_helper)); + disp.register(new AuthenticateHandler(_helper, _secMan)); + disp.register(new NetworkSettingHandler(_context, _helper)); + disp.register(new RouterInfoHandler(_context, _helper)); + disp.register(new RouterManagerHandler(_context, _helper)); + disp.register(new I2PControlHandler(_context, _helper)); + disp.register(new AdvancedSettingsHandler(_context, _helper)); } @Override @@ -80,12 +98,15 @@ public class JSONRPC2Servlet extends HttpServlet { if (msg instanceof JSONRPC2Request) { jsonResp = disp.dispatch((JSONRPC2Request)msg, null); jsonResp.toJSON().put("API", I2PControlVersion.API_VERSION); - _log.debug("Request: " + msg); - _log.debug("Response: " + jsonResp); + if (_log.shouldDebug()) { + _log.debug("Request: " + msg); + _log.debug("Response: " + jsonResp); + } } else if (msg instanceof JSONRPC2Notification) { disp.dispatch((JSONRPC2Notification)msg, null); - _log.debug("Notification: " + msg); + if (_log.shouldDebug()) + _log.debug("Notification: " + msg); } out.println(jsonResp); @@ -99,6 +120,7 @@ public class JSONRPC2Servlet extends HttpServlet { Writer writer = new StringWriter(); BufferedReader reader = new BufferedReader(new InputStreamReader(sis, "UTF-8")); + char[] readBuffer = new char[BUFFER_LENGTH]; int n; while ((n = reader.read(readBuffer)) != -1) { writer.write(readBuffer, 0, n); diff --git a/src/java/net/i2p/i2pcontrol/servlets/configuration/ConfigurationManager.java b/src/java/net/i2p/i2pcontrol/servlets/configuration/ConfigurationManager.java index 0002401..2a62533 100644 --- a/src/java/net/i2p/i2pcontrol/servlets/configuration/ConfigurationManager.java +++ b/src/java/net/i2p/i2pcontrol/servlets/configuration/ConfigurationManager.java @@ -2,8 +2,15 @@ package net.i2p.i2pcontrol.servlets.configuration; import net.i2p.I2PAppContext; import net.i2p.util.Log; +import net.i2p.util.SecureFileOutputStream; -import java.io.*; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.OutputStreamWriter; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -16,44 +23,30 @@ import java.util.TreeMap; * */ public class ConfigurationManager { - private static String configLocation = "I2PControl.conf"; - private static boolean configLocationModified = false; - private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(ConfigurationManager.class); + private final String CONFIG_FILE = "I2PControl.conf"; + private final String configLocation; + private final Log _log; - private static ConfigurationManager instance; + private ConfigurationManager instance; //Configurations with a String as value - private static Map stringConfigurations = new HashMap(); + private final Map stringConfigurations = new HashMap(); //Configurations with a Boolean as value - private static Map booleanConfigurations = new HashMap(); + private final Map booleanConfigurations = new HashMap(); //Configurations with an Integer as value - private static Map integerConfigurations = new HashMap(); + private final Map integerConfigurations = new HashMap(); - /** - * Should only be set before getInstance is first called. - * @param dir - */ - public static void setConfDir(String dir) { - if (!configLocationModified) { - if (dir.endsWith("/")) { - configLocation = dir + configLocation; - } else { - configLocation = dir + "/" + configLocation; - } + + public ConfigurationManager(String dir) { + _log = I2PAppContext.getGlobalContext().logManager().getLog(ConfigurationManager.class); + if (dir.endsWith("/")) { + configLocation = dir + CONFIG_FILE; + } else { + configLocation = dir + "/" + CONFIG_FILE; } - } - - private ConfigurationManager() { readConfFile(); } - public synchronized static ConfigurationManager getInstance() { - if (instance == null) { - instance = new ConfigurationManager(); - } - return instance; - } - /** * Collects settingNameuments of the form --word, --word=otherword and -blah * to determine user parameters. @@ -71,7 +64,7 @@ public class ConfigurationManager { /** * Reads configuration from file itoopie.conf, every line is parsed as key=value. */ - public static void readConfFile() { + public void readConfFile() { try { BufferedReader br = new BufferedReader(new FileReader(configLocation)); String input; @@ -89,7 +82,7 @@ public class ConfigurationManager { /** * Write configuration into default config file. */ - public static void writeConfFile() { + public void writeConfFile() { TreeMap tree = new TreeMap(); for (Entry e : stringConfigurations.entrySet()) { tree.put(e.getKey(), e.getValue()); @@ -101,7 +94,7 @@ public class ConfigurationManager { tree.put(e.getKey(), e.getValue().toString()); } try { - BufferedWriter bw = new BufferedWriter(new FileWriter(configLocation)); + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(configLocation))); for (Entry e : tree.entrySet()) { bw.write(e.getKey() + "=" + e.getValue() + "\r\n"); } @@ -116,7 +109,7 @@ public class ConfigurationManager { * where value will (in order) be parsed as integer/boolean/string. * @param str */ - public static void parseConfigStr(String str) { + public void parseConfigStr(String str) { int eqIndex = str.indexOf('='); if (eqIndex != -1) { String key = str.substring(0, eqIndex).trim().toLowerCase(); diff --git a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/AdvancedSettingsHandler.java b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/AdvancedSettingsHandler.java index 9f26ae7..f6607e3 100644 --- a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/AdvancedSettingsHandler.java +++ b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/AdvancedSettingsHandler.java @@ -7,7 +7,6 @@ import com.thetransactioncompany.jsonrpc2.server.MessageContext; import com.thetransactioncompany.jsonrpc2.server.RequestHandler; import net.i2p.I2PAppContext; -import net.i2p.i2pcontrol.router.RouterManager; import net.i2p.router.RouterContext; import net.i2p.util.Log; @@ -18,18 +17,20 @@ import java.util.Set; public class AdvancedSettingsHandler implements RequestHandler { - private static RouterContext _context; - private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(AdvancedSettingsHandler.class); + private final RouterContext _context; + private final Log _log; + private final JSONRPC2Helper _helper; + private static final String[] requiredArgs = {}; - static { - try { - _context = RouterManager.getRouterContext(); - } catch (Exception e) { - _log.error("Unable to initialize RouterContext.", e); - } + public AdvancedSettingsHandler(RouterContext ctx, JSONRPC2Helper helper) { + _helper = helper; + _context = ctx; + if (ctx != null) + _log = ctx.logManager().getLog(AdvancedSettingsHandler.class); + else + _log = I2PAppContext.getGlobalContext().logManager().getLog(AdvancedSettingsHandler.class); } - private String[] requiredArgs = {}; // Reports the method names of the handled requests public String[] handledRequests() { return new String[] {"AdvancedSettings"}; @@ -39,11 +40,18 @@ public class AdvancedSettingsHandler implements RequestHandler { @SuppressWarnings("unchecked") public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) { if (req.getMethod().equals("AdvancedSettings")) { - JSONRPC2Error err = JSONRPC2Helper.validateParams(requiredArgs, req); + JSONRPC2Error err = _helper.validateParams(requiredArgs, req); if (err != null) { return new JSONRPC2Response(err, req.getID()); } + if (_context == null) { + return new JSONRPC2Response(new JSONRPC2Error( + JSONRPC2Error.INTERNAL_ERROR.getCode(), + "RouterContext was not initialized. Query failed"), + req.getID()); + } + @SuppressWarnings("rawtypes") HashMap inParams = (HashMap) req.getParams(); Map outParams = new HashMap(); diff --git a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/AuthenticateHandler.java b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/AuthenticateHandler.java index 0e41907..cbc2e6b 100644 --- a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/AuthenticateHandler.java +++ b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/AuthenticateHandler.java @@ -31,7 +31,15 @@ import java.util.Map; public class AuthenticateHandler implements RequestHandler { - private String[] requiredArgs = {"Password", "API"}; + private static final String[] requiredArgs = {"Password", "API"}; + private final JSONRPC2Helper _helper; + private final SecurityManager _secMan; + + public AuthenticateHandler(JSONRPC2Helper helper, SecurityManager secMan) { + _helper = helper; + _secMan = secMan; + } + // Reports the method names of the handled requests public String[] handledRequests() { return new String[] {"Authenticate"}; @@ -40,7 +48,7 @@ public class AuthenticateHandler implements RequestHandler { // Processes the requests public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) { if (req.getMethod().equals("Authenticate")) { - JSONRPC2Error err = JSONRPC2Helper.validateParams(requiredArgs, req, JSONRPC2Helper.USE_NO_AUTH); + JSONRPC2Error err = _helper.validateParams(requiredArgs, req, JSONRPC2Helper.USE_NO_AUTH); if (err != null) return new JSONRPC2Response(err, req.getID()); @@ -50,7 +58,7 @@ public class AuthenticateHandler implements RequestHandler { // Try get an AuthToken - AuthToken token = SecurityManager.getInstance().validatePasswd(pwd); + AuthToken token = _secMan.validatePasswd(pwd); if (token == null) { return new JSONRPC2Response(JSONRPC2ExtendedError.INVALID_PASSWORD, req.getID()); } diff --git a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/EchoHandler.java b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/EchoHandler.java index e37dbac..f816ab4 100644 --- a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/EchoHandler.java +++ b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/EchoHandler.java @@ -11,7 +11,13 @@ import java.util.Map; public class EchoHandler implements RequestHandler { - private String[] requiredArgs = {"Echo"}; + private static final String[] requiredArgs = {"Echo"}; + private final JSONRPC2Helper _helper; + + public EchoHandler(JSONRPC2Helper helper) { + _helper = helper; + } + // Reports the method names of the handled requests public String[] handledRequests() { return new String[] {"Echo"}; @@ -20,7 +26,7 @@ public class EchoHandler implements RequestHandler { // Processes the requests public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) { if (req.getMethod().equals("Echo")) { - JSONRPC2Error err = JSONRPC2Helper.validateParams(requiredArgs, req); + JSONRPC2Error err = _helper.validateParams(requiredArgs, req); if (err != null) return new JSONRPC2Response(err, req.getID()); diff --git a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/GetRateHandler.java b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/GetRateHandler.java index fc83705..fdf8bdf 100644 --- a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/GetRateHandler.java +++ b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/GetRateHandler.java @@ -31,7 +31,13 @@ import java.util.Map; public class GetRateHandler implements RequestHandler { - private String[] requiredArgs = {"Stat", "Period"}; + private static final String[] requiredArgs = {"Stat", "Period"}; + private final JSONRPC2Helper _helper; + + public GetRateHandler(JSONRPC2Helper helper) { + _helper = helper; + } + // Reports the method names of the handled requests public String[] handledRequests() { return new String[] {"GetRate"}; @@ -40,7 +46,7 @@ public class GetRateHandler implements RequestHandler { // Processes the requests public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) { if (req.getMethod().equals("GetRate")) { - JSONRPC2Error err = JSONRPC2Helper.validateParams(requiredArgs, req); + JSONRPC2Error err = _helper.validateParams(requiredArgs, req); if (err != null) return new JSONRPC2Response(err, req.getID()); diff --git a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/I2PControlHandler.java b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/I2PControlHandler.java index be2ee7f..6eddf1a 100644 --- a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/I2PControlHandler.java +++ b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/I2PControlHandler.java @@ -5,14 +5,13 @@ import com.thetransactioncompany.jsonrpc2.JSONRPC2Request; import com.thetransactioncompany.jsonrpc2.JSONRPC2Response; import com.thetransactioncompany.jsonrpc2.server.MessageContext; import com.thetransactioncompany.jsonrpc2.server.RequestHandler; + import net.i2p.I2PAppContext; import net.i2p.i2pcontrol.I2PControlController; -import net.i2p.i2pcontrol.router.RouterManager; import net.i2p.i2pcontrol.security.SecurityManager; import net.i2p.i2pcontrol.servlets.configuration.ConfigurationManager; import net.i2p.router.RouterContext; import net.i2p.util.Log; -import org.eclipse.jetty.server.ssl.SslSocketConnector; import java.net.InetAddress; import java.net.UnknownHostException; @@ -37,20 +36,24 @@ import java.util.Map; */ public class I2PControlHandler implements RequestHandler { + private static final int BW_BURST_PCT = 110; private static final int BW_BURST_TIME = 20; - private static RouterContext _context; - private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(I2PControlHandler.class); - private static final ConfigurationManager _conf = ConfigurationManager.getInstance(); + private final RouterContext _context; + private final Log _log; + //private final ConfigurationManager _conf; + private final JSONRPC2Helper _helper; - static { - try { - _context = RouterManager.getRouterContext(); - } catch (Exception e) { - _log.error("Unable to initialize RouterContext.", e); - } + public I2PControlHandler(RouterContext ctx, JSONRPC2Helper helper) { + _helper = helper; + _context = ctx; + if (ctx != null) + _log = ctx.logManager().getLog(I2PControlHandler.class); + else + _log = I2PAppContext.getGlobalContext().logManager().getLog(I2PControlHandler.class); } + // Reports the method names of the handled requests public String[] handledRequests() { return new String[] {"I2PControl"}; @@ -59,7 +62,8 @@ public class I2PControlHandler implements RequestHandler { // Processes the requests public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) { if (req.getMethod().equals("I2PControl")) { - return process(req); + //return process(req); + return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, req.getID()); } else { // Method name not supported return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, req.getID()); @@ -67,8 +71,9 @@ public class I2PControlHandler implements RequestHandler { } +/**** private JSONRPC2Response process(JSONRPC2Request req) { - JSONRPC2Error err = JSONRPC2Helper.validateParams(null, req); + JSONRPC2Error err = _helper.validateParams(null, req); if (err != null) return new JSONRPC2Response(err, req.getID()); @@ -191,4 +196,5 @@ public class I2PControlHandler implements RequestHandler { outParams.put("RestartNeeded", restartNeeded); return new JSONRPC2Response(outParams, req.getID()); } +****/ } diff --git a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/JSONRPC2Helper.java b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/JSONRPC2Helper.java index 044c777..ade177e 100644 --- a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/JSONRPC2Helper.java +++ b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/JSONRPC2Helper.java @@ -30,6 +30,12 @@ public class JSONRPC2Helper { public final static Boolean USE_NO_AUTH = false; public final static Boolean USE_AUTH = true; + private final SecurityManager _secMan; + + public JSONRPC2Helper(SecurityManager secMan) { + _secMan = secMan; + } + /** * Check incoming request for required arguments, to make sure they are valid. * @param requiredArgs - Array of names of required arguments. If null don't check for any parameters. @@ -37,7 +43,7 @@ public class JSONRPC2Helper { * @param useAuth - If true, will validate authentication token. * @return - null if no errors were found. Corresponding JSONRPC2Error if error is found. */ - public static JSONRPC2Error validateParams(String[] requiredArgs, JSONRPC2Request req, Boolean useAuth) { + public JSONRPC2Error validateParams(String[] requiredArgs, JSONRPC2Request req, Boolean useAuth) { // Error on unnamed parameters if (req.getParamsType() != JSONRPC2ParamsType.OBJECT) { @@ -75,7 +81,7 @@ public class JSONRPC2Helper { * @param req - Incoming JSONRPC2 request * @return - null if no errors were found. Corresponding JSONRPC2Error if error is found. */ - public static JSONRPC2Error validateParams(String[] requiredArgs, JSONRPC2Request req) { + public JSONRPC2Error validateParams(String[] requiredArgs, JSONRPC2Request req) { return validateParams(requiredArgs, req, JSONRPC2Helper.USE_AUTH); } @@ -86,13 +92,13 @@ public class JSONRPC2Helper { * @param req - Parameters of incoming request * @return null if everything is fine, JSONRPC2Error for any corresponding error. */ - private static JSONRPC2Error validateToken(HashMap params) { + private JSONRPC2Error validateToken(HashMap params) { String tokenID = (String) params.get("Token"); if (tokenID == null) { return JSONRPC2ExtendedError.NO_TOKEN; } try { - SecurityManager.getInstance().verifyToken(tokenID); + _secMan.verifyToken(tokenID); } catch (InvalidAuthTokenException e) { return JSONRPC2ExtendedError.INVALID_TOKEN; } catch (ExpiredAuthTokenException e) { diff --git a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/NetworkSettingHandler.java b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/NetworkSettingHandler.java index 389d12c..80cd6e9 100644 --- a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/NetworkSettingHandler.java +++ b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/NetworkSettingHandler.java @@ -7,7 +7,6 @@ import com.thetransactioncompany.jsonrpc2.server.MessageContext; import com.thetransactioncompany.jsonrpc2.server.RequestHandler; import net.i2p.I2PAppContext; -import net.i2p.i2pcontrol.router.RouterManager; import net.i2p.router.Router; import net.i2p.router.RouterContext; import net.i2p.router.transport.FIFOBandwidthRefiller; @@ -41,15 +40,12 @@ import java.util.Map; public class NetworkSettingHandler implements RequestHandler { private static final int BW_BURST_PCT = 110; private static final int BW_BURST_TIME = 20; - private static RouterContext _context; - private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(NetworkSettingHandler.class); + private final JSONRPC2Helper _helper; + private final RouterContext _context; - static { - try { - _context = RouterManager.getRouterContext(); - } catch (Exception e) { - _log.error("Unable to initialize RouterContext.", e); - } + public NetworkSettingHandler(RouterContext ctx, JSONRPC2Helper helper) { + _helper = helper; + _context = ctx; } // Reports the method names of the handled requests @@ -69,7 +65,7 @@ public class NetworkSettingHandler implements RequestHandler { private JSONRPC2Response process(JSONRPC2Request req) { - JSONRPC2Error err = JSONRPC2Helper.validateParams(null, req); + JSONRPC2Error err = _helper.validateParams(null, req); if (err != null) return new JSONRPC2Response(err, req.getID()); diff --git a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/RouterInfoHandler.java b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/RouterInfoHandler.java index 5b5d1d6..94251cf 100644 --- a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/RouterInfoHandler.java +++ b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/RouterInfoHandler.java @@ -5,9 +5,9 @@ import com.thetransactioncompany.jsonrpc2.JSONRPC2Request; import com.thetransactioncompany.jsonrpc2.JSONRPC2Response; import com.thetransactioncompany.jsonrpc2.server.MessageContext; import com.thetransactioncompany.jsonrpc2.server.RequestHandler; + import net.i2p.I2PAppContext; import net.i2p.data.router.RouterAddress; -import net.i2p.i2pcontrol.router.RouterManager; import net.i2p.router.CommSystemFacade; import net.i2p.router.Router; import net.i2p.router.RouterContext; @@ -15,7 +15,6 @@ import net.i2p.router.RouterVersion; import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade; import net.i2p.router.transport.TransportUtil; import net.i2p.router.transport.ntcp.NTCPTransport; -import net.i2p.util.Log; import java.util.HashMap; import java.util.Map; @@ -38,17 +37,15 @@ import java.util.Map; */ public class RouterInfoHandler implements RequestHandler { - private static RouterContext _context; - private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(RouterInfoHandler.class); + private final JSONRPC2Helper _helper; + private final RouterContext _context; - static { - try { - _context = RouterManager.getRouterContext(); - } catch (Exception e) { - _log.error("Unable to initialize RouterContext.", e); - } + public RouterInfoHandler(RouterContext ctx, JSONRPC2Helper helper) { + _helper = helper; + _context = ctx; } + // Reports the method names of the handled requests public String[] handledRequests() { return new String[] { "RouterInfo" }; @@ -67,7 +64,7 @@ public class RouterInfoHandler implements RequestHandler { @SuppressWarnings("unchecked") private JSONRPC2Response process(JSONRPC2Request req) { - JSONRPC2Error err = JSONRPC2Helper.validateParams(null, req); + JSONRPC2Error err = _helper.validateParams(null, req); if (err != null) return new JSONRPC2Response(err, req.getID()); diff --git a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/RouterManagerHandler.java b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/RouterManagerHandler.java index 384fc1c..2a2dd89 100644 --- a/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/RouterManagerHandler.java +++ b/src/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/RouterManagerHandler.java @@ -5,6 +5,7 @@ import com.thetransactioncompany.jsonrpc2.JSONRPC2Request; import com.thetransactioncompany.jsonrpc2.JSONRPC2Response; import com.thetransactioncompany.jsonrpc2.server.MessageContext; import com.thetransactioncompany.jsonrpc2.server.RequestHandler; + import net.i2p.I2PAppContext; import net.i2p.app.ClientAppManager; import net.i2p.i2pcontrol.router.RouterManager; @@ -14,6 +15,7 @@ import net.i2p.router.networkdb.reseed.ReseedChecker; import net.i2p.update.UpdateManager; import net.i2p.update.UpdateType; import net.i2p.util.Log; + import org.tanukisoftware.wrapper.WrapperManager; import java.util.HashMap; @@ -37,17 +39,15 @@ import java.util.Map; */ public class RouterManagerHandler implements RequestHandler { - private static RouterContext _context; - private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(RouterManagerHandler.class); + private final JSONRPC2Helper _helper; + private final RouterContext _context; private final static int SHUTDOWN_WAIT = 1500; - static { - try { - _context = RouterManager.getRouterContext(); - } catch (Exception e) { - _log.error("Unable to initialize RouterContext.", e); - } + + public RouterManagerHandler(RouterContext ctx, JSONRPC2Helper helper) { + _helper = helper; + _context = ctx; } // Reports the method names of the handled requests @@ -67,7 +67,7 @@ public class RouterManagerHandler implements RequestHandler { } private JSONRPC2Response process(JSONRPC2Request req) { - JSONRPC2Error err = JSONRPC2Helper.validateParams(null, req); + JSONRPC2Error err = _helper.validateParams(null, req); if (err != null) return new JSONRPC2Response(err, req.getID());