tor-commits
Threads by month
- ----- 2025 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
July 2011
- 16 participants
- 868 discussions

17 Jul '11
commit 03aa67dcb5589e626306e89ef3dc92eb9c300de8
Author: Damian Johnson <atagar(a)torproject.org>
Date: Wed Apr 27 21:13:05 2011 -0700
fix: Misparsing circut paths for Tor < 0.2.2.1
For Tor versions prior to 0.2.2.1 our circuit paths could contain nickname-only
entries. These were being misparsed (expecting them to always start with
fingerprints), resulting in screwed up circuit entries in the connection panel.
Caught by asn.
---
src/util/torTools.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 65 insertions(+), 2 deletions(-)
diff --git a/src/util/torTools.py b/src/util/torTools.py
index 5d8ffd8..b527f1e 100644
--- a/src/util/torTools.py
+++ b/src/util/torTools.py
@@ -330,6 +330,7 @@ class Controller(TorCtl.PostEventListener):
self._fingerprintLookupCache = {} # lookup cache with (ip, port) -> fingerprint mappings
self._fingerprintsAttachedCache = None # cache of relays we're connected to
self._nicknameLookupCache = {} # lookup cache with fingerprint -> nickname mappings
+ self._nicknameToFpLookupCache = {} # lookup cache with nickname -> fingerprint mappings
self._consensusLookupCache = {} # lookup cache with network status entries
self._descriptorLookupCache = {} # lookup cache with relay descriptors
self._isReset = False # internal flag for tracking resets
@@ -389,6 +390,7 @@ class Controller(TorCtl.PostEventListener):
self._fingerprintLookupCache = {}
self._fingerprintsAttachedCache = None
self._nicknameLookupCache = {}
+ self._nicknameToFpLookupCache = {}
self._consensusLookupCache = {}
self._descriptorLookupCache = {}
@@ -1164,6 +1166,44 @@ class Controller(TorCtl.PostEventListener):
return result
+ def getNicknameFingerprint(self, relayNickname):
+ """
+ Provides the fingerprint associated with the given relay. This provides
+ None if no such relay exists.
+
+ Arguments:
+ relayNickname - nickname of the relay
+ """
+
+ self.connLock.acquire()
+
+ result = None
+ if self.isAlive():
+ # determine the nickname if it isn't yet cached
+ if not relayNickname in self._nicknameToFpLookupCache:
+ # Fingerprints are base64 encoded hex with an extra '='. For instance...
+ # GETINFO ns/name/torexp2 ->
+ # r torexp2 NPfjt8Vjr+drcbbFLQONN3KapNo LxoHteGax7ZNYh/9g/FF8I617fY 2011-04-27 15:20:35 141.161.20.50 9001 0
+ # decode base64 of "NPfjt8Vjr+drcbbFLQONN3KapNo=" ->
+ # "4\xf7\xe3\xb7\xc5c\xaf\xe7kq\xb6\xc5-\x03\x8d7r\x9a\xa4\xda"
+ # encode hex of the above ->
+ # "34f7e3b7c563afe76b71b6c52d038d37729aa4da"
+
+ relayFingerprint = None
+ consensusEntry = self.getInfo("ns/name/%s" % relayNickname)
+ if consensusEntry:
+ encodedFp = consensusEntry.split()[2]
+ decodedFp = (encodedFp + "=").decode('base64').encode('hex')
+ relayFingerprint = decodedFp.upper()
+
+ self._nicknameToFpLookupCache[relayNickname] = relayFingerprint
+
+ result = self._nicknameToFpLookupCache[relayNickname]
+
+ self.connLock.release()
+
+ return result
+
def addEventListener(self, listener):
"""
Directs further tor controller events to callback functions of the
@@ -1433,6 +1473,7 @@ class Controller(TorCtl.PostEventListener):
self._fingerprintLookupCache = {}
self._fingerprintsAttachedCache = None
self._nicknameLookupCache = {}
+ self._nicknameToFpLookupCache = {}
self._consensusLookupCache = {}
if self._fingerprintMappings != None:
@@ -1870,6 +1911,12 @@ class Controller(TorCtl.PostEventListener):
# 91 BUILT $E4AE6E2FE320FBBD31924E8577F3289D4BE0B4AD=Qwerty PURPOSE=GENERAL
# would belong to a single hop circuit, most likely fetching the
# consensus via a directory mirror.
+ #
+ # The path is made up of "$<fingerprint>[=<nickname]" entries for new
+ # versions of Tor, but in versions prior to 0.2.2.1-alpha this was
+ # just "$<fingerprint>" OR <nickname>. The dolar sign can't be used in
+ # nicknames so this can be used to differentiate.
+
circStatusResults = self.getInfo("circuit-status")
if circStatusResults == "":
@@ -1885,8 +1932,24 @@ class Controller(TorCtl.PostEventListener):
# 5 LAUNCHED PURPOSE=TESTING
if len(lineComp) < 4: continue
- path = tuple([hopEntry[1:41] for hopEntry in lineComp[2].split(",")])
- result.append((int(lineComp[0]), lineComp[1], lineComp[3][8:], path))
+ path = []
+ for hopEntry in lineComp[2].split(","):
+ if hopEntry[0] == "$": path.append(hopEntry[1:41])
+ else:
+ relayFingerprint = self.getNicknameFingerprint(hopEntry)
+
+ # It shouldn't be possible for this lookup to fail, but we
+ # need to fill something (callers won't expect our own client
+ # paths to have unknown relays). If this turns out to be wrong
+ # then log a warning.
+
+ if relayFingerprint: path.append(relayFingerprint)
+ else:
+ msg = "Unable to determine the fingerprint for a relay in our own circuit: %s" % hopEntry
+ log.log(log.WARN, msg)
+ path.append("0" * 40)
+
+ result.append((int(lineComp[0]), lineComp[1], lineComp[3][8:], tuple(path)))
elif key == "hsPorts":
result = []
hsOptions = self.getOptionMap("HiddenServiceOptions")
1
0

17 Jul '11
commit 1c907f900b09043c15b2fbdf0d87cdd67f111c18
Author: Damian Johnson <atagar(a)torproject.org>
Date: Wed Apr 27 21:20:54 2011 -0700
fix: Missed setup.py when renaming interface->cli
The setup.py still referenced interface, causing arm to fail to install.
---
setup.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/setup.py b/setup.py
index 9777da1..cb0069f 100644
--- a/setup.py
+++ b/setup.py
@@ -75,7 +75,7 @@ if "install" in sys.argv:
# When installing we include a bundled copy of TorCtl. However, when creating
# a deb we have a dependency on the python-torctl package instead:
# http://packages.debian.org/unstable/main/python-torctl
-installPackages = ['arm', 'arm.interface', 'arm.interface.graphing', 'arm.interface.connections', 'arm.util']
+installPackages = ['arm', 'arm.cli', 'arm.cli.graphing', 'arm.cli.connections', 'arm.util']
if not isDebInstall: installPackages.append('arm.TorCtl')
setup(name='arm',
1
0

17 Jul '11
commit c4454e05284d36fb6ecafc9c2f33c7b0a9ffab43
Author: Damian Johnson <atagar(a)torproject.org>
Date: Wed Apr 27 21:46:21 2011 -0700
fix: Stacktrace from unjoined thread at shutdown
There's a rare (but repeated) stack trace due to an unjoined thread at
shutdown:
Exception in thread Thread-3 (most likely raised during interpreter shutdown):
Traceback (most recent call last):
File "/usr/lib/python2.6/threading.py", line 525, in __bootstrap_inner
File "/home/atagar/Desktop/arm/src/util/sysTools.py", line 542, in run
File "/usr/lib/python2.6/threading.py", line 135, in release
<type 'exceptions.TypeError'>: 'NoneType' object is not callable
This isn't harmful, but it is annoying. This change is attempting to be extra
careful that all resourceTracker threads have been joined, and that the
controller is stopped beforehand (preventing stray BW events from triggering
additional queries).
The stacktrace _seems_ to be more commonly caused by long-running arm
processes, but I don't have a reliable repro case.
---
src/cli/controller.py | 13 ++++++-------
src/cli/graphing/resourceStats.py | 4 ++--
src/util/sysTools.py | 21 ++++-----------------
3 files changed, 12 insertions(+), 26 deletions(-)
diff --git a/src/cli/controller.py b/src/cli/controller.py
index 22d1772..8543ccb 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -825,20 +825,19 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
panels["conn"].join()
panels["log"].join()
- # joins on utility daemon threads - this might take a moment since
- # the internal threadpools being joined might be sleeping
conn = torTools.getConn()
- myPid = conn.getMyPid()
+ conn.close() # joins on TorCtl event thread
- resourceTracker = sysTools.getResourceTracker(myPid) if (myPid and sysTools.isTrackerAlive(myPid)) else None
+ # joins on utility daemon threads - this might take a moment since
+ # the internal threadpools being joined might be sleeping
+ resourceTrackers = sysTools.RESOURCE_TRACKERS.values()
resolver = connections.getResolver("tor") if connections.isResolverAlive("tor") else None
- if resourceTracker: resourceTracker.stop()
+ for tracker in resourceTrackers: tracker.stop()
if resolver: resolver.stop() # sets halt flag (returning immediately)
hostnames.stop() # halts and joins on hostname worker thread pool
- if resourceTracker: resourceTracker.join()
+ for tracker in resourceTrackers: tracker.join()
if resolver: resolver.join() # joins on halted resolver
- conn.close() # joins on TorCtl event thread
break
elif key == curses.KEY_LEFT or key == curses.KEY_RIGHT:
# switch page
diff --git a/src/cli/graphing/resourceStats.py b/src/cli/graphing/resourceStats.py
index f26d5c1..a9a8aee 100644
--- a/src/cli/graphing/resourceStats.py
+++ b/src/cli/graphing/resourceStats.py
@@ -36,9 +36,9 @@ class ResourceStats(graphPanel.GraphStats):
primary, secondary = 0, 0
if self.queryPid:
- resourceTracker = sysTools.getResourceTracker(self.queryPid)
+ resourceTracker = sysTools.getResourceTracker(self.queryPid, True)
- if not resourceTracker.lastQueryFailed():
+ if resourceTracker and not resourceTracker.lastQueryFailed():
primary, _, secondary, _ = resourceTracker.getResourceUsage()
primary *= 100 # decimal percentage to whole numbers
secondary /= 1048576 # translate size to MB so axis labels are short
diff --git a/src/util/sysTools.py b/src/util/sysTools.py
index 8d95733..2775194 100644
--- a/src/util/sysTools.py
+++ b/src/util/sysTools.py
@@ -347,27 +347,13 @@ def call(command, cacheAge=0, suppressExc=False, quiet=True):
return results
-def isTrackerAlive(pid):
- """
- Provides true if a running, singleton instance exists for the given pid,
- false otherwise.
-
- Arguments:
- pid - pid of the process being tracked
- """
-
- if pid in RESOURCE_TRACKERS:
- if RESOURCE_TRACKERS[pid].isAlive(): return True
- else: del RESOURCE_TRACKERS[pid]
-
- return False
-
-def getResourceTracker(pid):
+def getResourceTracker(pid, noSpawn = False):
"""
Provides a running singleton ResourceTracker instance for the given pid.
Arguments:
- pid - pid of the process being tracked
+ pid - pid of the process being tracked
+ noSpawn - returns None rather than generating a singleton instance if True
"""
if pid in RESOURCE_TRACKERS:
@@ -375,6 +361,7 @@ def getResourceTracker(pid):
if tracker.isAlive(): return tracker
else: del RESOURCE_TRACKERS[pid]
+ if noSpawn: return None
tracker = ResourceTracker(pid, CONFIG["queries.resourceUsage.rate"])
RESOURCE_TRACKERS[pid] = tracker
tracker.start()
1
0

17 Jul '11
commit f75b0c9f47ea4932cbdfea888cab55af7397c5a6
Author: Damian Johnson <atagar(a)torproject.org>
Date: Wed Apr 27 09:41:40 2011 -0700
Delaying connection resolution until after init
This is a huge performance fix, dropping the startup time from 0.84 seconds to
0.14 (83% improvement). Arm had been fetching connection contents on init,
blocking the first redraw. The fix is to both move connection init into its
runtime thread *and* delay its execution until after the initial redraw. This
hides the call latency in the period that we're usually waiting for user input
anyway.
There's still perceived latency if the user immediately switches to the
connections page, but this is both unavoidable and unusual.
---
src/cli/connections/connPanel.py | 13 ++++++++++---
1 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/cli/connections/connPanel.py b/src/cli/connections/connPanel.py
index 8339f88..7e12232 100644
--- a/src/cli/connections/connPanel.py
+++ b/src/cli/connections/connPanel.py
@@ -71,9 +71,6 @@ class ConnectionPanel(panel.Panel, threading.Thread):
# rate limits appResolver queries to once per update
self.appResolveSinceUpdate = False
- self._update() # populates initial entries
- self._resolveApps(False) # resolves initial applications
-
# mark the initially exitsing connection uptimes as being estimates
for entry in self._entries:
if isinstance(entry, connEntry.ConnectionEntry):
@@ -170,6 +167,16 @@ class ConnectionPanel(panel.Panel, threading.Thread):
"""
lastDraw = time.time() - 1
+
+ # Fetches out initial connection results. The wait is so this doesn't
+ # run during arm's interface initialization (otherwise there's a
+ # noticeable pause before the first redraw).
+ self._cond.acquire()
+ self._cond.wait(0.2)
+ self._cond.release()
+ self._update() # populates initial entries
+ self._resolveApps(False) # resolves initial applications
+
while not self._halt:
currentTime = time.time()
1
0

17 Jul '11
commit 4ca251a6b4be812d8fdc0468a0ae43bc5595cf27
Author: Damian Johnson <atagar(a)torproject.org>
Date: Wed Apr 27 19:48:32 2011 -0700
fix: ns/desc lookups could crash descriptor popup
The exception handling for the consensus and descriptor lookups in
descriptorPopup.py were expecting TorCtl exceptions rather than a None result.
Caught by asn.
---
src/cli/descriptorPopup.py | 25 +++++++++++--------------
1 files changed, 11 insertions(+), 14 deletions(-)
diff --git a/src/cli/descriptorPopup.py b/src/cli/descriptorPopup.py
index cdc959d..d57546c 100644
--- a/src/cli/descriptorPopup.py
+++ b/src/cli/descriptorPopup.py
@@ -3,9 +3,7 @@
# Released under the GPL v3 (http://www.gnu.org/licenses/gpl.html)
import math
-import socket
import curses
-from TorCtl import TorCtl
import controller
import connections.connEntry
@@ -47,20 +45,19 @@ class PopupProperties:
self.text.append(UNRESOLVED_MSG)
else:
conn = torTools.getConn()
+ self.showLineNum = True
- try:
- self.showLineNum = True
- self.text.append("ns/id/%s" % fingerprint)
- self.text += conn.getConsensusEntry(fingerprint).split("\n")
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed):
- self.text = self.text + [ERROR_MSG, ""]
+ self.text.append("ns/id/%s" % fingerprint)
+ consensusEntry = conn.getConsensusEntry(fingerprint)
- try:
- descCommand = "desc/id/%s" % fingerprint
- self.text.append("desc/id/%s" % fingerprint)
- self.text += conn.getDescriptorEntry(fingerprint).split("\n")
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed):
- self.text = self.text + [ERROR_MSG]
+ if consensusEntry: self.text += consensusEntry.split("\n")
+ else: self.text = self.text + [ERROR_MSG, ""]
+
+ self.text.append("desc/id/%s" % fingerprint)
+ descriptorEntry = conn.getDescriptorEntry(fingerprint)
+
+ if descriptorEntry: self.text += descriptorEntry.split("\n")
+ else: self.text = self.text + [ERROR_MSG]
def handleKey(self, key, height):
if key == curses.KEY_UP: self.scroll = max(self.scroll - 1, 0)
1
0

[arm/release] fix: Minor refactoring for moved/removed resources
by atagar@torproject.org 17 Jul '11
by atagar@torproject.org 17 Jul '11
17 Jul '11
commit 30f9ad4e138848cc012f45842a972d2065612eb7
Author: Damian Johnson <atagar(a)torproject.org>
Date: Wed Apr 27 08:32:40 2011 -0700
fix: Minor refactoring for moved/removed resources
Missed a couple spots where the connPanel and /src/interface were referenced.
---
README | 17 ++++++++---------
src/cli/__init__.py | 2 +-
2 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/README b/README
index 7beb7e3..50657a1 100644
--- a/README
+++ b/README
@@ -139,14 +139,7 @@ Layout:
torConfigDesc.txt - fallback descriptions of Tor's configuration options
uninstall - removal script
- interface/
- connections/
- __init__.py
- connPanel.py - (page 2) lists the active tor connections
- circEntry.py - circuit entries in the connection panel
- connEntry.py - individual connections to or from the system
- entries.py - common parent for connPanel display entries
-
+ cli/
graphing/
__init__.py
graphPanel.py - (page 1) presents graphs for data instances
@@ -154,13 +147,19 @@ Layout:
psStats.py - tracks system information (such as cpu/memory usage)
connStats.py - tracks number of tor connections
+ connections/
+ __init__.py
+ connPanel.py - (page 2) lists the active tor connections
+ circEntry.py - circuit entries in the connection panel
+ connEntry.py - individual connections to or from the system
+ entries.py - common parent for connPanel display entries
+
__init__.py
controller.py - main display loop, handling input and layout
headerPanel.py - top of all pages, providing general information
descriptorPopup.py - (popup) displays connection descriptor data
logPanel.py - (page 1) displays tor, arm, and torctl events
- connPanel.py - (page 2) deprecated counterpart for connections/*
configPanel.py - (page 3) editor panel for the tor configuration
torrcPanel.py - (page 4) displays torrc and validation
diff --git a/src/cli/__init__.py b/src/cli/__init__.py
index 0f11fc1..171af09 100644
--- a/src/cli/__init__.py
+++ b/src/cli/__init__.py
@@ -2,5 +2,5 @@
Panels, popups, and handlers comprising the arm user interface.
"""
-__all__ = ["configPanel", "connPanel", "controller", "descriptorPopup", "headerPanel", "logPanel", "torrcPanel"]
+__all__ = ["configPanel", "controller", "descriptorPopup", "headerPanel", "logPanel", "torrcPanel"]
1
0

17 Jul '11
commit d47b394d5ddab73d0bc0551daecae4c054d6a9b9
Author: Damian Johnson <atagar(a)torproject.org>
Date: Wed Apr 27 09:21:05 2011 -0700
fix: Blind mode was querying connections
Initializing the connection panel was inadvertently spawning connection
resolution, despite not being visible. Making the panel's update a no-op when
there's no pre-instantiated resolver.
---
src/cli/connections/connPanel.py | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/src/cli/connections/connPanel.py b/src/cli/connections/connPanel.py
index 569f57c..8339f88 100644
--- a/src/cli/connections/connPanel.py
+++ b/src/cli/connections/connPanel.py
@@ -251,9 +251,13 @@ class ConnectionPanel(panel.Panel, threading.Thread):
Fetches the newest resolved connections.
"""
+ self.appResolveSinceUpdate = False
+
+ # if we don't have an initialized resolver then this is a no-op
+ if not connections.isResolverAlive("tor"): return
+
connResolver = connections.getResolver("tor")
currentResolutionCount = connResolver.getResolutionCount()
- self.appResolveSinceUpdate = False
if self._lastResourceFetch != currentResolutionCount:
self.valsLock.acquire()
1
0

[arm/release] fix: Proc resolution without a pid gave stacktrace
by atagar@torproject.org 17 Jul '11
by atagar@torproject.org 17 Jul '11
17 Jul '11
commit f72aa4616ea605ac22fe012513d856aaeedb819e
Author: Damian Johnson <atagar(a)torproject.org>
Date: Wed Apr 27 08:52:54 2011 -0700
fix: Proc resolution without a pid gave stacktrace
When the connection resolver hadn't been configured with a pid and we queried
connections by proc resolution there was an uncaught exception. This didn't
crash arm - it just resulted in a stacktrace for a split second at startup.
This was most easily reproed by running arm on Linux in blind mode, with a
sleep command between panel creation and redrawing the screen to keep the stack
trace visible for a bit.
---
src/util/connections.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/util/connections.py b/src/util/connections.py
index f632306..c090893 100644
--- a/src/util/connections.py
+++ b/src/util/connections.py
@@ -486,7 +486,7 @@ class ConnectionResolver(threading.Thread):
else: self._rateThresholdBroken = 0
if isDefault: self._subsiquentFailures = 0
- except IOError, exc:
+ except (ValueError, IOError), exc:
# this logs in a couple of cases:
# - special failures noted by getConnections (most cases are already
# logged via sysTools)
1
0

17 Jul '11
commit a3c839db116c757fbad03c82b35688ca60f0be0f
Author: Damian Johnson <atagar(a)torproject.org>
Date: Wed Apr 27 08:24:03 2011 -0700
fix: Selecting 'auto' resolver gave stacktrace
A previous change caused a bug where, when selecting 'auto' from the resolver
dropdown menu, resolutions produced a stack trace due to a bad key. This
doesn't crash arm, but is clearly broken.
---
src/cli/controller.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/cli/controller.py b/src/cli/controller.py
index 2afbf6a..22d1772 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -1250,7 +1250,7 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
setPauseState(panels, isPaused, page, True)
selection = showMenu(stdscr, panels["popup"], "Resolver Util:", options, initialSelection)
- selectedOption = options[selection] if selection != "auto" else None
+ selectedOption = options[selection] if selection != 0 else None
# reverts changes made for popup
panels["conn"]._title = panelTitle
1
0

17 Jul '11
commit 8dc1fcaa32e2aef8dfd5b018709844b4c582f388
Author: Damian Johnson <atagar(a)torproject.org>
Date: Tue Apr 26 09:50:14 2011 -0700
Dropping deprecated conn panel from the codebase
Now that we have feature parity between the new and old connection panels we
can drop the old version. This change removes it, and all the confusing
controller hacks in place to allow us to have either or both panels be visible.
---
armrc.sample | 6 -
src/interface/connPanel.py | 997 --------------------------------------
src/interface/controller.py | 421 ++---------------
src/interface/descriptorPopup.py | 20 +-
4 files changed, 48 insertions(+), 1396 deletions(-)
diff --git a/armrc.sample b/armrc.sample
index 4cda197..58fe5c2 100644
--- a/armrc.sample
+++ b/armrc.sample
@@ -157,10 +157,6 @@ features.graph.bw.accounting.isTimeLong false
# Parameters for connection display
# ---------------------------------
-# oldPanel
-# includes the old connection panel in the interface
-# newPanel
-# includes the new connection panel in the interface
# listingType
# the primary category of information shown by default, options including:
# 0 -> IP Address / Port 1 -> Hostname
@@ -187,8 +183,6 @@ features.graph.bw.accounting.isTimeLong false
# showColumn.*
# toggles the visability of the connection table columns
-features.connection.oldPanel false
-features.connection.newPanel true
features.connection.listingType 0
features.connection.order 0, 2, 1
features.connection.refreshRate 5
diff --git a/src/interface/connPanel.py b/src/interface/connPanel.py
deleted file mode 100644
index e4d4be6..0000000
--- a/src/interface/connPanel.py
+++ /dev/null
@@ -1,997 +0,0 @@
-#!/usr/bin/env python
-# connPanel.py -- Lists network connections used by tor.
-# Released under the GPL v3 (http://www.gnu.org/licenses/gpl.html)
-
-import time
-import socket
-import curses
-from threading import RLock
-from TorCtl import TorCtl
-
-from util import log, connections, hostnames, panel, torTools, uiTools
-
-# Scrubs private data from any connection that might belong to client or exit
-# traffic. This is a little overly conservative, hiding anything that isn't
-# identified as a relay and meets the following criteria:
-# - Connection is inbound and relay's either a bridge (BridgeRelay is set) or
-# guard (making it a probable client connection)
-# - Outbound connection permitted by the exit policy (probable exit connection)
-#
-# Note that relay etiquette says these are bad things to look at (ie, DON'T
-# CHANGE THIS UNLESS YOU HAVE A DAMN GOOD REASON!)
-SCRUB_PRIVATE_DATA = True
-
-# 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.39", "9031"), # moria1
- ("216.224.124.114", "9030"), # ides
- ("80.190.246.100", "8180"), # gabelmoo
- ("194.109.206.212", "80"), # dizum
- ("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)
-LIST_LABEL = {LIST_IP: "IP Address", LIST_HOSTNAME: "Hostname", LIST_FINGERPRINT: "Fingerprint", LIST_NICKNAME: "Nickname"}
-
-# attributes for connection types
-TYPE_COLORS = {"inbound": "green", "outbound": "blue", "client": "cyan", "directory": "magenta", "control": "red", "family": "magenta", "localhost": "yellow"}
-TYPE_WEIGHTS = {"inbound": 0, "outbound": 1, "client": 2, "directory": 3, "control": 4, "family": 5, "localhost": 6} # defines ordering
-
-# enums for indexes of ConnPanel 'connections' fields
-CONN_TYPE, CONN_L_IP, CONN_L_PORT, CONN_F_IP, CONN_F_PORT, CONN_COUNTRY, CONN_TIME, CONN_PRIVATE = range(8)
-
-# labels associated to 'connectionCount'
-CONN_COUNT_LABELS = ["inbound", "outbound", "client", "directory", "control"]
-
-# enums for sorting types (note: ordering corresponds to SORT_TYPES for easy lookup)
-# TODO: add ORD_BANDWIDTH -> (ORD_BANDWIDTH, "Bandwidth", lambda x, y: ???)
-ORD_TYPE, ORD_FOREIGN_LISTING, ORD_SRC_LISTING, ORD_DST_LISTING, ORD_COUNTRY, ORD_FOREIGN_PORT, ORD_SRC_PORT, ORD_DST_PORT, ORD_TIME = range(9)
-SORT_TYPES = [(ORD_TYPE, "Connection Type",
- lambda x, y: TYPE_WEIGHTS[x[CONN_TYPE]] - TYPE_WEIGHTS[y[CONN_TYPE]]),
- (ORD_FOREIGN_LISTING, "Listing (Foreign)", None),
- (ORD_SRC_LISTING, "Listing (Source)", None),
- (ORD_DST_LISTING, "Listing (Dest.)", None),
- (ORD_COUNTRY, "Country Code",
- lambda x, y: cmp(x[CONN_COUNTRY], y[CONN_COUNTRY])),
- (ORD_FOREIGN_PORT, "Port (Foreign)",
- lambda x, y: int(x[CONN_F_PORT]) - int(y[CONN_F_PORT])),
- (ORD_SRC_PORT, "Port (Source)",
- lambda x, y: int(x[CONN_F_PORT] if x[CONN_TYPE] == "inbound" else x[CONN_L_PORT]) - int(y[CONN_F_PORT] if y[CONN_TYPE] == "inbound" else y[CONN_L_PORT])),
- (ORD_DST_PORT, "Port (Dest.)",
- lambda x, y: int(x[CONN_L_PORT] if x[CONN_TYPE] == "inbound" else x[CONN_F_PORT]) - int(y[CONN_L_PORT] if y[CONN_TYPE] == "inbound" else y[CONN_F_PORT])),
- (ORD_TIME, "Connection Time",
- lambda x, y: cmp(-x[CONN_TIME], -y[CONN_TIME]))]
-
-# provides bi-directional mapping of sorts with their associated labels
-def getSortLabel(sortType, withColor = False):
- """
- Provides label associated with a type of sorting. Throws ValueEror if no such
- sort exists. If adding color formatting this wraps with the following mappings:
- Connection Type red
- Listing * blue
- Port * green
- Bandwidth cyan
- Country Code yellow
- """
-
- for (type, label, func) in SORT_TYPES:
- if sortType == type:
- color = None
-
- if withColor:
- if label == "Connection Type": color = "red"
- elif label.startswith("Listing"): color = "blue"
- elif label.startswith("Port"): color = "green"
- elif label == "Bandwidth": color = "cyan"
- elif label == "Country Code": color = "yellow"
- elif label == "Connection Time": color = "magenta"
-
- #if color: return "<%s>%s</%s>" % (color, label, color)
- if color: return (label, color)
- else: return label
-
- raise ValueError(sortType)
-
-def getSortType(sortLabel):
- """
- Provides sort type associated with a given label. Throws ValueEror if label
- isn't recognized.
- """
-
- for (type, label, func) in SORT_TYPES:
- if sortLabel == label: return type
- raise ValueError(sortLabel)
-
-def ipAddressIsPrivate(ipAddr):
- """
- Provides true if the IP address belongs on the local network or belongs to
- loopback, false otherwise. These include:
- Private ranges: 10.*, 172.16.* - 172.31.*, 192.168.*
- Loopback: 127.*
-
- Arguments:
- ipAddr - IP address to be checked
- """
-
- # TODO: being lazy right now and just declaring all of 172.* as private
- return ipAddr.startswith("10.") or ipAddr.startswith("192.168.") or ipAddr.startswith("172.") or ipAddr.startswith("127.")
-
-class ConnPanel(TorCtl.PostEventListener, panel.Panel):
- """
- Lists tor related connection data.
- """
-
- def __init__(self, stdscr, conn, isDisabled):
- TorCtl.PostEventListener.__init__(self)
- panel.Panel.__init__(self, stdscr, "conn", 0)
- self.scroll = 0
- self.conn = conn # tor connection for querrying country codes
- self.listingType = LIST_IP # information used in listing entries
- self.allowDNS = False # permits hostname resolutions if true
- self.showLabel = True # shows top label if true, hides otherwise
- self.showingDetails = False # augments display to accomidate details window if true
- self.lastUpdate = -1 # time last stats was retrived
- self.localhostEntry = None # special connection - tuple with (entry for this node, fingerprint)
- self.sortOrdering = [ORD_TYPE, ORD_FOREIGN_LISTING, ORD_FOREIGN_PORT]
- self.fingerprintLookupCache = {} # cache of (ip, port) -> fingerprint
- self.nicknameLookupCache = {} # cache of (ip, port) -> nickname
- self.fingerprintMappings = _getFingerprintMappings(self.conn) # mappings of ip -> [(port, fingerprint, nickname), ...]
- self.providedGeoipWarning = False
- self.orconnStatusCache = [] # cache for 'orconn-status' calls
- self.orconnStatusCacheValid = False # indicates if cache has been invalidated
- self.clientConnectionCache = None # listing of nicknames for our client connections
- self.clientConnectionLock = RLock() # lock for clientConnectionCache
- self.isDisabled = isDisabled # prevent panel from updating entirely
- self.lastConnResults = None # used to check if connection results have changed
-
- self.isCursorEnabled = True
- self.cursorSelection = None
- self.cursorLoc = 0 # fallback cursor location if selection disappears
-
- # parameters used for pausing
- self.isPaused = False
- self.pauseTime = 0 # time when paused
- self.connectionsBuffer = [] # location where connections are stored while paused
- self.connectionCountBuffer = []
- self.familyResolutionsBuffer = {}
-
- # mapping of ip/port to fingerprint of family entries, used in hack to short circuit (ip / port) -> fingerprint lookups
- self.familyResolutions = {}
-
- # mapping of family entries to fingerprints
- self.familyFingerprints = {}
-
- self.address = ""
- self.nickname = ""
- self.listenPort = "0" # port used to identify inbound/outbound connections (from ORListenAddress if defined, otherwise ORPort)
- self.orPort = "0"
- self.dirPort = "0"
- self.controlPort = "0"
- self.socksPort = "0"
- self.family = [] # fingerpints of family entries
- self.isBridge = False # true if BridgeRelay is set
- self.exitPolicy = ""
- self.exitRejectPrivate = True # true if ExitPolicyRejectPrivate is 0
-
- self.resetOptions()
-
- # connection results are tuples of the form:
- # (type, local IP, local port, foreign IP, foreign port, country code)
- self.connections = []
- self.connectionsLock = RLock() # limits modifications of connections
-
- # count of total inbound, outbound, client, directory, and control connections
- self.connectionCount = [0] * 5
-
- self.reset()
-
- def resetOptions(self):
- self.familyResolutions = {}
- self.familyFingerprints = {}
-
- try:
- self.address = "" # fetched when needed if unset
- self.nickname = self.conn.get_option("Nickname")[0][1]
- if self.nickname == None: self.nickname = "Unnamed"
-
- self.orPort = self.conn.get_option("ORPort")[0][1]
- self.dirPort = self.conn.get_option("DirPort")[0][1]
- self.controlPort = self.conn.get_option("ControlPort")[0][1]
-
- # uses ports to identify type of connections (ORListenAddress port overwrites ORPort if set)
- listenAddr = self.conn.get_option("ORListenAddress")[0][1]
- if listenAddr and ":" in listenAddr:
- self.listenPort = listenAddr[listenAddr.find(":") + 1:]
- else: self.listenPort = self.orPort
-
- self.socksPort = torTools.getConn().getOption("SocksPort", "0")
-
- # entry is None if not set, otherwise of the format "$<fingerprint>,$<fingerprint>"
- familyEntry = self.conn.get_option("MyFamily")[0][1]
- if familyEntry: self.family = familyEntry.split(",")
- else: self.family = []
-
- self.isBridge = self.conn.get_option("BridgeRelay")[0][1] == "1"
-
- policyEntries = torTools.getConn().getOption("ExitPolicy", multiple=True)
- if not policyEntries: policyEntries = [] # if ExitPolicy is undefined, policyEntries is None
- self.exitPolicy = ",".join(policyEntries)
- self.exitPolicy = self.exitPolicy.replace("\\t", " ").replace("\"", "")
-
- if self.exitPolicy: self.exitPolicy += "," + self.conn.get_info("exit-policy/default")["exit-policy/default"]
- else: self.exitPolicy = self.conn.get_info("exit-policy/default")["exit-policy/default"]
-
- self.exitRejectPrivate = self.conn.get_option("ExitPolicyRejectPrivate")[0][1] == "1"
-
- self._resolveFamilyEntries()
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed):
- self.nickname = ""
- self.listenPort = None
- self.orPort = "0"
- self.dirPort = "0"
- self.controlPort = "0"
- self.socksPort = "0"
- self.family = []
- self.isBridge = False
- self.exitPolicy = ""
- self.exitRejectPrivate = True
-
- # change in client circuits
- def circ_status_event(self, event):
- self.clientConnectionLock.acquire()
- self.clientConnectionCache = None
- self.clientConnectionLock.release()
-
- # when consensus changes update fingerprint mappings
- # TODO: should also be taking NS events into account
- def new_consensus_event(self, event):
- self.orconnStatusCacheValid = False
- self.fingerprintLookupCache.clear()
- self.nicknameLookupCache.clear()
- self.fingerprintMappings = _getFingerprintMappings(self.conn, event.nslist)
- if self.listingType != LIST_HOSTNAME: self.sortConnections()
-
- def new_desc_event(self, event):
- self.orconnStatusCacheValid = False
- self._resolveFamilyEntries()
-
- for fingerprint in event.idlist:
- # clears entries with this fingerprint from the cache
- if fingerprint in self.fingerprintLookupCache.values():
- invalidEntries = set(k for k, v in self.fingerprintLookupCache.iteritems() if v == fingerprint)
- for k in invalidEntries:
- # nicknameLookupCache keys are a subset of fingerprintLookupCache
- del self.fingerprintLookupCache[k]
- if k in self.nicknameLookupCache.keys(): del self.nicknameLookupCache[k]
-
- # gets consensus data for the new description
- try: nsData = self.conn.get_network_status("id/%s" % fingerprint)
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): return
-
- if len(nsData) > 1:
- # multiple records for fingerprint (shouldn't happen)
- log.log(log.WARN, "Multiple consensus entries for fingerprint: %s" % fingerprint)
- return
- nsEntry = nsData[0]
-
- # updates fingerprintMappings with new data
- if nsEntry.ip in self.fingerprintMappings.keys():
- # if entry already exists with the same orport, remove it
- orportMatch = None
- for entryPort, entryFingerprint, entryNickname in self.fingerprintMappings[nsEntry.ip]:
- if entryPort == nsEntry.orport:
- orportMatch = (entryPort, entryFingerprint, entryNickname)
- break
-
- if orportMatch: self.fingerprintMappings[nsEntry.ip].remove(orportMatch)
-
- # add new entry
- self.fingerprintMappings[nsEntry.ip].append((nsEntry.orport, nsEntry.idhex, nsEntry.nickname))
- else:
- self.fingerprintMappings[nsEntry.ip] = [(nsEntry.orport, nsEntry.idhex, nsEntry.nickname)]
- if self.listingType != LIST_HOSTNAME: self.sortConnections()
-
- def reset(self):
- """
- Reloads connection results.
- """
-
- if self.isDisabled: return
-
- # inaccessable during startup so might need to be refetched
- try:
- if not self.address: self.address = self.conn.get_info("address")["address"]
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): pass
-
- self.connectionsLock.acquire()
- self.clientConnectionLock.acquire()
-
- # temporary variables for connections and count
- connectionsTmp = []
- connectionCountTmp = [0] * 5
- familyResolutionsTmp = {}
-
- # used (with isBridge) to determine if inbound connections should be scrubbed
- isGuard = False
- try:
- myFingerprint = self.conn.get_info("fingerprint")
- 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:
- if self.clientConnectionCache == None:
- # client connection cache was invalidated
- self.clientConnectionCache = _getClientConnections(self.conn)
-
- connTimes = {} # mapping of ip/port to connection time
- for entry in (self.connections if not self.isPaused else self.connectionsBuffer):
- connTimes[(entry[CONN_F_IP], entry[CONN_F_PORT])] = entry[CONN_TIME]
-
- results = connections.getResolver("tor").getConnections()
- if results == self.lastConnResults: return # contents haven't changed
-
- for lIp, lPort, fIp, fPort in results:
- fingerprint = self.getFingerprint(fIp, fPort)
-
- isPrivate = False
- if lPort in (self.listenPort, self.dirPort):
- type = "inbound"
- connectionCountTmp[0] += 1
- if SCRUB_PRIVATE_DATA and fIp not in self.fingerprintMappings.keys(): isPrivate = isGuard or self.isBridge
- elif lPort == self.socksPort:
- type = "client"
- connectionCountTmp[2] += 1
- elif lPort == self.controlPort:
- type = "control"
- connectionCountTmp[4] += 1
- else:
- nickname = self.getNickname(fIp, fPort)
-
- isClient = False
- for clientName in self.clientConnectionCache:
- if nickname == clientName or (len(clientName) > 1 and clientName[0] == "$" and fingerprint == clientName[1:]):
- isClient = True
- break
-
- if isClient:
- type = "client"
- connectionCountTmp[2] += 1
- elif (fIp, fPort) in DIR_SERVERS:
- type = "directory"
- connectionCountTmp[3] += 1
- else:
- type = "outbound"
- connectionCountTmp[1] += 1
- if SCRUB_PRIVATE_DATA and fIp not in self.fingerprintMappings.keys(): isPrivate = isExitAllowed(fIp, fPort, self.exitPolicy, self.exitRejectPrivate)
-
- # replace nat address with external version if available and the
- # external address isn't a private IP
- isPrivateIp = ipAddressIsPrivate(fIp)
- if self.address and type != "control" and not isPrivateIp: lIp = self.address
-
- if isPrivateIp:
- countryCode = "??"
- else:
- try:
- countryCodeQuery = "ip-to-country/%s" % fIp
- countryCode = self.conn.get_info(countryCodeQuery)[countryCodeQuery]
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed):
- countryCode = "??"
- if not self.providedGeoipWarning:
- log.log(log.WARN, "Tor geoip database is unavailable.")
- self.providedGeoipWarning = True
-
- if (fIp, fPort) in connTimes: connTime = connTimes[(fIp, fPort)]
- else: connTime = time.time()
-
- connectionsTmp.append((type, lIp, lPort, fIp, fPort, countryCode, connTime, isPrivate))
-
- # appends localhost connection to allow user to look up their own consensus entry
- selfFingerprint = None
- try:
- selfFingerprint = self.conn.get_info("fingerprint")["fingerprint"]
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): pass
-
- if self.address and selfFingerprint:
- try:
- countryCodeQuery = "ip-to-country/%s" % self.address
- selfCountryCode = self.conn.get_info(countryCodeQuery)[countryCodeQuery]
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed):
- selfCountryCode = "??"
-
- if (self.address, self.orPort) in connTimes: connTime = connTimes[(self.address, self.orPort)]
- else: connTime = time.time()
-
- self.localhostEntry = (("localhost", self.address, self.orPort, self.address, self.orPort, selfCountryCode, connTime, False), selfFingerprint)
- connectionsTmp.append(self.localhostEntry[0])
- else:
- self.localhostEntry = None
-
- # appends family connections
- tmpCounter = 0 # used for unique port of unresolved family entries (funky hack)
- for familyEntry in self.family:
- # TODO: turns out that "ns/name/<OR nickname>" accpets fingerprint
- # identifiers, so all this nickname -> fingerprint work is unnecessary,
- # but used for fingerprint lookup performance in draw... this could be
- # improved (might be completely unnecessary due to the fingerprint
- # lookup cache)
- fingerprint = None
- if familyEntry in self.familyFingerprints:
- fingerprint = self.familyFingerprints[familyEntry]
-
- try:
- if fingerprint: nsCall = self.conn.get_network_status("id/%s" % fingerprint)
- else: nsCall = self.conn.get_network_status("name/%s" % familyEntry)
- if nsCall: familyAddress, familyPort = nsCall[0].ip, nsCall[0].orport
- else: raise TorCtl.ErrorReply # network consensus couldn't be fetched
-
- countryCodeQuery = "ip-to-country/%s" % familyAddress
- familyCountryCode = self.conn.get_info(countryCodeQuery)[countryCodeQuery]
-
- if (familyAddress, familyPort) in connTimes: connTime = connTimes[(familyAddress, familyPort)]
- else: connTime = time.time()
-
- if fingerprint: familyResolutionsTmp[(familyAddress, familyPort)] = fingerprint
- connectionsTmp.append(("family", familyAddress, familyPort, familyAddress, familyPort, familyCountryCode, connTime, False))
- except (socket.error, TorCtl.ErrorReply):
- # use dummy entry for sorting - the draw function notes that entries are unknown
- portIdentifier = str(65536 + tmpCounter)
- if fingerprint: familyResolutionsTmp[("256.255.255.255", portIdentifier)] = fingerprint
- connectionsTmp.append(("family", "256.255.255.255", portIdentifier, "256.255.255.255", portIdentifier, "??", time.time(), False))
- tmpCounter += 1
- except TorCtl.TorCtlClosed:
- pass # connections aren't shown when control port is unavailable
-
- self.lastUpdate = time.time()
-
- # assigns results
- if self.isPaused:
- self.connectionsBuffer = connectionsTmp
- self.connectionCountBuffer = connectionCountTmp
- self.familyResolutionsBuffer = familyResolutionsTmp
- else:
- self.connections = connectionsTmp
- self.connectionCount = connectionCountTmp
- self.familyResolutions = familyResolutionsTmp
-
- # hostnames are sorted at draw - otherwise now's a good time
- if self.listingType != LIST_HOSTNAME: self.sortConnections()
- self.lastConnResults = results
- finally:
- self.connectionsLock.release()
- self.clientConnectionLock.release()
-
- def handleKey(self, key):
- # cursor or scroll movement
-
- #if key in (curses.KEY_UP, curses.KEY_DOWN, curses.KEY_PPAGE, curses.KEY_NPAGE):
- if uiTools.isScrollKey(key):
- pageHeight = self.getPreferredSize()[0] - 1
- if self.showingDetails: pageHeight -= 8
-
- self.connectionsLock.acquire()
- try:
- # determines location parameter to use
- if self.isCursorEnabled:
- try: currentLoc = self.connections.index(self.cursorSelection)
- except ValueError: currentLoc = self.cursorLoc # fall back to nearby entry
- else: currentLoc = self.scroll
-
- # location offset
- if key == curses.KEY_UP: shift = -1
- elif key == curses.KEY_DOWN: shift = 1
- elif key == curses.KEY_PPAGE: shift = -pageHeight + 1 if self.isCursorEnabled else -pageHeight
- elif key == curses.KEY_NPAGE: shift = pageHeight - 1 if self.isCursorEnabled else pageHeight
- elif key == curses.KEY_HOME: shift = -currentLoc
- elif key == curses.KEY_END: shift = len(self.connections) # always below the lower bound
- newLoc = currentLoc + shift
-
- # restricts to valid bounds
- maxLoc = len(self.connections) - 1 if self.isCursorEnabled else len(self.connections) - pageHeight
- newLoc = max(0, min(newLoc, maxLoc))
-
- # applies to proper parameter
- if self.isCursorEnabled and self.connections:
- self.cursorSelection, self.cursorLoc = self.connections[newLoc], newLoc
- else: self.scroll = newLoc
- finally:
- self.connectionsLock.release()
- elif key == ord('r') or key == ord('R'):
- self.allowDNS = not self.allowDNS
- if not self.allowDNS: hostnames.setPaused(True)
- elif self.listingType == LIST_HOSTNAME: hostnames.setPaused(False)
- else: return # skip following redraw
- self.redraw(True)
-
- def draw(self, width, height):
- self.connectionsLock.acquire()
- try:
- # hostnames frequently get updated so frequent sorting needed
- if self.listingType == LIST_HOSTNAME: self.sortConnections()
-
- if self.showLabel:
- # notes the number of connections for each type if above zero
- countLabel = ""
- for i in range(len(self.connectionCount)):
- if self.connectionCount[i] > 0: countLabel += "%i %s, " % (self.connectionCount[i], CONN_COUNT_LABELS[i])
- if countLabel: countLabel = " (%s)" % countLabel[:-2] # strips ending ", " and encases in parentheses
- self.addstr(0, 0, "Connections%s:" % countLabel, curses.A_STANDOUT)
-
- if self.connections:
- listingHeight = height - 1
- currentTime = time.time() if not self.isPaused else self.pauseTime
-
- if self.showingDetails:
- listingHeight -= 8
- isScrollBarVisible = len(self.connections) > height - 9
- if width > 80: self.win.hline(8, 80, curses.ACS_HLINE, width - 81)
- else:
- isScrollBarVisible = len(self.connections) > height - 1
- xOffset = 3 if isScrollBarVisible else 0 # content offset for scroll bar
-
- # ensure cursor location and scroll top are within bounds
- self.cursorLoc = max(min(self.cursorLoc, len(self.connections) - 1), 0)
- self.scroll = max(min(self.scroll, len(self.connections) - listingHeight), 0)
-
- if self.isCursorEnabled:
- # update cursorLoc with selection (or vice versa if selection not found)
- if self.cursorSelection not in self.connections:
- self.cursorSelection = self.connections[self.cursorLoc]
- else: self.cursorLoc = self.connections.index(self.cursorSelection)
-
- # shift scroll if necessary for cursor to be visible
- if self.cursorLoc < self.scroll: self.scroll = self.cursorLoc
- elif self.cursorLoc - listingHeight + 1 > self.scroll: self.scroll = self.cursorLoc - listingHeight + 1
-
- lineNum = (-1 * self.scroll) + 1
- for entry in self.connections:
- if lineNum >= 1:
- type = entry[CONN_TYPE]
- isPrivate = entry[CONN_PRIVATE]
- color = TYPE_COLORS[type]
-
- # adjustments to measurements for 'xOffset' are to account for scroll bar
- if self.listingType == LIST_IP:
- # base data requires 73 characters
- src = "%s:%s" % (entry[CONN_L_IP], entry[CONN_L_PORT])
- dst = "%s:%s" % (entry[CONN_F_IP], entry[CONN_F_PORT])
- if not ipAddressIsPrivate(entry[CONN_F_IP]):
- dst += " (%s)" % entry[CONN_COUNTRY]
-
- if isPrivate: dst = "<scrubbed>"
-
- src, dst = "%-21s" % src, "%-26s" % dst
-
- etc = ""
- if width > 115 + xOffset:
- # show fingerprint (column width: 42 characters)
- etc += "%-40s " % self.getFingerprint(entry[CONN_F_IP], entry[CONN_F_PORT])
-
- if width > 127 + xOffset:
- # show nickname (column width: remainder)
- nickname = self.getNickname(entry[CONN_F_IP], entry[CONN_F_PORT])
- nicknameSpace = width - 118 - xOffset
-
- # truncates if too long
- if len(nickname) > nicknameSpace: nickname = "%s..." % nickname[:nicknameSpace - 3]
-
- etc += ("%%-%is " % nicknameSpace) % nickname
- elif self.listingType == LIST_HOSTNAME:
- # base data requires 80 characters
- src = "localhost:%-5s" % entry[CONN_L_PORT]
-
- # space available for foreign hostname (stretched to claim any free space)
- foreignHostnameSpace = width - 42 - xOffset
-
- etc = ""
- if width > 102 + xOffset:
- # shows ip/locale (column width: 22 characters)
- foreignHostnameSpace -= 22
-
- if isPrivate: ipEntry = "<scrubbed>"
- else:
- ipEntry = "%s:%s" % (entry[CONN_F_IP], entry[CONN_F_PORT])
- if ipAddressIsPrivate(entry[CONN_F_IP]):
- ipEntry += " (%s)" % entry[CONN_COUNTRY]
-
- etc += "%-20s " % ipEntry
- if width > 134 + xOffset:
- # show fingerprint (column width: 42 characters)
- foreignHostnameSpace -= 42
- etc += "%-40s " % self.getFingerprint(entry[CONN_F_IP], entry[CONN_F_PORT])
-
- if width > 151 + xOffset:
- # show nickname (column width: min 17 characters, uses half of the remainder)
- nickname = self.getNickname(entry[CONN_F_IP], entry[CONN_F_PORT])
- nicknameSpace = 15 + (width - xOffset - 151) / 2
- foreignHostnameSpace -= (nicknameSpace + 2)
-
- if len(nickname) > nicknameSpace: nickname = "%s..." % nickname[:nicknameSpace - 3]
- etc += ("%%-%is " % nicknameSpace) % nickname
-
- if isPrivate: dst = "<scrubbed>"
- else:
- try: hostname = hostnames.resolve(entry[CONN_F_IP])
- except ValueError: hostname = None
-
- # truncates long hostnames
- portDigits = len(str(entry[CONN_F_PORT]))
- if hostname and (len(hostname) + portDigits) > foreignHostnameSpace - 1:
- hostname = hostname[:(foreignHostnameSpace - portDigits - 4)] + "..."
-
- dst = "%s:%s" % (hostname if hostname else entry[CONN_F_IP], entry[CONN_F_PORT])
-
- dst = ("%%-%is" % foreignHostnameSpace) % dst
- elif self.listingType == LIST_FINGERPRINT:
- # base data requires 75 characters
- src = "localhost"
- if entry[CONN_TYPE] == "control": dst = "localhost"
- else: dst = self.getFingerprint(entry[CONN_F_IP], entry[CONN_F_PORT])
- dst = "%-40s" % dst
-
- etc = ""
- if width > 92 + xOffset:
- # show nickname (column width: min 17 characters, uses remainder if extra room's available)
- nickname = self.getNickname(entry[CONN_F_IP], entry[CONN_F_PORT])
- nicknameSpace = width - 78 - xOffset if width < 126 else width - 106 - xOffset
- if len(nickname) > nicknameSpace: nickname = "%s..." % nickname[:nicknameSpace - 3]
- etc += ("%%-%is " % nicknameSpace) % nickname
-
- if width > 125 + xOffset:
- # shows ip/port/locale (column width: 28 characters)
- if isPrivate: ipEntry = "<scrubbed>"
- else:
- ipEntry = "%s:%s" % (entry[CONN_F_IP], entry[CONN_F_PORT])
- if ipAddressIsPrivate(entry[CONN_F_IP]):
- ipEntry += " (%s)" % entry[CONN_COUNTRY]
-
- etc += "%-26s " % ipEntry
- else:
- # base data uses whatever extra room's available (using minimun of 50 characters)
- src = self.nickname
- if entry[CONN_TYPE] == "control": dst = self.nickname
- else: dst = self.getNickname(entry[CONN_F_IP], entry[CONN_F_PORT])
-
- # space available for foreign nickname
- foreignNicknameSpace = width - len(self.nickname) - 27 - xOffset
-
- etc = ""
- if width > 92 + xOffset:
- # show fingerprint (column width: 42 characters)
- foreignNicknameSpace -= 42
- etc += "%-40s " % self.getFingerprint(entry[CONN_F_IP], entry[CONN_F_PORT])
-
- if width > 120 + xOffset:
- # shows ip/port/locale (column width: 28 characters)
- foreignNicknameSpace -= 28
-
- if isPrivate: ipEntry = "<scrubbed>"
- else:
- ipEntry = "%s:%s" % (entry[CONN_F_IP], entry[CONN_F_PORT])
- if ipAddressIsPrivate(entry[CONN_F_IP]):
- ipEntry += " (%s)" % entry[CONN_COUNTRY]
-
- etc += "%-26s " % ipEntry
-
- dst = ("%%-%is" % foreignNicknameSpace) % dst
-
- timeLabel = uiTools.getTimeLabel(currentTime - entry[CONN_TIME], 1)
- if type == "inbound": src, dst = dst, src
- elif type == "family" and int(entry[CONN_L_PORT]) > 65535:
- # this belongs to an unresolved family entry - replaces invalid data with "UNKNOWN"
- timeLabel = "---"
-
- if self.listingType == LIST_IP:
- src = "%-21s" % "UNKNOWN"
- dst = "%-26s" % "UNKNOWN"
- elif self.listingType == LIST_HOSTNAME:
- src = "%-15s" % "UNKNOWN"
- dst = ("%%-%is" % len(dst)) % "UNKNOWN"
- if len(etc) > 0: etc = etc.replace("256.255.255.255 (??)", "UNKNOWN" + " " * 13)
- else:
- ipStart = etc.find("256")
- if ipStart > -1: etc = etc[:ipStart] + ("%%-%is" % len(etc[ipStart:])) % "UNKNOWN"
-
- padding = width - (len(src) + len(dst) + len(etc) + 27) - xOffset # padding needed to fill full line
- lineEntry = "<%s>%s --> %s %s%s%5s (<b>%s</b>)%s</%s>" % (color, src, dst, etc, " " * padding, timeLabel, type.upper(), " " * (9 - len(type)), color)
-
- if self.isCursorEnabled and entry == self.cursorSelection:
- lineEntry = "<h>%s</h>" % lineEntry
-
- yOffset = 0 if not self.showingDetails else 8
- self.addfstr(lineNum + yOffset, xOffset, lineEntry)
- lineNum += 1
-
- if isScrollBarVisible:
- topY = 9 if self.showingDetails else 1
- bottomEntry = self.scroll + height - 9 if self.showingDetails else self.scroll + height - 1
- self.addScrollBar(self.scroll, bottomEntry, len(self.connections), topY)
- finally:
- self.connectionsLock.release()
-
- def getFingerprint(self, ipAddr, port):
- """
- Makes an effort to match connection to fingerprint - if there's multiple
- potential matches or the IP address isn't found in the discriptor then
- returns "UNKNOWN".
- """
-
- # checks to see if this matches the localhost entry
- if self.localhostEntry and ipAddr == self.localhostEntry[0][CONN_L_IP] and port == self.localhostEntry[0][CONN_L_PORT]:
- return self.localhostEntry[1]
-
- # checks if this belongs to a family entry
- if (ipAddr, port) in self.familyResolutions.keys():
- return self.familyResolutions[(ipAddr, port)]
-
- port = int(port)
- if (ipAddr, port) in self.fingerprintLookupCache:
- return self.fingerprintLookupCache[(ipAddr, port)]
- else:
- match = None
-
- # orconn-status provides a listing of Tor's current connections - used to
- # eliminated ambiguity for outbound connections
- if not self.orconnStatusCacheValid:
- self.orconnStatusCache, isOdd = [], True
- self.orconnStatusCacheValid = True
- try:
- for entry in self.conn.get_info("orconn-status")["orconn-status"].split():
- if isOdd: self.orconnStatusCache.append(entry)
- isOdd = not isOdd
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): self.orconnStatusCache = None
-
- if ipAddr in self.fingerprintMappings.keys():
- potentialMatches = self.fingerprintMappings[ipAddr]
-
- if len(potentialMatches) == 1: match = potentialMatches[0][1]
- else:
- # multiple potential matches - look for exact match with port
- for (entryPort, entryFingerprint, entryNickname) in potentialMatches:
- if entryPort == port:
- match = entryFingerprint
- break
-
- if not match:
- # still haven't found it - use trick from Mike's ConsensusTracker,
- # excluding possiblities that have...
- # ... lost their Running flag
- # ... list a bandwidth of 0
- # ... have 'opt hibernating' set
- operativeMatches = list(potentialMatches)
- for entryPort, entryFingerprint, entryNickname in potentialMatches:
- # gets router description to see if 'down' is set
- toRemove = False
- try:
- 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)
- toRemove = descEntry.down
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): pass # ns or desc lookup fails... also weird
-
- # eliminates connections not reported by orconn-status -
- # this has *very* little impact since few ips have multiple relays
- if self.orconnStatusCache and not toRemove: toRemove = entryNickname not in self.orconnStatusCache
-
- if toRemove: operativeMatches.remove((entryPort, entryFingerprint, entryNickname))
-
- if len(operativeMatches) == 1: match = operativeMatches[0][1]
-
- if not match: match = "UNKNOWN"
-
- self.fingerprintLookupCache[(ipAddr, port)] = match
- return match
-
- def getNickname(self, ipAddr, port):
- """
- Attempts to provide the nickname for an ip/port combination, "UNKNOWN"
- if this can't be determined.
- """
-
- if (ipAddr, port) in self.nicknameLookupCache:
- return self.nicknameLookupCache[(ipAddr, port)]
- else:
- match = self.getFingerprint(ipAddr, port)
-
- try:
- 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
- return match
-
- def setPaused(self, isPause):
- """
- If true, prevents connection listing from being updated.
- """
-
- if isPause == self.isPaused: return
-
- self.isPaused = isPause
- if isPause:
- self.pauseTime = time.time()
- self.connectionsBuffer = list(self.connections)
- self.connectionCountBuffer = list(self.connectionCount)
- self.familyResolutionsBuffer = dict(self.familyResolutions)
- else:
- self.connections = list(self.connectionsBuffer)
- self.connectionCount = list(self.connectionCountBuffer)
- self.familyResolutions = dict(self.familyResolutionsBuffer)
-
- # pause buffer connections may be unsorted
- if self.listingType != LIST_HOSTNAME: self.sortConnections()
-
- def sortConnections(self):
- """
- Sorts connections according to currently set ordering. This takes into
- account secondary and tertiary sub-keys in case of ties.
- """
-
- # Current implementation is very inefficient, but since connection lists
- # are decently small (count get up to arounk 1k) this shouldn't be a big
- # whoop. Suggestions for improvements are welcome!
-
- sorts = []
-
- # wrapper function for using current listed data (for 'LISTING' sorts)
- if self.listingType == LIST_IP:
- listingWrapper = lambda ip, port: _ipToInt(ip)
- elif self.listingType == LIST_HOSTNAME:
- # alphanumeric hostnames followed by unresolved IP addresses
- listingWrapper = lambda ip, port: _getHostname(ip).upper() if _getHostname(ip) else "zzzzz%099i" % _ipToInt(ip)
- elif self.listingType == LIST_FINGERPRINT:
- # alphanumeric fingerprints followed by UNKNOWN entries
- listingWrapper = lambda ip, port: self.getFingerprint(ip, port) if self.getFingerprint(ip, port) != "UNKNOWN" else "zzzzz%099i" % _ipToInt(ip)
- elif self.listingType == LIST_NICKNAME:
- # alphanumeric nicknames followed by Unnamed then UNKNOWN entries
- listingWrapper = lambda ip, port: self.getNickname(ip, port) if self.getNickname(ip, port) not in ("UNKNOWN", "Unnamed") else "zzzzz%i%099i" % (0 if self.getNickname(ip, port) == "Unnamed" else 1, _ipToInt(ip))
-
- for entry in self.sortOrdering:
- if entry == ORD_FOREIGN_LISTING:
- sorts.append(lambda x, y: cmp(listingWrapper(x[CONN_F_IP], x[CONN_F_PORT]), listingWrapper(y[CONN_F_IP], y[CONN_F_PORT])))
- elif entry == ORD_SRC_LISTING:
- sorts.append(lambda x, y: cmp(listingWrapper(x[CONN_F_IP] if x[CONN_TYPE] == "inbound" else x[CONN_L_IP], x[CONN_F_PORT]), listingWrapper(y[CONN_F_IP] if y[CONN_TYPE] == "inbound" else y[CONN_L_IP], y[CONN_F_PORT])))
- elif entry == ORD_DST_LISTING:
- sorts.append(lambda x, y: cmp(listingWrapper(x[CONN_L_IP] if x[CONN_TYPE] == "inbound" else x[CONN_F_IP], x[CONN_F_PORT]), listingWrapper(y[CONN_L_IP] if y[CONN_TYPE] == "inbound" else y[CONN_F_IP], y[CONN_F_PORT])))
- else: sorts.append(SORT_TYPES[entry][2])
-
- self.connectionsLock.acquire()
- try: self.connections.sort(lambda x, y: _multisort(x, y, sorts))
- finally: self.connectionsLock.release()
-
- def _resolveFamilyEntries(self):
- """
- Populates mappings of the torrc family entries to their fingerprints.
- """
-
- self.familyFingerprints = {}
-
- for familyEntry in self.family:
- if not familyEntry: continue
-
- if familyEntry[0] == "$":
- # relay identified by fingerprint
- self.familyFingerprints[familyEntry] = familyEntry[1:]
- else:
- # relay identified by nickname
- descEntry = torTools.getConn().getInfo("desc/name/%s" % familyEntry)
-
- if descEntry:
- fingerprintStart = descEntry.find("opt fingerprint") + 16
- fingerprintEnd = descEntry.find("\n", fingerprintStart)
- fingerprint = descEntry[fingerprintStart:fingerprintEnd].replace(" ", "")
-
- self.familyFingerprints[familyEntry] = fingerprint
-
-# recursively checks primary, secondary, and tertiary sorting parameter in ties
-def _multisort(conn1, conn2, sorts):
- comp = sorts[0](conn1, conn2)
- if comp or len(sorts) == 1: return comp
- else: return _multisort(conn1, conn2, sorts[1:])
-
-def _getHostname(ipAddr):
- try: return hostnames.resolve(ipAddr)
- except ValueError: return None
-
-# provides comparison int for sorting IP addresses
-def _ipToInt(ipAddr):
- total = 0
- for comp in ipAddr.split("."):
- total *= 255
- total += int(comp)
- return total
-
-# uses consensus data to map IP addresses to port / fingerprint combinations
-def _getFingerprintMappings(conn, nsList = None):
- ipToFingerprint = {}
-
- if not nsList:
- try: nsList = conn.get_network_status()
- except (socket.error, TorCtl.TorCtlClosed, TorCtl.ErrorReply): nsList = []
- except TypeError: nsList = [] # TODO: temporary workaround for a TorCtl bug, remove when fixed
-
- for entry in nsList:
- if entry.ip in ipToFingerprint.keys(): ipToFingerprint[entry.ip].append((entry.orport, entry.idhex, entry.nickname))
- else: ipToFingerprint[entry.ip] = [(entry.orport, entry.idhex, entry.nickname)]
-
- return ipToFingerprint
-
-# provides client relays we're currently attached to (first hops in circuits)
-# this consists of the nicknames and ${fingerprint} if unnamed
-def _getClientConnections(conn):
- clients = []
-
- try:
- for line in conn.get_info("circuit-status")["circuit-status"].split("\n"):
- components = line.split()
- if len(components) > 3: clients += [components[2].split(",")[0]]
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): pass
-
- return clients
-
-def isExitAllowed(ip, port, exitPolicy, isPrivateRejected):
- """
- Determines if a given connection is a permissable exit with the given
- policy or not (True if it's allowed to be an exit connection, False
- otherwise).
-
- NOTE: this is a little tricky and liable to need some tweaks
- """
-
- # might not be set when first starting up
- if not exitPolicy: return True
-
- # TODO: move into a utility and craft some unit tests (this is very error
- # prone...)
-
- # TODO: currently doesn't consider ExitPolicyRejectPrivate (which prevents
- # connections to private networks and local ip)
- for entry in exitPolicy.split(","):
- entry = entry.strip()
-
- isAccept = entry.startswith("accept")
- entry = entry[7:] # strips off "accept " or "reject "
-
- # parses ip address (with mask if provided) and port
- if ":" in entry:
- entryIP = entry[:entry.find(":")]
- entryPort = entry[entry.find(":") + 1:]
- else:
- entryIP = entry
- entryPort = "*"
-
- #raise AssertionError(str(exitPolicy) + " - " + entryIP + ":" + entryPort)
- isIPMatch = entryIP == ip or entryIP[0] == "*"
-
- if not "-" in entryPort:
- # single port
- isPortMatch = entryPort == str(port) or entryPort[0] == "*"
- else:
- # port range
- minPort = int(entryPort[:entryPort.find("-")])
- maxPort = int(entryPort[entryPort.find("-") + 1:])
- isPortMatch = port >= minPort and port <= maxPort
-
- # TODO: Currently being lazy and considering subnet masks or 'private'
- # keyword to be equivilant to wildcard if it would reject, and none
- # if it would accept (ie, being conservative with acceptance). Would be
- # nice to fix at some point.
- if not isAccept: isIPMatch |= "/" in entryIP or entryIP == "private"
-
- if isIPMatch and isPortMatch: return isAccept
-
- # we shouldn't ever fall through due to default exit policy
- log.log(log.WARN, "Exit policy left connection uncategorized: %s:%i" % (ip, port))
- return False
-
diff --git a/src/interface/controller.py b/src/interface/controller.py
index b88152f..5060188 100644
--- a/src/interface/controller.py
+++ b/src/interface/controller.py
@@ -18,7 +18,6 @@ from TorCtl import TorCtl
import headerPanel
import graphing.graphPanel
import logPanel
-import connPanel
import configPanel
import torrcPanel
import descriptorPopup
@@ -43,17 +42,14 @@ PAGE_S = ["header", "control", "popup"] # sticky (ie, always available) page
PAGES = [
["graph", "log"],
["conn"],
- ["conn2"],
["config"],
["torrc"]]
-PAUSEABLE = ["header", "graph", "log", "conn", "conn2"]
+PAUSEABLE = ["header", "graph", "log", "conn"]
CONFIG = {"log.torrc.readFailed": log.WARN,
"features.graph.type": 1,
"features.config.prepopulateEditValues": True,
- "features.connection.oldPanel": False,
- "features.connection.newPanel": True,
"queries.refreshRate.rate": 5,
"log.torEventTypeUnrecognized": log.NOTICE,
"features.graph.bw.prepopulate": True,
@@ -119,11 +115,7 @@ class ControlPanel(panel.Panel):
currentPage = self.page
pageCount = len(PAGES)
- if not CONFIG["features.connection.newPanel"]:
- if currentPage >= 3: currentPage -= 1
- pageCount -= 1
-
- if self.isBlindMode or not CONFIG["features.connection.oldPanel"]:
+ if self.isBlindMode:
if currentPage >= 2: currentPage -= 1
pageCount -= 1
@@ -562,17 +554,7 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
# before being positioned - the following is a quick hack til rewritten
panels["log"].setPaused(True)
- if CONFIG["features.connection.oldPanel"]:
- panels["conn"] = connPanel.ConnPanel(stdscr, conn, isBlindMode)
- else:
- panels["conn"] = panel.Panel(stdscr, "blank", 0, 0, 0)
- PAUSEABLE.remove("conn")
-
- if CONFIG["features.connection.newPanel"]:
- panels["conn2"] = interface.connections.connPanel.ConnectionPanel(stdscr, config)
- else:
- panels["conn2"] = panel.Panel(stdscr, "blank", 0, 0, 0)
- PAUSEABLE.remove("conn2")
+ panels["conn"] = interface.connections.connPanel.ConnectionPanel(stdscr, config)
panels["control"] = ControlPanel(stdscr, isBlindMode)
panels["config"] = configPanel.ConfigPanel(stdscr, configPanel.State.TOR, config)
@@ -599,8 +581,6 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
conn.add_event_listener(panels["graph"].stats["bandwidth"])
conn.add_event_listener(panels["graph"].stats["system resources"])
if not isBlindMode: conn.add_event_listener(panels["graph"].stats["connections"])
- if CONFIG["features.connection.oldPanel"]:
- conn.add_event_listener(panels["conn"])
conn.add_event_listener(sighupTracker)
# prepopulates bandwidth values from state file
@@ -627,8 +607,7 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
# tells revised panels to run as daemons
panels["header"].start()
panels["log"].start()
- if CONFIG["features.connection.newPanel"]:
- panels["conn2"].start()
+ panels["conn"].start()
# warns if tor isn't updating descriptors
#try:
@@ -687,8 +666,6 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
#panels["header"]._updateParams(True)
# other panels that use torrc data
- if CONFIG["features.connection.oldPanel"]:
- panels["conn"].resetOptions()
#if not isBlindMode: panels["graph"].stats["connections"].resetOptions(conn)
#panels["graph"].stats["bandwidth"].resetOptions()
@@ -749,9 +726,6 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
isUnresponsive = False
log.log(log.NOTICE, "Relay resumed")
- if CONFIG["features.connection.oldPanel"]:
- panels["conn"].reset()
-
# TODO: part two of hack to prevent premature drawing by log panel
if page == 0 and not isPaused: panels["log"].setPaused(False)
@@ -844,11 +818,11 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
# stops panel daemons
panels["header"].stop()
- if CONFIG["features.connection.newPanel"]: panels["conn2"].stop()
+ panels["conn"].stop()
panels["log"].stop()
panels["header"].join()
- if CONFIG["features.connection.newPanel"]: panels["conn2"].join()
+ panels["conn"].join()
panels["log"].join()
# joins on utility daemon threads - this might take a moment since
@@ -871,15 +845,10 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
if key == curses.KEY_LEFT: page = (page - 1) % len(PAGES)
else: page = (page + 1) % len(PAGES)
- # skip connections listings if it's disabled
- while True:
- if page == 1 and (isBlindMode or not CONFIG["features.connection.oldPanel"]):
- if key == curses.KEY_LEFT: page = (page - 1) % len(PAGES)
- else: page = (page + 1) % len(PAGES)
- elif page == 2 and (isBlindMode or not CONFIG["features.connection.newPanel"]):
- if key == curses.KEY_LEFT: page = (page - 1) % len(PAGES)
- else: page = (page + 1) % len(PAGES)
- else: break
+ # skip connections listing if it's disabled
+ if page == 1 and isBlindMode:
+ if key == curses.KEY_LEFT: page = (page - 1) % len(PAGES)
+ else: page = (page + 1) % len(PAGES)
# pauses panels that aren't visible to prevent events from accumilating
# (otherwise they'll wait on the curses lock which might get demanding)
@@ -977,37 +946,11 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
popup.addfstr(1, 41, "<b>down arrow</b>: scroll down a line")
popup.addfstr(2, 2, "<b>page up</b>: scroll up a page")
popup.addfstr(2, 41, "<b>page down</b>: scroll down a page")
- popup.addfstr(3, 2, "<b>enter</b>: connection details")
- popup.addfstr(3, 41, "<b>d</b>: raw consensus descriptor")
-
- listingType = connPanel.LIST_LABEL[panels["conn"].listingType].lower()
- popup.addfstr(4, 2, "<b>l</b>: listed identity (<b>%s</b>)" % listingType)
-
- resolverUtil = connections.getResolver("tor").overwriteResolver
- if resolverUtil == None: resolverUtil = "auto"
- popup.addfstr(4, 41, "<b>u</b>: resolving utility (<b>%s</b>)" % resolverUtil)
-
- if CONFIG["features.connection.oldPanel"]:
- allowDnsLabel = "allow" if panels["conn"].allowDNS else "disallow"
- else: allowDnsLabel = "disallow"
- popup.addfstr(5, 2, "<b>r</b>: permit DNS resolution (<b>%s</b>)" % allowDnsLabel)
-
- popup.addfstr(5, 41, "<b>s</b>: sort ordering")
- popup.addfstr(6, 2, "<b>c</b>: client circuits")
-
- #popup.addfstr(5, 41, "c: toggle cursor (<b>%s</b>)" % ("on" if panels["conn"].isCursorEnabled else "off"))
-
- pageOverrideKeys = (ord('d'), ord('l'), ord('s'), ord('c'))
- elif page == 2:
- popup.addfstr(1, 2, "<b>up arrow</b>: scroll up a line")
- popup.addfstr(1, 41, "<b>down arrow</b>: scroll down a line")
- popup.addfstr(2, 2, "<b>page up</b>: scroll up a page")
- popup.addfstr(2, 41, "<b>page down</b>: scroll down a page")
popup.addfstr(3, 2, "<b>enter</b>: edit configuration option")
popup.addfstr(3, 41, "<b>d</b>: raw consensus descriptor")
- listingType = panels["conn2"]._listingType.lower()
+ listingType = panels["conn"]._listingType.lower()
popup.addfstr(4, 2, "<b>l</b>: listed identity (<b>%s</b>)" % listingType)
popup.addfstr(4, 41, "<b>s</b>: sort ordering")
@@ -1017,7 +960,7 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
popup.addfstr(5, 2, "<b>u</b>: resolving utility (<b>%s</b>)" % resolverUtil)
pageOverrideKeys = (ord('d'), ord('l'), ord('s'), ord('u'))
- elif page == 3:
+ elif page == 2:
popup.addfstr(1, 2, "<b>up arrow</b>: scroll up a line")
popup.addfstr(1, 41, "<b>down arrow</b>: scroll down a line")
popup.addfstr(2, 2, "<b>page up</b>: scroll up a page")
@@ -1031,7 +974,7 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
popup.addfstr(4, 2, "<b>r</b>: reload torrc")
popup.addfstr(4, 41, "<b>x</b>: reset tor (issue sighup)")
- elif page == 4:
+ elif page == 3:
popup.addfstr(1, 2, "<b>up arrow</b>: scroll up a line")
popup.addfstr(1, 41, "<b>down arrow</b>: scroll down a line")
popup.addfstr(2, 2, "<b>page up</b>: scroll up a page")
@@ -1292,220 +1235,7 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
setPauseState(panels, isPaused, page)
finally:
panel.CURSES_LOCK.release()
- elif CONFIG["features.connection.oldPanel"] and key == 27 and panels["conn"].listingType == connPanel.LIST_HOSTNAME and panels["control"].resolvingCounter != -1:
- # canceling hostname resolution (esc on any page)
- panels["conn"].listingType = connPanel.LIST_IP
- panels["control"].resolvingCounter = -1
- hostnames.setPaused(True)
- panels["conn"].sortConnections()
- elif page == 1 and panels["conn"].isCursorEnabled and uiTools.isSelectionKey(key):
- # TODO: deprecated when migrated to the new connection panel, thought as
- # well keep around until there's a counterpart for hostname fetching
-
- # provides details on selected connection
- panel.CURSES_LOCK.acquire()
- try:
- setPauseState(panels, isPaused, page, True)
- popup = panels["popup"]
-
- # reconfigures connection panel to accomidate details dialog
- panels["conn"].showLabel = False
- panels["conn"].showingDetails = True
- panels["conn"].redraw(True)
-
- hostnames.setPaused(not panels["conn"].allowDNS)
- relayLookupCache = {} # temporary cache of entry -> (ns data, desc data)
-
- curses.cbreak() # wait indefinitely for key presses (no timeout)
- key = 0
-
- while not uiTools.isSelectionKey(key):
- popup.clear()
- popup.win.box()
- popup.addstr(0, 0, "Connection Details:", curses.A_STANDOUT)
-
- selection = panels["conn"].cursorSelection
- if not selection or not panels["conn"].connections: break
- selectionColor = connPanel.TYPE_COLORS[selection[connPanel.CONN_TYPE]]
- format = uiTools.getColor(selectionColor) | curses.A_BOLD
-
- selectedIp = selection[connPanel.CONN_F_IP]
- selectedPort = selection[connPanel.CONN_F_PORT]
- selectedIsPrivate = selection[connPanel.CONN_PRIVATE]
-
- addrLabel = "address: %s:%s" % (selectedIp, selectedPort)
-
- if selection[connPanel.CONN_TYPE] == "family" and int(selection[connPanel.CONN_L_PORT]) > 65535:
- # unresolved family entry - unknown ip/port
- addrLabel = "address: unknown"
-
- if selectedIsPrivate: hostname = None
- else:
- try: hostname = hostnames.resolve(selectedIp)
- except ValueError: hostname = "unknown" # hostname couldn't be resolved
-
- if hostname == None:
- if hostnames.isPaused() or selectedIsPrivate: hostname = "DNS resolution disallowed"
- else:
- # if hostname is still being resolved refresh panel every half-second until it's completed
- curses.halfdelay(5)
- hostname = "resolving..."
- elif len(hostname) > 73 - len(addrLabel):
- # hostname too long - truncate
- hostname = "%s..." % hostname[:70 - len(addrLabel)]
-
- if selectedIsPrivate:
- popup.addstr(1, 2, "address: <scrubbed> (unknown)", format)
- popup.addstr(2, 2, "locale: ??", format)
- popup.addstr(3, 2, "No consensus data found", format)
- else:
- popup.addstr(1, 2, "%s (%s)" % (addrLabel, hostname), format)
-
- locale = selection[connPanel.CONN_COUNTRY]
- popup.addstr(2, 2, "locale: %s" % locale, format)
-
- # provides consensus data for selection (needs fingerprint to get anywhere...)
- fingerprint = panels["conn"].getFingerprint(selectedIp, selectedPort)
-
- if fingerprint == "UNKNOWN":
- if selectedIp not in panels["conn"].fingerprintMappings.keys():
- # no consensus entry for this ip address
- popup.addstr(3, 2, "No consensus data found", format)
- else:
- # couldn't resolve due to multiple matches - list them all
- popup.addstr(3, 2, "Muliple matches, possible fingerprints are:", format)
- matchings = panels["conn"].fingerprintMappings[selectedIp]
-
- line = 4
- for (matchPort, matchFingerprint, matchNickname) in matchings:
- popup.addstr(line, 2, "%i. or port: %-5s fingerprint: %s" % (line - 3, matchPort, matchFingerprint), format)
- line += 1
-
- if line == 7 and len(matchings) > 4:
- popup.addstr(8, 2, "... %i more" % len(matchings) - 3, format)
- break
- else:
- # fingerprint found - retrieve related data
- lookupErrored = False
- if selection in relayLookupCache.keys(): nsEntry, descEntry = relayLookupCache[selection]
- else:
- try:
- nsCall = conn.get_network_status("id/%s" % fingerprint)
- if len(nsCall) == 0: raise TorCtl.ErrorReply() # no results provided
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed):
- # ns lookup fails or provides empty results - can happen with
- # localhost lookups if relay's having problems (orport not
- # reachable) and this will be empty if network consensus
- # couldn't be fetched
- lookupErrored = True
-
- if not lookupErrored and nsCall:
- if len(nsCall) > 1:
- # multiple records for fingerprint (shouldn't happen)
- log.log(log.WARN, "Multiple consensus entries for fingerprint: %s" % fingerprint)
-
- nsEntry = nsCall[0]
-
- try:
- descLookupCmd = "desc/id/%s" % fingerprint
- descEntry = TorCtl.Router.build_from_desc(conn.get_info(descLookupCmd)[descLookupCmd].split("\n"), nsEntry)
- relayLookupCache[selection] = (nsEntry, descEntry)
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): lookupErrored = True # desc lookup failed
-
- if lookupErrored:
- popup.addstr(3, 2, "Unable to retrieve consensus data", format)
- else:
- popup.addstr(2, 15, "fingerprint: %s" % fingerprint, format)
-
- nickname = panels["conn"].getNickname(selectedIp, selectedPort)
- dirPortLabel = "dirport: %i" % nsEntry.dirport if nsEntry.dirport else ""
- popup.addstr(3, 2, "nickname: %-25s orport: %-10i %s" % (nickname, nsEntry.orport, dirPortLabel), format)
-
- popup.addstr(4, 2, "published: %-24s os: %-14s version: %s" % (descEntry.published, descEntry.os, descEntry.version), format)
- popup.addstr(5, 2, "flags: %s" % ", ".join(nsEntry.flags), format)
-
- exitLine = ", ".join([str(k) for k in descEntry.exitpolicy])
- if len(exitLine) > 63: exitLine = "%s..." % exitLine[:60]
- popup.addstr(6, 2, "exit policy: %s" % exitLine, format)
-
- if descEntry.contact:
- # clears up some common obscuring
- contactAddr = descEntry.contact
- obscuring = [(" at ", "@"), (" AT ", "@"), ("AT", "@"), (" dot ", "."), (" DOT ", ".")]
- for match, replace in obscuring: contactAddr = contactAddr.replace(match, replace)
- if len(contactAddr) > 67: contactAddr = "%s..." % contactAddr[:64]
- popup.addstr(7, 2, "contact: %s" % contactAddr, format)
-
- popup.refresh()
- key = stdscr.getch()
-
- if key == curses.KEY_RIGHT: key = curses.KEY_DOWN
- elif key == curses.KEY_LEFT: key = curses.KEY_UP
-
- if key in (curses.KEY_DOWN, curses.KEY_UP, curses.KEY_PPAGE, curses.KEY_NPAGE):
- panels["conn"].handleKey(key)
- elif key in (ord('d'), ord('D')):
- descriptorPopup.showDescriptorPopup(panels["popup"], stdscr, panels["conn"])
- panels["conn"].redraw(True)
-
- panels["conn"].showLabel = True
- panels["conn"].showingDetails = False
- hostnames.setPaused(not panels["conn"].allowDNS and panels["conn"].listingType == connPanel.LIST_HOSTNAME)
- setPauseState(panels, isPaused, page)
- curses.halfdelay(REFRESH_RATE * 10) # reset normal pausing behavior
- finally:
- panel.CURSES_LOCK.release()
- elif page == 1 and panels["conn"].isCursorEnabled and key in (ord('d'), ord('D')):
- # presents popup for raw consensus data
- panel.CURSES_LOCK.acquire()
- try:
- setPauseState(panels, isPaused, page, True)
- curses.cbreak() # wait indefinitely for key presses (no timeout)
- panels["conn"].showLabel = False
- panels["conn"].redraw(True)
-
- descriptorPopup.showDescriptorPopup(panels["popup"], stdscr, panels["conn"])
-
- setPauseState(panels, isPaused, page)
- curses.halfdelay(REFRESH_RATE * 10) # reset normal pausing behavior
- panels["conn"].showLabel = True
- finally:
- panel.CURSES_LOCK.release()
- elif page == 1 and (key == ord('l') or key == ord('L')):
- # provides menu to pick identification info listed for connections
- optionTypes = [connPanel.LIST_IP, connPanel.LIST_HOSTNAME, connPanel.LIST_FINGERPRINT, connPanel.LIST_NICKNAME]
- options = [connPanel.LIST_LABEL[sortType] for sortType in optionTypes]
- initialSelection = panels["conn"].listingType # enums correspond to index
-
- # hides top label of conn panel and pauses panels
- panels["conn"].showLabel = False
- panels["conn"].redraw(True)
- setPauseState(panels, isPaused, page, True)
-
- selection = showMenu(stdscr, panels["popup"], "List By:", options, initialSelection)
-
- # reverts changes made for popup
- panels["conn"].showLabel = True
- setPauseState(panels, isPaused, page)
-
- # applies new setting
- if selection != -1 and optionTypes[selection] != panels["conn"].listingType:
- panels["conn"].listingType = optionTypes[selection]
-
- if panels["conn"].listingType == connPanel.LIST_HOSTNAME:
- curses.halfdelay(10) # refreshes display every second until done resolving
- panels["control"].resolvingCounter = hostnames.getRequestCount() - hostnames.getPendingCount()
-
- hostnames.setPaused(not panels["conn"].allowDNS)
- for connEntry in panels["conn"].connections:
- try: hostnames.resolve(connEntry[connPanel.CONN_F_IP])
- except ValueError: pass
- else:
- panels["control"].resolvingCounter = -1
- hostnames.setPaused(True)
-
- panels["conn"].sortConnections()
- elif page in (1, 2) and (key == ord('u') or key == ord('U')):
+ elif page == 1 and (key == ord('u') or key == ord('U')):
# provides menu to pick identification resolving utility
options = ["auto"] + connections.Resolver.values()
@@ -1514,7 +1244,8 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
else: initialSelection = options.index(currentOverwrite)
# hides top label of conn panel and pauses panels
- panels["conn"].showLabel = False
+ panelTitle = panels["conn"]._title
+ panels["conn"]._title = ""
panels["conn"].redraw(True)
setPauseState(panels, isPaused, page, True)
@@ -1522,133 +1253,67 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
selectedOption = options[selection] if selection != "auto" else None
# reverts changes made for popup
- panels["conn"].showLabel = True
+ panels["conn"]._title = panelTitle
setPauseState(panels, isPaused, page)
# applies new setting
if selection != -1 and selectedOption != connections.getResolver("tor").overwriteResolver:
connections.getResolver("tor").overwriteResolver = selectedOption
- elif page == 1 and (key == ord('s') or key == ord('S')):
- # set ordering for connection listing
- titleLabel = "Connection Ordering:"
- options = [connPanel.getSortLabel(i) for i in range(9)]
- oldSelection = [connPanel.getSortLabel(entry) for entry in panels["conn"].sortOrdering]
- optionColors = dict([connPanel.getSortLabel(i, True) for i in range(9)])
- results = showSortDialog(stdscr, panels, isPaused, page, titleLabel, options, oldSelection, optionColors)
-
- if results:
- # converts labels back to enums
- resultEnums = [connPanel.getSortType(entry) for entry in results]
- panels["conn"].sortOrdering = resultEnums
- panels["conn"].sortConnections()
-
- # TODO: not necessary until the connection panel rewrite
- #panels["conn"].redraw(True)
- elif page == 1 and (key == ord('c') or key == ord('C')):
- # displays popup with client circuits
- clientCircuits = None
- try:
- clientCircuits = conn.get_info("circuit-status")["circuit-status"].split("\n")
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): pass
-
- maxEntryLength = 0
- if clientCircuits:
- for clientEntry in clientCircuits: maxEntryLength = max(len(clientEntry), maxEntryLength)
-
- panel.CURSES_LOCK.acquire()
- try:
- setPauseState(panels, isPaused, page, True)
-
- # makes sure there's room for the longest entry
- popup = panels["popup"]
- if clientCircuits and maxEntryLength + 4 > popup.getPreferredSize()[1]:
- popup.height = max(popup.height, len(clientCircuits) + 3)
- popup.recreate(stdscr, maxEntryLength + 4)
-
- # lists commands
- popup.clear()
- popup.win.box()
- popup.addstr(0, 0, "Client Circuits:", curses.A_STANDOUT)
-
- if clientCircuits == None:
- popup.addstr(1, 2, "Unable to retireve current circuits")
- elif len(clientCircuits) == 1 and clientCircuits[0] == "":
- popup.addstr(1, 2, "No active client circuits")
- else:
- line = 1
- for clientEntry in clientCircuits:
- popup.addstr(line, 2, clientEntry)
- line += 1
-
- popup.addstr(popup.height - 2, 2, "Press any key...")
- popup.refresh()
-
- curses.cbreak()
- stdscr.getch()
- curses.halfdelay(REFRESH_RATE * 10)
-
- # reverts popup dimensions
- popup.height = 9
- popup.recreate(stdscr, 80)
-
- setPauseState(panels, isPaused, page)
- finally:
- panel.CURSES_LOCK.release()
- elif page == 2 and key in (ord('d'), ord('D')):
+ elif page == 1 and key in (ord('d'), ord('D')):
# presents popup for raw consensus data
panel.CURSES_LOCK.acquire()
try:
setPauseState(panels, isPaused, page, True)
curses.cbreak() # wait indefinitely for key presses (no timeout)
- panelTitle = panels["conn2"]._title
- panels["conn2"]._title = ""
- panels["conn2"].redraw(True)
+ panelTitle = panels["conn"]._title
+ panels["conn"]._title = ""
+ panels["conn"].redraw(True)
- descriptorPopup.showDescriptorPopup(panels["popup"], stdscr, panels["conn2"], True)
+ descriptorPopup.showDescriptorPopup(panels["popup"], stdscr, panels["conn"])
- panels["conn2"]._title = panelTitle
+ panels["conn"]._title = panelTitle
setPauseState(panels, isPaused, page)
curses.halfdelay(REFRESH_RATE * 10) # reset normal pausing behavior
finally:
panel.CURSES_LOCK.release()
- elif page == 2 and (key == ord('l') or key == ord('L')):
+ elif page == 1 and (key == ord('l') or key == ord('L')):
# provides a menu to pick the primary information we list connections by
options = interface.connections.entries.ListingType.values()
# dropping the HOSTNAME listing type until we support displaying that content
options.remove(interface.connections.entries.ListingType.HOSTNAME)
- initialSelection = options.index(panels["conn2"]._listingType)
+ initialSelection = options.index(panels["conn"]._listingType)
# hides top label of connection panel and pauses the display
- panelTitle = panels["conn2"]._title
- panels["conn2"]._title = ""
- panels["conn2"].redraw(True)
+ panelTitle = panels["conn"]._title
+ panels["conn"]._title = ""
+ panels["conn"].redraw(True)
setPauseState(panels, isPaused, page, True)
selection = showMenu(stdscr, panels["popup"], "List By:", options, initialSelection)
# reverts changes made for popup
- panels["conn2"]._title = panelTitle
+ panels["conn"]._title = panelTitle
setPauseState(panels, isPaused, page)
# applies new setting
- if selection != -1 and options[selection] != panels["conn2"]._listingType:
- panels["conn2"].setListingType(options[selection])
- panels["conn2"].redraw(True)
- elif page == 2 and (key == ord('s') or key == ord('S')):
+ if selection != -1 and options[selection] != panels["conn"]._listingType:
+ panels["conn"].setListingType(options[selection])
+ panels["conn"].redraw(True)
+ elif page == 1 and (key == ord('s') or key == ord('S')):
# set ordering for connection options
titleLabel = "Connection Ordering:"
options = interface.connections.entries.SortAttr.values()
- oldSelection = panels["conn2"]._sortOrdering
+ oldSelection = panels["conn"]._sortOrdering
optionColors = dict([(attr, interface.connections.entries.SORT_COLORS[attr]) for attr in options])
results = showSortDialog(stdscr, panels, isPaused, page, titleLabel, options, oldSelection, optionColors)
if results:
- panels["conn2"].setSortOrder(results)
+ panels["conn"].setSortOrder(results)
- panels["conn2"].redraw(True)
- elif page == 3 and (key == ord('c') or key == ord('C')) and False:
+ panels["conn"].redraw(True)
+ elif page == 2 and (key == ord('c') or key == ord('C')) and False:
# TODO: disabled for now (probably gonna be going with separate pages
# rather than popup menu)
# provides menu to pick config being displayed
@@ -1671,7 +1336,7 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
if selection != -1: panels["torrc"].setConfigType(selection)
selectiveRefresh(panels, page)
- elif page == 3 and (key == ord('w') or key == ord('W')):
+ elif page == 2 and (key == ord('w') or key == ord('W')):
# display a popup for saving the current configuration
panel.CURSES_LOCK.acquire()
try:
@@ -1800,7 +1465,7 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
panel.CURSES_LOCK.release()
panels["config"].redraw(True)
- elif page == 3 and (key == ord('s') or key == ord('S')):
+ elif page == 2 and (key == ord('s') or key == ord('S')):
# set ordering for config options
titleLabel = "Config Option Ordering:"
options = [configPanel.FIELD_ATTR[field][0] for field in configPanel.Field.values()]
@@ -1821,7 +1486,7 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
panels["config"].setSortOrder(resultEnums)
panels["config"].redraw(True)
- elif page == 3 and uiTools.isSelectionKey(key):
+ elif page == 2 and uiTools.isSelectionKey(key):
# let the user edit the configuration value, unchanged if left blank
panel.CURSES_LOCK.acquire()
try:
@@ -1877,7 +1542,7 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
setPauseState(panels, isPaused, page)
finally:
panel.CURSES_LOCK.release()
- elif page == 4 and key == ord('r') or key == ord('R'):
+ elif page == 3 and key == ord('r') or key == ord('R'):
# reloads torrc, providing a notice if successful or not
loadedTorrc = torConfig.getTorrc()
loadedTorrc.getLock().acquire()
@@ -1907,10 +1572,8 @@ def drawTorMonitor(stdscr, startTime, loggedEvents, isBlindMode):
elif page == 1:
panels["conn"].handleKey(key)
elif page == 2:
- panels["conn2"].handleKey(key)
- elif page == 3:
panels["config"].handleKey(key)
- elif page == 4:
+ elif page == 3:
panels["torrc"].handleKey(key)
def startTorMonitor(startTime, loggedEvents, isBlindMode):
diff --git a/src/interface/descriptorPopup.py b/src/interface/descriptorPopup.py
index 7d51ee5..cdc959d 100644
--- a/src/interface/descriptorPopup.py
+++ b/src/interface/descriptorPopup.py
@@ -8,7 +8,6 @@ import curses
from TorCtl import TorCtl
import controller
-import connPanel
import connections.connEntry
from util import panel, torTools, uiTools
@@ -69,7 +68,7 @@ class PopupProperties:
elif key == curses.KEY_PPAGE: self.scroll = max(self.scroll - height, 0)
elif key == curses.KEY_NPAGE: self.scroll = max(0, min(self.scroll + height, len(self.text) - height))
-def showDescriptorPopup(popup, stdscr, connectionPanel, isNewPanel=False):
+def showDescriptorPopup(popup, stdscr, connectionPanel):
"""
Presents consensus descriptor in popup window with the following controls:
Up, Down, Page Up, Page Down - scroll descriptor
@@ -83,18 +82,11 @@ def showDescriptorPopup(popup, stdscr, connectionPanel, isNewPanel=False):
if not panel.CURSES_LOCK.acquire(False): return
try:
while isVisible:
- if isNewPanel:
- selection = connectionPanel._scroller.getCursorSelection(connectionPanel._entryLines)
- if not selection: break
- fingerprint = selection.foreign.getFingerprint()
- entryColor = connections.connEntry.CATEGORY_COLOR[selection.getType()]
- properties.reset(fingerprint, entryColor)
- else:
- selection = connectionPanel.cursorSelection
- if not selection or not connectionPanel.connections: break
- fingerprint = connectionPanel.getFingerprint(selection[connPanel.CONN_F_IP], selection[connPanel.CONN_F_PORT])
- entryColor = connPanel.TYPE_COLORS[selection[connPanel.CONN_TYPE]]
- properties.reset(fingerprint, entryColor)
+ selection = connectionPanel._scroller.getCursorSelection(connectionPanel._entryLines)
+ if not selection: break
+ fingerprint = selection.foreign.getFingerprint()
+ entryColor = connections.connEntry.CATEGORY_COLOR[selection.getType()]
+ properties.reset(fingerprint, entryColor)
# constrains popup size to match text
width, height = 0, 0
1
0