def _update(self): self._vals = Sampling.create(self._vals) if self._vals.fd_used and self._vals.fd_limit != -1: fd_percent = 100 * self._vals.fd_used // self._vals.fd_limit if fd_percent >= 90: log_msg = "Tor's file descriptor usage is at %s%%. If you run out Tor will be unable to continue functioning." % fd_percent log.log_once('fd_used_at_ninety_percent', log.WARN, log_msg) log.DEDUPLICATION_MESSAGE_IDS.add('fd_used_at_sixty_percent') elif fd_percent >= 60: log_msg = "Tor's file descriptor usage is at %s%%." % fd_percent log.log_once('fd_used_at_sixty_percent', log.NOTICE, log_msg) if self._vals.is_connected: if not self._reported_inactive and ( time.time() - self._vals.last_heartbeat) >= 10: self._reported_inactive = True log.notice('Relay unresponsive (last heartbeat: %s)' % time.ctime(self._vals.last_heartbeat)) elif self._reported_inactive and (time.time() - self._vals.last_heartbeat) < 10: self._reported_inactive = False log.notice('Relay resumed') self.redraw()
def _update(self): self._vals = Sampling.create(self._vals) if self._vals.fd_used and self._vals.fd_limit != -1: fd_percent = 100 * self._vals.fd_used / self._vals.fd_limit if fd_percent >= 90: log_msg = msg('panel.header.fd_used_at_ninety_percent', percentage=fd_percent) log.log_once('fd_used_at_ninety_percent', log.WARN, log_msg) log.DEDUPLICATION_MESSAGE_IDS.add('fd_used_at_sixty_percent') elif fd_percent >= 60: log_msg = msg('panel.header.fd_used_at_sixty_percent', percentage=fd_percent) log.log_once('fd_used_at_sixty_percent', log.NOTICE, log_msg) if self._vals.is_connected: if not self._reported_inactive and ( time.time() - self._vals.last_heartbeat) >= 10: self._reported_inactive = True log.notice('Relay unresponsive (last heartbeat: %s)' % time.ctime(self._vals.last_heartbeat)) elif self._reported_inactive and (time.time() - self._vals.last_heartbeat) < 10: self._reported_inactive = False log.notice('Relay resumed') self.redraw()
def is_crypto_available(): """ Checks if the cryptography functions we use are available. This is used for verifying relay descriptor signatures. :returns: **True** if we can use the cryptography module and **False** otherwise """ try: from cryptography.utils import int_from_bytes, int_to_bytes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives.serialization import load_der_public_key if not hasattr(rsa.RSAPrivateKey, 'sign'): raise ImportError() return True except ImportError: from stem.util import log log.log_once('stem.prereq.is_crypto_available', log.INFO, CRYPTO_UNAVAILABLE) return False
def get_value(self, key, default=None, multiple=False): """ This provides the current value associated with a given key. :param str key: config setting to be fetched :param object default: value provided if no such key exists :param bool multiple: provides back a list of all values if **True**, otherwise this returns the last loaded configuration value :returns: **str** or **list** of string configuration values associated with the given key, providing the default if no such key exists """ with self._contents_lock: if key in self._contents: self._requested_keys.add(key) if multiple: return self._contents[key] else: return self._contents[key][-1] else: message_id = 'stem.util.conf.missing_config_key_%s' % key log.log_once( message_id, log.TRACE, "config entry '%s' not found, defaulting to '%s'" % (key, default)) return default
def get_value(self, key, default = None, multiple = False): """ This provides the current value associated with a given key. :param str key: config setting to be fetched :param object default: value provided if no such key exists :param bool multiple: provides back a list of all values if **True**, otherwise this returns the last loaded configuration value :returns: **str** or **list** of string configuration values associated with the given key, providing the default if no such key exists """ with self._contents_lock: if key in self._contents: self._requested_keys.add(key) if multiple: return self._contents[key] else: return self._contents[key][-1] else: message_id = "stem.util.conf.missing_config_key_%s" % key log.log_once(message_id, log.TRACE, "config entry '%s' not found, defaulting to '%s'" % (key, default)) return default
def is_crypto_available(): """ Checks if the pycrypto functions we use are available. :returns: **True** if we can use pycrypto and **False** otherwise """ global IS_CRYPTO_AVAILABLE if IS_CRYPTO_AVAILABLE is None: from stem.util import log try: from Crypto.PublicKey import RSA from Crypto.Util import asn1 from Crypto.Util.number import long_to_bytes IS_CRYPTO_AVAILABLE = True except ImportError: IS_CRYPTO_AVAILABLE = False # the code that verifies relay descriptor signatures uses the python-crypto library msg = "Unable to import the pycrypto module. Because of this we'll be unable to verify descriptor signature integrity. You can get pycrypto from: https://www.dlitz.net/software/pycrypto/" log.log_once("stem.prereq.is_crypto_available", log.INFO, msg) return IS_CRYPTO_AVAILABLE
def is_rsa_available(): global IS_RSA_AVAILABLE if IS_RSA_AVAILABLE == None: try: import rsa IS_RSA_AVAILABLE = True except ImportError: IS_RSA_AVAILABLE = False msg = "Unable to import the rsa module. Because of this we'll be unable to verify descriptor signature integrity." log.log_once("stem.prereq.is_rsa_available", log.INFO, msg) return IS_RSA_AVAILABLE
def is_crypto_available(): global IS_CRYPTO_AVAILABLE if IS_CRYPTO_AVAILABLE is None: try: from Crypto.PublicKey import RSA from Crypto.Util import asn1 from Crypto.Util.number import long_to_bytes IS_CRYPTO_AVAILABLE = True except ImportError: IS_CRYPTO_AVAILABLE = False # the code that verifies relay descriptor signatures uses the python-crypto library msg = "Unable to import the crypto module. Because of this we'll be unable to verify descriptor signature integrity." log.log_once("stem.prereq.is_crypto_available", log.INFO, msg) return IS_CRYPTO_AVAILABLE
def is_lzma_available(): """ Checks if the `lzma module <https://docs.python.org/3/library/lzma.html>`_ is available. This was added as a builtin in Python 3.3. .. versionadded:: 1.7.0 :returns: **True** if we can use the lzma module and **False** otherwise """ try: import lzma return True except ImportError: from stem.util import log log.log_once('stem.prereq.is_lzma_available', log.INFO, LZMA_UNAVAILABLE) return False
def _is_pynacl_available(): """ Checks if the pynacl functions we use are available. This is used for verifying ed25519 certificates in relay descriptor signatures. :returns: **True** if we can use pynacl and **False** otherwise """ from stem.util import log try: from nacl import encoding from nacl import signing return True except ImportError: log.log_once('stem.prereq._is_pynacl_available', log.INFO, PYNACL_UNAVAILABLE) return False
def is_crypto_available(): """ Checks if the pycrypto functions we use are available. This is used for verifying relay descriptor signatures. :returns: **True** if we can use pycrypto and **False** otherwise """ from stem.util import log try: from Crypto.PublicKey import RSA from Crypto.Util import asn1 from Crypto.Util.number import long_to_bytes return True except ImportError: log.log_once('stem.prereq.is_crypto_available', log.INFO, CRYPTO_UNAVAILABLE) return False
def _log_if_unrecognized(self, attr, attr_enum): """ Checks if an attribute exists in a given enumeration, logging a message if it isn't. Attributes can either be for a string or collection of strings :param str attr: name of the attribute to check :param stem.util.enum.Enum enum: enumeration to check against """ attr_values = getattr(self, attr) if attr_values: if isinstance(attr_values, (bytes, unicode)): attr_values = [attr_values] for value in attr_values: if not value in attr_enum: log_id = "event.%s.unknown_%s.%s" % (self.type.lower(), attr, value) unrecognized_msg = "%s event had an unrecognized %s (%s). Maybe a new addition to the control protocol? Full Event: '%s'" % (self.type, attr, value, self) log.log_once(log_id, log.INFO, unrecognized_msg)
def is_zstd_available(): """ Checks if the `zstd module <https://pypi.org/project/zstandard/>`_ is available. .. versionadded:: 1.7.0 :returns: **True** if we can use the zstd module and **False** otherwise """ try: # Unfortunately the zstandard module uses the same namespace as another # zstd module (https://pypi.org/project/zstd/), so we need to # differentiate them. import zstd return hasattr(zstd, 'ZstdDecompressor') except ImportError: from stem.util import log log.log_once('stem.prereq.is_zstd_available', log.INFO, ZSTD_UNAVAILABLE) return False
def _is_crypto_ed25519_supported(): """ Checks if ed25519 is supported by current versions of the cryptography package and OpenSSL. This is used for verifying ed25519 certificates in relay descriptor signatures. :returns: **True** if ed25519 is supported and **False** otherwise """ if not is_crypto_available(): return False from stem.util import log from cryptography.hazmat.backends.openssl.backend import backend if hasattr(backend, 'ed25519_supported') and backend.ed25519_supported(): return True else: log.log_once('stem.prereq._is_crypto_ed25519_supported', log.INFO, ED25519_UNSUPPORTED) return False
def is_crypto_available(ed25519 = False): """ Checks if the cryptography functions we use are available. This is used for verifying relay descriptor signatures. :param bool ed25519: check for `ed25519 support <https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ed25519/>`_, which requires both cryptography version 2.6 and OpenSSL support :returns: **True** if we can use the cryptography module and **False** otherwise """ from stem.util import log try: from cryptography.utils import int_from_bytes, int_to_bytes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends.openssl.backend import backend from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives.serialization import load_der_public_key if not hasattr(rsa.RSAPrivateKey, 'sign'): raise ImportError() if ed25519: # The following import confirms cryptography support (ie. version 2.6+), # whereas ed25519_supported() checks for OpenSSL bindings. from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey if not hasattr(backend, 'ed25519_supported') or not backend.ed25519_supported(): log.log_once('stem.prereq._is_crypto_ed25519_supported', log.INFO, ED25519_UNSUPPORTED) return False return True except ImportError: log.log_once('stem.prereq.is_crypto_available', log.INFO, CRYPTO_UNAVAILABLE) return False
def _update(self): self._vals = Sampling.create(self._vals) if self._vals.fd_used and self._vals.fd_limit != -1: fd_percent = 100 * self._vals.fd_used / self._vals.fd_limit if fd_percent >= 90: log_msg = msg('panel.header.fd_used_at_ninety_percent', percentage = fd_percent) log.log_once('fd_used_at_ninety_percent', log.WARN, log_msg) log.DEDUPLICATION_MESSAGE_IDS.add('fd_used_at_sixty_percent') elif fd_percent >= 60: log_msg = msg('panel.header.fd_used_at_sixty_percent', percentage = fd_percent) log.log_once('fd_used_at_sixty_percent', log.NOTICE, log_msg) if self._vals.is_connected: if not self._reported_inactive and (time.time() - self._vals.last_heartbeat) >= 10: self._reported_inactive = True log.notice('Relay unresponsive (last heartbeat: %s)' % time.ctime(self._vals.last_heartbeat)) elif self._reported_inactive and (time.time() - self._vals.last_heartbeat) < 10: self._reported_inactive = False log.notice('Relay resumed') self.redraw()
def _update(self): previous_height = self.get_height() self._vals = get_sampling(self._vals) if self._vals.fd_used and self._vals.fd_limit != -1: fd_percent = 100 * self._vals.fd_used / self._vals.fd_limit if fd_percent >= 90: log_msg = msg('panel.header.fd_used_at_ninety_percent', percentage = fd_percent) log.log_once('fd_used_at_ninety_percent', log.WARN, log_msg) log.DEDUPLICATION_MESSAGE_IDS.add('fd_used_at_sixty_percent') elif fd_percent >= 60: log_msg = msg('panel.header.fd_used_at_sixty_percent', percentage = fd_percent) log.log_once('fd_used_at_sixty_percent', log.NOTICE, log_msg) if previous_height != self.get_height(): # We're toggling between being a relay and client, causing the height # of this panel to change. Redraw all content so we don't get # overlapping content. nyx.controller.get_controller().redraw() else: self.redraw(True) # just need to redraw ourselves
def _parse_message(self): # Example: # 250-PROTOCOLINFO 1 # 250-AUTH METHODS=COOKIE COOKIEFILE="/home/atagar/.tor/control_auth_cookie" # 250-VERSION Tor="0.2.1.30" # 250 OK self.protocol_version = None self.tor_version = None self.auth_methods = () self.unknown_auth_methods = () self.cookie_path = None auth_methods, unknown_auth_methods = [], [] remaining_lines = list(self) if not self.is_ok() or not remaining_lines.pop() == "OK": raise stem.ProtocolError("PROTOCOLINFO response didn't have an OK status:\n%s" % self) # sanity check that we're a PROTOCOLINFO response if not remaining_lines[0].startswith("PROTOCOLINFO"): raise stem.ProtocolError("Message is not a PROTOCOLINFO response:\n%s" % self) while remaining_lines: line = remaining_lines.pop(0) line_type = line.pop() if line_type == "PROTOCOLINFO": # Line format: # FirstLine = "PROTOCOLINFO" SP PIVERSION CRLF # PIVERSION = 1*DIGIT if line.is_empty(): raise stem.ProtocolError("PROTOCOLINFO response's initial line is missing the protocol version: %s" % line) try: self.protocol_version = int(line.pop()) except ValueError: raise stem.ProtocolError("PROTOCOLINFO response version is non-numeric: %s" % line) # The piversion really should be "1" but, according to the spec, tor # does not necessarily need to provide the PROTOCOLINFO version that we # requested. Log if it's something we aren't expecting but still make # an effort to parse like a v1 response. if self.protocol_version != 1: log.info("We made a PROTOCOLINFO version 1 query but got a version %i response instead. We'll still try to use it, but this may cause problems." % self.protocol_version) elif line_type == "AUTH": # Line format: # AuthLine = "250-AUTH" SP "METHODS=" AuthMethod *("," AuthMethod) # *(SP "COOKIEFILE=" AuthCookieFile) CRLF # AuthMethod = "NULL" / "HASHEDPASSWORD" / "COOKIE" # AuthCookieFile = QuotedString # parse AuthMethod mapping if not line.is_next_mapping("METHODS"): raise stem.ProtocolError("PROTOCOLINFO response's AUTH line is missing its mandatory 'METHODS' mapping: %s" % line) for method in line.pop_mapping()[1].split(","): if method == "NULL": auth_methods.append(AuthMethod.NONE) elif method == "HASHEDPASSWORD": auth_methods.append(AuthMethod.PASSWORD) elif method == "COOKIE": auth_methods.append(AuthMethod.COOKIE) elif method == "SAFECOOKIE": auth_methods.append(AuthMethod.SAFECOOKIE) else: unknown_auth_methods.append(method) message_id = "stem.response.protocolinfo.unknown_auth_%s" % method log.log_once(message_id, log.INFO, "PROTOCOLINFO response included a type of authentication that we don't recognize: %s" % method) # our auth_methods should have a single AuthMethod.UNKNOWN entry if # any unknown authentication methods exist if not AuthMethod.UNKNOWN in auth_methods: auth_methods.append(AuthMethod.UNKNOWN) # parse optional COOKIEFILE mapping (quoted and can have escapes) if line.is_next_mapping("COOKIEFILE", True, True): self.cookie_path = line.pop_mapping(True, True)[1] elif line_type == "VERSION": # Line format: # VersionLine = "250-VERSION" SP "Tor=" TorVersion OptArguments CRLF # TorVersion = QuotedString if not line.is_next_mapping("Tor", True): raise stem.ProtocolError("PROTOCOLINFO response's VERSION line is missing its mandatory tor version mapping: %s" % line) try: self.tor_version = stem.version.Version(line.pop_mapping(True)[1]) except ValueError as exc: raise stem.ProtocolError(exc) else: log.debug("Unrecognized PROTOCOLINFO line type '%s', ignoring it: %s" % (line_type, line)) self.auth_methods = tuple(auth_methods) self.unknown_auth_methods = tuple(unknown_auth_methods)