BootstrapperPlugin.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import time
  2. from util import helper
  3. from Plugin import PluginManager
  4. from BootstrapperDb import BootstrapperDb
  5. from Crypt import CryptRsa
  6. from Config import config
  7. if "db" not in locals().keys(): # Share during reloads
  8. db = BootstrapperDb()
  9. @PluginManager.registerTo("FileRequest")
  10. class FileRequestPlugin(object):
  11. def checkOnionSigns(self, onions, onion_signs, onion_sign_this):
  12. if not onion_signs or len(onion_signs) != len(set(onions)):
  13. return False
  14. if time.time() - float(onion_sign_this) > 3 * 60:
  15. return False # Signed out of allowed 3 minutes
  16. onions_signed = []
  17. # Check onion signs
  18. for onion_publickey, onion_sign in onion_signs.items():
  19. if CryptRsa.verify(onion_sign_this, onion_publickey, onion_sign):
  20. onions_signed.append(CryptRsa.publickeyToOnion(onion_publickey))
  21. else:
  22. break
  23. # Check if the same onion addresses signed as the announced onces
  24. if sorted(onions_signed) == sorted(set(onions)):
  25. return True
  26. else:
  27. return False
  28. def actionAnnounce(self, params):
  29. time_started = time.time()
  30. s = time.time()
  31. # Backward compatibility
  32. if "ip4" in params["add"]:
  33. params["add"].append("ipv4")
  34. if "ip4" in params["need_types"]:
  35. params["need_types"].append("ipv4")
  36. hashes = params["hashes"]
  37. all_onions_signed = self.checkOnionSigns(params.get("onions", []), params.get("onion_signs"), params.get("onion_sign_this"))
  38. time_onion_check = time.time() - s
  39. ip_type = helper.getIpType(self.connection.ip)
  40. if ip_type == "onion" or self.connection.ip in config.ip_local:
  41. is_port_open = False
  42. elif ip_type in params["add"]:
  43. is_port_open = True
  44. else:
  45. is_port_open = False
  46. s = time.time()
  47. # Separatley add onions to sites or at once if no onions present
  48. i = 0
  49. onion_to_hash = {}
  50. for onion in params.get("onions", []):
  51. if onion not in onion_to_hash:
  52. onion_to_hash[onion] = []
  53. onion_to_hash[onion].append(hashes[i])
  54. i += 1
  55. hashes_changed = 0
  56. db.execute("BEGIN")
  57. for onion, onion_hashes in onion_to_hash.iteritems():
  58. hashes_changed += db.peerAnnounce(
  59. ip_type="onion",
  60. address=onion,
  61. port=params["port"],
  62. hashes=onion_hashes,
  63. onion_signed=all_onions_signed
  64. )
  65. db.execute("END")
  66. time_db_onion = time.time() - s
  67. s = time.time()
  68. if is_port_open:
  69. hashes_changed += db.peerAnnounce(
  70. ip_type=ip_type,
  71. address=self.connection.ip,
  72. port=params["port"],
  73. hashes=hashes,
  74. delete_missing_hashes=params.get("delete")
  75. )
  76. time_db_ip = time.time() - s
  77. s = time.time()
  78. # Query sites
  79. back = {}
  80. peers = []
  81. if params.get("onions") and not all_onions_signed and hashes_changed:
  82. back["onion_sign_this"] = "%.0f" % time.time() # Send back nonce for signing
  83. if len(hashes) > 500 or not hashes_changed:
  84. limit = 5
  85. order = False
  86. else:
  87. limit = 30
  88. order = True
  89. for hash in hashes:
  90. if time.time() - time_started > 1: # 1 sec limit on request
  91. self.connection.log("Announce time limit exceeded after %s/%s sites" % (len(peers), len(hashes)))
  92. break
  93. hash_peers = db.peerList(
  94. hash,
  95. address=self.connection.ip, onions=onion_to_hash.keys(), port=params["port"],
  96. limit=min(limit, params["need_num"]), need_types=params["need_types"], order=order
  97. )
  98. if "ip4" in params["need_types"]: # Backward compatibility
  99. hash_peers["ip4"] = hash_peers["ipv4"]
  100. del(hash_peers["ipv4"])
  101. peers.append(hash_peers)
  102. time_peerlist = time.time() - s
  103. back["peers"] = peers
  104. self.connection.log(
  105. "Announce %s sites (onions: %s, onion_check: %.3fs, db_onion: %.3fs, db_ip: %.3fs, peerlist: %.3fs, limit: %s)" %
  106. (len(hashes), len(onion_to_hash), time_onion_check, time_db_onion, time_db_ip, time_peerlist, limit)
  107. )
  108. self.response(back)
  109. @PluginManager.registerTo("UiRequest")
  110. class UiRequestPlugin(object):
  111. def actionStatsBootstrapper(self):
  112. self.sendHeader()
  113. # Style
  114. yield """
  115. <style>
  116. * { font-family: monospace; white-space: pre }
  117. table td, table th { text-align: right; padding: 0px 10px }
  118. </style>
  119. """
  120. hash_rows = db.execute("SELECT * FROM hash").fetchall()
  121. for hash_row in hash_rows:
  122. peer_rows = db.execute(
  123. "SELECT * FROM peer LEFT JOIN peer_to_hash USING (peer_id) WHERE hash_id = :hash_id",
  124. {"hash_id": hash_row["hash_id"]}
  125. ).fetchall()
  126. yield "<br>%s (added: %s, peers: %s)<br>" % (
  127. str(hash_row["hash"]).encode("hex"), hash_row["date_added"], len(peer_rows)
  128. )
  129. for peer_row in peer_rows:
  130. yield " - {ip4: <30} {onion: <30} added: {date_added}, announced: {date_announced}<br>".format(**dict(peer_row))