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
package/src/types.ts ADDED
@@ -0,0 +1,452 @@
1
+ import type { NetConnectOpts, Server, Socket } from "node:net";
2
+
3
+ import type {
4
+ ConnectBootstrapOptions,
5
+ ConnectBootstrapResult,
6
+ ReportSelfOptions,
7
+ ReportSelfResult,
8
+ } from "./gwebcache/types";
9
+
10
+ type EventBase<T extends string> = { type: T; at: string };
11
+
12
+ export type CliNode = {
13
+ getPeers(): Array<{
14
+ key: string;
15
+ remoteLabel: string;
16
+ browseTarget?: string;
17
+ role: PeerRole;
18
+ outbound: boolean;
19
+ dialTarget?: string;
20
+ userAgent?: string;
21
+ compression: boolean;
22
+ tls: boolean;
23
+ }>;
24
+ getResults(): Array<{
25
+ resultNo: number;
26
+ queryIdHex?: string;
27
+ queryHops?: number;
28
+ remoteHost: string;
29
+ remotePort: number;
30
+ speedKBps?: number;
31
+ fileIndex?: number;
32
+ fileSize: number;
33
+ fileName: string;
34
+ serventIdHex: string;
35
+ viaPeerKey?: string;
36
+ sha1Urn?: string;
37
+ urns?: string[];
38
+ metadata?: string[];
39
+ vendorCode?: string;
40
+ needsPush?: boolean;
41
+ busy?: boolean;
42
+ }>;
43
+ getShares(): Array<{ index: number; size: number; rel: string }>;
44
+ getStatus(): {
45
+ peers: number;
46
+ shares: number;
47
+ results: number;
48
+ knownPeers: number;
49
+ };
50
+ };
51
+
52
+ export type ParsedCli = {
53
+ config: string;
54
+ exec: string[];
55
+ command: string;
56
+ };
57
+
58
+ export type PeerAddr = { host: string; port: number };
59
+ export type PeerState = Record<string, number>;
60
+ export type Route = { peerKey: string; ts: number };
61
+ type NodeMode = "leaf" | "ultrapeer";
62
+ export type PeerRole = "leaf" | "ultrapeer";
63
+
64
+ export type RemoteQrpState = {
65
+ resetSeen: boolean;
66
+ tableSize: number;
67
+ infinity: number;
68
+ entryBits: number;
69
+ table: Uint8Array | null;
70
+ seqSize: number;
71
+ compressor: number;
72
+ parts: Map<number, Buffer>;
73
+ };
74
+
75
+ export type PeerCapabilities = {
76
+ version: string;
77
+ headers: Record<string, string>;
78
+ userAgent?: string;
79
+ supportsGgep: boolean;
80
+ supportsPongCaching: boolean;
81
+ supportsBye: boolean;
82
+ supportsCompression: boolean;
83
+ supportsTls: boolean;
84
+ compressIn: boolean;
85
+ compressOut: boolean;
86
+ isUltrapeer?: boolean;
87
+ ultrapeerNeeded?: boolean;
88
+ queryRoutingVersion?: string;
89
+ ultrapeerQueryRoutingVersion?: string;
90
+ dynamicQueryingVersion?: string;
91
+ extProbesVersion?: string;
92
+ degree?: number;
93
+ isCrawler?: boolean;
94
+ listenIp?: PeerAddr;
95
+ };
96
+
97
+ export type QueryDescriptor = {
98
+ search: string;
99
+ flagsRaw: number;
100
+ requesterFirewalled: boolean;
101
+ wantsXml: boolean;
102
+ leafGuidedDynamic: boolean;
103
+ ggepHAllowed: boolean;
104
+ outOfBand: boolean;
105
+ maxHits: number;
106
+ urns: string[];
107
+ xmlBlocks: string[];
108
+ rawExtensions: Buffer;
109
+ };
110
+
111
+ export type QueryHitDescriptor = {
112
+ hits: number;
113
+ port: number;
114
+ ip: string;
115
+ speedKBps: number;
116
+ results: Array<{
117
+ fileIndex: number;
118
+ fileSize: number;
119
+ fileName: string;
120
+ urns: string[];
121
+ metadata: string[];
122
+ rawExtension: Buffer;
123
+ }>;
124
+ vendorCode?: string;
125
+ openDataSize?: number;
126
+ flagGgep?: boolean;
127
+ flagBusy?: boolean;
128
+ flagHaveUploaded?: boolean;
129
+ flagUploadSpeedMeasured?: boolean;
130
+ flagPush?: boolean;
131
+ qhdPrivateArea?: Buffer;
132
+ serventId: Buffer;
133
+ serventIdHex: string;
134
+ };
135
+
136
+ export type ShareFile = {
137
+ index: number;
138
+ name: string;
139
+ rel: string;
140
+ abs: string;
141
+ size: number;
142
+ mtimeMs: number;
143
+ sha1?: Buffer;
144
+ sha1Urn?: string;
145
+ keywords: string[];
146
+ };
147
+
148
+ export type SearchHit = {
149
+ resultNo: number;
150
+ queryIdHex: string;
151
+ queryHops: number;
152
+ remoteHost: string;
153
+ remotePort: number;
154
+ speedKBps: number;
155
+ fileIndex: number;
156
+ fileName: string;
157
+ fileSize: number;
158
+ serventIdHex: string;
159
+ viaPeerKey: string;
160
+ sha1Urn?: string;
161
+ urns?: string[];
162
+ metadata?: string[];
163
+ vendorCode?: string;
164
+ needsPush?: boolean;
165
+ busy?: boolean;
166
+ };
167
+
168
+ export type PendingPush = {
169
+ serventIdHex: string;
170
+ result: SearchHit;
171
+ destPath: string;
172
+ createdAt: number;
173
+ resolve: (value: unknown) => void;
174
+ reject: (error: unknown) => void;
175
+ };
176
+
177
+ export type DownloadRecord = {
178
+ at: string;
179
+ fileName: string;
180
+ bytes: number;
181
+ host: string;
182
+ port: number;
183
+ mode: string;
184
+ destPath: string;
185
+ };
186
+
187
+ export type ConfigDoc = {
188
+ config: {
189
+ listenHost: string;
190
+ listenPort: number;
191
+ advertisedHost?: string;
192
+ advertisedPort?: number;
193
+ blockedIps?: string[];
194
+ gwebCacheUrls?: string[];
195
+ ultrapeer: boolean;
196
+ maxConnections?: number;
197
+ maxUltrapeerConnections?: number;
198
+ maxLeafConnections?: number;
199
+ enableTls?: boolean;
200
+ monitorIgnoreEvents?: string[];
201
+ dataDir: string;
202
+ };
203
+ state: {
204
+ serventIdHex: string;
205
+ peers: PeerState;
206
+ };
207
+ };
208
+
209
+ export type RuntimeConfig = {
210
+ listenHost: string;
211
+ listenPort: number;
212
+ advertisedHost?: string;
213
+ advertisedPort?: number;
214
+ blockedIps: string[];
215
+ gwebCacheUrls: string[];
216
+ ultrapeer: boolean;
217
+ monitorIgnoreEvents: string[];
218
+ nodeMode: NodeMode;
219
+ dataDir: string;
220
+ downloadsDir: string;
221
+ peerSeenThresholdSec: number;
222
+ maxConnections: number;
223
+ maxUltrapeerConnections: number;
224
+ maxLeafConnections: number;
225
+ connectTimeoutMs: number;
226
+ pingIntervalSec: number;
227
+ reconnectIntervalSec: number;
228
+ rescanSharesSec: number;
229
+ routeTtlSec: number;
230
+ seenTtlSec: number;
231
+ maxPayloadBytes: number;
232
+ maxTtl: number;
233
+ defaultPingTtl: number;
234
+ defaultQueryTtl: number;
235
+ advertisedSpeedKBps: number;
236
+ downloadTimeoutMs: number;
237
+ pushWaitMs: number;
238
+ maxResultsPerQuery: number;
239
+ userAgent: string;
240
+ queryRoutingVersion: "0.1" | "0.2";
241
+ enableCompression: boolean;
242
+ enableQrp: boolean;
243
+ enableBye: boolean;
244
+ enablePongCaching: boolean;
245
+ enableGgep: boolean;
246
+ enableTls: boolean;
247
+ serveUriRes: boolean;
248
+ vendorCode: string;
249
+ };
250
+
251
+ export type PeerInfo = {
252
+ key: string;
253
+ remoteLabel: string;
254
+ browseTarget?: string;
255
+ role: PeerRole;
256
+ outbound: boolean;
257
+ dialTarget?: string;
258
+ userAgent?: string;
259
+ compression: boolean;
260
+ tls: boolean;
261
+ };
262
+
263
+ export type NodeStatus = {
264
+ peers: number;
265
+ shares: number;
266
+ results: number;
267
+ knownPeers: number;
268
+ };
269
+
270
+ export type GnutellaEvent =
271
+ | (EventBase<"STARTED"> & {
272
+ listenHost: string;
273
+ listenPort: number;
274
+ advertisedHost: string;
275
+ advertisedPort: number;
276
+ })
277
+ | (EventBase<"IDENTITY"> & {
278
+ serventIdHex: string;
279
+ })
280
+ | (EventBase<"SHARES_REFRESHED"> & {
281
+ count: number;
282
+ totalKBytes: number;
283
+ })
284
+ | (EventBase<"MAINTENANCE_ERROR"> & {
285
+ operation:
286
+ | "SHARE_RESCAN"
287
+ | "RECONNECT"
288
+ | "SAVE"
289
+ | "GWEBCACHE_UPDATE";
290
+ message: string;
291
+ })
292
+ | (EventBase<"PROBE_REJECTED"> & {
293
+ message: string;
294
+ })
295
+ | (EventBase<"HANDSHAKE_DEBUG"> & {
296
+ direction: "inbound" | "outbound";
297
+ phase: string;
298
+ peer: string;
299
+ message: string;
300
+ })
301
+ | (EventBase<"PEER_CONNECTED"> & {
302
+ peer: PeerInfo;
303
+ })
304
+ | (EventBase<"PEER_DROPPED"> & {
305
+ peer: PeerInfo;
306
+ message: string;
307
+ })
308
+ | (EventBase<"PEER_MESSAGE_RECEIVED"> & {
309
+ peer: PeerInfo;
310
+ payloadType: number;
311
+ payloadTypeName: string;
312
+ descriptorIdHex: string;
313
+ ttl: number;
314
+ hops: number;
315
+ payloadLength: number;
316
+ })
317
+ | (EventBase<"PEER_MESSAGE_SENT"> & {
318
+ peer: PeerInfo;
319
+ payloadType: number;
320
+ payloadTypeName: string;
321
+ descriptorIdHex: string;
322
+ ttl: number;
323
+ hops: number;
324
+ payloadLength: number;
325
+ })
326
+ | (EventBase<"PONG"> & {
327
+ ip: string;
328
+ port: number;
329
+ files: number;
330
+ kbytes: number;
331
+ })
332
+ | (EventBase<"QUERY_RECEIVED"> & {
333
+ peer: PeerInfo;
334
+ descriptorIdHex: string;
335
+ ttl: number;
336
+ hops: number;
337
+ search: string;
338
+ urns: string[];
339
+ })
340
+ | (EventBase<"QUERY_RESULT"> & {
341
+ hit: SearchHit;
342
+ })
343
+ | (EventBase<"PUSH_REQUESTED"> & {
344
+ fileIndex: number;
345
+ fileName: string;
346
+ ip: string;
347
+ port: number;
348
+ })
349
+ | (EventBase<"PUSH_CALLBACK_FAILED"> & {
350
+ message: string;
351
+ })
352
+ | (EventBase<"PUSH_UPLOAD_FAILED"> & {
353
+ message: string;
354
+ })
355
+ | (EventBase<"DOWNLOAD_SUCCEEDED"> & {
356
+ mode: "direct" | "push";
357
+ resultNo: number;
358
+ fileName: string;
359
+ destPath: string;
360
+ remoteHost: string;
361
+ remotePort: number;
362
+ })
363
+ | (EventBase<"DOWNLOAD_DIRECT_FAILED"> & {
364
+ resultNo: number;
365
+ fileName: string;
366
+ destPath: string;
367
+ remoteHost: string;
368
+ remotePort: number;
369
+ message: string;
370
+ })
371
+ | (EventBase<"PING_SENT"> & {
372
+ descriptorIdHex: string;
373
+ ttl: number;
374
+ })
375
+ | (EventBase<"QUERY_SENT"> & {
376
+ descriptorIdHex: string;
377
+ ttl: number;
378
+ search: string;
379
+ })
380
+ | (EventBase<"QUERY_SKIPPED"> & {
381
+ reason: "NO_PEERS_CONNECTED";
382
+ });
383
+
384
+ export type GnutellaEventListener = (event: GnutellaEvent) => void;
385
+ type NodeClock = {
386
+ now: () => number;
387
+ };
388
+
389
+ type NodeScheduler = {
390
+ setTimeout: (fn: () => void, ms: number) => NodeJS.Timeout;
391
+ clearTimeout: (timer: NodeJS.Timeout) => void;
392
+ setInterval: (fn: () => void, ms: number) => NodeJS.Timeout;
393
+ clearInterval: (timer: NodeJS.Timeout) => void;
394
+ sleep: (ms: number) => Promise<void>;
395
+ };
396
+
397
+ type NodeNetFactory = {
398
+ createConnection: (options: NetConnectOpts) => Socket;
399
+ createServer: (listener: (socket: Socket) => void) => Server;
400
+ };
401
+
402
+ type NodeBootstrapClient = {
403
+ connectBootstrapPeers: (
404
+ options: ConnectBootstrapOptions,
405
+ ) => Promise<ConnectBootstrapResult>;
406
+ reportSelfToGWebCaches: (
407
+ options: ReportSelfOptions,
408
+ ) => Promise<ReportSelfResult>;
409
+ };
410
+
411
+ export type GnutellaServentCollaborators = {
412
+ clock: NodeClock;
413
+ scheduler: NodeScheduler;
414
+ netFactory: NodeNetFactory;
415
+ bootstrapClient: NodeBootstrapClient;
416
+ };
417
+
418
+ export type GnutellaServentCollaboratorOverrides = {
419
+ clock?: Partial<NodeClock>;
420
+ scheduler?: Partial<NodeScheduler>;
421
+ netFactory?: Partial<NodeNetFactory>;
422
+ bootstrapClient?: Partial<NodeBootstrapClient>;
423
+ };
424
+
425
+ export type GnutellaServentOptions = {
426
+ onEvent?: GnutellaEventListener;
427
+ runtimeConfig?: Partial<RuntimeConfig>;
428
+ collaborators?: GnutellaServentCollaboratorOverrides;
429
+ };
430
+
431
+ export type ConnectPeerResult = {
432
+ peer: string;
433
+ status:
434
+ | "connected"
435
+ | "already-connected"
436
+ | "dialing"
437
+ | "saved"
438
+ | "blocked";
439
+ message?: string;
440
+ };
441
+
442
+ export type BlockIpResult = {
443
+ ip: string;
444
+ status: "blocked" | "already-blocked";
445
+ droppedPeers: number;
446
+ removedKnownPeers: number;
447
+ };
448
+
449
+ export type UnblockIpResult = {
450
+ ip: string;
451
+ status: "unblocked" | "not-blocked";
452
+ };