/usr/share/arm/cli/connections/circEntry.py is in tor-arm 1.4.5.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | """
Connection panel entries for client circuits. This includes a header entry
followed by an entry for each hop in the circuit. For instance:
89.188.20.246:42667 --> 217.172.182.26 (de) General / Built 8.6m (CIRCUIT)
| 85.8.28.4 (se) 98FBC3B2B93897A78CDD797EF549E6B62C9A8523 1 / Guard
| 91.121.204.76 (fr) 546387D93F8D40CFF8842BB9D3A8EC477CEDA984 2 / Middle
+- 217.172.182.26 (de) 5CFA9EA136C0EA0AC096E5CEA7EB674F1207CF86 3 / Exit
"""
import curses
from cli.connections import entries, connEntry
from util import torTools, uiTools
class CircEntry(connEntry.ConnectionEntry):
def __init__(self, circuitID, status, purpose, path):
connEntry.ConnectionEntry.__init__(self, "127.0.0.1", "0", "127.0.0.1", "0")
self.circuitID = circuitID
self.status = status
# drops to lowercase except the first letter
if len(purpose) >= 2:
purpose = purpose[0].upper() + purpose[1:].lower()
self.lines = [CircHeaderLine(self.circuitID, purpose)]
# Overwrites attributes of the initial line to make it more fitting as the
# header for our listing.
self.lines[0].baseType = connEntry.Category.CIRCUIT
self.update(status, path)
def update(self, status, path):
"""
Our status and path can change over time if the circuit is still in the
process of being built. Updates these attributes of our relay.
Arguments:
status - new status of the circuit
path - list of fingerprints for the series of relays involved in the
circuit
"""
self.status = status
self.lines = [self.lines[0]]
conn = torTools.getConn()
if status == "BUILT" and not self.lines[0].isBuilt:
exitIp, exitORPort = conn.getRelayAddress(path[-1], ("192.168.0.1", "0"))
self.lines[0].setExit(exitIp, exitORPort, path[-1])
for i in range(len(path)):
relayFingerprint = path[i]
relayIp, relayOrPort = conn.getRelayAddress(relayFingerprint, ("192.168.0.1", "0"))
if i == len(path) - 1:
if status == "BUILT": placementType = "Exit"
else: placementType = "Extending"
elif i == 0: placementType = "Guard"
else: placementType = "Middle"
placementLabel = "%i / %s" % (i + 1, placementType)
self.lines.append(CircLine(relayIp, relayOrPort, relayFingerprint, placementLabel))
self.lines[-1].isLast = True
class CircHeaderLine(connEntry.ConnectionLine):
"""
Initial line of a client entry. This has the same basic format as connection
lines except that its etc field has circuit attributes.
"""
def __init__(self, circuitID, purpose):
connEntry.ConnectionLine.__init__(self, "127.0.0.1", "0", "0.0.0.0", "0", False, False)
self.circuitID = circuitID
self.purpose = purpose
self.isBuilt = False
def setExit(self, exitIpAddr, exitPort, exitFingerprint):
connEntry.ConnectionLine.__init__(self, "127.0.0.1", "0", exitIpAddr, exitPort, False, False)
self.isBuilt = True
self.foreign.fingerprintOverwrite = exitFingerprint
def getType(self):
return connEntry.Category.CIRCUIT
def getDestinationLabel(self, maxLength, includeLocale=False, includeHostname=False):
if not self.isBuilt: return "Building..."
return connEntry.ConnectionLine.getDestinationLabel(self, maxLength, includeLocale, includeHostname)
def getEtcContent(self, width, listingType):
"""
Attempts to provide all circuit related stats. Anything that can't be
shown completely (not enough room) is dropped.
"""
etcAttr = ["Purpose: %s" % self.purpose, "Circuit ID: %i" % self.circuitID]
for i in range(len(etcAttr), -1, -1):
etcLabel = ", ".join(etcAttr[:i])
if len(etcLabel) <= width:
return ("%%-%is" % width) % etcLabel
return ""
def getDetails(self, width):
if not self.isBuilt:
detailFormat = curses.A_BOLD | uiTools.getColor(connEntry.CATEGORY_COLOR[self.getType()])
return [("Building Circuit...", detailFormat)]
else: return connEntry.ConnectionLine.getDetails(self, width)
class CircLine(connEntry.ConnectionLine):
"""
An individual hop in a circuit. This overwrites the displayed listing, but
otherwise makes use of the ConnectionLine attributes (for the detail display,
caching, etc).
"""
def __init__(self, fIpAddr, fPort, fFingerprint, placementLabel):
connEntry.ConnectionLine.__init__(self, "127.0.0.1", "0", fIpAddr, fPort)
self.foreign.fingerprintOverwrite = fFingerprint
self.placementLabel = placementLabel
self.includePort = False
# determines the sort of left hand bracketing we use
self.isLast = False
def getType(self):
return connEntry.Category.CIRCUIT
def getListingPrefix(self):
if self.isLast: return (ord(' '), curses.ACS_LLCORNER, curses.ACS_HLINE, ord(' '))
else: return (ord(' '), curses.ACS_VLINE, ord(' '), ord(' '))
def getListingEntry(self, width, currentTime, listingType):
"""
Provides the [(msg, attr)...] listing for this relay in the circuilt
listing. Lines are composed of the following components:
<bracket> <dst> <etc> <placement label>
The dst and etc entries largely match their ConnectionEntry counterparts.
Arguments:
width - maximum length of the line
currentTime - the current unix time (ignored)
listingType - primary attribute we're listing connections by
"""
return entries.ConnectionPanelLine.getListingEntry(self, width, currentTime, listingType)
def _getListingEntry(self, width, currentTime, listingType):
lineFormat = uiTools.getColor(connEntry.CATEGORY_COLOR[self.getType()])
# The required widths are the sum of the following:
# initial space (1 character)
# bracketing (3 characters)
# placementLabel (14 characters)
# gap between etc and placement label (5 characters)
baselineSpace = 14 + 5
dst, etc = "", ""
if listingType == entries.ListingType.IP_ADDRESS:
# TODO: include hostname when that's available
# dst width is derived as:
# src (21) + dst (26) + divider (7) + right gap (2) - bracket (3) = 53 char
dst = "%-53s" % self.getDestinationLabel(53, includeLocale = True)
# fills the nickname into the empty space here
dst = "%s%-25s " % (dst[:25], uiTools.cropStr(self.foreign.getNickname(), 25, 0))
etc = self.getEtcContent(width - baselineSpace - len(dst), listingType)
elif listingType == entries.ListingType.HOSTNAME:
# min space for the hostname is 40 characters
etc = self.getEtcContent(width - baselineSpace - 40, listingType)
dstLayout = "%%-%is" % (width - baselineSpace - len(etc))
dst = dstLayout % self.foreign.getHostname(self.foreign.getIpAddr())
elif listingType == entries.ListingType.FINGERPRINT:
# dst width is derived as:
# src (9) + dst (40) + divider (7) + right gap (2) - bracket (3) = 55 char
dst = "%-55s" % self.foreign.getFingerprint()
etc = self.getEtcContent(width - baselineSpace - len(dst), listingType)
else:
# min space for the nickname is 56 characters
etc = self.getEtcContent(width - baselineSpace - 56, listingType)
dstLayout = "%%-%is" % (width - baselineSpace - len(etc))
dst = dstLayout % self.foreign.getNickname()
return ((dst + etc, lineFormat),
(" " * (width - baselineSpace - len(dst) - len(etc) + 5), lineFormat),
("%-14s" % self.placementLabel, lineFormat))
|