client.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. #!/usr/bin/env python3
  2. import random # For simulation, not cryptography!
  3. import math
  4. import network
  5. import dirauth
  6. import relay
  7. class CellClient(relay.CellHandler):
  8. """The subclass of CellHandler for clients."""
  9. def __init__(self, myaddr, dirauthaddrs):
  10. super().__init__(myaddr, dirauthaddrs)
  11. self.guardaddr = None
  12. def get_consensus_from_fallbackrelay(self):
  13. """Download a fresh consensus from a random fallbackrelay."""
  14. fb = random.choice(network.thenetwork.getfallbackrelays())
  15. self.send_msg(relay.RelayGetConsensusMsg(), fb.netaddr)
  16. def received_msg(self, msg, peeraddr, peer):
  17. """Callback when a NetMsg not specific to a circuit is
  18. received."""
  19. print("Client %s received msg %s from %s" % (self.myaddr, msg, peeraddr))
  20. if isinstance(msg, relay.RelayConsensusMsg):
  21. self.consensus = msg.consensus
  22. else:
  23. return super().received_msg(msg, peeraddr, peer)
  24. def received_cell(self, circid, cell, peeraddr, peer):
  25. """Callback with a circuit-specific cell is received."""
  26. print("Client %s received cell on circ %d: %s from %s" % (self.myaddr, circid, cell, peeraddr))
  27. return super().received_cell(circid, cell, peeraddr, peer)
  28. class Client:
  29. """A class representing a Tor client."""
  30. def __init__(self, dirauthaddrs):
  31. # Get a network address for client-side use only (do not bind it
  32. # to the network)
  33. self.netaddr = network.NetAddr()
  34. self.cellhandler = CellClient(self.netaddr, dirauthaddrs)
  35. # Register for epoch tick notifications
  36. network.thenetwork.wantepochticks(self, True)
  37. def terminate(self):
  38. """Quit this client."""
  39. # Stop listening for epoch ticks
  40. network.thenetwork.wantepochticks(self, False)
  41. # Close relay connections
  42. self.cellhandler.terminate()
  43. def get_consensus(self):
  44. """Fetch a new consensus."""
  45. # We're going to want a new consensus from our guard. In order
  46. # to get that, we'll need a channel to our guard. In order to
  47. # get that, we'll need a guard address. In order to get that,
  48. # we'll need a consensus (uh, oh; in that case, fetch the
  49. # consensus from a fallback relay).
  50. self.cellhandler.get_consensus_from_fallbackrelay()
  51. print('client consensus=', self.cellhandler.consensus)
  52. def newepoch(self, epoch):
  53. """Callback that fires at the start of each epoch"""
  54. if self.cellhandler.consensus is None or \
  55. self.cellhandler.consensus.consdict['epoch'] != \
  56. network.thenetwork.getepoch():
  57. # We'll need a new consensus
  58. self.get_consensus()
  59. if __name__ == '__main__':
  60. # Start some dirauths
  61. numdirauths = 9
  62. dirauthaddrs = []
  63. for i in range(numdirauths):
  64. dira = dirauth.DirAuth(i, numdirauths)
  65. dirauthaddrs.append(network.thenetwork.bind(dira))
  66. # Start some relays
  67. numrelays = 10
  68. relays = []
  69. for i in range(numrelays):
  70. # Relay bandwidths (at least the ones fast enough to get used)
  71. # in the live Tor network (as of Dec 2019) are well approximated
  72. # by (200000-(200000-25000)/3*log10(x)) where x is a
  73. # uniform integer in [1,2500]
  74. x = random.randint(1,2500)
  75. bw = int(200000-(200000-25000)/3*math.log10(x))
  76. relays.append(relay.Relay(dirauthaddrs, bw, 0))
  77. # The fallback relays are a hardcoded list of about 5% of the
  78. # relays, used by clients for bootstrapping
  79. numfallbackrelays = int(numrelays * 0.05) + 1
  80. fallbackrelays = random.sample(relays, numfallbackrelays)
  81. for r in fallbackrelays:
  82. r.set_is_fallbackrelay()
  83. network.thenetwork.setfallbackrelays(fallbackrelays)
  84. # Tick the epoch
  85. network.thenetwork.nextepoch()
  86. dirauth.verify_consensus(dirauth.DirAuth.consensus, network.thenetwork.dirauthkeys())
  87. print('ticked; epoch=', network.thenetwork.getepoch())
  88. relays[3].cellhandler.send_msg(relay.RelayRandomHopMsg(30), relays[5].netaddr)
  89. # See what channels exist and do a consistency check
  90. for r in relays:
  91. print("%s: %s" % (r.netaddr, [ str(k) for k in r.cellhandler.channels.keys()]))
  92. raddr = r.netaddr
  93. for ad, ch in r.cellhandler.channels.items():
  94. if ch.peer.cellhandler.myaddr != ad:
  95. print('address mismatch:', raddr, ad, ch.peer.cellhandler.myaddr)
  96. if ch.peer.cellhandler.channels[raddr].peer is not ch:
  97. print('asymmetry:', raddr, ad, ch, ch.peer.cellhandler.channels[raddr].peer)
  98. # Start some clients
  99. numclients = 1
  100. clients = []
  101. for i in range(numclients):
  102. clients.append(Client(dirauthaddrs))
  103. # Tick the epoch
  104. network.thenetwork.nextepoch()
  105. # See what channels exist and do a consistency check
  106. for c in clients:
  107. print("%s: %s" % (c.netaddr, [ str(k) for k in c.cellhandler.channels.keys()]))
  108. caddr = c.netaddr
  109. for ad, ch in c.cellhandler.channels.items():
  110. if ch.peer.cellhandler.myaddr != ad:
  111. print('address mismatch:', caddr, ad, ch.peer.cellhandler.myaddr)
  112. if ch.peer.cellhandler.channels[caddr].peer is not ch:
  113. print('asymmetry:', caddr, ad, ch, ch.peer.cellhandler.channels[caddr].peer)