Initial commit

This commit is contained in:
2023-03-23 00:47:07 +01:00
commit 329b222e67
10 changed files with 1216 additions and 0 deletions
+117
View File
@@ -0,0 +1,117 @@
import {
Container,
AuthenticationRequest,
AuthenticationResponse,
Message
} from './Protocol.js'
import { EventEmitter } from 'node:events'
import { createHash } from 'node:crypto'
export class Peer extends EventEmitter {
#id = 0
#socket = null
#msgMap = null
#salt = null
#address = null
constructor({ socket, salt }) {
super()
this.#socket = socket
this.#salt = salt
this.#msgMap = new Map()
this.#msgMap.set('AuthenticationRequest', this.#onauth)
this.#msgMap.set('Message', this.#onmessage)
this.#setupListeners()
}
#setupListeners() {
this.#socket.on('message', data => {
let message
try {
message = Container.decode(data)
} catch(err) {
console.error('(!) malformed message, disconnecting')
this.#socket.close()
return
}
const container = Container.toObject(message, {
oneofs: true
})
const handler = this.#msgMap.get(container.message)
if(handler) {
handler.call(this, container)
} else {
console.error(`(!) unknown handler for ${container.message}`)
}
})
this.on('incoming', ({ source, message }) => {
this.#send({
id: this.#id++,
message: Message.create({
address: source.address.toString(10),
port: message.Message.port,
data: message.Message.data
})
})
})
}
#send({ id, message }) {
const container = Container.create({
id,
[message.constructor.name]: message
})
const buf = Container.encode(container).finish()
console.log('sending:', buf)
this.#socket.send(buf)
}
#onauth(message) {
const { seed } = AuthenticationRequest.toObject(message.AuthenticationRequest, { longs: String })
if(this.#address !== null) {
this.#send({
id: message.id,
message: AuthenticationResponse.create({
status: 1,
address: this.#address
})
})
return
}
const hash = createHash('sha256').update(this.#salt + seed).digest('hex')
const address = BigInt('0x' + hash.substring(0, 16))
console.log('assigned address', address.toString(16).match(/.{1,4}/g).join(':'))
this.#address = address
console.log(address.toString(10))
this.#send({
id: message.id,
message: AuthenticationResponse.create({
status: 0,
address: address.toString(10)
})
})
}
#onmessage(message) {
const { address } = Message.toObject(message.Message, { longs: String })
this.emit('outgoing', {
destination: BigInt(address),
message
})
}
get address() {
return this.#address
}
}
+38
View File
@@ -0,0 +1,38 @@
import protobuf from 'protobufjs'
import { resolve } from 'path'
const import_proto = async (path, types) => {
const proto = await protobuf.load(`src/protocol/protobuf/${path}`)
return types.map(type => proto.lookupType(type))
}
export const [
Container
] = await import_proto('2.0/Container.proto', ['Container'])
export const [
AuthenticationRequest,
AuthenticationResponse
] = await import_proto('2.0/Authentication.proto', ['AuthenticationRequest', 'AuthenticationResponse'])
export const [
Message
] = await import_proto('2.0/Message.proto', ['Message'])
export const [
PublishRequest,
PublishResponse,
DiscoverRequest,
DiscoverResponse
] = await import_proto('2.0/PublishDiscover.proto', ['PublishRequest', 'PublishResponse', 'DiscoverRequest', 'DiscoverResponse'])
export const messages = [
Container,
AuthenticationRequest,
AuthenticationResponse,
Message,
PublishRequest,
PublishResponse,
DiscoverRequest,
DiscoverResponse
]
+72
View File
@@ -0,0 +1,72 @@
import { WebSocketServer } from 'ws'
import { randomBytes } from 'node:crypto'
import { Peer } from './Peer.js'
const DEFAULT_OPTIONS = {
zlibDeflateOptions: {
chunkSize: 1024,
memLevel: 7,
level: 3
},
zlibInflateOptions: {
chunkSize: 10 * 1024
},
clientNoContextTakeover: true,
serverNoContextTakeover: true,
serverMaxWindowBits: 10,
concurrencyLimit: 10,
threshold: 1024
}
export class Server {
#salt = null
#wss = null
#options = Object.create(null)
#peers = []
constructor({ deflate = false, salt }) {
if(deflate) {
this.#options.perMessageDeflate = DEFAULT_OPTIONS
}
this.#salt = salt
}
#randomizeAddress() {
let address
while(!address || this.#peers.find(peer => peer.address.compare(address) === 0)) {
address = randomBytes(8)
}
return address
}
listen(port) {
const wss = new WebSocketServer({
...this.#options,
port,
})
wss.on('connection', socket => {
const peer = new Peer({
salt: this.#salt,
socket
})
peer.on('outgoing', ({ destination, message }) => {
const destPeer = this.#peers.find(p => p.address === destination)
if(destPeer) {
destPeer.emit('incoming', { source: peer, message })
}
})
this.#peers.push(peer)
socket.on('close', () => {
this.#peers = this.#peers.filter(p => p !== peer)
})
})
this.#wss = wss
}
}
Submodule
+1
Submodule src/protocol added at d0747faf68