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