gnutella 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLI.md +189 -0
- package/DEVELOPER.md +193 -0
- package/LICENSE +674 -0
- package/QUICKSTART.md +133 -0
- package/README.md +74 -0
- package/bin/gnutella.ts +15 -0
- package/gnutella.json.example +18 -0
- package/package.json +72 -0
- package/src/cli.ts +692 -0
- package/src/cli_shared.ts +359 -0
- package/src/const.ts +138 -0
- package/src/gwebcache/bootstrap.ts +491 -0
- package/src/gwebcache/response.ts +391 -0
- package/src/gwebcache/shared.ts +116 -0
- package/src/gwebcache/types.ts +187 -0
- package/src/gwebcache_client.ts +13 -0
- package/src/protocol/browse_host.ts +552 -0
- package/src/protocol/client_blocking.ts +29 -0
- package/src/protocol/codec.ts +715 -0
- package/src/protocol/content_urn.ts +170 -0
- package/src/protocol/core_utils.ts +43 -0
- package/src/protocol/file_server.ts +245 -0
- package/src/protocol/ggep.ts +168 -0
- package/src/protocol/handshake.ts +199 -0
- package/src/protocol/http_download_reader.ts +112 -0
- package/src/protocol/magnet.ts +176 -0
- package/src/protocol/node.ts +416 -0
- package/src/protocol/node_handshake.ts +992 -0
- package/src/protocol/node_lifecycle.ts +210 -0
- package/src/protocol/node_protocol_runtime.ts +949 -0
- package/src/protocol/node_qrp_runtime.ts +97 -0
- package/src/protocol/node_query_routing.ts +208 -0
- package/src/protocol/node_state.ts +745 -0
- package/src/protocol/node_tls.ts +257 -0
- package/src/protocol/node_topology.ts +141 -0
- package/src/protocol/node_transfer.ts +455 -0
- package/src/protocol/node_types.ts +106 -0
- package/src/protocol/peer_state.ts +675 -0
- package/src/protocol/qrp.ts +549 -0
- package/src/protocol/query_search.ts +29 -0
- package/src/protocol/share_index.ts +131 -0
- package/src/protocol/share_library.ts +246 -0
- package/src/protocol.ts +36 -0
- package/src/shared.ts +236 -0
- package/src/types.ts +452 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import net from "node:net";
|
|
2
|
+
import tls from "node:tls";
|
|
3
|
+
|
|
4
|
+
import { ENABLE_TLS } from "../const";
|
|
5
|
+
import { errMsg } from "../shared";
|
|
6
|
+
import type { GnutellaServent } from "./node";
|
|
7
|
+
|
|
8
|
+
const TLS_UPGRADE_TOKEN = "TLS/1.0";
|
|
9
|
+
const TLS_TIMEOUT_MS = 5000;
|
|
10
|
+
|
|
11
|
+
// Opportunistic peer TLS does not authenticate remote identity, so a
|
|
12
|
+
// bundled self-signed certificate is sufficient for server-side handshakes.
|
|
13
|
+
const TLS_KEY_PEM = `-----BEGIN PRIVATE KEY-----
|
|
14
|
+
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD4hBqeBHY43TlX
|
|
15
|
+
zVeqDytDe9/8aJGk4DL9RXWdU6fExvMySt5l30OVBH/WqrAtSsxTzYF6Ara8erXG
|
|
16
|
+
vPQq0HbtbnAGzB7VOcadYCYFJFiNFQoR50U4vq0MHE/4QEHsRGZFtB1HRIrHEqJp
|
|
17
|
+
m9TYbaL6/Nw4atzcvEWcjok5rpvy2NHyI1QueixOb7KeukSrgR6WUNCldi0YPuXP
|
|
18
|
+
kePe48nifnzlbw2MqYP5u+TdThpyu5KGLwsvHOUUhr7HrEGpcl8FHpAhsWE2HzVE
|
|
19
|
+
5LGLLK3goASjJOYokl56yzLPNC4PgsmdgpykiMHqDXEwAfaM7dRb2BhAzx4jpxbv
|
|
20
|
+
jwbbJJtPAgMBAAECggEAAOBWD8JTM4UKhR9L71Ei/o2MT3ZhSxKjbYw/0qjG/K0I
|
|
21
|
+
3+hInEmnkMB3aqS1m7Xib0XpgSSKgmJjD/EIcG/lOCL3zIIzz38FK5fIbqxbAfDM
|
|
22
|
+
kwyzHBXt7apTBC4rFZxwhCzvMpjLU+u884mQL1K3d8vPjhCFJNJ/8WVa+0ulJyp+
|
|
23
|
+
otlQVzKWH5T/jjUlTxnaImsIdI8grqaBSC5Fd5Dnay0qUNYx3t5UFlkAZzWc/Uua
|
|
24
|
+
EuDvid1fsFGHbkXrh9jFLLKd+7MnNjCeQf56xzbN8mvFntnnqCJm3AywMGEQwwHB
|
|
25
|
+
84e/2jEeJBQsLuft1qAk2Wr3gKBkF8vcooDoArFpKQKBgQD/GIGsJGcfT5JnMobP
|
|
26
|
+
6Dv7UEIasv5MUZ6PSvB4bLk/AItnAms27No28w32S5X5f19Wm8+ff7AWdEjbs/XP
|
|
27
|
+
rgnLV6QL0e8pQCNBT5kfXidydrZrH8rbSV0h3Ius+UQSEMNnjBaJ8mgPUsH//jBh
|
|
28
|
+
dM077jSgoP3KHjQk2UHp9+p0yQKBgQD5ZaBjdQemDeI6D8Jzowjrr4IT0Y9wrT0u
|
|
29
|
+
O8noRMDxJM1BfFxOB/yrM+mMVEapOFuR7qTcHuJFUUSktROlhwvDHFSXEPs9/0Vy
|
|
30
|
+
d8FFgbd0OF4+Hdg5yk0F7LjgXh982Dc2H363vlPiOZ8BklTX73bnlw9WvgBDxqri
|
|
31
|
+
H7JW92wTVwKBgQD0W0tL1IsbySNay2GsIq/iat0HqlJCVSTn6kczdCJ3IVRn1j9R
|
|
32
|
+
m8zkOisztO/y0XpIAnT+Olg5CicInfhnejVTnZ483FqWTyP2WgM5sv1ifij7sLan
|
|
33
|
+
HD2kRBlgFl6IV5p2xBCLD7NyijnfuGQr1rEKKYIsJEs3o3sbmSm0r5DdUQKBgQCm
|
|
34
|
+
F5ZvZjtHzatCO8imtod0XxhkFoZO5jD+n3biJxfQAVBpMmdO2Gbfpdz+RgohHJVv
|
|
35
|
+
ZN2Kc08CFxN+FdIVxRCCSlXTnc2VBnK7vyGKJs+EqR2qhLnCEwak0Xh2hHi37k8m
|
|
36
|
+
zmbX+/tliDZrF4dFoAcySRpADJ2khaS8n5tn67OgVQKBgQDyHZ4zruZo5b+GQC+5
|
|
37
|
+
IN/FrtviKu0+W2dA+PGwxMcUF+373IDq7Kp+8A8j+QtG0uPYMZiFrWb10Aqp5yjZ
|
|
38
|
+
PH6GWdABuZ61cWEdbFCgG6/NCOWB84iPSHkttZPi0r+EbspHeIlLixUIFSVKUeLf
|
|
39
|
+
jlzk/A25vxdNIA4F+dVdBBy/Pg==
|
|
40
|
+
-----END PRIVATE KEY-----
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
const TLS_CERT_PEM = `-----BEGIN CERTIFICATE-----
|
|
44
|
+
MIIDDzCCAfegAwIBAgIURECewiM0o9kR9MKlXP0py8dw0XYwDQYJKoZIhvcNAQEL
|
|
45
|
+
BQAwFzEVMBMGA1UEAwwMZ251dGVsbGEtYnVuMB4XDTI2MDMyODA2NDMwMVoXDTM2
|
|
46
|
+
MDMyNTA2NDMwMVowFzEVMBMGA1UEAwwMZ251dGVsbGEtYnVuMIIBIjANBgkqhkiG
|
|
47
|
+
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+IQangR2ON05V81Xqg8rQ3vf/GiRpOAy/UV1
|
|
48
|
+
nVOnxMbzMkreZd9DlQR/1qqwLUrMU82BegK2vHq1xrz0KtB27W5wBswe1TnGnWAm
|
|
49
|
+
BSRYjRUKEedFOL6tDBxP+EBB7ERmRbQdR0SKxxKiaZvU2G2i+vzcOGrc3LxFnI6J
|
|
50
|
+
Oa6b8tjR8iNULnosTm+ynrpEq4EellDQpXYtGD7lz5Hj3uPJ4n585W8NjKmD+bvk
|
|
51
|
+
3U4acruShi8LLxzlFIa+x6xBqXJfBR6QIbFhNh81ROSxiyyt4KAEoyTmKJJeessy
|
|
52
|
+
zzQuD4LJnYKcpIjB6g1xMAH2jO3UW9gYQM8eI6cW748G2ySbTwIDAQABo1MwUTAd
|
|
53
|
+
BgNVHQ4EFgQUmmjFyMO+Yec6uc5oDZ32eQZpTVcwHwYDVR0jBBgwFoAUmmjFyMO+
|
|
54
|
+
Yec6uc5oDZ32eQZpTVcwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
|
|
55
|
+
AQEA1GGX8oZEKsOrC5vo2wtkEuA6A4jnc6+l6E2wHNRhBpK7Pi77i/IijMoAvW/d
|
|
56
|
+
6C+6vLNogZEsavsi9zfwxjCmu6eJO4QW7/Jpoev8ZPQQE0cDZUQigF5B6Qy8HQ1L
|
|
57
|
+
PT836ReE3X2e7TPVwrhtDC8hNSRar+Wh0VZXHvwhEkyfuea75QgYE8jyqpAviYZT
|
|
58
|
+
jH7OHoVlXZyuRBQOh03PJt+ZZyJLG172C39dmeNXC/N5a07PvDH0TRVlvdbnQVfE
|
|
59
|
+
J7/6IhSn3XbO000zgwcdGjZbPVym/xNiidJCPxrqF7ZaGWfWLmNXLnuGrOm0FusM
|
|
60
|
+
x+z+mIKYbuX4pACxG4t1ycRfOQ==
|
|
61
|
+
-----END CERTIFICATE-----
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
let secureContext: tls.SecureContext | undefined;
|
|
65
|
+
let tlsContextError: Error | undefined;
|
|
66
|
+
|
|
67
|
+
function hasHeaderToken(
|
|
68
|
+
value: string | undefined,
|
|
69
|
+
token: string,
|
|
70
|
+
): boolean {
|
|
71
|
+
if (!value) return false;
|
|
72
|
+
return value
|
|
73
|
+
.split(",")
|
|
74
|
+
.map((part) => part.trim().toLowerCase())
|
|
75
|
+
.includes(token.toLowerCase());
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function tlsContext(): tls.SecureContext {
|
|
79
|
+
if (secureContext) return secureContext;
|
|
80
|
+
if (tlsContextError) throw tlsContextError;
|
|
81
|
+
try {
|
|
82
|
+
secureContext = tls.createSecureContext({
|
|
83
|
+
key: TLS_KEY_PEM,
|
|
84
|
+
cert: TLS_CERT_PEM,
|
|
85
|
+
minVersion: "TLSv1.2",
|
|
86
|
+
});
|
|
87
|
+
return secureContext;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
tlsContextError =
|
|
90
|
+
error instanceof Error ? error : new Error(errMsg(error));
|
|
91
|
+
throw tlsContextError;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function secureEventNames(mode: "client" | "server"): string[] {
|
|
96
|
+
return mode === "client"
|
|
97
|
+
? ["secureConnect", "secure"]
|
|
98
|
+
: ["secure", "secureConnect"];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function maybeStartTlsSocket(
|
|
102
|
+
socket: tls.TLSSocket,
|
|
103
|
+
mode: "client" | "server",
|
|
104
|
+
): void {
|
|
105
|
+
// `tls.connect()` already starts the client handshake in Bun. Calling
|
|
106
|
+
// the internal starter again throws and aborts the upgrade.
|
|
107
|
+
if (mode !== "server") return;
|
|
108
|
+
const starter = socket as tls.TLSSocket & { _start?: () => void };
|
|
109
|
+
if (typeof starter._start === "function") starter._start();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function waitForSecureTlsSocket(
|
|
113
|
+
node: GnutellaServent,
|
|
114
|
+
socket: tls.TLSSocket,
|
|
115
|
+
mode: "client" | "server",
|
|
116
|
+
timeoutMs: number,
|
|
117
|
+
start?: () => void,
|
|
118
|
+
): Promise<tls.TLSSocket> {
|
|
119
|
+
return new Promise((resolve, reject) => {
|
|
120
|
+
let settled = false;
|
|
121
|
+
const cleanup = () => {
|
|
122
|
+
node.collaborators.scheduler.clearTimeout(timer);
|
|
123
|
+
for (const event of secureEventNames(mode)) {
|
|
124
|
+
socket.off(event, onSecure);
|
|
125
|
+
}
|
|
126
|
+
socket.off("error", onError);
|
|
127
|
+
socket.off("close", onClose);
|
|
128
|
+
};
|
|
129
|
+
const finish = (value: tls.TLSSocket) => {
|
|
130
|
+
if (settled) return;
|
|
131
|
+
settled = true;
|
|
132
|
+
cleanup();
|
|
133
|
+
resolve(value);
|
|
134
|
+
};
|
|
135
|
+
const fail = (error: unknown) => {
|
|
136
|
+
if (settled) return;
|
|
137
|
+
settled = true;
|
|
138
|
+
cleanup();
|
|
139
|
+
reject(error instanceof Error ? error : new Error(errMsg(error)));
|
|
140
|
+
};
|
|
141
|
+
const onSecure = () => finish(socket);
|
|
142
|
+
const onError = (error: unknown) => fail(error);
|
|
143
|
+
const onClose = () => fail(new Error("TLS socket closed"));
|
|
144
|
+
const timer = node.collaborators.scheduler.setTimeout(() => {
|
|
145
|
+
fail(new Error("TLS handshake timeout"));
|
|
146
|
+
}, timeoutMs);
|
|
147
|
+
|
|
148
|
+
for (const event of secureEventNames(mode)) {
|
|
149
|
+
socket.once(event, onSecure);
|
|
150
|
+
}
|
|
151
|
+
socket.once("error", onError);
|
|
152
|
+
socket.once("close", onClose);
|
|
153
|
+
try {
|
|
154
|
+
start?.();
|
|
155
|
+
} catch (error) {
|
|
156
|
+
fail(error);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function tlsEnabled(node: GnutellaServent): boolean {
|
|
162
|
+
if (!ENABLE_TLS) return false;
|
|
163
|
+
if (!node.config().enableTls) return false;
|
|
164
|
+
try {
|
|
165
|
+
tlsContext();
|
|
166
|
+
return true;
|
|
167
|
+
} catch {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function socketUsesTls(
|
|
173
|
+
_node: GnutellaServent,
|
|
174
|
+
socket: net.Socket,
|
|
175
|
+
): boolean {
|
|
176
|
+
return (
|
|
177
|
+
socket instanceof tls.TLSSocket ||
|
|
178
|
+
!!(socket as net.Socket & { encrypted?: boolean }).encrypted
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function canUpgradeSocketToTls(
|
|
183
|
+
_node: GnutellaServent,
|
|
184
|
+
socket: net.Socket,
|
|
185
|
+
): boolean {
|
|
186
|
+
return socket instanceof net.Socket;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function peerRequestedTlsUpgrade(
|
|
190
|
+
_node: GnutellaServent,
|
|
191
|
+
headers: Record<string, string>,
|
|
192
|
+
): boolean {
|
|
193
|
+
return hasHeaderToken(headers["upgrade"], TLS_UPGRADE_TOKEN);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function peerAcceptedTlsUpgrade(
|
|
197
|
+
_node: GnutellaServent,
|
|
198
|
+
headers: Record<string, string>,
|
|
199
|
+
): boolean {
|
|
200
|
+
return (
|
|
201
|
+
hasHeaderToken(headers["upgrade"], TLS_UPGRADE_TOKEN) &&
|
|
202
|
+
hasHeaderToken(headers["connection"], "upgrade")
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function clientAcceptedTlsUpgrade(
|
|
207
|
+
_node: GnutellaServent,
|
|
208
|
+
headers: Record<string, string>,
|
|
209
|
+
): boolean {
|
|
210
|
+
return hasHeaderToken(headers["connection"], "upgrade");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function tlsUpgradeToken(_node: GnutellaServent): string {
|
|
214
|
+
return TLS_UPGRADE_TOKEN;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export async function upgradeSocketToTls(
|
|
218
|
+
node: GnutellaServent,
|
|
219
|
+
socket: net.Socket,
|
|
220
|
+
mode: "client" | "server",
|
|
221
|
+
initialBuf: Uint8Array = Buffer.alloc(0),
|
|
222
|
+
): Promise<tls.TLSSocket> {
|
|
223
|
+
if (!node.tlsEnabled()) throw new Error("TLS unavailable");
|
|
224
|
+
if (initialBuf.length) socket.unshift(initialBuf);
|
|
225
|
+
socket.setTimeout(0);
|
|
226
|
+
socket.pause();
|
|
227
|
+
|
|
228
|
+
const timeoutMs = Math.max(
|
|
229
|
+
TLS_TIMEOUT_MS,
|
|
230
|
+
node.config().connectTimeoutMs,
|
|
231
|
+
);
|
|
232
|
+
const upgraded =
|
|
233
|
+
mode === "client"
|
|
234
|
+
? tls.connect({
|
|
235
|
+
socket,
|
|
236
|
+
secureContext: tlsContext(),
|
|
237
|
+
rejectUnauthorized: false,
|
|
238
|
+
minVersion: "TLSv1.2",
|
|
239
|
+
})
|
|
240
|
+
: new tls.TLSSocket(socket, {
|
|
241
|
+
isServer: true,
|
|
242
|
+
secureContext: tlsContext(),
|
|
243
|
+
requestCert: false,
|
|
244
|
+
rejectUnauthorized: false,
|
|
245
|
+
});
|
|
246
|
+
return await waitForSecureTlsSocket(
|
|
247
|
+
node,
|
|
248
|
+
upgraded,
|
|
249
|
+
mode,
|
|
250
|
+
timeoutMs,
|
|
251
|
+
() => {
|
|
252
|
+
upgraded.setNoDelay(true);
|
|
253
|
+
maybeStartTlsSocket(upgraded, mode);
|
|
254
|
+
upgraded.resume();
|
|
255
|
+
},
|
|
256
|
+
);
|
|
257
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { PeerCapabilities, PeerRole } from "../types";
|
|
2
|
+
import type { GnutellaServent } from "./node";
|
|
3
|
+
import type { Peer } from "./node_types";
|
|
4
|
+
|
|
5
|
+
function isMeshPeerRole(role: PeerRole): boolean {
|
|
6
|
+
return role !== "leaf";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function nodeMode(node: GnutellaServent) {
|
|
10
|
+
return node.config().nodeMode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function classifyPeerRole(
|
|
14
|
+
node: GnutellaServent,
|
|
15
|
+
capabilities: PeerCapabilities,
|
|
16
|
+
): PeerRole {
|
|
17
|
+
const remoteIsUltrapeer = capabilities.isUltrapeer;
|
|
18
|
+
const mode = node.nodeMode();
|
|
19
|
+
|
|
20
|
+
if (mode === "ultrapeer") {
|
|
21
|
+
return remoteIsUltrapeer === true ? "ultrapeer" : "leaf";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return remoteIsUltrapeer === true ? "ultrapeer" : "leaf";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function peerRole(
|
|
28
|
+
_node: GnutellaServent,
|
|
29
|
+
peer: Pick<Peer, "role">,
|
|
30
|
+
): PeerRole {
|
|
31
|
+
return peer.role;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function countPeersByRole(
|
|
35
|
+
node: GnutellaServent,
|
|
36
|
+
role: PeerRole,
|
|
37
|
+
): number {
|
|
38
|
+
let count = 0;
|
|
39
|
+
for (const peer of node.peers.values()) {
|
|
40
|
+
if (peer.role === role) count++;
|
|
41
|
+
}
|
|
42
|
+
return count;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function connectedLeafCount(node: GnutellaServent): number {
|
|
46
|
+
return node.countPeersByRole("leaf");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function connectedMeshPeerCount(node: GnutellaServent): number {
|
|
50
|
+
let count = 0;
|
|
51
|
+
for (const peer of node.peers.values()) {
|
|
52
|
+
if (isMeshPeerRole(peer.role)) count++;
|
|
53
|
+
}
|
|
54
|
+
return count;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function availableDialSlots(node: GnutellaServent): number {
|
|
58
|
+
const c = node.config();
|
|
59
|
+
if (node.nodeMode() === "ultrapeer") {
|
|
60
|
+
return Math.max(
|
|
61
|
+
0,
|
|
62
|
+
c.maxUltrapeerConnections -
|
|
63
|
+
node.connectedMeshPeerCount() -
|
|
64
|
+
node.dialing.size,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return Math.max(
|
|
68
|
+
0,
|
|
69
|
+
c.maxUltrapeerConnections -
|
|
70
|
+
node.connectedMeshPeerCount() -
|
|
71
|
+
node.dialing.size,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function canAcceptPeerRole(
|
|
76
|
+
node: GnutellaServent,
|
|
77
|
+
role: PeerRole,
|
|
78
|
+
): { ok: true } | { ok: false; code: number; reason: string } {
|
|
79
|
+
const c = node.config();
|
|
80
|
+
const mode = node.nodeMode();
|
|
81
|
+
|
|
82
|
+
if (mode === "leaf") {
|
|
83
|
+
if (role === "leaf") {
|
|
84
|
+
return {
|
|
85
|
+
ok: false,
|
|
86
|
+
code: 503,
|
|
87
|
+
reason: `Shielded leaf node (${c.maxUltrapeerConnections} ultrapeers max)`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (node.connectedMeshPeerCount() >= c.maxUltrapeerConnections) {
|
|
91
|
+
return {
|
|
92
|
+
ok: false,
|
|
93
|
+
code: 503,
|
|
94
|
+
reason: `Too many ultrapeer connections (${c.maxUltrapeerConnections} max)`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return { ok: true };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (role === "leaf") {
|
|
101
|
+
if (node.connectedLeafCount() >= c.maxLeafConnections) {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
code: 503,
|
|
105
|
+
reason: `Too many leaf connections (${c.maxLeafConnections} max)`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return { ok: true };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (node.connectedMeshPeerCount() >= c.maxUltrapeerConnections) {
|
|
112
|
+
return {
|
|
113
|
+
ok: false,
|
|
114
|
+
code: 503,
|
|
115
|
+
reason: `Too many ultrapeer connections (${c.maxUltrapeerConnections} max)`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return { ok: true };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function shouldRelayQueries(node: GnutellaServent): boolean {
|
|
122
|
+
return node.nodeMode() === "ultrapeer";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function shouldRelayPings(node: GnutellaServent): boolean {
|
|
126
|
+
return node.nodeMode() === "ultrapeer";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function isLeafPeer(
|
|
130
|
+
_node: GnutellaServent,
|
|
131
|
+
peer: Pick<Peer, "role">,
|
|
132
|
+
): boolean {
|
|
133
|
+
return peer.role === "leaf";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function isMeshPeer(
|
|
137
|
+
_node: GnutellaServent,
|
|
138
|
+
peer: Pick<Peer, "role">,
|
|
139
|
+
): boolean {
|
|
140
|
+
return isMeshPeerRole(peer.role);
|
|
141
|
+
}
|