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
September 2015
- 15 participants
- 1411 discussions
commit 9feead4c7cbd42898bc794feb9270601703a6561
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Sep 5 15:19:29 2015 -0700
Move connection colors to attributes
---
nyx/config/attributes.cfg | 18 ++++++++++++++++++
nyx/connection_panel.py | 37 ++++++++-----------------------------
2 files changed, 26 insertions(+), 29 deletions(-)
diff --git a/nyx/config/attributes.cfg b/nyx/config/attributes.cfg
index fe24120..e1d1b9e 100644
--- a/nyx/config/attributes.cfg
+++ b/nyx/config/attributes.cfg
@@ -57,3 +57,21 @@ attr.log_color NS => blue
attr.log_color NEWCONSENSUS => blue
attr.log_color GUARD => yellow
+attr.connection.category_color Inbound => green
+attr.connection.category_color Outbound => blue
+attr.connection.category_color Exit => red
+attr.connection.category_color Hidden => magenta
+attr.connection.category_color Socks => yellow
+attr.connection.category_color Circuit => cyan
+attr.connection.category_color Directory => magenta
+attr.connection.category_color Control => red
+
+attr.connection.sort_color Category => red
+attr.connection.sort_color Uptime => yellow
+attr.connection.sort_color Listing => green
+attr.connection.sort_color Ip Address => blue
+attr.connection.sort_color Port => blue
+attr.connection.sort_color Fingerprint => cyan
+attr.connection.sort_color Nickname => cyan
+attr.connection.sort_color Country => blue
+
diff --git a/nyx/connection_panel.py b/nyx/connection_panel.py
index 9fcacae..f133ff8 100644
--- a/nyx/connection_panel.py
+++ b/nyx/connection_panel.py
@@ -47,31 +47,8 @@ UPDATE_RATE = 5 # rate in seconds at which we refresh
# Control Tor controller (nyx, vidalia, etc).
Category = enum.Enum('INBOUND', 'OUTBOUND', 'EXIT', 'HIDDEN', 'SOCKS', 'CIRCUIT', 'DIRECTORY', 'CONTROL')
-
-CATEGORY_COLOR = {
- Category.INBOUND: 'green',
- Category.OUTBOUND: 'blue',
- Category.EXIT: 'red',
- Category.HIDDEN: 'magenta',
- Category.SOCKS: 'yellow',
- Category.CIRCUIT: 'cyan',
- Category.DIRECTORY: 'magenta',
- Category.CONTROL: 'red',
-}
-
SortAttr = enum.Enum('CATEGORY', 'UPTIME', 'LISTING', 'IP_ADDRESS', 'PORT', 'FINGERPRINT', 'NICKNAME', 'COUNTRY')
-SORT_COLORS = {
- SortAttr.CATEGORY: 'red',
- SortAttr.UPTIME: 'yellow',
- SortAttr.LISTING: 'green',
- SortAttr.IP_ADDRESS: 'blue',
- SortAttr.PORT: 'blue',
- SortAttr.FINGERPRINT: 'cyan',
- SortAttr.NICKNAME: 'cyan',
- SortAttr.COUNTRY: 'blue',
-}
-
# static data for listing format
# <src> --> <dst> <etc><padding>
@@ -87,6 +64,8 @@ def conf_handler(key, value):
CONFIG = conf.config_dict('nyx', {
+ 'attr.connection.category_color': {},
+ 'attr.connection.sort_color': {},
'features.connection.resolveApps': True,
'features.connection.listing_type': Listing.IP_ADDRESS,
'features.connection.order': [
@@ -320,7 +299,7 @@ class ConnectionLine(object):
# category - "<type>"
# postType - ") "
- line_format = nyx.util.ui_tools.get_color(CATEGORY_COLOR[entry_type])
+ line_format = nyx.util.ui_tools.get_color(CONFIG['attr.connection.category_color'].get(entry_type, 'white'))
draw_entry = [(' ', line_format),
(self._get_listing_content(width - 19, listing_type), line_format),
@@ -341,7 +320,7 @@ class ConnectionLine(object):
width - available space to display in
"""
- detail_format = (curses.A_BOLD, CATEGORY_COLOR[self._entry.get_type()])
+ detail_format = (curses.A_BOLD, CONFIG['attr.connection.category_color'].get(self._entry.get_type(), 'white'))
return [(line, detail_format) for line in self._get_detail_content(width)]
def get_etc_content(self, width, listing_type):
@@ -689,7 +668,7 @@ class CircHeaderLine(ConnectionLine):
@lru_cache()
def get_details(self, width):
if not self.is_built:
- detail_format = (curses.A_BOLD, CATEGORY_COLOR[self._entry.get_type()])
+ detail_format = (curses.A_BOLD, CONFIG['attr.connection.category_color'].get(self._entry.get_type(), 'white'))
return [('Building Circuit...', detail_format)]
else:
return ConnectionLine.get_details(self, width)
@@ -748,7 +727,7 @@ class CircLine(ConnectionLine):
@lru_cache()
def _get_listing_entry(self, width, listing_type):
- line_format = nyx.util.ui_tools.get_color(CATEGORY_COLOR[self._entry.get_type()])
+ line_format = nyx.util.ui_tools.get_color(CONFIG['attr.connection.category_color'].get(self._entry.get_type(), 'white'))
# The required widths are the sum of the following:
# initial space (1 character)
@@ -975,7 +954,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
title_label = 'Connection Ordering:'
options = list(SortAttr)
old_selection = CONFIG['features.connection.order']
- option_colors = dict([(attr, SORT_COLORS[attr]) for attr in options])
+ option_colors = dict([(attr, CONFIG['attr.connection.sort_color'].get(attr, 'white')) for attr in options])
results = nyx.popups.show_sort_dialog(title_label, options, old_selection, option_colors)
if results:
@@ -1044,7 +1023,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
if not selection:
break
- color = CATEGORY_COLOR[selection.get_type()]
+ color = CONFIG['attr.connection.category_color'].get(selection.get_type(), 'white')
fingerprint = selection.get_fingerprint()
is_close_key = lambda key: key.is_selection() or key.match('d') or key.match('left') or key.match('right')
key = nyx.popups.show_descriptor_popup(fingerprint, color, self.max_x, is_close_key)
1
0
commit 89d2e97dcefc8af51200e52128fb909fc6c49771
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Sep 5 09:52:20 2015 -0700
Drop expanded addresses
When we have a lot of space we showed three addresses of the form
'(local) => (externally visible) => (remote)'...
192.168.0.20:56088 --> 174.21.33.26:56088 --> 85.114.132.36:9001
Nice idea but it's confusing as hell for users. Hell, even I didn't know what
these were until I looked at the code. Keeping it simple and just showing the
important internal/external address.
---
nyx/connections/circ_entry.py | 4 ++--
nyx/connections/conn_entry.py | 41 +++--------------------------------------
2 files changed, 5 insertions(+), 40 deletions(-)
diff --git a/nyx/connections/circ_entry.py b/nyx/connections/circ_entry.py
index 84e5b2b..920d599 100644
--- a/nyx/connections/circ_entry.py
+++ b/nyx/connections/circ_entry.py
@@ -46,7 +46,7 @@ class CircHeaderLine(conn_entry.ConnectionLine):
self.is_built = False
self._remote_fingerprint = None
- conn_entry.ConnectionLine.__init__(self, entry, nyx.util.tracker.Connection(to_unix_time(circ.created), False, '127.0.0.1', 0, exit_address, exit_port, 'tcp'), False, False)
+ conn_entry.ConnectionLine.__init__(self, entry, nyx.util.tracker.Connection(to_unix_time(circ.created), False, '127.0.0.1', 0, exit_address, exit_port, 'tcp'), include_port = False)
self.circuit = circ
def get_fingerprint(self, default = None):
@@ -92,7 +92,7 @@ class CircLine(conn_entry.ConnectionLine):
def __init__(self, entry, circ, fingerprint):
relay_ip, relay_port = nyx.util.tracker.get_consensus_tracker().get_relay_address(fingerprint, ('192.168.0.1', 0))
- conn_entry.ConnectionLine.__init__(self, entry, nyx.util.tracker.Connection(to_unix_time(circ.created), False, '127.0.0.1', 0, relay_ip, relay_port, 'tcp'), False)
+ conn_entry.ConnectionLine.__init__(self, entry, nyx.util.tracker.Connection(to_unix_time(circ.created), False, '127.0.0.1', 0, relay_ip, relay_port, 'tcp'), include_port = False)
self._fingerprint = fingerprint
self._is_last = False
diff --git a/nyx/connections/conn_entry.py b/nyx/connections/conn_entry.py
index d76808d..3ed78a9 100644
--- a/nyx/connections/conn_entry.py
+++ b/nyx/connections/conn_entry.py
@@ -37,7 +37,7 @@ class ConnectionLine(object):
Display component of the ConnectionEntry.
"""
- def __init__(self, entry, conn, include_port=True, include_expanded_addresses=True):
+ def __init__(self, entry, conn, include_port = True):
self._entry = entry
self.connection = conn
@@ -45,7 +45,6 @@ class ConnectionLine(object):
# information if true
self.include_port = include_port
- self.include_expanded_addresses = include_expanded_addresses
def get_listing_prefix(self):
"""
@@ -262,19 +261,10 @@ class ConnectionLine(object):
if listing_type == nyx.connection_panel.Listing.IP_ADDRESS:
my_external_address = controller.get_info('address', self.connection.local_address)
- address_differ = my_external_address != self.connection.local_address
- # Expanding doesn't make sense, if the connection isn't actually
- # going through Tor's external IP address. As there isn't a known
- # method for checking if it is, we're checking the type instead.
- #
- # This isn't entirely correct. It might be a better idea to check if
- # the source and destination addresses are both private, but that might
- # not be perfectly reliable either.
+ # Show our external address if it's going through tor.
- is_expansion_type = my_type not in (Category.SOCKS, Category.HIDDEN, Category.CONTROL)
-
- if is_expansion_type:
+ if my_type not in (Category.SOCKS, Category.HIDDEN, Category.CONTROL):
src_address = my_external_address + local_port
else:
src_address = self.connection.local_address + local_port
@@ -293,31 +283,6 @@ class ConnectionLine(object):
used_space += len(src) + len(dst) # base data requires 47 characters
- # Showing the fingerprint (which has the width of 42) has priority over
- # an expanded address field. Hence check if we either have space for
- # both or wouldn't be showing the fingerprint regardless.
-
- is_expanded_address_visible = width > used_space + 28
-
- if is_expanded_address_visible:
- is_expanded_address_visible = width < used_space + 42 or width > used_space + 70
-
- if address_differ and is_expansion_type and is_expanded_address_visible and self.include_expanded_addresses:
- # include the internal address in the src (extra 28 characters)
-
- internal_address = self.connection.local_address + local_port
-
- # If this is an inbound connection then reverse ordering so it's:
- # <foreign> --> <external> --> <internal>
- # when the src and dst are swapped later
-
- if my_type == Category.INBOUND:
- src = '%-21s --> %s' % (src, internal_address)
- else:
- src = '%-21s --> %s' % (internal_address, src)
-
- used_space += 28
-
etc = self.get_etc_content(width - used_space, listing_type)
used_space += len(etc)
elif listing_type == nyx.connection_panel.Listing.FINGERPRINT:
1
0

[nyx/master] Drop features.connection.showColumn.* config options
by atagar@torproject.org 22 Sep '15
by atagar@torproject.org 22 Sep '15
22 Sep '15
commit 93446e76246a24c953a59d6ac5c4b95751d602da
Author: Damian Johnson <atagar(a)torproject.org>
Date: Thu Sep 3 07:53:36 2015 -0700
Drop features.connection.showColumn.* config options
More nyxrc options I don't think anybody's ever used.
---
nyx/connections/conn_entry.py | 24 +++++++++---------------
nyxrc.sample | 6 ------
2 files changed, 9 insertions(+), 21 deletions(-)
diff --git a/nyx/connections/conn_entry.py b/nyx/connections/conn_entry.py
index 1f2439d..d76808d 100644
--- a/nyx/connections/conn_entry.py
+++ b/nyx/connections/conn_entry.py
@@ -29,10 +29,6 @@ LABEL_MIN_PADDING = 2 # min space between listing label and following data
CONFIG = conf.config_dict('nyx', {
'features.connection.showIps': True,
'features.connection.showExitPort': True,
- 'features.connection.showColumn.fingerprint': True,
- 'features.connection.showColumn.nickname': True,
- 'features.connection.showColumn.destination': True,
- 'features.connection.showColumn.expandedIp': True,
})
@@ -194,13 +190,13 @@ class ConnectionLine(object):
etc, used_space = '', 0
if listing_type == nyx.connection_panel.Listing.IP_ADDRESS:
- if width > used_space + 42 and CONFIG['features.connection.showColumn.fingerprint']:
+ if width > used_space + 42:
# show fingerprint (column width: 42 characters)
etc += '%-40s ' % self.get_fingerprint('UNKNOWN')
used_space += 42
- if width > used_space + 10 and CONFIG['features.connection.showColumn.nickname']:
+ if width > used_space + 10:
# show nickname (column width: remainder)
nickname_space = width - used_space
@@ -217,26 +213,24 @@ class ConnectionLine(object):
# ip/port/locale (column width: 28 characters)
is_locale_included = width > used_space + 45
- is_locale_included &= CONFIG['features.connection.showColumn.destination']
if is_locale_included:
nickname_space -= 28
- if CONFIG['features.connection.showColumn.nickname']:
- nickname_label = str_tools.crop(self.get_nickname('UNKNOWN'), nickname_space, 0)
- etc += ('%%-%is ' % nickname_space) % nickname_label
- used_space += nickname_space + 2
+ nickname_label = str_tools.crop(self.get_nickname('UNKNOWN'), nickname_space, 0)
+ etc += ('%%-%is ' % nickname_space) % nickname_label
+ used_space += nickname_space + 2
if is_locale_included:
etc += '%-26s ' % destination_address
used_space += 28
else:
- if width > used_space + 42 and CONFIG['features.connection.showColumn.fingerprint']:
+ if width > used_space + 42:
# show fingerprint (column width: 42 characters)
etc += '%-40s ' % self.get_fingerprint('UNKNOWN')
used_space += 42
- if width > used_space + 28 and CONFIG['features.connection.showColumn.destination']:
+ if width > used_space + 28:
# show destination ip/port/locale (column width: 28 characters)
etc += '%-26s ' % destination_address
used_space += 28
@@ -305,10 +299,10 @@ class ConnectionLine(object):
is_expanded_address_visible = width > used_space + 28
- if is_expanded_address_visible and CONFIG['features.connection.showColumn.fingerprint']:
+ if is_expanded_address_visible:
is_expanded_address_visible = width < used_space + 42 or width > used_space + 70
- if address_differ and is_expansion_type and is_expanded_address_visible and self.include_expanded_addresses and CONFIG['features.connection.showColumn.expandedIp']:
+ if address_differ and is_expansion_type and is_expanded_address_visible and self.include_expanded_addresses:
# include the internal address in the src (extra 28 characters)
internal_address = self.connection.local_address + local_port
diff --git a/nyxrc.sample b/nyxrc.sample
index e453320..d922081 100644
--- a/nyxrc.sample
+++ b/nyxrc.sample
@@ -205,18 +205,12 @@ features.graph.bw.accounting.show true
# false
# showExitPort
# shows port related information of exit connections we relay if true
-# showColumn.*
-# toggles the visability of the connection table columns
features.connection.listingType IP_ADDRESS
features.connection.order CATEGORY, LISTING, UPTIME
features.connection.resolveApps true
features.connection.showIps true
features.connection.showExitPort true
-features.connection.showColumn.fingerprint true
-features.connection.showColumn.nickname true
-features.connection.showColumn.destination true
-features.connection.showColumn.expandedIp true
# Caching parameters
cache.logPanel.size 1000
1
0

22 Sep '15
commit 02327e0bed85ad2e87ce3c0ecb3bb62bcce117dd
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Sep 5 15:00:57 2015 -0700
Consolidate remaining nyx/connections/* module
Ok, trimmed the remaining bits enough that we can consolidate in
connection_panel.py. Still a mess, but now a manageable mess.
---
nyx/connection_panel.py | 594 ++++++++++++++++++++++++++++++++++-
nyx/connections/__init__.py | 10 -
nyx/connections/circ_entry.py | 177 -----------
nyx/connections/conn_entry.py | 453 --------------------------
nyx/connections/descriptor_popup.py | 174 ----------
nyx/popups.py | 167 +++++++++-
setup.py | 2 +-
7 files changed, 752 insertions(+), 825 deletions(-)
diff --git a/nyx/connection_panel.py b/nyx/connection_panel.py
index c583a00..9fcacae 100644
--- a/nyx/connection_panel.py
+++ b/nyx/connection_panel.py
@@ -6,17 +6,18 @@ import re
import time
import collections
import curses
+import datetime
import itertools
import threading
import nyx.popups
import nyx.util.tracker
+import nyx.util.ui_tools
-from nyx.connections import descriptor_popup
from nyx.util import panel, tor_controller, ui_tools
from stem.control import Listener, State
-from stem.util import conf, connection, enum
+from stem.util import conf, connection, enum, str_tools
try:
# added in python 3.2
@@ -71,6 +72,12 @@ SORT_COLORS = {
SortAttr.COUNTRY: 'blue',
}
+# static data for listing format
+# <src> --> <dst> <etc><padding>
+
+LABEL_FORMAT = '%s --> %s %s%s'
+LABEL_MIN_PADDING = 2 # min space between listing label and following data
+
def conf_handler(key, value):
if key == 'features.connection.listing_type':
@@ -90,6 +97,10 @@ CONFIG = conf.config_dict('nyx', {
}, conf_handler)
+def to_unix_time(dt):
+ return (dt - datetime.datetime(1970, 1, 1)).total_seconds()
+
+
class Entry(object):
@staticmethod
@lru_cache()
@@ -137,8 +148,7 @@ class ConnectionEntry(Entry):
@lru_cache()
def get_lines(self):
- import nyx.connections.conn_entry
- return [nyx.connections.conn_entry.ConnectionLine(self, self._connection)]
+ return [ConnectionLine(self, self._connection)]
@lru_cache()
def get_type(self):
@@ -201,7 +211,6 @@ class CircuitEntry(Entry):
@lru_cache()
def get_lines(self):
- from nyx.connections.circ_entry import CircHeaderLine, CircLine
return [CircHeaderLine(self, self._circuit)] + [CircLine(self, self._circuit, fp) for fp, _ in self._circuit.path]
def get_type(self):
@@ -211,6 +220,575 @@ class CircuitEntry(Entry):
return False
+class ConnectionLine(object):
+ """
+ Display component of the ConnectionEntry.
+ """
+
+ def __init__(self, entry, conn, include_port = True):
+ self._entry = entry
+ self.connection = conn
+
+ # includes the port or expanded ip address field when displaying listing
+ # information if true
+
+ self.include_port = include_port
+
+ def get_listing_prefix(self):
+ """
+ Provides a list of characters to be appended before the listing entry.
+ """
+
+ return ()
+
+ def get_locale(self, default = None):
+ """
+ Provides the two letter country code for the remote endpoint.
+ """
+
+ return tor_controller().get_info('ip-to-country/%s' % self.connection.remote_address, default)
+
+ def get_fingerprint(self, default = None):
+ """
+ Provides the fingerprint of this relay.
+ """
+
+ if self._entry.get_type() in (Category.OUTBOUND, Category.CIRCUIT, Category.DIRECTORY, Category.EXIT):
+ my_fingerprint = nyx.util.tracker.get_consensus_tracker().get_relay_fingerprint(self.connection.remote_address, self.connection.remote_port)
+ return my_fingerprint if my_fingerprint else default
+ else:
+ return default # inbound connections don't have an ORPort we can resolve
+
+ def get_nickname(self, default = None):
+ """
+ Provides the nickname of this relay.
+ """
+
+ nickname = nyx.util.tracker.get_consensus_tracker().get_relay_nickname(self.get_fingerprint())
+ return nickname if nickname else default
+
+ def get_listing_entry(self, width, current_time, listing_type):
+ """
+ Provides the tuple list for this connection's listing. Lines are composed
+ of the following components:
+ <src> --> <dst> <etc> <uptime> (<type>)
+
+ Listing.IP_ADDRESS:
+ src - <internal addr:port> --> <external addr:port>
+ dst - <destination addr:port>
+ etc - <fingerprint> <nickname>
+
+ Listing.FINGERPRINT:
+ src - localhost
+ dst - <destination fingerprint>
+ etc - <nickname> <destination addr:port>
+
+ Listing.NICKNAME:
+ src - <source nickname>
+ dst - <destination nickname>
+ etc - <fingerprint> <destination addr:port>
+
+ Arguments:
+ width - maximum length of the line
+ current_time - unix timestamp for what the results should consider to be
+ the current time
+ listing_type - primary attribute we're listing connections by
+ """
+
+ # fetch our (most likely cached) display entry for the listing
+
+ my_listing = self._get_listing_entry(width, listing_type)
+
+ # fill in the current uptime and return the results
+
+ time_prefix = '+' if self.connection.is_legacy else ' '
+
+ time_label = time_prefix + '%5s' % str_tools.time_label(current_time - self.connection.start_time, 1)
+ my_listing[2] = (time_label, my_listing[2][1])
+
+ return my_listing
+
+ @lru_cache()
+ def _get_listing_entry(self, width, listing_type):
+ entry_type = self._entry.get_type()
+
+ # Lines are split into the following components in reverse:
+ # init gap - " "
+ # content - "<src> --> <dst> <etc> "
+ # time - "<uptime>"
+ # preType - " ("
+ # category - "<type>"
+ # postType - ") "
+
+ line_format = nyx.util.ui_tools.get_color(CATEGORY_COLOR[entry_type])
+
+ draw_entry = [(' ', line_format),
+ (self._get_listing_content(width - 19, listing_type), line_format),
+ (' ', line_format),
+ (' (', line_format),
+ (entry_type.upper(), line_format | curses.A_BOLD),
+ (')' + ' ' * (9 - len(entry_type)), line_format)]
+
+ return draw_entry
+
+ @lru_cache()
+ def get_details(self, width):
+ """
+ Provides details on the connection, correlated against available consensus
+ data.
+
+ Arguments:
+ width - available space to display in
+ """
+
+ detail_format = (curses.A_BOLD, CATEGORY_COLOR[self._entry.get_type()])
+ return [(line, detail_format) for line in self._get_detail_content(width)]
+
+ def get_etc_content(self, width, listing_type):
+ """
+ Provides the optional content for the connection.
+
+ Arguments:
+ width - maximum length of the line
+ listing_type - primary attribute we're listing connections by
+ """
+
+ # for applications show the command/pid
+
+ if self._entry.get_type() in (Category.SOCKS, Category.HIDDEN, Category.CONTROL):
+ port = self.connection.local_port if self._entry.get_type() == Category.HIDDEN else self.connection.remote_port
+
+ try:
+ process = nyx.util.tracker.get_port_usage_tracker().fetch(port)
+ display_label = '%s (%s)' % (process.name, process.pid) if process.pid else process.name
+ except nyx.util.tracker.UnresolvedResult:
+ display_label = 'resolving...'
+ except nyx.util.tracker.UnknownApplication:
+ display_label = 'UNKNOWN'
+
+ if len(display_label) < width:
+ return ('%%-%is' % width) % display_label
+ else:
+ return ''
+
+ # for everything else display connection/consensus information
+
+ destination_address = self.get_destination_label(26, include_locale = True)
+ etc, used_space = '', 0
+
+ if listing_type == Listing.IP_ADDRESS:
+ if width > used_space + 42:
+ # show fingerprint (column width: 42 characters)
+
+ etc += '%-40s ' % self.get_fingerprint('UNKNOWN')
+ used_space += 42
+
+ if width > used_space + 10:
+ # show nickname (column width: remainder)
+
+ nickname_space = width - used_space
+ nickname_label = str_tools.crop(self.get_nickname('UNKNOWN'), nickname_space, 0)
+ etc += ('%%-%is ' % nickname_space) % nickname_label
+ used_space += nickname_space + 2
+ elif listing_type == Listing.FINGERPRINT:
+ if width > used_space + 17:
+ # show nickname (column width: min 17 characters, consumes any remaining space)
+
+ nickname_space = width - used_space - 2
+
+ # if there's room then also show a column with the destination
+ # ip/port/locale (column width: 28 characters)
+
+ is_locale_included = width > used_space + 45
+
+ if is_locale_included:
+ nickname_space -= 28
+
+ nickname_label = str_tools.crop(self.get_nickname('UNKNOWN'), nickname_space, 0)
+ etc += ('%%-%is ' % nickname_space) % nickname_label
+ used_space += nickname_space + 2
+
+ if is_locale_included:
+ etc += '%-26s ' % destination_address
+ used_space += 28
+ else:
+ if width > used_space + 42:
+ # show fingerprint (column width: 42 characters)
+ etc += '%-40s ' % self.get_fingerprint('UNKNOWN')
+ used_space += 42
+
+ if width > used_space + 28:
+ # show destination ip/port/locale (column width: 28 characters)
+ etc += '%-26s ' % destination_address
+ used_space += 28
+
+ return ('%%-%is' % width) % etc
+
+ def _get_listing_content(self, width, listing_type):
+ """
+ Provides the source, destination, and extra info for our listing.
+
+ Arguments:
+ width - maximum length of the line
+ listing_type - primary attribute we're listing connections by
+ """
+
+ controller = tor_controller()
+ my_type = self._entry.get_type()
+ destination_address = self.get_destination_label(26, include_locale = True)
+
+ # The required widths are the sum of the following:
+ # - room for LABEL_FORMAT and LABEL_MIN_PADDING (11 characters)
+ # - base data for the listing
+ # - that extra field plus any previous
+
+ used_space = len(LABEL_FORMAT % tuple([''] * 4)) + LABEL_MIN_PADDING
+ local_port = ':%s' % self.connection.local_port if self.include_port else ''
+
+ src, dst, etc = '', '', ''
+
+ if listing_type == Listing.IP_ADDRESS:
+ my_external_address = controller.get_info('address', self.connection.local_address)
+
+ # Show our external address if it's going through tor.
+
+ if my_type not in (Category.SOCKS, Category.HIDDEN, Category.CONTROL):
+ src_address = my_external_address + local_port
+ else:
+ src_address = self.connection.local_address + local_port
+
+ if my_type in (Category.SOCKS, Category.CONTROL):
+ # Like inbound connections these need their source and destination to
+ # be swapped. However, this only applies when listing by IP (their
+ # fingerprint and nickname are both for us). Reversing the fields here
+ # to keep the same column alignments.
+
+ src = '%-21s' % destination_address
+ dst = '%-26s' % src_address
+ else:
+ src = '%-21s' % src_address # ip:port = max of 21 characters
+ dst = '%-26s' % destination_address # ip:port (xx) = max of 26 characters
+
+ used_space += len(src) + len(dst) # base data requires 47 characters
+
+ etc = self.get_etc_content(width - used_space, listing_type)
+ used_space += len(etc)
+ elif listing_type == Listing.FINGERPRINT:
+ src = 'localhost'
+ dst = '%-40s' % ('localhost' if my_type == Category.CONTROL else self.get_fingerprint('UNKNOWN'))
+
+ used_space += len(src) + len(dst) # base data requires 49 characters
+
+ etc = self.get_etc_content(width - used_space, listing_type)
+ used_space += len(etc)
+ else:
+ # base data requires 50 min characters
+ src = controller.get_conf('nickname', 'UNKNOWN')
+ dst = controller.get_conf('nickname', 'UNKNOWN') if my_type == Category.CONTROL else self.get_nickname('UNKNOWN')
+
+ min_base_space = 50
+
+ etc = self.get_etc_content(width - used_space - min_base_space, listing_type)
+ used_space += len(etc)
+
+ base_space = width - used_space
+ used_space = width # prevents padding at the end
+
+ if len(src) + len(dst) > base_space:
+ src = str_tools.crop(src, base_space / 3)
+ dst = str_tools.crop(dst, base_space - len(src))
+
+ # pads dst entry to its max space
+
+ dst = ('%%-%is' % (base_space - len(src))) % dst
+
+ if my_type == Category.INBOUND:
+ src, dst = dst, src
+
+ padding = ' ' * (width - used_space + LABEL_MIN_PADDING)
+
+ return LABEL_FORMAT % (src, dst, etc, padding)
+
+ def _get_detail_content(self, width):
+ """
+ Provides a list with detailed information for this connection.
+
+ Arguments:
+ width - max length of lines
+ """
+
+ lines = [''] * 7
+ lines[0] = 'address: %s' % self.get_destination_label(width - 11)
+ lines[1] = 'locale: %s' % ('??' if self._entry.is_private() else self.get_locale('??'))
+
+ # Remaining data concerns the consensus results, with three possible cases:
+ # - if there's a single match then display its details
+ # - if there's multiple potential relays then list all of the combinations
+ # of ORPorts / Fingerprints
+ # - if no consensus data is available then say so (probably a client or
+ # exit connection)
+
+ fingerprint = self.get_fingerprint()
+ controller = tor_controller()
+
+ if fingerprint:
+ lines[1] = '%-13sfingerprint: %s' % (lines[1], fingerprint) # append fingerprint to second line
+
+ router_status_entry = controller.get_network_status(fingerprint, None)
+ server_descriptor = controller.get_server_descriptor(fingerprint, None)
+
+ if router_status_entry:
+ dir_port_label = 'dirport: %s' % router_status_entry.dir_port if router_status_entry.dir_port else ''
+ lines[2] = 'nickname: %-25s orport: %-10s %s' % (router_status_entry.nickname, router_status_entry.or_port, dir_port_label)
+ lines[3] = 'published: %s' % router_status_entry.published.strftime("%H:%M %m/%d/%Y")
+ lines[4] = 'flags: %s' % ', '.join(router_status_entry.flags)
+
+ if server_descriptor:
+ policy_label = server_descriptor.exit_policy.summary() if server_descriptor.exit_policy else 'unknown'
+ lines[5] = 'exit policy: %s' % policy_label
+ lines[3] = '%-35s os: %-14s version: %s' % (lines[3], server_descriptor.operating_system, server_descriptor.tor_version)
+
+ if server_descriptor.contact:
+ lines[6] = 'contact: %s' % server_descriptor.contact
+ else:
+ all_matches = nyx.util.tracker.get_consensus_tracker().get_all_relay_fingerprints(self.connection.remote_address)
+
+ if all_matches:
+ # multiple matches
+ lines[2] = 'Multiple matches, possible fingerprints are:'
+
+ for i in range(len(all_matches)):
+ is_last_line = i == 3
+
+ relay_port, relay_fingerprint = all_matches[i]
+ line_text = '%i. or port: %-5s fingerprint: %s' % (i + 1, relay_port, relay_fingerprint)
+
+ # if there's multiple lines remaining at the end then give a count
+
+ remaining_relays = len(all_matches) - i
+
+ if is_last_line and remaining_relays > 1:
+ line_text = '... %i more' % remaining_relays
+
+ lines[3 + i] = line_text
+
+ if is_last_line:
+ break
+ else:
+ # no consensus entry for this ip address
+ lines[2] = 'No consensus data found'
+
+ # crops any lines that are too long
+
+ for i in range(len(lines)):
+ lines[i] = str_tools.crop(lines[i], width - 2)
+
+ return lines
+
+ def get_destination_label(self, max_length, include_locale = False):
+ """
+ Provides a short description of the destination. This is made up of two
+ components, the base <ip addr>:<port> and an extra piece of information in
+ parentheses. The IP address is scrubbed from private connections.
+
+ Extra information is...
+ - the port's purpose for exit connections
+ - the locale, the address isn't private and isn't on the local network
+ - nothing otherwise
+
+ Arguments:
+ max_length - maximum length of the string returned
+ include_locale - possibly includes the locale
+ """
+
+ # destination of the connection
+
+ address_label = '<scrubbed>' if self._entry.is_private() else self.connection.remote_address
+ port_label = ':%s' % self.connection.remote_port
+ destination_address = address_label + port_label
+
+ # Only append the extra info if there's at least a couple characters of
+ # space (this is what's needed for the country codes).
+
+ if len(destination_address) + 5 <= max_length:
+ space_available = max_length - len(destination_address) - 3
+
+ if self._entry.get_type() == Category.EXIT:
+ purpose = connection.port_usage(self.connection.remote_port)
+
+ if purpose:
+ # BitTorrent is a common protocol to truncate, so just use "Torrent"
+ # if there's not enough room.
+
+ if len(purpose) > space_available and purpose == 'BitTorrent':
+ purpose = 'Torrent'
+
+ # crops with a hyphen if too long
+
+ purpose = str_tools.crop(purpose, space_available, ending = str_tools.Ending.HYPHEN)
+
+ destination_address += ' (%s)' % purpose
+ elif not connection.is_private_address(self.connection.remote_address):
+ extra_info = []
+
+ if include_locale and not tor_controller().is_geoip_unavailable():
+ foreign_locale = self.get_locale('??')
+ extra_info.append(foreign_locale)
+ space_available -= len(foreign_locale) + 2
+
+ if extra_info:
+ destination_address += ' (%s)' % ', '.join(extra_info)
+
+ return destination_address[:max_length]
+
+
+class CircHeaderLine(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, entry, circ):
+ if circ.status == 'BUILT':
+ self._remote_fingerprint = circ.path[-1][0]
+ exit_address, exit_port = nyx.util.tracker.get_consensus_tracker().get_relay_address(self._remote_fingerprint, ('192.168.0.1', 0))
+ self.is_built = True
+ else:
+ exit_address, exit_port = '0.0.0.0', 0
+ self.is_built = False
+ self._remote_fingerprint = None
+
+ ConnectionLine.__init__(self, entry, nyx.util.tracker.Connection(to_unix_time(circ.created), False, '127.0.0.1', 0, exit_address, exit_port, 'tcp'), include_port = False)
+ self.circuit = circ
+
+ def get_fingerprint(self, default = None):
+ return self._remote_fingerprint if self._remote_fingerprint else ConnectionLine.get_fingerprint(self, default)
+
+ def get_destination_label(self, max_length, include_locale = False):
+ if not self.is_built:
+ return 'Building...'
+
+ return ConnectionLine.get_destination_label(self, max_length, include_locale)
+
+ def get_etc_content(self, width, listing_type):
+ """
+ Attempts to provide all circuit related stats. Anything that can't be
+ shown completely (not enough room) is dropped.
+ """
+
+ etc_attr = ['Purpose: %s' % self.circuit.purpose.capitalize(), 'Circuit ID: %s' % self.circuit.id]
+
+ for i in range(len(etc_attr), -1, -1):
+ etc_label = ', '.join(etc_attr[:i])
+
+ if len(etc_label) <= width:
+ return ('%%-%is' % width) % etc_label
+
+ return ''
+
+ @lru_cache()
+ def get_details(self, width):
+ if not self.is_built:
+ detail_format = (curses.A_BOLD, CATEGORY_COLOR[self._entry.get_type()])
+ return [('Building Circuit...', detail_format)]
+ else:
+ return ConnectionLine.get_details(self, width)
+
+
+class CircLine(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, entry, circ, fingerprint):
+ relay_ip, relay_port = nyx.util.tracker.get_consensus_tracker().get_relay_address(fingerprint, ('192.168.0.1', 0))
+ ConnectionLine.__init__(self, entry, nyx.util.tracker.Connection(to_unix_time(circ.created), False, '127.0.0.1', 0, relay_ip, relay_port, 'tcp'), include_port = False)
+ self._fingerprint = fingerprint
+ self._is_last = False
+
+ circ_path = [path_entry[0] for path_entry in circ.path]
+ circ_index = circ_path.index(fingerprint)
+
+ if circ_index == len(circ_path) - 1:
+ placement_type = 'Exit' if circ.status == 'BUILT' else 'Extending'
+ self._is_last = True
+ elif circ_index == 0:
+ placement_type = 'Guard'
+ else:
+ placement_type = 'Middle'
+
+ self.placement_label = '%i / %s' % (circ_index + 1, placement_type)
+
+ def get_fingerprint(self, default = None):
+ self._fingerprint
+
+ def get_listing_prefix(self):
+ if self._is_last:
+ return (ord(' '), curses.ACS_LLCORNER, curses.ACS_HLINE, ord(' '))
+ else:
+ return (ord(' '), curses.ACS_VLINE, ord(' '), ord(' '))
+
+ def get_listing_entry(self, width, current_time, listing_type):
+ """
+ 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
+ current_time - the current unix time (ignored)
+ listing_type - primary attribute we're listing connections by
+ """
+
+ return self._get_listing_entry(width, listing_type)
+
+ @lru_cache()
+ def _get_listing_entry(self, width, listing_type):
+ line_format = nyx.util.ui_tools.get_color(CATEGORY_COLOR[self._entry.get_type()])
+
+ # The required widths are the sum of the following:
+ # initial space (1 character)
+ # bracketing (3 characters)
+ # placement_label (14 characters)
+ # gap between etc and placement label (5 characters)
+
+ baseline_space = 14 + 5
+
+ dst, etc = '', ''
+
+ if listing_type == Listing.IP_ADDRESS:
+ # dst width is derived as:
+ # src (21) + dst (26) + divider (7) + right gap (2) - bracket (3) = 53 char
+
+ dst = '%-53s' % self.get_destination_label(53, include_locale = True)
+
+ # fills the nickname into the empty space here
+
+ dst = '%s%-25s ' % (dst[:25], str_tools.crop(self.get_nickname('UNKNOWN'), 25, 0))
+
+ etc = self.get_etc_content(width - baseline_space - len(dst), listing_type)
+ elif listing_type == Listing.FINGERPRINT:
+ # dst width is derived as:
+ # src (9) + dst (40) + divider (7) + right gap (2) - bracket (3) = 55 char
+
+ dst = '%-55s' % self.get_fingerprint('UNKNOWN')
+ etc = self.get_etc_content(width - baseline_space - len(dst), listing_type)
+ else:
+ # min space for the nickname is 56 characters
+
+ etc = self.get_etc_content(width - baseline_space - 56, listing_type)
+ dst_layout = '%%-%is' % (width - baseline_space - len(etc))
+ dst = dst_layout % self.get_nickname('UNKNOWN')
+
+ return ((dst + etc, line_format),
+ (' ' * (width - baseline_space - len(dst) - len(etc) + 5), line_format),
+ ('%-14s' % self.placement_label, line_format))
+
+
class ConnectionPanel(panel.Panel, threading.Thread):
"""
Listing of connections tor is making, with information correlated against
@@ -280,10 +858,8 @@ class ConnectionPanel(panel.Panel, threading.Thread):
# mark the initially exitsing connection uptimes as being estimates
- from nyx.connections import conn_entry
-
for entry in self._entries:
- if isinstance(entry, conn_entry.ConnectionEntry):
+ if isinstance(entry, ConnectionEntry):
entry.get_lines()[0].is_initial_connection = True
# listens for when tor stops so we know to stop reflecting changes
@@ -471,7 +1047,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
color = CATEGORY_COLOR[selection.get_type()]
fingerprint = selection.get_fingerprint()
is_close_key = lambda key: key.is_selection() or key.match('d') or key.match('left') or key.match('right')
- key = descriptor_popup.show_descriptor_popup(fingerprint, color, self.max_x, is_close_key)
+ key = nyx.popups.show_descriptor_popup(fingerprint, color, self.max_x, is_close_key)
if not key or key.is_selection() or key.match('d'):
break # closes popup
diff --git a/nyx/connections/__init__.py b/nyx/connections/__init__.py
deleted file mode 100644
index 577823a..0000000
--- a/nyx/connections/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""
-Resources related to our connection panel.
-"""
-
-__all__ = [
- 'circ_entry',
- 'conn_entry',
- 'descriptor_popup',
- 'entries',
-]
diff --git a/nyx/connections/circ_entry.py b/nyx/connections/circ_entry.py
deleted file mode 100644
index 920d599..0000000
--- a/nyx/connections/circ_entry.py
+++ /dev/null
@@ -1,177 +0,0 @@
-"""
-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
-import datetime
-
-import nyx.util.tracker
-import nyx.util.ui_tools
-import nyx.connection_panel
-
-from nyx.connections import conn_entry
-
-from stem.util import str_tools
-
-try:
- # added in python 3.2
- from functools import lru_cache
-except ImportError:
- from stem.util.lru_cache import lru_cache
-
-
-def to_unix_time(dt):
- return (dt - datetime.datetime(1970, 1, 1)).total_seconds()
-
-
-class CircHeaderLine(conn_entry.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, entry, circ):
- if circ.status == 'BUILT':
- self._remote_fingerprint = circ.path[-1][0]
- exit_address, exit_port = nyx.util.tracker.get_consensus_tracker().get_relay_address(self._remote_fingerprint, ('192.168.0.1', 0))
- self.is_built = True
- else:
- exit_address, exit_port = '0.0.0.0', 0
- self.is_built = False
- self._remote_fingerprint = None
-
- conn_entry.ConnectionLine.__init__(self, entry, nyx.util.tracker.Connection(to_unix_time(circ.created), False, '127.0.0.1', 0, exit_address, exit_port, 'tcp'), include_port = False)
- self.circuit = circ
-
- def get_fingerprint(self, default = None):
- return self._remote_fingerprint if self._remote_fingerprint else conn_entry.ConnectionLine.get_fingerprint(self, default)
-
- def get_destination_label(self, max_length, include_locale = False):
- if not self.is_built:
- return 'Building...'
-
- return conn_entry.ConnectionLine.get_destination_label(self, max_length, include_locale)
-
- def get_etc_content(self, width, listing_type):
- """
- Attempts to provide all circuit related stats. Anything that can't be
- shown completely (not enough room) is dropped.
- """
-
- etc_attr = ['Purpose: %s' % self.circuit.purpose.capitalize(), 'Circuit ID: %s' % self.circuit.id]
-
- for i in range(len(etc_attr), -1, -1):
- etc_label = ', '.join(etc_attr[:i])
-
- if len(etc_label) <= width:
- return ('%%-%is' % width) % etc_label
-
- return ''
-
- @lru_cache()
- def get_details(self, width):
- if not self.is_built:
- detail_format = (curses.A_BOLD, nyx.connection_panel.CATEGORY_COLOR[self._entry.get_type()])
- return [('Building Circuit...', detail_format)]
- else:
- return conn_entry.ConnectionLine.get_details(self, width)
-
-
-class CircLine(conn_entry.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, entry, circ, fingerprint):
- relay_ip, relay_port = nyx.util.tracker.get_consensus_tracker().get_relay_address(fingerprint, ('192.168.0.1', 0))
- conn_entry.ConnectionLine.__init__(self, entry, nyx.util.tracker.Connection(to_unix_time(circ.created), False, '127.0.0.1', 0, relay_ip, relay_port, 'tcp'), include_port = False)
- self._fingerprint = fingerprint
- self._is_last = False
-
- circ_path = [path_entry[0] for path_entry in circ.path]
- circ_index = circ_path.index(fingerprint)
-
- if circ_index == len(circ_path) - 1:
- placement_type = 'Exit' if circ.status == 'BUILT' else 'Extending'
- self._is_last = True
- elif circ_index == 0:
- placement_type = 'Guard'
- else:
- placement_type = 'Middle'
-
- self.placement_label = '%i / %s' % (circ_index + 1, placement_type)
-
- def get_fingerprint(self, default = None):
- self._fingerprint
-
- def get_listing_prefix(self):
- if self._is_last:
- return (ord(' '), curses.ACS_LLCORNER, curses.ACS_HLINE, ord(' '))
- else:
- return (ord(' '), curses.ACS_VLINE, ord(' '), ord(' '))
-
- def get_listing_entry(self, width, current_time, listing_type):
- """
- 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
- current_time - the current unix time (ignored)
- listing_type - primary attribute we're listing connections by
- """
-
- return self._get_listing_entry(width, listing_type)
-
- @lru_cache()
- def _get_listing_entry(self, width, listing_type):
- line_format = nyx.util.ui_tools.get_color(nyx.connection_panel.CATEGORY_COLOR[self._entry.get_type()])
-
- # The required widths are the sum of the following:
- # initial space (1 character)
- # bracketing (3 characters)
- # placement_label (14 characters)
- # gap between etc and placement label (5 characters)
-
- baseline_space = 14 + 5
-
- dst, etc = '', ''
-
- if listing_type == nyx.connection_panel.Listing.IP_ADDRESS:
- # dst width is derived as:
- # src (21) + dst (26) + divider (7) + right gap (2) - bracket (3) = 53 char
-
- dst = '%-53s' % self.get_destination_label(53, include_locale = True)
-
- # fills the nickname into the empty space here
-
- dst = '%s%-25s ' % (dst[:25], str_tools.crop(self.get_nickname('UNKNOWN'), 25, 0))
-
- etc = self.get_etc_content(width - baseline_space - len(dst), listing_type)
- elif listing_type == nyx.connection_panel.Listing.FINGERPRINT:
- # dst width is derived as:
- # src (9) + dst (40) + divider (7) + right gap (2) - bracket (3) = 55 char
-
- dst = '%-55s' % self.get_fingerprint('UNKNOWN')
- etc = self.get_etc_content(width - baseline_space - len(dst), listing_type)
- else:
- # min space for the nickname is 56 characters
-
- etc = self.get_etc_content(width - baseline_space - 56, listing_type)
- dst_layout = '%%-%is' % (width - baseline_space - len(etc))
- dst = dst_layout % self.get_nickname('UNKNOWN')
-
- return ((dst + etc, line_format),
- (' ' * (width - baseline_space - len(dst) - len(etc) + 5), line_format),
- ('%-14s' % self.placement_label, line_format))
diff --git a/nyx/connections/conn_entry.py b/nyx/connections/conn_entry.py
deleted file mode 100644
index df36dd8..0000000
--- a/nyx/connections/conn_entry.py
+++ /dev/null
@@ -1,453 +0,0 @@
-"""
-Connection panel entries related to actual connections to or from the system
-(ie, results seen by netstat, lsof, etc).
-"""
-
-import curses
-
-import nyx.connection_panel
-import nyx.util.tracker
-import nyx.util.ui_tools
-
-from nyx.util import tor_controller
-from nyx.connection_panel import Category
-
-from stem.util import conf, connection, str_tools
-
-try:
- # added in python 3.2
- from functools import lru_cache
-except ImportError:
- from stem.util.lru_cache import lru_cache
-
-# static data for listing format
-# <src> --> <dst> <etc><padding>
-
-LABEL_FORMAT = '%s --> %s %s%s'
-LABEL_MIN_PADDING = 2 # min space between listing label and following data
-
-CONFIG = conf.config_dict('nyx', {
- 'features.connection.showIps': True,
-})
-
-
-class ConnectionLine(object):
- """
- Display component of the ConnectionEntry.
- """
-
- def __init__(self, entry, conn, include_port = True):
- self._entry = entry
- self.connection = conn
-
- # includes the port or expanded ip address field when displaying listing
- # information if true
-
- self.include_port = include_port
-
- def get_listing_prefix(self):
- """
- Provides a list of characters to be appended before the listing entry.
- """
-
- return ()
-
- def get_locale(self, default = None):
- """
- Provides the two letter country code for the remote endpoint.
- """
-
- return tor_controller().get_info('ip-to-country/%s' % self.connection.remote_address, default)
-
- def get_fingerprint(self, default = None):
- """
- Provides the fingerprint of this relay.
- """
-
- if self._entry.get_type() in (Category.OUTBOUND, Category.CIRCUIT, Category.DIRECTORY, Category.EXIT):
- my_fingerprint = nyx.util.tracker.get_consensus_tracker().get_relay_fingerprint(self.connection.remote_address, self.connection.remote_port)
- return my_fingerprint if my_fingerprint else default
- else:
- return default # inbound connections don't have an ORPort we can resolve
-
- def get_nickname(self, default = None):
- """
- Provides the nickname of this relay.
- """
-
- nickname = nyx.util.tracker.get_consensus_tracker().get_relay_nickname(self.get_fingerprint())
- return nickname if nickname else default
-
- def get_listing_entry(self, width, current_time, listing_type):
- """
- Provides the tuple list for this connection's listing. Lines are composed
- of the following components:
- <src> --> <dst> <etc> <uptime> (<type>)
-
- Listing.IP_ADDRESS:
- src - <internal addr:port> --> <external addr:port>
- dst - <destination addr:port>
- etc - <fingerprint> <nickname>
-
- Listing.FINGERPRINT:
- src - localhost
- dst - <destination fingerprint>
- etc - <nickname> <destination addr:port>
-
- Listing.NICKNAME:
- src - <source nickname>
- dst - <destination nickname>
- etc - <fingerprint> <destination addr:port>
-
- Arguments:
- width - maximum length of the line
- current_time - unix timestamp for what the results should consider to be
- the current time
- listing_type - primary attribute we're listing connections by
- """
-
- # fetch our (most likely cached) display entry for the listing
-
- my_listing = self._get_listing_entry(width, listing_type)
-
- # fill in the current uptime and return the results
-
- time_prefix = '+' if self.connection.is_legacy else ' '
-
- time_label = time_prefix + '%5s' % str_tools.time_label(current_time - self.connection.start_time, 1)
- my_listing[2] = (time_label, my_listing[2][1])
-
- return my_listing
-
- @lru_cache()
- def _get_listing_entry(self, width, listing_type):
- entry_type = self._entry.get_type()
-
- # Lines are split into the following components in reverse:
- # init gap - " "
- # content - "<src> --> <dst> <etc> "
- # time - "<uptime>"
- # preType - " ("
- # category - "<type>"
- # postType - ") "
-
- line_format = nyx.util.ui_tools.get_color(nyx.connection_panel.CATEGORY_COLOR[entry_type])
-
- draw_entry = [(' ', line_format),
- (self._get_listing_content(width - 19, listing_type), line_format),
- (' ', line_format),
- (' (', line_format),
- (entry_type.upper(), line_format | curses.A_BOLD),
- (')' + ' ' * (9 - len(entry_type)), line_format)]
-
- return draw_entry
-
- @lru_cache()
- def get_details(self, width):
- """
- Provides details on the connection, correlated against available consensus
- data.
-
- Arguments:
- width - available space to display in
- """
-
- detail_format = (curses.A_BOLD, nyx.connection_panel.CATEGORY_COLOR[self._entry.get_type()])
- return [(line, detail_format) for line in self._get_detail_content(width)]
-
- def get_etc_content(self, width, listing_type):
- """
- Provides the optional content for the connection.
-
- Arguments:
- width - maximum length of the line
- listing_type - primary attribute we're listing connections by
- """
-
- # for applications show the command/pid
-
- if self._entry.get_type() in (Category.SOCKS, Category.HIDDEN, Category.CONTROL):
- port = self.connection.local_port if self._entry.get_type() == Category.HIDDEN else self.connection.remote_port
-
- try:
- process = nyx.util.tracker.get_port_usage_tracker().fetch(port)
- display_label = '%s (%s)' % (process.name, process.pid) if process.pid else process.name
- except nyx.util.tracker.UnresolvedResult:
- display_label = 'resolving...'
- except nyx.util.tracker.UnknownApplication:
- display_label = 'UNKNOWN'
-
- if len(display_label) < width:
- return ('%%-%is' % width) % display_label
- else:
- return ''
-
- # for everything else display connection/consensus information
-
- destination_address = self.get_destination_label(26, include_locale = True)
- etc, used_space = '', 0
-
- if listing_type == nyx.connection_panel.Listing.IP_ADDRESS:
- if width > used_space + 42:
- # show fingerprint (column width: 42 characters)
-
- etc += '%-40s ' % self.get_fingerprint('UNKNOWN')
- used_space += 42
-
- if width > used_space + 10:
- # show nickname (column width: remainder)
-
- nickname_space = width - used_space
- nickname_label = str_tools.crop(self.get_nickname('UNKNOWN'), nickname_space, 0)
- etc += ('%%-%is ' % nickname_space) % nickname_label
- used_space += nickname_space + 2
- elif listing_type == nyx.connection_panel.Listing.FINGERPRINT:
- if width > used_space + 17:
- # show nickname (column width: min 17 characters, consumes any remaining space)
-
- nickname_space = width - used_space - 2
-
- # if there's room then also show a column with the destination
- # ip/port/locale (column width: 28 characters)
-
- is_locale_included = width > used_space + 45
-
- if is_locale_included:
- nickname_space -= 28
-
- nickname_label = str_tools.crop(self.get_nickname('UNKNOWN'), nickname_space, 0)
- etc += ('%%-%is ' % nickname_space) % nickname_label
- used_space += nickname_space + 2
-
- if is_locale_included:
- etc += '%-26s ' % destination_address
- used_space += 28
- else:
- if width > used_space + 42:
- # show fingerprint (column width: 42 characters)
- etc += '%-40s ' % self.get_fingerprint('UNKNOWN')
- used_space += 42
-
- if width > used_space + 28:
- # show destination ip/port/locale (column width: 28 characters)
- etc += '%-26s ' % destination_address
- used_space += 28
-
- return ('%%-%is' % width) % etc
-
- def _get_listing_content(self, width, listing_type):
- """
- Provides the source, destination, and extra info for our listing.
-
- Arguments:
- width - maximum length of the line
- listing_type - primary attribute we're listing connections by
- """
-
- controller = tor_controller()
- my_type = self._entry.get_type()
- destination_address = self.get_destination_label(26, include_locale = True)
-
- # The required widths are the sum of the following:
- # - room for LABEL_FORMAT and LABEL_MIN_PADDING (11 characters)
- # - base data for the listing
- # - that extra field plus any previous
-
- used_space = len(LABEL_FORMAT % tuple([''] * 4)) + LABEL_MIN_PADDING
- local_port = ':%s' % self.connection.local_port if self.include_port else ''
-
- src, dst, etc = '', '', ''
-
- if listing_type == nyx.connection_panel.Listing.IP_ADDRESS:
- my_external_address = controller.get_info('address', self.connection.local_address)
-
- # Show our external address if it's going through tor.
-
- if my_type not in (Category.SOCKS, Category.HIDDEN, Category.CONTROL):
- src_address = my_external_address + local_port
- else:
- src_address = self.connection.local_address + local_port
-
- if my_type in (Category.SOCKS, Category.CONTROL):
- # Like inbound connections these need their source and destination to
- # be swapped. However, this only applies when listing by IP (their
- # fingerprint and nickname are both for us). Reversing the fields here
- # to keep the same column alignments.
-
- src = '%-21s' % destination_address
- dst = '%-26s' % src_address
- else:
- src = '%-21s' % src_address # ip:port = max of 21 characters
- dst = '%-26s' % destination_address # ip:port (xx) = max of 26 characters
-
- used_space += len(src) + len(dst) # base data requires 47 characters
-
- etc = self.get_etc_content(width - used_space, listing_type)
- used_space += len(etc)
- elif listing_type == nyx.connection_panel.Listing.FINGERPRINT:
- src = 'localhost'
- dst = '%-40s' % ('localhost' if my_type == Category.CONTROL else self.get_fingerprint('UNKNOWN'))
-
- used_space += len(src) + len(dst) # base data requires 49 characters
-
- etc = self.get_etc_content(width - used_space, listing_type)
- used_space += len(etc)
- else:
- # base data requires 50 min characters
- src = controller.get_conf('nickname', 'UNKNOWN')
- dst = controller.get_conf('nickname', 'UNKNOWN') if my_type == Category.CONTROL else self.get_nickname('UNKNOWN')
-
- min_base_space = 50
-
- etc = self.get_etc_content(width - used_space - min_base_space, listing_type)
- used_space += len(etc)
-
- base_space = width - used_space
- used_space = width # prevents padding at the end
-
- if len(src) + len(dst) > base_space:
- src = str_tools.crop(src, base_space / 3)
- dst = str_tools.crop(dst, base_space - len(src))
-
- # pads dst entry to its max space
-
- dst = ('%%-%is' % (base_space - len(src))) % dst
-
- if my_type == Category.INBOUND:
- src, dst = dst, src
-
- padding = ' ' * (width - used_space + LABEL_MIN_PADDING)
-
- return LABEL_FORMAT % (src, dst, etc, padding)
-
- def _get_detail_content(self, width):
- """
- Provides a list with detailed information for this connection.
-
- Arguments:
- width - max length of lines
- """
-
- lines = [''] * 7
- lines[0] = 'address: %s' % self.get_destination_label(width - 11)
- lines[1] = 'locale: %s' % ('??' if self._entry.is_private() else self.get_locale('??'))
-
- # Remaining data concerns the consensus results, with three possible cases:
- # - if there's a single match then display its details
- # - if there's multiple potential relays then list all of the combinations
- # of ORPorts / Fingerprints
- # - if no consensus data is available then say so (probably a client or
- # exit connection)
-
- fingerprint = self.get_fingerprint()
- controller = tor_controller()
-
- if fingerprint:
- lines[1] = '%-13sfingerprint: %s' % (lines[1], fingerprint) # append fingerprint to second line
-
- router_status_entry = controller.get_network_status(fingerprint, None)
- server_descriptor = controller.get_server_descriptor(fingerprint, None)
-
- if router_status_entry:
- dir_port_label = 'dirport: %s' % router_status_entry.dir_port if router_status_entry.dir_port else ''
- lines[2] = 'nickname: %-25s orport: %-10s %s' % (router_status_entry.nickname, router_status_entry.or_port, dir_port_label)
- lines[3] = 'published: %s' % router_status_entry.published.strftime("%H:%M %m/%d/%Y")
- lines[4] = 'flags: %s' % ', '.join(router_status_entry.flags)
-
- if server_descriptor:
- policy_label = server_descriptor.exit_policy.summary() if server_descriptor.exit_policy else 'unknown'
- lines[5] = 'exit policy: %s' % policy_label
- lines[3] = '%-35s os: %-14s version: %s' % (lines[3], server_descriptor.operating_system, server_descriptor.tor_version)
-
- if server_descriptor.contact:
- lines[6] = 'contact: %s' % server_descriptor.contact
- else:
- all_matches = nyx.util.tracker.get_consensus_tracker().get_all_relay_fingerprints(self.connection.remote_address)
-
- if all_matches:
- # multiple matches
- lines[2] = 'Multiple matches, possible fingerprints are:'
-
- for i in range(len(all_matches)):
- is_last_line = i == 3
-
- relay_port, relay_fingerprint = all_matches[i]
- line_text = '%i. or port: %-5s fingerprint: %s' % (i + 1, relay_port, relay_fingerprint)
-
- # if there's multiple lines remaining at the end then give a count
-
- remaining_relays = len(all_matches) - i
-
- if is_last_line and remaining_relays > 1:
- line_text = '... %i more' % remaining_relays
-
- lines[3 + i] = line_text
-
- if is_last_line:
- break
- else:
- # no consensus entry for this ip address
- lines[2] = 'No consensus data found'
-
- # crops any lines that are too long
-
- for i in range(len(lines)):
- lines[i] = str_tools.crop(lines[i], width - 2)
-
- return lines
-
- def get_destination_label(self, max_length, include_locale = False):
- """
- Provides a short description of the destination. This is made up of two
- components, the base <ip addr>:<port> and an extra piece of information in
- parentheses. The IP address is scrubbed from private connections.
-
- Extra information is...
- - the port's purpose for exit connections
- - the locale, the address isn't private and isn't on the local network
- - nothing otherwise
-
- Arguments:
- max_length - maximum length of the string returned
- include_locale - possibly includes the locale
- """
-
- # destination of the connection
-
- address_label = '<scrubbed>' if self._entry.is_private() else self.connection.remote_address
- port_label = ':%s' % self.connection.remote_port
- destination_address = address_label + port_label
-
- # Only append the extra info if there's at least a couple characters of
- # space (this is what's needed for the country codes).
-
- if len(destination_address) + 5 <= max_length:
- space_available = max_length - len(destination_address) - 3
-
- if self._entry.get_type() == Category.EXIT:
- purpose = connection.port_usage(self.connection.remote_port)
-
- if purpose:
- # BitTorrent is a common protocol to truncate, so just use "Torrent"
- # if there's not enough room.
-
- if len(purpose) > space_available and purpose == 'BitTorrent':
- purpose = 'Torrent'
-
- # crops with a hyphen if too long
-
- purpose = str_tools.crop(purpose, space_available, ending = str_tools.Ending.HYPHEN)
-
- destination_address += ' (%s)' % purpose
- elif not connection.is_private_address(self.connection.remote_address):
- extra_info = []
-
- if include_locale and not tor_controller().is_geoip_unavailable():
- foreign_locale = self.get_locale('??')
- extra_info.append(foreign_locale)
- space_available -= len(foreign_locale) + 2
-
- if extra_info:
- destination_address += ' (%s)' % ', '.join(extra_info)
-
- return destination_address[:max_length]
diff --git a/nyx/connections/descriptor_popup.py b/nyx/connections/descriptor_popup.py
deleted file mode 100644
index 358784c..0000000
--- a/nyx/connections/descriptor_popup.py
+++ /dev/null
@@ -1,174 +0,0 @@
-"""
-Popup providing the raw descriptor and consensus information for a relay.
-"""
-
-import math
-import curses
-
-import nyx.popups
-
-from nyx.util import tor_controller, ui_tools
-
-from stem.util import str_tools
-
-HEADERS = ['Consensus:', 'Microdescriptor:', 'Server Descriptor:']
-HEADER_COLOR = 'cyan'
-LINE_NUMBER_COLOR = 'yellow'
-
-BLOCK_START, BLOCK_END = '-----BEGIN ', '-----END '
-
-UNRESOLVED_MSG = 'No consensus data available'
-ERROR_MSG = 'Unable to retrieve data'
-
-
-def show_descriptor_popup(fingerprint, color, max_width, is_close_key):
- """
- Provides a dialog showing the descriptors for a given relay.
-
- :param str fingerprint: fingerprint of the relay to be shown
- :param str color: text color of the dialog
- :param int max_width: maximum width of the dialog
- :param function is_close_key: method to indicate if a key should close the
- dialog or not
-
- :returns: :class:`~nyx.util.panel.KeyInput` for the keyboard input that
- closed the dialog
- """
-
- if fingerprint:
- title = 'Consensus Descriptor:'
- lines = _display_text(fingerprint)
- show_line_numbers = True
- else:
- title = 'Consensus Descriptor (%s):' % fingerprint
- lines = [UNRESOLVED_MSG]
- show_line_numbers = False
-
- popup_height, popup_width = _preferred_size(lines, max_width, show_line_numbers)
-
- with nyx.popups.popup_window(popup_height, popup_width) as (popup, _, height):
- if not popup:
- return None
-
- scroll, redraw = 0, True
-
- while True:
- if redraw:
- _draw(popup, title, lines, color, scroll, show_line_numbers)
- redraw = False
-
- key = nyx.controller.get_controller().key_input()
-
- if key.is_scroll():
- new_scroll = ui_tools.get_scroll_position(key, scroll, height - 2, len(lines))
-
- if scroll != new_scroll:
- scroll, redraw = new_scroll, True
- elif is_close_key(key):
- return key
-
-
-def _display_text(fingerprint):
- """
- Provides the descriptors for a relay.
-
- :param str fingerprint: relay fingerprint to be looked up
-
- :returns: **list** with the lines that should be displayed in the dialog
- """
-
- controller = tor_controller()
- router_status_entry = controller.get_network_status(fingerprint, None)
- microdescriptor = controller.get_microdescriptor(fingerprint, None)
- server_descriptor = controller.get_server_descriptor(fingerprint, None)
-
- description = 'Consensus:\n\n%s' % (router_status_entry if router_status_entry else ERROR_MSG)
-
- if server_descriptor:
- description += '\n\nServer Descriptor:\n\n%s' % server_descriptor
-
- if microdescriptor:
- description += '\n\nMicrodescriptor:\n\n%s' % microdescriptor
-
- return description.split('\n')
-
-
-def _preferred_size(text, max_width, show_line_numbers):
- """
- Provides the preferred dimensions of our dialog.
-
- :param list text: lines of text to be shown
- :param int max_width: maximum width the dialog can be
- :param bool show_line_numbers: if we should leave room for line numbers
-
- :returns: **tuple** of the preferred (height, width)
- """
-
- width, height = 0, len(text) + 2
- line_number_width = int(math.log10(len(text))) + 2 if show_line_numbers else 0
- max_content_width = max_width - line_number_width - 4
-
- for line in text:
- width = min(max_width, max(width, len(line) + line_number_width + 4))
- height += len(line) / max_content_width # extra lines due to text wrap
-
- return (height, width)
-
-
-def _draw(popup, title, lines, entry_color, scroll, show_line_numbers):
- def draw_msg(popup, min_x, x, y, width, msg, *attr):
- while msg:
- draw_msg, msg = str_tools.crop(msg, width - x, None, ending = None, get_remainder = True)
-
- if not draw_msg:
- draw_msg, msg = str_tools.crop(msg, width - x), '' # first word is longer than the line
-
- x = popup.addstr(y, x, draw_msg, *attr)
-
- if msg:
- x, y = min_x, y + 1
-
- return x, y
-
- popup.win.erase()
-
- line_number_width = int(math.log10(len(lines))) + 1
- in_block = False # flag indicating if we're currently in crypto content
- width = popup.max_x - 2 # leave space on the right for the border and an empty line
- height = popup.max_y - 2 # height of the dialog without the top and bottom border
- offset = line_number_width + 3 if show_line_numbers else 2
-
- y = 1
-
- for i, line in enumerate(lines):
- keyword, value = line, ''
- color = entry_color
-
- if line in HEADERS:
- color = HEADER_COLOR
- elif line.startswith(BLOCK_START):
- in_block = True
- elif line.startswith(BLOCK_END):
- in_block = False
- elif in_block:
- keyword, value = '', line
- elif ' ' in line and line != UNRESOLVED_MSG and line != ERROR_MSG:
- keyword, value = line.split(' ', 1)
-
- if i < scroll:
- continue
-
- if show_line_numbers:
- popup.addstr(y, 2, str(i + 1).rjust(line_number_width), curses.A_BOLD, LINE_NUMBER_COLOR)
-
- x, y = draw_msg(popup, offset, offset, y, width, keyword, color, curses.A_BOLD)
- x, y = draw_msg(popup, offset, x + 1, y, width, value, color)
-
- y += 1
-
- if y > height:
- break
-
- popup.win.box()
- popup.addstr(0, 0, title, curses.A_STANDOUT)
- popup.win.refresh()
diff --git a/nyx/popups.py b/nyx/popups.py
index 351467f..d2d5ded 100644
--- a/nyx/popups.py
+++ b/nyx/popups.py
@@ -2,16 +2,28 @@
Functions for displaying popups in the interface.
"""
+import math
import curses
import operator
import nyx.controller
from nyx import __version__, __release_date__
-from nyx.util import panel, ui_tools
+from nyx.util import tor_controller, panel, ui_tools
+
+from stem.util import str_tools
NO_STATS_MSG = "Usage stats aren't available yet, press any key..."
+HEADERS = ['Consensus:', 'Microdescriptor:', 'Server Descriptor:']
+HEADER_COLOR = 'cyan'
+LINE_NUMBER_COLOR = 'yellow'
+
+BLOCK_START, BLOCK_END = '-----BEGIN ', '-----END '
+
+UNRESOLVED_MSG = 'No consensus data available'
+ERROR_MSG = 'Unable to retrieve data'
+
def popup_window(height = -1, width = -1, top = 0, left = 0, below_static = True):
"""
@@ -418,3 +430,156 @@ def show_menu(title, options, old_selection):
top_panel.set_title_visible(True)
return selection
+
+
+def show_descriptor_popup(fingerprint, color, max_width, is_close_key):
+ """
+ Provides a dialog showing the descriptors for a given relay.
+
+ :param str fingerprint: fingerprint of the relay to be shown
+ :param str color: text color of the dialog
+ :param int max_width: maximum width of the dialog
+ :param function is_close_key: method to indicate if a key should close the
+ dialog or not
+
+ :returns: :class:`~nyx.util.panel.KeyInput` for the keyboard input that
+ closed the dialog
+ """
+
+ if fingerprint:
+ title = 'Consensus Descriptor:'
+ lines = _display_text(fingerprint)
+ show_line_numbers = True
+ else:
+ title = 'Consensus Descriptor (%s):' % fingerprint
+ lines = [UNRESOLVED_MSG]
+ show_line_numbers = False
+
+ popup_height, popup_width = _preferred_size(lines, max_width, show_line_numbers)
+
+ with popup_window(popup_height, popup_width) as (popup, _, height):
+ if not popup:
+ return None
+
+ scroll, redraw = 0, True
+
+ while True:
+ if redraw:
+ _draw(popup, title, lines, color, scroll, show_line_numbers)
+ redraw = False
+
+ key = nyx.controller.get_controller().key_input()
+
+ if key.is_scroll():
+ new_scroll = ui_tools.get_scroll_position(key, scroll, height - 2, len(lines))
+
+ if scroll != new_scroll:
+ scroll, redraw = new_scroll, True
+ elif is_close_key(key):
+ return key
+
+
+def _display_text(fingerprint):
+ """
+ Provides the descriptors for a relay.
+
+ :param str fingerprint: relay fingerprint to be looked up
+
+ :returns: **list** with the lines that should be displayed in the dialog
+ """
+
+ controller = tor_controller()
+ router_status_entry = controller.get_network_status(fingerprint, None)
+ microdescriptor = controller.get_microdescriptor(fingerprint, None)
+ server_descriptor = controller.get_server_descriptor(fingerprint, None)
+
+ description = 'Consensus:\n\n%s' % (router_status_entry if router_status_entry else ERROR_MSG)
+
+ if server_descriptor:
+ description += '\n\nServer Descriptor:\n\n%s' % server_descriptor
+
+ if microdescriptor:
+ description += '\n\nMicrodescriptor:\n\n%s' % microdescriptor
+
+ return description.split('\n')
+
+
+def _preferred_size(text, max_width, show_line_numbers):
+ """
+ Provides the preferred dimensions of our dialog.
+
+ :param list text: lines of text to be shown
+ :param int max_width: maximum width the dialog can be
+ :param bool show_line_numbers: if we should leave room for line numbers
+
+ :returns: **tuple** of the preferred (height, width)
+ """
+
+ width, height = 0, len(text) + 2
+ line_number_width = int(math.log10(len(text))) + 2 if show_line_numbers else 0
+ max_content_width = max_width - line_number_width - 4
+
+ for line in text:
+ width = min(max_width, max(width, len(line) + line_number_width + 4))
+ height += len(line) / max_content_width # extra lines due to text wrap
+
+ return (height, width)
+
+
+def _draw(popup, title, lines, entry_color, scroll, show_line_numbers):
+ def draw_msg(popup, min_x, x, y, width, msg, *attr):
+ while msg:
+ draw_msg, msg = str_tools.crop(msg, width - x, None, ending = None, get_remainder = True)
+
+ if not draw_msg:
+ draw_msg, msg = str_tools.crop(msg, width - x), '' # first word is longer than the line
+
+ x = popup.addstr(y, x, draw_msg, *attr)
+
+ if msg:
+ x, y = min_x, y + 1
+
+ return x, y
+
+ popup.win.erase()
+
+ line_number_width = int(math.log10(len(lines))) + 1
+ in_block = False # flag indicating if we're currently in crypto content
+ width = popup.max_x - 2 # leave space on the right for the border and an empty line
+ height = popup.max_y - 2 # height of the dialog without the top and bottom border
+ offset = line_number_width + 3 if show_line_numbers else 2
+
+ y = 1
+
+ for i, line in enumerate(lines):
+ keyword, value = line, ''
+ color = entry_color
+
+ if line in HEADERS:
+ color = HEADER_COLOR
+ elif line.startswith(BLOCK_START):
+ in_block = True
+ elif line.startswith(BLOCK_END):
+ in_block = False
+ elif in_block:
+ keyword, value = '', line
+ elif ' ' in line and line != UNRESOLVED_MSG and line != ERROR_MSG:
+ keyword, value = line.split(' ', 1)
+
+ if i < scroll:
+ continue
+
+ if show_line_numbers:
+ popup.addstr(y, 2, str(i + 1).rjust(line_number_width), curses.A_BOLD, LINE_NUMBER_COLOR)
+
+ x, y = draw_msg(popup, offset, offset, y, width, keyword, color, curses.A_BOLD)
+ x, y = draw_msg(popup, offset, x + 1, y, width, value, color)
+
+ y += 1
+
+ if y > height:
+ break
+
+ popup.win.box()
+ popup.addstr(0, 0, title, curses.A_STANDOUT)
+ popup.win.refresh()
diff --git a/setup.py b/setup.py
index f5399ef..f5b1c05 100644
--- a/setup.py
+++ b/setup.py
@@ -99,7 +99,7 @@ setup(
author = nyx.__author__,
author_email = nyx.__contact__,
url = nyx.__url__,
- packages = ['nyx', 'nyx.connections', 'nyx.menu', 'nyx.util'],
+ packages = ['nyx', 'nyx.menu', 'nyx.util'],
keywords = 'tor onion controller',
install_requires = ['stem>=1.4.1'],
package_data = {'nyx': ['config/*', 'resources/*']},
1
0
commit 22a5dc2abcac959a29a351c276f576cd027446a6
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sun Jul 26 12:14:31 2015 -0700
Expanded SocksPort caused a stacktrace
There might be other spots too, but when tor added additional field to the
SocksPort torrc entry we broke due to expecting an integer.
---
nyx/connections/conn_entry.py | 22 ++++++++--------------
1 file changed, 8 insertions(+), 14 deletions(-)
diff --git a/nyx/connections/conn_entry.py b/nyx/connections/conn_entry.py
index 42466d9..d945182 100644
--- a/nyx/connections/conn_entry.py
+++ b/nyx/connections/conn_entry.py
@@ -11,6 +11,7 @@ import nyx.util.ui_tools
from nyx.util import tor_controller
from nyx.connections import entries
+from stem.control import Listener
from stem.util import conf, connection, enum, str_tools
# Connection Categories:
@@ -82,10 +83,10 @@ class ConnectionLine(entries.ConnectionPanelLine):
self.application_pid = None
self.is_application_resolving = False
- my_or_port = controller.get_conf('ORPort', None)
- my_dir_port = controller.get_conf('DirPort', None)
- my_socks_port = controller.get_conf('SocksPort', '9050')
- my_ctl_port = controller.get_conf('ControlPort', None)
+ socks_ports = controller.get_ports(Listener.SOCKS, [])
+ or_ports = controller.get_ports(Listener.OR, [])
+ dir_ports = controller.get_ports(Listener.DIR, [])
+ control_ports = controller.get_ports(Listener.CONTROL, [])
# get all target ports in our hidden service configuation
@@ -94,20 +95,13 @@ class ConnectionLine(entries.ConnectionPanelLine):
for hs_config in controller.get_hidden_service_conf({}).values():
my_hidden_service_ports += [entry[2] for entry in hs_config['HiddenServicePort']]
- # the ORListenAddress can overwrite the ORPort
-
- listen_addr = controller.get_conf('ORListenAddress', None)
-
- if listen_addr and ':' in listen_addr:
- my_or_port = listen_addr[listen_addr.find(':') + 1:]
-
- if conn.local_port in (my_or_port, my_dir_port):
+ if conn.local_port in or_ports or conn.local_port in dir_ports:
self.base_type = Category.INBOUND
- elif conn.local_port == my_socks_port:
+ elif conn.local_port in socks_ports:
self.base_type = Category.SOCKS
elif conn.remote_port in my_hidden_service_ports:
self.base_type = Category.HIDDEN
- elif conn.local_port == my_ctl_port:
+ elif conn.local_port in control_ports:
self.base_type = Category.CONTROL
else:
self.base_type = Category.OUTBOUND
1
0
commit e65ce74cecea61bc2ee7683bbc8b82d1674c561b
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jul 25 13:33:11 2015 -0700
Simplify connection panel title
Premature optimization is the root of all evil. There's no value in
pre-calculating the title in our update loop. Counting a few connections
is damn cheap.
---
nyx/connections/conn_panel.py | 44 +++++++++++++----------------------------
1 file changed, 14 insertions(+), 30 deletions(-)
diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py
index 647a369..26d836d 100644
--- a/nyx/connections/conn_panel.py
+++ b/nyx/connections/conn_panel.py
@@ -4,6 +4,7 @@ Listing of the currently established connections tor has made.
import re
import time
+import collections
import curses
import threading
@@ -68,7 +69,6 @@ class ConnectionPanel(panel.Panel, threading.Thread):
nyx_config.set('features.connection.listing_type', Listing.keys()[Listing.index_of(Listing.FINGERPRINT)])
self._scroller = ui_tools.Scroller(True)
- self._title = 'Connections:' # title line of the panel
self._entries = [] # last fetched display entries
self._entry_lines = [] # individual lines rendered from the entries listing
self._show_details = False # presents the details panel if true
@@ -401,8 +401,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
# title label with connection counts
if self.is_title_visible():
- title = 'Connection Details:' if self._show_details else self._title
- self.addstr(0, 0, title, curses.A_STANDOUT)
+ self._draw_title(self._entries)
scroll_offset = 0
@@ -440,6 +439,18 @@ class ConnectionPanel(panel.Panel, threading.Thread):
if draw_line >= height:
break
+ def _draw_title(self, entries):
+ if self._show_details:
+ title = 'Connection Details:'
+ elif not entries:
+ title = 'Connections:'
+ else:
+ counts = collections.Counter([entry.get_lines()[0].get_type() for entry in entries])
+ count_labels = ['%i %s' % (counts[category], category.lower()) for category in conn_entry.Category if counts[category]]
+ title = 'Connections (%s):' % ', '.join(count_labels)
+
+ self.addstr(0, 0, title, curses.A_STANDOUT)
+
def stop(self):
"""
Halts further resolutions and terminates the thread.
@@ -485,33 +496,6 @@ class ConnectionPanel(panel.Panel, threading.Thread):
exit_port = entry_line.foreign.get_port()
self._exit_port_usage[exit_port] = self._exit_port_usage.get(exit_port, 0) + 1
- # Counts the relays in each of the categories. This also flushes the
- # type cache for all of the connections (in case its changed since last
- # fetched).
-
- category_types = list(conn_entry.Category)
- type_counts = dict((type, 0) for type in category_types)
-
- for entry in new_entries:
- if isinstance(entry, conn_entry.ConnectionEntry):
- type_counts[entry.get_lines()[0].get_type()] += 1
- elif isinstance(entry, circ_entry.CircEntry):
- type_counts[conn_entry.Category.CIRCUIT] += 1
-
- # makes labels for all the categories with connections (ie,
- # "21 outbound", "1 control", etc)
-
- count_labels = []
-
- for category in category_types:
- if type_counts[category] > 0:
- count_labels.append('%i %s' % (type_counts[category], category.lower()))
-
- if count_labels:
- self._title = 'Connections (%s):' % ', '.join(count_labels)
- else:
- self._title = 'Connections:'
-
self._entries = new_entries
self._entry_lines = []
1
0

22 Sep '15
commit f240710412e3c5df157ab264a3f5e7c557eda452
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jul 25 13:40:39 2015 -0700
Use itertools to enumerate connection lines
Simple one-liner to get a flattened list of our connection lines, rather than
loops.
---
nyx/connections/conn_panel.py | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)
diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py
index 26d836d..695c32e 100644
--- a/nyx/connections/conn_panel.py
+++ b/nyx/connections/conn_panel.py
@@ -6,6 +6,7 @@ import re
import time
import collections
import curses
+import itertools
import threading
import nyx.popups
@@ -162,11 +163,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
nyx_config.set('features.connection.order', ', '.join(ordering_keys))
self._entries.sort(key = lambda i: (i.get_sort_values(CONFIG['features.connection.order'], self.get_listing_type())))
-
- self._entry_lines = []
-
- for entry in self._entries:
- self._entry_lines += entry.get_lines()
+ self._entry_lines = list(itertools.chain.from_iterable([entry.get_lines() for entry in self._entries]))
def get_listing_type(self):
"""
@@ -496,12 +493,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
exit_port = entry_line.foreign.get_port()
self._exit_port_usage[exit_port] = self._exit_port_usage.get(exit_port, 0) + 1
- self._entries = new_entries
-
- self._entry_lines = []
-
- for entry in self._entries:
- self._entry_lines += entry.get_lines()
+ self._entries, self._entry_lines = new_entries, list(itertools.chain.from_iterable([entry.get_lines() for entry in new_entries]))
self.set_sort_order()
self._last_resource_fetch = current_resolution_count
1
0

22 Sep '15
commit ec33ca2aa9345c83736b229b8440ca83cb80e30b
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jul 25 19:36:57 2015 -0700
Drop address and port from Endpoint class
Arm had an Endpoint to encapsulate connection information. Great! But Stem
gives us a Connection struct so... yeah. Lets use that.
---
nyx/connections/conn_entry.py | 54 +++++++++++++++--------------------------
nyx/connections/conn_panel.py | 7 +++---
nyx/connections/entries.py | 2 +-
3 files changed, 24 insertions(+), 39 deletions(-)
diff --git a/nyx/connections/conn_entry.py b/nyx/connections/conn_entry.py
index ec1779e..d0ff8dc 100644
--- a/nyx/connections/conn_entry.py
+++ b/nyx/connections/conn_entry.py
@@ -59,34 +59,20 @@ class Endpoint:
"""
def __init__(self, address, port):
- self.address = address
- self.port = port
+ self._address = address
+ self._port = port
self.is_or_port = False # if set, consider the port to possibly be an ORPort
# if set then this overwrites fingerprint lookups
self.fingerprint_overwrite = None
- def get_address(self):
- """
- Provides the address of the endpoint.
- """
-
- return self.address
-
- def get_port(self):
- """
- Provides the port of the endpoint.
- """
-
- return self.port
-
def get_locale(self, default = None):
"""
Provides the two letter country code of this relay.
"""
- return tor_controller().get_info('ip-to-country/%s' % self.address, default)
+ return tor_controller().get_info('ip-to-country/%s' % self._address, default)
def get_fingerprint(self, default = None):
"""
@@ -96,7 +82,7 @@ class Endpoint:
if self.fingerprint_overwrite:
return self.fingerprint_overwrite
- my_fingerprint = nyx.util.tracker.get_consensus_tracker().get_relay_fingerprint(self.address, self.port if self.is_or_port else None)
+ my_fingerprint = nyx.util.tracker.get_consensus_tracker().get_relay_fingerprint(self._address, self._port if self.is_or_port else None)
return my_fingerprint if my_fingerprint else default
def get_nickname(self, default = None):
@@ -191,12 +177,12 @@ class ConnectionLine(entries.ConnectionPanelLine):
ip_value = 0
- for comp in self.foreign.get_address().split('.'):
+ for comp in self.connection.remote_address.split('.'):
ip_value *= 255
ip_value += int(comp)
self.sort_address = ip_value
- self.sort_port = int(self.foreign.get_port())
+ self.sort_port = self.connection.remote_port
def get_listing_entry(self, width, current_time, listing_type):
"""
@@ -307,7 +293,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
controller = tor_controller()
if controller.is_user_traffic_allowed().inbound:
- all_matches = nyx.util.tracker.get_consensus_tracker().get_all_relay_fingerprints(self.foreign.get_address())
+ all_matches = nyx.util.tracker.get_consensus_tracker().get_all_relay_fingerprints(self.connection.remote_address)
return all_matches == []
elif my_type == Category.EXIT:
# DNS connections exiting us aren't private (since they're hitting our
@@ -318,7 +304,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
# will take a bit more work to propagate the information up from the
# connection resolver.
- return self.foreign.get_port() != '53'
+ return self.connection.remote_port != 53
# for everything else this isn't a concern
@@ -351,9 +337,9 @@ class ConnectionLine(entries.ConnectionPanelLine):
# Not a known relay. This might be an exit connection.
exit_policy = controller.get_exit_policy(None)
- port = self.foreign.get_port() if self.foreign.get_port() else None
+ port = self.connection.remote_port if self.connection.remote_port else None
- if exit_policy and exit_policy.can_exit_to(self.foreign.get_address(), port):
+ if exit_policy and exit_policy.can_exit_to(self.connection.remote_address, port):
self.cached_type = Category.EXIT
elif self._possible_client or self._possible_directory:
# This belongs to a known relay. If we haven't eliminated ourselves as
@@ -495,13 +481,13 @@ class ConnectionLine(entries.ConnectionPanelLine):
# - that extra field plus any previous
used_space = len(LABEL_FORMAT % tuple([''] * 4)) + LABEL_MIN_PADDING
- local_port = ':%s' % self.local.get_port() if self.include_port else ''
+ local_port = ':%s' % self.connection.local_port if self.include_port else ''
src, dst, etc = '', '', ''
if listing_type == entries.ListingType.IP_ADDRESS:
- my_external_address = controller.get_info('address', self.local.get_address())
- address_differ = my_external_address != self.local.get_address()
+ my_external_address = controller.get_info('address', self.connection.local_address)
+ address_differ = my_external_address != self.connection.local_address
# Expanding doesn't make sense, if the connection isn't actually
# going through Tor's external IP address. As there isn't a known
@@ -516,7 +502,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
if is_expansion_type:
src_address = my_external_address + local_port
else:
- src_address = self.local.get_address() + local_port
+ src_address = self.connection.local_address + local_port
if my_type in (Category.SOCKS, Category.CONTROL):
# Like inbound connections these need their source and destination to
@@ -544,7 +530,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
if address_differ and is_expansion_type and is_expanded_address_visible and self.include_expanded_addresses and CONFIG['features.connection.showColumn.expandedIp']:
# include the internal address in the src (extra 28 characters)
- internal_address = self.local.get_address() + local_port
+ internal_address = self.connection.local_address + local_port
# If this is an inbound connection then reverse ordering so it's:
# <foreign> --> <external> --> <internal>
@@ -705,7 +691,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
if contact:
lines[6] = 'contact: %s' % contact
else:
- all_matches = nyx.util.tracker.get_consensus_tracker().get_all_relay_fingerprints(self.foreign.get_address())
+ all_matches = nyx.util.tracker.get_consensus_tracker().get_all_relay_fingerprints(self.connection.remote_address)
if all_matches:
# multiple matches
@@ -761,8 +747,8 @@ class ConnectionLine(entries.ConnectionPanelLine):
# destination of the connection
- address_label = '<scrubbed>' if self.is_private() else self.foreign.get_address()
- port_label = ':%s' % self.foreign.get_port() if include_port else ''
+ address_label = '<scrubbed>' if self.is_private() else self.connection.remote_address
+ port_label = ':%s' % self.connection.remote_port if include_port else ''
destination_address = address_label + port_label
# Only append the extra info if there's at least a couple characters of
@@ -772,7 +758,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
space_available = max_length - len(destination_address) - 3
if self.get_type() == Category.EXIT and include_port:
- purpose = connection.port_usage(self.foreign.get_port())
+ purpose = connection.port_usage(self.connection.remote_port)
if purpose:
# BitTorrent is a common protocol to truncate, so just use "Torrent"
@@ -786,7 +772,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
purpose = str_tools.crop(purpose, space_available, ending = str_tools.Ending.HYPHEN)
destination_address += ' (%s)' % purpose
- elif not connection.is_private_address(self.foreign.get_address()):
+ elif not connection.is_private_address(self.connection.remote_address):
extra_info = []
controller = tor_controller()
diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py
index 18fe7e7..193ec90 100644
--- a/nyx/connections/conn_panel.py
+++ b/nyx/connections/conn_panel.py
@@ -490,7 +490,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
if client_locale:
self._client_locale_usage[client_locale] = self._client_locale_usage.get(client_locale, 0) + 1
elif entry_line.get_type() == conn_entry.Category.EXIT:
- exit_port = entry_line.foreign.get_port()
+ exit_port = entry_line.connection.remote_port
self._exit_port_usage[exit_port] = self._exit_port_usage.get(exit_port, 0) + 1
self._entries, self._entry_lines = new_entries, list(itertools.chain.from_iterable([entry.get_lines() for entry in new_entries]))
@@ -516,8 +516,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
app_ports = []
for line in unresolved_lines:
- app_conn = line.local if line.get_type() == conn_entry.Category.HIDDEN else line.foreign
- app_ports.append(app_conn.get_port())
+ app_ports.append(line.connection.local_port if line.get_type() == conn_entry.Category.HIDDEN else line.connection.remote_port)
# Queue up resolution for the unresolved ports (skips if it's still working
# on the last query).
@@ -538,7 +537,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
for line in unresolved_lines:
is_local = line.get_type() == conn_entry.Category.HIDDEN
- line_port = line.local.get_port() if is_local else line.foreign.get_port()
+ line_port = line.connection.local_port if is_local else line.connection.remote_port
if line_port in app_results:
# sets application attributes if there's a result with this as the
diff --git a/nyx/connections/entries.py b/nyx/connections/entries.py
index 301b457..06d3831 100644
--- a/nyx/connections/entries.py
+++ b/nyx/connections/entries.py
@@ -146,7 +146,7 @@ class ConnectionPanelEntry:
elif attr == SortAttr.UPTIME:
return self.start_time
elif attr == SortAttr.COUNTRY:
- if connection_line.connection.is_private_address(self.lines[0].foreign.get_address()):
+ if connection_line.connection.is_private_address(self.lines[0].connection.remote_address):
return ''
else:
return connection_line.foreign.get_locale('')
1
0
commit 24092722fcf6b5eb8620e244ffd5816f7c3ffb19
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jul 25 14:52:03 2015 -0700
Merge CircEntry class into its parent
Now that we're no longer doing in-place modifications the CircEntry is just a
constructor. Making it a factory method of the parent class rather than its
own. Still more opportunity for simplification, but one step at a time.
---
nyx/connections/circ_entry.py | 74 ++---------------------------------------
nyx/connections/conn_panel.py | 4 +--
nyx/connections/entries.py | 42 +++++++++++++++++++++++
3 files changed, 47 insertions(+), 73 deletions(-)
diff --git a/nyx/connections/circ_entry.py b/nyx/connections/circ_entry.py
index a80bc8a..c49dbc4 100644
--- a/nyx/connections/circ_entry.py
+++ b/nyx/connections/circ_entry.py
@@ -9,7 +9,6 @@ followed by an entry for each hop in the circuit. For instance:
"""
import curses
-import datetime
import nyx.util.tracker
import nyx.util.ui_tools
@@ -19,73 +18,6 @@ from nyx.connections import entries, conn_entry
from stem.util import str_tools
-def to_unix_time(dt):
- return (dt - datetime.datetime(1970, 1, 1)).total_seconds()
-
-
-class CircEntry(conn_entry.ConnectionEntry):
- def __init__(self, circ):
- conn_entry.ConnectionEntry.__init__(self, nyx.util.tracker.Connection(to_unix_time(circ.created), False, '127.0.0.1', 0, '127.0.0.1', 0, 'tcp'))
-
- self._circuit = circ
- self.circuit_id = circ.id
- self.status = circ.status
-
- # drops to lowercase except the first letter
-
- purpose = circ.purpose
-
- if len(purpose) >= 2:
- purpose = purpose[0].upper() + purpose[1:].lower()
-
- self.lines = [CircHeaderLine(circ)]
-
- # Overwrites attributes of the initial line to make it more fitting as the
- # header for our listing.
-
- self.lines[0].base_type = conn_entry.Category.CIRCUIT
-
- self.update(circ.status, [entry[0] for entry in circ.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]]
-
- if status == 'BUILT' and not self.lines[0].is_built:
- exit_ip, exit_port = nyx.util.tracker.get_consensus_tracker().get_relay_address(path[-1], ('192.168.0.1', 0))
- self.lines[0].set_exit(exit_ip, exit_port, path[-1])
-
- for i in range(len(path)):
- relay_fingerprint = path[i]
- relay_ip, relay_port = nyx.util.tracker.get_consensus_tracker().get_relay_address(relay_fingerprint, ('192.168.0.1', 0))
-
- if i == len(path) - 1:
- if status == 'BUILT':
- placement_type = 'Exit'
- else:
- placement_type = 'Extending'
- elif i == 0:
- placement_type = 'Guard'
- else:
- placement_type = 'Middle'
-
- placement_label = '%i / %s' % (i + 1, placement_type)
-
- self.lines.append(CircLine(relay_ip, relay_port, relay_fingerprint, placement_label, to_unix_time(self._circuit.created)))
-
- self.lines[-1].is_last = True
-
-
class CircHeaderLine(conn_entry.ConnectionLine):
"""
Initial line of a client entry. This has the same basic format as connection
@@ -93,11 +25,11 @@ class CircHeaderLine(conn_entry.ConnectionLine):
"""
def __init__(self, circ):
- conn_entry.ConnectionLine.__init__(self, nyx.util.tracker.Connection(to_unix_time(circ.created), False, '127.0.0.1', 0, '0.0.0.0', 0, 'tcp'), False, False)
+ conn_entry.ConnectionLine.__init__(self, nyx.util.tracker.Connection(entries.to_unix_time(circ.created), False, '127.0.0.1', 0, '0.0.0.0', 0, 'tcp'), False, False)
self.circuit_id = circ.id
- self.purpose = circ.purpose
+ self.purpose = circ.purpose.capitalize()
self.is_built = False
- self._timestamp = to_unix_time(circ.created)
+ self._timestamp = entries.to_unix_time(circ.created)
def set_exit(self, exit_address, exit_port, exit_fingerprint):
conn_entry.ConnectionLine.__init__(self, nyx.util.tracker.Connection(self._timestamp, False, '127.0.0.1', 0, exit_address, exit_port, 'tcp'), False, False)
diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py
index 695c32e..364c42f 100644
--- a/nyx/connections/conn_panel.py
+++ b/nyx/connections/conn_panel.py
@@ -12,7 +12,7 @@ import threading
import nyx.popups
import nyx.util.tracker
-from nyx.connections import descriptor_popup, entries, conn_entry, circ_entry
+from nyx.connections import descriptor_popup, entries, conn_entry
from nyx.util import panel, tor_controller, tracker, ui_tools
from stem.control import State
@@ -477,7 +477,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
# fetches, not client circuits)
if not (circ.status == 'BUILT' and len(circ.path) == 1):
- new_entries.append(circ_entry.CircEntry(circ))
+ new_entries.append(entries.ConnectionPanelEntry.from_circuit(circ))
# update stats for client and exit connections
diff --git a/nyx/connections/entries.py b/nyx/connections/entries.py
index 9215b37..f4693b0 100644
--- a/nyx/connections/entries.py
+++ b/nyx/connections/entries.py
@@ -4,6 +4,8 @@ entry itself (ie, Tor connection, client circuit, etc) and the lines it
consists of in the listing.
"""
+import datetime
+
from stem.util import enum
# attributes we can list entries by
@@ -28,6 +30,10 @@ SORT_COLORS = {
PORT_COUNT = 65536
+def to_unix_time(dt):
+ return (dt - datetime.datetime(1970, 1, 1)).total_seconds()
+
+
class ConnectionPanelEntry:
"""
Common parent for connection panel entries. This consists of a list of lines
@@ -39,6 +45,42 @@ class ConnectionPanelEntry:
self.lines = []
self.flush_cache = True
+ @staticmethod
+ def from_circuit(circ):
+ import nyx.connections.circ_entry
+ import nyx.connections.conn_entry
+ import nyx.util.tracker
+
+ # TODO: should be ConnectionPanelEntry rather than a ConnectionEntry, but
+ # looks like that presently provides sorting
+
+ entry = nyx.connections.conn_entry.ConnectionEntry(nyx.util.tracker.Connection(to_unix_time(circ.created), False, '127.0.0.1', 0, '127.0.0.1', 0, 'tcp'))
+ entry.lines = [nyx.connections.circ_entry.CircHeaderLine(circ)]
+
+ path = [path_entry[0] for path_entry in circ.path]
+
+ if circ.status == 'BUILT':
+ exit_ip, exit_port = nyx.util.tracker.get_consensus_tracker().get_relay_address(path[-1], ('192.168.0.1', 0))
+ entry.lines[0].set_exit(exit_ip, exit_port, path[-1])
+
+ for i, relay_fingerprint in enumerate(path):
+ relay_ip, relay_port = nyx.util.tracker.get_consensus_tracker().get_relay_address(relay_fingerprint, ('192.168.0.1', 0))
+
+ if i == len(path) - 1:
+ placement_type = 'Exit' if circ.status == 'BUILT' else 'Extending'
+ elif i == 0:
+ placement_type = 'Guard'
+ else:
+ placement_type = 'Middle'
+
+ placement_label = '%i / %s' % (i + 1, placement_type)
+
+ entry.lines.append(nyx.connections.circ_entry.CircLine(relay_ip, relay_port, relay_fingerprint, placement_label, to_unix_time(circ.created)))
+
+ entry.lines[-1].is_last = True
+
+ return entry
+
def get_lines(self):
"""
Provides the individual lines in the connection listing.
1
0

22 Sep '15
commit a4c706c3be47fbc531d34860b874c196217c0790
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon Jul 27 09:35:39 2015 -0700
Start tracker daemons when they're requested
Why would we want a daemon that isn't running? Presently application resolution
is a no-op because we're talking with a dorment tracker. Lets always give
running instances.
We were also failing to stop one of the trackers, and this tweaks our check so
we skip trackers that were never requested (and hence don't exist).
---
nyx/controller.py | 1 -
nyx/util/tracker.py | 10 +++++++---
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/nyx/controller.py b/nyx/controller.py
index 77b2037..35b1e89 100644
--- a/nyx/controller.py
+++ b/nyx/controller.py
@@ -141,7 +141,6 @@ def init_controller(stdscr, start_time):
resolver = nyx.util.tracker.get_connection_tracker()
log.info('Operating System: %s, Connection Resolvers: %s' % (os.uname()[0], ', '.join(resolver._resolvers)))
- resolver.start()
else:
# constructs singleton resolver and, if tor isn't connected, initizes
# it to be paused
diff --git a/nyx/util/tracker.py b/nyx/util/tracker.py
index b122d4b..48697dc 100644
--- a/nyx/util/tracker.py
+++ b/nyx/util/tracker.py
@@ -95,6 +95,7 @@ def get_connection_tracker():
if CONNECTION_TRACKER is None:
CONNECTION_TRACKER = ConnectionTracker(CONFIG['queries.connections.rate'])
+ CONNECTION_TRACKER.start()
return CONNECTION_TRACKER
@@ -108,6 +109,7 @@ def get_resource_tracker():
if RESOURCE_TRACKER is None:
RESOURCE_TRACKER = ResourceTracker(CONFIG['queries.resources.rate'])
+ RESOURCE_TRACKER.start()
return RESOURCE_TRACKER
@@ -121,6 +123,7 @@ def get_port_usage_tracker():
if PORT_USAGE_TRACKER is None:
PORT_USAGE_TRACKER = PortUsageTracker(CONFIG['queries.port_usage.rate'])
+ PORT_USAGE_TRACKER.start()
return PORT_USAGE_TRACKER
@@ -146,9 +149,10 @@ def stop_trackers():
"""
def halt_trackers():
- trackers = filter(lambda t: t.is_alive(), [
- get_resource_tracker(),
- get_connection_tracker(),
+ trackers = filter(lambda t: t and t.is_alive(), [
+ CONNECTION_TRACKER,
+ RESOURCE_TRACKER,
+ PORT_USAGE_TRACKER,
])
for tracker in trackers:
1
0