Example #1
0
  def test_is_running(self, call_mock):
    """
    Exercises multiple use cases for the is_running function.
    """

    # mock response with a linux and bsd resolver
    running_commands = [str_type('irssi'), str_type('moc'), str_type('tor'),
                        str_type('ps'), str_type('  firefox  ')]

    for ps_cmd in (system.IS_RUNNING_PS_LINUX, system.IS_RUNNING_PS_BSD):
      call_mock.side_effect = mock_call(ps_cmd, running_commands)

      self.assertTrue(system.is_running('irssi'))
      self.assertTrue(system.is_running('moc'))
      self.assertTrue(system.is_running('tor'))
      self.assertTrue(system.is_running('ps'))
      self.assertTrue(system.is_running('firefox'))
      self.assertEqual(False, system.is_running('something_else'))

    # mock both calls failing

    call_mock.return_value = None
    call_mock.side_effect = None
    self.assertFalse(system.is_running('irssi'))
    self.assertEqual(None, system.is_running('irssi'))
Example #2
0
    def test_is_running(self, call_mock):
        """
    Exercises multiple use cases for the is_running function.
    """

        # mock response with a linux and bsd resolver
        running_commands = [
            str_type('irssi'),
            str_type('moc'),
            str_type('tor'),
            str_type('ps'),
            str_type('  firefox  ')
        ]

        for ps_cmd in (system.IS_RUNNING_PS_LINUX, system.IS_RUNNING_PS_BSD):
            call_mock.side_effect = mock_call(ps_cmd, running_commands)

            self.assertTrue(system.is_running('irssi'))
            self.assertTrue(system.is_running('moc'))
            self.assertTrue(system.is_running('tor'))
            self.assertTrue(system.is_running('ps'))
            self.assertTrue(system.is_running('firefox'))
            self.assertEqual(False, system.is_running('something_else'))

        # mock both calls failing

        call_mock.return_value = None
        call_mock.side_effect = None
        self.assertFalse(system.is_running('irssi'))
        self.assertEqual(None, system.is_running('irssi'))
Example #3
0
    def test_local_microdescriptors(self):
        """
    Checks a small microdescriptor file with known contents.
    """

        descriptor_path = get_resource('cached-microdescs')

        with open(descriptor_path, 'rb') as descriptor_file:
            descriptors = stem.descriptor.parse_file(descriptor_file,
                                                     'microdescriptor 1.0')

            router = next(descriptors)
            self.assertEqual(FIRST_ONION_KEY, router.onion_key)
            self.assertEqual(None, router.ntor_onion_key)
            self.assertEqual([], router.or_addresses)
            self.assertEqual([], router.family)
            self.assertEqual(
                stem.exit_policy.MicroExitPolicy('reject 1-65535'),
                router.exit_policy)
            self.assertEqual({b'@last-listed': b'2013-02-24 00:18:36'},
                             router.get_annotations())
            self.assertEqual([b'@last-listed 2013-02-24 00:18:36'],
                             router.get_annotation_lines())

            router = next(descriptors)
            self.assertEqual(SECOND_ONION_KEY, router.onion_key)
            self.assertEqual(
                str_type('r5572HzD+PMPBbXlZwBhsm6YEbxnYgis8vhZ1jmdI2k='),
                router.ntor_onion_key)
            self.assertEqual([], router.or_addresses)
            self.assertEqual(['$6141629FA0D15A6AEAEF3A1BEB76E64C767B3174'],
                             router.family)
            self.assertEqual(
                stem.exit_policy.MicroExitPolicy('reject 1-65535'),
                router.exit_policy)
            self.assertEqual({b'@last-listed': b'2013-02-24 00:18:37'},
                             router.get_annotations())
            self.assertEqual([b'@last-listed 2013-02-24 00:18:37'],
                             router.get_annotation_lines())

            router = next(descriptors)
            self.assertEqual(THIRD_ONION_KEY, router.onion_key)
            self.assertEqual(None, router.ntor_onion_key)
            self.assertEqual([(str_type('2001:6b0:7:125::242'), 9001, True)],
                             router.or_addresses)
            self.assertEqual([], router.family)
            self.assertEqual(stem.exit_policy.MicroExitPolicy('accept 80,443'),
                             router.exit_policy)
            self.assertEqual({b'@last-listed': b'2013-02-24 00:18:36'},
                             router.get_annotations())
            self.assertEqual([b'@last-listed 2013-02-24 00:18:36'],
                             router.get_annotation_lines())
Example #4
0
    def test_load_processed_files(self, open_mock):
        """
    Successful load of content.
    """

        test_lines = (
            str_type("/dir/ 0"),
            str_type("/dir/file 12345"),
            str_type("/dir/file with spaces 7138743"),
            str_type("  /dir/with extra space 12345   "),
            str_type("   \t   "),
            str_type(""),
            str_type("/dir/after empty line 12345"),
        )

        expected_value = {
            "/dir/": 0,
            "/dir/file": 12345,
            "/dir/file with spaces": 7138743,
            "/dir/with extra space": 12345,
            "/dir/after empty line": 12345,
        }

        open_mock.return_value = io.StringIO(str_type("\n".join(test_lines)))
        self.assertEqual(expected_value, stem.descriptor.reader.load_processed_files(""))
Example #5
0
    def test_load_processed_files(self, open_mock):
        """
    Successful load of content.
    """

        test_lines = (
            str_type('/dir/ 0'),
            str_type('/dir/file 12345'),
            str_type('/dir/file with spaces 7138743'),
            str_type('  /dir/with extra space 12345   '),
            str_type('   \t   '),
            str_type(''),
            str_type('/dir/after empty line 12345'),
        )

        expected_value = {
            '/dir/': 0,
            '/dir/file': 12345,
            '/dir/file with spaces': 7138743,
            '/dir/with extra space': 12345,
            '/dir/after empty line': 12345,
        }

        open_mock.return_value = io.StringIO(str_type('\n'.join(test_lines)))
        self.assertEqual(expected_value,
                         stem.descriptor.reader.load_processed_files(''))
Example #6
0
  def test_load_processed_files(self, open_mock):
    """
    Successful load of content.
    """

    test_lines = (
      str_type('/dir/ 0'),
      str_type('/dir/file 12345'),
      str_type('/dir/file with spaces 7138743'),
      str_type('  /dir/with extra space 12345   '),
      str_type('   \t   '),
      str_type(''),
      str_type('/dir/after empty line 12345'),
    )

    expected_value = {
      '/dir/': 0,
      '/dir/file': 12345,
      '/dir/file with spaces': 7138743,
      '/dir/with extra space': 12345,
      '/dir/after empty line': 12345,
    }

    open_mock.return_value = io.StringIO(str_type('\n'.join(test_lines)))
    self.assertEqual(expected_value, stem.descriptor.reader.load_processed_files(''))
Example #7
0
    def test_load_processed_files_empty(self, open_mock):
        """
    Tests the load_processed_files() function with an empty file.
    """

        open_mock.return_value = io.StringIO(str_type(''))
        self.assertEqual({}, stem.descriptor.reader.load_processed_files(''))
Example #8
0
  def test_load_processed_files_empty(self, open_mock):
    """
    Tests the load_processed_files() function with an empty file.
    """

    open_mock.return_value = io.StringIO(str_type(''))
    self.assertEqual({}, stem.descriptor.reader.load_processed_files(''))
Example #9
0
    def test_load_processed_files_no_timestamp(self, open_mock):
        """
    Tests the load_processed_files() function content that is malformed because
    it is missing the timestamp.
    """

        open_mock.return_value = io.StringIO(str_type("/dir/file "))
        self.assertRaises(TypeError, stem.descriptor.reader.load_processed_files, "")
Example #10
0
def _parse_exit_policy(descriptor, entries):
  if hasattr(descriptor, '_unparsed_exit_policy'):
    if descriptor._unparsed_exit_policy == [str_type('reject *:*')]:
      descriptor.exit_policy = REJECT_ALL_POLICY
    else:
      descriptor.exit_policy = stem.exit_policy.ExitPolicy(*descriptor._unparsed_exit_policy)

    del descriptor._unparsed_exit_policy
Example #11
0
  def test_load_processed_files_malformed_timestamp(self, open_mock):
    """
    Tests the load_processed_files() function content that is malformed because
    it has a non-numeric timestamp.
    """

    open_mock.return_value = io.StringIO(str_type('/dir/file 123a'))
    self.assertRaises(TypeError, stem.descriptor.reader.load_processed_files, '')
Example #12
0
  def test_load_processed_files_malformed_file(self, open_mock):
    """
    Tests the load_processed_files() function content that is malformed because
    it has an invalid file path.
    """

    open_mock.return_value = io.StringIO(str_type('not_an_absolute_file 12345'))
    self.assertRaises(TypeError, stem.descriptor.reader.load_processed_files, '')
Example #13
0
def _parse_exit_policy(descriptor, entries):
    if hasattr(descriptor, '_unparsed_exit_policy'):
        if descriptor._unparsed_exit_policy == [str_type('reject *:*')]:
            descriptor.exit_policy = REJECT_ALL_POLICY
        else:
            descriptor.exit_policy = stem.exit_policy.ExitPolicy(
                *descriptor._unparsed_exit_policy)

        del descriptor._unparsed_exit_policy
Example #14
0
    def test_load_processed_files_malformed_timestamp(self, open_mock):
        """
    Tests the load_processed_files() function content that is malformed because
    it has a non-numeric timestamp.
    """

        open_mock.return_value = io.StringIO(str_type('/dir/file 123a'))
        self.assertRaises(TypeError,
                          stem.descriptor.reader.load_processed_files, '')
Example #15
0
    def test_load_processed_files_no_file(self, open_mock):
        """
    Tests the load_processed_files() function content that is malformed because
    it is missing the file path.
    """

        open_mock.return_value = io.StringIO(str_type(' 12345'))
        self.assertRaises(TypeError,
                          stem.descriptor.reader.load_processed_files, '')
Example #16
0
  def test_local_microdescriptors(self):
    """
    Checks a small microdescriptor file with known contents.
    """

    descriptor_path = get_resource('cached-microdescs')

    with open(descriptor_path, 'rb') as descriptor_file:
      descriptors = stem.descriptor.parse_file(descriptor_file, 'microdescriptor 1.0')

      router = next(descriptors)
      self.assertEqual(FIRST_ONION_KEY, router.onion_key)
      self.assertEqual(None, router.ntor_onion_key)
      self.assertEqual([], router.or_addresses)
      self.assertEqual([], router.family)
      self.assertEqual(stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy)
      self.assertEqual({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations())
      self.assertEqual([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines())

      router = next(descriptors)
      self.assertEqual(SECOND_ONION_KEY, router.onion_key)
      self.assertEqual(str_type('r5572HzD+PMPBbXlZwBhsm6YEbxnYgis8vhZ1jmdI2k='), router.ntor_onion_key)
      self.assertEqual([], router.or_addresses)
      self.assertEqual(['$6141629FA0D15A6AEAEF3A1BEB76E64C767B3174'], router.family)
      self.assertEqual(stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy)
      self.assertEqual({b'@last-listed': b'2013-02-24 00:18:37'}, router.get_annotations())
      self.assertEqual([b'@last-listed 2013-02-24 00:18:37'], router.get_annotation_lines())

      router = next(descriptors)
      self.assertEqual(THIRD_ONION_KEY, router.onion_key)
      self.assertEqual(None, router.ntor_onion_key)
      self.assertEqual([(str_type('2001:6b0:7:125::242'), 9001, True)], router.or_addresses)
      self.assertEqual([], router.family)
      self.assertEqual(stem.exit_policy.MicroExitPolicy('accept 80,443'), router.exit_policy)
      self.assertEqual({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations())
      self.assertEqual([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines())
Example #17
0
from test.mocking import (
    get_relay_server_descriptor,
    get_bridge_server_descriptor,
    CRYPTO_BLOB,
)

from test.unit.descriptor import get_resource

try:
    # added in python 3.3
    from unittest.mock import Mock, patch
except ImportError:
    from mock import Mock, patch

TARFILE_FINGERPRINTS = set([
    str_type('B6D83EC2D9E18B0A7A33428F8CFA9C536769E209'),
    str_type('E0BD57A11F00041A9789577C53A1B784473669E4'),
    str_type('1F43EE37A0670301AD9CB555D94AFEC2C89FDE86'),
])


class TestServerDescriptor(unittest.TestCase):
    def test_with_tarfile_path(self):
        """
    Fetch server descriptors via parse_file() for a tarfile path.
    """

        descriptors = list(
            stem.descriptor.parse_file(get_resource('descriptor_archive.tar')))
        self.assertEqual(3, len(descriptors))
Example #18
0
  def test_flag_thresholds(self):
    """
    Parses the flag-thresholds entry.
    """

    test_values = (
      ('', {}),
      ('fast-speed=40960', {str_type('fast-speed'): 40960}),    # numeric value
      ('guard-wfu=94.669%', {str_type('guard-wfu'): 0.94669}),  # percentage value
      ('guard-wfu=94.669% guard-tk=691200', {str_type('guard-wfu'): 0.94669, str_type('guard-tk'): 691200}),  # multiple values
    )

    for test_value, expected_value in test_values:
      document = get_network_status_document_v3({'vote-status': 'vote', 'flag-thresholds': test_value})
      self.assertEqual(expected_value, document.flag_thresholds)

    # parses a full entry found in an actual vote

    full_line = 'stable-uptime=693369 stable-mtbf=153249 fast-speed=40960 guard-wfu=94.669% guard-tk=691200 guard-bw-inc-exits=174080 guard-bw-exc-exits=184320 enough-mtbf=1'

    expected_value = {
      str_type('stable-uptime'): 693369,
      str_type('stable-mtbf'): 153249,
      str_type('fast-speed'): 40960,
      str_type('guard-wfu'): 0.94669,
      str_type('guard-tk'): 691200,
      str_type('guard-bw-inc-exits'): 174080,
      str_type('guard-bw-exc-exits'): 184320,
      str_type('enough-mtbf'): 1,
    }

    document = get_network_status_document_v3({'vote-status': 'vote', 'flag-thresholds': full_line})
    self.assertEqual(expected_value, document.flag_thresholds)

    test_values = (
      'stable-uptime 693369',   # not a key=value mapping
      'stable-uptime=a693369',  # non-numeric value
      'guard-wfu=94.669%%',     # double quote
      'stable-uptime=693369\tstable-mtbf=153249',  # non-space divider
    )

    for test_value in test_values:
      content = get_network_status_document_v3({'vote-status': 'vote', 'flag-thresholds': test_value}, content = True)
      self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True)

      document = NetworkStatusDocumentV3(content, False)
      self.assertEqual({}, document.flag_thresholds)
Example #19
0
    def __init__(self, raw_contents, validate=True, annotations=None):
        """
    Server descriptor constructor, created from an individual relay's
    descriptor content (as provided by 'GETINFO desc/*', cached descriptors,
    and metrics).

    By default this validates the descriptor's content as it's parsed. This
    validation can be disables to either improve performance or be accepting of
    malformed data.

    :param str raw_contents: descriptor content provided by the relay
    :param bool validate: checks the validity of the descriptor's content if
      **True**, skips these checks otherwise
    :param list annotations: lines that appeared prior to the descriptor

    :raises: **ValueError** if the contents is malformed and validate is True
    """

        super(ServerDescriptor, self).__init__(raw_contents)

        # Only a few things can be arbitrary bytes according to the dir-spec, so
        # parsing them separately.

        self.platform = _get_bytes_field('platform', raw_contents)
        self.contact = _get_bytes_field('contact', raw_contents)

        raw_contents = stem.util.str_tools._to_unicode(raw_contents)

        self.nickname = None
        self.fingerprint = None
        self.published = None

        self.address = None
        self.or_port = None
        self.socks_port = None
        self.dir_port = None

        self.tor_version = None
        self.operating_system = None
        self.uptime = None
        self.exit_policy = None
        self.exit_policy_v6 = DEFAULT_IPV6_EXIT_POLICY
        self.family = set()

        self.average_bandwidth = None
        self.burst_bandwidth = None
        self.observed_bandwidth = None

        self.link_protocols = None
        self.circuit_protocols = None
        self.hibernating = False
        self.allow_single_hop_exits = False
        self.extra_info_cache = False
        self.extra_info_digest = None
        self.hidden_service_dir = None
        self.eventdns = None
        self.or_addresses = []

        self.read_history_end = None
        self.read_history_interval = None
        self.read_history_values = None

        self.write_history_end = None
        self.write_history_interval = None
        self.write_history_values = None

        self._unrecognized_lines = []

        self._annotation_lines = annotations if annotations else []

        # A descriptor contains a series of 'keyword lines' which are simply a
        # keyword followed by an optional value. Lines can also be followed by a
        # signature block.
        #
        # We care about the ordering of 'accept' and 'reject' entries because this
        # influences the resulting exit policy, but for everything else the order
        # does not matter so breaking it into key / value pairs.

        entries, policy = _get_descriptor_components(raw_contents, validate,
                                                     ('accept', 'reject'))

        if policy == [str_type('reject *:*')]:
            self.exit_policy = REJECT_ALL_POLICY
        else:
            self.exit_policy = stem.exit_policy.ExitPolicy(*policy)

        self._parse(entries, validate)

        if validate:
            self._check_constraints(entries)
Example #20
0
from test.mocking import (
  get_relay_server_descriptor,
  get_bridge_server_descriptor,
  CRYPTO_BLOB,
)

from test.unit.descriptor import get_resource

try:
  # added in python 3.3
  from unittest.mock import Mock, patch
except ImportError:
  from mock import Mock, patch

TARFILE_FINGERPRINTS = set([
  str_type('B6D83EC2D9E18B0A7A33428F8CFA9C536769E209'),
  str_type('E0BD57A11F00041A9789577C53A1B784473669E4'),
  str_type('1F43EE37A0670301AD9CB555D94AFEC2C89FDE86'),
])


class TestServerDescriptor(unittest.TestCase):
  def test_with_tarfile_path(self):
    """
    Fetch server descriptors via parse_file() for a tarfile path.
    """

    descriptors = list(stem.descriptor.parse_file(get_resource('descriptor_archive.tar')))
    self.assertEqual(3, len(descriptors))

    fingerprints = set([desc.fingerprint for desc in descriptors])
Example #21
0
CIRC_CONTENT = '650 CIRC %d %s \
%s \
PURPOSE=%s'

PATH_CONTENT = '$%s=%s,$%s=%s,$%s=%s'

LIST_CIRCUITS_OUTPUT = str_type("""\

Circuit 4 (GENERAL)
 |- B1FA7D51B8B6F0CB585D944F450E7C06EDE7E44C (ByTORAndTheSnowDog, 173.209.180.61)
 |- 0DD9935C5E939CFA1E07B8DDA6D91C1A2A9D9338 (afo02, 87.238.194.176)
 +- DB3B1CFBD3E4D97B84B548ADD5B9A31451EEC4CC (edwardsnowden3, 109.163.234.10)

Circuit 6 (GENERAL)
 |- B1FA7D51B8B6F0CB585D944F450E7C06EDE7E44C (ByTORAndTheSnowDog, 173.209.180.61)
 |- EC01CB4766BADC1611678555CE793F2A7EB2D723 (sprockets, 46.165.197.96)
 +- 9EA317EECA56BDF30CAEB208A253FB456EDAB1A0 (bolobolo1, 96.47.226.20)

Circuit 10 (GENERAL)
 |- B1FA7D51B8B6F0CB585D944F450E7C06EDE7E44C (ByTORAndTheSnowDog, 173.209.180.61)
 |- 00C2C2A16AEDB51D5E5FB7D6168FC66B343D822F (ph3x, 86.59.119.83)
 +- 65242C91BFF30F165DA4D132C81A9EBA94B71D62 (torexit16, 176.67.169.171)
""")

EXIT_USED_OUTPUT = str_type("""\
Tracking requests for tor exits. Press 'enter' to end.

Exit relay for our connection to 64.15.112.44:80
  address: 31.172.30.2:443
  fingerprint: A59E1E7C7EAEE083D756EE1FF6EC31CA3D8651D7
Example #22
0
CIRC_CONTENT = '650 CIRC %d %s \
%s \
PURPOSE=%s'

PATH_CONTENT = '$%s=%s,$%s=%s,$%s=%s'

LIST_CIRCUITS_OUTPUT = str_type("""\

Circuit 4 (GENERAL)
 |- B1FA7D51B8B6F0CB585D944F450E7C06EDE7E44C (ByTORAndTheSnowDog, 173.209.180.61)
 |- 0DD9935C5E939CFA1E07B8DDA6D91C1A2A9D9338 (afo02, 87.238.194.176)
 +- DB3B1CFBD3E4D97B84B548ADD5B9A31451EEC4CC (edwardsnowden3, 109.163.234.10)

Circuit 6 (GENERAL)
 |- B1FA7D51B8B6F0CB585D944F450E7C06EDE7E44C (ByTORAndTheSnowDog, 173.209.180.61)
 |- EC01CB4766BADC1611678555CE793F2A7EB2D723 (sprockets, 46.165.197.96)
 +- 9EA317EECA56BDF30CAEB208A253FB456EDAB1A0 (bolobolo1, 96.47.226.20)

Circuit 10 (GENERAL)
 |- B1FA7D51B8B6F0CB585D944F450E7C06EDE7E44C (ByTORAndTheSnowDog, 173.209.180.61)
 |- 00C2C2A16AEDB51D5E5FB7D6168FC66B343D822F (ph3x, 86.59.119.83)
 +- 65242C91BFF30F165DA4D132C81A9EBA94B71D62 (torexit16, 176.67.169.171)
""")

EXIT_USED_OUTPUT = str_type("""\
Tracking requests for tor exits. Press 'enter' to end.

Exit relay for our connection to 64.15.112.44:80
  address: 31.172.30.2:443
  fingerprint: A59E1E7C7EAEE083D756EE1FF6EC31CA3D8651D7