[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r21852: {arm} Weekend bugfix bundle. added: returned option to reload torr (in arm/trunk: . init interface)
Author: atagar
Date: 2010-03-08 05:38:39 +0000 (Mon, 08 Mar 2010)
New Revision: 21852
Modified:
arm/trunk/ChangeLog
arm/trunk/TODO
arm/trunk/init/starter.py
arm/trunk/interface/confPanel.py
arm/trunk/interface/connPanel.py
arm/trunk/interface/controller.py
arm/trunk/interface/fileDescriptorPopup.py
arm/trunk/interface/headerPanel.py
Log:
Weekend bugfix bundle.
added: returned option to reload torrc and added option to issue a sighup
fix: header panel wasn't interpreting multi-line exit policies (caught by dun)
fix: substantial display bug when stripping comments and torrc is bigger than the panel (caught by Paul Menzel)
fix: deb specific hack for estimating the file descriptor limit was broken (caught by Paul Menzel)
fix: skip printing stack trace in case of keyboard interrupt
fix: updated listing of directory authorities (for tor version 0.2.1.24)
fix: several uncaught exceptions when the network consensus couldn't be fetched
fix: torrc comment stripping wasn't removing comments on the same lines as commands
fix: torrc validation was failing under some conditions for CSV values (like ExitPolicy)
Modified: arm/trunk/ChangeLog
===================================================================
--- arm/trunk/ChangeLog 2010-03-08 03:34:17 UTC (rev 21851)
+++ arm/trunk/ChangeLog 2010-03-08 05:38:39 UTC (rev 21852)
@@ -1,6 +1,19 @@
CHANGE LOG
-2/27/10 - version 1.3.3
+3/7/10 - version 1.3.4
+Weekend bugfix bundle.
+
+ * added: returned option to reload torrc and added option to issue a sighup
+ * fix: header panel wasn't interpreting multi-line exit policies (caught by dun)
+ * fix: substantial display bug when stripping comments and torrc is bigger than the panel (caught by Paul Menzel)
+ * fix: deb specific hack for estimating the file descriptor limit was broken (caught by Paul Menzel)
+ * fix: skip printing stack trace in case of keyboard interrupt
+ * fix: updated listing of directory authorities (for tor version 0.2.1.24)
+ * fix: several uncaught exceptions when the network consensus couldn't be fetched
+ * fix: torrc comment stripping wasn't removing comments on the same lines as commands
+ * fix: torrc validation was failing under some conditions for CSV values (like ExitPolicy)
+
+2/27/10 - version 1.3.3 (r21772)
Hiding client/exit information to address privacy concerns and fixes for numerous issues brought up in irc.
* added: scrubbing connection details of possible client and exit connections
Modified: arm/trunk/TODO
===================================================================
--- arm/trunk/TODO 2010-03-08 03:34:17 UTC (rev 21851)
+++ arm/trunk/TODO 2010-03-08 05:38:39 UTC (rev 21852)
@@ -30,6 +30,9 @@
be given the "UNKNOWN" type.
* regex fails for multiline log entries
* when logging no events still showing brackets
+ * scrolling in the torrc isn't working properly when comments are stripped
+ Current method of displaying torrc is pretty stupid (lots of repeated
+ work in display loop). When rewritten fixing this bug should be trivial.
* quitting can hang several seconds when there's hostnames left to resolve
Not sure how to address this - problem is that the calls to 'host' can
take a while to time out. Might need another thread to kill the calls?
Modified: arm/trunk/init/starter.py
===================================================================
--- arm/trunk/init/starter.py 2010-03-08 03:34:17 UTC (rev 21851)
+++ arm/trunk/init/starter.py 2010-03-08 05:38:39 UTC (rev 21852)
@@ -17,8 +17,8 @@
from TorCtl import TorCtl, TorUtil
from interface import controller, logPanel
-VERSION = "1.3.3"
-LAST_MODIFIED = "Feb 27, 2010"
+VERSION = "1.3.4"
+LAST_MODIFIED = "Mar 7, 2010"
DEFAULT_CONTROL_ADDR = "127.0.0.1"
DEFAULT_CONTROL_PORT = 9051
@@ -151,7 +151,11 @@
conn.authenticate("")
elif authInfo.startswith("AUTH METHODS=HASHEDPASSWORD"):
# password authentication, promts for password if it wasn't provided
- if not authPassword: authPassword = getpass.getpass()
+ try:
+ if not authPassword: authPassword = getpass.getpass()
+ except KeyboardInterrupt:
+ sys.exit()
+
conn.authenticate(authPassword)
elif authInfo.startswith("AUTH METHODS=COOKIE"):
# cookie authtication, parses path to authentication cookie
Modified: arm/trunk/interface/confPanel.py
===================================================================
--- arm/trunk/interface/confPanel.py 2010-03-08 03:34:17 UTC (rev 21851)
+++ arm/trunk/interface/confPanel.py 2010-03-08 05:38:39 UTC (rev 21852)
@@ -54,11 +54,15 @@
self.reset()
- def reset(self):
+ def reset(self, logErrors=True):
"""
- Reloads torrc contents and resets scroll height.
+ Reloads torrc contents and resets scroll height. Returns True if
+ successful, else false.
"""
+
try:
+ resetSuccessful = True
+
confFile = open(self.confLocation, "r")
self.confContents = confFile.readlines()
confFile.close()
@@ -114,10 +118,27 @@
else:
# general case - fetch all valid values
for key, val in self.conn.get_option(command):
- actualValues.append(val)
+ # TODO: check for a better way of figuring out CSV parameters
+ # (kinda doubt this is right... in config.c its listed as being
+ # a 'LINELIST') - still, good enough for common cases
+ if command in MULTI_LINE_PARAM: toAdd = val.split(",")
+ else: toAdd = [val]
+
+ for newVal in toAdd:
+ newVal = newVal.strip()
+ if newVal not in actualValues: actualValues.append(newVal)
- if not argument in actualValues:
- self.corrections[lineNumber + 1] = ", ".join(actualValues)
+ # there might be multiple values on a single line - if so, check each
+ if command in MULTI_LINE_PARAM and "," in argument:
+ arguments = []
+ for entry in argument.split(","):
+ arguments.append(entry.strip())
+ else:
+ arguments = [argument]
+
+ for entry in arguments:
+ if not entry in actualValues:
+ self.corrections[lineNumber + 1] = ", ".join(actualValues)
except (TypeError, socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed):
# TODO: for some reason the above provided:
# TypeError: sequence item 0: expected string, NoneType found
@@ -127,21 +148,25 @@
# reproduce. Catching the TypeError to just drop the torrc
# validation for those systems
- self.logger.monitor_event("WARN", "Unable to validate torrc")
+ if logErrors: self.logger.monitor_event("WARN", "Unable to validate torrc")
# logs issues that arose
- if self.irrelevantLines:
+ if self.irrelevantLines and logErrors:
if len(self.irrelevantLines) > 1: first, second, third = "Entries", "are", ", including lines"
else: first, second, third = "Entry", "is", " on line"
baseMsg = "%s in your torrc %s ignored due to duplication%s" % (first, second, third)
self.logger.monitor_event("NOTICE", "%s: %s (highlighted in blue)" % (baseMsg, ", ".join([str(val) for val in self.irrelevantLines])))
- if self.corrections:
+
+ if self.corrections and logErrors:
self.logger.monitor_event("WARN", "Tor's state differs from loaded torrc")
except IOError, exc:
+ resetSuccessful = False
self.confContents = ["### Unable to load torrc ###"]
- self.logger.monitor_event("WARN", "Unable to load torrc (%s)" % str(exc))
+ if logErrors: self.logger.monitor_event("WARN", "Unable to load torrc (%s)" % str(exc))
+
self.scroll = 0
+ return resetSuccessful
def handleKey(self, key):
self._resetBounds()
@@ -163,7 +188,24 @@
numFieldWidth = int(math.log10(len(self.confContents))) + 1
lineNum, displayLineNum = self.scroll + 1, 1 # lineNum corresponds to torrc, displayLineNum concerns what's presented
- for i in range(self.scroll, min(len(self.confContents), self.scroll + pageHeight)):
+ # determine the ending line in the display (prevents us from going to the
+ # effort of displaying lines that aren't visible - isn't really a
+ # noticeable improvement unless the torrc is bazaarly long)
+ if not self.stripComments:
+ endingLine = min(len(self.confContents), self.scroll + pageHeight)
+ else:
+ # checks for the last line of displayable content (ie, non-comment)
+ endingLine = self.scroll
+ displayedLines = 0 # number of lines of content
+ for i in range(self.scroll, len(self.confContents)):
+ endingLine += 1
+ lineText = self.confContents[i].strip()
+
+ if lineText and lineText[0] != "#":
+ displayedLines += 1
+ if displayedLines == pageHeight: break
+
+ for i in range(self.scroll, endingLine):
lineText = self.confContents[i].strip()
skipLine = False # true if we're not presenting line due to stripping
@@ -184,6 +226,7 @@
if argEnd == -1: argEnd = len(lineText)
command, argument, comment = lineText[:ctlEnd], lineText[ctlEnd:argEnd], lineText[argEnd:]
+ if self.stripComments: comment = ""
# changes presentation if value's incorrect or irrelevant
if lineNum in self.corrections.keys():
Modified: arm/trunk/interface/connPanel.py
===================================================================
--- arm/trunk/interface/connPanel.py 2010-03-08 03:34:17 UTC (rev 21851)
+++ arm/trunk/interface/connPanel.py 2010-03-08 05:38:39 UTC (rev 21852)
@@ -22,14 +22,16 @@
# CHANGE THIS UNLESS YOU HAVE A DAMN GOOD REASON!)
SCRUB_PRIVATE_DATA = True
-# directory servers (IP, port) for tor version 0.2.2.1-alpha-dev
+# directory servers (IP, port) for tor version 0.2.1.24
+# this comes from the dirservers array in src/or/config.c
DIR_SERVERS = [("86.59.21.38", "80"), # tor26
- ("128.31.0.34", "9031"), # moria1
+ ("128.31.0.39", "9031"), # moria1
("216.224.124.114", "9030"), # ides
- ("80.190.246.100", "80"), # gabelmoo
+ ("80.190.246.100", "8180"), # gabelmoo
("194.109.206.212", "80"), # dizum
- ("213.73.91.31", "80"), # dannenberg
- ("208.83.223.34", "443")] # urras
+ ("193.23.244.244", "80"), # dannenberg
+ ("208.83.223.34", "443"), # urras
+ ("82.94.251.203", "80")] # Tonga
# enums for listing types
LIST_IP, LIST_HOSTNAME, LIST_FINGERPRINT, LIST_NICKNAME = range(4)
@@ -286,7 +288,9 @@
isGuard = False
try:
myFingerprint = self.conn.get_info("fingerprint")
- isGuard = "Guard" in self.conn.get_network_status("id/%s" % myFingerprint)[0].flags
+ nsCall = self.conn.get_network_status("id/%s" % myFingerprint)
+ if nsCall: isGuard = "Guard" in nsCall[0].flags
+ else: raise TorCtl.ErrorReply # network consensus couldn't be fetched
except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): pass
try:
@@ -379,9 +383,9 @@
tmpCounter = 0 # used for unique port of unresolved family entries (funky hack)
for fingerprint in self.family:
try:
- nsCommand = "ns/id/%s" % fingerprint
- familyInfo = self.conn.get_info(nsCommand)[nsCommand].split()
- familyAddress, familyPort = familyInfo[6], familyInfo[7]
+ nsCall = self.conn.get_network_status("id/%s" % fingerprint)
+ if nsCall: familyAddress, familyPort = nsCall[0][6], nsCall[0][7]
+ else: raise TorCtl.ErrorReply # network consensus couldn't be fetched
countryCodeQuery = "ip-to-country/%s" % familyAddress
familyCountryCode = self.conn.get_info(countryCodeQuery)[countryCodeQuery]
@@ -706,9 +710,9 @@
# gets router description to see if 'down' is set
toRemove = False
try:
- nsData = self.conn.get_network_status("id/%s" % entryFingerprint)
- if len(nsData) != 1: raise TorCtl.ErrorReply() # ns lookup failed... weird
- else: nsEntry = nsData[0]
+ nsCall = self.conn.get_network_status("id/%s" % entryFingerprint)
+ if not nsCall: raise TorCtl.ErrorReply() # network consensus couldn't be fetched
+ else: nsEntry = nsCall[0]
descLookupCmd = "desc/id/%s" % entryFingerprint
descEntry = TorCtl.Router.build_from_desc(self.conn.get_info(descLookupCmd)[descLookupCmd].split("\n"), nsEntry)
@@ -740,7 +744,10 @@
match = self.getFingerprint(ipAddr, port)
try:
- if match != "UNKNOWN": match = self.conn.get_network_status("id/%s" % match)[0].nickname
+ if match != "UNKNOWN":
+ nsCall = self.conn.get_network_status("id/%s" % match)
+ if nsCall: match = nsCall[0].nickname
+ else: raise TorCtl.ErrorReply # network consensus couldn't be fetched
except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): return "UNKNOWN" # don't cache result
self.nicknameLookupCache[(ipAddr, port)] = match
Modified: arm/trunk/interface/controller.py
===================================================================
--- arm/trunk/interface/controller.py 2010-03-08 03:34:17 UTC (rev 21851)
+++ arm/trunk/interface/controller.py 2010-03-08 05:38:39 UTC (rev 21852)
@@ -554,6 +554,9 @@
lineNumLabel = "on" if panels["torrc"].showLineNum else "off"
popup.addfstr(3, 41, "n: line numbering (<b>%s</b>)" % lineNumLabel)
+
+ popup.addstr(4, 2, "r: reload torrc")
+ popup.addstr(4, 41, "x: reset tor (issue sighup)")
popup.addstr(7, 2, "Press any key...")
popup.refresh()
@@ -861,15 +864,16 @@
if selection in relayLookupCache.keys(): nsEntry, descEntry = relayLookupCache[selection]
else:
# ns lookup fails, can happen with localhost lookups if relay's having problems (orport not reachable)
- try: nsData = conn.get_network_status("id/%s" % fingerprint)
+ # and this will be empty if network consensus couldn't be fetched
+ try: nsCall = conn.get_network_status("id/%s" % fingerprint)
except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): lookupErrored = True
- if not lookupErrored:
- if len(nsData) > 1:
+ if not lookupErrored and nsCall:
+ if len(nsCall) > 1:
# multiple records for fingerprint (shouldn't happen)
panels["log"].monitor_event("WARN", "Multiple consensus entries for fingerprint: %s" % fingerprint)
- nsEntry = nsData[0]
+ nsEntry = nsCall[0]
try:
descLookupCmd = "desc/id/%s" % fingerprint
@@ -1087,6 +1091,74 @@
setPauseState(panels, isPaused, page)
finally:
panel.CURSES_LOCK.release()
+ elif page == 2 and key == ord('r') or key == ord('R'):
+ # reloads torrc, providing a notice if successful or not
+ isSuccessful = panels["torrc"].reset(False)
+ resetMsg = "torrc reloaded" if isSuccessful else "failed to reload torrc"
+ if isSuccessful: panels["torrc"].redraw()
+
+ panels["control"].setMsg(resetMsg, curses.A_STANDOUT)
+ panels["control"].redraw()
+ time.sleep(1)
+
+ panels["control"].setMsg(CTL_PAUSED if isPaused else CTL_HELP)
+ elif page == 2 and (key == ord('x') or key == ord('X')):
+ # provides prompt to confirm that arm should issue a sighup
+ panel.CURSES_LOCK.acquire()
+ try:
+ setPauseState(panels, isPaused, page, True)
+
+ # provides prompt
+ panels["control"].setMsg("This will reset Tor's internal state. Are you sure (x again to confirm)?", curses.A_BOLD)
+ panels["control"].redraw()
+
+ curses.cbreak()
+ confirmationKey = stdscr.getch()
+ if confirmationKey in (ord('x'), ord('X')):
+ try:
+ # Redirects stderr to stdout so we can check error status (output
+ # should be empty if successful). Example error:
+ # pkill: 5592 - Operation not permitted
+ #
+ # note that this may provide multiple errors, even if successful,
+ # hence this:
+ # - only provide an error if Tor fails to log a sighup
+ # - provide the error message associated with the tor pid (others
+ # would be a red herring)
+ pkillCall = os.popen("pkill -sighup tor 2> /dev/stdout")
+ pkillOutput = pkillCall.readlines()
+ pkillCall.close()
+
+ # Give the sighupTracker a moment to detect the sighup signal. This
+ # is, of course, a possible concurrency bug. However I'm not sure
+ # of a better method for blocking on this...
+ waitStart = time.time()
+ while time.time() - waitStart < 1:
+ time.sleep(0.1)
+ if sighupTracker.isReset: break
+
+ if not sighupTracker.isReset:
+ errorLine = ""
+ if torPid:
+ for line in pkillOutput:
+ if line.startswith("pkill: %s - " % torPid):
+ errorLine = line
+ break
+
+ if errorLine: raise IOError(" ".join(errorLine.split()[3:]))
+ else: raise IOError()
+ except IOError, err:
+ errorMsg = " (%s)" % str(err) if str(err) else ""
+ panels["control"].setMsg("Sighup failed%s" % errorMsg, curses.A_STANDOUT)
+ panels["control"].redraw()
+ time.sleep(2)
+
+ # reverts display settings
+ curses.halfdelay(REFRESH_RATE * 10)
+ panels["control"].setMsg(CTL_PAUSED if isPaused else CTL_HELP)
+ setPauseState(panels, isPaused, page)
+ finally:
+ panel.CURSES_LOCK.release()
elif page == 0:
panels["log"].handleKey(key)
elif page == 1:
@@ -1095,5 +1167,8 @@
panels["torrc"].handleKey(key)
def startTorMonitor(conn, loggedEvents, isBlindMode):
- curses.wrapper(drawTorMonitor, conn, loggedEvents, isBlindMode)
+ try:
+ curses.wrapper(drawTorMonitor, conn, loggedEvents, isBlindMode)
+ except KeyboardInterrupt:
+ pass # skip printing stack trace in case of keyboard interrupt
Modified: arm/trunk/interface/fileDescriptorPopup.py
===================================================================
--- arm/trunk/interface/fileDescriptorPopup.py 2010-03-08 03:34:17 UTC (rev 21851)
+++ arm/trunk/interface/fileDescriptorPopup.py 2010-03-08 05:38:39 UTC (rev 21852)
@@ -68,7 +68,7 @@
# get the file descriptor limit for an arbitrary process. What we need is
# for the tor process to provide the return value of the "getrlimit"
# function via a GET_INFO call.
- if torUser == "debian-tor":
+ if torUser.strip() == "debian-tor":
# probably loaded via /etc/init.d/tor which changes descriptor limit
self.fdLimit = 8192
else:
Modified: arm/trunk/interface/headerPanel.py
===================================================================
--- arm/trunk/interface/headerPanel.py 2010-03-08 03:34:17 UTC (rev 21851)
+++ arm/trunk/interface/headerPanel.py 2010-03-08 05:38:39 UTC (rev 21852)
@@ -18,8 +18,8 @@
"Stable": "blue", "Running": "yellow", "Unnamed": "magenta", "Valid": "green",
"V2Dir": "cyan", "V3Dir": "white"}
-VERSION_STATUS_COLORS = {"new": "blue", "new in series": "blue", "recommended": "green",
- "old": "red", "obsolete": "red", "unrecommended": "red"}
+VERSION_STATUS_COLORS = {"new": "blue", "new in series": "blue", "recommended": "green", "old": "red",
+ "obsolete": "red", "unrecommended": "red", "unknown": "cyan"}
class HeaderPanel(panel.Panel):
"""
@@ -191,9 +191,16 @@
try:
# parameters from the user's torrc
- configFields = ["Nickname", "ORPort", "DirPort", "ControlPort", "ExitPolicy"]
+ configFields = ["Nickname", "ORPort", "DirPort", "ControlPort"]
self.vals.update(dict([(key, self.conn.get_option(key)[0][1]) for key in configFields]))
+ # fetch exit policy (might span over multiple lines)
+ exitPolicyEntries = []
+ for (key, value) in self.conn.get_option("ExitPolicy"):
+ exitPolicyEntries.append(value)
+
+ self.vals["ExitPolicy"] = ", ".join(exitPolicyEntries)
+
# simply keeps booleans for if authentication info is set
self.vals["IsPasswordAuthSet"] = not self.conn.get_option("HashedControlPassword")[0][1] == None
self.vals["IsCookieAuthSet"] = self.conn.get_option("CookieAuthentication")[0][1] == "1"
@@ -231,7 +238,10 @@
# flags held by relay
self.vals["flags"] = []
if self.vals["fingerprint"] != "Unknown":
- try: self.vals["flags"] = self.conn.get_network_status("id/%s" % self.vals["fingerprint"])[0].flags
+ try:
+ nsCall = self.conn.get_network_status("id/%s" % self.vals["fingerprint"])
+ if nsCall: self.vals["flags"] = nsCall[0].flags
+ else: raise TorCtl.ErrorReply # network consensus couldn't be fetched
except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): pass
psParams = ["%cpu", "rss", "%mem", "etime"]