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.
Files changed (45) hide show
  1. package/CLI.md +189 -0
  2. package/DEVELOPER.md +193 -0
  3. package/LICENSE +674 -0
  4. package/QUICKSTART.md +133 -0
  5. package/README.md +74 -0
  6. package/bin/gnutella.ts +15 -0
  7. package/gnutella.json.example +18 -0
  8. package/package.json +72 -0
  9. package/src/cli.ts +692 -0
  10. package/src/cli_shared.ts +359 -0
  11. package/src/const.ts +138 -0
  12. package/src/gwebcache/bootstrap.ts +491 -0
  13. package/src/gwebcache/response.ts +391 -0
  14. package/src/gwebcache/shared.ts +116 -0
  15. package/src/gwebcache/types.ts +187 -0
  16. package/src/gwebcache_client.ts +13 -0
  17. package/src/protocol/browse_host.ts +552 -0
  18. package/src/protocol/client_blocking.ts +29 -0
  19. package/src/protocol/codec.ts +715 -0
  20. package/src/protocol/content_urn.ts +170 -0
  21. package/src/protocol/core_utils.ts +43 -0
  22. package/src/protocol/file_server.ts +245 -0
  23. package/src/protocol/ggep.ts +168 -0
  24. package/src/protocol/handshake.ts +199 -0
  25. package/src/protocol/http_download_reader.ts +112 -0
  26. package/src/protocol/magnet.ts +176 -0
  27. package/src/protocol/node.ts +416 -0
  28. package/src/protocol/node_handshake.ts +992 -0
  29. package/src/protocol/node_lifecycle.ts +210 -0
  30. package/src/protocol/node_protocol_runtime.ts +949 -0
  31. package/src/protocol/node_qrp_runtime.ts +97 -0
  32. package/src/protocol/node_query_routing.ts +208 -0
  33. package/src/protocol/node_state.ts +745 -0
  34. package/src/protocol/node_tls.ts +257 -0
  35. package/src/protocol/node_topology.ts +141 -0
  36. package/src/protocol/node_transfer.ts +455 -0
  37. package/src/protocol/node_types.ts +106 -0
  38. package/src/protocol/peer_state.ts +675 -0
  39. package/src/protocol/qrp.ts +549 -0
  40. package/src/protocol/query_search.ts +29 -0
  41. package/src/protocol/share_index.ts +131 -0
  42. package/src/protocol/share_library.ts +246 -0
  43. package/src/protocol.ts +36 -0
  44. package/src/shared.ts +236 -0
  45. package/src/types.ts +452 -0
@@ -0,0 +1,97 @@
1
+ import { DEFAULT_QRP_ENTRY_BITS, TYPE } from "../const";
2
+ import { errMsg } from "../shared";
3
+ import { parseRouteTableUpdate } from "./codec";
4
+ import type { GnutellaServent } from "./node";
5
+ import {
6
+ publishedQrpTableForPeer,
7
+ sendPublishedQrpToMeshPeers,
8
+ } from "./node_query_routing";
9
+ import type { Peer } from "./node_types";
10
+ import {
11
+ QrpTable,
12
+ validateRemoteQrpPatchSequence,
13
+ validateRemoteQrpReset,
14
+ } from "./qrp";
15
+
16
+ function rejectQrpUpdate(
17
+ node: GnutellaServent,
18
+ peer: Peer,
19
+ reason: string,
20
+ ): void {
21
+ if (peer.capabilities.supportsBye) node.sendBye(peer, 413, reason);
22
+ else peer.socket.end();
23
+ }
24
+
25
+ export function onRouteTableUpdate(
26
+ node: GnutellaServent,
27
+ peer: Peer,
28
+ payload: Buffer,
29
+ ): void {
30
+ let msg: ReturnType<typeof parseRouteTableUpdate>;
31
+ try {
32
+ msg = parseRouteTableUpdate(payload);
33
+ } catch (error) {
34
+ rejectQrpUpdate(node, peer, errMsg(error));
35
+ return;
36
+ }
37
+ if (msg.variant === "reset") {
38
+ const rejection = validateRemoteQrpReset(msg);
39
+ if (rejection) {
40
+ rejectQrpUpdate(node, peer, rejection);
41
+ return;
42
+ }
43
+ peer.remoteQrp.resetSeen = true;
44
+ peer.remoteQrp.tableSize = msg.tableLength;
45
+ peer.remoteQrp.infinity = msg.infinity;
46
+ peer.remoteQrp.entryBits = DEFAULT_QRP_ENTRY_BITS;
47
+ peer.remoteQrp.table = null;
48
+ peer.remoteQrp.seqSize = 0;
49
+ peer.remoteQrp.parts.clear();
50
+ return;
51
+ }
52
+ const rejection = validateRemoteQrpPatchSequence(peer.remoteQrp, msg);
53
+ if (rejection) {
54
+ rejectQrpUpdate(node, peer, rejection);
55
+ return;
56
+ }
57
+ peer.remoteQrp.seqSize = msg.seqSize;
58
+ peer.remoteQrp.compressor = msg.compressor;
59
+ peer.remoteQrp.entryBits = msg.entryBits;
60
+ peer.remoteQrp.parts.set(msg.seqNo, Buffer.from(msg.data));
61
+ const applyRejection = QrpTable.applyPatch(peer.remoteQrp);
62
+ if (applyRejection) {
63
+ rejectQrpUpdate(node, peer, applyRejection);
64
+ return;
65
+ }
66
+ if (peer.remoteQrp.table && peer.role === "leaf")
67
+ sendPublishedQrpToMeshPeers(node);
68
+ }
69
+
70
+ export async function sendQrpTable(
71
+ node: GnutellaServent,
72
+ peer: Peer,
73
+ ): Promise<void> {
74
+ const published = publishedQrpTableForPeer(node, peer);
75
+ if (!published) return;
76
+ node.sendToPeer(
77
+ peer,
78
+ TYPE.ROUTE_TABLE_UPDATE,
79
+ node.randomId16(),
80
+ 1,
81
+ 0,
82
+ published.encodeReset(),
83
+ );
84
+ for (const patch of published.encodePatchChunks(
85
+ Math.min(node.config().maxPayloadBytes, 60 * 1024),
86
+ )) {
87
+ node.sendToPeer(
88
+ peer,
89
+ TYPE.ROUTE_TABLE_UPDATE,
90
+ node.randomId16(),
91
+ 1,
92
+ 0,
93
+ patch,
94
+ );
95
+ await node.sleep(5);
96
+ }
97
+ }
@@ -0,0 +1,208 @@
1
+ import { TYPE } from "../const";
2
+ import type { QueryDescriptor } from "../types";
3
+ import type { GnutellaServent } from "./node";
4
+ import type { Peer } from "./node_types";
5
+ import { canRouteRemoteQrpQuery, QrpTable } from "./qrp";
6
+
7
+ function canRouteQueryToPeer(
8
+ node: GnutellaServent,
9
+ peer: Peer,
10
+ q: QueryDescriptor,
11
+ ): boolean {
12
+ if (!node.config().enableQrp) return true;
13
+ if (node.isLeafPeer(peer))
14
+ return canRouteRemoteQrpQuery(peer.remoteQrp, q);
15
+ if (
16
+ peer.role === "ultrapeer" &&
17
+ peer.capabilities.ultrapeerQueryRoutingVersion
18
+ ) {
19
+ return canRouteRemoteQrpQuery(peer.remoteQrp, q);
20
+ }
21
+ return true;
22
+ }
23
+
24
+ function buildAggregateUltrapeerQrp(node: GnutellaServent): QrpTable {
25
+ const table = new QrpTable(
26
+ node.qrpTable.tableSize,
27
+ node.qrpTable.infinity,
28
+ 1,
29
+ );
30
+ table.clear();
31
+ table.mergeFromQrp(node.qrpTable);
32
+ for (const peer of node.peers.values()) {
33
+ if (!node.isLeafPeer(peer)) continue;
34
+ table.mergeFromRemoteQrp(peer.remoteQrp);
35
+ }
36
+ return table;
37
+ }
38
+
39
+ export function sendPublishedQrpToMeshPeers(node: GnutellaServent): void {
40
+ if (node.nodeMode() !== "ultrapeer" || !node.config().enableQrp) return;
41
+ for (const peer of node.peers.values()) {
42
+ if (!node.isMeshPeer(peer)) continue;
43
+ void node.sendQrpTable(peer).catch(() => void 0);
44
+ }
45
+ }
46
+
47
+ export function publishedQrpTableForPeer(
48
+ node: GnutellaServent,
49
+ peer: Peer,
50
+ ): QrpTable | undefined {
51
+ if (!node.config().enableQrp) return undefined;
52
+ if (node.nodeMode() === "ultrapeer") {
53
+ if (!node.isMeshPeer(peer)) return undefined;
54
+ if (!peer.capabilities.ultrapeerQueryRoutingVersion) return undefined;
55
+ return buildAggregateUltrapeerQrp(node);
56
+ }
57
+ if (
58
+ !(
59
+ peer.capabilities.queryRoutingVersion ||
60
+ peer.capabilities.ultrapeerQueryRoutingVersion
61
+ )
62
+ ) {
63
+ return undefined;
64
+ }
65
+ return node.qrpTable;
66
+ }
67
+
68
+ function sendQueryToLeafPeer(
69
+ node: GnutellaServent,
70
+ peer: Peer,
71
+ descriptorId: Buffer,
72
+ logicalTtl: number,
73
+ hops: number,
74
+ payload: Buffer,
75
+ q: QueryDescriptor,
76
+ ): boolean {
77
+ if (!node.isLeafPeer(peer)) return false;
78
+ if (!canRouteQueryToPeer(node, peer, q)) return true;
79
+ node.sendToPeer(
80
+ peer,
81
+ TYPE.QUERY,
82
+ descriptorId,
83
+ Math.max(1, logicalTtl),
84
+ hops,
85
+ payload,
86
+ );
87
+ return true;
88
+ }
89
+
90
+ function sendLocalOriginQueryToMeshPeer(
91
+ node: GnutellaServent,
92
+ peer: Peer,
93
+ descriptorId: Buffer,
94
+ logicalTtl: number,
95
+ hops: number,
96
+ payload: Buffer,
97
+ q: QueryDescriptor,
98
+ ): boolean {
99
+ if (logicalTtl <= 0) return true;
100
+ if (
101
+ node.nodeMode() === "ultrapeer" &&
102
+ logicalTtl === 1 &&
103
+ !canRouteQueryToPeer(node, peer, q)
104
+ ) {
105
+ return true;
106
+ }
107
+ node.sendToPeer(
108
+ peer,
109
+ TYPE.QUERY,
110
+ descriptorId,
111
+ logicalTtl,
112
+ hops,
113
+ payload,
114
+ );
115
+ return true;
116
+ }
117
+
118
+ function sendRelayedQueryToMeshPeer(
119
+ node: GnutellaServent,
120
+ peer: Peer,
121
+ descriptorId: Buffer,
122
+ logicalTtl: number,
123
+ hops: number,
124
+ payload: Buffer,
125
+ q: QueryDescriptor,
126
+ ): void {
127
+ if (logicalTtl <= 0 || node.nodeMode() === "leaf") return;
128
+ if (node.nodeMode() === "ultrapeer" && logicalTtl === 1) {
129
+ if (!canRouteQueryToPeer(node, peer, q)) return;
130
+ node.sendToPeer(peer, TYPE.QUERY, descriptorId, 1, hops, payload);
131
+ return;
132
+ }
133
+ node.sendToPeer(
134
+ peer,
135
+ TYPE.QUERY,
136
+ descriptorId,
137
+ logicalTtl - 1,
138
+ hops + 1,
139
+ payload,
140
+ );
141
+ }
142
+
143
+ export function routeQueryToPeers(
144
+ node: GnutellaServent,
145
+ descriptorId: Buffer,
146
+ logicalTtl: number,
147
+ hops: number,
148
+ payload: Buffer,
149
+ q: QueryDescriptor,
150
+ exceptPeerKey?: string,
151
+ localOrigin = false,
152
+ ): void {
153
+ for (const candidate of node.peers.values()) {
154
+ if (exceptPeerKey && candidate.key === exceptPeerKey) continue;
155
+ if (
156
+ sendQueryToLeafPeer(
157
+ node,
158
+ candidate,
159
+ descriptorId,
160
+ logicalTtl,
161
+ hops,
162
+ payload,
163
+ q,
164
+ )
165
+ ) {
166
+ continue;
167
+ }
168
+ if (localOrigin) {
169
+ sendLocalOriginQueryToMeshPeer(
170
+ node,
171
+ candidate,
172
+ descriptorId,
173
+ logicalTtl,
174
+ hops,
175
+ payload,
176
+ q,
177
+ );
178
+ continue;
179
+ }
180
+ sendRelayedQueryToMeshPeer(
181
+ node,
182
+ candidate,
183
+ descriptorId,
184
+ logicalTtl,
185
+ hops,
186
+ payload,
187
+ q,
188
+ );
189
+ }
190
+ }
191
+
192
+ export function broadcastPingToPeers(
193
+ node: GnutellaServent,
194
+ descriptorId: Buffer,
195
+ ttl: number,
196
+ hops: number,
197
+ payload: Buffer,
198
+ exceptPeerKey?: string,
199
+ ): void {
200
+ const skipLeaves = node.nodeMode() === "ultrapeer";
201
+ const peers = [...node.peers.values()];
202
+ for (const peer of peers) {
203
+ const skipPeer = exceptPeerKey != null && peer.key === exceptPeerKey;
204
+ const skipLeaf = skipLeaves && node.isLeafPeer(peer);
205
+ if (skipPeer || skipLeaf) continue;
206
+ node.sendToPeer(peer, TYPE.PING, descriptorId, ttl, hops, payload);
207
+ }
208
+ }