[tor-commits] [arm/release] Torrc generation from the wizard input

atagar at torproject.org atagar at torproject.org
Sun Jul 17 06:08:30 UTC 2011


commit b888d3657c85beed4c63d1db4e3df06f4ffeef3d
Author: Damian Johnson <atagar at torproject.org>
Date:   Mon Jul 4 22:45:19 2011 -0700

    Torrc generation from the wizard input
    
    This is most of the functionality needed to generate a torrc from the relay
    setup wizard's input (minus a few special-case fields like the exit policy
    and burst rate).
    
    This is done via a template that's filled in by the wizard results. The
    templating language just contains the simple set of control structures (if,
    if not, or) needed for this.
---
 src/cli/wizard.py               |   43 +++++++++++++++-
 src/resources/torrcTemplate.txt |   77 ++++++++++++++++++++++++++++
 src/util/torConfig.py           |  106 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 224 insertions(+), 2 deletions(-)

diff --git a/src/cli/wizard.py b/src/cli/wizard.py
index 8239487..03cfb48 100644
--- a/src/cli/wizard.py
+++ b/src/cli/wizard.py
@@ -3,13 +3,18 @@ Provides user prompts for setting up a new relay. This autogenerates a torrc
 that's used by arm to run its own tor instance.
 """
 
+import os
+import sys
 import functools
 import curses
 
 import cli.popups
 import cli.controller
 
-from util import connections, enum, uiTools
+from util import connections, enum, log, torConfig, uiTools
+
+# template used to generate the torrc
+TORRC_TEMPLATE = "resources/torrcTemplate.txt"
 
 # basic configuration types we can run as
 RelayType = enum.Enum("RELAY", "EXIT", "BRIDGE", "CLIENT")
@@ -249,7 +254,10 @@ def showWizard():
       selection = promptConfigOptions(relayType, config)
       
       if selection == BACK: relayType = None
-      elif selection == NEXT: break # TODO: implement next screen
+      elif selection == NEXT:
+        generatedTorrc = getTorrc(relayType, config)
+        log.log(log.NOTICE, "Resulting torrc:\n%s" % generatedTorrc)
+        break # TODO: implement next screen
     
     # redraws screen to clear away the dialog we just showed
     cli.controller.getController().requestRedraw(True)
@@ -407,6 +415,37 @@ def promptConfigOptions(relayType, config):
   finally:
     cli.popups.finalize()
 
+def getTorrc(relayType, config):
+  """
+  Provides the torrc generated for the given options.
+  """
+  
+  pathPrefix = os.path.dirname(sys.argv[0])
+  if pathPrefix and not pathPrefix.endswith("/"):
+    pathPrefix = pathPrefix + "/"
+  
+  templateFile = open("%s%s" % (pathPrefix, TORRC_TEMPLATE), "r")
+  template = templateFile.readlines()
+  templateFile.close()
+  
+  # generates the options the template expects
+  templateOptions = {}
+  
+  for key, value in config.items():
+    if isinstance(value, ConfigOption):
+      value = value.getValue()
+    
+    templateOptions[key.upper()] = value
+  
+  #templateOptions = dict([(key.upper(), config[key].getValue()) for key in config])
+  templateOptions[relayType.upper()] = True
+  templateOptions["LOW_PORTS"] = config[Options.LOWPORTS]
+  #templateOptions["BURST"] = config[Options.BANDWIDTH] * 2 # TODO: implement
+  templateOptions["NOTICE_PATH"] = "/path/to/.arm/exit-notice.html" # TODO: actually prepend the right prefix
+  templateOptions["EXIT_POLICY"] = "" # TODO: fill in configured policy
+  
+  return torConfig.renderTorrc(template, templateOptions)
+
 def _splitStr(msg, width):
   """
   Splits a string into substrings of a given length.
diff --git a/src/resources/torrcTemplate.txt b/src/resources/torrcTemplate.txt
new file mode 100644
index 0000000..bb4dccd
--- /dev/null
+++ b/src/resources/torrcTemplate.txt
@@ -0,0 +1,77 @@
+# This is a tor configuration made by arm. To change the configuration by hand
+# edit this file then either...
+# - tell arm to reset tor by pressing 'x'
+# - run 'pkill -sighup tor'
+# - restart tor
+#
+# Descriptions of all of these configuraiton attibutes (and many more) are
+# available in the tor man page.
+[NEWLINE]
+ControlPort 9052             # port controllers can connect to
+CookieAuthentication 1       # method for controller authentication
+
+[IF RELAY | EXIT | BRIDGE]
+  RunAsDaemon 1              # runs as a background process
+[END IF]
+[NEWLINE]
+
+[IF RELAY | EXIT | BRIDGE]
+  [IF LOWPORTS]
+    ORPort 443               # port used for relaying traffic
+  [ELSE]
+    ORPort 9001              # port used for relaying traffic
+  [END IF]
+  
+  [IF RELAY | EXIT]
+    [IF LOWPORTS]
+      DirPort 80             # port used for mirroring directory information
+    [ELSE]
+      DirPort 9030           # port used for mirroring directory information
+    [END IF]
+    
+    Nickname [NICKNAME]      # name for this relay
+    ContactInfo [CONTACT]    # contact information in case there's an issue
+  [END IF]
+  
+  [IF BRIDGE]
+    BridgeRelay 1            # makes us a bridge rather than a public relay
+    
+    [IF DISTRIBUTE]
+      # TODO: drop this?
+      PublishServerDescriptor bridge # publishes to users looking for bridges
+    [ELSE]
+      PublishServerDescriptor 0 # keeps our bridge descriptor private
+    [END IF]
+  [END IF]
+  
+  RelayBandwidthRate [BANDWIDTH] # limit for the bandwidth we'll use to relay
+  RelayBandwidthBurst [BURST] # maximum rate when relaying bursts of traffic
+  AccountingMax [LIMIT]      # maximum amount of traffic we'll relay per month
+  
+  [IF NOT CLIENT]
+    SocksPort 0              # prevents tor from being used as a client
+  [END IF]
+  
+  [IF PORTFORWARD]
+    PortForwarding 1         # negotiates UPnP and NAT-PMP if needed
+  [END IF]
+  
+  [IF RELAY | BRIDGE]
+    ExitPolicy reject *:*    # prevents us from connecting to non-relays
+  [ELSE]
+    [IF NOTICE]
+      DirPortFrontPage [NOTICE_PATH] # disclaimer saying that this is an exit
+    [END IF]
+    
+    [EXIT_POLICY]
+  [END IF]
+[ELSE]
+  ClientOnly 1               # prevents us from ever being used as a relay
+  MaxCircuitDirtiness [REUSE] # duration that circuits can be reused
+  
+  [IF BRIDGED]
+    UseBridges 1             # uses the following bridges to connect
+    [BRIDGES]
+  [END IF]
+[END IF]
+
diff --git a/src/util/torConfig.py b/src/util/torConfig.py
index a63eddb..e5f1cb2 100644
--- a/src/util/torConfig.py
+++ b/src/util/torConfig.py
@@ -845,3 +845,109 @@ def _testConfigDescriptions():
     print "\"%s\"" % description
     if i != len(sortedOptions) - 1: print "-" * 80
 
+def renderTorrc(template, options, commentIndent = 30):
+  """
+  Uses the given template to generate a nicely formatted torrc with the given
+  options. The tempating language this recognizes is a simple one, recognizing
+  the following options:
+    [IF <option>]         # if <option> maps to true or a non-empty string
+    [IF NOT <option>]     # logical inverse
+    [IF <opt1> | <opt2>]  # logical or of the options
+    [ELSE]          # if the prior conditional evaluated to false
+    [END IF]        # ends the control block
+    
+    [<option>]      # inputs the option value, omitting the line if it maps
+                    # to a boolean or empty string
+    [NEWLINE]       # empty line, otherwise templating white space is ignored
+  
+  Arguments:
+    template      - torrc template lines used to generate the results
+    options       - mapping of keywords to their given values, with values
+                    being booleans or strings (possibly multi-line)
+    commentIndent - minimum column that comments align on
+  """
+  
+  results = []
+  templateIter = iter(template)
+  commentLineFormat = "%%-%is%%s" % commentIndent
+  
+  try:
+    while True:
+      line = templateIter.next().strip()
+      
+      if line.startswith("[IF ") and line.endswith("]"):
+        # checks if any of the conditional options are true or a non-empty string
+        evaluatesTrue = False
+        for cond in line[4:-1].split("|"):
+          isInverse = False
+          if cond.startswith("NOT "):
+            isInverse = True
+            cond = cond[4:]
+          
+          if isInverse != bool(options.get(cond.strip())):
+            evaluatesTrue = True
+            break
+        
+        if evaluatesTrue:
+          continue
+        else:
+          # skips lines until we come to an else or the end of the block
+          depth = 0
+          
+          while depth != -1:
+            line = templateIter.next().strip()
+            
+            if line.startswith("[IF ") and line.endswith("]"): depth += 1
+            elif line == "[END IF]": depth -= 1
+            elif depth == 0 and line == "[ELSE]": depth -= 1
+      elif line == "[ELSE]":
+        # an else block we aren't using - skip to the end of it
+        depth = 0
+        
+        while depth != -1:
+          line = templateIter.next().strip()
+          
+          if line.startswith("[IF "): depth += 1
+          elif line == "[END IF]": depth -= 1
+      elif line == "[NEWLINE]":
+        # explicit newline
+        results.append("")
+      elif line.startswith("#"):
+        # comment only
+        results.append(line)
+      elif line.startswith("[") and line.endswith("]"):
+        # completely dynamic entry
+        optValue = options.get(line[1:-1])
+        if optValue: results.append(optValue)
+      else:
+        # torrc option line
+        option, arg, comment = "", "", ""
+        parsedLine = line
+        
+        if "#" in parsedLine:
+          parsedLine, comment = parsedLine.split("#", 1)
+          parsedLine = parsedLine.strip()
+          comment = "# %s" % comment.strip()
+        
+        # parses the argument from the option
+        if " " in parsedLine:
+          option, arg = parsedLine.split(" ", 1)
+          option = option.strip()
+        else:
+          log.log(log.INFO, "torrc template option lacks an argument: '%s'" % line)
+          continue
+        
+        # inputs dynamic arguments
+        if arg.startswith("[") and arg.endswith("]"):
+          arg = options.get(arg[1:-1])
+        
+        # skips argument if it's false or an empty string
+        if not arg: continue
+        
+        torrcEntry = "%s %s" % (option, arg)
+        if comment: results.append(commentLineFormat % (torrcEntry + " ", comment))
+        else: results.append(torrcEntry)
+  except StopIteration: pass
+  
+  return "\n".join(results)
+





More information about the tor-commits mailing list