UNPKG

holesail-client

Version:

A client for securely connecting to holesail-server using P2P

179 lines (167 loc) 5.59 kB
// Importing required modules const HyperDHT = require('hyperdht') // HyperDHT module for DHT functionality const net = require('net') // Net module for creating network clients and servers const libNet = require('@holesail/hyper-cmd-lib-net') // Custom network library const b4a = require('b4a') const z32 = require('z32') class HolesailClient { constructor (opts = {}) { this.logger = opts.logger || { log: () => {} } this.seed = opts.key this.secure = opts.secure || false if (this.secure) { this.keyPair = HyperDHT.keyPair(z32.decode(this.seed)) this.publicKey = this.keyPair.publicKey } else { this.publicKey = z32.decode(this.seed) } this.dht = new HyperDHT({ keyPair: this.keyPair }) this.stats = {} } async connect (options = {}, callback) { this.logger.log({ type: 1, msg: `Connecting to key: ${this.seed}, secure: ${this.secure}` }) let dhtData = {} const dhtValue = await this.get() if (dhtValue) { dhtData = JSON.parse(dhtValue.value) this.logger.log({ type: 0, msg: `Retrieved DHT data: ${JSON.stringify(dhtData)}` }) } else { this.logger.log({ type: 2, msg: 'No DHT data retrieved' }) } options.port = options.port ?? dhtData.port ?? 8989 options.host = options.host ?? dhtData.host ?? '127.0.0.1' options.udp = options.udp ?? dhtData.udp ?? false this.args = options this.state = 'waiting' if (!options.udp) { this.handleTCP(options, callback) } else { this.handleUDP(options, callback) } } // end connect // Handle TCP connections handleTCP (options, callback) { this.logger.log({ type: 0, msg: 'Handling TCP connection' }) this.proxy = net.createServer({ allowHalfOpen: true }, (c) => { this.logger.log({ type: 0, msg: 'Incoming proxy connection' }) return libNet.connPiper( c, () => { this.logger.log({ type: 0, msg: `Connecting to remote DHT: ${z32.encode(this.publicKey)}` }) return this.dht.connect(this.publicKey, { reusableSocket: true }) }, { compress: false, logger: this.logger }, this.stats ) }) this.proxy.listen(options.port, options.host, () => { this.state = 'listening' this.logger.log({ type: 1, msg: `Proxy listening on ${options.host}:${options.port}` }) callback?.() }) } // Handle UDP connections handleUDP (options, callback) { this.logger.log({ type: 0, msg: 'Handling UDP connection' }) const opts = { port: options.port, host: options.host, bind: true } libNet.udpConnect(opts, (c) => { this.logger.log({ type: 0, msg: 'UDP socket connected' }) const stream = () => { this.logger.log({ type: 0, msg: `Connecting to remote DHT: ${z32.encode(this.publicKey)}` }) return this.dht.connect(this.publicKey) } libNet.udpPiper(stream, c, { retryDelay: 2000, client: true, logger: this.logger }) this.state = 'listening' callback?.() }) } // resume functionality async resume () { this.logger.log({ type: 1, msg: 'Resuming client' }) await this.dht.resume() this.state = 'listening' this.logger.log({ type: 1, msg: 'Client resumed' }) } async pause () { this.logger.log({ type: 1, msg: 'Pausing client' }) await this.dht.suspend() this.state = 'paused' this.logger.log({ type: 1, msg: 'Client paused' }) } async destroy () { this.logger.log({ type: 1, msg: 'Destroying client' }) await this.dht.destroy() this.proxy?.close() this.proxy = null this.state = 'destroyed' this.logger.log({ type: 1, msg: 'Client destroyed' }) } // get mutable record stored on the dht async get (opts = {}) { this.logger.log({ type: 0, msg: 'Getting DHT record' }) const record = await this.dht.mutableGet(this.publicKey, opts) if (record) { const value = b4a.toString(record.value) this.logger.log({ type: 0, msg: `DHT get completed: seq=${record.seq}, value=${value}` }) return { seq: record.seq, value } } this.logger.log({ type: 2, msg: 'DHT get: no record found' }) return null } get info () { return { type: 'client', state: this.state, secure: this.secure, port: this.args.port, host: this.args.host, protocol: this.args.udp ? 'udp' : 'tcp', key: this.seed, publicKey: z32.encode(this.publicKey) } } static async ping (key, dht = null) { let ownDht = false if (!dht) { dht = new HyperDHT() ownDht = true } let result = null const keyBuffer = z32.decode(key) // Try assuming secure: true first let publicKey = HyperDHT.keyPair(keyBuffer).publicKey let record = await dht.mutableGet(publicKey, { latest: true }) if (record) { const value = b4a.toString(record.value) try { result = JSON.parse(value) result.protocol = result.udp ? 'udp' : 'tcp' } catch { // Ignore invalid JSON } } // If no result, try assuming secure: false if (!result) { publicKey = keyBuffer record = await dht.mutableGet(publicKey, { latest: true }) if (record) { const value = b4a.toString(record.value) try { result = JSON.parse(value) result.protocol = result.udp ? 'udp' : 'tcp' } catch { // Ignore invalid JSON } } } if (ownDht) { await dht.destroy() } return result } } // end client class module.exports = HolesailClient