package org.torproject.jtor.config.impl; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.net.InetAddress; import org.torproject.jtor.TorConfig; import org.torproject.jtor.logging.Logger; /** * * @author Merlijn Hofstra */ public class TorConfigParser { private TorConfigParser() {} public static boolean parseFile(TorConfig tc, Logger logger, File in) { try { BufferedReader reader = new BufferedReader(new FileReader(in)); String line = null; while ((line=reader.readLine()) != null) { if (line.startsWith("#") || line.matches("^\\s*$")) { // skip comments and empty lines continue; } // strip comments after a setting int index = line.indexOf("#"); if (index != -1 && !line.substring(index-1, index).equals("\\")) { line = line.substring(0, index-1); } //strip any trailing whitespace line = line.replaceAll("\\s*$", ""); int separator = line.indexOf(" "); String key = line.substring(0, separator); String value = line.substring(separator+1); if (!setConf(tc, key, value)) { // syntax error in file logger.error("torrc: Could not parse this line: " + line); return false; } } } catch (FileNotFoundException ex) { //no config file available return true; } catch (IOException e) { return false; } return true; } public static boolean setConf(TorConfig tc, String key, String value) { try { key = key.toLowerCase(); if (key.equals("configfile")) { tc.setConfigFile(value); } else if (key.equals("datadirectory")) { tc.setDataDirectory(new File(value)); } else if (key.equals("bandwidthrate")) { tc.setBandwidthRate(toBytes(value)); } else if (key.equals("bandwidthburst")) { tc.setBandwidthBurst(toBytes(value)); } else if (key.equals("maxadvertisedbandwidth")) { tc.setMaxAdvertisedBandwidth(toBytes(value)); } else if (key.equals("controlport")) { tc.setControlPort(Short.parseShort(value)); } else if (key.equals("hashedcontrolpassword")) { tc.setHashedControlPassword(value); } else if (key.equals("cookieauthentication")) { tc.setCookieAuthentication(isTrue(value)); } else if (key.equals("dirfetchperiod")) { tc.setDirFetchPeriod(toSeconds(value)); } else if (key.equals("dirserver")) { String[] newar = addToStringArray(tc.getDirServer(), value); tc.setDirServer(newar); } else if (key.equals("tunneldirconns")) { tc.setTunnelDirConns(isTrue(value)); } else if (key.equals("prefertunneleddirconns")) { tc.setPreferTunneledDirConns(isTrue(value)); } else if (key.equals("disableallswap")) { tc.setDisableAllSwap(isTrue(value)); } else if (key.equals("group")) { tc.setGroup(value); } else if (key.equals("httpproxy")) { tc.setHttpProxy(value); } else if (key.equals("httpproxyauthenticator")) { tc.setHttpProxyAuthenticator(value); } else if (key.equals("httpsproxy")) { tc.setHttpsProxy(value); } else if (key.equals("httpsproxyauthenticator")) { tc.setHttpsProxyAuthenticator(value); } else if (key.equals("keepaliveperiod")) { tc.setKeepalivePeriod((int)toSeconds(value)); } else if (key.equals("log")) { String[] newar = addToStringArray(tc.getLog(), value); tc.setLog(newar); } else if (key.equals("maxconn")) { tc.setMaxConn(Integer.parseInt(value)); } else if (key.equals("outboundbindaddress")) { tc.setOutboundBindAddress(InetAddress.getByName(value)); } else if (key.equals("pidfile")) { tc.setPidFile(value); } else if (key.equals("runasdaemon")) { tc.setRunAsDaemon(isTrue(value)); } else if (key.equals("safelogging")) { tc.setSafeLogging(isTrue(value)); } else if (key.equals("statusfetchperiod")) { tc.setStatusFetchPeriod(toSeconds(value)); } else if (key.equals("user")) { tc.setUser(value); } else if (key.equals("hardwareaccel")) { tc.setHardwareAccel(isTrue(value)); } else if (key.equals("allowunverifiednodes")) { tc.setAllowUnverifiedNodes(value); } else if (key.equals("clientonly")) { tc.setClientOnly(isTrue(value)); } else if (key.equals("entrynodes")) { String[] newar = addToStringArray(tc.getEntryNodes(), value); tc.setEntryNodes(newar); } else if (key.equals("exitnodes")) { String[] newar = addToStringArray(tc.getExitNodes(), value); tc.setExitNodes(newar); } else if (key.equals("excludenodes")) { String[] newar = addToStringArray(tc.getExcludeNodes(), value); tc.setExcludeNodes(newar); } else if (key.equals("strictexitnodes")) { tc.setStrictExitNodes(isTrue(value)); } else if (key.equals("strictentrynodes")) { tc.setStrictEntryNodes(isTrue(value)); } else if (key.equals("fascistfirewall")) { tc.setFascistFirewall(isTrue(value)); } else if (key.equals("firewallports")) { // needs splitting TODO short[] newar = addToShortArray(tc.getFirewallPorts(), Short.parseShort(value)); tc.setFirewallPorts(newar); } else if (key.equals("firewallips")) { String[] newar = addToStringArray(tc.getFirewallIPs(), value); tc.setExcludeNodes(newar); } else if (key.equals("reachableaddresses")) { tc.setReachableAddresses(value); } else if (key.equals("longlivedports")) { // needs splitting TODO short[] newar = addToShortArray(tc.getLongLivedPorts(), Short.parseShort(value)); tc.setLongLivedPorts(newar); } else if (key.equals("mapaddress")) { String[] newar = addToStringArray(tc.getMapAddress(), value); tc.setMapAddress(newar); } else if (key.equals("newcircuitperiod")) { tc.setNewCircuitPeriod(toSeconds(value)); } else if (key.equals("maxcircuitdirtiness")) { tc.setMaxCircuitDirtiness(toSeconds(value)); } else if (key.equals("nodefamily")) { String[] newar = addToStringArray(tc.getNodeFamily(), value); tc.setNodeFamily(newar); } else if (key.equals("rendnodes")) { String[] newar = addToStringArray(tc.getRendNodes(), value); tc.setRendNodes(newar); } else if (key.equals("rendexcludenodes")) { String[] newar = addToStringArray(tc.getRendExcludeNodes(), value); tc.setRendExcludeNodes(newar); } else if (key.equals("socksport")) { tc.setSocksPort(Short.parseShort(value)); } else if (key.equals("socksbindaddress")) { tc.setSocksBindAddress(value); } else if (key.equals("sockslistenaddress")) { tc.setSocksBindAddress(value); } else if (key.equals("sockspolicy")) { tc.setSocksPolicy(value); } else if (key.equals("trackhostexits")) { String[] newar = addToStringArray(tc.getTrackHostExits(), value); tc.setTrackHostExits(newar); } else if (key.equals("trackhostexitsexpire")) { tc.setTrackHostExitsExpire(toSeconds(value)); } else if (key.equals("usehelpernodes")) { tc.setUseHelperNodes(isTrue(value)); } else if (key.equals("numhelpernodes")) { tc.setNumHelperNodes(Integer.parseInt(value)); } else if (key.equals("hiddenservicedir")) { String[] newar = addToStringArray(tc.getHiddenServiceDir(), value); tc.setHiddenServiceDir(newar); } else if (key.equals("hiddenserviceport")) { // needs splitting? String[] newar = addToStringArray(tc.getHiddenServicePort(), value); tc.setHiddenServicePort(newar); } else if (key.equals("hiddenservicenodes")) { String[] newar = addToStringArray(tc.getHiddenServiceNodes(), value); tc.setHiddenServiceNodes(newar); } else if (key.equals("hiddenserviceexcludenodes")) { String[] newar = addToStringArray(tc.getHiddenServiceExcludeNodes(), value); tc.setHiddenServiceExcludeNodes(newar); } else if (key.equals("hiddenserviceversion")) { tc.setHiddenServiceVersion(value); } else if (key.equals("rendpostperiod")) { tc.setRendPostPeriod(toSeconds(value)); } else if (key.equals("__alldiroptionsprivate")) { tc.set__AllDirOptionsPrivate(isTrue(value)); } else if (key.equals("__disablepredictedcircuits")) { tc.set__DisablePredictedCircuits(isTrue(value)); } else if (key.equals("__leavestreamsunattached")) { tc.set__LeaveStreamsUnattached(isTrue(value)); } else if (key.equals("__hashedcontrolsessionpassword")) { tc.set__HashedControlSessionPassword(value); } else if (key.equals("__reloadtorrconsighup")) { tc.set__ReloadTorrcOnSIGHUP(isTrue(value)); } else { // key was not found return false; } } catch(Throwable t) { // if any errors occur, the operation failed. return false; } return true; } /** * convert a human readable timestamp to seconds * @param in - a String like '2 minutes' or '4 weeks' * @return - value in seconds or 0 if arg cannot be parsed */ public static long toSeconds(String in) { if (in.matches("^\\d+$")) { return Long.parseLong(in); } String mp = in.replaceAll("^\\d+ ?(\\w+)$", "$1"); long num = Long.parseLong(in.replaceAll("^(\\d+) ?\\w*$", "$1")); if (mp.equalsIgnoreCase("seconds")) { return num; } if (mp.equalsIgnoreCase("minutes")) { return num * 60; } if (mp.equalsIgnoreCase("hours")) { return num * 60 * 60; } if (mp.equalsIgnoreCase("days")) { return num * 60 * 60 * 24; } if (mp.equalsIgnoreCase("weeks")) { return num * 60 * 60 * 24 * 7; } return 0; } /** * convert a human readable size format to bytes * @param in - a String in the format of 5MB or 10GB * @return - value as bytes or 0 if arg cannot be parsed */ public static long toBytes(String in) { if (in.matches("^\\d+$")) { return Long.parseLong(in); } String[] map = { "b", "kb", "mb", "gb", "tb" }; String mp = in.replaceAll("^\\d+ ?(\\w+)$", "$1"); long num = Long.parseLong(in.replaceAll("^(\\d+) ?\\w*$", "$1")); for (int i = 0; i < map.length; i++) { if (mp.equalsIgnoreCase(map[i])) { return num * (long)Math.pow(1024, i); } } return 0; } public static String[] addToStringArray(String[] ar, String val) { if (ar == null) { String[] ret = new String[1]; ret[0] = val; return ret; } String[] ret = new String[ar.length+1]; System.arraycopy(ar, 0, ret, 0, ar.length); ret[ar.length] = val; return ret; } public static short[] addToShortArray(short[] ar, short val) { if (ar == null) { short[] ret = new short[1]; ret[0] = val; return ret; } short[] ret = new short[ar.length+1]; System.arraycopy(ar, 0, ret, 0, ar.length); ret[ar.length] = val; return ret; } public static boolean isTrue(String in) { return !(in.equals("0") || in.equals("false")); } }