1
0

LocalDiscovery.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. # Zeroconf discovery of other BT clients on the local network.
  2. #
  3. # by Greg Hazel
  4. import sys
  5. import random
  6. import socket
  7. import logging
  8. import Zeroconf
  9. from BTL import stackthreading as threading
  10. from BTL.HostIP import get_deferred_host_ip, get_host_ip
  11. discovery_logger = logging.getLogger('LocalDiscovery')
  12. discovery_logger.setLevel(logging.DEBUG)
  13. #discovery_logger.addHandler(logging.StreamHandler(sys.stdout))
  14. server = None
  15. def _get_server():
  16. global server
  17. if not server:
  18. server = Zeroconf.Zeroconf()
  19. return server
  20. class LocalDiscovery(object):
  21. def __init__(self, rawserver, port, got_peer):
  22. self.rawserver = rawserver
  23. self.port = port
  24. self.got_peer = got_peer
  25. self.server = _get_server()
  26. self.services = []
  27. def announce(self, infohash, peerid):
  28. discovery_logger.info("announcing: %s", infohash)
  29. # old
  30. #service_name = "_BitTorrent-%s._tcp.local." % infohash
  31. #service_type = service_name
  32. service_name = "%s._%s" % (peerid, infohash)
  33. service_type = "_bittorrent._tcp.local."
  34. browser = Zeroconf.ServiceBrowser(self.server, service_type, self)
  35. service = Zeroconf.ServiceInfo(service_type,
  36. "%s.%s" % (service_name, service_type),
  37. address = None, # to be filled in later
  38. port = self.port,
  39. weight = 0, priority = 0,
  40. properties = {}
  41. )
  42. service.browser = browser
  43. service.registered = False
  44. self.services.append(service)
  45. df = get_deferred_host_ip()
  46. df.addCallback(self._announce2, service)
  47. return service
  48. def _announce2(self, ip, service):
  49. if service not in self.services:
  50. # already removed
  51. return
  52. service.registered = True
  53. service.address = socket.inet_aton(ip)
  54. #t = threading.Thread(target=self.server.registerService, args=(service,))
  55. #t.setDaemon(False)
  56. #t.start()
  57. # blocks!
  58. self.server.registerService(service)
  59. def unannounce(self, service):
  60. assert isinstance(service, Zeroconf.ServiceInfo)
  61. if service.registered:
  62. service.registered = False
  63. service.browser.cancel()
  64. self.server.unregisterService(service)
  65. self.services.remove(service)
  66. def addService(self, server, type, name):
  67. discovery_logger.info("Service %s added", repr(name))
  68. # Request more information about the service
  69. info = server.getServiceInfo(type, name)
  70. if info and info.address is not None:
  71. host = socket.inet_ntoa(info.address)
  72. try:
  73. port = int(info.port)
  74. except:
  75. discovery_logger.exception("Invalid Service (port not an int): "
  76. "%r" % info.__dict__)
  77. return
  78. addr = (host, port)
  79. ip = get_host_ip()
  80. if addr == (ip, self.port):
  81. # talking to self
  82. return
  83. # old
  84. #infohash = name.split("_BitTorrent-")[1][:-len("._tcp.local.")]
  85. peerid, infohash, service_type = name.split('.', 2)
  86. infohash = infohash[1:] # _
  87. discovery_logger.info("Got peer: %s:%d %s", host, port, infohash)
  88. # BUG: BitTorrent is so broken!
  89. #t = random.random() * 3
  90. # But I fixed it.
  91. t = 0
  92. self.rawserver.external_add_task(t, self._got_peer, addr, infohash)
  93. def removeService(self, server, type, name):
  94. discovery_logger.info("Service %s removed", repr(name))
  95. def _got_peer(self, addr, infohash):
  96. if self.got_peer:
  97. self.got_peer(addr, infohash)
  98. def stop(self):
  99. self.port = None
  100. self.got_peer = None
  101. for service in self.services:
  102. self.unannounce(service)
  103. if __name__ == '__main__':
  104. import string
  105. from BitTorrent.RawServer_twisted import RawServer
  106. from BitTorrent.PeerID import make_id
  107. rawserver = RawServer()
  108. def run_task_and_exit():
  109. l = LocalDiscovery(rawserver, 6881,
  110. lambda *a:sys.stdout.write("GOT: %s\n" % str(a)))
  111. l.announce("63f27f5023d7e49840ce89fc1ff988336c514b64",
  112. make_id().encode('hex'))
  113. rawserver.add_task(0, run_task_and_exit)
  114. rawserver.listen_forever()