示例#1
0
    def test_votes_by_bandwidth_authorities(self, stdout_mock,
                                            authorities_mock, query_mock):
        authorities_mock().values.return_value = [
            DIRECTORY_AUTHORITIES['gabelmoo'],
            DIRECTORY_AUTHORITIES['moria1'],
            DIRECTORY_AUTHORITIES['maatuska'],
        ]

        entry_with_measurement = RouterStatusEntryV3.create(
            {'w': 'Bandwidth=1 Measured=1'})
        entry_without_measurement = RouterStatusEntryV3.create()

        query1 = Mock()
        query1.download_url = 'http://131.188.40.189:80/tor/status-vote/current/authority'
        query1.run.return_value = [entry_with_measurement] * 5935 + [
            entry_without_measurement
        ] * 1332

        query2 = Mock()
        query2.download_url = 'http://128.31.0.39:9131/tor/status-vote/current/authority'
        query2.run.return_value = [entry_with_measurement
                                   ] * 6647 + [entry_without_measurement] * 625

        query3 = Mock()
        query3.download_url = 'http://171.25.193.9:443/tor/status-vote/current/authority'
        query3.run.return_value = [entry_with_measurement] * 6313 + [
            entry_without_measurement
        ] * 1112

        query_mock.side_effect = [query1, query2, query3]

        import votes_by_bandwidth_authorities

        self.assertEqual(EXPECTED_VOTES_BY_BANDWIDTH_AUTHORITIES,
                         stdout_mock.getvalue())
示例#2
0
  def test_votes_by_bandwidth_authorities(self, query_mock, authorities_mock, stdout_mock):
    directory_values = [
      DIRECTORY_AUTHORITIES['gabelmoo'],
      DIRECTORY_AUTHORITIES['moria1'],
      DIRECTORY_AUTHORITIES['maatuska'],
    ]

    directory_values[0].address = '131.188.40.189'
    authorities_mock().values.return_value = directory_values

    entry_with_measurement = RouterStatusEntryV3.create({'w': 'Bandwidth=1 Measured=1'})
    entry_without_measurement = RouterStatusEntryV3.create()

    query1 = Mock()
    query1.download_url = 'http://131.188.40.189:80/tor/status-vote/current/authority'
    query1.run.return_value = [entry_with_measurement] * 5935 + [entry_without_measurement] * 1332

    query2 = Mock()
    query2.download_url = 'http://128.31.0.39:9131/tor/status-vote/current/authority'
    query2.run.return_value = [entry_with_measurement] * 6647 + [entry_without_measurement] * 625

    query3 = Mock()
    query3.download_url = 'http://171.25.193.9:443/tor/status-vote/current/authority'
    query3.run.return_value = [entry_with_measurement] * 6313 + [entry_without_measurement] * 1112

    query_mock.side_effect = [query1, query2, query3]

    exec_documentation_example('votes_by_bandwidth_authorities.py')
    self.assertCountEqual(VOTES_BY_BANDWIDTH_AUTHORITIES_OUTPUT.splitlines(), stdout_mock.getvalue().splitlines())
示例#3
0
    def test_microdescriptor_hashes(self):
        """
    Handles a variety of 'm' lines.
    """

        test_values = {
            '8,9,10,11,12': [([8, 9, 10, 11, 12], {})],
            '8,9,10,11,12 sha256=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs':
            [([8, 9, 10, 11, 12], {
                'sha256': 'g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs'
            })],
            '8,9,10,11,12 sha256=g1vx9si329muxV md5=3tquWIXXySNOIwRGMeAESKs/v4DWs':
            [([8, 9, 10, 11, 12], {
                'sha256': 'g1vx9si329muxV',
                'md5': '3tquWIXXySNOIwRGMeAESKs/v4DWs'
            })],
        }

        for m_line, expected in test_values.items():
            content = get_router_status_entry_v3({'m': m_line}, content=True)
            entry = RouterStatusEntryV3(content, document=vote_document())
            self.assertEqual(expected, entry.microdescriptor_hashes)

        # try with multiple 'm' lines

        content = get_router_status_entry_v3(content=True)
        content += b'\nm 11,12 sha256=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs'
        content += b'\nm 31,32 sha512=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs'

        expected = [
            ([11, 12], {
                'sha256': 'g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs'
            }),
            ([31, 32], {
                'sha512': 'g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs'
            }),
        ]

        entry = RouterStatusEntryV3(content, document=vote_document())
        self.assertEqual(expected, entry.microdescriptor_hashes)

        # try without a document
        content = get_router_status_entry_v3({'m': '8,9,10,11,12'},
                                             content=True)
        self._expect_invalid_attr(content,
                                  'microdescriptor_hashes',
                                  expected_value=[])

        # tries some invalid inputs
        test_values = (
            '',
            '4,a,2',
            '1,2,3 stuff',
        )

        for m_line in test_values:
            content = get_router_status_entry_v3({'m': m_line}, content=True)
            self.assertRaises(ValueError, RouterStatusEntryV3, content, True,
                              vote_document())
示例#4
0
  def test_missing_fields(self):
    """
    Parses a router status entry that's missing fields.
    """

    expect_invalid_attr_for_text(self, RouterStatusEntryV3.content(exclude = ('r', 's')), 'address')
    expect_invalid_attr_for_text(self, RouterStatusEntryV3.content(exclude = ('r',)), 'address')
    expect_invalid_attr_for_text(self, RouterStatusEntryV3.content(exclude = ('s',)), 'flags')
示例#5
0
  def test_missing_fields(self):
    """
    Parses a router status entry that's missing fields.
    """

    expect_invalid_attr_for_text(self, RouterStatusEntryV3.content(exclude = ('r', 's')), 'address')
    expect_invalid_attr_for_text(self, RouterStatusEntryV3.content(exclude = ('r',)), 'address')
    expect_invalid_attr_for_text(self, RouterStatusEntryV3.content(exclude = ('s',)), 'flags')
示例#6
0
    def test_blank_lines(self):
        """
    Includes blank lines, which should be ignored.
    """

        content = RouterStatusEntryV3.content() + b'\n\nv Tor 0.2.2.35\n\n'
        entry = RouterStatusEntryV3(content)
        self.assertEqual('Tor 0.2.2.35', entry.version_line)
示例#7
0
  def test_proceeding_line(self):
    """
    Includes content prior to the 'r' line.
    """

    content = b'z some stuff\n' + RouterStatusEntryV3.content()
    self.assertRaises(ValueError, RouterStatusEntryV3, content, True)
    self.assertEqual(['z some stuff'], RouterStatusEntryV3(content, False).get_unrecognized_lines())
示例#8
0
  def test_from_str(self):
    """
    Exercise our RouterStatusEntry.from_str().
    """

    desc = RouterStatusEntryV3.create()
    content = desc.get_bytes()

    self.assertEqual(desc, RouterStatusEntryV3.from_str(content))

    self.assertRaisesWith(NotImplementedError, 'Please use the from_str() method from RouterStatusEntry subclasses, not RouterStatusEntry itself', RouterStatusEntry.from_str, content)
    self.assertRaisesWith(ValueError, "Router status entries don't have their own @type annotation. As such providing a 'descriptor_type' argument with RouterStatusEntry.from_str() does not work. Please drop the 'descriptor_type' argument when using this these subclasses' from_str() method.", RouterStatusEntryV3.from_str, content, descriptor_type = 'network-status-consensus-3 1.0')
示例#9
0
  def test_microdescriptor_hashes(self):
    """
    Handles a variety of 'm' lines.
    """

    test_values = {
      "8,9,10,11,12":
        [([8, 9, 10, 11, 12], {})],
      "8,9,10,11,12 sha256=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs":
        [([8, 9, 10, 11, 12], {"sha256": "g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs"})],
      "8,9,10,11,12 sha256=g1vx9si329muxV md5=3tquWIXXySNOIwRGMeAESKs/v4DWs":
        [([8, 9, 10, 11, 12], {"sha256": "g1vx9si329muxV", "md5": "3tquWIXXySNOIwRGMeAESKs/v4DWs"})],
    }

    # we need a document that's a vote
    mock_document = lambda x: x  # just need anything with a __dict__
    setattr(mock_document, "is_vote", True)
    setattr(mock_document, "is_consensus", False)

    for m_line, expected in test_values.items():
      content = get_router_status_entry_v3({'m': m_line}, content = True)
      entry = RouterStatusEntryV3(content, document = mock_document)
      self.assertEquals(expected, entry.microdescriptor_hashes)

    # try with multiple 'm' lines

    content = get_router_status_entry_v3(content = True)
    content += "\nm 11,12 sha256=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs"
    content += "\nm 31,32 sha512=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs"

    expected = [
      ([11, 12], {"sha256": "g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs"}),
      ([31, 32], {"sha512": "g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs"}),
    ]

    entry = RouterStatusEntryV3(content, document = mock_document)
    self.assertEquals(expected, entry.microdescriptor_hashes)

    # try without a document
    content = get_router_status_entry_v3({'m': "8,9,10,11,12"}, content = True)
    self._expect_invalid_attr(content, "microdescriptor_hashes", expected_value = [])

    # tries some invalid inputs
    test_values = (
      "",
      "4,a,2",
      "1,2,3 stuff",
    )

    for m_line in test_values:
      content = get_router_status_entry_v3({'m': m_line}, content = True)
      self.assertRaises(ValueError, RouterStatusEntryV3, content, True, mock_document)
示例#10
0
  def test_duplicate_lines(self):
    """
    Duplicates linesin the entry.
    """

    lines = RouterStatusEntryV3.content().split(b'\n')

    for index, duplicate_line in enumerate(lines):
      content = b'\n'.join(lines[:index] + [duplicate_line] + lines[index:])
      self.assertRaises(ValueError, RouterStatusEntryV3, content, True)

      entry = RouterStatusEntryV3(content, False)
      self.assertTrue(entry.nickname.startswith('Unnamed'))
示例#11
0
    def test_compare_flags(self, stdout_mock, query_mock, authorities_mock):
        authorities_mock().items.return_value = [
            ('moria1', DIRECTORY_AUTHORITIES['moria1']),
            ('maatuska', DIRECTORY_AUTHORITIES['maatuska']),
        ]

        r_line = 'caerSidi %s oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 71.35.150.29 9001 0'

        moria1_consensus = NetworkStatusDocumentV3.create(routers=[
            RouterStatusEntryV3.create({
                'r': r_line % 'kvy2dIpA5giOIvurlDqy3XQ+qBg=',
                's': ' '
            }),
            RouterStatusEntryV3.create({
                'r': r_line % 'aHH2gjULqTGDjA7B5KIwRNrganM=',
                's': ' '
            }),
            RouterStatusEntryV3.create({
                'r': r_line % '4rsTqi9pYM2Tq+UleoJWh/OXPGI=',
                's': ' '
            }),
            RouterStatusEntryV3.create(
                {'r': r_line % 'VGxU4qidiOB5TQSuy/Gsisnagd4='}),
            RouterStatusEntryV3.create(
                {'r': r_line % '3K7D0GncOarkPRPIrzG1ZF4F7WE='}),
        ])

        maatuska_consensus = NetworkStatusDocumentV3.create(routers=[
            RouterStatusEntryV3.create(
                {'r': r_line % 'kvy2dIpA5giOIvurlDqy3XQ+qBg='}),
            RouterStatusEntryV3.create(
                {'r': r_line % 'aHH2gjULqTGDjA7B5KIwRNrganM='}),
            RouterStatusEntryV3.create(
                {'r': r_line % '4rsTqi9pYM2Tq+UleoJWh/OXPGI='}),
            RouterStatusEntryV3.create({
                'r': r_line % 'VGxU4qidiOB5TQSuy/Gsisnagd4=',
                's': ' '
            }),
            RouterStatusEntryV3.create({
                'r': r_line % '3K7D0GncOarkPRPIrzG1ZF4F7WE=',
                's': ' '
            }),
        ])

        query_mock().run.side_effect = [[moria1_consensus],
                                        [maatuska_consensus]]

        import compare_flags

        self.assertEqual(EXPECTED_COMPARE_FLAGS, stdout_mock.getvalue())
示例#12
0
  def test_unrecognized_lines(self):
    """
    Parses a router status entry with new keywords.
    """

    entry = RouterStatusEntryV3.create({'z': 'New tor feature: sparkly unicorns!'})
    self.assertEqual(['z New tor feature: sparkly unicorns!'], entry.get_unrecognized_lines())
示例#13
0
  def test_with_microdescriptor_router_status_entries(self):
    """
    Includes microdescriptor flavored router status entries within the
    document.
    """

    entry1 = get_router_status_entry_micro_v3({'s': "Fast"})
    entry2 = get_router_status_entry_micro_v3({
      'r': "tornodeviennasil AcWxDFxrHetHYS5m6/MVt8ZN6AM 2013-03-13 22:09:13 78.142.142.246 443 80",
      's': "Valid",
    })

    document = get_network_status_document_v3({"network-status-version": "3 microdesc"}, routers = (entry1, entry2))

    self.assertTrue(entry1 in document.routers.values())
    self.assertTrue(entry2 in document.routers.values())

    # try with an invalid RouterStatusEntry

    entry3 = RouterStatusEntryMicroV3(get_router_status_entry_micro_v3({'r': "ugabuga"}, content = True), False)

    content = get_network_status_document_v3({"network-status-version": "3 microdesc"}, routers = (entry3,), content = True)
    self.assertRaises(ValueError, NetworkStatusDocumentV3, content)

    document = NetworkStatusDocumentV3(content, False)
    self.assertEquals([entry3], document.routers.values())

    # try including microdescriptor entry in a normal consensus

    content = get_network_status_document_v3(routers = (entry1,), content = True)
    self.assertRaises(ValueError, NetworkStatusDocumentV3, content)

    document = NetworkStatusDocumentV3(content, False)
    self.assertEqual([RouterStatusEntryV3(str(entry1), False)], document.routers.values())
示例#14
0
  def test_with_router_status_entries(self):
    """
    Includes router status entries within the document. This isn't to test the
    RouterStatusEntry parsing but rather the inclusion of it within the
    document.
    """

    entry1 = get_router_status_entry_v3({'s': "Fast"})
    entry2 = get_router_status_entry_v3({
      'r': "Nightfae AWt0XNId/OU2xX5xs5hVtDc5Mes 6873oEfM7fFIbxYtwllw9GPDwkA 2013-02-20 11:12:27 85.177.66.233 9001 9030",
      's': "Valid",
    })

    document = get_network_status_document_v3(routers = (entry1, entry2))

    self.assertTrue(entry1 in document.routers.values())
    self.assertTrue(entry2 in document.routers.values())

    # try with an invalid RouterStatusEntry

    entry3 = RouterStatusEntryV3(get_router_status_entry_v3({'r': "ugabuga"}, content = True), False)
    content = get_network_status_document_v3(routers = (entry3,), content = True)

    self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
    document = NetworkStatusDocumentV3(content, False)
    self.assertEquals([entry3], document.routers.values())

    # try including with a microdescriptor consensus

    content = get_network_status_document_v3({"network-status-version": "3 microdesc"}, routers = (entry1,), content = True)
    self.assertRaises(ValueError, NetworkStatusDocumentV3, content)

    document = NetworkStatusDocumentV3(content, False)
    self.assertEqual([RouterStatusEntryMicroV3(str(entry1), False)], document.routers.values())
示例#15
0
  def test_flags(self):
    """
    Handles a variety of flag inputs.
    """

    test_values = {
      '': [],
      'Fast': [Flag.FAST],
      'Fast Valid': [Flag.FAST, Flag.VALID],
      'Ugabuga': ['Ugabuga'],
    }

    for s_line, expected in test_values.items():
      entry = RouterStatusEntryV3.create({'s': s_line})
      self.assertEqual(expected, entry.flags)

    # tries some invalid inputs
    test_values = {
      'Fast   ': [Flag.FAST, '', '', ''],
      'Fast  Valid': [Flag.FAST, '', Flag.VALID],
      'Fast Fast': [Flag.FAST, Flag.FAST],
    }

    for s_line, expected in test_values.items():
      expect_invalid_attr(self, {'s': s_line}, 'flags', expected)
示例#16
0
  def test_minimal_v3(self):
    """
    Parses a minimal v3 router status entry.
    """

    entry = RouterStatusEntryV3.create()

    expected_flags = set([Flag.FAST, Flag.NAMED, Flag.RUNNING, Flag.STABLE, Flag.VALID])
    self.assertEqual(None, entry.document)
    self.assertTrue(entry.nickname.startswith('Unnamed'))
    self.assertEqual('A106452D87BD7B803B6CE916291ED368DC5BD091', entry.digest)
    self.assertEqual(9001, entry.or_port)
    self.assertEqual(None, entry.dir_port)
    self.assertEqual(expected_flags, set(entry.flags))
    self.assertEqual(None, entry.version_line)
    self.assertEqual(None, entry.version)
    self.assertEqual(None, entry.bandwidth)
    self.assertEqual(None, entry.measured)
    self.assertEqual(False, entry.is_unmeasured)
    self.assertEqual([], entry.unrecognized_bandwidth_entries)
    self.assertEqual(None, entry.exit_policy)
    self.assertEqual([], entry.microdescriptor_hashes)
    self.assertEqual(None, entry.identifier_type)
    self.assertEqual(None, entry.identifier)
    self.assertEqual([], entry.get_unrecognized_lines())
示例#17
0
  def test_with_ed25519(self):
    """
    Parses a router status entry with a ed25519 value.
    """

    microdescriptor_hashes = [
      ([13], {'sha256': 'PTSHzE7RKnRGZMRmBddSzDiZio254FUhv9+V4F5zq8s'}),
      ([14, 15], {'sha256': '0wsEwBbxJ8RtPmGYwilHQTVEw2pWzUBEVlSgEO77OyU'}),
      ([16, 17], {'sha256': 'JK2xhYr/VsCF60px+LsT990BCpfKfQTeMxRbD63o2vE'}),
      ([18, 19, 20], {'sha256': 'AkZH3gIvz3wunsroqh5izBJizdYuR7kn2oVbsvqgML8'}),
      ([21], {'sha256': 'AVp41YVxKEJCaoEf0+77Cdvyw5YgpyDXdob0+LSv/pE'}),
    ]

    entry = RouterStatusEntryV3(ENTRY_WITH_ED25519, document = vote_document(), validate = True)
    self.assertEqual('PDrelay1', entry.nickname)
    self.assertEqual('000149E6EF7102AACA9690D6E8DD2932124B94AB', entry.fingerprint)
    self.assertEqual(datetime.datetime(2015, 8, 23, 16, 52, 37), entry.published)
    self.assertEqual('95.215.44.189', entry.address)
    self.assertEqual(8080, entry.or_port)
    self.assertEqual(None, entry.dir_port)
    self.assertEqual(set([Flag.FAST, Flag.RUNNING, Flag.STABLE, Flag.VALID]), set(entry.flags))
    self.assertEqual('Tor 0.2.7.2-alpha-dev', entry.version_line)
    self.assertEqual(Version('0.2.7.2-alpha-dev'), entry.version)
    self.assertEqual(608, entry.bandwidth)
    self.assertEqual(472, entry.measured)
    self.assertEqual(False, entry.is_unmeasured)
    self.assertEqual([], entry.unrecognized_bandwidth_entries)
    self.assertEqual(MicroExitPolicy('reject 1-65535'), entry.exit_policy)
    self.assertEqual(microdescriptor_hashes, entry.microdescriptor_hashes)
    self.assertEqual('ed25519', entry.identifier_type)
    self.assertEqual('8RH34kO07Pp+XYwzdoATVyCibIvmbslUjRkAm7J4IA8', entry.identifier)
    self.assertEqual('CAB27A6FFEF7A661C18B0B11120C3E8A77FC585C', entry.digest)
    self.assertEqual([], entry.get_unrecognized_lines())
示例#18
0
    def test_flags(self):
        """
    Handles a variety of flag inputs.
    """

        test_values = {
            '': [],
            'Fast': [Flag.FAST],
            'Fast Valid': [Flag.FAST, Flag.VALID],
            'Ugabuga': ['Ugabuga'],
        }

        for s_line, expected in test_values.items():
            entry = RouterStatusEntryV3.create({'s': s_line})
            self.assertEqual(expected, entry.flags)

        # tries some invalid inputs
        test_values = {
            'Fast   ': [Flag.FAST, '', '', ''],
            'Fast  Valid': [Flag.FAST, '', Flag.VALID],
            'Fast Fast': [Flag.FAST, Flag.FAST],
        }

        for s_line, expected in test_values.items():
            expect_invalid_attr(self, {'s': s_line}, 'flags', expected)
示例#19
0
    def test_minimal_v3(self):
        """
    Parses a minimal v3 router status entry.
    """

        entry = RouterStatusEntryV3.create()

        expected_flags = set(
            [Flag.FAST, Flag.NAMED, Flag.RUNNING, Flag.STABLE, Flag.VALID])
        self.assertEqual(None, entry.document)
        self.assertTrue(entry.nickname.startswith('Unnamed'))
        self.assertEqual('A106452D87BD7B803B6CE916291ED368DC5BD091',
                         entry.digest)
        self.assertEqual(9001, entry.or_port)
        self.assertEqual(None, entry.dir_port)
        self.assertEqual(expected_flags, set(entry.flags))
        self.assertEqual(None, entry.version_line)
        self.assertEqual(None, entry.version)
        self.assertEqual(None, entry.bandwidth)
        self.assertEqual(None, entry.measured)
        self.assertEqual(False, entry.is_unmeasured)
        self.assertEqual([], entry.unrecognized_bandwidth_entries)
        self.assertEqual(None, entry.exit_policy)
        self.assertEqual([], entry.microdescriptor_hashes)
        self.assertEqual(None, entry.identifier_type)
        self.assertEqual(None, entry.identifier)
        self.assertEqual([], entry.get_unrecognized_lines())
示例#20
0
    def test_exit_used(self, stdout_mock, from_port_mock):
        path_1 = ('9EA317EECA56BDF30CAEB208A253FB456EDAB1A0', 'bolobolo1')
        path_2 = ('00C2C2A16AEDB51D5E5FB7D6168FC66B343D822F', 'ph3x')
        path_3 = ('A59E1E7C7EAEE083D756EE1FF6EC31CA3D8651D7',
                  'chaoscomputerclub19')

        event = ControlMessage.from_str(
            '650 STREAM 15 SUCCEEDED 3 64.15.112.44:80',
            'EVENT',
            normalize=True)
        r_line = '%s pZ4efH6u4IPXVu4f9uwxyj2GUdc= oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 31.172.30.2 443 0'

        controller = from_port_mock().__enter__()
        controller.get_circuit.return_value = _make_circ_event(
            1, path_1, path_2, path_3)
        controller.get_network_status.return_value = RouterStatusEntryV3.create(
            {'r': r_line % path_3[1]})
        controller.get_info.return_value = 'unknown'

        import exit_used

        with patch(
                'builtins.input',
                Mock(side_effect=functools.partial(exit_used.stream_event,
                                                   controller, event))):
            exit_used.main()

        self.assertEqual(EXPECTED_EXIT_USED, stdout_mock.getvalue())
示例#21
0
    def test_persisting_a_consensus(self, query_mock, parse_file_mock,
                                    stdout_mock):
        def tutorial_example_2():
            from stem.descriptor import DocumentHandler, parse_file

            consensus = next(
                parse_file(
                    '/tmp/descriptor_dump',
                    descriptor_type='network-status-consensus-3 1.0',
                    document_handler=DocumentHandler.DOCUMENT,
                ))

            for fingerprint, relay in consensus.routers.items():
                print('%s: %s' % (fingerprint, relay.nickname))

        network_status = NetworkStatusDocumentV3.create(
            routers=(RouterStatusEntryV3.create({
                'r':
                'caerSidi p1aag7VwarGxqctS7/fS0y5FU+s oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 71.35.150.29 9001 0',
            }), ))

        query_mock().run.return_value = [network_status]
        parse_file_mock.return_value = itertools.cycle([network_status])

        exec_documentation_example('persisting_a_consensus.py')
        exec_documentation_example('persisting_a_consensus_with_parse_file.py')

        self.assertEqual(PERSISTING_A_CONSENSUS_OUTPUT, stdout_mock.getvalue())

        if os.path.exists('/tmp/descriptor_dump'):
            os.remove('/tmp/descriptor_dump')
示例#22
0
  def test_unrecognized_lines(self):
    """
    Parses a router status entry with new keywords.
    """

    entry = RouterStatusEntryV3.create({'z': 'New tor feature: sparkly unicorns!'})
    self.assertEqual(['z New tor feature: sparkly unicorns!'], entry.get_unrecognized_lines())
示例#23
0
  def test_without_ed25519(self):
    """
    Parses a router status entry without a ed25519 value.
    """

    microdescriptor_hashes = [
      ([13, 14, 15], {'sha256': 'uaAYTOVuYRqUwJpNfP2WizjzO0FiNQB4U97xSQu+vMc'}),
      ([16, 17], {'sha256': 'G6FmPe/ehgfb6tsRzFKDCwvvae+RICeP1MaP0vWDGyI'}),
      ([18, 19, 20, 21], {'sha256': '/XhIMOnhElo2UiKjL2S10uRka/fhg1CFfNd+9wgUwEE'}),
    ]

    entry = RouterStatusEntryV3(ENTRY_WITHOUT_ED25519, document = vote_document(), validate = True)
    self.assertEqual('seele', entry.nickname)
    self.assertEqual('000A10D43011EA4928A35F610405F92B4433B4DC', entry.fingerprint)
    self.assertEqual(datetime.datetime(2015, 8, 23, 0, 26, 35), entry.published)
    self.assertEqual('73.15.150.172', entry.address)
    self.assertEqual(9001, entry.or_port)
    self.assertEqual(None, entry.dir_port)
    self.assertEqual(set([Flag.RUNNING, Flag.STABLE, Flag.VALID]), set(entry.flags))
    self.assertEqual('Tor 0.2.6.10', entry.version_line)
    self.assertEqual(Version('0.2.6.10'), entry.version)
    self.assertEqual(102, entry.bandwidth)
    self.assertEqual(31, entry.measured)
    self.assertEqual(False, entry.is_unmeasured)
    self.assertEqual([], entry.unrecognized_bandwidth_entries)
    self.assertEqual(MicroExitPolicy('reject 1-65535'), entry.exit_policy)
    self.assertEqual(microdescriptor_hashes, entry.microdescriptor_hashes)
    self.assertEqual('ed25519', entry.identifier_type)
    self.assertEqual('none', entry.identifier)
    self.assertEqual('9B4CA73EEC3349EC6DCEC897609600D0771EF82B', entry.digest)
    self.assertEqual([], entry.get_unrecognized_lines())
示例#24
0
  def test_proceeding_line(self):
    """
    Includes content prior to the 'r' line.
    """

    content = b'z some stuff\n' + RouterStatusEntryV3.content()
    self.assertRaises(ValueError, RouterStatusEntryV3, content, True)
    self.assertEqual(['z some stuff'], RouterStatusEntryV3(content, False).get_unrecognized_lines())
示例#25
0
  def test_blank_lines(self):
    """
    Includes blank lines, which should be ignored.
    """

    content = RouterStatusEntryV3.content() + b'\n\nv Tor 0.2.2.35\n\n'
    entry = RouterStatusEntryV3(content)
    self.assertEqual('Tor 0.2.2.35', entry.version_line)
示例#26
0
  def test_blank_lines(self):
    """
    Includes blank lines, which should be ignored.
    """

    content = get_router_status_entry_v3(content = True) + "\n\nv Tor 0.2.2.35\n\n"
    entry = RouterStatusEntryV3(content)
    self.assertEqual("Tor 0.2.2.35", entry.version_line)
示例#27
0
  def test_microdescriptor_hashes(self):
    """
    Handles a variety of 'm' lines.
    """

    test_values = {
      '8,9,10,11,12':
        [([8, 9, 10, 11, 12], {})],
      '8,9,10,11,12 sha256=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs':
        [([8, 9, 10, 11, 12], {'sha256': 'g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs'})],
      '8,9,10,11,12 sha256=g1vx9si329muxV md5=3tquWIXXySNOIwRGMeAESKs/v4DWs':
        [([8, 9, 10, 11, 12], {'sha256': 'g1vx9si329muxV', 'md5': '3tquWIXXySNOIwRGMeAESKs/v4DWs'})],
    }

    for m_line, expected in test_values.items():
      content = RouterStatusEntryV3.content({'m': m_line})
      entry = RouterStatusEntryV3(content, document = vote_document())
      self.assertEqual(expected, entry.microdescriptor_hashes)

    # try with multiple 'm' lines

    content = RouterStatusEntryV3.content()
    content += b'\nm 11,12 sha256=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs'
    content += b'\nm 31,32 sha512=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs'

    expected = [
      ([11, 12], {'sha256': 'g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs'}),
      ([31, 32], {'sha512': 'g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs'}),
    ]

    entry = RouterStatusEntryV3(content, document = vote_document())
    self.assertEqual(expected, entry.microdescriptor_hashes)

    # try without a document
    expect_invalid_attr(self, {'m': '8,9,10,11,12'}, 'microdescriptor_hashes', expected_value = [])

    # tries some invalid inputs
    test_values = (
      '',
      '4,a,2',
      '1,2,3 stuff',
    )

    for m_line in test_values:
      content = RouterStatusEntryV3.content({'m': m_line})
      self.assertRaises(ValueError, RouterStatusEntryV3, content, True, vote_document())
示例#28
0
    def test_check_digests(self):
        import check_digests as module
        fingerprint = 'A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB'

        extrainfo_desc = RelayExtraInfoDescriptor.create()
        server_desc = RelayDescriptor.create(
            {'extra-info-digest': extrainfo_desc.digest()}, sign=True)

        encoded_digest = base64.b64encode(
            binascii.unhexlify(server_desc.digest())).rstrip(b'=')

        consensus_desc = RouterStatusEntryV3.create({
            'r':
            'caerSidi p1aag7VwarGxqctS7/fS0y5FU+s %s 2012-08-06 11:19:31 71.35.150.29 9001 0'
            % encoded_digest.decode('utf-8'),
        })

        bad_consensus_desc = RouterStatusEntryV3.create({
            'r':
            'caerSidi p1aag7VwarGxqctS7/fS0y5FU+s oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 71.35.150.29 9001 0',
        })

        with patch('stem.descriptor.remote.get_server_descriptors',
                   _download_of(server_desc)):
            with patch('stem.descriptor.remote.get_extrainfo_descriptors',
                       _download_of(extrainfo_desc)):
                # correctly signed descriptors

                with patch('stem.descriptor.remote.get_consensus',
                           _download_of(consensus_desc)):
                    with patch('sys.stdout',
                               new_callable=io.StringIO) as stdout_mock:
                        module.validate_relay(fingerprint)
                        self.assertEqual(EXPECTED_CHECK_DIGESTS_OK,
                                         stdout_mock.getvalue())

                # incorrect server descriptor digest

                with patch('stem.descriptor.remote.get_consensus',
                           _download_of(bad_consensus_desc)):
                    with patch('sys.stdout',
                               new_callable=io.StringIO) as stdout_mock:
                        module.validate_relay(fingerprint)
                        self.assertEqual(
                            EXPECTED_CHECK_DIGESTS_BAD % server_desc.digest(),
                            stdout_mock.getvalue())
示例#29
0
def _get_router_status(address = None, port = None, nickname = None, fingerprint_base64 = None, s_line = None):
  r_line = 'caerSidi p1aag7VwarGxqctS7/fS0y5FU+s oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 71.35.150.29 9001 0'

  if address:
    r_line = r_line.replace('71.35.150.29', address)

  if port:
    r_line = r_line.replace('9001', port)

  if nickname:
    r_line = r_line.replace('caerSidi', nickname)

  if fingerprint_base64:
    r_line = r_line.replace('p1aag7VwarGxqctS7/fS0y5FU+s', fingerprint_base64)

  if s_line:
    return RouterStatusEntryV3.create({'r': r_line, 's': s_line})
  else:
    return RouterStatusEntryV3.create({'r': r_line})
示例#30
0
    def test_with_microdescriptor_router_status_entries(self):
        """
    Includes microdescriptor flavored router status entries within the
    document.
    """

        entry1 = get_router_status_entry_micro_v3({'s': "Fast"})
        entry2 = get_router_status_entry_micro_v3({'s': "Valid"})
        document = get_network_status_document_v3(
            {"network-status-version": "3 microdesc"},
            routers=(entry1, entry2))

        self.assertEquals((entry1, entry2), document.routers)

        # try with an invalid RouterStatusEntry

        entry3 = RouterStatusEntryMicroV3(
            get_router_status_entry_micro_v3({'r': "ugabuga"}, content=True),
            False)
        content = get_network_status_document_v3(
            {"network-status-version": "3 microdesc"},
            routers=(entry3, ),
            content=True)

        self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
        document = NetworkStatusDocumentV3(content, False)
        self.assertEquals((entry3, ), document.routers)

        # try including microdescriptor entries in a normal consensus

        content = get_network_status_document_v3(routers=(entry1, entry2),
                                                 content=True)
        self.assertRaises(ValueError, NetworkStatusDocumentV3, content)

        expected_routers = (
            RouterStatusEntryV3(str(entry1), False),
            RouterStatusEntryV3(str(entry2), False),
        )

        document = NetworkStatusDocumentV3(content, False)
        self.assertEquals(expected_routers, document.routers)
示例#31
0
    def test_validate_descriptor_content(self, stdout_mock, parse_file_mock):
        parse_file_mock.return_value = [
            RouterStatusEntryV3.create({
                'r':
                'caerSidi p1aag7VwarGxqctS7/fS0y5FU+s oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 71.35.150.29 9001 0',
            })
        ]

        import validate_descriptor_content

        self.assertEqual(
            'found relay caerSidi (A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB)\n',
            stdout_mock.getvalue())
示例#32
0
  def test_duplicate_lines(self):
    """
    Duplicates linesin the entry.
    """

    lines = get_router_status_entry_v3(content = True).split("\n")

    for index, duplicate_line in enumerate(lines):
      content = "\n".join(lines[:index] + [duplicate_line] + lines[index:])
      self.assertRaises(ValueError, RouterStatusEntryV3, content)

      entry = RouterStatusEntryV3(content, False)
      self.assertEqual("caerSidi", entry.nickname)
示例#33
0
  def test_duplicate_lines(self):
    """
    Duplicates linesin the entry.
    """

    lines = RouterStatusEntryV3.content().split(b'\n')

    for index, duplicate_line in enumerate(lines):
      content = b'\n'.join(lines[:index] + [duplicate_line] + lines[index:])
      self.assertRaises(ValueError, RouterStatusEntryV3, content, True)

      entry = RouterStatusEntryV3(content, False)
      self.assertTrue(entry.nickname.startswith('Unnamed'))
示例#34
0
    def test_descriptor_from_orport(self, stdout_mock, downloader_mock):
        downloader_mock().get_consensus.return_value = [
            RouterStatusEntryV3.create({
                'r':
                'caerSidi p1aag7VwarGxqctS7/fS0y5FU+s oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 71.35.150.29 9001 0',
            })
        ]

        import descriptor_from_orport

        self.assertEqual(
            'found relay caerSidi (A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB)\n',
            stdout_mock.getvalue())
示例#35
0
    def make_router_status_entry(
            self) -> 'stem.descriptor.router_status_entry.RouterStatusEntryV3':
        """
    Provides a RouterStatusEntryV3 for this descriptor content.

    .. versionadded:: 1.6.0

    :returns: :class:`~stem.descriptor.router_status_entry.RouterStatusEntryV3`
      that would be in the consensus
    """

        if not self.fingerprint:
            raise ValueError(
                'Server descriptor lacks a fingerprint. This is an optional field, but required to make a router status entry.'
            )

        attr = {
            'r':
            ' '.join([
                self.nickname,
                _truncated_b64encode(
                    binascii.unhexlify(
                        stem.util.str_tools._to_bytes(self.fingerprint))),
                _truncated_b64encode(
                    binascii.unhexlify(
                        stem.util.str_tools._to_bytes(self.digest()))),
                self.published.strftime('%Y-%m-%d %H:%M:%S'),
                self.address,
                str(self.or_port),
                str(self.dir_port) if self.dir_port else '0',
            ]),
            'w':
            'Bandwidth=%i' % self.average_bandwidth,
            'p':
            self.exit_policy.summary().replace(', ', ','),
        }

        if self.tor_version:
            attr['v'] = 'Tor %s' % self.tor_version

        if self.or_addresses:
            attr['a'] = [
                '%s:%s' % (addr, port) for addr, port, _ in self.or_addresses
            ]

        if self.certificate:
            attr['id'] = 'ed25519 %s' % _truncated_b64encode(
                self.certificate.key)

        return RouterStatusEntryV3.create(attr)  # type: ignore
示例#36
0
    def _expect_invalid_attr(self, content, attr=None, expected_value=None):
        """
    Asserts that construction will fail due to content having a malformed
    attribute. If an attr is provided then we check that it matches an expected
    value when we're constructed without validation.
    """

        self.assertRaises(ValueError, RouterStatusEntryV3, content)
        entry = RouterStatusEntryV3(content, False)

        if attr:
            self.assertEquals(expected_value, getattr(entry, attr))
        else:
            self.assertEquals('caerSidi', entry.nickname)
示例#37
0
    def test_persisting_a_consensus_with_parse_file(self, stdout_mock,
                                                    parse_file_mock):
        consensus = NetworkStatusDocumentV3.create(
            routers=(RouterStatusEntryV3.create({
                'r':
                'caerSidi p1aag7VwarGxqctS7/fS0y5FU+s oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 71.35.150.29 9001 0',
            }), ))

        parse_file_mock.return_value = iter([consensus])

        import persisting_a_consensus_with_parse_file

        self.assertEqual(EXPECTED_PERSISTING_A_CONSENSUS,
                         stdout_mock.getvalue())
示例#38
0
    def test_ipv6_addresses(self):
        """
    Handles a variety of 'a' lines.
    """

        test_values = {
            '[2607:fcd0:daaa:101::602c:bd62]:443':
            [('2607:fcd0:daaa:101::602c:bd62', 443, True)],
        }

        for a_line, expected in test_values.items():
            entry = RouterStatusEntryV3.create({'a': a_line})
            self.assertEqual(expected, entry.or_addresses)

        # includes multiple 'a' lines

        content = RouterStatusEntryV3.content()
        content += b'\na [2607:fcd0:daaa:101::602c:bd62]:443'
        content += b'\na [1148:fcd0:daaa:101::602c:bd62]:80'

        expected = [
            ('2607:fcd0:daaa:101::602c:bd62', 443, True),
            ('1148:fcd0:daaa:101::602c:bd62', 80, True),
        ]

        entry = RouterStatusEntryV3(content)
        self.assertEqual(expected, entry.or_addresses)

        # tries some invalid inputs

        test_values = (
            '',
            '[1148:fcd0:daaa:101::602c:bd62]:80000',
        )

        for a_line in test_values:
            expect_invalid_attr(self, {'a': a_line}, expected_value={})
示例#39
0
  def test_ipv6_addresses(self):
    """
    Handles a variety of 'a' lines.
    """

    test_values = {
      '[2607:fcd0:daaa:101::602c:bd62]:443': [
        ('2607:fcd0:daaa:101::602c:bd62', 443, True)],
    }

    for a_line, expected in test_values.items():
      entry = RouterStatusEntryV3.create({'a': a_line})
      self.assertEqual(expected, entry.or_addresses)

    # includes multiple 'a' lines

    content = RouterStatusEntryV3.content()
    content += b'\na [2607:fcd0:daaa:101::602c:bd62]:443'
    content += b'\na [1148:fcd0:daaa:101::602c:bd62]:80'

    expected = [
      ('2607:fcd0:daaa:101::602c:bd62', 443, True),
      ('1148:fcd0:daaa:101::602c:bd62', 80, True),
    ]

    entry = RouterStatusEntryV3(content)
    self.assertEqual(expected, entry.or_addresses)

    # tries some invalid inputs

    test_values = (
      '',
      '[1148:fcd0:daaa:101::602c:bd62]:80000',
    )

    for a_line in test_values:
      expect_invalid_attr(self, {'a': a_line}, expected_value = {})
示例#40
0
  def test_mirror_mirror_on_the_wall_3(self, open_mock, stdout_mock):
    def tutorial_example():
      from stem.descriptor import parse_file

      for desc in parse_file(open('/home/atagar/.tor/cached-consensus')):
        print('found relay %s (%s)' % (desc.nickname, desc.fingerprint))

    test_file = io.BytesIO(NetworkStatusDocumentV3.content(routers = [RouterStatusEntryV3.create({
      'r': 'caerSidi p1aag7VwarGxqctS7/fS0y5FU+s oQZFLYe9e4A7bOkWKR7TaNxb0JE 2012-08-06 11:19:31 71.35.150.29 9001 0',
    })]))
    test_file.name = '/home/atagar/.tor/cached-consensus'
    open_mock.return_value = test_file

    tutorial_example()
    self.assertEqual('found relay caerSidi (A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB)\n', stdout_mock.getvalue())
示例#41
0
  def test_versions(self):
    """
    Handles a variety of version inputs.
    """

    test_values = {
      'Tor 0.2.2.35': Version('0.2.2.35'),
      'Tor 0.1.2': Version('0.1.2'),
      'Torr new_stuff': None,
      'new_stuff and stuff': None,
    }

    for v_line, expected in test_values.items():
      entry = RouterStatusEntryV3.create({'v': v_line})
      self.assertEqual(expected, entry.version)
      self.assertEqual(v_line, entry.version_line)

    # tries an invalid input
    expect_invalid_attr(self, {'v': 'Tor ugabuga'}, 'version')
示例#42
0
  def test_bandwidth(self):
    """
    Handles a variety of 'w' lines.
    """

    test_values = {
      'Bandwidth=0': (0, None, False, []),
      'Bandwidth=63138': (63138, None, False, []),
      'Bandwidth=11111 Measured=482': (11111, 482, False, []),
      'Bandwidth=11111 Measured=482 Blarg!': (11111, 482, False, ['Blarg!']),
      'Bandwidth=11111 Measured=482 Unmeasured=1 Blarg!': (11111, 482, True, ['Blarg!']),
    }

    for w_line, expected in test_values.items():
      entry = RouterStatusEntryV3.create({'w': w_line})
      self.assertEqual(expected[0], entry.bandwidth)
      self.assertEqual(expected[1], entry.measured)
      self.assertEqual(expected[2], entry.is_unmeasured)
      self.assertEqual(expected[3], entry.unrecognized_bandwidth_entries)

    # tries some invalid inputs
    test_values = (
      '',
      'blarg',
      'Bandwidth',
      'Bandwidth=',
      'Bandwidth:0',
      'Bandwidth 0',
      'Bandwidth=-10',
      'Bandwidth=10 Measured',
      'Bandwidth=10 Measured=',
      'Bandwidth=10 Measured=-50',
      'Bandwidth=10 Measured=482 Unmeasured',
      'Bandwidth=10 Measured=482 Unmeasured=',
      'Bandwidth=10 Measured=482 Unmeasured=0',
      'Bandwidth=10 Measured=482 Unmeasured=842',
      'Bandwidth=10 Measured=482 Unmeasured=-5',
    )

    for w_line in test_values:
      expect_invalid_attr(self, {'w': w_line})
示例#43
0
  def make_router_status_entry(self):
    """
    Provides a RouterStatusEntryV3 for this descriptor content.

    .. versionadded:: 1.6.0

    :returns: :class:`~stem.descriptor.router_status_entry.RouterStatusEntryV3`
      that would be in the consensus
    """

    if not self.fingerprint:
      raise ValueError('Server descriptor lacks a fingerprint. This is an optional field, but required to make a router status entry.')

    attr = {
      'r': ' '.join([
        self.nickname,
        _truncated_b64encode(binascii.unhexlify(stem.util.str_tools._to_bytes(self.fingerprint))),
        _truncated_b64encode(binascii.unhexlify(stem.util.str_tools._to_bytes(self.digest()))),
        self.published.strftime('%Y-%m-%d %H:%M:%S'),
        self.address,
        str(self.or_port),
        str(self.dir_port) if self.dir_port else '0',
      ]),
      'w': 'Bandwidth=%i' % self.average_bandwidth,
      'p': self.exit_policy.summary().replace(', ', ','),
    }

    if self.tor_version:
      attr['v'] = 'Tor %s' % self.tor_version

    if self.or_addresses:
      attr['a'] = ['%s:%s' % (addr, port) for addr, port, _ in self.or_addresses]

    if self.certificate:
      attr['id'] = 'ed25519 %s' % _truncated_b64encode(self.certificate.key)

    return RouterStatusEntryV3.create(attr)
示例#44
0
  def test_exit_policy(self):
    """
    Handles a variety of 'p' lines.
    """

    test_values = {
      'reject 1-65535': MicroExitPolicy('reject 1-65535'),
      'accept 80,110,143,443': MicroExitPolicy('accept 80,110,143,443'),
    }

    for p_line, expected in test_values.items():
      entry = RouterStatusEntryV3.create({'p': p_line})
      self.assertEqual(expected, entry.exit_policy)

    # tries some invalid inputs
    test_values = (
      '',
      'blarg',
      'reject -50',
      'accept 80,',
    )

    for p_line in test_values:
      expect_invalid_attr(self, {'p': p_line}, 'exit_policy')
示例#45
0
 def test_protocols(self):
   desc = RouterStatusEntryV3.create({'pr': 'Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 Link=1-4 LinkAuth=1 Microdesc=1 Relay=1-2'})
   self.assertEqual(10, len(desc.protocols))