def __init__(self, builder):
    CliConnectionPanel.__init__(self, None)

    self.builder = builder

    conn = torTools.getConn()
    torPid = conn.getMyPid()
    torCmdName = sysTools.getProcessName(torPid, 'tor')
    connections.getResolver(torCmdName, torPid, 'tor')

    treeStore = self.builder.get_object('treestore_conn')
    self._wrappedEntryLines = EntryLines(self._entryLines, treeStore)
Beispiel #2
0
def shutdownDaemons():
    """
  Stops and joins on worker threads.
  """

    # prevents further worker threads from being spawned
    torTools.NO_SPAWN = True

    # stops panel daemons
    control = getController()
    for panelImpl in control.getDaemonPanels():
        panelImpl.stop()
    for panelImpl in control.getDaemonPanels():
        panelImpl.join()

    # joins on stem threads
    torTools.getConn().close()

    # joins on utility daemon threads - this might take a moment since the
    # internal threadpools being joined might be sleeping
    hostnames.stop()
    resourceTrackers = sysTools.RESOURCE_TRACKERS.values()
    resolver = connections.getResolver("tor") if connections.isResolverAlive("tor") else None
    for tracker in resourceTrackers:
        tracker.stop()
    if resolver:
        resolver.stop()  # sets halt flag (returning immediately)
    for tracker in resourceTrackers:
        tracker.join()
    if resolver:
        resolver.join()  # joins on halted resolver
def shutdownDaemons():
    """
  Stops and joins on worker threads.
  """

    # prevents further worker threads from being spawned
    torTools.NO_SPAWN = True

    # stops panel daemons
    control = getController()
    for panelImpl in control.getDaemonPanels():
        panelImpl.stop()
    for panelImpl in control.getDaemonPanels():
        panelImpl.join()

    # joins on TorCtl event thread
    torTools.getConn().close()

    # joins on utility daemon threads - this might take a moment since the
    # internal threadpools being joined might be sleeping
    hostnames.stop()
    resourceTrackers = sysTools.RESOURCE_TRACKERS.values()
    resolver = connections.getResolver("tor") if connections.isResolverAlive(
        "tor") else None
    for tracker in resourceTrackers:
        tracker.stop()
    if resolver: resolver.stop()  # sets halt flag (returning immediately)
    for tracker in resourceTrackers:
        tracker.join()
    if resolver: resolver.join()  # joins on halted resolver
Beispiel #4
0
    def handleKey(self, key):
        self.valsLock.acquire()

        isKeystrokeConsumed = True
        if uiTools.isScrollKey(key):
            pageHeight = self.getPreferredSize()[0] - 1
            if self._showDetails: pageHeight -= (DETAILS_HEIGHT + 1)
            isChanged = self._scroller.handleKey(key, self._entryLines,
                                                 pageHeight)
            if isChanged: self.redraw(True)
        elif uiTools.isSelectionKey(key):
            self._showDetails = not self._showDetails
            self.redraw(True)
        elif key == ord('s') or key == ord('S'):
            self.showSortDialog()
        elif key == ord('u') or key == ord('U'):
            # provides a menu to pick the connection resolver
            title = "Resolver Util:"
            options = ["auto"] + list(connections.Resolver)
            connResolver = connections.getResolver("tor")

            currentOverwrite = connResolver.overwriteResolver
            if currentOverwrite == None: oldSelection = 0
            else: oldSelection = options.index(currentOverwrite)

            selection = cli.popups.showMenu(title, options, oldSelection)

            # applies new setting
            if selection != -1:
                selectedOption = options[selection] if selection != 0 else None
                connResolver.overwriteResolver = selectedOption
        elif key == ord('l') or key == ord('L'):
            # provides a menu to pick the primary information we list connections by
            title = "List By:"
            options = list(entries.ListingType)

            # dropping the HOSTNAME listing type until we support displaying that content
            options.remove(cli.connections.entries.ListingType.HOSTNAME)

            oldSelection = options.index(self.getListingType())
            selection = cli.popups.showMenu(title, options, oldSelection)

            # applies new setting
            if selection != -1: self.setListingType(options[selection])
        elif key == ord('d') or key == ord('D'):
            # presents popup for raw consensus data
            descriptorPopup.showDescriptorPopup(self)
        elif (key == ord('c') or key == ord('C')) and self.isClientsAllowed():
            countPopup.showCountDialog(countPopup.CountType.CLIENT_LOCALE,
                                       self._clientLocaleUsage)
        elif (key == ord('e') or key == ord('E')) and self.isExitsAllowed():
            countPopup.showCountDialog(countPopup.CountType.EXIT_PORT,
                                       self._exitPortUsage)
        else:
            isKeystrokeConsumed = False

        self.valsLock.release()
        return isKeystrokeConsumed
Beispiel #5
0
 def handleKey(self, key):
   self.valsLock.acquire()
   
   isKeystrokeConsumed = True
   if uiTools.isScrollKey(key):
     pageHeight = self.getPreferredSize()[0] - 1
     if self._showDetails: pageHeight -= (DETAILS_HEIGHT + 1)
     isChanged = self._scroller.handleKey(key, self._entryLines, pageHeight)
     if isChanged: self.redraw(True)
   elif uiTools.isSelectionKey(key):
     self._showDetails = not self._showDetails
     self.redraw(True)
   elif key == ord('s') or key == ord('S'):
     self.showSortDialog()
   elif key == ord('u') or key == ord('U'):
     # provides a menu to pick the connection resolver
     title = "Resolver Util:"
     options = ["auto"] + connections.Resolver.values()
     connResolver = connections.getResolver("tor")
     
     currentOverwrite = connResolver.overwriteResolver
     if currentOverwrite == None: oldSelection = 0
     else: oldSelection = options.index(currentOverwrite)
     
     selection = cli.popups.showMenu(title, options, oldSelection)
     
     # applies new setting
     if selection != -1:
       selectedOption = options[selection] if selection != 0 else None
       connResolver.overwriteResolver = selectedOption
   elif key == ord('l') or key == ord('L'):
     # provides a menu to pick the primary information we list connections by
     title = "List By:"
     options = entries.ListingType.values()
     
     # dropping the HOSTNAME listing type until we support displaying that content
     options.remove(cli.connections.entries.ListingType.HOSTNAME)
     
     oldSelection = options.index(self._listingType)
     selection = cli.popups.showMenu(title, options, oldSelection)
     
     # applies new setting
     if selection != -1: self.setListingType(options[selection])
   elif key == ord('d') or key == ord('D'):
     # presents popup for raw consensus data
     descriptorPopup.showDescriptorPopup(self)
   elif (key == ord('c') or key == ord('C')) and self.isClientsAllowed():
     countPopup.showCountDialog(countPopup.CountType.CLIENT_LOCALE, self._clientLocaleUsage)
   elif (key == ord('e') or key == ord('E')) and self.isExitsAllowed():
     countPopup.showCountDialog(countPopup.CountType.EXIT_PORT, self._exitPortUsage)
   else: isKeystrokeConsumed = False
   
   self.valsLock.release()
   return isKeystrokeConsumed
Beispiel #6
0
 def eventTick(self):
   """
   Fetches connection stats from cached information.
   """
   
   inboundCount, outboundCount = 0, 0
   
   for entry in connections.getResolver("tor").getConnections():
     localPort = entry[1]
     if localPort in (self.orPort, self.dirPort): inboundCount += 1
     elif localPort == self.controlPort: pass # control connection
     else: outboundCount += 1
   
   self._processEvent(inboundCount, outboundCount)
Beispiel #7
0
    def eventTick(self):
        """
    Fetches connection stats from cached information.
    """

        inboundCount, outboundCount = 0, 0

        for entry in connections.getResolver("tor").getConnections():
            localPort = entry[1]
            if localPort in (self.orPort, self.dirPort): inboundCount += 1
            elif localPort == self.controlPort: pass  # control connection
            else: outboundCount += 1

        self._processEvent(inboundCount, outboundCount)
Beispiel #8
0
def makeConnectionsMenu(connPanel):
    """
  Submenu for the connections panel, consisting of...
    [X] IP Address
    [ ] Fingerprint
    [ ] Nickname
        Sorting...
        Resolver (Submenu)
  
  Arguments:
    connPanel - instance of the connections panel
  """

    connectionsMenu = cli.menu.item.Submenu("Connections")

    # listing options
    listingGroup = cli.menu.item.SelectionGroup(connPanel.setListingType,
                                                connPanel.getListingType())

    listingOptions = list(cli.connections.entries.ListingType)
    listingOptions.remove(cli.connections.entries.ListingType.HOSTNAME)

    for option in listingOptions:
        connectionsMenu.add(
            cli.menu.item.SelectionMenuItem(option, listingGroup, option))

    # sorting option
    connectionsMenu.add(
        cli.menu.item.MenuItem("Sorting...", connPanel.showSortDialog))

    # resolver submenu
    connResolver = connections.getResolver("tor")
    resolverMenu = cli.menu.item.Submenu("Resolver")
    resolverGroup = cli.menu.item.SelectionGroup(
        connResolver.setOverwriteResolver, connResolver.getOverwriteResolver())

    resolverMenu.add(
        cli.menu.item.SelectionMenuItem("auto", resolverGroup, None))

    for option in connections.Resolver:
        resolverMenu.add(
            cli.menu.item.SelectionMenuItem(option, resolverGroup, option))

    connectionsMenu.add(resolverMenu)

    return connectionsMenu
Beispiel #9
0
def makeConnectionsMenu(connPanel):
  """
  Submenu for the connections panel, consisting of...
    [X] IP Address
    [ ] Fingerprint
    [ ] Nickname
        Sorting...
        Resolver (Submenu)
  
  Arguments:
    connPanel - instance of the connections panel
  """
  
  connectionsMenu = cli.menu.item.Submenu("Connections")
  
  # listing options
  listingGroup = cli.menu.item.SelectionGroup(connPanel.setListingType, connPanel.getListingType())
  
  listingOptions = list(cli.connections.entries.ListingType)
  listingOptions.remove(cli.connections.entries.ListingType.HOSTNAME)
  
  for option in listingOptions:
    connectionsMenu.add(cli.menu.item.SelectionMenuItem(option, listingGroup, option))
  
  # sorting option
  connectionsMenu.add(cli.menu.item.MenuItem("Sorting...", connPanel.showSortDialog))
  
  # resolver submenu
  connResolver = connections.getResolver("tor")
  resolverMenu = cli.menu.item.Submenu("Resolver")
  resolverGroup = cli.menu.item.SelectionGroup(connResolver.setOverwriteResolver, connResolver.getOverwriteResolver())
  
  resolverMenu.add(cli.menu.item.SelectionMenuItem("auto", resolverGroup, None))
  
  for option in connections.Resolver:
    resolverMenu.add(cli.menu.item.SelectionMenuItem(option, resolverGroup, option))
  
  connectionsMenu.add(resolverMenu)
  
  return connectionsMenu
Beispiel #10
0
    def getHelp(self):
        resolverUtil = connections.getResolver("tor").overwriteResolver
        if resolverUtil == None: resolverUtil = "auto"

        options = []
        options.append(("up arrow", "scroll up a line", None))
        options.append(("down arrow", "scroll down a line", None))
        options.append(("page up", "scroll up a page", None))
        options.append(("page down", "scroll down a page", None))
        options.append(("enter", "show connection details", None))
        options.append(("d", "raw consensus descriptor", None))

        if self.isClientsAllowed():
            options.append(("c", "client locale usage summary", None))

        if self.isExitsAllowed():
            options.append(("e", "exit port usage summary", None))

        options.append(("l", "listed identity", self.getListingType().lower()))
        options.append(("s", "sort ordering", None))
        options.append(("u", "resolving utility", resolverUtil))
        return options
Beispiel #11
0
 def getHelp(self):
   resolverUtil = connections.getResolver("tor").overwriteResolver
   if resolverUtil == None: resolverUtil = "auto"
   
   options = []
   options.append(("up arrow", "scroll up a line", None))
   options.append(("down arrow", "scroll down a line", None))
   options.append(("page up", "scroll up a page", None))
   options.append(("page down", "scroll down a page", None))
   options.append(("enter", "edit configuration option", None))
   options.append(("d", "raw consensus descriptor", None))
   
   if self.isClientsAllowed():
     options.append(("c", "client locale usage summary", None))
   
   if self.isExitsAllowed():
     options.append(("e", "exit port usage summary", None))
   
   options.append(("l", "listed identity", self._listingType.lower()))
   options.append(("s", "sort ordering", None))
   options.append(("u", "resolving utility", resolverUtil))
   return options
Beispiel #12
0
def connResetListener(controller, eventType, _):
    """
  Pauses connection resolution when tor's shut down, and resumes with the new
  pid if started again.
  """

    if connections.isResolverAlive("tor"):
        resolver = connections.getResolver("tor")
        resolver.setPaused(eventType == State.CLOSED)

        if eventType in (State.INIT, State.RESET):
            # Reload the torrc contents. If the torrc panel is present then it will
            # do this instead since it wants to do validation and redraw _after_ the
            # new contents are loaded.

            if getController().getPanel("torrc") == None:
                torConfig.getTorrc().load(True)

            torPid = controller.get_info("process/pid", None)

            if torPid and torPid != resolver.getPid():
                resolver.setPid(torPid)
def connResetListener(conn, eventType):
    """
  Pauses connection resolution when tor's shut down, and resumes with the new
  pid if started again.
  """

    if connections.isResolverAlive("tor"):
        resolver = connections.getResolver("tor")
        resolver.setPaused(eventType == torTools.State.CLOSED)

        if eventType in (torTools.State.INIT, torTools.State.RESET):
            # Reload the torrc contents. If the torrc panel is present then it will
            # do this instead since it wants to do validation and redraw _after_ the
            # new contents are loaded.

            if getController().getPanel("torrc") == None:
                torConfig.getTorrc().load(True)

            torPid = conn.getMyPid()

            if torPid and torPid != resolver.getPid():
                resolver.setPid(torPid)
Beispiel #14
0
 def reset(self):
   """
   Reloads connection results.
   """
   
   if self.isDisabled: return
   
   # inaccessable during startup so might need to be refetched
   try:
     if not self.address: self.address = self.conn.get_info("address")["address"]
   except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): pass
   
   self.connectionsLock.acquire()
   self.clientConnectionLock.acquire()
   
   # temporary variables for connections and count
   connectionsTmp = []
   connectionCountTmp = [0] * 5
   familyResolutionsTmp = {}
   
   # used (with isBridge) to determine if inbound connections should be scrubbed
   isGuard = False
   try:
     myFingerprint = self.conn.get_info("fingerprint")
     nsCall = self.conn.get_network_status("id/%s" % myFingerprint)
     if nsCall: isGuard = "Guard" in nsCall[0].flags
     else: raise TorCtl.ErrorReply # network consensus couldn't be fetched
   except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): pass
   
   try:
     if self.clientConnectionCache == None:
       # client connection cache was invalidated
       self.clientConnectionCache = _getClientConnections(self.conn)
     
     connTimes = {} # mapping of ip/port to connection time
     for entry in (self.connections if not self.isPaused else self.connectionsBuffer):
       connTimes[(entry[CONN_F_IP], entry[CONN_F_PORT])] = entry[CONN_TIME]
     
     results = connections.getResolver("tor").getConnections()
     if results == self.lastConnResults: return # contents haven't changed
     
     for lIp, lPort, fIp, fPort in results:
       fingerprint = self.getFingerprint(fIp, fPort)
       
       isPrivate = False
       if lPort in (self.listenPort, self.dirPort):
         type = "inbound"
         connectionCountTmp[0] += 1
         if SCRUB_PRIVATE_DATA and fIp not in self.fingerprintMappings.keys(): isPrivate = isGuard or self.isBridge
       elif lPort == self.socksPort:
         type = "client"
         connectionCountTmp[2] += 1
       elif lPort == self.controlPort:
         type = "control"
         connectionCountTmp[4] += 1
       else:
         nickname = self.getNickname(fIp, fPort)
         
         isClient = False
         for clientName in self.clientConnectionCache:
           if nickname == clientName or (len(clientName) > 1 and clientName[0] == "$" and fingerprint == clientName[1:]):
             isClient = True
             break
         
         if isClient:
           type = "client"
           connectionCountTmp[2] += 1
         elif (fIp, fPort) in DIR_SERVERS:
           type = "directory"
           connectionCountTmp[3] += 1
         else:
           type = "outbound"
           connectionCountTmp[1] += 1
           if SCRUB_PRIVATE_DATA and fIp not in self.fingerprintMappings.keys(): isPrivate = isExitAllowed(fIp, fPort, self.exitPolicy, self.exitRejectPrivate)
       
       # replace nat address with external version if available and the
       # external address isn't a private IP
       # TODO: range should restrict to the following address ranges:
       #   10.*, 172.16.* - 172.31.*, 192.168.*
       # being lazy right now - fix the 172.* range when rewriting
       isPrivateIp = fIp.startswith("10.") or fIp.startswith("192.168.") or fIp.startswith("172.")
       if self.address and type != "control" and not isPrivateIp: lIp = self.address
       
       try:
         countryCodeQuery = "ip-to-country/%s" % fIp
         countryCode = self.conn.get_info(countryCodeQuery)[countryCodeQuery]
       except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed):
         countryCode = "??"
         if not self.providedGeoipWarning:
           log.log(log.WARN, "Tor geoip database is unavailable.")
           self.providedGeoipWarning = True
       
       if (fIp, fPort) in connTimes: connTime = connTimes[(fIp, fPort)]
       else: connTime = time.time()
       
       connectionsTmp.append((type, lIp, lPort, fIp, fPort, countryCode, connTime, isPrivate))
     
     # appends localhost connection to allow user to look up their own consensus entry
     selfFingerprint = None
     try:
       selfFingerprint = self.conn.get_info("fingerprint")["fingerprint"]
     except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): pass
     
     if self.address and selfFingerprint:
       try:
         countryCodeQuery = "ip-to-country/%s" % self.address
         selfCountryCode = self.conn.get_info(countryCodeQuery)[countryCodeQuery]
       except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed):
         selfCountryCode = "??"
       
       if (self.address, self.orPort) in connTimes: connTime = connTimes[(self.address, self.orPort)]
       else: connTime = time.time()
       
       self.localhostEntry = (("localhost", self.address, self.orPort, self.address, self.orPort, selfCountryCode, connTime, False), selfFingerprint)
       connectionsTmp.append(self.localhostEntry[0])
     else:
       self.localhostEntry = None
     
     # appends family connections
     tmpCounter = 0 # used for unique port of unresolved family entries (funky hack)
     for familyEntry in self.family:
       # TODO: turns out that "ns/name/<OR nickname>" accpets fingerprint
       # identifiers, so all this nickname -> fingerprint work is unnecessary,
       # but used for fingerprint lookup performance in draw... this could be
       # improved (might be completely unnecessary due to the fingerprint
       # lookup cache)
       fingerprint = None
       if familyEntry in self.familyFingerprints:
         fingerprint = self.familyFingerprints[familyEntry]
       
       try:
         if fingerprint: nsCall = self.conn.get_network_status("id/%s" % fingerprint)
         else: nsCall = self.conn.get_network_status("name/%s" % familyEntry)
         if nsCall: familyAddress, familyPort = nsCall[0].ip, nsCall[0].orport
         else: raise TorCtl.ErrorReply # network consensus couldn't be fetched
         
         countryCodeQuery = "ip-to-country/%s" % familyAddress
         familyCountryCode = self.conn.get_info(countryCodeQuery)[countryCodeQuery]
         
         if (familyAddress, familyPort) in connTimes: connTime = connTimes[(familyAddress, familyPort)]
         else: connTime = time.time()
         
         if fingerprint: familyResolutionsTmp[(familyAddress, familyPort)] = fingerprint
         connectionsTmp.append(("family", familyAddress, familyPort, familyAddress, familyPort, familyCountryCode, connTime, False))
       except (socket.error, TorCtl.ErrorReply):
         # use dummy entry for sorting - the draw function notes that entries are unknown
         portIdentifier = str(65536 + tmpCounter)
         if fingerprint: familyResolutionsTmp[("256.255.255.255", portIdentifier)] = fingerprint
         connectionsTmp.append(("family", "256.255.255.255", portIdentifier, "256.255.255.255", portIdentifier, "??", time.time(), False))
         tmpCounter += 1
       except TorCtl.TorCtlClosed:
         pass # connections aren't shown when control port is unavailable
     
     self.lastUpdate = time.time()
     
     # assigns results
     if self.isPaused:
       self.connectionsBuffer = connectionsTmp
       self.connectionCountBuffer = connectionCountTmp
       self.familyResolutionsBuffer = familyResolutionsTmp
     else:
       self.connections = connectionsTmp
       self.connectionCount = connectionCountTmp
       self.familyResolutions = familyResolutionsTmp
       
       # hostnames are sorted at draw - otherwise now's a good time
       if self.listingType != LIST_HOSTNAME: self.sortConnections()
     self.lastConnResults = results
   finally:
     self.connectionsLock.release()
     self.clientConnectionLock.release()
def startTorMonitor(startTime):
    """
  Initializes the interface and starts the main draw loop.
  
  Arguments:
    startTime - unix time for when arm was started
  """

    # initializes interface configs
    config = conf.getConfig("arm")
    config.update(CONFIG, {
        "features.redrawRate": 1,
        "features.refreshRate": 0
    })

    cli.graphing.graphPanel.loadConfig(config)
    cli.connections.connEntry.loadConfig(config)
    cli.wizard.loadConfig(config)

    # attempts to fetch the tor pid, warning if unsuccessful (this is needed for
    # checking its resource usage, among other things)
    conn = torTools.getConn()
    torPid = conn.getMyPid()

    if not torPid and conn.isAlive():
        msg = "Unable to determine Tor's pid. Some information, like its resource usage will be unavailable."
        log.log(CONFIG["log.unknownTorPid"], msg)

    # adds events needed for arm functionality to the torTools REQ_EVENTS
    # mapping (they're then included with any setControllerEvents call, and log
    # a more helpful error if unavailable)

    torTools.REQ_EVENTS["BW"] = "bandwidth graph won't function"

    if not CONFIG["startup.blindModeEnabled"]:
        torTools.REQ_EVENTS[
            "CIRC"] = "may cause issues in identifying client connections"

        # Configures connection resoultions. This is paused/unpaused according to
        # if Tor's connected or not.
        conn.addStatusListener(connResetListener)

        if torPid:
            # use the tor pid to help narrow connection results
            torCmdName = sysTools.getProcessName(torPid, "tor")
            connections.getResolver(torCmdName, torPid, "tor")
        else:
            # constructs singleton resolver and, if tor isn't connected, initizes
            # it to be paused
            connections.getResolver("tor").setPaused(not conn.isAlive())

        # hack to display a better (arm specific) notice if all resolvers fail
        connections.RESOLVER_FINAL_FAILURE_MSG = "We were unable to use any of your system's resolvers to get tor's connections. This is fine, but means that the connections page will be empty. This is usually permissions related so if you would like to fix this then run arm with the same user as tor (ie, \"sudo -u <tor user> arm\")."

    # provides a notice about any event types tor supports but arm doesn't
    missingEventTypes = cli.logPanel.getMissingEventTypes()

    if missingEventTypes:
        pluralLabel = "s" if len(missingEventTypes) > 1 else ""
        log.log(
            CONFIG["log.torEventTypeUnrecognized"],
            "arm doesn't recognize the following event type%s: %s (log 'UNKNOWN' events to see them)"
            % (pluralLabel, ", ".join(missingEventTypes)))

    try:
        curses.wrapper(drawTorMonitor, startTime)
    except KeyboardInterrupt:
        # Skip printing stack trace in case of keyboard interrupt. The
        # HALT_ACTIVITY attempts to prevent daemons from triggering a curses redraw
        # (which would leave the user's terminal in a screwed up state). There is
        # still a tiny timing issue here (after the exception but before the flag
        # is set) but I've never seen it happen in practice.

        panel.HALT_ACTIVITY = True
        shutdownDaemons()
Beispiel #16
0
    def _update(self):
        """
    Fetches the newest resolved connections.
    """

        self.appResolveSinceUpdate = False

        # if we don't have an initialized resolver then this is a no-op
        if not connections.isResolverAlive("tor"): return

        connResolver = connections.getResolver("tor")
        currentResolutionCount = connResolver.getResolutionCount()

        self.valsLock.acquire()

        newEntries = []  # the new results we'll display

        # Fetches new connections and client circuits...
        # newConnections  [(local ip, local port, foreign ip, foreign port)...]
        # newCircuits     {circuitID => (status, purpose, path)...}

        newConnections = connResolver.getConnections()
        newCircuits = {}

        for circuitID, status, purpose, path in torTools.getConn().getCircuits(
        ):
            # Skips established single-hop circuits (these are for directory
            # fetches, not client circuits)
            if not (status == "BUILT" and len(path) == 1):
                newCircuits[circuitID] = (status, purpose, path)

        # Populates newEntries with any of our old entries that still exist.
        # This is both for performance and to keep from resetting the uptime
        # attributes. Note that CircEntries are a ConnectionEntry subclass so
        # we need to check for them first.

        for oldEntry in self._entries:
            if isinstance(oldEntry, circEntry.CircEntry):
                newEntry = newCircuits.get(oldEntry.circuitID)

                if newEntry:
                    oldEntry.update(newEntry[0], newEntry[2])
                    newEntries.append(oldEntry)
                    del newCircuits[oldEntry.circuitID]
            elif isinstance(oldEntry, connEntry.ConnectionEntry):
                connLine = oldEntry.getLines()[0]
                connAttr = (connLine.local.getIpAddr(),
                            connLine.local.getPort(),
                            connLine.foreign.getIpAddr(),
                            connLine.foreign.getPort())

                if connAttr in newConnections:
                    newEntries.append(oldEntry)
                    newConnections.remove(connAttr)

        # Reset any display attributes for the entries we're keeping
        for entry in newEntries:
            entry.resetDisplay()

        # Adds any new connection and circuit entries.
        for lIp, lPort, fIp, fPort in newConnections:
            newConnEntry = connEntry.ConnectionEntry(lIp, lPort, fIp, fPort)
            newConnLine = newConnEntry.getLines()[0]

            if newConnLine.getType() != connEntry.Category.CIRCUIT:
                newEntries.append(newConnEntry)

                # updates exit port and client locale usage information
                if newConnLine.isPrivate():
                    if newConnLine.getType() == connEntry.Category.INBOUND:
                        # client connection, update locale information
                        clientLocale = newConnLine.foreign.getLocale()

                        if clientLocale:
                            self._clientLocaleUsage[
                                clientLocale] = self._clientLocaleUsage.get(
                                    clientLocale, 0) + 1
                    elif newConnLine.getType() == connEntry.Category.EXIT:
                        exitPort = newConnLine.foreign.getPort()
                        self._exitPortUsage[
                            exitPort] = self._exitPortUsage.get(exitPort,
                                                                0) + 1

        for circuitID in newCircuits:
            status, purpose, path = newCircuits[circuitID]
            newEntries.append(
                circEntry.CircEntry(circuitID, status, purpose, path))

        # 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).

        categoryTypes = list(connEntry.Category)
        typeCounts = dict((type, 0) for type in categoryTypes)
        for entry in newEntries:
            if isinstance(entry, connEntry.ConnectionEntry):
                typeCounts[entry.getLines()[0].getType()] += 1
            elif isinstance(entry, circEntry.CircEntry):
                typeCounts[connEntry.Category.CIRCUIT] += 1

        # makes labels for all the categories with connections (ie,
        # "21 outbound", "1 control", etc)
        countLabels = []

        for category in categoryTypes:
            if typeCounts[category] > 0:
                countLabels.append("%i %s" %
                                   (typeCounts[category], category.lower()))

        if countLabels:
            self._title = "Connections (%s):" % ", ".join(countLabels)
        else:
            self._title = "Connections:"

        self._entries = newEntries

        self._entryLines = []
        for entry in self._entries:
            self._entryLines += entry.getLines()

        self.setSortOrder()
        self._lastResourceFetch = currentResolutionCount
        self.valsLock.release()
Beispiel #17
0
def startTorMonitor(startTime):
  """
  Initializes the interface and starts the main draw loop.
  
  Arguments:
    startTime - unix time for when arm was started
  """
  
  # initializes interface configs
  config = conf.getConfig("arm")
  config.update(CONFIG, {
    "features.redrawRate": 1,
    "features.refreshRate": 0})
  
  cli.graphing.graphPanel.loadConfig(config)
  cli.connections.connEntry.loadConfig(config)
  cli.wizard.loadConfig(config)
  
  # attempts to fetch the tor pid, warning if unsuccessful (this is needed for
  # checking its resource usage, among other things)
  conn = torTools.getConn()
  torPid = conn.getMyPid()
  
  if not torPid and conn.isAlive():
    msg = "Unable to determine Tor's pid. Some information, like its resource usage will be unavailable."
    log.log(CONFIG["log.unknownTorPid"], msg)
  
  # adds events needed for arm functionality to the torTools REQ_EVENTS
  # mapping (they're then included with any setControllerEvents call, and log
  # a more helpful error if unavailable)
  
  torTools.REQ_EVENTS["BW"] = "bandwidth graph won't function"
  
  if not CONFIG["startup.blindModeEnabled"]:
    torTools.REQ_EVENTS["CIRC"] = "may cause issues in identifying client connections"
    
    # Configures connection resoultions. This is paused/unpaused according to
    # if Tor's connected or not.
    conn.addStatusListener(connResetListener)
    
    if torPid:
      # use the tor pid to help narrow connection results
      torCmdName = sysTools.getProcessName(torPid, "tor")
      connections.getResolver(torCmdName, torPid, "tor")
    else:
      # constructs singleton resolver and, if tor isn't connected, initizes
      # it to be paused
      connections.getResolver("tor").setPaused(not conn.isAlive())
    
    # hack to display a better (arm specific) notice if all resolvers fail
    connections.RESOLVER_FINAL_FAILURE_MSG = "We were unable to use any of your system's resolvers to get tor's connections. This is fine, but means that the connections page will be empty. This is usually permissions related so if you would like to fix this then run arm with the same user as tor (ie, \"sudo -u <tor user> arm\")."
  
  # provides a notice about any event types tor supports but arm doesn't
  missingEventTypes = cli.logPanel.getMissingEventTypes()
  
  if missingEventTypes:
    pluralLabel = "s" if len(missingEventTypes) > 1 else ""
    log.log(CONFIG["log.torEventTypeUnrecognized"], "arm doesn't recognize the following event type%s: %s (log 'UNKNOWN' events to see them)" % (pluralLabel, ", ".join(missingEventTypes)))
  
  try:
    curses.wrapper(drawTorMonitor, startTime)
  except KeyboardInterrupt:
    # Skip printing stack trace in case of keyboard interrupt. The
    # HALT_ACTIVITY attempts to prevent daemons from triggering a curses redraw
    # (which would leave the user's terminal in a screwed up state). There is
    # still a tiny timing issue here (after the exception but before the flag
    # is set) but I've never seen it happen in practice.
    
    panel.HALT_ACTIVITY = True
    shutdownDaemons()
Beispiel #18
0
def startTorMonitor(startTime):
    """
  Initializes the interface and starts the main draw loop.
  
  Arguments:
    startTime - unix time for when arm was started
  """

    # attempts to fetch the tor pid, warning if unsuccessful (this is needed for
    # checking its resource usage, among other things)
    conn = torTools.getConn()
    torPid = conn.getMyPid()

    if not torPid and conn.isAlive():
        log.warn("Unable to determine Tor's pid. Some information, like its resource usage will be unavailable.")

    # adds events needed for arm functionality to the torTools REQ_EVENTS
    # mapping (they're then included with any setControllerEvents call, and log
    # a more helpful error if unavailable)

    torTools.REQ_EVENTS["BW"] = "bandwidth graph won't function"

    if not CONFIG["startup.blindModeEnabled"]:
        # The DisableDebuggerAttachment will prevent our connection panel from really
        # functioning. It'll have circuits, but little else. If this is the case then
        # notify the user and tell them what they can do to fix it.

        if conn.getOption("DisableDebuggerAttachment", None) == "1":
            log.notice(
                "Tor is preventing system utilities like netstat and lsof from working. This means that arm can't provide you with connection information. You can change this by adding 'DisableDebuggerAttachment 0' to your torrc and restarting tor. For more information see...\nhttps://trac.torproject.org/3313"
            )
            connections.getResolver("tor").setPaused(True)
        else:
            torTools.REQ_EVENTS["CIRC"] = "may cause issues in identifying client connections"

            # Configures connection resoultions. This is paused/unpaused according to
            # if Tor's connected or not.
            conn.addStatusListener(connResetListener)

            if torPid:
                # use the tor pid to help narrow connection results
                torCmdName = sysTools.getProcessName(torPid, "tor")
                connections.getResolver(torCmdName, torPid, "tor")
            else:
                # constructs singleton resolver and, if tor isn't connected, initizes
                # it to be paused
                connections.getResolver("tor").setPaused(not conn.isAlive())

            # hack to display a better (arm specific) notice if all resolvers fail
            connections.RESOLVER_FINAL_FAILURE_MSG = "We were unable to use any of your system's resolvers to get tor's connections. This is fine, but means that the connections page will be empty. This is usually permissions related so if you would like to fix this then run arm with the same user as tor (ie, \"sudo -u <tor user> arm\")."

    # provides a notice about any event types tor supports but arm doesn't
    missingEventTypes = cli.logPanel.getMissingEventTypes()

    if missingEventTypes:
        pluralLabel = "s" if len(missingEventTypes) > 1 else ""
        log.info(
            "arm doesn't recognize the following event type%s: %s (log 'UNKNOWN' events to see them)"
            % (pluralLabel, ", ".join(missingEventTypes))
        )

    try:
        curses.wrapper(drawTorMonitor, startTime)
    except KeyboardInterrupt:
        # Skip printing stack trace in case of keyboard interrupt. The
        # HALT_ACTIVITY attempts to prevent daemons from triggering a curses redraw
        # (which would leave the user's terminal in a screwed up state). There is
        # still a tiny timing issue here (after the exception but before the flag
        # is set) but I've never seen it happen in practice.

        panel.HALT_ACTIVITY = True
        shutdownDaemons()
Beispiel #19
0
def startTorMonitor(startTime):
    """
  Initializes the interface and starts the main draw loop.
  
  Arguments:
    startTime - unix time for when arm was started
  """

    # attempts to fetch the tor pid, warning if unsuccessful (this is needed for
    # checking its resource usage, among other things)
    conn = torTools.getConn()
    torPid = conn.getMyPid()

    if not torPid and conn.isAlive():
        log.warn(
            "Unable to determine Tor's pid. Some information, like its resource usage will be unavailable."
        )

    # adds events needed for arm functionality to the torTools REQ_EVENTS
    # mapping (they're then included with any setControllerEvents call, and log
    # a more helpful error if unavailable)

    torTools.REQ_EVENTS["BW"] = "bandwidth graph won't function"

    if not CONFIG["startup.blindModeEnabled"]:
        # The DisableDebuggerAttachment will prevent our connection panel from really
        # functioning. It'll have circuits, but little else. If this is the case then
        # notify the user and tell them what they can do to fix it.

        if conn.getOption("DisableDebuggerAttachment", None) == "1":
            log.notice(
                "Tor is preventing system utilities like netstat and lsof from working. This means that arm can't provide you with connection information. You can change this by adding 'DisableDebuggerAttachment 0' to your torrc and restarting tor. For more information see...\nhttps://trac.torproject.org/3313"
            )
            connections.getResolver("tor").setPaused(True)
        else:
            torTools.REQ_EVENTS[
                "CIRC"] = "may cause issues in identifying client connections"

            # Configures connection resoultions. This is paused/unpaused according to
            # if Tor's connected or not.
            conn.addStatusListener(connResetListener)

            if torPid:
                # use the tor pid to help narrow connection results
                torCmdName = sysTools.getProcessName(torPid, "tor")
                connections.getResolver(torCmdName, torPid, "tor")
            else:
                # constructs singleton resolver and, if tor isn't connected, initizes
                # it to be paused
                connections.getResolver("tor").setPaused(not conn.isAlive())

            # hack to display a better (arm specific) notice if all resolvers fail
            connections.RESOLVER_FINAL_FAILURE_MSG = "We were unable to use any of your system's resolvers to get tor's connections. This is fine, but means that the connections page will be empty. This is usually permissions related so if you would like to fix this then run arm with the same user as tor (ie, \"sudo -u <tor user> arm\")."

    # provides a notice about any event types tor supports but arm doesn't
    missingEventTypes = cli.logPanel.getMissingEventTypes()

    if missingEventTypes:
        pluralLabel = "s" if len(missingEventTypes) > 1 else ""
        log.info(
            "arm doesn't recognize the following event type%s: %s (log 'UNKNOWN' events to see them)"
            % (pluralLabel, ", ".join(missingEventTypes)))

    try:
        curses.wrapper(drawTorMonitor, startTime)
    except KeyboardInterrupt:
        # Skip printing stack trace in case of keyboard interrupt. The
        # HALT_ACTIVITY attempts to prevent daemons from triggering a curses redraw
        # (which would leave the user's terminal in a screwed up state). There is
        # still a tiny timing issue here (after the exception but before the flag
        # is set) but I've never seen it happen in practice.

        panel.HALT_ACTIVITY = True
        shutdownDaemons()
Beispiel #20
0
 def _update(self):
   """
   Fetches the newest resolved connections.
   """
   
   self.appResolveSinceUpdate = False
   
   # if we don't have an initialized resolver then this is a no-op
   if not connections.isResolverAlive("tor"): return
   
   connResolver = connections.getResolver("tor")
   currentResolutionCount = connResolver.getResolutionCount()
   
   if self._lastResourceFetch != currentResolutionCount:
     self.valsLock.acquire()
     
     newEntries = [] # the new results we'll display
     
     # Fetches new connections and client circuits...
     # newConnections  [(local ip, local port, foreign ip, foreign port)...]
     # newCircuits     {circuitID => (status, purpose, path)...}
     
     newConnections = connResolver.getConnections()
     newCircuits = {}
     
     for circuitID, status, purpose, path in torTools.getConn().getCircuits():
       # Skips established single-hop circuits (these are for directory
       # fetches, not client circuits)
       if not (status == "BUILT" and len(path) == 1):
         newCircuits[circuitID] = (status, purpose, path)
     
     # Populates newEntries with any of our old entries that still exist.
     # This is both for performance and to keep from resetting the uptime
     # attributes. Note that CircEntries are a ConnectionEntry subclass so
     # we need to check for them first.
     
     for oldEntry in self._entries:
       if isinstance(oldEntry, circEntry.CircEntry):
         newEntry = newCircuits.get(oldEntry.circuitID)
         
         if newEntry:
           oldEntry.update(newEntry[0], newEntry[2])
           newEntries.append(oldEntry)
           del newCircuits[oldEntry.circuitID]
       elif isinstance(oldEntry, connEntry.ConnectionEntry):
         connLine = oldEntry.getLines()[0]
         connAttr = (connLine.local.getIpAddr(), connLine.local.getPort(),
                     connLine.foreign.getIpAddr(), connLine.foreign.getPort())
         
         if connAttr in newConnections:
           newEntries.append(oldEntry)
           newConnections.remove(connAttr)
     
     # Reset any display attributes for the entries we're keeping
     for entry in newEntries: entry.resetDisplay()
     
     # Adds any new connection and circuit entries.
     for lIp, lPort, fIp, fPort in newConnections:
       newConnEntry = connEntry.ConnectionEntry(lIp, lPort, fIp, fPort)
       newConnLine = newConnEntry.getLines()[0]
       
       if newConnLine.getType() != connEntry.Category.CIRCUIT:
         newEntries.append(newConnEntry)
         
         # updates exit port and client locale usage information
         if newConnLine.isPrivate():
           if newConnLine.getType() == connEntry.Category.INBOUND:
             # client connection, update locale information
             clientLocale = newConnLine.foreign.getLocale()
             
             if clientLocale:
               self._clientLocaleUsage[clientLocale] = self._clientLocaleUsage.get(clientLocale, 0) + 1
           elif newConnLine.getType() == connEntry.Category.EXIT:
             exitPort = newConnLine.foreign.getPort()
             self._exitPortUsage[exitPort] = self._exitPortUsage.get(exitPort, 0) + 1
     
     for circuitID in newCircuits:
       status, purpose, path = newCircuits[circuitID]
       newEntries.append(circEntry.CircEntry(circuitID, status, purpose, path))
     
     # 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).
     
     categoryTypes = connEntry.Category.values()
     typeCounts = dict((type, 0) for type in categoryTypes)
     for entry in newEntries:
       if isinstance(entry, connEntry.ConnectionEntry):
         typeCounts[entry.getLines()[0].getType()] += 1
       elif isinstance(entry, circEntry.CircEntry):
         typeCounts[connEntry.Category.CIRCUIT] += 1
     
     # makes labels for all the categories with connections (ie,
     # "21 outbound", "1 control", etc)
     countLabels = []
     
     for category in categoryTypes:
       if typeCounts[category] > 0:
         countLabels.append("%i %s" % (typeCounts[category], category.lower()))
     
     if countLabels: self._title = "Connections (%s):" % ", ".join(countLabels)
     else: self._title = "Connections:"
     
     self._entries = newEntries
     
     self._entryLines = []
     for entry in self._entries:
       self._entryLines += entry.getLines()
     
     self.setSortOrder()
     self._lastResourceFetch = currentResolutionCount
     self.valsLock.release()