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